uGifViewer.pas 95 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785
  1. Unit uGifViewer;
  2. (*==============================================================================
  3. DESCRIPTION : Visual component for displaying an animated image in the
  4. GIF (Graphic Interchange Format) format
  5. DATE : 17/06/2018
  6. UPDATE : 01/07/2025
  7. VERSION : 1.0
  8. AUTHOR : J.Delauney (BeanzMaster)
  9. CONTRIBUTORS : Jipete, Jurassik Pork, bpranoto, Alexander Koblov
  10. LICENSE : MPL 2.0
  11. ================================================================================
  12. *)
  13. {$mode objfpc}{$H+}
  14. Interface
  15. Uses
  16. Types, Classes, SysUtils, Graphics, Math, Contnrs, Dialogs,
  17. Controls, ExtCtrls,
  18. Lresources, GifViewerStrConsts,
  19. uFastBitmap;
  20. {%region=====[ Définitions des types et constantes utiles pour le format GIF ]===================================}
  21. Const
  22. GIF_MaxColors : Integer = 256; // Nombre de couleurs maximum supportées. NE PAS TOUCHER A CETTE VALEUR
  23. GIF_DelayFactor : Integer = 10; // Facteur de multiplication pour les délais en ms entre chaque image de l'animation
  24. GIF_DefaultDelay : Integer = 100; // 10*10
  25. Type
  26. TGIFVersion = (gvUnknown, gv87a, gv89a);
  27. TGIFVersionRec = Array[0..2] Of AnsiChar;
  28. Const
  29. GIFVersions : Array[gv87a..gv89a] Of TGIFVersionRec = ('87a', '89a');
  30. Type
  31. { En-tête }
  32. TGIFFileHeader = Packed Record
  33. Signature: Array[0..2] Of AnsiChar; // 'GIF'
  34. Version: TGIFVersionRec; // '87a' ou '89a' }
  35. End;
  36. { Description globale de l'image }
  37. TGIFLogicalScreenDescriptorRec = Packed Record
  38. ScreenWidth: Word; // Largeur de l'image en pixels // Width
  39. ScreenHeight: Word; // Hauteur de l'image en pixels // Height
  40. PackedFields: Byte; // champs compactés // Compacted field
  41. BackgroundColorIndex: Byte; // Index globale de la couleur de fond // Index of background color
  42. AspectRatio: Byte; // Ratio d'échelle = (AspectRatio + 15) / 64
  43. End;
  44. { Description d'une image }
  45. TGIFImageDescriptorRec = Packed Record
  46. //Separator: byte; // On lis toujours un byte avant // we always read it before
  47. Left: Word; // Colonne en pixels par rapport au bord gauche de l'écran // Column in pixels from the left edge of the screen
  48. Top: Word; // Rangée en pixels par rapport au haut de l'écran // Row in pixels from the top edge of the screen
  49. Width: Word; // Largeur de l'image en cours en pixels // image width
  50. Height: Word; // Hauteur de l'image en cours pixels // Image height
  51. PackedFields: Byte; // Champs compactés // Compacted field
  52. End;
  53. { Graphic Control Extension bloc a.k.a GCE }
  54. TGIFGraphicControlExtensionRec = Packed Record
  55. // BlockSize: byte; // Normalement toujours 4 octets // Always 4 bytes
  56. PackedFields: Byte; // Champs compacté // Compacted field
  57. DelayTime: Word; // Délai entre chaque image en centième de secondes // Delay between each image in hundredths of a second
  58. TransparentColorIndex: Byte; // Index dans la palette si plus petit ou égale // Delay between each image in hundredths of a second
  59. // Terminator: Byte; // Normalement toujours ZERO // Normally always ZERO
  60. End;
  61. TGIFDisposalFlag = (dmNone, dmKeep, dmErase, dmRestore); // Methodes pour l'affichage des images lors de l'animation
  62. { Plain Text Extension }
  63. TGIFPlainTextExtensionRec = Packed Record
  64. // BlockSize: byte; // Normalement égal à 12 octets // Normally equal to 12 bytes
  65. Left, Top, Width, Height: Word; // Positions et dimensions du texte // position and dimension of text
  66. CellWidth, CellHeight: Byte; // Dimensions d'une cellule dans l'image // Size of cell
  67. TextFGColorIndex, // Index de la couleur de fond dans la palette // Index of the background color
  68. TextBGColorIndex: Byte; // Index de la couleur du texte dans la palette // Index of the text color
  69. End;
  70. { Application Extension }
  71. TGIFApplicationExtensionRec = Packed Record
  72. AppID: Array [0..7] Of AnsiChar; // Identification de l'application majoritairement 'NETSCAPE' ou ''
  73. AppAuthenticationCode: Array [0..2] Of AnsiChar; // Code d'authentification ou numero de version
  74. End;
  75. { Informations de "l'application extension" si disponible }
  76. TGIFNSLoopExtensionRec = Packed Record
  77. Loops: Word; // Nombre de boucle de l'animation 0 = infinie // nb loop
  78. BufferSize: DWord; // Taille du tampon. Usage ?????
  79. End;
  80. Const
  81. // Description des masques pour la description globale de l'image
  82. GIF_GLOBALCOLORTABLE = $80; // Défini si la table de couleurs globale suit la description globale
  83. GIF_COLORRESOLUTION = $70; // Résolution de la couleur (BitsPerPixel) - 3 bits
  84. GIF_GLOBALCOLORTABLESORTED = $08; // Définit si la palette globale est triée - 1 bit
  85. GIF_COLORTABLESIZE = $07; // Taille de la palette - 3 bits
  86. GIF_RESERVED = $0C; // Réservé - doit être défini avec $00 - Taille des données = 2^value+1 - 3 bits
  87. // Descption des masques pour les images
  88. GIF_LOCALCOLORTABLE = $80; // Défini si la table de couleurs locale suit la description de l'image
  89. GIF_INTERLACED = $40; // Défini si l'image est entrelacée
  90. GIF_LOCALCOLORTABLESORTED = $20; // Définit si la palette locale est triée
  91. // Identification des blocs
  92. GIF_PLAINTEXT = $01;
  93. GIF_GRAPHICCONTROLEXTENSION = $F9;
  94. GIF_COMMENTEXTENSION = $FE;
  95. GIF_APPLICATIONEXTENSION = $FF;
  96. GIF_IMAGEDESCRIPTOR = $2C; // ','
  97. GIF_EXTENSIONINTRODUCER = $21; // '!'
  98. GIF_TRAILER = $3B; // ';'
  99. // Graphic Control Extension - Définition des masques pour les paramètres
  100. GIF_NO_DISPOSAL = $00; // 0
  101. GIF_DO_NOT_DISPOSE = $04; // 1
  102. GIF_RESTORE_BACKGROUND_COLOR = $08; // 2
  103. GIF_RESTORE_PREVIOUS = $12; // 3
  104. GIF_DISPOSAL_ALL = $1C; // bits 2-4 ($1C)
  105. GIF_USER_INPUT_FLAG = $02;
  106. GIF_TRANSPARENT_FLAG = $01;
  107. GIF_RESERVED_FLAG = $E0;
  108. // Identification des sous-blocs pour "Application Extension"
  109. GIF_LOOPEXTENSION = 1;
  110. GIF_BUFFEREXTENSION = 2;
  111. Const
  112. GifGCEDisposalModeStr : Array[TGIFDisposalFlag] Of String = ('None', 'Keep', 'Erase', 'Restore');
  113. Type
  114. { Informations sur une image de l'animation }
  115. TGIFFrameInformations = Record
  116. Left, Top, // Position de l'image
  117. Width, Height: Integer; // Dimension de l'image
  118. HasLocalPalette: Boolean; // Palette locale disponible
  119. IsTransparent: Boolean; // Image transparente
  120. UserInput: Boolean; // Données personnelle
  121. BackgroundColorIndex: Byte; // Normalement seulement valide si une palette globale existe
  122. TransparentColorIndex: Byte; // Index de la couleur transparente
  123. DelayTime: Word; // Délai d'animation
  124. Disposal: TGIFDisposalFlag; // Methode d'affichage
  125. Interlaced: Boolean; // Image entrelacée
  126. End;
  127. PGifFrameInformations = ^TGifFrameInformations;
  128. {%endregion%}
  129. { TGIFFastMemoryStream }
  130. { Classe d'aide à la lecture des données dans un flux en mémoire }
  131. TGIFFastMemoryStream = Class
  132. Private
  133. FBuffer: PByte;
  134. FPosition: Int64;
  135. FBytesRead, FBytesLeft, FSize: Int64;
  136. Public
  137. Constructor Create(AStream : TStream);
  138. Destructor Destroy; Override;
  139. { Lit un Byte dans le tampon / Read a byte in buffer }
  140. Function ReadByte: Byte;
  141. { Lit un Word dans le tampon / Read a word in buffer}
  142. Function ReadWord: Word;
  143. { Lit un DWord dans le tampon / Read a DWord in buffer }
  144. Function ReadDWord: DWord;
  145. { Lit et retourne un tampon "Buffer" de taille "Count" octets / Read a buffer of size "count" }
  146. Function Read(Var Buffer; Count : Int64): Int64;
  147. { Déplacement dans le flux de "Offset" depuis "Origin"
  148. TSeekOrigin =
  149. - soBeginning : Depuis le début du flux
  150. - soCurrent : a partir de la position courante
  151. - soEnd : A partir de la fin du flux
  152. }
  153. Function Seek(Const Offset : Int64; Origin : TSeekOrigin): Int64;
  154. { Déplacement dans le flux vers l'avant de "Cnt" octet depuis la position courrante }
  155. Procedure SeekForward(Cnt : Integer);
  156. { Indique si la fin du flux est atteinte (EOS = End Of Stream) }
  157. Function EOS: Boolean;
  158. { Retourne la taille du flux en octet // Size in byte of the buffer}
  159. Property Size: Int64 read FSize;
  160. { Retourne la position courrante de lecture dans le tampon // Current position in buffer }
  161. Property Position: Int64 read FPosition;
  162. End;
  163. { TGIFLoadErrorEvent : Fonction d'évènement levée en cas d'erreur(s) dans le chargement // Event raise on error }
  164. TGIFLoadErrorEvent = Procedure(Sender : TObject; Const ErrorCount : Integer; Const ErrorList : TStringList) Of Object;
  165. { TGIFImageListItem }
  166. { Définition d'une image contenue dans le fichier GIF }
  167. TGIFImageListItem = Class
  168. Private
  169. FBitmap: TFastBitmap;
  170. FDrawMode: TGIFDisposalFlag;
  171. FLeft, FTop: Integer;
  172. FComment: TStringList;
  173. FDelay: Integer;
  174. FTransparent: Boolean;
  175. FIsCorrupted : Boolean;
  176. Protected
  177. Public
  178. Constructor Create;
  179. Destructor Destroy; Override;
  180. { Objet contenant l'image }
  181. Property Bitmap: TFastBitmap read FBitmap write FBitmap;
  182. { Mode de rendu de l'image // Render Mode}
  183. Property DrawMode: TGIFDisposalFlag read FDrawMode write FDrawMode;
  184. { Position gauche de l'image }
  185. Property Left: Integer read FLeft write FLeft;
  186. { Position Haut de l'image }
  187. Property Top: Integer read FTop write FTop;
  188. { Temps d'attente entre deux image de l'animation }
  189. Property Delay: Integer read FDelay write FDelay;
  190. { Commentaire sur l'image }
  191. Property Comment: TStringList read FComment write FComment;
  192. { Retourne TRUE si l'image utilise la transparence }
  193. Property IsTransparent: Boolean read FTransparent write FTransparent;
  194. { Indique si l'image est corrompue }
  195. property IsCorrupted : Boolean read FIsCorrupted write FIsCorrupted;
  196. End;
  197. { TGIFImageList }
  198. { Classe d'aide à la gestion des images contenues dans le fichier GIF }
  199. { Helper class for manage image in GIF }
  200. TGIFImageList = Class(TObjectList)
  201. Private
  202. Protected
  203. Function GetItems(Index : Integer): TGIFImageListItem;
  204. Procedure SetItems(Index : Integer; AGifImage : TGIFImageListItem);
  205. Public
  206. { Efface la liste }
  207. Procedure Clear; Override;
  208. { Ajoute une nouvelle image vide à la liste }
  209. Function AddNewImage: TGIFImageListItem;
  210. { Ajout d'une image dans la liste }
  211. Function Add(AGifImage : TGIFImageListItem): Integer;
  212. { Extraction d'une image de la liste }
  213. Function Extract(Item : TGIFImageListItem): TGIFImageListItem;
  214. { Effacement d'une image dans la liste }
  215. Function Remove(AGifImage : TGIFImageListItem): Integer;
  216. { Retourne l'index de l'image recherchée (retourne -1 si non trouvé) }
  217. Function IndexOf(AGifImage : TGIFImageListItem): Integer;
  218. { Retourne la première image }
  219. Function First: TGIFImageListItem;
  220. { Retourne la dernière image }
  221. Function Last: TGIFImageListItem;
  222. { Insertion d'une image à la position "Index" }
  223. Procedure Insert(Index : Integer; AGifImage : TGIFImageListItem);
  224. { Liste des images }
  225. Property Items[Index: Integer]: TGIFImageListItem read GetItems write SetItems; Default;
  226. End;
  227. { TGIFImageLoader }
  228. { Classe spécialisée pour la lecture d'une image au format GIF }
  229. { Special class for read a GIF }
  230. TGIFImageLoader = Class
  231. Private
  232. FCurrentLayerIndex: Integer;
  233. FGIFFIleHeader: TGIFFileHeader;
  234. FLogicalScreenChunk: TGIFLogicalScreenDescriptorRec;
  235. FHasGlobalPalette: Boolean;
  236. FTransparent: Boolean;
  237. FGlobalPalette: TColor32List;
  238. FVersion: String;
  239. FWidth, FHeight: Integer;
  240. FBackgroundColor: TColor32;
  241. FFrames: TGIFImageList;
  242. FErrorList: TStringList;
  243. FErrorCount: Integer;
  244. FOnLoadError: TGIFLoadErrorEvent;
  245. Procedure SetCurrentLayerIndex(AValue : Integer);
  246. Protected
  247. Memory: TGIFFastMemoryStream;
  248. CurrentFrameInfos: TGifFrameInformations;
  249. Function GetFrameCount: Integer;
  250. Procedure LoadFromMemory();
  251. Function CheckFormat(): Boolean;
  252. Function ReadImageProperties: Boolean;
  253. Procedure AddError(Msg : String);
  254. Procedure NotifyError;
  255. Public
  256. Constructor Create;
  257. Destructor Destroy; Override;
  258. { LoadFromStream : Charge les données depuis un flux }
  259. Procedure LoadFromStream(aStream : TStream); Virtual;
  260. { LoadFromFile : Charge les données depuis un fichier physique }
  261. Procedure LoadFromFile(Const FileName : String); Virtual;
  262. { Chargement depuis une Resource Lazarus }
  263. Procedure LoadFromResource(Const ResName : String);
  264. { Retourne la version du fichier GIF }
  265. Property Version: String read FVersion;
  266. { Retourne la largeur de l'image GIF }
  267. Property Width: Integer read FWidth;
  268. { Retourne la hauteur de l'image GIF }
  269. Property Height: Integer read FHeight;
  270. { Retourne la couleur de l'image GIF si elle existe,. Sinon retourne une couleur transparente (clrTransparent) }
  271. Property BackgroundColor: TColor32 read FBackgroundColor write FBackgroundColor;
  272. { Prise en charge de la transparence dans l'image GIF // Take transparency in account}
  273. Property Transparent: Boolean read FTransparent write FTransparent;
  274. { Retourne l'index courrant de l'image de l'animation traité // Return the current index frame}
  275. Property CurrentFrameIndex: Integer read FCurrentLayerIndex write SetCurrentLayerIndex;
  276. { Liste des images de l'animation // List of frame}
  277. Property Frames: TGIFImageList read FFrames;
  278. { Nombre d'image de l'animation // Nb frames }
  279. Property FrameCount: Integer read GetFrameCount;
  280. { Nombre d'erreur produite loars d'un cahrgement ou d'un enregistrement // Nb error }
  281. Property ErrorCount: Integer read FErrorCount;
  282. { Liste des erreurs // List of error }
  283. Property Errors: TStringList read FErrorList;
  284. { Evenement pour intercepter les erreurs notifiées lors du chargement des données // Error Event }
  285. Property OnLoadError: TGIFLoadErrorEvent read FOnLoadError write FOnLoadError;
  286. End;
  287. { TGIFRenderCacheListItem }
  288. { Définition d'une image cache de l'animation }
  289. { Image cache class }
  290. TGIFRenderCacheListItem = Class
  291. Private
  292. FBitmap: Graphics.TBitmap;
  293. FDelay: Integer;
  294. FIsCorrupted : Boolean;
  295. Public
  296. Constructor Create;
  297. Destructor Destroy; Override;
  298. { Image cache prérendu de l'animation }
  299. Property Bitmap: Graphics.TBitmap read FBitmap write FBitmap;
  300. { Temps d'attente en ms avec l'image suivante }
  301. Property Delay: Integer read FDelay write FDelay;
  302. { Indique si l'image est corrompue }
  303. property IsCorrupted : Boolean read FIsCorrupted write FIsCorrupted;
  304. End;
  305. { TGIFRenderCacheList }
  306. { Classe d'aide à la gestion des images rendues de l'animation }
  307. { Helper class for manage list of image cache }
  308. TGIFRenderCacheList = Class(TObjectList)
  309. Private
  310. Protected
  311. Function GetItems(Index : Integer): TGIFRenderCacheListItem;
  312. Procedure SetItems(Index : Integer; AGIFRenderCache : TGIFRenderCacheListItem);
  313. Public
  314. { Efface la liste }
  315. Procedure Clear; Override;
  316. { Ajoute un nouvel objet cache vide }
  317. Function AddNewCache: TGIFRenderCacheListItem;
  318. { Ajoute un nouveau cache }
  319. Function Add(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
  320. { Extrait un cache de la liste }
  321. Function Extract(Item : TGIFRenderCacheListItem): TGIFRenderCacheListItem;
  322. { Supprime un cache de la liste }
  323. Function Remove(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
  324. { Retourne l'index du cache recherchée (retourne -1 si non trouvé) }
  325. Function IndexOf(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
  326. { Retourne le premier élément de la liste }
  327. Function First: TGIFRenderCacheListItem;
  328. { Retourne le dernier élément de la liste }
  329. Function Last: TGIFRenderCacheListItem;
  330. { Insertion d'un cache à la position "Index" }
  331. Procedure Insert(Index : Integer; AGIFRenderCache : TGIFRenderCacheListItem);
  332. { Vérifie si "anIndex" ne dépasse pas la nombre d'élément dans la liste. Retroune FALSE si l'index est hors limite }
  333. { Check if 'anIndex' does not exceed the number of items in the list. Retrieve FALSE if the index is out of range }
  334. function IsIndexOk(anIndex : Integer) : Boolean;
  335. { Supprime les éléments dont le drapeau "IsCorrupted" est vrai }
  336. { Remove items wich "IsCorrupted" flag is on True }
  337. procedure Pack;
  338. { Liste des caches }
  339. Property Items[Index: Integer]: TGIFRenderCacheListItem read GetItems write SetItems; Default;
  340. End;
  341. { TGIFAutoStretchMode
  342. Mode de redimensionnement automatique}
  343. TGIFAutoStretchMode = (smManual, smStretchAll, smStretchOnlyBigger, smStretchOnlySmaller );
  344. TOnStretchChanged = procedure (Sender:TObject; IsStretched : Boolean) of object;
  345. { TGIFViewer }
  346. { Composant visuel pour afficher une image GIF animée }
  347. { Visual component for display the animated GIF }
  348. TGIFViewer = Class(TGraphicControl)
  349. Private
  350. FAutoStretchMode: TGIFAutoStretchMode;
  351. FGIFLoader: TGIFImageLoader;
  352. FLastDrawMode : TGIFDisposalFlag;
  353. FFileName: String;
  354. FRestoreBitmap, FVirtualView: TFastBitmap;
  355. FRenderCache: TGIFRenderCacheList;
  356. FCurrentFrameIndex: Integer;
  357. FGIFWidth, FGIFHeight: Integer;
  358. FCurrentView: Graphics.TBitmap;
  359. FAnimateTimer: TTimer;
  360. FAnimateSpeed: Integer;
  361. FAnimated, FPause: Boolean;
  362. FAutoPlay: Boolean;
  363. FCache: Boolean;
  364. FDisplayInvalidFrames : Boolean;
  365. FAutoRemoveInvalidFrame : Boolean;
  366. FPainting: Boolean;
  367. FBorderShow: Boolean;
  368. FBorderColor: TColor;
  369. FBorderWidth: Byte;
  370. FBevelInner, FBevelOuter: TPanelBevel;
  371. FBevelWidth: TBevelWidth;
  372. FBevelColor, FColor: TColor;
  373. FCenter, FStretch, FTransparent: Boolean;
  374. FOnStart, FOnStop, FOnPause, FOnFrameChange: TNotifyEvent;
  375. FOnLoadError : TGIFLoadErrorEvent;
  376. FOnStretchChanged : TOnStretchChanged;
  377. Function GetCanvas: TCanvas;
  378. Function GetFrameCount: Integer;
  379. Function GetGIFVersion: String;
  380. Function GetRawFrameItem(Index : Integer): TGIFImageListItem;
  381. Procedure SetAutoStretchMode(AValue: TGIFAutoStretchMode);
  382. Procedure SetCenter(Const Value : Boolean);
  383. Procedure SetStretch(Const Value : Boolean);
  384. Procedure SetPause(Const Value : Boolean);
  385. Procedure SetFileName(Const Value : String);
  386. Function GetFrame(Const Index : Integer): Graphics.TBitmap;
  387. Procedure SetTransparent(Const Value : Boolean);
  388. Procedure SetBevelInner(Const Value : TPanelBevel);
  389. Procedure SetBevelOuter(Const Value : TPanelBevel);
  390. Procedure SetBevelWidth(Const Value : TBevelWidth);
  391. procedure ResetCurrentView;
  392. Protected
  393. Procedure DoInternalOnLoadError(Sender : TObject; Const ErrorCount : Integer; Const ErrorList : TStringList);
  394. Procedure DoTimerAnimate(Sender : TObject);
  395. { Rendu d'une image de l'animation }
  396. procedure RenderFrame(Index : Integer); Virtual;
  397. { Creation des image cache pour l'animation }
  398. Procedure ComputeCache; Virtual;
  399. { Calcul de la postion et de la dimension pour l'afficchage sur le "Canvas" }
  400. Function DestRect: TRect; Virtual;
  401. { Fonctions hérités }
  402. Procedure CalculatePreferredSize(Var PreferredWidth, PreferredHeight : Integer; {%H-}WithThemeSpace : Boolean); Override;
  403. Class Function GetControlClassDefaultSize: TSize; Override;
  404. Procedure Paint; Override;
  405. procedure Loaded; override;
  406. procedure BeforeLoad;
  407. procedure AfterLoad;
  408. Public
  409. { Création du composant }
  410. Constructor Create(AOwner : TComponent); Override;
  411. { Destruction du composant }
  412. Destructor Destroy; Override;
  413. { Mise à jour de la surface de dessin (Canvas) du composant }
  414. Procedure Invalidate; Override;
  415. { LoadFromStream : Charge les données depuis un flux }
  416. Procedure LoadFromStream(aStream : TStream);
  417. { Chargement depuis un fichier }
  418. Procedure LoadFromFile(Const aFileName : String);
  419. { Chargement depuis une Resource Lazarus }
  420. Procedure LoadFromResource(Const ResName : String);
  421. { Joue l'animation }
  422. Procedure Start;
  423. { Arrête l'animation }
  424. Procedure Stop;
  425. { Met en pause l'animation }
  426. Procedure Pause;
  427. Procedure NextFrame;
  428. Procedure PriorFrame;
  429. { Retourne l'image brute du GIF à la position Index }
  430. Function GetRawFrame(Index : Integer): TBitmap;
  431. { Affiche l'image de l'animation mise en cache à la position Index }
  432. Procedure DisplayFrame(Index : Integer);
  433. { Affiche l'image brute de l'animation à la position Index }
  434. Procedure DisplayRawFrame(Index : Integer);
  435. { Extrait l'image de l'animation mise en cache à la position Index vers un TBitmap }
  436. procedure ExtractFrame(Index : Integer; Var bmp:TBitmap) ;
  437. { Extrait l'image brute de l'animation à la position Index vers un TBitmap}
  438. procedure ExtractRawFrame(Index : Integer; Var bmp:TBitmap);
  439. { Retourne le Canvas du composant }
  440. Property Canvas: TCanvas read GetCanvas;
  441. { Retourne TRUE si l'animation est en pause }
  442. Property Paused: Boolean read FPause;
  443. { Retourne TRUE si l'animation est en cours }
  444. Property Playing: Boolean read FAnimated;
  445. { Retourne l'index actuel de l'image affichée // Current Index of displayed frame }
  446. Property CurrentFrameIndex: Integer read FCurrentFrameIndex;
  447. { Liste des images de l'animation // List of frame}
  448. Property Frames[Index: Integer]: TBitmap read GetFrame;
  449. { Retourne le nombre d'image de l'animation // Number of frames }
  450. Property FrameCount: Integer read GetFrameCount;
  451. { Retourne la version du fichier GIF chargé // version of the gif }
  452. Property Version: String read GetGIFVersion;
  453. { Image courante de l'animation affichée // Current displayed image }
  454. Property CurrentView: Graphics.TBitmap read FCurrentView;
  455. property RawFrames[Index : Integer] : TGIFImageListItem read GetRawFrameItem;
  456. Published
  457. Property Color: TColor read FColor write FColor;
  458. { Bordure visible autour du composant // Border visible around component }
  459. Property Border: Boolean read FBorderShow write FBorderShow;
  460. { Couleur de la bordure // Color of border }
  461. Property BorderColor: TColor read FBorderColor write FBorderColor;
  462. { Epaisseur de la bordure // Width of border }
  463. Property BorderWidth: Byte read FBorderWidth write FBorderWidth;
  464. Property BevelColor: TColor read FBevelColor write FBevelColor;
  465. Property BevelInner: TPanelBevel read FBevelInner write SetBevelInner Default bvNone;
  466. Property BevelOuter: TPanelBevel read FBevelOuter write SetBevelOuter Default bvRaised;
  467. Property BevelWidth: TBevelWidth read FBevelWidth write SetBevelWidth Default 1;
  468. Property Cache: Boolean read FCache write FCache;
  469. { Joue l'animation automatiquement lors du chargement d'une image GIF animée }
  470. { Play animation automatically when loading an animated GIF image }
  471. Property AutoPlay: Boolean read FAutoPlay write FAutoPlay;
  472. { Affichage du GIF avec prise en charge de la transparence }
  473. { GIF view with transparency support }
  474. Property Transparent: Boolean read FTransparent write SetTransparent;
  475. { Centrer l'affichage // Center display }
  476. Property Center: Boolean read FCenter write SetCenter;
  477. { Mode du redimensionnement // Automatic stretch mode
  478. smManual : Adpatation Manuelle via la propriété stretch
  479. smStretchAll : Adapte toute les images
  480. smStretchOnlyBigger : Adapte seulement les images plus grande
  481. smStretchOnlySmaller : Adapte seulement les images plus petite
  482. }
  483. property AutoStretchMode : TGIFAutoStretchMode read FAutoStretchMode write SetAutoStretchMode;
  484. { Redimensionner l'affichage proportionnellement // Resize the display proportionally }
  485. Property Stretch: Boolean read FStretch write SetStretch;
  486. { Nom du fichier à charger // Name of file to load }
  487. Property FileName: String read FFileName write SetFileName;
  488. { 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 }
  489. property DisplayInvalidFrames : Boolean read FDisplayInvalidFrames write FDisplayInvalidFrames;
  490. { Définis si les images corrompues doivent être effacées de la liste de l'animation automatiquement. Par defaut TRUE }
  491. property AutoRemoveInvalidFrame : Boolean Read FAutoRemoveInvalidFrame write FAutoRemoveInvalidFrame;
  492. { Evènement déclenché lorsque l'animation débute }
  493. { Event triggered when the animation starts }
  494. Property OnStart: TNotifyEvent read FOnStart write FOnStart;
  495. { Evènement déclenché lorsque l'animation s'arrête }
  496. { Event triggered when the animation stops }
  497. Property OnStop: TNotifyEvent read FOnStop write FOnStop;
  498. { Evènement déclenché lorsque l'animation est mise en pause }
  499. { Event triggered when the animation is paused }
  500. Property OnPause: TNotifyEvent read FOnPause write FOnPause;
  501. { Evènement déclenché lorsque une nouvelle image est affiché lors de l'animation }
  502. { Event triggered when a new image is displayed during the animation }
  503. Property OnFrameChange: TNotifyEvent read FOnFrameChange write FOnFrameChange;
  504. { Evenement pour intercepter les erreurs notifiées lors du chargement des données }
  505. Property OnLoadError: TGIFLoadErrorEvent read FOnLoadError write FOnLoadError;
  506. { Evenement pour intercepter le changement du mode stretch. Uniquement si AutoStretchMode <> smManual }
  507. { Event to intercept the change of the stretch mode. Only if AutoStretchMode <> smManual }
  508. property OnStretchChanged : TOnStretchChanged read FOnStretchChanged write FOnStretchChanged;
  509. { Propriétés héritées }
  510. Property Align;
  511. Property Anchors;
  512. Property AutoSize;
  513. Property Constraints;
  514. Property BorderSpacing;
  515. Property Visible;
  516. Property ParentShowHint;
  517. Property ShowHint;
  518. { Evènements héritées }
  519. Property OnClick;
  520. Property OnMouseDown;
  521. Property OnMouseEnter;
  522. Property OnMouseLeave;
  523. Property OnMouseMove;
  524. Property OnMouseUp;
  525. Property OnMouseWheel;
  526. Property OnMouseWheelDown;
  527. Property OnMouseWheelUp;
  528. End;
  529. TGIFView = Class(TGIFViewer);
  530. Procedure Register;
  531. Implementation
  532. Uses
  533. GraphType;
  534. {$R ../gifview.res}
  535. {%region=====[ Constantes et types internes ]===================================}
  536. Type
  537. // Statut de décodage / encodage LZW
  538. TLZWDecoderStatus = (
  539. dsOK, // Tout va bien
  540. dsNotEnoughInput, // Tampon d'entrée trop petit
  541. dsOutputBufferTooSmall, // Tampon de sortie trop petit
  542. dsInvalidInput, // Donnée corrompue
  543. dsBufferOverflow, // débordement de tampon
  544. dsInvalidBufferSize, // Taille d'un des tampons invalide
  545. dsInvalidInputBufferSize, // Taille du tampon d'entrée invalide
  546. dsInvalidOutputBufferSize,// Taille du tampon de sortie invalide
  547. dsInternalError // Erreur interne signifiant qu'il y a un défaut dans le code
  548. );
  549. {%endregion%}
  550. {%region=====[ Fonctions utiles ]===============================================}
  551. Function FixPathDelimiter(S : String): String;
  552. Var
  553. I: Integer;
  554. Begin
  555. Result := S;
  556. For I := Length(Result) Downto 1 Do
  557. Begin
  558. If (Result[I] = '/') Or (Result[I] = '\') Then Result[I] := PathDelim;
  559. End;
  560. End;
  561. Function CreateFileStream(Const fileName : String; mode : Word = fmOpenRead + fmShareDenyNone): TStream;
  562. Var
  563. fn: String;
  564. Begin
  565. fn := filename;
  566. FixPathDelimiter(fn);
  567. If ((mode And fmCreate) = fmCreate) Or FileExists(fn) Then Result := TFileStream.Create(fn, mode)
  568. Else
  569. Raise Exception.Create('Fichier non trouvé : "' + fn + '"');
  570. End;
  571. {%endregion%}
  572. {%region=====[ TGIFFastMemoryStream ]==============================================}
  573. Constructor TGIFFastMemoryStream.Create(AStream : TStream);
  574. Var
  575. ms: TMemoryStream;
  576. Begin
  577. ms := TMemoryStream.Create;
  578. With ms Do
  579. Begin
  580. CopyFrom(aStream, 0);
  581. Position := 0;
  582. End;
  583. FSize := ms.Size;
  584. FPosition := 0;
  585. FBytesLeft := FSize;
  586. FBytesRead := 0;
  587. FBuffer := nil;
  588. ReAllocMem(FBuffer, FSize);
  589. Move(PByte(ms.Memory)^, FBuffer^, FSize);
  590. FreeAndNil(ms);
  591. End;
  592. Destructor TGIFFastMemoryStream.Destroy;
  593. Begin
  594. If FBuffer <> nil Then
  595. Begin
  596. FreeMem(FBuffer);
  597. FBuffer := nil;
  598. End;
  599. Inherited Destroy;
  600. End;
  601. Function TGIFFastMemoryStream.ReadByte: Byte;
  602. Begin
  603. Result := 0;
  604. If FBytesLeft > 0 Then
  605. Begin
  606. Result := PByte(FBuffer + FPosition)^;
  607. Inc(FPosition);
  608. Inc(FBytesRead);
  609. Dec(FBytesLeft);
  610. End;
  611. End;
  612. Function TGIFFastMemoryStream.ReadWord: Word;
  613. Begin
  614. Result := 0;
  615. If (FBytesLeft >= 2) Then
  616. Begin
  617. Result := PWord(FBuffer + FPosition)^;
  618. Inc(FPosition, 2);
  619. Inc(FBytesRead, 2);
  620. Dec(FBytesLeft, 2);
  621. End;
  622. End;
  623. Function TGIFFastMemoryStream.ReadDWord: DWord;
  624. Begin
  625. Result := 0;
  626. If (FBytesLeft >= 4) Then
  627. Begin
  628. Result := PDWord(FBuffer + FPosition)^;
  629. Inc(FPosition, 4);
  630. Inc(FBytesRead, 4);
  631. Dec(FBytesLeft, 4);
  632. End;
  633. End;
  634. Function TGIFFastMemoryStream.Read(Var Buffer; Count : Int64): Int64;
  635. Var
  636. NumOfBytesToCopy, NumOfBytesLeft: Longint;
  637. CachePtr, BufferPtr: PByte;
  638. Begin
  639. Result := 0;
  640. If (Count > FBytesLeft) Then NumOfBytesLeft := FBytesLeft
  641. Else
  642. NumOfBytesLeft := Count;
  643. BufferPtr := @Buffer;
  644. While NumOfBytesLeft > 0 Do
  645. Begin
  646. // On copie les données
  647. NumOfBytesToCopy := Min(FSize - FPosition, NumOfBytesLeft);
  648. CachePtr := FBuffer;
  649. Inc(CachePtr, FPosition);
  650. Move(CachePtr^, BufferPtr^, NumOfBytesToCopy);
  651. Inc(Result, NumOfBytesToCopy);
  652. Inc(FPosition, NumOfBytesToCopy);
  653. Inc(BufferPtr, NumOfBytesToCopy);
  654. // On met à jour les marqueur de notre tampon
  655. Inc(FBytesRead, NumOfBytesToCopy);
  656. Dec(FBytesLeft, NumOfBytesToCopy);
  657. Dec(NumOfBytesLeft, NumOfBytesToCopy);
  658. End;
  659. End;
  660. Function TGIFFastMemoryStream.Seek(Const Offset : Int64; Origin : TSeekOrigin): Int64;
  661. Var
  662. NewPos: Integer;
  663. Begin
  664. // Calcul de la nouvelle position
  665. Case Origin Of
  666. soBeginning: NewPos := Offset;
  667. soCurrent: NewPos := FPosition + Offset;
  668. soEnd: NewPos := pred(FSize) - Offset;
  669. Else
  670. Raise Exception.Create('TFastStream.Seek: Origine Invalide');
  671. End;
  672. Result := NewPos;
  673. If Offset = 0 Then exit;
  674. FPosition := NewPos;
  675. FBytesLeft := FSize - FPosition;
  676. Result := NewPos;
  677. End;
  678. Procedure TGIFFastMemoryStream.SeekForward(Cnt : Integer);
  679. Begin
  680. Seek(Cnt, soCurrent);
  681. End;
  682. Function TGIFFastMemoryStream.EOS: Boolean;
  683. Begin
  684. Result := ((FBytesLeft <= 0) Or (FPosition >= Pred(FSize)));
  685. End;
  686. {%endregion%}
  687. {%region=====[ TGIFImageListItem ]==============================================}
  688. Constructor TGIFImageListItem.Create;
  689. Begin
  690. FBitmap := TFastBitmap.Create;
  691. FLeft := 0;
  692. FTop := 0;
  693. FDelay := 0;
  694. FDrawMode := dmNone;
  695. FComment := TStringList.Create;
  696. FComment.Clear;
  697. FIsCorrupted := False;
  698. End;
  699. Destructor TGIFImageListItem.Destroy;
  700. Begin
  701. FreeAndNil(FComment);
  702. FreeAndNil(FBitmap);
  703. Inherited Destroy;
  704. End;
  705. {%endregion%}
  706. {%region=====[ TGIFImageList ]==================================================}
  707. Function TGIFImageList.GetItems(Index : Integer): TGIFImageListItem;
  708. Begin
  709. Result := TGIFImageListItem(Inherited Items[Index]);
  710. End;
  711. Procedure TGIFImageList.SetItems(Index : Integer; AGifImage : TGIFImageListItem);
  712. Begin
  713. Put(Index, AGifImage);
  714. End;
  715. Procedure TGIFImageList.Clear;
  716. Var
  717. anItem: TGIFImageListItem;
  718. i: Integer;
  719. Begin
  720. If Count > 0 Then
  721. Begin
  722. For i := Count - 1 Downto 0 do
  723. Begin
  724. AnItem := Items[i];
  725. If anItem <> nil Then anItem.Free;
  726. End;
  727. End;
  728. Inherited Clear;
  729. End;
  730. Function TGIFImageList.AddNewImage: TGIFImageListItem;
  731. Var
  732. anItem: TGIFImageListItem;
  733. Begin
  734. anitem := TGIFImageListItem.Create;
  735. Add(anItem);
  736. Result := Items[Self.Count - 1];
  737. End;
  738. Function TGIFImageList.Add(AGifImage : TGIFImageListItem): Integer;
  739. Begin
  740. Result := Inherited Add(AGifImage);
  741. End;
  742. Function TGIFImageList.Extract(Item : TGIFImageListItem): TGIFImageListItem;
  743. Begin
  744. Result := TGIFImageListItem(Inherited Extract(Item));
  745. End;
  746. Function TGIFImageList.Remove(AGifImage : TGIFImageListItem): Integer;
  747. Begin
  748. Result := Inherited Remove(AGifImage);
  749. End;
  750. Function TGIFImageList.IndexOf(AGifImage : TGIFImageListItem): Integer;
  751. Begin
  752. Result := Inherited IndexOf(AGifImage);
  753. End;
  754. Function TGIFImageList.First: TGIFImageListItem;
  755. Begin
  756. Result := TGIFImageListItem(Inherited First);
  757. End;
  758. Function TGIFImageList.Last: TGIFImageListItem;
  759. Begin
  760. Result := TGIFImageListItem(Inherited Last);
  761. End;
  762. Procedure TGIFImageList.Insert(Index : Integer; AGifImage : TGIFImageListItem);
  763. Begin
  764. Inherited Insert(Index, AGifImage);
  765. End;
  766. {%endregion%}
  767. {%region=====[ TGIFImageLoader ]================================================}
  768. Constructor TGIFImageLoader.Create;
  769. Begin
  770. Inherited Create;
  771. FFrames := TGIFImageList.Create(False);
  772. FErrorList := TStringList.Create;
  773. FErrorCount := 0;
  774. FGlobalPalette := nil;
  775. FTransparent := True;
  776. FBackgroundColor := clrTransparent;
  777. End;
  778. Destructor TGIFImageLoader.Destroy;
  779. Begin
  780. FreeAndNil(FFrames);
  781. FreeAndNil(FErrorList);
  782. Inherited Destroy;
  783. End;
  784. Function TGIFImageLoader.CheckFormat(): Boolean;
  785. Begin
  786. Result := False;
  787. // Chargement de l'en-tête
  788. Memory.Read(FGIFFileHeader, SizeOf(TGIFFileHeader));
  789. // Vérification de quelques paramètres
  790. Result := uppercase(String(FGIFFileHeader.Signature)) = 'GIF';
  791. If Result Then
  792. Begin
  793. // Le fichier est valide
  794. // On sauvegarde la version du GIF
  795. FVersion := String(FGIFFileHeader.Version);
  796. If (FVersion = GIFVersions[gv87a]) Or (FVersion = GIFVersions[gv89a]) Then Result := ReadImageProperties // On lit les propriétés
  797. Else
  798. Raise Exception.Create(rsUnknownVersion);
  799. End
  800. Else
  801. Begin
  802. // Signature du fichier GIF Invalide. On lève une exception
  803. Raise Exception.Create(Format(rsBadSignature,[uppercase(String(FGIFFileHeader.Signature))]));
  804. End;
  805. End;
  806. Function TGIFImageLoader.ReadImageProperties: Boolean;
  807. Begin
  808. Result := False;
  809. Memory.Read(FLogicalScreenChunk, SizeOf(TGIFLogicalScreenDescriptorRec));
  810. // On sauvegarde en local les dimensions de l'image, pour plus tard
  811. FWidth := FLogicalScreenChunk.ScreenWidth;
  812. FHeight := FLogicalScreenChunk.ScreenHeight;
  813. If (FWidth < 1) Or (FHeight < 1) Then
  814. Begin
  815. // Dimensions incorrectes on lève une exception
  816. Raise Exception.Create(Format(rsBadScreenSize,[FWidth,FHeight]));
  817. exit;
  818. End;
  819. FHasGlobalPalette := (FLogicalScreenChunk.PackedFields And GIF_GLOBALCOLORTABLE) <> 0;
  820. Result := True;
  821. End;
  822. Procedure TGIFImageLoader.AddError(Msg : String);
  823. Begin
  824. FErrorList.Add(Msg);
  825. End;
  826. Procedure TGIFImageLoader.NotifyError;
  827. Begin
  828. If FErrorList.Count > 0 Then
  829. Begin
  830. If Assigned(FOnLoadError) Then FOnLoadError(Self, FErrorList.Count, FErrorList);
  831. End;
  832. End;
  833. Procedure TGIFImageLoader.LoadFromStream(aStream : TStream);
  834. Begin
  835. If Memory <> nil Then FreeAndNil(Memory);
  836. Memory := TGIFFastMemoryStream.Create(aStream);
  837. If CheckFormat Then LoadFromMemory;
  838. FreeAndNil(Memory);
  839. End;
  840. Procedure TGIFImageLoader.LoadFromFile(Const FileName : String);
  841. Var
  842. Stream: TStream;
  843. Begin
  844. FErrorList.Clear;
  845. FErrorCOunt := 0;
  846. Stream := CreateFileStream(FileName);
  847. Try
  848. LoadFromStream(Stream);
  849. Finally
  850. FreeAndNil(Stream);
  851. End;
  852. End;
  853. Procedure TGIFImageLoader.LoadFromResource(Const ResName : String);
  854. Var
  855. Stream: TLazarusResourceStream;
  856. Begin
  857. FErrorList.Clear;
  858. FErrorCOunt := 0;
  859. Stream := TLazarusResourceStream.Create(ResName, nil);
  860. Try
  861. LoadFromStream(Stream);
  862. Finally
  863. FreeAndNil(Stream);
  864. End;
  865. End;
  866. Function TGIFImageLoader.GetFrameCount: Integer;
  867. Begin
  868. Result := FFrames.Count;
  869. End;
  870. Procedure TGIFImageLoader.SetCurrentLayerIndex(AValue : Integer);
  871. Begin
  872. If FCurrentLayerIndex = AValue Then Exit;
  873. FCurrentLayerIndex := AValue;
  874. End;
  875. Procedure TGIFImageLoader.LoadFromMemory();
  876. Var
  877. aRGBColor: TColorRGB24;
  878. aColor: TColor32;
  879. PaletteCount: Integer;
  880. Done: Boolean;
  881. BlockID: Byte;
  882. BlockSize: Byte;
  883. Terminator{%H-}: Byte;
  884. CurrentLayer: TGIFImageListItem;
  885. ImageDescriptor: TGIFImageDescriptorRec;
  886. GraphicControlExtensionChunk: TGIFGraphicControlExtensionRec;
  887. ApplicationExtensionChunk: TGIFApplicationExtensionRec;
  888. NSLoopExtensionChunk: TGIFNSLoopExtensionRec;
  889. PlainTextChunk: TGIFPlainTextExtensionRec;
  890. LocalPalette: TColor32List;
  891. ColorCount: Integer;
  892. DMode: Byte;
  893. ret: TLZWDecoderStatus;
  894. { Chargement palette globale }
  895. Procedure LoadGlobalPalette;
  896. Var
  897. J: Byte;
  898. Begin
  899. If FHasGlobalPalette Then
  900. Begin
  901. // Remise à zero de la palette globale si elle existe sinon création de celle-ci
  902. If FGlobalPalette = nil Then FGlobalPalette := TColor32List.Create
  903. Else
  904. FGlobalPalette.Clear;
  905. PaletteCount := 2 Shl (FLogicalScreenChunk.PackedFields And GIF_COLORTABLESIZE);
  906. // Le cas ou le nombre de couleurs serait plus grand que 256. On prend en charge.
  907. If (PaletteCount < 2) Then //or (PaletteCount>256) then
  908. Raise Exception.Create(rsScreenBadColorSize + ' : ' + IntToStr(PaletteCount));
  909. // On charge la palette
  910. For J := 0 To PaletteCount - 1 Do
  911. Begin
  912. Memory.Read(aRGBColor, SizeOF(TColorRGB24));
  913. aColor.Create(aRGBColor);
  914. FGlobalPalette.AddColor(aColor);
  915. End;
  916. End;
  917. End;
  918. { Chargement palette locale }
  919. Procedure LoadLocalPalette;
  920. Var
  921. J: Byte;
  922. Begin
  923. // Aucune palette locale n'a été assignée. On en créer une nouvelle. Sinon on efface simplement son contenu.
  924. If LocalPalette = nil Then LocalPalette := TColor32List.Create
  925. Else
  926. LocalPalette.Clear;
  927. // On verifie que le nombre de couleur dans la palette est correcte
  928. ColorCount := (2 Shl (ImageDescriptor.PackedFields And GIF_COLORTABLESIZE));
  929. // Le cas ou le nombre de couleurs serait plus grand que 256. On prend en charge qudn même et on charge la palette.
  930. If (ColorCount < 2) Then //or (ColorCount>256) then
  931. Raise Exception.Create(rsImageBadColorSize + ' : ' + IntToStr(ColorCount));
  932. // On charge la palette
  933. For J := 0 To ColorCount - 1 Do
  934. Begin
  935. Memory.Read(aRGBColor, SizeOF(TColorRGB24));
  936. aColor.Create(aRGBColor);
  937. LocalPalette.AddColor(aColor);
  938. End;
  939. End;
  940. { Lecture des extensions }
  941. Procedure ReadExtension;
  942. Var
  943. ExtensionID, BlockType: Byte;
  944. BufStr: Array[0..255] Of Char;
  945. Loops: Word;
  946. CurrentExtension : String;
  947. Begin
  948. // 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
  949. Repeat
  950. //showmessage('Read extension at '+ Memory.Position.ToString);
  951. ExtensionID := Memory.ReadByte;
  952. CurrentExtension :='';
  953. // Si c'est un nouveau marqueur d'introduction d'extension. On lit le nouvel ID
  954. If (ExtensionID = GIF_EXTENSIONINTRODUCER) Then ExtensionID := Memory.ReadByte;
  955. If (ExtensionID = 0) Then
  956. Begin
  957. // On Saute les ID Nul
  958. Repeat
  959. ExtensionID := Memory.ReadByte;
  960. Until (ExtensionID <> 0);
  961. End;
  962. Case ExtensionID Of
  963. GIF_PLAINTEXT:
  964. Begin
  965. BlockSize := Memory.ReadByte;
  966. Memory.Read(PlainTextChunk, SizeOf(TGIFPlainTextExtensionRec));
  967. Repeat
  968. // On lit la taille du bloc. Si Zero alors fin des données de l'extension
  969. BlockSize := Memory.ReadByte;
  970. // On lit la chaine de caractères
  971. If (BlockSize > 0) Then
  972. Begin
  973. fillchar({%H-}BufStr, 256, 0);
  974. Memory.Read(BufStr, BlockSize);
  975. BufStr[BlockSize] := #0;
  976. // On place le texte dans les commentaires
  977. CurrentLayer.Comment.Add(String(BufStr));
  978. End;
  979. Until (BlockSize = 0);
  980. // On ajoute une ligne vide de séparation
  981. CurrentLayer.Comment.Add('');
  982. End;
  983. GIF_COMMENTEXTENSION:
  984. Begin
  985. Repeat
  986. // On lit la taille du commentaire. Si Zero alors fin des données de l'extension
  987. BlockSize := Memory.ReadByte;
  988. // On lit la chaine de caractères
  989. If (BlockSize > 0) Then
  990. Begin
  991. Memory.Read(BufStr, BlockSize);
  992. BufStr[BlockSize] := #0;
  993. // On place le texte dans les commentaires
  994. CurrentLayer.Comment.Add(String(BufStr));
  995. End;
  996. Until (BlockSize <= 0);
  997. // On ajoute une ligne vide de séparation
  998. CurrentLayer.Comment.Add('');
  999. End;
  1000. GIF_APPLICATIONEXTENSION:
  1001. Begin
  1002. BlockSize := Memory.ReadByte;
  1003. // Certains vieux filtres d'exportation Adobe, ou d'autres logiciels utilisent par erreur une valeur de 10, ou plus petite ou trop grande
  1004. If (BlockSize <> 11) Then
  1005. Begin
  1006. FillChar(ApplicationExtensionChunk, SizeOf(TGIFApplicationExtensionRec), 0);
  1007. End;
  1008. //else if (BlockSize<11) then
  1009. // Raise Exception.Create('Bad extension size' + ' : ' + inttostr(BlockSize) +' octets. ( Taille valide = 11 octets )');
  1010. Memory.Read(ApplicationExtensionChunk, SizeOf(TGIFApplicationExtensionRec));
  1011. CurrentExtension := ApplicationExtensionChunk.AppAuthenticationCode;
  1012. Repeat
  1013. // On lit la taille du bloc. Zero si il n'y a pas de données supplémentaires
  1014. BlockSize := Memory.ReadByte;
  1015. If (BlockSize > 0) Then
  1016. Begin
  1017. if UpperCase(CurrentExtension) = 'NETSCAPE' then
  1018. begin
  1019. BlockType := Memory.ReadByte;
  1020. Dec(BlockSize);
  1021. Case (BlockType And $07) Of
  1022. GIF_LOOPEXTENSION:
  1023. Begin
  1024. // Lecture du nombre de boucle, Si Zero alors boucle infinie
  1025. Loops := Memory.ReadWord;
  1026. If Loops > 0 Then Inc(NSLoopExtensionChunk.Loops);
  1027. Dec(BlockSize, SizeOf(Loops));
  1028. End;
  1029. GIF_BUFFEREXTENSION:
  1030. Begin
  1031. // Lecture de la taille du tampon. Utilisé pour ??????
  1032. NSLoopExtensionChunk.BufferSize := Memory.ReadDWord;
  1033. Dec(BlockSize, SizeOF(NSLoopExtensionChunk.BufferSize));
  1034. End;
  1035. else // Extension NETSCAPE inconnue
  1036. begin
  1037. Memory.SeekForward(BlockSize);
  1038. //BlockSize := 0;
  1039. end;
  1040. End;
  1041. end
  1042. else
  1043. // On saute et on ignore les donnée non lues
  1044. If (BlockSize > 0) Then
  1045. Begin
  1046. Memory.SeekForward(BlockSize);
  1047. //BlockSize := 0;
  1048. End;
  1049. End;
  1050. Until (BlockSize = 0);
  1051. End;
  1052. GIF_GRAPHICCONTROLEXTENSION:
  1053. Begin
  1054. // 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.
  1055. BlockSize := Memory.ReadByte;
  1056. //if BlockSize = 4 then
  1057. //begin
  1058. Memory.Read(GraphicControlExtensionChunk, SizeOf(TGIFGraphicControlExtensionRec));
  1059. // On renseigne notre tampon d'informations pour les prochaines images décodées
  1060. DMode := ((GraphicControlExtensionChunk.PackedFields And GIF_DISPOSAL_ALL) Shr 2);
  1061. With CurrentFrameInfos Do
  1062. Begin
  1063. // Ces valeurs peuvent être utilisées pour plusieurs image. Elles restent valides jusqu'a la lecture du prochain "GCE" trouvé.
  1064. Disposal := TGIFDisposalFlag(DMode);
  1065. IsTransparent := (GraphicControlExtensionChunk.PackedFields And GIF_TRANSPARENT_FLAG) <> 0;
  1066. UserInput := (GraphicControlExtensionChunk.PackedFields And GIF_USER_INPUT_FLAG) <> 0;
  1067. TransparentColorIndex := GraphicControlExtensionChunk.TransparentColorIndex;
  1068. BackgroundColorIndex := FLogicalScreenChunk.BackgroundColorIndex;
  1069. DelayTime := GraphicControlExtensionChunk.DelayTime;
  1070. End;
  1071. // Lecture de l'octet de fin de l'extension
  1072. Terminator := Memory.ReadByte;
  1073. End;
  1074. End;
  1075. Until (ExtensionID = GIF_IMAGEDESCRIPTOR) Or Memory.EOS;
  1076. // 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
  1077. If (ExtensionID = GIF_IMAGEDESCRIPTOR) Then Memory.Seek(-1, soCurrent);
  1078. End;
  1079. { Chargement d'une image }
  1080. Procedure LoadImage;
  1081. Var
  1082. DecoderStatus{%H-}: TLZWDecoderStatus;
  1083. BufferSize, TargetBufferSize, BytesRead: Int64;
  1084. InitCodeSize: Byte;
  1085. OldPosition: Int64;
  1086. Buffer, BufferPtr: PByte;
  1087. TargetBuffer, TargetBufferPtr: PByte;
  1088. LinePtr: PColor32;
  1089. Pass, Increment: Byte;
  1090. x: Integer;
  1091. TargetColor: TColor32;
  1092. ColIdx: Byte;
  1093. CurrentLine: Integer;
  1094. OutBmp: TFastBitmap;
  1095. // Decodeur GIF LZW. Basé sour le code source de la bibliothèque GraphicEX pour Delphi
  1096. Function DecodeLZW(Var Source, Dest : Pointer; PackedSize, UnpackedSize : Integer): TLZWDecoderStatus;
  1097. Const
  1098. { Constantes pour la décompression LZW }
  1099. _LZWGIFCodeBits = 12; // Nombre maximal de bits par code d'un jeton (12 bits = 4095)
  1100. _LZWGIFCodeMax = 4096; // Nombre maximum de jeton
  1101. _LZWGIFStackSize = (2 Shl _LZWGIFCodeBits); // Taille de la pile de décompression
  1102. _LZWGIFTableSize = (1 Shl _LZWGIFCodeBits); // Taille de la table de décompression
  1103. Var
  1104. J: Integer;
  1105. Data, // Données actuelle
  1106. Bits, // Compteur de bit
  1107. Code: Cardinal; // Valeur courrante du Code
  1108. SourcePtr: PByte;
  1109. InCode: Cardinal; // Tampon pour passé le Code
  1110. CodeSize: Cardinal;
  1111. CodeMask: Cardinal;
  1112. FreeCode: Cardinal;
  1113. OldCode: Cardinal;
  1114. Prefix: Array[0.._LZWGIFTableSize] Of Cardinal; // LZW prefix
  1115. Suffix, // LZW suffix
  1116. Stack: Array [0.._LZWGIFStackSize] Of Byte;
  1117. StackPointer: PByte;
  1118. MaxStackPointer: PBYte;
  1119. Target: PByte;
  1120. FirstChar: Byte; // Tampon de décodage d'un octet
  1121. ClearCode, EOICode: Word;
  1122. MaxCode: Boolean;
  1123. Begin
  1124. Result := dsOk;
  1125. DecoderStatus := dsOk;
  1126. If (PackedSize <= 0) Or (UnpackedSize <= 0) Then
  1127. Begin
  1128. // Taille des tampons invalides
  1129. If (PackedSize <= 0) And (UnpackedSize <= 0) Then Result := dsInvalidBufferSize
  1130. Else If PackedSize <= 0 Then Result := dsInvalidInputBufferSize
  1131. Else If UnpackedSize <= 0 Then Result := dsInvalidOutputBufferSize;
  1132. Exit;
  1133. End;
  1134. // Initialisation des paramètres pour la décompression
  1135. CodeSize := InitCodeSize + 1;
  1136. ClearCode := 1 Shl InitCodeSize;
  1137. EOICode := ClearCode + 1;
  1138. FreeCode := ClearCode + 2;
  1139. OldCode := _LZWGIFCodeMax - 1;
  1140. CodeMask := (1 Shl CodeSize) - 1;
  1141. MaxCode := False;
  1142. Code := 0;
  1143. Target := PByte(Dest);
  1144. SourcePtr := PByte(Source);
  1145. // Initialisation des tables de Code
  1146. For J := 0 To _LZWGIFTableSize Do
  1147. Begin
  1148. Prefix[J] := _LZWGIFCodeMax;
  1149. Suffix[J] := J;
  1150. End;
  1151. // Initalisation de la pile
  1152. StackPointer := @Stack;
  1153. MaxStackPointer := @Stack[_LZWGIFStackSize];
  1154. FirstChar := 0;
  1155. Data := 0;
  1156. Bits := 0;
  1157. While (UnpackedSize > 0) And (PackedSize > 0) Do
  1158. Begin
  1159. // On lit le "Code" dans le tampon d'entrée
  1160. Inc(Data, SourcePtr^ Shl Bits);
  1161. Inc(Bits, 8);
  1162. While (Bits > CodeSize) And (UnpackedSize > 0) Do
  1163. Begin
  1164. // Code actuel
  1165. Code := Data And CodeMask;
  1166. // Préparation pour la donnée suivante
  1167. Data := Data Shr CodeSize;
  1168. Dec(Bits, CodeSize);
  1169. // Décompression finie ?
  1170. If Code = EOICode Then
  1171. Begin
  1172. // Si nous arrivons ici, il y a probablement quelque chose de suspect avec l'image GIF
  1173. // Car normalement on stoppe dès que le tampon de sortie est plein.
  1174. // Cela signifie que nous ne lirons jamais l'EOICode de fermeture dans les images normales.
  1175. // Comme l'état du buffer est déjà vérifié après la boucle principale, nous ne le ferons pas ici.
  1176. Break;
  1177. End;
  1178. // On vérifie s'il s'agit d'un code valide déjà enregistré
  1179. If Code > FreeCode Then
  1180. Begin
  1181. // Code ne peux à être supérieur à FreeCode. Nous avons donc une image cassée.
  1182. // On notifie l'erreur à l'utilisateur. Et on considère qu'il n'ya pas d'erreur.
  1183. DecoderStatus := dsInvalidInput;
  1184. AddError(Format(rsLZWInvalidInput,[CurrentFrameIndex]));
  1185. //NotifyUser('Le décodeur a rencontré une entrée invalide (données corrompues)');
  1186. Code := ClearCode;
  1187. //Break; //Ici, on continue le chargement du reste de l'image au lieu de le stopper
  1188. End;
  1189. // RAZ
  1190. If Code = ClearCode Then
  1191. Begin
  1192. // réinitialisation de toutes les variables
  1193. CodeSize := InitCodeSize + 1;
  1194. CodeMask := (1 Shl CodeSize) - 1; //CodeMasks[CodeSize];
  1195. FreeCode := ClearCode + 2;
  1196. OldCode := _LZWGIFCodeMax;
  1197. MaxCode := False;
  1198. End
  1199. Else If OldCode = _LZWGIFCodeMax Then
  1200. Begin
  1201. // Gestion du premier Code LZW : On le définit dans le tampon de sortie et on le conserve
  1202. FirstChar := Suffix[Code];
  1203. Target^ := FirstChar;
  1204. Inc(Target);
  1205. Dec(UnpackedSize);
  1206. OldCode := Code;
  1207. End
  1208. Else
  1209. Begin
  1210. //On conserve le Code LZW actuel
  1211. InCode := Code;
  1212. // On place le nouveau code LZW sur la pile sauf quand nous avons déjà utilisé tous les codes disponibles
  1213. If (Code = FreeCode) And Not MaxCode Then
  1214. Begin
  1215. StackPointer^ := FirstChar;
  1216. Inc(StackPointer);
  1217. Code := OldCode;
  1218. End;
  1219. // boucle pour placer les octets décodés sur la pile
  1220. While Code > ClearCode Do
  1221. Begin
  1222. StackPointer^ := Suffix[Code];
  1223. If StackPointer >= MaxStackPointer Then
  1224. Begin
  1225. // Ne doit jamais arriver, c'est juste une précaution au cas ou.
  1226. Result := dsBufferOverflow;
  1227. break;
  1228. End;
  1229. Inc(StackPointer);
  1230. Code := Prefix[Code];
  1231. End;
  1232. If Result <> dsOK Then break; // Si il ya eu des erreurs on ne va pas plus loin
  1233. // Place le nouveau Code dans la table
  1234. FirstChar := Suffix[Code];
  1235. StackPointer^ := FirstChar;
  1236. Inc(StackPointer);
  1237. //Transfert des données décodées vers notre tampon de sortie
  1238. Repeat
  1239. If UnpackedSize <= 0 Then
  1240. Begin
  1241. // Le tampon de sortie est trop petit. On ne va pas plus loin
  1242. // On notifie l'erreur à l'utilisateur. Et on considère qu'il n'ya pas d'erreur.
  1243. // Afin de pouvoir afficher le GIF et continuer le chargement des images suivantes
  1244. Result := dsOutputBufferTooSmall;
  1245. AddError(Format(rsLZWOutputBufferTooSmall,[CurrentFrameIndex]));
  1246. break;
  1247. End;
  1248. Dec(StackPointer);
  1249. Target^ := StackPointer^;
  1250. Inc(Target);
  1251. Dec(UnpackedSize);
  1252. Until StackPointer = @Stack;
  1253. If Result <> dsOK Then break;
  1254. If Not MaxCode Then
  1255. Begin
  1256. If FreeCode <= _LZWGIFCodeMax Then
  1257. Begin
  1258. Prefix[FreeCode] := OldCode;
  1259. Suffix[FreeCode] := FirstChar;
  1260. End
  1261. Else If FreeCode > _LZWGIFCodeMax Then
  1262. Begin
  1263. // On a intercepter une donnée corrompue. On continue quand la même décompression sans en tenir compte.
  1264. // On notifie juste l'erreur à l'utilisateur
  1265. DecoderStatus := dsInvalidInput;
  1266. AddError(Format(rsLZWInvalidInput,[CurrentFrameIndex]));
  1267. FreeCode := _LZWGIFCodeMax;
  1268. Prefix[FreeCode] := OldCode;
  1269. Suffix[FreeCode] := FirstChar;
  1270. //MaxCode := True;
  1271. End;
  1272. // On augmente la taille du Code si nécessaire
  1273. If (FreeCode = CodeMask) And Not (MaxCode) Then
  1274. Begin
  1275. If (CodeSize < _LZWGIFCodeBits) Then
  1276. Begin
  1277. Inc(CodeSize);
  1278. CodeMask := (1 Shl CodeSize) - 1;//CodeMasks[CodeSize];
  1279. End
  1280. Else //On a atteind la limite maximum
  1281. MaxCode := True;
  1282. End;
  1283. If FreeCode < _LZWGIFTableSize Then Inc(FreeCode);
  1284. End;
  1285. OldCode := InCode;
  1286. End;
  1287. End;
  1288. Inc(SourcePtr);
  1289. Dec(PackedSize);
  1290. If (Result <> dsOK) Or (Code = EOICode) Then Break;
  1291. End;
  1292. If Result = dsOK Then
  1293. Begin
  1294. // 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.
  1295. // Notez qu'il est normal que PackedSize soit un peu> 0 parce que nous pouvons
  1296. // pas lire l'EOICode mais arrêter dès que notre tampon de sortie est plein et
  1297. // qui devrait normalement être le code juste avant l'EOICode.
  1298. If PackedSize < 0 Then
  1299. Begin
  1300. Result := dsInternalError;
  1301. // 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.
  1302. Raise Exception.Create(rsLZWInternalErrorInputBufferOverflow);
  1303. Exit;
  1304. End;
  1305. If UnpackedSize <> 0 Then
  1306. Begin
  1307. //if UnpackedSize > 0 then
  1308. //begin
  1309. // // Image corrompue
  1310. // DecoderStatus := dsNotEnoughInput;
  1311. // 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');
  1312. // //NotifyUser('Le décodeur n''a pas pu décoder toutes les données car le tampon d''entrée est trop petit');
  1313. //End
  1314. //else
  1315. If UnpackedSize < 0 Then
  1316. Begin
  1317. Result := dsInternalError;
  1318. // 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.
  1319. Raise Exception.Create(rsLZWInternalErrorOutputBufferOverFlow);
  1320. End;
  1321. End;
  1322. End;
  1323. End;
  1324. Begin
  1325. BufferSize := 0;
  1326. TargetBufferSize := 0;
  1327. // On lit la description de l'image
  1328. Memory.Read(ImageDescriptor, SizeOf(TGIFImageDescriptorRec));
  1329. // On vérifie que les dimensions sont correctes.
  1330. // Si on trouve des dimensions à zero, il se peut qu'il faudra traiter
  1331. // une extension PlainText et dessiner ce texte en fonction des paramètres
  1332. If (ImageDescriptor.Height = 0) Or (ImageDescriptor.Width = 0) Then
  1333. Begin
  1334. // On assigne les dimensions par défaut du GIF
  1335. ImageDescriptor.Width := FLogicalScreenChunk.ScreenWidth;
  1336. ImageDescriptor.Height := FLogicalScreenChunk.ScreenHeight;
  1337. // On notifie à l'utilisateur que les dimensions de l'image sont erronée. Mais on tente le chargement quand même
  1338. // ShowMessage
  1339. End;
  1340. // 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.
  1341. If (ImageDescriptor.Width > FLogicalScreenChunk.ScreenWidth) Or (ImageDescriptor.Height > FLogicalScreenChunk.ScreenHeight) Then
  1342. Begin
  1343. // On assigne les dimensions par défaut du GIF
  1344. If (ImageDescriptor.Width > FLogicalScreenChunk.ScreenWidth) Then ImageDescriptor.Width := FLogicalScreenChunk.ScreenWidth;
  1345. If (ImageDescriptor.Height > FLogicalScreenChunk.ScreenHeight) Then ImageDescriptor.Height := FLogicalScreenChunk.ScreenHeight;
  1346. // On notifie à l'utilisateur que les dimensions de l'image sont erronée. Mais on tente le chargement quand même
  1347. // ShowMessage
  1348. End;
  1349. // On renseigne notre tampon d'informations
  1350. With CurrentFrameInfos Do
  1351. Begin
  1352. Left := ImageDescriptor.Left;
  1353. Top := ImageDescriptor.Top;
  1354. Width := ImageDescriptor.Width;
  1355. Height := ImageDescriptor.Height;
  1356. Interlaced := (ImageDescriptor.PackedFields And GIF_INTERLACED) = GIF_INTERLACED;
  1357. HasLocalPalette := (ImageDescriptor.PackedFields And GIF_LOCALCOLORTABLE) = GIF_LOCALCOLORTABLE;
  1358. End;
  1359. // L'image possède-t-elle sa propre palette de couleur ? Si oui on la charge.
  1360. If CurrentFrameInfos.HasLocalPalette Then LoadLocalPalette;
  1361. // Decompression de l'image
  1362. // On ajoute une nouvelle image si besoin
  1363. If (FCurrentLayerIndex > 0) And (FCurrentLayerIndex > FFrames.Count - 1) Then CurrentLayer := FFrames.AddNewImage;
  1364. // On assigne la nouvelle image au Bitmap de travail
  1365. OutBmp := FFrames.Items[CurrentFrameIndex].Bitmap;
  1366. // On met à jour les informations
  1367. With FFrames.Items[FCurrentLayerIndex] Do
  1368. Begin
  1369. Drawmode := CurrentFrameInfos.Disposal;
  1370. // Showmessage('#'+inttostr(FCurrentLayerIndex) + 'DrawMode : '+ GifGCEDisposalModeStr[Drawmode]);
  1371. Left := CurrentFrameInfos.Left;
  1372. Top := CurrentFrameInfos.Top;
  1373. IsTransparent := CurrentFrameInfos.IsTransparent;
  1374. If CurrentFrameInfos.DelayTime = 0 Then Delay := GIF_DefaultDelay
  1375. Else
  1376. Delay := CurrentFrameInfos.DelayTime * GIF_DelayFactor;
  1377. End;
  1378. // On lit le code d'initalisation de la compression LZW
  1379. InitCodeSize := Memory.ReadByte;
  1380. If InitCodeSize < 2 Then InitCodeSize := 2;
  1381. If InitCodeSize > 8 Then InitCodeSize := 8;
  1382. // On sauve la position actuelle dans le flux
  1383. OldPosition := Memory.position;
  1384. BufferSize := 0;
  1385. // 1) On comptabilise la taille totale des données compresser. Afin de les décompresser en une seule fois.
  1386. // On lit la taille du premier bloc
  1387. BlockSize := Memory.ReadByte;
  1388. While (BlockSize > 0) And Not (Memory.EOS) Do
  1389. Begin
  1390. Inc(BufferSize, BlockSize);
  1391. // On saute les données
  1392. Memory.SeekForward(BlockSize);
  1393. If Not (Memory.EOS) Then BlockSize := Memory.ReadByte
  1394. Else
  1395. blocksize := 0;
  1396. End;
  1397. // 2) On initalise notre bitmap avec les bonnes dimensions
  1398. OutBmp.SetSize(CurrentFrameInfos.Width, CurrentFrameInfos.Height);
  1399. BufferPtr := nil;
  1400. Buffer := nil;
  1401. // 3) On alloue notre tampon pour les données compressées
  1402. If (BufferSize > 0) Then Reallocmem(Buffer, BufferSize);
  1403. // 4) On charge toutes les données dans notre tampon
  1404. // On se replace au début des données
  1405. Memory.Seek(OldPosition, soBeginning);
  1406. // On travail toujours sur une copie du "pointer"
  1407. BufferPtr := Buffer;
  1408. // On lit la taille du premier bloque
  1409. BlockSize := Memory.ReadByte;
  1410. While (BlockSize > 0) And Not (Memory.EOS) Do
  1411. Begin
  1412. // On charge les données dans le tampon. On previent des erreurs en cas de dépassements
  1413. BytesRead := Memory.Read(BufferPtr^, BlockSize);
  1414. Inc(BufferPtr, BytesRead);
  1415. If Not (Memory.EOS) Then BlockSize := Memory.ReadByte
  1416. Else
  1417. blocksize := 0;
  1418. End;
  1419. // On se replace au debut du tampon
  1420. BufferPtr := Buffer;
  1421. // 5) On decompresse les données
  1422. // On initialise notre buffer ou seront décompressées les données
  1423. TargetBufferSize := Int64(CurrentFrameInfos.Width) * Int64(CurrentFrameInfos.Height);
  1424. TargetBufferPtr := nil;
  1425. TargetBuffer := nil;
  1426. // Si la taille est plus grande que zero, on alloue l'espace nécessaire à notre tampon
  1427. If (TargetBufferSize > 0) Then Reallocmem(TargetBuffer, TargetBufferSize);
  1428. // Décodage des données compressées
  1429. Ret := DecodeLZW(Buffer, TargetBuffer, BufferSize, TargetBufferSize);
  1430. // 6) On transfert les données de l'image vers notre bitmap. Si il n'y a pas eu d'erreurs
  1431. If (Ret = dsOk) Then
  1432. Begin
  1433. TargetBufferPtr := TargetBuffer;
  1434. OutBmp.Clear(clrTransparent);
  1435. // Image non entrelacée
  1436. If Not (CurrentFrameInfos.Interlaced) Then
  1437. Begin
  1438. CurrentLine := 0;
  1439. While (CurrentLine <= CurrentFrameInfos.Height - 1) Do
  1440. Begin
  1441. LinePtr := OutBmp.GetScanLine(CurrentLine);// FFrames.Items[CurrentFrameIndex].Bitmap.GetScanLine(CurrentLine);
  1442. For x := 0 To (CurrentFrameInfos.Width - 1) Do
  1443. Begin
  1444. // Lecture de l'index de la couleur dans la palette
  1445. ColIdx := TargetBufferPtr^;
  1446. // On utilise la palette de couleur locale
  1447. If CurrentFrameInfos.HasLocalPalette Then
  1448. Begin
  1449. If LocalPalette <> nil Then // La palette est-elle chargée ?
  1450. Begin
  1451. //if (ColIdx> ColorCount-1) then ColIdx := ColorCount -1;
  1452. If (ColIdx < ColorCount) Then TargetColor := LocalPalette.Colors[ColIdx].Value
  1453. Else
  1454. TargetColor := clrTransparent;
  1455. End
  1456. Else If FGlobalPalette <> nil Then // Non, alors on utilise la palette globale si elle est présente
  1457. Begin
  1458. //if (ColIdx> PaletteCount-1) then ColIdx := PaletteCount -1;
  1459. If (ColIdx < PaletteCount) Then TargetColor := FGlobalPalette.Colors[ColIdx].Value
  1460. Else
  1461. TargetColor := clrTransparent;
  1462. End
  1463. Else
  1464. Begin
  1465. AddError(rsEmptyColorMap);
  1466. Exit;
  1467. End;
  1468. End
  1469. Else // On utilise la palette de couleur globale
  1470. Begin
  1471. If FGlobalPalette <> nil Then
  1472. Begin
  1473. //if (ColIdx> PaletteCount-1) then ColIdx := PaletteCount -1;
  1474. If (ColIdx < PaletteCount) Then TargetColor := FGlobalPalette.Colors[ColIdx].Value
  1475. Else
  1476. TargetColor := clrTransparent;
  1477. End
  1478. Else If LocalPalette <> nil Then
  1479. Begin
  1480. //if (ColIdx> ColorCount-1) then ColIdx := ColorCount -1;
  1481. If (ColIdx > ColorCount - 1) Then //ColIdx := ColorCount -1;
  1482. TargetColor := LocalPalette.Colors[ColIdx].Value
  1483. Else
  1484. TargetColor := clrTransparent;
  1485. End
  1486. Else
  1487. Begin
  1488. AddError(rsEmptyColorMap);
  1489. Exit;
  1490. End;
  1491. End;
  1492. If CurrentFrameInfos.IsTransparent Then
  1493. Begin
  1494. If FHasGlobalPalette Then If ColIdx < FGlobalPalette.Count Then OutBmp.TransparentColor := FGlobalPalette.Colors[ColIdx].Value.ToColor
  1495. Else If ColIdx < LocalPalette.Count Then OutBmp.TransparentColor := LocalPalette.Colors[ColIdx].Value.ToColor;
  1496. If (Self.FTransparent) Then
  1497. Begin
  1498. If (ColIdx = CurrentFrameInfos.TransparentColorIndex) Then
  1499. begin
  1500. TargetColor.Alpha := 0; // clrTransparent;
  1501. end;
  1502. If (CurrentFrameInfos.TransparentColorIndex = CurrentFrameInfos.BackgroundColorIndex) Then FbackgroundColor.Alpha := 0; //clrTransparent;
  1503. End;
  1504. End;
  1505. LinePtr^ := TargetColor;
  1506. // On avance de 1 élément dans nos "pointer"
  1507. Inc(TargetBufferPtr);
  1508. Inc(LinePtr);
  1509. End;
  1510. Inc(CurrentLine);
  1511. End;
  1512. End
  1513. Else // Image entrelacée
  1514. Begin
  1515. CurrentLine := 0;
  1516. For pass := 0 To 3 Do
  1517. Begin
  1518. Case Pass Of
  1519. 0:
  1520. Begin
  1521. CurrentLine := 0;
  1522. Increment := 8;
  1523. End;
  1524. 1:
  1525. Begin
  1526. CurrentLine := 4;
  1527. Increment := 8;
  1528. End;
  1529. 2:
  1530. Begin
  1531. CurrentLine := 2;
  1532. Increment := 4;
  1533. End;
  1534. Else
  1535. Begin
  1536. CurrentLine := 1;
  1537. Increment := 2;
  1538. End;
  1539. End;
  1540. While (CurrentLine < CurrentFrameInfos.Height) Do
  1541. Begin
  1542. LinePtr :=OutBmp.GetScanLine(CurrentLine); // FFrames.Items[CurrentFrameIndex].Bitmap
  1543. For x := 0 To (FFrames.Items[CurrentFrameIndex].Bitmap.Width - 1) Do
  1544. Begin
  1545. // Lecture de l'index de la couleur dans la palette
  1546. ColIdx := TargetBufferPtr^;
  1547. // On utilise la palette de couleur locale
  1548. If CurrentFrameInfos.HasLocalPalette Then
  1549. Begin
  1550. If LocalPalette <> nil Then // La palette est-elle chargée ?
  1551. Begin
  1552. If (ColIdx < ColorCount) Then // Dans le cas contraire il s'agit d'un index pour la transparence
  1553. TargetColor := LocalPalette.Colors[ColIdx].Value;
  1554. End
  1555. Else If FGlobalPalette <> nil Then // Non, alors on utilise la palette globale si elle est présente
  1556. Begin
  1557. If (ColIdx < PaletteCount) Then //if (ColIdx< PaletteCount-1) then ColIdx := PaletteCount -1;
  1558. TargetColor := FGlobalPalette.Colors[ColIdx].Value;
  1559. End
  1560. Else
  1561. Begin
  1562. AddError(rsEmptyColorMap);
  1563. Exit;
  1564. End;
  1565. End
  1566. Else // On utilise la palette de couleur globale
  1567. Begin
  1568. If FGlobalPalette <> nil Then
  1569. Begin
  1570. If (ColIdx > PaletteCount - 1) Then ColIdx := PaletteCount - 1;
  1571. TargetColor := FGlobalPalette.Colors[ColIdx].Value;
  1572. End
  1573. Else If LocalPalette <> nil Then
  1574. Begin
  1575. If (ColIdx > ColorCount - 1) Then ColIdx := ColorCount - 1;
  1576. TargetColor := LocalPalette.Colors[ColIdx].Value;
  1577. End
  1578. Else
  1579. Begin
  1580. AddError(rsEmptyColorMap);
  1581. Exit;
  1582. End;
  1583. End;
  1584. If CurrentFrameInfos.IsTransparent Then
  1585. Begin
  1586. If FHasGlobalPalette Then If ColIdx < FGlobalPalette.Count Then OutBmp.TransparentColor := FGlobalPalette.Colors[ColIdx].Value.ToColor
  1587. Else If ColIdx < LocalPalette.Count Then OutBmp.TransparentColor := LocalPalette.Colors[ColIdx].Value.ToColor;
  1588. If (FTransparent) Then
  1589. Begin
  1590. If CurrentFrameInfos.TransparentColorIndex = colIdx Then
  1591. begin
  1592. TargetColor.Alpha := 0; // := clrTransparent;
  1593. End;
  1594. If (CurrentFrameInfos.TransparentColorIndex = CurrentFrameInfos.BackgroundColorIndex) Then FBackgroundColor.Alpha := 0;
  1595. End;
  1596. End;
  1597. LinePtr^ := TargetColor;
  1598. Inc(TargetBufferPtr);
  1599. If (CurrentLine < CurrentFrameInfos.Height - 1) Then Inc(LinePtr);
  1600. End;
  1601. Inc(CurrentLine, Increment);
  1602. End;
  1603. End;
  1604. End;
  1605. if DecoderStatus <> dsOk then
  1606. begin
  1607. //outBmp.Clear(ClrTransparent);
  1608. FFrames.Items[FCurrentLayerIndex].IsCorrupted := True;
  1609. FFrames.Items[FCurrentLayerIndex].Delay:= 1;
  1610. End;
  1611. Inc(FCurrentLayerIndex); // Index pour la prochaine image
  1612. End
  1613. Else
  1614. Begin
  1615. Case Ret Of
  1616. dsInvalidBufferSize: AddError(Format(rsInvalidBufferSize,[CurrentFrameIndex]));
  1617. dsInvalidInputBufferSize: AddError(Format(rsInvalidInputBufferSize,[CurrentFrameIndex]));
  1618. dsInvalidOutputBufferSize: AddError(Format(rsInvalidOutputBufferSize,[CurrentFrameIndex]));
  1619. dsBufferOverflow: AddError(Format(rsBufferOverFlow,[CurrentFrameIndex]));
  1620. dsOutputBufferTooSmall :
  1621. (* begin
  1622. // 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
  1623. //FFrames.Delete(CurrentFrameIndex);
  1624. end;*)
  1625. dec(FCurrentLayerIndex);
  1626. End;
  1627. if Ret<>dsOutputBufferTooSmall then
  1628. begin
  1629. FFrames.Items[FCurrentLayerIndex].IsCorrupted := True;
  1630. FFrames.Items[FCurrentLayerIndex].Delay:= 1;
  1631. end;
  1632. End;
  1633. // On libére la mémoire allouée pour nos tampons
  1634. If (TargetBufferSize > 0) And (targetBuffer <> nil) Then FreeMem(TargetBuffer);
  1635. If (BufferSize > 0) And (Buffer <> nil) Then FreeMem(Buffer);
  1636. End;
  1637. Begin
  1638. PaletteCount := 0;
  1639. ColorCount := 0;
  1640. LocalPalette := nil;
  1641. FFrames.Clear;
  1642. // Par defaut, on considère que la couleur de fond est totalement transparente
  1643. FBackgroundColor := clrTransparent;
  1644. // Si une palette globale existe, alors on charge
  1645. LoadGlobalPalette;
  1646. If FHasGlobalPalette Then
  1647. Begin
  1648. If FLogicalScreenChunk.BackgroundColorIndex < PaletteCount - 1 Then FBackgroundColor := FGlobalPalette.Colors[FLogicalScreenChunk.BackgroundColorIndex].Value
  1649. Else
  1650. Begin
  1651. FBackgroundColor := clrTransparent; //FGlobalPalette.Colors[FLogicalScreenChunk.BackgroundColorIndex].Value;
  1652. End;
  1653. End;
  1654. // Les valeurs suivante seront renseignées lors du chargement d'une image
  1655. // 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
  1656. With CurrentFrameInfos Do
  1657. Begin
  1658. Left := 0;
  1659. Top := 0;
  1660. Width := FLogicalScreenChunk.ScreenWidth;
  1661. Height := FLogicalScreenChunk.ScreenHeight;
  1662. Interlaced := False;
  1663. HasLocalPalette := False;
  1664. IsTransparent := False;
  1665. End;
  1666. // On ajoute l'image de départ afin de pouvoir assigner les valeurs des premières extensions (Extensions déclarées avant l'image)
  1667. CurrentLayer := FFrames.AddNewImage;
  1668. // On efface l'image avec la couleur de fond
  1669. //CurrentLayer.Bitmap.Clear(FBackgroundColor);
  1670. FCurrentLayerIndex := 0;
  1671. // On lit le 1er octet
  1672. Done := False;
  1673. While Not (Done) Do
  1674. Begin
  1675. // On verifie l'existence d'extensions avant les données de l'image (Application, Graphic Control, PlainText, Comment)
  1676. If Not (Memory.EOS) Then BlockID := Memory.ReadByte
  1677. Else
  1678. BlockID := GIF_Trailer;
  1679. If (BlockID = GIF_Trailer) Then
  1680. Begin
  1681. Done := True;
  1682. End;
  1683. If (BlockID = 0) Then
  1684. Begin
  1685. // On Saute les ID Nul
  1686. While (BlockId = 0) Do BlockId := Memory.ReadByte;
  1687. End
  1688. Else If (BlockID = GIF_IMAGEDESCRIPTOR) Then // C'est une image
  1689. Begin
  1690. // On charge l'image
  1691. LoadImage;
  1692. End
  1693. Else If (BlockID = GIF_EXTENSIONINTRODUCER) Then // c'est une extension
  1694. Begin
  1695. ReadExtension; // On charge toutes les extensions qui sont à la suite
  1696. End
  1697. Else
  1698. Begin
  1699. // Extension inconnue on saute jusqu'a trouver un ZERO.
  1700. // A Verifier avec le flag UseInput dans le "Graphic Control Extension"
  1701. // Ici on ignore simplement les données
  1702. While BlockID <> 0 Do
  1703. Begin
  1704. BlockID := Memory.ReadByte;
  1705. End;
  1706. End;
  1707. End;
  1708. // Si il y a des erreurs elles seront notifier à l'utilisateur
  1709. NotifyError;
  1710. // Il n'y a aucune images on notifie l'erreur
  1711. If FFrames.Count = 0 Then Raise Exception.Create(rsEmptyImage);
  1712. // On libere la mémoire, prise par nos palettes de couleurs si besoin
  1713. If (LocalPalette <> nil) Then
  1714. Begin
  1715. FreeAndNil(LocalPalette);
  1716. End;
  1717. If (FGlobalPalette <> nil) Then
  1718. Begin
  1719. FreeAndNil(FGlobalPalette);
  1720. End;
  1721. End;
  1722. {%endregion%}
  1723. {%region=====[ TGIFRenderCacheListItem ]========================================}
  1724. Constructor TGIFRenderCacheListItem.Create;
  1725. Begin
  1726. Inherited Create;
  1727. FBitmap := Graphics.TBitmap.Create;
  1728. FDelay := 0;
  1729. End;
  1730. Destructor TGIFRenderCacheListItem.Destroy;
  1731. Begin
  1732. FreeAndNil(FBitmap);
  1733. Inherited Destroy;
  1734. End;
  1735. {%endregion%}
  1736. {%region=====[ TGIFRenderCacheList ]============================================}
  1737. Function TGIFRenderCacheList.GetItems(Index : Integer): TGIFRenderCacheListItem;
  1738. Begin
  1739. Result := TGIFRenderCacheListItem(Inherited Items[Index]);
  1740. End;
  1741. Procedure TGIFRenderCacheList.SetItems(Index : Integer; AGIFRenderCache : TGIFRenderCacheListItem);
  1742. Begin
  1743. Put(Index, AGIFRenderCache);
  1744. End;
  1745. Procedure TGIFRenderCacheList.Clear;
  1746. Var
  1747. anItem: TGIFRenderCacheListItem;
  1748. i: Integer;
  1749. Begin
  1750. If Count > 0 Then
  1751. Begin
  1752. For i := Count - 1 Downto 0 do
  1753. Begin
  1754. AnItem := Items[i];
  1755. If anItem <> nil Then anItem.Free;
  1756. End;
  1757. End;
  1758. Inherited Clear;
  1759. End;
  1760. Function TGIFRenderCacheList.AddNewCache: TGIFRenderCacheListItem;
  1761. Var
  1762. anItem: TGIFRenderCacheListItem;
  1763. Begin
  1764. anitem := TGIFRenderCacheListItem.Create;
  1765. Add(anItem);
  1766. Result := Items[Self.Count - 1];
  1767. End;
  1768. Function TGIFRenderCacheList.Add(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
  1769. Begin
  1770. Result := Inherited Add(AGIFRenderCache);
  1771. End;
  1772. Function TGIFRenderCacheList.Extract(Item : TGIFRenderCacheListItem): TGIFRenderCacheListItem;
  1773. Begin
  1774. Result := TGIFRenderCacheListItem(Inherited Extract(Item));
  1775. End;
  1776. Function TGIFRenderCacheList.Remove(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
  1777. Begin
  1778. Result := Inherited Remove(AGIFRenderCache);
  1779. End;
  1780. Function TGIFRenderCacheList.IndexOf(AGIFRenderCache : TGIFRenderCacheListItem): Integer;
  1781. Begin
  1782. Result := Inherited IndexOf(AGIFRenderCache);
  1783. End;
  1784. Function TGIFRenderCacheList.First: TGIFRenderCacheListItem;
  1785. Begin
  1786. Result := TGIFRenderCacheListItem(Inherited First);
  1787. End;
  1788. Function TGIFRenderCacheList.Last: TGIFRenderCacheListItem;
  1789. Begin
  1790. Result := TGIFRenderCacheListItem(Inherited Last);
  1791. End;
  1792. Procedure TGIFRenderCacheList.Insert(Index : Integer; AGIFRenderCache : TGIFRenderCacheListItem);
  1793. Begin
  1794. Inherited Insert(Index, AGIFRenderCache);
  1795. End;
  1796. Function TGIFRenderCacheList.IsIndexOk(anIndex: Integer): Boolean;
  1797. Begin
  1798. Result := True;
  1799. If (anIndex < 0) or (anIndex > Count-1) then result := False;
  1800. End;
  1801. Procedure TGIFRenderCacheList.Pack;
  1802. Var
  1803. i: Integer;
  1804. Begin
  1805. if Count>1 then
  1806. begin
  1807. I := 0;
  1808. While I<Count do
  1809. begin
  1810. if Items[I].IsCorrupted then
  1811. begin
  1812. Remove(Items[I]);
  1813. break;
  1814. End;
  1815. inc(I);
  1816. End;
  1817. if I<Count then Pack;
  1818. End;
  1819. End;
  1820. {%endregion%}
  1821. {%region=====[ TGIFViewer ]=====================================================}
  1822. Constructor TGIFViewer.Create(AOwner: TComponent);
  1823. Begin
  1824. Inherited Create(AOwner);
  1825. ControlStyle := [csCaptureMouse, csClickEvents, csDoubleClicks];
  1826. AutoSize := False;
  1827. FCenter := False;
  1828. FStretch := False;
  1829. FTransparent := True;
  1830. With GetControlClassDefaultSize Do SetInitialBounds(0, 0, CX, CY);
  1831. FRestoreBitmap := nil;
  1832. FRenderCache := TGIFRenderCacheList.Create(False);
  1833. FGIFLoader := TGIFImageLoader.Create;
  1834. FGIFLoader.OnLoadError := @DoInternalOnLoadError;
  1835. FVirtualView := TFastBitmap.Create;
  1836. FCurrentView := nil;
  1837. FCurrentView := Graphics.TBitmap.Create;
  1838. FRestoreBitmap := nil;
  1839. FAutoPlay := False;
  1840. FBorderShow := False;
  1841. FBorderColor := clBlack;
  1842. FBorderWidth := 1;
  1843. FBevelInner := bvNone;
  1844. FBevelOuter := bvNone;
  1845. FBevelWidth := 1;
  1846. FColor := clNone;
  1847. FDisplayInvalidFrames := False;
  1848. FAutoRemoveInvalidFrame := True;
  1849. FLastDrawMode := dmNone;
  1850. FAnimateTimer := TTimer.Create(nil);
  1851. With FAnimateTimer Do
  1852. Begin
  1853. Enabled := False;
  1854. Interval := 1000;
  1855. OnTimer := @DoTimerAnimate;
  1856. End;
  1857. FAnimateSpeed := 1;
  1858. FCurrentFrameIndex := 0;
  1859. FGIFWidth := 90;
  1860. FGIFHeight := 90;
  1861. FAutoStretchMode := smManual;
  1862. End;
  1863. Destructor TGIFViewer.Destroy;
  1864. Begin
  1865. FAnimateTimer.Enabled := False;
  1866. FreeAndNil(FAnimateTimer);
  1867. If FCurrentView <> nil Then FreeAndNil(FCurrentView);
  1868. If FRestoreBitmap <> nil Then FreeAndNil(FRestoreBitmap);
  1869. FreeAndNil(FVirtualView);
  1870. FRenderCache.Clear;
  1871. FreeAndNil(FRenderCache);
  1872. FreeAndNil(FGIFLoader);
  1873. Inherited Destroy;
  1874. End;
  1875. Procedure TGIFViewer.SetCenter(Const Value: Boolean);
  1876. Begin
  1877. If Value = FCenter Then exit;
  1878. FCenter := Value;
  1879. Invalidate;
  1880. End;
  1881. Function TGIFViewer.GetCanvas: TCanvas;
  1882. Begin
  1883. Result := Inherited Canvas;// FCurrentView.Canvas
  1884. End;
  1885. Function TGIFViewer.GetFrameCount: Integer;
  1886. Begin
  1887. If FCache Then
  1888. Result := FRenderCache.Count
  1889. Else Begin
  1890. Result := FGifLoader.FrameCount;
  1891. End;
  1892. End;
  1893. Function TGIFViewer.GetGIFVersion: String;
  1894. Begin
  1895. Result := FGIFLoader.Version;
  1896. End;
  1897. Function TGIFViewer.GetRawFrameItem(Index : Integer): TGIFImageListItem;
  1898. Begin
  1899. Result := nil;
  1900. If (Index >= 0) And (Index < FGIFLoader.FrameCount) Then Result := FGIFLoader.Frames[Index];
  1901. end;
  1902. Procedure TGIFViewer.SetAutoStretchMode(AValue: TGIFAutoStretchMode);
  1903. Begin
  1904. If FAutoStretchMode = AValue Then Exit;
  1905. FAutoStretchMode := AValue;
  1906. Invalidate;
  1907. End;
  1908. Procedure TGIFViewer.SetStretch(Const Value: Boolean);
  1909. Begin
  1910. If Value = FStretch Then exit;
  1911. FStretch := Value;
  1912. Invalidate;
  1913. End;
  1914. Procedure TGIFViewer.SetPause(Const Value: Boolean);
  1915. Begin
  1916. If Value = FPause Then exit;
  1917. FPause := Value;
  1918. If FPause Then FAnimateTimer.Enabled := False;
  1919. If Assigned(FOnPause) Then FOnPause(Self);
  1920. End;
  1921. Procedure TGIFViewer.SetFileName(Const Value: String);
  1922. Begin
  1923. If Value = FFileName Then exit;
  1924. FFileName := Value;
  1925. LoadFromFile(FFileName);
  1926. End;
  1927. Function TGIFViewer.GetFrame(Const Index: Integer): Graphics.TBitmap;
  1928. Begin
  1929. Result := nil;
  1930. If (Index >= 0) And (Index < FrameCount) Then Result := FRenderCache.Items[Index].Bitmap;
  1931. End;
  1932. Procedure TGIFViewer.SetTransparent(Const Value: Boolean);
  1933. Begin
  1934. If FTransparent = Value Then exit;
  1935. FTransparent := Value;
  1936. FGIFLoader.Transparent := Value;
  1937. If FFileName <> '' Then LoadFromFile(FFileName);
  1938. End;
  1939. Procedure TGIFViewer.SetBevelWidth(Const Value: TBevelWidth);
  1940. Begin
  1941. If FBevelWidth <> Value Then
  1942. Begin
  1943. FBevelWidth := Value;
  1944. Invalidate;
  1945. End;
  1946. End;
  1947. Procedure TGIFViewer.ResetCurrentView;
  1948. Var
  1949. I: Integer;
  1950. Corrupted : Boolean;
  1951. begin
  1952. if FRenderCache.Count>1 then
  1953. begin
  1954. if not(FDisplayInvalidFrames) then
  1955. begin
  1956. Corrupted := false;
  1957. i := 0;
  1958. Repeat
  1959. Corrupted := FRenderCache.Items[i].IsCorrupted;
  1960. inc(i);
  1961. until (i>FRenderCache.Count-1) or (Corrupted = false);
  1962. if (i>FRenderCache.Count-1) and (Corrupted = true) then
  1963. begin
  1964. Raise Exception.Create(rsAllFrameCorrupted);
  1965. exit;
  1966. end
  1967. else
  1968. begin
  1969. Dec(i);
  1970. FCurrentframeIndex := i;
  1971. FAnimateTimer.Interval := FRenderCache.Items[i].Delay;
  1972. FCurrentView.Assign(FRenderCache.Items[i].Bitmap);
  1973. end;
  1974. end
  1975. else
  1976. begin
  1977. FAnimateTimer.Interval := FRenderCache.Items[0].Delay;
  1978. FCurrentView.Assign(FRenderCache.Items[0].Bitmap);
  1979. end;
  1980. end
  1981. else
  1982. begin
  1983. FCurrentView.Assign(FRenderCache.Items[0].Bitmap);
  1984. end;
  1985. FLastDrawMode := dmNone;
  1986. End;
  1987. Procedure TGIFViewer.SetBevelInner(Const Value: TPanelBevel);
  1988. Begin
  1989. If BevelInner <> Value Then
  1990. Begin
  1991. FBevelInner := Value;
  1992. Invalidate;
  1993. End;
  1994. End;
  1995. Procedure TGIFViewer.SetBevelOuter(Const Value: TPanelBevel);
  1996. Begin
  1997. If BevelOuter <> Value Then
  1998. Begin
  1999. FBevelOuter := Value;
  2000. Invalidate;
  2001. End;
  2002. End;
  2003. Procedure TGIFViewer.DoInternalOnLoadError(Sender: TObject; Const ErrorCount: Integer; Const ErrorList: TStringList);
  2004. Begin
  2005. If Assigned(FOnLoadError) Then FOnloadError(Self, ErrorCount, ErrorList);
  2006. End;
  2007. Procedure TGIFViewer.DoTimerAnimate(Sender: TObject);
  2008. Begin
  2009. Inc(FCurrentFrameIndex);
  2010. If FCurrentFrameIndex > (FGIFLoader.FrameCount - 1) Then FCurrentFrameIndex := 0;
  2011. If (not FCache) and (FCurrentFrameIndex >= FRenderCache.Count) Then
  2012. Begin
  2013. RenderFrame(FCurrentFrameIndex);
  2014. End;
  2015. If Assigned(FOnFrameChange) Then FOnFrameChange(Self);
  2016. if not(FDisplayInvalidFrames) then
  2017. begin
  2018. if not(FRenderCache.Items[FCurrentFrameIndex].IsCorrupted) then
  2019. begin
  2020. FAnimateTimer.Interval := FRenderCache.Items[FCurrentFrameIndex].Delay;
  2021. FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
  2022. End
  2023. else FAnimateTimer.Interval := FRenderCache.Items[FCurrentFrameIndex].Delay;
  2024. end
  2025. else
  2026. begin
  2027. FAnimateTimer.Interval := FRenderCache.Items[FCurrentFrameIndex].Delay;
  2028. FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
  2029. end;
  2030. Invalidate;
  2031. End;
  2032. Procedure TGIFViewer.RenderFrame(Index: Integer);
  2033. Var
  2034. Src: TFastBitmap;
  2035. pTop, pLeft: Integer;
  2036. iDrawMode: TFastBitmapDrawMode;
  2037. TmpBmp : Graphics.TBitmap;
  2038. Begin
  2039. Src := FGIFLoader.Frames.Items[Index].Bitmap;
  2040. pLeft := FGIFLoader.Frames.Items[Index].Left;
  2041. pTop := FGIFLoader.Frames.Items[Index].Top;
  2042. FRenderCache.AddNewCache;
  2043. FRenderCache.Items[Index].Delay := FGIFLoader.Frames[Index].Delay * FAnimateSpeed;
  2044. FRenderCache.Items[Index].IsCorrupted := FGIFLoader.Frames[Index].IsCorrupted;
  2045. If (FTransparent) Then
  2046. Begin
  2047. iDrawMode := dmAlphaCheck;
  2048. End
  2049. Else
  2050. Begin
  2051. iDrawMode := dmSet;
  2052. End;
  2053. If Index = 0 Then
  2054. Begin
  2055. If (FTransparent) Then
  2056. Begin
  2057. FVirtualView.Clear(clrTransparent);
  2058. End
  2059. Else
  2060. Begin
  2061. FVirtualView.Clear(FGIFLoader.BackgroundColor);
  2062. End;
  2063. FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, dmSet);
  2064. if FGIFLoader.Frames.Items[0].DrawMode = dmKeep then begin
  2065. if Assigned( FRestoreBitmap) then begin
  2066. FRestoreBitmap.Free;
  2067. end;
  2068. FRestoreBitmap := FVirtualView.Clone;
  2069. end;
  2070. End
  2071. Else
  2072. Begin
  2073. With FGIFLoader.Frames.Items[Index] Do
  2074. Begin
  2075. Case DrawMode Of
  2076. dmNone:
  2077. Begin
  2078. FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, iDrawMode);
  2079. End;
  2080. dmKeep:
  2081. Begin
  2082. if FLastDrawMode = dmErase then
  2083. begin
  2084. If (FGIFLoader.Frames.Items[Index].IsTransparent And FTransparent) Then FVirtualView.Clear(clrTransparent)
  2085. Else
  2086. FVirtualView.Clear(FGIFLoader.BackgroundColor);
  2087. end;
  2088. FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, iDrawMode);
  2089. If Assigned(FRestoreBitmap) Then FreeAndNil(FRestoreBitmap);
  2090. FRestoreBitmap := FVirtualView.Clone;
  2091. End;
  2092. dmErase:
  2093. Begin
  2094. If (FGIFLoader.Frames.Items[Index].IsTransparent And FTransparent) Then FVirtualView.Clear(clrTransparent)
  2095. Else
  2096. FVirtualView.Clear(FGIFLoader.BackgroundColor);
  2097. FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, iDrawMode);
  2098. End;
  2099. dmRestore:
  2100. Begin
  2101. if FLastDrawMode = dmErase then
  2102. begin
  2103. If (FGIFLoader.Frames.Items[Index].IsTransparent And FTransparent) Then FVirtualView.Clear(clrTransparent)
  2104. Else
  2105. FVirtualView.Clear(FGIFLoader.BackgroundColor);
  2106. End;
  2107. If Assigned(FRestoreBitmap) Then FVirtualView.PutImage(FRestoreBitmap, 0, 0, FRestoreBitmap.Width, FRestoreBitmap.Height, 0, 0, dmSet)
  2108. else
  2109. begin
  2110. If (FGIFLoader.Frames.Items[Index].IsTransparent And FTransparent) Then FVirtualView.Clear(clrTransparent)
  2111. Else
  2112. FVirtualView.Clear(FGIFLoader.BackgroundColor);
  2113. end;
  2114. FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, iDrawMode);
  2115. End;
  2116. Else
  2117. FVirtualView.PutImage(Src, 0, 0, Src.Width, Src.Height, pLeft, pTop, dmSet);
  2118. End;
  2119. FLastDrawMode := DrawMode;
  2120. End;
  2121. End;
  2122. // Note : Sous MacOS on ne peux pas assigner FRenderCache.Items[Index].Bitmap directement avec
  2123. // FVirtualView.GetBitmap; On est obligé de créer le bitmap de destination et utiliser Assign.
  2124. // Dans le cas contraire seulment la première image sera affichée.
  2125. //TmpBmp := Graphics.TBitmap.Create; <== MEMORY LEAK
  2126. TmpBmp := FVirtualView.GetBitmap;
  2127. FRenderCache.Items[Index].Bitmap.Assign(TmpBmp);
  2128. FreeAndNil(TmpBmp);
  2129. End;
  2130. Procedure TGIFViewer.ComputeCache;
  2131. Var
  2132. I: Integer;
  2133. Begin
  2134. FCurrentFrameIndex := 0;
  2135. FRenderCache.Clear;
  2136. If FGIFLoader.FrameCount > 0 Then
  2137. Begin
  2138. For I := 0 To Pred(FGIFLoader.FrameCount) Do
  2139. Begin
  2140. RenderFrame(I);
  2141. End;
  2142. end;
  2143. if AutoRemoveInvalidFrame then FRenderCache.Pack;
  2144. ResetCurrentView;
  2145. End;
  2146. Procedure TGIFViewer.CalculatePreferredSize(Var PreferredWidth, PreferredHeight: Integer; WithThemeSpace: Boolean);
  2147. Var
  2148. extraWidth: Integer;
  2149. Begin
  2150. extraWidth := - 2;
  2151. if FBorderShow then extraWidth := (FBorderWidth * 2) + (FBevelWidth * 2);
  2152. PreferredWidth := FGIFWidth + extraWidth + 2;
  2153. PreferredHeight := FGIFHeight + extraWidth + 2;
  2154. End;
  2155. Class Function TGIFViewer.GetControlClassDefaultSize: TSize;
  2156. Begin
  2157. Result.CX := 90; // = ClientWidth
  2158. Result.CY := 90; // = ClientHeight
  2159. End;
  2160. Function TGIFViewer.DestRect: TRect;
  2161. Var
  2162. PicWidth, PicHeight: Integer;
  2163. ImgWidth, ImgHeight: Integer;
  2164. n: Integer;
  2165. procedure KeepAspectRatio( Var aWidth, aHeight : Integer; MaxWidth, MaxHeight : Integer);
  2166. var
  2167. w, h : Integer;
  2168. begin
  2169. w := MaxWidth;
  2170. h := (aHeight * w) Div aWidth;
  2171. If h > MaxHeight Then
  2172. Begin
  2173. h := MaxHeight;
  2174. w := (aWidth * h) Div aHeight;
  2175. End;
  2176. aWidth := w;
  2177. aHeight := h;
  2178. End;
  2179. Begin
  2180. PicWidth := FCurrentView.Width;
  2181. PicHeight := FCurrentView.Height;
  2182. ImgWidth := ClientWidth;
  2183. ImgHeight := ClientHeight;
  2184. If (PicWidth = 0) Or (PicHeight = 0) Then Exit(Rect(0, 0, 0, 0));
  2185. if FAutoStretchMode <> smManual then
  2186. begin
  2187. Case FAutoStretchMode of
  2188. smStretchAll : FStretch := True;
  2189. smStretchOnlyBigger : if (PicWidth > ImgWidth) or (PicHeight > ImgHeight) then FStretch := True else FStretch := False;
  2190. smStretchOnlySmaller : if (PicWidth < ImgWidth) and (PicHeight < ImgHeight) then FStretch := True else FStretch := False;
  2191. end;
  2192. if Assigned(FOnStretchChanged) then FOnStretchChanged(Self,FStretch);
  2193. End;
  2194. If FStretch Then
  2195. Begin
  2196. KeepAspectRatio(PicWidth, PicHeight,ImgWidth, ImgHeight);
  2197. End;
  2198. n := FBorderWidth + FBevelWidth;
  2199. If FBorderShow Then
  2200. Begin
  2201. Result := Rect(n, n, n + PicWidth, n + PicHeight);
  2202. End
  2203. Else
  2204. Result := Rect(0, 0, PicWidth, PicHeight);
  2205. If FCenter Then
  2206. Begin
  2207. If FBorderShow Then
  2208. Begin
  2209. Result.Left := n + ((ClientWidth -(n+n)) - PicWidth) shr 1;
  2210. Result.Top := n + ((ClientHeight-(n+n)) - PicHeight) shr 1;
  2211. end
  2212. else
  2213. begin
  2214. Result.Left := ((ClientWidth - PicWidth) shr 1);
  2215. Result.Top := ((ClientHeight - PicHeight) shr 1);
  2216. end;
  2217. Result.Right := Result.Left + PicWidth;
  2218. Result.Bottom := Result.Top + PicHeight;
  2219. End;
  2220. End;
  2221. Procedure TGIFViewer.Paint;
  2222. Procedure DrawFrame;
  2223. Begin
  2224. With Inherited Canvas Do
  2225. Begin
  2226. Pen.Color := clBlack;
  2227. Pen.Style := psDash;
  2228. MoveTo(0, 0);
  2229. LineTo(Self.Width - 1, 0);
  2230. LineTo(Self.Width - 1, Self.Height - 1);
  2231. LineTo(0, Self.Height - 1);
  2232. LineTo(0, 0);
  2233. End;
  2234. End;
  2235. Var
  2236. R: TRect;
  2237. C: TCanvas;
  2238. ARect: TRect;
  2239. w: Integer;
  2240. Begin
  2241. If csDesigning In ComponentState Then DrawFrame;
  2242. C := Inherited Canvas;
  2243. FPainting := True;
  2244. R := DestRect;
  2245. Try
  2246. C.Lock;
  2247. // Fond
  2248. If (FColor <> clNone) Then //and Not(FTransparent)
  2249. Begin
  2250. With C Do
  2251. Begin
  2252. Brush.Style := bsSolid;
  2253. Brush.Color := FColor;
  2254. FillRect(0, 0, ClientWidth, ClientHeight);
  2255. End;
  2256. End;
  2257. // Bitmap
  2258. FCurrentView.Transparent := FTransparent;
  2259. C.StretchDraw(R, FCurrentView);
  2260. // Bordures
  2261. If FBorderShow Then
  2262. Begin
  2263. ARect := rect(0, 0, ClientWidth, ClientHeight);
  2264. w := FBevelWidth;
  2265. If (FBevelInner <> bvNone) And (w > 0) Then C.Frame3d(ARect, w, BevelInner); // Note: Frame3D inflates ARect
  2266. InflateRect(ARect, -(FBorderWidth + 1), -(FBorderWidth + 1));
  2267. If (FBevelOuter <> bvNone) And (w > 0) Then C.Frame3d(ARect, w, BevelOuter);
  2268. If FBorderWidth > 0 Then With C Do
  2269. Begin
  2270. Pen.Style := psSolid;
  2271. Pen.Width := FBorderWidth;
  2272. Pen.Color := FBorderColor;
  2273. Brush.Style := bsClear;
  2274. Rectangle(0, 0, ClientWidth, ClientHeight);
  2275. End;
  2276. End;
  2277. C.UnLock;
  2278. Finally
  2279. FPainting := False;
  2280. End;
  2281. Inherited Paint;
  2282. End;
  2283. Procedure TGIFViewer.Loaded;
  2284. begin
  2285. if FFileName<>'' then LoadFromFile(FFileName);
  2286. inherited Loaded;
  2287. end;
  2288. procedure TGIFViewer.BeforeLoad;
  2289. begin
  2290. FAnimateTimer.Enabled := False;
  2291. FPause := False;
  2292. FAnimated := False;
  2293. FCurrentFrameIndex := 0;
  2294. end;
  2295. procedure TGIFViewer.AfterLoad;
  2296. begin
  2297. FGIFWidth := FGIFLoader.Width;
  2298. FGIFHeight := FGIFLoader.Height;
  2299. FVirtualView.SetSize(FGIFWidth, FGIFHeight);
  2300. if FCache then
  2301. ComputeCache
  2302. else begin
  2303. FRenderCache.Clear;
  2304. FCurrentFrameIndex := 0;
  2305. RenderFrame(0);
  2306. ResetCurrentView;
  2307. end;
  2308. If AutoSize Then
  2309. Begin
  2310. InvalidatePreferredSize;
  2311. AdjustSize;
  2312. End;
  2313. Invalidate;
  2314. If FAutoPlay Then Start;
  2315. end;
  2316. Procedure TGIFViewer.Invalidate;
  2317. Begin
  2318. If FPainting Then exit;
  2319. Inherited Invalidate;
  2320. End;
  2321. Procedure TGIFViewer.LoadFromStream(aStream : TStream);
  2322. Begin
  2323. BeforeLoad;
  2324. FGIFLoader.FErrorList.Clear;
  2325. FGIFLoader.FErrorCOunt := 0;
  2326. FGIFLoader.LoadFromStream(aStream);
  2327. AfterLoad;
  2328. End;
  2329. Procedure TGIFViewer.LoadFromFile(Const aFileName: String);
  2330. Begin
  2331. BeforeLoad;
  2332. if Not(FileExists(aFileName)) then
  2333. begin
  2334. MessageDlg(Format(rsFileNotFound,[aFileName]), mtError, [mbOK],0);
  2335. Exit;
  2336. end;
  2337. FGIFLoader.LoadFromFile(aFileName);
  2338. FFileName := aFileName;
  2339. AfterLoad;
  2340. End;
  2341. Procedure TGIFViewer.LoadFromResource(Const ResName: String);
  2342. Var
  2343. Resource: TLResource;
  2344. Begin
  2345. BeforeLoad;
  2346. Resource := LazarusResources.Find(ResName);
  2347. If Resource = nil Then Raise Exception.Create(Format(rsResourceNotFound,[ResName]))
  2348. Else If CompareText(LazarusResources.Find(ResName).ValueType, 'gif') = 0 Then
  2349. Begin
  2350. FGIFLoader.LoadFromResource(ResName);
  2351. AfterLoad;
  2352. End;
  2353. End;
  2354. Procedure TGIFViewer.Start;
  2355. Begin
  2356. If Not (FPause) Then FCurrentFrameIndex := 0;
  2357. FPause := False;
  2358. FAnimated := True;
  2359. FAnimateTimer.Enabled := True;
  2360. If Assigned(FOnStart) Then FOnStart(Self);
  2361. End;
  2362. Procedure TGIFViewer.Stop;
  2363. Begin
  2364. FAnimateTimer.Enabled := False;
  2365. FAnimated := False;
  2366. FPause := False;
  2367. If Assigned(FOnStop) Then FOnStop(Self);
  2368. FCurrentframeIndex := 0;
  2369. ResetCurrentView;
  2370. Invalidate;
  2371. End;
  2372. Procedure TGIFViewer.Pause;
  2373. Begin
  2374. FAnimateTimer.Enabled := False;
  2375. FPause := True;
  2376. End;
  2377. Procedure TGIFViewer.NextFrame;
  2378. begin
  2379. if FCurrentFrameIndex < FGifLoader.FrameCount - 1 then
  2380. begin
  2381. Inc(FCurrentFrameIndex);
  2382. repeat
  2383. If (not FCache) and (FCurrentFrameIndex >= FRenderCache.Count) Then
  2384. begin
  2385. RenderFrame(FCurrentFrameIndex);
  2386. end;
  2387. If Assigned(FOnFrameChange) Then FOnFrameChange(Self);
  2388. if not(FDisplayInvalidFrames) then
  2389. begin
  2390. if not(FRenderCache.Items[FCurrentFrameIndex].IsCorrupted) then
  2391. begin
  2392. FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
  2393. End
  2394. Else If FCurrentFrameIndex > 0 Then
  2395. Begin
  2396. Inc(FCurrentFrameIndex);
  2397. Continue;
  2398. End;
  2399. end
  2400. else
  2401. begin
  2402. FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
  2403. end;
  2404. Break;
  2405. until False;
  2406. FAnimateTimer.Interval := FRenderCache.Items[FCurrentFrameIndex].Delay;
  2407. end;
  2408. Invalidate;
  2409. end;
  2410. Procedure TGIFViewer.PriorFrame;
  2411. begin
  2412. if FCurrentFrameIndex > 0 then
  2413. begin
  2414. Dec(FCurrentFrameIndex);
  2415. repeat
  2416. If Assigned(FOnFrameChange) Then FOnFrameChange(Self);
  2417. if not(FDisplayInvalidFrames) then
  2418. begin
  2419. if not(FRenderCache.Items[FCurrentFrameIndex].IsCorrupted) then
  2420. begin
  2421. FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
  2422. End
  2423. Else If FCurrentFrameIndex > 0 Then
  2424. Begin
  2425. Dec(FCurrentFrameIndex);
  2426. Continue;
  2427. End;
  2428. end
  2429. else
  2430. begin
  2431. FCurrentView.Assign(FRenderCache.Items[FCurrentFrameIndex].Bitmap);
  2432. end;
  2433. Break;
  2434. until False;
  2435. FAnimateTimer.Interval := FRenderCache.Items[FCurrentFrameIndex].Delay;
  2436. end;
  2437. Invalidate;
  2438. end;
  2439. Function TGIFViewer.GetRawFrame(Index: Integer): TBitmap;
  2440. Begin
  2441. Result := FGIFLoader.Frames[Index].Bitmap.GetBitmap;
  2442. End;
  2443. Procedure TGIFViewer.DisplayFrame(Index: Integer);
  2444. Begin
  2445. If not(FRenderCache.IsIndexOk(Index)) then exit;
  2446. if Not(DisplayInvalidFrames) then
  2447. begin
  2448. if FRenderCache.Items[Index].IsCorrupted then
  2449. begin
  2450. inc(Index);
  2451. DisplayFrame(Index);
  2452. End
  2453. else
  2454. begin
  2455. FCurrentView.Assign(FRenderCache.Items[Index].Bitmap);
  2456. End;
  2457. end
  2458. else
  2459. begin
  2460. FCurrentView.Assign(FRenderCache.Items[Index].Bitmap);
  2461. End;
  2462. Invalidate;
  2463. End;
  2464. Procedure TGIFViewer.DisplayRawFrame(Index: Integer);
  2465. Var
  2466. Tmp: Graphics.TBitmap;
  2467. Begin
  2468. If not(FRenderCache.IsIndexOk(Index)) Then exit;
  2469. Tmp := GetRawFrame(Index);
  2470. FCurrentView.Assign(Tmp);
  2471. FreeAndNil(Tmp);
  2472. Invalidate;
  2473. End;
  2474. Procedure TGIFViewer.ExtractFrame(Index: Integer; Var bmp: TBitmap);
  2475. Begin
  2476. If not(FRenderCache.IsIndexOk(Index)) then exit;
  2477. Bmp.Assign(FRenderCache.Items[Index].Bitmap);
  2478. End;
  2479. Procedure TGIFViewer.ExtractRawFrame(Index: Integer; Var bmp: TBitmap);
  2480. Var
  2481. Tmp: Graphics.TBitmap;
  2482. Begin
  2483. If not(FRenderCache.IsIndexOk(Index)) Then exit;
  2484. Tmp := GetRawFrame(Index);
  2485. Bmp.Assign(Tmp);
  2486. FreeAndNil(Tmp);
  2487. End;
  2488. {%endregion}
  2489. Procedure Register;
  2490. Begin
  2491. RegisterComponents('Misc', [TGIFView]);
  2492. End;
  2493. End.