ImagingPsd.pas 19 KB

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