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