From f067ec08d69bd75acff660df64c05840ff457dfa Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Wed, 6 Nov 2013 20:01:24 +0100 Subject: [PATCH] * refactored LoadTGA --- glBitmap.pas | 543 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 330 insertions(+), 213 deletions(-) diff --git a/glBitmap.pas b/glBitmap.pas index 68d9cde..88724cf 100644 --- a/glBitmap.pas +++ b/glBitmap.pas @@ -1004,6 +1004,7 @@ procedure glBitmapGetDefaultTextureWrap(var S, T, R: Cardinal); function glBitmapPosition(X: Integer = -1; Y: Integer = -1): TglBitmapPixelPosition; function glBitmapColorRec(const r, g, b, a: Cardinal): TglBitmapColorRec; +function glBitmapColorRecCmp(const r1, r2: TglBitmapColorRec): Boolean; var glBitmapDefaultDeleteTextureOnFree: Boolean; @@ -1052,6 +1053,7 @@ type fFormat: TglBitmapFormat; fWithAlpha: TglBitmapFormat; fWithoutAlpha: TglBitmapFormat; + fRGBInverted: TglBitmapFormat; fPixelSize: Single; fRange: TglBitmapColorRec; @@ -1066,6 +1068,7 @@ type property Format: TglBitmapFormat read fFormat; property WithAlpha: TglBitmapFormat read fWithAlpha; property WithoutAlpha: TglBitmapFormat read fWithoutAlpha; + property RGBInverted: TglBitmapFormat read fRGBInverted; property Components: Integer read GetComponents; property PixelSize: Single read fPixelSize; @@ -1554,6 +1557,18 @@ begin end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +function glBitmapColorRecCmp(const r1, r2: TglBitmapColorRec): Boolean; +var + i: Integer; +begin + result := false; + for i := 0 to high(r1.arr) do + if (r1.arr[i] <> r2.arr[i]) then + exit; + result := true; +end; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function glBitmapShiftRec(const r, g, b, a: Byte): TShiftRec; begin result.r := r; @@ -1565,7 +1580,7 @@ end; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function FormatGetSupportedFiles(const aFormat: TglBitmapFormat): TglBitmapFileTypes; begin - result := [ftDDS, ftTGA]; + result := [ftDDS]; if (aFormat in [ //4 bbp @@ -1586,6 +1601,22 @@ begin tfBGR10, tfBGR10A2, tfBGRA8]) then result := result + [ftBMP]; + if (aFormat in [ + //8 bpp + tfLuminance8, tfAlpha8, + + //16 bpp + tfLuminance16, tfLuminance8Alpha8, + tfRGB5, tfRGB5A1, tfRGBA4, + tfBGR5, tfBGR5A1, tfBGRA4, + + //24 bpp + tfRGB8, tfBGR8, + + //32 bpp + tfRGB10A2, tfRGBA8, tfBGR10A2, tfBGRA8]) then + result := result + [ftTGA]; + //TODO Supported File Formats! (* @@ -2204,6 +2235,7 @@ begin fFormat := tfEmpty; fWithAlpha := tfEmpty; fWithoutAlpha := tfEmpty; + fRGBInverted := tfEmpty; fPixelSize := 0.0; fglFormat := 0; @@ -2932,6 +2964,7 @@ begin fFormat := tfRGB4; fWithAlpha := tfRGBA4; fWithoutAlpha := tfRGB4; + fRGBInverted := tfBGR4; fRange.r := $F; fRange.g := $F; fRange.b := $F; @@ -2949,6 +2982,7 @@ begin fFormat := tfR5G6B5; fWithAlpha := tfRGBA4; fWithoutAlpha := tfR5G6B5; + fRGBInverted := tfB5G6R5; fRange.r := $1F; fRange.g := $3F; fRange.b := $1F; @@ -2966,6 +3000,7 @@ begin fFormat := tfRGB5; fWithAlpha := tfRGB5A1; fWithoutAlpha := tfRGB5; + fRGBInverted := tfBGR5; fRange.r := $1F; fRange.g := $1F; fRange.b := $1F; @@ -2983,6 +3018,7 @@ begin fFormat := tfRGB8; fWithAlpha := tfRGBA8; fWithoutAlpha := tfRGB8; + fRGBInverted := tfBGR8; fglInternalFormat := GL_RGB8; end; @@ -2992,6 +3028,7 @@ begin fFormat := tfRGB10; fWithAlpha := tfRGB10A2; fWithoutAlpha := tfRGB10; + fRGBInverted := tfBGR10; fRange.r := $3FF; fRange.g := $3FF; fRange.b := $3FF; @@ -3009,6 +3046,7 @@ begin fFormat := tfRGB12; fWithAlpha := tfRGBA12; fWithoutAlpha := tfRGB12; + fRGBInverted := tfBGR12; fglInternalFormat := GL_RGB12; end; @@ -3018,6 +3056,7 @@ begin fFormat := tfRGB16; fWithAlpha := tfRGBA16; fWithoutAlpha := tfRGB16; + fRGBInverted := tfBGR16; fglInternalFormat := GL_RGB16; end; @@ -3027,6 +3066,7 @@ begin fFormat := tfRGBA2; fWithAlpha := tfRGBA2; fWithoutAlpha := tfR3G3B2; + fRGBInverted := tfBGRA2; fglInternalFormat := GL_RGBA2; end; @@ -3036,6 +3076,7 @@ begin fFormat := tfRGBA4; fWithAlpha := tfRGBA4; fWithoutAlpha := tfRGB4; + fRGBInverted := tfBGRA4; fRange.r := $F; fRange.g := $F; fRange.b := $F; @@ -3055,6 +3096,7 @@ begin fFormat := tfRGB5A1; fWithAlpha := tfRGB5A1; fWithoutAlpha := tfRGB5; + fRGBInverted := tfBGR5A1; fRange.r := $1F; fRange.g := $1F; fRange.b := $1F; @@ -3074,6 +3116,7 @@ begin fFormat := tfRGBA8; fWithAlpha := tfRGBA8; fWithoutAlpha := tfRGB8; + fRGBInverted := tfBGRA8; fglInternalFormat := GL_RGBA8; end; @@ -3083,6 +3126,7 @@ begin fFormat := tfRGB10A2; fWithAlpha := tfRGB10A2; fWithoutAlpha := tfRGB10; + fRGBInverted := tfBGR10A2; fRange.r := $3FF; fRange.g := $3FF; fRange.b := $3FF; @@ -3102,6 +3146,7 @@ begin fFormat := tfRGBA12; fWithAlpha := tfRGBA12; fWithoutAlpha := tfRGB12; + fRGBInverted := tfBGRA12; fglInternalFormat := GL_RGBA12; end; @@ -3111,6 +3156,7 @@ begin fFormat := tfRGBA16; fWithAlpha := tfRGBA16; fWithoutAlpha := tfRGB16; + fRGBInverted := tfBGRA16; fglInternalFormat := GL_RGBA16; end; @@ -3121,6 +3167,7 @@ begin fFormat := tfBGR4; fWithAlpha := tfBGRA4; fWithoutAlpha := tfBGR4; + fRGBInverted := tfRGB4; fRange.r := $F; fRange.g := $F; fRange.b := $F; @@ -3143,6 +3190,7 @@ begin fFormat := tfB5G6R5; fWithAlpha := tfBGRA4; fWithoutAlpha := tfB5G6R5; + fRGBInverted := tfR5G6B5; fRange.r := $1F; fRange.g := $3F; fRange.b := $1F; @@ -3154,9 +3202,6 @@ begin fglDataFormat := GL_UNSIGNED_SHORT_5_6_5; end; -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// constructor TfdBGR5.Create; begin inherited Create; @@ -3164,6 +3209,7 @@ begin fFormat := tfBGR5; fWithAlpha := tfBGR5A1; fWithoutAlpha := tfBGR5; + fRGBInverted := tfRGB5; fRange.r := $1F; fRange.g := $1F; fRange.b := $1F; @@ -3183,6 +3229,7 @@ begin fFormat := tfBGR8; fWithAlpha := tfBGRA8; fWithoutAlpha := tfBGR8; + fRGBInverted := tfRGB8; fglInternalFormat := GL_RGB8; end; @@ -3192,6 +3239,7 @@ begin fFormat := tfBGR10; fWithAlpha := tfBGR10A2; fWithoutAlpha := tfBGR10; + fRGBInverted := tfRGB10; fRange.r := $3FF; fRange.g := $3FF; fRange.b := $3FF; @@ -3211,6 +3259,7 @@ begin fFormat := tfBGR12; fWithAlpha := tfBGRA12; fWithoutAlpha := tfBGR12; + fRGBInverted := tfRGB12; fglInternalFormat := GL_RGB12; end; @@ -3220,6 +3269,7 @@ begin fFormat := tfBGR16; fWithAlpha := tfBGRA16; fWithoutAlpha := tfBGR16; + fRGBInverted := tfRGB16; fglInternalFormat := GL_RGB16; end; @@ -3229,6 +3279,7 @@ begin fFormat := tfBGRA2; fWithAlpha := tfBGRA4; fWithoutAlpha := tfBGR4; + fRGBInverted := tfRGBA2; fglInternalFormat := GL_RGBA2; end; @@ -3238,6 +3289,7 @@ begin fFormat := tfBGRA4; fWithAlpha := tfBGRA4; fWithoutAlpha := tfBGR4; + fRGBInverted := tfRGBA4; fRange.r := $F; fRange.g := $F; fRange.b := $F; @@ -3257,6 +3309,7 @@ begin fFormat := tfBGR5A1; fWithAlpha := tfBGR5A1; fWithoutAlpha := tfBGR5; + fRGBInverted := tfRGB5A1; fRange.r := $1F; fRange.g := $1F; fRange.b := $1F; @@ -3276,6 +3329,7 @@ begin fFormat := tfBGRA8; fWithAlpha := tfBGRA8; fWithoutAlpha := tfBGR8; + fRGBInverted := tfRGBA8; fglInternalFormat := GL_RGBA8; end; @@ -3285,6 +3339,7 @@ begin fFormat := tfBGR10A2; fWithAlpha := tfBGR10A2; fWithoutAlpha := tfBGR10; + fRGBInverted := tfRGB10A2; fRange.r := $3FF; fRange.g := $3FF; fRange.b := $3FF; @@ -3304,6 +3359,7 @@ begin fFormat := tfBGRA12; fWithAlpha := tfBGRA12; fWithoutAlpha := tfBGR12; + fRGBInverted := tfRGBA12; fglInternalFormat := GL_RGBA12; end; @@ -3313,6 +3369,7 @@ begin fFormat := tfBGRA16; fWithAlpha := tfBGRA16; fWithoutAlpha := tfBGR16; + fRGBInverted := tfRGBA16; fglInternalFormat := GL_RGBA16; end; @@ -3531,9 +3588,6 @@ begin 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; @@ -4004,6 +4058,8 @@ procedure TglBitmap.LoadFromFile(const aFilename: String); var fs: TFileStream; begin + if not FileExists(aFilename) then + raise EglBitmapException.Create('file does not exist: ' + aFilename); fFilename := aFilename; fs := TFileStream.Create(fFilename, fmOpenRead); try @@ -6422,29 +6478,43 @@ type ImageID: Byte; ColorMapType: Byte; ImageType: Byte; - ColorMapSpec: Array[0..4] of Byte; + //ColorMapSpec: Array[0..4] of Byte; + ColorMapStart: Word; + ColorMapLength: Word; + ColorMapEntrySize: Byte; OrigX: Word; OrigY: Word; Width: Word; Height: Word; Bpp: Byte; - ImageDes: Byte; + ImageDesc: Byte; end; const - TGA_UNCOMPRESSED_RGB = 2; - TGA_UNCOMPRESSED_GRAY = 3; - TGA_COMPRESSED_RGB = 10; - TGA_COMPRESSED_GRAY = 11; + TGA_UNCOMPRESSED_COLOR_TABLE = 1; + TGA_UNCOMPRESSED_RGB = 2; + TGA_UNCOMPRESSED_GRAY = 3; + TGA_COMPRESSED_COLOR_TABLE = 9; + TGA_COMPRESSED_RGB = 10; + TGA_COMPRESSED_GRAY = 11; + + TGA_NONE_COLOR_TABLE = 0; + TGA_COLOR_TABLE = 1; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TglBitmap.LoadTGA(const aStream: TStream): Boolean; var Header: TTGAHeader; - NewImage, pData: PByte; - StreamPos: Int64; - PixelSize, LineSize, YStart, YEnd, YInc: Integer; - Format: TglBitmapFormat; + ImageData: PByte; + StartPosition: Int64; + PixelSize, LineSize: Integer; + tgaFormat: TglBitmapFormat; + FormatDesc: TFormatDescriptor; + Counter: packed record + X, Y: packed record + low, high, dir: Integer; + end; + end; const CACHE_SIZE = $4000; @@ -6452,195 +6522,233 @@ const //////////////////////////////////////////////////////////////////////////////////////// procedure ReadUncompressed; var - RowSize: Integer; + i, j: Integer; + buf, tmp1, tmp2: PByte; begin - RowSize := Header.Width * PixelSize; - // copy line by line - while YStart <> YEnd + YInc do begin - pData := NewImage; - Inc(pData, YStart * LineSize); - aStream.Read(pData^, RowSize); - Inc(YStart, YInc); + buf := nil; + if (Counter.X.dir < 0) then + buf := GetMem(LineSize); + try + while (Counter.Y.low <> Counter.Y.high + counter.Y.dir) do begin + tmp1 := ImageData + (Counter.Y.low * LineSize); //pointer to LineStart + if (Counter.X.dir < 0) then begin //flip X + aStream.Read(buf^, LineSize); + tmp2 := buf + LineSize - PixelSize; //pointer to last pixel in line + for i := 0 to Header.Width-1 do begin //for all pixels in line + for j := 0 to PixelSize-1 do begin //for all bytes in pixel + tmp1^ := tmp2^; + inc(tmp1); + inc(tmp2); + end; + dec(tmp2, 2*PixelSize); //move 2 backwards, because j-loop moved 1 forward + end; + end else + aStream.Read(tmp1^, LineSize); + inc(Counter.Y.low, Counter.Y.dir); //move to next line index + end; + finally + if Assigned(buf) then + FreeMem(buf); end; end; //////////////////////////////////////////////////////////////////////////////////////// procedure ReadCompressed; - var - HeaderWidth, HeaderHeight: Integer; - LinePixelsRead, ImgPixelsRead, ImgPixelsToRead: Integer; - - Cache: PByte; - CacheSize, CachePos: Integer; - - Temp: Byte; - TempBuf: Array [0..15] of Byte; - - PixelRepeat: Boolean; - PixelToRead, TempPixels: Integer; ///////////////////////////////////////////////////////////////// + var + TmpData: PByte; + LinePixelsRead: Integer; procedure CheckLine; begin - if LinePixelsRead >= HeaderWidth then begin + if (LinePixelsRead >= Header.Width) then begin LinePixelsRead := 0; - pData := NewImage; - Inc(YStart, YInc); - Inc(pData, YStart * LineSize); + inc(Counter.Y.low, Counter.Y.dir); //next line index + TmpData := ImageData + Counter.Y.low * LineSize; //set line + if (Counter.X.dir < 0) then //if x flipped then + TmpData := TmpData + LineSize - PixelSize; //set last pixel end; end; ///////////////////////////////////////////////////////////////// + var + Cache: PByte; + CacheSize, CachePos: Integer; procedure CachedRead(out Buffer; Count: Integer); var BytesRead: Integer; begin - if (CachePos + Count) > CacheSize then begin + if (CachePos + Count > CacheSize) then begin + //if buffer overflow save non read bytes BytesRead := 0; - - // Read Data - if CacheSize - CachePos > 0 then begin + if (CacheSize - CachePos > 0) then begin BytesRead := CacheSize - CachePos; - Move(pByteArray(Cache)^[CachePos], Buffer, BytesRead); - Inc(CachePos, BytesRead); + Move(PByteArray(Cache)^[CachePos], Buffer, BytesRead); + inc(CachePos, BytesRead); end; - // Reload Data + //load cache from file CacheSize := Min(CACHE_SIZE, aStream.Size - aStream.Position); aStream.Read(Cache^, CacheSize); CachePos := 0; - // Read else - if Count - BytesRead > 0 then begin - Move(pByteArray(Cache)^[CachePos], TByteArray(Buffer)[BytesRead], Count - BytesRead); - Inc(CachePos, Count - BytesRead); + //read rest of requested bytes + if (Count - BytesRead > 0) then begin + Move(PByteArray(Cache)^[CachePos], TByteArray(Buffer)[BytesRead], Count - BytesRead); + inc(CachePos, Count - BytesRead); end; end else begin - Move(pByteArray(Cache)^[CachePos], Buffer, Count); - Inc(CachePos, Count); + //if no buffer overflow just read the data + Move(PByteArray(Cache)^[CachePos], Buffer, Count); + inc(CachePos, Count); + end; + end; + + procedure PixelToBuffer(const aData: PByte; var aBuffer: PByte); + begin + case PixelSize of + 1: begin + aBuffer^ := aData^; + inc(aBuffer, Counter.X.dir); + end; + 2: begin + PWord(aBuffer)^ := PWord(aData)^; + inc(aBuffer, 2 * Counter.X.dir); + end; + 3: begin + PByteArray(aBuffer)^[0] := PByteArray(aData)^[0]; + PByteArray(aBuffer)^[1] := PByteArray(aData)^[1]; + PByteArray(aBuffer)^[2] := PByteArray(aData)^[2]; + inc(aBuffer, 3 * Counter.X.dir); + end; + 4: begin + PCardinal(aBuffer)^ := PCardinal(aData)^; + inc(aBuffer, 4 * Counter.X.dir); + end; end; end; + var + TotalPixelsToRead, TotalPixelsRead: Integer; + Temp: Byte; + buf: array [0..3] of Byte; //1 pixel is max 32bit long + PixelRepeat: Boolean; + PixelsToRead, PixelCount: Integer; begin CacheSize := 0; - CachePos := 0; + CachePos := 0; - HeaderWidth := Header.Width; - HeaderHeight := Header.Height; + TotalPixelsToRead := Header.Width * Header.Height; + TotalPixelsRead := 0; + LinePixelsRead := 0; - GetMem(Cache, CACHE_SIZE); // 16K Buffer + GetMem(Cache, CACHE_SIZE); try - ImgPixelsToRead := HeaderWidth * HeaderHeight; - ImgPixelsRead := 0; - LinePixelsRead := 0; + TmpData := ImageData + Counter.Y.low * LineSize; //set line + if (Counter.X.dir < 0) then //if x flipped then + TmpData := TmpData + LineSize - PixelSize; //set last pixel - pData := NewImage; - Inc(pData, YStart * LineSize); - - // Read until all Pixels repeat + //read CommandByte CachedRead(Temp, 1); - - PixelRepeat := Temp and $80 > 0; - PixelToRead := (Temp and $7F) + 1; - - Inc(ImgPixelsRead, PixelToRead); - - if PixelRepeat then begin - // repeat one pixel x times - CachedRead(TempBuf[0], PixelSize); - - // repeat Pixel - while PixelToRead > 0 do begin - CheckLine; - - TempPixels := HeaderWidth - LinePixelsRead; - if PixelToRead < TempPixels then - TempPixels := PixelToRead; - - Inc(LinePixelsRead, TempPixels); - Dec(PixelToRead, TempPixels); - - while TempPixels > 0 do begin - case PixelSize of - 1: begin - pData^ := TempBuf[0]; - Inc(pData); - end; - 2: begin - pWord(pData)^ := pWord(@TempBuf[0])^; - Inc(pData, 2); - end; - 3: begin - pWord(pData)^ := pWord(@TempBuf[0])^; - Inc(pData, 2); - pData^ := TempBuf[2]; - Inc(pData); - end; - 4: begin - pDWord(pData)^ := pDWord(@TempBuf[0])^; - Inc(pData, 4); - end; - end; - Dec(TempPixels); - end; - end; - end else begin - // copy x pixels - while PixelToRead > 0 do begin - CheckLine; - TempPixels := HeaderWidth - LinePixelsRead; - if PixelToRead < TempPixels then - TempPixels := PixelToRead; - CachedRead(pData^, PixelSize * TempPixels); - Inc(pData, PixelSize * TempPixels); - Inc(LinePixelsRead, TempPixels); - Dec(PixelToRead, TempPixels); + PixelRepeat := (Temp and $80) > 0; + PixelsToRead := (Temp and $7F) + 1; + inc(TotalPixelsRead, PixelsToRead); + + if PixelRepeat then + CachedRead(buf[0], PixelSize); + while (PixelsToRead > 0) do begin + CheckLine; + PixelCount := Min(Header.Width - LinePixelsRead, PixelsToRead); //max read to EOL or EOF + while (PixelCount > 0) do begin + if not PixelRepeat then + CachedRead(buf[0], PixelSize); + PixelToBuffer(@buf[0], TmpData); + inc(LinePixelsRead); + dec(PixelsToRead); + dec(PixelCount); end; end; - until ImgPixelsRead >= ImgPixelsToRead; + until (TotalPixelsRead >= TotalPixelsToRead); finally - FreeMem(Cache) + FreeMem(Cache); end; end; + function IsGrayFormat: Boolean; + begin + result := Header.ImageType in [TGA_UNCOMPRESSED_GRAY, TGA_COMPRESSED_GRAY]; + end; + begin result := false; // reading header to test file and set cursor back to begin - StreamPos := aStream.Position; + StartPosition := aStream.Position; aStream.Read(Header, SizeOf(Header)); // no colormapped files - if (Header.ColorMapType = 0) then begin - if Header.ImageType in [TGA_UNCOMPRESSED_RGB, TGA_UNCOMPRESSED_GRAY, TGA_COMPRESSED_RGB, TGA_COMPRESSED_GRAY] then begin + if (Header.ColorMapType = TGA_NONE_COLOR_TABLE) and (Header.ImageType in [ + TGA_UNCOMPRESSED_RGB, TGA_UNCOMPRESSED_GRAY, TGA_COMPRESSED_RGB, TGA_COMPRESSED_GRAY]) then + begin + try + if Header.ImageID <> 0 then // skip image ID + aStream.Position := aStream.Position + Header.ImageID; + case Header.Bpp of - //TODO 8: Format := tfAlpha8; - 16: Format := tfLuminance8Alpha8; - 24: Format := tfBGR8; - 32: Format := tfBGRA8; - else - raise EglBitmapException.Create('LoadTga - unsupported BitsPerPixel found.'); + 8: if IsGrayFormat then case (Header.ImageDesc and $F) of + 0: tgaFormat := tfLuminance8; + 8: tgaFormat := tfAlpha8; + end; + + 16: if IsGrayFormat then case (Header.ImageDesc and $F) of + 0: tgaFormat := tfLuminance16; + 8: tgaFormat := tfLuminance8Alpha8; + end else case (Header.ImageDesc and $F) of + 0: tgaFormat := tfBGR5; + 1: tgaFormat := tfBGR5A1; + 4: tgaFormat := tfBGRA4; + end; + + 24: if not IsGrayFormat then case (Header.ImageDesc and $F) of + 0: tgaFormat := tfBGR8; + end; + + 32: if not IsGrayFormat then case (Header.ImageDesc and $F) of + 2: tgaFormat := tfBGR10A2; + 8: tgaFormat := tfBGRA8; + end; end; - // skip image ID - if Header.ImageID <> 0 then - aStream.Position := aStream.Position + Header.ImageID; + if (tgaFormat = tfEmpty) then + raise EglBitmapException.Create('LoadTga - unsupported format'); - PixelSize := TFormatDescriptor.Get(Format).GetSize(1, 1); - LineSize := Trunc(Header.Width * PixelSize); + FormatDesc := TFormatDescriptor.Get(tgaFormat); + PixelSize := FormatDesc.GetSize(1, 1); + LineSize := FormatDesc.GetSize(Header.Width, 1); - GetMem(NewImage, LineSize * Header.Height); + GetMem(ImageData, LineSize * Header.Height); try + //column direction + if ((Header.ImageDesc and (1 shl 4)) > 0) then begin + Counter.X.low := Header.Height-1;; + Counter.X.high := 0; + Counter.X.dir := -1; + end else begin + Counter.X.low := 0; + Counter.X.high := Header.Height-1; + Counter.X.dir := 1; + end; + // Row direction - if (Header.ImageDes and $20 > 0) then begin - YStart := 0; - YEnd := Header.Height -1; - YInc := 1; + if ((Header.ImageDesc and (1 shl 5)) > 0) then begin + Counter.Y.low := 0; + Counter.Y.high := Header.Height-1; + Counter.Y.dir := 1; end else begin - YStart := Header.Height -1; - YEnd := 0; - YInc := -1; + Counter.Y.low := Header.Height-1;; + Counter.Y.high := 0; + Counter.Y.dir := -1; end; // Read Image @@ -6651,97 +6759,106 @@ begin ReadCompressed; end; - SetDataPointer(NewImage, Format, Header.Width, Header.Height); + SetDataPointer(ImageData, tgaFormat, Header.Width, Header.Height); result := true; except - FreeMem(NewImage); + FreeMem(ImageData); raise; end; - end - else aStream.Position := StreamPos; + finally + aStream.Position := StartPosition; + end; end - else aStream.Position := StreamPos; + else aStream.Position := StartPosition; end; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// procedure TglBitmap.SaveTGA(const aStream: TStream); var Header: TTGAHeader; - Size: Integer; - pTemp: pByte; + LineSize, Size, x, y: Integer; + Pixel: TglBitmapPixelData; + LineBuf, SourceData, DestData: PByte; + SourceMD, DestMD: Pointer; FormatDesc: TFormatDescriptor; - - procedure ConvertData(pTemp: pByte); - var - Idx, PixelSize: Integer; - Temp: byte; - begin - PixelSize := fPixelSize; - for Idx := 1 to Height * Width do begin - Temp := pByteArray(pTemp)^[2]; - pByteArray(pTemp)^[2] := pByteArray(pTemp)^[0]; - pByteArray(pTemp)^[0] := Temp; - Inc(pTemp, PixelSize); - end; - end; - + Converter: TFormatDescriptor; begin if not (ftTGA in FormatGetSupportedFiles(Format)) then raise EglBitmapUnsupportedFormatFormat.Create('SaveTGA - ' + UNSUPPORTED_FORMAT); + //prepare header FillChar(Header, SizeOf(Header), 0); - case Format of - //TODO ifAlpha8, ifLuminance8, ifDepth8: begin - tfLuminance8: begin - Header.ImageType := TGA_UNCOMPRESSED_GRAY; - Header.Bpp := 8; - end; - tfLuminance8Alpha8: begin - Header.ImageType := TGA_UNCOMPRESSED_GRAY; - Header.Bpp := 16; - end; - tfRGB8, tfBGR8: begin - Header.ImageType := TGA_UNCOMPRESSED_RGB; - Header.Bpp := 24; - end; - tfRGBA8, tfBGRA8: begin - Header.ImageType := TGA_UNCOMPRESSED_RGB; - Header.Bpp := 32; - end; - else - raise EglBitmapUnsupportedFormatFormat.Create('SaveTGA - ' + UNSUPPORTED_FORMAT); - end; - Header.Width := Width; - Header.Height := Height; - Header.ImageDes := $20; - FormatDesc := TFormatDescriptor.Get(Format); + //set ImageType + if (Format in [tfLuminance8, tfLuminance6Alpha2, tfLuminance4Alpha4, tfAlpha8, + tfLuminance16, tfLuminance12Alpha4, tfLuminance8Alpha8]) then + Header.ImageType := TGA_UNCOMPRESSED_GRAY + else + Header.ImageType := TGA_UNCOMPRESSED_RGB; + + //set BitsPerPixel + if (Format in [tfLuminance8, tfLuminance6Alpha2, tfLuminance4Alpha4, tfAlpha8]) then + Header.Bpp := 8 + else if (Format in [tfLuminance16, tfLuminance12Alpha4, tfLuminance8Alpha8, + tfRGB5, tfBGR5, tfRGB5A1, tfBGR5A1, tfRGBA4, tfBGRA4]) then + Header.Bpp := 16 + else if (Format in [tfBGR8, tfRGB8]) then + Header.Bpp := 24 + else + Header.Bpp := 32; - if FormatDesc.HasAlpha then - Header.ImageDes := Header.ImageDes or $08; + //set AlphaBitCount + case Format of + tfRGB5A1, tfBGR5A1: + Header.ImageDesc := 1 and $F; + tfRGB10A2, tfBGR10A2: + Header.ImageDesc := 2 and $F; + tfRGBA4, tfBGRA4: + Header.ImageDesc := 4 and $F; + tfAlpha8, tfLuminance8Alpha8, tfRGBA8, tfBGRA8: + Header.ImageDesc := 8 and $F; + end; + + Header.Width := Width; + Header.Height := Height; + Header.ImageDesc := Header.ImageDesc or $20; //flip y aStream.Write(Header, SizeOf(Header)); // convert RGB(A) to BGR(A) - Size := FormatDesc.GetSize(Dimension); - if Format in [tfRGB8, tfRGBA8] then begin - GetMem(pTemp, Size); - end else - pTemp := Data; - - try - // convert data - if Format in [tfRGB8, tfRGBA8] then begin - Move(Data^, pTemp^, Size); - ConvertData(pTemp); + Converter := nil; + FormatDesc := TFormatDescriptor.Get(Format); + Size := FormatDesc.GetSize(Dimension); + if Format in [tfRGB5, tfRGB5A1, tfRGBA4, tfRGB8, tfRGB10A2, tfRGBA8] then begin + if (FormatDesc.RGBInverted = tfEmpty) then + raise EglBitmapException.Create('inverted RGB format is empty'); + Converter := TFormatDescriptor.Get(FormatDesc.RGBInverted); + if not glBitmapColorRecCmp(Converter.Range, FormatDesc.Range) or + (Converter.PixelSize <> FormatDesc.PixelSize) then + raise EglBitmapException.Create('invalid inverted RGB format'); + end; + + if Assigned(Converter) then begin + LineSize := FormatDesc.GetSize(Width, 1); + LineBuf := GetMem(LineSize); + SourceMD := FormatDesc.CreateMappingData; + DestMD := Converter.CreateMappingData; + try + SourceData := Data; + for y := 0 to Height-1 do begin + DestData := LineBuf; + for x := 0 to Width-1 do begin + FormatDesc.Unmap(SourceData, Pixel, SourceMD); + Converter.Map(Pixel, DestData, DestMD); + end; + aStream.Write(LineBuf^, LineSize); + end; + finally + FreeMem(LineBuf); + FormatDesc.FreeMappingData(SourceMD); + FormatDesc.FreeMappingData(DestMD); end; - - // write data - aStream.Write(pTemp^, Size); - finally - // free tempdata - if Format in [tfRGB8, tfRGBA8] then - FreeMem(pTemp); - end; + end else + aStream.Write(Data^, Size); end; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -- 2.1.4