2849 lines
84 KiB
ObjectPascal
2849 lines
84 KiB
ObjectPascal
{=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
|
|
KKKKK KKKKK OOOOOOOOO LLLLL
|
|
KKKKK KKKKK OOOOOOOOOOOOO LLLLL
|
|
KKKKK KKKKK OOOOO OOOOO LLLLL
|
|
KKKKK KKKKK OOOOO OOOOO LLLLL
|
|
KKKKKKKKKK OOOOO OOOOO LLLLL
|
|
KKKKK KKKKK OOOOO OOOOO LLLLL
|
|
KKKKK KKKKK OOOOO OOOOO LLLLL
|
|
KKKKK KKKKK OOOOOOOOOOOOO LLLLLLLLLLLLL
|
|
KKKKK KKKKK OOOOOOOOO LLLLLLLLLLLLL
|
|
|
|
Key Objects Library (C) 2000 by Kladov Vladimir.
|
|
|
|
K.O.L. - is a set of objects to create small programs
|
|
with Delphi, but without the VCL. It is based on the
|
|
idea of XCL, which also allows the creation of smaller
|
|
programs then in the VCL (about 5 times smaller).
|
|
However, this is not as small as the author (me) would
|
|
like. KOL allows the creation of applications about
|
|
10 times smaller then those created with the VCL. But
|
|
this does not mean that KOL is less power then the
|
|
VCL - perhaps just the opposite...
|
|
|
|
XCL and KOL are provided free with the source code.
|
|
Idea is copyrighted (C) to me, Vladimir Kladov.
|
|
The most of the code is also copyrighted (C) to me.
|
|
Code provided by other developers (even if later
|
|
changed by me) is fully aknowledged.
|
|
|
|
If You wish to take part in developing KOL, please
|
|
do let me know.
|
|
|
|
mailto: bonanzas@xcl.cjb.net
|
|
Home: http://kol.nm.ru
|
|
http://xcl.cjb.net
|
|
http://xcl.nm.ru
|
|
|
|
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
|
|
|
|
unit KOLGif;
|
|
{* This unit contains TGif and TGifDecoder object definitions, implementing
|
|
decoding and right painting of Graphic Interchange File format (gif-images).
|
|
Encoding is not implemented here.
|
|
|<br>
|
|
This code is ported from XCL code ( XGifs.pas ) with some enchancements.
|
|
|<br>
|
|
Originally, this code was extracted from freeware RXLib Delphi VCL
|
|
components library (rxgif.pas). VCL bloats and unneeded dependances
|
|
from other parts of RXLib were removed, and important add
|
|
was made: exact transparency mask, which helps to correctly
|
|
decode and paint ANY gif independantly from current video
|
|
settings.
|
|
|<br>
|
|
To get know about authors of RXLib, please visit
|
|
|<a href="http://www.rxlib.com">their site</a>.
|
|
|<br>
|
|
Rxgif code, was originally based on source of freeware GBM
|
|
program (C++) by
|
|
|<a href="mailto:nyangau@interalpha.co.uk">
|
|
Andy Key (nyangau@interalpha.co.uk)
|
|
|</a>.
|
|
}
|
|
|
|
//{$DEFINE CHK_BITBLT}
|
|
|
|
interface
|
|
|
|
{$I KOLDEF.INC}
|
|
|
|
{$IFDEF _D6orHigher}
|
|
{$WARN SYMBOL_PLATFORM OFF}
|
|
{$ENDIF}
|
|
|
|
uses windows, KOL {, ChkGdi};
|
|
|
|
type
|
|
TGifVersion = ( gvUnknown, gv87a, gv89a );
|
|
|
|
TGifBits = 1..8;
|
|
|
|
TDisposeMethod = ( dmUndefined, dmLeave, dmRestoreBackground,
|
|
dmRestorePrevious );
|
|
|
|
TGifColorItem = record
|
|
Red: Byte;
|
|
Green:Byte;
|
|
Blue: Byte;
|
|
end;
|
|
|
|
TGifColorTable = record
|
|
ColorCount : Integer;
|
|
Colors : packed array[ Byte ] of TGifColorItem;
|
|
end;
|
|
|
|
PGifFrame = ^TGifFrame;
|
|
|
|
//PGifItem = ^TGifItem;
|
|
TGifItem = packed record // object( TObj )
|
|
//private
|
|
FImageData : PStream; // memory stream
|
|
FSize : TPoint;
|
|
FPackedFields : Byte;
|
|
FBitsPerPixel : Byte;
|
|
FColorMap : TGifColorTable;
|
|
{public
|
|
destructor Destroy; virtual;}
|
|
end;
|
|
|
|
//PGifData = ^TGifData;
|
|
TGifData = packed record // object( TObj )
|
|
//private
|
|
DComment : PStrList;
|
|
DAspectRatio : Byte;
|
|
DBitsPerPixel : Byte;
|
|
DColorResBits : Byte;
|
|
DColorMap : TGifColorTable;
|
|
{public
|
|
constructor Create;
|
|
destructor Destroy; virtual;}
|
|
end;
|
|
|
|
PGifDecoder = ^TGifDecoder;
|
|
TGifDecoder = object( TObj )
|
|
{* This object can be used directly to load gif image from file or stream
|
|
and to decode its frames. To provide correct drawing of animated gif
|
|
images, use TGif object, which is much more power, and works correctly
|
|
in the most cases. Therefore, TGifDecoder allows to decode single-frame
|
|
images easy and can be used to pack bitmap resources delivered together
|
|
with the application. }
|
|
private
|
|
FGifData : TGifData;
|
|
FVersion : TGifVersion;
|
|
FItems : PList; // of PGifFrame
|
|
FFrameIndex : Integer;
|
|
FGifWidth : Integer;
|
|
FGifHeight : Integer;
|
|
FBkColor : TColor;
|
|
FBackIndex : Integer;
|
|
FLooping : Boolean;
|
|
FRepeatCount : Word;
|
|
FNeedMask: Boolean;
|
|
FTransparent : Boolean;
|
|
FCorrupted: Boolean;
|
|
FOnNeedMask: procedure( Sender: PObj; var BIH: TBitmapInfoHeader; Bits: Pointer );
|
|
procedure NewImage;
|
|
procedure ClearItems;
|
|
function GetFrames(Idx: Integer): PGifFrame;
|
|
function GetComment: PStrList;
|
|
function GetBitmap: PBitmap;
|
|
procedure SetNeedMask(const Value: Boolean);
|
|
function GetMask: PBitmap;
|
|
protected
|
|
function GetWidth : Integer;
|
|
function GetHeight: Integer;
|
|
function GetFrameCount : Integer;
|
|
function GetFrame : Integer;
|
|
procedure SetFrame( Value : Integer );
|
|
public
|
|
destructor Destroy; virtual;
|
|
{* Use Free method instead. }
|
|
procedure Clear;
|
|
{* Clears gif image. }
|
|
property Count : Integer read GetFrameCount;
|
|
{* Returns count of frames stored in the gif image. }
|
|
property Frame : Integer read GetFrame write SetFrame;
|
|
{* Index of current frame (between 0 and Count-1). }
|
|
property Width : Integer read GetWidth;
|
|
{* Width of entire gif image. }
|
|
property Height : Integer read GetHeight;
|
|
{* Height of entire gif image. }
|
|
property BkColor : TColor read FBkColor write FBkColor;
|
|
{* Background color. After loading gif, this property contains a value,
|
|
which is used as a background (e.g. transparent) color of the entire
|
|
set of frames. For non-transparent images, this value is set to
|
|
clNone after loading the image. It is possible to change this value,
|
|
but this will affect only the Draw method (if TGifDecoder object is
|
|
used in TGif container). DrawTransp and DrawTransparent methods (of
|
|
TGif, too) use BkColor only for non-transparent images, and in case when
|
|
NeedMask is reset to False. }
|
|
property Looping: Boolean read FLooping write FLooping;
|
|
{* True, if loaded image is marked (by its authors) as "looping". }
|
|
property RepeatCount : Word read FRepeatCount write FRepeatCount;
|
|
{* Repeat count set by the author of gif image. }
|
|
property NeedMask : Boolean read FNeedMask write SetNeedMask;
|
|
{* This value is False by default for TGifDecoder instances used stanalone,
|
|
but it is set to True, when TGif is using the owned TGifDecoder object.
|
|
True requires a bit larger code to implement really truth transparency,
|
|
which independs from display resolution and color depth, and works
|
|
correctly even in case when background color of the first frame matches
|
|
non-transparent colors of other ones. }
|
|
property Transparent : Boolean read FTransparent;
|
|
{* True, if loaded gif image is transparent. }
|
|
property Version : TGifVersion read FVersion;
|
|
{* Version of gif. }
|
|
property Frames[ Idx : Integer ] : PGifFrame read GetFrames;
|
|
{* Acess frames as an array of pointers to TGifFrame object instances,
|
|
created while gif image is loading. }
|
|
property Comment : PStrList read GetComment;
|
|
{* Text comment, provided with gif image. }
|
|
function LoadFromStream( Stream : PStream ) : Boolean;
|
|
{* Call this method to load gif image from a stream and decode it. After
|
|
loading, first frame is decoded and ready to be drawn immediately. All
|
|
other frames are decoded when requested first time. Since this, a property
|
|
Corrupted can be not set to True just after loading the image and decoding
|
|
several first frames, and can become True later, when requested frame
|
|
found corrupted while decoding it. }
|
|
function LoadFromFile( const FileName : String ) : Boolean;
|
|
{* Call this method to load gif image from the file and decode it. See
|
|
also LoadFromStream - the most told there is true here too. }
|
|
function LoadFromResourceName( Inst: HInst; RsrcName: PChar ): Boolean;
|
|
{* Call this method to load GIF image from resource by its name.
|
|
GIF image must be stored in RCDATA resource named using unique
|
|
ANSI string. }
|
|
function LoadFromResourceID(Instance: HInst; ResID: Integer): Boolean;
|
|
{* Call this method to load GIF from resource (RCDATA) by ID. }
|
|
property Bitmap : PBitmap read GetBitmap;
|
|
{* Current frame bitmap. }
|
|
property Mask: PBitmap read GetMask;
|
|
{* Current frame truth mask. }
|
|
procedure FreeResources;
|
|
{* Call this method to free the most of GDI resources allocated while
|
|
decoding the image. This forces using DIB bitmaps without handles
|
|
(see TBitmap.Dormant), releases Canvas, Brush objects etc. }
|
|
property Corrupted: Boolean read FCorrupted;
|
|
{* True, if the image found corrupted while decoding. Note, that just after
|
|
loading the image and after decoding several first frames, this property
|
|
can be yet not set even for bad gifs. It becomes True for such gif images
|
|
when a frame requested is corrupted. }
|
|
end;
|
|
|
|
TGifFrame = object( TObj )
|
|
{* Object to manipulate with certain frame data of TGIF object. }
|
|
private
|
|
FOwner : PGifDecoder;
|
|
FBitmap : PBitmap;
|
|
FItem : TGifItem;
|
|
FExtensions : PList;
|
|
FTopLeft : TPoint;
|
|
FInterlaced : Boolean;
|
|
FCorrupted : Boolean;
|
|
FTranspColor : TColor;
|
|
FDelay : Word;
|
|
FLocalColors : Boolean;
|
|
FTransparent : Boolean;
|
|
FTransIndex : Integer;
|
|
FTransMask : PBitmap;
|
|
FFrameIndex : Integer;
|
|
FReallyTransparent : Boolean;
|
|
FDisposalMethod: TDisposeMethod;
|
|
procedure SetDelay(const Value: Word);
|
|
function GetColorCount: Integer;
|
|
function FindComment(ForceCreate: Boolean): PStrList;
|
|
function GetComment: PStrList;
|
|
procedure SetComment(const Value: PStrList);
|
|
procedure SetDisposalMethod(const Value: TDisposeMethod);
|
|
procedure SetTranspColor(const Value: TColor);
|
|
procedure SetTopLeft(const Value: TPoint);
|
|
function GetHeight: Integer;
|
|
function GetWidth: Integer;
|
|
function GetReallyTransparent: Boolean;
|
|
function GetBitmap: PBitmap;
|
|
procedure New_Bitmap;
|
|
protected
|
|
//constructor Create( AOwner : PGifDecoder );
|
|
function LoadFromStream( Stream : PStream ) : Boolean;
|
|
public
|
|
destructor Destroy; virtual;
|
|
{* Do not destroy frames manually. The owner of frames (TGifDecoder) is
|
|
responsible for freeing its frames. }
|
|
property Bitmap : PBitmap read GetBitmap;
|
|
{* Frame bitmap. This can be only a small rectangle in bounds of
|
|
entire GIF image. If You do not know how to combine frame
|
|
bitmaps to produce GIF animation, use TGIF drawing methods
|
|
to perform this task. }
|
|
property Mask : PBitmap read FTransMask;
|
|
{* Exact monochrome mask of transparency. Used in ZGIF drawing
|
|
to produce correct showing of any GIF image independently
|
|
from display resolution. }
|
|
property Delay : Word read FDelay write SetDelay;
|
|
{* Frame delay (delay of frame exposure). }
|
|
property ColorCount : Integer read GetColorCount;
|
|
{* Number of colors. }
|
|
property Comment: PStrList read GetComment write SetComment;
|
|
{* Comment to frame. }
|
|
property DisposalMethod : TDisposeMethod read FDisposalMethod write SetDisposalMethod;
|
|
{* Disposal method. This is the most hard part for recognition
|
|
how to animate certain GIF image. It seems that it is implemented
|
|
in TGIF well, at least, it was tested for about 200 different
|
|
GIF clips, and no errors were found. }
|
|
property Interlaced: Boolean read FInterlaced;
|
|
{* True, if interlaced. }
|
|
property Corrupted: Boolean read FCorrupted;
|
|
{* True, if corrupted. }
|
|
property TranspColor : TColor read FTranspColor write SetTranspColor;
|
|
{* Transparent color. }
|
|
property Origin: TPoint read FTopLeft write SetTopLeft;
|
|
{* Offset of a frame from top left corner of GIF image. }
|
|
property Width: Integer read GetWidth;
|
|
{* Width of frame. }
|
|
property Height: Integer read GetHeight;
|
|
{* Height of frame. }
|
|
property Transparent : Boolean read FTransparent;
|
|
{* True, if frame is transparent. }
|
|
property TransColorIndex : Integer read fTransIndex;
|
|
{* Exact index of transparent color in frame's palette. }
|
|
property ReallyTransparent : Boolean read GetReallyTransparent;
|
|
{* True, if frame is "really" transparent (i.e. its transparent
|
|
color is used in frame at least for one pixel). }
|
|
procedure FreeResources;
|
|
{* }
|
|
procedure Draw( DC : HDC; X, Y : Integer );
|
|
{* }
|
|
procedure StretchDraw( DC : HDC; Rect : TRect );
|
|
{* }
|
|
end;
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
PGif = ^TGif;
|
|
TGif = object( TObj )
|
|
{* GIF decoding and painting object. This object represents almost full
|
|
decoder, which yet not a control but already sufficiently "clever"
|
|
to treat "frame" as a result of all previous frame commands. I.e.
|
|
You do not need to combine frames by yourself to provide animation.
|
|
Just change current frame index (usually increase) and call one of
|
|
drawing methods to paint the desired frame. }
|
|
private
|
|
FGifImage : PGifDecoder;
|
|
FCurFrame : PBitmap;
|
|
FCurMask : PBitmap;
|
|
FCurIndex : Integer;
|
|
FPrevFrame : PBitmap;
|
|
FPrevMask : PBitmap;
|
|
procedure PrepareFrame;
|
|
function GetBkColor: TColor;
|
|
procedure SetBkColor(const Value: TColor);
|
|
function GetFrames(Idx: Integer): PGifFrame;
|
|
function GetTransparent: Boolean;
|
|
function GetCorrupted: Boolean;
|
|
protected
|
|
FOnChanged: TOnEvent;
|
|
function GetWidth : Integer;
|
|
procedure SetWidth( Value : Integer );
|
|
function GetHeight : Integer;
|
|
procedure SetHeight( Value : Integer );
|
|
function GetFrame : Integer;
|
|
procedure SetFrame( Value : Integer );
|
|
function GetFrameCount : Integer;
|
|
function GetDelays( Idx : Integer ) : Integer;
|
|
procedure SetDelays( Idx : Integer; Value : Integer );
|
|
procedure Changed;
|
|
public
|
|
destructor Destroy; virtual;
|
|
{* Destructor. }
|
|
procedure Clear;
|
|
{* Obvious. }
|
|
property Width: Integer read GetWidth write SetWidth;
|
|
{* Width of total GIF image. }
|
|
property Height: Integer read GetHeight write SetHeight;
|
|
{* Height of total GIF image. }
|
|
procedure FreeResources;
|
|
{* Call this method to free GDI resources, allocated for decoding gif image,
|
|
and to drawing it. This does not destroy any image information or data
|
|
already obtained from encoded frames. It is possible to call this method
|
|
after drawing every other frame, but this can slow down drawing process
|
|
a bit. }
|
|
procedure Draw( DC : HDC; X, Y : Integer );
|
|
{* Draws current frame. }
|
|
procedure DrawTransp( DC: HDC; X, Y: Integer );
|
|
{* Draws current frame transparently, using its native TranspColor as
|
|
transparent color if any. If the frame is not transparent, it is
|
|
drawing non-transparently. }
|
|
procedure DrawTransparent( DC : HDC; X, Y : Integer; TranspColor : TColor );
|
|
{* Draws current frame transparently. }
|
|
// By Dufa
|
|
procedure DrawTransparentEx(DC: hDC; X, Y, iWidth, iHeight, SrcX, SrcY: Integer);
|
|
// Draw Tranparent Frame
|
|
procedure StretchDraw( DC : HDC; const Dest : TRect ); //override;
|
|
{* Draws current frame with stretching. }
|
|
procedure StretchDrawTransp( DC: HDC; const Dest: TRect );
|
|
{* Draws current frame stretched and transparently using BkColor as a
|
|
transparent color or using Mask if available. }
|
|
procedure StretchDrawTransparent( DC : HDC; const Dest : TRect; TranspColor : TColor );
|
|
{* Draws current frame with strethcing transparently. }
|
|
property BkColor : TColor read GetBkColor write SetBkColor;
|
|
{* Background color. }
|
|
property Frames[ Idx : Integer ] : PGifFrame read GetFrames;
|
|
{* Array of frame data. }
|
|
property Transparent : Boolean read GetTransparent;
|
|
{* True, if GIF is transparent (i.e. at least one of frames is transparent). }
|
|
function LoadFromStream( Stream : PStream ) : Boolean;
|
|
{* Loads GIF from a stream. }
|
|
function LoadFromFile( const FileName : String ) : Boolean;
|
|
{* Loads GIF from a file. }
|
|
function LoadFromResourceName( Inst: HInst; RsrcName: PChar ): Boolean;
|
|
{* Call this method to load GIF image from resource by its name.
|
|
GIF image must be stored in RCDATA resource named using unique
|
|
ANSI string. }
|
|
function LoadFromResourceID(Instance: HInst; ResID: Integer): Boolean;
|
|
{* Call this method to load GIF from resource (RCDATA) by ID. }
|
|
property Count: Integer read GetFrameCount;
|
|
{* Number of frames in a gif. }
|
|
property Frame: Integer read GetFrame write SetFrame;
|
|
{* Index of current frame. }
|
|
property Delay[ Idx: Integer ]: Integer read GetDelays write SetDelays;
|
|
{* Delay for every frame. }
|
|
property Corrupted: Boolean read GetCorrupted;
|
|
{* True if any of frames decoded is corrupted or could not be decoded. }
|
|
end;
|
|
|
|
function NewGif: PGif;
|
|
{* Call this function to create fully featured gif decoding and painting object.
|
|
This adds about 30K code to the executable. }
|
|
function NewGifNoMask: PGif;
|
|
{* Call this method to create gif decoding object, which does not support for
|
|
truth mask (some animated and / or transparent images are drawn incorrectly,
|
|
but code used is smaller a bit). Actually, this economies only about 1K of code. }
|
|
function NewGifDecoder: PGifDecoder;
|
|
{* Call this method to create simple gif reading object, which just decodes
|
|
separate frames. If only this type of objects is used, smaller portion of
|
|
code is included into final executeable. Actually, this economies about 5-6K
|
|
of executable size. }
|
|
|
|
procedure DrawBitmapMaskMask( DC : HDC; X, Y : Integer; Bmp, Msk : PBitmap );
|
|
procedure DrawBitmapMask( DC : HDC; X, Y : Integer; Bmp, Msk : PBitmap );
|
|
|
|
type
|
|
PGifShow = ^TGifShow;
|
|
TGifShow = object( TControl )
|
|
private
|
|
function GetDummy: Boolean;
|
|
protected
|
|
function GetAnimate: Boolean;
|
|
function GetGif: PGif;
|
|
procedure SetAnimate(const Value: Boolean);
|
|
function GetLoop: Boolean;
|
|
function GetOnEndLoop: TOnEvent;
|
|
procedure SetLoop(const Value: Boolean);
|
|
procedure SetOnEndLoop(const Value: TOnEvent);
|
|
function GetAutosize: Boolean;
|
|
function GetStretch: Boolean;
|
|
procedure SetAutosize(const Value: Boolean);
|
|
procedure SetStretch(const Value: Boolean);
|
|
protected
|
|
procedure GifChanged( Sender: PObj );
|
|
procedure NextFrame( Sender: PObj );
|
|
procedure PaintFrame( Sender: PControl; DC: HDC );
|
|
public
|
|
{$WARNINGS OFF}
|
|
property Autosize: Boolean read GetAutosize write SetAutosize;
|
|
{$WARNINGS ON}
|
|
property Stretch: Boolean read GetStretch write SetStretch;
|
|
property Animate: Boolean read GetAnimate write SetAnimate;
|
|
property Loop: Boolean read GetLoop write SetLoop;
|
|
//property Dormant: Boolean read GetDormant write SetDormant;
|
|
property Gif: PGif read GetGif;
|
|
property OnEndLoop: TOnEvent read GetOnEndLoop write SetOnEndLoop;
|
|
property OnPaint: Boolean read GetDummy;
|
|
function LoadFromStream( Stream : PStream ) : Boolean;
|
|
{* Call this method to load gif image from a stream and decode it. After
|
|
loading, first frame is decoded and ready to be drawn immediately. All
|
|
other frames are decoded when requested first time. Since this, a property
|
|
Corrupted can be not set to True just after loading the image and decoding
|
|
several first frames, and can become True later, when requested frame
|
|
found corrupted while decoding it. }
|
|
function LoadFromFile( const FileName : String ) : Boolean;
|
|
{* Call this method to load gif image from the file and decode it. See
|
|
also LoadFromStream - the most told there is true here too. }
|
|
function LoadFromResourceName( Inst: HInst; RsrcName: PChar ): Boolean;
|
|
{* Call this method to load GIF image from resource by its name.
|
|
GIF image must be stored in RCDATA resource named using unique
|
|
ANSI string. }
|
|
function LoadFromResourceID(Instance: HInst; ResID: Integer): Boolean;
|
|
{* Call this method to load GIF from resource (RCDATA) by ID. }
|
|
end;
|
|
|
|
function NewGifShow( AParent: PControl ): PGifShow;
|
|
|
|
type TKOLGifShow = PGifShow;
|
|
|
|
implementation
|
|
|
|
const
|
|
ROP_DstAndNotSrc = $00220326;
|
|
|
|
function NewGifFrame( AOwner: PGifDecoder ): PGifFrame; forward;
|
|
|
|
procedure DrawBitmapMaskMask( DC : HDC; X, Y : Integer; Bmp, Msk : PBitmap );
|
|
begin
|
|
if Msk = nil then
|
|
Bmp.Draw( DC, X, Y )
|
|
else
|
|
//if Bmp.HandleAllocated then
|
|
begin
|
|
BitBlt( DC, X, Y, Bmp.Width, Bmp.Height, Msk.Canvas.Handle,
|
|
0, 0, SrcAnd );
|
|
{$IFDEF CHK_BITBLT} Chk_BitBlt; {$ENDIF}
|
|
BitBlt( DC, X, Y, Bmp.Width, Bmp.Height,
|
|
Bmp.Canvas.Handle, 0, 0, SRCPAINT );
|
|
{$IFDEF CHK_BITBLT} Chk_BitBlt; {$ENDIF}
|
|
end;
|
|
end;
|
|
procedure DrawBitmapMask( DC : HDC; X, Y : Integer; Bmp, Msk : PBitmap );
|
|
var TmpBmp : PBitmap;
|
|
begin
|
|
if Msk = nil then
|
|
Bmp.Draw( DC, X, Y )
|
|
else
|
|
//if Bmp.HandleAllocated then
|
|
begin
|
|
TmpBmp := NewBitmap( 0, 0 );
|
|
TmpBmp.Assign( Bmp );
|
|
BitBlt( TmpBmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height,
|
|
Msk.Canvas.Handle, 0, 0, ROP_DstAndNotSrc );
|
|
{$IFDEF CHK_BITBLT} Chk_BitBlt; {$ENDIF}
|
|
DrawBitmapMaskMask( DC, X, Y, TmpBmp, Msk );
|
|
//TmpBmp.SaveToFile( GetStartDir + 'DrawBitmapMask.TmpBmp.bmp' );
|
|
//Msk.SaveToFile( GetStartDir + 'DrawBitmapMask.Msk.bmp' );
|
|
TmpBmp.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure StretchBitmapMaskMask( DC : HDC; Rect : TRect; Bmp, Msk : PBitmap );
|
|
var OldMode: Integer;
|
|
OldOrgX: TPoint;
|
|
begin
|
|
OldMode := SetStretchBltMode( DC, HALFTONE );
|
|
SetBrushOrgEx( DC, 0, 0, @ OldOrgX );
|
|
if Msk = nil then
|
|
Bmp.StretchDraw( DC, Rect )
|
|
else
|
|
//if Bmp.HandleAllocated then
|
|
begin
|
|
StretchBlt( DC, Rect.Left, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top,
|
|
Msk.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, SrcAnd );
|
|
StretchBlt( DC, Rect.Left, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top,
|
|
Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, SRCPAINT );
|
|
end;
|
|
SetBrushOrgEx( DC, OldOrgX.x, OldOrgX.y, nil );
|
|
SetStretchBltMode( DC, OldMode );
|
|
end;
|
|
|
|
procedure StretchBitmapMask( DC : HDC; Rect : TRect; Bmp, Msk : PBitmap );
|
|
var TmpBmp : PBitmap;
|
|
OldMode: Integer;
|
|
OldOrgX: TPoint;
|
|
begin
|
|
OldMode := SetStretchBltMode( DC, HALFTONE );
|
|
SetBrushOrgEx( DC, 0, 0, @ OldOrgX );
|
|
if Msk = nil then
|
|
Bmp.StretchDraw( DC, Rect )
|
|
else
|
|
//if Bmp.HandleAllocated then
|
|
begin
|
|
//TmpBmp := NewDIBBitmap( 0, 0, Bmp.PixelFormat );
|
|
TmpBmp := NewBitmap( 0, 0 );
|
|
TmpBmp.Assign( Bmp );
|
|
BitBlt( TmpBmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height,
|
|
Msk.Canvas.Handle, 0, 0, ROP_DstAndNotSrc );
|
|
{$IFDEF CHK_BITBLT} Chk_BitBlt; {$ENDIF}
|
|
StretchBitmapMaskMask( DC, Rect, TmpBmp, Msk );
|
|
TmpBmp.Free;
|
|
end;
|
|
SetBrushOrgEx( DC, OldOrgX.x, OldOrgX.y, nil );
|
|
SetStretchBltMode( DC, OldMode );
|
|
end;
|
|
|
|
{function HugeOffset(HugePtr: Pointer; Amount: Longint): Pointer;
|
|
begin
|
|
Result := PChar(HugePtr) + Amount;
|
|
end;}
|
|
|
|
function AllocMemo(Size: Longint): Pointer;
|
|
begin
|
|
if Size > 0 then
|
|
Result := GlobalAllocPtr(HeapAllocFlags or GMEM_ZEROINIT, Size)
|
|
else Result := nil;
|
|
end;
|
|
|
|
procedure FreeMemo(var fpBlock: Pointer);
|
|
begin
|
|
if fpBlock <> nil then begin
|
|
GlobalFreePtr(fpBlock);
|
|
fpBlock := nil;
|
|
end;
|
|
end;
|
|
|
|
const
|
|
GIFSignature = 'GIF';
|
|
GIFVersionStr: array[TGIFVersion] of PChar = (#0#0#0, '87a', '89a');
|
|
|
|
const
|
|
CODE_TABLE_SIZE = 4096;
|
|
HASH_TABLE_SIZE = 17777;
|
|
MAX_LOOP_COUNT = 30000;
|
|
|
|
CHR_EXT_INTRODUCER = '!';
|
|
CHR_IMAGE_SEPARATOR = ',';
|
|
CHR_TRAILER = ';'; { indicates the end of the GIF Data stream }
|
|
|
|
{ Image descriptor bit masks }
|
|
|
|
ID_LOCAL_COLOR_TABLE = $80; { set if a local color table follows }
|
|
ID_INTERLACED = $40; { set if image is interlaced }
|
|
ID_SORT = $20; { set if color table is sorted }
|
|
ID_RESERVED = $0C; { reserved - must be set to $00 }
|
|
ID_COLOR_TABLE_SIZE = $07; { Size of color table as above }
|
|
|
|
{ Logical screen descriptor packed field masks }
|
|
|
|
LSD_GLOBAL_COLOR_TABLE = $80; { set if global color table follows L.S.D. }
|
|
LSD_COLOR_RESOLUTION = $70; { Color resolution - 3 bits }
|
|
LSD_SORT = $08; { set if global color table is sorted - 1 bit }
|
|
LSD_COLOR_TABLE_SIZE = $07; { Size of global color table - 3 bits }
|
|
{ Actual Size = 2^value+1 - value is 3 bits }
|
|
|
|
{ Graphic control extension packed field masks }
|
|
|
|
GCE_TRANSPARENT = $01; { whether a transparency Index is given }
|
|
GCE_USER_INPUT = $02; { whether or not user input is expected }
|
|
GCE_DISPOSAL_METHOD = $1C; { the way in which the graphic is to be treated after being displayed }
|
|
GCE_RESERVED = $E0; { reserved - must be set to $00 }
|
|
|
|
{ Application extension }
|
|
|
|
AE_LOOPING = $01; { looping Netscape extension }
|
|
|
|
GIFColors: array[TGIFBits] of Word = (2, 4, 8, 16, 32, 64, 128, 256);
|
|
|
|
function ColorsToBits(ColorCount: Word): Byte;
|
|
var
|
|
I: TGIFBits;
|
|
begin
|
|
for I := Low(TGIFBits) to High(TGIFBits) do
|
|
if ColorCount = GIFColors[I] then
|
|
begin
|
|
Result := I;
|
|
Exit;
|
|
end;
|
|
Result := 0;
|
|
end;
|
|
|
|
{function ColorsToPixelFormat(Colors: Word): TPixelFormat;
|
|
begin
|
|
//if Colors <= 2 then Result := pf1bit
|
|
//else if Colors <= 16 then Result := pf4bit
|
|
//else if Colors <= 256 then Result := pf8bit
|
|
//else Result := pf24bit;
|
|
//else
|
|
Result := pf16bit; //&&&
|
|
end;}
|
|
|
|
function ItemToRGB(const Item: TGIFColorItem): Longint;
|
|
begin
|
|
with Item do
|
|
Result := RGB(Red, Green, Blue);
|
|
end;
|
|
|
|
{ The following types and function declarations are used to call into
|
|
functions of the GIF implementation of the GIF image
|
|
compression/decompression standard. }
|
|
|
|
type
|
|
TGIFHeader = packed record
|
|
Signature: array[0..2] of Char; { contains 'GIF' }
|
|
Version: array[0..2] of Char; { '87a' or '89a' }
|
|
end;
|
|
|
|
TScreenDescriptor = packed record
|
|
ScreenWidth: Word; { logical screen width }
|
|
ScreenHeight: Word; { logical screen height }
|
|
PackedFields: Byte;
|
|
BackgroundColorIndex: Byte; { Index to global color table }
|
|
AspectRatio: Byte; { actual ratio = (AspectRatio + 15) / 64 }
|
|
end;
|
|
|
|
TImageDescriptor = packed record
|
|
ImageLeftPos: Word; { column in pixels in respect to left of logical screen }
|
|
ImageTopPos: Word; { row in pixels in respect to top of logical screen }
|
|
ImageWidth: Word; { width of image in pixels }
|
|
ImageHeight: Word; { height of image in pixels }
|
|
PackedFields: Byte;
|
|
end;
|
|
|
|
{ GIF Extensions support }
|
|
|
|
type
|
|
TExtensionType = (etGraphic, etPlainText, etApplication, etComment);
|
|
|
|
const
|
|
ExtLabels: array[TExtensionType] of Byte = ($F9, $01, $FF, $FE);
|
|
LoopExt: string[11] = 'NETSCAPE2.0';
|
|
|
|
type
|
|
TGraphicControlExtension = packed record
|
|
BlockSize: Byte; { should be 4 }
|
|
PackedFields: Byte;
|
|
DelayTime: Word; { in centiseconds }
|
|
TransparentColorIndex: Byte;
|
|
Terminator: Byte;
|
|
end;
|
|
|
|
TPlainTextExtension = packed record
|
|
BlockSize: Byte; { should be 12 }
|
|
Left, Top, Width, Height: Word;
|
|
CellWidth, CellHeight: Byte;
|
|
FGColorIndex, BGColorIndex: Byte;
|
|
end;
|
|
|
|
TAppExtension = packed record
|
|
BlockSize: Byte; { should be 11 }
|
|
AppId: array[1..8] of Byte;
|
|
Authentication: array[1..3] of Byte;
|
|
end;
|
|
|
|
TExtensionRecord = packed record
|
|
case ExtensionType: TExtensionType of
|
|
etGraphic: (GCE: TGraphicControlExtension);
|
|
etPlainText: (PTE: TPlainTextExtension);
|
|
etApplication: (APPE: TAppExtension);
|
|
end;
|
|
|
|
type
|
|
PExtension = ^TExtension;
|
|
TExtension = object( TObj )
|
|
private
|
|
FExtType: TExtensionType;
|
|
FList: PStrList;
|
|
FExtRec: TExtensionRecord;
|
|
public
|
|
function IsLoopExtension: Boolean;
|
|
destructor Destroy; virtual;
|
|
end;
|
|
|
|
destructor TExtension.Destroy;
|
|
begin
|
|
FList.Free;
|
|
inherited;
|
|
end;
|
|
|
|
function TExtension.IsLoopExtension: Boolean;
|
|
begin
|
|
Result := (FExtType = etApplication) and CompareMem(@FExtRec.APPE.AppId,
|
|
@LoopExt[1], FExtRec.APPE.BlockSize) and (FList.Count > 0) and
|
|
(Length(FList.Items[0]) >= 3) and (Byte(FList.Items[0][1]) = AE_LOOPING);
|
|
end;
|
|
|
|
function FindExtension(Extensions: PList; ExtType: TExtensionType): PExtension;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if Extensions <> nil then
|
|
for I := Extensions.Count - 1 downto 0 do begin
|
|
Result := PExtension(Extensions.Items[I]);
|
|
if (Result <> nil) and (Result.FExtType = ExtType) then Exit;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure FreeExtensions(Extensions: PList);
|
|
begin
|
|
if Extensions <> nil then
|
|
begin
|
|
while Extensions.Count > 0 do
|
|
begin
|
|
PObj(Extensions.Items[Extensions.Count - 1]).Free;
|
|
Extensions.Delete(Extensions.Count - 1);
|
|
end;
|
|
Extensions.Free;
|
|
end;
|
|
end;
|
|
|
|
{ GIF read procedures
|
|
|
|
Procedures to read and write GIF files, GIF-decoding and encoding
|
|
based on freeware C source code of GBM package by Andy Key
|
|
(nyangau@interalpha.co.uk). The home page of GBM author is
|
|
at http://www.interalpha.net/customer/nyangau/. }
|
|
|
|
type
|
|
PIntCodeTable = ^TIntCodeTable;
|
|
TIntCodeTable = array[0..CODE_TABLE_SIZE - 1] of Word;
|
|
|
|
PReadContext = ^TReadContext;
|
|
TReadContext = record
|
|
Inx, Size: Longint;
|
|
Buf: array[0..255 + 4] of Byte;
|
|
CodeSize: Longint;
|
|
ReadMask: Longint;
|
|
end;
|
|
|
|
TOutputContext = record
|
|
W, H, X, Y: Longint;
|
|
BitsPerPixel, Pass: Integer;
|
|
Interlace: Boolean;
|
|
LineIdent: Longint;
|
|
Data, CurrLineData: Pointer;
|
|
end;
|
|
|
|
PImageDict = ^TImageDict;
|
|
TImageDict = record
|
|
Tail, Index: Word;
|
|
Col: Byte;
|
|
end;
|
|
|
|
PDictTable = ^TDictTable;
|
|
TDictTable = array[0..CODE_TABLE_SIZE - 1] of TImageDict;
|
|
|
|
PRGBPalette = ^TRGBPalette;
|
|
TRGBPalette = array [Byte] of TRGBQuad;
|
|
|
|
function InterlaceStep(Y, Height: Integer; var Pass: Integer): Integer;
|
|
begin
|
|
Result := Y;
|
|
case Pass of
|
|
0, 1: Inc(Result, 8);
|
|
2: Inc(Result, 4);
|
|
3: Inc(Result, 2);
|
|
end;
|
|
if Result >= Height then begin
|
|
if Pass = 0 then begin
|
|
Pass := 1; Result := 4;
|
|
if (Result < Height) then Exit;
|
|
end;
|
|
if Pass = 1 then begin
|
|
Pass := 2; Result := 2;
|
|
if (Result < Height) then Exit;
|
|
end;
|
|
if Pass = 2 then begin
|
|
Pass := 3; Result := 1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure ReadImageStream(Stream : PStream; Dest: PStream; var Desc: TImageDescriptor;
|
|
var Interlaced, LocalColors, Corrupted: Boolean; var BitsPerPixel: Byte;
|
|
var ColorTable: TGIFColorTable);
|
|
const BufSize = 1024;
|
|
var
|
|
CodeSize, BlockSize: Byte;
|
|
|
|
procedure ProvideDestSize( Size : DWord );
|
|
begin
|
|
if Dest.Size < Size then
|
|
Dest.Size := Size;
|
|
end;
|
|
begin
|
|
Corrupted := False;
|
|
Stream.Read(Desc, SizeOf(TImageDescriptor));
|
|
Interlaced := (Desc.PackedFields and ID_INTERLACED) <> 0;
|
|
if (Desc.PackedFields and ID_LOCAL_COLOR_TABLE) <> 0 then
|
|
begin
|
|
{ Local colors table follows }
|
|
BitsPerPixel := 1 + Desc.PackedFields and ID_COLOR_TABLE_SIZE;
|
|
LocalColors := True;
|
|
ColorTable.ColorCount := 1 shl BitsPerPixel;
|
|
Stream.Read(ColorTable.Colors[0],
|
|
ColorTable.ColorCount * SizeOf(TGIFColorItem));
|
|
end
|
|
else
|
|
begin
|
|
LocalColors := False;
|
|
FillChar(ColorTable, SizeOf(ColorTable), 0);
|
|
end;
|
|
Stream.Read(CodeSize, 1);
|
|
ProvideDestSize( BufSize );
|
|
Dest.Write(CodeSize, 1);
|
|
repeat
|
|
Stream.Read(BlockSize, 1);
|
|
if (Stream.Position + BlockSize) > Stream.Size then
|
|
begin
|
|
Corrupted := True;
|
|
Exit; {!!?}
|
|
end;
|
|
ProvideDestSize( ((Dest.Size + 1 + BlockSize + BufSize - 1) div BufSize) * BufSize );
|
|
Dest.Write(BlockSize, 1);
|
|
if (Stream.Position + BlockSize) > Stream.Size then
|
|
begin
|
|
BlockSize := Stream.Size - Stream.Position;
|
|
Corrupted := True;
|
|
end;
|
|
if BlockSize > 0 then
|
|
Stream2Stream( Dest, Stream, BlockSize );
|
|
until (BlockSize = 0) or (Stream.Position >= Stream.Size);
|
|
end;
|
|
|
|
procedure FillRGBPalette(const ColorTable: TGIFColorTable;
|
|
var Colors: TRGBPalette);
|
|
var
|
|
I: Byte;
|
|
begin
|
|
FillChar(Colors, SizeOf(Colors), $80);
|
|
for I := 0 to ColorTable.ColorCount - 1 do begin
|
|
Colors[I].rgbRed := ColorTable.Colors[I].Red;
|
|
Colors[I].rgbGreen := ColorTable.Colors[I].Green;
|
|
Colors[I].rgbBlue := ColorTable.Colors[I].Blue;
|
|
Colors[I].rgbReserved := 0;
|
|
end;
|
|
end;
|
|
|
|
function ReadCode(Stream: PStream; var Context: TReadContext): Longint;
|
|
var
|
|
RawCode: Longint;
|
|
ByteIndex: Longint;
|
|
Bytes: Byte;
|
|
BytesToLose: Longint;
|
|
begin
|
|
while (Context.Inx + Context.CodeSize > Context.Size) and
|
|
(Stream.Position < Stream.Size) do
|
|
begin
|
|
{ not enough bits in buffer - refill it }
|
|
{ Not very efficient, but infrequently called }
|
|
BytesToLose := Context.Inx shr 3;
|
|
{ Note biggest Code Size is 12 bits. And this can at worst span 3 Bytes }
|
|
Move(Context.Buf[Word(BytesToLose)], Context.Buf[0], 3);
|
|
Context.Inx := Context.Inx and 7;
|
|
Context.Size := Context.Size - (BytesToLose shl 3);
|
|
Stream.Read(Bytes, 1);
|
|
if Bytes > 0 then
|
|
Stream.Read(Context.Buf[Word(Context.Size shr 3)], Bytes);
|
|
Context.Size := Context.Size + (Bytes shl 3);
|
|
end;
|
|
ByteIndex := Context.Inx shr 3;
|
|
RawCode := Context.Buf[Word(ByteIndex)] +
|
|
(Word(Context.Buf[Word(ByteIndex + 1)]) shl 8);
|
|
if Context.CodeSize > 8 then
|
|
RawCode := RawCode + (Longint(Context.Buf[ByteIndex + 2]) shl 16);
|
|
RawCode := RawCode shr (Context.Inx and 7);
|
|
Context.Inx := Context.Inx + Byte(Context.CodeSize);
|
|
Result := RawCode and Context.ReadMask;
|
|
end;
|
|
|
|
procedure Output(Value: Byte; var Context: TOutputContext);
|
|
var
|
|
P: PByte;
|
|
begin
|
|
if (Context.Y >= Context.H) then Exit;
|
|
case Context.BitsPerPixel of
|
|
1: begin
|
|
//P := HugeOffset(Context.CurrLineData, Context.X shr 3);
|
|
P := Pointer( Integer( Context.CurrLineData ) + Context.X shr 3 );
|
|
if (Context.X and $07 <> 0) then
|
|
P^ := P^ or Word(value shl (7 - (Word(Context.X and 7))))
|
|
else P^ := Byte(value shl 7);
|
|
end;
|
|
4: begin
|
|
//P := HugeOffset(Context.CurrLineData, Context.X shr 1);
|
|
P := Pointer( Integer( Context.CurrLineData ) + Context.X shr 1 );
|
|
if (Context.X and 1 <> 0) then P^ := P^ or Value
|
|
else P^ := Byte(value shl 4);
|
|
end;
|
|
8: begin
|
|
//P := HugeOffset(Context.CurrLineData, Context.X);
|
|
P := Pointer( Integer( Context.CurrLineData ) + Context.X );
|
|
P^ := Value;
|
|
end;
|
|
end;
|
|
Inc(Context.X);
|
|
if Context.X < Context.W then Exit;
|
|
Context.X := 0;
|
|
if Context.Interlace then
|
|
Context.Y := InterlaceStep(Context.Y, Context.H, Context.Pass)
|
|
else Inc(Context.Y);
|
|
Context.CurrLineData := //HugeOffset(Context.Data,
|
|
//(Context.H - 1 - Context.Y) * Context.LineIdent);
|
|
Pointer( Integer( Context.Data ) + (Context.H - 1 - Context.Y) * Context.LineIdent );
|
|
end;
|
|
|
|
|
|
procedure ReadGIFData(Stream: PStream; const Header: TBitmapInfoHeader;
|
|
Interlaced: Boolean; IntBitPerPixel: Byte; Data: Pointer;
|
|
var Corrupted: Boolean);
|
|
var
|
|
MinCodeSize: Byte;
|
|
MaxCode, BitMask, InitCodeSize: Longint;
|
|
ClearCode, EndingCode, FirstFreeCode, FreeCode: Word;
|
|
I, OutCount, Code: Longint;
|
|
CurCode, OldCode, InCode, FinalChar: Word;
|
|
Prefix, Suffix, OutCode: PIntCodeTable;
|
|
ReadCtxt: TReadContext;
|
|
OutCtxt: TOutputContext;
|
|
TableFull: Boolean;
|
|
begin
|
|
Corrupted := False;
|
|
OutCount := 0; OldCode := 0; FinalChar := 0;
|
|
TableFull := False;
|
|
Prefix := AllocMem(SizeOf(TIntCodeTable));
|
|
//try
|
|
Suffix := AllocMem(SizeOf(TIntCodeTable));
|
|
//try
|
|
OutCode := AllocMem(SizeOf(TIntCodeTable) + SizeOf(Word));
|
|
//try
|
|
//try
|
|
Stream.Read(MinCodeSize, 1);
|
|
if (MinCodeSize < 2) or (MinCodeSize > 9) then
|
|
begin
|
|
//GifError( 'Bad GIF Code Size' );
|
|
Corrupted := True;
|
|
Exit;
|
|
end;
|
|
{ Initial read context }
|
|
ReadCtxt.Inx := 0;
|
|
ReadCtxt.Size := 0;
|
|
ReadCtxt.CodeSize := MinCodeSize + 1;
|
|
ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1;
|
|
{ Initialise pixel-output context }
|
|
OutCtxt.X := 0; OutCtxt.Y := 0;
|
|
OutCtxt.Pass := 0;
|
|
OutCtxt.W := Header.biWidth;
|
|
OutCtxt.H := Header.biHeight;
|
|
OutCtxt.BitsPerPixel := Header.biBitCount;
|
|
OutCtxt.Interlace := Interlaced;
|
|
OutCtxt.LineIdent := ((Header.biWidth * Header.biBitCount + 31)
|
|
div 32) * 4;
|
|
OutCtxt.Data := Data;
|
|
//OutCtxt.CurrLineData := HugeOffset(Data, (Header.biHeight - 1) *
|
|
// OutCtxt.LineIdent);
|
|
OutCtxt.CurrLineData := Pointer( Integer( Data ) + (Header.biHeight - 1) *
|
|
OutCtxt.LineIdent );
|
|
BitMask := (1 shl IntBitPerPixel) - 1;
|
|
{ 2 ^ MinCodeSize accounts for all colours in file }
|
|
ClearCode := 1 shl MinCodeSize;
|
|
EndingCode := ClearCode + 1;
|
|
FreeCode := ClearCode + 2;
|
|
FirstFreeCode := FreeCode;
|
|
{ 2^ (MinCodeSize + 1) includes clear and eoi Code and space too }
|
|
InitCodeSize := ReadCtxt.CodeSize;
|
|
MaxCode := 1 shl ReadCtxt.CodeSize;
|
|
Code := ReadCode(Stream, ReadCtxt);
|
|
while (Code <> EndingCode) and (Code <> $FFFF) and
|
|
(OutCtxt.Y < OutCtxt.H) do
|
|
begin
|
|
if (Code = ClearCode) then begin
|
|
ReadCtxt.CodeSize := InitCodeSize;
|
|
MaxCode := 1 shl ReadCtxt.CodeSize;
|
|
ReadCtxt.ReadMask := MaxCode - 1;
|
|
FreeCode := FirstFreeCode;
|
|
Code := ReadCode(Stream, ReadCtxt);
|
|
CurCode := Code; OldCode := Code;
|
|
if (Code = $FFFF) then Break;
|
|
FinalChar := (CurCode and BitMask);
|
|
Output(Byte(FinalChar), OutCtxt);
|
|
TableFull := False;
|
|
end
|
|
else
|
|
begin
|
|
CurCode := Code;
|
|
InCode := Code;
|
|
if CurCode >= FreeCode then begin
|
|
CurCode := OldCode;
|
|
OutCode^[OutCount] := FinalChar;
|
|
Inc(OutCount);
|
|
end;
|
|
while (CurCode > BitMask) do
|
|
begin
|
|
if (OutCount > CODE_TABLE_SIZE) then
|
|
begin
|
|
//if LoadCorrupt then
|
|
//begin
|
|
CurCode := BitMask;
|
|
OutCount := 1;
|
|
Corrupted := True;
|
|
Break;
|
|
{end
|
|
else //GifError( 'GIF Decode Error' );
|
|
begin
|
|
Corrupted := True;
|
|
Break;
|
|
end;}
|
|
end;
|
|
OutCode^[OutCount] := Suffix^[CurCode];
|
|
Inc(OutCount);
|
|
CurCode := Prefix^[CurCode];
|
|
end;
|
|
if Corrupted then Break;
|
|
FinalChar := CurCode and BitMask;
|
|
OutCode^[OutCount] := FinalChar;
|
|
Inc(OutCount);
|
|
for I := OutCount - 1 downto 0 do
|
|
Output(Byte(OutCode^[I]), OutCtxt);
|
|
OutCount := 0;
|
|
{ Update dictionary }
|
|
if not TableFull then begin
|
|
Prefix^[FreeCode] := OldCode;
|
|
Suffix^[FreeCode] := FinalChar;
|
|
{ Advance to next free slot }
|
|
Inc(FreeCode);
|
|
if (FreeCode >= MaxCode) then begin
|
|
if (ReadCtxt.CodeSize < 12) then begin
|
|
Inc(ReadCtxt.CodeSize);
|
|
MaxCode := MaxCode shl 1;
|
|
ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1;
|
|
end
|
|
else TableFull := True;
|
|
end;
|
|
end;
|
|
OldCode := InCode;
|
|
end;
|
|
Code := ReadCode(Stream, ReadCtxt);
|
|
end; { while }
|
|
if Code = $FFFF then //GifError('Read GIF Error');
|
|
begin
|
|
Corrupted := True;
|
|
//Break;
|
|
end;
|
|
//finally
|
|
//end;
|
|
//finally
|
|
FreeMem( OutCode {, SizeOf(TIntCodeTable) + SizeOf(Word)} );
|
|
//end;
|
|
//finally
|
|
FreeMem(Suffix {, SizeOf(TIntCodeTable)} );
|
|
//end;
|
|
//finally
|
|
FreeMem(Prefix {, SizeOf(TIntCodeTable)} );
|
|
//end;
|
|
end;
|
|
|
|
{ TGifFrame }
|
|
|
|
function NewGifFrame(AOwner: PGifDecoder): PGifFrame;
|
|
begin
|
|
new( Result, Create );
|
|
Result.FOwner := AOwner;
|
|
Result.FTransIndex := -1;
|
|
Result.FItem.FImageData := NewMemoryStream;
|
|
Result.FTranspColor := clNone;
|
|
end;
|
|
|
|
destructor TGifFrame.Destroy;
|
|
begin
|
|
FBitmap.Free;
|
|
FTransMask.Free;
|
|
FItem.FImageData.Free;
|
|
FreeExtensions( FExtensions );
|
|
inherited;
|
|
end;
|
|
|
|
procedure TGifFrame.Draw(DC : HDC; X, Y: Integer);
|
|
begin
|
|
GetBitmap; // create Mask if it is needed
|
|
|
|
if Mask = nil then
|
|
FBitmap.Draw( DC, X, Y )
|
|
else
|
|
DrawBitmapMask( DC, X, Y, FBitmap, FTransMask );
|
|
end;
|
|
|
|
function TGifFrame.FindComment(ForceCreate: Boolean): PStrList;
|
|
var
|
|
Ext: PExtension;
|
|
begin
|
|
Ext := FindExtension(FExtensions, etComment);
|
|
if (Ext = nil) and ForceCreate then
|
|
begin
|
|
new( Ext, Create );
|
|
Ext.FExtType := etComment;
|
|
if FExtensions = nil then FExtensions := NewList;
|
|
FExtensions.Add(Ext);
|
|
end;
|
|
if (Ext <> nil) then
|
|
begin
|
|
if (Ext.FList = nil) and ForceCreate then
|
|
Ext.FList := NewStrList;
|
|
Result := Ext.FList;
|
|
end
|
|
else Result := nil;
|
|
end;
|
|
|
|
procedure TGifFrame.FreeResources;
|
|
begin
|
|
if FBitmap <> nil then
|
|
FBitmap.Dormant;
|
|
if FTransMask <> nil then
|
|
FTransMask.Dormant;
|
|
end;
|
|
|
|
{procedure SnapStream2File( Strm: PStream; const Fname: String );
|
|
var PP: Integer;
|
|
FS: PStream;
|
|
begin
|
|
PP := Strm.Position;
|
|
Strm.Position := 0;
|
|
FS := NewWriteFileStream( Fname );
|
|
Stream2Stream( FS, Strm, Strm.Size );
|
|
FS.Free;
|
|
Strm.Position := PP;
|
|
end;}
|
|
|
|
function FillMaskLine4( Mask, Scan : PByte; W : Integer; TransIdx : Integer )
|
|
: Boolean;
|
|
assembler;
|
|
asm
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBX
|
|
MOV EDI, EAX
|
|
MOV ESI, EDX
|
|
MOV EDX, TransIdx
|
|
MOV DH, 0
|
|
INC ECX
|
|
SHR ECX, 1
|
|
JZ @@fin
|
|
MOV BX, 8000h
|
|
CLD
|
|
@@loop1:
|
|
LODSB
|
|
MOV AH, AL
|
|
SHR AH, 4
|
|
CMP AH, DL
|
|
JNZ @@1
|
|
OR BL, BH
|
|
MOV DH, BL
|
|
@@1: ROR BH, 1
|
|
AND AL, 0Fh
|
|
CMP AL, DL
|
|
JNZ @@2
|
|
OR BL, BH
|
|
MOV DH, BL
|
|
@@2: ROR BH, 1
|
|
JNC @@e_loop
|
|
MOV [EDI], BL
|
|
INC EDI
|
|
MOV BL, 0
|
|
@@e_loop:
|
|
LOOP @@loop1
|
|
CMP BH, 80h
|
|
JZ @@fin
|
|
MOV [EDI], BL
|
|
|
|
@@fin:
|
|
XOR EAX, EAX
|
|
MOV AL, DH
|
|
POP EBX
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
|
|
{function FillMaskLine8( Mask, Scan : PByte; W : Integer; TransIdx : Integer )
|
|
: Boolean;
|
|
assembler;
|
|
asm
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBX
|
|
MOV EDI, EAX
|
|
MOV ESI, EDX
|
|
MOV EDX, TransIdx
|
|
MOV DH, 0
|
|
JECXZ @@fin
|
|
MOV BX, 8000h
|
|
CLD
|
|
@@loop1:
|
|
LODSB
|
|
CMP AL, DL
|
|
JNZ @@2
|
|
OR BL, BH
|
|
MOV DH, BL
|
|
@@2: ROR BH, 1
|
|
JNC @@e_loop
|
|
MOV [EDI], BL
|
|
INC EDI
|
|
MOV BL, 0
|
|
@@e_loop:
|
|
LOOP @@loop1
|
|
CMP BH, 80h
|
|
JZ @@fin
|
|
MOV [EDI], BL
|
|
|
|
@@fin:
|
|
XOR EAX, EAX
|
|
MOV AL, DH
|
|
POP EBX
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
|
|
function FillMaskLine0( Mask, Scan : PByte; W : Integer )
|
|
: Boolean;
|
|
assembler;
|
|
asm
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV EDI, EAX
|
|
MOV ESI, EDX
|
|
MOV EDX, 0
|
|
ADD ECX, 7
|
|
SHR ECX, 3
|
|
JZ @@fin
|
|
CLD
|
|
@@loop1:
|
|
LODSB
|
|
NOT AL
|
|
STOSB
|
|
OR DL, AL
|
|
LOOP @@loop1
|
|
|
|
@@fin:
|
|
MOV EAX, EDX
|
|
POP EDI
|
|
POP ESI
|
|
end;}
|
|
|
|
function FillMaskLine1( Mask, Scan : PByte; W : Integer )
|
|
: Boolean;
|
|
assembler;
|
|
asm
|
|
PUSH ESI
|
|
PUSH EDI
|
|
MOV EDI, EAX
|
|
MOV ESI, EDX
|
|
MOV EDX, 0
|
|
ADD ECX, 7
|
|
SHR ECX, 3
|
|
JZ @@fin
|
|
CLD
|
|
@@loop1:
|
|
LODSB
|
|
STOSB
|
|
OR DL, AL
|
|
LOOP @@loop1
|
|
|
|
@@fin:
|
|
MOV EAX, EDX
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
|
|
{function FillMaskBitmap(Mask: PBitmap; Width, Height: Integer;
|
|
Bits: PByte; BitsPerPixel, LineWidth, TransIndex: Integer): Boolean;
|
|
var Y : Integer;
|
|
P, S : PByte;
|
|
begin
|
|
Result := False;
|
|
if TransIndex < 0 then Exit;
|
|
P := Mask.ScanLine[ 0 ];
|
|
if P = nil then Exit;
|
|
if BitsPerPixel = 4 then
|
|
for Y := Height - 1 downto 0 do
|
|
begin
|
|
P := Mask.ScanLine[ Y ];
|
|
S := Bits;
|
|
Result := FillMaskLine4( P, S, Width, TransIndex );
|
|
Inc( Bits, LineWidth );
|
|
end;
|
|
if BitsPerPixel = 8 then
|
|
for Y := Height - 1 downto 0 do
|
|
begin
|
|
P := Mask.ScanLine[ Y ];
|
|
S := Bits;
|
|
Result := FillMaskLine8( P, S, Width, TransIndex );
|
|
Inc( Bits, LineWidth );
|
|
end;
|
|
if BitsPerPixel = 1 then
|
|
for Y := Height - 1 downto 0 do
|
|
begin
|
|
P := Mask.ScanLine[ Y ];
|
|
S := Bits;
|
|
if Byte( TransIndex ) = 0 then
|
|
Result := FillMaskLine0( P, S, Width )
|
|
else
|
|
Result := FillMaskLine1( P, S, Width );
|
|
Inc( Bits, LineWidth );
|
|
end;
|
|
end;}
|
|
|
|
procedure ProvideTruthMask( Sender: PObj; var BIH: TBitmapInfoHeader; Bits: Pointer );
|
|
var Frame: PGifFrame;
|
|
begin
|
|
Frame := PGifFrame( Sender );
|
|
if Frame.FTransIndex >= 0 then
|
|
begin
|
|
Frame.FTransMask := NewBitmap( BIH.biWidth, BIH.biHeight );
|
|
Frame.FTransMask.PixelFormat := pf1bit;
|
|
|
|
//Frame.FReallyTransparent :=
|
|
{FillMaskBitmap( Frame.FTransMask, BIH.biWidth, BIH.biHeight, Bits,
|
|
BIH.biBitCount,
|
|
((BIH.biWidth * BIH.biBitCount + 31) div 32) * 4,
|
|
Frame.FTransIndex );}
|
|
end;
|
|
end;
|
|
|
|
function TGifFrame.GetBitmap: PBitmap;
|
|
var Mem : PStream;
|
|
|
|
function ConvertBitsPerPixel: TPixelFormat;
|
|
begin
|
|
case FItem.FBitsPerPixel of
|
|
1: Result := pf1bit;
|
|
2..4: Result := pf4bit;
|
|
5..8: Result := pf8bit;
|
|
else Result := pfDevice;
|
|
end;
|
|
end;
|
|
|
|
procedure SaveToBmpStream;
|
|
var
|
|
HeaderSize: Longword;
|
|
Length: Longword;
|
|
BIH: TBitmapInfoHeader;
|
|
BFH: TBitmapFileHeader;
|
|
Colors: TRGBPalette;
|
|
Bits: Pointer;
|
|
Corrupt: Boolean;
|
|
begin
|
|
with BIH do begin
|
|
biSize := Sizeof(TBitmapInfoHeader);
|
|
biWidth := FItem.FSize.X;
|
|
biHeight := FItem.FSize.Y;
|
|
biPlanes := 1;
|
|
biBitCount := 0;
|
|
case ConvertBitsPerPixel of
|
|
pf1bit: biBitCount := 1;
|
|
pf4bit: biBitCount := 4;
|
|
else biBitCount := 8;
|
|
end;
|
|
biCompression := BI_RGB;
|
|
biSizeImage := (((biWidth * biBitCount + 31) div 32) * 4) * biHeight;
|
|
biXPelsPerMeter := 0;
|
|
biYPelsPerMeter := 0;
|
|
biClrUsed := 0;
|
|
biClrImportant := 0;
|
|
end;
|
|
HeaderSize := SizeOf(TBitmapFileHeader) + SizeOf(TBitmapInfoHeader) +
|
|
SizeOf(TRGBQuad) * (1 shl BIH.biBitCount);
|
|
Length := HeaderSize + BIH.biSizeImage;
|
|
Mem.Size := 0;
|
|
with BFH do begin
|
|
bfType := $4D42; { 'BM' }
|
|
bfSize := Length;
|
|
bfOffBits := HeaderSize;
|
|
end;
|
|
Mem.Write(BFH, SizeOf(TBitmapFileHeader));
|
|
Mem.Write(BIH, SizeOf(TBitmapInfoHeader));
|
|
FillRGBPalette(FItem.FColorMap, Colors);
|
|
Mem.Write(Colors, SizeOf(TRGBQuad) * (1 shl BIH.biBitCount));
|
|
Bits := AllocMemo(BIH.biSizeImage);
|
|
//try
|
|
ZeroMemory(Bits, BIH.biSizeImage);
|
|
FItem.FImageData.Seek( 0, spBegin );
|
|
|
|
ReadGIFData(FItem.FImageData, BIH, FInterlaced,
|
|
FItem.FBitsPerPixel, Bits, Corrupt);
|
|
FTransMask.Free;
|
|
FTransMask := nil;
|
|
|
|
if Assigned( FOwner.FOnNeedMask ) then
|
|
FOwner.FOnNeedMask( @Self, BIH, Bits );
|
|
(*
|
|
if FOwner.NeedMask then
|
|
begin
|
|
if FTransIndex >= 0 then
|
|
begin
|
|
FTransMask := NewBitmap( BIH.biWidth, BIH.biHeight );
|
|
FTransMask.PixelFormat := pf1bit;
|
|
|
|
FReallyTransparent :=
|
|
FillMaskBitmap( FTransMask, BIH.biWidth, BIH.biHeight, Bits,
|
|
BIH.biBitCount,
|
|
((BIH.biWidth * BIH.biBitCount + 31) div 32) * 4,
|
|
FTransIndex );
|
|
{if not ReallyTransparent then
|
|
begin
|
|
FTransMask.Free;
|
|
FTransMask := nil;
|
|
end;}
|
|
end;
|
|
end;
|
|
*)
|
|
FCorrupted := FCorrupted or Corrupt;
|
|
FOwner.FCorrupted := FOwner.FCorrupted or FCorrupted;
|
|
Mem.Write(Bits^, BIH.biSizeImage);
|
|
//finally
|
|
FreeMemo(Bits);
|
|
//end;
|
|
Mem.Seek( 0, spBegin );
|
|
end;
|
|
|
|
begin
|
|
if FBitmap = nil then
|
|
begin
|
|
New_Bitmap;
|
|
Mem := NewMemoryStream;
|
|
SaveToBmpStream;
|
|
|
|
//--SnapStream2File( Mem, GetStartDir + 'loaded_mem.bmp' );
|
|
|
|
FBitmap.LoadFromStream( Mem );
|
|
|
|
//--FBitmap.SaveToFile( GetStartDir + 'loaded.bmp' );
|
|
|
|
{$IFDEF TOPF16BIT}
|
|
FBitmap.PixelFormat := pf16bit; //&&& // ColorsToPixelFormat( 1 shl FItem.FBitsPerPixel );
|
|
{$ELSE}
|
|
FBitmap.PixelFormat := pf24bit;
|
|
{$ENDIF}
|
|
//FBitmap.FreeResources;
|
|
Mem.Free;
|
|
end;
|
|
Result := FBitmap;
|
|
end;
|
|
|
|
function TGifFrame.GetColorCount: Integer;
|
|
begin
|
|
Result := FItem.FColorMap.ColorCount;
|
|
Assert( Result <> 0, 'Unknown color count in gif frame bitmap' );
|
|
{if (Result = 0) and Assigned( FBitmap ) and (FBitmap.Palette <> 0) then
|
|
GetObject( FBitmap.Palette, Sizeof( Integer ), @Result );}
|
|
end;
|
|
|
|
function TGifFrame.GetComment: PStrList;
|
|
begin
|
|
Result := FindComment( True );
|
|
end;
|
|
|
|
function TGifFrame.GetHeight: Integer;
|
|
begin
|
|
if Assigned(FBitmap) or Assigned(FItem.FImageData) then
|
|
Result := Bitmap.Height
|
|
else Result := 0;
|
|
end;
|
|
|
|
function TGifFrame.GetReallyTransparent: Boolean;
|
|
begin
|
|
GetBitmap;
|
|
Result := fReallyTransparent;
|
|
end;
|
|
|
|
function TGifFrame.GetWidth: Integer;
|
|
begin
|
|
if Assigned(FBitmap) or Assigned(FItem.FImageData) then
|
|
Result := Bitmap.Width
|
|
else Result := 0;
|
|
end;
|
|
|
|
function TGifFrame.LoadFromStream(Stream: PStream): Boolean;
|
|
function DoLoadStream : Boolean;
|
|
var
|
|
ImageDesc: TImageDescriptor;
|
|
I, TransIndex: Integer;
|
|
begin
|
|
//Result := False;
|
|
fTransIndex := -1;
|
|
//
|
|
FItem.FImageData.Free;
|
|
FItem.FImageData := NewMemoryStream;
|
|
//
|
|
ReadImageStream(Stream, FItem.FImageData, ImageDesc, FInterlaced,
|
|
FLocalColors, FCorrupted, FItem.FBitsPerPixel, FItem.FColorMap);
|
|
FItem.FImageData.Position := 0;
|
|
with ImageDesc do
|
|
begin
|
|
FTopLeft := MakePoint(ImageLeftPos, ImageTopPos);
|
|
FItem.FSize := MakePoint(ImageWidth, ImageHeight);
|
|
FItem.FPackedFields := PackedFields;
|
|
end;
|
|
if not FLocalColors then
|
|
FItem.FColorMap := FOwner.FGifData.DColorMap;
|
|
FDelay := 0;
|
|
if FExtensions <> nil then
|
|
begin
|
|
for I := 0 to FExtensions.Count - 1 do
|
|
with PExtension(FExtensions.Items[I])^ do
|
|
if FExtType = etGraphic then
|
|
begin
|
|
if (FExtRec.GCE.PackedFields and GCE_TRANSPARENT) <> 0 then
|
|
begin
|
|
TransIndex := FExtRec.GCE.TransparentColorIndex;
|
|
if FItem.FColorMap.ColorCount > TransIndex then
|
|
begin
|
|
fTransIndex := TransIndex;
|
|
FTranspColor := ItemToRGB(FItem.FColorMap.Colors[TransIndex]);
|
|
FTransparent := True;
|
|
end;
|
|
end
|
|
else
|
|
FTranspColor := clNone;
|
|
FDelay := Max(FExtRec.GCE.DelayTime * 10, FDelay);
|
|
FDisposalMethod := TDisposeMethod((FExtRec.GCE.PackedFields and
|
|
GCE_DISPOSAL_METHOD) shr 2);
|
|
end;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
begin
|
|
Result := DoLoadStream;
|
|
if not Result then
|
|
begin
|
|
FItem.FImageData.Free;
|
|
FItem.FImageData := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TGifFrame.New_Bitmap;
|
|
begin
|
|
FBitmap.Free;
|
|
FBitmap := NewBitmap( 0, 0 );
|
|
end;
|
|
|
|
procedure TGifFrame.SetComment(const Value: PStrList);
|
|
begin
|
|
GetComment.Assign( Value );
|
|
end;
|
|
|
|
procedure TGifFrame.SetDelay(const Value: Word);
|
|
begin
|
|
if FDelay = Value then Exit;
|
|
//FOwner.Changing;
|
|
FDelay := Value;
|
|
if FDelay > 0 then
|
|
FOwner.FVersion := gv89a;
|
|
//FOwner.Changed;
|
|
end;
|
|
|
|
procedure TGifFrame.SetDisposalMethod(const Value: TDisposeMethod);
|
|
begin
|
|
if FDisposalMethod = Value then Exit;
|
|
//FOwner.Changing;
|
|
FDisposalMethod := Value;
|
|
if Value <> dmUndefined then
|
|
FOwner.FVersion := gv89a;
|
|
//FOwner.Changed;
|
|
end;
|
|
|
|
procedure TGifFrame.SetTopLeft(const Value: TPoint);
|
|
begin
|
|
if (FTopLeft.X = Value.X) and (FTopLeft.Y = Value.Y) then Exit;
|
|
//FOwner.Changing;
|
|
FTopLeft := Value;
|
|
FOwner.FGifWidth := Max(FOwner.FGifWidth,
|
|
FItem.FSize.X + FTopLeft.X);
|
|
FOwner.FGifHeight := Max(FOwner.FGifHeight,
|
|
FItem.FSize.Y + FTopLeft.Y);
|
|
//FOwner.Changed;
|
|
end;
|
|
|
|
procedure TGifFrame.SetTranspColor(const Value: TColor);
|
|
begin
|
|
if FTranspColor = Value then Exit;
|
|
//FOwner.Changing;
|
|
if Value <> clNone then
|
|
FOwner.FVersion := gv89a;
|
|
FTranspColor := Value;
|
|
//FOwner.Changed;
|
|
end;
|
|
|
|
procedure TGifFrame.StretchDraw(DC: HDC; Rect: TRect);
|
|
var OldMode: Integer;
|
|
OldOrgX: TPoint;
|
|
begin
|
|
GetBitmap; // need to create Mask if it is needed
|
|
if Mask = nil then
|
|
begin
|
|
OldMode := SetStretchBltMode( DC, HALFTONE );
|
|
SetBrushOrgEx( DC, 0, 0, @ OldOrgX );
|
|
Bitmap.StretchDraw( DC, Rect );
|
|
SetBrushOrgEx( DC, OldOrgX.x, OldOrgX.y, nil );
|
|
SetStretchBltMode( DC, OldMode );
|
|
end
|
|
else
|
|
begin
|
|
StretchBitmapMask( DC, Rect, Bitmap, Mask );
|
|
end;
|
|
end;
|
|
|
|
{ TGifDecoder }
|
|
|
|
procedure TGifDecoder.Clear;
|
|
begin
|
|
FGifData.DComment.Free;
|
|
FGifData.DComment := nil;
|
|
ClearItems;
|
|
FGifWidth := 0;
|
|
FGifHeight := 0;
|
|
FCorrupted := FALSE;
|
|
end;
|
|
|
|
procedure TGifDecoder.ClearItems;
|
|
var I: Integer;
|
|
begin
|
|
if FItems <> nil then
|
|
begin
|
|
for I := 0 to FItems.Count-1 do
|
|
PObj(FItems.Items[I]).Free;
|
|
FItems.Clear;
|
|
end;
|
|
end;
|
|
|
|
function NewGifDecoder: PGifDecoder;
|
|
begin
|
|
new( Result, Create );
|
|
Result.NewImage;
|
|
Result.FTransparent := True;
|
|
Result.FBkColor := clNone;
|
|
end;
|
|
|
|
destructor TGifDecoder.Destroy;
|
|
begin
|
|
Clear;
|
|
FItems.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TGifDecoder.FreeResources;
|
|
var I : Integer;
|
|
begin
|
|
if FItems <> nil then
|
|
for I := 0 to FItems.Count - 1 do
|
|
PGifFrame( FItems.Items[ I ] ).FreeResources;
|
|
end;
|
|
|
|
function TGifDecoder.GetBitmap: PBitmap;
|
|
begin
|
|
if (FItems.Count > 0) then begin
|
|
if (FFrameIndex >= 0) and (FFrameIndex < FItems.Count) then
|
|
Result := PGIFFrame(FItems.Items[FFrameIndex]).Bitmap
|
|
else Result := PGIFFrame(FItems.Items[0]).Bitmap
|
|
end
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TGifDecoder.GetComment: PStrList;
|
|
begin
|
|
Result := FGifData.DComment;
|
|
end;
|
|
|
|
function TGifDecoder.GetFrame: Integer;
|
|
begin
|
|
Result := FFrameIndex;
|
|
end;
|
|
|
|
function TGifDecoder.GetFrameCount: Integer;
|
|
begin
|
|
Result := 0;
|
|
if FItems <> nil then
|
|
Result := FItems.Count;
|
|
end;
|
|
|
|
function TGifDecoder.GetFrames(Idx: Integer): PGifFrame;
|
|
begin
|
|
Result := nil;
|
|
if Idx >= 0 then
|
|
Result := PGifFrame( FItems.Items[ Idx ] );
|
|
end;
|
|
|
|
function TGifDecoder.GetHeight: Integer;
|
|
begin
|
|
Result := FGifHeight;
|
|
end;
|
|
|
|
function TGifDecoder.GetMask: PBitmap;
|
|
begin
|
|
if (FItems.Count > 0) then begin
|
|
if (FFrameIndex >= 0) and (FFrameIndex < FItems.Count) then
|
|
Result := PGIFFrame(FItems.Items[FFrameIndex]).Mask
|
|
else Result := PGIFFrame(FItems.Items[0]).Mask
|
|
end
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TGifDecoder.GetWidth: Integer;
|
|
begin
|
|
Result := FGifWidth;
|
|
end;
|
|
|
|
function TGifDecoder.LoadFromFile(const FileName: String): Boolean;
|
|
var Strm : PStream;
|
|
begin
|
|
Strm := NewReadFileStream( FileName {, ofOpenRead or ofOpenExisting or ofShareDenyWrite} );
|
|
Result := LoadFromStream( Strm );
|
|
Strm.Free;
|
|
end;
|
|
|
|
function TGifDecoder.LoadFromResourceID(Instance: HInst;
|
|
ResID: Integer): Boolean;
|
|
var Strm: PStream;
|
|
begin
|
|
Strm := NewMemoryStream;
|
|
Resource2Stream( Strm, Instance, PChar( ResID ), RT_RCDATA );
|
|
Strm.Position := 0;
|
|
Result := LoadFromStream( Strm );
|
|
Strm.Free;
|
|
end;
|
|
|
|
function TGifDecoder.LoadFromResourceName(Inst: HInst;
|
|
RsrcName: PChar): Boolean;
|
|
var Strm: PStream;
|
|
begin
|
|
Strm := NewMemoryStream;
|
|
Resource2Stream( Strm, Inst, PChar( RsrcName ), RT_RCDATA );
|
|
Strm.Position := 0;
|
|
Result := LoadFromStream( Strm );
|
|
Strm.Free;
|
|
end;
|
|
|
|
function TGifDecoder.LoadFromStream(Stream: PStream): Boolean;
|
|
var
|
|
SeparatorChar: Char;
|
|
NewItem: PGIFFrame;
|
|
Extensions: PList;
|
|
ScreenDesc: TScreenDescriptor;
|
|
Data: PStream;
|
|
|
|
function ReadSignature(Stream: PStream) : Boolean;
|
|
var
|
|
I: TGIFVersion;
|
|
S: array[ 0..3 ] of Char;
|
|
begin
|
|
Result := False;
|
|
FVersion := gvUnknown;
|
|
S[ 3 ] := #0;
|
|
Stream.Read(S[0], 3);
|
|
//if CompareText(GIFSignature, S) <> 0 then
|
|
if GIFSignature <> S then
|
|
//GifError( 'Incorrect GIF Version' );
|
|
Exit;
|
|
Stream.Read(S[0], 3);
|
|
for I := Low(TGIFVersion) to High(TGIFVersion) do
|
|
//if CompareText(S, StrPas(GIFVersionStr[I])) = 0 then
|
|
if CompareMem( @S[ 0 ], PChar( GifVersionStr[ I ] ), 4 ) then
|
|
begin
|
|
FVersion := I;
|
|
Break;
|
|
end;
|
|
if FVersion = gvUnknown then
|
|
//GifError('Unknown GIF Version');
|
|
Exit;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure ReadScreenDescriptor(Stream: PStream);
|
|
begin
|
|
Stream.Read(ScreenDesc, SizeOf(ScreenDesc));
|
|
FGifWidth := ScreenDesc.ScreenWidth;
|
|
FGifHeight := ScreenDesc.ScreenHeight;
|
|
FGifData.DAspectRatio := ScreenDesc.AspectRatio;
|
|
FGifData.DBitsPerPixel := 1 + (ScreenDesc.PackedFields and
|
|
LSD_COLOR_TABLE_SIZE);
|
|
FGifData.DColorResBits := 1 + (ScreenDesc.PackedFields and
|
|
LSD_COLOR_RESOLUTION) shr 4;
|
|
end;
|
|
|
|
procedure ReadGlobalColorMap(Stream: PStream);
|
|
begin
|
|
if (ScreenDesc.PackedFields and LSD_GLOBAL_COLOR_TABLE) <> 0 then
|
|
begin
|
|
FGifData.DColorMap.ColorCount := 1 shl FGifData.DBitsPerPixel;
|
|
Stream.Read( FGifData.DColorMap.Colors[0],
|
|
FGifData.DColorMap.ColorCount * SizeOf(TGIFColorItem) );
|
|
{if FGifData.DColorMap.ColorCount > ScreenDesc.BackgroundColorIndex then
|
|
begin
|
|
fBackIndex := ScreenDesc.BackgroundColorIndex;
|
|
FBkColor := ItemToRGB( FGifData.DColorMap.Colors[ fBackIndex ] );
|
|
end;}
|
|
fBackIndex := ScreenDesc.BackgroundColorIndex;
|
|
if fBackIndex >= FGifData.DColorMap.ColorCount then
|
|
fBackIndex := 0;
|
|
FBkColor := ItemToRGB( FGifData.DColorMap.Colors[fBackIndex] );
|
|
end;
|
|
end;
|
|
|
|
function ReadDataBlock(Stream: PStream): PStrList;
|
|
var
|
|
BlockSize: Byte;
|
|
S: string;
|
|
begin
|
|
Result := NewStrlist;
|
|
//try
|
|
repeat
|
|
Stream.Read(BlockSize, SizeOf(Byte));
|
|
if BlockSize <> 0 then begin
|
|
SetLength(S, BlockSize);
|
|
Stream.Read(S[1], BlockSize);
|
|
Result.Add(S);
|
|
end;
|
|
until (BlockSize = 0) or (Stream.Position >= Stream.Size);
|
|
//except
|
|
{
|
|
Result.Free;
|
|
raise;
|
|
}
|
|
//end;
|
|
end;
|
|
|
|
function ReadExtension(Stream: PStream): PExtension;
|
|
var
|
|
ExtensionLabel: Byte;
|
|
begin
|
|
//Result := TExtension.Create;
|
|
new( Result, Create );
|
|
//try
|
|
Stream.Read(ExtensionLabel, SizeOf(Byte));
|
|
if ExtensionLabel = ExtLabels[etGraphic] then
|
|
begin
|
|
{ graphic control extension }
|
|
Result.FExtType := etGraphic;
|
|
Stream.Read(Result.FExtRec.GCE, SizeOf(TGraphicControlExtension));
|
|
end
|
|
else
|
|
if ExtensionLabel = ExtLabels[etComment] then
|
|
begin
|
|
{ comment extension }
|
|
Result.FExtType := etComment;
|
|
Result.FList := ReadDataBlock(Stream);
|
|
end
|
|
else
|
|
if ExtensionLabel = ExtLabels[etPlainText] then
|
|
begin
|
|
{ plain text extension }
|
|
Result.FExtType := etPlainText;
|
|
Stream.Read(Result.FExtRec.PTE, SizeOf(TPlainTextExtension));
|
|
Result.FList := ReadDataBlock(Stream);
|
|
end
|
|
else
|
|
if ExtensionLabel = ExtLabels[etApplication] then
|
|
begin
|
|
{ application extension }
|
|
Result.FExtType := etApplication;
|
|
Stream.Read(Result.FExtRec.APPE, SizeOf(TAppExtension));
|
|
Result.FList := ReadDataBlock(Stream);
|
|
end
|
|
else
|
|
begin
|
|
//GifError('Unrecognized GIF Extention ' + IntToStr( ExtensionLabel ) );
|
|
//...
|
|
Result.Free;
|
|
Result := nil;
|
|
end;
|
|
//except
|
|
{
|
|
Result.Free;
|
|
raise;
|
|
}
|
|
//end;
|
|
end;
|
|
|
|
function ReadExtensionBlock(Stream: PStream; var SeparatorChar: Char): PList;
|
|
var
|
|
NewExt: PExtension;
|
|
begin
|
|
Result := nil;
|
|
//try
|
|
while SeparatorChar = CHR_EXT_INTRODUCER do
|
|
begin
|
|
NewExt := ReadExtension(Stream);
|
|
if (NewExt.FExtType = etPlainText) then
|
|
begin
|
|
{ plain text data blocks are not supported,
|
|
clear all previous readed extensions }
|
|
FreeExtensions(Result);
|
|
Result := nil;
|
|
end;
|
|
if (NewExt.FExtType in [etPlainText, etApplication]) then
|
|
begin
|
|
{ check for loop extension }
|
|
if NewExt.IsLoopExtension then
|
|
begin
|
|
FLooping := True;
|
|
FRepeatCount := Min( PWord( @NewExt.FList.Items[0][2] )^,
|
|
//MakeWord(Byte(NewExt.FList.Items[0][2]),
|
|
//Byte(NewExt.FList.Items[0][3])),
|
|
MAX_LOOP_COUNT);
|
|
end;
|
|
{ not supported yet, must be ignored }
|
|
NewExt.Free;
|
|
end
|
|
else
|
|
begin
|
|
if Result = nil then
|
|
Result := NewList;
|
|
Result.Add(NewExt);
|
|
end;
|
|
if Stream.Size > Stream.Position then
|
|
Stream.Read(SeparatorChar, SizeOf(Byte))
|
|
else
|
|
SeparatorChar := CHR_TRAILER;
|
|
end;
|
|
if (Result <> nil) and (Result.Count = 0) then
|
|
begin
|
|
Result.Free;
|
|
Result := nil;
|
|
end;
|
|
//except
|
|
{
|
|
if Result <> nil then Result.Free;
|
|
raise;
|
|
}
|
|
//end;
|
|
end;
|
|
|
|
function DoLoadStream : Boolean;
|
|
var
|
|
Size : Integer;
|
|
I, OldPos: Integer;
|
|
Ext: PExtension;
|
|
Idx : Integer;
|
|
begin
|
|
Size := Stream.Size - Stream.Position;
|
|
Result := False;
|
|
//Changing;
|
|
NewImage;
|
|
Idx := 0;
|
|
Data := NewMemoryStream;
|
|
//try
|
|
Data.Size := Size;
|
|
Stream.Read(Data.Memory^, Size);
|
|
if Size > 0 then
|
|
begin
|
|
Data.Seek( 0, spBegin );
|
|
if not ReadSignature(Data) then Exit;
|
|
ReadScreenDescriptor(Data);
|
|
ReadGlobalColorMap(Data);
|
|
Data.Read(SeparatorChar, SizeOf(Byte));
|
|
OldPos := -1;
|
|
while not (SeparatorChar in [CHR_TRAILER, #0]) and not
|
|
(Data.Position >= Data.Size) and (DWORD(OldPos) <> Data.Position) do
|
|
begin
|
|
OldPos := Data.Position;
|
|
Extensions := ReadExtensionBlock(Data, SeparatorChar);
|
|
if SeparatorChar = CHR_IMAGE_SEPARATOR then
|
|
begin
|
|
//try
|
|
NewItem := NewGIFFrame(@Self);
|
|
NewItem.fFrameIndex := Idx;
|
|
Inc( Idx );
|
|
//try
|
|
if FGifData.DColorMap.ColorCount > 0 then
|
|
NewItem.FItem.FBitsPerPixel :=
|
|
ColorsToBits(FGifData.DColorMap.ColorCount);
|
|
NewItem.FExtensions := Extensions;
|
|
Extensions := nil;
|
|
if not NewItem.LoadFromStream(Data) then
|
|
begin
|
|
NewItem.Free;
|
|
Exit;
|
|
end;
|
|
FItems.Add(NewItem);
|
|
//except
|
|
{
|
|
NewItem.Free;
|
|
raise;
|
|
}
|
|
//end;
|
|
if not (Data.Position >= Data.Size) then
|
|
begin
|
|
Data.Read(SeparatorChar, SizeOf(Byte));
|
|
while (SeparatorChar = #0) and (Data.Position < Data.Size) do
|
|
Data.Read(SeparatorChar, SizeOf(Byte));
|
|
end
|
|
else
|
|
SeparatorChar := CHR_TRAILER;
|
|
if not (SeparatorChar in [CHR_EXT_INTRODUCER,
|
|
CHR_IMAGE_SEPARATOR, CHR_TRAILER]) then
|
|
begin
|
|
SeparatorChar := #0;
|
|
{GifError(LoadStr(SGIFDecodeError));}
|
|
//Corrupted := TRUE;
|
|
//break;
|
|
end;
|
|
//except
|
|
{
|
|
FreeExtensions(Extensions);
|
|
raise;
|
|
}
|
|
//end
|
|
end
|
|
else
|
|
if (FGifData.DComment.Count = 0) and (Extensions <> nil) then
|
|
begin
|
|
//try
|
|
{ trailig extensions }
|
|
for I := 0 to Extensions.Count - 1 do
|
|
begin
|
|
Ext := Extensions.Items[I];
|
|
if (Ext <> nil) and (Ext.FExtType = etComment) then
|
|
begin
|
|
if FGifData.DComment.Count > 0 then
|
|
FGifData.DComment.Add(#13#10#13#10);
|
|
FGifData.DComment.AddStrings(Ext.FList);
|
|
end;
|
|
end;
|
|
//finally
|
|
FreeExtensions(Extensions);
|
|
//end;
|
|
end
|
|
else
|
|
if not (SeparatorChar in [CHR_TRAILER, #0]) then
|
|
begin
|
|
//GifError('GIF Read Error');
|
|
//...
|
|
FreeExtensions(Extensions);
|
|
FCorrupted := TRUE;
|
|
end;
|
|
end;
|
|
end;
|
|
//finally
|
|
//Data.Free;
|
|
//end;
|
|
if Count > 0 then
|
|
begin
|
|
FFrameIndex := 0;
|
|
//if ForceDecode then
|
|
//try
|
|
GetBitmap; { force bitmap creation }
|
|
FTransparent := Frames[ 0 ].FTransparent;
|
|
//except
|
|
{
|
|
Frames[0].Free;
|
|
FItems.Delete(0);
|
|
raise;
|
|
}
|
|
//end;
|
|
end;
|
|
//Changed;
|
|
//if not Corrupted then
|
|
Result := True;
|
|
end;
|
|
|
|
begin
|
|
Clear;
|
|
Result := DoLoadStream;
|
|
Data.Free;
|
|
if not Result then Clear;
|
|
end;
|
|
|
|
procedure TGifDecoder.NewImage;
|
|
begin
|
|
FGifData.DComment.Free;
|
|
FGifData.DComment := NewStrList;
|
|
|
|
if FItems = nil then FItems := NewList;
|
|
ClearItems;
|
|
FFrameIndex := -1;
|
|
FBkColor := clNone;
|
|
FRepeatCount := 1;
|
|
FLooping := False;
|
|
FVersion := gvUnknown;
|
|
end;
|
|
|
|
procedure TGifDecoder.SetFrame(Value: Integer);
|
|
begin
|
|
If FFrameIndex = Value Then Exit;
|
|
//Changing;
|
|
FFrameIndex:= Value;
|
|
If (FFrameIndex >= FItems.Count) Or (FFrameIndex < 0) Then FFrameIndex:= 0;
|
|
//Changed;
|
|
end;
|
|
|
|
procedure TGifDecoder.SetNeedMask(const Value: Boolean);
|
|
begin
|
|
FNeedMask := Value;
|
|
if Value then
|
|
FOnNeedMask := ProvideTruthMask
|
|
else
|
|
FOnNeedMask := nil;
|
|
end;
|
|
|
|
{ TGif }
|
|
|
|
function NewGifNoMask: PGif;
|
|
begin
|
|
new( Result, Create );
|
|
Result.FGifImage := NewGifDecoder;
|
|
end;
|
|
|
|
function NewGif: PGif;
|
|
begin
|
|
Result := NewGifNoMask;
|
|
Result.FGifImage.NeedMask := TRUE;
|
|
end;
|
|
|
|
procedure TGif.Clear;
|
|
begin
|
|
FGifImage.Clear;
|
|
FCurIndex := -1;
|
|
FCurFrame.Free; FCurFrame := nil;
|
|
FCurMask.Free; FCurMask := nil;
|
|
FPrevFrame.Free; FPrevFrame := nil;
|
|
FPrevMask.Free; FPrevMask := nil;
|
|
Changed;
|
|
end;
|
|
|
|
destructor TGif.Destroy;
|
|
begin
|
|
//OnChanging := nil;
|
|
//OnChanged := nil;
|
|
Clear;
|
|
FGifImage.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TGif.Draw(DC: HDC; X, Y: Integer);
|
|
begin
|
|
if Count = 0 then Exit;
|
|
PrepareFrame;
|
|
FCurFrame.Draw( DC, X, Y );
|
|
end;
|
|
|
|
procedure TGif.DrawTransp(DC: HDC; X, Y: Integer);
|
|
begin
|
|
If Count > 0 Then DrawTransparent( DC, X, Y, Frames[ Frame ].TranspColor );
|
|
end;
|
|
|
|
procedure MyDraw(DC: hDC; X, Y, iWidth, iHeight, SrcX, SrcY: Integer; Bmp, Msk: PBitmap);
|
|
begin
|
|
BitBlt(Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, Msk.Canvas.Handle, 0, 0, ROP_DstAndNotSrc);
|
|
BitBlt(DC, X, Y, iWidth, iHeight, Msk.Canvas.Handle, SrcX, SrcY, SrcAnd);
|
|
BitBlt(DC, X, Y, iWidth, iHeight, Bmp.Canvas.Handle, SrcX, SrcY, SrcPaint);
|
|
end;
|
|
procedure TGif.DrawTransparentEx(DC: hDC; X, Y, iWidth, iHeight, SrcX, SrcY: Integer);
|
|
begin
|
|
If Count = 0 then Exit;
|
|
PrepareFrame;
|
|
If FCurMask = Nil Then
|
|
BitBlt(DC, X, Y, iWidth, iHeight, FCurFrame.Canvas.Handle, SrcX, SrcY, SrcCopy)
|
|
Else
|
|
MyDraw(DC, X, Y, iWidth, iHeight, SrcX, SrcY, FCurFrame, FCurMask);
|
|
end;
|
|
|
|
procedure TGif.DrawTransparent(DC: HDC; X, Y: Integer; TranspColor: TColor);
|
|
begin
|
|
if Count = 0 then Exit;
|
|
PrepareFrame;
|
|
//-----------------------------------------------------------------------
|
|
{if FCurMask <> nil then
|
|
FCurMask.SaveToFile( GetStartDir + 'TGif.DrawTransparent.FCurMask.bmp' )
|
|
else
|
|
DeleteFile( PChar( GetStartDir + 'TGif.DrawTransparent.FCurMask.bmp' ) );
|
|
if FCurFrame <> nil then
|
|
FCurFrame.SaveToFile( GetStartDir + 'TGif.DrawTransparent.FCurFrame.bmp' )
|
|
else
|
|
DeleteFile( PChar( GetStartDir + 'TGif.DrawTransparent.FCurFrame.bmp' ) );}
|
|
//-------------------------------------------------------------------------
|
|
if FCurMask = Nil Then
|
|
FCurFrame.Draw( DC, X, Y )
|
|
Else
|
|
DrawBitmapMask( DC, X, Y, FCurFrame, FCurMask );
|
|
end;
|
|
|
|
procedure TGif.FreeResources;
|
|
begin
|
|
FGifImage.FreeResources;
|
|
if FCurFrame <> nil then
|
|
FCurFrame.Dormant;
|
|
if FCurMask <> nil then
|
|
FCurMask.Dormant;
|
|
if FPrevFrame <> nil then
|
|
FPrevFrame.Dormant;
|
|
if FPrevMask <> nil then
|
|
FPrevMask.Dormant;
|
|
end;
|
|
|
|
function TGif.GetHeight: Integer;
|
|
begin
|
|
Result := FGifImage.Height;
|
|
end;
|
|
|
|
function TGif.GetWidth: Integer;
|
|
begin
|
|
Result := FGifImage.Width;
|
|
end;
|
|
|
|
procedure TGif.StretchDrawTransp(DC: HDC; const Dest: TRect);
|
|
begin
|
|
if Count = 0 then Exit;
|
|
StretchDrawTransparent( DC, Dest, BkColor );
|
|
end;
|
|
|
|
procedure TGif.StretchDraw(DC: HDC; const Dest: TRect);
|
|
var OldMode: Integer; OldOrgX: TPoint;
|
|
begin
|
|
if Count = 0 then Exit;
|
|
PrepareFrame;
|
|
OldMode := SetStretchBltMode( DC, HALFTONE );
|
|
SetBrushOrgEx( DC, 0, 0, @ OldOrgX );
|
|
FCurFrame.StretchDraw( DC, Dest );
|
|
SetBrushOrgEx( DC, OldOrgX.x, OldOrgX.y, nil );
|
|
SetStretchBltMode( DC, OldMode );
|
|
end;
|
|
|
|
procedure TGif.StretchDrawTransparent(DC: HDC; const Dest: TRect; TranspColor: TColor);
|
|
begin
|
|
if Count = 0 then Exit;
|
|
PrepareFrame;
|
|
if FCurMask = nil then
|
|
FCurFrame.StretchDraw( DC, Dest )
|
|
else
|
|
StretchBitmapMask( DC, Dest, FCurFrame, FCurMask );
|
|
end;
|
|
|
|
procedure TGif.PrepareFrame;
|
|
var DM : TDisposeMethod; I : Integer;
|
|
procedure DrawCurFrameMask;
|
|
var F: PGifFrame;
|
|
begin
|
|
F := Frames[ FCurIndex ];
|
|
F.GetBitmap;
|
|
//if F.ReallyTransparent then
|
|
if F.Mask <> nil then
|
|
begin
|
|
BitBlt( FCurMask.Canvas.Handle,
|
|
F.Origin.x,
|
|
F.Origin.y,
|
|
F.Origin.X + F.Width,
|
|
F.Origin.Y + F.Height,
|
|
F.Mask.Canvas.Handle, 0, 0, SRCAND );
|
|
{$IFDEF CHK_BITBLT} Chk_BitBlt; {$ENDIF}
|
|
end
|
|
else
|
|
begin
|
|
//^^^FCurMask.Canvas.Brush.Color := clBlack;
|
|
FCurMask.BkColor := clBlack;
|
|
FCurMask.Canvas.FillRect( MakeRect( F.Origin.x, F.Origin.y,
|
|
F.Origin.x + F.Width, F.Origin.y + F.Height ) );
|
|
end;
|
|
end;
|
|
procedure Prepare0;
|
|
var Frame0: PGifFrame;
|
|
begin
|
|
FCurIndex := 0;
|
|
Frame0 := Frames[ 0 ];
|
|
FCurFrame.PixelFormat := Frame0.Bitmap.PixelFormat;
|
|
//^^^FCurFrame.Canvas.Brush.Color := BkColor;
|
|
FCurFrame.BkColor := BkColor;
|
|
FCurFrame.Canvas.FillRect( MakeRect( 0, 0, Width, Height ) );
|
|
if FCurMask <> nil then
|
|
begin
|
|
//^^^FCurMask.Canvas.Brush.Color := clWhite;
|
|
FCurMask.BkColor := clWhite;
|
|
FCurMask.Canvas.FillRect( MakeRect( 0, 0, Width, Height ) );
|
|
end;
|
|
Frame0.Draw( FCurFrame.Canvas.Handle, Frame0.Origin.x, Frame0.Origin.y );
|
|
|
|
//FCurFrame.SaveToFile( GetStartDir + '0_Frame.bmp' );
|
|
|
|
if FCurMask <> nil then
|
|
begin
|
|
|
|
DrawCurFrameMask;
|
|
|
|
//FCurMask.SaveToFile( GetStartDir + '0_Mask.bmp' );
|
|
end;
|
|
end;
|
|
var F: PGifFrame;
|
|
begin
|
|
if Count = 0 then Exit;
|
|
if FCurFrame = nil then
|
|
begin
|
|
FCurFrame := NewBitmap( Width, Height );
|
|
{$IFDEF TOPF16BIT}
|
|
FCurFrame.PixelFormat := pf16bit; //&&&
|
|
{$ELSE}
|
|
FCurFrame.PixelFormat := pf24bit;
|
|
{$ENDIF}
|
|
|
|
if Transparent then
|
|
begin
|
|
FCurMask := NewBitmap( Width, Height );
|
|
FCurMask.PixelFormat := pf1bit;
|
|
end;
|
|
|
|
FCurIndex := -1;
|
|
end;
|
|
if FCurIndex >= 0 then
|
|
if Frames[ FCurIndex ].ReallyTransparent then
|
|
if FCurMask = nil then
|
|
begin
|
|
FCurMask := NewBitmap( Width, Height );
|
|
FCurMask.PixelFormat := pf1bit;
|
|
//^^^FCurMask.Canvas.Brush.Color := clWhite;
|
|
FCurMask.BkColor := clBlack; //---%%%---
|
|
FCurMask.Canvas.FillRect( MakeRect( 0, 0, Width, Height ) );
|
|
end;
|
|
if (FCurIndex < 0) or (FCurIndex > Frame) then
|
|
Prepare0;
|
|
while FCurIndex < Frame do
|
|
begin
|
|
DM := Frames[ FCurIndex ].DisposalMethod;
|
|
if DM = dmRestorePrevious then
|
|
if FCurIndex = 0 then
|
|
DM := dmRestoreBackground;
|
|
if DM = dmUndefined then
|
|
for I := FCurIndex - 1 downto 0 do
|
|
if Frames[ I ].DisposalMethod <> DM then
|
|
begin
|
|
DM := Frames[ I ].DisposalMethod;
|
|
break;
|
|
end;
|
|
if (DM = dmUndefined) and Frames[ FCurIndex + 1 ].Transparent then
|
|
DM := dmLeave;
|
|
case DM of
|
|
dmRestoreBackground:
|
|
begin
|
|
|
|
//^^^FCurFrame.Canvas.Brush.Color := BkColor;
|
|
FCurFrame.BkColor := BkColor;
|
|
|
|
F := Frames[ FCurIndex ];
|
|
FCurFrame.Canvas.FillRect( MakeRect( F.Origin.x, F.Origin.y, F.Origin.x + F.Width,
|
|
F.Origin.y + F.Height ) );
|
|
if FCurMask <> nil then
|
|
begin
|
|
//^^^FCurMask.Canvas.Brush.Color := clWhite;
|
|
FCurMask.BkColor := clWhite;
|
|
|
|
if FCurIndex < Count then
|
|
begin
|
|
Frames[ FCurIndex + 1 ].GetBitmap;
|
|
if Frames[ FCurIndex + 1 ].Mask = nil then
|
|
//^^^FCurMask.Canvas.Brush.Color := clBlack;
|
|
FCurMask.BkColor := clBlack;
|
|
end;
|
|
FCurMask.Canvas.FillRect( MakeRect( F.Origin.x, F.Origin.y,
|
|
F.Origin.x + F.Width, F.Origin.y + F.Height ) );
|
|
end;
|
|
|
|
end;
|
|
dmRestorePrevious:
|
|
begin
|
|
FCurFrame.Assign( FPrevFrame );
|
|
if FCurMask <> nil then
|
|
FCurMask.Assign( FPrevMask );
|
|
if FCurMask <> nil then
|
|
if FCurMask.Empty then
|
|
begin
|
|
FCurMask.Free;
|
|
FCurMask := nil;
|
|
end;
|
|
end;
|
|
dmUndefined:
|
|
begin
|
|
F := Frames[ FCurIndex + 1 ];
|
|
//^^^FCurFrame.Canvas.Brush.Color := BkColor;
|
|
FCurFrame.BkColor := BkColor;
|
|
FCurFrame.Canvas.FillRect( MakeRect( F.Origin.x, F.Origin.y,
|
|
F.Origin.x + F.Width, F.Origin.y + F.Height ) );
|
|
if FCurMask <> nil then
|
|
begin
|
|
//^^^FCurMask.Canvas.Brush.Color := clBlack;
|
|
FCurMask.BkColor := clBlack;
|
|
FCurMask.Canvas.FillRect( MakeRect( F.Origin.x, F.Origin.y,
|
|
F.Origin.x + F.Width, F.Origin.y + F.Height ) );
|
|
end;
|
|
end;
|
|
end;
|
|
Inc( FCurIndex );
|
|
F := Frames[ FCurIndex ];
|
|
if F.DisposalMethod = dmRestorePrevious then
|
|
begin
|
|
if FPrevFrame = nil then
|
|
FPrevFrame := NewBitmap( 0, 0 );
|
|
FPrevFrame.Assign( FCurFrame );
|
|
if FCurMask <> nil then
|
|
begin
|
|
if FPrevMask = nil then
|
|
FPrevMask := NewBitmap( 0, 0 );
|
|
FPrevMask.Assign( FCurMask );
|
|
end;
|
|
end;
|
|
F.Draw( FCurFrame.Canvas.Handle, F.Origin.x, F.Origin.y );
|
|
|
|
//F.Bitmap.SaveToFile( GetStartDir + Int2Str( FCurIndex ) + 'img.bmp' );
|
|
//FCurFrame.SaveToFile( GetStartDir + Int2Str( FCurIndex ) + '=fr.bmp' );
|
|
|
|
if FCurMask <> nil then
|
|
begin
|
|
//if F.Mask <> nil then F.Mask.SaveToFile( GetStartDir + Int2Str( FCurIndex ) + 'Msk.bmp' );
|
|
DrawCurFrameMask;
|
|
//---------------------
|
|
//FCurMask.SaveToFile( GetStartDir + Int2Str( FCurIndex ) + '=MS.bmp' );
|
|
//---------------------
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TGif.GetBkColor: TColor;
|
|
begin
|
|
Result := FGifImage.BkColor;
|
|
end;
|
|
|
|
procedure TGif.SetBkColor(const Value: TColor);
|
|
begin
|
|
FGifImage.BkColor := Value;
|
|
Changed;
|
|
end;
|
|
|
|
function TGif.GetFrames(Idx: Integer): PGifFrame;
|
|
begin
|
|
Result := FGifImage.Frames[ Idx ];
|
|
end;
|
|
|
|
function TGif.GetTransparent: Boolean;
|
|
begin
|
|
Result := FGifImage.Transparent;
|
|
end;
|
|
|
|
function TGif.GetFrame: Integer;
|
|
begin
|
|
Result := FGifImage.FFrameIndex;
|
|
end;
|
|
|
|
procedure TGif.SetFrame(Value: Integer);
|
|
begin
|
|
if Value >= Count then
|
|
Value := 0;
|
|
FGifImage.Frame := Value;
|
|
end;
|
|
|
|
function TGif.LoadFromStream(Stream: PStream): Boolean;
|
|
begin
|
|
Clear;
|
|
Result := FGifImage.LoadFromStream( Stream );
|
|
Changed;
|
|
end;
|
|
|
|
function TGif.LoadFromFile(const FileName: String): Boolean;
|
|
begin
|
|
Clear;
|
|
Result := FGifImage.LoadFromFile( FileName );
|
|
Changed;
|
|
end;
|
|
|
|
function TGif.GetFrameCount: Integer;
|
|
begin
|
|
Result := FGifImage.Count;
|
|
end;
|
|
|
|
function TGif.GetDelays(Idx: Integer): Integer;
|
|
begin
|
|
Result := 0;
|
|
if Idx < Count then
|
|
Result := Frames[ Idx ].Delay;
|
|
end;
|
|
|
|
procedure TGif.SetDelays(Idx, Value: Integer);
|
|
begin
|
|
Frames[ Idx ].Delay := Value;
|
|
Changed;
|
|
end;
|
|
|
|
procedure TGif.SetWidth(Value: Integer);
|
|
begin
|
|
// nothing!
|
|
end;
|
|
|
|
procedure TGif.SetHeight(Value: Integer);
|
|
begin
|
|
// nothing !
|
|
end;
|
|
|
|
{
|
|
procedure TGif.SetForceBkTransparent(const Value: Boolean);
|
|
begin
|
|
if FForceBkTransparent = Value then Exit;
|
|
FForceBkTransparent := Value;
|
|
FCurIndex := -1;
|
|
end;
|
|
}
|
|
|
|
function TGif.GetCorrupted: Boolean;
|
|
begin
|
|
Result := FGifImage.Corrupted;
|
|
end;
|
|
|
|
procedure TGif.Changed;
|
|
begin
|
|
if Assigned( FOnChanged ) then
|
|
FOnChanged( @Self );
|
|
end;
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//+ TGifShow - a control to show (animated) GIF on a form. +
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
type
|
|
PGifShowData = ^TGifShowData;
|
|
TGifShowData = object( TObj )
|
|
gsdGifShow: PGifShow;
|
|
gsdAutosize: Boolean;
|
|
gsdStretch: Boolean;
|
|
gsdAnimate: Boolean;
|
|
gsdLoop: Boolean;
|
|
//gsdTimer: PTimer;
|
|
gsdTimerSet: Integer;
|
|
gsdGif: PGif;
|
|
gsdOnEndLoop: TOnEvent;
|
|
destructor Destroy; virtual;
|
|
end;
|
|
|
|
function NewGifShow( AParent: PControl ): PGifShow;
|
|
var D: PGifShowData;
|
|
begin
|
|
Result := PGifShow( NewPaintBox( AParent ) );
|
|
new( D, Create );
|
|
D.gsdGifShow := Result;
|
|
D.gsdAutosize := TRUE;
|
|
D.gsdStretch := TRUE;
|
|
D.gsdAnimate := TRUE;
|
|
D.gsdLoop := TRUE;
|
|
D.gsdGif := NewGif;
|
|
D.gsdGif.FOnChanged := Result.GifChanged;
|
|
Result.CustomObj := D;
|
|
Result.SetOnPaint( Result.PaintFrame );
|
|
end;
|
|
|
|
function TGif.LoadFromResourceID(Instance: HInst; ResID: Integer): Boolean;
|
|
var Strm: PStream;
|
|
begin
|
|
Strm := NewMemoryStream;
|
|
Resource2Stream( Strm, Instance, PChar( ResID ), RT_RCDATA );
|
|
Strm.Position := 0;
|
|
Result := LoadFromStream( Strm );
|
|
Strm.Free;
|
|
end;
|
|
|
|
function TGif.LoadFromResourceName(Inst: HInst; RsrcName: PChar): Boolean;
|
|
var Strm: PStream;
|
|
begin
|
|
Strm := NewMemoryStream;
|
|
Resource2Stream( Strm, Inst, PChar( RsrcName ), RT_RCDATA );
|
|
Strm.Position := 0;
|
|
Result := LoadFromStream( Strm );
|
|
Strm.Free;
|
|
end;
|
|
|
|
{ TGifShow }
|
|
|
|
function TGifShow.GetAnimate: Boolean;
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
Result := D.gsdAnimate;
|
|
end;
|
|
|
|
function TGifShow.GetAutosize: Boolean;
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
Result := D.gsdAutosize;
|
|
end;
|
|
|
|
function TGifShow.GetDummy: Boolean;
|
|
begin
|
|
Result := FALSE;
|
|
end;
|
|
|
|
function TGifShow.GetGif: PGif;
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
Result := D.gsdGif;
|
|
end;
|
|
|
|
function TGifShow.GetLoop: Boolean;
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
Result := D.gsdLoop;
|
|
end;
|
|
|
|
function TGifShow.GetOnEndLoop: TOnEvent;
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
Result := D.gsdOnEndLoop;
|
|
end;
|
|
|
|
function TGifShow.GetStretch: Boolean;
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
Result := D.gsdStretch;
|
|
end;
|
|
|
|
procedure GoNextFrame( Wnd: HWnd; Msg: DWORD; GifShow: PGifShow; dwTime: DWORD );
|
|
stdcall;
|
|
begin
|
|
GifShow.NextFrame( nil );
|
|
end;
|
|
|
|
procedure TGifShow.GifChanged(Sender: PObj);
|
|
var D: PGifShowData;
|
|
NewDelay: Integer;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
if (D.gsdGif.Count > 1) and D.gsdAnimate then
|
|
begin
|
|
NewDelay := Max( 1, D.gsdGif.Frames[ 0 ].Delay );
|
|
if D.gsdTimerSet = 0 then
|
|
begin
|
|
D.gsdTimerSet := NewDelay;
|
|
SetTimer( GetWindowHandle, DWORD( @ Self ), D.gsdTimerSet, @ GoNextFrame );
|
|
end;
|
|
end
|
|
else
|
|
if D.gsdTimerSet <> 0 then
|
|
begin
|
|
KillTimer( Handle, DWORD( @ Self ) );
|
|
D.gsdTimerSet := 0;
|
|
end;
|
|
if D.gsdAutosize then
|
|
SetAutosize( TRUE );
|
|
Invalidate;
|
|
end;
|
|
|
|
function TGifShow.LoadFromFile(const FileName: String): Boolean;
|
|
begin
|
|
Result := Gif.LoadFromFile( FileName );
|
|
end;
|
|
|
|
function TGifShow.LoadFromResourceID(Instance: HInst;
|
|
ResID: Integer): Boolean;
|
|
begin
|
|
Result := Gif.LoadFromResourceID( Instance, ResID );
|
|
end;
|
|
|
|
function TGifShow.LoadFromResourceName(Inst: HInst;
|
|
RsrcName: PChar): Boolean;
|
|
begin
|
|
Result := Gif.LoadFromResourceName( Inst, RsrcName );
|
|
end;
|
|
|
|
function TGifShow.LoadFromStream(Stream: PStream): Boolean;
|
|
begin
|
|
Result := Gif.LoadFromStream( Stream );
|
|
end;
|
|
|
|
procedure TGifShow.NextFrame(Sender: PObj);
|
|
var D: PGifShowData;
|
|
NewDelay: Integer;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
if D.gsdGif.Frame >= D.gsdGif.Count-1 then
|
|
begin
|
|
if D.gsdLoop then
|
|
begin
|
|
D.gsdGif.Frame := 0;
|
|
//D.gsdTimer.Interval := D.gsdGif.Frames[ 0 ].Delay;
|
|
end
|
|
else
|
|
begin
|
|
D.gsdAnimate := FALSE;
|
|
end;
|
|
if Assigned( D.gsdOnEndLoop ) then
|
|
D.gsdOnEndLoop( @ Self );
|
|
end
|
|
else
|
|
D.gsdGif.Frame := D.gsdGif.Frame + 1;
|
|
Invalidate;
|
|
NewDelay := Max( 1, D.gsdGif.Frames[ D.gsdGif.Frame ].Delay );
|
|
if D.gsdTimerSet <> NewDelay then
|
|
begin
|
|
if D.gsdTimerSet <> 0 then
|
|
KillTimer( Handle, DWORD( @Self ) );
|
|
D.gsdTimerSet := NewDelay;
|
|
SetTimer( Handle, DWORD( @ Self ), NewDelay, @ GoNextFrame );
|
|
end;
|
|
end;
|
|
|
|
procedure TGifShow.PaintFrame(Sender: PControl; DC: HDC);
|
|
var D: PGifShowData;
|
|
Br: HBrush;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
if (D.gsdGif.Width > 0) and (D.gsdGif.Height > 0) then
|
|
begin
|
|
if Stretch and ((D.gsdGif.Width <> Width) or (D.gsdGif.Height <> Height)) then
|
|
if Transparent then
|
|
D.gsdGif.StretchDrawTransp( DC, ClientRect )
|
|
else
|
|
D.gsdGif.StretchDraw( DC, ClientRect )
|
|
else
|
|
if Transparent then
|
|
D.gsdGif.DrawTransp( DC, 0, 0 )
|
|
else
|
|
D.gsdGif.Draw( DC, 0, 0 );
|
|
end
|
|
else
|
|
begin
|
|
if not Transparent then
|
|
begin
|
|
Br := CreateSolidBrush( Color2RGB( Color ) );
|
|
Windows.FillRect( DC, ClientRect, Br );
|
|
DeleteObject( Br );
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TGifShow.SetAnimate(const Value: Boolean);
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
D.gsdAnimate := Value;
|
|
GifChanged( nil );
|
|
end;
|
|
|
|
procedure TGifShow.SetAutosize(const Value: Boolean);
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
D.gsdAutosize := Value;
|
|
if Value and (D.gsdGif.Width > 0) and (D.gsdGif.Height > 0) then
|
|
begin
|
|
Width := D.gsdGif.Width;
|
|
Height := D.gsdGif.Height;
|
|
end;
|
|
end;
|
|
|
|
procedure TGifShow.SetLoop(const Value: Boolean);
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
if D.gsdLoop = Value then Exit;
|
|
D.gsdLoop := Value;
|
|
GifChanged( nil );
|
|
end;
|
|
|
|
procedure TGifShow.SetOnEndLoop(const Value: TOnEvent);
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
D.gsdOnEndLoop := Value;
|
|
end;
|
|
|
|
procedure TGifShow.SetStretch(const Value: Boolean);
|
|
var D: PGifShowData;
|
|
begin
|
|
D := Pointer( CustomObj );
|
|
D.gsdStretch := Value;
|
|
Invalidate;
|
|
end;
|
|
|
|
{ TGifShowData }
|
|
|
|
destructor TGifShowData.Destroy;
|
|
begin
|
|
//gsdTimer.Free;
|
|
if gsdTimerSet <> 0 then
|
|
KillTimer( gsdGifShow.Handle, DWORD( gsdGifShow ) );
|
|
gsdGif.Free;
|
|
inherited;
|
|
end;
|
|
|
|
end.
|