Browse Source

UPD: Use GifView component instead GifAnim (fixes #814)

Alexander Koblov 2 months ago
parent
commit
ceb271de20

+ 1 - 1
components/build.bat

@@ -13,7 +13,7 @@ lazbuild doublecmd\doublecmd_common.lpk %DC_ARCH%
 lazbuild Image32\Image32.lpk %DC_ARCH%
 lazbuild KASToolBar\kascomp.lpk %DC_ARCH%
 lazbuild viewer\viewerpackage.lpk %DC_ARCH%
-lazbuild gifanim\pkg_gifanim.lpk %DC_ARCH%
+lazbuild gifview\gifview.lpk %DC_ARCH%
 lazbuild synunihighlighter\synuni.lpk %DC_ARCH%
 lazbuild virtualterminal\virtualterminal.lpk %DC_ARCH%
 popd

+ 1 - 1
components/build.sh

@@ -31,7 +31,7 @@ $lazbuild doublecmd/doublecmd_common.lpk $DC_ARCH
 $lazbuild Image32/Image32.lpk $DC_ARCH
 $lazbuild KASToolBar/kascomp.lpk $DC_ARCH
 $lazbuild viewer/viewerpackage.lpk $DC_ARCH
-$lazbuild gifanim/pkg_gifanim.lpk $DC_ARCH
+$lazbuild gifview/gifview.lpk $DC_ARCH
 $lazbuild synunihighlighter/synuni.lpk $DC_ARCH
 $lazbuild virtualterminal/virtualterminal.lpk $DC_ARCH
 cd $basedir

+ 0 - 370
components/gifanim/doublecmd.diff

@@ -1,370 +0,0 @@
-Index: gifanim.pas
-===================================================================
---- gifanim.pas	(revision none)
-+++ gifanim.pas	(working copy)
-@@ -26,7 +26,7 @@
- 
- uses
-   Classes, LCLProc, Lresources, SysUtils, Controls, Graphics, ExtCtrls,
--  IntfGraphics, FPimage, Contnrs, GraphType, dialogs;
-+  IntfGraphics, FPimage, Contnrs, GraphType, dialogs, types;
- 
- const
- 
-@@ -193,7 +193,7 @@
-     procedure DoAutoSize; override;
-     procedure DoStartAnim;
-     procedure DoStopAnim;
--    class function GetControlClassDefaultSize: TPoint; override;
-+    class function GetControlClassDefaultSize: TSize; override;
-     procedure GifChanged;
-     procedure LoadFromFile(const Filename: string); virtual;
-     procedure Paint; override;
-@@ -203,6 +203,8 @@
-     { Public declarations }
-     constructor Create(AOwner: TComponent); override;
-     destructor Destroy; override;
-+    procedure NextFrame;
-+    procedure PriorFrame;
-     property Empty: boolean Read FEmpty;
-     property GifBitmaps: TGifList Read FGifBitmaps;
-     property GifIndex: integer Read FCurrentImage;
-@@ -237,28 +239,9 @@
- 
- implementation
- 
--uses LazIDEIntf, propedits;
--Type
--  TGifFileNamePropertyEditor=class(TFileNamePropertyEditor)
--  protected
--    function GetFilter: String; override;
--    function GetInitialDirectory: string; override;
--  end;
--function TGifFileNamePropertyEditor.GetFilter: String;
--begin
--  Result := 'GIF|*.gif';
--end;
--
--function TGifFileNamePropertyEditor.GetInitialDirectory: string;
--begin
--  Result:= ExtractFilePath(LazarusIDE.ActiveProject.ProjectInfoFile);
--end;
--
- procedure Register;
- begin
-   RegisterComponents('Wile64', [TGifAnim]);
--  RegisterPropertyEditor(TypeInfo(String),
--    TGifAnim, 'FileName', TGifFileNamePropertyEditor);
- end;
- 
- { TGifAnim }
-@@ -268,7 +251,7 @@
-   inherited Create(AOwner);
-   ControlStyle := [csCaptureMouse, csClickEvents, csDoubleClicks];
-   AutoSize     := True;
--  SetInitialBounds(0, 0, GetControlClassDefaultSize.X, GetControlClassDefaultSize.Y);
-+  SetInitialBounds(0, 0, GetControlClassDefaultSize.CX, GetControlClassDefaultSize.CY);
-   FEmpty      := True;
-   FCurrentImage := 0;
-   CurrentView := TBitmap.Create;
-@@ -295,6 +278,59 @@
-   CurrentView.Free;
- end;
- 
-+procedure TGifAnim.NextFrame;
-+begin
-+  if (not FEmpty) and Visible and (not FAnimate) then
-+  begin
-+    if FCurrentImage >= GifBitmaps.Count - 1 then
-+      FCurrentImage := 0
-+    else
-+      Inc(FCurrentImage);
-+    if Assigned(FOnFrameChanged) then
-+      FOnFrameChanged(Self);
-+    Repaint;
-+  end;
-+end;
-+
-+procedure TGifAnim.PriorFrame;
-+var
-+  DesiredImage: Integer;
-+begin
-+  if (not FEmpty) and Visible and (not FAnimate) then
-+  begin
-+    if FCurrentImage = 0 then
-+      DesiredImage:= GifBitmaps.Count - 1
-+    else
-+      DesiredImage:= FCurrentImage - 1;
-+    // For proper display repaint image from first frame to desired frame
-+    FCurrentImage:= 0;
-+    while FCurrentImage < DesiredImage do
-+    begin
-+      with GifBitmaps.Items[FCurrentImage] do
-+        begin
-+          BufferImg.Canvas.Brush.Color := (Self.Color);
-+          if FCurrentImage = 0 then
-+            BufferImg.Canvas.FillRect(Rect(0, 0, Width, Height));
-+          if Delay <> 0 then
-+            FWait.Interval := Delay * 10;
-+          BufferImg.Canvas.Draw(PosX, PosY, Bitmap);
-+          case Method of
-+            //0 : Not specified...
-+            //1 : No change Background
-+            2: BufferImg.Canvas.FillRect(
-+                Rect(PosX, PosY, Bitmap.Width + PosX, Bitmap.Height + PosY));
-+
-+            3: BufferImg.Canvas.FillRect(Rect(0, 0, Width, Height));
-+          end;
-+        end;
-+      Inc(FCurrentImage);
-+    end;
-+    if Assigned(FOnFrameChanged) then
-+      FOnFrameChanged(Self);
-+    Repaint;
-+  end;
-+end;
-+
- function TGifAnim.LoadFromLazarusResource(const ResName: String): boolean;
- var
-   GifLoader: TGifLoader;
-@@ -340,12 +376,13 @@
- begin
-   if (not Empty) and Visible then
-   begin
--    if FCurrentImage > GifBitmaps.Count - 1 then
--      FCurrentImage := 0;
--    if assigned(FOnFrameChanged) then
--      FOnFrameChanged(self);
--    Paint;
--    Inc(FCurrentImage);
-+    if FCurrentImage >= GifBitmaps.Count - 1 then
-+      FCurrentImage := 0
-+    else
-+      Inc(FCurrentImage);
-+    if Assigned(FOnFrameChanged) then
-+      FOnFrameChanged(Self);
-+    Repaint;
-   end;
- end;
- 
-@@ -365,27 +402,12 @@
- end;
- 
- procedure TGifAnim.SetFileName(const AValue: string);
--var
--  fn: string;
- begin
--
--  if (FFileName = AValue) then
--    exit;
-+  if (FFileName = AValue) then Exit;
-   FFileName := AValue;
-   ResetImage;
--  if (FFileName = '') then exit;
--  if (csDesigning in ComponentState) then
--  begin
--     fn:= ExtractFileName(AValue);
--     FFileName:= ExtractFilePath(AValue);
--     FFileName:= ExtractRelativepath(ExtractFilePath(LazarusIDE.ActiveProject.ProjectInfoFile) ,FFileName);
--     FFileName:=FFileName+fn;
--     LoadFromFile(FFileName+fn);
--  end
--  else begin
--     FFileName := AValue;
--     LoadFromFile(FFileName);
--  end;
-+  if (FFileName = '') then Exit;
-+  LoadFromFile(FFileName);
-   if not Empty then
-     GifChanged;
- end;
-@@ -441,10 +463,10 @@
-   end;
- end;
- 
--class function TGifAnim.GetControlClassDefaultSize: TPoint;
-+class function TGifAnim.GetControlClassDefaultSize: TSize;
- begin
--  Result.X := 90;
--  Result.Y := 90;
-+  Result.CX := 90;
-+  Result.CY := 90;
- end;
- 
- procedure TGifAnim.GifChanged;
-Index: gifanimdsgn.pas
-===================================================================
---- gifanimdsgn.pas	(revision 0)
-+++ gifanimdsgn.pas	(revision 0)
-@@ -0,0 +1,41 @@
-+unit GifAnimDsgn;
-+
-+{$mode objfpc}{$H+}
-+
-+interface
-+
-+uses
-+  LazIDEIntf, PropEdits;
-+
-+Type
-+  TGifFileNamePropertyEditor = class(TFileNamePropertyEditor)
-+  protected
-+    function GetFilter: String; override;
-+    function GetInitialDirectory: string; override;
-+  end;
-+
-+procedure Register;
-+
-+implementation
-+
-+uses
-+  SysUtils, GifAnim;
-+
-+function TGifFileNamePropertyEditor.GetFilter: String;
-+begin
-+  Result := 'GIF|*.gif';
-+end;
-+
-+function TGifFileNamePropertyEditor.GetInitialDirectory: string;
-+begin
-+  Result:= ExtractFilePath(LazarusIDE.ActiveProject.ProjectInfoFile);
-+end;
-+
-+procedure Register;
-+begin
-+  RegisterPropertyEditor(TypeInfo(String), TGifAnim,
-+                         'FileName', TGifFileNamePropertyEditor);
-+end;
-+
-+end.
-+
-Index: pkg_gifanim.lpk
-===================================================================
---- pkg_gifanim.lpk	(revision none)
-+++ pkg_gifanim.lpk	(working copy)
-@@ -1,15 +1,21 @@
--<?xml version="1.0"?>
-+<?xml version="1.0" encoding="UTF-8"?>
- <CONFIG>
--  <Package Version="3">
-+  <Package Version="4">
-     <PathDelim Value="\"/>
-     <Name Value="pkg_gifanim"/>
-+    <AddToProjectUsesSection Value="True"/>
-     <Author Value="Laurent Jacques"/>
-     <CompilerOptions>
--      <Version Value="8"/>
-+      <Version Value="11"/>
-       <PathDelim Value="\"/>
-       <SearchPaths>
--        <OtherUnitFiles Value="$(LazarusDir)\ide\"/>
-+        <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
-       </SearchPaths>
-+      <Linking>
-+        <Debugging>
-+          <DebugInfoType Value="dsDwarf2Set"/>
-+        </Debugging>
-+      </Linking>
-       <Other>
-         <CompilerPath Value="$(CompPath)"/>
-       </Other>
-@@ -33,15 +39,16 @@
-     <Type Value="RunAndDesignTime"/>
-     <RequiredPkgs Count="2">
-       <Item1>
--        <PackageName Value="FCL"/>
-+        <PackageName Value="LCL"/>
-         <MinVersion Major="1" Valid="True"/>
-       </Item1>
-       <Item2>
--        <PackageName Value="IDEIntf"/>
-+        <PackageName Value="FCL"/>
-+        <MinVersion Major="1" Valid="True"/>
-       </Item2>
-     </RequiredPkgs>
-     <UsageOptions>
--      <UnitPath Value="$(PkgOutDir)\"/>
-+      <UnitPath Value="$(PkgOutDir)"/>
-     </UsageOptions>
-     <PublishOptions>
-       <Version Value="2"/>
-Index: pkg_gifanim_dsgn.lpk
-===================================================================
---- pkg_gifanim_dsgn.lpk	(revision 0)
-+++ pkg_gifanim_dsgn.lpk	(revision 0)
-@@ -0,0 +1,49 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<CONFIG>
-+  <Package Version="4">
-+    <PathDelim Value="\"/>
-+    <Name Value="pkg_gifanim_dsgn"/>
-+    <CompilerOptions>
-+      <Version Value="11"/>
-+      <PathDelim Value="\"/>
-+      <SearchPaths>
-+        <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
-+      </SearchPaths>
-+      <Linking>
-+        <Debugging>
-+          <DebugInfoType Value="dsDwarf2Set"/>
-+        </Debugging>
-+      </Linking>
-+      <Other>
-+        <CompilerMessages>
-+          <MsgFileName Value=""/>
-+        </CompilerMessages>
-+        <CompilerPath Value="$(CompPath)"/>
-+      </Other>
-+    </CompilerOptions>
-+    <License Value="GPL"/>
-+    <Version Major="1" Minor="4"/>
-+    <Files Count="1">
-+      <Item1>
-+        <Filename Value="gifanimdsgn.pas"/>
-+        <HasRegisterProc Value="True"/>
-+        <UnitName Value="GifAnimDsgn"/>
-+      </Item1>
-+    </Files>
-+    <Type Value="DesignTime"/>
-+    <RequiredPkgs Count="2">
-+      <Item1>
-+        <PackageName Value="IDEIntf"/>
-+      </Item1>
-+      <Item2>
-+        <PackageName Value="pkg_gifanim"/>
-+      </Item2>
-+    </RequiredPkgs>
-+    <UsageOptions>
-+      <UnitPath Value="$(PkgOutDir)"/>
-+    </UsageOptions>
-+    <PublishOptions>
-+      <Version Value="2"/>
-+    </PublishOptions>
-+  </Package>
-+</CONFIG>
-Index: pkg_gifanim_dsgn.pas
-===================================================================
---- pkg_gifanim_dsgn.pas	(revision 0)
-+++ pkg_gifanim_dsgn.pas	(revision 0)
-@@ -0,0 +1,21 @@
-+{ This file was automatically created by Lazarus. Do not edit!
-+  This source is only used to compile and install the package.
-+ }
-+
-+unit pkg_gifanim_dsgn;
-+
-+interface
-+
-+uses
-+  GifAnimDsgn, LazarusPackageIntf;
-+
-+implementation
-+
-+procedure Register;
-+begin
-+  RegisterUnit('GifAnimDsgn', @GifAnimDsgn.Register);
-+end;
-+
-+initialization
-+  RegisterPackage('pkg_gifanim_dsgn', @Register);
-+end.

+ 0 - 25
components/gifanim/gifanim.lrs

@@ -1,25 +0,0 @@
-LazarusResources.Add('tgifanim','XPM',[
-  '/* XPM */'#13#10'static char * gifanim_xpm[] = {'#13#10'"24 24 31 1",'#13#10
-  +'" '#9'c None",'#13#10'".'#9'c #959595",'#13#10'"+'#9'c #E1E1E1",'#13#10'"@'
-  +#9'c #919191",'#13#10'"#'#9'c #848484",'#13#10'"$'#9'c #888888",'#13#10'"%'#9
-  +'c #EEEEEE",'#13#10'"&'#9'c #E6E9EC",'#13#10'"*'#9'c #D6DFE8",'#13#10'"='#9
-  +'c #FFFFFF",'#13#10'"-'#9'c #E7F0F9",'#13#10'";'#9'c #3783CE",'#13#10'">'#9
-  +'c #FFF7F7",'#13#10'",'#9'c #FFEFEF",'#13#10'"'''#9'c #F7F2F5",'#13#10'")'#9
-  +'c #F7FAFD",'#13#10'"!'#9'c #FF5757",'#13#10'"~'#9'c #FFDFDF",'#13#10'"{'#9
-  +'c #FF4F4F",'#13#10'"]'#9'c #FFD7D7",'#13#10'"^'#9'c #FF4747",'#13#10'"/'#9
-  +'c #FF3F3F",'#13#10'"('#9'c #8D8D8D",'#13#10'"_'#9'c #EEE6E6",'#13#10'":'#9
-  +'c #EED6D6",'#13#10'"<'#9'c #EECECE",'#13#10'"['#9'c #FAFAFA",'#13#10'"}'#9
-  +'c #F2F2F2",'#13#10'"|'#9'c #F6F6F6",'#13#10'"1'#9'c #A2A2A2",'#13#10'"2'#9
-  +'c #FAF2F2",'#13#10'" .++@              @++. ",'#13#10'" @@@##$$$$$$$$$$$$##'
-  +'@@@ ",'#13#10'" .++$#$$$$$$$$$$$$#$++. ",'#13#10'" .++@+%%&**&&**&%%+@++. "'
-  +','#13#10'" @@@@%==-;;--;;-==%@@@@ ",'#13#10'" .++.%==-;;--;;-==%.++. ",'#13
-  +#10'" .++.%>,''--))--'',>%.++. ",'#13#10'" @@@@%,!~>====>~!,%@@@@ ",'#13#10
-  +'" .++.%>~{]~~~~]{~>%.++. ",'#13#10'" .++.%=>~^////^~>=%.++. ",'#13#10'" @@@'
-  +'(+%%_:<<<<:_%%+(@@@ ",'#13#10'" .++$#$$$$$$$$$$$$#$++. ",'#13#10'" .++$#$$$'
-  +'$$$$$$$$$#$++. ",'#13#10'" @@@(+%%&**&%%%%%%+(@@@ ",'#13#10'" .++.%==-;;-[}'
-  +'}[==%.++. ",'#13#10'" .++.%==-;;-|11|==%.++. ",'#13#10'" @@@@%>,''--)[}}2,>'
-  +'%@@@@ ",'#13#10'" .++.%,!~>====>~!,%.++. ",'#13#10'" .++.%>~{]~~~~]{~>%.++.'
-  +' ",'#13#10'" @@@@%=>~^////^~>=%@@@@ ",'#13#10'" .++@+%%_:<<<<:_%%+@++. ",'
-  +#13#10'" .++$#$$$$$$$$$$$$#$++. ",'#13#10'" @@@##$$$$$$$$$$$$##@@@ ",'#13#10
-  +'" .++@              @++. "};'#13#10
-]);

+ 0 - 1117
components/gifanim/gifanim.pas

@@ -1,1117 +0,0 @@
-{
-  Copyright (C) 2009 Laurent Jacques
-  Copyright (C) 2012-2022 Alexander Koblov
-
-  This source is free software; you can redistribute it and/or modify it under
-  the terms of the GNU General Public License as published by the Free
-  Software Foundation; either version 2 of the License, or (at your option)
-  any later version.
-
-  This code is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
-  details.
-
-  A copy of the GNU General Public License is available on the World Wide Web
-  at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
-  to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
-  MA 02111-1307, USA.
-}
-
-unit GifAnim;
-
-{$mode objfpc}{$H+}
-
-interface
-
-uses
-  Classes, LCLProc, Lresources, SysUtils, Controls, Graphics, ExtCtrls,
-  IntfGraphics, FPimage, Contnrs, GraphType, dialogs, types;
-
-const
-
-  EXT_INTRODUCER  = $21;
-  EXT_GRAPHICS_CONTROL = $F9;
-  EXT_PLAIN_TEXT  = $01;
-  EXT_APPLICATION = $FF;
-  EXT_COMMENT     = $FE;
-
-  DSC_LOCAL_IMAGE = $2C;
-
-  ID_TRANSPARENT = $01;
-  ID_COLOR_TABLE_SIZE = $07;
-  ID_SORT    = $20;
-  ID_INTERLACED = $40;
-  ID_COLOR_TABLE = $80;
-  ID_IMAGE_DESCRIPTOR = $2C;
-  ID_TRAILER = $3B;
-
-  CODE_TABLE_SIZE = 4096;
-
-type
-  TRGB = packed record
-    Red, Green, Blue: byte;
-  end;
-
-  TGIFHeader = packed record
-    Signature:    array[0..2] of char;  //* Header Signature (always "GIF") */
-    Version:      array[0..2] of char;  //* GIF format version("87a" or "89a") */
-    ScreenWidth:  word;                 //* Width of Display Screen in Pixels */
-    ScreenHeight: word;                 //* Height of Display Screen in Pixels */
-    Packedbit,                          //* Screen and Color Map Information */
-    BackgroundColor,                    //* Background Color Index */
-    AspectRatio:  byte;                 //* Pixel Aspect Ratio */
-  end;
-
-  TGifImageDescriptor = packed record
-    Left,                 //* X position of image on the display */
-    Top,                  //* Y position of image on the display */
-    Width,                //* Width of the image in pixels */
-    Height:    word;      //* Height of the image in pixels */
-    Packedbit: byte;      //* Image and Color Table Data Information */
-  end;
-
-  TGifGraphicsControlExtension = packed record
-    BlockSize,          //* Size of remaining fields (always 04h) */
-    Packedbit:  byte;   //* Method of graphics disposal to use */
-    DelayTime:  word;   //* Hundredths of seconds to wait  */
-    ColorIndex,         //* Transparent Color Index */
-    Terminator: byte;   //* Block Terminator (always 0) */
-  end;
-
-  TGifAnim = class;
-
-  { TGifImage }
-
-  TGifImage = class
-  private
-    FBitmap: TBitmap;
-    FPosX:   word;
-    FPosY:   word;
-    FDelay:  word;
-    FMethod: byte;
-  public
-    constructor Create;
-    destructor Destroy; override;
-    property Bitmap: TBitmap Read FBitmap;
-    property Delay: word Read FDelay;
-    property Method: byte Read FMethod;
-    property PosX: word Read FPosX;
-    property PosY: word Read FPosY;
-  end;
-
-  { TGifList }
-
-  TGifList = class(TObjectList)
-  private
-  protected
-    function GetItems(Index: integer): TGifImage;
-    procedure SetItems(Index: integer; AGifImage: TGifImage);
-  public
-    function Add(AGifImage: TGifImage): integer;
-    function Extract(Item: TGifImage): TGifImage;
-    function Remove(AGifImage: TGifImage): integer;
-    function IndexOf(AGifImage: TGifImage): integer;
-    function First: TGifImage;
-    function Last: TGifImage;
-    procedure Insert(Index: integer; AGifImage: TGifImage);
-    property Items[Index: integer]: TGifImage Read GetItems Write SetItems; default;
-  end;
-
-
-  { TGifLoader }
-
-  TGifLoader = class
-  private
-    FGifHeader:  TGIFHeader;
-    FGifDescriptor: TGifImageDescriptor;
-    FGifGraphicsCtrlExt: TGifGraphicsControlExtension;
-    FGifUseGraphCtrlExt: boolean;
-    FGifBackgroundColor: byte;
-    FInterlaced: boolean;
-    FScanLine:   PByte;
-    FLineSize:   integer;
-    FDisposalMethod: byte;
-    FEmpty:      boolean;
-    FFileName:   string;
-    FHeight:     integer;
-    FIsTransparent: boolean;
-    FWidth:      integer;
-    FPalette:    TFPPalette;
-    FLocalHeight: integer;
-    FLocalWidth: integer;
-    procedure ReadPalette(Stream: TStream; Size: integer);
-    procedure ReadScanLine(Stream: TStream);
-    procedure ReadHeader(Stream: TStream);
-    procedure ReadGlobalPalette(Stream: TStream);
-    procedure ReadGraphCtrlExt;
-    procedure SetInterlaced(const AValue: boolean);
-    procedure SetTransparent(const AValue: boolean);
-    function SkipBlock(Stream: TStream): byte;
-    procedure WriteScanLine(Img: TFPCustomImage);
-    procedure ReadGifBitmap(Stream: TStream);
-  public
-    constructor Create(const FileName: string);
-    destructor Destroy; override;
-    function LoadFromStream(GifStream: TStream; var AGifList: TGifList): boolean;
-    function LoadAllBitmap(var AGifList: TGifList): boolean;
-    function LoadFromLazarusResource(const ResName: String; var AGifList: TGifList): boolean;
-    function LoadFirstBitmap(var ABitmap: TBitmap): boolean;
-    property Empty: boolean Read FEmpty;
-    property Height: integer Read FHeight;
-    property Width: integer Read FWidth;
-    property IsTransparent: boolean Read FIsTransparent Write SetTransparent;
-    property Interlaced: boolean Read FInterlaced Write SetInterlaced;
-  end;
-
-  { TGifAnim }
-
-  TGifAnim = class(TGraphicControl)
-  private
-    { Private declarations }
-    FAnimate:   boolean;
-    FEmpty:     boolean;
-    FFileName:  string;
-    FGifBitmaps: TGifList;
-    FOnFrameChanged: TNotifyEvent;
-    FOnStart:   TNotifyEvent;
-    FOnStop:    TNotifyEvent;
-    FWait:      TTimer;
-    FCurrentImage: integer;
-    FGifHeight: integer;
-    FGifWidth:  integer;
-    procedure OnTime(Sender: TObject);
-    procedure SetAnimate(const AValue: boolean);
-    procedure SetFileName(const AValue: string);
-    procedure DefineSize(AWidth, AHeight: integer);
-  protected
-    { Protected declarations }
-    BufferImg:   TBitmap;
-    CurrentView: TBitmap;
-    procedure CalculatePreferredSize(
-      var PreferredWidth, PreferredHeight: integer;
-      WithThemeSpace: boolean); override;
-    procedure DoAutoSize; override;
-    procedure DoStartAnim;
-    procedure DoStopAnim;
-    class function GetControlClassDefaultSize: TSize; override;
-    procedure GifChanged;
-    procedure LoadFromFile(const Filename: string); virtual;
-    procedure Paint; override;
-    procedure ResetImage;
-    procedure SetColor(Value: TColor); override;
-  public
-    { Public declarations }
-    constructor Create(AOwner: TComponent); override;
-    destructor Destroy; override;
-    procedure NextFrame;
-    procedure PriorFrame;
-    property Empty: boolean Read FEmpty;
-    property GifBitmaps: TGifList Read FGifBitmaps;
-    property GifIndex: integer Read FCurrentImage;
-    function LoadFromLazarusResource(const ResName: String): boolean;
-  published
-    { Published declarations }
-    property Anchors;
-    property AutoSize default True;
-    property Animate: boolean Read FAnimate Write SetAnimate default True;
-    property BorderSpacing;
-    property Color default clBtnFace;
-    property Constraints;
-    property FileName: string Read FFileName Write SetFileName;
-    property Height;
-    property OnClick;
-    property OnDblClick;
-    property OnFrameChanged: TNotifyEvent Read FOnFrameChanged Write FOnFrameChanged;
-    property OnMouseDown;
-    property OnMouseEnter;
-    property OnMouseLeave;
-    property OnMouseMove;
-    property OnMouseUp;
-    property OnStartAnim: TNotifyEvent Read FOnStart Write FOnStart;
-    property OnStopAnim: TNotifyEvent Read FOnStop Write FOnStop;
-    property ParentShowHint;
-    property ShowHint;
-    property Visible;
-    property Width;
-  end;
-
-procedure Register;
-
-implementation
-
-procedure Register;
-begin
-  RegisterComponents('Wile64', [TGifAnim]);
-end;
-
-{ TGifAnim }
-
-constructor TGifAnim.Create(AOwner: TComponent);
-begin
-  inherited Create(AOwner);
-  ControlStyle := [csCaptureMouse, csClickEvents, csDoubleClicks];
-  AutoSize     := True;
-  SetInitialBounds(0, 0, GetControlClassDefaultSize.CX, GetControlClassDefaultSize.CY);
-  FEmpty      := True;
-  FCurrentImage := 0;
-  CurrentView := TBitmap.Create;
-  if not (csDesigning in ComponentState) then
-  begin
-    BufferImg := TBitmap.Create;
-    FWait     := TTimer.Create(Self);
-    with FWait do
-    begin
-      Interval := 100;
-      OnTimer  := @OnTime;
-      Enabled  := False;
-    end;
-  end;
-  Animate := True;
-end;
-
-destructor TGifAnim.Destroy;
-begin
-  inherited Destroy;
-  if assigned(FGifBitmaps) then
-    FreeAndNil(FGifBitmaps);
-  BufferImg.Free;
-  CurrentView.Free;
-end;
-
-procedure TGifAnim.NextFrame;
-begin
-  if (not FEmpty) and Visible and (not FAnimate) then
-  begin
-    if FCurrentImage >= GifBitmaps.Count - 1 then
-      FCurrentImage := 0
-    else
-      Inc(FCurrentImage);
-    if Assigned(FOnFrameChanged) then
-      FOnFrameChanged(Self);
-    Repaint;
-  end;
-end;
-
-procedure TGifAnim.PriorFrame;
-var
-  DesiredImage: Integer;
-begin
-  if (not FEmpty) and Visible and (not FAnimate) then
-  begin
-    if FCurrentImage = 0 then
-      DesiredImage:= GifBitmaps.Count - 1
-    else
-      DesiredImage:= FCurrentImage - 1;
-    // For proper display repaint image from first frame to desired frame
-    FCurrentImage:= 0;
-    while FCurrentImage < DesiredImage do
-    begin
-      with GifBitmaps.Items[FCurrentImage] do
-        begin
-          BufferImg.Canvas.Brush.Color := (Self.Color);
-          if FCurrentImage = 0 then
-            BufferImg.Canvas.FillRect(Rect(0, 0, Width, Height));
-          if Delay <> 0 then
-            FWait.Interval := Delay * 10;
-          BufferImg.Canvas.Draw(PosX, PosY, Bitmap);
-          case Method of
-            //0 : Not specified...
-            //1 : No change Background
-            2: BufferImg.Canvas.FillRect(
-                Rect(PosX, PosY, Bitmap.Width + PosX, Bitmap.Height + PosY));
-
-            3: BufferImg.Canvas.FillRect(Rect(0, 0, Width, Height));
-          end;
-        end;
-      Inc(FCurrentImage);
-    end;
-    if Assigned(FOnFrameChanged) then
-      FOnFrameChanged(Self);
-    Repaint;
-  end;
-end;
-
-function TGifAnim.LoadFromLazarusResource(const ResName: String): boolean;
-var
-  GifLoader: TGifLoader;
-  StateAnimate: boolean;
-  Resource: TLResource;
-begin
-  Result:=false;
-  StateAnimate:= Animate;
-  FWait.Enabled:= false;
-  ResetImage;
-  Resource:=nil;
-  Resource:=LazarusResources.Find(ResName);
-  if Resource <> nil then
-  if CompareText(LazarusResources.Find(ResName).ValueType, 'gif')=0 then begin
-    GifLoader := TGifLoader.Create(Filename);
-    FEmpty := not GifLoader.LoadFromLazarusResource(ResName, FGifBitmaps);
-    DefineSize(GifLoader.Width, GifLoader.Height);
-    GifLoader.Free;
-    Result:= FEmpty;
-  end;
-  if not Empty then
-    GifChanged;
-  FWait.Enabled:= StateAnimate;
-end;
-
-procedure TGifAnim.LoadFromFile(const Filename: string);
-var
-  GifLoader: TGifLoader;
-begin
-  FEmpty    := True;
-  if not FileExists(Filename) then
-    Exit;
-  GifLoader := TGifLoader.Create(Filename);
-  if (csDesigning in ComponentState) then
-    FEmpty := not GifLoader.LoadFirstBitmap(CurrentView)
-  else
-    FEmpty := not GifLoader.LoadAllBitmap(FGifBitmaps);
-  DefineSize(GifLoader.Width, GifLoader.Height);
-  GifLoader.Free;
-end;
-
-procedure TGifAnim.OnTime(Sender: TObject);
-begin
-  if (not Empty) and Visible then
-  begin
-    if FCurrentImage >= GifBitmaps.Count - 1 then
-      FCurrentImage := 0
-    else
-      Inc(FCurrentImage);
-    if Assigned(FOnFrameChanged) then
-      FOnFrameChanged(Self);
-    Repaint;
-  end;
-end;
-
-procedure TGifAnim.SetAnimate(const AValue: boolean);
-begin
-  if FAnimate = AValue then
-    exit;
-  FAnimate := AValue;
-  if not (csDesigning in ComponentState) then
-  begin
-    FWait.Enabled := Animate;
-    if Animate then
-      DoStartAnim
-    else
-      DoStopAnim;
-  end;
-end;
-
-procedure TGifAnim.SetFileName(const AValue: string);
-begin
-  if (FFileName = AValue) then Exit;
-  FFileName := AValue;
-  ResetImage;
-  if (FFileName = '') then Exit;
-  LoadFromFile(FFileName);
-  if not Empty then
-    GifChanged;
-end;
-
-procedure TGifAnim.DefineSize(AWidth, AHeight: integer);
-begin
-  if (AWidth = FGifWidth) and (AHeight = FGifHeight) then
-    Exit;
-  FGifWidth  := AWidth;
-  FGifHeight := AHeight;
-  Height     := FGifHeight;
-  Width      := FGifWidth;
-  if not (csDesigning in ComponentState) then
-  begin
-    BufferImg.Height := Height;
-    BufferImg.Width  := Width;
-  end;
-end;
-
-procedure TGifAnim.CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer;
-  WithThemeSpace: boolean);
-begin
-  PreferredWidth  := FGifWidth;
-  PreferredHeight := FGifHeight;
-end;
-
-procedure TGifAnim.DoAutoSize;
-var
-  ModifyWidth, ModifyHeight: boolean;
-  NewWidth:  integer;
-  NewHeight: integer;
-begin
-  if AutoSizing then
-    Exit;    // we shouldn't come here in the first place
-
-  BeginAutoSizing;
-  try
-    GetPreferredSize(NewWidth, NewHeight);
-    ModifyWidth  := [akLeft, akRight] * (Anchors + AnchorAlign[Align]) <> [akLeft, akRight];
-    ModifyHeight := [akTop, akBottom] * (Anchors + AnchorAlign[Align]) <> [akTop, akBottom];
-
-    if not ModifyWidth then
-      NewWidth := Width;
-    if not ModifyHeight then
-      NewHeight := Height;
-
-    if (NewWidth <> Width) or (NewHeight <> Height) then
-    begin
-      SetBounds(Left, Top, NewWidth, NewHeight);
-    end;
-  finally
-    EndAutoSizing;
-  end;
-end;
-
-class function TGifAnim.GetControlClassDefaultSize: TSize;
-begin
-  Result.CX := 90;
-  Result.CY := 90;
-end;
-
-procedure TGifAnim.GifChanged;
-begin
-  if not (csDesigning in ComponentState) then
-  begin
-    BufferImg.Canvas.Brush.Color := (self.Color);
-    BufferImg.Canvas.FillRect(Rect(0, 0, Width, Height));
-    with GifBitmaps.Items[FCurrentImage] do
-      BufferImg.Canvas.Draw(PosX, PosY, Bitmap);
-    CurrentView.Assign(BufferImg);
-  end;
-  InvalidatePreferredSize;
-  AdjustSize;
-end;
-
-procedure TGifAnim.Paint;
-begin
-  if (not Empty) and Visible then
-  begin
-    if not (csDesigning in ComponentState) then
-    begin
-      if (FCurrentImage < GifBitmaps.Count) then
-        with GifBitmaps.Items[FCurrentImage] do
-        begin
-          BufferImg.Canvas.Brush.Color := (self.Color);
-          if FCurrentImage = 0 then
-            BufferImg.Canvas.FillRect(Rect(0, 0, Width, Height));
-          if Delay <> 0 then
-            FWait.Interval := Delay * 10;
-          BufferImg.Canvas.Draw(PosX, PosY, Bitmap);
-          CurrentView.Assign(BufferImg);
-          case Method of
-            //0 : Not specified...
-            //1 : No change Background
-            2: BufferImg.Canvas.FillRect(
-                Rect(PosX, PosY, Bitmap.Width + PosX, Bitmap.Height + PosY));
-
-            3: BufferImg.Canvas.FillRect(Rect(0, 0, Width, Height));
-          end;
-        end;
-    end
-    else
-    begin
-      Canvas.Brush.Color := (self.Color);
-      Canvas.FillRect(Rect(0, 0, Width, Height));
-    end;
-    Canvas.Draw(0, 0, CurrentView);
-  end;
-  inherited Paint;
-end;
-
-procedure TGifAnim.ResetImage;
-begin
-  if assigned(FGifBitmaps) then
-    FreeAndNil(FGifBitmaps);
-  FCurrentImage:=0;
-  with CurrentView do
-  begin
-    Canvas.Brush.Color := (self.Color);
-    Canvas.FillRect(Rect(0, 0, Width, Height));
-  end;
-end;
-
-procedure TGifAnim.SetColor(Value: TColor);
-begin
-  inherited SetColor(Value);
-end;
-
-procedure TGifAnim.DoStartAnim;
-begin
-  if assigned(OnStartAnim) then
-    OnStartAnim(Self);
-end;
-
-procedure TGifAnim.DoStopAnim;
-begin
-  if assigned(OnStopAnim) then
-    OnStartAnim(Self);
-end;
-
-{ TGifLoader }
-
-constructor TGifLoader.Create(const FileName: string);
-begin
-  FFileName := FileName;
-  FGifUseGraphCtrlExt := False;
-  FPalette  := TFPPalette.Create(0);
-  FHeight   := 20;
-  FWidth    := 20;
-end;
-
-destructor TGifLoader.Destroy;
-begin
-  inherited Destroy;
-  FPalette.Free;
-end;
-
-function TGifLoader.LoadFromStream(GifStream: TStream; var AGifList: TGifList): boolean;
-var
-  Introducer: byte;
-  FPImage:    TLazIntfImage;
-  ImgFormatDescription: TRawImageDescription;
-  GifBitmap:  TGifImage;
-begin
-  Result := False;
-  if not Assigned(AGifList) then
-    AGifList := TGifList.Create(True);
-
-  GifStream.Position := 0;
-
-  ReadHeader(GifStream);
-  if (FGifHeader.Version <> '89a') then
-    Exit;
-
-  // skip first block extention if exist
-  repeat
-    Introducer := SkipBlock(GifStream);
-  until (Introducer = ID_IMAGE_DESCRIPTOR) or (Introducer = ID_TRAILER) or (GifStream.Position = GifStream.Size);
-
-  repeat
-    ReadGifBitmap(GifStream);
-    // decode Gif bitmap in Scanline buffer
-    ReadScanLine(GifStream);
-    // Create temp Fp Image for put scanline pixel
-    FPImage := TLazIntfImage.Create(FLocalWidth, FLocalHeight);
-    ImgFormatDescription.Init_BPP32_B8G8R8A8_BIO_TTB(FLocalWidth,
-      FLocalHeight);
-    FPImage.DataDescription := ImgFormatDescription;
-
-    WriteScanLine(FPImage);
-
-    GifBitmap := TGifImage.Create;
-    GifBitmap.FBitmap.LoadFromIntfImage(FPImage);
-    GifBitmap.FPosX   := FGifDescriptor.Left;
-    GifBitmap.FPosY   := FGifDescriptor.Top;
-    GifBitmap.FMethod := FDisposalMethod;
-    GifBitmap.FDelay  := FGifGraphicsCtrlExt.DelayTime;
-
-    AGifList.Add(GifBitmap);
-
-    FPImage.Free;
-    FreeMem(FScanLine, FLineSize);
-    // reset FGifUseGraphCtrlExt flag
-    FGifUseGraphCtrlExt := False;
-
-    repeat
-      Introducer := SkipBlock(GifStream);
-    until (Introducer = ID_IMAGE_DESCRIPTOR) or (Introducer = ID_TRAILER) or (GifStream.Position = GifStream.Size);
-
-  until (Introducer = ID_TRAILER) or (GifStream.Position = GifStream.Size);
-  Result := True;
-end;
-
-function TGifLoader.LoadAllBitmap(var AGifList: TGifList): boolean;
-var
-  GifStream:  TMemoryStream;
-begin
-  Result := False;
-  if not FileExists(FFileName) then
-    Exit;
-
-  GifStream := TMemoryStream.Create;
-  try
-    GifStream.LoadFromFile(FFileName);
-    Result := LoadFromStream(GifStream, AGifList);
-  finally
-    GifStream.Free;
-  end;
-end;
-
-function TGifLoader.LoadFromLazarusResource(const ResName: String; var AGifList: TGifList): boolean;
-var
-  GifStream:  TLazarusResourceStream;
-begin
-  Result := False;
-  GifStream := TLazarusResourceStream.Create(ResName, nil);
-  try
-    Result := LoadFromStream(GifStream, AGifList);
-  finally
-    GifStream.Free;
-  end;
-end;
-
-function TGifLoader.LoadFirstBitmap(var ABitmap: TBitmap): boolean;
-var
-  GifStream:  TMemoryStream;
-  Introducer: byte;
-  FPImage:    TLazIntfImage;
-  ImgFormatDescription: TRawImageDescription;
-begin
-  Result := False;
-  if not FileExists(FFileName) then
-    exit;
-  if not assigned(ABitmap) then
-    ABitmap := TBitmap.Create;
-
-  GifStream := TMemoryStream.Create;
-  GifStream.LoadFromFile(FFileName);
-  GifStream.Position := 0;
-
-  ReadHeader(GifStream);
-  if (FGifHeader.Version <> '89a') then
-    Exit;
-
-  // skip first block extention if exist
-  repeat
-    Introducer := SkipBlock(GifStream);
-  until (Introducer = ID_IMAGE_DESCRIPTOR) or (Introducer = ID_TRAILER);
-
-  ReadGifBitmap(GifStream);
-  // decode Gif bitmap in Scanline buffer
-  ReadScanLine(GifStream);
-  // Create temp Fp Image for put scanline pixel
-  FPImage := TLazIntfImage.Create(FLocalWidth, FLocalHeight);
-  ImgFormatDescription.Init_BPP32_B8G8R8A8_BIO_TTB(FLocalWidth,
-    FLocalHeight);
-  FPImage.DataDescription := ImgFormatDescription;
-
-  WriteScanLine(FPImage);
-
-  ABitmap.LoadFromIntfImage(FPImage);
-  FPImage.Free;
-  FreeMem(FScanLine, FLineSize);
-  // reset FGifUseGraphCtrlExt flag
-  FGifUseGraphCtrlExt := False;
-
-  GifStream.Free;
-  Result := True;
-end;
-
-procedure TGifLoader.SetTransparent(const AValue: boolean);
-begin
-  if FIsTransparent = AValue then
-    exit;
-  FIsTransparent := AValue;
-end;
-
-function TGifLoader.SkipBlock(Stream: TStream): byte;
-var
-  Introducer, Labels, SkipByte: byte;
-begin
-  Introducer := 0;
-  Labels     := 0;
-  SkipByte   := 0;
-  Stream.Read(Introducer, 1);
-  if Introducer = EXT_INTRODUCER then
-  begin
-    Stream.Read(Labels, 1);
-    case Labels of
-      EXT_COMMENT,
-      EXT_APPLICATION:
-        while True do
-        begin
-          Stream.Read(SkipByte, 1);
-          if SkipByte = 0 then
-            Break;
-          Stream.Seek(SkipByte, soFromCurrent);
-        end;
-      EXT_GRAPHICS_CONTROL:
-      begin
-        Stream.Read(FGifGraphicsCtrlExt, SizeOf(FGifGraphicsCtrlExt));
-        FGifUseGraphCtrlExt := True;
-      end;
-      EXT_PLAIN_TEXT:
-      begin
-        Stream.Read(SkipByte, 1);
-        Stream.Seek(SkipByte, soFromCurrent);
-        while True do
-        begin
-          Stream.Read(SkipByte, 1);
-          if SkipByte = 0 then
-            Break;
-          Stream.Seek(SkipByte, soFromCurrent);
-        end;
-      end;
-    end;
-  end;
-  Result := Introducer;
-end;
-
-procedure TGifLoader.ReadScanLine(Stream: TStream);
-var
-  OldPos, UnpackedSize, PackedSize: longint;
-  I:      integer;
-  Data, Bits, Code: cardinal;
-  SourcePtr: PByte;
-  InCode: cardinal;
-
-  CodeSize: cardinal;
-  CodeMask: cardinal;
-  FreeCode: cardinal;
-  OldCode:  cardinal;
-  Prefix:   array[0..CODE_TABLE_SIZE - 1] of cardinal;
-  Suffix, Stack: array [0..CODE_TABLE_SIZE - 1] of byte;
-  StackPointer: PByte;
-  DataComp, Target: PByte;
-  B, FInitialCodeSize, FirstChar: byte;
-  ClearCode, EOICode: word;
-
-begin
-  FInitialCodeSize := 0;
-  B := 0;
-  DataComp := nil;
-
-  // initialisation du dictionnaire de decompression
-  Stream.Read(FInitialCodeSize, 1);
-
-  // Recherche la taille des données compresser
-  OldPos     := Stream.Position;
-  PackedSize := 0;
-  repeat
-    Stream.Read(B, 1);
-    if B > 0 then
-    begin
-      Inc(PackedSize, B);
-      Stream.Seek(B, soFromCurrent);
-    end;
-  until B = 0;
-
-  Getmem(DataComp, PackedSize);
-  // lecture des données conpresser
-  SourcePtr := DataComp;
-  Stream.Position := OldPos;
-  repeat
-    Stream.Read(B, 1);
-    if B > 0 then
-    begin
-      Stream.ReadBuffer(SourcePtr^, B);
-      Inc(SourcePtr, B);
-    end;
-  until B = 0;
-
-  SourcePtr := DataComp;
-  Target    := FScanLine;
-  CodeSize  := FInitialCodeSize + 1;
-  ClearCode := 1 shl FInitialCodeSize;
-  EOICode   := ClearCode + 1;
-  FreeCode  := ClearCode + 2;
-  OldCode   := CODE_TABLE_SIZE;
-  CodeMask  := (1 shl CodeSize) - 1;
-  UnpackedSize := FLocalWidth * FLocalHeight;
-  for I := 0 to ClearCode - 1 do
-  begin
-    Prefix[I] := CODE_TABLE_SIZE;
-    Suffix[I] := I;
-  end;
-  StackPointer := @Stack;
-  FirstChar := 0;
-  Data := 0;
-  Bits := 0;
-  //Decompression LZW gif
-  while (UnpackedSize > 0) and (PackedSize > 0) do
-  begin
-    Inc(Data, SourcePtr^ shl Bits);
-    Inc(Bits, 8);
-    while Bits >= CodeSize do
-    begin
-      Code := Data and CodeMask;
-      Data := Data shr CodeSize;
-      Dec(Bits, CodeSize);
-      if Code = EOICode then
-        Break;
-      if Code = ClearCode then
-      begin
-        CodeSize := FInitialCodeSize + 1;
-        CodeMask := (1 shl CodeSize) - 1;
-        FreeCode := ClearCode + 2;
-        OldCode  := CODE_TABLE_SIZE;
-        Continue;
-      end;
-      if Code > FreeCode then
-        Break;
-      if OldCode = CODE_TABLE_SIZE then
-      begin
-        FirstChar := Suffix[Code];
-        Target^   := FirstChar;
-        Inc(Target);
-        Dec(UnpackedSize);
-        OldCode := Code;
-        Continue;
-      end;
-      InCode := Code;
-      if Code = FreeCode then
-      begin
-        StackPointer^ := FirstChar;
-        Inc(StackPointer);
-        Code := OldCode;
-      end;
-      while Code > ClearCode do
-      begin
-        StackPointer^ := Suffix[Code];
-        Inc(StackPointer);
-        Code := Prefix[Code];
-      end;
-      FirstChar     := Suffix[Code];
-      StackPointer^ := FirstChar;
-      Inc(StackPointer);
-      Prefix[FreeCode] := OldCode;
-      Suffix[FreeCode] := FirstChar;
-      if (FreeCode = CodeMask) and (CodeSize < 12) then
-      begin
-        Inc(CodeSize);
-        CodeMask := (1 shl CodeSize) - 1;
-      end;
-      if FreeCode < CODE_TABLE_SIZE - 1 then
-        Inc(FreeCode);
-      OldCode := InCode;
-      repeat
-        Dec(StackPointer);
-        Target^ := StackPointer^;
-        Inc(Target);
-        Dec(UnpackedSize);
-      until StackPointer = @Stack;
-    end;
-    Inc(SourcePtr);
-    Dec(PackedSize);
-  end;
-  FreeMem(DataComp);
-end;
-
-procedure TGifLoader.ReadHeader(Stream: TStream);
-begin
-  Stream.Read(FGifHeader, SizeOf(FGifHeader));
-
-  with FGifHeader do
-  begin
-    FGifBackgroundColor := BackgroundColor;
-
-    FWidth  := ScreenWidth;
-    FHeight := ScreenHeight;
-
-    FLocalWidth  := ScreenWidth;
-    FLocalHeight := ScreenHeight;
-
-    IsTransparent := False;
-  end;
-  ReadGlobalPalette(Stream);
-end;
-
-procedure TGifLoader.ReadGlobalPalette(Stream: TStream);
-var
-  ColorTableSize: integer;
-begin
-  if (FGifHeader.Packedbit and ID_COLOR_TABLE) <> 0 then
-  begin
-    ColorTableSize := FGifHeader.Packedbit and ID_COLOR_TABLE_SIZE + 1;
-    ReadPalette(Stream, 1 shl ColorTableSize);
-  end;
-end;
-
-procedure TGifLoader.ReadGraphCtrlExt;
-var
-  C: TFPColor;
-begin
-  IsTransparent := (FGifGraphicsCtrlExt.Packedbit and ID_TRANSPARENT) <> 0;
-
-  FDisposalMethod := (FGifGraphicsCtrlExt.Packedbit and $1C) shr 2;
-
-  if IsTransparent then
-  begin
-    // if Transparent bitmap change alpha channel
-    FGifBackgroundColor := FGifGraphicsCtrlExt.ColorIndex;
-    C := FPalette[FGifBackgroundColor];
-    C.alpha := alphaTransparent;
-    FPalette[FGifBackgroundColor] := C;
-  end;
-end;
-
-procedure TGifLoader.SetInterlaced(const AValue: boolean);
-begin
-  if FInterlaced = AValue then
-    exit;
-  FInterlaced := AValue;
-end;
-
-procedure TGifLoader.ReadPalette(Stream: TStream; Size: integer);
-var
-  RGBEntry: TRGB;
-  I: integer;
-  C: TFPColor;
-begin
-  FPalette.Clear;
-  FPalette.Count := 0;
-  Fillchar(RGBEntry, SizeOf(RGBEntry), 0);
-  for I := 0 to Size - 1 do
-  begin
-    Stream.Read(RGBEntry, SizeOf(RGBEntry));
-    with C do
-    begin
-      Red   := RGBEntry.Red or (RGBEntry.Red shl 8);
-      Green := RGBEntry.Green or (RGBEntry.Green shl 8);
-      Blue  := RGBEntry.Blue or (RGBEntry.Blue shl 8);
-      Alpha := alphaOpaque;
-    end;
-    FPalette.Add(C);
-  end;
-end;
-
-procedure TGifLoader.WriteScanLine(Img: TFPCustomImage);
-var
-  Row, Col: integer;
-  Pass, Every: byte;
-  P: PByte;
-begin
-  P := FScanLine;
-  if Interlaced then
-  begin
-    for Pass := 1 to 4 do
-    begin
-      case Pass of
-        1:
-        begin
-          Row   := 0;
-          Every := 8;
-        end;
-        2:
-        begin
-          Row   := 4;
-          Every := 8;
-        end;
-        3:
-        begin
-          Row   := 2;
-          Every := 4;
-        end;
-        4:
-        begin
-          Row   := 1;
-          Every := 2;
-        end;
-      end;
-      repeat
-        for Col := 0 to FLocalWidth - 1 do
-        begin
-          Img.Colors[Col, Row] := FPalette[P^];
-          Inc(P);
-        end;
-        Inc(Row, Every);
-      until Row >= FLocalHeight;
-    end;
-  end
-  else
-  begin
-    for Row := 0 to FLocalHeight - 1 do
-      for Col := 0 to FLocalWidth - 1 do
-      begin
-        Img.Colors[Col, Row] := FPalette[P^];
-        Inc(P);
-      end;
-  end;
-end;
-
-procedure TGifLoader.ReadGifBitmap(Stream: TStream);
-var
-  ColorTableSize: integer;
-begin
-  Stream.Read(FGifDescriptor, SizeOf(FGifDescriptor));
-
-  with FGifDescriptor do
-  begin
-    FLocalWidth  := Width;
-    FLocalHeight := Height;
-    Interlaced   := (Packedbit and ID_INTERLACED = ID_INTERLACED);
-  end;
-
-  FLineSize := FLocalWidth * (FLocalHeight + 1);
-  GetMem(FScanLine, FLineSize);
-
-  if (FGifDescriptor.Packedbit and ID_COLOR_TABLE) <> 0 then
-  begin
-    ColorTableSize := FGifDescriptor.Packedbit and ID_COLOR_TABLE_SIZE + 1;
-    ReadPalette(Stream, 1 shl ColorTableSize);
-  end;
-  if FGifUseGraphCtrlExt then
-    ReadGraphCtrlExt;
-
-end;
-
-{ TGifImage }
-
-constructor TGifImage.Create;
-begin
-  FBitmap := TBitmap.Create;
-  FPosX   := 0;
-  FPosY   := 0;
-  FDelay  := 0;
-  FMethod := 0;
-end;
-
-destructor TGifImage.Destroy;
-begin
-  inherited Destroy;
-  FBitmap.Free;
-end;
-
-{ TGifList }
-
-function TGifList.GetItems(Index: integer): TGifImage;
-begin
-  Result := TGifImage(inherited Items[Index]);
-end;
-
-procedure TGifList.SetItems(Index: integer; AGifImage: TGifImage);
-begin
-  Put(Index, AGifImage);
-end;
-
-function TGifList.Add(AGifImage: TGifImage): integer;
-begin
-  Result := inherited Add(AGifImage);
-end;
-
-function TGifList.Extract(Item: TGifImage): TGifImage;
-begin
-  Result := TGifImage(inherited Extract(Item));
-end;
-
-function TGifList.Remove(AGifImage: TGifImage): integer;
-begin
-  Result := inherited Remove(AGifImage);
-end;
-
-function TGifList.IndexOf(AGifImage: TGifImage): integer;
-begin
-  Result := inherited IndexOf(AGifImage);
-end;
-
-function TGifList.First: TGifImage;
-begin
-  Result := TGifImage(inherited First);
-end;
-
-function TGifList.Last: TGifImage;
-begin
-  Result := TGifImage(inherited Last);
-end;
-
-procedure TGifList.Insert(Index: integer; AGifImage: TGifImage);
-begin
-  inherited Insert(Index, AGifImage);
-end;
-
-initialization
-{$I gifanim.lrs}
-
-end.

+ 0 - 41
components/gifanim/gifanimdsgn.pas

@@ -1,41 +0,0 @@
-unit GifAnimDsgn;
-
-{$mode objfpc}{$H+}
-
-interface
-
-uses
-  LazIDEIntf, PropEdits;
-
-Type
-  TGifFileNamePropertyEditor = class(TFileNamePropertyEditor)
-  protected
-    function GetFilter: String; override;
-    function GetInitialDirectory: string; override;
-  end;
-
-procedure Register;
-
-implementation
-
-uses
-  SysUtils, GifAnim;
-
-function TGifFileNamePropertyEditor.GetFilter: String;
-begin
-  Result := 'GIF|*.gif';
-end;
-
-function TGifFileNamePropertyEditor.GetInitialDirectory: string;
-begin
-  Result:= ExtractFilePath(LazarusIDE.ActiveProject.ProjectInfoFile);
-end;
-
-procedure Register;
-begin
-  RegisterPropertyEditor(TypeInfo(String), TGifAnim,
-                         'FileName', TGifFileNamePropertyEditor);
-end;
-
-end.
-

+ 0 - 58
components/gifanim/pkg_gifanim.lpk

@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<CONFIG>
-  <Package Version="4">
-    <PathDelim Value="\"/>
-    <Name Value="pkg_gifanim"/>
-    <AddToProjectUsesSection Value="True"/>
-    <Author Value="Laurent Jacques"/>
-    <CompilerOptions>
-      <Version Value="11"/>
-      <PathDelim Value="\"/>
-      <SearchPaths>
-        <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
-      </SearchPaths>
-      <Linking>
-        <Debugging>
-          <DebugInfoType Value="dsDwarf2Set"/>
-        </Debugging>
-      </Linking>
-      <Other>
-        <CompilerPath Value="$(CompPath)"/>
-      </Other>
-    </CompilerOptions>
-    <Description Value="Show Gif Animation
-"/>
-    <License Value="GPL
-"/>
-    <Version Major="1" Minor="5"/>
-    <Files Count="2">
-      <Item1>
-        <Filename Value="gifanim.pas"/>
-        <HasRegisterProc Value="True"/>
-        <UnitName Value="GifAnim"/>
-      </Item1>
-      <Item2>
-        <Filename Value="gifanim.lrs"/>
-        <Type Value="LRS"/>
-      </Item2>
-    </Files>
-    <Type Value="RunAndDesignTime"/>
-    <RequiredPkgs Count="2">
-      <Item1>
-        <PackageName Value="LCL"/>
-        <MinVersion Major="1" Valid="True"/>
-      </Item1>
-      <Item2>
-        <PackageName Value="FCL"/>
-        <MinVersion Major="1" Valid="True"/>
-      </Item2>
-    </RequiredPkgs>
-    <UsageOptions>
-      <UnitPath Value="$(PkgOutDir)"/>
-    </UsageOptions>
-    <PublishOptions>
-      <Version Value="2"/>
-      <IgnoreBinaries Value="False"/>
-    </PublishOptions>
-  </Package>
-</CONFIG>

+ 0 - 21
components/gifanim/pkg_gifanim.pas

@@ -1,21 +0,0 @@
-{ This file was automatically created by Lazarus. Do not edit!
-  This source is only used to compile and install the package.
- }
-
-unit pkg_gifanim;
-
-interface
-
-uses
-  GifAnim, LazarusPackageIntf;
-
-implementation
-
-procedure Register;
-begin
-  RegisterUnit('GifAnim', @GifAnim.Register);
-end;
-
-initialization
-  RegisterPackage('pkg_gifanim', @Register);
-end.

+ 0 - 49
components/gifanim/pkg_gifanim_dsgn.lpk

@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<CONFIG>
-  <Package Version="4">
-    <PathDelim Value="\"/>
-    <Name Value="pkg_gifanim_dsgn"/>
-    <CompilerOptions>
-      <Version Value="11"/>
-      <PathDelim Value="\"/>
-      <SearchPaths>
-        <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
-      </SearchPaths>
-      <Linking>
-        <Debugging>
-          <DebugInfoType Value="dsDwarf2Set"/>
-        </Debugging>
-      </Linking>
-      <Other>
-        <CompilerMessages>
-          <MsgFileName Value=""/>
-        </CompilerMessages>
-        <CompilerPath Value="$(CompPath)"/>
-      </Other>
-    </CompilerOptions>
-    <License Value="GPL"/>
-    <Version Major="1" Minor="4"/>
-    <Files Count="1">
-      <Item1>
-        <Filename Value="gifanimdsgn.pas"/>
-        <HasRegisterProc Value="True"/>
-        <UnitName Value="GifAnimDsgn"/>
-      </Item1>
-    </Files>
-    <Type Value="DesignTime"/>
-    <RequiredPkgs Count="2">
-      <Item1>
-        <PackageName Value="IDEIntf"/>
-      </Item1>
-      <Item2>
-        <PackageName Value="pkg_gifanim"/>
-      </Item2>
-    </RequiredPkgs>
-    <UsageOptions>
-      <UnitPath Value="$(PkgOutDir)"/>
-    </UsageOptions>
-    <PublishOptions>
-      <Version Value="2"/>
-    </PublishOptions>
-  </Package>
-</CONFIG>

+ 0 - 21
components/gifanim/pkg_gifanim_dsgn.pas

@@ -1,21 +0,0 @@
-{ This file was automatically created by Lazarus. Do not edit!
-  This source is only used to compile and install the package.
- }
-
-unit pkg_gifanim_dsgn;
-
-interface
-
-uses
-  GifAnimDsgn, LazarusPackageIntf;
-
-implementation
-
-procedure Register;
-begin
-  RegisterUnit('GifAnimDsgn', @GifAnimDsgn.Register);
-end;
-
-initialization
-  RegisterPackage('pkg_gifanim_dsgn', @Register);
-end.

+ 0 - 5
components/gifanim/readme.txt

@@ -1,5 +0,0 @@
-GifAnim
-http://wile64.perso.neuf.fr/download/download.php?cat=4&id=8
-Version 1.4 (14/09/2009)
-
-Some modifications done for Double Commander (see doublecmd.diff).

+ 0 - 28
components/gifanim/ressource-file.txt

@@ -1,28 +0,0 @@
-
-1) Gif in ressource file
--------------------------
-
-- Create ressource file with lazres ex : lazres mygif.lrs gif1.gif gif2.gif
-- Put TGifAnim on your form 
-- In the FormCreate add (see down)
-
-[code]
-
-procedure TForm1.FormCreate(Sender: TObject);
-begin
-  GifAnim1.LoadFromLazarusResource('gif1');
-  GifAnim2.LoadFromLazarusResource('gif2');
-end;
-
-[/code]
-
-- And insert ressouce file in initialization section (see down)
-
-[code]
-
-initialization
-  {$I unit1.lrs}
-  {$I mesgif.lrs}
-
-[/code]
-

+ 0 - 58
components/gifanim/tgifanim.xpm

@@ -1,58 +0,0 @@
-/* XPM */
-static char * gifanim_xpm[] = {
-"24 24 31 1",
-" 	c None",
-".	c #959595",
-"+	c #E1E1E1",
-"@	c #919191",
-"#	c #848484",
-"$	c #888888",
-"%	c #EEEEEE",
-"&	c #E6E9EC",
-"*	c #D6DFE8",
-"=	c #FFFFFF",
-"-	c #E7F0F9",
-";	c #3783CE",
-">	c #FFF7F7",
-",	c #FFEFEF",
-"'	c #F7F2F5",
-")	c #F7FAFD",
-"!	c #FF5757",
-"~	c #FFDFDF",
-"{	c #FF4F4F",
-"]	c #FFD7D7",
-"^	c #FF4747",
-"/	c #FF3F3F",
-"(	c #8D8D8D",
-"_	c #EEE6E6",
-":	c #EED6D6",
-"<	c #EECECE",
-"[	c #FAFAFA",
-"}	c #F2F2F2",
-"|	c #F6F6F6",
-"1	c #A2A2A2",
-"2	c #FAF2F2",
-" .++@              @++. ",
-" @@@##$$$$$$$$$$$$##@@@ ",
-" .++$#$$$$$$$$$$$$#$++. ",
-" .++@+%%&**&&**&%%+@++. ",
-" @@@@%==-;;--;;-==%@@@@ ",
-" .++.%==-;;--;;-==%.++. ",
-" .++.%>,'--))--',>%.++. ",
-" @@@@%,!~>====>~!,%@@@@ ",
-" .++.%>~{]~~~~]{~>%.++. ",
-" .++.%=>~^////^~>=%.++. ",
-" @@@(+%%_:<<<<:_%%+(@@@ ",
-" .++$#$$$$$$$$$$$$#$++. ",
-" .++$#$$$$$$$$$$$$#$++. ",
-" @@@(+%%&**&%%%%%%+(@@@ ",
-" .++.%==-;;-[}}[==%.++. ",
-" .++.%==-;;-|11|==%.++. ",
-" @@@@%>,'--)[}}2,>%@@@@ ",
-" .++.%,!~>====>~!,%.++. ",
-" .++.%>~{]~~~~]{~>%.++. ",
-" @@@@%=>~^////^~>=%@@@@ ",
-" .++@+%%_:<<<<:_%%+@++. ",
-" .++$#$$$$$$$$$$$$#$++. ",
-" @@@##$$$$$$$$$$$$##@@@ ",
-" .++@              @++. "};

+ 373 - 0
components/gifview/LICENSE.txt

@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.

+ 15 - 0
components/gifview/README.txt

@@ -0,0 +1,15 @@
+GifView
+
+Animated GIF Viewer Component
+
+Copyright (C) 2025 Alexander Koblov
+
+License:
+  MPL 2.0
+
+Based on:
+  TGIFViewer
+  https://github.com/jdelauney/TGIFViewer
+
+Original author:
+  Copyright (C) 2018 J.Delauney (BeanzMaster)

+ 56 - 0
components/gifview/gifview.lpk

@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <Package Version="5">
+    <PathDelim Value="\"/>
+    <Name Value="GifView"/>
+    <Type Value="RunAndDesignTime"/>
+    <Author Value="Jérôme Delauney (BeanzMaster), Alexander Koblov"/>
+    <CompilerOptions>
+      <Version Value="11"/>
+      <PathDelim Value="\"/>
+      <SearchPaths>
+        <OtherUnitFiles Value="source"/>
+        <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
+      </SearchPaths>
+      <Linking>
+        <Debugging>
+          <DebugInfoType Value="dsDwarf2Set"/>
+        </Debugging>
+      </Linking>
+    </CompilerOptions>
+    <Description Value="GIF Viewer"/>
+    <License Value="MPL-2.0"/>
+    <Version Major="1"/>
+    <Files>
+      <Item>
+        <Filename Value="source\GifViewerStrConsts.pas"/>
+        <UnitName Value="GifViewerStrConsts"/>
+      </Item>
+      <Item>
+        <Filename Value="source\uFastBitmap.pas"/>
+        <UnitName Value="uFastBitmap"/>
+      </Item>
+      <Item>
+        <Filename Value="source\uGifViewer.pas"/>
+        <HasRegisterProc Value="True"/>
+        <UnitName Value="uGifViewer"/>
+      </Item>
+    </Files>
+    <RequiredPkgs>
+      <Item>
+        <PackageName Value="LCL"/>
+        <MinVersion Major="2" Minor="2" Valid="True"/>
+      </Item>
+      <Item>
+        <PackageName Value="FCL"/>
+      </Item>
+    </RequiredPkgs>
+    <UsageOptions>
+      <UnitPath Value="$(PkgOutDir)"/>
+    </UsageOptions>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+  </Package>
+</CONFIG>

+ 22 - 0
components/gifview/gifview.pas

@@ -0,0 +1,22 @@
+{ This file was automatically created by Lazarus. Do not edit!
+  This source is only used to compile and install the package.
+ }
+
+unit GifView;
+
+{$warn 5023 off : no warning about unused units}
+interface
+
+uses
+  GifViewerStrConsts, uFastBitmap, uGifViewer, LazarusPackageIntf;
+
+implementation
+
+procedure Register;
+begin
+  RegisterUnit('uGifViewer', @uGifViewer.Register);
+end;
+
+initialization
+  RegisterPackage('GifView', @Register);
+end.

BIN
components/gifview/gifview.res


+ 36 - 0
components/gifview/source/GifViewerStrConsts.pas

@@ -0,0 +1,36 @@
+Unit GifViewerStrConsts;
+
+{$mode objfpc}{$H+}
+
+Interface
+
+ResourceString
+  // Messages d'erreurs ou de notifications
+  // Error or notification messages
+  //uGifViewer
+  rsScreenBadColorSize       = 'Invalid number of colors in the global palette.';
+  rsImageBadColorSize        = 'Number of colors is invalid in local palette.';
+  rsBadSignature             = 'GIF invalid signature: %s';
+  rsBadScreenSize            = 'Invalid image size: %dx%d';
+  rsEmptyColorMap            = 'Error no palette of color available for this image!';
+  rsEmptyImage               = 'The picture is empty';
+  rsUnknownVersion           = 'Unknown GIF version';
+  rsFileNotFound             = 'The file %s  not found !';
+  rsResourceNotFound         = 'Resource %s not found!';
+  rsBufferOverFlow           = 'Image #%d: The decoder has been stopped to prevent a buffer overflow';
+  rsInvalidOutputBufferSize  = 'Image #%d: The size of the output buffer is invalid (size < = 0)';
+  rsInvalidInputBufferSize   = 'Image #%d: The size of the input buffer is invalid (size < = 0)';
+  rsInvalidBufferSize        = 'Image #%d: The size of the input and output buffer are invalid (size < = 0)';
+  rsLZWInternalErrorOutputBufferOverflow = 'Output buffer overflow in the GIF LZW decoder buffer. Report this bug. This is a serious bug!';
+  rsLZWInternalErrorInputBufferOverflow  = 'Input buffer overflow in the GIF LZW decoder. Report this bug. This is a serious bug!';
+  rsLZWInvalidInput         = 'Image #%d: The decoder encountered an invalid entry (corrupted data)';
+  rsLZWOutputBufferTooSmall  = 'Image #%d: The decoder could not decode all the data because the output buffer is too small';
+  rsAllFrameCorrupted        = 'All images in the GIF are corrupted. Unable to display GIF file.';
+  //uFastBitmap
+  rsBitmapCreateError = 'An Error occured while creating TBitmap';
+  rsBitmapScanlineOutOfRange = 'Scanline : Index Out of range';
+
+Implementation
+
+End.
+

+ 1048 - 0
components/gifview/source/uFastBitmap.pas

@@ -0,0 +1,1048 @@
+Unit uFastBitmap;
+(*==============================================================================
+ DESCRIPTION   : Classe de manipulation basique de bitmap en 32 bit.
+                 Basic Class for manipulating 32 bit Bitmap
+ DATE          : 17/06/2018
+ VERSION       : 1.0
+ AUTEUR        : J.Delauney (BeanzMaster)
+ LICENCE       : MPL
+================================================================================
+*)
+
+{$mode objfpc}{$H+}
+{$modeswitch advancedrecords}
+
+Interface
+
+Uses
+  LCLType, LCLIntf, Classes, SysUtils, GraphType, Graphics, Contnrs, Dialogs,
+  IntfGraphics, FPimage;
+
+Const
+  { Constantes utiles pour le calcul sur les masques de couleur }
+  { Useful constants for calculation on color masks }
+  {$IFDEF WINDOWS} // Format BGRA
+    cBlueOrder = 0;
+    cGreenOrder = 1;
+    cRedOrder = 2;
+    cAlphaOrder = 3;
+  {$ELSE} // Format RGBA
+    cRedOrder = 0;
+    cGreenOrder = 1;
+    cBlueOrder = 2;
+    cAlphaOrder = 3;
+  {$ENDIF}
+  cRedShift = cRedOrder * 8;
+  cGreenShift = cGreenOrder * 8;
+  cBlueShift = cBlueOrder * 8;
+  cAlphaShift = cAlphaOrder * 8;
+
+  maskRed = 1;
+  maskGreen = 2;
+  maskBlue = 4;
+  maskAlpha = 8;
+  maskRGB = maskRed Or maskGreen Or maskBlue;
+  maskRGBA = maskRGB Or maskAlpha;
+
+Type
+  { TColorRGB24 : Définition d'un pixel sur 24 bits au format RGB }
+  { TColorRGB24 : Definition of a 24-bit pixel in RGB format }
+  TColorRGB24Type = packed array[0..2] of byte;
+  TColorRGB24 = packed record
+    { Creation de la couleur / Create Color }
+    procedure Create(R,G,B : Byte); Overload;
+    procedure Create(Color:TColor); Overload;
+
+    { Conversion vers un TColor / Convert to TColor }
+    function ToColor : TColor;
+
+    Case Integer of
+     0 : (V:TColorRGB24Type);     // Acces via Tableau / Array
+     1 : (Red, Green, Blue:Byte); // Acces via Composantes / Channel
+  end;
+
+  { TColor32 : Définition d'un pixel sur 32 bits au format RGBA ou BGRA suivant l'OS }
+  { TColor32: Definition of a 32-bit pixel in RGBA or BGRA format depending on the OS }
+  TColor32Type = packed array[0..3] of byte;
+  TColor32 = Packed Record
+  private
+    function getColorComponent(Index : Integer): byte;
+    procedure SetColorComponent(Index : Integer; aValue:Byte);
+  public
+    { Creation de la couleur / Create Color }
+    procedure Create(R,G,B,A : Byte); Overload;
+    procedure Create(R,G,B : Byte);   Overload;
+    procedure Create(Color : TColor); Overload;
+    procedure Create(Color : TColorRGB24); Overload;
+
+    { Conversion vers un TColor / Convert to TColor }
+    function ToColor : TColor;
+    { Conversion vers un TColorRGB24 / Convert to TColorRGB24 }
+    function ToColorRGB24 : TColorRGB24;
+    { Conversion vers un TFPColor / Convert to TFPColor }
+    function ToFPColor : TFPColor;
+
+    { Mixage de la couleur courrante avec la couleur "Color" avec prise en charge du canal Alpha }
+    { Mix current color with 'Color' color with Alpha channel support }
+    function Blend(Color : TColor32): TColor32;
+
+    { Vérifie si 2 valeurs sont identiques / Check if 2 colors are equal }
+    class operator =(Color1,Color2 : TColor32):Boolean;
+
+    { Accès aux composantes de la couleur / Color channel access }
+    property Red:Byte Index cRedOrder read GetColorComponent Write SetColorComponent;
+    property Green:Byte Index cGreenOrder read GetColorComponent Write SetColorComponent;
+    property Blue:Byte Index cBlueOrder read GetColorComponent Write SetColorComponent;
+    property Alpha:Byte Index cAlphaOrder read GetColorComponent Write SetColorComponent;
+
+    Case Integer of
+     0 : (V:TColor32Type);  // Acces via tableau / Array
+     1 : (AsInteger : Integer); // Acces via Integer
+  End;
+  PColor32 = ^TColor32;
+
+  { TColor32Item : Objet persistant englobant une couleur de type TColor32 }
+  { TColor32Item: Persistent object that includes a TColor32 color }
+  TColor32Item = Class(TPersistent)
+  Private
+    FColor: TColor32;
+    FName:  String;
+    FTag:   Integer;
+
+    Procedure SetRed(Const AValue: Byte);
+    Procedure SetGreen(Const AValue: Byte);
+    Procedure SetBlue(Const AValue: Byte);
+    Procedure SetAlpha(Const AValue: Byte);
+    Procedure SetValue(Const AValue: TColor32);
+    Procedure SetColorName(Const aName: String);
+
+    Function getRed: Byte;
+    Function getGreen: Byte;
+    Function getBlue: Byte;
+    Function getAlpha: Byte;
+    Function getValue: TColor32;
+
+  Protected
+  Public
+    Constructor Create;
+    Destructor Destroy; override;
+
+    { Valeur de la couleur / Value of the color }
+    Property Value: TColor32 read getValue write setValue;
+    { Nom de la couleur eg : clrRed  / Name of the color}
+    Property Name: String read FName write setColorName;
+  Published
+    { Valeur du canal rouge / Red channel }
+    Property Red: Byte read getRed write setRed;
+    { Valeur du canal vert / Green channel }
+    Property Green: Byte read getRed write setGreen;
+    { Valeur du canal Bleu / Blue channel }
+    Property Blue: Byte read getRed write setBlue;
+    { Valeur du canal alpha pour la transparence / Alpha channel for transparency }
+    Property Alpha: Byte read getRed write setAlpha;
+    {  Valeur complémentaire personnel / User define value }
+    Property Tag: Integer read FTag write FTag;
+  End;
+
+  { TColor32List : Classe pour la gestion d'une palette (liste) de couleurs }
+  { TColor32List : Class for managing a palette (list) of colors }
+  TColor32List = Class(TObjectList)
+  Private
+  Protected
+    Function GetColorItem(index: Integer): TColor32Item;
+    Procedure SetColorItem(index: Integer; val: TColor32Item);
+  Public
+    { Efface la liste / Clear the list }
+    procedure Clear; override;
+    { Ajoute une couleur à la liste / Add a color to the list }
+    Function AddColor(Const aColor: TColor32): Integer; Overload;
+    { Ajoute une couleur à la liste /Add a color to the list }
+    Function AddColor(Const aName: String; Const aColor: TColor32): Integer; Overload;
+    { Ajoute une couleur à la liste / Add a color to the list}
+    Function AddColor(Const aColorItem: TColor32Item): Integer; Overload;
+    { Supprime une couleur de la liste / Delete a color of the list }
+    Procedure RemoveColor(Const aName: String);
+    { Recherche une couleur dans la liste / Search color in list }
+    Function FindColorByName(Const aName: String; Out Index: Integer):TColor32; Overload;
+    { Recherche une couleur dans la liste  / Search color in list }
+    Function FindColorByName(Const aName: String): TColor32; Overload;
+
+    { Colors : Acceder à la couleur "Index" de la liste / Color access with Index }
+    Property Colors[Index: Integer]: TColor32Item read GetColorItem write setColorItem;
+  End;
+
+Const
+  clrTransparent : TColor32 = (v:($00,$00,$00,$00));
+  clrBlack       : TColor32 = (v:($00,$00,$00,$FF));
+  clrWhite       : TColor32 = (v:($FF,$FF,$FF,$FF));
+
+
+Type
+  { TFastBitmapDrawMode : Mode d'Affichage pour la fonction PutImage de TFastBitmap }
+  { TFastBitmapDrawMode : Display Mode for the PutImage Function of TFastBitmap }
+  TFastBitmapDrawMode = ( dmSet, dmAlpha, dmAlphaCheck);
+
+  { TFastBitmap }
+  { Classe d'aide à la manipulation d'une image }
+  { Help class for image manipulation }
+  TFastBitmap = Class
+  Strict private
+    FTransparentColor : TColor; // Couleur transparent à pour l'affichage via TBitmap de la LCL si besoin / Transparent color for display via TBitmap of the LCL if needed
+
+    FData     : PDWord;    // Tampon de stockage des données d'un bitmap / Buffer for storing data from a bitmap
+    FWidth    : Integer;   // Largeur du bitmap / Width
+    FHeight   : Integer;   // Hauteur du Bitmap / Height
+    FSize     : Int64;     // Taille du tampon en octet / Size in byte
+
+  protected
+
+    procedure SetWidth(NewWidth : Integer);
+    procedure SetHeight(NewHeight : Integer);
+
+    function BuildBitmap : Graphics.TBitmap;
+    function IsClipped(X,Y:Integer) : Boolean;
+
+
+  Public
+    Constructor Create; Overload;
+    Constructor Create(NewWidth, NewHeight : Integer); Overload;
+    Destructor Destroy; Override;
+
+    { Assigne les donnée d'un autre TFastBitmap / Assign another TFastBitmap }
+    procedure Assign(aFastBitmap : TFastBitmap);
+    { Modifie les dimensions du bitmap / Change size of bitmap }
+    procedure SetSize(NewWidth, NewHeight : Integer);
+    { Importation des données d'un TRawImage. Retourne "TRUE" en cas de succès }
+    { Import from RawImage. Return TRUE on success }
+    function ImportFromRawImage(Const ARawImage : TRawImage):Boolean;
+    { Importation des données d'un TBitmap. Retourne "TRUE" en cas de succès }
+    { Import from TBitmap. Return TRUE on success }
+    function ImportFromBitmap(Const ABitmap :Graphics.TBitmap):Boolean;
+    { Efface le bitmap avec la couleur "Color" / Clear bitmap with Color }
+    procedure Clear(Color : TColor32);
+    { Retourne le tampon du bitmap / Return bitmap buffer }
+    function GetSurfaceBuffer : PColor32;
+    { Retourne l'adresse de la ligne "Y" dans le tampon / Return address in buffer of a line }
+    function GetScanLine(Y : Integer) : PColor32;
+    { Retourne l'adresse du pixel à la position "X,Y" dans le tampon / Return address at X,Y}
+    function GetPixelPtr(X, Y : Integer) : PColor32;
+    { Ecrit un pixel de couleur "Color" à la position "X,Y / Put pixel X,Y with Color }
+    procedure PutPixel(X,Y:Integer; Color : TColor32);
+    { Lit un pixel de couleur "Color" à la position "X,Y / Get color of pixel at X,Y }
+    function GetPixel(X,Y:Integer): TColor32;
+    { Ecrit un pixel de en mixant couleur "Color" avec la couleur du pixel présent dans le tampon à la position "X,Y }
+    { Writes a pixel by mixing 'Color' color with the color of the pixel present in the buffer at the 'X, Y' position }
+    procedure PutPixelBlend(X,Y : Integer; Color : TColor32);
+    { Copie une image source "Src" depuis la position "SrcX,SrcY" et de dimension "SrcWidthxSrcHeight" dans le bitmap à la position "DstX, DstY
+      et suivant le "Mode"
+       Mode : TFastBitmapDrawMode
+        - dmSet : Copie brute de l'image
+        - dmAlpha : Copie les pixel de l'image source en mixant les couleurs avec celles du bitmap en fonction de leur valeur Alpha
+        - dmAlphaCheck : Copie les pixels de l'image source seulement si le pixel est visible (Alpha <> 0)
+       Note : les dimensions et les positions entre le bitmap et l'image source sont automatiquement ajustées si besoin.
+
+    --------------------------
+      Copy a source image 'Src' from the position 'SrcX, SrcY' and dimension 'SrcWidthxSrcHeight' into the bitmap at the position 'DstX, DstY
+      and following the 'Mode'
+       Mode: TFastBitmapDrawMode
+        - dmSet: Raw copy of the image
+        - dmAlpha: Copy the pixels of the source image by mixing the colors with those of the bitmap according to their Alpha value
+        - dmAlphaCheck: Copy the pixels of the source image only if the pixel is invisible (Alpha <> 0)
+       Note: The dimensions and positions between the bitmap and the source image are automatically adjusted if necessary.
+    }
+    procedure PutImage(Src : TFastBitmap; SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY : Integer; Mode : TFastBitmapDrawMode);
+    { Creation  d'un clone du bitmap (nouvelle instance) / Create clone (new instance) }
+    function Clone : TFastBitmap;
+    { Retourne un bitmap de type LCL ==> Graphics.TBitmap / Return a TBitmap}
+    function GetBitmap : Graphics.TBitmap;
+    { Dessine le bitmap sur un canvas à la position "X,Y" / Draw the bitmap on a canvas }
+    procedure Draw(ACanvas : TCanvas; X,Y : Integer);  Overload;
+    { Dessine le bitmap sur un canvas délimité par "Rect" / Draw the bitmap on a canvas delimited by "Rect" }
+    procedure Draw(ACanvas : TCanvas; Rect : TRect);   Overload;
+    { Inverse les composante de couleur Rouge et Bleu du bitmap  / Swap Red and Blue channel }
+    procedure SwapRB;
+
+   // procedure HLine(X,Y,X2 : Integer; aColor : TColor32);
+    { Information sur la couleur assignée à la transparence (seulement valable si différent de clrTransparent) / Return the transparency color }
+    property TransparentColor : TColor Read FTransparentColor Write FTransparentColor;
+    { Largeur du bitmap / Width }
+    property Width : Integer Read FWidth Write SetWidth;
+    { Hauteur du bitmap / Height }
+    property Height : Integer Read FHeight Write SetHeight;
+    { Taille du tampon en octet / Size of the buffer }
+    property Size : Int64 Read FSize;
+  End;
+
+Implementation
+
+Uses Types, Math, GifViewerStrConsts;
+
+
+
+{%region=====[ TColorRGB24 ]====================================================}
+
+Procedure TColorRGB24.Create(R, G, B : Byte);
+Begin
+ Red := R;
+ Green := G;
+ Blue := B;
+End;
+
+Procedure TColorRGB24.Create(Color : TColor);
+Var
+  lr,lg,lb : Byte;
+Begin
+  lr := Color;
+  lg := Color shr 8;
+  lb := Color shr 16;
+  Create(lr,lg,lb);
+End;
+
+Function TColorRGB24.ToColor : TColor;
+Begin
+  Result := Red + (Green shl 8) + (Blue shl 16);
+End;
+
+{%endregion%}
+
+{%region=====[ TColor32 ]===================================================}
+
+function TColor32.getColorComponent(Index: Integer): byte;
+Begin
+  result := v[Index];
+End;
+
+procedure TColor32.SetColorComponent(Index: Integer; aValue: Byte);
+Begin
+  v[Index] := aValue;
+End;
+
+procedure TColor32.Create(R, G, B, A: Byte);
+Begin
+  Red := R;
+  Green := G;
+  Blue := B;
+  Alpha := A;
+End;
+
+procedure TColor32.Create(R, G, B: Byte);
+Begin
+  Create(R,G,B,255);
+End;
+
+procedure TColor32.Create(Color: TColor);
+Var
+  ColorRGB24 : TColorRGB24;
+Begin
+  {%H-}ColorRGB24.Create(Color);
+  Create(ColorRGB24);
+End;
+
+procedure TColor32.Create(Color: TColorRGB24);
+Begin
+  Create(Color.Red,Color.Green,Color.Blue);
+End;
+
+function TColor32.ToColor: TColor;
+Begin
+ Result := ToColorRGB24.ToColor;
+End;
+
+function TColor32.ToColorRGB24: TColorRGB24;
+Begin
+ Result.Red := Red;
+ Result.Green := Green;
+ Result.Blue := Blue;
+End;
+
+function TColor32.ToFPColor: TFPColor;
+begin
+  Result.Red   := Self.Red shl 8 + Self.Red;
+  Result.Green := Self.Green shl 8 + Self.Green;
+  Result.Blue  := Self.Blue shl 8 + Self.Blue;
+  Result.Alpha := Self.Alpha shl 8 + Self.Alpha;
+end;
+
+function TColor32.Blend(Color: TColor32): TColor32;
+var
+  factor, factor2:single;
+begin
+
+  if Color.Alpha = 255 then Result := Color
+  else  if (Color.Alpha = 0) or (Self = Color) then Result:= Self
+  else
+  begin
+    factor := Color.Alpha / 255;
+    factor2 := 1 - Factor;
+    Result.Red  := Round((Self.Red*Factor)+(Color.Red*factor2));
+    Result.Green  := Round((Self.Green*Factor)+(Color.Green*Factor2));
+    Result.Blue  := Round((Self.Blue*Factor)+(Color.Blue*Factor2));
+    Result.alpha := Round((Self.Alpha*Factor)+(Color.Alpha*Factor2));
+  End;
+end;
+
+class operator TColor32.=(Color1, Color2: TColor32): Boolean;
+Begin
+  Result := False;
+  if (Color1.Alpha = 0) and (Color2.Alpha = 0) then Result :=True
+  else Result := ((Color1.Red = Color2.Red) and (Color1.Green = Color2.Green) and (Color1.Blue = Color2.Blue) and (Color1.Alpha = Color2.Alpha))
+End;
+
+{%endregion%}
+
+{%region=====[ TColor32Item ]===============================================}
+
+Constructor TColor32Item.Create;
+Begin
+  Inherited Create;
+  FName := 'Black';
+  FColor.Create(0,0,0);
+  FTag := 0;
+End;
+
+Destructor TColor32Item.Destroy;
+Begin
+  Inherited Destroy;
+End;
+
+Procedure TColor32Item.SetRed(Const AValue: Byte);
+Begin
+  If AValue = FColor.red Then exit;
+  FColor.Red := AValue;
+End;
+
+Procedure TColor32Item.SetGreen(Const AValue: Byte);
+Begin
+  If AValue = FColor.Green Then exit;
+  FColor.Green := AValue;
+End;
+
+Procedure TColor32Item.SetBlue(Const AValue: Byte);
+Begin
+  If AValue = FColor.Blue Then exit;
+  FColor.Blue := AValue;
+End;
+
+Procedure TColor32Item.SetAlpha(Const AValue: Byte);
+Begin
+  If AValue = FColor.Alpha Then exit;
+  FColor.Alpha := AValue;
+End;
+
+Procedure TColor32Item.SetValue(Const AValue: TColor32);
+Begin
+  If AValue = FColor Then exit;
+  FColor := AValue;
+End;
+
+Function TColor32Item.getRed: Byte;
+Begin
+  Result := FColor.Red;
+End;
+
+Function TColor32Item.getGreen: Byte;
+Begin
+  Result := FColor.Green;
+End;
+
+Function TColor32Item.getBlue: Byte;
+Begin
+  Result := FColor.Blue;
+End;
+
+Function TColor32Item.getAlpha: Byte;
+Begin
+  Result := FColor.Alpha;
+End;
+
+Function TColor32Item.getValue: TColor32;
+Begin
+  Result := FColor;
+End;
+
+Procedure TColor32Item.SetColorName(Const aName: String);
+Begin
+  If FName = aName Then exit;
+  FName := aName;
+End;
+
+{%endregion%}
+
+{%region ====[ TColor32List ]===============================================}
+
+Function TColor32List.GetColorItem(index: Integer): TColor32Item;
+Begin
+  Result := TColor32Item(Get(Index));
+End;
+
+Procedure TColor32List.SetColorItem(index: Integer; val: TColor32Item);
+Begin
+  Put(Index, Val);
+End;
+
+procedure TColor32List.Clear;
+Var
+  anItem: TColor32Item;
+  i : Integer;
+Begin
+  inherited Clear;
+  If Count > 0 then
+  begin
+    For i :=Count -1 downto 0 do
+    begin
+      AnItem:= Colors[i];
+      if anItem<>nil then anItem.Free;
+    End;
+  End;
+End;
+
+Function TColor32List.AddColor(Const aColor: TColor32): Integer;
+Var
+  aColorItem: TColor32Item;
+Begin
+  aColorItem := TColor32Item.Create;
+  aColorItem.Value := aColor;
+  Result := Add(aColorItem);
+End;
+
+Function TColor32List.AddColor(Const aName: String; Const aColor: TColor32): Integer;
+Var
+  aColorItem: TColor32Item;
+Begin
+  aColorItem := TColor32Item.Create;
+  aColorItem.Value := aColor;
+  aColorItem.Name := aName;
+  Result := Add(aColorItem);
+End;
+
+Function TColor32List.AddColor(Const aColorItem: TColor32Item): Integer;
+Begin
+  Result := Add(aColorItem);
+End;
+
+Procedure TColor32List.RemoveColor(Const aName: String);
+Var
+  I:   Integer;
+  Col: TColor32Item;
+Begin
+  FindColorByName(aName, I);
+  If I > -1 Then
+  Begin
+    Col := GetColorItem(I);
+    If Assigned(Col) Then
+      Col.Free;
+    Delete(I);
+  End;
+End;
+
+Function TColor32List.FindColorByName(Const aName: String; Out Index: Integer): TColor32;
+Var
+  i: Integer;
+Begin
+  Result := clrTransparent;
+  Index := -1;
+  For i := 0 To Count - 1 Do
+    If TColor32Item(Items[i]).Name = aName Then
+    Begin
+      Index := I;
+      Result := TColor32Item(Items[i]).Value;
+      break;
+    End;
+End;
+
+Function TColor32List.FindColorByName(Const aName: String): TColor32;
+Var
+  i: Integer;
+Begin
+  Result := FindColorByName(aName, I);
+End;
+
+{%endregion%}
+
+{%region=====[ TFastBitmap ]====================================================}
+
+Constructor TFastBitmap.Create(NewWidth, NewHeight : Integer);
+Begin
+ inherited Create;
+  FWidth  := Max(1,NewWidth);
+  FHeight := Max(1,NewHeight);
+  FData := Nil;
+  FSize := (int64(FWidth) * int64(FHeight))*4;
+  ReAllocMem(FData,FSize);
+  FTransparentColor := clBlack;
+End;
+
+Constructor TFastBitmap.Create;
+Begin
+  Create(1,1);
+End;
+
+Destructor TFastBitmap.Destroy;
+Begin
+  FreeMem(FData);
+  FData := Nil;
+  inherited Destroy;
+End;
+
+Procedure TFastBitmap.SetWidth(NewWidth : Integer);
+Begin
+  if NewWidth = FWidth then Exit;
+  SetSize(NewWidth, FHeight);
+End;
+
+Procedure TFastBitmap.SetHeight(NewHeight : Integer);
+Begin
+  if NewHeight = FHeight then Exit;
+  SetSize(FWidth, NewHeight);
+End;
+
+Function TFastBitmap.BuildBitmap: Graphics.TBitmap;
+Var
+  Temp : Graphics.TBitmap;
+  IntfBmp : TLazIntfImage;
+  ImgFormatDescription: TRawImageDescription;
+  W,H,X,Y : Integer;
+  SrcPix : PColor32;
+Begin
+
+  (* /!\ Le code si dessous fonctionne parfaitement sous Windows et Mac.
+     Mais sous Linux ce code produit des erreur au niveau de la transparence
+
+    BmpHandle := 0;
+    MskHandle := 0;
+    W := FWidth;
+    H := FHeight;
+    Buffer := PByte(GetSurfaceBuffer);
+
+    RawImage.Init;
+    {$IFDEF WINDOWS}
+    RawImage.Description.Init_BPP32_B8G8R8A8_BIO_TTB(W,H);
+    {$ELSE}
+    RawImage.Description.Init_BPP32_R8G8B8A8_BIO_TTB(W,H);
+    {$ENDIF}
+
+    RawImage.Data := Buffer;
+    RawImage.DataSize := FSize;
+
+    if not RawImage_CreateBitmaps(RawImage, BmpHandle, MskHandle,False) then
+      Raise Exception.Create('Impossible de créer le TBitmap')
+    else
+    begin
+      Temp := Graphics.TBitmap.Create;
+      Temp.Width := W;
+      Temp.Height := H;
+      Temp.PixelFormat := pf32bit;
+      Temp.Handle := BmpHandle;
+      Temp.MaskHandle := MskHandle;
+      Temp.Transparent := True;
+      //Temp.TransparentColor := FTransparentColor;
+      //temp.TransparentMode := tmAuto;
+      Result := Temp;
+    End;
+  *)
+
+  Result := nil;
+
+  W := FWidth;
+  H := FHeight;
+
+  // Pour que la transparence soit gérée correctement sous Linux on est obligé de passer par TLazIntfImage
+  IntfBmp := TLazIntfImage.Create(W,H);
+  ImgFormatDescription.Init_BPP32_B8G8R8A8_BIO_TTB(W, H);
+  IntfBmp.DataDescription := ImgFormatDescription;
+
+  SrcPix := Self.GetSurfaceBuffer;
+  For Y:=0 to H-1 do
+    For X:=0 to W-1 do
+    begin
+      IntfBmp.Colors[x, y]:=SrcPix^.ToFPColor;
+      inc(SrcPix);
+    end;
+
+  begin
+    Temp := Graphics.TBitmap.Create;
+    Temp.LoadFromIntfImage(IntfBmp);
+    Result := Temp;
+    IntfBmp.Free;
+  End;
+  if Result = nil then
+    Raise Exception.Create(rsBitmapCreateError);
+End;
+
+Function TFastBitmap.IsClipped(X, Y : Integer) : Boolean;
+Begin
+  Result := ((X>=0) and (Y>=0) and (X<FWidth) and (Y<FHeight));
+End;
+
+Procedure TFastBitmap.SwapRB;
+var
+  Pixptr: PColor32;
+  AIntColor : Cardinal;
+  PixelCount : Integer;
+begin
+  PixPtr := GetSurfaceBuffer;
+  PixelCount := (FWidth * FHeight)-1;
+  while pixelCount > 0 do
+  begin
+    AIntColor := PixPtr^.AsInteger;
+    PixPtr^.AsInteger := AIntColor and $FF00FF00 or (AintColor and $000000FF SHL 16) or (AIntColor and $00FF0000 SHR 16);
+    Inc(PixPtr);
+    Dec(pixelCount);
+  end;
+end;
+
+Procedure TFastBitmap.Assign(aFastBitmap : TFastBitmap);
+Begin
+  SetSize(aFastBitMap.Width, aFastBitmap.Height);
+  Move(PByte(aFastBitmap.GetSurfaceBuffer)^, PByte(FData)^, FSize);
+End;
+
+Procedure TFastBitmap.SetSize(NewWidth, NewHeight : Integer);
+Begin
+  FWidth  := Max(1,NewWidth);
+  FHeight := Max(1,NewHeight);
+  FSize :=(int64(FWidth) * int64(FHeight))*4;
+  if (FData<>nil) then
+  begin
+    FreeMem(FData);
+    FData := Nil;
+  End;
+  ReAllocMem(FData,FSize);
+  Clear(clrTransparent);
+End;
+
+Function TFastBitmap.ImportFromRawImage(Const ARawImage: TRawImage): Boolean;
+var
+  BufferData : PByte;
+begin
+  SetSize(ARawImage.Description.Width,ARawImage.Description.Height);
+  result:=false;
+  // On verifie si la taille des deux tampons sont identique
+  // Si ce n'est pas le cas, cela veut dire que le TRawImage n'est pas au format 32bit
+  if (ARawImage.DataSize= FSize) then
+  begin
+    try
+      BufferData := PByte(Self.getSurfaceBuffer);
+      Move(ARawImage.Data^, BufferData^, self.Size);
+      {$IFDEF WINDOWS}
+        if (ARawImage.Description.RedShift = 0) and ((ARawImage.Description.BlueShift = 16)) then Self.SwapRB; // Le RawImage est-il en RGB, si oui on échange
+      {$ELSE}
+        if (ARawImage.Description.RedShift = 16) and ((ARawImage.Description.BlueShift = 0)) then Self.SwapRB; // Le RawImage est-il en BGR, si oui on échange
+      {$ENDIF}
+    finally
+      result:=true;
+    end;
+  end;
+End;
+
+Function TFastBitmap.ImportFromBitmap(Const ABitmap: Graphics.TBitmap): Boolean;
+var
+  LTempBitmap: Graphics.TBitmap;
+  ok,ResetAlpha:Boolean;
+
+  procedure SetAlpha(Value : Byte);
+  var
+    i : Integer;
+    PixPtr : PColor32;
+    maxi : Integer;
+  begin
+    i:=0;
+    Maxi := (FWidth * FHeight)-1;
+    PixPtr :=PColor32(FData);// Self.GetScanLine(0);
+    While i<Maxi do
+    begin
+      PixPtr^.Alpha:= Value;
+      inc(PixPtr);
+      inc(i);
+    end;
+  end;
+
+begin
+  ResetAlpha:=False;
+  result:=false;
+  if (ABitmap.PixelFormat <> pf32bit)  then
+  begin
+    LTempBitmap := Graphics.TBitmap.Create;
+    try
+      ResetAlpha:=True;
+      LTempBitmap.SetSize(ABitmap.Width, ABitmap.Height);
+      LTempBitmap.PixelFormat := pf32bit;
+      LTempBitmap.Canvas.Draw(0, 0, ABitmap);
+    finally
+      ok:=Self.ImportFromRawImage(LTempBitmap.RawImage);
+      if ResetAlpha then SetAlpha(255);
+      FreeAndNil(LTempBitmap);
+      result:=true and (ok);
+    end;
+  end
+  else
+  begin
+   ok:=Self.ImportFromRawImage(ABitmap.RawImage);
+   result:=true and (ok);
+  end;
+End;
+
+Procedure TFastBitmap.Clear(Color : TColor32);
+Begin
+  FillDWord(FData^,FWidth * FHeight, DWord(Color));
+End;
+
+Function TFastBitmap.GetSurfaceBuffer: PColor32;
+Begin
+   Result := PColor32(FData);
+End;
+
+Function TFastBitmap.GetScanLine(Y : Integer) : PColor32;
+Var
+  yy : DWord;
+Begin
+  If (Y<0) or (Y>=FHeight) then
+    Raise Exception.Create(rsBitmapScanlineOutOfRange)
+  else
+  begin
+    yy := DWord(FWidth) * DWord(Y);
+    Result := PColor32(FData + YY);
+  End;
+End;
+
+Function TFastBitmap.GetPixelPtr(X, Y : Integer) : PColor32;
+Begin
+  Result := nil;
+  if IsClipped(X,Y) then
+  Begin
+    Result := PColor32(FData + (FWidth * Y) + X);
+  End;
+End;
+
+Procedure TFastBitmap.PutPixel(X, Y : Integer; Color : TColor32);
+Var
+  PixelPtr : PColor32;
+Begin
+  if IsClipped(X,Y) then
+  Begin
+    PixelPtr := PColor32(FData + DWord(FWidth * Y));
+    Inc(PixelPtr,X);
+    PixelPtr^:= Color;
+  End;
+End;
+
+Function TFastBitmap.GetPixel(X, Y : Integer) : TColor32;
+Var
+  PixelPtr : PColor32;
+Begin
+  Result := clrTransparent;
+  if IsClipped(X,Y) then
+  Begin
+    PixelPtr := PColor32(FData + (FWidth * Y) + X);
+    Result := PixelPtr^;
+  End;
+End;
+
+Procedure TFastBitmap.PutPixelBlend(X, Y : Integer; Color : TColor32);
+Var
+  PixelPtr : PColor32;
+Begin
+  if IsClipped(X,Y) then
+  Begin
+    PixelPtr := PColor32(FData + (FWidth * Y) + X);
+    PixelPtr^:= PixelPtr^.Blend(Color);
+  End;
+End;
+
+Procedure TFastBitmap.PutImage(Src : TFastBitmap; SrcX, SrcY, SrcWidth, SrcHeight, DstX, DstY : Integer; Mode : TFastBitmapDrawMode);
+Var
+  SrcPtr, DstPtr : PColor32;
+  NextSrcLine, NextDstLine : Integer;
+  DstCol, SrcCol : TColor32;
+  LineSize,TotalSize,xx,yy,i : Integer;
+
+  Procedure ClipCopyRect(Var SrcX, SrcY, rWidth, rHeight, DstX, DstY: Integer; SrcImageWidth, SrcImageHeight: Integer; Const DstClip: Types.TRect);
+    Var
+      diff, OldDstPosX, OldDstPosY: Integer;
+    Begin
+      OldDstPosX := 0;
+      If (DstX < 0) Then OldDstPosX := DstX;
+      OldDstPosY := 0;
+      If (DstY < 0) Then OldDstPosY := DstY;
+
+      If DstX < DstClip.Left Then
+      Begin
+        Diff := DstClip.Left - DstX;
+        rWidth := rWidth - Diff;
+        SrcX := SrcX + Diff;
+        DstX := DstClip.Left;
+      End;
+
+      If DstY < DstClip.Top Then
+      Begin
+        Diff := DstClip.Top - DstY;
+        rHeight := rHeight - Diff;
+        SrcY := SrcY + Diff;
+        DstY := DstClip.Bottom;
+      End;
+
+      If SrcX < 0 Then
+      Begin
+        Width := Width + SrcX - OldDstPosX;
+        DstX := DstX - SrcX + OldDstPosX;
+        SrcX := 0;
+      End;
+      If SrcY < 0 Then
+      Begin
+        rHeight := rHeight + SrcX - OldDstPosY;
+        DstY := DstY - SrcY + OldDstPosY;
+        SrcY := 0;
+      End;
+
+      If ((SrcX + rWidth) > SrcImageWidth) Then rWidth := SrcImageWidth - SrcX;
+      If ((SrcY + rHeight) > SrcImageHeight) Then rHeight := SrcImageHeight - SrcY;
+
+      if DstX > FWidth then DstX := 0;
+      if DstY > FHeight then DstY := 0;
+
+      If ((DstX + rWidth) >  (DstClip.Right+1)) Then rWidth := DstClip.Right - DstX;
+      If ((DstY + rHeight) > (DstClip.Bottom+1)) Then rHeight := DstClip.Bottom - DstY;
+
+    End;
+Begin
+
+  if (SrcWidth = 0) and (SrcHeight = 0) then exit;
+  ClipCopyRect(SrcX, SrcY, SrcWidth,SrcHeight, DstX, DstY, Src.Width, Src.Height, Types.Rect(0,0,FWidth-1, FHeight-1));
+
+
+  if (SrcWidth = 1) and (SrcHeight = 1) then
+  begin
+    Case Mode of
+      dmSet :
+        begin
+          SrcCol := Src.GetPixel(0,0);
+          PutPixel(0,0,SrcCol);
+        End;
+      dmAlpha :
+      begin
+        SrcCol := Src.GetPixel(0,0);
+        DstCol := GetPixel(0,0);
+        PutPixel(0,0,DstCol.Blend(SrcCol));
+      End;
+      dmAlphaCheck :
+        begin
+          If SrcCol.Alpha > 0 Then
+          begin
+            SrcCol := Src.GetPixel(0,0);
+            DstCol := GetPixel(0,0);
+            PutPixel(0,0,DstCol.Blend(SrcCol));
+          End
+          Else
+          begin
+            DstCol := GetPixel(0,0);
+            PutPixel(0,0,DstCol);
+          End;
+        End;
+    End;
+    exit;
+  End;
+
+  SrcPtr := Src.GetPixelPtr(SrcX,SrcY);
+  DstPtr := GetPixelPtr(DstX, DstY);
+
+  if SrcWidth <= Src.Width then
+    nextSrcLine := Src.Width
+  else
+    nextSrcLine := SrcX + (Src.Width - (SrcX + SrcWidth));
+  if Mode = dmSet then
+  begin
+    if (((Src.Width = FWidth) and (Src.Height = FHeight)) and ((SrcWidth = FWidth) and (SrcHeight = FHeight))) then
+      Move(SrcPtr^,DstPtr^,DWord(Src.Size))
+    else
+    begin
+      LineSize := SrcWidth * 4;
+      For I := 0 to SrcHeight-1 do
+      begin
+        Move(SrcPtr^, DstPtr^, LineSize);
+        Inc(SrcPtr, NextSrcLine);
+        Inc(DstPtr, FWidth);
+      End;
+    End;
+  End
+  else
+  begin
+    totalsize := (Src.Width * Src.Height) - 1;
+    Dec(SrcHeight);
+    xx := 0;
+    Dec(SrcWidth);
+    nextSrcLine := SrcX + (Src.Width - (SrcX + SrcWidth));
+    nextDstLine := DstX + (FWidth - (DstX + SrcWidth));
+    yy := 0;
+    xx := 0;
+    SrcCol := clrTransparent;
+    DstCol := clrTransparent;
+    While (yy <= TotalSize) Do
+    Begin
+      DstCol := DstPtr^;
+      SrcCol := SrcPtr^;
+      Case Mode of
+        dmAlpha :
+        begin
+          DstPtr^ := DstCol.Blend(SrcCol);
+        End;
+        dmAlphaCheck :
+        begin
+          If SrcCol.Alpha > 0 Then
+            DstPtr^ := DstCol.Blend(SrcCol)
+          Else
+            DstPtr^ := DstCol;
+        End;
+      End;
+      Inc(xx);
+      Inc(yy);
+      If (xx > SrcWidth) Then
+      Begin
+        xx := 0;
+        Inc(DstPtr, NextDstLine);
+        Inc(SrcPtr, NextSrcLine);
+      End
+      Else
+      Begin
+         Inc(SrcPtr);
+         Inc(DstPtr);
+      End;
+    End;
+  End;
+End;
+
+Function TFastBitmap.Clone : TFastBitmap;
+Var
+  NewBmp : TFastBitmap;
+Begin
+ NewBmp := TFastBitmap.Create;
+ NewBmp.Assign(Self);
+ Result := NewBmp;
+End;
+
+Function TFastBitmap.GetBitmap : Graphics.TBitmap;
+Begin
+  Result := BuildBitmap;
+End;
+
+Procedure TFastBitmap.Draw(ACanvas : TCanvas; X, Y : Integer);
+Var
+  Tmp : Graphics.TBitmap;
+Begin
+  Tmp :=  BuildBitmap;
+  ACanvas.Draw(X,Y,Tmp);
+  FreeAndNil(Tmp);
+End;
+
+Procedure TFastBitmap.Draw(ACanvas : TCanvas; Rect : TRect);
+Var
+  Tmp : Graphics.TBitmap;
+Begin
+  Tmp :=  BuildBitmap;
+  ACanvas.StretchDraw(Rect, Tmp);
+  FreeAndNil(Tmp);
+End;
+
+{%endregion%}
+End.
+
+

+ 2785 - 0
components/gifview/source/uGifViewer.pas

@@ -0,0 +1,2785 @@
+Unit uGifViewer;
+
+(*==============================================================================
+ DESCRIPTION   : Visual component for displaying an animated image in the
+                 GIF (Graphic Interchange Format) format
+ DATE          : 17/06/2018
+ UPDATE        : 01/07/2025
+ VERSION       : 1.0
+ AUTHOR        : J.Delauney (BeanzMaster)
+ CONTRIBUTORS  : Jipete, Jurassik Pork, bpranoto, Alexander Koblov
+ LICENSE       : MPL 2.0
+================================================================================
+*)
+
+{$mode objfpc}{$H+}
+
+Interface
+
+Uses
+  Types, Classes, SysUtils, Graphics, Math, Contnrs, Dialogs,
+  Controls, ExtCtrls,
+  Lresources, GifViewerStrConsts,
+  uFastBitmap;
+
+{%region=====[ Définitions des types et constantes utiles pour le format GIF ]===================================}
+Const
+  GIF_MaxColors  : Integer    = 256;    // Nombre de couleurs maximum supportées. NE PAS TOUCHER A CETTE VALEUR
+  GIF_DelayFactor  : Integer  = 10;     // Facteur de multiplication pour les délais en ms entre chaque image de l'animation
+  GIF_DefaultDelay  : Integer = 100;    // 10*10
+
+Type
+  TGIFVersion    = (gvUnknown, gv87a, gv89a);
+  TGIFVersionRec = Array[0..2] Of AnsiChar;
+
+Const
+  GIFVersions  : Array[gv87a..gv89a] Of TGIFVersionRec = ('87a', '89a');
+
+Type
+  { En-tête }
+  TGIFFileHeader = Packed Record
+    Signature: Array[0..2] Of AnsiChar; // 'GIF'
+    Version:   TGIFVersionRec;          // '87a' ou '89a' }
+  End;
+
+  { Description globale de l'image }
+  TGIFLogicalScreenDescriptorRec = Packed Record
+    ScreenWidth:  Word;             // Largeur de l'image en pixels // Width
+    ScreenHeight: Word;             // Hauteur de l'image en pixels // Height
+    PackedFields: Byte;             // champs compactés // Compacted field
+    BackgroundColorIndex: Byte;     // Index globale de la couleur de fond // Index of background color
+    AspectRatio:  Byte;             // Ratio d'échelle = (AspectRatio + 15) / 64
+  End;
+
+  { Description d'une image }
+  TGIFImageDescriptorRec = Packed Record
+    //Separator: byte;    // On lis toujours un byte avant  // we always read it before
+    Left:   Word;         // Colonne en pixels par rapport au bord gauche de l'écran // Column in pixels from the left edge of the screen
+    Top:    Word;         // Rangée en pixels par rapport au haut de l'écran // Row in pixels from the top edge of the screen
+    Width:  Word;         // Largeur de l'image en cours en pixels // image width
+    Height: Word;         // Hauteur de l'image en cours pixels // Image height
+    PackedFields: Byte;   // Champs compactés // Compacted field
+  End;
+
+  { Graphic Control Extension bloc a.k.a GCE }
+  TGIFGraphicControlExtensionRec = Packed Record
+    // BlockSize: byte;           // Normalement toujours 4 octets // Always 4 bytes
+    PackedFields: Byte;           // Champs compacté // Compacted field
+    DelayTime:    Word;           // Délai entre chaque image en centième de secondes // Delay between each image in hundredths of a second
+    TransparentColorIndex: Byte;  // Index dans la palette si plus petit ou égale  // Delay between each image in hundredths of a second
+    // Terminator: Byte;          // Normalement toujours ZERO // Normally always ZERO
+  End;
+
+  TGIFDisposalFlag = (dmNone, dmKeep, dmErase, dmRestore); // Methodes pour l'affichage des images lors de l'animation
+
+  { Plain Text Extension }
+  TGIFPlainTextExtensionRec = Packed Record
+    // BlockSize: byte;              // Normalement égal à 12 octets // Normally equal to 12 bytes
+    Left, Top, Width, Height: Word;  // Positions et dimensions du texte  // position and dimension of text
+    CellWidth, CellHeight:    Byte;  // Dimensions d'une cellule dans l'image // Size of cell
+    TextFGColorIndex,                // Index de la couleur de fond dans la palette // Index of the background color
+    TextBGColorIndex:         Byte;  // Index de la couleur du texte dans la palette // Index of the text color
+  End;
+
+  { Application Extension }
+  TGIFApplicationExtensionRec = Packed Record
+    AppID: Array [0..7] Of AnsiChar;                  // Identification de l'application majoritairement 'NETSCAPE' ou ''
+    AppAuthenticationCode: Array [0..2] Of AnsiChar;  // Code d'authentification ou numero de version
+  End;
+
+  { Informations de "l'application extension" si disponible }
+  TGIFNSLoopExtensionRec = Packed Record
+    Loops:      Word;   // Nombre de boucle de l'animation 0 = infinie  // nb loop
+    BufferSize: DWord;  // Taille du tampon. Usage ?????
+  End;
+
+Const
+  // Description des masques pour la description globale de l'image
+  GIF_GLOBALCOLORTABLE = $80;        // Défini si la table de couleurs globale suit la description globale
+  GIF_COLORRESOLUTION = $70;         // Résolution de la couleur (BitsPerPixel) - 3 bits
+  GIF_GLOBALCOLORTABLESORTED = $08;  // Définit si la palette globale est triée - 1 bit
+  GIF_COLORTABLESIZE  = $07;         // Taille de la palette - 3 bits
+  GIF_RESERVED        = $0C;         // Réservé - doit être défini avec $00 - Taille des données = 2^value+1 - 3 bits
+
+  // Descption des masques pour les images
+  GIF_LOCALCOLORTABLE = $80;        // Défini si la table de couleurs locale suit la description de l'image
+  GIF_INTERLACED      = $40;        // Défini si l'image est entrelacée
+  GIF_LOCALCOLORTABLESORTED = $20;  // Définit si la palette locale est triée
+
+  // Identification des blocs
+  GIF_PLAINTEXT       = $01;
+  GIF_GRAPHICCONTROLEXTENSION = $F9;
+  GIF_COMMENTEXTENSION = $FE;
+  GIF_APPLICATIONEXTENSION = $FF;
+  GIF_IMAGEDESCRIPTOR = $2C;       // ','
+  GIF_EXTENSIONINTRODUCER = $21;   // '!'
+  GIF_TRAILER         = $3B;               // ';'
+
+  // Graphic Control Extension - Définition des masques pour les paramètres
+  GIF_NO_DISPOSAL     = $00;           // 0
+  GIF_DO_NOT_DISPOSE  = $04;           // 1
+  GIF_RESTORE_BACKGROUND_COLOR = $08;  // 2
+  GIF_RESTORE_PREVIOUS = $12;          // 3
+  GIF_DISPOSAL_ALL    = $1C;           // bits 2-4 ($1C)
+  GIF_USER_INPUT_FLAG = $02;
+  GIF_TRANSPARENT_FLAG = $01;
+  GIF_RESERVED_FLAG   = $E0;
+
+  // Identification des sous-blocs pour "Application Extension"
+  GIF_LOOPEXTENSION   = 1;
+  GIF_BUFFEREXTENSION = 2;
+
+Const
+  GifGCEDisposalModeStr  : Array[TGIFDisposalFlag] Of String = ('None', 'Keep', 'Erase', 'Restore');
+
+Type
+  { Informations sur une image de l'animation }
+  TGIFFrameInformations = Record
+    Left, Top,                          // Position de l'image
+    Width, Height:   Integer;           // Dimension de l'image
+    HasLocalPalette: Boolean;           // Palette locale disponible
+    IsTransparent:   Boolean;           // Image transparente
+    UserInput:       Boolean;           // Données personnelle
+    BackgroundColorIndex: Byte;         // Normalement seulement valide si une palette globale existe
+    TransparentColorIndex: Byte;        // Index de la couleur transparente
+    DelayTime:       Word;              // Délai d'animation
+    Disposal:        TGIFDisposalFlag;  // Methode d'affichage
+    Interlaced:      Boolean;           // Image entrelacée
+  End;
+  PGifFrameInformations = ^TGifFrameInformations;
+
+  {%endregion%}
+
+  { TGIFFastMemoryStream }
+  { Classe d'aide à la lecture des données dans un flux en mémoire }
+  TGIFFastMemoryStream = Class
+  Private
+    FBuffer:   PByte;
+    FPosition: Int64;
+    FBytesRead, FBytesLeft, FSize: Int64;
+  Public
+    Constructor Create(AStream : TStream);
+    Destructor Destroy; Override;
+
+    { Lit un Byte dans le tampon / Read a byte in buffer }
+    Function ReadByte: Byte;
+    { Lit un Word dans le tampon / Read a word in buffer}
+    Function ReadWord: Word;
+    { Lit un DWord dans le tampon / Read a DWord in buffer }
+    Function ReadDWord: DWord;
+    { Lit et retourne un tampon "Buffer" de taille "Count" octets / Read a buffer of size "count" }
+    Function Read(Var Buffer; Count : Int64): Int64;
+    { Déplacement dans le flux de "Offset" depuis  "Origin"
+      TSeekOrigin =
+        - soBeginning : Depuis le début du flux
+        - soCurrent   : a partir de la position courante
+        - soEnd       : A partir de la fin du flux
+    }
+    Function Seek(Const Offset : Int64; Origin : TSeekOrigin): Int64;
+    { Déplacement dans le flux vers l'avant de "Cnt" octet depuis la position courrante }
+    Procedure SeekForward(Cnt : Integer);
+    { Indique si la fin du flux est atteinte (EOS = End Of Stream) }
+    Function EOS: Boolean;
+
+    { Retourne la taille du flux en octet // Size in byte of the buffer}
+    Property Size: Int64 read FSize;
+    { Retourne la position courrante de lecture dans le tampon // Current position in buffer }
+    Property Position: Int64 read FPosition;
+  End;
+
+  { TGIFLoadErrorEvent : Fonction d'évènement levée en cas d'erreur(s) dans le chargement // Event raise on error }
+  TGIFLoadErrorEvent = Procedure(Sender : TObject; Const ErrorCount : Integer; Const ErrorList : TStringList) Of Object;
+
+  { TGIFImageListItem }
+  { Définition d'une image contenue dans le fichier GIF }
+  TGIFImageListItem = Class
+  Private
+    FBitmap:      TFastBitmap;
+    FDrawMode:    TGIFDisposalFlag;
+    FLeft, FTop:  Integer;
+    FComment:     TStringList;
+    FDelay:       Integer;
+    FTransparent: Boolean;
+    FIsCorrupted : Boolean;
+  Protected
+  Public
+    Constructor Create;
+    Destructor Destroy; Override;
+
+    { Objet contenant l'image }
+    Property Bitmap: TFastBitmap read FBitmap write FBitmap;
+    { Mode de rendu de l'image // Render Mode}
+    Property DrawMode: TGIFDisposalFlag read FDrawMode write FDrawMode;
+    { Position gauche de l'image }
+    Property Left: Integer read FLeft write FLeft;
+    { Position Haut de l'image }
+    Property Top: Integer read FTop write FTop;
+    { Temps d'attente entre deux image de l'animation }
+    Property Delay: Integer read FDelay write FDelay;
+    { Commentaire sur l'image }
+    Property Comment: TStringList read FComment write FComment;
+    { Retourne TRUE si l'image utilise la transparence }
+    Property IsTransparent: Boolean read FTransparent write FTransparent;
+    { Indique si l'image est corrompue }
+    property IsCorrupted : Boolean read FIsCorrupted write FIsCorrupted;
+  End;
+
+  { TGIFImageList }
+  { Classe d'aide à la gestion des images contenues dans le fichier GIF }
+  { Helper class for manage image in GIF }
+  TGIFImageList = Class(TObjectList)
+  Private
+  Protected
+    Function GetItems(Index : Integer): TGIFImageListItem;
+    Procedure SetItems(Index : Integer; AGifImage : TGIFImageListItem);
+  Public
+    { Efface la liste  }
+    Procedure Clear; Override;
+    { Ajoute une nouvelle image vide à la liste }
+    Function AddNewImage: TGIFImageListItem;
+    { Ajout d'une image dans la liste }
+    Function Add(AGifImage : TGIFImageListItem): Integer;
+    { Extraction d'une image de la liste }
+    Function Extract(Item : TGIFImageListItem): TGIFImageListItem;
+    { Effacement d'une image dans la liste }
+    Function Remove(AGifImage : TGIFImageListItem): Integer;
+    { Retourne l'index de l'image recherchée (retourne -1 si non trouvé) }
+    Function IndexOf(AGifImage : TGIFImageListItem): Integer;
+    { Retourne la première image }
+    Function First: TGIFImageListItem;
+    { Retourne la dernière image }
+    Function Last: TGIFImageListItem;
+    { Insertion d'une image à la position "Index" }
+    Procedure Insert(Index : Integer; AGifImage : TGIFImageListItem);
+
+    { Liste des images }
+    Property Items[Index: Integer]: TGIFImageListItem read GetItems write SetItems; Default;
+  End;
+
+  { TGIFImageLoader }
+  { Classe spécialisée pour la lecture d'une image au format GIF }
+  { Special class for read a GIF }
+  TGIFImageLoader = Class
+  Private
+    FCurrentLayerIndex: Integer;
+    FGIFFIleHeader: TGIFFileHeader;
+    FLogicalScreenChunk: TGIFLogicalScreenDescriptorRec;
+    FHasGlobalPalette: Boolean;
+    FTransparent: Boolean;
+    FGlobalPalette: TColor32List;
+    FVersion: String;
+
+    FWidth, FHeight:  Integer;
+    FBackgroundColor: TColor32;
+
+    FFrames: TGIFImageList;
+
+    FErrorList:   TStringList;
+    FErrorCount:  Integer;
+    FOnLoadError: TGIFLoadErrorEvent;
+    Procedure SetCurrentLayerIndex(AValue : Integer);
+
+  Protected
+    Memory: TGIFFastMemoryStream;
+
+    CurrentFrameInfos: TGifFrameInformations;
+
+    Function GetFrameCount: Integer;
+    Procedure LoadFromMemory();
+    Function CheckFormat(): Boolean;
+    Function ReadImageProperties: Boolean;
+    Procedure AddError(Msg : String);
+    Procedure NotifyError;
+  Public
+    Constructor Create;
+    Destructor Destroy; Override;
+
+    { LoadFromStream : Charge les données depuis un flux }
+    Procedure LoadFromStream(aStream : TStream); Virtual;
+    { LoadFromFile : Charge les données depuis un fichier physique }
+    Procedure LoadFromFile(Const FileName : String); Virtual;
+    { Chargement depuis une Resource Lazarus }
+    Procedure LoadFromResource(Const ResName : String);
+    { Retourne la version du fichier GIF }
+    Property Version: String read FVersion;
+    { Retourne la largeur de l'image GIF }
+    Property Width: Integer read FWidth;
+    { Retourne la hauteur de l'image GIF }
+    Property Height: Integer read FHeight;
+    { Retourne la couleur de l'image GIF si elle existe,. Sinon retourne une couleur transparente (clrTransparent) }
+    Property BackgroundColor: TColor32 read FBackgroundColor write FBackgroundColor;
+    { Prise en charge de la transparence dans l'image GIF  // Take transparency in account}
+    Property Transparent: Boolean read FTransparent write FTransparent;
+    { Retourne l'index courrant de l'image de l'animation traité  // Return the current index frame}
+    Property CurrentFrameIndex: Integer read FCurrentLayerIndex write SetCurrentLayerIndex;
+    { Liste des images de l'animation // List of frame}
+    Property Frames: TGIFImageList read FFrames;
+    { Nombre d'image de l'animation // Nb frames }
+    Property FrameCount: Integer read GetFrameCount;
+    { Nombre d'erreur produite loars d'un cahrgement ou d'un enregistrement // Nb error }
+    Property ErrorCount: Integer read FErrorCount;
+    { Liste des erreurs // List of error }
+    Property Errors: TStringList read FErrorList;
+
+    { Evenement pour intercepter les erreurs notifiées lors du chargement des données // Error Event }
+    Property OnLoadError: TGIFLoadErrorEvent read FOnLoadError write FOnLoadError;
+  End;
+
+  { TGIFRenderCacheListItem }
+  { Définition d'une image cache de l'animation }
+  { Image cache class }
+  TGIFRenderCacheListItem = Class
+  Private
+    FBitmap: Graphics.TBitmap;
+    FDelay:  Integer;
+    FIsCorrupted : Boolean;
+  Public
+    Constructor Create;
+    Destructor Destroy; Override;
+    { Image cache prérendu de l'animation }
+    Property Bitmap: Graphics.TBitmap read FBitmap write FBitmap;
+    { Temps d'attente en ms avec l'image suivante }
+    Property Delay: Integer read FDelay write FDelay;
+    { Indique si l'image est corrompue }
+    property IsCorrupted : Boolean read FIsCorrupted write FIsCorrupted;
+  End;
+
+  { TGIFRenderCacheList }
+  { Classe d'aide à la gestion des images rendues de l'animation }
+  { Helper class for manage list of image cache }
+  TGIFRenderCacheList = Class(TObjectList)
+  Private
+  Protected
+    Function GetItems(Index : Integer): TGIFRenderCacheListItem;
+    Procedure SetItems(Index : Integer; AGIFRenderCache : TGIFRenderCacheListItem);
+  Public
+    { Efface la liste }
+    Procedure Clear; Override;
+    { Ajoute un nouvel objet cache vide }
+    Function AddNewCache: TGIFRenderCacheListItem;
+    { Ajoute un nouveau cache }
+    Function Add(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
+    { Extrait un cache de la liste }
+    Function Extract(Item : TGIFRenderCacheListItem): TGIFRenderCacheListItem;
+    { Supprime un cache de la liste }
+    Function Remove(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
+    { Retourne l'index du cache recherchée (retourne -1 si non trouvé) }
+    Function IndexOf(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
+    { Retourne le premier élément de la liste }
+    Function First: TGIFRenderCacheListItem;
+    { Retourne le dernier élément de la liste }
+    Function Last: TGIFRenderCacheListItem;
+    { Insertion d'un cache à la position "Index" }
+    Procedure Insert(Index : Integer; AGIFRenderCache : TGIFRenderCacheListItem);
+    { Vérifie si "anIndex" ne dépasse pas la nombre d'élément dans la liste. Retroune FALSE si l'index est hors limite }
+    { Check if 'anIndex' does not exceed the number of items in the list. Retrieve FALSE if the index is out of range }
+    function IsIndexOk(anIndex : Integer) : Boolean;
+    { Supprime les éléments dont le drapeau "IsCorrupted" est vrai  }
+    { Remove items wich "IsCorrupted" flag is on True }
+    procedure Pack;
+    { Liste des caches }
+    Property Items[Index: Integer]: TGIFRenderCacheListItem read GetItems write SetItems; Default;
+  End;
+
+  { TGIFAutoStretchMode
+    Mode de redimensionnement automatique}
+  TGIFAutoStretchMode = (smManual, smStretchAll, smStretchOnlyBigger, smStretchOnlySmaller );
+  TOnStretchChanged = procedure (Sender:TObject; IsStretched : Boolean) of object;
+  { TGIFViewer }
+  { Composant visuel pour afficher une image GIF animée }
+  { Visual component for display the animated GIF }
+  TGIFViewer = Class(TGraphicControl)
+  Private
+    FAutoStretchMode: TGIFAutoStretchMode;
+    FGIFLoader: TGIFImageLoader;
+    FLastDrawMode : TGIFDisposalFlag;
+    FFileName:  String;
+
+    FRestoreBitmap, FVirtualView: TFastBitmap;
+
+    FRenderCache:       TGIFRenderCacheList;
+    FCurrentFrameIndex: Integer;
+    FGIFWidth, FGIFHeight: Integer;
+    FCurrentView:       Graphics.TBitmap;
+
+    FAnimateTimer:     TTimer;
+    FAnimateSpeed:     Integer;
+    FAnimated, FPause: Boolean;
+    FAutoPlay:         Boolean;
+    FCache:            Boolean;
+
+    FDisplayInvalidFrames : Boolean;
+    FAutoRemoveInvalidFrame : Boolean;
+
+    FPainting:         Boolean;
+
+    FBorderShow:       Boolean;
+    FBorderColor:      TColor;
+    FBorderWidth:      Byte;
+    FBevelInner, FBevelOuter: TPanelBevel;
+    FBevelWidth:       TBevelWidth;
+    FBevelColor, FColor: TColor;
+
+    FCenter, FStretch, FTransparent: Boolean;
+
+    FOnStart, FOnStop, FOnPause, FOnFrameChange: TNotifyEvent;
+    FOnLoadError : TGIFLoadErrorEvent;
+    FOnStretchChanged : TOnStretchChanged;
+
+    Function GetCanvas: TCanvas;
+    Function GetFrameCount: Integer;
+    Function GetGIFVersion: String;
+    Function GetRawFrameItem(Index : Integer): TGIFImageListItem;
+    Procedure SetAutoStretchMode(AValue: TGIFAutoStretchMode);
+    Procedure SetCenter(Const Value : Boolean);
+    Procedure SetStretch(Const Value : Boolean);
+    Procedure SetPause(Const Value : Boolean);
+    Procedure SetFileName(Const Value : String);
+    Function GetFrame(Const Index : Integer): Graphics.TBitmap;
+    Procedure SetTransparent(Const Value : Boolean);
+    Procedure SetBevelInner(Const Value : TPanelBevel);
+    Procedure SetBevelOuter(Const Value : TPanelBevel);
+    Procedure SetBevelWidth(Const Value : TBevelWidth);
+
+    procedure ResetCurrentView;
+  Protected
+    Procedure DoInternalOnLoadError(Sender : TObject; Const ErrorCount : Integer; Const ErrorList : TStringList);
+    Procedure DoTimerAnimate(Sender : TObject);
+
+    { Rendu d'une image de l'animation }
+    procedure RenderFrame(Index : Integer); Virtual;
+    { Creation des image cache pour l'animation }
+    Procedure ComputeCache; Virtual;
+    { Calcul de la postion et de la dimension pour l'afficchage sur le "Canvas" }
+    Function DestRect: TRect; Virtual;
+
+    { Fonctions hérités }
+    Procedure CalculatePreferredSize(Var PreferredWidth, PreferredHeight : Integer; {%H-}WithThemeSpace : Boolean); Override;
+    Class Function GetControlClassDefaultSize: TSize; Override;
+    Procedure Paint; Override;
+    procedure Loaded; override;
+    procedure BeforeLoad;
+    procedure AfterLoad;
+  Public
+    { Création du composant }
+    Constructor Create(AOwner : TComponent); Override;
+    { Destruction du composant }
+    Destructor Destroy; Override;
+
+    { Mise à jour de la surface de dessin (Canvas) du composant }
+    Procedure Invalidate; Override;
+    { LoadFromStream : Charge les données depuis un flux }
+    Procedure LoadFromStream(aStream : TStream);
+    { Chargement depuis un fichier }
+    Procedure LoadFromFile(Const aFileName : String);
+    { Chargement depuis une Resource Lazarus }
+    Procedure LoadFromResource(Const ResName : String);
+    { Joue l'animation }
+    Procedure Start;
+    { Arrête l'animation }
+    Procedure Stop;
+    { Met en pause l'animation }
+    Procedure Pause;
+    Procedure NextFrame;
+    Procedure PriorFrame;
+    { Retourne l'image brute du GIF à la position Index }
+    Function GetRawFrame(Index : Integer): TBitmap;
+    { Affiche l'image de l'animation mise en cache à la position Index }
+    Procedure DisplayFrame(Index : Integer);
+    { Affiche l'image brute de l'animation à la position Index }
+    Procedure DisplayRawFrame(Index : Integer);
+    { Extrait l'image de l'animation mise en cache à la position Index vers un TBitmap }
+    procedure ExtractFrame(Index : Integer; Var bmp:TBitmap) ;
+    { Extrait l'image brute de l'animation à la position Index vers un TBitmap}
+    procedure ExtractRawFrame(Index : Integer; Var bmp:TBitmap);
+    { Retourne le Canvas du composant }
+    Property Canvas: TCanvas read GetCanvas;
+    { Retourne TRUE si l'animation est en pause }
+    Property Paused: Boolean read FPause;
+    { Retourne TRUE si l'animation est en cours }
+    Property Playing: Boolean read FAnimated;
+    { Retourne l'index actuel de l'image affichée // Current Index of displayed frame }
+    Property CurrentFrameIndex: Integer read FCurrentFrameIndex;
+    { Liste des images de l'animation // List of frame}
+    Property Frames[Index: Integer]: TBitmap read GetFrame;
+    { Retourne le nombre d'image de l'animation // Number of frames }
+    Property FrameCount: Integer read GetFrameCount;
+    { Retourne la version du fichier GIF chargé // version of the gif }
+    Property Version: String read GetGIFVersion;
+    { Image courante de l'animation affichée // Current displayed image }
+    Property CurrentView: Graphics.TBitmap read FCurrentView;
+
+    property RawFrames[Index : Integer] : TGIFImageListItem read GetRawFrameItem;
+  Published
+    Property Color: TColor read FColor write FColor;
+    { Bordure visible autour du composant // Border visible around component }
+    Property Border: Boolean read FBorderShow write FBorderShow;
+    { Couleur de la bordure // Color of border }
+    Property BorderColor: TColor read FBorderColor write FBorderColor;
+    { Epaisseur de la bordure // Width of border }
+    Property BorderWidth: Byte read FBorderWidth write FBorderWidth;
+
+    Property BevelColor: TColor read FBevelColor write FBevelColor;
+    Property BevelInner: TPanelBevel read FBevelInner write SetBevelInner Default bvNone;
+    Property BevelOuter: TPanelBevel read FBevelOuter write SetBevelOuter Default bvRaised;
+    Property BevelWidth: TBevelWidth read FBevelWidth write SetBevelWidth Default 1;
+
+    Property Cache: Boolean read FCache write FCache;
+    { Joue l'animation automatiquement lors du chargement d'une image GIF animée }
+    { Play animation automatically when loading an animated GIF image }
+    Property AutoPlay: Boolean read FAutoPlay write FAutoPlay;
+    { Affichage du GIF avec prise en charge de la transparence }
+    { GIF view with transparency support }
+    Property Transparent: Boolean read FTransparent write SetTransparent;
+    { Centrer l'affichage // Center display }
+    Property Center: Boolean read FCenter write SetCenter;
+    { Mode du redimensionnement // Automatic stretch mode
+      smManual             : Adpatation Manuelle via la propriété stretch
+      smStretchAll         : Adapte toute les images
+      smStretchOnlyBigger  : Adapte seulement les images plus grande
+      smStretchOnlySmaller : Adapte seulement les images plus petite
+     }
+    property AutoStretchMode : TGIFAutoStretchMode read FAutoStretchMode write SetAutoStretchMode;
+    { Redimensionner l'affichage proportionnellement // Resize the display proportionally }
+    Property Stretch: Boolean read FStretch write SetStretch;
+    { Nom du fichier à charger // Name of file to load }
+    Property FileName: String read FFileName write SetFileName;
+    { Définis si les images corrompues doivent être affichées. Si le GIF contient que une seule image ce paramètre n'est pas appliqué. Par defaut FALSE }
+    property DisplayInvalidFrames : Boolean read FDisplayInvalidFrames write FDisplayInvalidFrames;
+    { Définis si les images corrompues doivent être effacées de la liste de l'animation automatiquement. Par defaut TRUE }
+    property AutoRemoveInvalidFrame : Boolean Read FAutoRemoveInvalidFrame write FAutoRemoveInvalidFrame;
+
+    { Evènement déclenché lorsque l'animation débute }
+    { Event triggered when the animation starts }
+    Property OnStart: TNotifyEvent read FOnStart write FOnStart;
+    { Evènement déclenché lorsque l'animation s'arrête }
+    { Event triggered when the animation stops }
+    Property OnStop: TNotifyEvent read FOnStop write FOnStop;
+    { Evènement déclenché lorsque l'animation est mise en pause }
+    { Event triggered when the animation is paused }
+    Property OnPause: TNotifyEvent read FOnPause write FOnPause;
+    { Evènement déclenché lorsque une nouvelle image est affiché lors de l'animation }
+    { Event triggered when a new image is displayed during the animation }
+    Property OnFrameChange: TNotifyEvent read FOnFrameChange write FOnFrameChange;
+    { Evenement pour intercepter les erreurs notifiées lors du chargement des données }
+    Property OnLoadError: TGIFLoadErrorEvent read FOnLoadError write FOnLoadError;
+    { Evenement pour intercepter le changement du mode stretch. Uniquement si AutoStretchMode <> smManual }
+    { Event to intercept the change of the stretch mode. Only if AutoStretchMode <> smManual }
+    property OnStretchChanged : TOnStretchChanged read FOnStretchChanged write FOnStretchChanged;
+
+    { Propriétés héritées }
+    Property Align;
+    Property Anchors;
+    Property AutoSize;
+    Property Constraints;
+    Property BorderSpacing;
+    Property Visible;
+    Property ParentShowHint;
+    Property ShowHint;
+    { Evènements héritées }
+    Property OnClick;
+    Property OnMouseDown;
+    Property OnMouseEnter;
+    Property OnMouseLeave;
+    Property OnMouseMove;
+    Property OnMouseUp;
+    Property OnMouseWheel;
+    Property OnMouseWheelDown;
+    Property OnMouseWheelUp;
+
+  End;
+
+  TGIFView = Class(TGIFViewer);
+
+Procedure Register;
+
+Implementation
+
+Uses
+  GraphType;
+
+{$R ../gifview.res}
+
+{%region=====[ Constantes et types internes ]===================================}
+
+
+Type
+  // Statut de décodage / encodage LZW
+  TLZWDecoderStatus = (
+    dsOK,                     // Tout va bien
+    dsNotEnoughInput,         // Tampon d'entrée trop petit
+    dsOutputBufferTooSmall,   // Tampon de sortie trop petit
+    dsInvalidInput,           // Donnée corrompue
+    dsBufferOverflow,         // débordement de tampon
+    dsInvalidBufferSize,      // Taille d'un des tampons invalide
+    dsInvalidInputBufferSize, // Taille du tampon d'entrée invalide
+    dsInvalidOutputBufferSize,// Taille du tampon de sortie invalide
+    dsInternalError           // Erreur interne signifiant qu'il y a un défaut dans le code
+    );
+
+{%endregion%}
+
+{%region=====[ Fonctions utiles ]===============================================}
+
+Function FixPathDelimiter(S : String): String;
+Var
+  I: Integer;
+Begin
+  Result := S;
+  For I  := Length(Result) Downto 1 Do
+  Begin
+    If (Result[I] = '/') Or (Result[I] = '\') Then Result[I] := PathDelim;
+  End;
+End;
+
+Function CreateFileStream(Const fileName : String; mode : Word = fmOpenRead + fmShareDenyNone): TStream;
+Var
+  fn: String;
+Begin
+  fn := filename;
+  FixPathDelimiter(fn);
+  If ((mode And fmCreate) = fmCreate) Or FileExists(fn) Then Result := TFileStream.Create(fn, mode)
+  Else
+    Raise Exception.Create('Fichier non trouvé : "' + fn + '"');
+
+End;
+
+{%endregion%}
+
+{%region=====[ TGIFFastMemoryStream ]==============================================}
+
+Constructor TGIFFastMemoryStream.Create(AStream : TStream);
+Var
+  ms: TMemoryStream;
+Begin
+  ms := TMemoryStream.Create;
+  With ms Do
+  Begin
+    CopyFrom(aStream, 0);
+    Position := 0;
+  End;
+  FSize      := ms.Size;
+  FPosition  := 0;
+  FBytesLeft := FSize;
+  FBytesRead := 0;
+  FBuffer    := nil;
+  ReAllocMem(FBuffer, FSize);
+  Move(PByte(ms.Memory)^, FBuffer^, FSize);
+  FreeAndNil(ms);
+End;
+
+Destructor TGIFFastMemoryStream.Destroy;
+Begin
+  If FBuffer <> nil Then
+  Begin
+    FreeMem(FBuffer);
+    FBuffer := nil;
+  End;
+  Inherited Destroy;
+End;
+
+Function TGIFFastMemoryStream.ReadByte: Byte;
+Begin
+  Result := 0;
+  If FBytesLeft > 0 Then
+  Begin
+    Result := PByte(FBuffer + FPosition)^;
+    Inc(FPosition);
+    Inc(FBytesRead);
+    Dec(FBytesLeft);
+  End;
+End;
+
+Function TGIFFastMemoryStream.ReadWord: Word;
+Begin
+  Result := 0;
+  If (FBytesLeft >= 2) Then
+  Begin
+    Result := PWord(FBuffer + FPosition)^;
+    Inc(FPosition, 2);
+    Inc(FBytesRead, 2);
+    Dec(FBytesLeft, 2);
+  End;
+End;
+
+Function TGIFFastMemoryStream.ReadDWord: DWord;
+Begin
+  Result := 0;
+  If (FBytesLeft >= 4) Then
+  Begin
+    Result := PDWord(FBuffer + FPosition)^;
+    Inc(FPosition, 4);
+    Inc(FBytesRead, 4);
+    Dec(FBytesLeft, 4);
+  End;
+End;
+
+Function TGIFFastMemoryStream.Read(Var Buffer; Count : Int64): Int64;
+Var
+  NumOfBytesToCopy, NumOfBytesLeft: Longint;
+  CachePtr, BufferPtr: PByte;
+Begin
+  Result := 0;
+
+  If (Count > FBytesLeft) Then NumOfBytesLeft := FBytesLeft
+  Else
+    NumOfBytesLeft := Count;
+
+  BufferPtr := @Buffer;
+
+  While NumOfBytesLeft > 0 Do
+  Begin
+    // On copie les données
+    NumOfBytesToCopy := Min(FSize - FPosition, NumOfBytesLeft);
+    CachePtr         := FBuffer;
+    Inc(CachePtr, FPosition);
+
+    Move(CachePtr^, BufferPtr^, NumOfBytesToCopy);
+    Inc(Result, NumOfBytesToCopy);
+    Inc(FPosition, NumOfBytesToCopy);
+    Inc(BufferPtr, NumOfBytesToCopy);
+    // On met à jour les marqueur de notre tampon
+    Inc(FBytesRead, NumOfBytesToCopy);
+    Dec(FBytesLeft, NumOfBytesToCopy);
+    Dec(NumOfBytesLeft, NumOfBytesToCopy);
+
+  End;
+End;
+
+Function TGIFFastMemoryStream.Seek(Const Offset : Int64; Origin : TSeekOrigin): Int64;
+Var
+  NewPos: Integer;
+Begin
+  // Calcul de la nouvelle position
+  Case Origin Of
+    soBeginning: NewPos := Offset;
+    soCurrent: NewPos   := FPosition + Offset;
+    soEnd: NewPos       := pred(FSize) - Offset;
+    Else
+      Raise Exception.Create('TFastStream.Seek: Origine Invalide');
+  End;
+  Result := NewPos;
+  If Offset = 0 Then exit;
+
+  FPosition  := NewPos;
+  FBytesLeft := FSize - FPosition;
+  Result     := NewPos;
+End;
+
+Procedure TGIFFastMemoryStream.SeekForward(Cnt : Integer);
+Begin
+  Seek(Cnt, soCurrent);
+End;
+
+Function TGIFFastMemoryStream.EOS: Boolean;
+Begin
+  Result := ((FBytesLeft <= 0) Or (FPosition >= Pred(FSize)));
+End;
+
+{%endregion%}
+
+{%region=====[ TGIFImageListItem ]==============================================}
+
+Constructor TGIFImageListItem.Create;
+Begin
+  FBitmap   := TFastBitmap.Create;
+  FLeft     := 0;
+  FTop      := 0;
+  FDelay    := 0;
+  FDrawMode := dmNone;
+  FComment  := TStringList.Create;
+  FComment.Clear;
+  FIsCorrupted := False;
+End;
+
+Destructor TGIFImageListItem.Destroy;
+Begin
+  FreeAndNil(FComment);
+  FreeAndNil(FBitmap);
+  Inherited Destroy;
+End;
+
+{%endregion%}
+
+{%region=====[ TGIFImageList ]==================================================}
+
+Function TGIFImageList.GetItems(Index : Integer): TGIFImageListItem;
+Begin
+  Result := TGIFImageListItem(Inherited Items[Index]);
+End;
+
+Procedure TGIFImageList.SetItems(Index : Integer; AGifImage : TGIFImageListItem);
+Begin
+  Put(Index, AGifImage);
+End;
+
+Procedure TGIFImageList.Clear;
+Var
+  anItem: TGIFImageListItem;
+  i:      Integer;
+Begin
+  If Count > 0 Then
+  Begin
+    For i := Count - 1 Downto 0 do
+    Begin
+      AnItem := Items[i];
+      If anItem <> nil Then anItem.Free;
+    End;
+  End;
+  Inherited Clear;
+End;
+
+Function TGIFImageList.AddNewImage: TGIFImageListItem;
+Var
+  anItem: TGIFImageListItem;
+Begin
+  anitem := TGIFImageListItem.Create;
+  Add(anItem);
+  Result := Items[Self.Count - 1];
+End;
+
+Function TGIFImageList.Add(AGifImage : TGIFImageListItem): Integer;
+Begin
+  Result := Inherited Add(AGifImage);
+End;
+
+Function TGIFImageList.Extract(Item : TGIFImageListItem): TGIFImageListItem;
+Begin
+  Result := TGIFImageListItem(Inherited Extract(Item));
+End;
+
+Function TGIFImageList.Remove(AGifImage : TGIFImageListItem): Integer;
+Begin
+  Result := Inherited Remove(AGifImage);
+End;
+
+Function TGIFImageList.IndexOf(AGifImage : TGIFImageListItem): Integer;
+Begin
+  Result := Inherited IndexOf(AGifImage);
+End;
+
+Function TGIFImageList.First: TGIFImageListItem;
+Begin
+  Result := TGIFImageListItem(Inherited First);
+End;
+
+Function TGIFImageList.Last: TGIFImageListItem;
+Begin
+  Result := TGIFImageListItem(Inherited Last);
+End;
+
+Procedure TGIFImageList.Insert(Index : Integer; AGifImage : TGIFImageListItem);
+Begin
+  Inherited Insert(Index, AGifImage);
+End;
+
+{%endregion%}
+
+{%region=====[ TGIFImageLoader ]================================================}
+
+Constructor TGIFImageLoader.Create;
+Begin
+  Inherited Create;
+  FFrames        := TGIFImageList.Create(False);
+  FErrorList     := TStringList.Create;
+  FErrorCount    := 0;
+  FGlobalPalette := nil;
+  FTransparent   := True;
+  FBackgroundColor := clrTransparent;
+End;
+
+Destructor TGIFImageLoader.Destroy;
+Begin
+  FreeAndNil(FFrames);
+  FreeAndNil(FErrorList);
+  Inherited Destroy;
+End;
+
+Function TGIFImageLoader.CheckFormat(): Boolean;
+Begin
+  Result := False;
+  // Chargement de l'en-tête
+  Memory.Read(FGIFFileHeader, SizeOf(TGIFFileHeader));
+  // Vérification de quelques paramètres
+  Result := uppercase(String(FGIFFileHeader.Signature)) = 'GIF';
+  If Result Then
+  Begin
+    // Le fichier est valide
+    // On sauvegarde la version du GIF
+    FVersion := String(FGIFFileHeader.Version);
+    If (FVersion = GIFVersions[gv87a]) Or (FVersion = GIFVersions[gv89a]) Then Result := ReadImageProperties // On lit les propriétés
+    Else
+      Raise Exception.Create(rsUnknownVersion);
+  End
+  Else
+  Begin
+    // Signature du fichier GIF Invalide. On lève une exception
+    Raise Exception.Create(Format(rsBadSignature,[uppercase(String(FGIFFileHeader.Signature))]));
+  End;
+End;
+
+Function TGIFImageLoader.ReadImageProperties: Boolean;
+Begin
+  Result := False;
+
+  Memory.Read(FLogicalScreenChunk, SizeOf(TGIFLogicalScreenDescriptorRec));
+
+  // On sauvegarde en local les dimensions de l'image, pour plus tard
+  FWidth  := FLogicalScreenChunk.ScreenWidth;
+  FHeight := FLogicalScreenChunk.ScreenHeight;
+
+  If (FWidth < 1) Or (FHeight < 1) Then
+  Begin
+    // Dimensions incorrectes on lève une exception
+    Raise Exception.Create(Format(rsBadScreenSize,[FWidth,FHeight]));
+    exit;
+  End;
+  FHasGlobalPalette := (FLogicalScreenChunk.PackedFields And GIF_GLOBALCOLORTABLE) <> 0;
+
+  Result := True;
+End;
+
+Procedure TGIFImageLoader.AddError(Msg : String);
+Begin
+  FErrorList.Add(Msg);
+End;
+
+Procedure TGIFImageLoader.NotifyError;
+Begin
+  If FErrorList.Count > 0 Then
+  Begin
+    If Assigned(FOnLoadError) Then FOnLoadError(Self, FErrorList.Count, FErrorList);
+  End;
+End;
+
+Procedure TGIFImageLoader.LoadFromStream(aStream : TStream);
+Begin
+  If Memory <> nil Then FreeAndNil(Memory);
+  Memory := TGIFFastMemoryStream.Create(aStream);
+  If CheckFormat Then LoadFromMemory;
+  FreeAndNil(Memory);
+End;
+
+Procedure TGIFImageLoader.LoadFromFile(Const FileName : String);
+Var
+  Stream: TStream;
+Begin
+  FErrorList.Clear;
+  FErrorCOunt := 0;
+  Stream      := CreateFileStream(FileName);
+  Try
+    LoadFromStream(Stream);
+  Finally
+    FreeAndNil(Stream);
+  End;
+End;
+
+Procedure TGIFImageLoader.LoadFromResource(Const ResName : String);
+Var
+  Stream: TLazarusResourceStream;
+Begin
+  FErrorList.Clear;
+  FErrorCOunt := 0;
+  Stream      := TLazarusResourceStream.Create(ResName, nil);
+  Try
+    LoadFromStream(Stream);
+  Finally
+    FreeAndNil(Stream);
+  End;
+End;
+
+Function TGIFImageLoader.GetFrameCount: Integer;
+Begin
+  Result := FFrames.Count;
+End;
+
+Procedure TGIFImageLoader.SetCurrentLayerIndex(AValue : Integer);
+Begin
+  If FCurrentLayerIndex = AValue Then Exit;
+  FCurrentLayerIndex := AValue;
+End;
+
+Procedure TGIFImageLoader.LoadFromMemory();
+Var
+  aRGBColor: TColorRGB24;
+  aColor: TColor32;
+  PaletteCount: Integer;
+  Done: Boolean;
+  BlockID: Byte;
+  BlockSize: Byte;
+  Terminator{%H-}: Byte;
+  CurrentLayer: TGIFImageListItem;
+
+  ImageDescriptor: TGIFImageDescriptorRec;
+  GraphicControlExtensionChunk: TGIFGraphicControlExtensionRec;
+  ApplicationExtensionChunk: TGIFApplicationExtensionRec;
+  NSLoopExtensionChunk: TGIFNSLoopExtensionRec;
+  PlainTextChunk: TGIFPlainTextExtensionRec;
+
+  LocalPalette: TColor32List;
+  ColorCount: Integer;
+  DMode: Byte;
+  ret: TLZWDecoderStatus;
+
+  { Chargement palette globale }
+  Procedure LoadGlobalPalette;
+  Var
+    J: Byte;
+  Begin
+    If FHasGlobalPalette Then
+    Begin
+      // Remise à zero de la palette globale si elle existe sinon création de celle-ci
+      If FGlobalPalette = nil Then FGlobalPalette := TColor32List.Create
+      Else
+        FGlobalPalette.Clear;
+
+      PaletteCount := 2 Shl (FLogicalScreenChunk.PackedFields And GIF_COLORTABLESIZE);
+      // Le cas ou le nombre de couleurs serait plus grand que 256. On prend en charge.
+      If (PaletteCount < 2) Then //or (PaletteCount>256) then
+        Raise Exception.Create(rsScreenBadColorSize + ' : ' + IntToStr(PaletteCount));
+
+      // On charge la palette
+      For J := 0 To PaletteCount - 1 Do
+      Begin
+        Memory.Read(aRGBColor, SizeOF(TColorRGB24));
+        aColor.Create(aRGBColor);
+        FGlobalPalette.AddColor(aColor);
+      End;
+    End;
+  End;
+
+  { Chargement palette locale }
+  Procedure LoadLocalPalette;
+  Var
+    J: Byte;
+  Begin
+    // Aucune palette locale n'a été assignée. On en créer une nouvelle. Sinon on efface simplement son contenu.
+    If LocalPalette = nil Then LocalPalette := TColor32List.Create
+    Else
+      LocalPalette.Clear;
+
+    // On verifie que le nombre de couleur dans la palette est correcte
+    ColorCount := (2 Shl (ImageDescriptor.PackedFields And GIF_COLORTABLESIZE));
+    // Le cas ou le nombre de couleurs serait plus grand que 256. On prend en charge qudn même et on charge la palette.
+    If (ColorCount < 2) Then //or (ColorCount>256) then
+      Raise Exception.Create(rsImageBadColorSize + ' : ' + IntToStr(ColorCount));
+
+    // On charge la palette
+    For J := 0 To ColorCount - 1 Do
+    Begin
+      Memory.Read(aRGBColor, SizeOF(TColorRGB24));
+      aColor.Create(aRGBColor);
+      LocalPalette.AddColor(aColor);
+    End;
+  End;
+
+  { Lecture des extensions }
+  Procedure ReadExtension;
+  Var
+    ExtensionID, BlockType: Byte;
+    BufStr: Array[0..255] Of Char;
+    Loops:  Word;
+    CurrentExtension : String;
+  Begin
+    // On lit les extension jusqu'a ce qu'un bloc de description d'une image soit détecter ou que jusqu'a la fin du fichier
+    Repeat
+      //showmessage('Read extension at '+ Memory.Position.ToString);
+      ExtensionID := Memory.ReadByte;
+      CurrentExtension :='';
+      // Si c'est un  nouveau marqueur d'introduction d'extension. On lit le nouvel ID
+      If (ExtensionID = GIF_EXTENSIONINTRODUCER) Then ExtensionID := Memory.ReadByte;
+      If (ExtensionID = 0) Then
+      Begin
+        // On Saute les ID Nul
+        Repeat
+          ExtensionID := Memory.ReadByte;
+        Until (ExtensionID <> 0);
+      End;
+      Case ExtensionID Of
+        GIF_PLAINTEXT:
+        Begin
+          BlockSize   := Memory.ReadByte;
+          Memory.Read(PlainTextChunk, SizeOf(TGIFPlainTextExtensionRec));
+          Repeat
+            // On lit la taille du bloc. Si Zero alors fin des données de l'extension
+            BlockSize := Memory.ReadByte;
+            // On lit la chaine de caractères
+            If (BlockSize > 0) Then
+            Begin
+              fillchar({%H-}BufStr, 256, 0);
+              Memory.Read(BufStr, BlockSize);
+              BufStr[BlockSize] := #0;
+              // On place le texte dans les commentaires
+              CurrentLayer.Comment.Add(String(BufStr));
+            End;
+          Until (BlockSize = 0);
+          // On ajoute une ligne vide de séparation
+          CurrentLayer.Comment.Add('');
+        End;
+        GIF_COMMENTEXTENSION:
+        Begin
+          Repeat
+            // On lit la taille du commentaire. Si Zero alors fin des données de l'extension
+            BlockSize := Memory.ReadByte;
+            // On lit la chaine de caractères
+            If (BlockSize > 0) Then
+            Begin
+              Memory.Read(BufStr, BlockSize);
+              BufStr[BlockSize] := #0;
+              // On place le texte dans les commentaires
+              CurrentLayer.Comment.Add(String(BufStr));
+            End;
+          Until (BlockSize <= 0);
+          // On ajoute une ligne vide de séparation
+          CurrentLayer.Comment.Add('');
+        End;
+        GIF_APPLICATIONEXTENSION:
+        Begin
+
+          BlockSize := Memory.ReadByte;
+          // Certains vieux filtres d'exportation Adobe, ou d'autres logiciels utilisent par erreur une valeur de 10, ou plus petite ou trop grande
+          If (BlockSize <> 11) Then
+          Begin
+            FillChar(ApplicationExtensionChunk, SizeOf(TGIFApplicationExtensionRec), 0);
+          End;
+          //else if (BlockSize<11) then
+          //   Raise Exception.Create('Bad extension size' + ' : ' + inttostr(BlockSize) +' octets. ( Taille valide = 11 octets )');
+          Memory.Read(ApplicationExtensionChunk, SizeOf(TGIFApplicationExtensionRec));
+          CurrentExtension := ApplicationExtensionChunk.AppAuthenticationCode;
+          Repeat
+            // On lit la taille du  bloc. Zero si il n'y a pas de données supplémentaires
+            BlockSize := Memory.ReadByte;
+            If (BlockSize > 0) Then
+            Begin
+              if UpperCase(CurrentExtension) = 'NETSCAPE' then
+              begin
+                BlockType := Memory.ReadByte;
+                Dec(BlockSize);
+                Case (BlockType And $07) Of
+                  GIF_LOOPEXTENSION:
+                  Begin
+                    // Lecture du nombre de boucle, Si Zero alors boucle infinie
+                    Loops := Memory.ReadWord;
+                    If Loops > 0 Then Inc(NSLoopExtensionChunk.Loops);
+                    Dec(BlockSize, SizeOf(Loops));
+                  End;
+                  GIF_BUFFEREXTENSION:
+                  Begin
+                    // Lecture de la taille du tampon. Utilisé pour ??????
+                    NSLoopExtensionChunk.BufferSize := Memory.ReadDWord;
+                    Dec(BlockSize, SizeOF(NSLoopExtensionChunk.BufferSize));
+                  End;
+                  else // Extension NETSCAPE inconnue
+                    begin
+                      Memory.SeekForward(BlockSize);
+                      //BlockSize := 0;
+                    end;
+                End;
+              end
+              else
+              // On saute et on ignore les donnée non lues
+              If (BlockSize > 0) Then
+              Begin
+                Memory.SeekForward(BlockSize);
+                //BlockSize := 0;
+              End;
+            End;
+          Until (BlockSize = 0);
+        End;
+        GIF_GRAPHICCONTROLEXTENSION:
+        Begin
+          // On lit la taille de l'extension. Normalement 4 Octets. Cette valeur peut-être erronée. On en tient pas compte ici et on lit les données.
+          BlockSize := Memory.ReadByte;
+          //if BlockSize = 4 then
+          //begin
+          Memory.Read(GraphicControlExtensionChunk, SizeOf(TGIFGraphicControlExtensionRec));
+          // On renseigne notre tampon d'informations pour les prochaines images décodées
+          DMode     := ((GraphicControlExtensionChunk.PackedFields And GIF_DISPOSAL_ALL) Shr 2);
+          With CurrentFrameInfos Do
+          Begin
+            // Ces valeurs peuvent être utilisées pour plusieurs image. Elles restent valides jusqu'a la lecture du prochain "GCE" trouvé.
+            Disposal      := TGIFDisposalFlag(DMode);
+            IsTransparent := (GraphicControlExtensionChunk.PackedFields And GIF_TRANSPARENT_FLAG) <> 0;
+            UserInput     := (GraphicControlExtensionChunk.PackedFields And GIF_USER_INPUT_FLAG) <> 0;
+            TransparentColorIndex := GraphicControlExtensionChunk.TransparentColorIndex;
+            BackgroundColorIndex := FLogicalScreenChunk.BackgroundColorIndex;
+            DelayTime     := GraphicControlExtensionChunk.DelayTime;
+          End;
+          // Lecture de l'octet de fin de l'extension
+          Terminator := Memory.ReadByte;
+        End;
+      End;
+    Until (ExtensionID = GIF_IMAGEDESCRIPTOR) Or Memory.EOS;
+
+    // Si l'ID pour la description de l'image est détecter on revient en arrière pour la prise en charge par le traitement des données
+    If (ExtensionID = GIF_IMAGEDESCRIPTOR) Then Memory.Seek(-1, soCurrent);
+  End;
+
+  { Chargement d'une image }
+  Procedure LoadImage;
+  Var
+    DecoderStatus{%H-}: TLZWDecoderStatus;
+    BufferSize, TargetBufferSize, BytesRead: Int64;
+    InitCodeSize: Byte;
+    OldPosition: Int64;
+    Buffer, BufferPtr: PByte;
+    TargetBuffer, TargetBufferPtr: PByte;
+    LinePtr: PColor32;
+    Pass, Increment: Byte;
+    x:      Integer;
+    TargetColor: TColor32;
+    ColIdx: Byte;
+    CurrentLine: Integer;
+    OutBmp: TFastBitmap;
+
+    // Decodeur GIF LZW. Basé sour le code source de la bibliothèque GraphicEX pour Delphi
+    Function DecodeLZW(Var Source, Dest : Pointer; PackedSize, UnpackedSize : Integer): TLZWDecoderStatus;
+    Const
+      { Constantes pour la décompression LZW }
+      _LZWGIFCodeBits  = 12;    // Nombre maximal de bits par code d'un jeton (12 bits = 4095)
+      _LZWGIFCodeMax   = 4096; // Nombre maximum de jeton
+      _LZWGIFStackSize = (2 Shl _LZWGIFCodeBits);   // Taille de la pile de décompression
+      _LZWGIFTableSize = (1 Shl _LZWGIFCodeBits);   // Taille de la table de décompression
+
+    Var
+      J:         Integer;
+      Data,             // Données actuelle
+      Bits,             // Compteur de bit
+      Code:      Cardinal;   // Valeur courrante du Code
+      SourcePtr: PByte;
+      InCode:    Cardinal; // Tampon pour passé le Code
+
+      CodeSize:  Cardinal;
+      CodeMask:  Cardinal;
+      FreeCode:  Cardinal;
+      OldCode:   Cardinal;
+      Prefix:    Array[0.._LZWGIFTableSize] Of Cardinal; // LZW prefix
+      Suffix,                                         // LZW suffix
+      Stack:     Array [0.._LZWGIFStackSize] Of Byte;
+      StackPointer: PByte;
+      MaxStackPointer: PBYte;
+      Target:    PByte;
+      FirstChar: Byte;  // Tampon de décodage d'un octet
+      ClearCode, EOICode: Word;
+      MaxCode:   Boolean;
+
+    Begin
+      Result        := dsOk;
+      DecoderStatus := dsOk;
+      If (PackedSize <= 0) Or (UnpackedSize <= 0) Then
+      Begin
+        // Taille des tampons invalides
+        If (PackedSize <= 0) And (UnpackedSize <= 0) Then Result := dsInvalidBufferSize
+        Else If PackedSize <= 0 Then Result   := dsInvalidInputBufferSize
+        Else If UnpackedSize <= 0 Then Result := dsInvalidOutputBufferSize;
+        Exit;
+      End;
+
+      // Initialisation  des paramètres pour la décompression
+      CodeSize  := InitCodeSize + 1;
+      ClearCode := 1 Shl InitCodeSize;
+      EOICode   := ClearCode + 1;
+      FreeCode  := ClearCode + 2;
+      OldCode   := _LZWGIFCodeMax - 1;
+      CodeMask  := (1 Shl CodeSize) - 1;
+      MaxCode   := False;
+      Code      := 0;
+      Target    := PByte(Dest);
+      SourcePtr := PByte(Source);
+
+      // Initialisation des tables de Code
+      For J := 0 To _LZWGIFTableSize Do
+      Begin
+        Prefix[J] := _LZWGIFCodeMax;
+        Suffix[J] := J;
+      End;
+
+      // Initalisation de la pile
+      StackPointer    := @Stack;
+      MaxStackPointer := @Stack[_LZWGIFStackSize];
+      FirstChar       := 0;
+
+      Data := 0;
+      Bits := 0;
+      While (UnpackedSize > 0) And (PackedSize > 0) Do
+      Begin
+        // On lit le "Code" dans le tampon d'entrée
+        Inc(Data, SourcePtr^ Shl Bits);
+        Inc(Bits, 8);
+        While (Bits > CodeSize) And (UnpackedSize > 0) Do
+        Begin
+          // Code actuel
+          Code := Data And CodeMask;
+          // Préparation pour la donnée suivante
+          Data := Data Shr CodeSize;
+          Dec(Bits, CodeSize);
+
+          // Décompression finie ?
+          If Code = EOICode Then
+          Begin
+            // Si nous arrivons ici, il y a probablement quelque chose de suspect avec l'image GIF
+            // Car normalement on stoppe dès que le tampon de sortie est plein.
+            // Cela signifie que nous ne lirons jamais l'EOICode de fermeture dans les images normales.
+            // Comme l'état du buffer est déjà vérifié après la boucle principale, nous ne le ferons pas ici.
+            Break;
+          End;
+
+          // On vérifie s'il s'agit d'un code valide déjà enregistré
+          If Code > FreeCode Then
+          Begin
+            // Code ne peux à être supérieur à FreeCode. Nous avons donc une image cassée.
+            // On notifie l'erreur à l'utilisateur. Et on considère qu'il n'ya pas d'erreur.
+            DecoderStatus := dsInvalidInput;
+            AddError(Format(rsLZWInvalidInput,[CurrentFrameIndex]));
+            //NotifyUser('Le décodeur a rencontré une entrée invalide (données corrompues)');
+            Code :=  ClearCode;
+            //Break; //Ici, on continue le chargement du reste de l'image au lieu de le stopper
+          End;
+
+          // RAZ
+          If Code = ClearCode Then
+          Begin
+            // réinitialisation de toutes les variables
+            CodeSize := InitCodeSize + 1;
+            CodeMask := (1 Shl CodeSize) - 1; //CodeMasks[CodeSize];
+            FreeCode := ClearCode + 2;
+            OldCode  := _LZWGIFCodeMax;
+            MaxCode  := False;
+          End
+          Else If OldCode = _LZWGIFCodeMax Then
+          Begin
+            // Gestion du premier Code LZW : On le définit dans le tampon de sortie et on le conserve
+            FirstChar := Suffix[Code];
+            Target^   := FirstChar;
+            Inc(Target);
+            Dec(UnpackedSize);
+            OldCode   := Code;
+          End
+          Else
+          Begin
+            //On conserve le Code LZW actuel
+            InCode := Code;
+
+            // On place le nouveau code LZW sur la pile sauf quand nous avons déjà utilisé tous les codes disponibles
+            If (Code = FreeCode) And Not MaxCode Then
+            Begin
+              StackPointer^ := FirstChar;
+              Inc(StackPointer);
+              Code := OldCode;
+            End;
+
+            // boucle pour placer les octets décodés sur la pile
+            While Code > ClearCode Do
+            Begin
+              StackPointer^ := Suffix[Code];
+              If StackPointer >= MaxStackPointer Then
+              Begin
+                // Ne doit jamais arriver, c'est juste une précaution au cas ou.
+                Result := dsBufferOverflow;
+                break;
+              End;
+              Inc(StackPointer);
+              Code := Prefix[Code];
+            End;
+            If Result <> dsOK Then break; // Si il ya eu des erreurs on ne va pas plus loin
+
+            // Place le nouveau Code dans la table
+            FirstChar     := Suffix[Code];
+            StackPointer^ := FirstChar;
+            Inc(StackPointer);
+
+            //Transfert des données décodées vers notre tampon de sortie
+            Repeat
+              If UnpackedSize <= 0 Then
+              Begin
+                // Le tampon de sortie est trop petit. On ne va pas plus loin
+                // On notifie l'erreur à l'utilisateur. Et on considère qu'il n'ya pas d'erreur.
+                // Afin de pouvoir afficher le GIF et continuer le chargement des images suivantes
+                Result := dsOutputBufferTooSmall;
+                AddError(Format(rsLZWOutputBufferTooSmall,[CurrentFrameIndex]));
+                break;
+              End;
+              Dec(StackPointer);
+              Target^ := StackPointer^;
+              Inc(Target);
+              Dec(UnpackedSize);
+            Until StackPointer = @Stack;
+            If Result <> dsOK Then break;
+
+            If Not MaxCode Then
+            Begin
+              If FreeCode <= _LZWGIFCodeMax Then
+              Begin
+                Prefix[FreeCode] := OldCode;
+                Suffix[FreeCode] := FirstChar;
+              End
+              Else If FreeCode > _LZWGIFCodeMax Then
+              Begin
+                // On a intercepter une donnée corrompue. On continue quand la même décompression sans en tenir compte.
+                // On notifie juste l'erreur à l'utilisateur
+                DecoderStatus := dsInvalidInput;
+                AddError(Format(rsLZWInvalidInput,[CurrentFrameIndex]));
+                FreeCode :=  _LZWGIFCodeMax;
+                Prefix[FreeCode] := OldCode;
+                Suffix[FreeCode] := FirstChar;
+                //MaxCode       := True;
+              End;
+
+              // On augmente la taille du Code si nécessaire
+              If (FreeCode = CodeMask) And Not (MaxCode) Then
+              Begin
+                If (CodeSize < _LZWGIFCodeBits) Then
+                Begin
+                  Inc(CodeSize);
+                  CodeMask := (1 Shl CodeSize) - 1;//CodeMasks[CodeSize];
+                End
+                Else //On a atteind la limite maximum
+                  MaxCode  := True;
+              End;
+
+              If FreeCode < _LZWGIFTableSize Then Inc(FreeCode);
+            End;
+            OldCode := InCode;
+          End;
+        End;
+        Inc(SourcePtr);
+        Dec(PackedSize);
+        If (Result <> dsOK) Or (Code = EOICode) Then Break;
+      End;
+
+      If Result = dsOK Then
+      Begin
+        // On vérifie seulement si il n'ya pas eu d'erreur. Si ce n'est pas le cas, nous savons déjà que quelque chose ne va pas.
+        // Notez qu'il est normal que PackedSize soit un peu> 0 parce que nous pouvons
+        // pas lire l'EOICode mais arrêter dès que notre tampon de sortie est plein et
+        // qui devrait normalement être le code juste avant l'EOICode.
+        If PackedSize < 0 Then
+        Begin
+          Result := dsInternalError;
+          // C'est une erreur sérieuse : nous avons eu un dépassement de tampon d'entrée que nous aurions dû intercepter. Nous devons arrêter maintenant.
+          Raise Exception.Create(rsLZWInternalErrorInputBufferOverflow);
+          Exit;
+        End;
+        If UnpackedSize <> 0 Then
+        Begin
+          //if UnpackedSize > 0 then
+          //begin
+          //  //  Image corrompue
+          //  DecoderStatus := dsNotEnoughInput;
+          //  AddError('Image #'+CurrentFrameIndex)+' : Le décodeur n''a pas pu décoder toutes les données car le tampon d''entrée est trop petit');
+          //  //NotifyUser('Le décodeur  n''a pas pu décoder toutes les données car le tampon d''entrée est trop petit');
+          //End
+          //else
+          If UnpackedSize < 0 Then
+          Begin
+            Result := dsInternalError;
+            // C'est une erreur sérieuse : nous avons eu un dépassement de tampon de sortie que nous aurions dû intercepter. Nous devons arrêter maintenant.
+            Raise Exception.Create(rsLZWInternalErrorOutputBufferOverFlow);
+          End;
+        End;
+      End;
+    End;
+
+  Begin
+    BufferSize       := 0;
+    TargetBufferSize := 0;
+
+    // On lit la description de l'image
+    Memory.Read(ImageDescriptor, SizeOf(TGIFImageDescriptorRec));
+
+    // On vérifie que les dimensions sont correctes.
+    // Si on trouve des dimensions à zero, il se peut qu'il faudra traiter
+    // une extension PlainText et dessiner ce texte en fonction des paramètres
+    If (ImageDescriptor.Height = 0) Or (ImageDescriptor.Width = 0) Then
+    Begin
+      // On assigne les dimensions par défaut du GIF
+      ImageDescriptor.Width  := FLogicalScreenChunk.ScreenWidth;
+      ImageDescriptor.Height := FLogicalScreenChunk.ScreenHeight;
+      // On notifie à l'utilisateur que les dimensions de l'image sont erronée. Mais on tente le chargement quand même
+      // ShowMessage
+    End;
+
+    // Dans le cas ou les dimensions de l'image sont incorrectes dans "l'image descriptor". Ou que la taille des données compressées soit erronée.
+    If (ImageDescriptor.Width > FLogicalScreenChunk.ScreenWidth) Or (ImageDescriptor.Height > FLogicalScreenChunk.ScreenHeight) Then
+    Begin
+      // On assigne les dimensions par défaut du GIF
+      If (ImageDescriptor.Width > FLogicalScreenChunk.ScreenWidth) Then ImageDescriptor.Width := FLogicalScreenChunk.ScreenWidth;
+      If (ImageDescriptor.Height > FLogicalScreenChunk.ScreenHeight) Then ImageDescriptor.Height := FLogicalScreenChunk.ScreenHeight;
+      // On notifie à l'utilisateur que les dimensions de l'image sont erronée. Mais on tente le chargement quand même
+      // ShowMessage
+    End;
+
+    // On renseigne notre tampon d'informations
+    With CurrentFrameInfos Do
+    Begin
+      Left       := ImageDescriptor.Left;
+      Top        := ImageDescriptor.Top;
+      Width      := ImageDescriptor.Width;
+      Height     := ImageDescriptor.Height;
+      Interlaced := (ImageDescriptor.PackedFields And GIF_INTERLACED) = GIF_INTERLACED;
+      HasLocalPalette := (ImageDescriptor.PackedFields And GIF_LOCALCOLORTABLE) = GIF_LOCALCOLORTABLE;
+    End;
+
+    // L'image possède-t-elle sa propre palette de couleur ? Si oui on la charge.
+    If CurrentFrameInfos.HasLocalPalette Then LoadLocalPalette;
+
+    // Decompression de l'image
+
+    // On ajoute une nouvelle image si besoin
+    If (FCurrentLayerIndex > 0) And (FCurrentLayerIndex > FFrames.Count - 1) Then CurrentLayer := FFrames.AddNewImage;
+    // On assigne la nouvelle image au Bitmap de travail
+    OutBmp := FFrames.Items[CurrentFrameIndex].Bitmap;
+
+    // On met à jour les informations
+    With FFrames.Items[FCurrentLayerIndex] Do
+    Begin
+      Drawmode := CurrentFrameInfos.Disposal;
+     // Showmessage('#'+inttostr(FCurrentLayerIndex) + 'DrawMode : '+ GifGCEDisposalModeStr[Drawmode]);
+      Left     := CurrentFrameInfos.Left;
+      Top      := CurrentFrameInfos.Top;
+      IsTransparent := CurrentFrameInfos.IsTransparent;
+      If CurrentFrameInfos.DelayTime = 0 Then Delay := GIF_DefaultDelay
+      Else
+        Delay := CurrentFrameInfos.DelayTime * GIF_DelayFactor;
+    End;
+
+    // On lit le code d'initalisation de la compression LZW
+    InitCodeSize := Memory.ReadByte;
+    If InitCodeSize < 2 Then InitCodeSize := 2;
+    If InitCodeSize > 8 Then InitCodeSize := 8;
+
+    // On sauve la position actuelle dans le flux
+    OldPosition := Memory.position;
+
+    BufferSize := 0;
+
+    // 1) On comptabilise la taille totale des données compresser. Afin de les décompresser en une seule fois.
+    // On lit la taille du premier bloc
+    BlockSize := Memory.ReadByte;
+    While (BlockSize > 0) And Not (Memory.EOS) Do
+    Begin
+      Inc(BufferSize, BlockSize);
+      // On saute les données
+      Memory.SeekForward(BlockSize);
+      If Not (Memory.EOS) Then BlockSize := Memory.ReadByte
+      Else
+        blocksize := 0;
+    End;
+
+    // 2) On initalise notre bitmap avec les bonnes dimensions
+    OutBmp.SetSize(CurrentFrameInfos.Width, CurrentFrameInfos.Height);
+
+    BufferPtr := nil;
+    Buffer    := nil;
+    // 3) On alloue notre tampon pour les données compressées
+    If (BufferSize > 0) Then Reallocmem(Buffer, BufferSize);
+
+    // 4) On charge toutes les données dans notre tampon
+    // On se replace au début des données
+    Memory.Seek(OldPosition, soBeginning);
+    // On travail toujours sur une copie du "pointer"
+    BufferPtr := Buffer;
+    // On lit la taille du premier bloque
+    BlockSize := Memory.ReadByte;
+    While (BlockSize > 0) And Not (Memory.EOS) Do
+    Begin
+      // On charge les données dans le tampon. On previent des erreurs en cas de dépassements
+      BytesRead := Memory.Read(BufferPtr^, BlockSize);
+      Inc(BufferPtr, BytesRead);
+      If Not (Memory.EOS) Then BlockSize := Memory.ReadByte
+      Else
+        blocksize := 0;
+    End;
+    // On se replace au debut du tampon
+    BufferPtr := Buffer;
+    // 5) On decompresse les données
+    //  On initialise notre buffer ou seront décompressées les données
+    TargetBufferSize := Int64(CurrentFrameInfos.Width) * Int64(CurrentFrameInfos.Height);
+    TargetBufferPtr  := nil;
+    TargetBuffer     := nil;
+    // Si la taille est plus grande que zero, on alloue l'espace nécessaire à notre tampon
+    If (TargetBufferSize > 0) Then Reallocmem(TargetBuffer, TargetBufferSize);
+
+    // Décodage des données compressées
+    Ret := DecodeLZW(Buffer, TargetBuffer, BufferSize, TargetBufferSize);
+
+    // 6) On transfert les données de l'image vers notre bitmap. Si il n'y a pas eu d'erreurs
+    If (Ret = dsOk) Then
+    Begin
+      TargetBufferPtr := TargetBuffer;
+      OutBmp.Clear(clrTransparent);
+
+      // Image non entrelacée
+      If Not (CurrentFrameInfos.Interlaced) Then
+      Begin
+        CurrentLine := 0;
+        While (CurrentLine <= CurrentFrameInfos.Height - 1) Do
+        Begin
+          LinePtr  := OutBmp.GetScanLine(CurrentLine);// FFrames.Items[CurrentFrameIndex].Bitmap.GetScanLine(CurrentLine);
+          For x    := 0 To (CurrentFrameInfos.Width - 1) Do
+          Begin
+            // Lecture de l'index de la couleur dans la palette
+            ColIdx := TargetBufferPtr^;
+            // On utilise la palette de couleur locale
+            If CurrentFrameInfos.HasLocalPalette Then
+            Begin
+              If LocalPalette <> nil Then // La palette est-elle chargée ?
+              Begin
+                //if (ColIdx> ColorCount-1) then ColIdx := ColorCount -1;
+                If (ColIdx < ColorCount) Then TargetColor := LocalPalette.Colors[ColIdx].Value
+                Else
+                  TargetColor := clrTransparent;
+              End
+              Else If FGlobalPalette <> nil Then // Non, alors on utilise la palette globale si elle est présente
+              Begin
+                //if (ColIdx> PaletteCount-1) then ColIdx := PaletteCount -1;
+                If (ColIdx < PaletteCount) Then TargetColor := FGlobalPalette.Colors[ColIdx].Value
+                Else
+                  TargetColor := clrTransparent;
+              End
+              Else
+              Begin
+                AddError(rsEmptyColorMap);
+                Exit;
+              End;
+            End
+            Else // On utilise la palette de couleur globale
+            Begin
+              If FGlobalPalette <> nil Then
+              Begin
+                //if (ColIdx> PaletteCount-1) then ColIdx := PaletteCount -1;
+                If (ColIdx < PaletteCount) Then TargetColor := FGlobalPalette.Colors[ColIdx].Value
+                Else
+                  TargetColor := clrTransparent;
+              End
+              Else If LocalPalette <> nil Then
+              Begin
+                //if (ColIdx> ColorCount-1) then ColIdx := ColorCount -1;
+                If (ColIdx > ColorCount - 1) Then //ColIdx := ColorCount -1;
+                  TargetColor := LocalPalette.Colors[ColIdx].Value
+                Else
+                  TargetColor := clrTransparent;
+              End
+              Else
+              Begin
+                AddError(rsEmptyColorMap);
+                Exit;
+              End;
+            End;
+
+            If CurrentFrameInfos.IsTransparent Then
+            Begin
+              If FHasGlobalPalette Then If ColIdx < FGlobalPalette.Count Then OutBmp.TransparentColor := FGlobalPalette.Colors[ColIdx].Value.ToColor
+                Else If ColIdx < LocalPalette.Count Then OutBmp.TransparentColor := LocalPalette.Colors[ColIdx].Value.ToColor;
+
+              If (Self.FTransparent) Then
+              Begin
+                If (ColIdx = CurrentFrameInfos.TransparentColorIndex) Then
+                begin
+                  TargetColor.Alpha := 0; // clrTransparent;
+                end;
+                If (CurrentFrameInfos.TransparentColorIndex = CurrentFrameInfos.BackgroundColorIndex) Then FbackgroundColor.Alpha := 0; //clrTransparent;
+              End;
+            End;
+            LinePtr^ := TargetColor;
+            // On avance de 1 élément dans nos "pointer"
+            Inc(TargetBufferPtr);
+            Inc(LinePtr);
+          End;
+          Inc(CurrentLine);
+        End;
+      End
+      Else // Image entrelacée
+      Begin
+      CurrentLine := 0;
+      For pass    := 0 To 3 Do
+      Begin
+        Case Pass Of
+          0:
+          Begin
+            CurrentLine := 0;
+            Increment   := 8;
+          End;
+          1:
+          Begin
+            CurrentLine := 4;
+            Increment   := 8;
+          End;
+          2:
+          Begin
+            CurrentLine := 2;
+            Increment   := 4;
+          End;
+          Else
+          Begin
+            CurrentLine := 1;
+            Increment   := 2;
+          End;
+        End;
+        While (CurrentLine < CurrentFrameInfos.Height) Do
+        Begin
+          LinePtr  :=OutBmp.GetScanLine(CurrentLine); // FFrames.Items[CurrentFrameIndex].Bitmap
+          For x    := 0 To (FFrames.Items[CurrentFrameIndex].Bitmap.Width - 1) Do
+          Begin
+            // Lecture de l'index de la couleur dans la palette
+            ColIdx := TargetBufferPtr^;
+            // On utilise la palette de couleur locale
+            If CurrentFrameInfos.HasLocalPalette Then
+            Begin
+              If LocalPalette <> nil Then // La palette est-elle chargée ?
+              Begin
+                If (ColIdx < ColorCount) Then // Dans le cas contraire il s'agit d'un index pour la transparence
+                  TargetColor := LocalPalette.Colors[ColIdx].Value;
+              End
+              Else If FGlobalPalette <> nil Then // Non, alors on utilise la palette globale si elle est présente
+              Begin
+                If (ColIdx < PaletteCount) Then //if (ColIdx< PaletteCount-1) then ColIdx := PaletteCount -1;
+                  TargetColor := FGlobalPalette.Colors[ColIdx].Value;
+              End
+              Else
+              Begin
+                AddError(rsEmptyColorMap);
+                Exit;
+              End;
+            End
+            Else // On utilise la palette de couleur globale
+            Begin
+              If FGlobalPalette <> nil Then
+              Begin
+                If (ColIdx > PaletteCount - 1) Then ColIdx := PaletteCount - 1;
+                TargetColor := FGlobalPalette.Colors[ColIdx].Value;
+              End
+              Else If LocalPalette <> nil Then
+              Begin
+                If (ColIdx > ColorCount - 1) Then ColIdx := ColorCount - 1;
+                TargetColor := LocalPalette.Colors[ColIdx].Value;
+              End
+              Else
+              Begin
+                AddError(rsEmptyColorMap);
+                Exit;
+              End;
+            End;
+
+            If CurrentFrameInfos.IsTransparent Then
+            Begin
+              If FHasGlobalPalette Then If ColIdx < FGlobalPalette.Count Then OutBmp.TransparentColor := FGlobalPalette.Colors[ColIdx].Value.ToColor
+                Else If ColIdx < LocalPalette.Count Then OutBmp.TransparentColor := LocalPalette.Colors[ColIdx].Value.ToColor;
+              If (FTransparent) Then
+              Begin
+                If CurrentFrameInfos.TransparentColorIndex = colIdx Then
+                begin
+                   TargetColor.Alpha := 0; // := clrTransparent;
+                End;
+                If (CurrentFrameInfos.TransparentColorIndex = CurrentFrameInfos.BackgroundColorIndex) Then FBackgroundColor.Alpha := 0;
+              End;
+            End;
+
+            LinePtr^ := TargetColor;
+            Inc(TargetBufferPtr);
+            If (CurrentLine < CurrentFrameInfos.Height - 1) Then Inc(LinePtr);
+          End;
+          Inc(CurrentLine, Increment);
+        End;
+      End;
+    End;
+      if DecoderStatus <> dsOk then
+      begin
+        //outBmp.Clear(ClrTransparent);
+        FFrames.Items[FCurrentLayerIndex].IsCorrupted := True;
+        FFrames.Items[FCurrentLayerIndex].Delay:= 1;
+      End;
+      Inc(FCurrentLayerIndex);   // Index pour la prochaine image
+    End
+    Else
+    Begin
+      Case Ret Of
+        dsInvalidBufferSize: AddError(Format(rsInvalidBufferSize,[CurrentFrameIndex]));
+        dsInvalidInputBufferSize: AddError(Format(rsInvalidInputBufferSize,[CurrentFrameIndex]));
+        dsInvalidOutputBufferSize: AddError(Format(rsInvalidOutputBufferSize,[CurrentFrameIndex]));
+        dsBufferOverflow: AddError(Format(rsBufferOverFlow,[CurrentFrameIndex]));
+        dsOutputBufferTooSmall :
+         (* begin
+            // On supprime l'image. Le tampon de sortie étant trop petit, cela va générer des erreurs lors du transfert des données décompressées vers l'image
+            //FFrames.Delete(CurrentFrameIndex);
+
+          end;*)
+          dec(FCurrentLayerIndex);
+
+      End;
+      if Ret<>dsOutputBufferTooSmall then
+      begin
+        FFrames.Items[FCurrentLayerIndex].IsCorrupted := True;
+        FFrames.Items[FCurrentLayerIndex].Delay:= 1;
+      end;
+    End;
+
+    // On libére la mémoire allouée pour nos tampons
+    If (TargetBufferSize > 0) And (targetBuffer <> nil) Then FreeMem(TargetBuffer);
+    If (BufferSize > 0) And (Buffer <> nil) Then FreeMem(Buffer);
+  End;
+
+Begin
+  PaletteCount := 0;
+  ColorCount   := 0;
+  LocalPalette := nil;
+  FFrames.Clear;
+
+  // Par defaut, on considère que la couleur de fond est totalement transparente
+  FBackgroundColor := clrTransparent;
+  // Si une palette globale existe, alors on charge
+  LoadGlobalPalette;
+  If FHasGlobalPalette Then
+  Begin
+    If FLogicalScreenChunk.BackgroundColorIndex < PaletteCount - 1 Then FBackgroundColor := FGlobalPalette.Colors[FLogicalScreenChunk.BackgroundColorIndex].Value
+    Else
+    Begin
+      FBackgroundColor := clrTransparent; //FGlobalPalette.Colors[FLogicalScreenChunk.BackgroundColorIndex].Value;
+    End;
+  End;
+
+  // Les valeurs suivante seront renseignées lors du chargement d'une image
+  // On réinitialise juste les valeurs par défaut des informations de l'image en cours au cas ou il n'y aurait pas de GCE
+  With CurrentFrameInfos Do
+  Begin
+    Left       := 0;
+    Top        := 0;
+    Width      := FLogicalScreenChunk.ScreenWidth;
+    Height     := FLogicalScreenChunk.ScreenHeight;
+    Interlaced := False;
+    HasLocalPalette := False;
+    IsTransparent := False;
+  End;
+  // On ajoute l'image de départ afin de pouvoir assigner les valeurs des premières extensions (Extensions déclarées avant l'image)
+  CurrentLayer := FFrames.AddNewImage;
+  // On efface l'image avec la couleur de fond
+  //CurrentLayer.Bitmap.Clear(FBackgroundColor);
+  FCurrentLayerIndex := 0;
+  // On lit le 1er octet
+  Done := False;
+  While Not (Done) Do
+  Begin
+    // On verifie l'existence d'extensions avant les données de l'image (Application, Graphic Control, PlainText, Comment)
+    If Not (Memory.EOS) Then BlockID := Memory.ReadByte
+    Else
+      BlockID := GIF_Trailer;
+    If (BlockID = GIF_Trailer) Then
+    Begin
+      Done := True;
+    End;
+    If (BlockID = 0) Then
+    Begin
+      // On Saute les ID Nul
+      While (BlockId = 0) Do BlockId := Memory.ReadByte;
+    End
+    Else If (BlockID = GIF_IMAGEDESCRIPTOR) Then  // C'est une image
+    Begin
+      // On charge l'image
+      LoadImage;
+    End
+    Else If (BlockID = GIF_EXTENSIONINTRODUCER) Then // c'est une extension
+    Begin
+      ReadExtension; // On charge toutes les extensions qui sont à la suite
+    End
+    Else
+    Begin
+      // Extension inconnue on saute jusqu'a trouver un ZERO.
+      // A Verifier avec le flag UseInput dans le "Graphic Control Extension"
+      // Ici on ignore simplement les données
+      While BlockID <> 0 Do
+      Begin
+        BlockID := Memory.ReadByte;
+      End;
+    End;
+  End;
+  // Si il y a des erreurs elles seront notifier à l'utilisateur
+  NotifyError;
+
+  // Il n'y a aucune images on notifie l'erreur
+  If FFrames.Count = 0 Then Raise Exception.Create(rsEmptyImage);
+
+  // On libere la mémoire, prise par nos palettes de couleurs si besoin
+  If (LocalPalette <> nil) Then
+  Begin
+    FreeAndNil(LocalPalette);
+  End;
+  If (FGlobalPalette <> nil) Then
+  Begin
+    FreeAndNil(FGlobalPalette);
+  End;
+End;
+
+{%endregion%}
+
+{%region=====[ TGIFRenderCacheListItem ]========================================}
+
+Constructor TGIFRenderCacheListItem.Create;
+Begin
+  Inherited Create;
+  FBitmap := Graphics.TBitmap.Create;
+  FDelay  := 0;
+End;
+
+Destructor TGIFRenderCacheListItem.Destroy;
+Begin
+  FreeAndNil(FBitmap);
+  Inherited Destroy;
+End;
+
+{%endregion%}
+
+{%region=====[ TGIFRenderCacheList ]============================================}
+
+Function TGIFRenderCacheList.GetItems(Index : Integer): TGIFRenderCacheListItem;
+Begin
+  Result := TGIFRenderCacheListItem(Inherited Items[Index]);
+End;
+
+Procedure TGIFRenderCacheList.SetItems(Index : Integer; AGIFRenderCache : TGIFRenderCacheListItem);
+Begin
+  Put(Index, AGIFRenderCache);
+End;
+
+Procedure TGIFRenderCacheList.Clear;
+Var
+  anItem: TGIFRenderCacheListItem;
+  i:      Integer;
+Begin
+  If Count > 0 Then
+  Begin
+    For i := Count - 1 Downto 0 do
+    Begin
+      AnItem := Items[i];
+      If anItem <> nil Then anItem.Free;
+    End;
+  End;
+  Inherited Clear;
+End;
+
+Function TGIFRenderCacheList.AddNewCache: TGIFRenderCacheListItem;
+Var
+  anItem: TGIFRenderCacheListItem;
+Begin
+  anitem := TGIFRenderCacheListItem.Create;
+  Add(anItem);
+  Result := Items[Self.Count - 1];
+End;
+
+Function TGIFRenderCacheList.Add(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
+Begin
+  Result := Inherited Add(AGIFRenderCache);
+End;
+
+Function TGIFRenderCacheList.Extract(Item : TGIFRenderCacheListItem): TGIFRenderCacheListItem;
+Begin
+  Result := TGIFRenderCacheListItem(Inherited Extract(Item));
+End;
+
+Function TGIFRenderCacheList.Remove(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
+Begin
+  Result := Inherited Remove(AGIFRenderCache);
+End;
+
+Function TGIFRenderCacheList.IndexOf(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
+Begin
+  Result := Inherited IndexOf(AGIFRenderCache);
+End;
+
+Function TGIFRenderCacheList.First: TGIFRenderCacheListItem;
+Begin
+  Result := TGIFRenderCacheListItem(Inherited First);
+End;
+
+Function TGIFRenderCacheList.Last: TGIFRenderCacheListItem;
+Begin
+  Result := TGIFRenderCacheListItem(Inherited Last);
+End;
+
+Procedure TGIFRenderCacheList.Insert(Index : Integer; AGIFRenderCache : TGIFRenderCacheListItem);
+Begin
+  Inherited Insert(Index, AGIFRenderCache);
+End;
+
+Function TGIFRenderCacheList.IsIndexOk(anIndex: Integer): Boolean;
+Begin
+  Result := True;
+  If (anIndex < 0) or (anIndex > Count-1) then result := False;
+End;
+
+Procedure TGIFRenderCacheList.Pack;
+Var
+  i: Integer;
+Begin
+  if Count>1 then
+  begin
+    I := 0;
+    While I<Count do
+    begin
+      if Items[I].IsCorrupted then
+      begin
+        Remove(Items[I]);
+        break;
+      End;
+      inc(I);
+    End;
+    if I<Count then Pack;
+  End;
+End;
+
+{%endregion%}
+
+{%region=====[ TGIFViewer ]=====================================================}
+
+Constructor TGIFViewer.Create(AOwner: TComponent);
+Begin
+  Inherited Create(AOwner);
+  ControlStyle := [csCaptureMouse, csClickEvents, csDoubleClicks];
+  AutoSize     := False;
+  FCenter      := False;
+  FStretch     := False;
+  FTransparent := True;
+  With GetControlClassDefaultSize Do SetInitialBounds(0, 0, CX, CY);
+  FRestoreBitmap := nil;
+  FRenderCache   := TGIFRenderCacheList.Create(False);
+  FGIFLoader     := TGIFImageLoader.Create;
+  FGIFLoader.OnLoadError := @DoInternalOnLoadError;
+  FVirtualView   := TFastBitmap.Create;
+  FCurrentView   := nil;
+  FCurrentView   := Graphics.TBitmap.Create;
+  FRestoreBitmap := nil;
+  FAutoPlay      := False;
+  FBorderShow    := False;
+  FBorderColor   := clBlack;
+  FBorderWidth   := 1;
+  FBevelInner    := bvNone;
+  FBevelOuter    := bvNone;
+  FBevelWidth    := 1;
+  FColor         := clNone;
+  FDisplayInvalidFrames := False;
+  FAutoRemoveInvalidFrame := True;
+  FLastDrawMode  := dmNone;
+
+  FAnimateTimer := TTimer.Create(nil);
+  With FAnimateTimer Do
+  Begin
+    Enabled  := False;
+    Interval := 1000;
+    OnTimer  := @DoTimerAnimate;
+  End;
+  FAnimateSpeed := 1;
+  FCurrentFrameIndex := 0;
+  FGIFWidth  := 90;
+  FGIFHeight := 90;
+  FAutoStretchMode := smManual;
+End;
+
+Destructor TGIFViewer.Destroy;
+Begin
+  FAnimateTimer.Enabled := False;
+
+  FreeAndNil(FAnimateTimer);
+  If FCurrentView <> nil Then FreeAndNil(FCurrentView);
+  If FRestoreBitmap <> nil Then FreeAndNil(FRestoreBitmap);
+  FreeAndNil(FVirtualView);
+  FRenderCache.Clear;
+  FreeAndNil(FRenderCache);
+  FreeAndNil(FGIFLoader);
+
+  Inherited Destroy;
+End;
+
+Procedure TGIFViewer.SetCenter(Const Value: Boolean);
+Begin
+  If Value = FCenter Then exit;
+  FCenter := Value;
+  Invalidate;
+End;
+
+Function TGIFViewer.GetCanvas: TCanvas;
+Begin
+  Result := Inherited Canvas;// FCurrentView.Canvas
+End;
+
+Function TGIFViewer.GetFrameCount: Integer;
+Begin
+  If FCache Then
+    Result := FRenderCache.Count
+  Else Begin
+    Result := FGifLoader.FrameCount;
+  End;
+End;
+
+Function TGIFViewer.GetGIFVersion: String;
+Begin
+  Result := FGIFLoader.Version;
+End;
+
+Function TGIFViewer.GetRawFrameItem(Index : Integer): TGIFImageListItem;
+Begin
+  Result := nil;
+  If (Index >= 0) And (Index < FGIFLoader.FrameCount) Then Result := FGIFLoader.Frames[Index];
+end;
+
+Procedure TGIFViewer.SetAutoStretchMode(AValue: TGIFAutoStretchMode);
+Begin
+  If FAutoStretchMode = AValue Then Exit;
+  FAutoStretchMode := AValue;
+  Invalidate;
+End;
+
+Procedure TGIFViewer.SetStretch(Const Value: Boolean);
+Begin
+  If Value = FStretch Then exit;
+  FStretch := Value;
+  Invalidate;
+End;
+
+Procedure TGIFViewer.SetPause(Const Value: Boolean);
+Begin
+  If Value = FPause Then exit;
+  FPause := Value;
+  If FPause Then FAnimateTimer.Enabled := False;
+  If Assigned(FOnPause) Then FOnPause(Self);
+End;
+
+Procedure TGIFViewer.SetFileName(Const Value: String);
+Begin
+  If Value = FFileName Then exit;
+  FFileName := Value;
+  LoadFromFile(FFileName);
+End;
+
+Function TGIFViewer.GetFrame(Const Index: Integer): Graphics.TBitmap;
+Begin
+  Result := nil;
+  If (Index >= 0) And (Index < FrameCount) Then Result := FRenderCache.Items[Index].Bitmap;
+End;
+
+Procedure TGIFViewer.SetTransparent(Const Value: Boolean);
+Begin
+  If FTransparent = Value Then exit;
+  FTransparent := Value;
+  FGIFLoader.Transparent := Value;
+  If FFileName <> '' Then LoadFromFile(FFileName);
+End;
+
+Procedure TGIFViewer.SetBevelWidth(Const Value: TBevelWidth);
+Begin
+  If FBevelWidth <> Value Then
+  Begin
+    FBevelWidth := Value;
+    Invalidate;
+  End;
+End;
+
+Procedure TGIFViewer.ResetCurrentView;
+Var
+  I: Integer;
+  Corrupted : Boolean;
+begin
+  if FRenderCache.Count>1 then
+  begin
+    if not(FDisplayInvalidFrames) then
+    begin
+      Corrupted := false;
+      i := 0;
+      Repeat
+        Corrupted := FRenderCache.Items[i].IsCorrupted;
+        inc(i);
+      until (i>FRenderCache.Count-1) or (Corrupted = false);
+      if (i>FRenderCache.Count-1) and (Corrupted = true) then
+      begin
+        Raise Exception.Create(rsAllFrameCorrupted);
+        exit;
+      end
+      else
+      begin
+        Dec(i);
+        FCurrentframeIndex     := i;
+        FAnimateTimer.Interval := FRenderCache.Items[i].Delay;
+        FCurrentView.Assign(FRenderCache.Items[i].Bitmap);
+      end;
+    end
+    else
+    begin
+      FAnimateTimer.Interval := FRenderCache.Items[0].Delay;
+      FCurrentView.Assign(FRenderCache.Items[0].Bitmap);
+    end;
+  end
+  else
+  begin
+    FCurrentView.Assign(FRenderCache.Items[0].Bitmap);
+  end;
+  FLastDrawMode := dmNone;
+End;
+
+Procedure TGIFViewer.SetBevelInner(Const Value: TPanelBevel);
+Begin
+  If BevelInner <> Value Then
+  Begin
+    FBevelInner := Value;
+    Invalidate;
+  End;
+End;
+
+Procedure TGIFViewer.SetBevelOuter(Const Value: TPanelBevel);
+Begin
+  If BevelOuter <> Value Then
+  Begin
+    FBevelOuter := Value;
+    Invalidate;
+  End;
+End;
+
+Procedure TGIFViewer.DoInternalOnLoadError(Sender: TObject; Const ErrorCount: Integer; Const ErrorList: TStringList);
+Begin
+  If Assigned(FOnLoadError) Then FOnloadError(Self, ErrorCount, ErrorList);
+End;
+
+Procedure TGIFViewer.DoTimerAnimate(Sender: TObject);
+Begin
+  Inc(FCurrentFrameIndex);
+  If FCurrentFrameIndex > (FGIFLoader.FrameCount - 1) Then FCurrentFrameIndex := 0;
+
+  If (not FCache) and (FCurrentFrameIndex >= FRenderCache.Count) Then
+  Begin
+    RenderFrame(FCurrentFrameIndex);
+  End;
+
+  If Assigned(FOnFrameChange) Then FOnFrameChange(Self);
+  if not(FDisplayInvalidFrames) then
+  begin
+    if not(FRenderCache.Items[FCurrentFrameIndex].IsCorrupted) then
+    begin
+      FAnimateTimer.Interval := FRenderCache.Items[FCurrentFrameIndex].Delay;
+      FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
+    End
+    else FAnimateTimer.Interval := FRenderCache.Items[FCurrentFrameIndex].Delay;
+  end
+  else
+  begin
+    FAnimateTimer.Interval := FRenderCache.Items[FCurrentFrameIndex].Delay;
+    FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
+  end;
+  Invalidate;
+End;
+
+Procedure TGIFViewer.RenderFrame(Index: Integer);
+Var
+  Src:         TFastBitmap;
+  pTop, pLeft: Integer;
+  iDrawMode:   TFastBitmapDrawMode;
+  TmpBmp : Graphics.TBitmap;
+Begin
+
+  Src   := FGIFLoader.Frames.Items[Index].Bitmap;
+  pLeft := FGIFLoader.Frames.Items[Index].Left;
+  pTop  := FGIFLoader.Frames.Items[Index].Top;
+
+  FRenderCache.AddNewCache;
+  FRenderCache.Items[Index].Delay  := FGIFLoader.Frames[Index].Delay * FAnimateSpeed;
+  FRenderCache.Items[Index].IsCorrupted  := FGIFLoader.Frames[Index].IsCorrupted;
+
+  If (FTransparent) Then
+  Begin
+    iDrawMode := dmAlphaCheck;
+  End
+  Else
+  Begin
+    iDrawMode := dmSet;
+  End;
+
+  If Index = 0 Then
+  Begin
+    If (FTransparent) Then
+    Begin
+      FVirtualView.Clear(clrTransparent);
+    End
+    Else
+    Begin
+      FVirtualView.Clear(FGIFLoader.BackgroundColor);
+    End;
+    FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, dmSet);
+    if FGIFLoader.Frames.Items[0].DrawMode = dmKeep then begin
+      if Assigned( FRestoreBitmap) then begin
+        FRestoreBitmap.Free;
+      end;
+      FRestoreBitmap := FVirtualView.Clone;
+    end;
+  End
+  Else
+  Begin
+
+    With FGIFLoader.Frames.Items[Index] Do
+    Begin
+      Case DrawMode Of
+        dmNone:
+        Begin
+          FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, iDrawMode);
+        End;
+        dmKeep:
+        Begin
+          if FLastDrawMode = dmErase then
+          begin
+            If (FGIFLoader.Frames.Items[Index].IsTransparent And FTransparent) Then FVirtualView.Clear(clrTransparent)
+            Else
+              FVirtualView.Clear(FGIFLoader.BackgroundColor);
+          end;
+          FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, iDrawMode);
+          If Assigned(FRestoreBitmap) Then FreeAndNil(FRestoreBitmap);
+          FRestoreBitmap := FVirtualView.Clone;
+        End;
+        dmErase:
+        Begin
+          If (FGIFLoader.Frames.Items[Index].IsTransparent And FTransparent) Then FVirtualView.Clear(clrTransparent)
+          Else
+            FVirtualView.Clear(FGIFLoader.BackgroundColor);
+          FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, iDrawMode);
+        End;
+        dmRestore:
+        Begin
+          if FLastDrawMode = dmErase then
+          begin
+            If (FGIFLoader.Frames.Items[Index].IsTransparent And FTransparent) Then FVirtualView.Clear(clrTransparent)
+            Else
+              FVirtualView.Clear(FGIFLoader.BackgroundColor);
+          End;
+
+          If Assigned(FRestoreBitmap) Then FVirtualView.PutImage(FRestoreBitmap, 0, 0, FRestoreBitmap.Width, FRestoreBitmap.Height, 0, 0, dmSet)
+          else
+          begin
+            If (FGIFLoader.Frames.Items[Index].IsTransparent And FTransparent) Then FVirtualView.Clear(clrTransparent)
+            Else
+              FVirtualView.Clear(FGIFLoader.BackgroundColor);
+          end;
+
+          FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, iDrawMode);
+        End;
+        Else
+          FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, dmSet);
+      End;
+      FLastDrawMode := DrawMode;
+    End;
+  End;
+  // Note : Sous MacOS on ne peux pas assigner FRenderCache.Items[Index].Bitmap directement avec
+  // FVirtualView.GetBitmap; On est obligé de créer le bitmap de destination et utiliser Assign.
+  // Dans le cas contraire seulment la première image sera affichée.
+  //TmpBmp := Graphics.TBitmap.Create; <== MEMORY LEAK
+  TmpBmp := FVirtualView.GetBitmap;
+  FRenderCache.Items[Index].Bitmap.Assign(TmpBmp);
+  FreeAndNil(TmpBmp);
+End;
+
+Procedure TGIFViewer.ComputeCache;
+Var
+  I: Integer;
+Begin
+  FCurrentFrameIndex := 0;
+  FRenderCache.Clear;
+  If FGIFLoader.FrameCount > 0 Then
+  Begin
+    For I := 0 To Pred(FGIFLoader.FrameCount) Do
+    Begin
+      RenderFrame(I);
+    End;
+  end;
+  if AutoRemoveInvalidFrame then FRenderCache.Pack;
+  ResetCurrentView;
+End;
+
+Procedure TGIFViewer.CalculatePreferredSize(Var PreferredWidth, PreferredHeight: Integer; WithThemeSpace: Boolean);
+Var
+  extraWidth: Integer;
+Begin
+  extraWidth := - 2;
+  if FBorderShow then extraWidth      := (FBorderWidth * 2) + (FBevelWidth * 2);
+  PreferredWidth  := FGIFWidth + extraWidth + 2;
+  PreferredHeight := FGIFHeight + extraWidth + 2;
+End;
+
+Class Function TGIFViewer.GetControlClassDefaultSize: TSize;
+Begin
+  Result.CX := 90; // = ClientWidth
+  Result.CY := 90; // = ClientHeight
+End;
+
+Function TGIFViewer.DestRect: TRect;
+Var
+  PicWidth, PicHeight: Integer;
+  ImgWidth, ImgHeight: Integer;
+  n: Integer;
+
+  procedure KeepAspectRatio( Var aWidth, aHeight : Integer; MaxWidth, MaxHeight : Integer);
+  var
+     w, h : Integer;
+  begin
+      w :=  MaxWidth;
+      h := (aHeight * w) Div aWidth;
+      If h > MaxHeight Then
+      Begin
+        h := MaxHeight;
+        w := (aWidth * h) Div aHeight;
+      End;
+      aWidth := w;
+      aHeight := h;
+  End;
+
+Begin
+  PicWidth  := FCurrentView.Width;
+  PicHeight := FCurrentView.Height;
+  ImgWidth  := ClientWidth;
+  ImgHeight := ClientHeight;
+  If (PicWidth = 0) Or (PicHeight = 0) Then Exit(Rect(0, 0, 0, 0));
+
+  if FAutoStretchMode <> smManual then
+  begin
+    Case FAutoStretchMode of
+     smStretchAll : FStretch := True;
+     smStretchOnlyBigger : if (PicWidth > ImgWidth) or (PicHeight > ImgHeight) then FStretch := True else FStretch := False;
+     smStretchOnlySmaller : if (PicWidth < ImgWidth) and (PicHeight < ImgHeight) then FStretch := True else FStretch := False;
+    end;
+    if Assigned(FOnStretchChanged) then FOnStretchChanged(Self,FStretch);
+  End;
+
+  If FStretch Then
+  Begin
+   KeepAspectRatio(PicWidth, PicHeight,ImgWidth, ImgHeight);
+  End;
+
+  n  := FBorderWidth + FBevelWidth;
+  If FBorderShow Then
+  Begin
+    Result := Rect(n, n, n + PicWidth, n + PicHeight);
+  End
+  Else
+    Result := Rect(0, 0, PicWidth, PicHeight);
+
+  If FCenter Then
+  Begin
+    If FBorderShow Then
+    Begin
+      Result.Left   := n + ((ClientWidth -(n+n))  - PicWidth)  shr 1;
+      Result.Top    := n + ((ClientHeight-(n+n))  - PicHeight) shr 1;
+    end
+    else
+    begin
+      Result.Left   := ((ClientWidth  - PicWidth) shr 1);
+      Result.Top    := ((ClientHeight - PicHeight) shr 1);
+    end;
+    Result.Right  := Result.Left + PicWidth;
+    Result.Bottom := Result.Top + PicHeight;
+  End;
+End;
+
+Procedure TGIFViewer.Paint;
+
+  Procedure DrawFrame;
+  Begin
+    With Inherited Canvas Do
+    Begin
+      Pen.Color := clBlack;
+      Pen.Style := psDash;
+      MoveTo(0, 0);
+      LineTo(Self.Width - 1, 0);
+      LineTo(Self.Width - 1, Self.Height - 1);
+      LineTo(0, Self.Height - 1);
+      LineTo(0, 0);
+    End;
+  End;
+
+Var
+  R:     TRect;
+  C:     TCanvas;
+  ARect: TRect;
+  w:     Integer;
+Begin
+
+  If csDesigning In ComponentState Then DrawFrame;
+
+  C         := Inherited Canvas;
+  FPainting := True;
+  R         := DestRect;
+  Try
+    C.Lock;
+    // Fond
+    If (FColor <> clNone) Then //and Not(FTransparent)
+    Begin
+      With C Do
+      Begin
+        Brush.Style := bsSolid;
+        Brush.Color := FColor;
+        FillRect(0, 0, ClientWidth, ClientHeight);
+      End;
+    End;
+
+    // Bitmap
+    FCurrentView.Transparent := FTransparent;
+    C.StretchDraw(R, FCurrentView);
+
+    // Bordures
+    If FBorderShow Then
+    Begin
+      ARect := rect(0, 0, ClientWidth, ClientHeight);
+      w     := FBevelWidth;
+      If (FBevelInner <> bvNone) And (w > 0) Then C.Frame3d(ARect, w, BevelInner); // Note: Frame3D inflates ARect
+      InflateRect(ARect, -(FBorderWidth + 1), -(FBorderWidth + 1));
+      If (FBevelOuter <> bvNone) And (w > 0) Then C.Frame3d(ARect, w, BevelOuter);
+
+      If FBorderWidth > 0 Then With C Do
+        Begin
+          Pen.Style   := psSolid;
+          Pen.Width   := FBorderWidth;
+          Pen.Color   := FBorderColor;
+          Brush.Style := bsClear;
+          Rectangle(0, 0, ClientWidth, ClientHeight);
+        End;
+    End;
+
+    C.UnLock;
+  Finally
+    FPainting := False;
+  End;
+
+  Inherited Paint;
+End;
+
+Procedure TGIFViewer.Loaded;
+begin
+  if FFileName<>'' then LoadFromFile(FFileName);
+  inherited Loaded;
+end;
+
+procedure TGIFViewer.BeforeLoad;
+begin
+  FAnimateTimer.Enabled := False;
+  FPause     := False;
+  FAnimated  := False;
+  FCurrentFrameIndex := 0;
+end;
+
+procedure TGIFViewer.AfterLoad;
+begin
+  FGIFWidth  := FGIFLoader.Width;
+  FGIFHeight := FGIFLoader.Height;
+  FVirtualView.SetSize(FGIFWidth, FGIFHeight);
+
+  if FCache then
+    ComputeCache
+  else begin
+    FRenderCache.Clear;
+    FCurrentFrameIndex := 0;
+    RenderFrame(0);
+    ResetCurrentView;
+  end;
+
+  If AutoSize Then
+  Begin
+    InvalidatePreferredSize;
+    AdjustSize;
+  End;
+  Invalidate;
+  If FAutoPlay Then Start;
+end;
+
+Procedure TGIFViewer.Invalidate;
+Begin
+  If FPainting Then exit;
+  Inherited Invalidate;
+End;
+
+Procedure TGIFViewer.LoadFromStream(aStream : TStream);
+Begin
+  BeforeLoad;
+  FGIFLoader.FErrorList.Clear;
+  FGIFLoader.FErrorCOunt := 0;
+  FGIFLoader.LoadFromStream(aStream);
+  AfterLoad;
+End;
+
+Procedure TGIFViewer.LoadFromFile(Const aFileName: String);
+Begin
+  BeforeLoad;
+  if Not(FileExists(aFileName)) then
+  begin
+    MessageDlg(Format(rsFileNotFound,[aFileName]), mtError, [mbOK],0);
+    Exit;
+  end;
+  FGIFLoader.LoadFromFile(aFileName);
+  FFileName  := aFileName;
+  AfterLoad;
+End;
+
+Procedure TGIFViewer.LoadFromResource(Const ResName: String);
+Var
+  Resource: TLResource;
+Begin
+  BeforeLoad;
+  Resource  := LazarusResources.Find(ResName);
+  If Resource = nil Then Raise Exception.Create(Format(rsResourceNotFound,[ResName]))
+  Else If CompareText(LazarusResources.Find(ResName).ValueType, 'gif') = 0 Then
+  Begin
+    FGIFLoader.LoadFromResource(ResName);
+    AfterLoad;
+  End;
+End;
+
+Procedure TGIFViewer.Start;
+Begin
+  If Not (FPause) Then FCurrentFrameIndex := 0;
+  FPause    := False;
+  FAnimated := True;
+  FAnimateTimer.Enabled := True;
+  If Assigned(FOnStart) Then FOnStart(Self);
+End;
+
+Procedure TGIFViewer.Stop;
+Begin
+  FAnimateTimer.Enabled := False;
+  FAnimated := False;
+  FPause    := False;
+  If Assigned(FOnStop) Then FOnStop(Self);
+  FCurrentframeIndex     := 0;
+  ResetCurrentView;
+  Invalidate;
+End;
+
+Procedure TGIFViewer.Pause;
+Begin
+  FAnimateTimer.Enabled := False;
+  FPause := True;
+End;
+
+Procedure TGIFViewer.NextFrame;
+begin
+  if FCurrentFrameIndex < FGifLoader.FrameCount - 1 then
+  begin
+    Inc(FCurrentFrameIndex);
+
+    repeat
+
+      If (not FCache) and (FCurrentFrameIndex >= FRenderCache.Count) Then
+      begin
+        RenderFrame(FCurrentFrameIndex);
+      end;
+
+      If Assigned(FOnFrameChange) Then FOnFrameChange(Self);
+
+      if not(FDisplayInvalidFrames) then
+      begin
+        if not(FRenderCache.Items[FCurrentFrameIndex].IsCorrupted) then
+        begin
+          FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
+        End
+        Else If FCurrentFrameIndex > 0 Then
+        Begin
+          Inc(FCurrentFrameIndex);
+          Continue;
+        End;
+      end
+      else
+      begin
+        FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
+      end;
+      Break;
+    until False;
+
+    FAnimateTimer.Interval := FRenderCache.Items[FCurrentFrameIndex].Delay;
+  end;
+  Invalidate;
+end;
+
+Procedure TGIFViewer.PriorFrame;
+begin
+  if FCurrentFrameIndex > 0 then
+  begin
+    Dec(FCurrentFrameIndex);
+
+    repeat
+      If Assigned(FOnFrameChange) Then FOnFrameChange(Self);
+
+      if not(FDisplayInvalidFrames) then
+      begin
+        if not(FRenderCache.Items[FCurrentFrameIndex].IsCorrupted) then
+        begin
+          FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
+        End
+        Else If FCurrentFrameIndex > 0 Then
+        Begin
+          Dec(FCurrentFrameIndex);
+          Continue;
+        End;
+      end
+      else
+      begin
+        FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
+      end;
+      Break;
+    until False;
+
+    FAnimateTimer.Interval := FRenderCache.Items[FCurrentFrameIndex].Delay;
+  end;
+  Invalidate;
+end;
+
+Function TGIFViewer.GetRawFrame(Index: Integer): TBitmap;
+Begin
+  Result := FGIFLoader.Frames[Index].Bitmap.GetBitmap;
+End;
+
+Procedure TGIFViewer.DisplayFrame(Index: Integer);
+Begin
+  If not(FRenderCache.IsIndexOk(Index)) then exit;
+  if Not(DisplayInvalidFrames) then
+  begin
+    if FRenderCache.Items[Index].IsCorrupted then
+    begin
+      inc(Index);
+      DisplayFrame(Index);
+    End
+    else
+    begin
+      FCurrentView.Assign(FRenderCache.Items[Index].Bitmap);
+    End;
+  end
+  else
+  begin
+    FCurrentView.Assign(FRenderCache.Items[Index].Bitmap);
+  End;
+  Invalidate;
+End;
+
+Procedure TGIFViewer.DisplayRawFrame(Index: Integer);
+Var
+  Tmp: Graphics.TBitmap;
+Begin
+  If not(FRenderCache.IsIndexOk(Index)) Then exit;
+  Tmp := GetRawFrame(Index);
+  FCurrentView.Assign(Tmp);
+  FreeAndNil(Tmp);
+  Invalidate;
+End;
+
+Procedure TGIFViewer.ExtractFrame(Index: Integer; Var bmp: TBitmap);
+Begin
+  If not(FRenderCache.IsIndexOk(Index)) then exit;
+  Bmp.Assign(FRenderCache.Items[Index].Bitmap);
+End;
+
+Procedure TGIFViewer.ExtractRawFrame(Index: Integer; Var bmp: TBitmap);
+Var
+  Tmp: Graphics.TBitmap;
+Begin
+  If not(FRenderCache.IsIndexOk(Index)) Then exit;
+  Tmp := GetRawFrame(Index);
+  Bmp.Assign(Tmp);
+  FreeAndNil(Tmp);
+End;
+
+{%endregion}
+
+Procedure Register;
+Begin
+  RegisterComponents('Misc', [TGIFView]);
+End;
+
+End.

BIN
components/gifview/tgifview.png


+ 1 - 1
doc/INSTALL.txt

@@ -15,7 +15,7 @@ directory of DC sources. You must open each .lpk package file:
 - multithreadprocs/multithreadprocslaz.lpk
 - kascrypt/kascrypt.lpk
 - doublecmd/doublecmd_common.lpk
-- gifanim/pkg_gifanim.lpk
+- gifview/gifview.lpk
 - KASToolBar/kascomp.lpk
 - synunihighlighter/synuni.lpk
 - viewer/viewerpackage.lpk

+ 1 - 1
fpmake.pp

@@ -19,7 +19,7 @@ const
     'components\Image32\Image32.lpk',
     'components\KASToolBar\kascomp.lpk',
     'components\viewer\viewerpackage.lpk',
-    'components\gifanim\pkg_gifanim.lpk',
+    'components\gifview\gifview.lpk',
     'components\synunihighlighter\synuni.lpk',
     'components\virtualterminal\virtualterminal.lpk'
   );

+ 1 - 2
src/doublecmd.lpi

@@ -299,8 +299,7 @@ end;"/>
         <MinVersion Valid="True"/>
       </Item5>
       <Item6>
-        <PackageName Value="pkg_gifanim"/>
-        <MinVersion Major="1" Minor="5" Valid="True"/>
+        <PackageName Value="GifView"/>
       </Item6>
       <Item7>
         <PackageName Value="VirtualTerminal"/>

+ 13 - 3
src/fviewer.lfm

@@ -371,7 +371,6 @@ object frmViewer: TfrmViewer
     ParentFont = False
     TabOrder = 3
     Visible = False
-    OnResize = pnlImageResize
     object sboxImage: TScrollBox
       Left = 0
       Height = 307
@@ -389,6 +388,7 @@ object frmViewer: TfrmViewer
       OnMouseEnter = sboxImageMouseEnter
       OnMouseLeave = sboxImageMouseLeave
       OnMouseMove = sboxImageMouseMove
+      OnResize = sboxImageResize
       object Image: TImage
         Left = 56
         Height = 288
@@ -407,12 +407,22 @@ object frmViewer: TfrmViewer
         OnMouseWheelUp = ImageMouseWheelUp
         Proportional = True
       end
-      object GifAnim: TGifAnim
+      object GifAnim: TGIFView
         Left = 0
         Height = 90
         Top = 0
         Width = 106
-        AutoSize = False
+        Border = False
+        BorderColor = clBlack
+        BorderWidth = 0
+        BevelOuter = bvNone
+        AutoPlay = False
+        Transparent = True
+        Center = False
+        AutoStretchMode = smManual
+        Stretch = True
+        DisplayInvalidFrames = False
+        AutoRemoveInvalidFrame = True
         OnMouseDown = GifAnimMouseDown
         OnMouseEnter = GifAnimMouseEnter
       end

+ 83 - 53
src/fviewer.pas

@@ -58,7 +58,7 @@ interface
 uses
   SysUtils, Classes, Graphics, Controls, Forms, ExtCtrls, ComCtrls, LMessages,
   LCLProc, Menus, Dialogs, ExtDlgs, StdCtrls, Buttons, SynEditHighlighter,
-  Grids, ActnList, viewercontrol, GifAnim, fFindView, WLXPlugin, uWLXModule,
+  Grids, ActnList, viewercontrol, uGifViewer, fFindView, WLXPlugin, uWLXModule,
   uFileSource, fModView, Types, uThumbnails, uFormCommands, uOSForms,Clipbrd,
   uExifReader, KASStatusBar, SynEdit, uShowForm, uRegExpr, uRegExprU,
   Messages, fEditSearch, uMasks, uSearchTemplate, uFileSourceOperation,
@@ -159,7 +159,7 @@ type
     btnPrev1: TSpeedButton;
     btnReload1: TSpeedButton;
     DrawPreview: TDrawGrid;
-    GifAnim: TGifAnim;
+    GifAnim: TGIFView;
     memFolder: TMemo;
     mnuPlugins: TMenuItem;
     miCode: TMenuItem;
@@ -315,7 +315,6 @@ type
     procedure miPenClick(Sender: TObject);
     procedure miLookBookClick(Sender: TObject);
     procedure pmEditMenuPopup(Sender: TObject);
-    procedure pnlImageResize(Sender: TObject);
     procedure miPluginsClick(Sender: TObject);
 
     procedure pnlTextMouseWheelUp(Sender: TObject; Shift: TShiftState;
@@ -325,6 +324,7 @@ type
     procedure sboxImageMouseMove(Sender: TObject; Shift: TShiftState; X,
       Y: Integer);
     procedure btnNextGifFrameClick(Sender: TObject);
+    procedure sboxImageResize(Sender: TObject);
     procedure SplitterChangeBounds;
     procedure TimerReloadTimer(Sender: TObject);
     procedure TimerScreenshotTimer(Sender: TObject);
@@ -351,6 +351,7 @@ type
     procedure UpdateImagePlacement;
     procedure StartCalcFolderSize;
     procedure StopCalcFolderSize;
+    procedure UpdateAnimState;
 
   private
     FFileName: String;
@@ -2061,11 +2062,6 @@ begin
   end;
 end;
 
-procedure TfrmViewer.pnlImageResize(Sender: TObject);
-begin
-  if bImage then AdjustImageSize;
-end;
-
 procedure TfrmViewer.miPluginsClick(Sender: TObject);
 var
   ShowFlags: Integer;
@@ -2117,10 +2113,27 @@ begin
   end;
 end;
 
+procedure TfrmViewer.UpdateAnimState;
+begin
+  btnPrevGifFrame.Enabled:= GifAnim.Paused and (GifAnim.CurrentFrameIndex > 0);
+  btnNextGifFrame.Enabled:= GifAnim.Paused and (GifAnim.CurrentFrameIndex < GifAnim.FrameCount - 1);
+end;
+
+procedure TfrmViewer.btnPrevGifFrameClick(Sender: TObject);
+begin
+  GifAnim.PriorFrame;
+  UpdateAnimState;
+end;
+
 procedure TfrmViewer.btnNextGifFrameClick(Sender: TObject);
 begin
-  GifAnim.Animate:=false;
   GifAnim.NextFrame;
+  UpdateAnimState;
+end;
+
+procedure TfrmViewer.sboxImageResize(Sender: TObject);
+begin
+  if bImage or bAnimation then AdjustImageSize;
 end;
 
 procedure TfrmViewer.SplitterChangeBounds;
@@ -2289,6 +2302,10 @@ begin
       UndoTmp;
     end;
     AdjustImageSize;
+  end
+  else if bAnimation then
+  begin
+    AdjustImageSize;
   end;
 end;
 
@@ -2414,8 +2431,6 @@ begin
 
   FixFormIcon(Handle);
 
-  GifAnim.Align:=alClient;
-
   for Index:= 1 to 25 do
   begin
     MenuItem:= TMenuItem.Create(btnPenWidth);
@@ -2521,24 +2536,26 @@ end;
 
 procedure TfrmViewer.btnGifMoveClick(Sender: TObject);
 begin
-  GifAnim.Animate:=not GifAnim.Animate;
-  btnNextGifFrame.Enabled:= not GifAnim.Animate;
-  btnPrevGifFrame.Enabled:= not GifAnim.Animate;
-  if GifAnim.Animate then
-    btnGifMove.ImageIndex:= 11
+  if GifAnim.Paused then
+    GifAnim.Start
   else begin
+    GifAnim.Pause;
+  end;
+  if GifAnim.Paused then
     btnGifMove.ImageIndex:= 12
+  else begin
+    btnGifMove.ImageIndex:= 11
   end;
+  UpdateAnimState;
 end;
 
 procedure TfrmViewer.btnGifToBmpClick(Sender: TObject);
 begin
-  GifAnim.Animate:=False;
-  Image.Picture.Bitmap.Create;
-  Image.Picture.Bitmap.Width := GifAnim.Width;
-  Image.Picture.Bitmap.Height := GifAnim.Height;
-  Image.Picture.Bitmap.Canvas.CopyRect(Rect(0,0,GifAnim.Width,GifAnim.Height),GifAnim.Canvas,Rect(0,0,GifAnim.Width,GifAnim.Height));
+  GifAnim.Pause;
+  btnGifMove.ImageIndex:= 12;
+  Image.Picture.Bitmap:= GifAnim.CurrentView;
   cm_SaveAs(['']);
+  UpdateAnimState;
 end;
 
 procedure TfrmViewer.btnPaintHightlight(Sender: TObject);
@@ -2602,12 +2619,6 @@ begin
   btnPenMode.Down:= not btnPenMode.Down;
 end;
 
-procedure TfrmViewer.btnPrevGifFrameClick(Sender: TObject);
-begin
-  GifAnim.Animate:=False;
-  GifAnim.PriorFrame;
-end;
-
 procedure TfrmViewer.btnRedEyeClick(Sender: TObject);
 begin
   RedEyes;
@@ -2829,23 +2840,42 @@ procedure TfrmViewer.AdjustImageSize;
 const
   fmtImageInfo = '%dx%d (%.0f %%)';
 var
+  AControl: TControl;
   dScaleFactor : Double;
+  ImgWidth, ImgHeight : Integer;
   iLeft, iTop, iWidth, iHeight : Integer;
 begin
-  if (Image.Picture = nil) then Exit;
-  if (Image.Picture.Width = 0) or (Image.Picture.Height = 0) then Exit;
+  if not (bImage or bAnimation) then
+    Exit;
+
+  if bImage then
+  begin
+    if (Image.Picture = nil) then Exit;
+    ImgHeight:= Image.Picture.Height;
+    ImgWidth:= Image.Picture.Width;
+    AControl:= Image;
+  end
+  else if (bAnimation) then
+  begin
+    if GifAnim.CurrentView = nil then Exit;
+    ImgHeight:= GifAnim.CurrentView.Height;
+    ImgWidth:= GifAnim.CurrentView.Width;
+    AControl:= GifAnim;
+  end;
+
+  if (ImgWidth = 0) or (ImgHeight = 0) then Exit;
 
   dScaleFactor:= FZoomFactor / 100;
 
   // Place and resize image
   if (FZoomFactor = 100) and (miStretch.Checked or miStretchOnlyLarge.Checked) then
   begin
-    dScaleFactor:= Min(sboxImage.ClientWidth / Image.Picture.Width ,sboxImage.ClientHeight / Image.Picture.Height);
+    dScaleFactor:= Min(sboxImage.ClientWidth / ImgWidth, sboxImage.ClientHeight / ImgHeight);
     dScaleFactor:= IfThen((miStretchOnlyLarge.Checked) and (dScaleFactor > 1.0), 1.0, dScaleFactor);
   end;
 
-  iWidth:= Trunc(Image.Picture.Width * dScaleFactor);
-  iHeight:= Trunc(Image.Picture.Height * dScaleFactor);
+  iWidth:= Trunc(ImgWidth * dScaleFactor);
+  iHeight:= Trunc(ImgHeight * dScaleFactor);
   if (miCenter.Checked) then
   begin
     iLeft:= (sboxImage.ClientWidth - iWidth) div 2;
@@ -2856,7 +2886,7 @@ begin
     iLeft:= 0;
     iTop:= 0;
   end;
-  Image.SetBounds(Max(iLeft,0), Max(iTop,0), iWidth , iHeight);
+  AControl.SetBounds(Max(iLeft,0), Max(iTop,0), iWidth , iHeight);
 
   // Update scrollbars
   // TODO: fix - calculations are correct but it seems like scroll bars
@@ -2870,8 +2900,8 @@ begin
   end;
 
   // Update status bar
-  Status.Panels[sbpCurrentResolution].Text:= Format(fmtImageInfo, [iWidth,iHeight,  100.0 * dScaleFactor]);
-  Status.Panels[sbpFullResolution].Text:= Format(fmtImageInfo, [Image.Picture.Width,Image.Picture.Height, 100.0]);
+  Status.Panels[sbpCurrentResolution].Text:= Format(fmtImageInfo, [iWidth, iHeight,  100.0 * dScaleFactor]);
+  Status.Panels[sbpFullResolution].Text:= Format(fmtImageInfo, [ImgWidth, ImgHeight, 100.0]);
 end;
 
 function TfrmViewer.GetListerRect: TRect;
@@ -2957,7 +2987,6 @@ function TfrmViewer.LoadGraphics(const sFileName:String): Boolean;
     btnHightlight.Enabled:= bImage and (not miFullScreen.Checked);
     btnPaint.Enabled:= bImage and (not miFullScreen.Checked);
     btnResize.Enabled:= bImage and (not miFullScreen.Checked);
-    miImage.Visible:= bImage;
     btnZoomIn.Enabled:= bImage;
     btnZoomOut.Enabled:= bImage;
     btn270.Enabled:= bImage;
@@ -2973,23 +3002,13 @@ function TfrmViewer.LoadGraphics(const sFileName:String): Boolean;
 
 var
   sExt: String;
-  fsFileHandle: System.THandle;
-  fsFileStream: TFileStreamEx = nil;
-  gifHeader: array[0..5] of AnsiChar;
+  fsFileStream: TFileStreamEx;
 begin
   Result:= True;
   FZoomFactor:= 100;
   sExt:= ExtractOnlyFileExt(sFilename);
-  if SameText(sExt, 'gif') then
+  if not SameText(sExt, 'gif') then
   begin
-    fsFileHandle:= mbFileOpen(sFileName, fmOpenRead or fmShareDenyNone);
-    if (fsFileHandle = feInvalidHandle) then Exit(False);
-    FileRead(fsFileHandle, gifHeader, SizeOf(gifHeader));
-    FileClose(fsFileHandle);
-  end;
-  // GifAnim supports only GIF89a
-  if gifHeader <> 'GIF89a' then
-    begin
       Image.Visible:= True;
       GifAnim.Visible:= False;
       try
@@ -3008,6 +3027,8 @@ begin
             end;
           end;
 {$ENDIF}
+          bImage:= True;
+          bAnimation:= False;
           UpdateToolbar(True);
         finally
           FreeAndNil(fsFileStream);
@@ -3016,7 +3037,6 @@ begin
         begin
           if FExif.LoadFromFile(sFileName) then
           begin
-            bImage:= True;
             case FExif.Orientation of
               2: cm_MirrorHorz([]);
               3: cm_Rotate180([]);
@@ -3040,8 +3060,18 @@ begin
       GifAnim.Visible:= True;
       Image.Visible:= False;
       try
-        GifAnim.FileName:= sFileName;
-        UpdateToolbar(False);
+        fsFileStream:= TFileStreamEx.Create(sFileName, fmOpenRead or fmShareDenyNone);
+        try
+          GifAnim.LoadFromStream(fsFileStream);
+          bImage:= False;
+          bAnimation:= True;
+          UpdateToolbar(False);
+          AdjustImageSize;
+          GifAnim.Start;
+          UpdateAnimState;
+        finally
+          fsFileStream.Free;
+        end;
       except
         on E: Exception do
         begin
@@ -3591,7 +3621,7 @@ begin
   miGraphics.Checked   := (Panel = pnlImage);
   miEncoding.Visible   := (Panel = pnlText) or (Panel = pnlCode) or (bPlugin and FWlxModule.CanCommand);
   miAutoReload.Visible := (Panel = pnlText);
-  miImage.Visible      := (bImage or (bPlugin and FWlxModule.CanCommand));
+  miImage.Visible      := (bImage or bAnimation or (bPlugin and FWlxModule.CanCommand));
   miRotate.Visible     := bImage;
   miZoomIn.Visible     := bImage;
   miZoomOut.Visible    := bImage;
@@ -3968,7 +3998,7 @@ begin
       if not bAnimation then
         Clipboard.Assign(Image.Picture)
       else
-        Clipboard.Assign(GifAnim.GifBitmaps[GifAnim.GifIndex].Bitmap);
+        Clipboard.Assign(GifAnim.CurrentView);
     end else
        ViewerControl.CopyToClipboard;
   end;