fpreadpng.pp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. {
  2. $Id: fpreadpng.pp,v 1.10 2003/10/19 21:09:51 luk Exp $
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 2003 by the Free Pascal development team
  5. PNG reader implementation
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$mode objfpc}{$h+}
  13. unit FPReadPNG;
  14. interface
  15. uses
  16. SysUtils,Classes, FPImage, FPImgCmn, PNGComn, ZStream;
  17. Type
  18. TSetPixelProc = procedure (x,y:integer; CD : TColordata) of object;
  19. TConvertColorProc = function (CD:TColorData) : TFPColor of object;
  20. TFPReaderPNG = class (TFPCustomImageReader)
  21. private
  22. FHeader : THeaderChunk;
  23. ZData : TMemoryStream; // holds compressed data until all blocks are read
  24. Decompress : TDeCompressionStream; // decompresses the data
  25. FPltte : boolean; // if palette is used
  26. FCountScanlines : EightLong; //Number of scanlines to process for each pass
  27. FScanLineLength : EightLong; //Length of scanline for each pass
  28. FCurrentPass : byte;
  29. ByteWidth : byte; // number of bytes to read for pixel information
  30. BitsUsed : EightLong; // bitmasks to use to split a byte into smaller parts
  31. BitShift : byte; // shift right to do of the bits extracted with BitsUsed for 1 element
  32. CountBitsUsed : byte; // number of bit groups (1 pixel) per byte (when bytewidth = 1)
  33. //CFmt : TColorFormat; // format of the colors to convert from
  34. StartX,StartY, DeltaX,DeltaY, StartPass,EndPass : integer; // number and format of passes
  35. FSwitchLine, FCurrentLine, FPreviousLine : pByteArray;
  36. FPalette : TFPPalette;
  37. FSetPixel : TSetPixelProc;
  38. FConvertColor : TConvertColorProc;
  39. procedure ReadChunk;
  40. procedure HandleData;
  41. procedure HandleUnknown;
  42. function ColorGray1 (CD:TColorData) : TFPColor;
  43. function ColorGray2 (CD:TColorData) : TFPColor;
  44. function ColorGray4 (CD:TColorData) : TFPColor;
  45. function ColorGray8 (CD:TColorData) : TFPColor;
  46. function ColorGray16 (CD:TColorData) : TFPColor;
  47. function ColorGrayAlpha8 (CD:TColorData) : TFPColor;
  48. function ColorGrayAlpha16 (CD:TColorData) : TFPColor;
  49. function ColorColor8 (CD:TColorData) : TFPColor;
  50. function ColorColor16 (CD:TColorData) : TFPColor;
  51. function ColorColorAlpha8 (CD:TColorData) : TFPColor;
  52. function ColorColorAlpha16 (CD:TColorData) : TFPColor;
  53. protected
  54. Chunk : TChunk;
  55. UseTransparent, EndOfFile : boolean;
  56. TransparentDataValue : TColorData;
  57. UsingBitGroup : byte;
  58. DataIndex : longword;
  59. DataBytes : TColorData;
  60. function CurrentLine(x:longword) : byte;
  61. function PrevSample (x:longword): byte;
  62. function PreviousLine (x:longword) : byte;
  63. function PrevLinePrevSample (x:longword): byte;
  64. procedure HandleChunk; virtual;
  65. procedure HandlePalette; virtual;
  66. procedure HandleAlpha; virtual;
  67. function CalcX (relX:integer) : integer;
  68. function CalcY (relY:integer) : integer;
  69. function CalcColor: TColorData;
  70. procedure HandleScanLine (const y : integer; const ScanLine : PByteArray); virtual;
  71. procedure DoDecompress; virtual;
  72. function DoFilter(LineFilter:byte;index:longword; b:byte) : byte; virtual;
  73. procedure SetPalettePixel (x,y:integer; CD : TColordata);
  74. procedure SetPalColPixel (x,y:integer; CD : TColordata);
  75. procedure SetColorPixel (x,y:integer; CD : TColordata);
  76. procedure SetColorTrPixel (x,y:integer; CD : TColordata);
  77. function DecideSetPixel : TSetPixelProc; virtual;
  78. procedure InternalRead (Str:TStream; Img:TFPCustomImage); override;
  79. function InternalCheck (Str:TStream) : boolean; override;
  80. class function InternalSize(Str:TStream): TPoint; override;
  81. //property ColorFormat : TColorformat read CFmt;
  82. property ConvertColor : TConvertColorProc read FConvertColor;
  83. property CurrentPass : byte read FCurrentPass;
  84. property Pltte : boolean read FPltte;
  85. property ThePalette : TFPPalette read FPalette;
  86. property Header : THeaderChunk read FHeader;
  87. property CountScanlines : EightLong read FCountScanlines;
  88. property ScanLineLength : EightLong read FScanLineLength;
  89. public
  90. constructor create; override;
  91. destructor destroy; override;
  92. end;
  93. implementation
  94. const StartPoints : array[0..7, 0..1] of word =
  95. ((0,0),(0,0),(4,0),(0,4),(2,0),(0,2),(1,0),(0,1));
  96. Delta : array[0..7,0..1] of word =
  97. ((1,1),(8,8),(8,8),(4,8),(4,4),(2,4),(2,2),(1,2));
  98. BitsUsed1Depth : EightLong = ($80,$40,$20,$10,$08,$04,$02,$01);
  99. BitsUsed2Depth : EightLong = ($C0,$30,$0C,$03,0,0,0,0);
  100. BitsUsed4Depth : EightLong = ($F0,$0F,0,0,0,0,0,0);
  101. constructor TFPReaderPNG.create;
  102. begin
  103. inherited;
  104. chunk.acapacity := 0;
  105. chunk.data := nil;
  106. UseTransparent := False;
  107. end;
  108. destructor TFPReaderPNG.destroy;
  109. begin
  110. with chunk do
  111. if acapacity > 0 then
  112. freemem (data);
  113. inherited;
  114. end;
  115. procedure TFPReaderPNG.ReadChunk;
  116. var ChunkHeader : TChunkHeader;
  117. readCRC : longword;
  118. l : longword;
  119. begin
  120. TheStream.Read (ChunkHeader,sizeof(ChunkHeader));
  121. with chunk do
  122. begin
  123. // chunk header
  124. with ChunkHeader do
  125. begin
  126. {$IFDEF ENDIAN_LITTLE}
  127. alength := swap(CLength);
  128. {$ELSE}
  129. alength := CLength;
  130. {$ENDIF}
  131. ReadType := CType;
  132. end;
  133. aType := low(TChunkTypes);
  134. while (aType < high(TChunkTypes)) and (ChunkTypes[aType] <> ReadType) do
  135. inc (aType);
  136. if alength > MaxChunkLength then
  137. raise PNGImageException.Create ('Invalid chunklength');
  138. if alength > acapacity then
  139. begin
  140. if acapacity > 0 then
  141. freemem (data);
  142. GetMem (data, alength);
  143. acapacity := alength;
  144. end;
  145. l := TheStream.read (data^, alength);
  146. if l <> alength then
  147. raise PNGImageException.Create ('Chunk length exceeds stream length');
  148. TheStream.Read (readCRC, sizeof(ReadCRC));
  149. l := CalculateCRC (All1Bits, ReadType, sizeOf(ReadType));
  150. l := CalculateCRC (l, data^, alength);
  151. {$IFDEF ENDIAN_LITTLE}
  152. l := swap(l xor All1Bits);
  153. {$ELSE}
  154. l := l xor All1Bits;
  155. {$ENDIF}
  156. if ReadCRC <> l then
  157. raise PNGImageException.Create ('CRC check failed');
  158. end;
  159. end;
  160. procedure TFPReaderPNG.HandleData;
  161. var OldSize : longword;
  162. begin
  163. OldSize := ZData.size;
  164. ZData.Size := OldSize + Chunk.aLength;
  165. ZData.Write (chunk.Data^, chunk.aLength);
  166. end;
  167. procedure TFPReaderPNG.HandleAlpha;
  168. procedure PaletteAlpha;
  169. var r : integer;
  170. a : word;
  171. c : TFPColor;
  172. begin
  173. with chunk do
  174. begin
  175. if alength > longword(ThePalette.count) then
  176. raise PNGImageException.create ('To much alpha values for palette');
  177. for r := 0 to alength-1 do
  178. begin
  179. c := ThePalette[r];
  180. a := data^[r];
  181. c.alpha := (a shl 8) + a;
  182. ThePalette[r] := c;
  183. end;
  184. end;
  185. end;
  186. procedure TransparentGray;
  187. var a : word;
  188. begin
  189. move (chunk.data^[0], a, 2);
  190. {$IFDEF ENDIAN_LITTLE}
  191. a := swap (a);
  192. {$ENDIF}
  193. TransparentDataValue := a;
  194. UseTransparent := True;
  195. end;
  196. procedure TransparentColor;
  197. var d : byte;
  198. r,g,b : word;
  199. a : TColorData;
  200. begin
  201. with chunk do
  202. begin
  203. move (data^[0], r, 2);
  204. move (data^[2], g, 2);
  205. move (data^[4], b, 2);
  206. end;
  207. {$IFDEF ENDIAN_LITTLE}
  208. r := swap (r);
  209. g := swap (g);
  210. b := swap (b);
  211. {$ENDIF}
  212. d := header.bitdepth;
  213. a := (TColorData(b) shl d) shl d;
  214. a := a + (TColorData(g) shl d) + r;
  215. TransparentDataValue := a;
  216. UseTransparent := True;
  217. end;
  218. begin
  219. case header.ColorType of
  220. 3 : PaletteAlpha;
  221. 0 : TransparentGray;
  222. 2 : TransparentColor;
  223. end;
  224. end;
  225. procedure TFPReaderPNG.HandlePalette;
  226. var r : longword;
  227. c : TFPColor;
  228. t : word;
  229. begin
  230. if header.colortype = 3 then
  231. with chunk do
  232. begin
  233. if TheImage.UsePalette then
  234. FPalette := TheImage.Palette
  235. else
  236. FPalette := TFPPalette.Create(0);
  237. c.Alpha := AlphaOpaque;
  238. if (aLength mod 3) > 0 then
  239. raise PNGImageException.Create ('Impossible length for PLTE-chunk');
  240. r := 0;
  241. ThePalette.count := 0;
  242. while r < alength do
  243. begin
  244. t := data^[r];
  245. c.red := t + (t shl 8);
  246. inc (r);
  247. t := data^[r];
  248. c.green := t + (t shl 8);
  249. inc (r);
  250. t := data^[r];
  251. c.blue := t + (t shl 8);
  252. inc (r);
  253. ThePalette.Add (c);
  254. end;
  255. end;
  256. end;
  257. procedure TFPReaderPNG.SetPalettePixel (x,y:integer; CD : TColordata);
  258. begin // both PNG and palette have palette
  259. TheImage.Pixels[x,y] := CD;
  260. end;
  261. procedure TFPReaderPNG.SetPalColPixel (x,y:integer; CD : TColordata);
  262. begin // PNG with palette, Img without
  263. TheImage.Colors[x,y] := ThePalette[CD];
  264. end;
  265. procedure TFPReaderPNG.SetColorPixel (x,y:integer; CD : TColordata);
  266. var c : TFPColor;
  267. begin // both PNG and Img work without palette, and no transparency colordata
  268. // c := ConvertColor (CD,CFmt);
  269. c := ConvertColor (CD);
  270. TheImage.Colors[x,y] := c;
  271. end;
  272. procedure TFPReaderPNG.SetColorTrPixel (x,y:integer; CD : TColordata);
  273. var c : TFPColor;
  274. begin // both PNG and Img work without palette, and there is a transparency colordata
  275. //c := ConvertColor (CD,CFmt);
  276. c := ConvertColor (CD);
  277. if TransparentDataValue = CD then
  278. c.alpha := alphaTransparent;
  279. TheImage.Colors[x,y] := c;
  280. end;
  281. function TFPReaderPNG.CurrentLine(x:longword):byte;
  282. begin
  283. result := FCurrentLine^[x];
  284. end;
  285. function TFPReaderPNG.PrevSample (x:longword): byte;
  286. begin
  287. if x < byteWidth then
  288. result := 0
  289. else
  290. result := FCurrentLine^[x - bytewidth];
  291. end;
  292. function TFPReaderPNG.PreviousLine (x:longword) : byte;
  293. begin
  294. result := FPreviousline^[x];
  295. end;
  296. function TFPReaderPNG.PrevLinePrevSample (x:longword): byte;
  297. begin
  298. if x < byteWidth then
  299. result := 0
  300. else
  301. result := FPreviousLine^[x - bytewidth];
  302. end;
  303. function TFPReaderPNG.DoFilter(LineFilter:byte;index:longword; b:byte) : byte;
  304. var diff : byte;
  305. procedure FilterSub;
  306. begin
  307. diff := PrevSample(index);
  308. end;
  309. procedure FilterUp;
  310. begin
  311. diff := PreviousLine(index);
  312. end;
  313. procedure FilterAverage;
  314. var l, p : word;
  315. begin
  316. l := PrevSample(index);
  317. p := PreviousLine(index);
  318. diff := (l + p) div 2;
  319. end;
  320. procedure FilterPaeth;
  321. var dl, dp, dlp : word; // index for previous and distances for:
  322. l, p, lp : byte; // r:predictor, Left, Previous, LeftPrevious
  323. r : integer;
  324. begin
  325. l := PrevSample(index);
  326. lp := PrevLinePrevSample(index);
  327. p := PreviousLine(index);
  328. r := l + p - lp;
  329. dl := abs (r - l);
  330. dlp := abs (r - lp);
  331. dp := abs (r - p);
  332. if (dl <= dp) and (dl <= dlp) then
  333. diff := l
  334. else if dp <= dlp then
  335. diff := p
  336. else
  337. diff := lp;
  338. end;
  339. begin
  340. case LineFilter of
  341. 0 : diff := 0;
  342. 1 : FilterSub;
  343. 2 : FilterUp;
  344. 3 : FilterAverage;
  345. 4 : FilterPaeth;
  346. end;
  347. result := (b + diff) mod $100;
  348. end;
  349. function TFPReaderPNG.DecideSetPixel : TSetPixelProc;
  350. begin
  351. if Pltte then
  352. if TheImage.UsePalette then
  353. result := @SetPalettePixel
  354. else
  355. result := @SetPalColPixel
  356. else
  357. if UseTransparent then
  358. result := @SetColorTrPixel
  359. else
  360. result := @SetColorPixel;
  361. end;
  362. function TFPReaderPNG.CalcX (relX:integer) : integer;
  363. begin
  364. result := StartX + (relX * deltaX);
  365. end;
  366. function TFPReaderPNG.CalcY (relY:integer) : integer;
  367. begin
  368. result := StartY + (relY * deltaY);
  369. end;
  370. function TFPReaderPNG.CalcColor: TColorData;
  371. var cd : longword;
  372. r : word;
  373. b : pbyte;
  374. begin
  375. if UsingBitGroup = 0 then
  376. begin
  377. Databytes := 0;
  378. if Header.BitDepth = 16 then
  379. begin
  380. b := @Databytes;
  381. b^ := 0;
  382. r := 0;
  383. while (r < ByteWidth-1) do
  384. begin
  385. b^ := FCurrentLine^[DataIndex+r+1];
  386. inc (b);
  387. b^ := FCurrentLine^[DataIndex+r];
  388. inc (b);
  389. inc (r,2);
  390. end;
  391. end
  392. else move (FCurrentLine^[DataIndex], Databytes, bytewidth);
  393. {$IFDEF ENDIAN_BIG}
  394. Databytes:=swap(Databytes);
  395. {$ENDIF}
  396. inc (DataIndex,bytewidth);
  397. end;
  398. if bytewidth = 1 then
  399. begin
  400. cd := (Databytes and BitsUsed[UsingBitGroup]);
  401. result := cd shr ((CountBitsUsed-UsingBitGroup-1) * BitShift);
  402. inc (UsingBitgroup);
  403. if UsingBitGroup >= CountBitsUsed then
  404. UsingBitGroup := 0;
  405. end
  406. else
  407. result := Databytes;
  408. end;
  409. procedure TFPReaderPNG.HandleScanLine (const y : integer; const ScanLine : PByteArray);
  410. var x, rx : integer;
  411. c : TColorData;
  412. begin
  413. UsingBitGroup := 0;
  414. DataIndex := 0;
  415. for rx := 0 to ScanlineLength[CurrentPass]-1 do
  416. begin
  417. X := CalcX(rx);
  418. c := CalcColor;
  419. FSetPixel (x,y,c);
  420. end
  421. end;
  422. function TFPReaderPNG.ColorGray1 (CD:TColorDAta) : TFPColor;
  423. begin
  424. if CD = 0 then
  425. result := colBlack
  426. else
  427. result := colWhite;
  428. end;
  429. function TFPReaderPNG.ColorGray2 (CD:TColorDAta) : TFPColor;
  430. var c : word;
  431. begin
  432. c := CD and 3;
  433. c := c + (c shl 2);
  434. c := c + (c shl 4);
  435. c := c + (c shl 8);
  436. with result do
  437. begin
  438. red := c;
  439. green := c;
  440. blue := c;
  441. alpha := alphaOpaque;
  442. end;
  443. end;
  444. function TFPReaderPNG.ColorGray4 (CD:TColorDAta) : TFPColor;
  445. var c : word;
  446. begin
  447. c := CD and $F;
  448. c := c + (c shl 4);
  449. c := c + (c shl 8);
  450. with result do
  451. begin
  452. red := c;
  453. green := c;
  454. blue := c;
  455. alpha := alphaOpaque;
  456. end;
  457. end;
  458. function TFPReaderPNG.ColorGray8 (CD:TColorDAta) : TFPColor;
  459. var c : word;
  460. begin
  461. c := CD and $FF;
  462. c := c + (c shl 8);
  463. with result do
  464. begin
  465. red := c;
  466. green := c;
  467. blue := c;
  468. alpha := alphaOpaque;
  469. end;
  470. end;
  471. function TFPReaderPNG.ColorGray16 (CD:TColorDAta) : TFPColor;
  472. var c : word;
  473. begin
  474. c := CD and $FFFF;
  475. with result do
  476. begin
  477. red := c;
  478. green := c;
  479. blue := c;
  480. alpha := alphaOpaque;
  481. end;
  482. end;
  483. function TFPReaderPNG.ColorGrayAlpha8 (CD:TColorData) : TFPColor;
  484. var c : word;
  485. begin
  486. c := CD and $00FF;
  487. c := c + (c shl 8);
  488. with result do
  489. begin
  490. red := c;
  491. green := c;
  492. blue := c;
  493. c := CD and $FF00;
  494. alpha := c + (c shr 8);
  495. end;
  496. end;
  497. function TFPReaderPNG.ColorGrayAlpha16 (CD:TColorData) : TFPColor;
  498. var c : word;
  499. begin
  500. {$ifdef FPC_LITTLE_ENDIAN}
  501. c := CD and $FFFF;
  502. {$else}
  503. c := (CD shr 16) and $FFFF;
  504. {$endif}
  505. with result do
  506. begin
  507. red := c;
  508. green := c;
  509. blue := c;
  510. {$ifdef FPC_LITTLE_ENDIAN}
  511. alpha := (CD shr 16) and $FFFF;
  512. {$else}
  513. alpha := CD and $FFFF;
  514. {$endif}
  515. end;
  516. end;
  517. function TFPReaderPNG.ColorColor8 (CD:TColorData) : TFPColor;
  518. var c : word;
  519. begin
  520. with result do
  521. begin
  522. c := CD and $FF;
  523. red := c + (c shl 8);
  524. CD:=CD shr 8;
  525. c := CD and $FF;
  526. green := c + (c shl 8);
  527. CD:=CD shr 8;
  528. c := CD and $FF;
  529. blue := c + (c shl 8);
  530. alpha := alphaOpaque;
  531. end;
  532. end;
  533. function TFPReaderPNG.ColorColor16 (CD:TColorData) : TFPColor;
  534. begin
  535. with result do
  536. begin
  537. red := CD and $FFFF;
  538. CD:=CD shr 16;
  539. green := CD and $FFFF;
  540. CD:=CD shr 16;
  541. blue := CD and $FFFF;
  542. alpha := alphaOpaque;
  543. end;
  544. end;
  545. function TFPReaderPNG.ColorColorAlpha8 (CD:TColorData) : TFPColor;
  546. var c : word;
  547. begin
  548. with result do
  549. begin
  550. c := CD and $FF;
  551. red := c + (c shl 8);
  552. CD:=CD shr 8;
  553. c := CD and $FF;
  554. green := c + (c shl 8);
  555. CD:=CD shr 8;
  556. c := CD and $FF;
  557. blue := c + (c shl 8);
  558. CD:=CD shr 8;
  559. c := CD and $FF;
  560. alpha := c + (c shl 8);
  561. end;
  562. end;
  563. function TFPReaderPNG.ColorColorAlpha16 (CD:TColorData) : TFPColor;
  564. begin
  565. with result do
  566. begin
  567. red := CD and $FFFF;
  568. CD:=CD shr 16;
  569. green := CD and $FFFF;
  570. CD:=CD shr 16;
  571. blue := CD and $FFFF;
  572. CD:=CD shr 16;
  573. alpha := CD and $FFFF;
  574. end;
  575. end;
  576. procedure TFPReaderPNG.DoDecompress;
  577. procedure initVars;
  578. var r,d : integer;
  579. begin
  580. with Header do
  581. begin
  582. if interlace=0 then
  583. begin
  584. StartPass := 0;
  585. EndPass := 0;
  586. FCountScanlines[0] := Height;
  587. FScanLineLength[0] := Width;
  588. end
  589. else
  590. begin
  591. StartPass := 1;
  592. EndPass := 7;
  593. for r := 1 to 7 do
  594. begin
  595. d := Height div delta[r,1];
  596. if (height mod delta[r,1]) > startpoints[r,1] then
  597. inc (d);
  598. FCountScanlines[r] := d;
  599. d := width div delta[r,0];
  600. if (width mod delta[r,0]) > startpoints[r,0] then
  601. inc (d);
  602. FScanLineLength[r] := d;
  603. end;
  604. end;
  605. Fpltte := (ColorType = 3);
  606. case colortype of
  607. 0 : case Bitdepth of
  608. 1 : begin
  609. FConvertColor := @ColorGray1; //CFmt := cfMono;
  610. ByteWidth := 1;
  611. end;
  612. 2 : begin
  613. FConvertColor := @ColorGray2; //CFmt := cfGray2;
  614. ByteWidth := 1;
  615. end;
  616. 4 : begin
  617. FConvertColor := @ColorGray4; //CFmt := cfGray4;
  618. ByteWidth := 1;
  619. end;
  620. 8 : begin
  621. FConvertColor := @ColorGray8; //CFmt := cfGray8;
  622. ByteWidth := 1;
  623. end;
  624. 16 : begin
  625. FConvertColor := @ColorGray16; //CFmt := cfGray16;
  626. ByteWidth := 2;
  627. end;
  628. end;
  629. 2 : if BitDepth = 8 then
  630. begin
  631. FConvertColor := @ColorColor8; //CFmt := cfBGR24
  632. ByteWidth := 3;
  633. end
  634. else
  635. begin
  636. FConvertColor := @ColorColor16; //CFmt := cfBGR48;
  637. ByteWidth := 6;
  638. end;
  639. 3 : if BitDepth = 16 then
  640. ByteWidth := 2
  641. else
  642. ByteWidth := 1;
  643. 4 : if BitDepth = 8 then
  644. begin
  645. FConvertColor := @ColorGrayAlpha8; //CFmt := cfGrayA16
  646. ByteWidth := 2;
  647. end
  648. else
  649. begin
  650. FConvertColor := @ColorGrayAlpha16; //CFmt := cfGrayA32;
  651. ByteWidth := 4;
  652. end;
  653. 6 : if BitDepth = 8 then
  654. begin
  655. FConvertColor := @ColorColorAlpha8; //CFmt := cfABGR32
  656. ByteWidth := 4;
  657. end
  658. else
  659. begin
  660. FConvertColor := @ColorColorAlpha16; //CFmt := cfABGR64;
  661. ByteWidth := 8;
  662. end;
  663. end;
  664. //ByteWidth := BytesNeeded[CFmt];
  665. case BitDepth of
  666. 1 : begin
  667. CountBitsUsed := 8;
  668. BitShift := 1;
  669. BitsUsed := BitsUsed1Depth;
  670. end;
  671. 2 : begin
  672. CountBitsUsed := 4;
  673. BitShift := 2;
  674. BitsUsed := BitsUsed2Depth;
  675. end;
  676. 4 : begin
  677. CountBitsUsed := 2;
  678. BitShift := 4;
  679. BitsUsed := BitsUsed4Depth;
  680. end;
  681. 8 : begin
  682. CountBitsUsed := 1;
  683. BitShift := 0;
  684. BitsUsed[0] := $FF;
  685. end;
  686. end;
  687. end;
  688. end;
  689. procedure Decode;
  690. var y, rp, ry, rx, l : integer;
  691. lf : byte;
  692. begin
  693. FSetPixel := DecideSetPixel;
  694. for rp := StartPass to EndPass do
  695. begin
  696. FCurrentPass := rp;
  697. StartX := StartPoints[rp,0];
  698. StartY := StartPoints[rp,1];
  699. DeltaX := Delta[rp,0];
  700. DeltaY := Delta[rp,1];
  701. if bytewidth = 1 then
  702. begin
  703. l := (ScanLineLength[rp] div CountBitsUsed);
  704. if (ScanLineLength[rp] mod CountBitsUsed) > 0 then
  705. inc (l);
  706. end
  707. else
  708. l := ScanLineLength[rp]*ByteWidth;
  709. if (l>0) then
  710. begin
  711. GetMem (FPreviousLine, l);
  712. GetMem (FCurrentLine, l);
  713. fillchar (FCurrentLine^,l,0);
  714. try
  715. for ry := 0 to CountScanlines[rp]-1 do
  716. begin
  717. FSwitchLine := FCurrentLine;
  718. FCurrentLine := FPreviousLine;
  719. FPreviousLine := FSwitchLine;
  720. Y := CalcY(ry);
  721. Decompress.Read (lf, sizeof(lf));
  722. Decompress.Read (FCurrentLine^, l);
  723. if lf <> 0 then // Do nothing when there is no filter used
  724. for rx := 0 to l-1 do
  725. FCurrentLine^[rx] := DoFilter (lf, rx, FCurrentLine^[rx]);
  726. HandleScanLine (y, FCurrentLine);
  727. end;
  728. finally
  729. freemem (FPreviousLine);
  730. freemem (FCurrentLine);
  731. end;
  732. end;
  733. end;
  734. end;
  735. begin
  736. InitVars;
  737. DeCode;
  738. end;
  739. procedure TFPReaderPNG.HandleChunk;
  740. begin
  741. case chunk.AType of
  742. ctIHDR : raise PNGImageException.Create ('Second IHDR chunk found');
  743. ctPLTE : HandlePalette;
  744. ctIDAT : HandleData;
  745. ctIEND : EndOfFile := True;
  746. cttRNS : HandleAlpha;
  747. else HandleUnknown;
  748. end;
  749. end;
  750. procedure TFPReaderPNG.HandleUnknown;
  751. begin
  752. if (chunk.readtype[0] in ['A'..'Z']) then
  753. raise PNGImageException.Create('Critical chunk '+chunk.readtype+' not recognized');
  754. end;
  755. // NOTE: It is assumed that signature and IDHDR chunk already have been read.
  756. procedure TFPReaderPNG.InternalRead (Str:TStream; Img:TFPCustomImage);
  757. begin
  758. {$ifdef FPC_Debug_Image}
  759. if Str<>TheStream then
  760. writeln('WARNING: TFPReaderPNG.InternalRead Str<>TheStream');
  761. {$endif}
  762. with Header do
  763. Img.SetSize (Width, Height);
  764. ZData := TMemoryStream.Create;
  765. try
  766. EndOfFile := false;
  767. while not EndOfFile do
  768. begin
  769. ReadChunk;
  770. HandleChunk;
  771. end;
  772. ZData.position:=0;
  773. Decompress := TDecompressionStream.Create (ZData);
  774. try
  775. DoDecompress;
  776. finally
  777. Decompress.Free;
  778. end;
  779. finally
  780. ZData.Free;
  781. if not img.UsePalette and assigned(FPalette) then
  782. begin
  783. FreeAndNil(FPalette);
  784. end;
  785. end;
  786. end;
  787. class function TFPReaderPNG.InternalSize(Str: TStream): TPoint;
  788. var
  789. SigCheck: array[0..7] of byte;
  790. r: Integer;
  791. Width, Height: Word;
  792. StartPos: Int64;
  793. begin
  794. Result.X := 0;
  795. Result.Y := 0;
  796. StartPos := Str.Position;
  797. // Check Signature
  798. Str.Read(SigCheck, SizeOf(SigCheck));
  799. for r := Low(SigCheck) to High(SigCheck) do
  800. begin
  801. If SigCheck[r] <> Signature[r] then
  802. Exit;
  803. end;
  804. if not(
  805. (Str.Seek(10, soFromCurrent)=StartPos+18)
  806. and (Str.Read(Width, 2)=2)
  807. and (Str.Seek(2, soFromCurrent)=StartPos+22)
  808. and (Str.Read(Height, 2)=2))
  809. then
  810. Exit;
  811. {$IFDEF ENDIAN_LITTLE}
  812. Width := Swap(Width);
  813. Height := Swap(Height);
  814. {$ENDIF}
  815. Result.X := Width;
  816. Result.Y := Height;
  817. end;
  818. // NOTE: Stream does not rewind here!
  819. function TFPReaderPNG.InternalCheck (Str:TStream) : boolean;
  820. var SigCheck : array[0..7] of byte;
  821. r : integer;
  822. begin
  823. Result:=False;
  824. if Str=Nil then
  825. exit;
  826. // Check Signature
  827. if Str.Read(SigCheck, SizeOf(SigCheck)) <> SizeOf(SigCheck) then
  828. Exit;
  829. for r := 0 to 7 do
  830. begin
  831. If SigCheck[r] <> Signature[r] then
  832. Exit;
  833. end;
  834. // Check IHDR
  835. ReadChunk;
  836. move (chunk.data^, FHeader, sizeof(Header));
  837. with header do
  838. begin
  839. {$IFDEF ENDIAN_LITTLE}
  840. Width := swap(width);
  841. height := swap (height);
  842. {$ENDIF}
  843. result :=(width > 0) and (height > 0) and (compression = 0)
  844. and (filter = 0) and (Interlace in [0,1]);
  845. end;
  846. end;
  847. initialization
  848. ImageHandlers.RegisterImageReader ('Portable Network Graphics', 'png', TFPReaderPNG);
  849. end.