| @@ -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; | |||
| @@ -1553,6 +1556,18 @@ begin | |||
| result.a := a; | |||
| 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 | |||
| @@ -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; | |||
| ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |||