BSOne.SFC/EM.Lib/ImageEn_SRC/Source/ietgafil.pas

1252 lines
36 KiB
Plaintext

(* ImageEn Build 7.0.0.06.2637 @ 7-4-17 14:58:42.679 *)
(*
Copyright (c) 1998-2017 by Carlotta Calandra. All rights reserved.
Copyright (c) 2011-2017 by Xequte Software.
This software comes without express or implied warranty.
In no case shall the author be liable for any damage or unwanted behavior of any
computer hardware and/or software.
Author grants you the right to include the component
in your application, whether COMMERCIAL, SHAREWARE, or FREEWARE.
ImageEn, IEvolution and ImageEn ActiveX may not be included in any
commercial, shareware or freeware libraries or components.
www.ImageEn.com
*)
(*
File version 1003
*)
unit ietgafil;
{$R-}
{$Q-}
{$I ie.inc}
interface
uses
Windows, Classes, Graphics, SysUtils, ImageEnIO, hyiedefs, iexBitmaps;
procedure ReadTGAStream(Stream: TStream; Bitmap: TIEBitmap; var IOParams: TIOParams; var Progress: TProgressRec; Preview: boolean; var AlphaChannel: TIEMask; IgnoreAlpha: boolean);
procedure WriteTGAStream(Stream: TStream; Bitmap: TIEBitmap; var IOParams: TIOParams; var Progress: TProgressRec; AlphaChannel: TIEMask);
function TryTGA(Stream: TStream): boolean;
implementation
uses
ImageEnProc, neurquant, ImageEnView, ieview, iesettings, hyieutils;
{$R-}
type
TGAHeader = packed record
IdentSize: Byte; // length of Identifier String
ColorMaptype: Byte; // 0 = no map
Imagetype: Byte; // image type
ColorMapStart: Word; // index of first color map entry
ColorMapLength: Word; // number of entries in color map
ColorMapBits: Byte; // size of color map entry (15, 16, 24, 32)
XStart: Word; // x origin of image
YStart: Word; // y origin of image
Width: Word; // width of image
Height: Word; // height of image
Bits: Byte; // pixel size (8, 16, 24, 32)
Descriptor: Byte; // image descriptor
end;
TGAFooter = packed record
ExtensionArea: dword;
DeveloperDir: dword;
Signature: array[0..17] of AnsiChar; // must be 'TRUEVISION-XFILE.\0'
end;
TGAExtension = packed record
ExtSize: word;
AuthorName: array[0..40] of AnsiChar;
AuthorComments: array[0..323] of AnsiChar;
DateTime: array[0..5] of word;
JobName: array[0..40] of AnsiChar;
JobTime: array[0..2] of word;
SoftwareID: array[0..40] of AnsiChar;
SoftwareVer: array[0..2] of byte;
KeyColor: array[0..3] of byte;
AspectRatio: array[0..1] of word;
Gamma: array[0..1] of word;
ColorCorrection: dword;
PostageStamp: dword;
ScanLine: dword;
AttributesType: byte;
end;
TRC = record
IndexData: array[0..8192 - 1] of Byte;
Palette256: array[0..255] of TRGB;
alpha256: array [0..255] of byte;
hasalpha256: boolean;
TempArrayD: PBYTEROW;
//TempArrayD2: PBYTEROW;
TempArrayDBIg: PBYTEROW;
TempArrayAlpha: PBYTEROW;
CompRow: PBYTEROW;
Index1: Word;
Index2: Word;
Newtype: boolean;
Footer: TGAFooter;
Extension: TGAExtension;
sbase: integer;
RemSize, RemCode: integer;
end;
PRC = ^TRC;
function TryTGA(Stream: TStream): boolean;
var
TGAHead: TGAHeader;
c: AnsiChar;
B: Byte;
sp: int64;
begin
sp := Stream.Position;
// Read Targa Header
Stream.Read(TGAHead, Sizeof(TGAHeader));
if (TGAHead.Imagetype in [1, 2, 3, 9, 10, 11]) and (TGAHead.Bits in [1, 4, 8, 16, 24, 32]) and
(TGAHead.ColorMaptype < 2) and (TGAHead.Width>0) and (TGAHead.Height>0) then
begin
result := true;
Stream.Position := sp;
Stream.Read(c, 1);
if c = 'P' then
begin
Stream.Read(b, 1);
if (b - 48 > 01) and (b - 48 < 7) then
begin
Stream.Read(c, 1);
if (c <> ' ') and (c <> '#') and (c <> #$0A) then
result := false;
end;
end;
end
else
result := false;
Stream.Position := sp;
end;
procedure SetUpMaskGrayPalette(var rc: TRC);
var
J: integer;
begin
for J := 0 to 255 do
with rc.Palette256[J] do
begin
r := J;
g := J;
b := J;
end;
end;
procedure MakeGenPalette(var rc: TRC);
var
X: integer;
R, G, B: Word;
begin
with rc do
begin
X := 0;
for R := 0 to 7 do
for G := 0 to 7 do
for B := 0 to 3 do
begin
Palette256[X].r := ((R + 1) * 8 - 1) * 4;
Palette256[X].g := ((G + 1) * 8 - 1) * 4;
Palette256[X].b := ((B + 1) * 16 - 1) * 4;
Inc(X);
end;
end;
end;
procedure ReadTGAStream(Stream: TStream; Bitmap: TIEBitmap; var IOParams: TIOParams; var Progress: TProgressRec; Preview: boolean; var AlphaChannel: TIEMask; IgnoreAlpha: boolean);
var
rc: PRC;
Width: Word;
Height: Word;
BitsPerPixel: SmallInt;
Compressed: Boolean;
TGAHead: TGAHeader;
FileOk: Boolean;
//
procedure FileGetMore;
var
NumRead: integer;
begin
with rc^ do
begin
NumRead := Stream.Size - Stream.Position;
//FillChar(IndexData, 8192, 0);
if NumRead < 8192 then
begin
Stream.Read(IndexData, NumRead);
Index1 := NumRead;
end
else
begin
Stream.Read(IndexData, 8192);
Index1 := 8192;
end;
Index2 := 0;
end;
end;
//
procedure FastGetBytes(var Ptr1; NumBytes: Word);
var
X: Integer;
begin
with rc^ do
begin
// if we have enough the block it! Otherwise do one at a time!
if Index1 < NumBytes then
begin
if Index1 = 0 then
FileGetMore;
for X := 0 to NumBytes - 1 do
begin
TBYTEROW(Ptr1)[X] := IndexData[Index2];
Inc(Index2);
Dec(Index1);
if Index1 = 0 then
FileGetMore;
end;
end
else
begin
// Block it fast!
Move(IndexData[Index2], TBYTEROW(Ptr1)[0], NumBytes);
Index2 := Index2 + Numbytes;
Index1 := Index1 - NumBytes;
end;
end;
end;
//
function FastGetByte: Byte;
begin
with rc^ do
begin
if Index1 = 0 then
FileGetMore;
FastGetByte := IndexData[Index2];
Inc(Index2);
Dec(Index1);
end;
end;
//
function FastGetWord: Word;
begin
FastGetWord := Word(FastGetByte) + Word(FastGetByte) * 256;
end;
//
procedure ReadTGAFileHeader(var FileOk: Boolean;
var Width: Word; var Height: Word; var BitsPerPixel: SmallInt;
var Compressed: Boolean);
var
W1: Word;
I: integer;
ss: AnsiString;
begin
with rc^ do
begin
// Read Targa footer (if exists)
Stream.Seek(-sizeof(TGAFooter), soEnd);
Stream.Read(Footer, sizeof(TGAFooter));
NewType := Footer.Signature = 'TRUEVISION-XFILE.';
// default values
IOParams.TGA_Author := '';
IOParams.TGA_Date := date;
IOParams.TGA_ImageName := '';
IOParams.TGA_Background := CreateRGB(0, 0, 0);
IOParams.TGA_AspectRatio := 1;
IOParams.TGA_Gamma := 2.2;
if NewType then
with Extension do
begin
Stream.Position := sbase + integer(Footer.ExtensionArea);
Stream.Read(Extension, sizeof(TGAExtension));
IOParams.TGA_Author := AuthorName;
try
if (DateTime[0] <> 0) and (DateTime[1] <> 0) and (DateTime[2] <> 0) and
(DateTime[2] > 0) and (DateTime[2] < 2500) and
(DateTime[0] > 0) and (DateTime[0] < 13) and
(DateTime[1] > 0) and (DateTime[1] < 32) and
(DateTime[3] < 24) and
(DateTime[4] < 60) and
(DateTime[5] < 60) then
IOParams.TGA_Date := EncodeDate(DateTime[2], DateTime[0], DateTime[1]) +
EncodeTime(DateTime[3], DateTime[4], DateTime[5], 0);
except
end;
IOParams.TGA_ImageName := JobName;
IOParams.TGA_Background := CreateRGB(KeyColor[1], KeyColor[2], KeyColor[3]);
if (AspectRatio[0] <> 0) and (AspectRatio[1] <> 0) then
IOParams.TGA_AspectRatio := AspectRatio[0] / AspectRatio[1];
if (Gamma[0] <> 0) and (Gamma[1] <> 0) then
IOParams.TGA_Gamma := Gamma[0] / Gamma[1];
end;
Stream.Position := sbase;
// Read Targa Header
FastGetBytes(TGAHead, Sizeof(TGAHeader));
IOParams.TGA_XPos := TGAHead.XStart;
IOParams.TGA_YPos := TGAHead.YStart;
FileOk := (TGAHead.Imagetype in [1, 2, 3, 9, 10, 11]) and (TGAHead.Bits in [1, 4, 8, 16, 24, 32]);
if FileOk then
begin
Width := TGAHead.Width;
Height := TGAHead.Height;
BitsPerPixel := TGAHead.Bits;
SetLength(ss, TGAHead.IdentSize);
FastGetBytes(ss[1], TGAHead.IdentSize);
IOParams.TGA_Descriptor := ss;
// Read in colormap
MakeGenPalette(rc^);
if TGAHead.ColorMaptype = 1 then
begin
case TGAHead.ColorMapBits of
15, 16:
for I := TGAHead.ColorMapStart to TGAHead.ColorMapStart + TGAHead.ColorMapLength - 1 do
begin
W1 := FastGetWord;
Palette256[I].r := (((W1 shr 10) and $1F) shl 1) * 4;
Palette256[I].g := (((W1 shr 5) and $1F) shl 1) * 4;
Palette256[I].b := (((W1 shr 0) and $1F) shl 1) * 4;
end;
24:
for I := TGAHead.ColorMapStart to TGAHead.ColorMapStart + TGAHead.ColorMapLength - 1 do
begin
Palette256[I].b := FastGetByte;
Palette256[I].g := FastGetByte;
Palette256[I].r := FastGetByte;
end;
32:
begin
hasalpha256 := true;
for I := TGAHead.ColorMapStart to TGAHead.ColorMapStart + TGAHead.ColorMapLength - 1 do
begin
Palette256[I].b := FastGetByte;
Palette256[I].g := FastGetByte;
Palette256[I].r := FastGetByte;
alpha256[I] := FastGetByte;
end;
end;
end;
IOParams.FreeColorMap;
IOParams.fColorMapCount := TGAHead.ColorMapLength;
IOParams.fColorMap := allocmem(TGAHead.ColorMapLength * sizeof(TRGB));
move(Palette256[0], IOParams.fcolorMap^[0], TGAHead.ColorMapLength * sizeof(TRGB));
IOParams.TGA_GrayLevel := false;
end
else
if BitsPerPixel = 8 then
begin
// gray level image (8bpp but without colormap)
SetUpMaskGrayPalette(rc^);
IOParams.TGA_GrayLevel := true;
end
else
if BitsPerPixel = 1 then
begin
// bilevel image
Palette256[0] := CreateRGB(0, 0, 0);
Palette256[1] := CreateRGB(255, 255, 255);
end;
Compressed := TGAHead.Imagetype in [9, 10, 11];
IOParams.TGA_Compressed := Compressed;
end;
end;
end;
//
const
MaskTable: array[0..7] of Byte = (128, 64, 32, 16, 8, 4, 2, 1);
var
II: Word;
LineBytes: Word;
StartLine, IncLine, I: SmallInt;
Ptr1: Pointer;
//
procedure PixelSwapArray(var TempArrayD; Wide: Word);
var
W, X, Y, Z: integer;
Byte1, Byte2, Byte3: Byte;
begin
// Should I do 1 byte pixel or 3 byte pixels
case BitsPerPixel of
8:
begin
Y := Wide shr 1;
Z := Wide - 1;
for X := 0 to Y - 1 do
begin
Byte1 := TBYTEROW(TempArrayD)[X];
TBYTEROW(TempArrayD)[X] := TBYTEROW(TempArrayD)[Z];
TBYTEROW(TempArrayD)[Z] := Byte1;
Dec(Z);
end;
end;
24:
begin
Y := (Wide div 3) div 2;
Z := Wide - 3;
W := 0;
for X := 0 to Y - 1 do
begin
Byte1 := TBYTEROW(TempArrayD)[W + 0];
Byte2 := TBYTEROW(TempArrayD)[W + 1];
Byte3 := TBYTEROW(TempArrayD)[W + 2];
TBYTEROW(TempArrayD)[W + 0] := TBYTEROW(TempArrayD)[Z + 0];
TBYTEROW(TempArrayD)[W + 1] := TBYTEROW(TempArrayD)[Z + 1];
TBYTEROW(TempArrayD)[W + 2] := TBYTEROW(TempArrayD)[Z + 2];
TBYTEROW(TempArrayD)[Z + 0] := Byte1;
TBYTEROW(TempArrayD)[Z + 1] := Byte2;
TBYTEROW(TempArrayD)[Z + 2] := Byte3;
dec(Z, 3);
inc(W, 3);
end;
end;
32:
begin
Y := Wide shr 3;
Z := Wide - 4;
W := 0;
for X := 0 to Y - 1 do
begin
Byte1 := TBYTEROW(TempArrayD)[W + 0];
Byte2 := TBYTEROW(TempArrayD)[W + 1];
Byte3 := TBYTEROW(TempArrayD)[W + 2];
TBYTEROW(TempArrayD)[W + 0] := TBYTEROW(TempArrayD)[Z + 0];
TBYTEROW(TempArrayD)[W + 1] := TBYTEROW(TempArrayD)[Z + 1];
TBYTEROW(TempArrayD)[W + 2] := TBYTEROW(TempArrayD)[Z + 2];
TBYTEROW(TempArrayD)[Z + 0] := Byte1;
TBYTEROW(TempArrayD)[Z + 1] := Byte2;
TBYTEROW(TempArrayD)[Z + 2] := Byte3;
Z := Z - 4;
W := W + 4;
end;
end;
end;
end;
//
procedure TGAReverse(var TempArrayD: TBYTEROW);
begin
if TGAHead.Descriptor and $10 <> 0 then
PixelSwapArray(TempArrayD, LineBytes);
end;
//
procedure TGA16_ANY_U(var Z: integer; var TempArrayD; Width: Word);
var
W1: Word;
I: integer;
R, G, B: Byte;
begin
for I := 0 to Width - 1 do
begin
W1 := FastGetWord;
R := ((W1 shr 10) and $1F) shl 3;
G := ((W1 shr 5) and $1F) shl 3;
B := ((W1 shr 0) and $1F) shl 3;
TBYTEROW(TempArrayD)[Z + 0] := B;
TBYTEROW(TempArrayD)[Z + 1] := G;
TBYTEROW(TempArrayD)[Z + 2] := R;
inc(z, 3);
end;
end;
//
procedure TGA24_ANY_U(var Z: integer; Flag: Byte; var TempArrayD; Width: Word; TempArrayAlpha: PBYTEROW);
var
I: integer;
begin
for I := 0 to Width - 1 do
begin
TBYTEROW(TempArrayD)[Z + 0] := FastGetByte;
TBYTEROW(TempArrayD)[Z + 1] := FastGetByte;
TBYTEROW(TempArrayD)[Z + 2] := FastGetByte;
if Flag = 1 then
begin
if TempArrayAlpha <> nil then
TempArrayAlpha[Z div 3] := FastGetByte
else
FastGetByte;
end;
inc(Z, 3);
end;
end;
//
procedure ReadTGALine;
var
Size, LineSize: integer;
W1: Word;
Z: integer;
R, G, B, B1: Byte;
procedure do8;
var
I: integer;
begin
with rc^ do
for I := 0 to Size - 1 do
begin
TempArrayD^[Z] := ((R shl 5) + (G shl 6) + (B * 12)) div 108;
Inc(Z);
end;
end;
procedure do24Raw;
var
I, Z: integer;
begin
with rc^ do
begin
Z := 0;
for I := 0 to Width - 1 do
begin
B1 := FastGetByte;
TempArrayD^[Z + 0] := Palette256[B1].b;
TempArrayD^[Z + 1] := Palette256[B1].g;
TempArrayD^[Z + 2] := Palette256[B1].r;
if (TempArrayAlpha<>nil) and hasalpha256 then
TempArrayAlpha[Z div 3] := alpha256[B1];
inc(Z, 3);
end;
end;
end;
procedure do24RawPart;
var
I: integer;
begin
with rc^ do
for I := 0 to Size - 1 do
begin
B1 := FastGetByte;
TempArrayD^[Z + 0] := Palette256[B1].b;
TempArrayD^[Z + 1] := Palette256[B1].g;
TempArrayD^[Z + 2] := Palette256[B1].r;
if (TempArrayAlpha<>nil) and hasalpha256 then
TempArrayAlpha[Z div 3] := alpha256[B1];
inc(Z, 3);
end;
end;
procedure do24Fill(B1: Byte);
var
I: integer;
R, G, B: Byte;
begin
with rc^ do
begin
R := Palette256[B1].r;
G := Palette256[B1].g;
B := Palette256[B1].b;
for I := 0 to Size - 1 do
begin
TempArrayD^[Z + 0] := B;
TempArrayD^[Z + 1] := G;
TempArrayD^[Z + 2] := R;
if (TempArrayAlpha<>nil) and hasalpha256 then
TempArrayAlpha[Z div 3] := alpha256[B1];
inc(Z, 3);
end;
end;
end;
procedure do24;
var
I: integer;
begin
with rc^ do
for I := 0 to Size - 1 do
begin
TempArrayD^[Z + 0] := B;
TempArrayD^[Z + 1] := G;
TempArrayD^[Z + 2] := R;
inc(Z, 3);
end;
end;
var
col, q: integer;
begin
// ReadTGALine
with rc^ do
begin
if BitsPerPixel = 1 then
LineSize := (Width + 7) shr 3
else
LineSize := Width;
// Uncompressed Lines
if TGAHead.Imagetype in [1, 2, 3] then
case BitsPerPixel of
1: FastGetBytes(TempArrayD^[0], LineBytes);
8: do24Raw;
16:
begin
Z := 0;
TGA16_ANY_U(Z, TempArrayD^[0], Width);
end;
24:
begin
Z := 0;
TGA24_ANY_U(Z, 0, TempArrayD^[0], Width, TempArrayAlpha);
end;
32:
begin
Z := 0;
TGA24_ANY_U(Z, 1, TempArrayD^[0], Width, TempArrayAlpha);
end;
end
else
begin
// Compressed Lines
Z := 0;
col := 0;
repeat
if RemCode>-1 then
begin
B1 := RemCode;
Size := RemSize;
RemCode := -1;
end
else
begin
B1 := FastGetByte;
Size := (B1 and $7F) + 1;
end;
if Size+col>LineSize then
begin
RemSize := (Size+Col)-LineSize;
RemCode := B1;
Size := LineSize-col;
end;
if (B1 and $80) <> 0 then
begin
// Run length packet
case BitsPerPixel of
1, 8:
begin
B1 := FastGetByte;
do24Fill(B1);
end;
16:
begin
W1 := FastGetWord;
R := ((W1 shr 10) and $1F) shl 3;
G := ((W1 shr 5) and $1F) shl 3;
B := ((W1 shr 0) and $1F) shl 3;
do24;
end;
24, 32:
begin
B := FastGetByte;
G := FastGetByte;
R := FastGetByte;
if BitsPerPixel = 32 then
begin
B1 := FastGetByte;
if TempArrayAlpha <> nil then
for q := col to col+Size-1 do
TempArrayAlpha[q] := B1;
end;
do24;
end;
end;
end
else
// Single bytes
case BitsPerPixel of
1, 8:
do24RawPart;
16:
TGA16_ANY_U(Z, TempArrayD^[0], Size);
24:
TGA24_ANY_U(Z, 0, TempArrayD^[0], Size, TempArrayAlpha);
32:
TGA24_ANY_U(Z, 1, TempArrayD^[0], Size, TempArrayAlpha);
end;
inc(col, Size);
until col >= LineSize;
end;
end;
end;
//
begin
new(rc);
try
zeromemory(rc, sizeof(TRC));
with rc^ do
begin
// init alpha
for i := 0 to 255 do
alpha256[i] := 255;
hasalpha256 := false;
// Read Targa Stream
sbase := Stream.Position;
Index1 := 0;
Index2 := 0;
FileOk := true;
ReadTgaFileHeader(FileOK, Width, Height, BitsPerPixel, Compressed);
if FileOK then
begin
IOParams.Width := Width;
IOParams.Height := Height;
IOParams.OriginalWidth := Width;
IOParams.OriginalHeight := Height;
IOParams.DpiX := IEGlobalSettings().DefaultDPIX;
IOParams.DpiY := IEGlobalSettings().DefaultDPIY;
IOParams.ImageCount := 1;
case BitsPerPixel of
1:
begin
IOParams.BitsPerSample := 1;
IOParams.SamplesPerPixel := 1;
end;
8:
begin
IOParams.BitsPerSample := 8;
IOParams.SamplesPerPixel := 1;
end;
16:
begin
IOParams.BitsPerSample := 5;
IOParams.SamplesPerPixel := 3;
end;
24:
begin
IOParams.BitsPerSample := 8;
IOParams.SamplesPerPixel := 3;
end;
32:
begin
IOParams.BitsPerSample := 8;
IOParams.SamplesPerPixel := 4;
end;
end;
if not Preview then
begin
Progress.per1 := 100 / Height;
Progress.val := 0;
if BitsPerPixel = 1 then
begin
Bitmap.Allocate(Width, Height, ie1g);
LineBytes := (Width + 7) shr 3;
end
else
begin
Bitmap.Allocate(Width, Height, ie24RGB);
LineBytes := Width * 3;
end;
TempArrayD := nil;
TempArrayAlpha := nil;
try
GetMem(TempArrayD, LineBytes);
if (not IgnoreAlpha) and ( ((BitsPerPixel = 32) (*and ((TGAHead.Descriptor and 8) <> 0)*)) or (TGAHead.ColorMapBits=32) ) then
begin
if not assigned(AlphaChannel) then
AlphaChannel := TIEMask.Create;
AlphaChannel.AllocateBits(Width, Height, 8);
AlphaChannel.Fill(255);
getmem(TempArrayAlpha, Width);
FillChar(TempArrayAlpha^, Width, 255);
AlphaChannel.Full := false;
end
else
TempArrayAlpha := nil;
if ((ord(TGAHead.Descriptor) and 32) <> 32) and ((ord(TGAHead.Descriptor) and 16) <> 16) then
begin
StartLine := Height - 1;
IncLine := -1;
end
else
begin
StartLine := 0;
IncLine := 1;
end;
RemCode := -1;
I := StartLine;
II := 0;
if TGAHead.Imagetype in [1, 2, 3, 9, 10, 11] then
repeat
ReadTGALine;
TGAReverse(TempArrayD^);
Ptr1 := BitMap.ScanLine[I];
// Copy the data
Move(TempArrayD^, Ptr1^, LineBytes);
// copy alpha
if TempArrayAlpha <> nil then
copymemory(AlphaChannel.Scanline[I], TempArrayAlpha, Width);
Inc(II);
I := I + IncLine;
with Progress do
begin
inc(val);
if assigned(fOnProgress) then
fOnProgress(Sender, trunc(per1 * val));
end;
until (II >= Height) or (Progress.Aborting^=True)
else
Progress.Aborting^ := True;
finally
FreeMem(TempArrayD);
if TempArrayAlpha <> nil then
FreeMem(TempArrayAlpha);
end;
end; // not preview
end
else
Progress.Aborting^ := True;
end;
finally
dispose(rc);
end;
if assigned(AlphaChannel) then
begin
AlphaChannel.SyncRect;
if AlphaChannel.IsEmpty then
FreeAndNil(AlphaChannel);
end;
end;
///////////////////////////////////////////////////////////////////////////////////////
procedure WriteTGAStream(Stream: TStream; Bitmap: TIEBitmap; var IOParams: TIOParams; var Progress: TProgressRec; AlphaChannel: TIEMask);
var
rc: PRC;
TGAHead: TGAHeader;
OutputWidth: integer;
OutputHeight: integer;
DestBitsPerPixel: integer;
OrigBitsPerPixel: integer;
NewLine: PBYTEROW;
XBitmap: TIEBitmap;
qt: TIEQuantizer;
NullProgress: TProgressRec;
//
procedure TGAWriteHeader;
begin
TGAHead.IdentSize := Length(IOParams.TGA_Descriptor) + 1;
TGAHead.Bits := DestBitsPerPixel;
TGAHead.Descriptor := 0;
case DestBitsPerPixel of
1:
begin
// bilevel image
TGAHead.ColorMaptype := 0;
if IOParams.TGA_Compressed then
TGAHead.Imagetype := 11
else
TGAHead.Imagetype := 3;
TGAHead.ColorMapStart := 0;
TGAHead.ColorMapLength := 0;
TGAHead.ColorMapBits := 0;
end;
4, 8:
begin
if (DestBitsPerPixel = 8) and IOParams.TGA_GrayLevel then
begin
// gray scaled image
TGAHead.ColorMaptype := 0;
if IOParams.TGA_Compressed then
TGAHead.Imagetype := 11
else
TGAHead.Imagetype := 3;
TGAHead.ColorMapStart := 0;
TGAHead.ColorMapLength := 1 shl DestBitsPerPixel;
TGAHead.ColorMapBits := 24;
end
else
begin
// colormapped image
TGAHead.ColorMaptype := 1;
if IOParams.TGA_Compressed then
TGAHead.Imagetype := 9
else
TGAHead.Imagetype := 1;
TGAHead.ColorMapStart := 0;
TGAHead.ColorMapLength := 1 shl DestBitsPerPixel;
TGAHead.ColorMapBits := 24;
end;
end;
24:
begin
// true color image
TGAHead.ColorMaptype := 0;
if IOParams.TGA_Compressed then
TGAHead.Imagetype := 10
else
TGAHead.Imagetype := 2;
TGAHead.ColorMapStart := 0;
TGAHead.ColorMapLength := 0;
TGAHead.ColorMapBits := 0;
end;
32:
begin
// true color image with alpha (AlphaChannel must be valid and not empty)
TGAHead.ColorMaptype := 0;
if IOParams.TGA_Compressed then
TGAHead.Imagetype := 10
else
TGAHead.Imagetype := 2;
TGAHead.ColorMapStart := 0;
TGAHead.ColorMapLength := 0;
TGAHead.ColorMapBits := 0;
TGAHead.Descriptor := TGAHead.Descriptor or $8; // alpha channel with 8 bit
end;
end;
TGAHead.XStart := IOParams.TGA_XPos;
TGAHead.YStart := IOParams.TGA_YPos;
TGAHead.Width := OutputWidth;
TGAHead.Height := OutputHeight;
TGAHead.Descriptor := TGAHead.Descriptor or $20; // isn't IOParams.TGA_Descriptor!!
SafeStreamWrite(Stream, Progress.Aborting^, TGAHead, Sizeof(TGAHead));
SafeStreamWrite(Stream, Progress.Aborting^, PAnsiChar(IOParams.TGA_Descriptor)^, Length(IOParams.TGA_Descriptor) + 1);
if TGAHead.ColorMaptype = 1 then
SafeStreamWrite(Stream, Progress.Aborting^, rc^.Palette256[0], (1 shl DestBitsPerPixel) * 3);
end;
//
// convert to gray TempArrayDBIG (TBYTEROW)
// only for 8bpp
procedure TGAConvertToGray;
var
i: integer;
begin
for i := 0 to OutputWidth - 1 do
with rc^, Palette256[TempArrayDBIG^[i]] do
TempArrayDBIG^[i] := _RGBToGray(CreateRGB(r, g, b));
end;
//
// compress TempArrayDBIG (TBYTEROW) and saves it in CompRow and then in the stream
// implemented only for 8 and 24 bit
procedure TGACompress;
var
p, bwidth: integer;
l8: byte;
l24: TRGB;
l: byte;
warr: pbyte;
begin
with rc^ do
begin
warr := pbyte(@CompRow[0]);
p := 0;
case DestBitsPerPixel of
8: // encode 8 bit row (1 byte)
begin
bwidth := OutputWidth;
repeat
l8 := TempArrayDBIG^[p];
inc(p);
l := 1;
while (p < bwidth) and (l8 = TempArrayDBIG^[p]) and (l < 128) do
begin
inc(p);
inc(l);
end;
if l > 1 then
begin
// encode as run-length packet
warr^ := $80 or (l - 1);
inc(warr);
warr^ := l8;
inc(warr);
end
else
begin
// encode as raw packet
dec(p);
l := 0;
while (p < bwidth - 1) and (TempArrayDBIG^[p] <> TemparrayDBIG^[p + 1]) and (l < 127) do
begin
inc(p);
inc(l);
end;
if p = bwidth - 1 then
begin
inc(p);
inc(l);
end;
warr^ := l - 1;
inc(warr);
CopyMemory(warr, @TempArrayDBIG^[p - l], l);
inc(warr, l);
end;
until p >= bwidth;
end;
24: // encode 24 bit row (3 byte)
begin
bwidth := OutputWidth * 3;
repeat
l24 := PRGB(@TempArrayDBIG^[p])^;
inc(p, 3);
l := 1;
while (p < bwidth) and equalrgb(l24, PRGB(@TempArrayDBIG^[p])^) and (l < 128) do
begin
inc(p, 3);
inc(l);
end;
if l > 1 then
begin
// encode as run-length packet
warr^ := $80 or (l - 1);
inc(warr);
PRGB(warr)^ := l24;
inc(warr, 3);
end
else
begin
// encode as raw packet
dec(p, 3);
l := 0;
while (p < bwidth - 3) and (not equalrgb(PRGB(@TempArrayDBIG^[p])^, PRGB(@TemparrayDBIG^[p + 3])^)) and (l < 127) do
begin
inc(p, 3);
inc(l);
end;
if p = bwidth - 3 then
begin
inc(p, 3);
inc(l);
end;
warr^ := l - 1;
inc(warr);
CopyMemory(warr, @TempArrayDBIG^[p - l * 3], l * 3);
inc(warr, l * 3);
end;
until p >= bwidth;
end;
end;
SafeStreamWrite(Stream, Progress.Aborting^, CompRow^[0], uint64(warr) - uint64(@CompRow[0]));
end;
end;
//
procedure TGAWriteBody;
var
i, l, x: integer;
oarr, px, al, sp: pbyte;
begin
with rc^ do
begin
i := 0;
Progress.per1 := 100 / OutputHeight;
Progress.val := 0;
if DestBitsPerPixel = 32 then
getmem(oarr, XBitmap.Width * 4)
else
oarr := nil;
try
repeat
TempArrayD := XBitMap.ScanLine[i];
if DestBitsPerPixel = 32 then
l := _ConvertXBitsToYBits(TempArrayD^, TempArrayDBIG^, OrigBitsPerPixel, 24, OutputWidth, Palette256, qt)
else
l := _ConvertXBitsToYBits(TempArrayD^, TempArrayDBIG^, OrigBitsPerPixel, DestBitsPerPixel, OutputWidth, Palette256, qt);
if IOParams.TGA_GrayLevel and (DestBitsPerPixel = 8) then
TGAConvertToGray;
if IOParams.TGA_Compressed then
TGACompress
else
begin
if DestBitsPerPixel = 32 then
begin
// add and save alpha channel
al := AlphaChannel.Scanline[i];
px := oarr;
sp := pbyte(TempArrayDBIG);
for x := 0 to XBitmap.Width - 1 do
begin
px^ := sp^;
inc(px);
inc(sp);
px^ := sp^;
inc(px);
inc(sp);
px^ := sp^;
inc(px);
inc(sp);
px^ := al^;
inc(px);
inc(al);
end;
SafeStreamWrite(Stream, Progress.Aborting^, oarr^, 4 * XBitmap.Width);
end
else
SafeStreamWrite(Stream, Progress.Aborting^, TempArrayDBIG^[0], l);
end;
inc(i);
with Progress do
begin
inc(val);
if assigned(fOnProgress) then
fOnProgress(Sender, trunc(per1 * val));
end;
until (i >= OutputHeight);
finally
if DestBitsPerPixel = 32 then
freemem(oarr);
end;
end;
end;
//
procedure TGAWriteExtension;
var
ms: word;
begin
with rc^, Extension do
begin
zeromemory(@Extension, sizeof(TGAExtension));
IEStrCopy(AuthorName, PAnsiChar(IOParams.TGA_Author));
IEStrCopy(JobName, PAnsiChar(IOParams.TGA_ImageName));
DecodeDate(IOParams.TGA_Date, DateTime[2], DateTime[0], DateTime[1]);
DecodeTime(IOParams.TGA_Date, DateTime[3], DateTime[4], DateTime[5], ms);
KeyColor[0] := 0;
KeyColor[1] := IOParams.TGA_Background.r;
KeyColor[2] := IOParams.TGA_Background.g;
KeyColor[3] := IOParams.TGA_Background.b;
AspectRatio[0] := trunc(IOParams.TGA_AspectRatio) * 10000;
AspectRatio[1] := 10000;
Gamma[0] := trunc(IOParams.TGA_Gamma) * 10000;
Gamma[1] := 10000;
SafeStreamWrite(Stream, Progress.Aborting^, Extension, sizeof(TGAExtension));
if DestBitsPerPixel = 32 then
AttributesType := 0;
end;
end;
//
procedure TGAWriteFooter;
begin
with rc^ do
begin
Footer.Signature := 'TRUEVISION-XFILE.' + AnsiChar(0);
Footer.DeveloperDir := 0;
Footer.ExtensionArea := sbase + Stream.Position;
TGAWriteExtension;
SafeStreamWrite(Stream, Progress.Aborting^, Footer, sizeof(TGAFooter));
end;
end;
//
var
rgb1, rgb2: TRGB;
begin
if (Bitmap.PixelFormat <> ie24RGB) and (Bitmap.PixelFormat <> ie1g) then
exit;
NullProgress := NullProgressRec( Progress.Aborting, False );
XBitmap := Bitmap;
qt := nil;
new(rc);
zeromemory(rc, sizeof(TRC));
try
with rc^ do
begin
// Write TARGA Stream.
sbase := Stream.Position;
Index1 := 0;
Index2 := 0;
if Bitmap.PixelFormat = ie24RGB then
OrigBitsPerPixel := 24
else
OrigBitsPerPixel := 1;
if IOParams.SamplesPerPixel = 1 then
begin
case IOParams.BitsPerSample of
1:
begin
if OrigBitsPerPixel = 24 then
begin
XBitmap := _ConvertTo1bitEx(Bitmap, rgb1, rgb2);
if XBitmap = nil then
begin
// impossible to convert to 1 bit, converts from color to black/white
// 3.0.0
XBitmap := TIEBitmap.Create(Bitmap.Width, Bitmap.Height, ie1g);
XBitmap.CopyAndConvertFormat(Bitmap);
end;
OrigBitsPerPixel := 1;
end;
DestBitsPerPixel := 1;
Palette256[0] := CreateRGB(0, 0, 0);
Palette256[1] := CreateRGB(255, 255, 255);
IOParams.TGA_Compressed := false;
end;
4:
begin
if OrigBitsPerPixel = 24 then
begin
IOParams.FreeColorMap;
IOParams.fColorMapCount := 16;
getmem(IOParams.fColorMap, 16 * 3);
qt := TIEQuantizer.Create(Bitmap, IOParams.ColorMap^, 16);
copymemory(@Palette256[0], IOparams.ColorMap, 16 * 3);
end;
DestBitsPerPixel := 4;
IOParams.TGA_Compressed := false;
end;
8:
begin
if OrigBitsPerPixel = 24 then
begin
IOParams.FreeColorMap;
IOParams.fColorMapCount := 256;
getmem(IOParams.fColorMap, 256 * 3);
qt := TIEQuantizer.Create(Bitmap, IOParams.ColorMap^, 256);
copymemory(@Palette256[0], IOparams.ColorMap, 256 * 3);
end;
DestBitsPerPixel := 8;
end;
end;
end
else
begin
if assigned(AlphaChannel) then
begin
DestBitsPerPixel := 32;
IOParams.TGA_Compressed := false; // alpha+compression not supported
end
else
DestBitsPerPixel := 24;
end;
OutputWidth := XBitmap.Width;
OutputHeight := XBitmap.Height;
GetMem(NewLine, OutputWidth * 3);
GetMem(TempArrayDBig, OutputWidth * 3);
if IOParams.TGA_Compressed then
GetMem(CompRow, OutputWidth * 3 * 3);
try
TGAWriteHeader;
TGAWriteBody;
TGAWriteFooter;
finally
FreeMem(TempArrayDBig);
FreeMem(NewLine);
if IOParams.TGA_Compressed then
freemem(CompRow);
end;
end;
finally
dispose(rc);
if XBitmap <> Bitmap then
FreeAndNil(XBitmap);
if assigned(qt) then
FreeAndNil(qt);
end;
end;
end.