home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 September
/
Chip_2001-09_cd1.bin
/
zkuste
/
delphi
/
kolekce
/
d6
/
RX275D6.ZIP
/
Units
/
Rxgif.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2001-06-24
|
83KB
|
2,763 lines
{*******************************************************}
{ }
{ Delphi VCL Extensions (RX) }
{ }
{ Copyright (c) 1997, 1998 Master-Bank }
{ }
{*******************************************************}
unit RxGIF;
interface
{$I RX.INC}
uses Windows, RTLConsts, SysUtils, Classes, Graphics, RxGraph;
const
RT_GIF = 'GIF'; { GIF Resource Type }
type
{$IFNDEF RX_D3}
TProgressStage = (psStarting, psRunning, psEnding);
TProgressEvent = procedure (Sender: TObject; Stage: TProgressStage;
PercentDone: Byte; RedrawNow: Boolean; const R: TRect;
const Msg: string) of object;
{ TSharedImage }
TSharedImage = class
private
FRefCount: Integer;
protected
procedure Reference;
procedure Release;
procedure FreeHandle; virtual; abstract;
property RefCount: Integer read FRefCount;
end;
{$ENDIF RX_D3}
TGIFVersion = (gvUnknown, gv87a, gv89a);
TGIFBits = 1..8;
TDisposalMethod = (dmUndefined, dmLeave, dmRestoreBackground,
dmRestorePrevious, dmReserved4, dmReserved5, dmReserved6, dmReserved7);
TGIFColorItem = packed record
Red, Green, Blue: Byte;
end;
TGIFColorTable = packed record
Count: Integer;
Colors: packed array[Byte] of TGIFColorItem;
end;
TGIFFrame = class;
TGIFData = class;
TGIFItem = class;
{ TGIFImage }
TGIFImage = class(TGraphic)
private
FImage: TGIFData;
FVersion: TGIFVersion;
FItems: TList;
FFrameIndex: Integer;
FScreenWidth: Word;
FScreenHeight: Word;
FBackgroundColor: TColor;
FLooping: Boolean;
FCorrupted: Boolean;
FRepeatCount: Word;
{$IFNDEF RX_D3}
FOnProgress: TProgressEvent;
{$ENDIF}
function GetBitmap: TBitmap;
function GetCount: Integer;
function GetComment: TStrings;
function GetScreenWidth: Integer;
function GetScreenHeight: Integer;
function GetGlobalColorCount: Integer;
procedure UpdateScreenSize;
procedure SetComment(Value: TStrings);
function GetFrame(Index: Integer): TGIFFrame;
procedure SetFrameIndex(Value: Integer);
procedure SetBackgroundColor(Value: TColor);
procedure SetLooping(Value: Boolean);
procedure SetRepeatCount(Value: Word);
procedure ReadSignature(Stream: TStream);
procedure DoProgress(Stage: TProgressStage; PercentDone: Byte;
const Msg: string);
function GetCorrupted: Boolean;
function GetTransparentColor: TColor;
function GetBackgroundColor: TColor;
function GetPixelFormat: TPixelFormat;
procedure EncodeFrames(ReverseDecode: Boolean);
procedure ReadStream(Size: Longint; Stream: TStream; ForceDecode: Boolean);
procedure WriteStream(Stream: TStream; WriteSize: Boolean);
protected
procedure AssignTo(Dest: TPersistent); override;
procedure Draw(ACanvas: TCanvas; const ARect: TRect); override;
{$IFDEF WIN32}
function Equals(Graphic: TGraphic): Boolean; override;
{$ENDIF}
function GetEmpty: Boolean; override;
function GetHeight: Integer; override;
function GetWidth: Integer; override;
function GetPalette: HPALETTE; {$IFDEF RX_D3} override; {$ENDIF}
function GetTransparent: Boolean; {$IFDEF RX_D3} override; {$ENDIF}
procedure ClearItems;
procedure NewImage;
procedure UniqueImage;
{$IFNDEF RX_D3}
procedure Progress(Sender: TObject; Stage: TProgressStage;
PercentDone: Byte; RedrawNow: Boolean; const R: TRect;
const Msg: string); dynamic;
{$ENDIF}
procedure ReadData(Stream: TStream); override;
procedure SetHeight(Value: Integer); override;
procedure SetWidth(Value: Integer); override;
procedure WriteData(Stream: TStream); override;
property Bitmap: TBitmap read GetBitmap; { volatile }
public
constructor Create; override;
destructor Destroy; override;
procedure Clear;
procedure DecodeAllFrames;
procedure EncodeAllFrames;
procedure Assign(Source: TPersistent); override;
procedure LoadFromStream(Stream: TStream); override;
procedure SaveToStream(Stream: TStream); override;
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE); override;
procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
var APalette: HPALETTE); override;
procedure LoadFromResourceName(Instance: THandle; const ResName: string;
ResType: PChar);
procedure LoadFromResourceID(Instance: THandle; ResID: Integer;
ResType: PChar);
function AddFrame(Value: TGraphic): Integer; virtual;
procedure DeleteFrame(Index: Integer);
procedure MoveFrame(CurIndex, NewIndex: Integer);
procedure Grayscale(ForceEncoding: Boolean);
property BackgroundColor: TColor read GetBackgroundColor write SetBackgroundColor;
property Comment: TStrings read GetComment write SetComment;
property Corrupted: Boolean read GetCorrupted;
property Count: Integer read GetCount;
property Frames[Index: Integer]: TGIFFrame read GetFrame; default;
property FrameIndex: Integer read FFrameIndex write SetFrameIndex;
property GlobalColorCount: Integer read GetGlobalColorCount;
property Looping: Boolean read FLooping write SetLooping;
property PixelFormat: TPixelFormat read GetPixelFormat;
property RepeatCount: Word read FRepeatCount write SetRepeatCount;
property ScreenWidth: Integer read GetScreenWidth;
property ScreenHeight: Integer read GetScreenHeight;
property TransparentColor: TColor read GetTransparentColor;
property Version: TGIFVersion read FVersion;
{$IFNDEF RX_D3}
property Palette: HPALETTE read GetPalette;
property Transparent: Boolean read GetTransparent;
property OnProgress: TProgressEvent read FOnProgress write FOnProgress;
{$ENDIF}
end;
{ TGIFFrame }
TGIFFrame = class(TPersistent)
private
FOwner: TGIFImage;
FBitmap: TBitmap;
FImage: TGIFItem;
FExtensions: TList;
FTopLeft: TPoint;
FInterlaced: Boolean;
FCorrupted: Boolean;
FGrayscale: Boolean;
FTransparentColor: TColor;
FAnimateInterval: Word;
FDisposal: TDisposalMethod;
FLocalColors: Boolean;
function GetBitmap: TBitmap;
function GetHeight: Integer;
function GetWidth: Integer;
function GetColorCount: Integer;
function FindComment(ForceCreate: Boolean): TStrings;
function GetComment: TStrings;
procedure SetComment(Value: TStrings);
procedure SetTransparentColor(Value: TColor);
procedure SetDisposalMethod(Value: TDisposalMethod);
procedure SetAnimateInterval(Value: Word);
procedure SetTopLeft(const Value: TPoint);
procedure NewBitmap;
procedure NewImage;
procedure SaveToBitmapStream(Stream: TMemoryStream);
procedure EncodeBitmapStream(Stream: TMemoryStream);
procedure EncodeRasterData;
procedure UpdateExtensions;
procedure WriteImageDescriptor(Stream: TStream);
procedure WriteLocalColorMap(Stream: TStream);
procedure WriteRasterData(Stream: TStream);
protected
constructor Create(AOwner: TGIFImage); virtual;
procedure LoadFromStream(Stream: TStream);
procedure AssignTo(Dest: TPersistent); override;
procedure GrayscaleImage(ForceEncoding: Boolean);
public
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure Draw(ACanvas: TCanvas; const ARect: TRect;
Transparent: Boolean);
property AnimateInterval: Word read FAnimateInterval write SetAnimateInterval;
property Bitmap: TBitmap read GetBitmap; { volatile }
property ColorCount: Integer read GetColorCount;
property Comment: TStrings read GetComment write SetComment;
property DisposalMethod: TDisposalMethod read FDisposal write SetDisposalMethod;
property Interlaced: Boolean read FInterlaced;
property Corrupted: Boolean read FCorrupted;
property TransparentColor: TColor read FTransparentColor write SetTransparentColor;
property Origin: TPoint read FTopLeft write SetTopLeft;
property Height: Integer read GetHeight;
property Width: Integer read GetWidth;
end;
{ TGIFData }
TGIFData = class(TSharedImage)
private
FComment: TStrings;
FAspectRatio: Byte;
FBitsPerPixel: Byte;
FColorResBits: Byte;
FColorMap: TGIFColorTable;
protected
procedure FreeHandle; override;
public
constructor Create;
destructor Destroy; override;
end;
{ TGIFItem }
TGIFItem = class(TSharedImage)
private
FImageData: TMemoryStream;
FSize: TPoint;
FPackedFields: Byte;
FBitsPerPixel: Byte;
FColorMap: TGIFColorTable;
protected
procedure FreeHandle; override;
public
destructor Destroy; override;
end;
{ Clipboard format for GIF image }
var
CF_GIF: Word;
{ Load incomplete or corrupted images without exceptions }
const
GIFLoadCorrupted: Boolean = True;
function GIFVersionName(Version: TGIFVersion): string;
procedure rxgif_dummy;
implementation
uses Consts, {$IFNDEF WIN32} Str16, {$ENDIF} VclUtils, AniFile, RxConst,
MaxMin, RxGConst;
{$R-}
procedure rxgif_dummy;
begin
end;
procedure GifError(const Msg: string);
{$IFDEF WIN32}
function ReturnAddr: Pointer;
asm
MOV EAX,[EBP+4]
end;
{$ELSE}
function ReturnAddr: Pointer; assembler;
asm
MOV AX,[BP].Word[2]
MOV DX,[BP].Word[4]
end;
{$ENDIF}
begin
raise EInvalidGraphicOperation.Create(Msg) at ReturnAddr;
end;
{$IFNDEF RX_D3}
{ TSharedImage }
procedure TSharedImage.Reference;
begin
Inc(FRefCount);
end;
procedure TSharedImage.Release;
begin
if Pointer(Self) <> nil then begin
Dec(FRefCount);
if FRefCount = 0 then begin
FreeHandle;
Free;
end;
end;
end;
{$ENDIF}
const
GIFSignature = 'GIF';
GIFVersionStr: array[TGIFVersion] of PChar = (#0#0#0, '87a', '89a');
function GIFVersionName(Version: TGIFVersion): string;
begin
Result := StrPas(GIFVersionStr[Version]);
end;
const
CODE_TABLE_SIZE = 4096;
{$IFDEF WIN32}
HASH_TABLE_SIZE = 17777;
{$ELSE}
HASH_TABLE_SIZE = MaxListSize - $10;
{$ENDIF}
MAX_LOOP_COUNT = 30000;
CHR_EXT_INTRODUCER = '!';
CHR_IMAGE_SEPARATOR = ',';
CHR_TRAILER = ';'; { indicates the end of the GIF Data stream }
{ Image descriptor bit masks }
ID_LOCAL_COLOR_TABLE = $80; { set if a local color table follows }
ID_INTERLACED = $40; { set if image is interlaced }
ID_SORT = $20; { set if color table is sorted }
ID_RESERVED = $0C; { reserved - must be set to $00 }
ID_COLOR_TABLE_SIZE = $07; { Size of color table as above }
{ Logical screen descriptor packed field masks }
LSD_GLOBAL_COLOR_TABLE = $80; { set if global color table follows L.S.D. }
LSD_COLOR_RESOLUTION = $70; { Color resolution - 3 bits }
LSD_SORT = $08; { set if global color table is sorted - 1 bit }
LSD_COLOR_TABLE_SIZE = $07; { Size of global color table - 3 bits }
{ Actual Size = 2^value+1 - value is 3 bits }
{ Graphic control extension packed field masks }
GCE_TRANSPARENT = $01; { whether a transparency Index is given }
GCE_USER_INPUT = $02; { whether or not user input is expected }
GCE_DISPOSAL_METHOD = $1C; { the way in which the graphic is to be treated after being displayed }
GCE_RESERVED = $E0; { reserved - must be set to $00 }
{ Application extension }
AE_LOOPING = $01; { looping Netscape extension }
GIFColors: array[TGIFBits] of Word = (2, 4, 8, 16, 32, 64, 128, 256);
function ColorsToBits(ColorCount: Word): Byte; near;
var
I: TGIFBits;
begin
Result := 0;
for I := Low(TGIFBits) to High(TGIFBits) do
if ColorCount = GIFColors[I] then begin
Result := I;
Exit;
end;
GifError(LoadStr(SWrongGIFColors));
end;
function ColorsToPixelFormat(Colors: Word): TPixelFormat;
begin
if Colors <= 2 then Result := pf1bit
else if Colors <= 16 then Result := pf4bit
else if Colors <= 256 then Result := pf8bit
else Result := pf24bit;
end;
function ItemToRGB(Item: TGIFColorItem): Longint; near;
begin
with Item do Result := RGB(Red, Green, Blue);
end;
function GrayColor(Color: TColor): TColor;
var
Index: Integer;
begin
Index := Byte(Longint(Word(GetRValue(Color)) * 77 +
Word(GetGValue(Color)) * 150 + Word(GetBValue(Color)) * 29) shr 8);
Result := RGB(Index, Index, Index);
end;
procedure GrayColorTable(var ColorTable: TGIFColorTable);
var
I: Byte;
Index: Integer;
begin
for I := 0 to ColorTable.Count - 1 do begin
with ColorTable.Colors[I] do begin
Index := Byte(Longint(Word(Red) * 77 + Word(Green) * 150 +
Word(Blue) * 29) shr 8);
Red := Index;
Green := Index;
Blue := Index;
end;
end;
end;
function FindColorIndex(const ColorTable: TGIFColorTable;
Color: TColor): Integer;
begin
if (Color <> clNone) then
for Result := 0 to ColorTable.Count - 1 do
if ItemToRGB(ColorTable.Colors[Result]) = ColorToRGB(Color) then Exit;
Result := -1;
end;
{ The following types and function declarations are used to call into
functions of the GIF implementation of the GIF image
compression/decompression standard. }
type
TGIFHeader = packed record
Signature: array[0..2] of Char; { contains 'GIF' }
Version: array[0..2] of Char; { '87a' or '89a' }
end;
TScreenDescriptor = packed record
ScreenWidth: Word; { logical screen width }
ScreenHeight: Word; { logical screen height }
PackedFields: Byte;
BackgroundColorIndex: Byte; { Index to global color table }
AspectRatio: Byte; { actual ratio = (AspectRatio + 15) / 64 }
end;
TImageDescriptor = packed record
ImageLeftPos: Word; { column in pixels in respect to left of logical screen }
ImageTopPos: Word; { row in pixels in respect to top of logical screen }
ImageWidth: Word; { width of image in pixels }
ImageHeight: Word; { height of image in pixels }
PackedFields: Byte;
end;
{ GIF Extensions support }
type
TExtensionType = (etGraphic, etPlainText, etApplication, etComment);
const
ExtLabels: array[TExtensionType] of Byte = ($F9, $01, $FF, $FE);
LoopExtNS: string[11] = 'NETSCAPE2.0';
LoopExtAN: string[11] = 'ANIMEXTS1.0';
type
TGraphicControlExtension = packed record
BlockSize: Byte; { should be 4 }
PackedFields: Byte;
DelayTime: Word; { in centiseconds }
TransparentColorIndex: Byte;
Terminator: Byte;
end;
TPlainTextExtension = packed record
BlockSize: Byte; { should be 12 }
Left, Top, Width, Height: Word;
CellWidth, CellHeight: Byte;
FGColorIndex, BGColorIndex: Byte;
end;
TAppExtension = packed record
BlockSize: Byte; { should be 11 }
AppId: array[1..8] of Byte;
Authentication: array[1..3] of Byte;
end;
TExtensionRecord = packed record
case ExtensionType: TExtensionType of
etGraphic: (GCE: TGraphicControlExtension);
etPlainText: (PTE: TPlainTextExtension);
etApplication: (APPE: TAppExtension);
end;
{ TExtension }
TExtension = class(TPersistent)
private
FExtType: TExtensionType;
FData: TStrings;
FExtRec: TExtensionRecord;
public
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
function IsLoopExtension: Boolean;
end;
destructor TExtension.Destroy;
begin
FData.Free;
inherited Destroy;
end;
procedure TExtension.Assign(Source: TPersistent);
begin
if (Source <> nil) and (Source is TExtension) then begin
FExtType := TExtension(Source).FExtType;
FExtRec := TExtension(Source).FExtRec;
if TExtension(Source).FData <> nil then begin
if FData = nil then FData := TStringList.Create;
FData.Assign(TExtension(Source).FData);
end;
end
else inherited Assign(Source);
end;
function TExtension.IsLoopExtension: Boolean;
begin
Result := (FExtType = etApplication) and (FData.Count > 0) and
(CompareMem(@FExtRec.APPE.AppId, @LoopExtNS[1], FExtRec.APPE.BlockSize) or
CompareMem(@FExtRec.APPE.AppId, @LoopExtAN[1], FExtRec.APPE.BlockSize)) and
(Length(FData[0]) >= 3) and (Byte(FData[0][1]) = AE_LOOPING);
end;
procedure FreeExtensions(Extensions: TList); near;
begin
if Extensions <> nil then begin
while Extensions.Count > 0 do begin
TObject(Extensions[0]).Free;
Extensions.Delete(0);
end;
Extensions.Free;
end;
end;
function FindExtension(Extensions: TList; ExtType: TExtensionType): TExtension;
var
I: Integer;
begin
if Extensions <> nil then
for I := Extensions.Count - 1 downto 0 do begin
Result := TExtension(Extensions[I]);
if (Result <> nil) and (Result.FExtType = ExtType) then Exit;
end;
Result := nil;
end;
{
function CopyExtensions(Source: TList): TList; near;
var
I: Integer;
Ext: TExtension;
begin
Result := TList.Create;
try
for I := 0 to Source.Count - 1 do
if (Source[I] <> nil) and (TObject(Source[I]) is TExtension) then begin
Ext := TExtension.Create;
try
Ext.Assign(Source[I]);
Result.Add(Ext);
except
Ext.Free;
raise;
end;
end;
except
Result.Free;
raise;
end;
end;
}
type
TProgressProc = procedure (Stage: TProgressStage; PercentDone: Byte;
const Msg: string) of object;
{ GIF reading/writing routines
Procedures to read and write GIF files, GIF-decoding and encoding
based on freeware C source code of GBM package by Andy Key
(nyangau@interalpha.co.uk). The home page of GBM author is
at http://www.interalpha.net/customer/nyangau/. }
type
PIntCodeTable = ^TIntCodeTable;
TIntCodeTable = array[0..CODE_TABLE_SIZE - 1] of Word;
PReadContext = ^TReadContext;
TReadContext = record
Inx, Size: Longint;
Buf: array[0..255 + 4] of Byte;
CodeSize: Longint;
ReadMask: Longint;
end;
PWriteContext = ^TWriteContext;
TWriteContext = record
Inx: Longint;
CodeSize: Longint;
Buf: array[0..255 + 4] of Byte;
end;
TOutputContext = record
W, H, X, Y: Longint;
BitsPerPixel, Pass: Integer;
Interlace: Boolean;
LineIdent: Longint;
Data, CurrLineData: Pointer;
end;
PImageDict = ^TImageDict;
TImageDict = record
Tail, Index: Word;
Col: Byte;
end;
PDictTable = ^TDictTable;
TDictTable = array[0..CODE_TABLE_SIZE - 1] of TImageDict;
PRGBPalette = ^TRGBPalette;
TRGBPalette = array [Byte] of TRGBQuad;
function InitHash(P: Longint): Longint;
begin
Result := (P + 3) * 301;
end;
function InterlaceStep(Y, Height: Integer; var Pass: Integer): Integer;
begin
Result := Y;
case Pass of
0, 1: Inc(Result, 8);
2: Inc(Result, 4);
3: Inc(Result, 2);
end;
if Result >= Height then begin
if Pass = 0 then begin
Pass := 1; Result := 4;
if (Result < Height) then Exit;
end;
if Pass = 1 then begin
Pass := 2; Result := 2;
if (Result < Height) then Exit;
end;
if Pass = 2 then begin
Pass := 3; Result := 1;
end;
end;
end;
procedure ReadImageStream(Stream, Dest: TStream; var Desc: TImageDescriptor;
var Interlaced, LocalColors, Corrupted: Boolean; var BitsPerPixel: Byte;
var ColorTable: TGIFColorTable);
var
CodeSize, BlockSize: Byte;
begin
Corrupted := False;
Stream.ReadBuffer(Desc, SizeOf(TImageDescriptor));
Interlaced := (Desc.PackedFields and ID_INTERLACED) <> 0;
if (Desc.PackedFields and ID_LOCAL_COLOR_TABLE) <> 0 then
begin
{ Local colors table follows }
BitsPerPixel := 1 + Desc.PackedFields and ID_COLOR_TABLE_SIZE;
LocalColors := True;
ColorTable.Count := 1 shl BitsPerPixel;
Stream.ReadBuffer(ColorTable.Colors[0],
ColorTable.Count * SizeOf(TGIFColorItem));
end
else begin
LocalColors := False;
FillChar(ColorTable, SizeOf(ColorTable), 0);
end;
Stream.ReadBuffer(CodeSize, 1);
Dest.Write(CodeSize, 1);
repeat
Stream.Read(BlockSize, 1);
if (Stream.Position + BlockSize) > Stream.Size then begin
Corrupted := True;
Exit; {!!?}
end;
Dest.Write(BlockSize, 1);
if (Stream.Position + BlockSize) > Stream.Size then begin
BlockSize := Stream.Size - Stream.Position;
Corrupted := True;
end;
if BlockSize > 0 then Dest.CopyFrom(Stream, BlockSize);
until (BlockSize = 0) or (Stream.Position >= Stream.Size);
end;
procedure FillRGBPalette(const ColorTable: TGIFColorTable;
var Colors: TRGBPalette);
var
I: Byte;
begin
FillChar(Colors, SizeOf(Colors), $80);
for I := 0 to ColorTable.Count - 1 do begin
Colors[I].rgbRed := ColorTable.Colors[I].Red;
Colors[I].rgbGreen := ColorTable.Colors[I].Green;
Colors[I].rgbBlue := ColorTable.Colors[I].Blue;
Colors[I].rgbReserved := 0;
end;
end;
function ReadCode(Stream: TStream; var Context: TReadContext): Longint;
var
RawCode: Longint;
ByteIndex: Longint;
Bytes: Byte;
BytesToLose: Longint;
begin
while (Context.Inx + Context.CodeSize > Context.Size) and
(Stream.Position < Stream.Size) do
begin
{ not enough bits in buffer - refill it }
{ Not very efficient, but infrequently called }
BytesToLose := Context.Inx shr 3;
{ Note biggest Code Size is 12 bits. And this can at worst span 3 Bytes }
Move(Context.Buf[Word(BytesToLose)], Context.Buf[0], 3);
Context.Inx := Context.Inx and 7;
Context.Size := Context.Size - (BytesToLose shl 3);
Stream.ReadBuffer(Bytes, 1);
if Bytes > 0 then
Stream.ReadBuffer(Context.Buf[Word(Context.Size shr 3)], Bytes);
Context.Size := Context.Size + (Bytes shl 3);
end;
ByteIndex := Context.Inx shr 3;
RawCode := Context.Buf[Word(ByteIndex)] +
(Word(Context.Buf[Word(ByteIndex + 1)]) shl 8);
if Context.CodeSize > 8 then
RawCode := RawCode + (Longint(Context.Buf[ByteIndex + 2]) shl 16);
RawCode := RawCode shr (Context.Inx and 7);
Context.Inx := Context.Inx + Byte(Context.CodeSize);
Result := RawCode and Context.ReadMask;
end;
procedure Output(Value: Byte; var Context: TOutputContext);
var
P: PByte;
begin
if (Context.Y >= Context.H) then Exit;
case Context.BitsPerPixel of
1: begin
P := HugeOffset(Context.CurrLineData, Context.X shr 3);
if (Context.X and $07 <> 0) then
P^ := P^ or Word(value shl (7 - (Word(Context.X and 7))))
else P^ := Byte(value shl 7);
end;
4: begin
P := HugeOffset(Context.CurrLineData, Context.X shr 1);
if (Context.X and 1 <> 0) then P^ := P^ or Value
else P^ := Byte(value shl 4);
end;
8: begin
P := HugeOffset(Context.CurrLineData, Context.X);
P^ := Value;
end;
end;
Inc(Context.X);
if Context.X < Context.W then Exit;
Context.X := 0;
if Context.Interlace then
Context.Y := InterlaceStep(Context.Y, Context.H, Context.Pass)
else Inc(Context.Y);
Context.CurrLineData := HugeOffset(Context.Data,
(Context.H - 1 - Context.Y) * Context.LineIdent);
end;
procedure ReadGIFData(Stream: TStream; const Header: TBitmapInfoHeader;
Interlaced, LoadCorrupt: Boolean; IntBitPerPixel: Byte; Data: Pointer;
var Corrupted: Boolean; ProgressProc: TProgressProc);
var
MinCodeSize, Temp: Byte;
MaxCode, BitMask, InitCodeSize: Longint;
ClearCode, EndingCode, FirstFreeCode, FreeCode: Word;
I, OutCount, Code: Longint;
CurCode, OldCode, InCode, FinalChar: Word;
Prefix, Suffix, OutCode: PIntCodeTable;
ReadCtxt: TReadContext;
OutCtxt: TOutputContext;
TableFull: Boolean;
begin
Corrupted := False;
OutCount := 0; OldCode := 0; FinalChar := 0;
TableFull := False;
Prefix := AllocMem(SizeOf(TIntCodeTable));
try
Suffix := AllocMem(SizeOf(TIntCodeTable));
try
OutCode := AllocMem(SizeOf(TIntCodeTable) + SizeOf(Word));
try
if Assigned(ProgressProc) then ProgressProc(psStarting, 0, '');
try
Stream.ReadBuffer(MinCodeSize, 1);
if (MinCodeSize < 2) or (MinCodeSize > 9) then begin
if LoadCorrupt then begin
Corrupted := True;
MinCodeSize := Max(2, Min(MinCodeSize, 9));
end
else GifError(LoadStr(SBadGIFCodeSize));
end;
{ Initial read context }
ReadCtxt.Inx := 0;
ReadCtxt.Size := 0;
ReadCtxt.CodeSize := MinCodeSize + 1;
ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1;
{ Initialise pixel-output context }
OutCtxt.X := 0; OutCtxt.Y := 0;
OutCtxt.Pass := 0;
OutCtxt.W := Header.biWidth;
OutCtxt.H := Header.biHeight;
OutCtxt.BitsPerPixel := Header.biBitCount;
OutCtxt.Interlace := Interlaced;
OutCtxt.LineIdent := ((Header.biWidth * Header.biBitCount + 31)
div 32) * 4;
OutCtxt.Data := Data;
OutCtxt.CurrLineData := HugeOffset(Data, (Header.biHeight - 1) *
OutCtxt.LineIdent);
BitMask := (1 shl IntBitPerPixel) - 1;
{ 2 ^ MinCodeSize accounts for all colours in file }
ClearCode := 1 shl MinCodeSize;
EndingCode := ClearCode + 1;
FreeCode := ClearCode + 2;
FirstFreeCode := FreeCode;
{ 2^ (MinCodeSize + 1) includes clear and eoi Code and space too }
InitCodeSize := ReadCtxt.CodeSize;
MaxCode := 1 shl ReadCtxt.CodeSize;
Code := ReadCode(Stream, ReadCtxt);
while (Code <> EndingCode) and (Code <> $FFFF) and
(OutCtxt.Y < OutCtxt.H) do
begin
if (Code = ClearCode) then begin
ReadCtxt.CodeSize := InitCodeSize;
MaxCode := 1 shl ReadCtxt.CodeSize;
ReadCtxt.ReadMask := MaxCode - 1;
FreeCode := FirstFreeCode;
Code := ReadCode(Stream, ReadCtxt);
CurCode := Code; OldCode := Code;
if (Code = $FFFF) then Break;
FinalChar := (CurCode and BitMask);
Output(Byte(FinalChar), OutCtxt);
TableFull := False;
end
else begin
CurCode := Code;
InCode := Code;
if CurCode >= FreeCode then begin
CurCode := OldCode;
OutCode^[OutCount] := FinalChar;
Inc(OutCount);
end;
while (CurCode > BitMask) do begin
if (OutCount > CODE_TABLE_SIZE) then begin
if LoadCorrupt then begin
CurCode := BitMask;
OutCount := 1;
Corrupted := True;
Break;
end
else GifError(LoadStr(SGIFDecodeError));
end;
OutCode^[OutCount] := Suffix^[CurCode];
Inc(OutCount);
CurCode := Prefix^[CurCode];
end;
if Corrupted then Break;
FinalChar := CurCode and BitMask;
OutCode^[OutCount] := FinalChar;
Inc(OutCount);
for I := OutCount - 1 downto 0 do
Output(Byte(OutCode^[I]), OutCtxt);
OutCount := 0;
{ Update dictionary }
if not TableFull then begin
Prefix^[FreeCode] := OldCode;
Suffix^[FreeCode] := FinalChar;
{ Advance to next free slot }
Inc(FreeCode);
if (FreeCode >= MaxCode) then begin
if (ReadCtxt.CodeSize < 12) then begin
Inc(ReadCtxt.CodeSize);
MaxCode := MaxCode shl 1;
ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1;
end
else TableFull := True;
end;
end;
OldCode := InCode;
end;
Code := ReadCode(Stream, ReadCtxt);
if Stream.Size > 0 then begin
Temp := Trunc(100.0 * (Stream.Position / Stream.Size));
if Assigned(ProgressProc) then ProgressProc(psRunning, Temp, '');
end;
end; { while }
if Code = $FFFF then GifError(ResStr(SReadError));
finally
if Assigned(ProgressProc) then begin
if ExceptObject = nil then ProgressProc(psEnding, 100, '')
else ProgressProc(psEnding, 0, Exception(ExceptObject).Message);
end;
end;
finally
FreeMem(OutCode, SizeOf(TIntCodeTable) + SizeOf(Word));
end;
finally
FreeMem(Suffix, SizeOf(TIntCodeTable));
end;
finally
FreeMem(Prefix, SizeOf(TIntCodeTable));
end;
end;
procedure WriteCode(Stream: TStream; Code: Longint;
var Context: TWriteContext);
var
BufIndex: Longint;
Bytes: Byte;
begin
BufIndex := Context.Inx shr 3;
Code := Code shl (Context.Inx and 7);
Context.Buf[BufIndex] := Context.Buf[BufIndex] or (Code);
Context.Buf[BufIndex + 1] := (Code shr 8);
Context.Buf[BufIndex + 2] := (Code shr 16);
Context.Inx := Context.Inx + Context.CodeSize;
if Context.Inx >= 255 * 8 then begin
{ Flush out full buffer }
Bytes := 255;
Stream.WriteBuffer(Bytes, 1);
Stream.WriteBuffer(Context.Buf, Bytes);
Move(Context.Buf[255], Context.Buf[0], 2);
FillChar(Context.Buf[2], 255, 0);
Context.Inx := Context.Inx - (255 * 8);
end;
end;
procedure FlushCode(Stream: TStream; var Context: TWriteContext);
var
Bytes: Byte;
begin
Bytes := (Context.Inx + 7) shr 3;
if Bytes > 0 then begin
Stream.WriteBuffer(Bytes, 1);
Stream.WriteBuffer(Context.Buf, Bytes);
end;
{ Data block terminator - a block of zero Size }
Bytes := 0;
Stream.WriteBuffer(Bytes, 1);
end;
procedure FillColorTable(var ColorTable: TGIFColorTable;
const Colors: TRGBPalette; Count: Integer);
var
I: Byte;
begin
FillChar(ColorTable, SizeOf(ColorTable), 0);
ColorTable.Count := Min(256, Count);
for I := 0 to ColorTable.Count - 1 do begin
ColorTable.Colors[I].Red := Colors[I].rgbRed;
ColorTable.Colors[I].Green := Colors[I].rgbGreen;
ColorTable.Colors[I].Blue := Colors[I].rgbBlue;
end;
end;
procedure WriteGIFData(Stream: TStream; var Header: TBitmapInfoHeader;
Interlaced: Boolean; Data: Pointer; ProgressProc: TProgressProc);
{ LZW encode data }
var
LineIdent: Longint;
MinCodeSize, Col, Temp: Byte;
InitCodeSize, X, Y: Longint;
Pass: Integer;
MaxCode: Longint; { 1 shl CodeSize }
ClearCode, EndingCode, LastCode, Tail: Longint;
I, HashValue: Longint;
LenString: Word;
Dict: PDictTable;
HashTable: TList;
PData: PByte;
WriteCtxt: TWriteContext;
begin
LineIdent := ((Header.biWidth * Header.biBitCount + 31) div 32) * 4;
Tail := 0; HashValue := 0;
Dict := AllocMem(SizeOf(TDictTable));
try
HashTable := TList.Create;
try
for I := 0 to HASH_TABLE_SIZE - 1 do HashTable.Add(nil);
{ Initialise encoder variables }
InitCodeSize := Header.biBitCount + 1;
if InitCodeSize = 2 then Inc(InitCodeSize);
MinCodeSize := InitCodeSize - 1;
Stream.WriteBuffer(MinCodeSize, 1);
ClearCode := 1 shl MinCodeSize;
EndingCode := ClearCode + 1;
LastCode := EndingCode;
MaxCode := 1 shl InitCodeSize;
LenString := 0;
{ Setup write context }
WriteCtxt.Inx := 0;
WriteCtxt.CodeSize := InitCodeSize;
FillChar(WriteCtxt.Buf, SizeOf(WriteCtxt.Buf), 0);
WriteCode(Stream, ClearCode, WriteCtxt);
for I := 0 to HASH_TABLE_SIZE - 1 do HashTable[I] := nil;
Data := HugeOffset(Data, (Header.biHeight - 1) * LineIdent);
Y := 0; Pass := 0;
if Assigned(ProgressProc) then ProgressProc(psStarting, 0, '');
try
while (Y < Header.biHeight) do begin
PData := HugeOffset(Data, -(Y * LineIdent));
for X := 0 to Header.biWidth - 1 do begin
case Header.biBitCount of
8: begin
Col := PData^;
PData := HugeOffset(PData, 1);
end;
4: begin
if X and 1 <> 0 then begin
Col := PData^ and $0F;
PData := HugeOffset(PData, 1);
end
else Col := PData^ shr 4;
end;
else { must be 1 }
begin
if X and 7 = 7 then begin
Col := PData^ and 1;
PData := HugeOffset(PData, 1);
end
else Col := (PData^ shr (7 - (X and $07))) and $01;
end;
end; { case }
Inc(LenString);
if LenString = 1 then begin
Tail := Col;
HashValue := InitHash(Col);
end
else begin
HashValue := HashValue * (Col + LenString + 4);
I := HashValue mod HASH_TABLE_SIZE;
HashValue := HashValue mod HASH_TABLE_SIZE;
while (HashTable[I] <> nil) and
((PImageDict(HashTable[I])^.Tail <> Tail) or
(PImageDict(HashTable[I])^.Col <> Col)) do
begin
Inc(I);
if (I >= HASH_TABLE_SIZE) then I := 0;
end;
if (HashTable[I] <> nil) then { Found in the strings table }
Tail := PImageDict(HashTable[I])^.Index
else begin
{ Not found }
WriteCode(Stream, Tail, WriteCtxt);
Inc(LastCode);
HashTable[I] := @Dict^[LastCode];
PImageDict(HashTable[I])^.Index := LastCode;
PImageDict(HashTable[I])^.Tail := Tail;
PImageDict(HashTable[I])^.Col := Col;
Tail := Col;
HashValue := InitHash(Col);
LenString := 1;
if (LastCode >= MaxCode) then begin
{ Next Code will be written longer }
MaxCode := MaxCode shl 1;
Inc(WriteCtxt.CodeSize);
end
else if (LastCode >= CODE_TABLE_SIZE - 2) then begin
{ Reset tables }
WriteCode(Stream, Tail, WriteCtxt);
WriteCode(Stream, ClearCode, WriteCtxt);
LenString := 0;
LastCode := EndingCode;
WriteCtxt.CodeSize := InitCodeSize;
MaxCode := 1 shl InitCodeSize;
for I := 0 to HASH_TABLE_SIZE - 1 do HashTable[I] := nil;
end;
end;
end;
end; { for X loop }
if Interlaced then Y := InterlaceStep(Y, Header.biHeight, Pass)
else Inc(Y);
Temp := Trunc(100.0 * (Y / Header.biHeight));
if Assigned(ProgressProc) then ProgressProc(psRunning, Temp, '');
end; { while Y loop }
WriteCode(Stream, Tail, WriteCtxt);
WriteCode(Stream, EndingCode, WriteCtxt);
FlushCode(Stream, WriteCtxt);
finally
if Assigned(ProgressProc) then begin
if ExceptObject = nil then ProgressProc(psEnding, 100, '')
else ProgressProc(psEnding, 0, Exception(ExceptObject).Message);
end;
end;
finally
HashTable.Free;
end;
finally
FreeMem(Dict, SizeOf(TDictTable));
end;
end;
{ TGIFItem }
destructor TGIFItem.Destroy;
begin
FImageData.Free;
inherited Destroy;
end;
procedure TGIFItem.FreeHandle;
begin
if FImageData <> nil then FImageData.SetSize(0);
end;
{ TGIFData }
constructor TGIFData.Create;
begin
inherited Create;
FComment := TStringList.Create;
end;
destructor TGIFData.Destroy;
begin
FComment.Free;
inherited Destroy;
end;
procedure TGIFData.FreeHandle;
begin
if FComment <> nil then FComment.Clear;
end;
{ TGIFFrame }
constructor TGIFFrame.Create(AOwner: TGIFImage);
begin
FOwner := AOwner;
inherited Create;
NewImage;
end;
destructor TGIFFrame.Destroy;
begin
FBitmap.Free;
FreeExtensions(FExtensions);
FImage.Release;
inherited Destroy;
end;
procedure TGIFFrame.SetAnimateInterval(Value: Word);
begin
if FAnimateInterval <> Value then begin
FAnimateInterval := Value;
if Value > 0 then FOwner.FVersion := gv89a;
FOwner.Changed(FOwner);
end;
end;
procedure TGIFFrame.SetDisposalMethod(Value: TDisposalMethod);
begin
if FDisposal <> Value then begin
FDisposal := Value;
if Value <> dmUndefined then FOwner.FVersion := gv89a;
FOwner.Changed(FOwner);
end;
end;
procedure TGIFFrame.SetTopLeft(const Value: TPoint);
begin
if (FTopLeft.X <> Value.X) or (FTopLeft.Y <> Value.Y) then begin
FTopLeft.X := Value.X;
FTopLeft.Y := Value.Y;
FOwner.FScreenWidth := Max(FOwner.FScreenWidth,
FImage.FSize.X + FTopLeft.X);
FOwner.FScreenHeight := Max(FOwner.FScreenHeight,
FImage.FSize.Y + FTopLeft.Y);
FOwner.Changed(FOwner);
end;
end;
procedure TGIFFrame.SetTransparentColor(Value: TColor);
begin
if FTransparentColor <> Value then begin
FTransparentColor := Value;
if Value <> clNone then FOwner.FVersion := gv89a;
FOwner.Changed(FOwner);
end;
end;
function TGIFFrame.GetBitmap: TBitmap;
var
Mem: TMemoryStream;
begin
Result := FBitmap;
if (Result = nil) or Result.Empty then begin
NewBitmap;
Result := FBitmap;
if Assigned(FImage.FImageData) then
try
Mem := TMemoryStream.Create;
try
SaveToBitmapStream(Mem);
FBitmap.LoadFromStream(Mem);
{$IFDEF RX_D3}
if not FBitmap.Monochrome then FBitmap.HandleType := bmDDB;
{$ENDIF}
finally
Mem.Free;
end;
except
raise;
end;
end;
end;
function TGIFFrame.GetHeight: Integer;
begin
if Assigned(FBitmap) or Assigned(FImage.FImageData) then
Result := Bitmap.Height
else Result := 0;
end;
function TGIFFrame.GetWidth: Integer;
begin
if Assigned(FBitmap) or Assigned(FImage.FImageData) then
Result := Bitmap.Width
else Result := 0;
end;
function TGIFFrame.GetColorCount: Integer;
begin
Result := FImage.FColormap.Count;
if (Result = 0) and Assigned(FBitmap) and (FBitmap.Palette <> 0) then
Result := PaletteEntries(FBitmap.Palette);
end;
procedure TGIFFrame.GrayscaleImage(ForceEncoding: Boolean);
var
Mem: TMemoryStream;
TransIndex: Integer;
begin
if not FGrayscale and (Assigned(FBitmap) or
Assigned(FImage.FImageData)) then
begin
if Assigned(FImage.FImageData) and (FImage.FColorMap.Count > 0) then begin
FBitmap.Free;
FBitmap := nil;
TransIndex := FindColorIndex(FImage.FColorMap, FTransparentColor);
GrayColorTable(FImage.FColorMap);
if TransIndex >= 0 then
FTransparentColor := ItemToRGB(FImage.FColorMap.Colors[TransIndex])
else FTransparentColor := clNone;
FGrayscale := True;
try
GetBitmap;
except
on EAbort do;
else raise;
end;
end
else begin
Mem := BitmapToMemoryStream(Bitmap, pf8bit, mmGrayscale);
try
FImage.Release;
FImage := TGIFItem.Create;
FImage.Reference;
if ForceEncoding then EncodeBitmapStream(Mem);
FGrayscale := True;
if FTransparentColor <> clNone then
FTransparentColor := GrayColor(FTransparentColor);
FBitmap.LoadFromStream(Mem);
finally
Mem.Free;
end;
end;
end;
end;
procedure TGIFFrame.Assign(Source: TPersistent);
var
AComment: TStrings;
begin
if Source = nil then begin
NewImage;
FBitmap.Free;
FBitmap := nil;
end
else if (Source is TGIFFrame) then begin
if Source <> Self then begin
FImage.Release;
FImage := TGIFFrame(Source).FImage;
if TGIFFrame(Source).FOwner <> FOwner then FLocalColors := True
else FLocalColors := TGIFFrame(Source).FLocalColors;
FImage.Reference;
FTopLeft := TGIFFrame(Source).FTopLeft;
FInterlaced := TGIFFrame(Source).FInterlaced;
if TGIFFrame(Source).FBitmap <> nil then begin
NewBitmap;
FBitmap.Assign(TGIFFrame(Source).FBitmap);
end;
FTransparentColor := TGIFFrame(Source).FTransparentColor;
FAnimateInterval := TGIFFrame(Source).FAnimateInterval;
FDisposal := TGIFFrame(Source).FDisposal;
FGrayscale := TGIFFrame(Source).FGrayscale;
FCorrupted := TGIFFrame(Source).FCorrupted;
AComment := TGIFFrame(Source).FindComment(False);
if (AComment <> nil) and (AComment.Count > 0) then
SetComment(AComment);
end;
end
else if Source is TGIFImage then begin
if (TGIFImage(Source).Count > 0) then begin
if (TGIFImage(Source).FrameIndex >= 0) then
Assign(TGIFImage(Source).Frames[TGIFImage(Source).FrameIndex])
else
Assign(TGIFImage(Source).Frames[0]);
end
else Assign(nil);
end
else if Source is TGraphic then begin
{ TBitmap, TJPEGImage... }
if TGraphic(Source).Empty then begin
Assign(nil);
Exit;
end;
NewImage;
NewBitmap;
try
FBitmap.Assign(Source);
if Source is TBitmap then
FBitmap.Monochrome := TBitmap(Source).Monochrome;
except
FBitmap.Canvas.Brush.Color := clFuchsia;
FBitmap.Width := TGraphic(Source).Width;
FBitmap.Height := TGraphic(Source).Height;
FBitmap.Canvas.Draw(0, 0, TGraphic(Source));
end;
{$IFDEF RX_D3}
if TGraphic(Source).Transparent then begin
if Source is TBitmap then
FTransparentColor := TBitmap(Source).TransparentColor
else FTransparentColor := GetNearestColor(FBitmap.Canvas.Handle,
ColorToRGB(FBitmap.Canvas.Brush.Color));
end;
{$ELSE}
if (Source is TIcon) or (Source is TMetafile) then
FTransparentColor := GetNearestColor(FBitmap.Canvas.Handle,
ColorToRGB(FBitmap.Canvas.Brush.Color));
{$ENDIF}
end
else inherited Assign(Source);
if FOwner <> nil then FOwner.UpdateScreenSize;
end;
procedure TGIFFrame.AssignTo(Dest: TPersistent);
begin
if (Dest is TGIFFrame) or (Dest is TGIFImage) then Dest.Assign(Self)
else if Dest is TGraphic then begin
Dest.Assign(Bitmap);
{$IFDEF RX_D3}
if (Dest is TBitmap) and (FTransparentColor <> clNone) then begin
TBitmap(Dest).TransparentColor := GetNearestColor(
TBitmap(Dest).Canvas.Handle, ColorToRGB(FTransparentColor));
TBitmap(Dest).Transparent := True;
end;
{$ENDIF}
end
else inherited AssignTo(Dest);
end;
procedure TGIFFrame.NewBitmap;
begin
FBitmap.Free;
FBitmap := TBitmap.Create;
end;
procedure TGIFFrame.NewImage;
begin
if FImage <> nil then FImage.Release;
FImage := TGIFItem.Create;
FImage.Reference;
FGrayscale := False;
FCorrupted := False;
FTransparentColor := clNone;
FTopLeft := Point(0, 0);
FInterlaced := False;
FLocalColors := False;
FAnimateInterval := 0;
FDisposal := dmUndefined;
end;
function TGIFFrame.FindComment(ForceCreate: Boolean): TStrings;
var
Ext: TExtension;
begin
Ext := FindExtension(FExtensions, etComment);
if (Ext = nil) and ForceCreate then begin
Ext := TExtension.Create;
try
Ext.FExtType := etComment;
if FExtensions = nil then FExtensions := TList.Create;
FExtensions.Add(Ext);
except
Ext.Free;
raise;
end;
end;
if (Ext <> nil) then begin
if (Ext.FData = nil) and ForceCreate then
Ext.FData := TStringList.Create;
Result := Ext.FData;
end
else Result := nil;
end;
function TGIFFrame.GetComment: TStrings;
begin
Result := FindComment(True);
end;
procedure TGIFFrame.SetComment(Value: TStrings);
begin
GetComment.Assign(Value);
end;
procedure TGIFFrame.UpdateExtensions;
var
Ext: TExtension;
I: Integer;
begin
Ext := FindExtension(FExtensions, etGraphic);
if (FAnimateInterval > 0) or (FTransparentColor <> clNone) or
(FDisposal <> dmUndefined) then
begin
if Ext = nil then begin
Ext := TExtension.Create;
Ext.FExtType := etGraphic;
if FExtensions = nil then FExtensions := TList.Create;
FExtensions.Add(Ext);
with Ext.FExtRec.GCE do begin
BlockSize := 4;
PackedFields := 0;
Terminator := 0;
end;
end;
end;
if Ext <> nil then
with Ext.FExtRec.GCE do begin
DelayTime := FAnimateInterval div 10;
I := FindColorIndex(FImage.FColorMap, FTransparentColor);
if I >= 0 then begin
TransparentColorIndex := I;
PackedFields := PackedFields or GCE_TRANSPARENT;
end
else PackedFields := PackedFields and not GCE_TRANSPARENT;
PackedFields := (PackedFields and not GCE_DISPOSAL_METHOD) or
(Ord(FDisposal) shl 2);
end;
if FExtensions <> nil then
for I := FExtensions.Count - 1 downto 0 do begin
Ext := TExtension(FExtensions[I]);
if (Ext <> nil) and (Ext.FExtType = etComment) and
((Ext.FData = nil) or (Ext.FData.Count = 0)) then
begin
Ext.Free;
FExtensions.Delete(I);
end;
end;
if (FExtensions <> nil) and (FExtensions.Count > 0) then
FOwner.FVersion := gv89a;
end;
procedure TGIFFrame.EncodeBitmapStream(Stream: TMemoryStream);
var
BI: PBitmapInfoHeader;
ColorCount, W, H: Integer;
Bits, Pal: Pointer;
begin
ColorCount := 0;
Stream.Position := 0;
BI := PBitmapInfoHeader(Longint(Stream.Memory) + SizeOf(TBitmapFileHeader));
W := BI^.biWidth; H := BI^.biHeight;
Pal := PRGBPalette(Longint(BI) + SizeOf(TBitmapInfoHeader));
Bits := Pointer(Longword(Stream.Memory) + PBitmapFileHeader(Stream.Memory)^.bfOffBits);
case BI^.biBitCount of
1: ColorCount := 2;
4: ColorCount := 16;
8: ColorCount := 256;
else GifError(LoadStr(SGIFEncodeError));
end;
FInterlaced := False;
FillColorTable(FImage.FColorMap, PRGBPalette(Pal)^, ColorCount);
if FImage.FImageData = nil then FImage.FImageData := TMemoryStream.Create
else FImage.FImageData.SetSize(0);
try
WriteGIFData(FImage.FImageData, BI^, FInterlaced, Bits, FOwner.DoProgress);
except
on EAbort do begin
NewImage; { OnProgress can raise EAbort to cancel image save }
raise;
end
else raise;
end;
FImage.FBitsPerPixel := 1;
while FImage.FColorMap.Count > 1 shl FImage.FBitsPerPixel do
Inc(FImage.FBitsPerPixel);
if FOwner.FImage.FColorMap.Count = 0 then begin
FOwner.FImage.FColorMap := FImage.FColorMap;
FOwner.FImage.FBitsPerPixel := FImage.FBitsPerPixel;
FLocalColors := False;
end
else FLocalColors := True;
FImage.FSize.X := W; FImage.FSize.Y := H;
FOwner.FScreenWidth := Max(FOwner.FScreenWidth, FImage.FSize.X + FTopLeft.X);
FOwner.FScreenHeight := Max(FOwner.FScreenHeight, FImage.FSize.Y + FTopLeft.Y);
end;
procedure TGIFFrame.EncodeRasterData;
var
Method: TMappingMethod;
Mem: TMemoryStream;
begin
if not Assigned(FBitmap) or FBitmap.Empty then GifError(LoadStr(SNoGIFData));
if not (GetBitmapPixelFormat(FBitmap) in [pf1bit, pf4bit, pf8bit]) then
begin
if FGrayscale then Method := mmGrayscale
else Method := DefaultMappingMethod;
Mem := BitmapToMemoryStream(FBitmap, pf8bit, Method);
if (Method = mmGrayscale) then FGrayscale := True;
end
else Mem := TMemoryStream.Create;
try
if Mem.Size = 0 then FBitmap.SaveToStream(Mem);
EncodeBitmapStream(Mem);
finally
Mem.Free;
end;
end;
procedure TGIFFrame.WriteImageDescriptor(Stream: TStream);
var
ImageDesc: TImageDescriptor;
begin
with ImageDesc do begin
PackedFields := 0;
if FLocalColors then begin
FImage.FBitsPerPixel := 1;
while FImage.FColorMap.Count > 1 shl FImage.FBitsPerPixel do
Inc(FImage.FBitsPerPixel);
PackedFields := (PackedFields or ID_LOCAL_COLOR_TABLE) +
(FImage.FBitsPerPixel - 1);
end;
if FInterlaced then PackedFields := PackedFields or ID_INTERLACED;
ImageLeftPos := FTopLeft.X;
ImageTopPos := FTopLeft.Y;
ImageWidth := FImage.FSize.X;
ImageHeight := FImage.FSize.Y;
end;
Stream.Write(ImageDesc, SizeOf(TImageDescriptor));
end;
procedure TGIFFrame.WriteLocalColorMap(Stream: TStream);
begin
if FLocalColors then
with FImage.FColorMap do
Stream.Write(Colors[0], Count * SizeOf(TGIFColorItem));
end;
procedure TGIFFrame.WriteRasterData(Stream: TStream);
begin
Stream.WriteBuffer(FImage.FImageData.Memory^, FImage.FImageData.Size);
end;
procedure TGIFFrame.SaveToBitmapStream(Stream: TMemoryStream);
function ConvertBitsPerPixel: TPixelFormat;
begin
Result := pfDevice;
case FImage.FBitsPerPixel of
1: Result := pf1bit;
2..4: Result := pf4bit;
5..8: Result := pf8bit;
else GifError(LoadStr(SWrongGIFColors));
end;
end;
var
HeaderSize: Longword;
Length: Longword;
BI: TBitmapInfoHeader;
BitFile: TBitmapFileHeader;
Colors: TRGBPalette;
Bits: Pointer;
Corrupt: Boolean;
begin
with BI do begin
biSize := Sizeof(TBitmapInfoHeader);
biWidth := FImage.FSize.X;
biHeight := FImage.FSize.Y;
biPlanes := 1;
biBitCount := 0;
case ConvertBitsPerPixel of
pf1bit: biBitCount := 1;
pf4bit: biBitCount := 4;
pf8bit: biBitCount := 8;
end;
biCompression := BI_RGB;
biSizeImage := (((biWidth * biBitCount + 31) div 32) * 4) * biHeight;
biXPelsPerMeter := 0;
biYPelsPerMeter := 0;
biClrUsed := 0;
biClrImportant := 0;
end;
HeaderSize := SizeOf(TBitmapFileHeader) + SizeOf(TBitmapInfoHeader) +
SizeOf(TRGBQuad) * (1 shl BI.biBitCount);
Length := HeaderSize + BI.biSizeImage;
Stream.SetSize(0);
Stream.Position := 0;
with BitFile do begin
bfType := $4D42; { BM }
bfSize := Length;
bfOffBits := HeaderSize;
end;
Stream.Write(BitFile, SizeOf(TBitmapFileHeader));
Stream.Write(BI, SizeOf(TBitmapInfoHeader));
FillRGBPalette(FImage.FColorMap, Colors);
Stream.Write(Colors, SizeOf(TRGBQuad) * (1 shl BI.biBitCount));
Bits := AllocMemo(BI.biSizeImage);
try
ZeroMemory(Bits, BI.biSizeImage);
FImage.FImageData.Position := 0;
ReadGIFData(FImage.FImageData, BI, FInterlaced, GIFLoadCorrupted,
FImage.FBitsPerPixel, Bits, Corrupt, FOwner.DoProgress);
FCorrupted := FCorrupted or Corrupt;
Stream.WriteBuffer(Bits^, BI.biSizeImage);
finally
FreeMemo(Bits);
end;
Stream.Position := 0;
end;
procedure TGIFFrame.LoadFromStream(Stream: TStream);
var
ImageDesc: TImageDescriptor;
I, TransIndex: Integer;
begin
FImage.FImageData := TMemoryStream.Create;
try
ReadImageStream(Stream, FImage.FImageData, ImageDesc, FInterlaced,
FLocalColors, FCorrupted, FImage.FBitsPerPixel, FImage.FColorMap);
if FCorrupted and not GIFLoadCorrupted then GifError(ResStr(SReadError));
FImage.FImageData.Position := 0;
with ImageDesc do begin
if ImageHeight = 0 then ImageHeight := FOwner.FScreenHeight;
if ImageWidth = 0 then ImageWidth := FOwner.FScreenWidth;
FTopLeft := Point(ImageLeftPos, ImageTopPos);
FImage.FSize := Point(ImageWidth, ImageHeight);
FImage.FPackedFields := PackedFields;
end;
if not FLocalColors then FImage.FColorMap := FOwner.FImage.FColorMap;
FAnimateInterval := 0;
if FExtensions <> nil then begin
for I := 0 to FExtensions.Count - 1 do
with TExtension(FExtensions[I]) do
if FExtType = etGraphic then begin
if (FExtRec.GCE.PackedFields and GCE_TRANSPARENT) <> 0 then
begin
TransIndex := FExtRec.GCE.TransparentColorIndex;
if FImage.FColorMap.Count > TransIndex then
FTransparentColor := ItemToRGB(FImage.FColorMap.Colors[TransIndex]);
end
else FTransparentColor := clNone;
FAnimateInterval := Max(FExtRec.GCE.DelayTime * 10,
FAnimateInterval);
FDisposal := TDisposalMethod((FExtRec.GCE.PackedFields and
GCE_DISPOSAL_METHOD) shr 2);
end;
end;
except
FImage.FImageData.Free;
FImage.FImageData := nil;
raise;
end;
end;
procedure TGIFFrame.Draw(ACanvas: TCanvas; const ARect: TRect;
Transparent: Boolean);
begin
if (FTransparentColor <> clNone) and Transparent then begin
with ARect do
StretchBitmapRectTransparent(ACanvas, Left, Top, Right - Left,
Bottom - Top, Bounds(0, 0, Bitmap.Width, Bitmap.Height), Bitmap,
FTransparentColor);
end
else ACanvas.StretchDraw(ARect, Bitmap);
end;
{ TGIFImage }
constructor TGIFImage.Create;
begin
inherited Create;
NewImage;
{$IFDEF RX_D3}
inherited SetTransparent(True);
{$ENDIF}
end;
destructor TGIFImage.Destroy;
begin
OnChange := nil;
FImage.Release;
ClearItems;
FItems.Free;
inherited Destroy;
end;
procedure TGIFImage.Clear;
begin
Assign(nil);
end;
procedure TGIFImage.ClearItems;
begin
if FItems <> nil then
while FItems.Count > 0 do begin
TObject(FItems[0]).Free;
FItems.Delete(0);
end;
end;
procedure TGIFImage.Assign(Source: TPersistent);
var
I: Integer;
AFrame: TGIFFrame;
begin
if (Source = nil) then begin
NewImage;
Changed(Self);
end
else if (Source is TGIFImage) and (Source <> Self) then begin
FImage.Release;
FImage := TGIFImage(Source).FImage;
FImage.Reference;
FVersion := TGIFImage(Source).FVersion;
FBackgroundColor := TGIFImage(Source).FBackgroundColor;
FRepeatCount := TGIFImage(Source).FRepeatCount;
FLooping := TGIFImage(Source).FLooping;
FCorrupted := TGIFImage(Source).FCorrupted;
if FItems = nil then FItems := TList.Create
else ClearItems;
with TGIFImage(Source) do begin
for I := 0 to FItems.Count - 1 do begin
AFrame := TGIFFrame.Create(Self);
try
AFrame.FImage.FBitsPerPixel :=
TGIFFrame(FItems[I]).FImage.FBitsPerPixel;
AFrame.Assign(TGIFFrame(FItems[I]));
AFrame.FLocalColors := TGIFFrame(FItems[I]).FLocalColors;
Self.FItems.Add(AFrame);
except
AFrame.Free;
raise;
end;
end;
Self.FScreenWidth := FScreenWidth;
Self.FScreenHeight := FScreenHeight;
end;
FFrameIndex := TGIFImage(Source).FFrameIndex;
Changed(Self);
end
else if Source is TGIFFrame then begin
NewImage;
with TGIFFrame(Source).FOwner.FImage do begin
FImage.FAspectRatio := FAspectRatio;
FImage.FBitsPerPixel := FBitsPerPixel;
FImage.FColorResBits := FColorResBits;
Move(FColorMap, FImage.FColorMap, SizeOf(FColorMap));
end;
FFrameIndex := FItems.Add(TGIFFrame.Create(Self));
TGIFFrame(FItems[FFrameIndex]).Assign(Source);
if FVersion = gvUnknown then FVersion := gv87a;
Changed(Self);
end
else if Source is TBitmap then begin
NewImage;
AddFrame(TBitmap(Source));
Changed(Self);
end
else if Source is TAnimatedCursorImage then begin
NewImage;
FBackgroundColor := clWindow;
with TAnimatedCursorImage(Source) do begin
for I := 0 to IconCount - 1 do begin
AddFrame(TIcon(Icons[I]));
Self.Frames[FrameIndex].FAnimateInterval :=
Longint(Frames[I].JiffRate * 100) div 6;
end;
end;
Changed(Self);
end
else inherited Assign(Source);
end;
procedure TGIFImage.AssignTo(Dest: TPersistent);
begin
if Dest is TGIFImage then Dest.Assign(Self)
else if Dest is TGraphic then begin
if Empty then
Dest.Assign(nil)
else if FFrameIndex >= 0 then
TGIFFrame(FItems[FFrameIndex]).AssignTo(Dest)
else Dest.Assign(Bitmap);
end
else inherited AssignTo(Dest);
end;
procedure TGIFImage.Draw(ACanvas: TCanvas; const ARect: TRect);
begin
if FFrameIndex >= 0 then
TGIFFrame(FItems[FFrameIndex]).Draw(ACanvas, ARect, Self.Transparent);
end;
function TGIFImage.GetBackgroundColor: TColor;
begin
Result := FBackgroundColor;
end;
procedure TGIFImage.SetBackgroundColor(Value: TColor);
begin
if Value <> FBackgroundColor then begin
FBackgroundColor := Value;
Changed(Self);
end;
end;
procedure TGIFImage.SetLooping(Value: Boolean);
begin
if Value <> FLooping then begin
FLooping := Value;
Changed(Self);
end;
end;
procedure TGIFImage.SetRepeatCount(Value: Word);
begin
if Min(Value, MAX_LOOP_COUNT) <> FRepeatCount then begin
FRepeatCount := Min(Value, MAX_LOOP_COUNT);
Changed(Self);
end;
end;
function TGIFImage.GetPixelFormat: TPixelFormat;
var
I: Integer;
begin
Result := pfDevice;
if not Empty then begin
Result := ColorsToPixelFormat(FImage.FColorMap.Count);
for I := 0 to FItems.Count - 1 do begin
if (Frames[I].FImage.FImageData = nil) or
(Frames[I].FImage.FImageData.Size = 0) then
begin
if Assigned(Frames[I].FBitmap) then
Result := TPixelFormat(Max(Ord(Result),
Ord(GetBitmapPixelFormat(Frames[I].FBitmap))))
else Result := TPixelFormat(Max(Ord(Result), Ord(pfDevice)));
end
else if Frames[I].FLocalColors then
Result := TPixelFormat(Max(Ord(Result),
Ord(ColorsToPixelFormat(Frames[I].FImage.FColorMap.Count))));
end;
end;
end;
function TGIFImage.GetCorrupted: Boolean;
var
I: Integer;
begin
Result := FCorrupted;
if not Result then
for I := 0 to FItems.Count - 1 do
if Frames[I].Corrupted then begin
Result := True;
Exit;
end;
end;
function TGIFImage.GetTransparentColor: TColor;
begin
if (FItems.Count > 0) and (FFrameIndex >= 0) then
Result := TGIFFrame(FItems[FFrameIndex]).FTransparentColor
else Result := clNone;
end;
function TGIFImage.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TGIFImage.GetFrame(Index: Integer): TGIFFrame;
begin
Result := TGIFFrame(FItems[Index]);
end;
procedure TGIFImage.SetFrameIndex(Value: Integer);
begin
Value := Min(FItems.Count - 1, Max(-1, Value));
if FFrameIndex <> Value then begin
FFrameIndex := Value;
{$IFDEF RX_D3}
PaletteModified := True;
{$ENDIF}
Changed(Self);
end;
end;
{$IFDEF WIN32}
function TGIFImage.Equals(Graphic: TGraphic): Boolean;
begin
Result := (Graphic is TGIFImage) and
(FImage = TGIFImage(Graphic).FImage);
end;
{$ENDIF}
function TGIFImage.GetBitmap: TBitmap;
var
Bmp: TBitmap;
begin
if (FItems.Count > 0) then begin
if (FFrameIndex >= 0) and (FFrameIndex < FItems.Count) then
Result := TGIFFrame(FItems[FFrameIndex]).Bitmap
else Result := TGIFFrame(FItems[0]).Bitmap
end
else begin
FFrameIndex := 0;
Bmp := TBitmap.Create;
try
Bmp.Handle := 0;
Assign(Bmp);
Result := TGIFFrame(FItems[FFrameIndex]).Bitmap;
finally
Bmp.Free;
end;
end;
end;
function TGIFImage.GetGlobalColorCount: Integer;
begin
Result := FImage.FColormap.Count;
end;
function TGIFImage.GetEmpty: Boolean;
var
I: Integer;
begin
I := Max(FFrameIndex, 0);
Result := (FItems.Count = 0) or
((TGIFFrame(FItems[I]).FBitmap = nil) and
((TGIFFrame(FItems[I]).FImage.FImageData = nil) or
(TGIFFrame(FItems[I]).FImage.FImageData.Size = 0)));
end;
function TGIFImage.GetPalette: HPalette;
begin
if FItems.Count > 0 then Result := Bitmap.Palette
else Result := 0;
end;
function TGIFImage.GetTransparent: Boolean;
var
I: Integer;
begin
{$IFDEF RX_D3}
if inherited GetTransparent then
{$ENDIF}
for I := 0 to FItems.Count - 1 do
if Frames[I].TransparentColor <> clNone then begin
Result := True;
Exit;
end;
Result := False;
end;
function TGIFImage.GetHeight: Integer;
begin
if not Empty and (FFrameIndex >= 0) and (FFrameIndex < Count) then
Result := TGIFFrame(FItems[FFrameIndex]).Bitmap.Height
else Result := 0;
end;
function TGIFImage.GetWidth: Integer;
begin
if not Empty and (FFrameIndex >= 0) and (FFrameIndex < Count) then
Result := TGIFFrame(FItems[FFrameIndex]).Bitmap.Width
else Result := 0;
end;
function TGIFImage.GetScreenWidth: Integer;
begin
if Empty then Result := 0
else Result := FScreenWidth;
end;
function TGIFImage.GetScreenHeight: Integer;
begin
if Empty then Result := 0
else Result := FScreenHeight;
end;
procedure TGIFImage.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE);
var
Bmp: TBitmap;
Stream: TMemoryStream;
Size: Longint;
Buffer: Pointer;
Data: THandle;
begin
{ !! check for gif clipboard Data, mime type image/gif }
Data := GetClipboardData(CF_GIF);
if Data <> 0 then begin
Buffer := GlobalLock(Data);
try
Stream := TMemoryStream.Create;
try
Stream.Write(Buffer^, GlobalSize(Data));
Stream.Position := 0;
Stream.Read(Size, SizeOf(Size));
ReadStream(Size, Stream, False);
if Count > 0 then begin
FFrameIndex := 0;
AData := GetClipboardData(CF_BITMAP);
if AData <> 0 then begin
Frames[0].NewBitmap;
Frames[0].FBitmap.LoadFromClipboardFormat(CF_BITMAP,
AData, APalette);
end;
end;
finally
Stream.Free;
end;
finally
GlobalUnlock(Data);
end;
end
else begin
Bmp := TBitmap.Create;
try
Bmp.LoadFromClipboardFormat(AFormat, AData, APalette);
Assign(Bmp);
finally
Bmp.Free;
end;
end;
end;
procedure TGIFImage.LoadFromStream(Stream: TStream);
begin
ReadStream(Stream.Size - Stream.Position, Stream, True);
end;
procedure TGIFImage.LoadFromResourceName(Instance: THandle; const ResName: string;
ResType: PChar);
var
Stream: TStream;
begin
Stream := TResourceStream.Create(Instance, ResName, ResType);
try
ReadStream(Stream.Size - Stream.Position, Stream, True);
finally
Stream.Free;
end;
end;
procedure TGIFImage.LoadFromResourceID(Instance: THandle; ResID: Integer;
ResType: PChar);
var
Stream: TStream;
begin
Stream := TResourceStream.CreateFromID(Instance, ResID, ResType);
try
ReadStream(Stream.Size - Stream.Position, Stream, True);
finally
Stream.Free;
end;
end;
procedure TGIFImage.UpdateScreenSize;
var
I: Integer;
begin
FScreenWidth := 0;
FScreenHeight := 0;
for I := 0 to FItems.Count - 1 do
if Frames[I] <> nil then begin
FScreenWidth := Max(FScreenWidth, Frames[I].Width +
Frames[I].FTopLeft.X);
FScreenHeight := Max(FScreenHeight, Frames[I].Height +
Frames[I].FTopLeft.Y);
end;
end;
function TGIFImage.AddFrame(Value: TGraphic): Integer;
begin
FFrameIndex := FItems.Add(TGIFFrame.Create(Self));
TGIFFrame(FItems[FFrameIndex]).Assign(Value);
if FVersion = gvUnknown then FVersion := gv87a;
if FItems.Count > 1 then FVersion := gv89a;
Result := FFrameIndex;
end;
procedure TGIFImage.DeleteFrame(Index: Integer);
begin
Frames[Index].Free;
FItems.Delete(Index);
UpdateScreenSize;
if FFrameIndex >= FItems.Count then Dec(FFrameIndex);
Changed(Self);
end;
procedure TGIFImage.MoveFrame(CurIndex, NewIndex: Integer);
begin
FItems.Move(CurIndex, NewIndex);
FFrameIndex := NewIndex;
Changed(Self);
end;
procedure TGIFImage.NewImage;
begin
if FImage <> nil then FImage.Release;
FImage := TGIFData.Create;
FImage.Reference;
if FItems = nil then FItems := TList.Create;
ClearItems;
FCorrupted := False;
FFrameIndex := -1;
FBackgroundColor := clNone;
FRepeatCount := 1;
FLooping := False;
FVersion := gvUnknown;
end;
procedure TGIFImage.UniqueImage;
var
Temp: TGIFData;
begin
if FImage = nil then NewImage
else if FImage.RefCount > 1 then begin
Temp := TGIFData.Create;
with Temp do
try
FComment.Assign(FImage.FComment);
FAspectRatio := FImage.FAspectRatio;
FBitsPerPixel := FImage.FBitsPerPixel;
FColorResBits := FImage.FColorResBits;
FColorMap := FImage.FColorMap;
except
Temp.Free;
raise;
end;
FImage.Release;
FImage := Temp;
FImage.Reference;
end;
end;
function TGIFImage.GetComment: TStrings;
begin
Result := FImage.FComment;
end;
procedure TGIFImage.SetComment(Value: TStrings);
begin
UniqueImage;
FImage.FComment.Assign(Value);
end;
procedure TGIFImage.DecodeAllFrames;
var
FrameNo, I: Integer;
begin
for FrameNo := 0 to FItems.Count - 1 do
try
TGIFFrame(FItems[FrameNo]).GetBitmap;
except
on EAbort do begin { OnProgress can raise EAbort to cancel image load }
for I := FItems.Count - 1 downto FrameNo do begin
TObject(FItems[I]).Free;
FItems.Delete(I);
end;
FCorrupted := True;
Break;
end;
else raise;
end;
end;
procedure TGIFImage.EncodeFrames(ReverseDecode: Boolean);
var
FrameNo: Integer;
begin
for FrameNo := 0 to FItems.Count - 1 do
with TGIFFrame(FItems[FrameNo]) do begin
if (FImage.FImageData = nil) or (FImage.FImageData.Size = 0) then
begin
FImage.FImageData.Free;
FImage.FImageData := nil;
EncodeRasterData;
if ReverseDecode and (FBitmap.Palette = 0) then begin
FBitmap.Free;
FBitmap := nil;
try
GetBitmap;
except
on EAbort do; { OnProgress can raise EAbort to cancel encoding }
else raise;
end;
end;
end;
UpdateExtensions;
end;
end;
procedure TGIFImage.EncodeAllFrames;
begin
EncodeFrames(True);
end;
procedure TGIFImage.ReadData(Stream: TStream);
var
Size: Longint;
begin
Stream.Read(Size, SizeOf(Size));
ReadStream(Size, Stream, True);
end;
procedure TGIFImage.ReadSignature(Stream: TStream);
var
I: TGIFVersion;
S: string[3];
begin
FVersion := gvUnknown;
SetLength(S, 3);
Stream.Read(S[1], 3);
if CompareText(GIFSignature, S) <> 0 then GifError(LoadStr(SGIFVersion));
SetLength(S, 3);
Stream.Read(S[1], 3);
for I := Low(TGIFVersion) to High(TGIFVersion) do
if CompareText(S, StrPas(GIFVersionStr[I])) = 0 then begin
FVersion := I;
Break;
end;
if FVersion = gvUnknown then GifError(LoadStr(SGIFVersion));
end;
procedure TGIFImage.ReadStream(Size: Longint; Stream: TStream;
ForceDecode: Boolean);
var
SeparatorChar: Char;
NewItem: TGIFFrame;
Extensions: TList;
ScreenDesc: TScreenDescriptor;
Data: TMemoryStream;
procedure ReadScreenDescriptor(Stream: TStream);
begin
Stream.Read(ScreenDesc, SizeOf(ScreenDesc));
FScreenWidth := ScreenDesc.ScreenWidth;
FScreenHeight := ScreenDesc.ScreenHeight;
with FImage do begin
FAspectRatio := ScreenDesc.AspectRatio;
FBitsPerPixel := 1 + (ScreenDesc.PackedFields and
LSD_COLOR_TABLE_SIZE);
FColorResBits := 1 + (ScreenDesc.PackedFields and
LSD_COLOR_RESOLUTION) shr 4;
end;
end;
procedure ReadGlobalColorMap(Stream: TStream);
begin
if (ScreenDesc.PackedFields and LSD_GLOBAL_COLOR_TABLE) <> 0 then
with FImage.FColorMap do begin
Count := 1 shl FImage.FBitsPerPixel;
Stream.Read(Colors[0], Count * SizeOf(TGIFColorItem));
if Count > ScreenDesc.BackgroundColorIndex then
FBackgroundColor := ItemToRGB(Colors[ScreenDesc.BackgroundColorIndex]);
end;
end;
function ReadDataBlock(Stream: TStream): TStrings;
var
BlockSize: Byte;
S: string;
begin
Result := TStringlist.Create;
try
repeat
Stream.Read(BlockSize, SizeOf(Byte));
if BlockSize <> 0 then begin
SetLength(S, BlockSize);
Stream.Read(S[1], BlockSize);
Result.Add(S);
end;
until (BlockSize = 0) or (Stream.Position >= Stream.Size);
except
Result.Free;
raise;
end;
end;
function ReadExtension(Stream: TStream): TExtension;
var
ExtensionLabel: Byte;
begin
Result := TExtension.Create;
try
Stream.Read(ExtensionLabel, SizeOf(Byte));
with Result do
if ExtensionLabel = ExtLabels[etGraphic] then begin
{ graphic control extension }
FExtType := etGraphic;
Stream.Read(FExtRec.GCE, SizeOf(TGraphicControlExtension));
end
else if ExtensionLabel = ExtLabels[etComment] then begin
{ comment extension }
FExtType := etComment;
FData := ReadDataBlock(Stream);
end
else if ExtensionLabel = ExtLabels[etPlainText] then begin
{ plain text extension }
FExtType := etPlainText;
Stream.Read(FExtRec.PTE, SizeOf(TPlainTextExtension));
FData := ReadDataBlock(Stream);
end
else if ExtensionLabel = ExtLabels[etApplication] then begin
{ application extension }
FExtType := etApplication;
Stream.Read(FExtRec.APPE, SizeOf(TAppExtension));
FData := ReadDataBlock(Stream);
end
else GifError(Format(LoadStr(SUnrecognizedGIFExt), [ExtensionLabel]));
except
Result.Free;
raise;
end;
end;
function ReadExtensionBlock(Stream: TStream; var SeparatorChar: Char): TList;
var
NewExt: TExtension;
begin
Result := nil;
try
while SeparatorChar = CHR_EXT_INTRODUCER do begin
NewExt := ReadExtension(Stream);
if (NewExt.FExtType = etPlainText) then begin
{ plain text data blocks are not supported,
clear all previous readed extensions }
FreeExtensions(Result);
Result := nil;
end;
if (NewExt.FExtType in [etPlainText, etApplication]) then begin
{ check for loop extension }
if NewExt.IsLoopExtension then begin
FLooping := True;
FRepeatCount := Min(MakeWord(Byte(NewExt.FData[0][2]),
Byte(NewExt.FData[0][3])), MAX_LOOP_COUNT);
end;
{ not supported yet, must be ignored }
NewExt.Free;
end
else begin
if Result = nil then Result := TList.Create;
Result.Add(NewExt);
end;
if Stream.Size > Stream.Position then
Stream.Read(SeparatorChar, SizeOf(Byte))
else SeparatorChar := CHR_TRAILER;
end;
if (Result <> nil) and (Result.Count = 0) then begin
Result.Free;
Result := nil;
end;
except
if Result <> nil then Result.Free;
raise;
end;
end;
var
I: Integer;
Ext: TExtension;
begin
NewImage;
with FImage do begin
Data := TMemoryStream.Create;
try
TMemoryStream(Data).SetSize(Size);
Stream.ReadBuffer(Data.Memory^, Size);
if Size > 0 then begin
Data.Position := 0;
ReadSignature(Data);
ReadScreenDescriptor(Data);
ReadGlobalColorMap(Data);
Data.Read(SeparatorChar, SizeOf(Byte));
while not (SeparatorChar in [CHR_TRAILER, #0]) and not
(Data.Position >= Data.Size) do
begin
Extensions := ReadExtensionBlock(Data, SeparatorChar);
if SeparatorChar = CHR_IMAGE_SEPARATOR then
try
NewItem := TGIFFrame.Create(Self);
try
if FImage.FColorMap.Count > 0 then
NewItem.FImage.FBitsPerPixel :=
ColorsToBits(FImage.FColorMap.Count);
NewItem.FExtensions := Extensions;
Extensions := nil;
NewItem.LoadFromStream(Data);
FItems.Add(NewItem);
except
NewItem.Free;
raise;
end;
if not (Data.Position >= Data.Size) then begin
Data.Read(SeparatorChar, SizeOf(Byte));
while (SeparatorChar = #0) and (Data.Position < Data.Size) do
Data.Read(SeparatorChar, SizeOf(Byte));
end
else SeparatorChar := CHR_TRAILER;
if not (SeparatorChar in [CHR_EXT_INTRODUCER,
CHR_IMAGE_SEPARATOR, CHR_TRAILER]) then
begin
SeparatorChar := #0;
{GifError(LoadStr(SGIFDecodeError));}
end;
except
FreeExtensions(Extensions);
raise;
end
else if (FComment.Count = 0) and (Extensions <> nil) then begin
try
{ trailig extensions }
for I := 0 to Extensions.Count - 1 do begin
Ext := TExtension(Extensions[I]);
if (Ext <> nil) and (Ext.FExtType = etComment) then begin
if FComment.Count > 0 then
FComment.Add(#13#10#13#10);
FComment.AddStrings(Ext.FData);
end;
end;
finally
FreeExtensions(Extensions);
end;
end
else if not (SeparatorChar in [CHR_TRAILER, #0]) then
GifError(ResStr(SReadError));
end;
end;
finally
Data.Free;
end;
end;
if Count > 0 then begin
FFrameIndex := 0;
if ForceDecode then
try
GetBitmap; { force bitmap creation }
except
Frames[0].Free;
FItems.Delete(0);
raise;
end;
end;
{$IFDEF RX_D3}
PaletteModified := True;
{$ENDIF}
Changed(Self);
end;
procedure TGIFImage.SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
var APalette: HPALETTE);
var
Stream: TMemoryStream;
Data: THandle;
Buffer: Pointer;
I: Integer;
begin
{ !! check for gif clipboard format, mime type image/gif }
if FItems.Count = 0 then Exit;
Frames[0].Bitmap.SaveToClipboardFormat(AFormat, AData, APalette);
for I := 0 to FItems.Count - 1 do
with Frames[I] do begin
if (FImage.FImageData = nil) or (FImage.FImageData.Size = 0) then
Exit;
end;
Stream := TMemoryStream.Create;
try
WriteStream(Stream, True);
Stream.Position := 0;
Data := GlobalAlloc(HeapAllocFlags, Stream.Size);
try
if Data <> 0 then begin
Buffer := GlobalLock(Data);
try
Stream.Read(Buffer^, Stream.Size);
SetClipboardData(CF_GIF, Data);
finally
GlobalUnlock(Data);
end;
end;
except
GlobalFree(Data);
raise;
end;
finally
Stream.Free;
end;
end;
procedure TGIFImage.WriteData(Stream: TStream);
begin
WriteStream(Stream, True);
end;
procedure TGIFImage.SetHeight(Value: Integer);
begin
GifError(LoadStr(SChangeGIFSize));
end;
procedure TGIFImage.SetWidth(Value: Integer);
begin
GifError(LoadStr(SChangeGIFSize));
end;
procedure TGIFImage.WriteStream(Stream: TStream; WriteSize: Boolean);
var
Separator: Char;
Temp: Byte;
FrameNo: Integer;
Frame: TGIFFrame;
Mem: TMemoryStream;
Size: Longint;
StrList: TStringList;
procedure WriteSignature(Stream: TStream);
var
Header: TGIFHeader;
begin
Header.Signature := GIFSignature;
Move(GIFVersionStr[FVersion][0], Header.Version[0], 3);
Stream.Write(Header, SizeOf(TGIFHeader));
end;
procedure WriteScreenDescriptor(Stream: TStream);
var
ColorResBits: Byte;
ScreenDesc: TScreenDescriptor;
I: Integer;
begin
UpdateScreenSize;
with ScreenDesc do begin
ScreenWidth := Self.FScreenWidth;
ScreenHeight := Self.FScreenHeight;
AspectRatio := FImage.FAspectRatio;
PackedFields := 0;
BackgroundColorIndex := 0;
if FImage.FColorMap.Count > 0 then begin
PackedFields := PackedFields or LSD_GLOBAL_COLOR_TABLE;
ColorResBits := ColorsToBits(FImage.FColorMap.Count);
if FBackgroundColor <> clNone then
for I := 0 to FImage.FColorMap.Count - 1 do
if ColorToRGB(FBackgroundColor) =
ItemToRGB(FImage.FColorMap.Colors[I]) then
begin
BackgroundColorIndex := I;
Break;
end;
PackedFields := PackedFields + ((ColorResBits - 1) shl 4) +
(FImage.FBitsPerPixel - 1);
end;
end;
Stream.Write(ScreenDesc, SizeOf(ScreenDesc));
end;
procedure WriteDataBlock(Stream: TStream; Data: TStrings);
var
I: Integer;
S: string;
BlockSize: Byte;
begin
for I := 0 to Data.Count - 1 do begin
S := Data[I];
BlockSize := Min(Length(S), 255);
if BlockSize > 0 then begin
Stream.Write(BlockSize, SizeOf(Byte));
Stream.Write(S[1], BlockSize);
end;
end;
BlockSize := 0;
Stream.Write(BlockSize, SizeOf(Byte));
end;
procedure WriteExtensionBlock(Stream: TStream; Extensions: TList);
var
I: Integer;
Ext: TExtension;
ExtensionLabel: Byte;
SeparateChar: Char;
begin
SeparateChar := CHR_EXT_INTRODUCER;
for I := 0 to Extensions.Count - 1 do begin
Ext := TExtension(Extensions[I]);
if Ext <> nil then begin
Stream.Write(SeparateChar, SizeOf(Byte));
ExtensionLabel := ExtLabels[Ext.FExtType];
Stream.Write(ExtensionLabel, SizeOf(Byte));
case Ext.FExtType of
etGraphic:
begin
Stream.Write(Ext.FExtRec.GCE, SizeOf(TGraphicControlExtension));
end;
etComment: WriteDataBlock(Stream, Ext.FData);
etPlainText:
begin
Stream.Write(Ext.FExtRec.PTE, SizeOf(TPlainTextExtension));
WriteDataBlock(Stream, Ext.FData);
end;
etApplication:
begin
Stream.Write(Ext.FExtRec.APPE, SizeOf(TAppExtension));
WriteDataBlock(Stream, Ext.FData);
end;
end;
end;
end;
end;
begin
if FItems.Count = 0 then GifError(LoadStr(SNoGIFData));
EncodeFrames(False);
Mem := TMemoryStream.Create;
try
if FImage.FComment.Count > 0 then FVersion := gv89a;
WriteSignature(Mem);
WriteScreenDescriptor(Mem);
if FImage.FColorMap.Count > 0 then begin
with FImage.FColorMap do
Mem.Write(Colors[0], Count * SizeOf(TGIFColorItem));
end;
if FLooping and (FItems.Count > 1) then begin
{ write looping extension }
Separator := CHR_EXT_INTRODUCER;
Mem.Write(Separator, SizeOf(Byte));
Temp := ExtLabels[etApplication];
Mem.Write(Temp, SizeOf(Byte));
Temp := SizeOf(TAppExtension) - SizeOf(Byte);
Mem.Write(Temp, SizeOf(Byte));
Mem.Write(LoopExtNS[1], Temp);
StrList := TStringList.Create;
try
StrList.Add(Char(AE_LOOPING) + Char(LoByte(FRepeatCount)) +
Char(HiByte(FRepeatCount)));
WriteDataBlock(Mem, StrList);
finally
StrList.Free;
end;
end;
Separator := CHR_IMAGE_SEPARATOR;
for FrameNo := 0 to FItems.Count - 1 do begin
Frame := TGIFFrame(FItems[FrameNo]);
if Frame.FExtensions <> nil then
WriteExtensionBlock(Mem, Frame.FExtensions);
Mem.Write(Separator, SizeOf(Byte));
Frame.WriteImageDescriptor(Mem);
Frame.WriteLocalColorMap(Mem);
Frame.WriteRasterData(Mem);
end;
if FImage.FComment.Count > 0 then begin
Separator := CHR_EXT_INTRODUCER;
Mem.Write(Separator, SizeOf(Byte));
Temp := ExtLabels[etComment];
Mem.Write(Temp, SizeOf(Byte));
WriteDataBlock(Mem, FImage.FComment);
end;
Separator := CHR_TRAILER;
Mem.Write(Separator, SizeOf(Byte));
Size := Mem.Size;
if WriteSize then Stream.Write(Size, SizeOf(Size));
Stream.Write(Mem.Memory^, Size);
finally
Mem.Free;
end;
end;
procedure TGIFImage.Grayscale(ForceEncoding: Boolean);
var
I: Integer;
begin
if FItems.Count = 0 then GifError(LoadStr(SNoGIFData));
for I := 0 to FItems.Count - 1 do
Frames[I].GrayscaleImage(ForceEncoding);
if FBackgroundColor <> clNone then begin
if FImage.FColorMap.Count > 0 then begin
I := FindColorIndex(FImage.FColorMap, FBackgroundColor);
GrayColorTable(FImage.FColorMap);
if I >= 0 then
FBackgroundColor := ItemToRGB(FImage.FColorMap.Colors[I])
else FBackgroundColor := GrayColor(FBackgroundColor);
end
else FBackgroundColor := GrayColor(FBackgroundColor);
end;
{$IFDEF RX_D3}
PaletteModified := True;
{$ENDIF}
Changed(Self);
end;
procedure TGIFImage.SaveToStream(Stream: TStream);
begin
WriteStream(Stream, False);
end;
procedure TGIFImage.DoProgress(Stage: TProgressStage; PercentDone: Byte;
const Msg: string);
begin
Progress(Self, Stage, PercentDone, False, Rect(0, 0, 0, 0), Msg);
end;
{$IFNDEF RX_D3}
procedure TGIFImage.Progress(Sender: TObject; Stage: TProgressStage;
PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string);
begin
if Assigned(FOnProgress) then
FOnProgress(Sender, Stage, PercentDone, RedrawNow, R, Msg);
end;
{$ENDIF}
initialization
CF_GIF := RegisterClipboardFormat('GIF Image');
RegisterClasses([TGIFFrame, TGIFImage]);
{$IFDEF USE_RX_GIF}
TPicture.RegisterFileFormat('gif', LoadStr(SGIFImage), TGIFImage);
TPicture.RegisterClipboardFormat(CF_GIF, TGIFImage);
{$IFDEF RX_D3}
finalization
TPicture.UnRegisterGraphicClass(TGIFImage);
{$ENDIF}
{$ENDIF}
end.