fpreadgif.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by the Free Pascal development team
  4. GIF reader for fpImage.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************
  11. ToDo: read further images
  12. }
  13. unit FPReadGif;
  14. {$mode objfpc}{$H+}
  15. interface
  16. uses
  17. Classes, SysUtils, FPimage;
  18. type
  19. TGifRGB = packed record
  20. Red, Green, Blue : Byte;
  21. end;
  22. TGIFHeader = packed record
  23. Signature:array[0..2] of Char; //* Header Signature (always "GIF") */
  24. Version:array[0..2] of Char; //* GIF format version("87a" or "89a") */
  25. // Logical Screen Descriptor
  26. ScreenWidth:word; //* Width of Display Screen in Pixels */
  27. ScreenHeight:word; //* Height of Display Screen in Pixels */
  28. Packedbit, //* Screen and Color Map Information */
  29. BackgroundColor, //* Background Color Index */
  30. AspectRatio:byte; //* Pixel Aspect Ratio */
  31. end;
  32. TGifImageDescriptor = packed record
  33. Left, //* X position of image on the display */
  34. Top, //* Y position of image on the display */
  35. Width, //* Width of the image in pixels */
  36. Height:word; //* Height of the image in pixels */
  37. Packedbit:byte; //* Image and Color Table Data Information */
  38. end;
  39. TGifGraphicsControlExtension = packed record
  40. BlockSize, //* Size of remaining fields (always 04h) */
  41. Packedbit:byte; //* Method of graphics disposal to use */
  42. DelayTime:word; //* Hundredths of seconds to wait */
  43. ColorIndex, //* Transparent Color Index */
  44. Terminator:byte; //* Block Terminator (always 0) */
  45. end;
  46. TFPReaderGif = class;
  47. TGifCreateCompatibleImgEvent = procedure(Sender: TFPReaderGif;
  48. var NewImage: TFPCustomImage) of object;
  49. { TFPReaderGif }
  50. TFPReaderGif = class(TFPCustomImageReader)
  51. protected
  52. FHeader: TGIFHeader;
  53. FDescriptor: TGifImageDescriptor;
  54. FGraphicsCtrlExt: TGifGraphicsControlExtension;
  55. FTransparent: Boolean;
  56. FGraphCtrlExt: Boolean;
  57. FScanLine: PByte;
  58. FLineSize: Integer;
  59. FPalette: TFPPalette;
  60. FWidth: integer;
  61. FHeight: Integer;
  62. FInterlace: boolean;
  63. FBitsPerPixel: byte;
  64. FBackground: byte;
  65. FResolution: byte;
  66. FOnCreateImage: TGifCreateCompatibleImgEvent;
  67. procedure ReadPalette(Stream: TStream; Size: integer);
  68. function AnalyzeHeader: Boolean;
  69. procedure InternalRead(Stream: TStream; Img: TFPCustomImage); override;
  70. function ReadScanLine(Stream: TStream): boolean; virtual;
  71. function WriteScanLine(Img: TFPCustomImage): Boolean; virtual;
  72. function InternalCheck (Stream: TStream) : boolean; override;
  73. function SkipBlock(Stream: TStream): byte;
  74. public
  75. constructor Create; override;
  76. destructor Destroy; override;
  77. property Header: TGIFHeader read FHeader;
  78. property Descriptor: TGifImageDescriptor read FDescriptor;
  79. property GraphCtrlExt: Boolean read FGraphCtrlExt;
  80. property GraphicsCtrlExt: TGifGraphicsControlExtension read FGraphicsCtrlExt;
  81. property Transparent: Boolean read FTransparent;
  82. property Palette: TFPPalette read FPalette;
  83. property Width: integer read FWidth;
  84. property Height: Integer read FHeight;
  85. property Interlace: boolean read FInterlace;
  86. property BitsPerPixel: byte read FBitsPerPixel;
  87. property Background: byte read FBackground;
  88. property Resolution: byte read FResolution;
  89. property OnCreateImage: TGifCreateCompatibleImgEvent read FOnCreateImage write FOnCreateImage;
  90. end;
  91. implementation
  92. { TFPReaderGif }
  93. procedure TFPReaderGif.ReadPalette(Stream: TStream; Size: integer);
  94. Var
  95. RGBEntry : TGifRGB;
  96. I : Integer;
  97. c : TFPColor;
  98. begin
  99. FPalette.count := 0;
  100. For I:=0 To Size-1 Do
  101. Begin
  102. Stream.Read(RGBEntry, SizeOf(RGBEntry));
  103. With c do
  104. begin
  105. Red:=RGBEntry.Red or (RGBEntry.Red shl 8);
  106. Green:=RGBEntry.Green or (RGBEntry.Green shl 8);
  107. Blue:=RGBEntry.Blue or (RGBEntry.Blue shl 8);
  108. Alpha:=alphaOpaque;
  109. end;
  110. FPalette.Add(C);
  111. End;
  112. end;
  113. function TFPReaderGif.AnalyzeHeader: Boolean;
  114. var
  115. C : TFPColor;
  116. begin
  117. Result:=false;
  118. With FHeader do
  119. begin
  120. if (Signature = 'GIF') and
  121. ((Version = '87a') or
  122. (Version = '89a')) then
  123. else
  124. Raise Exception.Create('Unknown/Unsupported GIF image type');
  125. FResolution := Packedbit and $70 shr 5 + 1;
  126. FBitsPerPixel:=Packedbit and 7 + 1;
  127. FBackground := BackgroundColor;
  128. With FDescriptor do
  129. begin
  130. fWidth:=Width;
  131. fHeight:=Height;
  132. FInterlace := (Packedbit and $40 = $40);
  133. end;
  134. FTransparent:= FBackground <> 0;
  135. if FGraphCtrlExt then
  136. begin
  137. FTransparent:=(FGraphicsCtrlExt.Packedbit and $01)<>0;
  138. If FTransparent then
  139. FBackground:=FGraphicsCtrlExt.ColorIndex;
  140. end;
  141. FLineSize:=FWidth*(FHeight+1);
  142. GetMem(FScanLine,FLineSize);
  143. If FTransparent then
  144. begin
  145. C:=FPalette.Color[FBackground];
  146. C.alpha:=alphaTransparent;
  147. FPalette.Color[FBackground]:=C;
  148. end;
  149. end;
  150. Result:=true;
  151. end;
  152. procedure TFPReaderGif.InternalRead(Stream: TStream; Img: TFPCustomImage);
  153. var
  154. Introducer:byte;
  155. ColorTableSize :Integer;
  156. ContProgress: Boolean;
  157. begin
  158. FPalette:=nil;
  159. FScanLine:=nil;
  160. try
  161. ContProgress:=true;
  162. Progress(psStarting, 0, False, Rect(0,0,0,0), '', ContProgress);
  163. if not ContProgress then exit;
  164. FPalette := TFPPalette.Create(0);
  165. Stream.Position:=0;
  166. // header
  167. Stream.Read(FHeader,SizeOf(FHeader));
  168. Progress(psRunning, trunc(100.0 * (Stream.position / Stream.size)), False, Rect(0,0,0,0), '', ContProgress);
  169. if not ContProgress then exit;
  170. // Endian Fix Mantis 8541. Gif is always little endian
  171. {$IFDEF ENDIAN_BIG}
  172. with FHeader do
  173. begin
  174. ScreenWidth := LEtoN(ScreenWidth);
  175. ScreenHeight := LEtoN(ScreenHeight);
  176. end;
  177. {$ENDIF}
  178. // global palette
  179. if (FHeader.Packedbit and $80) <> 0 then
  180. begin
  181. ColorTableSize := FHeader.Packedbit and 7 + 1;
  182. ReadPalette(stream, 1 shl ColorTableSize);
  183. end;
  184. // skip extensions
  185. Repeat
  186. Introducer:=SkipBlock(Stream);
  187. until (Introducer = $2C) or (Introducer = $3B);
  188. // descriptor
  189. Stream.Read(FDescriptor, SizeOf(FDescriptor));
  190. {$IFDEF ENDIAN_BIG}
  191. with FDescriptor do
  192. begin
  193. Left := LEtoN(Left);
  194. Top := LEtoN(Top);
  195. Width := LEtoN(Width);
  196. Height := LEtoN(Height);
  197. end;
  198. {$ENDIF}
  199. // local palette
  200. if (FDescriptor.Packedbit and $80) <> 0 then
  201. begin
  202. ColorTableSize := FDescriptor.Packedbit and 7 + 1;
  203. ReadPalette(stream, 1 shl ColorTableSize);
  204. end;
  205. // parse header
  206. if not AnalyzeHeader then exit;
  207. // create image
  208. if Assigned(OnCreateImage) then
  209. OnCreateImage(Self,Img);
  210. Img.SetSize(FWidth,FHeight);
  211. // read pixels
  212. if not ReadScanLine(Stream) then exit;
  213. if not WriteScanLine(Img) then exit;
  214. // ToDo: read further images
  215. finally
  216. FreeAndNil(FPalette);
  217. ReAllocMem(FScanLine,0);
  218. end;
  219. Progress(FPimage.psEnding, 100, false, Rect(0,0,FWidth,FHeight), '', ContProgress);
  220. end;
  221. function TFPReaderGif.ReadScanLine(Stream: TStream): Boolean;
  222. var
  223. OldPos,
  224. UnpackedSize,
  225. PackedSize:longint;
  226. I: Integer;
  227. Data,
  228. Bits,
  229. Code: Cardinal;
  230. SourcePtr: PByte;
  231. InCode: Cardinal;
  232. CodeSize: Cardinal;
  233. CodeMask: Cardinal;
  234. FreeCode: Cardinal;
  235. OldCode: Cardinal;
  236. Prefix: array[0..4095] of Cardinal;
  237. Suffix,
  238. Stack: array [0..4095] of Byte;
  239. StackPointer: PByte;
  240. DataComp,
  241. Target: PByte;
  242. B,
  243. FInitialCodeSize,
  244. FirstChar: Byte;
  245. ClearCode,
  246. EOICode: Word;
  247. ContProgress: Boolean;
  248. begin
  249. DataComp:=nil;
  250. ContProgress:=true;
  251. try
  252. // read dictionary size
  253. Stream.read(FInitialCodeSize, 1);
  254. // search end of compressor table
  255. OldPos:=Stream.Position;
  256. PackedSize := 0;
  257. Repeat
  258. Stream.read(B, 1);
  259. if B > 0 then
  260. begin
  261. inc(PackedSize, B);
  262. Stream.Seek(B, soFromCurrent);
  263. CodeMask := (1 shl CodeSize) - 1;
  264. end;
  265. until B = 0;
  266. Progress(psRunning, trunc(100.0 * (Stream.position / Stream.size)),
  267. False, Rect(0,0,0,0), '', ContProgress);
  268. if not ContProgress then exit(false);
  269. Getmem(DataComp, PackedSize);
  270. // read compressor table
  271. SourcePtr:=DataComp;
  272. Stream.Position:=OldPos;
  273. Repeat
  274. Stream.read(B, 1);
  275. if B > 0 then
  276. begin
  277. Stream.ReadBuffer(SourcePtr^, B);
  278. Inc(SourcePtr,B);
  279. end;
  280. until B = 0;
  281. Progress(psRunning, trunc(100.0 * (Stream.position / Stream.size)),
  282. False, Rect(0,0,0,0), '', ContProgress);
  283. if not ContProgress then exit(false);
  284. SourcePtr:=DataComp;
  285. Target := FScanLine;
  286. CodeSize := FInitialCodeSize + 1;
  287. ClearCode := 1 shl FInitialCodeSize;
  288. EOICode := ClearCode + 1;
  289. FreeCode := ClearCode + 2;
  290. OldCode := 4096;
  291. CodeMask := (1 shl CodeSize) - 1;
  292. UnpackedSize:=FWidth * FHeight;
  293. for I := 0 to ClearCode - 1 do
  294. begin
  295. Prefix[I] := 4096;
  296. Suffix[I] := I;
  297. end;
  298. StackPointer := @Stack;
  299. FirstChar := 0;
  300. Data := 0;
  301. Bits := 0;
  302. // LZW decompression gif
  303. while (UnpackedSize > 0) and (PackedSize > 0) do
  304. begin
  305. Inc(Data, SourcePtr^ shl Bits);
  306. Inc(Bits, 8);
  307. while Bits >= CodeSize do
  308. begin
  309. Code := Data and CodeMask;
  310. Data := Data shr CodeSize;
  311. Dec(Bits, CodeSize);
  312. if Code = EOICode then Break;
  313. if Code = ClearCode then
  314. begin
  315. CodeSize := FInitialCodeSize + 1;
  316. CodeMask := (1 shl CodeSize) - 1;
  317. FreeCode := ClearCode + 2;
  318. OldCode := 4096;
  319. Continue;
  320. end;
  321. if Code > FreeCode then Break;
  322. if OldCode = 4096 then
  323. begin
  324. FirstChar := Suffix[Code];
  325. Target^ := FirstChar;
  326. Inc(Target);
  327. Dec(UnpackedSize);
  328. OldCode := Code;
  329. Continue;
  330. end;
  331. InCode := Code;
  332. if Code = FreeCode then
  333. begin
  334. StackPointer^ := FirstChar;
  335. Inc(StackPointer);
  336. Code := OldCode;
  337. end;
  338. while Code > ClearCode do
  339. begin
  340. StackPointer^ := Suffix[Code];
  341. Inc(StackPointer);
  342. Code := Prefix[Code];
  343. end;
  344. FirstChar := Suffix[Code];
  345. StackPointer^ := FirstChar;
  346. Inc(StackPointer);
  347. Prefix[FreeCode] := OldCode;
  348. Suffix[FreeCode] := FirstChar;
  349. if (FreeCode = CodeMask) and
  350. (CodeSize < 12) then
  351. begin
  352. Inc(CodeSize);
  353. CodeMask := (1 shl CodeSize) - 1;
  354. end;
  355. if FreeCode < 4095 then Inc(FreeCode);
  356. OldCode := InCode;
  357. repeat
  358. Dec(StackPointer);
  359. Target^ := StackPointer^;
  360. Inc(Target);
  361. Dec(UnpackedSize);
  362. until StackPointer = @Stack;
  363. end;
  364. Inc(SourcePtr);
  365. Dec(PackedSize);
  366. end;
  367. Progress(psRunning, trunc(100.0 * (Stream.position / Stream.size)),
  368. False, Rect(0,0,0,0), '', ContProgress);
  369. if not ContProgress then exit(false);
  370. finally
  371. if DataComp<>nil then
  372. FreeMem(DataComp);
  373. end;
  374. Result:=true;
  375. end;
  376. function TFPReaderGif.WriteScanLine(Img: TFPCustomImage): Boolean;
  377. Var
  378. Row, Col : Integer;
  379. Pass, Every : byte;
  380. P : PByte;
  381. function IsMultiple(NumberA, NumberB: Integer): Boolean;
  382. begin
  383. Result := (NumberA >= NumberB) and
  384. (NumberB > 0) and
  385. (NumberA mod NumberB = 0);
  386. end;
  387. begin
  388. Result:=false;
  389. P:=FScanLine;
  390. If FInterlace then
  391. begin
  392. For Pass := 1 to 4 do
  393. begin
  394. Case Pass of
  395. 1 : begin
  396. Row := 0;
  397. Every := 8;
  398. end;
  399. 2 : begin
  400. Row := 4;
  401. Every := 8;
  402. end;
  403. 3 : begin
  404. Row := 2;
  405. Every := 4;
  406. end;
  407. 4 : begin
  408. Row := 1;
  409. Every := 2;
  410. end;
  411. end;
  412. Repeat
  413. for Col:=0 to Img.Width-1 do
  414. begin
  415. Img.Colors[Col,Row]:=FPalette[P^];
  416. Inc(P);
  417. end;
  418. Inc(Row, Every);
  419. until Row >= Img.Height;
  420. end;
  421. end
  422. else
  423. begin
  424. for Row:=0 to Img.Height-1 do
  425. for Col:=0 to Img.Width-1 do
  426. begin
  427. Img.Colors[Col,Row]:=FPalette[P^];
  428. Inc(P);
  429. end;
  430. end;
  431. Result:=true;
  432. end;
  433. function TFPReaderGif.InternalCheck(Stream: TStream): boolean;
  434. var
  435. OldPos: Int64;
  436. begin
  437. try
  438. OldPos:=Stream.Position;
  439. Stream.Read(FHeader,SizeOf(FHeader));
  440. Result:=(FHeader.Signature = 'GIF') and
  441. ((FHeader.Version = '87a') or (FHeader.Version = '89a'));
  442. Stream.Position:=OldPos;
  443. except
  444. Result:=False;
  445. end;
  446. end;
  447. function TFPReaderGif.SkipBlock(Stream: TStream): byte;
  448. var
  449. Introducer,
  450. Labels,
  451. SkipByte : byte;
  452. begin
  453. Stream.read(Introducer,1);
  454. if Introducer = $21 then
  455. begin
  456. Stream.read(Labels,1);
  457. Case Labels of
  458. $FE, $FF : // Comment Extension block or Application Extension block
  459. while true do
  460. begin
  461. Stream.Read(SkipByte, 1);
  462. if SkipByte = 0 then Break;
  463. Stream.Seek(SkipByte, soFromCurrent);
  464. end;
  465. $F9 : // Graphics Control Extension block
  466. begin
  467. Stream.Read(FGraphicsCtrlExt, SizeOf(FGraphicsCtrlExt));
  468. FGraphCtrlExt:=True;
  469. end;
  470. $01 : // Plain Text Extension block
  471. begin
  472. Stream.Read(SkipByte, 1);
  473. Stream.Seek(SkipByte, soFromCurrent);
  474. while true do
  475. begin
  476. Stream.Read(SkipByte, 1);
  477. if SkipByte = 0 then Break;
  478. Stream.Seek(SkipByte, soFromCurrent);
  479. end;
  480. end;
  481. end;
  482. end;
  483. Result:=Introducer;
  484. end;
  485. constructor TFPReaderGif.Create;
  486. begin
  487. inherited Create;
  488. end;
  489. destructor TFPReaderGif.Destroy;
  490. begin
  491. inherited Destroy;
  492. end;
  493. initialization
  494. ImageHandlers.RegisterImageReader ('GIF Graphics', 'gif', TFPReaderGif);
  495. end.