CentrED/Imaging/ImagingBitmap.pas

858 lines
27 KiB
Plaintext
Raw Permalink Normal View History

{
$Id: ImagingBitmap.pas 129 2008-08-06 20:01:30Z galfar $
Vampyre Imaging Library
by Marek Mauder
http://imaginglib.sourceforge.net
The contents of this file are used with permission, subject to the Mozilla
Public License Version 1.1 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
Alternatively, the contents of this file may be used under the terms of the
GNU Lesser General Public License (the "LGPL License"), in which case the
provisions of the LGPL License are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms
of the LGPL License and not to allow others to use your version of this file
under the MPL, indicate your decision by deleting the provisions above and
replace them with the notice and other provisions required by the LGPL
License. If you do not delete the provisions above, a recipient may use
your version of this file under either the MPL or the LGPL License.
For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
}
{ 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;
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
constructor Create; override;
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: LongWord; // Filesize
Reserved1: Word;
Reserved2: Word;
Offset: LongWord; // Offset from start pos to beginning of image bits
end;
{ Info Header for Windows bitmap file version 4.}
TBitmapInfoHeader = packed record
Size: LongWord;
Width: LongInt;
Height: LongInt;
Planes: Word;
BitCount: Word;
Compression: LongWord;
SizeImage: LongWord;
XPelsPerMeter: LongInt;
YPelsPerMeter: LongInt;
ClrUsed: LongInt;
ClrImportant: LongInt;
RedMask: LongWord;
GreenMask: LongWord;
BlueMask: LongWord;
AlphaMask: LongWord;
CSType: LongWord;
EndPoints: array[0..8] of LongWord;
GammaRed: LongWord;
GammaGreen: LongWord;
GammaBlue: LongWord;
end;
{ Info Header for OS2 bitmaps.}
TBitmapCoreHeader = packed record
Size: LongWord;
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 }
constructor TBitmapFileFormat.Create;
begin
inherited Create;
FName := SBitmapFormatName;
FCanLoad := True;
FCanSave := True;
FIsMultiImageFormat := False;
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: LongInt;
SrcPos: LongWord;
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 actuall writting 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: LongInt;
SrcPos: LongWord;
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 actuall writting 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 laoded 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 begining 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);
4:
begin
// RLE4 bitmaps are translated to 8bit during RLE decoding
if BI.Compression <> BI_RLE4 then
Convert4To8(Data, Bits, Width, Height, AlignedWidthBytes);
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.
- Addded 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 rewritting bug causing image corruption
}
end.