ImagingGif.pas 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235
  1. {
  2. $Id$
  3. Vampyre Imaging Library
  4. by Marek Mauder
  5. http://imaginglib.sourceforge.net
  6. The contents of this file are used with permission, subject to the Mozilla
  7. Public License Version 1.1 (the "License"); you may not use this file except
  8. in compliance with the License. You may obtain a copy of the License at
  9. http://www.mozilla.org/MPL/MPL-1.1.html
  10. Software distributed under the License is distributed on an "AS IS" basis,
  11. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. the specific language governing rights and limitations under the License.
  13. Alternatively, the contents of this file may be used under the terms of the
  14. GNU Lesser General Public License (the "LGPL License"), in which case the
  15. provisions of the LGPL License are applicable instead of those above.
  16. If you wish to allow use of your version of this file only under the terms
  17. of the LGPL License and not to allow others to use your version of this file
  18. under the MPL, indicate your decision by deleting the provisions above and
  19. replace them with the notice and other provisions required by the LGPL
  20. License. If you do not delete the provisions above, a recipient may use
  21. your version of this file under either the MPL or the LGPL License.
  22. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  23. }
  24. { This unit contains image format loader/saver for GIF images.}
  25. unit ImagingGif;
  26. {$I ImagingOptions.inc}
  27. interface
  28. uses
  29. SysUtils, Classes, Imaging, ImagingTypes, ImagingIO, ImagingUtility;
  30. type
  31. { GIF (Graphics Interchange Format) loader/saver class. GIF was
  32. (and is still used) popular format for storing images supporting
  33. multiple images per file and single color transparency.
  34. Pixel format is 8 bit indexed where each image frame can have
  35. its own color palette. GIF uses lossless LZW compression
  36. (patent expired few years ago).
  37. Imaging can load and save all GIFs with all frames and supports
  38. transparency. Imaging can load just raw ifIndex8 frames or
  39. also animate them in ifA8R8G8B8 format. See ImagingGIFLoadAnimated option.}
  40. TGIFFileFormat = class(TImageFileFormat)
  41. private
  42. FLoadAnimated: LongBool;
  43. function InterlaceStep(Y, Height: Integer; var Pass: Integer): Integer;
  44. procedure LZWDecompress(Stream: TStream; Handle: TImagingHandle;
  45. Width, Height: Integer; Interlaced: Boolean; Data: Pointer);
  46. procedure LZWCompress(const IO: TIOFunctions; Handle: TImagingHandle;
  47. Width, Height, BitCount: Integer; Interlaced: Boolean; Data: Pointer);
  48. protected
  49. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  50. OnlyFirstLevel: Boolean): Boolean; override;
  51. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  52. Index: LongInt): Boolean; override;
  53. procedure ConvertToSupported(var Image: TImageData;
  54. const Info: TImageFormatInfo); override;
  55. public
  56. constructor Create; override;
  57. function TestFormat(Handle: TImagingHandle): Boolean; override;
  58. published
  59. property LoadAnimated: LongBool read FLoadAnimated write FLoadAnimated;
  60. end;
  61. implementation
  62. const
  63. SGIFFormatName = 'Graphics Interchange Format';
  64. SGIFMasks = '*.gif';
  65. GIFSupportedFormats: TImageFormats = [ifIndex8];
  66. GIFDefaultLoadAnimated = True;
  67. type
  68. TGIFVersion = (gv87, gv89);
  69. TDisposalMethod = (dmNoRemoval, dmLeave, dmRestoreBackground,
  70. dmRestorePrevious, dmReserved4, dmReserved5, dmReserved6, dmReserved7);
  71. const
  72. GIFSignature: TChar3 = 'GIF';
  73. GIFVersions: array[TGIFVersion] of TChar3 = ('87a', '89a');
  74. // Masks for accessing fields in PackedFields of TGIFHeader
  75. GIFGlobalColorTable = $80;
  76. GIFColorResolution = $70;
  77. GIFColorTableSorted = $08;
  78. GIFColorTableSize = $07;
  79. // Masks for accessing fields in PackedFields of TImageDescriptor
  80. GIFLocalColorTable = $80;
  81. GIFInterlaced = $40;
  82. GIFLocalTableSorted = $20;
  83. // Block identifiers
  84. GIFPlainText: Byte = $01;
  85. GIFGraphicControlExtension: Byte = $F9;
  86. GIFCommentExtension: Byte = $FE;
  87. GIFApplicationExtension: Byte = $FF;
  88. GIFImageDescriptor: Byte = Ord(',');
  89. GIFExtensionIntroducer: Byte = Ord('!');
  90. GIFTrailer: Byte = Ord(';');
  91. GIFBlockTerminator: Byte = $00;
  92. // Masks for accessing fields in PackedFields of TGraphicControlExtension
  93. GIFTransparent = $01;
  94. GIFUserInput = $02;
  95. GIFDisposalMethod = $1C;
  96. type
  97. TGIFHeader = packed record
  98. // File header part
  99. Signature: TChar3; // Header Signature (always "GIF")
  100. Version: TChar3; // GIF format version("87a" or "89a")
  101. // Logical Screen Descriptor part
  102. ScreenWidth: Word; // Width of Display Screen in Pixels
  103. ScreenHeight: Word; // Height of Display Screen in Pixels
  104. PackedFields: Byte; // Screen and color map information
  105. BackgroundColorIndex: Byte; // Background color index (in global color table)
  106. AspectRatio: Byte; // Pixel aspect ratio, ratio = (AspectRatio + 15) / 64
  107. end;
  108. TImageDescriptor = packed record
  109. //Separator: Byte; // leave that out since we always read one bye ahead
  110. Left: Word; // X position of image with respect to logical screen
  111. Top: Word; // Y position
  112. Width: Word;
  113. Height: Word;
  114. PackedFields: Byte;
  115. end;
  116. const
  117. // GIF extension labels
  118. GIFExtTypeGraphic = $F9;
  119. GIFExtTypePlainText = $01;
  120. GIFExtTypeApplication = $FF;
  121. GIFExtTypeComment = $FE;
  122. type
  123. TGraphicControlExtension = packed record
  124. BlockSize: Byte;
  125. PackedFields: Byte;
  126. DelayTime: Word;
  127. TransparentColorIndex: Byte;
  128. Terminator: Byte;
  129. end;
  130. const
  131. // Netscape sub block types
  132. GIFAppLoopExtension = 1;
  133. GIFAppBufferExtension = 2;
  134. type
  135. TGIFIdentifierCode = array[0..7] of char;
  136. TGIFAuthenticationCode = array[0..2] of char;
  137. TGIFApplicationRec = packed record
  138. Identifier : TGIFIdentifierCode;
  139. Authentication : TGIFAuthenticationCode;
  140. end;
  141. const
  142. CodeTableSize = 4096;
  143. HashTableSize = 17777;
  144. type
  145. TReadContext = record
  146. Inx: Integer;
  147. Size: Integer;
  148. Buf: array [0..255 + 4] of Byte;
  149. CodeSize: Integer;
  150. ReadMask: Integer;
  151. end;
  152. PReadContext = ^TReadContext;
  153. TWriteContext = record
  154. Inx: Integer;
  155. CodeSize: Integer;
  156. Buf: array [0..255 + 4] of Byte;
  157. end;
  158. PWriteContext = ^TWriteContext;
  159. TOutputContext = record
  160. W: Integer;
  161. H: Integer;
  162. X: Integer;
  163. Y: Integer;
  164. BitsPerPixel: Integer;
  165. Pass: Integer;
  166. Interlace: Boolean;
  167. LineIdent: Integer;
  168. Data: Pointer;
  169. CurrLineData: Pointer;
  170. end;
  171. TImageDict = record
  172. Tail: Word;
  173. Index: Word;
  174. Col: Byte;
  175. end;
  176. PImageDict = ^TImageDict;
  177. PIntCodeTable = ^TIntCodeTable;
  178. TIntCodeTable = array [0..CodeTableSize - 1] of Word;
  179. TDictTable = array [0..CodeTableSize - 1] of TImageDict;
  180. PDictTable = ^TDictTable;
  181. resourcestring
  182. SGIFDecodingError = 'Error when decoding GIF LZW data';
  183. {
  184. TGIFFileFormat implementation
  185. }
  186. constructor TGIFFileFormat.Create;
  187. begin
  188. inherited Create;
  189. FName := SGIFFormatName;
  190. FCanLoad := True;
  191. FCanSave := True;
  192. FIsMultiImageFormat := True;
  193. FSupportedFormats := GIFSupportedFormats;
  194. FLoadAnimated := GIFDefaultLoadAnimated;
  195. AddMasks(SGIFMasks);
  196. RegisterOption(ImagingGIFLoadAnimated, @FLoadAnimated);
  197. end;
  198. function TGIFFileFormat.InterlaceStep(Y, Height: Integer; var Pass: Integer): Integer;
  199. begin
  200. Result := Y;
  201. case Pass of
  202. 0, 1:
  203. Inc(Result, 8);
  204. 2:
  205. Inc(Result, 4);
  206. 3:
  207. Inc(Result, 2);
  208. end;
  209. if Result >= Height then
  210. begin
  211. if Pass = 0 then
  212. begin
  213. Pass := 1;
  214. Result := 4;
  215. if Result < Height then
  216. Exit;
  217. end;
  218. if Pass = 1 then
  219. begin
  220. Pass := 2;
  221. Result := 2;
  222. if Result < Height then
  223. Exit;
  224. end;
  225. if Pass = 2 then
  226. begin
  227. Pass := 3;
  228. Result := 1;
  229. end;
  230. end;
  231. end;
  232. { GIF LZW decompresion code is from JVCL JvGIF.pas unit.}
  233. procedure TGIFFileFormat.LZWDecompress(Stream: TStream; Handle: TImagingHandle; Width, Height: Integer;
  234. Interlaced: Boolean; Data: Pointer);
  235. var
  236. MinCodeSize: Byte;
  237. MaxCode, BitMask, InitCodeSize: Integer;
  238. ClearCode, EndingCode, FirstFreeCode, FreeCode: Word;
  239. I, OutCount, Code: Integer;
  240. CurCode, OldCode, InCode, FinalChar: Word;
  241. Prefix, Suffix, OutCode: PIntCodeTable;
  242. ReadCtxt: TReadContext;
  243. OutCtxt: TOutputContext;
  244. TableFull: Boolean;
  245. function ReadCode(var Context: TReadContext): Integer;
  246. var
  247. RawCode: Integer;
  248. ByteIndex: Integer;
  249. Bytes: Byte;
  250. BytesToLose: Integer;
  251. begin
  252. while (Context.Inx + Context.CodeSize > Context.Size) and
  253. (Stream.Position < Stream.Size) do
  254. begin
  255. // Not enough bits in buffer - refill it - Not very efficient, but infrequently called
  256. BytesToLose := Context.Inx shr 3;
  257. // Note biggest Code Size is 12 bits. And this can at worst span 3 Bytes
  258. Move(Context.Buf[Word(BytesToLose)], Context.Buf[0], 3);
  259. Context.Inx := Context.Inx and 7;
  260. Context.Size := Context.Size - (BytesToLose shl 3);
  261. Stream.Read(Bytes, 1);
  262. if Bytes > 0 then
  263. Stream.Read(Context.Buf[Word(Context.Size shr 3)], Bytes);
  264. Context.Size := Context.Size + (Bytes shl 3);
  265. end;
  266. ByteIndex := Context.Inx shr 3;
  267. RawCode := Context.Buf[Word(ByteIndex)] +
  268. (Word(Context.Buf[Word(ByteIndex + 1)]) shl 8);
  269. if Context.CodeSize > 8 then
  270. RawCode := RawCode + (LongInt(Context.Buf[ByteIndex + 2]) shl 16);
  271. RawCode := RawCode shr (Context.Inx and 7);
  272. Context.Inx := Context.Inx + Byte(Context.CodeSize);
  273. Result := RawCode and Context.ReadMask;
  274. end;
  275. procedure Output(Value: Byte; var Context: TOutputContext);
  276. var
  277. P: PByte;
  278. begin
  279. if Context.Y >= Context.H then
  280. Exit;
  281. // Only ifIndex8 supported
  282. P := @PByteArray(Context.CurrLineData)[Context.X];
  283. P^ := Value;
  284. {case Context.BitsPerPixel of
  285. 1:
  286. begin
  287. P := @PByteArray(Context.CurrLineData)[Context.X shr 3];
  288. if (Context.X and $07) <> 0 then
  289. P^ := P^ or Word(Value shl (7 - (Word(Context.X and 7))))
  290. else
  291. P^ := Byte(Value shl 7);
  292. end;
  293. 4:
  294. begin
  295. P := @PByteArray(Context.CurrLineData)[Context.X shr 1];
  296. if (Context.X and 1) <> 0 then
  297. P^ := P^ or Value
  298. else
  299. P^ := Byte(Value shl 4);
  300. end;
  301. 8:
  302. begin
  303. P := @PByteArray(Context.CurrLineData)[Context.X];
  304. P^ := Value;
  305. end;
  306. end;}
  307. Inc(Context.X);
  308. if Context.X < Context.W then
  309. Exit;
  310. Context.X := 0;
  311. if Context.Interlace then
  312. Context.Y := InterlaceStep(Context.Y, Context.H, Context.Pass)
  313. else
  314. Inc(Context.Y);
  315. Context.CurrLineData := @PByteArray(Context.Data)[Context.Y * Context.LineIdent];
  316. end;
  317. begin
  318. OutCount := 0;
  319. OldCode := 0;
  320. FinalChar := 0;
  321. TableFull := False;
  322. GetMem(Prefix, SizeOf(TIntCodeTable));
  323. GetMem(Suffix, SizeOf(TIntCodeTable));
  324. GetMem(OutCode, SizeOf(TIntCodeTable) + SizeOf(Word));
  325. try
  326. Stream.Read(MinCodeSize, 1);
  327. if (MinCodeSize < 2) or (MinCodeSize > 9) then
  328. RaiseImaging(SGIFDecodingError, []);
  329. // Initial read context
  330. ReadCtxt.Inx := 0;
  331. ReadCtxt.Size := 0;
  332. ReadCtxt.CodeSize := MinCodeSize + 1;
  333. ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1;
  334. // Initialise pixel-output context
  335. OutCtxt.X := 0;
  336. OutCtxt.Y := 0;
  337. OutCtxt.Pass := 0;
  338. OutCtxt.W := Width;
  339. OutCtxt.H := Height;
  340. OutCtxt.BitsPerPixel := MinCodeSize;
  341. OutCtxt.Interlace := Interlaced;
  342. OutCtxt.LineIdent := Width;
  343. OutCtxt.Data := Data;
  344. OutCtxt.CurrLineData := Data;
  345. BitMask := (1 shl OutCtxt.BitsPerPixel) - 1;
  346. // 2 ^ MinCodeSize accounts for all colours in file
  347. ClearCode := 1 shl MinCodeSize;
  348. EndingCode := ClearCode + 1;
  349. FreeCode := ClearCode + 2;
  350. FirstFreeCode := FreeCode;
  351. // 2^ (MinCodeSize + 1) includes clear and eoi Code and space too
  352. InitCodeSize := ReadCtxt.CodeSize;
  353. MaxCode := 1 shl ReadCtxt.CodeSize;
  354. Code := ReadCode(ReadCtxt);
  355. while (Code <> EndingCode) and (Code <> $FFFF) and
  356. (OutCtxt.Y < OutCtxt.H) do
  357. begin
  358. if Code = ClearCode then
  359. begin
  360. ReadCtxt.CodeSize := InitCodeSize;
  361. MaxCode := 1 shl ReadCtxt.CodeSize;
  362. ReadCtxt.ReadMask := MaxCode - 1;
  363. FreeCode := FirstFreeCode;
  364. Code := ReadCode(ReadCtxt);
  365. CurCode := Code;
  366. OldCode := Code;
  367. if Code = $FFFF then
  368. Break;
  369. FinalChar := (CurCode and BitMask);
  370. Output(Byte(FinalChar), OutCtxt);
  371. TableFull := False;
  372. end
  373. else
  374. begin
  375. CurCode := Code;
  376. InCode := Code;
  377. if CurCode >= FreeCode then
  378. begin
  379. CurCode := OldCode;
  380. OutCode^[OutCount] := FinalChar;
  381. Inc(OutCount);
  382. end;
  383. while CurCode > BitMask do
  384. begin
  385. if OutCount > CodeTableSize then
  386. RaiseImaging(SGIFDecodingError, []);
  387. OutCode^[OutCount] := Suffix^[CurCode];
  388. Inc(OutCount);
  389. CurCode := Prefix^[CurCode];
  390. end;
  391. FinalChar := CurCode and BitMask;
  392. OutCode^[OutCount] := FinalChar;
  393. Inc(OutCount);
  394. for I := OutCount - 1 downto 0 do
  395. Output(Byte(OutCode^[I]), OutCtxt);
  396. OutCount := 0;
  397. // Update dictionary
  398. if not TableFull then
  399. begin
  400. Prefix^[FreeCode] := OldCode;
  401. Suffix^[FreeCode] := FinalChar;
  402. // Advance to next free slot
  403. Inc(FreeCode);
  404. if FreeCode >= MaxCode then
  405. begin
  406. if ReadCtxt.CodeSize < 12 then
  407. begin
  408. Inc(ReadCtxt.CodeSize);
  409. MaxCode := MaxCode shl 1;
  410. ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1;
  411. end
  412. else
  413. TableFull := True;
  414. end;
  415. end;
  416. OldCode := InCode;
  417. end;
  418. Code := ReadCode(ReadCtxt);
  419. end;
  420. if Code = $FFFF then
  421. RaiseImaging(SGIFDecodingError, []);
  422. finally
  423. FreeMem(Prefix);
  424. FreeMem(OutCode);
  425. FreeMem(Suffix);
  426. end;
  427. end;
  428. { GIF LZW compresion code is from JVCL JvGIF.pas unit.}
  429. procedure TGIFFileFormat.LZWCompress(const IO: TIOFunctions; Handle: TImagingHandle; Width, Height, BitCount: Integer;
  430. Interlaced: Boolean; Data: Pointer);
  431. var
  432. LineIdent: Integer;
  433. MinCodeSize, Col: Byte;
  434. InitCodeSize, X, Y: Integer;
  435. Pass: Integer;
  436. MaxCode: Integer; { 1 shl CodeSize }
  437. ClearCode, EndingCode, LastCode, Tail: Integer;
  438. I, HashValue: Integer;
  439. LenString: Word;
  440. Dict: PDictTable;
  441. HashTable: TList;
  442. PData: PByte;
  443. WriteCtxt: TWriteContext;
  444. function InitHash(P: Integer): Integer;
  445. begin
  446. Result := (P + 3) * 301;
  447. end;
  448. procedure WriteCode(Code: Integer; var Context: TWriteContext);
  449. var
  450. BufIndex: Integer;
  451. Bytes: Byte;
  452. begin
  453. BufIndex := Context.Inx shr 3;
  454. Code := Code shl (Context.Inx and 7);
  455. Context.Buf[BufIndex] := Context.Buf[BufIndex] or Byte(Code);
  456. Context.Buf[BufIndex + 1] := Byte(Code shr 8);
  457. Context.Buf[BufIndex + 2] := Byte(Code shr 16);
  458. Context.Inx := Context.Inx + Context.CodeSize;
  459. if Context.Inx >= 255 * 8 then
  460. begin
  461. // Flush out full buffer
  462. Bytes := 255;
  463. IO.Write(Handle, @Bytes, 1);
  464. IO.Write(Handle, @Context.Buf, Bytes);
  465. Move(Context.Buf[255], Context.Buf[0], 2);
  466. FillChar(Context.Buf[2], 255, 0);
  467. Context.Inx := Context.Inx - (255 * 8);
  468. end;
  469. end;
  470. procedure FlushCode(var Context: TWriteContext);
  471. var
  472. Bytes: Byte;
  473. begin
  474. Bytes := (Context.Inx + 7) shr 3;
  475. if Bytes > 0 then
  476. begin
  477. IO.Write(Handle, @Bytes, 1);
  478. IO.Write(Handle, @Context.Buf, Bytes);
  479. end;
  480. // Data block terminator - a block of zero Size
  481. Bytes := 0;
  482. IO.Write(Handle, @Bytes, 1);
  483. end;
  484. begin
  485. LineIdent := Width;
  486. Tail := 0;
  487. HashValue := 0;
  488. Col := 0;
  489. HashTable := TList.Create;
  490. GetMem(Dict, SizeOf(TDictTable));
  491. try
  492. for I := 0 to HashTableSize - 1 do
  493. HashTable.Add(nil);
  494. // Initialise encoder variables
  495. InitCodeSize := BitCount + 1;
  496. if InitCodeSize = 2 then
  497. Inc(InitCodeSize);
  498. MinCodeSize := InitCodeSize - 1;
  499. IO.Write(Handle, @MinCodeSize, 1);
  500. ClearCode := 1 shl MinCodeSize;
  501. EndingCode := ClearCode + 1;
  502. LastCode := EndingCode;
  503. MaxCode := 1 shl InitCodeSize;
  504. LenString := 0;
  505. // Setup write context
  506. WriteCtxt.Inx := 0;
  507. WriteCtxt.CodeSize := InitCodeSize;
  508. FillChar(WriteCtxt.Buf, SizeOf(WriteCtxt.Buf), 0);
  509. WriteCode(ClearCode, WriteCtxt);
  510. Y := 0;
  511. Pass := 0;
  512. while Y < Height do
  513. begin
  514. PData := @PByteArray(Data)[Y * LineIdent];
  515. for X := 0 to Width - 1 do
  516. begin
  517. // Only ifIndex8 support
  518. case BitCount of
  519. 8:
  520. begin
  521. Col := PData^;
  522. PData := @PByteArray(PData)[1];
  523. end;
  524. {4:
  525. begin
  526. if X and 1 <> 0 then
  527. begin
  528. Col := PData^ and $0F;
  529. PData := @PByteArray(PData)[1];
  530. end
  531. else
  532. Col := PData^ shr 4;
  533. end;
  534. 1:
  535. begin
  536. if X and 7 = 7 then
  537. begin
  538. Col := PData^ and 1;
  539. PData := @PByteArray(PData)[1];
  540. end
  541. else
  542. Col := (PData^ shr (7 - (X and $07))) and $01;
  543. end;}
  544. end;
  545. Inc(LenString);
  546. if LenString = 1 then
  547. begin
  548. Tail := Col;
  549. HashValue := InitHash(Col);
  550. end
  551. else
  552. begin
  553. HashValue := HashValue * (Col + LenString + 4);
  554. I := HashValue mod HashTableSize;
  555. HashValue := HashValue mod HashTableSize;
  556. while (HashTable[I] <> nil) and
  557. ((PImageDict(HashTable[I])^.Tail <> Tail) or
  558. (PImageDict(HashTable[I])^.Col <> Col)) do
  559. begin
  560. Inc(I);
  561. if I >= HashTableSize then
  562. I := 0;
  563. end;
  564. if HashTable[I] <> nil then // Found in the strings table
  565. Tail := PImageDict(HashTable[I])^.Index
  566. else
  567. begin
  568. // Not found
  569. WriteCode(Tail, WriteCtxt);
  570. Inc(LastCode);
  571. HashTable[I] := @Dict^[LastCode];
  572. PImageDict(HashTable[I])^.Index := LastCode;
  573. PImageDict(HashTable[I])^.Tail := Tail;
  574. PImageDict(HashTable[I])^.Col := Col;
  575. Tail := Col;
  576. HashValue := InitHash(Col);
  577. LenString := 1;
  578. if LastCode >= MaxCode then
  579. begin
  580. // Next Code will be written longer
  581. MaxCode := MaxCode shl 1;
  582. Inc(WriteCtxt.CodeSize);
  583. end
  584. else
  585. if LastCode >= CodeTableSize - 2 then
  586. begin
  587. // Reset tables
  588. WriteCode(Tail, WriteCtxt);
  589. WriteCode(ClearCode, WriteCtxt);
  590. LenString := 0;
  591. LastCode := EndingCode;
  592. WriteCtxt.CodeSize := InitCodeSize;
  593. MaxCode := 1 shl InitCodeSize;
  594. for I := 0 to HashTableSize - 1 do
  595. HashTable[I] := nil;
  596. end;
  597. end;
  598. end;
  599. end;
  600. if Interlaced then
  601. Y := InterlaceStep(Y, Height, Pass)
  602. else
  603. Inc(Y);
  604. end;
  605. WriteCode(Tail, WriteCtxt);
  606. WriteCode(EndingCode, WriteCtxt);
  607. FlushCode(WriteCtxt);
  608. finally
  609. HashTable.Free;
  610. FreeMem(Dict);
  611. end;
  612. end;
  613. function TGIFFileFormat.LoadData(Handle: TImagingHandle;
  614. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  615. type
  616. TFrameInfo = record
  617. Left, Top: Integer;
  618. Width, Height: Integer;
  619. Disposal: TDisposalMethod;
  620. HasTransparency: Boolean;
  621. HasLocalPal: Boolean;
  622. TransIndex: Integer;
  623. BackIndex: Integer;
  624. end;
  625. var
  626. Header: TGIFHeader;
  627. HasGlobalPal: Boolean;
  628. GlobalPalLength: Integer;
  629. GlobalPal: TPalette32Size256;
  630. ScreenWidth, ScreenHeight, I, CachedIndex: Integer;
  631. BlockID: Byte;
  632. HasGraphicExt: Boolean;
  633. GraphicExt: TGraphicControlExtension;
  634. FrameInfos: array of TFrameInfo;
  635. AppRead: Boolean;
  636. CachedFrame: TImageData;
  637. AnimFrames: TDynImageDataArray;
  638. function ReadBlockID: Byte;
  639. begin
  640. Result := GIFTrailer;
  641. if GetIO.Read(Handle, @Result, SizeOf(Result)) < SizeOf(Result) then
  642. Result := GIFTrailer;
  643. end;
  644. procedure ReadExtensions;
  645. var
  646. BlockSize, BlockType, ExtType: Byte;
  647. AppRec: TGIFApplicationRec;
  648. LoopCount: SmallInt;
  649. procedure SkipBytes;
  650. begin
  651. with GetIO do
  652. repeat
  653. // Read block sizes and skip them
  654. Read(Handle, @BlockSize, SizeOf(BlockSize));
  655. Seek(Handle, BlockSize, smFromCurrent);
  656. until BlockSize = 0;
  657. end;
  658. begin
  659. HasGraphicExt := False;
  660. AppRead := False;
  661. // Read extensions until image descriptor is found. Only graphic extension
  662. // is stored now (for transparency), others are skipped.
  663. while BlockID = GIFExtensionIntroducer do
  664. with GetIO do
  665. begin
  666. Read(Handle, @ExtType, SizeOf(ExtType));
  667. while ExtType in [GIFGraphicControlExtension, GIFCommentExtension, GIFApplicationExtension, GIFPlainText] do
  668. begin
  669. if ExtType = GIFGraphicControlExtension then
  670. begin
  671. HasGraphicExt := True;
  672. Read(Handle, @GraphicExt, SizeOf(GraphicExt));
  673. end
  674. else if (ExtType = GIFApplicationExtension) and not AppRead then
  675. begin
  676. Read(Handle, @BlockSize, SizeOf(BlockSize));
  677. if BlockSize >= SizeOf(AppRec) then
  678. begin
  679. Read(Handle, @AppRec, SizeOf(AppRec));
  680. if (AppRec.Identifier = 'NETSCAPE') and (AppRec.Authentication = '2.0') then
  681. begin
  682. Read(Handle, @BlockSize, SizeOf(BlockSize));
  683. while BlockSize <> 0 do
  684. begin
  685. BlockType := ReadBlockID;
  686. Dec(BlockSize);
  687. case BlockType of
  688. GIFAppLoopExtension:
  689. if (BlockSize >= SizeOf(LoopCount)) then
  690. begin
  691. // Read loop count
  692. Read(Handle, @LoopCount, SizeOf(LoopCount));
  693. Dec(BlockSize, SizeOf(LoopCount));
  694. end;
  695. GIFAppBufferExtension:
  696. begin
  697. Dec(BlockSize, SizeOf(Word));
  698. Seek(Handle, SizeOf(Word), smFromCurrent);
  699. end;
  700. end;
  701. end;
  702. SkipBytes;
  703. AppRead := True;
  704. end
  705. else
  706. begin
  707. // Revert all bytes reading
  708. Seek(Handle, - SizeOf(AppRec) - SizeOf(BlockSize), smFromCurrent);
  709. SkipBytes;
  710. end;
  711. end
  712. else
  713. begin
  714. Seek(Handle, - BlockSize - SizeOf(BlockSize), smFromCurrent);
  715. SkipBytes;
  716. end;
  717. end
  718. else if ExtType in [GIFCommentExtension, GIFApplicationExtension, GIFPlainText] then
  719. repeat
  720. // Read block sizes and skip them
  721. Read(Handle, @BlockSize, SizeOf(BlockSize));
  722. Seek(Handle, BlockSize, smFromCurrent);
  723. until BlockSize = 0;
  724. // Read ID of following block
  725. BlockID := ReadBlockID;
  726. ExtType := BlockID;
  727. end
  728. end;
  729. end;
  730. procedure CopyLZWData(Dest: TStream);
  731. var
  732. CodeSize, BlockSize: Byte;
  733. InputSize: Integer;
  734. Buff: array[Byte] of Byte;
  735. begin
  736. InputSize := ImagingIO.GetInputSize(GetIO, Handle);
  737. // Copy codesize to stream
  738. GetIO.Read(Handle, @CodeSize, 1);
  739. Dest.Write(CodeSize, 1);
  740. repeat
  741. // Read and write data blocks, last is block term value of 0
  742. GetIO.Read(Handle, @BlockSize, 1);
  743. Dest.Write(BlockSize, 1);
  744. if BlockSize > 0 then
  745. begin
  746. GetIO.Read(Handle, @Buff[0], BlockSize);
  747. Dest.Write(Buff[0], BlockSize);
  748. end;
  749. until (BlockSize = 0) or (GetIO.Tell(Handle) >= InputSize);
  750. end;
  751. procedure ReadFrame;
  752. var
  753. ImageDesc: TImageDescriptor;
  754. Interlaced: Boolean;
  755. I, Idx, LocalPalLength: Integer;
  756. LocalPal: TPalette32Size256;
  757. LZWStream: TMemoryStream;
  758. procedure RemoveBadFrame;
  759. begin
  760. FreeImage(Images[Idx]);
  761. SetLength(Images, Length(Images) - 1);
  762. end;
  763. begin
  764. Idx := Length(Images);
  765. SetLength(Images, Idx + 1);
  766. SetLength(FrameInfos, Idx + 1);
  767. FillChar(LocalPal, SizeOf(LocalPal), 0);
  768. with GetIO do
  769. begin
  770. // Read and parse image descriptor
  771. Read(Handle, @ImageDesc, SizeOf(ImageDesc));
  772. FrameInfos[Idx].HasLocalPal := (ImageDesc.PackedFields and GIFLocalColorTable) = GIFLocalColorTable;
  773. Interlaced := (ImageDesc.PackedFields and GIFInterlaced) = GIFInterlaced;
  774. LocalPalLength := ImageDesc.PackedFields and GIFColorTableSize;
  775. LocalPalLength := 1 shl (LocalPalLength + 1); // Total pal length is 2^(n+1)
  776. // From Mozilla source
  777. if (ImageDesc.Width = 0) or (ImageDesc.Width > Header.ScreenWidth) then
  778. ImageDesc.Width := Header.ScreenWidth;
  779. if (ImageDesc.Height = 0) or (ImageDesc.Height > Header.ScreenHeight) then
  780. ImageDesc.Height := Header.ScreenHeight;
  781. FrameInfos[Idx].Left := ImageDesc.Left;
  782. FrameInfos[Idx].Top := ImageDesc.Top;
  783. FrameInfos[Idx].Width := ImageDesc.Width;
  784. FrameInfos[Idx].Height := ImageDesc.Height;
  785. FrameInfos[Idx].BackIndex := Header.BackgroundColorIndex;
  786. // Create new image for this frame which would be later pasted onto logical screen
  787. NewImage(ImageDesc.Width, ImageDesc.Height, ifIndex8, Images[Idx]);
  788. // Load local palette if there is any
  789. if FrameInfos[Idx].HasLocalPal then
  790. for I := 0 to LocalPalLength - 1 do
  791. begin
  792. LocalPal[I].A := 255;
  793. Read(Handle, @LocalPal[I].R, SizeOf(LocalPal[I].R));
  794. Read(Handle, @LocalPal[I].G, SizeOf(LocalPal[I].G));
  795. Read(Handle, @LocalPal[I].B, SizeOf(LocalPal[I].B));
  796. end;
  797. // Use local pal if present or global pal if present or create
  798. // default pal if neither of them is present
  799. if FrameInfos[Idx].HasLocalPal then
  800. Move(LocalPal, Images[Idx].Palette^, SizeOf(LocalPal))
  801. else if HasGlobalPal then
  802. Move(GlobalPal, Images[Idx].Palette^, SizeOf(GlobalPal))
  803. else
  804. FillCustomPalette(Images[Idx].Palette, GlobalPalLength, 3, 3, 2);
  805. if (ImageDesc.Left <= Header.ScreenWidth + 1) and (ImageDesc.Top <= Header.ScreenHeight + 1) then
  806. begin
  807. // Resize the screen if needed to fit the frame
  808. ScreenWidth := Max(ScreenWidth, ImageDesc.Width + ImageDesc.Left);
  809. ScreenHeight := Max(ScreenHeight, ImageDesc.Height + ImageDesc.Top);
  810. end
  811. else
  812. begin
  813. // Remove frame outside logical screen
  814. RemoveBadFrame;
  815. Exit;
  816. end;
  817. // If Grahic Control Extension is present make use of it
  818. if HasGraphicExt then
  819. begin
  820. FrameInfos[Idx].HasTransparency := (GraphicExt.PackedFields and GIFTransparent) = GIFTransparent;
  821. FrameInfos[Idx].Disposal := TDisposalMethod((GraphicExt.PackedFields and GIFDisposalMethod) shr 2);
  822. if FrameInfos[Idx].HasTransparency then
  823. begin
  824. FrameInfos[Idx].TransIndex := GraphicExt.TransparentColorIndex;
  825. Images[Idx].Palette[FrameInfos[Idx].TransIndex].A := 0;
  826. end;
  827. end
  828. else
  829. FrameInfos[Idx].HasTransparency := False;
  830. LZWStream := TMemoryStream.Create;
  831. try
  832. try
  833. // Copy LZW data to temp stream, needed for correct decompression
  834. CopyLZWData(LZWStream);
  835. LZWStream.Position := 0;
  836. // Data decompression finally
  837. LZWDecompress(LZWStream, Handle, ImageDesc.Width, ImageDesc.Height, Interlaced, Images[Idx].Bits);
  838. except
  839. RemoveBadFrame;
  840. Exit;
  841. end;
  842. finally
  843. LZWStream.Free;
  844. end;
  845. end;
  846. end;
  847. procedure CopyFrameTransparent32(const Image, Frame: TImageData; Left, Top: Integer);
  848. var
  849. X, Y: Integer;
  850. Src: PByte;
  851. Dst: PColor32;
  852. begin
  853. Src := Frame.Bits;
  854. // Copy all pixels from frame to log screen but ignore the transparent ones
  855. for Y := 0 to Frame.Height - 1 do
  856. begin
  857. Dst := @PColor32RecArray(Image.Bits)[(Top + Y) * Image.Width + Left];
  858. for X := 0 to Frame.Width - 1 do
  859. begin
  860. if (Frame.Palette[Src^].A <> 0) then
  861. Dst^ := Frame.Palette[Src^].Color;
  862. Inc(Src);
  863. Inc(Dst);
  864. end;
  865. end;
  866. end;
  867. procedure AnimateFrame(Index: Integer; var AnimFrame: TImageData);
  868. var
  869. I, First, Last: Integer;
  870. UseCache: Boolean;
  871. BGColor: TColor32;
  872. begin
  873. // We may need to use raw frame 0 to n to correctly animate n-th frame
  874. Last := Index;
  875. First := Max(0, Last);
  876. // See if we can use last animate frame as a basis for this one
  877. // (so we don't have to use previous raw frames).
  878. UseCache := TestImage(CachedFrame) and (CachedIndex = Index - 1) and (CachedIndex >= 0) and
  879. (FrameInfos[CachedIndex].Disposal <> dmRestorePrevious);
  880. // Reuse or release cache
  881. if UseCache then
  882. CloneImage(CachedFrame, AnimFrame)
  883. else
  884. FreeImage(CachedFrame);
  885. // Default color for clearing of the screen
  886. BGColor := Images[Index].Palette[FrameInfos[Index].BackIndex].Color;
  887. // Now prepare logical screen for drawing of raw frame at Index.
  888. // We may need to use all previous raw frames to get the screen
  889. // to proper state (according to their disposal methods).
  890. if not UseCache then
  891. begin
  892. if FrameInfos[Index].HasTransparency then
  893. BGColor := Images[Index].Palette[FrameInfos[Index].TransIndex].Color;
  894. // Clear whole screen
  895. FillMemoryLongWord(AnimFrame.Bits, AnimFrame.Size, BGColor);
  896. // Try to maximize First so we don't have to use all 0 to n raw frames
  897. while First > 0 do
  898. begin
  899. if (ScreenWidth = Images[First].Width) and (ScreenHeight = Images[First].Height) then
  900. begin
  901. if (FrameInfos[First].Disposal = dmRestoreBackground) and (First < Last) then
  902. Break;
  903. end;
  904. Dec(First);
  905. end;
  906. for I := First to Last - 1 do
  907. begin
  908. case FrameInfos[I].Disposal of
  909. dmNoRemoval, dmLeave:
  910. begin
  911. // Copy previous raw frame onto screen
  912. CopyFrameTransparent32(AnimFrame, Images[I], FrameInfos[I].Left, FrameInfos[I].Top);
  913. end;
  914. dmRestoreBackground:
  915. if (I > First) then
  916. begin
  917. // Restore background color
  918. FillRect(AnimFrame, FrameInfos[I].Left, FrameInfos[I].Top,
  919. FrameInfos[I].Width, FrameInfos[I].Height, @BGColor);
  920. end;
  921. dmRestorePrevious: ; // Do nothing - previous state is already on screen
  922. end;
  923. end;
  924. end
  925. else if FrameInfos[CachedIndex].Disposal = dmRestoreBackground then
  926. begin
  927. // We have our cached result but also need to restore
  928. // background in a place of cached frame
  929. if FrameInfos[CachedIndex].HasTransparency then
  930. BGColor := Images[CachedIndex].Palette[FrameInfos[CachedIndex].TransIndex].Color;
  931. FillRect(AnimFrame, FrameInfos[CachedIndex].Left, FrameInfos[CachedIndex].Top,
  932. FrameInfos[CachedIndex].Width, FrameInfos[CachedIndex].Height, @BGColor);
  933. end;
  934. // Copy current raw frame to prepared screen
  935. CopyFrameTransparent32(AnimFrame, Images[Index], FrameInfos[Index].Left, FrameInfos[Index].Top);
  936. // Cache animated result
  937. CloneImage(AnimFrame, CachedFrame);
  938. CachedIndex := Index;
  939. end;
  940. begin
  941. AppRead := False;
  942. SetLength(Images, 0);
  943. FillChar(GlobalPal, SizeOf(GlobalPal), 0);
  944. with GetIO do
  945. begin
  946. // Read GIF header
  947. Read(Handle, @Header, SizeOf(Header));
  948. ScreenWidth := Header.ScreenWidth;
  949. ScreenHeight := Header.ScreenHeight;
  950. HasGlobalPal := Header.PackedFields and GIFGlobalColorTable = GIFGlobalColorTable; // Bit 7
  951. GlobalPalLength := Header.PackedFields and GIFColorTableSize; // Bits 0-2
  952. GlobalPalLength := 1 shl (GlobalPalLength + 1); // Total pal length is 2^(n+1)
  953. // Read global palette from file if present
  954. if HasGlobalPal then
  955. begin
  956. for I := 0 to GlobalPalLength - 1 do
  957. begin
  958. GlobalPal[I].A := 255;
  959. Read(Handle, @GlobalPal[I].R, SizeOf(GlobalPal[I].R));
  960. Read(Handle, @GlobalPal[I].G, SizeOf(GlobalPal[I].G));
  961. Read(Handle, @GlobalPal[I].B, SizeOf(GlobalPal[I].B));
  962. end;
  963. end;
  964. // Read ID of the first block
  965. BlockID := ReadBlockID;
  966. // Now read all data blocks in the file until file trailer is reached
  967. while BlockID <> GIFTrailer do
  968. begin
  969. // Read blocks until we find the one of known type
  970. while not (BlockID in [GIFTrailer, GIFExtensionIntroducer, GIFImageDescriptor]) do
  971. BlockID := ReadBlockID;
  972. // Read supported and skip unsupported extensions
  973. ReadExtensions;
  974. // If image frame is found read it
  975. if BlockID = GIFImageDescriptor then
  976. ReadFrame;
  977. // Read next block's ID
  978. BlockID := ReadBlockID;
  979. // If block ID is unknown set it to end-of-GIF marker
  980. if not (BlockID in [GIFExtensionIntroducer, GIFTrailer, GIFImageDescriptor]) then
  981. BlockID := GIFTrailer;
  982. end;
  983. if FLoadAnimated then
  984. begin
  985. // Aniated frames will be stored in AnimFrames
  986. SetLength(AnimFrames, Length(Images));
  987. InitImage(CachedFrame);
  988. CachedIndex := -1;
  989. for I := 0 to High(Images) do
  990. begin
  991. // Create new logical screen
  992. NewImage(ScreenWidth, ScreenHeight, ifA8R8G8B8, AnimFrames[I]);
  993. // Animate frames to current log screen
  994. AnimateFrame(I, AnimFrames[I]);
  995. end;
  996. // Now release raw 8bit frames and put animated 32bit ones
  997. // to output array
  998. FreeImage(CachedFrame);
  999. for I := 0 to High(AnimFrames) do
  1000. begin
  1001. FreeImage(Images[I]);
  1002. Images[I] := AnimFrames[I];
  1003. end;
  1004. end;
  1005. Result := True;
  1006. end;
  1007. end;
  1008. function TGIFFileFormat.SaveData(Handle: TImagingHandle;
  1009. const Images: TDynImageDataArray; Index: Integer): Boolean;
  1010. var
  1011. Header: TGIFHeader;
  1012. ImageDesc: TImageDescriptor;
  1013. ImageToSave: TImageData;
  1014. MustBeFreed: Boolean;
  1015. I, J: Integer;
  1016. GraphicExt: TGraphicControlExtension;
  1017. procedure FindMaxDimensions(var MaxWidth, MaxHeight: Word);
  1018. var
  1019. I: Integer;
  1020. begin
  1021. MaxWidth := Images[FFirstIdx].Width;
  1022. MaxHeight := Images[FFirstIdx].Height;
  1023. for I := FFirstIdx + 1 to FLastIdx do
  1024. begin
  1025. MaxWidth := Iff(Images[I].Width > MaxWidth, Images[I].Width, MaxWidth);
  1026. MaxHeight := Iff(Images[I].Height > MaxWidth, Images[I].Height, MaxHeight);
  1027. end;
  1028. end;
  1029. begin
  1030. // Fill header with data, select size of largest image in array as
  1031. // logical screen size
  1032. FillChar(Header, Sizeof(Header), 0);
  1033. Header.Signature := GIFSignature;
  1034. Header.Version := GIFVersions[gv89];
  1035. FindMaxDimensions(Header.ScreenWidth, Header.ScreenHeight);
  1036. Header.PackedFields := GIFColorResolution; // Color resolution is 256
  1037. GetIO.Write(Handle, @Header, SizeOf(Header));
  1038. // Prepare default GC extension with delay
  1039. FillChar(GraphicExt, Sizeof(GraphicExt), 0);
  1040. GraphicExt.DelayTime := 65;
  1041. GraphicExt.BlockSize := 4;
  1042. for I := FFirstIdx to FLastIdx do
  1043. begin
  1044. if MakeCompatible(Images[I], ImageToSave, MustBeFreed) then
  1045. with GetIO, ImageToSave do
  1046. try
  1047. // Write Graphic Control Extension with default delay
  1048. Write(Handle, @GIFExtensionIntroducer, SizeOf(GIFExtensionIntroducer));
  1049. Write(Handle, @GIFGraphicControlExtension, SizeOf(GIFGraphicControlExtension));
  1050. Write(Handle, @GraphicExt, SizeOf(GraphicExt));
  1051. // Write frame marker and fill and write image descriptor for this frame
  1052. Write(Handle, @GIFImageDescriptor, SizeOf(GIFImageDescriptor));
  1053. FillChar(ImageDesc, Sizeof(ImageDesc), 0);
  1054. ImageDesc.Width := Width;
  1055. ImageDesc.Height := Height;
  1056. ImageDesc.PackedFields := GIFLocalColorTable or GIFColorTableSize; // Use lccal color table with 256 entries
  1057. Write(Handle, @ImageDesc, SizeOf(ImageDesc));
  1058. // Write local color table for each frame
  1059. for J := 0 to 255 do
  1060. begin
  1061. Write(Handle, @Palette[J].R, SizeOf(Palette[J].R));
  1062. Write(Handle, @Palette[J].G, SizeOf(Palette[J].G));
  1063. Write(Handle, @Palette[J].B, SizeOf(Palette[J].B));
  1064. end;
  1065. // Fonally compress image data
  1066. LZWCompress(GetIO, Handle, Width, Height, 8, False, Bits);
  1067. finally
  1068. if MustBeFreed then
  1069. FreeImage(ImageToSave);
  1070. end;
  1071. end;
  1072. GetIO.Write(Handle, @GIFTrailer, SizeOf(GIFTrailer));
  1073. Result := True;
  1074. end;
  1075. procedure TGIFFileFormat.ConvertToSupported(var Image: TImageData;
  1076. const Info: TImageFormatInfo);
  1077. begin
  1078. ConvertImage(Image, ifIndex8);
  1079. end;
  1080. function TGIFFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  1081. var
  1082. Header: TGIFHeader;
  1083. ReadCount: LongInt;
  1084. begin
  1085. Result := False;
  1086. if Handle <> nil then
  1087. begin
  1088. ReadCount := GetIO.Read(Handle, @Header, SizeOf(Header));
  1089. GetIO.Seek(Handle, -ReadCount, smFromCurrent);
  1090. Result := (ReadCount >= SizeOf(Header)) and
  1091. (Header.Signature = GIFSignature) and
  1092. ((Header.Version = GIFVersions[gv87]) or (Header.Version = GIFVersions[gv89]));
  1093. end;
  1094. end;
  1095. initialization
  1096. RegisterImageFileFormat(TGIFFileFormat);
  1097. {
  1098. File Notes:
  1099. -- TODOS ----------------------------------------------------
  1100. - nothing now
  1101. -- 0.26.1 Changes/Bug Fixes ---------------------------------
  1102. - GIF loading and animation mostly rewritten, based on
  1103. modification by Sergey Galezdinov (ExtraGIF in Extras/Contrib).
  1104. -- 0.25.0 Changes/Bug Fixes ---------------------------------
  1105. - Fixed loading of some rare GIFs, problems with LZW
  1106. decompression.
  1107. -- 0.24.3 Changes/Bug Fixes ---------------------------------
  1108. - Better solution to transparency for some GIFs. Background not
  1109. transparent by default.
  1110. -- 0.24.1 Changes/Bug Fixes ---------------------------------
  1111. - Made backround color transparent by default (alpha = 0).
  1112. -- 0.23 Changes/Bug Fixes -----------------------------------
  1113. - Fixed other loading bugs (local pal size, transparency).
  1114. - Added GIF saving.
  1115. - Fixed bug when loading multiframe GIFs and implemented few animation
  1116. features (disposal methods, ...).
  1117. - Loading of GIFs working.
  1118. - Unit created with initial stuff!
  1119. }
  1120. end.