Rxgif.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:81k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 1997, 1998 Master-Bank          }
  6. {                                                       }
  7. {*******************************************************}
  8. unit RxGIF;
  9. interface
  10. {$I RX.INC}
  11. uses Windows, RTLConsts, SysUtils, Classes, Graphics, RxGraph;
  12. const
  13.   RT_GIF = 'GIF'; { GIF Resource Type }
  14. type
  15. {$IFNDEF RX_D3}
  16.   TProgressStage = (psStarting, psRunning, psEnding);
  17.   TProgressEvent = procedure (Sender: TObject; Stage: TProgressStage;
  18.     PercentDone: Byte; RedrawNow: Boolean; const R: TRect;
  19.     const Msg: string) of object;
  20. { TSharedImage }
  21.   TSharedImage = class
  22.   private
  23.     FRefCount: Integer;
  24.   protected
  25.     procedure Reference;
  26.     procedure Release;
  27.     procedure FreeHandle; virtual; abstract;
  28.     property RefCount: Integer read FRefCount;
  29.   end;
  30. {$ENDIF RX_D3}
  31.   TGIFVersion = (gvUnknown, gv87a, gv89a);
  32.   TGIFBits = 1..8;
  33.   TDisposalMethod = (dmUndefined, dmLeave, dmRestoreBackground,
  34.     dmRestorePrevious, dmReserved4, dmReserved5, dmReserved6, dmReserved7);
  35.   TGIFColorItem = packed record
  36.     Red, Green, Blue: Byte;
  37.   end;
  38.   TGIFColorTable = packed record
  39.     Count: Integer;
  40.     Colors: packed array[Byte] of TGIFColorItem;
  41.   end;
  42.   TGIFFrame = class;
  43.   TGIFData = class;
  44.   TGIFItem = class;
  45. { TGIFImage }
  46.   TGIFImage = class(TGraphic)
  47.   private
  48.     FImage: TGIFData;
  49.     FVersion: TGIFVersion;
  50.     FItems: TList;
  51.     FFrameIndex: Integer;
  52.     FScreenWidth: Word;
  53.     FScreenHeight: Word;
  54.     FBackgroundColor: TColor;
  55.     FLooping: Boolean;
  56.     FCorrupted: Boolean;
  57.     FRepeatCount: Word;
  58. {$IFNDEF RX_D3}
  59.     FOnProgress: TProgressEvent;
  60. {$ENDIF}
  61.     function GetBitmap: TBitmap;
  62.     function GetCount: Integer;
  63.     function GetComment: TStrings;
  64.     function GetScreenWidth: Integer;
  65.     function GetScreenHeight: Integer;
  66.     function GetGlobalColorCount: Integer;
  67.     procedure UpdateScreenSize;
  68.     procedure SetComment(Value: TStrings);
  69.     function GetFrame(Index: Integer): TGIFFrame;
  70.     procedure SetFrameIndex(Value: Integer);
  71.     procedure SetBackgroundColor(Value: TColor);
  72.     procedure SetLooping(Value: Boolean);
  73.     procedure SetRepeatCount(Value: Word);
  74.     procedure ReadSignature(Stream: TStream);
  75.     procedure DoProgress(Stage: TProgressStage; PercentDone: Byte;
  76.       const Msg: string);
  77.     function GetCorrupted: Boolean;
  78.     function GetTransparentColor: TColor;
  79.     function GetBackgroundColor: TColor;
  80.     function GetPixelFormat: TPixelFormat;
  81.     procedure EncodeFrames(ReverseDecode: Boolean);
  82.     procedure ReadStream(Size: Longint; Stream: TStream; ForceDecode: Boolean);
  83.     procedure WriteStream(Stream: TStream; WriteSize: Boolean);
  84.   protected
  85.     procedure AssignTo(Dest: TPersistent); override;
  86.     procedure Draw(ACanvas: TCanvas; const ARect: TRect); override;
  87. {$IFDEF WIN32}
  88.     function Equals(Graphic: TGraphic): Boolean; override;
  89. {$ENDIF}
  90.     function GetEmpty: Boolean; override;
  91.     function GetHeight: Integer; override;
  92.     function GetWidth: Integer; override;
  93.     function GetPalette: HPALETTE; {$IFDEF RX_D3} override; {$ENDIF}
  94.     function GetTransparent: Boolean; {$IFDEF RX_D3} override; {$ENDIF}
  95.     procedure ClearItems;
  96.     procedure NewImage;
  97.     procedure UniqueImage;
  98. {$IFNDEF RX_D3}
  99.     procedure Progress(Sender: TObject; Stage: TProgressStage;
  100.       PercentDone: Byte; RedrawNow: Boolean; const R: TRect;
  101.       const Msg: string); dynamic;
  102. {$ENDIF}
  103.     procedure ReadData(Stream: TStream); override;
  104.     procedure SetHeight(Value: Integer); override;
  105.     procedure SetWidth(Value: Integer); override;
  106.     procedure WriteData(Stream: TStream); override;
  107.     property Bitmap: TBitmap read GetBitmap;   { volatile }
  108.   public
  109.     constructor Create; override;
  110.     destructor Destroy; override;
  111.     procedure Clear;
  112.     procedure DecodeAllFrames;
  113.     procedure EncodeAllFrames;
  114.     procedure Assign(Source: TPersistent); override;
  115.     procedure LoadFromStream(Stream: TStream); override;
  116.     procedure SaveToStream(Stream: TStream); override;
  117.     procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
  118.       APalette: HPALETTE); override;
  119.     procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
  120.       var APalette: HPALETTE); override;
  121.     procedure LoadFromResourceName(Instance: THandle; const ResName: string;
  122.       ResType: PChar);
  123.     procedure LoadFromResourceID(Instance: THandle; ResID: Integer;
  124.       ResType: PChar);
  125.     function AddFrame(Value: TGraphic): Integer; virtual;
  126.     procedure DeleteFrame(Index: Integer);
  127.     procedure MoveFrame(CurIndex, NewIndex: Integer);
  128.     procedure Grayscale(ForceEncoding: Boolean);
  129.     property BackgroundColor: TColor read GetBackgroundColor write SetBackgroundColor;
  130.     property Comment: TStrings read GetComment write SetComment;
  131.     property Corrupted: Boolean read GetCorrupted;
  132.     property Count: Integer read GetCount;
  133.     property Frames[Index: Integer]: TGIFFrame read GetFrame; default;
  134.     property FrameIndex: Integer read FFrameIndex write SetFrameIndex;
  135.     property GlobalColorCount: Integer read GetGlobalColorCount;
  136.     property Looping: Boolean read FLooping write SetLooping;
  137.     property PixelFormat: TPixelFormat read GetPixelFormat;
  138.     property RepeatCount: Word read FRepeatCount write SetRepeatCount;
  139.     property ScreenWidth: Integer read GetScreenWidth;
  140.     property ScreenHeight: Integer read GetScreenHeight;
  141.     property TransparentColor: TColor read GetTransparentColor;
  142.     property Version: TGIFVersion read FVersion;
  143. {$IFNDEF RX_D3}
  144.     property Palette: HPALETTE read GetPalette;
  145.     property Transparent: Boolean read GetTransparent;
  146.     property OnProgress: TProgressEvent read FOnProgress write FOnProgress;
  147. {$ENDIF}
  148.   end;
  149. { TGIFFrame }
  150.   TGIFFrame = class(TPersistent)
  151.   private
  152.     FOwner: TGIFImage;
  153.     FBitmap: TBitmap;
  154.     FImage: TGIFItem;
  155.     FExtensions: TList;
  156.     FTopLeft: TPoint;
  157.     FInterlaced: Boolean;
  158.     FCorrupted: Boolean;
  159.     FGrayscale: Boolean;
  160.     FTransparentColor: TColor;
  161.     FAnimateInterval: Word;
  162.     FDisposal: TDisposalMethod;
  163.     FLocalColors: Boolean;
  164.     function GetBitmap: TBitmap;
  165.     function GetHeight: Integer;
  166.     function GetWidth: Integer;
  167.     function GetColorCount: Integer;
  168.     function FindComment(ForceCreate: Boolean): TStrings;
  169.     function GetComment: TStrings;
  170.     procedure SetComment(Value: TStrings);
  171.     procedure SetTransparentColor(Value: TColor);
  172.     procedure SetDisposalMethod(Value: TDisposalMethod);
  173.     procedure SetAnimateInterval(Value: Word);
  174.     procedure SetTopLeft(const Value: TPoint);
  175.     procedure NewBitmap;
  176.     procedure NewImage;
  177.     procedure SaveToBitmapStream(Stream: TMemoryStream);
  178.     procedure EncodeBitmapStream(Stream: TMemoryStream);
  179.     procedure EncodeRasterData;
  180.     procedure UpdateExtensions;
  181.     procedure WriteImageDescriptor(Stream: TStream);
  182.     procedure WriteLocalColorMap(Stream: TStream);
  183.     procedure WriteRasterData(Stream: TStream);
  184.   protected
  185.     constructor Create(AOwner: TGIFImage); virtual;
  186.     procedure LoadFromStream(Stream: TStream);
  187.     procedure AssignTo(Dest: TPersistent); override;
  188.     procedure GrayscaleImage(ForceEncoding: Boolean);
  189.   public
  190.     destructor Destroy; override;
  191.     procedure Assign(Source: TPersistent); override;
  192.     procedure Draw(ACanvas: TCanvas; const ARect: TRect;
  193.       Transparent: Boolean);
  194.     property AnimateInterval: Word read FAnimateInterval write SetAnimateInterval;
  195.     property Bitmap: TBitmap read GetBitmap; { volatile }
  196.     property ColorCount: Integer read GetColorCount;
  197.     property Comment: TStrings read GetComment write SetComment;
  198.     property DisposalMethod: TDisposalMethod read FDisposal write SetDisposalMethod;
  199.     property Interlaced: Boolean read FInterlaced;
  200.     property Corrupted: Boolean read FCorrupted;
  201.     property TransparentColor: TColor read FTransparentColor write SetTransparentColor;
  202.     property Origin: TPoint read FTopLeft write SetTopLeft;
  203.     property Height: Integer read GetHeight;
  204.     property Width: Integer read GetWidth;
  205.   end;
  206. { TGIFData }
  207.   TGIFData = class(TSharedImage)
  208.   private
  209.     FComment: TStrings;
  210.     FAspectRatio: Byte;
  211.     FBitsPerPixel: Byte;
  212.     FColorResBits: Byte;
  213.     FColorMap: TGIFColorTable;
  214.   protected
  215.     procedure FreeHandle; override;
  216.   public
  217.     constructor Create;
  218.     destructor Destroy; override;
  219.   end;
  220. { TGIFItem }
  221.   TGIFItem = class(TSharedImage)
  222.   private
  223.     FImageData: TMemoryStream;
  224.     FSize: TPoint;
  225.     FPackedFields: Byte;
  226.     FBitsPerPixel: Byte;
  227.     FColorMap: TGIFColorTable;
  228.   protected
  229.     procedure FreeHandle; override;
  230.   public
  231.     destructor Destroy; override;
  232.   end;
  233. { Clipboard format for GIF image }
  234. var
  235.   CF_GIF: Word;
  236. { Load incomplete or corrupted images without exceptions }
  237. const
  238.   GIFLoadCorrupted: Boolean = True;
  239. function GIFVersionName(Version: TGIFVersion): string;
  240. procedure rxgif_dummy;
  241. implementation
  242. uses Consts, {$IFNDEF WIN32} Str16, {$ENDIF} VclUtils, AniFile, RxConst,
  243.   MaxMin, RxGConst;
  244. {$R-}
  245. procedure rxgif_dummy;
  246. begin
  247. end;
  248. procedure GifError(const Msg: string);
  249. {$IFDEF WIN32}
  250.   function ReturnAddr: Pointer;
  251.   asm
  252.           MOV     EAX,[EBP+4]
  253.   end;
  254. {$ELSE}
  255.   function ReturnAddr: Pointer; assembler;
  256.   asm
  257.           MOV     AX,[BP].Word[2]
  258.           MOV     DX,[BP].Word[4]
  259.   end;
  260. {$ENDIF}
  261. begin
  262.   raise EInvalidGraphicOperation.Create(Msg) at ReturnAddr;
  263. end;
  264. {$IFNDEF RX_D3}
  265. { TSharedImage }
  266. procedure TSharedImage.Reference;
  267. begin
  268.   Inc(FRefCount);
  269. end;
  270. procedure TSharedImage.Release;
  271. begin
  272.   if Pointer(Self) <> nil then begin
  273.     Dec(FRefCount);
  274.     if FRefCount = 0 then begin
  275.       FreeHandle;
  276.       Free;
  277.     end;
  278.   end;
  279. end;
  280. {$ENDIF}
  281. const
  282.   GIFSignature = 'GIF';
  283.   GIFVersionStr: array[TGIFVersion] of PChar = (#0#0#0, '87a', '89a');
  284. function GIFVersionName(Version: TGIFVersion): string;
  285. begin
  286.   Result := StrPas(GIFVersionStr[Version]);
  287. end;
  288. const
  289.   CODE_TABLE_SIZE = 4096;
  290. {$IFDEF WIN32}
  291.   HASH_TABLE_SIZE = 17777;
  292. {$ELSE}
  293.   HASH_TABLE_SIZE = MaxListSize - $10;
  294. {$ENDIF}
  295.   MAX_LOOP_COUNT  = 30000;
  296.   CHR_EXT_INTRODUCER    = '!';
  297.   CHR_IMAGE_SEPARATOR   = ',';
  298.   CHR_TRAILER           = ';';  { indicates the end of the GIF Data stream }
  299. { Image descriptor bit masks }
  300.   ID_LOCAL_COLOR_TABLE  = $80;  { set if a local color table follows }
  301.   ID_INTERLACED         = $40;  { set if image is interlaced }
  302.   ID_SORT               = $20;  { set if color table is sorted }
  303.   ID_RESERVED           = $0C;  { reserved - must be set to $00 }
  304.   ID_COLOR_TABLE_SIZE   = $07;  { Size of color table as above }
  305. { Logical screen descriptor packed field masks }
  306.   LSD_GLOBAL_COLOR_TABLE = $80; { set if global color table follows L.S.D. }
  307.   LSD_COLOR_RESOLUTION   = $70; { Color resolution - 3 bits }
  308.   LSD_SORT               = $08; { set if global color table is sorted - 1 bit }
  309.   LSD_COLOR_TABLE_SIZE   = $07; { Size of global color table - 3 bits }
  310.                                 { Actual Size = 2^value+1    - value is 3 bits }
  311. { Graphic control extension packed field masks }
  312.   GCE_TRANSPARENT     = $01; { whether a transparency Index is given }
  313.   GCE_USER_INPUT      = $02; { whether or not user input is expected }
  314.   GCE_DISPOSAL_METHOD = $1C; { the way in which the graphic is to be treated after being displayed }
  315.   GCE_RESERVED        = $E0; { reserved - must be set to $00 }
  316. { Application extension }
  317.   AE_LOOPING          = $01; { looping Netscape extension }
  318.   GIFColors: array[TGIFBits] of Word = (2, 4, 8, 16, 32, 64, 128, 256);
  319. function ColorsToBits(ColorCount: Word): Byte; near;
  320. var
  321.   I: TGIFBits;
  322. begin
  323.   Result := 0;
  324.   for I := Low(TGIFBits) to High(TGIFBits) do
  325.     if ColorCount = GIFColors[I] then begin
  326.       Result := I;
  327.       Exit;
  328.     end;
  329.   GifError(LoadStr(SWrongGIFColors));
  330. end;
  331. function ColorsToPixelFormat(Colors: Word): TPixelFormat;
  332. begin
  333.   if Colors <= 2 then Result := pf1bit
  334.   else if Colors <= 16 then Result := pf4bit
  335.   else if Colors <= 256 then Result := pf8bit
  336.   else Result := pf24bit;
  337. end;
  338. function ItemToRGB(Item: TGIFColorItem): Longint; near;
  339. begin
  340.   with Item do Result := RGB(Red, Green, Blue);
  341. end;
  342. function GrayColor(Color: TColor): TColor;
  343. var
  344.   Index: Integer;
  345. begin
  346.   Index := Byte(Longint(Word(GetRValue(Color)) * 77 +
  347.     Word(GetGValue(Color)) * 150 + Word(GetBValue(Color)) * 29) shr 8);
  348.   Result := RGB(Index, Index, Index);
  349. end;
  350. procedure GrayColorTable(var ColorTable: TGIFColorTable);
  351. var
  352.   I: Byte;
  353.   Index: Integer;
  354. begin
  355.   for I := 0 to ColorTable.Count - 1 do begin
  356.     with ColorTable.Colors[I] do begin
  357.       Index := Byte(Longint(Word(Red) * 77 + Word(Green) * 150 +
  358.         Word(Blue) * 29) shr 8);
  359.       Red := Index;
  360.       Green := Index;
  361.       Blue := Index;
  362.     end;
  363.   end;
  364. end;
  365. function FindColorIndex(const ColorTable: TGIFColorTable;
  366.   Color: TColor): Integer;
  367. begin
  368.   if (Color <> clNone) then
  369.     for Result := 0 to ColorTable.Count - 1 do
  370.       if ItemToRGB(ColorTable.Colors[Result]) = ColorToRGB(Color) then Exit;
  371.   Result := -1;
  372. end;
  373. { The following types and function declarations are used to call into
  374.   functions of the GIF implementation of the GIF image
  375.   compression/decompression standard. }
  376. type
  377.   TGIFHeader = packed record
  378.     Signature: array[0..2] of Char; { contains 'GIF' }
  379.     Version: array[0..2] of Char;   { '87a' or '89a' }
  380.   end;
  381.   TScreenDescriptor = packed record
  382.     ScreenWidth: Word;            { logical screen width }
  383.     ScreenHeight: Word;           { logical screen height }
  384.     PackedFields: Byte;
  385.     BackgroundColorIndex: Byte;   { Index to global color table }
  386.     AspectRatio: Byte;            { actual ratio = (AspectRatio + 15) / 64 }
  387.   end;
  388.   TImageDescriptor = packed record
  389.     ImageLeftPos: Word;   { column in pixels in respect to left of logical screen }
  390.     ImageTopPos: Word;    { row in pixels in respect to top of logical screen }
  391.     ImageWidth: Word;     { width of image in pixels }
  392.     ImageHeight: Word;    { height of image in pixels }
  393.     PackedFields: Byte;
  394.   end;
  395. { GIF Extensions support }
  396. type
  397.   TExtensionType = (etGraphic, etPlainText, etApplication, etComment);
  398. const
  399.   ExtLabels: array[TExtensionType] of Byte = ($F9, $01, $FF, $FE);
  400.   LoopExtNS: string[11] = 'NETSCAPE2.0';
  401.   LoopExtAN: string[11] = 'ANIMEXTS1.0';
  402. type
  403.   TGraphicControlExtension = packed record
  404.     BlockSize: Byte; { should be 4 }
  405.     PackedFields: Byte;
  406.     DelayTime: Word; { in centiseconds }
  407.     TransparentColorIndex: Byte;
  408.     Terminator: Byte;
  409.   end;
  410.   TPlainTextExtension = packed record
  411.     BlockSize: Byte; { should be 12 }
  412.     Left, Top, Width, Height: Word;
  413.     CellWidth, CellHeight: Byte;
  414.     FGColorIndex, BGColorIndex: Byte;
  415.   end;
  416.   TAppExtension = packed record
  417.     BlockSize: Byte; { should be 11 }
  418.     AppId: array[1..8] of Byte;
  419.     Authentication: array[1..3] of Byte;
  420.   end;
  421.   TExtensionRecord = packed record
  422.     case ExtensionType: TExtensionType of
  423.       etGraphic: (GCE: TGraphicControlExtension);
  424.       etPlainText: (PTE: TPlainTextExtension);
  425.       etApplication: (APPE: TAppExtension);
  426.   end;
  427. { TExtension }
  428.   TExtension = class(TPersistent)
  429.   private
  430.     FExtType: TExtensionType;
  431.     FData: TStrings;
  432.     FExtRec: TExtensionRecord;
  433.   public
  434.     destructor Destroy; override;
  435.     procedure Assign(Source: TPersistent); override;
  436.     function IsLoopExtension: Boolean;
  437.   end;
  438. destructor TExtension.Destroy;
  439. begin
  440.   FData.Free;
  441.   inherited Destroy;
  442. end;
  443. procedure TExtension.Assign(Source: TPersistent);
  444. begin
  445.   if (Source <> nil) and (Source is TExtension) then begin
  446.     FExtType := TExtension(Source).FExtType;
  447.     FExtRec := TExtension(Source).FExtRec;
  448.     if TExtension(Source).FData <> nil then begin
  449.       if FData = nil then FData := TStringList.Create;
  450.       FData.Assign(TExtension(Source).FData);
  451.     end;
  452.   end
  453.   else inherited Assign(Source);
  454. end;
  455. function TExtension.IsLoopExtension: Boolean;
  456. begin
  457.   Result := (FExtType = etApplication) and (FData.Count > 0) and
  458.     (CompareMem(@FExtRec.APPE.AppId, @LoopExtNS[1], FExtRec.APPE.BlockSize) or
  459.     CompareMem(@FExtRec.APPE.AppId, @LoopExtAN[1], FExtRec.APPE.BlockSize)) and
  460.     (Length(FData[0]) >= 3) and (Byte(FData[0][1]) = AE_LOOPING);
  461. end;
  462. procedure FreeExtensions(Extensions: TList); near;
  463. begin
  464.   if Extensions <> nil then begin
  465.     while Extensions.Count > 0 do begin
  466.       TObject(Extensions[0]).Free;
  467.       Extensions.Delete(0);
  468.     end;
  469.     Extensions.Free;
  470.   end;
  471. end;
  472. function FindExtension(Extensions: TList; ExtType: TExtensionType): TExtension;
  473. var
  474.   I: Integer;
  475. begin
  476.   if Extensions <> nil then
  477.     for I := Extensions.Count - 1 downto 0 do begin
  478.       Result := TExtension(Extensions[I]);
  479.       if (Result <> nil) and (Result.FExtType = ExtType) then Exit;
  480.     end;
  481.   Result := nil;
  482. end;
  483. {
  484. function CopyExtensions(Source: TList): TList; near;
  485. var
  486.   I: Integer;
  487.   Ext: TExtension;
  488. begin
  489.   Result := TList.Create;
  490.   try
  491.     for I := 0 to Source.Count - 1 do
  492.       if (Source[I] <> nil) and (TObject(Source[I]) is TExtension) then begin
  493.         Ext := TExtension.Create;
  494.         try
  495.           Ext.Assign(Source[I]);
  496.           Result.Add(Ext);
  497.         except
  498.           Ext.Free;
  499.           raise;
  500.         end;
  501.       end;
  502.   except
  503.     Result.Free;
  504.     raise;
  505.   end;
  506. end;
  507. }
  508. type
  509.   TProgressProc = procedure (Stage: TProgressStage; PercentDone: Byte;
  510.     const Msg: string) of object;
  511. { GIF reading/writing routines
  512.   Procedures to read and write GIF files, GIF-decoding and encoding
  513.   based on freeware C source code of GBM package by Andy Key
  514.   (nyangau@interalpha.co.uk). The home page of GBM author is
  515.   at http://www.interalpha.net/customer/nyangau/. }
  516. type
  517.   PIntCodeTable = ^TIntCodeTable;
  518.   TIntCodeTable = array[0..CODE_TABLE_SIZE - 1] of Word;
  519.   PReadContext = ^TReadContext;
  520.   TReadContext = record
  521.     Inx, Size: Longint;
  522.     Buf: array[0..255 + 4] of Byte;
  523.     CodeSize: Longint;
  524.     ReadMask: Longint;
  525.   end;
  526.   PWriteContext = ^TWriteContext;
  527.   TWriteContext = record
  528.     Inx: Longint;
  529.     CodeSize: Longint;
  530.     Buf: array[0..255 + 4] of Byte;
  531.   end;
  532.   TOutputContext = record
  533.     W, H, X, Y: Longint;
  534.     BitsPerPixel, Pass: Integer;
  535.     Interlace: Boolean;
  536.     LineIdent: Longint;
  537.     Data, CurrLineData: Pointer;
  538.   end;
  539.   PImageDict = ^TImageDict;
  540.   TImageDict = record
  541.     Tail, Index: Word;
  542.     Col: Byte;
  543.   end;
  544.   PDictTable = ^TDictTable;
  545.   TDictTable = array[0..CODE_TABLE_SIZE - 1] of TImageDict;
  546.   PRGBPalette = ^TRGBPalette;
  547.   TRGBPalette = array [Byte] of TRGBQuad;
  548. function InitHash(P: Longint): Longint;
  549. begin
  550.   Result := (P + 3) * 301;
  551. end;
  552. function InterlaceStep(Y, Height: Integer; var Pass: Integer): Integer;
  553. begin
  554.   Result := Y;
  555.   case Pass of
  556.     0, 1: Inc(Result, 8);
  557.     2: Inc(Result, 4);
  558.     3: Inc(Result, 2);
  559.   end;
  560.   if Result >= Height then begin
  561.     if Pass = 0 then begin
  562.       Pass := 1; Result := 4;
  563.       if (Result < Height) then Exit;
  564.     end;
  565.     if Pass = 1 then begin
  566.       Pass := 2; Result := 2;
  567.       if (Result < Height) then Exit;
  568.     end;
  569.     if Pass = 2 then begin
  570.       Pass := 3; Result := 1;
  571.     end;
  572.   end;
  573. end;
  574. procedure ReadImageStream(Stream, Dest: TStream; var Desc: TImageDescriptor;
  575.   var Interlaced, LocalColors, Corrupted: Boolean; var BitsPerPixel: Byte;
  576.   var ColorTable: TGIFColorTable);
  577. var
  578.   CodeSize, BlockSize: Byte;
  579. begin
  580.   Corrupted := False;
  581.   Stream.ReadBuffer(Desc, SizeOf(TImageDescriptor));
  582.   Interlaced := (Desc.PackedFields and ID_INTERLACED) <> 0;
  583.   if (Desc.PackedFields and ID_LOCAL_COLOR_TABLE) <> 0 then
  584.   begin
  585.     { Local colors table follows }
  586.     BitsPerPixel := 1 + Desc.PackedFields and ID_COLOR_TABLE_SIZE;
  587.     LocalColors := True;
  588.     ColorTable.Count := 1 shl BitsPerPixel;
  589.     Stream.ReadBuffer(ColorTable.Colors[0],
  590.       ColorTable.Count * SizeOf(TGIFColorItem));
  591.   end
  592.   else begin
  593.     LocalColors := False;
  594.     FillChar(ColorTable, SizeOf(ColorTable), 0);
  595.   end;
  596.   Stream.ReadBuffer(CodeSize, 1);
  597.   Dest.Write(CodeSize, 1);
  598.   repeat
  599.     Stream.Read(BlockSize, 1);
  600.     if (Stream.Position + BlockSize) > Stream.Size then begin
  601.       Corrupted := True;
  602.       Exit; {!!?}
  603.     end;
  604.     Dest.Write(BlockSize, 1);
  605.     if (Stream.Position + BlockSize) > Stream.Size then begin
  606.       BlockSize := Stream.Size - Stream.Position;
  607.       Corrupted := True;
  608.     end;
  609.     if BlockSize > 0 then Dest.CopyFrom(Stream, BlockSize);
  610.   until (BlockSize = 0) or (Stream.Position >= Stream.Size);
  611. end;
  612. procedure FillRGBPalette(const ColorTable: TGIFColorTable;
  613.   var Colors: TRGBPalette);
  614. var
  615.   I: Byte;
  616. begin
  617.   FillChar(Colors, SizeOf(Colors), $80);
  618.   for I := 0 to ColorTable.Count - 1 do begin
  619.     Colors[I].rgbRed := ColorTable.Colors[I].Red;
  620.     Colors[I].rgbGreen := ColorTable.Colors[I].Green;
  621.     Colors[I].rgbBlue := ColorTable.Colors[I].Blue;
  622.     Colors[I].rgbReserved := 0;
  623.   end;
  624. end;
  625. function ReadCode(Stream: TStream; var Context: TReadContext): Longint;
  626. var
  627.   RawCode: Longint;
  628.   ByteIndex: Longint;
  629.   Bytes: Byte;
  630.   BytesToLose: Longint;
  631. begin
  632.   while (Context.Inx + Context.CodeSize > Context.Size) and
  633.     (Stream.Position < Stream.Size) do
  634.   begin
  635.     { not enough bits in buffer - refill it }
  636.     { Not very efficient, but infrequently called }
  637.     BytesToLose := Context.Inx shr 3;
  638.     { Note biggest Code Size is 12 bits. And this can at worst span 3 Bytes }
  639.     Move(Context.Buf[Word(BytesToLose)], Context.Buf[0], 3);
  640.     Context.Inx := Context.Inx and 7;
  641.     Context.Size := Context.Size - (BytesToLose shl 3);
  642.     Stream.ReadBuffer(Bytes, 1);
  643.     if Bytes > 0 then
  644.       Stream.ReadBuffer(Context.Buf[Word(Context.Size shr 3)], Bytes);
  645.     Context.Size := Context.Size + (Bytes shl 3);
  646.   end;
  647.   ByteIndex := Context.Inx shr 3;
  648.   RawCode := Context.Buf[Word(ByteIndex)] +
  649.     (Word(Context.Buf[Word(ByteIndex + 1)]) shl 8);
  650.   if Context.CodeSize > 8 then
  651.     RawCode := RawCode + (Longint(Context.Buf[ByteIndex + 2]) shl 16);
  652.   RawCode := RawCode shr (Context.Inx and 7);
  653.   Context.Inx := Context.Inx + Byte(Context.CodeSize);
  654.   Result := RawCode and Context.ReadMask;
  655. end;
  656. procedure Output(Value: Byte; var Context: TOutputContext);
  657. var
  658.   P: PByte;
  659. begin
  660.   if (Context.Y >= Context.H) then Exit;
  661.   case Context.BitsPerPixel of
  662.     1: begin
  663.          P := HugeOffset(Context.CurrLineData, Context.X shr 3);
  664.          if (Context.X and $07 <> 0) then
  665.            P^ := P^ or Word(value shl (7 - (Word(Context.X and 7))))
  666.          else P^ := Byte(value shl 7);
  667.        end;
  668.     4: begin
  669.          P := HugeOffset(Context.CurrLineData, Context.X shr 1);
  670.          if (Context.X and 1 <> 0) then P^ := P^ or Value
  671.          else P^ := Byte(value shl 4);
  672.        end;
  673.     8: begin
  674.          P := HugeOffset(Context.CurrLineData, Context.X);
  675.          P^ := Value;
  676.        end;
  677.   end;
  678.   Inc(Context.X);
  679.   if Context.X < Context.W then Exit;
  680.   Context.X := 0;
  681.   if Context.Interlace then
  682.     Context.Y := InterlaceStep(Context.Y, Context.H, Context.Pass)
  683.   else Inc(Context.Y);
  684.   Context.CurrLineData := HugeOffset(Context.Data,
  685.     (Context.H - 1 - Context.Y) * Context.LineIdent);
  686. end;
  687. procedure ReadGIFData(Stream: TStream; const Header: TBitmapInfoHeader;
  688.   Interlaced, LoadCorrupt: Boolean; IntBitPerPixel: Byte; Data: Pointer;
  689.   var Corrupted: Boolean; ProgressProc: TProgressProc);
  690. var
  691.   MinCodeSize, Temp: Byte;
  692.   MaxCode, BitMask, InitCodeSize: Longint;
  693.   ClearCode, EndingCode, FirstFreeCode, FreeCode: Word;
  694.   I, OutCount, Code: Longint;
  695.   CurCode, OldCode, InCode, FinalChar: Word;
  696.   Prefix, Suffix, OutCode: PIntCodeTable;
  697.   ReadCtxt: TReadContext;
  698.   OutCtxt: TOutputContext;
  699.   TableFull: Boolean;
  700. begin
  701.   Corrupted := False;
  702.   OutCount := 0; OldCode := 0; FinalChar := 0;
  703.   TableFull := False;
  704.   Prefix := AllocMem(SizeOf(TIntCodeTable));
  705.   try
  706.     Suffix := AllocMem(SizeOf(TIntCodeTable));
  707.     try
  708.       OutCode := AllocMem(SizeOf(TIntCodeTable) + SizeOf(Word));
  709.       try
  710.         if Assigned(ProgressProc) then ProgressProc(psStarting, 0, '');
  711.         try
  712.           Stream.ReadBuffer(MinCodeSize, 1);
  713.           if (MinCodeSize < 2) or (MinCodeSize > 9) then begin
  714.             if LoadCorrupt then begin
  715.               Corrupted := True;
  716.               MinCodeSize := Max(2, Min(MinCodeSize, 9));
  717.             end
  718.             else GifError(LoadStr(SBadGIFCodeSize));
  719.           end;
  720.           { Initial read context }
  721.           ReadCtxt.Inx := 0;
  722.           ReadCtxt.Size := 0;
  723.           ReadCtxt.CodeSize := MinCodeSize + 1;
  724.           ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1;
  725.           { Initialise pixel-output context }
  726.           OutCtxt.X := 0; OutCtxt.Y := 0;
  727.           OutCtxt.Pass := 0;
  728.           OutCtxt.W := Header.biWidth;
  729.           OutCtxt.H := Header.biHeight;
  730.           OutCtxt.BitsPerPixel := Header.biBitCount;
  731.           OutCtxt.Interlace := Interlaced;
  732.           OutCtxt.LineIdent := ((Header.biWidth * Header.biBitCount + 31)
  733.             div 32) * 4;
  734.           OutCtxt.Data := Data;
  735.           OutCtxt.CurrLineData := HugeOffset(Data, (Header.biHeight - 1) *
  736.             OutCtxt.LineIdent);
  737.           BitMask := (1 shl IntBitPerPixel) - 1;
  738.           { 2 ^ MinCodeSize accounts for all colours in file }
  739.           ClearCode := 1 shl MinCodeSize;
  740.           EndingCode := ClearCode + 1;
  741.           FreeCode := ClearCode + 2;
  742.           FirstFreeCode := FreeCode;
  743.           { 2^ (MinCodeSize + 1) includes clear and eoi Code and space too }
  744.           InitCodeSize := ReadCtxt.CodeSize;
  745.           MaxCode := 1 shl ReadCtxt.CodeSize;
  746.           Code := ReadCode(Stream, ReadCtxt);
  747.           while (Code <> EndingCode) and (Code <> $FFFF) and
  748.             (OutCtxt.Y < OutCtxt.H) do
  749.           begin
  750.             if (Code = ClearCode) then begin
  751.               ReadCtxt.CodeSize := InitCodeSize;
  752.               MaxCode := 1 shl ReadCtxt.CodeSize;
  753.               ReadCtxt.ReadMask := MaxCode - 1;
  754.               FreeCode := FirstFreeCode;
  755.               Code := ReadCode(Stream, ReadCtxt);
  756.               CurCode := Code; OldCode := Code;
  757.               if (Code = $FFFF) then Break;
  758.               FinalChar := (CurCode and BitMask);
  759.               Output(Byte(FinalChar), OutCtxt);
  760.               TableFull := False;
  761.             end
  762.             else begin
  763.               CurCode := Code;
  764.               InCode := Code;
  765.               if CurCode >= FreeCode then begin
  766.                 CurCode := OldCode;
  767.                 OutCode^[OutCount] := FinalChar;
  768.                 Inc(OutCount);
  769.               end;
  770.               while (CurCode > BitMask) do begin
  771.                 if (OutCount > CODE_TABLE_SIZE) then begin
  772.                   if LoadCorrupt then begin
  773.                     CurCode := BitMask;
  774.                     OutCount := 1;
  775.                     Corrupted := True;
  776.                     Break;
  777.                   end
  778.                   else GifError(LoadStr(SGIFDecodeError));
  779.                 end;
  780.                 OutCode^[OutCount] := Suffix^[CurCode];
  781.                 Inc(OutCount);
  782.                 CurCode := Prefix^[CurCode];
  783.               end;
  784.               if Corrupted then Break;
  785.               FinalChar := CurCode and BitMask;
  786.               OutCode^[OutCount] := FinalChar;
  787.               Inc(OutCount);
  788.               for I := OutCount - 1 downto 0 do
  789.                 Output(Byte(OutCode^[I]), OutCtxt);
  790.               OutCount := 0;
  791.               { Update dictionary }
  792.               if not TableFull then begin
  793.                 Prefix^[FreeCode] := OldCode;
  794.                 Suffix^[FreeCode] := FinalChar;
  795.                 { Advance to next free slot }
  796.                 Inc(FreeCode);
  797.                 if (FreeCode >= MaxCode) then begin
  798.                   if (ReadCtxt.CodeSize < 12) then begin
  799.                     Inc(ReadCtxt.CodeSize);
  800.                     MaxCode := MaxCode shl 1;
  801.                     ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1;
  802.                   end
  803.                   else TableFull := True;
  804.                 end;
  805.               end;
  806.               OldCode := InCode;
  807.             end;
  808.             Code := ReadCode(Stream, ReadCtxt);
  809.             if Stream.Size > 0 then begin
  810.               Temp := Trunc(100.0 * (Stream.Position / Stream.Size));
  811.               if Assigned(ProgressProc) then ProgressProc(psRunning, Temp, '');
  812.             end;
  813.           end; { while }
  814.           if Code = $FFFF then GifError(ResStr(SReadError));
  815.         finally
  816.           if Assigned(ProgressProc) then begin
  817.             if ExceptObject = nil then ProgressProc(psEnding, 100, '')
  818.             else ProgressProc(psEnding, 0, Exception(ExceptObject).Message);
  819.           end;
  820.         end;
  821.       finally
  822.         FreeMem(OutCode, SizeOf(TIntCodeTable) + SizeOf(Word));
  823.       end;
  824.     finally
  825.       FreeMem(Suffix, SizeOf(TIntCodeTable));
  826.     end;
  827.   finally
  828.     FreeMem(Prefix, SizeOf(TIntCodeTable));
  829.   end;
  830. end;
  831. procedure WriteCode(Stream: TStream; Code: Longint;
  832.   var Context: TWriteContext);
  833. var
  834.   BufIndex: Longint;
  835.   Bytes: Byte;
  836. begin
  837.   BufIndex := Context.Inx shr 3;
  838.   Code := Code shl (Context.Inx and 7);
  839.   Context.Buf[BufIndex] := Context.Buf[BufIndex] or (Code);
  840.   Context.Buf[BufIndex + 1] := (Code shr 8);
  841.   Context.Buf[BufIndex + 2] := (Code shr 16);
  842.   Context.Inx := Context.Inx + Context.CodeSize;
  843.   if Context.Inx >= 255 * 8 then begin
  844.     { Flush out full buffer }
  845.     Bytes := 255;
  846.     Stream.WriteBuffer(Bytes, 1);
  847.     Stream.WriteBuffer(Context.Buf, Bytes);
  848.     Move(Context.Buf[255], Context.Buf[0], 2);
  849.     FillChar(Context.Buf[2], 255, 0);
  850.     Context.Inx := Context.Inx - (255 * 8);
  851.   end;
  852. end;
  853. procedure FlushCode(Stream: TStream; var Context: TWriteContext);
  854. var
  855.   Bytes: Byte;
  856. begin
  857.   Bytes := (Context.Inx + 7) shr 3;
  858.   if Bytes > 0 then begin
  859.     Stream.WriteBuffer(Bytes, 1);
  860.     Stream.WriteBuffer(Context.Buf, Bytes);
  861.   end;
  862.   { Data block terminator - a block of zero Size }
  863.   Bytes := 0;
  864.   Stream.WriteBuffer(Bytes, 1);
  865. end;
  866. procedure FillColorTable(var ColorTable: TGIFColorTable;
  867.   const Colors: TRGBPalette; Count: Integer);
  868. var
  869.   I: Byte;
  870. begin
  871.   FillChar(ColorTable, SizeOf(ColorTable), 0);
  872.   ColorTable.Count := Min(256, Count);
  873.   for I := 0 to ColorTable.Count - 1 do begin
  874.     ColorTable.Colors[I].Red := Colors[I].rgbRed;
  875.     ColorTable.Colors[I].Green := Colors[I].rgbGreen;
  876.     ColorTable.Colors[I].Blue := Colors[I].rgbBlue;
  877.   end;
  878. end;
  879. procedure WriteGIFData(Stream: TStream; var Header: TBitmapInfoHeader;
  880.   Interlaced: Boolean; Data: Pointer; ProgressProc: TProgressProc);
  881.   { LZW encode data }
  882. var
  883.   LineIdent: Longint;
  884.   MinCodeSize, Col, Temp: Byte;
  885.   InitCodeSize, X, Y: Longint;
  886.   Pass: Integer;
  887.   MaxCode: Longint; { 1 shl CodeSize }
  888.   ClearCode, EndingCode, LastCode, Tail: Longint;
  889.   I, HashValue: Longint;
  890.   LenString: Word;
  891.   Dict: PDictTable;
  892.   HashTable: TList;
  893.   PData: PByte;
  894.   WriteCtxt: TWriteContext;
  895. begin
  896.   LineIdent := ((Header.biWidth * Header.biBitCount + 31) div 32) * 4;
  897.   Tail := 0; HashValue := 0;
  898.   Dict := AllocMem(SizeOf(TDictTable));
  899.   try
  900.     HashTable := TList.Create;
  901.     try
  902.       for I := 0 to HASH_TABLE_SIZE - 1 do HashTable.Add(nil);
  903.       { Initialise encoder variables }
  904.       InitCodeSize := Header.biBitCount + 1;
  905.       if InitCodeSize = 2 then Inc(InitCodeSize);
  906.       MinCodeSize := InitCodeSize - 1;
  907.       Stream.WriteBuffer(MinCodeSize, 1);
  908.       ClearCode := 1 shl MinCodeSize;
  909.       EndingCode := ClearCode + 1;
  910.       LastCode := EndingCode;
  911.       MaxCode := 1 shl InitCodeSize;
  912.       LenString := 0;
  913.       { Setup write context }
  914.       WriteCtxt.Inx := 0;
  915.       WriteCtxt.CodeSize := InitCodeSize;
  916.       FillChar(WriteCtxt.Buf, SizeOf(WriteCtxt.Buf), 0);
  917.       WriteCode(Stream, ClearCode, WriteCtxt);
  918.       for I := 0 to HASH_TABLE_SIZE - 1 do HashTable[I] := nil;
  919.       Data := HugeOffset(Data, (Header.biHeight - 1) * LineIdent);
  920.       Y := 0; Pass := 0;
  921.       if Assigned(ProgressProc) then ProgressProc(psStarting, 0, '');
  922.       try
  923.         while (Y < Header.biHeight) do begin
  924.           PData := HugeOffset(Data, -(Y * LineIdent));
  925.           for X := 0 to Header.biWidth - 1 do begin
  926.             case Header.biBitCount of
  927.               8: begin
  928.                    Col := PData^;
  929.                    PData := HugeOffset(PData, 1);
  930.                  end;
  931.               4: begin
  932.                    if X and 1 <> 0 then begin
  933.                      Col := PData^ and $0F;
  934.                      PData := HugeOffset(PData, 1);
  935.                    end
  936.                    else Col := PData^ shr 4;
  937.                  end;
  938.               else { must be 1 }
  939.                 begin
  940.                   if X and 7 = 7 then begin
  941.                     Col := PData^ and 1;
  942.                     PData := HugeOffset(PData, 1);
  943.                   end
  944.                   else Col := (PData^ shr (7 - (X and $07))) and $01;
  945.                 end;
  946.             end; { case }
  947.             Inc(LenString);
  948.             if LenString = 1 then begin
  949.               Tail := Col;
  950.               HashValue := InitHash(Col);
  951.             end
  952.             else begin
  953.               HashValue := HashValue * (Col + LenString + 4);
  954.               I := HashValue mod HASH_TABLE_SIZE;
  955.               HashValue := HashValue mod HASH_TABLE_SIZE;
  956.               while (HashTable[I] <> nil) and
  957.                 ((PImageDict(HashTable[I])^.Tail <> Tail) or
  958.                 (PImageDict(HashTable[I])^.Col <> Col)) do
  959.               begin
  960.                 Inc(I);
  961.                 if (I >= HASH_TABLE_SIZE) then I := 0;
  962.               end;
  963.               if (HashTable[I] <> nil) then { Found in the strings table }
  964.                 Tail := PImageDict(HashTable[I])^.Index
  965.               else begin
  966.                 { Not found }
  967.                 WriteCode(Stream, Tail, WriteCtxt);
  968.                 Inc(LastCode);
  969.                 HashTable[I] := @Dict^[LastCode];
  970.                 PImageDict(HashTable[I])^.Index := LastCode;
  971.                 PImageDict(HashTable[I])^.Tail := Tail;
  972.                 PImageDict(HashTable[I])^.Col := Col;
  973.                 Tail := Col;
  974.                 HashValue := InitHash(Col);
  975.                 LenString := 1;
  976.                 if (LastCode >= MaxCode) then begin
  977.                   { Next Code will be written longer }
  978.                   MaxCode := MaxCode shl 1;
  979.                   Inc(WriteCtxt.CodeSize);
  980.                 end
  981.                 else if (LastCode >= CODE_TABLE_SIZE - 2) then begin
  982.                   { Reset tables }
  983.                   WriteCode(Stream, Tail, WriteCtxt);
  984.                   WriteCode(Stream, ClearCode, WriteCtxt);
  985.                   LenString := 0;
  986.                   LastCode := EndingCode;
  987.                   WriteCtxt.CodeSize := InitCodeSize;
  988.                   MaxCode := 1 shl InitCodeSize;
  989.                   for I := 0 to HASH_TABLE_SIZE - 1 do HashTable[I] := nil;
  990.                 end;
  991.               end;
  992.             end;
  993.           end; { for X loop }
  994.           if Interlaced then Y := InterlaceStep(Y, Header.biHeight, Pass)
  995.           else Inc(Y);
  996.           Temp := Trunc(100.0 * (Y / Header.biHeight));
  997.           if Assigned(ProgressProc) then ProgressProc(psRunning, Temp, '');
  998.         end; { while Y loop }
  999.         WriteCode(Stream, Tail, WriteCtxt);
  1000.         WriteCode(Stream, EndingCode, WriteCtxt);
  1001.         FlushCode(Stream, WriteCtxt);
  1002.       finally
  1003.         if Assigned(ProgressProc) then begin
  1004.           if ExceptObject = nil then ProgressProc(psEnding, 100, '')
  1005.           else ProgressProc(psEnding, 0, Exception(ExceptObject).Message);
  1006.         end;
  1007.       end;
  1008.     finally
  1009.       HashTable.Free;
  1010.     end;
  1011.   finally
  1012.     FreeMem(Dict, SizeOf(TDictTable));
  1013.   end;
  1014. end;
  1015. { TGIFItem }
  1016. destructor TGIFItem.Destroy;
  1017. begin
  1018.   FImageData.Free;
  1019.   inherited Destroy;
  1020. end;
  1021. procedure TGIFItem.FreeHandle;
  1022. begin
  1023.   if FImageData <> nil then FImageData.SetSize(0);
  1024. end;
  1025. { TGIFData }
  1026. constructor TGIFData.Create;
  1027. begin
  1028.   inherited Create;
  1029.   FComment := TStringList.Create;
  1030. end;
  1031. destructor TGIFData.Destroy;
  1032. begin
  1033.   FComment.Free;
  1034.   inherited Destroy;
  1035. end;
  1036. procedure TGIFData.FreeHandle;
  1037. begin
  1038.   if FComment <> nil then FComment.Clear;
  1039. end;
  1040. { TGIFFrame }
  1041. constructor TGIFFrame.Create(AOwner: TGIFImage);
  1042. begin
  1043.   FOwner := AOwner;
  1044.   inherited Create;
  1045.   NewImage;
  1046. end;
  1047. destructor TGIFFrame.Destroy;
  1048. begin
  1049.   FBitmap.Free;
  1050.   FreeExtensions(FExtensions);
  1051.   FImage.Release;
  1052.   inherited Destroy;
  1053. end;
  1054. procedure TGIFFrame.SetAnimateInterval(Value: Word);
  1055. begin
  1056.   if FAnimateInterval <> Value then begin
  1057.     FAnimateInterval := Value;
  1058.     if Value > 0 then FOwner.FVersion := gv89a;
  1059.     FOwner.Changed(FOwner);
  1060.   end;
  1061. end;
  1062. procedure TGIFFrame.SetDisposalMethod(Value: TDisposalMethod);
  1063. begin
  1064.   if FDisposal <> Value then begin
  1065.     FDisposal := Value;
  1066.     if Value <> dmUndefined then FOwner.FVersion := gv89a;
  1067.     FOwner.Changed(FOwner);
  1068.   end;
  1069. end;
  1070. procedure TGIFFrame.SetTopLeft(const Value: TPoint);
  1071. begin
  1072.   if (FTopLeft.X <> Value.X) or (FTopLeft.Y <> Value.Y) then begin
  1073.     FTopLeft.X := Value.X;
  1074.     FTopLeft.Y := Value.Y;
  1075.     FOwner.FScreenWidth := Max(FOwner.FScreenWidth,
  1076.       FImage.FSize.X + FTopLeft.X);
  1077.     FOwner.FScreenHeight := Max(FOwner.FScreenHeight,
  1078.       FImage.FSize.Y + FTopLeft.Y);
  1079.     FOwner.Changed(FOwner);
  1080.   end;
  1081. end;
  1082. procedure TGIFFrame.SetTransparentColor(Value: TColor);
  1083. begin
  1084.   if FTransparentColor <> Value then begin
  1085.     FTransparentColor := Value;
  1086.     if Value <> clNone then FOwner.FVersion := gv89a;
  1087.     FOwner.Changed(FOwner);
  1088.   end;
  1089. end;
  1090. function TGIFFrame.GetBitmap: TBitmap;
  1091. var
  1092.   Mem: TMemoryStream;
  1093. begin
  1094.   Result := FBitmap;
  1095.   if (Result = nil) or Result.Empty then begin
  1096.     NewBitmap;
  1097.     Result := FBitmap;
  1098.     if Assigned(FImage.FImageData) then
  1099.     try
  1100.       Mem := TMemoryStream.Create;
  1101.       try
  1102.         SaveToBitmapStream(Mem);
  1103.         FBitmap.LoadFromStream(Mem);
  1104. {$IFDEF RX_D3}
  1105.         if not FBitmap.Monochrome then FBitmap.HandleType := bmDDB;
  1106. {$ENDIF}
  1107.       finally
  1108.         Mem.Free;
  1109.       end;
  1110.     except
  1111.       raise;
  1112.     end;
  1113.   end;
  1114. end;
  1115. function TGIFFrame.GetHeight: Integer;
  1116. begin
  1117.   if Assigned(FBitmap) or Assigned(FImage.FImageData) then
  1118.     Result := Bitmap.Height
  1119.   else Result := 0;
  1120. end;
  1121. function TGIFFrame.GetWidth: Integer;
  1122. begin
  1123.   if Assigned(FBitmap) or Assigned(FImage.FImageData) then
  1124.     Result := Bitmap.Width
  1125.   else Result := 0;
  1126. end;
  1127. function TGIFFrame.GetColorCount: Integer;
  1128. begin
  1129.   Result := FImage.FColormap.Count;
  1130.   if (Result = 0) and Assigned(FBitmap) and (FBitmap.Palette <> 0) then
  1131.     Result := PaletteEntries(FBitmap.Palette);
  1132. end;
  1133. procedure TGIFFrame.GrayscaleImage(ForceEncoding: Boolean);
  1134. var
  1135.   Mem: TMemoryStream;
  1136.   TransIndex: Integer;
  1137. begin
  1138.   if not FGrayscale and (Assigned(FBitmap) or
  1139.     Assigned(FImage.FImageData)) then
  1140.   begin
  1141.     if Assigned(FImage.FImageData) and (FImage.FColorMap.Count > 0) then begin
  1142.       FBitmap.Free;
  1143.       FBitmap := nil;
  1144.       TransIndex := FindColorIndex(FImage.FColorMap, FTransparentColor);
  1145.       GrayColorTable(FImage.FColorMap);
  1146.       if TransIndex >= 0 then
  1147.         FTransparentColor := ItemToRGB(FImage.FColorMap.Colors[TransIndex])
  1148.       else FTransparentColor := clNone;
  1149.       FGrayscale := True;
  1150.       try
  1151.         GetBitmap;
  1152.       except
  1153.         on EAbort do;
  1154.         else raise;
  1155.       end;
  1156.     end
  1157.     else begin
  1158.       Mem := BitmapToMemoryStream(Bitmap, pf8bit, mmGrayscale);
  1159.       try
  1160.         FImage.Release;
  1161.         FImage := TGIFItem.Create;
  1162.         FImage.Reference;
  1163.         if ForceEncoding then EncodeBitmapStream(Mem);
  1164.         FGrayscale := True;
  1165.         if FTransparentColor <> clNone then
  1166.           FTransparentColor := GrayColor(FTransparentColor);
  1167.         FBitmap.LoadFromStream(Mem);
  1168.       finally
  1169.         Mem.Free;
  1170.       end;
  1171.     end;
  1172.   end;
  1173. end;
  1174. procedure TGIFFrame.Assign(Source: TPersistent);
  1175. var
  1176.   AComment: TStrings;
  1177. begin
  1178.   if Source = nil then begin
  1179.     NewImage;
  1180.     FBitmap.Free;
  1181.     FBitmap := nil;
  1182.   end
  1183.   else if (Source is TGIFFrame) then begin
  1184.     if Source <> Self then begin
  1185.       FImage.Release;
  1186.       FImage := TGIFFrame(Source).FImage;
  1187.       if TGIFFrame(Source).FOwner <> FOwner then FLocalColors := True
  1188.       else FLocalColors := TGIFFrame(Source).FLocalColors;
  1189.       FImage.Reference;
  1190.       FTopLeft := TGIFFrame(Source).FTopLeft;
  1191.       FInterlaced := TGIFFrame(Source).FInterlaced;
  1192.       if TGIFFrame(Source).FBitmap <> nil then begin
  1193.         NewBitmap;
  1194.         FBitmap.Assign(TGIFFrame(Source).FBitmap);
  1195.       end;
  1196.       FTransparentColor := TGIFFrame(Source).FTransparentColor;
  1197.       FAnimateInterval := TGIFFrame(Source).FAnimateInterval;
  1198.       FDisposal := TGIFFrame(Source).FDisposal;
  1199.       FGrayscale := TGIFFrame(Source).FGrayscale;
  1200.       FCorrupted := TGIFFrame(Source).FCorrupted;
  1201.       AComment := TGIFFrame(Source).FindComment(False);
  1202.       if (AComment <> nil) and (AComment.Count > 0) then
  1203.         SetComment(AComment);
  1204.     end;
  1205.   end
  1206.   else if Source is TGIFImage then begin
  1207.     if (TGIFImage(Source).Count > 0) then begin
  1208.       if (TGIFImage(Source).FrameIndex >= 0) then
  1209.         Assign(TGIFImage(Source).Frames[TGIFImage(Source).FrameIndex])
  1210.       else
  1211.         Assign(TGIFImage(Source).Frames[0]);
  1212.     end
  1213.     else Assign(nil);
  1214.   end
  1215.   else if Source is TGraphic then begin
  1216.     { TBitmap, TJPEGImage... }
  1217.     if TGraphic(Source).Empty then begin
  1218.       Assign(nil);
  1219.       Exit;
  1220.     end;
  1221.     NewImage;
  1222.     NewBitmap;
  1223.     try
  1224.       FBitmap.Assign(Source);
  1225.       if Source is TBitmap then
  1226.         FBitmap.Monochrome := TBitmap(Source).Monochrome;
  1227.     except
  1228.       FBitmap.Canvas.Brush.Color := clFuchsia;
  1229.       FBitmap.Width := TGraphic(Source).Width;
  1230.       FBitmap.Height := TGraphic(Source).Height;
  1231.       FBitmap.Canvas.Draw(0, 0, TGraphic(Source));
  1232.     end;
  1233. {$IFDEF RX_D3}
  1234.     if TGraphic(Source).Transparent then begin
  1235.       if Source is TBitmap then
  1236.         FTransparentColor := TBitmap(Source).TransparentColor
  1237.       else FTransparentColor := GetNearestColor(FBitmap.Canvas.Handle,
  1238.         ColorToRGB(FBitmap.Canvas.Brush.Color));
  1239.     end;
  1240. {$ELSE}
  1241.     if (Source is TIcon) or (Source is TMetafile) then
  1242.       FTransparentColor := GetNearestColor(FBitmap.Canvas.Handle,
  1243.         ColorToRGB(FBitmap.Canvas.Brush.Color));
  1244. {$ENDIF}
  1245.   end
  1246.   else inherited Assign(Source);
  1247.   if FOwner <> nil then FOwner.UpdateScreenSize;
  1248. end;
  1249. procedure TGIFFrame.AssignTo(Dest: TPersistent);
  1250. begin
  1251.   if (Dest is TGIFFrame) or (Dest is TGIFImage) then Dest.Assign(Self)
  1252.   else if Dest is TGraphic then begin
  1253.     Dest.Assign(Bitmap);
  1254. {$IFDEF RX_D3}
  1255.     if (Dest is TBitmap) and (FTransparentColor <> clNone) then begin
  1256.       TBitmap(Dest).TransparentColor := GetNearestColor(
  1257.         TBitmap(Dest).Canvas.Handle, ColorToRGB(FTransparentColor));
  1258.       TBitmap(Dest).Transparent := True;
  1259.     end;
  1260. {$ENDIF}
  1261.   end
  1262.   else inherited AssignTo(Dest);
  1263. end;
  1264. procedure TGIFFrame.NewBitmap;
  1265. begin
  1266.   FBitmap.Free;
  1267.   FBitmap := TBitmap.Create;
  1268. end;
  1269. procedure TGIFFrame.NewImage;
  1270. begin
  1271.   if FImage <> nil then FImage.Release;
  1272.   FImage := TGIFItem.Create;
  1273.   FImage.Reference;
  1274.   FGrayscale := False;
  1275.   FCorrupted := False;
  1276.   FTransparentColor := clNone;
  1277.   FTopLeft := Point(0, 0);
  1278.   FInterlaced := False;
  1279.   FLocalColors := False;
  1280.   FAnimateInterval := 0;
  1281.   FDisposal := dmUndefined;
  1282. end;
  1283. function TGIFFrame.FindComment(ForceCreate: Boolean): TStrings;
  1284. var
  1285.   Ext: TExtension;
  1286. begin
  1287.   Ext := FindExtension(FExtensions, etComment);
  1288.   if (Ext = nil) and ForceCreate then begin
  1289.     Ext := TExtension.Create;
  1290.     try
  1291.       Ext.FExtType := etComment;
  1292.       if FExtensions = nil then FExtensions := TList.Create;
  1293.       FExtensions.Add(Ext);
  1294.     except
  1295.       Ext.Free;
  1296.       raise;
  1297.     end;
  1298.   end;
  1299.   if (Ext <> nil) then begin
  1300.     if (Ext.FData = nil) and ForceCreate then
  1301.       Ext.FData := TStringList.Create;
  1302.     Result := Ext.FData;
  1303.   end
  1304.   else Result := nil;
  1305. end;
  1306. function TGIFFrame.GetComment: TStrings;
  1307. begin
  1308.   Result := FindComment(True);
  1309. end;
  1310. procedure TGIFFrame.SetComment(Value: TStrings);
  1311. begin
  1312.   GetComment.Assign(Value);
  1313. end;
  1314. procedure TGIFFrame.UpdateExtensions;
  1315. var
  1316.   Ext: TExtension;
  1317.   I: Integer;
  1318. begin
  1319.   Ext := FindExtension(FExtensions, etGraphic);
  1320.   if (FAnimateInterval > 0) or (FTransparentColor <> clNone) or
  1321.     (FDisposal <> dmUndefined) then
  1322.   begin
  1323.     if Ext = nil then begin
  1324.       Ext := TExtension.Create;
  1325.       Ext.FExtType := etGraphic;
  1326.       if FExtensions = nil then FExtensions := TList.Create;
  1327.       FExtensions.Add(Ext);
  1328.       with Ext.FExtRec.GCE do begin
  1329.         BlockSize := 4;
  1330.         PackedFields := 0;
  1331.         Terminator := 0;
  1332.       end;
  1333.     end;
  1334.   end;
  1335.   if Ext <> nil then
  1336.     with Ext.FExtRec.GCE do begin
  1337.       DelayTime := FAnimateInterval div 10;
  1338.       I := FindColorIndex(FImage.FColorMap, FTransparentColor);
  1339.       if I >= 0 then begin
  1340.         TransparentColorIndex := I;
  1341.         PackedFields := PackedFields or GCE_TRANSPARENT;
  1342.       end
  1343.       else PackedFields := PackedFields and not GCE_TRANSPARENT;
  1344.       PackedFields := (PackedFields and not GCE_DISPOSAL_METHOD) or
  1345.         (Ord(FDisposal) shl 2);
  1346.     end;
  1347.   if FExtensions <> nil then
  1348.     for I := FExtensions.Count - 1 downto 0 do begin
  1349.       Ext := TExtension(FExtensions[I]);
  1350.       if (Ext <> nil) and (Ext.FExtType = etComment) and
  1351.         ((Ext.FData = nil) or (Ext.FData.Count = 0)) then
  1352.       begin
  1353.         Ext.Free;
  1354.         FExtensions.Delete(I);
  1355.       end;
  1356.     end;
  1357.   if (FExtensions <> nil) and (FExtensions.Count > 0) then
  1358.     FOwner.FVersion := gv89a;
  1359. end;
  1360. procedure TGIFFrame.EncodeBitmapStream(Stream: TMemoryStream);
  1361. var
  1362.   BI: PBitmapInfoHeader;
  1363.   ColorCount, W, H: Integer;
  1364.   Bits, Pal: Pointer;
  1365. begin
  1366.   ColorCount := 0;
  1367.   Stream.Position := 0;
  1368.   BI := PBitmapInfoHeader(Longint(Stream.Memory) + SizeOf(TBitmapFileHeader));
  1369.   W := BI^.biWidth; H := BI^.biHeight;
  1370.   Pal := PRGBPalette(Longint(BI) + SizeOf(TBitmapInfoHeader));
  1371.   Bits := Pointer(Longword(Stream.Memory) + PBitmapFileHeader(Stream.Memory)^.bfOffBits);
  1372.   case BI^.biBitCount of
  1373.     1: ColorCount := 2;
  1374.     4: ColorCount := 16;
  1375.     8: ColorCount := 256;
  1376.     else GifError(LoadStr(SGIFEncodeError));
  1377.   end;
  1378.   FInterlaced := False;
  1379.   FillColorTable(FImage.FColorMap, PRGBPalette(Pal)^, ColorCount);
  1380.   if FImage.FImageData = nil then FImage.FImageData := TMemoryStream.Create
  1381.   else FImage.FImageData.SetSize(0);
  1382.   try
  1383.     WriteGIFData(FImage.FImageData, BI^, FInterlaced, Bits, FOwner.DoProgress);
  1384.   except
  1385.     on EAbort do begin
  1386.       NewImage; { OnProgress can raise EAbort to cancel image save }
  1387.       raise;
  1388.     end
  1389.     else raise;
  1390.   end;
  1391.   FImage.FBitsPerPixel := 1;
  1392.   while FImage.FColorMap.Count > 1 shl FImage.FBitsPerPixel do
  1393.     Inc(FImage.FBitsPerPixel);
  1394.   if FOwner.FImage.FColorMap.Count = 0 then begin
  1395.     FOwner.FImage.FColorMap := FImage.FColorMap;
  1396.     FOwner.FImage.FBitsPerPixel := FImage.FBitsPerPixel;
  1397.     FLocalColors := False;
  1398.   end
  1399.   else FLocalColors := True;
  1400.   FImage.FSize.X := W; FImage.FSize.Y := H;
  1401.   FOwner.FScreenWidth := Max(FOwner.FScreenWidth, FImage.FSize.X + FTopLeft.X);
  1402.   FOwner.FScreenHeight := Max(FOwner.FScreenHeight, FImage.FSize.Y + FTopLeft.Y);
  1403. end;
  1404. procedure TGIFFrame.EncodeRasterData;
  1405. var
  1406.   Method: TMappingMethod;
  1407.   Mem: TMemoryStream;
  1408. begin
  1409.   if not Assigned(FBitmap) or FBitmap.Empty then GifError(LoadStr(SNoGIFData));
  1410.   if not (GetBitmapPixelFormat(FBitmap) in [pf1bit, pf4bit, pf8bit]) then
  1411.   begin
  1412.     if FGrayscale then Method := mmGrayscale
  1413.     else Method := DefaultMappingMethod;
  1414.     Mem := BitmapToMemoryStream(FBitmap, pf8bit, Method);
  1415.     if (Method = mmGrayscale) then FGrayscale := True;
  1416.   end
  1417.   else Mem := TMemoryStream.Create;
  1418.   try
  1419.     if Mem.Size = 0 then FBitmap.SaveToStream(Mem);
  1420.     EncodeBitmapStream(Mem);
  1421.   finally
  1422.     Mem.Free;
  1423.   end;
  1424. end;
  1425. procedure TGIFFrame.WriteImageDescriptor(Stream: TStream);
  1426. var
  1427.   ImageDesc: TImageDescriptor;
  1428. begin
  1429.   with ImageDesc do begin
  1430.     PackedFields := 0;
  1431.     if FLocalColors then begin
  1432.       FImage.FBitsPerPixel := 1;
  1433.       while FImage.FColorMap.Count > 1 shl FImage.FBitsPerPixel do
  1434.         Inc(FImage.FBitsPerPixel);
  1435.       PackedFields := (PackedFields or ID_LOCAL_COLOR_TABLE) +
  1436.         (FImage.FBitsPerPixel - 1);
  1437.     end;
  1438.     if FInterlaced then PackedFields := PackedFields or ID_INTERLACED;
  1439.     ImageLeftPos := FTopLeft.X;
  1440.     ImageTopPos := FTopLeft.Y;
  1441.     ImageWidth := FImage.FSize.X;
  1442.     ImageHeight := FImage.FSize.Y;
  1443.   end;
  1444.   Stream.Write(ImageDesc, SizeOf(TImageDescriptor));
  1445. end;
  1446. procedure TGIFFrame.WriteLocalColorMap(Stream: TStream);
  1447. begin
  1448.   if FLocalColors then
  1449.     with FImage.FColorMap do
  1450.       Stream.Write(Colors[0], Count * SizeOf(TGIFColorItem));
  1451. end;
  1452. procedure TGIFFrame.WriteRasterData(Stream: TStream);
  1453. begin
  1454.   Stream.WriteBuffer(FImage.FImageData.Memory^, FImage.FImageData.Size);
  1455. end;
  1456. procedure TGIFFrame.SaveToBitmapStream(Stream: TMemoryStream);
  1457.   function ConvertBitsPerPixel: TPixelFormat;
  1458.   begin
  1459.     Result := pfDevice;
  1460.     case FImage.FBitsPerPixel of
  1461.       1: Result := pf1bit;
  1462.       2..4: Result := pf4bit;
  1463.       5..8: Result := pf8bit;
  1464.       else GifError(LoadStr(SWrongGIFColors));
  1465.     end;
  1466.   end;
  1467. var
  1468.   HeaderSize: Longword;
  1469.   Length: Longword;
  1470.   BI: TBitmapInfoHeader;
  1471.   BitFile: TBitmapFileHeader;
  1472.   Colors: TRGBPalette;
  1473.   Bits: Pointer;
  1474.   Corrupt: Boolean;
  1475. begin
  1476.   with BI do begin
  1477.     biSize := Sizeof(TBitmapInfoHeader);
  1478.     biWidth := FImage.FSize.X;
  1479.     biHeight := FImage.FSize.Y;
  1480.     biPlanes := 1;
  1481.     biBitCount := 0;
  1482.     case ConvertBitsPerPixel of
  1483.       pf1bit: biBitCount := 1;
  1484.       pf4bit: biBitCount := 4;
  1485.       pf8bit: biBitCount := 8;
  1486.     end;
  1487.     biCompression := BI_RGB;
  1488.     biSizeImage := (((biWidth * biBitCount + 31) div 32) * 4) * biHeight;
  1489.     biXPelsPerMeter := 0;
  1490.     biYPelsPerMeter := 0;
  1491.     biClrUsed := 0;
  1492.     biClrImportant := 0;
  1493.   end;
  1494.   HeaderSize := SizeOf(TBitmapFileHeader) + SizeOf(TBitmapInfoHeader) +
  1495.     SizeOf(TRGBQuad) * (1 shl BI.biBitCount);
  1496.   Length := HeaderSize + BI.biSizeImage;
  1497.   Stream.SetSize(0);
  1498.   Stream.Position := 0;
  1499.   with BitFile do begin
  1500.     bfType := $4D42; { BM }
  1501.     bfSize := Length;
  1502.     bfOffBits := HeaderSize;
  1503.   end;
  1504.   Stream.Write(BitFile, SizeOf(TBitmapFileHeader));
  1505.   Stream.Write(BI, SizeOf(TBitmapInfoHeader));
  1506.   FillRGBPalette(FImage.FColorMap, Colors);
  1507.   Stream.Write(Colors, SizeOf(TRGBQuad) * (1 shl BI.biBitCount));
  1508.   Bits := AllocMemo(BI.biSizeImage);
  1509.   try
  1510.     ZeroMemory(Bits, BI.biSizeImage);
  1511.     FImage.FImageData.Position := 0;
  1512.     ReadGIFData(FImage.FImageData, BI, FInterlaced, GIFLoadCorrupted,
  1513.       FImage.FBitsPerPixel, Bits, Corrupt, FOwner.DoProgress);
  1514.     FCorrupted := FCorrupted or Corrupt;
  1515.     Stream.WriteBuffer(Bits^, BI.biSizeImage);
  1516.   finally
  1517.     FreeMemo(Bits);
  1518.   end;
  1519.   Stream.Position := 0;
  1520. end;
  1521. procedure TGIFFrame.LoadFromStream(Stream: TStream);
  1522. var
  1523.   ImageDesc: TImageDescriptor;
  1524.   I, TransIndex: Integer;
  1525. begin
  1526.   FImage.FImageData := TMemoryStream.Create;
  1527.   try
  1528.     ReadImageStream(Stream, FImage.FImageData, ImageDesc, FInterlaced,
  1529.       FLocalColors, FCorrupted, FImage.FBitsPerPixel, FImage.FColorMap);
  1530.     if FCorrupted and not GIFLoadCorrupted then GifError(ResStr(SReadError));
  1531.     FImage.FImageData.Position := 0;
  1532.     with ImageDesc do begin
  1533.       if ImageHeight = 0 then ImageHeight := FOwner.FScreenHeight;
  1534.       if ImageWidth = 0 then ImageWidth := FOwner.FScreenWidth;
  1535.       FTopLeft := Point(ImageLeftPos, ImageTopPos);
  1536.       FImage.FSize := Point(ImageWidth, ImageHeight);
  1537.       FImage.FPackedFields := PackedFields;
  1538.     end;
  1539.     if not FLocalColors then FImage.FColorMap := FOwner.FImage.FColorMap;
  1540.     FAnimateInterval := 0;
  1541.     if FExtensions <> nil then begin
  1542.       for I := 0 to FExtensions.Count - 1 do
  1543.         with TExtension(FExtensions[I]) do
  1544.           if FExtType = etGraphic then begin
  1545.             if (FExtRec.GCE.PackedFields and GCE_TRANSPARENT) <> 0 then
  1546.             begin
  1547.               TransIndex := FExtRec.GCE.TransparentColorIndex;
  1548.               if FImage.FColorMap.Count > TransIndex then
  1549.                 FTransparentColor := ItemToRGB(FImage.FColorMap.Colors[TransIndex]);
  1550.             end
  1551.             else FTransparentColor := clNone;
  1552.             FAnimateInterval := Max(FExtRec.GCE.DelayTime * 10,
  1553.               FAnimateInterval);
  1554.             FDisposal := TDisposalMethod((FExtRec.GCE.PackedFields and
  1555.               GCE_DISPOSAL_METHOD) shr 2);
  1556.           end;
  1557.     end;
  1558.   except
  1559.     FImage.FImageData.Free;
  1560.     FImage.FImageData := nil;
  1561.     raise;
  1562.   end;
  1563. end;
  1564. procedure TGIFFrame.Draw(ACanvas: TCanvas; const ARect: TRect;
  1565.   Transparent: Boolean);
  1566. begin
  1567.   if (FTransparentColor <> clNone) and Transparent then begin
  1568.     with ARect do
  1569.       StretchBitmapRectTransparent(ACanvas, Left, Top, Right - Left,
  1570.         Bottom - Top, Bounds(0, 0, Bitmap.Width, Bitmap.Height), Bitmap,
  1571.         FTransparentColor);
  1572.   end
  1573.   else ACanvas.StretchDraw(ARect, Bitmap);
  1574. end;
  1575. { TGIFImage }
  1576. constructor TGIFImage.Create;
  1577. begin
  1578.   inherited Create;
  1579.   NewImage;
  1580. {$IFDEF RX_D3}
  1581.   inherited SetTransparent(True);
  1582. {$ENDIF}
  1583. end;
  1584. destructor TGIFImage.Destroy;
  1585. begin
  1586.   OnChange := nil;
  1587.   FImage.Release;
  1588.   ClearItems;
  1589.   FItems.Free;
  1590.   inherited Destroy;
  1591. end;
  1592. procedure TGIFImage.Clear;
  1593. begin
  1594.   Assign(nil);
  1595. end;
  1596. procedure TGIFImage.ClearItems;
  1597. begin
  1598.   if FItems <> nil then
  1599.     while FItems.Count > 0 do begin
  1600.       TObject(FItems[0]).Free;
  1601.       FItems.Delete(0);
  1602.     end;
  1603. end;
  1604. procedure TGIFImage.Assign(Source: TPersistent);
  1605. var
  1606.   I: Integer;
  1607.   AFrame: TGIFFrame;
  1608. begin
  1609.   if (Source = nil) then begin
  1610.     NewImage;
  1611.     Changed(Self);
  1612.   end
  1613.   else if (Source is TGIFImage) and (Source <> Self) then begin
  1614.     FImage.Release;
  1615.     FImage := TGIFImage(Source).FImage;
  1616.     FImage.Reference;
  1617.     FVersion := TGIFImage(Source).FVersion;
  1618.     FBackgroundColor := TGIFImage(Source).FBackgroundColor;
  1619.     FRepeatCount := TGIFImage(Source).FRepeatCount;
  1620.     FLooping := TGIFImage(Source).FLooping;
  1621.     FCorrupted := TGIFImage(Source).FCorrupted;
  1622.     if FItems = nil then FItems := TList.Create
  1623.     else ClearItems;
  1624.     with TGIFImage(Source) do begin
  1625.       for I := 0 to FItems.Count - 1 do begin
  1626.         AFrame := TGIFFrame.Create(Self);
  1627.         try
  1628.           AFrame.FImage.FBitsPerPixel :=
  1629.             TGIFFrame(FItems[I]).FImage.FBitsPerPixel;
  1630.           AFrame.Assign(TGIFFrame(FItems[I]));
  1631.           AFrame.FLocalColors := TGIFFrame(FItems[I]).FLocalColors;
  1632.           Self.FItems.Add(AFrame);
  1633.         except
  1634.           AFrame.Free;
  1635.           raise;
  1636.         end;
  1637.       end;
  1638.       Self.FScreenWidth := FScreenWidth;
  1639.       Self.FScreenHeight := FScreenHeight;
  1640.     end;
  1641.     FFrameIndex := TGIFImage(Source).FFrameIndex;
  1642.     Changed(Self);
  1643.   end
  1644.   else if Source is TGIFFrame then begin
  1645.     NewImage;
  1646.     with TGIFFrame(Source).FOwner.FImage do begin
  1647.       FImage.FAspectRatio := FAspectRatio;
  1648.       FImage.FBitsPerPixel := FBitsPerPixel;
  1649.       FImage.FColorResBits := FColorResBits;
  1650.       Move(FColorMap, FImage.FColorMap, SizeOf(FColorMap));
  1651.     end;
  1652.     FFrameIndex := FItems.Add(TGIFFrame.Create(Self));
  1653.     TGIFFrame(FItems[FFrameIndex]).Assign(Source);
  1654.     if FVersion = gvUnknown then FVersion := gv87a;
  1655.     Changed(Self);
  1656.   end
  1657.   else if Source is TBitmap then begin
  1658.     NewImage;
  1659.     AddFrame(TBitmap(Source));
  1660.     Changed(Self);
  1661.   end
  1662.   else if Source is TAnimatedCursorImage then begin
  1663.     NewImage;
  1664.     FBackgroundColor := clWindow;
  1665.     with TAnimatedCursorImage(Source) do begin
  1666.       for I := 0 to IconCount - 1 do begin
  1667.         AddFrame(TIcon(Icons[I]));
  1668.         Self.Frames[FrameIndex].FAnimateInterval :=
  1669.           Longint(Frames[I].JiffRate * 100) div 6;
  1670.       end;
  1671.     end;
  1672.     Changed(Self);
  1673.   end
  1674.   else inherited Assign(Source);
  1675. end;
  1676. procedure TGIFImage.AssignTo(Dest: TPersistent);
  1677. begin
  1678.   if Dest is TGIFImage then Dest.Assign(Self)
  1679.   else if Dest is TGraphic then begin
  1680.     if Empty then
  1681.       Dest.Assign(nil)
  1682.     else if FFrameIndex >= 0 then
  1683.       TGIFFrame(FItems[FFrameIndex]).AssignTo(Dest)
  1684.     else Dest.Assign(Bitmap);
  1685.   end
  1686.   else inherited AssignTo(Dest);
  1687. end;
  1688. procedure TGIFImage.Draw(ACanvas: TCanvas; const ARect: TRect);
  1689. begin
  1690.   if FFrameIndex >= 0 then
  1691.     TGIFFrame(FItems[FFrameIndex]).Draw(ACanvas, ARect, Self.Transparent);
  1692. end;
  1693. function TGIFImage.GetBackgroundColor: TColor;
  1694. begin
  1695.   Result := FBackgroundColor;
  1696. end;
  1697. procedure TGIFImage.SetBackgroundColor(Value: TColor);
  1698. begin
  1699.   if Value <> FBackgroundColor then begin
  1700.     FBackgroundColor := Value;
  1701.     Changed(Self);
  1702.   end;
  1703. end;
  1704. procedure TGIFImage.SetLooping(Value: Boolean);
  1705. begin
  1706.   if Value <> FLooping then begin
  1707.     FLooping := Value;
  1708.     Changed(Self);
  1709.   end;
  1710. end;
  1711. procedure TGIFImage.SetRepeatCount(Value: Word);
  1712. begin
  1713.   if Min(Value, MAX_LOOP_COUNT) <> FRepeatCount then begin
  1714.     FRepeatCount := Min(Value, MAX_LOOP_COUNT);
  1715.     Changed(Self);
  1716.   end;
  1717. end;
  1718. function TGIFImage.GetPixelFormat: TPixelFormat;
  1719. var
  1720.   I: Integer;
  1721. begin
  1722.   Result := pfDevice;
  1723.   if not Empty then begin
  1724.     Result := ColorsToPixelFormat(FImage.FColorMap.Count);
  1725.     for I := 0 to FItems.Count - 1 do begin
  1726.       if (Frames[I].FImage.FImageData = nil) or
  1727.         (Frames[I].FImage.FImageData.Size = 0) then
  1728.       begin
  1729.         if Assigned(Frames[I].FBitmap) then
  1730.           Result := TPixelFormat(Max(Ord(Result),
  1731.             Ord(GetBitmapPixelFormat(Frames[I].FBitmap))))
  1732.         else Result := TPixelFormat(Max(Ord(Result), Ord(pfDevice)));
  1733.       end
  1734.       else if Frames[I].FLocalColors then
  1735.         Result := TPixelFormat(Max(Ord(Result),
  1736.           Ord(ColorsToPixelFormat(Frames[I].FImage.FColorMap.Count))));
  1737.     end;
  1738.   end;
  1739. end;
  1740. function TGIFImage.GetCorrupted: Boolean;
  1741. var
  1742.   I: Integer;
  1743. begin
  1744.   Result := FCorrupted;
  1745.   if not Result then
  1746.     for I := 0 to FItems.Count - 1 do
  1747.       if Frames[I].Corrupted then begin
  1748.         Result := True;
  1749.         Exit;
  1750.       end;
  1751. end;
  1752. function TGIFImage.GetTransparentColor: TColor;
  1753. begin
  1754.   if (FItems.Count > 0) and (FFrameIndex >= 0) then
  1755.     Result := TGIFFrame(FItems[FFrameIndex]).FTransparentColor
  1756.   else Result := clNone;
  1757. end;
  1758. function TGIFImage.GetCount: Integer;
  1759. begin
  1760.   Result := FItems.Count;
  1761. end;
  1762. function TGIFImage.GetFrame(Index: Integer): TGIFFrame;
  1763. begin
  1764.   Result := TGIFFrame(FItems[Index]);
  1765. end;
  1766. procedure TGIFImage.SetFrameIndex(Value: Integer);
  1767. begin
  1768.   Value := Min(FItems.Count - 1, Max(-1, Value));
  1769.   if FFrameIndex <> Value then begin
  1770.     FFrameIndex := Value;
  1771. {$IFDEF RX_D3}
  1772.     PaletteModified := True;
  1773. {$ENDIF}
  1774.     Changed(Self);
  1775.   end;
  1776. end;
  1777. {$IFDEF WIN32}
  1778. function TGIFImage.Equals(Graphic: TGraphic): Boolean;
  1779. begin
  1780.   Result := (Graphic is TGIFImage) and
  1781.     (FImage = TGIFImage(Graphic).FImage);
  1782. end;
  1783. {$ENDIF}
  1784. function TGIFImage.GetBitmap: TBitmap;
  1785. var
  1786.   Bmp: TBitmap;
  1787. begin
  1788.   if (FItems.Count > 0) then begin
  1789.     if (FFrameIndex >= 0) and (FFrameIndex < FItems.Count) then
  1790.       Result := TGIFFrame(FItems[FFrameIndex]).Bitmap
  1791.     else Result := TGIFFrame(FItems[0]).Bitmap
  1792.   end
  1793.   else begin
  1794.     FFrameIndex := 0;
  1795.     Bmp := TBitmap.Create;
  1796.     try
  1797.       Bmp.Handle := 0;
  1798.       Assign(Bmp);
  1799.       Result := TGIFFrame(FItems[FFrameIndex]).Bitmap;
  1800.     finally
  1801.       Bmp.Free;
  1802.     end;
  1803.   end;
  1804. end;
  1805. function TGIFImage.GetGlobalColorCount: Integer;
  1806. begin
  1807.   Result := FImage.FColormap.Count;
  1808. end;
  1809. function TGIFImage.GetEmpty: Boolean;
  1810. var
  1811.   I: Integer;
  1812. begin
  1813.   I := Max(FFrameIndex, 0);
  1814.   Result := (FItems.Count = 0) or
  1815.     ((TGIFFrame(FItems[I]).FBitmap = nil) and
  1816.     ((TGIFFrame(FItems[I]).FImage.FImageData = nil) or
  1817.     (TGIFFrame(FItems[I]).FImage.FImageData.Size = 0)));
  1818. end;
  1819. function TGIFImage.GetPalette: HPalette;
  1820. begin
  1821.   if FItems.Count > 0 then Result := Bitmap.Palette
  1822.   else Result := 0;
  1823. end;
  1824. function TGIFImage.GetTransparent: Boolean;
  1825. var
  1826.   I: Integer;
  1827. begin
  1828. {$IFDEF RX_D3}
  1829.   if inherited GetTransparent then
  1830. {$ENDIF}
  1831.     for I := 0 to FItems.Count - 1 do
  1832.       if Frames[I].TransparentColor <> clNone then begin
  1833.         Result := True;
  1834.         Exit;
  1835.       end;
  1836.   Result := False;
  1837. end;
  1838. function TGIFImage.GetHeight: Integer;
  1839. begin
  1840.   if not Empty and (FFrameIndex >= 0) and (FFrameIndex < Count) then
  1841.     Result := TGIFFrame(FItems[FFrameIndex]).Bitmap.Height
  1842.   else Result := 0;
  1843. end;
  1844. function TGIFImage.GetWidth: Integer;
  1845. begin
  1846.   if not Empty and (FFrameIndex >= 0) and (FFrameIndex < Count) then
  1847.     Result := TGIFFrame(FItems[FFrameIndex]).Bitmap.Width
  1848.   else Result := 0;
  1849. end;
  1850. function TGIFImage.GetScreenWidth: Integer;
  1851. begin
  1852.   if Empty then Result := 0
  1853.   else Result := FScreenWidth;
  1854. end;
  1855. function TGIFImage.GetScreenHeight: Integer;
  1856. begin
  1857.   if Empty then Result := 0
  1858.   else Result := FScreenHeight;
  1859. end;
  1860. procedure TGIFImage.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
  1861.   APalette: HPALETTE);
  1862. var
  1863.   Bmp: TBitmap;
  1864.   Stream: TMemoryStream;
  1865.   Size: Longint;
  1866.   Buffer: Pointer;
  1867.   Data: THandle;
  1868. begin
  1869.   { !! check for gif clipboard Data, mime type image/gif }
  1870.   Data := GetClipboardData(CF_GIF);
  1871.   if Data <> 0 then begin
  1872.     Buffer := GlobalLock(Data);
  1873.     try
  1874.       Stream := TMemoryStream.Create;
  1875.       try
  1876.         Stream.Write(Buffer^, GlobalSize(Data));
  1877.         Stream.Position := 0;
  1878.         Stream.Read(Size, SizeOf(Size));
  1879.         ReadStream(Size, Stream, False);
  1880.         if Count > 0 then begin
  1881.           FFrameIndex := 0;
  1882.           AData := GetClipboardData(CF_BITMAP);
  1883.           if AData <> 0 then begin
  1884.             Frames[0].NewBitmap;
  1885.             Frames[0].FBitmap.LoadFromClipboardFormat(CF_BITMAP,
  1886.               AData, APalette);
  1887.           end;
  1888.         end;
  1889.       finally
  1890.         Stream.Free;
  1891.       end;
  1892.     finally
  1893.       GlobalUnlock(Data);
  1894.     end;
  1895.   end
  1896.   else begin
  1897.     Bmp := TBitmap.Create;
  1898.     try
  1899.       Bmp.LoadFromClipboardFormat(AFormat, AData, APalette);
  1900.       Assign(Bmp);
  1901.     finally
  1902.       Bmp.Free;
  1903.     end;
  1904.   end;
  1905. end;
  1906. procedure TGIFImage.LoadFromStream(Stream: TStream);
  1907. begin
  1908.   ReadStream(Stream.Size - Stream.Position, Stream, True);
  1909. end;
  1910. procedure TGIFImage.LoadFromResourceName(Instance: THandle; const ResName: string;
  1911.   ResType: PChar);
  1912. var
  1913.   Stream: TStream;
  1914. begin
  1915.   Stream := TResourceStream.Create(Instance, ResName, ResType);
  1916.   try
  1917.     ReadStream(Stream.Size - Stream.Position, Stream, True);
  1918.   finally
  1919.     Stream.Free;
  1920.   end;
  1921. end;
  1922. procedure TGIFImage.LoadFromResourceID(Instance: THandle; ResID: Integer;
  1923.   ResType: PChar);
  1924. var
  1925.   Stream: TStream;
  1926. begin
  1927.   Stream := TResourceStream.CreateFromID(Instance, ResID, ResType);
  1928.   try
  1929.     ReadStream(Stream.Size - Stream.Position, Stream, True);
  1930.   finally
  1931.     Stream.Free;
  1932.   end;
  1933. end;
  1934. procedure TGIFImage.UpdateScreenSize;
  1935. var
  1936.   I: Integer;
  1937. begin
  1938.   FScreenWidth := 0;
  1939.   FScreenHeight := 0;
  1940.   for I := 0 to FItems.Count - 1 do
  1941.     if Frames[I] <> nil then begin
  1942.       FScreenWidth := Max(FScreenWidth, Frames[I].Width +
  1943.         Frames[I].FTopLeft.X);
  1944.       FScreenHeight := Max(FScreenHeight, Frames[I].Height +
  1945.         Frames[I].FTopLeft.Y);
  1946.     end;
  1947. end;
  1948. function TGIFImage.AddFrame(Value: TGraphic): Integer;
  1949. begin
  1950.   FFrameIndex := FItems.Add(TGIFFrame.Create(Self));
  1951.   TGIFFrame(FItems[FFrameIndex]).Assign(Value);
  1952.   if FVersion = gvUnknown then FVersion := gv87a;
  1953.   if FItems.Count > 1 then FVersion := gv89a;
  1954.   Result := FFrameIndex;
  1955. end;
  1956. procedure TGIFImage.DeleteFrame(Index: Integer);
  1957. begin
  1958.   Frames[Index].Free;
  1959.   FItems.Delete(Index);
  1960.   UpdateScreenSize;
  1961.   if FFrameIndex >= FItems.Count then Dec(FFrameIndex);
  1962.   Changed(Self);
  1963. end;
  1964. procedure TGIFImage.MoveFrame(CurIndex, NewIndex: Integer);
  1965. begin
  1966.   FItems.Move(CurIndex, NewIndex);
  1967.   FFrameIndex := NewIndex;
  1968.   Changed(Self);
  1969. end;
  1970. procedure TGIFImage.NewImage;
  1971. begin
  1972.   if FImage <> nil then FImage.Release;
  1973.   FImage := TGIFData.Create;
  1974.   FImage.Reference;
  1975.   if FItems = nil then FItems := TList.Create;
  1976.   ClearItems;
  1977.   FCorrupted := False;
  1978.   FFrameIndex := -1;
  1979.   FBackgroundColor := clNone;
  1980.   FRepeatCount := 1;
  1981.   FLooping := False;
  1982.   FVersion := gvUnknown;
  1983. end;
  1984. procedure TGIFImage.UniqueImage;
  1985. var
  1986.   Temp: TGIFData;
  1987. begin
  1988.   if FImage = nil then NewImage
  1989.   else if FImage.RefCount > 1 then begin
  1990.     Temp := TGIFData.Create;
  1991.     with Temp do
  1992.     try
  1993.       FComment.Assign(FImage.FComment);
  1994.       FAspectRatio := FImage.FAspectRatio;
  1995.       FBitsPerPixel := FImage.FBitsPerPixel;
  1996.       FColorResBits := FImage.FColorResBits;
  1997.       FColorMap := FImage.FColorMap;
  1998.     except
  1999.       Temp.Free;
  2000.       raise;
  2001.     end;
  2002.     FImage.Release;
  2003.     FImage := Temp;
  2004.     FImage.Reference;
  2005.   end;
  2006. end;
  2007. function TGIFImage.GetComment: TStrings;
  2008. begin
  2009.   Result := FImage.FComment;
  2010. end;
  2011. procedure TGIFImage.SetComment(Value: TStrings);
  2012. begin
  2013.   UniqueImage;
  2014.   FImage.FComment.Assign(Value);
  2015. end;
  2016. procedure TGIFImage.DecodeAllFrames;
  2017. var
  2018.   FrameNo, I: Integer;
  2019. begin
  2020.   for FrameNo := 0 to FItems.Count - 1 do
  2021.     try
  2022.       TGIFFrame(FItems[FrameNo]).GetBitmap;
  2023.     except
  2024.       on EAbort do begin { OnProgress can raise EAbort to cancel image load }
  2025.         for I := FItems.Count - 1 downto FrameNo do begin
  2026.           TObject(FItems[I]).Free;
  2027.           FItems.Delete(I);
  2028.         end;
  2029.         FCorrupted := True;
  2030.         Break;
  2031.       end;
  2032.       else raise;
  2033.     end;
  2034. end;
  2035. procedure TGIFImage.EncodeFrames(ReverseDecode: Boolean);
  2036. var
  2037.   FrameNo: Integer;
  2038. begin
  2039.   for FrameNo := 0 to FItems.Count - 1 do
  2040.     with TGIFFrame(FItems[FrameNo]) do begin
  2041.       if (FImage.FImageData = nil) or (FImage.FImageData.Size = 0) then
  2042.       begin
  2043.         FImage.FImageData.Free;
  2044.         FImage.FImageData := nil;
  2045.         EncodeRasterData;
  2046.         if ReverseDecode and (FBitmap.Palette = 0) then begin
  2047.           FBitmap.Free;
  2048.           FBitmap := nil;
  2049.           try
  2050.             GetBitmap;
  2051.           except
  2052.             on EAbort do; { OnProgress can raise EAbort to cancel encoding }
  2053.             else raise;
  2054.           end;
  2055.         end;
  2056.       end;
  2057.       UpdateExtensions;
  2058.     end;
  2059. end;
  2060. procedure TGIFImage.EncodeAllFrames;
  2061. begin
  2062.   EncodeFrames(True);
  2063. end;
  2064. procedure TGIFImage.ReadData(Stream: TStream);
  2065. var
  2066.   Size: Longint;
  2067. begin
  2068.   Stream.Read(Size, SizeOf(Size));
  2069.   ReadStream(Size, Stream, True);
  2070. end;
  2071. procedure TGIFImage.ReadSignature(Stream: TStream);
  2072. var
  2073.   I: TGIFVersion;
  2074.   S: string[3];
  2075. begin
  2076.   FVersion := gvUnknown;
  2077.   SetLength(S, 3);
  2078.   Stream.Read(S[1], 3);
  2079.   if CompareText(GIFSignature, S) <> 0 then GifError(LoadStr(SGIFVersion));
  2080.   SetLength(S, 3);
  2081.   Stream.Read(S[1], 3);
  2082.   for I := Low(TGIFVersion) to High(TGIFVersion) do
  2083.     if CompareText(S, StrPas(GIFVersionStr[I])) = 0 then begin
  2084.       FVersion := I;
  2085.       Break;
  2086.     end;
  2087.   if FVersion = gvUnknown then GifError(LoadStr(SGIFVersion));
  2088. end;
  2089. procedure TGIFImage.ReadStream(Size: Longint; Stream: TStream;
  2090.   ForceDecode: Boolean);
  2091. var
  2092.   SeparatorChar: Char;
  2093.   NewItem: TGIFFrame;
  2094.   Extensions: TList;
  2095.   ScreenDesc: TScreenDescriptor;
  2096.   Data: TMemoryStream;
  2097.   procedure ReadScreenDescriptor(Stream: TStream);
  2098.   begin
  2099.     Stream.Read(ScreenDesc, SizeOf(ScreenDesc));
  2100.     FScreenWidth := ScreenDesc.ScreenWidth;
  2101.     FScreenHeight := ScreenDesc.ScreenHeight;
  2102.     with FImage do begin
  2103.       FAspectRatio := ScreenDesc.AspectRatio;
  2104.       FBitsPerPixel := 1 + (ScreenDesc.PackedFields and
  2105.         LSD_COLOR_TABLE_SIZE);
  2106.       FColorResBits := 1 + (ScreenDesc.PackedFields and
  2107.         LSD_COLOR_RESOLUTION) shr 4;
  2108.     end;
  2109.   end;
  2110.   procedure ReadGlobalColorMap(Stream: TStream);
  2111.   begin
  2112.     if (ScreenDesc.PackedFields and LSD_GLOBAL_COLOR_TABLE) <> 0 then
  2113.       with FImage.FColorMap do begin
  2114.         Count := 1 shl FImage.FBitsPerPixel;
  2115.         Stream.Read(Colors[0], Count * SizeOf(TGIFColorItem));
  2116.         if Count > ScreenDesc.BackgroundColorIndex then
  2117.           FBackgroundColor := ItemToRGB(Colors[ScreenDesc.BackgroundColorIndex]);
  2118.       end;
  2119.   end;
  2120.   function ReadDataBlock(Stream: TStream): TStrings;
  2121.   var
  2122.     BlockSize: Byte;
  2123.     S: string;
  2124.   begin
  2125.     Result := TStringlist.Create;
  2126.     try
  2127.       repeat
  2128.         Stream.Read(BlockSize, SizeOf(Byte));
  2129.         if BlockSize <> 0 then begin
  2130.           SetLength(S, BlockSize);
  2131.           Stream.Read(S[1], BlockSize);
  2132.           Result.Add(S);
  2133.         end;
  2134.       until (BlockSize = 0) or (Stream.Position >= Stream.Size);
  2135.     except
  2136.       Result.Free;
  2137.       raise;
  2138.     end;
  2139.   end;
  2140.   function ReadExtension(Stream: TStream): TExtension;
  2141.   var
  2142.     ExtensionLabel: Byte;
  2143.   begin
  2144.     Result := TExtension.Create;
  2145.     try
  2146.       Stream.Read(ExtensionLabel, SizeOf(Byte));
  2147.       with Result do
  2148.         if ExtensionLabel = ExtLabels[etGraphic] then begin
  2149.           { graphic control extension }
  2150.           FExtType := etGraphic;
  2151.           Stream.Read(FExtRec.GCE, SizeOf(TGraphicControlExtension));
  2152.         end
  2153.         else if ExtensionLabel = ExtLabels[etComment] then begin
  2154.           { comment extension }
  2155.           FExtType := etComment;
  2156.           FData := ReadDataBlock(Stream);
  2157.         end
  2158.         else if ExtensionLabel = ExtLabels[etPlainText] then begin
  2159.           { plain text extension }
  2160.           FExtType := etPlainText;
  2161.           Stream.Read(FExtRec.PTE, SizeOf(TPlainTextExtension));
  2162.           FData := ReadDataBlock(Stream);
  2163.         end
  2164.         else if ExtensionLabel = ExtLabels[etApplication] then begin
  2165.           { application extension }
  2166.           FExtType := etApplication;
  2167.           Stream.Read(FExtRec.APPE, SizeOf(TAppExtension));
  2168.           FData := ReadDataBlock(Stream);
  2169.         end
  2170.         else GifError(Format(LoadStr(SUnrecognizedGIFExt), [ExtensionLabel]));
  2171.     except
  2172.       Result.Free;
  2173.       raise;
  2174.     end;
  2175.   end;
  2176.   function ReadExtensionBlock(Stream: TStream; var SeparatorChar: Char): TList;
  2177.   var
  2178.     NewExt: TExtension;
  2179.   begin
  2180.     Result := nil;
  2181.     try
  2182.       while SeparatorChar = CHR_EXT_INTRODUCER do begin
  2183.         NewExt := ReadExtension(Stream);
  2184.         if (NewExt.FExtType = etPlainText) then begin
  2185.           { plain text data blocks are not supported,
  2186.             clear all previous readed extensions }
  2187.           FreeExtensions(Result);
  2188.           Result := nil;
  2189.         end;
  2190.         if (NewExt.FExtType in [etPlainText, etApplication]) then begin
  2191.           { check for loop extension }
  2192.           if NewExt.IsLoopExtension then begin
  2193.             FLooping := True;
  2194.             FRepeatCount := Min(MakeWord(Byte(NewExt.FData[0][2]),
  2195.               Byte(NewExt.FData[0][3])), MAX_LOOP_COUNT);
  2196.           end;
  2197.           { not supported yet, must be ignored }
  2198.           NewExt.Free;
  2199.         end
  2200.         else begin
  2201.           if Result = nil then Result := TList.Create;
  2202.           Result.Add(NewExt);
  2203.         end;
  2204.         if Stream.Size > Stream.Position then
  2205.           Stream.Read(SeparatorChar, SizeOf(Byte))
  2206.         else SeparatorChar := CHR_TRAILER;
  2207.       end;
  2208.       if (Result <> nil) and (Result.Count = 0) then begin
  2209.         Result.Free;
  2210.         Result := nil;
  2211.       end;
  2212.     except
  2213.       if Result <> nil then Result.Free;
  2214.       raise;
  2215.     end;
  2216.   end;
  2217. var
  2218.   I: Integer;
  2219.   Ext: TExtension;
  2220. begin
  2221.   NewImage;
  2222.   with FImage do begin
  2223.     Data := TMemoryStream.Create;
  2224.     try
  2225.       TMemoryStream(Data).SetSize(Size);
  2226.       Stream.ReadBuffer(Data.Memory^, Size);
  2227.       if Size > 0 then begin
  2228.         Data.Position := 0;
  2229.         ReadSignature(Data);
  2230.         ReadScreenDescriptor(Data);
  2231.         ReadGlobalColorMap(Data);
  2232.         Data.Read(SeparatorChar, SizeOf(Byte));
  2233.         while not (SeparatorChar in [CHR_TRAILER, #0]) and not 
  2234.           (Data.Position >= Data.Size) do
  2235.         begin
  2236.           Extensions := ReadExtensionBlock(Data, SeparatorChar);
  2237.           if SeparatorChar = CHR_IMAGE_SEPARATOR then
  2238.             try
  2239.               NewItem := TGIFFrame.Create(Self);
  2240.               try
  2241.                 if FImage.FColorMap.Count > 0 then
  2242.                   NewItem.FImage.FBitsPerPixel :=
  2243.                     ColorsToBits(FImage.FColorMap.Count);
  2244.                 NewItem.FExtensions := Extensions;
  2245.                 Extensions := nil;
  2246.                 NewItem.LoadFromStream(Data);
  2247.                 FItems.Add(NewItem);
  2248.               except
  2249.                 NewItem.Free;
  2250.                 raise;
  2251.               end;
  2252.               if not (Data.Position >= Data.Size) then begin
  2253.                 Data.Read(SeparatorChar, SizeOf(Byte));
  2254.                 while (SeparatorChar = #0) and (Data.Position < Data.Size) do
  2255.                   Data.Read(SeparatorChar, SizeOf(Byte));
  2256.               end
  2257.               else SeparatorChar := CHR_TRAILER;
  2258.               if not (SeparatorChar in [CHR_EXT_INTRODUCER,
  2259.                 CHR_IMAGE_SEPARATOR, CHR_TRAILER]) then
  2260.               begin
  2261.                 SeparatorChar := #0;
  2262.                 {GifError(LoadStr(SGIFDecodeError));}
  2263.               end;
  2264.             except
  2265.               FreeExtensions(Extensions);
  2266.               raise;
  2267.             end
  2268.           else if (FComment.Count = 0) and (Extensions <> nil) then begin
  2269.             try
  2270.               { trailig extensions }
  2271.               for I := 0 to Extensions.Count - 1 do begin
  2272.                 Ext := TExtension(Extensions[I]);
  2273.                 if (Ext <> nil) and (Ext.FExtType = etComment) then begin
  2274.                   if FComment.Count > 0 then
  2275.                     FComment.Add(#13#10#13#10);
  2276.                   FComment.AddStrings(Ext.FData);
  2277.                 end;
  2278.               end;
  2279.             finally
  2280.               FreeExtensions(Extensions);
  2281.             end;
  2282.           end
  2283.           else if not (SeparatorChar in [CHR_TRAILER, #0]) then
  2284.             GifError(ResStr(SReadError));
  2285.         end;
  2286.       end;
  2287.     finally
  2288.       Data.Free;
  2289.     end;
  2290.   end;
  2291.   if Count > 0 then begin
  2292.     FFrameIndex := 0;
  2293.     if ForceDecode then
  2294.     try
  2295.       GetBitmap; { force bitmap creation }
  2296.     except
  2297.       Frames[0].Free;
  2298.       FItems.Delete(0);
  2299.       raise;
  2300.     end;
  2301.   end;
  2302. {$IFDEF RX_D3}
  2303.   PaletteModified := True;
  2304. {$ENDIF}
  2305.   Changed(Self);
  2306. end;
  2307. procedure TGIFImage.SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
  2308.   var APalette: HPALETTE);
  2309. var
  2310.   Stream: TMemoryStream;
  2311.   Data: THandle;
  2312.   Buffer: Pointer;
  2313.   I: Integer;
  2314. begin
  2315.   { !! check for gif clipboard format, mime type image/gif }
  2316.   if FItems.Count = 0 then Exit;
  2317.   Frames[0].Bitmap.SaveToClipboardFormat(AFormat, AData, APalette);
  2318.   for I := 0 to FItems.Count - 1 do
  2319.     with Frames[I] do begin
  2320.       if (FImage.FImageData = nil) or (FImage.FImageData.Size = 0) then
  2321.         Exit;
  2322.     end;
  2323.   Stream := TMemoryStream.Create;
  2324.   try
  2325.     WriteStream(Stream, True);
  2326.     Stream.Position := 0;
  2327.     Data := GlobalAlloc(HeapAllocFlags, Stream.Size);
  2328.     try
  2329.       if Data <> 0 then begin
  2330.         Buffer := GlobalLock(Data);
  2331.         try
  2332.           Stream.Read(Buffer^, Stream.Size);
  2333.           SetClipboardData(CF_GIF, Data);
  2334.         finally
  2335.           GlobalUnlock(Data);
  2336.         end;
  2337.       end;
  2338.     except
  2339.       GlobalFree(Data);
  2340.       raise;
  2341.     end;
  2342.   finally
  2343.     Stream.Free;
  2344.   end;
  2345. end;
  2346. procedure TGIFImage.WriteData(Stream: TStream);
  2347. begin
  2348.   WriteStream(Stream, True);
  2349. end;
  2350. procedure TGIFImage.SetHeight(Value: Integer);
  2351. begin
  2352.   GifError(LoadStr(SChangeGIFSize));
  2353. end;
  2354. procedure TGIFImage.SetWidth(Value: Integer);
  2355. begin
  2356.   GifError(LoadStr(SChangeGIFSize));
  2357. end;
  2358. procedure TGIFImage.WriteStream(Stream: TStream; WriteSize: Boolean);
  2359. var
  2360.   Separator: Char;
  2361.   Temp: Byte;
  2362.   FrameNo: Integer;
  2363.   Frame: TGIFFrame;
  2364.   Mem: TMemoryStream;
  2365.   Size: Longint;
  2366.   StrList: TStringList;
  2367.   procedure WriteSignature(Stream: TStream);
  2368.   var
  2369.     Header: TGIFHeader;
  2370.   begin
  2371.     Header.Signature := GIFSignature;
  2372.     Move(GIFVersionStr[FVersion][0], Header.Version[0], 3);
  2373.     Stream.Write(Header, SizeOf(TGIFHeader));
  2374.   end;
  2375.   procedure WriteScreenDescriptor(Stream: TStream);
  2376.   var
  2377.     ColorResBits: Byte;
  2378.     ScreenDesc: TScreenDescriptor;
  2379.     I: Integer;
  2380.   begin
  2381.     UpdateScreenSize;
  2382.     with ScreenDesc do begin
  2383.       ScreenWidth := Self.FScreenWidth;
  2384.       ScreenHeight := Self.FScreenHeight;
  2385.       AspectRatio := FImage.FAspectRatio;
  2386.       PackedFields := 0;
  2387.       BackgroundColorIndex := 0;
  2388.       if FImage.FColorMap.Count > 0 then begin
  2389.         PackedFields := PackedFields or LSD_GLOBAL_COLOR_TABLE;
  2390.         ColorResBits := ColorsToBits(FImage.FColorMap.Count);
  2391.         if FBackgroundColor <> clNone then
  2392.           for I := 0 to FImage.FColorMap.Count - 1 do
  2393.             if ColorToRGB(FBackgroundColor) =
  2394.               ItemToRGB(FImage.FColorMap.Colors[I]) then
  2395.             begin
  2396.               BackgroundColorIndex := I;
  2397.               Break;
  2398.             end;
  2399.         PackedFields := PackedFields + ((ColorResBits - 1) shl 4) +
  2400.           (FImage.FBitsPerPixel - 1);
  2401.       end;
  2402.     end;
  2403.     Stream.Write(ScreenDesc, SizeOf(ScreenDesc));
  2404.   end;
  2405.   procedure WriteDataBlock(Stream: TStream; Data: TStrings);
  2406.   var
  2407.     I: Integer;
  2408.     S: string;
  2409.     BlockSize: Byte;
  2410.   begin
  2411.     for I := 0 to Data.Count - 1 do begin
  2412.       S := Data[I];
  2413.       BlockSize := Min(Length(S), 255);
  2414.       if BlockSize > 0 then begin
  2415.         Stream.Write(BlockSize, SizeOf(Byte));
  2416.         Stream.Write(S[1], BlockSize);
  2417.       end;
  2418.     end;
  2419.     BlockSize := 0;
  2420.     Stream.Write(BlockSize, SizeOf(Byte));
  2421.   end;
  2422.   procedure WriteExtensionBlock(Stream: TStream; Extensions: TList);
  2423.   var
  2424.     I: Integer;
  2425.     Ext: TExtension;
  2426.     ExtensionLabel: Byte;
  2427.     SeparateChar: Char;
  2428.   begin
  2429.     SeparateChar := CHR_EXT_INTRODUCER;
  2430.     for I := 0 to Extensions.Count - 1 do begin
  2431.       Ext := TExtension(Extensions[I]);
  2432.       if Ext <> nil then begin
  2433.         Stream.Write(SeparateChar, SizeOf(Byte));
  2434.         ExtensionLabel := ExtLabels[Ext.FExtType];
  2435.         Stream.Write(ExtensionLabel, SizeOf(Byte));
  2436.         case Ext.FExtType of
  2437.           etGraphic:
  2438.             begin
  2439.               Stream.Write(Ext.FExtRec.GCE, SizeOf(TGraphicControlExtension));
  2440.             end;
  2441.           etComment: WriteDataBlock(Stream, Ext.FData);
  2442.           etPlainText:
  2443.             begin
  2444.               Stream.Write(Ext.FExtRec.PTE, SizeOf(TPlainTextExtension));
  2445.               WriteDataBlock(Stream, Ext.FData);
  2446.             end;
  2447.           etApplication:
  2448.             begin
  2449.               Stream.Write(Ext.FExtRec.APPE, SizeOf(TAppExtension));
  2450.               WriteDataBlock(Stream, Ext.FData);
  2451.             end;
  2452.         end;
  2453.       end;
  2454.     end;
  2455.   end;
  2456. begin
  2457.   if FItems.Count = 0 then GifError(LoadStr(SNoGIFData));
  2458.   EncodeFrames(False);
  2459.   Mem := TMemoryStream.Create;
  2460.   try
  2461.     if FImage.FComment.Count > 0 then FVersion := gv89a;
  2462.     WriteSignature(Mem);
  2463.     WriteScreenDescriptor(Mem);
  2464.     if FImage.FColorMap.Count > 0 then begin
  2465.       with FImage.FColorMap do
  2466.         Mem.Write(Colors[0], Count * SizeOf(TGIFColorItem));
  2467.     end;
  2468.     if FLooping and (FItems.Count > 1) then begin
  2469.       { write looping extension }
  2470.       Separator := CHR_EXT_INTRODUCER;
  2471.       Mem.Write(Separator, SizeOf(Byte));
  2472.       Temp := ExtLabels[etApplication];
  2473.       Mem.Write(Temp, SizeOf(Byte));
  2474.       Temp := SizeOf(TAppExtension) - SizeOf(Byte);
  2475.       Mem.Write(Temp, SizeOf(Byte));
  2476.       Mem.Write(LoopExtNS[1], Temp);
  2477.       StrList := TStringList.Create;
  2478.       try
  2479.         StrList.Add(Char(AE_LOOPING) + Char(LoByte(FRepeatCount)) +
  2480.           Char(HiByte(FRepeatCount)));
  2481.         WriteDataBlock(Mem, StrList);
  2482.       finally
  2483.         StrList.Free;
  2484.       end;
  2485.     end;
  2486.     Separator := CHR_IMAGE_SEPARATOR;
  2487.     for FrameNo := 0 to FItems.Count - 1 do begin
  2488.       Frame := TGIFFrame(FItems[FrameNo]);
  2489.       if Frame.FExtensions <> nil then
  2490.         WriteExtensionBlock(Mem, Frame.FExtensions);
  2491.       Mem.Write(Separator, SizeOf(Byte));
  2492.       Frame.WriteImageDescriptor(Mem);
  2493.       Frame.WriteLocalColorMap(Mem);
  2494.       Frame.WriteRasterData(Mem);
  2495.     end;
  2496.     if FImage.FComment.Count > 0 then begin
  2497.       Separator := CHR_EXT_INTRODUCER;
  2498.       Mem.Write(Separator, SizeOf(Byte));
  2499.       Temp := ExtLabels[etComment];
  2500.       Mem.Write(Temp, SizeOf(Byte));
  2501.       WriteDataBlock(Mem, FImage.FComment);
  2502.     end;
  2503.     Separator := CHR_TRAILER;
  2504.     Mem.Write(Separator, SizeOf(Byte));
  2505.     Size := Mem.Size;
  2506.     if WriteSize then Stream.Write(Size, SizeOf(Size));
  2507.     Stream.Write(Mem.Memory^, Size);
  2508.   finally
  2509.     Mem.Free;
  2510.   end;
  2511. end;
  2512. procedure TGIFImage.Grayscale(ForceEncoding: Boolean);
  2513. var
  2514.   I: Integer;
  2515. begin
  2516.   if FItems.Count = 0 then GifError(LoadStr(SNoGIFData));
  2517.   for I := 0 to FItems.Count - 1 do
  2518.     Frames[I].GrayscaleImage(ForceEncoding);
  2519.   if FBackgroundColor <> clNone then begin
  2520.     if FImage.FColorMap.Count > 0 then begin
  2521.       I := FindColorIndex(FImage.FColorMap, FBackgroundColor);
  2522.       GrayColorTable(FImage.FColorMap);
  2523.       if I >= 0 then
  2524.         FBackgroundColor := ItemToRGB(FImage.FColorMap.Colors[I])
  2525.       else FBackgroundColor := GrayColor(FBackgroundColor);
  2526.     end
  2527.     else FBackgroundColor := GrayColor(FBackgroundColor);
  2528.   end;
  2529. {$IFDEF RX_D3}
  2530.   PaletteModified := True;
  2531. {$ENDIF}
  2532.   Changed(Self);
  2533. end;
  2534. procedure TGIFImage.SaveToStream(Stream: TStream);
  2535. begin
  2536.   WriteStream(Stream, False);
  2537. end;
  2538. procedure TGIFImage.DoProgress(Stage: TProgressStage; PercentDone: Byte;
  2539.   const Msg: string);
  2540. begin
  2541.   Progress(Self, Stage, PercentDone, False, Rect(0, 0, 0, 0), Msg);
  2542. end;
  2543. {$IFNDEF RX_D3}
  2544. procedure TGIFImage.Progress(Sender: TObject; Stage: TProgressStage;
  2545.   PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string);
  2546. begin
  2547.   if Assigned(FOnProgress) then
  2548.     FOnProgress(Sender, Stage, PercentDone, RedrawNow, R, Msg);
  2549. end;
  2550. {$ENDIF}
  2551. initialization
  2552.   CF_GIF := RegisterClipboardFormat('GIF Image');
  2553.   RegisterClasses([TGIFFrame, TGIFImage]);
  2554. {$IFDEF USE_RX_GIF}
  2555.   TPicture.RegisterFileFormat('gif', LoadStr(SGIFImage), TGIFImage);
  2556.   TPicture.RegisterClipboardFormat(CF_GIF, TGIFImage);
  2557.  {$IFDEF RX_D3}
  2558. finalization
  2559.   TPicture.UnRegisterGraphicClass(TGIFImage);
  2560.  {$ENDIF}
  2561. {$ENDIF}
  2562. end.