CentrED/Imaging/ImagingBitmap.pas

841 lines
27 KiB
Plaintext
Raw Normal View History

2022-05-08 10:47:53 +02:00
{
Vampyre Imaging Library
by Marek Mauder
https://github.com/galfar/imaginglib
https://imaginglib.sourceforge.io
- - - - -
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0.
}
{
This unit contains image format loader/saver for Windows Bitmap images.
}
unit ImagingBitmap;
{$I ImagingOptions.inc}
interface
uses
ImagingTypes, Imaging, ImagingUtility, ImagingFormats, ImagingIO;
type
{ Class for loading and saving Windows Bitmap images.
It can load/save 8bit indexed, 16, 24, 32 bit RGB or ARGB
images with or without RLE compression. It can also load 1/4 bit
indexed images and OS2 bitmaps.}
TBitmapFileFormat = class(TImageFileFormat)
protected
FUseRLE: LongBool;
procedure Define; override;
function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
OnlyFirstLevel: Boolean): Boolean; override;
function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
Index: LongInt): Boolean; override;
procedure ConvertToSupported(var Image: TImageData;
const Info: TImageFormatInfo); override;
public
function TestFormat(Handle: TImagingHandle): Boolean; override;
published
{ Controls that RLE compression is used during saving. Accessible trough
ImagingBitmapRLE option.}
property UseRLE: LongBool read FUseRLE write FUseRLE;
end;
implementation
const
SBitmapFormatName = 'Windows Bitmap Image';
SBitmapMasks = '*.bmp,*.dib';
BitmapSupportedFormats: TImageFormats = [ifIndex8, ifA1R5G5B5, ifA4R4G4B4,
ifR5G6B5, ifR8G8B8, ifA8R8G8B8, ifX1R5G5B5, ifX4R4G4B4, ifX8R8G8B8];
BitmapDefaultRLE = True;
const
{ Bitmap file identifier 'BM'.}
BMMagic: Word = 19778;
{ Constants for the TBitmapInfoHeader.Compression field.}
BI_RGB = 0;
BI_RLE8 = 1;
BI_RLE4 = 2;
BI_BITFIELDS = 3;
V3InfoHeaderSize = 40;
V4InfoHeaderSize = 108;
type
{ File Header for Windows/OS2 bitmap file.}
TBitmapFileHeader = packed record
ID: Word; // Is always 19778 : 'BM'
Size: UInt32; // File size
Reserved1: Word;
Reserved2: Word;
Offset: UInt32; // Offset from start pos to beginning of image bits
end;
{ Info Header for Windows bitmap file version 4.}
TBitmapInfoHeader = packed record
Size: UInt32;
Width: Int32;
Height: Int32;
Planes: Word;
BitCount: Word;
Compression: UInt32;
SizeImage: UInt32;
XPelsPerMeter: Int32;
YPelsPerMeter: Int32;
ClrUsed: UInt32;
ClrImportant: UInt32;
RedMask: UInt32;
GreenMask: UInt32;
BlueMask: UInt32;
AlphaMask: UInt32;
CSType: UInt32;
EndPoints: array[0..8] of UInt32;
GammaRed: UInt32;
GammaGreen: UInt32;
GammaBlue: UInt32;
end;
{ Info Header for OS2 bitmaps.}
TBitmapCoreHeader = packed record
Size: UInt32;
Width: Word;
Height: Word;
Planes: Word;
BitCount: Word;
end;
{ Used in RLE encoding and decoding.}
TRLEOpcode = packed record
Count: Byte;
Command: Byte;
end;
PRLEOpcode = ^TRLEOpcode;
{ TBitmapFileFormat class implementation }
procedure TBitmapFileFormat.Define;
begin
inherited;
FName := SBitmapFormatName;
FFeatures := [ffLoad, ffSave];
FSupportedFormats := BitmapSupportedFormats;
FUseRLE := BitmapDefaultRLE;
AddMasks(SBitmapMasks);
RegisterOption(ImagingBitmapRLE, @FUseRLE);
end;
function TBitmapFileFormat.LoadData(Handle: TImagingHandle;
var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
var
BF: TBitmapFileHeader;
BI: TBitmapInfoHeader;
BC: TBitmapCoreHeader;
IsOS2: Boolean;
PalRGB: PPalette24;
I, FPalSize, AlignedSize, StartPos, HeaderSize, AlignedWidthBytes, WidthBytes: LongInt;
Info: TImageFormatInfo;
Data: Pointer;
procedure LoadRGB;
var
I: LongInt;
LineBuffer: PByte;
begin
with Images[0], GetIO do
begin
// If BI.Height is < 0 then image data are stored non-flipped
// but default in windows is flipped so if Height is positive we must
// flip it
if BI.BitCount < 8 then
begin
// For 1 and 4 bit images load aligned data, they will be converted to
// 8 bit and unaligned later
GetMem(Data, AlignedSize);
if BI.Height < 0 then
Read(Handle, Data, AlignedSize)
else
for I := Height - 1 downto 0 do
Read(Handle, @PByteArray(Data)[I * AlignedWidthBytes], AlignedWidthBytes);
end
else
begin
// Images with pixels of size >= 1 Byte are read line by line and
// copied to image bits without padding bytes
GetMem(LineBuffer, AlignedWidthBytes);
try
if BI.Height < 0 then
for I := 0 to Height - 1 do
begin
Read(Handle, LineBuffer, AlignedWidthBytes);
Move(LineBuffer^, PByteArray(Bits)[I * WidthBytes], WidthBytes);
end
else
for I := Height - 1 downto 0 do
begin
Read(Handle, LineBuffer, AlignedWidthBytes);
Move(LineBuffer^, PByteArray(Bits)[I * WidthBytes], WidthBytes);
end;
finally
FreeMemNil(LineBuffer);
end;
end;
end;
end;
procedure LoadRLE4;
var
RLESrc: PByteArray;
Row, Col, WriteRow, I: Integer;
SrcPos: UInt32;
DeltaX, DeltaY, Low, High: Byte;
Pixels: PByteArray;
OpCode: TRLEOpcode;
NegHeightBitmap: Boolean;
begin
GetMem(RLESrc, BI.SizeImage);
GetIO.Read(Handle, RLESrc, BI.SizeImage);
with Images[0] do
try
Low := 0;
Pixels := Bits;
SrcPos := 0;
NegHeightBitmap := BI.Height < 0;
Row := 0; // Current row in dest image
Col := 0; // Current column in dest image
// Row in dest image where actual writing will be done
WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row);
while (Row < Height) and (SrcPos < BI.SizeImage) do
begin
// Read RLE op-code
OpCode := PRLEOpcode(@RLESrc[SrcPos])^;
Inc(SrcPos, SizeOf(OpCode));
if OpCode.Count = 0 then
begin
// A byte Count of zero means that this is a special
// instruction.
case OpCode.Command of
0:
begin
// Move to next row
Inc(Row);
WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row);
Col := 0;
end ;
1: Break; // Image is finished
2:
begin
// Move to a new relative position
DeltaX := RLESrc[SrcPos];
DeltaY := RLESrc[SrcPos + 1];
Inc(SrcPos, 2);
Inc(Col, DeltaX);
Inc(Row, DeltaY);
end
else
// Do not read data after EOF
if SrcPos + OpCode.Command > BI.SizeImage then
OpCode.Command := BI.SizeImage - SrcPos;
// Take padding bytes and nibbles into account
if Col + OpCode.Command > Width then
OpCode.Command := Width - Col;
// Store absolute data. Command code is the
// number of absolute bytes to store
for I := 0 to OpCode.Command - 1 do
begin
if (I and 1) = 0 then
begin
High := RLESrc[SrcPos] shr 4;
Low := RLESrc[SrcPos] and $F;
Pixels[WriteRow * Width + Col] := High;
Inc(SrcPos);
end
else
Pixels[WriteRow * Width + Col] := Low;
Inc(Col);
end;
// Odd number of bytes is followed by a pad byte
if (OpCode.Command mod 4) in [1, 2] then
Inc(SrcPos);
end;
end
else
begin
// Take padding bytes and nibbles into account
if Col + OpCode.Count > Width then
OpCode.Count := Width - Col;
// Store a run of the same color value
for I := 0 to OpCode.Count - 1 do
begin
if (I and 1) = 0 then
Pixels[WriteRow * Width + Col] := OpCode.Command shr 4
else
Pixels[WriteRow * Width + Col] := OpCode.Command and $F;
Inc(Col);
end;
end;
end;
finally
FreeMem(RLESrc);
end;
end;
procedure LoadRLE8;
var
RLESrc: PByteArray;
SrcCount, Row, Col, WriteRow: Integer;
SrcPos: UInt32;
DeltaX, DeltaY: Byte;
Pixels: PByteArray;
OpCode: TRLEOpcode;
NegHeightBitmap: Boolean;
begin
GetMem(RLESrc, BI.SizeImage);
GetIO.Read(Handle, RLESrc, BI.SizeImage);
with Images[0] do
try
Pixels := Bits;
SrcPos := 0;
NegHeightBitmap := BI.Height < 0;
Row := 0; // Current row in dest image
Col := 0; // Current column in dest image
// Row in dest image where actual writing will be done
WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row);
while (Row < Height) and (SrcPos < BI.SizeImage) do
begin
// Read RLE op-code
OpCode := PRLEOpcode(@RLESrc[SrcPos])^;
Inc(SrcPos, SizeOf(OpCode));
if OpCode.Count = 0 then
begin
// A byte Count of zero means that this is a special
// instruction.
case OpCode.Command of
0:
begin
// Move to next row
Inc(Row);
WriteRow := Iff(NegHeightBitmap, Row, Height - 1 - Row);
Col := 0;
end ;
1: Break; // Image is finished
2:
begin
// Move to a new relative position
DeltaX := RLESrc[SrcPos];
DeltaY := RLESrc[SrcPos + 1];
Inc(SrcPos, 2);
Inc(Col, DeltaX);
Inc(Row, DeltaY);
end
else
SrcCount := OpCode.Command;
// Do not read data after EOF
if SrcPos + OpCode.Command > BI.SizeImage then
OpCode.Command := BI.SizeImage - SrcPos;
// Take padding bytes into account
if Col + OpCode.Command > Width then
OpCode.Command := Width - Col;
// Store absolute data. Command code is the
// number of absolute bytes to store
Move(RLESrc[SrcPos], Pixels[WriteRow * Width + Col], OpCode.Command);
Inc(SrcPos, SrcCount);
Inc(Col, OpCode.Command);
// Odd number of bytes is followed by a pad byte
if (SrcCount mod 2) = 1 then
Inc(SrcPos);
end;
end
else
begin
// Take padding bytes into account
if Col + OpCode.Count > Width then
OpCode.Count := Width - Col;
// Store a run of the same color value. Count is number of bytes to store
FillChar(Pixels [WriteRow * Width + Col], OpCode.Count, OpCode.Command);
Inc(Col, OpCode.Count);
end;
end;
finally
FreeMem(RLESrc);
end;
end;
begin
Data := nil;
SetLength(Images, 1);
with GetIO, Images[0] do
try
FillChar(BI, SizeOf(BI), 0);
StartPos := Tell(Handle);
Read(Handle, @BF, SizeOf(BF));
Read(Handle, @BI.Size, SizeOf(BI.Size));
IsOS2 := BI.Size = SizeOf(TBitmapCoreHeader);
// Bitmap Info reading
if IsOS2 then
begin
// OS/2 type bitmap, reads info header without 4 already read bytes
Read(Handle, @PByteArray(@BC)[SizeOf(BI.Size)],
SizeOf(TBitmapCoreHeader) - SizeOf(BI.Size));
with BI do
begin
ClrUsed := 0;
Compression := BI_RGB;
BitCount := BC.BitCount;
Height := BC.Height;
Width := BC.Width;
end;
end
else
begin
// Windows type bitmap
HeaderSize := Min(BI.Size - SizeOf(BI.Size), SizeOf(BI) - SizeOf(BI.Size)); // do not read more than size of BI!
Read(Handle, @PByteArray(@BI)[SizeOf(BI.Size)], HeaderSize);
// SizeImage can be 0 for BI_RGB images, but it is here because of:
// I saved 8bit bitmap in Paint Shop Pro 8 as OS2 RLE compressed.
// It wrote strange 64 Byte Info header with SizeImage set to 0
// Some progs were able to open it, some were not.
if BI.SizeImage = 0 then
BI.SizeImage := BF.Size - BF.Offset;
end;
// Bit mask reading. Only read it if there is V3 header, V4 header has
// masks loaded already (only masks for RGB in V3).
if (BI.Compression = BI_BITFIELDS) and (BI.Size = V3InfoHeaderSize) then
Read(Handle, @BI.RedMask, SizeOf(BI.RedMask) * 3);
case BI.BitCount of
1, 4, 8: Format := ifIndex8;
16:
if BI.RedMask = $0F00 then
// Set XRGB4 or ARGB4 according to value of alpha mask
Format := IffFormat(BI.AlphaMask = 0, ifX4R4G4B4, ifA4R4G4B4)
else if BI.RedMask = $F800 then
Format := ifR5G6B5
else
// R5G5B5 is default 16bit format (with Compression = BI_RGB or masks).
// We set it to A1.. and later there is a check if there are any alpha values
// and if not it is changed to X1R5G5B5
Format := ifA1R5G5B5;
24: Format := ifR8G8B8;
32: Format := ifA8R8G8B8; // As with R5G5B5 there is alpha check later
end;
NewImage(BI.Width, Abs(BI.Height), Format, Images[0]);
Info := GetFormatInfo(Format);
WidthBytes := Width * Info.BytesPerPixel;
AlignedWidthBytes := (((Width * BI.BitCount) + 31) shr 5) * 4;
AlignedSize := Height * LongInt(AlignedWidthBytes);
// Palette settings and reading
if BI.BitCount <= 8 then
begin
// Seek to the beginning of palette
Seek(Handle, StartPos + SizeOf(TBitmapFileHeader) + LongInt(BI.Size),
smFromBeginning);
if IsOS2 then
begin
// OS/2 type
FPalSize := 1 shl BI.BitCount;
GetMem(PalRGB, FPalSize * SizeOf(TColor24Rec));
try
Read(Handle, PalRGB, FPalSize * SizeOf(TColor24Rec));
for I := 0 to FPalSize - 1 do
with PalRGB[I] do
begin
Palette[I].R := R;
Palette[I].G := G;
Palette[I].B := B;
end;
finally
FreeMemNil(PalRGB);
end;
end
else
begin
// Windows type
FPalSize := BI.ClrUsed;
if FPalSize = 0 then
FPalSize := 1 shl BI.BitCount;
Read(Handle, Palette, FPalSize * SizeOf(TColor32Rec));
end;
for I := 0 to Info.PaletteEntries - 1 do
Palette[I].A := $FF;
end;
// Seek to the beginning of image bits
Seek(Handle, StartPos + LongInt(BF.Offset), smFromBeginning);
case BI.Compression of
BI_RGB: LoadRGB;
BI_RLE4: LoadRLE4;
BI_RLE8: LoadRLE8;
BI_BITFIELDS: LoadRGB;
end;
if BI.AlphaMask = 0 then
begin
// Alpha mask is not stored in file (V3) or not defined.
// Check alpha channels of loaded images if they might contain them.
if Format = ifA1R5G5B5 then
begin
// Check if there is alpha channel present in A1R5GB5 images, if it is not
// change format to X1R5G5B5
if not Has16BitImageAlpha(Width * Height, Bits) then
Format := ifX1R5G5B5;
end
else if Format = ifA8R8G8B8 then
begin
// Check if there is alpha channel present in A8R8G8B8 images, if it is not
// change format to X8R8G8B8
if not Has32BitImageAlpha(Width * Height, Bits) then
Format := ifX8R8G8B8;
end;
end;
if BI.BitCount < 8 then
begin
// 1 and 4 bpp images are supported only for loading which is now
// so we now convert them to 8bpp (and unalign scanlines).
case BI.BitCount of
1: Convert1To8(Data, Bits, Width, Height, AlignedWidthBytes, False);
4:
begin
// RLE4 bitmaps are translated to 8bit during RLE decoding
if BI.Compression <> BI_RLE4 then
Convert4To8(Data, Bits, Width, Height, AlignedWidthBytes, False);
end;
end;
// Enlarge palette
ReallocMem(Palette, Info.PaletteEntries * SizeOf(TColor32Rec));
end;
Result := True;
finally
FreeMemNil(Data);
end;
end;
function TBitmapFileFormat.SaveData(Handle: TImagingHandle;
const Images: TDynImageDataArray; Index: LongInt): Boolean;
var
StartPos, EndPos, I, Pad, PadSize, WidthBytes: LongInt;
BF: TBitmapFileHeader;
BI: TBitmapInfoHeader;
Info: TImageFormatInfo;
ImageToSave: TImageData;
MustBeFreed: Boolean;
procedure SaveRLE8;
const
BufferSize = 8 * 1024;
var
X, Y, I, SrcPos: LongInt;
DiffCount, SameCount: Byte;
Pixels: PByteArray;
Buffer: array[0..BufferSize - 1] of Byte;
BufferPos: LongInt;
procedure WriteByte(ByteToWrite: Byte);
begin
if BufferPos = BufferSize then
begin
// Flush buffer if necessary
GetIO.Write(Handle, @Buffer, BufferPos);
BufferPos := 0;
end;
Buffer[BufferPos] := ByteToWrite;
Inc(BufferPos);
end;
begin
BufferPos := 0;
with GetIO, ImageToSave do
begin
for Y := Height - 1 downto 0 do
begin
X := 0;
SrcPos := 0;
Pixels := @PByteArray(Bits)[Y * Width];
while X < Width do
begin
SameCount := 1;
DiffCount := 0;
// Determine run length
while X + SameCount < Width do
begin
// If we reach max run length or byte with different value
// we end this run
if (SameCount = 255) or (Pixels[SrcPos + SameCount] <> Pixels[SrcPos]) then
Break;
Inc(SameCount);
end;
if SameCount = 1 then
begin
// If there are not some bytes with the same value we
// compute how many different bytes are there
while X + DiffCount < Width do
begin
// Stop diff byte counting if there two bytes with the same value
// or DiffCount is too big
if (DiffCount = 255) or (Pixels[SrcPos + DiffCount + 1] =
Pixels[SrcPos + DiffCount]) then
Break;
Inc(DiffCount);
end;
end;
// Now store absolute data (direct copy image->file) or
// store RLE code only (number of repeats + byte to be repeated)
if DiffCount > 2 then
begin
// Save 'Absolute Data' (0 + number of bytes) but only
// if number is >2 because (0+1) and (0+2) are other special commands
WriteByte(0);
WriteByte(DiffCount);
// Write absolute data to buffer
for I := 0 to DiffCount - 1 do
WriteByte(Pixels[SrcPos + I]);
Inc(X, DiffCount);
Inc(SrcPos, DiffCount);
// Odd number of bytes must be padded
if (DiffCount mod 2) = 1 then
WriteByte(0);
end
else
begin
// Save number of repeats and byte that should be repeated
WriteByte(SameCount);
WriteByte(Pixels[SrcPos]);
Inc(X, SameCount);
Inc(SrcPos, SameCount);
end;
end;
// Save 'End Of Line' command
WriteByte(0);
WriteByte(0);
end;
// Save 'End Of Bitmap' command
WriteByte(0);
WriteByte(1);
// Flush buffer
GetIO.Write(Handle, @Buffer, BufferPos);
end;
end;
begin
Result := False;
if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then
with GetIO, ImageToSave do
try
Info := GetFormatInfo(Format);
StartPos := Tell(Handle);
FillChar(BF, SizeOf(BF), 0);
FillChar(BI, SizeOf(BI), 0);
// Other fields will be filled later - we don't know all values now
BF.ID := BMMagic;
Write(Handle, @BF, SizeOf(BF));
if Info.HasAlphaChannel and (Info.BytesPerPixel = 2){V4 temp hack} then
// Save images with alpha in V4 format
BI.Size := V4InfoHeaderSize
else
// Save images without alpha in V3 format - for better compatibility
BI.Size := V3InfoHeaderSize;
BI.Width := Width;
BI.Height := Height;
BI.Planes := 1;
BI.BitCount := Info.BytesPerPixel * 8;
BI.XPelsPerMeter := 2835; // 72 dpi
BI.YPelsPerMeter := 2835; // 72 dpi
// Set compression
if (Info.BytesPerPixel = 1) and FUseRLE then
BI.Compression := BI_RLE8
else if (Info.HasAlphaChannel or
((BI.BitCount = 16) and (Format <> ifX1R5G5B5))) and (Info.BytesPerPixel = 2){V4 temp hack} then
BI.Compression := BI_BITFIELDS
else
BI.Compression := BI_RGB;
// Write header (first time)
Write(Handle, @BI, BI.Size);
// Write mask info
if BI.Compression = BI_BITFIELDS then
begin
if BI.BitCount = 16 then
with Info.PixelFormat^ do
begin
BI.RedMask := RBitMask;
BI.GreenMask := GBitMask;
BI.BlueMask := BBitMask;
BI.AlphaMask := ABitMask;
end
else
begin
// Set masks for A8R8G8B8
BI.RedMask := $00FF0000;
BI.GreenMask := $0000FF00;
BI.BlueMask := $000000FF;
BI.AlphaMask := $FF000000;
end;
// If V3 header is used RGB masks must be written to file separately.
// V4 header has embedded masks (V4 is default for formats with alpha).
if BI.Size = V3InfoHeaderSize then
Write(Handle, @BI.RedMask, SizeOf(BI.RedMask) * 3);
end;
// Write palette
if Palette <> nil then
Write(Handle, Palette, Info.PaletteEntries * SizeOf(TColor32Rec));
BF.Offset := Tell(Handle) - StartPos;
if BI.Compression <> BI_RLE8 then
begin
// Save uncompressed data, scanlines must be filled with pad bytes
// to be multiples of 4, save as bottom-up (Windows native) bitmap
Pad := 0;
WidthBytes := Width * Info.BytesPerPixel;
PadSize := ((Width * BI.BitCount + 31) div 32) * 4 - WidthBytes;
for I := Height - 1 downto 0 do
begin
Write(Handle, @PByteArray(Bits)[I * WidthBytes], WidthBytes);
if PadSize > 0 then
Write(Handle, @Pad, PadSize);
end;
end
else
begin
// Save data with RLE8 compression
SaveRLE8;
end;
EndPos := Tell(Handle);
Seek(Handle, StartPos, smFromBeginning);
// Rewrite header with new values
BF.Size := EndPos - StartPos;
BI.SizeImage := BF.Size - BF.Offset;
Write(Handle, @BF, SizeOf(BF));
Write(Handle, @BI, BI.Size);
Seek(Handle, EndPos, smFromBeginning);
Result := True;
finally
if MustBeFreed then
FreeImage(ImageToSave);
end;
end;
procedure TBitmapFileFormat.ConvertToSupported(var Image: TImageData;
const Info: TImageFormatInfo);
var
ConvFormat: TImageFormat;
begin
if Info.IsFloatingPoint then
// Convert FP image to RGB/ARGB according to presence of alpha channel
ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8)
else if Info.HasGrayChannel or Info.IsIndexed then
// Convert all grayscale and indexed images to Index8 unless they have alpha
// (preserve it)
ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifIndex8)
else if Info.HasAlphaChannel then
// Convert images with alpha channel to A8R8G8B8
ConvFormat := ifA8R8G8B8
else if Info.UsePixelFormat then
// Convert 16bit RGB images (no alpha) to X1R5G5B5
ConvFormat := ifX1R5G5B5
else
// Convert all other formats to R8G8B8
ConvFormat := ifR8G8B8;
ConvertImage(Image, ConvFormat);
end;
function TBitmapFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
var
Hdr: TBitmapFileHeader;
ReadCount: LongInt;
begin
Result := False;
if Handle <> nil then
with GetIO do
begin
ReadCount := Read(Handle, @Hdr, SizeOf(Hdr));
Seek(Handle, -ReadCount, smFromCurrent);
Result := (Hdr.ID = BMMagic) and (ReadCount = SizeOf(Hdr));
end;
end;
initialization
RegisterImageFileFormat(TBitmapFileFormat);
{
File Notes:
-- TODOS ----------------------------------------------------
- nothing now
- Add option to choose to save V3 or V4 headers.
-- 0.25.0 Changes/Bug Fixes ---------------------------------
- Fixed problem with indexed BMP loading - some pal entries
could end up with alpha=0.
-- 0.23 Changes/Bug Fixes -----------------------------------
- Now saves bitmaps as bottom-up for better compatibility
(mainly Lazarus' TImage!).
- Fixed crash when loading bitmaps with headers larger than V4.
- Temp hacks to disable V4 headers for 32bit images (compatibility with
other soft).
-- 0.21 Changes/Bug Fixes -----------------------------------
- Removed temporary data allocation for image with aligned scanlines.
They are now directly written to output so memory requirements are
much lower now.
- Now uses and recognizes BITMAPINFOHEADERV4 when loading/saving.
Mainly for formats with alpha channels.
- Added ifR5G6B5 to supported formats, changed converting to supported
formats little bit.
- Rewritten SaveRLE8 nested procedure. Old code was long and
mysterious - new is short and much more readable.
- MakeCompatible method moved to base class, put ConvertToSupported here.
GetSupportedFormats removed, it is now set in constructor.
- Rewritten LoadRLE4 and LoadRLE8 nested procedures.
Should be less buggy an more readable (load inspired by Colosseum Builders' code).
- Made public properties for options registered to SetOption/GetOption
functions.
- Added alpha check to 32b bitmap loading too (teh same as in 16b
bitmap loading).
- Moved Convert1To8 and Convert4To8 to ImagingFormats
- Changed extensions to filename masks.
- Changed SaveData, LoadData, and MakeCompatible methods according
to changes in base class in Imaging unit.
-- 0.19 Changes/Bug Fixes -----------------------------------
- fixed wrong const that caused A4R4G4B4 BMPs to load as A1R5G5B5
- fixed the bug that caused 8bit RLE compressed bitmaps to load as
whole black
-- 0.17 Changes/Bug Fixes -----------------------------------
- 16 bit images are usually without alpha but some has alpha
channel and there is no indication of it - so I have added
a check: if all pixels of image are with alpha = 0 image is treated
as X1R5G5B5 otherwise as A1R5G5B5
-- 0.13 Changes/Bug Fixes -----------------------------------
- when loading 1/4 bit images with dword aligned dimensions
there was ugly memory rewriting bug causing image corruption
}
end.