ImagingPsd.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  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 Photoshop PSD image format.}
  25. unit ImagingPsd;
  26. {$I ImagingOptions.inc}
  27. interface
  28. uses
  29. SysUtils, ImagingTypes, Imaging, ImagingColors, ImagingUtility;
  30. type
  31. { Class for loading and saving Adobe Photoshop PSD images.
  32. Loading and saving of indexed, grayscale, RGB(A), HDR (FP32), and CMYK
  33. (auto converted to RGB) images is supported. Non-HDR gray, RGB,
  34. and CMYK images can have 8bit or 16bit color channels.
  35. There is no support for loading mono images, duotone images are treated
  36. like grayscale images, and multichannel and CIE Lab images are loaded as
  37. RGB images but without actual conversion to RGB color space.
  38. Also no layer information is loaded.}
  39. TPSDFileFormat = class(TImageFileFormat)
  40. protected
  41. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  42. OnlyFirstLevel: Boolean): Boolean; override;
  43. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  44. Index: LongInt): Boolean; override;
  45. procedure ConvertToSupported(var Image: TImageData;
  46. const Info: TImageFormatInfo); override;
  47. public
  48. constructor Create; override;
  49. function TestFormat(Handle: TImagingHandle): Boolean; override;
  50. end;
  51. implementation
  52. const
  53. SPSDFormatName = 'Photoshop Image';
  54. SPSDMasks = '*.psd,*.pdd';
  55. PSDSupportedFormats: TImageFormats = [ifIndex8, ifGray8, ifA8Gray8,
  56. ifR8G8B8, ifA8R8G8B8, ifGray16, ifA16Gray16, ifR16G16B16, ifA16R16G16B16,
  57. ifR32F, ifA32R32G32B32F];
  58. const
  59. SPSDMagic = '8BPS';
  60. type
  61. {$MINENUMSIZE 2}
  62. { PSD Image color mode.}
  63. TPSDColorMode = (
  64. cmMono = 0,
  65. cmGrayscale = 1,
  66. cmIndexed = 2,
  67. cmRGB = 3,
  68. cmCMYK = 4,
  69. cmMultiChannel = 7,
  70. cmDuoTone = 8,
  71. cmLab = 9
  72. );
  73. { PSD image main header.}
  74. TPSDHeader = packed record
  75. Signature: TChar4; // Format ID '8BPS'
  76. Version: Word; // Always 1
  77. Reserved: array[0..5] of Byte; // Reserved, all zero
  78. Channels: Word; // Number of color channels (1-24) including alpha channels
  79. Rows : LongWord; // Height of image in pixels (1-30000)
  80. Columns: LongWord; // Width of image in pixels (1-30000)
  81. Depth: Word; // Number of bits per channel (1, 8, and 16)
  82. Mode: TPSDColorMode; // Color mode
  83. end;
  84. procedure SwapHeader(var Header: TPSDHeader);
  85. begin
  86. Header.Version := SwapEndianWord(Header.Version);
  87. Header.Channels := SwapEndianWord(Header.Channels);
  88. Header.Depth := SwapEndianWord(Header.Depth);
  89. Header.Rows := SwapEndianLongWord(Header.Rows);
  90. Header.Columns := SwapEndianLongWord(Header.Columns);
  91. Header.Mode := TPSDColorMode(SwapEndianWord(Word(Header.Mode)));
  92. end;
  93. {
  94. TPSDFileFormat class implementation
  95. }
  96. constructor TPSDFileFormat.Create;
  97. begin
  98. inherited Create;
  99. FName := SPSDFormatName;
  100. FCanLoad := True;
  101. FCanSave := True;
  102. FIsMultiImageFormat := False;
  103. FSupportedFormats := PSDSupportedFormats;
  104. AddMasks(SPSDMasks);
  105. end;
  106. function TPSDFileFormat.LoadData(Handle: TImagingHandle;
  107. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  108. var
  109. Header: TPSDHeader;
  110. ByteCount: LongWord;
  111. RawPal: array[0..767] of Byte;
  112. Compression, PackedSize: Word;
  113. LineSize, ChannelPixelSize, WidthBytes,
  114. CurrChannel, MaxRLESize, I, Y, X: LongInt;
  115. Info: TImageFormatInfo;
  116. PackedLine, LineBuffer: PByte;
  117. RLELineSizes: array of Word;
  118. Col32: TColor32Rec;
  119. Col64: TColor64Rec;
  120. PCol32: PColor32Rec;
  121. PCol64: PColor64Rec;
  122. PColF: PColorFPRec;
  123. { PackBits RLE decode code from Mike Lischke's GraphicEx library.}
  124. procedure DecodeRLE(Source, Dest: PByte; PackedSize, UnpackedSize: LongInt);
  125. var
  126. Count: LongInt;
  127. begin
  128. while (UnpackedSize > 0) and (PackedSize > 0) do
  129. begin
  130. Count := ShortInt(Source^);
  131. Inc(Source);
  132. Dec(PackedSize);
  133. if Count < 0 then
  134. begin
  135. // Replicate next byte -Count + 1 times
  136. if Count = -128 then
  137. Continue;
  138. Count := -Count + 1;
  139. if Count > UnpackedSize then
  140. Count := UnpackedSize;
  141. FillChar(Dest^, Count, Source^);
  142. Inc(Source);
  143. Dec(PackedSize);
  144. Inc(Dest, Count);
  145. Dec(UnpackedSize, Count);
  146. end
  147. else
  148. begin
  149. // Copy next Count + 1 bytes from input
  150. Inc(Count);
  151. if Count > UnpackedSize then
  152. Count := UnpackedSize;
  153. if Count > PackedSize then
  154. Count := PackedSize;
  155. Move(Source^, Dest^, Count);
  156. Inc(Dest, Count);
  157. Inc(Source, Count);
  158. Dec(PackedSize, Count);
  159. Dec(UnpackedSize, Count);
  160. end;
  161. end;
  162. end;
  163. begin
  164. Result := False;
  165. SetLength(Images, 1);
  166. with GetIO, Images[0] do
  167. begin
  168. // Read PSD header
  169. Read(Handle, @Header, SizeOf(Header));
  170. SwapHeader(Header);
  171. // Determine image data format
  172. Format := ifUnknown;
  173. case Header.Mode of
  174. cmGrayscale, cmDuoTone:
  175. begin
  176. if Header.Depth in [8, 16] then
  177. begin
  178. if Header.Channels = 1 then
  179. Format := IffFormat(Header.Depth = 8, ifGray8, ifGray16)
  180. else if Header.Channels >= 2 then
  181. Format := IffFormat(Header.Depth = 8, ifA8Gray8, ifA16Gray16);
  182. end
  183. else if (Header.Depth = 32) and (Header.Channels = 1) then
  184. Format := ifR32F;
  185. end;
  186. cmIndexed:
  187. begin
  188. if Header.Depth = 8 then
  189. Format := ifIndex8;
  190. end;
  191. cmRGB, cmMultiChannel, cmCMYK, cmLab:
  192. begin
  193. if Header.Depth in [8, 16] then
  194. begin
  195. if Header.Channels = 3 then
  196. Format := IffFormat(Header.Depth = 8, ifR8G8B8, ifR16G16B16)
  197. else if Header.Channels >= 4 then
  198. Format := IffFormat(Header.Depth = 8, ifA8R8G8B8, ifA16R16G16B16);
  199. end
  200. else if Header.Depth = 32 then
  201. Format := ifA32R32G32B32F;
  202. end;
  203. cmMono:; // Not supported
  204. end;
  205. // Exit if no compatible format was found
  206. if Format = ifUnknown then
  207. Exit;
  208. NewImage(Header.Columns, Header.Rows, Format, Images[0]);
  209. Info := GetFormatInfo(Format);
  210. // Read or skip Color Mode Data Block (palette)
  211. Read(Handle, @ByteCount, SizeOf(ByteCount));
  212. ByteCount := SwapEndianLongWord(ByteCount);
  213. if Format = ifIndex8 then
  214. begin
  215. // Read palette only for indexed images
  216. Read(Handle, @RawPal, SizeOf(RawPal));
  217. for I := 0 to 255 do
  218. begin
  219. Palette[I].A := $FF;
  220. Palette[I].R := RawPal[I + 0];
  221. Palette[I].G := RawPal[I + 256];
  222. Palette[I].B := RawPal[I + 512];
  223. end;
  224. end
  225. else
  226. Seek(Handle, ByteCount, smFromCurrent);
  227. // Skip Image Resources Block
  228. Read(Handle, @ByteCount, SizeOf(ByteCount));
  229. ByteCount := SwapEndianLongWord(ByteCount);
  230. Seek(Handle, ByteCount, smFromCurrent);
  231. // Skip Layer and Mask Information Block
  232. Read(Handle, @ByteCount, SizeOf(ByteCount));
  233. ByteCount := SwapEndianLongWord(ByteCount);
  234. Seek(Handle, ByteCount, smFromCurrent);
  235. // Read compression flag
  236. Read(Handle, @Compression, SizeOf(Compression));
  237. Compression := SwapEndianWord(Compression);
  238. if Compression = 1 then
  239. begin
  240. // RLE compressed PSDs (most) have first lengths of compressed scanlines
  241. // for each channel stored
  242. SetLength(RLELineSizes, Height * Header.Channels);
  243. Read(Handle, @RLELineSizes[0], Length(RLELineSizes) * SizeOf(Word));
  244. SwapEndianWord(@RLELineSizes[0], Height * Header.Channels);
  245. MaxRLESize := RLELineSizes[0];
  246. for I := 1 to High(RLELineSizes) do
  247. begin
  248. if MaxRLESize < RLELineSizes[I] then
  249. MaxRLESize := RLELineSizes[I];
  250. end;
  251. end
  252. else
  253. MaxRLESize := 0;
  254. ChannelPixelSize := Info.BytesPerPixel div Info.ChannelCount;
  255. LineSize := Width * ChannelPixelSize;
  256. WidthBytes := Width * Info.BytesPerPixel;
  257. GetMem(LineBuffer, LineSize);
  258. GetMem(PackedLine, MaxRLESize);
  259. try
  260. // Image color chanels are stored separately in PSDs so we will load
  261. // one by one and copy their data to appropriate addresses of dest image.
  262. for I := 0 to Header.Channels - 1 do
  263. begin
  264. // Now determine to which color channel of destination image we are going
  265. // to write pixels.
  266. if I <= 4 then
  267. begin
  268. // If PSD has alpha channel we need to switch current channel order -
  269. // PSDs have alpha stored after blue channel but Imaging has alpha
  270. // before red.
  271. if Info.HasAlphaChannel and (Header.Mode <> cmCMYK) then
  272. begin
  273. if I = Info.ChannelCount - 1 then
  274. CurrChannel := I
  275. else
  276. CurrChannel := Info.ChannelCount - 2 - I;
  277. end
  278. else
  279. CurrChannel := Info.ChannelCount - 1 - I;
  280. end
  281. else
  282. begin
  283. // No valid channel remains
  284. CurrChannel := -1;
  285. end;
  286. if CurrChannel >= 0 then
  287. begin
  288. for Y := 0 to Height - 1 do
  289. begin
  290. if Compression = 1 then
  291. begin
  292. // Read RLE line and decompress it
  293. PackedSize := RLELineSizes[I * Height + Y];
  294. Read(Handle, PackedLine, PackedSize);
  295. DecodeRLE(PackedLine, LineBuffer, PackedSize, LineSize);
  296. end
  297. else
  298. begin
  299. // Just read uncompressed line
  300. Read(Handle, LineBuffer, LineSize);
  301. end;
  302. // Swap endian if needed
  303. if ChannelPixelSize = 4 then
  304. SwapEndianLongWord(PLongWord(LineBuffer), Width)
  305. else if ChannelPixelSize = 2 then
  306. SwapEndianWord(PWordArray(LineBuffer), Width);
  307. if Info.ChannelCount > 1 then
  308. begin
  309. // Copy each pixel fragment to its right place in destination image
  310. for X := 0 to Width - 1 do
  311. begin
  312. Move(PByteArray(LineBuffer)[X * ChannelPixelSize],
  313. PByteArray(Bits)[Y * WidthBytes + X * Info.BytesPerPixel + CurrChannel * ChannelPixelSize],
  314. ChannelPixelSize);
  315. end;
  316. end
  317. else
  318. begin
  319. // Just copy the line
  320. Move(LineBuffer^, PByteArray(Bits)[Y * LineSize], LineSize);
  321. end;
  322. end;
  323. end
  324. else
  325. begin
  326. // Skip current color channel, not needed for image loading - just to
  327. // get stream's position to the and of PSD
  328. if Compression = 1 then
  329. begin
  330. for Y := 0 to Height - 1 do
  331. Seek(Handle, RLELineSizes[I * Height + Y], smFromCurrent);
  332. end
  333. else
  334. Seek(Handle, LineSize * Height, smFromCurrent);
  335. end;
  336. end;
  337. if Header.Mode = cmCMYK then
  338. begin
  339. // Convert CMYK images to RGB (alpha is ignored here). PSD stores CMYK
  340. // channels in the way that first requires substraction from max channel value
  341. if ChannelPixelSize = 1 then
  342. begin
  343. PCol32 := Bits;
  344. for X := 0 to Width * Height - 1 do
  345. begin
  346. Col32.A := 255 - PCol32.A;
  347. Col32.R := 255 - PCol32.R;
  348. Col32.G := 255 - PCol32.G;
  349. Col32.B := 255 - PCol32.B;
  350. CMYKToRGB(Col32.A, Col32.R, Col32.G, Col32.B, PCol32.R, PCol32.G, PCol32.B);
  351. PCol32.A := 255;
  352. Inc(PCol32);
  353. end;
  354. end
  355. else
  356. begin
  357. PCol64 := Bits;
  358. for X := 0 to Width * Height - 1 do
  359. begin
  360. Col64.A := 65535 - PCol64.A;
  361. Col64.R := 65535 - PCol64.R;
  362. Col64.G := 65535 - PCol64.G;
  363. Col64.B := 65535 - PCol64.B;
  364. CMYKToRGB16(Col64.A, Col64.R, Col64.G, Col64.B, PCol64.R, PCol64.G, PCol64.B);
  365. PCol64.A := 65535;
  366. Inc(PCol64);
  367. end;
  368. end;
  369. end;
  370. if Header.Depth = 32 then
  371. begin
  372. if (Header.Channels = 3) and (Header.Mode = cmRGB) then
  373. begin
  374. // RGB images were loaded as ARGB so we must wet alpha manually to 1.0
  375. PColF := Bits;
  376. for X := 0 to Width * Height - 1 do
  377. begin
  378. PColF.A := 1.0;
  379. Inc(PColF);
  380. end;
  381. end;
  382. end;
  383. Result := True;
  384. finally
  385. FreeMem(LineBuffer);
  386. FreeMem(PackedLine);
  387. end;
  388. end;
  389. end;
  390. function TPSDFileFormat.SaveData(Handle: TImagingHandle;
  391. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  392. var
  393. MustBeFreed: Boolean;
  394. ImageToSave: TImageData;
  395. Info: TImageFormatInfo;
  396. Header: TPSDHeader;
  397. I, X, Y, CurrChannel, LineSize, ChannelPixelSize, WidthBytes: LongInt;
  398. LongVal: LongWord;
  399. WordVal: Word;
  400. RawPal: array[0..767] of Byte;
  401. LineBuffer: PByte;
  402. begin
  403. Result := False;
  404. if MakeCompatible(Images[Index], ImageToSave, MustBeFreed) then
  405. with GetIO, ImageToSave do
  406. try
  407. Info := GetFormatInfo(Format);
  408. // Fill header with proper info and save it
  409. FillChar(Header, SizeOf(Header), 0);
  410. Header.Signature := SPSDMagic;
  411. Header.Version := 1;
  412. Header.Channels := Info.ChannelCount;
  413. Header.Rows := Height;
  414. Header.Columns := Width;
  415. Header.Depth := Info.BytesPerPixel div Info.ChannelCount * 8;
  416. if Info.IsIndexed then
  417. Header.Mode := cmIndexed
  418. else if Info.HasGrayChannel or (Info.ChannelCount = 1) then
  419. Header.Mode := cmGrayscale
  420. else
  421. Header.Mode := cmRGB;
  422. SwapHeader(Header);
  423. Write(Handle, @Header, SizeOf(Header));
  424. // Write palette size and data
  425. LongVal := SwapEndianLongWord(IffUnsigned(Info.IsIndexed, SizeOf(RawPal), 0));
  426. Write(Handle, @LongVal, SizeOf(LongVal));
  427. if Info.IsIndexed then
  428. begin
  429. for I := 0 to Info.PaletteEntries - 1 do
  430. begin
  431. RawPal[I] := Palette[I].R;
  432. RawPal[I + 256] := Palette[I].G;
  433. RawPal[I + 512] := Palette[I].B;
  434. end;
  435. Write(Handle, @RawPal, SizeOf(RawPal));
  436. end;
  437. // Write resource and layer block sizes
  438. LongVal := 0;
  439. Write(Handle, @LongVal, SizeOf(LongVal));
  440. Write(Handle, @LongVal, SizeOf(LongVal));
  441. // Set compression off
  442. WordVal := 0;
  443. Write(Handle, @WordVal, SizeOf(WordVal));
  444. ChannelPixelSize := Info.BytesPerPixel div Info.ChannelCount;
  445. LineSize := Width * ChannelPixelSize;
  446. WidthBytes := Width * Info.BytesPerPixel;
  447. GetMem(LineBuffer, LineSize);
  448. for I := 0 to Info.ChannelCount - 1 do
  449. begin
  450. // Now determine which color channel we are going to write to file.
  451. if Info.HasAlphaChannel then
  452. begin
  453. if I = Info.ChannelCount - 1 then
  454. CurrChannel := I
  455. else
  456. CurrChannel := Info.ChannelCount - 2 - I;
  457. end
  458. else
  459. CurrChannel := Info.ChannelCount - 1 - I;
  460. for Y := 0 to Height - 1 do
  461. begin
  462. if Info.ChannelCount > 1 then
  463. begin
  464. // Copy each pixel fragment to its right place in destination image
  465. for X := 0 to Width - 1 do
  466. begin
  467. Move(PByteArray(Bits)[Y * WidthBytes + X * Info.BytesPerPixel + CurrChannel * ChannelPixelSize],
  468. PByteArray(LineBuffer)[X * ChannelPixelSize], ChannelPixelSize);
  469. end;
  470. end
  471. else
  472. Move(PByteArray(Bits)[Y * LineSize], LineBuffer^, LineSize);
  473. // Write current channel line to file (swap endian if needed first)
  474. if ChannelPixelSize = 4 then
  475. SwapEndianLongWord(PLongWord(LineBuffer), Width)
  476. else if ChannelPixelSize = 2 then
  477. SwapEndianWord(PWordArray(LineBuffer), Width);
  478. Write(Handle, LineBuffer, LineSize);
  479. end;
  480. end;
  481. Result := True;
  482. finally
  483. if MustBeFreed then
  484. FreeImage(ImageToSave);
  485. end;
  486. end;
  487. procedure TPSDFileFormat.ConvertToSupported(var Image: TImageData;
  488. const Info: TImageFormatInfo);
  489. var
  490. ConvFormat: TImageFormat;
  491. begin
  492. if Info.IsFloatingPoint then
  493. ConvFormat := IffFormat(Info.ChannelCount = 1, ifR32F, ifA32R32G32B32F)
  494. else if Info.HasGrayChannel then
  495. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA16Gray16, ifGray16)
  496. else if Info.RBSwapFormat in GetSupportedFormats then
  497. ConvFormat := Info.RBSwapFormat
  498. else
  499. ConvFormat := IffFormat(Info.HasAlphaChannel, ifA8R8G8B8, ifR8G8B8);
  500. ConvertImage(Image, ConvFormat);
  501. end;
  502. function TPSDFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  503. var
  504. Header: TPSDHeader;
  505. ReadCount: LongInt;
  506. begin
  507. Result := False;
  508. if Handle <> nil then
  509. begin
  510. ReadCount := GetIO.Read(Handle, @Header, SizeOf(Header));
  511. SwapHeader(Header);
  512. GetIO.Seek(Handle, -ReadCount, smFromCurrent);
  513. Result := (ReadCount >= SizeOf(Header)) and
  514. (Header.Signature = SPSDMagic) and
  515. (Header.Version = 1);
  516. end;
  517. end;
  518. initialization
  519. RegisterImageFileFormat(TPSDFileFormat);
  520. {
  521. File Notes:
  522. -- TODOS ----------------------------------------------------
  523. - nothing now
  524. -- 0.23 Changes/Bug Fixes -----------------------------------
  525. - Saving implemented.
  526. - Loading implemented.
  527. - Unit created with initial stuff!
  528. }
  529. end.