end;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- TBitfieldFormat = class(TFormatDescriptor)
+ TbmpBitfieldFormat = class(TFormatDescriptor)
private
procedure SetRedMask (const aValue: UInt64);
procedure SetGreenMask(const aValue: UInt64);
end;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- TColorTableEnty = packed record
+ TbmpColorTableEnty = packed record
b, g, r, a: Byte;
end;
- TColorTable = array of TColorTableEnty;
- TColorTableFormat = class(TFormatDescriptor)
+ TbmpColorTable = array of TbmpColorTableEnty;
+ TbmpColorTableFormat = class(TFormatDescriptor)
private
- fColorTable: TColorTable;
+ fColorTable: TbmpColorTable;
public
property PixelSize: Single read fPixelSize write fPixelSize;
- property ColorTable: TColorTable read fColorTable write fColorTable;
+ property ColorTable: TbmpColorTable read fColorTable write fColorTable;
property Range: TglBitmapColorRec read fRange write fRange;
+ property Shift: TShiftRec read fShift write fShift;
+ property Format: TglBitmapFormat read fFormat write fFormat;
+
+ procedure CreateColorTable;
procedure Map(const aPixel: TglBitmapPixelData; var aData: PByte; var aMapData: Pointer); override;
procedure Unmap(var aData: PByte; var aPixel: TglBitmapPixelData; var aMapData: Pointer); override;
end;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+function glBitmapShiftRec(const r, g, b, a: Byte): TShiftRec;
+begin
+ result.r := r;
+ result.g := g;
+ result.b := b;
+ result.a := a;
+end;
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function FormatGetSupportedFiles(const aFormat: TglBitmapFormat): TglBitmapFileTypes;
begin
+ result := [ftDDS, ftTGA];
+
+ if (aFormat in [
+ //4 bbp
+ tfLuminance4,
+
+ //8bpp
+ tfR3G3B2, tfLuminance8,
+
+ //16bpp
+ tfRGB4, tfRGB5, tfR5G6B5, tfRGB5A1, tfRGBA4,
+ tfBGR4, tfBGR5, tfB5G6R5, tfBGR5A1, tfBGRA4,
+
+ //24bpp
+ tfBGR8, tfRGB8,
+
+ //32bpp
+ tfRGB10, tfRGB10A2, tfRGBA8,
+ tfBGR10, tfBGR10A2, tfBGRA8]) then
+ result := result + [ftBMP];
+
//TODO Supported File Formats!
- result := [ftDDS, ftTGA, ftBMP];
+
(*
{$IFDEF GLB_SUPPORT_PNG_WRITE}
if aFormat in [
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TBitfieldFormat/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TBitfieldFormat.SetRedMask(const aValue: UInt64);
+procedure TbmpBitfieldFormat.SetRedMask(const aValue: UInt64);
begin
Update(aValue, fRange.r, fShift.r);
end;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TBitfieldFormat.SetGreenMask(const aValue: UInt64);
+procedure TbmpBitfieldFormat.SetGreenMask(const aValue: UInt64);
begin
Update(aValue, fRange.g, fShift.g);
end;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TBitfieldFormat.SetBlueMask(const aValue: UInt64);
+procedure TbmpBitfieldFormat.SetBlueMask(const aValue: UInt64);
begin
Update(aValue, fRange.b, fShift.b);
end;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TBitfieldFormat.SetAlphaMask(const aValue: UInt64);
+procedure TbmpBitfieldFormat.SetAlphaMask(const aValue: UInt64);
begin
Update(aValue, fRange.a, fShift.a);
end;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TBitfieldFormat.Update(aMask: UInt64; out aRange: Cardinal; out
+procedure TbmpBitfieldFormat.Update(aMask: UInt64; out aRange: Cardinal; out
aShift: Byte);
begin
aShift := 0;
end;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TBitfieldFormat.Map(const aPixel: TglBitmapPixelData; var aData: PByte; var aMapData: Pointer);
+procedure TbmpBitfieldFormat.Map(const aPixel: TglBitmapPixelData; var aData: PByte; var aMapData: Pointer);
var
data: UInt64;
s: Integer;
end;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TBitfieldFormat.Unmap(var aData: PByte; var aPixel: TglBitmapPixelData; var aMapData: Pointer);
+procedure TbmpBitfieldFormat.Unmap(var aData: PByte; var aPixel: TglBitmapPixelData; var aMapData: Pointer);
var
data: UInt64;
s, i: Integer;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//TColorTableFormat///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-procedure TColorTableFormat.Map(const aPixel: TglBitmapPixelData; var aData: PByte; var aMapData: Pointer);
+procedure TbmpColorTableFormat.CreateColorTable;
+var
+ bits: Byte;
+ len: Integer;
+ i: Integer;
begin
- raise EglBitmapException.Create('mapping of color table formats is not supported');
+ if not (Format in [tfLuminance4, tfLuminance8, tfR3G3B2]) then
+ raise EglBitmapException.Create(UNSUPPORTED_FORMAT);
+
+ if (Format = tfLuminance4) then
+ SetLength(fColorTable, 16)
+ else
+ SetLength(fColorTable, 256);
+
+ case Format of
+ tfLuminance4: begin
+ for i := 0 to High(fColorTable) do begin
+ fColorTable[i].r := 16 * i;
+ fColorTable[i].g := 16 * i;
+ fColorTable[i].b := 16 * i;
+ fColorTable[i].a := 0;
+ end;
+ end;
+
+ tfLuminance8: begin
+ for i := 0 to High(fColorTable) do begin
+ fColorTable[i].r := i;
+ fColorTable[i].g := i;
+ fColorTable[i].b := i;
+ fColorTable[i].a := 0;
+ end;
+ end;
+
+ tfR3G3B2: begin
+ for i := 0 to High(fColorTable) do begin
+ fColorTable[i].r := Round(((i shr Shift.r) and Range.r) / Range.r * 255);
+ fColorTable[i].g := Round(((i shr Shift.g) and Range.g) / Range.g * 255);
+ fColorTable[i].b := Round(((i shr Shift.b) and Range.b) / Range.b * 255);
+ fColorTable[i].a := 0;
+
+ s := SysUtils.Format('%.2X%.2X%.2X' + sLineBreak, [fColorTable[i].r, fColorTable[i].g, fColorTable[i].b]);
+ fs.Write(s[1], Length(s));
+ end;
+ end;
+ end;
end;
-procedure TColorTableFormat.Unmap(var aData: PByte; var aPixel: TglBitmapPixelData; var aMapData: Pointer);
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+procedure TbmpColorTableFormat.Map(const aPixel: TglBitmapPixelData; var aData: PByte; var aMapData: Pointer);
+var
+ d: Byte;
+begin
+ if not (Format in [tfLuminance4, tfLuminance8, tfR3G3B2]) then
+ raise EglBitmapException.Create(UNSUPPORTED_FORMAT);
+
+ case Format of
+ tfLuminance4: begin
+ if (aMapData = nil) then
+ aData^ := 0;
+ d := LuminanceWeight(aPixel) and Range.r;
+ aData^ := aData^ or (d shl (4 - PtrInt(aMapData)));
+ inc(aMapData, 4);
+ if (PtrInt(aMapData) >= 8) then begin
+ inc(aData);
+ aMapData := nil;
+ end;
+ end;
+
+ tfLuminance8: begin
+ aData^ := LuminanceWeight(aPixel) and Range.r;
+ inc(aData);
+ end;
+
+ tfR3G3B2: begin
+ aData^ := Round(
+ ((aPixel.Data.r and Range.r) shl Shift.r) or
+ ((aPixel.Data.g and Range.g) shl Shift.g) or
+ ((aPixel.Data.b and Range.b) shl Shift.b));
+ inc(aData);
+ end;
+ end;
+end;
+
+procedure TbmpColorTableFormat.Unmap(var aData: PByte; var aPixel: TglBitmapPixelData; var aMapData: Pointer);
type
PUInt64 = ^UInt64;
var
inc(aData, s);
end;
-destructor TColorTableFormat.Destroy;
+destructor TbmpColorTableFormat.Destroy;
begin
SetLength(fColorTable, 0);
inherited Destroy;
biClrImportant: Cardinal;
end;
- (* TODO: delete?
- TBMPInfoOS = packed record
- biSize: Cardinal;
- biWidth: Longint;
- biHeight: Longint;
- biPlanes: Word;
- biBitCount: Word;
- end;
- *)
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function TglBitmap.LoadBMP(const aStream: TStream): Boolean;
end;
end;
- function ReadColorTable(var aFormat: TglBitmapFormat; const aInfo: TBMPInfo): TColorTableFormat;
+ function ReadColorTable(var aFormat: TglBitmapFormat; const aInfo: TBMPInfo): TbmpColorTableFormat;
var
i, c: Integer;
- ColorTable: TColorTable;
+ ColorTable: TbmpColorTable;
begin
result := nil;
if (aInfo.biBitCount >= 16) then
c := 1 shl aInfo.biBitCount;
SetLength(ColorTable, c);
for i := 0 to c-1 do begin
- aStream.Read(ColorTable[i], SizeOf(TColorTableEnty));
+ aStream.Read(ColorTable[i], SizeOf(TbmpColorTableEnty));
if (ColorTable[i].r <> ColorTable[i].g) or (ColorTable[i].g <> ColorTable[i].b) then
aFormat := tfRGB8;
end;
- result := TColorTableFormat.Create;
+ result := TbmpColorTableFormat.Create;
result.PixelSize := aInfo.biBitCount / 8;
result.ColorTable := ColorTable;
- result.Range := glBitmapColorRec($FF, $FF, $FF, $00);
+ result.Range := glBitmapColorRec($FF, $FF, $FF, $00);
end;
//////////////////////////////////////////////////////////////////////////////////////////////////
function CheckBitfields(var aFormat: TglBitmapFormat; const aMask: TglBitmapColorRec;
- const aInfo: TBMPInfo): TBitfieldFormat;
+ const aInfo: TBMPInfo): TbmpBitfieldFormat;
var
TmpFormat: TglBitmapFormat;
FormatDesc: TFormatDescriptor;
if (aMask.a <> 0) and not TFormatDescriptor.Get(aFormat).HasAlpha then
aFormat := TFormatDescriptor.Get(aFormat).WithAlpha;
- result := TBitfieldFormat.Create;
+ result := TbmpBitfieldFormat.Create;
result.PixelSize := aInfo.biBitCount / 8;
result.RedMask := aMask.r;
result.GreenMask := aMask.g;
LineBuf, ImageData, TmpData: PByte;
SourceMD, DestMD: Pointer;
BmpFormat: TglBitmapFormat;
- ColorTable: TColorTable;
+ ColorTable: TbmpColorTable;
//records
Mask: TglBitmapColorRec;
var
Header: TBMPHeader;
Info: TBMPInfo;
- pData, pTemp: pByte;
+ Converter: TbmpColorTableFormat;
+ FormatDesc: TFormatDescriptor;
+ SourceFD, DestFD: Pointer;
+ pData, srcData, dstData, ConvertBuffer: pByte;
+ Pixel: TglBitmapPixelData;
PixelFormat: TglBitmapPixelData;
- FormatDesc: TFormatDescriptor;
- ImageSize, LineSize, Padding, LineIdx, ColorIdx: Integer;
- Temp, RedMask, GreenMask, BlueMask, AlphaMask: Cardinal;
+ ImageSize, wbLineSize, rbLineSize, Padding, LineIdx, PixelIdx, i: Integer;
+ RedMask, GreenMask, BlueMask, AlphaMask: Cardinal;
PaddingBuff: Cardinal;
if not (ftBMP in FormatGetSupportedFiles(Format)) then
raise EglBitmapUnsupportedFormatFormat.Create('SaveBMP - ' + UNSUPPORTED_FORMAT);
- ImageSize := TFormatDescriptor.Get(Format).GetSize(Dimension);
+ Converter := nil;
+ FormatDesc := TFormatDescriptor.Get(Format);
+ ImageSize := FormatDesc.GetSize(Dimension);
+ FillChar(Header, SizeOf(Header), 0);
Header.bfType := BMP_MAGIC;
Header.bfSize := SizeOf(Header) + SizeOf(Info) + ImageSize;
Header.bfReserved1 := 0;
Info.biPlanes := 1;
Info.biCompression := BMP_COMP_RGB;
Info.biSizeImage := ImageSize;
- case Format of
- tfR3G3B2, tfLuminance8: begin
- Info.biBitCount := 8;
- Header.bfOffBits := Header.bfOffBits + 256 * SizeOf(Cardinal);
- end;
-
- tfRGB5, tfRGB5A1, tfR5G6B5, tfRGB4, tfRGBA4,
- tfBGR5, tfBGR5A1, tfB5G6R5, tfBGR4, tfBGRA4: begin
- Info.biBitCount := 16;
- Info.biCompression := BMP_COMP_BITFIELDS;
- end;
- tfBGR8, tfRGB8: begin
- Info.biBitCount := 24;
- end;
+ try
+ case Format of
+ tfLuminance4: begin
+ Info.biBitCount := 4;
+ Header.bfSize := Header.bfSize + 16 * SizeOf(Cardinal);
+ Header.bfOffBits := Header.bfOffBits + 16 * SizeOf(Cardinal); //16 ColorTable entries
+ Converter := TbmpColorTableFormat.Create;
+ Converter.PixelSize := 0.5;
+ Converter.Format := Format;
+ Converter.Range := glBitmapColorRec($F, $F, $F, $0);
+ Converter.CreateColorTable;
+ end;
- tfRGB10, tfRGB10A2, tfRGBA8,
- tfBGR10, tfBGR10A2, tfBGRA8: begin
- Info.biBitCount := 32;
- Info.biCompression := BMP_COMP_BITFIELDS;
- end;
- else
- raise EglBitmapUnsupportedFormatFormat.Create('SaveBMP - ' + UNSUPPORTED_FORMAT);
- end;
- Info.biXPelsPerMeter := 2835;
- Info.biYPelsPerMeter := 2835;
+ tfR3G3B2, tfLuminance8: begin
+ Info.biBitCount := 8;
+ Header.bfSize := Header.bfSize + 256 * SizeOf(Cardinal);
+ Header.bfOffBits := Header.bfOffBits + 256 * SizeOf(Cardinal); //256 ColorTable entries
+ Converter := TbmpColorTableFormat.Create;
+ Converter.PixelSize := 1;
+ Converter.Format := Format;
+ if (Format = tfR3G3B2) then begin
+ Converter.Range := glBitmapColorRec($7, $7, $3, $0);
+ Converter.Shift := glBitmapShiftRec(0, 3, 6, 0);
+ end else
+ Converter.Range := glBitmapColorRec($FF, $FF, $FF, $0);
+ Converter.CreateColorTable;
+ end;
- // prepare bitmasks
- if Info.biCompression = BMP_COMP_BITFIELDS then begin
- Info.biSize := Info.biSize + 4 * SizeOf(Cardinal);
- Header.bfSize := Header.bfSize + 4 * SizeOf(Cardinal);
- Header.bfOffBits := Header.bfOffBits + 4 * SizeOf(Cardinal);
+ tfRGB4, tfRGB5, tfR5G6B5, tfRGB5A1, tfRGBA4,
+ tfBGR4, tfBGR5, tfB5G6R5, tfBGR5A1, tfBGRA4: begin
+ Info.biBitCount := 16;
+ Info.biCompression := BMP_COMP_BITFIELDS;
+ end;
- FormatDesc := TFormatDescriptor.Get(Format);
- RedMask := FormatDesc.RedMask;
- GreenMask := FormatDesc.GreenMask;
- BlueMask := FormatDesc.BlueMask;
- AlphaMask := FormatDesc.AlphaMask;
- end;
+ tfBGR8, tfRGB8: begin
+ Info.biBitCount := 24;
+ end;
- // headers
- aStream.Write(Header, SizeOf(Header));
- aStream.Write(Info, SizeOf(Info));
-
- // colortable
- if Info.biBitCount = 8 then begin
- Temp := 0;
- for ColorIdx := Low(Byte) to High(Byte) do begin
- aStream.Write(Temp, 4);
- Temp := Temp + $00010101;
+ tfRGB10, tfRGB10A2, tfRGBA8,
+ tfBGR10, tfBGR10A2, tfBGRA8: begin
+ Info.biBitCount := 32;
+ Info.biCompression := BMP_COMP_BITFIELDS;
+ end;
+ else
+ raise EglBitmapUnsupportedFormatFormat.Create('SaveBMP - ' + UNSUPPORTED_FORMAT);
+ end;
+ Info.biXPelsPerMeter := 2835;
+ Info.biYPelsPerMeter := 2835;
+
+ // prepare bitmasks
+ if Info.biCompression = BMP_COMP_BITFIELDS then begin
+ Header.bfSize := Header.bfSize + 4 * SizeOf(Cardinal);
+ Header.bfOffBits := Header.bfOffBits + 4 * SizeOf(Cardinal);
+
+ RedMask := FormatDesc.RedMask;
+ GreenMask := FormatDesc.GreenMask;
+ BlueMask := FormatDesc.BlueMask;
+ AlphaMask := FormatDesc.AlphaMask;
end;
- end;
-
- // bitmasks
- if Info.biCompression = BMP_COMP_BITFIELDS then begin
- aStream.Write(RedMask, SizeOf(Cardinal));
- aStream.Write(GreenMask, SizeOf(Cardinal));
- aStream.Write(BlueMask, SizeOf(Cardinal));
- aStream.Write(AlphaMask, SizeOf(Cardinal));
- end;
-
- // image data
- LineSize := Trunc(Width * TFormatDescriptor.Get(Format).PixelSize);
- Padding := GetLineWidth - LineSize;
- PaddingBuff := 0;
- pData := Data;
- Inc(pData, (Height -1) * LineSize);
+ // headers
+ aStream.Write(Header, SizeOf(Header));
+ aStream.Write(Info, SizeOf(Info));
+
+ // colortable
+ if Assigned(Converter) then
+ aStream.Write(Converter.ColorTable[0].b,
+ SizeOf(TbmpColorTableEnty) * Length(Converter.ColorTable));
+
+ // bitmasks
+ if Info.biCompression = BMP_COMP_BITFIELDS then begin
+ aStream.Write(RedMask, SizeOf(Cardinal));
+ aStream.Write(GreenMask, SizeOf(Cardinal));
+ aStream.Write(BlueMask, SizeOf(Cardinal));
+ aStream.Write(AlphaMask, SizeOf(Cardinal));
+ end;
- // prepare row buffer. But only for RGB because RGBA supports color masks
- // so it's possible to change color within the image.
- if (Format = tfRGB8) then
- GetMem(pTemp, fRowSize)
- else
- pTemp := nil;
+ // image data
+ rbLineSize := Round(Info.biWidth * FormatDesc.PixelSize);
+ wbLineSize := Round(Info.biWidth * Info.biBitCount / 8);
+ Padding := GetLineWidth - wbLineSize;
+ PaddingBuff := 0;
+
+ pData := Data;
+ inc(pData, (Height-1) * rbLineSize);
+
+ // prepare row buffer. But only for RGB because RGBA supports color masks
+ // so it's possible to change color within the image.
+ if Assigned(Converter) then begin
+ FormatDesc.PreparePixel(Pixel);
+ GetMem(ConvertBuffer, wbLineSize);
+ SourceFD := FormatDesc.CreateMappingData;
+ DestFD := Converter.CreateMappingData;
+ end else
+ ConvertBuffer := nil;
- try
- // write image data
- for LineIdx := 0 to Height - 1 do begin
- // preparing row
- if Format = tfRGB8 then begin
- Move(pData^, pTemp^, fRowSize);
- SwapRGB(pTemp, Width, false);
- end else
- pTemp := pData;
- aStream.Write(pTemp^, LineSize);
- Dec(pData, LineSize);
- if Padding > 0 then
- aStream.Write(PaddingBuff, Padding);
+ try
+ for LineIdx := 0 to Height - 1 do begin
+ // preparing row
+ if Assigned(Converter) then begin
+ srcData := pData;
+ dstData := ConvertBuffer;
+ for PixelIdx := 0 to Info.biWidth-1 do begin
+ FormatDesc.Unmap(srcData, Pixel, SourceFD);
+ with FormatDesc do begin
+ //TODO use convert function
+ for i := 0 to 3 do
+ if (Converter.Range.arr[i] <> Range.arr[i]) then begin
+ if (Range.arr[i] > 0) then
+ Pixel.Data.arr[i] := Round(Pixel.Data.arr[i] / Range.arr[i] * Converter.Range.arr[i])
+ else
+ Pixel.Data.arr[i] := 0;
+ end;
+ end;
+ Converter.Map(Pixel, dstData, DestFD);
+ end;
+ aStream.Write(ConvertBuffer^, wbLineSize);
+ end else begin
+ aStream.Write(pData^, rbLineSize);
+ end;
+ dec(pData, rbLineSize);
+ if (Padding > 0) then
+ aStream.Write(PaddingBuff, Padding);
+ end;
+ finally
+ // destroy row buffer
+ if Assigned(ConvertBuffer) then begin
+ FormatDesc.FreeMappingData(SourceFD);
+ Converter.FreeMappingData(DestFD);
+ FreeMem(ConvertBuffer);
+ end;
end;
finally
- // destroy row buffer
- if Format = tfRGB8 then
- FreeMem(pTemp);
+ if Assigned(Converter) then
+ Converter.Free;
end;
end;
TFormatDescriptor.Finalize;
end.
+