{ Copyright (C) 2009 Laurent Jacques 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 . You can also obtain it by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Version 1.4 } 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 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.LoadAllBitmap(var AGifList: TGifList): boolean; var GifStream: TMemoryStream; Introducer: byte; FPImage: TLazIntfImage; ImgFormatDescription: TRawImageDescription; GifBitmap: TGifImage; begin Result := False; if not FileExists(FFileName) then exit; if not assigned(AGifList) then AGifList := TGifList.Create(True); 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); 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); until (Introducer = ID_TRAILER); GifStream.Free; Result := True; end; function TGifLoader.LoadFromLazarusResource(const ResName: String; var AGifList: TGifList): boolean; var GifStream: TLazarusResourceStream; Introducer: byte; FPImage: TLazIntfImage; ImgFormatDescription: TRawImageDescription; GifBitmap: TGifImage; begin Result := False; if not assigned(AGifList) then AGifList := TGifList.Create(True); GifStream := TLazarusResourceStream.Create(ResName, nil); 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); 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); until (Introducer = ID_TRAILER); GifStream.Free; Result := True; 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.