home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 September / Chip_2001-09_cd1.bin / zkuste / delphi / kolekce / d6 / RX275D6.ZIP / Units / Rxgif.pas < prev    next >
Pascal/Delphi Source File  |  2001-06-24  |  83KB  |  2,763 lines

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