fpreadpsd.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by the Free Pascal development team
  4. Tiff 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 FPReadPSD;
  14. {$mode objfpc}{$H+}
  15. interface
  16. uses
  17. Classes, SysUtils, FPimage;
  18. type
  19. TRGB = packed record
  20. Red, Green, Blue : Byte;
  21. end;
  22. TLab = record
  23. L, a, b: byte;
  24. end;
  25. TPSDHeader = packed record
  26. Signature : array[0..3] of Char; // File IDs '8BPS'
  27. Version : word; // Version number, always 1
  28. Reserved : array[0..5] of Byte; // Reserved, must be zeroed
  29. Channels : Word; // Number of color channels (1-24) including alpha channels
  30. Rows : Cardinal; // Height of image in pixels (1-30000)
  31. Columns : Cardinal; // Width of image in pixels (1-30000)
  32. Depth : Word; // Number of bits per channel (1, 8, and 16)
  33. Mode: Word; // Color mode
  34. end;
  35. {
  36. Mode Description
  37. 0 Bitmap (monochrome)
  38. 1 Gray-scale
  39. 2 Indexed color (palette color)
  40. 3 RGB color
  41. 4 CMYK color
  42. 7 Multichannel color
  43. 8 Duotone (halftone)
  44. 9 Lab color
  45. }
  46. TColorModeDataBlock = packed record
  47. Types : array[0..3] of Char; // Always "8BIM"
  48. ID:word; // (See table below)
  49. Name:byte; // Even-length Pascal-format string, 2 bytes or longer
  50. Size : Cardinal; // Length of resource data following, in bytes
  51. Data:byte; // Resource data, padded to even length
  52. end;
  53. {
  54. ID Data Format Description
  55. 03e8 WORD[5] Channels, rows, columns, depth, and mode
  56. 03e9 Optional Macintosh print manager information
  57. 03eb Indexed color table
  58. 03ed (See below) Resolution information
  59. "TResolutionInfo"
  60. 03ee BYTE[] Alpha channel names (Pascal-format strings)
  61. 03ef (See below) Display information for each channel
  62. "TDisplayInfo"
  63. 03f0 BYTE[] Optional Pascal-format caption string
  64. 03f1 LONG, WORD Fixed-point border width, border units (see below)
  65. 03f2 Background color
  66. 03f3 BYTE[8] Print flags (see below)
  67. 03f4 Gray-scale and halftoning information
  68. 03f5 Color halftoning information
  69. 03f6 Duotone halftoning information
  70. 03f7 Gray-scale and multichannel transfer function
  71. 03f8 Color transfer functions
  72. 03f9 Duotone transfer functions
  73. 03fa Duotone image information
  74. 03fb BYTE[2] Effective black and white value for dot range
  75. 03fc
  76. 03fd EPS options
  77. 03fe WORD, BYTE Quick Mask channel ID, flag for mask initially empty
  78. 03ff
  79. 0400 WORD Index of target layer (0=bottom)
  80. 0401 Working path
  81. 0402 WORD[] Layers group info, group ID for dragging groups
  82. 0403
  83. 0404 IPTC-NAA record
  84. 0405 Image mode for raw-format files
  85. 0406 JPEG quality (Adobe internal)
  86. 07d0
  87. 0bb6 Saved path information
  88. 0bb7 Clipping pathname
  89. 2710 (See below) Print flags information
  90. }
  91. TResolutionInfo = record
  92. hRes:Cardinal; // Fixed-point number: pixels per inch
  93. hResUnit:word; // 1=pixels per inch, 2=pixels per centimeter
  94. WidthUnit:word; // 1=in, 2=cm, 3=pt, 4=picas, 5=columns
  95. vRes:Cardinal; // Fixed-point number: pixels per inch
  96. vResUnit:word; // 1=pixels per inch, 2=pixels per centimeter
  97. HeightUnit:word; // 1=in, 2=cm, 3=pt, 4=picas, 5=columns
  98. end;
  99. TDisplayInfo = record
  100. ColorSpace:word;
  101. Color:array[0..3] of word;
  102. Opacity:word; // 0-100
  103. Kind:byte; // 0=selected, 1=protected
  104. Padding:byte; // Always zero
  105. end;
  106. TFPReaderPSD = class;
  107. TPSDCreateCompatibleImgEvent = procedure(Sender: TFPReaderPSD;
  108. var NewImage: TFPCustomImage) of object;
  109. { TFPReaderPSD }
  110. TFPReaderPSD = class(TFPCustomImageReader)
  111. private
  112. FCompressed: boolean;
  113. FOnCreateImage: TPSDCreateCompatibleImgEvent;
  114. protected
  115. FHeader : TPSDHeader;
  116. FColorDataBlock: TColorModeDataBlock;
  117. FBytesPerPixel : Byte;
  118. FScanLine : PByte;
  119. FLineSize : PtrInt;
  120. FPalette : TFPPalette;
  121. FWidth : integer;
  122. FHeight : Integer;
  123. FBlockCount : word;
  124. FChannelCount : word;
  125. FLengthOfLine : array of Word;
  126. FByteRead : PtrInt;
  127. procedure CreateGrayPalette;
  128. procedure CreateBWPalette;
  129. function ReadPalette(Stream: TStream): boolean;
  130. procedure AnalyzeHeader;
  131. procedure InternalRead(Stream: TStream; Img: TFPCustomImage); override;
  132. function ReadScanLine(Stream: TStream): boolean; virtual;
  133. procedure WriteScanLine(Img: TFPCustomImage); virtual;
  134. function InternalCheck(Stream: TStream) : boolean; override;
  135. public
  136. constructor Create; override;
  137. property Compressed: Boolean read FCompressed;
  138. property ThePalette: TFPPalette read FPalette;
  139. property Width: integer read FWidth;
  140. property Height: integer read FHeight;
  141. property BytesPerPixel: Byte read FBytesPerPixel;
  142. property BlockCount: word read FBlockCount;
  143. property ChannelCount: word read FChannelCount;
  144. property Header: TPSDHeader read FHeader;
  145. property OnCreateImage: TPSDCreateCompatibleImgEvent read FOnCreateImage write FOnCreateImage;
  146. end;
  147. implementation
  148. function CorrectCMYK(const C : TFPColor): TFPColor;
  149. var
  150. MinColor: word;
  151. begin
  152. if C.red<C.green then MinColor:=C.red
  153. else MinColor:= C.green;
  154. if C.blue<MinColor then MinColor:= C.blue;
  155. if MinColor+ C.alpha>$FFFF then MinColor:=$FFFF-C.alpha;
  156. Result.red:=C.red-MinColor;
  157. Result.green:=C.green-MinColor;
  158. Result.blue:=C.blue-MinColor;
  159. Result.alpha:=C.alpha+MinColor;
  160. end;
  161. function CMYKtoRGB ( C : TFPColor): TFPColor;
  162. begin
  163. C:=CorrectCMYK(C);
  164. if (C.red + C.Alpha)<$FFFF then
  165. Result.Red:=$FFFF-(C.red+C.Alpha) else Result.Red:=0;
  166. if (C.Green+C.Alpha)<$FFFF then
  167. Result.Green:=$FFFF-(C.Green+C.Alpha) else Result.Green:=0;
  168. if (C.blue+C.Alpha)<$FFFF then
  169. Result.blue:=$FFFF-(C.blue+C.Alpha) else Result.blue:=0;
  170. Result.alpha:=alphaOpaque;
  171. end;
  172. function XYZToRGB(const X, Y, Z :double):TFPColor;
  173. begin
  174. // ToDo
  175. Result:=colBlack;
  176. end;
  177. function LabToRGB(const L:TLab):TFPColor;
  178. begin
  179. // ToDo
  180. Result:=colBlack;
  181. end;
  182. { TFPReaderPSD }
  183. procedure TFPReaderPSD.CreateGrayPalette;
  184. Var
  185. I : Integer;
  186. c : TFPColor;
  187. Begin
  188. ThePalette.count := 0;
  189. For I:=0 To 255 Do
  190. Begin
  191. With c do
  192. begin
  193. Red:=I*255;
  194. Green:=I*255;
  195. Blue:=I*255;
  196. Alpha:=alphaOpaque;
  197. end;
  198. ThePalette.Add (c);
  199. End;
  200. end;
  201. procedure TFPReaderPSD.CreateBWPalette;
  202. begin
  203. ThePalette.count := 0;
  204. ThePalette.Add (colBlack);
  205. ThePalette.Add (colWhite);
  206. end;
  207. function TFPReaderPSD.ReadPalette(Stream: TStream): boolean;
  208. Var
  209. I : Integer;
  210. c : TFPColor;
  211. OldPos: Integer;
  212. BufSize:Longint;
  213. PalBuf: array[0..767] of Byte;
  214. ContProgress: Boolean;
  215. begin
  216. Result:=false;
  217. ThePalette.count := 0;
  218. OldPos := Stream.Position;
  219. BufSize:=0;
  220. Stream.Read(BufSize, SizeOf(BufSize));
  221. BufSize:=BEtoN(BufSize);
  222. Stream.Read(PalBuf, BufSize);
  223. ContProgress:=true;
  224. Progress(FPimage.psRunning, trunc(100.0 * (Stream.position / Stream.size)),
  225. False, Rect(0,0,0,0), '', ContProgress);
  226. if not ContProgress then exit;
  227. For I:=0 To BufSize div 3 Do
  228. Begin
  229. With c do
  230. begin
  231. Red:=PalBuf[I] shl 8;
  232. Green:=PalBuf[I+(BufSize div 3)] shl 8;
  233. Blue:=PalBuf[I+(BufSize div 3)* 2] shl 8;
  234. Alpha:=alphaOpaque;
  235. end;
  236. ThePalette.Add(C);
  237. End;
  238. Stream.Position := OldPos;
  239. Result:=true;
  240. end;
  241. procedure TFPReaderPSD.AnalyzeHeader;
  242. begin
  243. With FHeader do
  244. begin
  245. Depth:=BEtoN(Depth);
  246. if (Signature <> '8BPS') then
  247. Raise Exception.Create('Unknown/Unsupported PSD image type');
  248. Channels:=BEtoN(Channels);
  249. if Channels > 4 then
  250. FBytesPerPixel:=Depth*4
  251. else
  252. FBytesPerPixel:=Depth*Channels;
  253. Mode:=BEtoN(Mode);
  254. FWidth:=BEtoN(Columns);
  255. FHeight:=BEtoN(Rows);
  256. FChannelCount:=Channels;
  257. FLineSize:=PtrInt(FHeight)*FWidth*Depth div 8;
  258. FLineSize:=FLineSize*Channels;
  259. GetMem(FScanLine,FLineSize);
  260. end;
  261. end;
  262. procedure TFPReaderPSD.InternalRead(Stream: TStream; Img: TFPCustomImage);
  263. var
  264. H: Integer;
  265. BufSize:Cardinal;
  266. Encoding:word;
  267. ContProgress: Boolean;
  268. begin
  269. FScanLine:=nil;
  270. FPalette:=nil;
  271. try
  272. Stream.Position:=0;
  273. ContProgress:=true;
  274. Progress(FPimage.psStarting, 0, False, Rect(0,0,0,0), '', ContProgress);
  275. if not ContProgress then exit;
  276. // read header
  277. Stream.Read(FHeader, SizeOf(FHeader));
  278. Progress(FPimage.psRunning, trunc(100.0 * (Stream.position / Stream.size)), False, Rect(0,0,0,0), '', ContProgress);
  279. if not ContProgress then exit;
  280. AnalyzeHeader;
  281. Case FHeader.Mode of
  282. 0:begin // Bitmap (monochrome)
  283. FPalette := TFPPalette.Create(0);
  284. CreateBWPalette;
  285. end;
  286. 1, 8:begin // Gray-scale
  287. FPalette := TFPPalette.Create(0);
  288. CreateGrayPalette;
  289. end;
  290. 2:begin // Indexed color (palette color)
  291. FPalette := TFPPalette.Create(0);
  292. if not ReadPalette(stream) then exit;
  293. end;
  294. end;
  295. if Assigned(OnCreateImage) then
  296. OnCreateImage(Self,Img);
  297. Img.SetSize(FWidth,FHeight);
  298. // color palette
  299. BufSize:=0;
  300. Stream.Read(BufSize, SizeOf(BufSize));
  301. BufSize:=BEtoN(BufSize);
  302. Stream.Seek(BufSize, soCurrent);
  303. // color data block
  304. Stream.Read(BufSize, SizeOf(BufSize));
  305. BufSize:=BEtoN(BufSize);
  306. Stream.Read(FColorDataBlock, SizeOf(FColorDataBlock));
  307. Stream.Seek(BufSize-SizeOf(FColorDataBlock), soCurrent);
  308. // mask
  309. Stream.Read(BufSize, SizeOf(BufSize));
  310. BufSize:=BEtoN(BufSize);
  311. Stream.Seek(BufSize, soCurrent);
  312. // compression type
  313. Encoding:=0;
  314. Stream.Read(Encoding, SizeOf(Encoding));
  315. FCompressed:=BEtoN(Encoding) = 1;
  316. if BEtoN(Encoding)>1 then
  317. Raise Exception.Create('Unknown compression type');
  318. If FCompressed then
  319. begin
  320. SetLength(FLengthOfLine, FHeight * FChannelCount);
  321. Stream.ReadBuffer(FLengthOfLine[0], 2 * Length(FLengthOfLine));
  322. FByteRead:=0;
  323. Progress(FPimage.psRunning, trunc(100.0 * (Stream.position / Stream.size)), False, Rect(0,0,0,0), '', ContProgress);
  324. if not ContProgress then exit;
  325. for H := 0 to High(FLengthOfLine) do
  326. Inc(FByteRead, BEtoN(FLengthOfLine[H]));
  327. if not FHeader.Mode in [ 0, 2] then
  328. FByteRead := FByteRead * FHeader.Depth div 8;
  329. end else
  330. FByteRead:= FLineSize;
  331. ReadScanLine(Stream);
  332. Progress(FPimage.psRunning, trunc(100.0 * (Stream.position / Stream.size)), False, Rect(0,0,0,0), '', ContProgress);
  333. if not ContProgress then exit;
  334. WriteScanLine(Img);
  335. {$ifdef FPC_Debug_Image}
  336. WriteLn('TFPReaderPSD.InternalRead AAA1 ',Stream.position,' ',Stream.size);
  337. {$endif}
  338. finally
  339. FreeAndNil(FPalette);
  340. ReAllocMem(FScanLine,0);
  341. end;
  342. Progress(FPimage.psEnding, 100, false, Rect(0,0,FWidth,FHeight), '', ContProgress);
  343. end;
  344. function TFPReaderPSD.ReadScanLine(Stream: TStream): boolean;
  345. Var
  346. P : PByte;
  347. B : Byte;
  348. I : PtrInt;
  349. J : integer;
  350. N : Shortint;
  351. Count:integer;
  352. ContProgress: Boolean;
  353. begin
  354. Result:=false;
  355. ContProgress:=true;
  356. If Not Compressed then
  357. Stream.ReadBuffer(FScanLine^,FLineSize)
  358. else
  359. begin
  360. P:=FScanLine;
  361. i:=FByteRead;
  362. repeat
  363. Count:=0;
  364. N:=0;
  365. Stream.ReadBuffer(N,1);
  366. Progress(FPimage.psRunning, trunc(100.0 * (Stream.position / Stream.size)), False, Rect(0,0,0,0), '', ContProgress);
  367. if not ContProgress then exit;
  368. dec(i);
  369. If N = -128 then
  370. else
  371. if N < 0 then
  372. begin
  373. Count:=-N+1;
  374. B:=0;
  375. Stream.ReadBuffer(B,1);
  376. dec(i);
  377. For j := 0 to Count-1 do
  378. begin
  379. P[0]:=B;
  380. inc(p);
  381. end;
  382. end
  383. else
  384. begin
  385. Count:=N+1;
  386. For j := 0 to Count-1 do
  387. begin
  388. Stream.ReadBuffer(B,1);
  389. P[0]:=B;
  390. inc(p);
  391. dec(i);
  392. end;
  393. end;
  394. until (i <= 0);
  395. end;
  396. Result:=true;
  397. end;
  398. procedure TFPReaderPSD.WriteScanLine(Img: TFPCustomImage);
  399. Var
  400. Col : Integer;
  401. C : TFPColor;
  402. P, P1, P2, P3 : PByte;
  403. Z2 : Longint;
  404. Row : Integer;
  405. Lab : TLab;
  406. begin
  407. C.Alpha:=AlphaOpaque;
  408. P:=FScanLine;
  409. Z2:=FHeader.Depth div 8;
  410. Z2:=Z2 *FHeight*FWidth;
  411. begin
  412. case FBytesPerPixel of
  413. 1 : begin
  414. for Row:=0 to Img.Height-1 do
  415. begin
  416. for Col:=0 to Img.Width-1 do
  417. if (P[col div 8] and (128 shr (col mod 8))) <> 0 then
  418. Img.Colors[Col,Row]:=ThePalette[0]
  419. else
  420. Img.Colors[Col,Row]:=ThePalette[1];
  421. inc(P, Img.Width div 8);
  422. end;
  423. end;
  424. 8 : begin
  425. for Row:=0 to Img.Height-1 do
  426. for Col:=0 to Img.Width-1 do
  427. begin
  428. Img.Colors[Col,Row]:=ThePalette[P[0]];
  429. inc(p);
  430. end;
  431. end;
  432. 16 : begin
  433. for Row:=0 to Img.Height-1 do
  434. for Col:=0 to Img.Width-1 do
  435. begin
  436. Img.Colors[Col,Row]:=ThePalette[BEtoN(PWord(P)^)];
  437. inc(p,2);
  438. end;
  439. end;
  440. 24 :begin
  441. P1:=P;
  442. inc(P1,Z2);
  443. P2:=P;
  444. inc(P2,Z2*2);
  445. for Row:=0 to Img.Height-1 do
  446. for Col:=0 to Img.Width-1 do
  447. begin
  448. if (FHeader.Mode =9) then
  449. begin
  450. Lab.L:=(P[0]);
  451. Lab.a:=(P1[0]);
  452. Lab.b:=(P2[0]);
  453. C:=LabToRGB(Lab);
  454. end
  455. else
  456. With C do
  457. begin
  458. Red:=P[0] or (P[0] shl 8);
  459. green:=P1[0] or (P1[0] shl 8);
  460. blue:=P2[0] or (P2[0] shl 8);
  461. alpha:=alphaOpaque;
  462. end;
  463. Inc(P);
  464. Inc(P1);
  465. Inc(P2);
  466. // if (Header.Mode =9) then C:=XYZtoRGB(C); // Lab color
  467. Img[col, row] := C;
  468. end;
  469. end;
  470. 32 :begin
  471. P1:=P;
  472. inc(P1,Z2);
  473. P2:=P;
  474. inc(P2,Z2*2);
  475. P3:=P;
  476. inc(P3,Z2*3);
  477. for Row:=0 to Img.Height-1 do
  478. for Col:=0 to Img.Width-1 do
  479. begin
  480. if (FHeader.Mode =4) then
  481. begin
  482. P^ := 255 - P^;
  483. P1^ := 255 - P1^;
  484. P2^ := 255 - P2^;
  485. P3^ := 255 - P3^;
  486. end;
  487. C.Red:=P[0] or (P[0] shl 8);
  488. C.green:=P1[0] or (P1[0] shl 8);
  489. C.blue:=P2[0] or (P2[0] shl 8);
  490. C.alpha:=P3[0] or (P3[0] shl 8);
  491. if (FHeader.Mode =4) then C:=CMYKtoRGB(C); // CMYK to RGB
  492. Img[col, row] := C;
  493. Inc(P);
  494. Inc(P1);
  495. Inc(P2);
  496. Inc(P3);
  497. end;
  498. end;
  499. 48 :begin
  500. P1:=P;
  501. inc(P1,Z2);
  502. P2:=P;
  503. inc(P2,Z2*2);
  504. C.alpha:=alphaOpaque;
  505. for Row:=0 to Img.Height-1 do
  506. for Col:=0 to Img.Width-1 do
  507. begin
  508. With C do
  509. begin
  510. Red:=BEtoN(PWord(P)^);
  511. green:=BEtoN(PWord(P1)^);
  512. blue:=BEtoN(PWord(P2)^);
  513. end;
  514. Inc(P,2);
  515. Inc(P1,2);
  516. Inc(P2,2);
  517. Img[col, row] := C;
  518. end;
  519. end;
  520. 64 :begin
  521. P1:=P;
  522. inc(P1,Z2);
  523. P2:=P;
  524. inc(P2,Z2*2);
  525. P3:=P;
  526. inc(P3,Z2*3);
  527. for Row:=0 to Img.Height-1 do
  528. for Col:=0 to Img.Width-1 do
  529. begin
  530. C.Red:=BEtoN(PWord(P)^);
  531. C.green:=BEtoN(PWord(P1)^);
  532. C.blue:=BEtoN(PWord(P2)^);
  533. C.alpha:=BEtoN(PWord(P3)^);
  534. if (FHeader.Mode =4) then
  535. begin
  536. C.red:=$ffff-C.red;
  537. C.green:=$ffff-C.green;
  538. C.blue:=$ffff-C.blue;
  539. C.alpha:=$ffff-C.alpha;
  540. end;
  541. if (FHeader.Mode =4) then C:=CMYKtoRGB(C); // CMYK to RGB
  542. Img[col, row] := C;
  543. Inc(P,2);
  544. Inc(P1,2);
  545. Inc(P2,2);
  546. Inc(P3,2);
  547. end;
  548. end;
  549. end;
  550. end;
  551. end;
  552. function TFPReaderPSD.InternalCheck(Stream: TStream): boolean;
  553. var
  554. OldPos: Int64;
  555. begin
  556. try
  557. OldPos:=Stream.Position;
  558. Stream.Read(FHeader,SizeOf(FHeader));
  559. Result:=(FHeader.Signature = '8BPS');
  560. Stream.Position:=OldPos;
  561. except
  562. Result:=False;
  563. end;
  564. end;
  565. constructor TFPReaderPSD.Create;
  566. begin
  567. inherited Create;
  568. end;
  569. initialization
  570. ImageHandlers.RegisterImageReader ('PSD Format', 'PSD', TFPReaderPSD);
  571. ImageHandlers.RegisterImageReader ('PDD Format', 'PDD', TFPReaderPSD);
  572. end.