unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, BGRABitmap, BGRAAnimatedGif, LCLType, StdCtrls; type { TGifAnimationPanel } TGifAnimationPanel = class(TPanel) private FOnRenderFrame: TNotifyEvent; procedure SetImage(AValue: TBGRAAnimatedGif); procedure SetOnRenderFrame(AValue: TNotifyEvent); protected FImage: TBGRAAnimatedGif; FFirstPaint, FAnimatePaint: boolean; FTimer: TTimer; procedure Paint; override; procedure EraseBackground(DC: HDC); override; procedure ImageChanged(Sender: TObject); procedure TimerTimer(Sender: TObject); public constructor Create(TheOwner: TComponent); override; destructor Destroy; override; property Image: TBGRAAnimatedGif read FImage; property OnRenderFrame: TNotifyEvent read FOnRenderFrame write SetOnRenderFrame; end; { TForm1 } TForm1 = class(TForm) SaveDialog1: TSaveDialog; Timer1: TTimer; Panel1: TPanel; Label1: TLabel; Label2: TLabel; Button1: TButton; Button2: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDropFiles(Sender: TObject; const FileNames: array of string); protected { private declarations } procedure GifAnimationPanelRenderFrame(Sender: TObject); public { public declarations } filename, error: string; animationPanel: TGifAnimationPanel; procedure LoadImage(AFilename: string); procedure FillMemo; end; var Form1: TForm1; implementation {$R *.lfm} { TGifAnimationPanel } constructor TGifAnimationPanel.Create(TheOwner: TComponent); begin inherited Create(TheOwner); FFirstPaint:= true; FAnimatePaint:= false; BevelInner:= bvNone; BevelOuter:= bvNone; FImage := TBGRAAnimatedGif.Create; FImage.OnChange := @ImageChanged; FTimer := TTimer.Create(self); FTimer.Enabled := false; FTimer.OnTimer := @TimerTimer; end; destructor TGifAnimationPanel.Destroy; begin inherited Destroy; end; procedure TGifAnimationPanel.SetImage(AValue: TBGRAAnimatedGif); begin FImage.Free; end; procedure TGifAnimationPanel.SetOnRenderFrame(AValue: TNotifyEvent); begin if FOnRenderFrame=AValue then Exit; FOnRenderFrame:=AValue; end; procedure TGifAnimationPanel.Paint; var waitTime: Integer; begin inherited Paint; if FFirstPaint or not FAnimatePaint then begin Canvas.Brush.Assign(Brush); Canvas.FillRect(ClientRect); image.BackgroundMode := gbmEraseBackground; image.EraseColor:= Color; if Assigned(FImage) and (FImage.Count > 0) then FImage.Show(Canvas, ClientRect); end else begin if Assigned(FImage) and (FImage.Count > 0) then FImage.Update(Canvas, ClientRect); end; FAnimatePaint := false; FFirstPaint := false; if Assigned(FOnRenderFrame) then FOnRenderFrame(self); if Assigned(FImage) and (FImage.Count > 1) then begin waitTime := Image.TimeUntilNextImageMs; if waitTime < 15 then waitTime := 15; FTimer.Interval := waitTime; FTimer.Enabled := true; end; end; procedure TGifAnimationPanel.EraseBackground(DC: HDC); begin // don't erase background end; procedure TGifAnimationPanel.ImageChanged(Sender: TObject); begin FTimer.Enabled:= false; Invalidate; end; procedure TGifAnimationPanel.TimerTimer(Sender: TObject); begin FTimer.Enabled := false; FAnimatePaint := true; Refresh; end; { TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin animationPanel := TGifAnimationPanel.Create(self); animationPanel.OnRenderFrame := @GifAnimationPanelRenderFrame; animationPanel.Left := 4; animationPanel.Top := 4; animationPanel.Color:= self.Color; InsertControl(animationPanel, 0); LoadImage(ConcatPaths([ExtractFilePath(Application.ExeName), 'waterdrops.gif'])); FillMemo; end; procedure TForm1.Button1Click(Sender: TObject); begin animationPanel.Refresh; end; procedure TForm1.Button2Click(Sender: TObject); begin try if SaveDialog1.Execute then animationPanel.Image.SaveToFile(SaveDialog1.FileName); except on ex: exception do begin error := ex.Message; FillMemo; end; end; end; procedure TForm1.FormDropFiles(Sender: TObject; const FileNames: array of string); begin LoadImage(FileNames[0]); end; procedure TForm1.GifAnimationPanelRenderFrame(Sender: TObject); begin Label1.Caption := 'Frame: ' + inttostr(animationPanel.image.CurrentImage) + ', Wait: ' + inttostr(animationPanel.Image.TimeUntilNextImageMs); end; procedure TForm1.LoadImage(AFilename: string); begin filename := AFilename; try if not FileExists(filename) then begin animationPanel.Image.Clear; error := 'Not found'; end else begin animationPanel.Image.LoadFromFile(filename); error := 'Ok'; end; except on ex:Exception do error := ex.Message; end; animationPanel.Width := animationPanel.Image.Width; animationPanel.Height := animationPanel.Image.Height; FillMemo; end; procedure TForm1.FillMemo; var s: string; i: Integer; begin with animationPanel do begin s := error + LineEnding + ExtractFileName(filename) + LineEnding + inttostr(image.Width)+'x'+inttostr(image.height) + LineEnding + inttostr(image.Count)+' frames' + LineEnding; if image.LoopCount = 0 then s += 'infinite loop' + LineEnding else s += inttostr(image.LoopCount)+' loops' + LineEnding; for i := 0 to image.Count-1 do s += '#' + inttostr(i)+': '+inttostr(image.FrameDelayMs[i])+' ms, '+ inttostr(image.FrameImage[i].Width)+'x'+inttostr(image.FrameImage[i].Height) + LineEnding; end; Memo1.Text := s; end; end.