Bez popisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PsdFile.cs 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. /////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Photoshop PSD FileType Plugin for Paint.NET
  4. // http://psdplugin.codeplex.com/
  5. //
  6. // This software is provided under the MIT License:
  7. // Copyright (c) 2006-2007 Frank Blumenberg
  8. // Copyright (c) 2010-2016 Tao Yue
  9. //
  10. // Portions of this file are provided under the BSD 3-clause License:
  11. // Copyright (c) 2006, Jonas Beckeman
  12. //
  13. // See LICENSE.txt for complete licensing and attribution information.
  14. //
  15. /////////////////////////////////////////////////////////////////////////////////
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using PDNWrapper;
  20. using System.IO;
  21. using System.Linq;
  22. using System.Text;
  23. using PaintDotNet.Data.PhotoshopFileType;
  24. namespace PhotoshopFile
  25. {
  26. [Flags]
  27. internal enum ELoadFlag
  28. {
  29. Header = 1,
  30. ColorMode = Header | 1 << 2,
  31. ImageData = ColorMode | 1 << 3,
  32. All = Header | ColorMode | ImageData
  33. }
  34. internal enum PsdColorMode
  35. {
  36. Bitmap = 0,
  37. Grayscale = 1,
  38. Indexed = 2,
  39. RGB = 3,
  40. CMYK = 4,
  41. Multichannel = 7,
  42. Duotone = 8,
  43. Lab = 9
  44. };
  45. internal enum PsdFileVersion : short
  46. {
  47. Psd = 1,
  48. PsbLargeDocument = 2
  49. }
  50. internal class PsdFile
  51. {
  52. #region Constructors
  53. ELoadFlag m_LoadFlag;
  54. public PsdFile(PsdFileVersion version = PsdFileVersion.Psd)
  55. {
  56. Version = version;
  57. BaseLayer = new Layer(this);
  58. ImageResources = new ImageResources();
  59. Layers = new List<Layer>();
  60. AdditionalInfo = new List<LayerInfo>();
  61. }
  62. public PsdFile(string filename, LoadContext loadContext, ELoadFlag loadFlag = ELoadFlag.All)
  63. : this()
  64. {
  65. using (var stream = new FileStream(filename, FileMode.Open))
  66. {
  67. Load(stream, loadContext, loadFlag);
  68. }
  69. }
  70. public PsdFile(Stream stream, LoadContext loadContext, ELoadFlag loadFlag = ELoadFlag.All)
  71. : this()
  72. {
  73. Load(stream, loadContext, loadFlag);
  74. }
  75. #endregion
  76. #region Load and save
  77. internal LoadContext LoadContext { get; private set; }
  78. private void Load(Stream stream, LoadContext loadContext, ELoadFlag loadFlag)
  79. {
  80. LoadContext = loadContext;
  81. var reader = new PsdBinaryReader(stream, loadContext.Encoding);
  82. if ((loadFlag & ELoadFlag.Header) == ELoadFlag.Header)
  83. LoadHeader(reader);
  84. if ((loadFlag & ELoadFlag.ColorMode) == ELoadFlag.ColorMode)
  85. LoadColorModeData(reader);
  86. if ((loadFlag & ELoadFlag.ImageData) == ELoadFlag.ImageData)
  87. {
  88. LoadImageResources(reader);
  89. LoadLayerAndMaskInfo(reader);
  90. LoadImage(reader);
  91. DecompressImages();
  92. }
  93. }
  94. #endregion
  95. #region Header
  96. /// <summary>
  97. /// Photoshop file format version.
  98. /// </summary>
  99. public PsdFileVersion Version { get; private set; }
  100. public bool IsLargeDocument
  101. {
  102. get { return Version == PsdFileVersion.PsbLargeDocument; }
  103. }
  104. private Int16 channelCount;
  105. /// <summary>
  106. /// The number of channels in the image, including any alpha channels.
  107. /// </summary>
  108. public Int16 ChannelCount
  109. {
  110. get { return channelCount; }
  111. set
  112. {
  113. if (value < 1 || value > 56)
  114. throw new ArgumentException("Number of channels must be from 1 to 56.");
  115. channelCount = value;
  116. }
  117. }
  118. private void CheckDimension(int dimension)
  119. {
  120. if (dimension < 1)
  121. {
  122. throw new ArgumentException("Image dimension must be at least 1.");
  123. }
  124. if ((Version == PsdFileVersion.Psd) && (dimension > 30000))
  125. {
  126. throw new ArgumentException("PSD image dimension cannot exceed 30000.");
  127. }
  128. if ((Version == PsdFileVersion.PsbLargeDocument) && (dimension > 300000))
  129. {
  130. throw new ArgumentException("PSB image dimension cannot exceed 300000.");
  131. }
  132. }
  133. /// <summary>
  134. /// The height of the image in pixels.
  135. /// </summary>
  136. public int RowCount
  137. {
  138. get { return this.BaseLayer.Rect.Height; }
  139. set
  140. {
  141. CheckDimension(value);
  142. BaseLayer.Rect = new Rectangle(0, 0, BaseLayer.Rect.Width, value);
  143. }
  144. }
  145. /// <summary>
  146. /// The width of the image in pixels.
  147. /// </summary>
  148. public int ColumnCount
  149. {
  150. get { return this.BaseLayer.Rect.Width; }
  151. set
  152. {
  153. CheckDimension(value);
  154. BaseLayer.Rect = new Rectangle(0, 0, value, BaseLayer.Rect.Height);
  155. }
  156. }
  157. private int bitDepth;
  158. /// <summary>
  159. /// The number of bits per channel. Supported values are 1, 8, 16, and 32.
  160. /// </summary>
  161. public int BitDepth
  162. {
  163. get { return bitDepth; }
  164. set
  165. {
  166. switch (value)
  167. {
  168. case 1:
  169. case 8:
  170. case 16:
  171. case 32:
  172. bitDepth = value;
  173. break;
  174. default:
  175. throw new NotImplementedException("Invalid bit depth.");
  176. }
  177. }
  178. }
  179. /// <summary>
  180. /// The color mode of the file.
  181. /// </summary>
  182. public PsdColorMode ColorMode { get; set; }
  183. ///////////////////////////////////////////////////////////////////////////
  184. private void LoadHeader(PsdBinaryReader reader)
  185. {
  186. Util.DebugMessage(reader.BaseStream, "Load, Begin, File header");
  187. var signature = reader.ReadAsciiChars(4);
  188. if (signature != "8BPS")
  189. throw new PsdInvalidException("The given stream is not a valid PSD file");
  190. Version = (PsdFileVersion)reader.ReadInt16();
  191. Util.DebugMessage(reader.BaseStream, "Load, Info, Version {0}", (int)Version);
  192. if ((Version != PsdFileVersion.Psd)
  193. && (Version != PsdFileVersion.PsbLargeDocument))
  194. {
  195. throw new PsdInvalidException("The PSD file has an unknown version");
  196. }
  197. //6 bytes reserved
  198. reader.BaseStream.Position += 6;
  199. this.ChannelCount = reader.ReadInt16();
  200. this.RowCount = reader.ReadInt32();
  201. this.ColumnCount = reader.ReadInt32();
  202. BitDepth = reader.ReadInt16();
  203. ColorMode = (PsdColorMode)reader.ReadInt16();
  204. Util.DebugMessage(reader.BaseStream, "Load, End, File header");
  205. }
  206. #endregion
  207. ///////////////////////////////////////////////////////////////////////////
  208. #region ColorModeData
  209. /// <summary>
  210. /// If ColorMode is ColorModes.Indexed, the following 768 bytes will contain
  211. /// a 256-color palette. If the ColorMode is ColorModes.Duotone, the data
  212. /// following presumably consists of screen parameters and other related information.
  213. /// Unfortunately, it is intentionally not documented by Adobe, and non-Photoshop
  214. /// readers are advised to treat duotone images as gray-scale images.
  215. /// </summary>
  216. public byte[] ColorModeData = new byte[0];
  217. private void LoadColorModeData(PsdBinaryReader reader)
  218. {
  219. Util.DebugMessage(reader.BaseStream, "Load, Begin, ColorModeData");
  220. var paletteLength = reader.ReadUInt32();
  221. if (paletteLength > 0)
  222. {
  223. ColorModeData = reader.ReadBytes((int)paletteLength);
  224. }
  225. Util.DebugMessage(reader.BaseStream, "Load, End, ColorModeData");
  226. }
  227. #endregion
  228. ///////////////////////////////////////////////////////////////////////////
  229. #region ImageResources
  230. /// <summary>
  231. /// The Image resource blocks for the file
  232. /// </summary>
  233. public ImageResources ImageResources { get; set; }
  234. public ResolutionInfo Resolution
  235. {
  236. get
  237. {
  238. return (ResolutionInfo)ImageResources.Get(ResourceID.ResolutionInfo);
  239. }
  240. set
  241. {
  242. ImageResources.Set(value);
  243. }
  244. }
  245. ///////////////////////////////////////////////////////////////////////////
  246. private void LoadImageResources(PsdBinaryReader reader)
  247. {
  248. Util.DebugMessage(reader.BaseStream, "Load, Begin, ImageResources");
  249. var imageResourcesLength = reader.ReadUInt32();
  250. if (imageResourcesLength <= 0)
  251. return;
  252. var startPosition = reader.BaseStream.Position;
  253. var endPosition = startPosition + imageResourcesLength;
  254. while (reader.BaseStream.Position < endPosition)
  255. {
  256. var imageResource = ImageResourceFactory.CreateImageResource(reader);
  257. ImageResources.Add(imageResource);
  258. }
  259. Util.DebugMessage(reader.BaseStream, "Load, End, ImageResources");
  260. //-----------------------------------------------------------------------
  261. // make sure we are not on a wrong offset, so set the stream position
  262. // manually
  263. reader.BaseStream.Position = startPosition + imageResourcesLength;
  264. }
  265. #endregion
  266. ///////////////////////////////////////////////////////////////////////////
  267. #region LayerAndMaskInfo
  268. public List<Layer> Layers { get; private set; }
  269. public List<LayerInfo> AdditionalInfo { get; private set; }
  270. public bool AbsoluteAlpha { get; set; }
  271. ///////////////////////////////////////////////////////////////////////////
  272. private void LoadLayerAndMaskInfo(PsdBinaryReader reader)
  273. {
  274. Util.DebugMessage(reader.BaseStream, "Load, Begin, Layer and mask info");
  275. var layersAndMaskLength = IsLargeDocument
  276. ? reader.ReadInt64()
  277. : reader.ReadUInt32();
  278. if (layersAndMaskLength <= 0)
  279. return;
  280. var startPosition = reader.BaseStream.Position;
  281. var endPosition = startPosition + layersAndMaskLength;
  282. LoadLayers(reader, true);
  283. LoadGlobalLayerMask(reader);
  284. //-----------------------------------------------------------------------
  285. // Load Additional Layer Information
  286. while (reader.BaseStream.Position < endPosition)
  287. {
  288. var info = LayerInfoFactory.Load(reader, this, true, endPosition);
  289. AdditionalInfo.Add(info);
  290. if (info is RawLayerInfo)
  291. {
  292. var layerInfo = (RawLayerInfo)info;
  293. switch (info.Key)
  294. {
  295. case "LMsk":
  296. m_GlobalLayerMaskData = layerInfo.Data;
  297. break;
  298. }
  299. }
  300. }
  301. Util.DebugMessage(reader.BaseStream, "Load, End, Layer and mask info");
  302. //-----------------------------------------------------------------------
  303. // make sure we are not on a wrong offset, so set the stream position
  304. // manually
  305. reader.BaseStream.Position = startPosition + layersAndMaskLength;
  306. }
  307. ///////////////////////////////////////////////////////////////////////////
  308. /// <summary>
  309. /// Load Layers Info section, including image data.
  310. /// </summary>
  311. /// <param name="reader">PSD reader.</param>
  312. /// <param name="hasHeader">Whether the Layers Info section has a length header.</param>
  313. internal void LoadLayers(PsdBinaryReader reader, bool hasHeader)
  314. {
  315. Util.DebugMessage(reader.BaseStream, "Load, Begin, Layers Info section");
  316. long sectionLength = 0;
  317. if (hasHeader)
  318. {
  319. sectionLength = IsLargeDocument
  320. ? reader.ReadInt64()
  321. : reader.ReadUInt32();
  322. if (sectionLength <= 0)
  323. {
  324. // The callback may take action when there are 0 layers, so it must
  325. // be called even though the Layers Info section is empty.
  326. LoadContext.OnLoadLayersHeader(this);
  327. Util.DebugMessage(reader.BaseStream, "Load, End, Layers Info section");
  328. return;
  329. }
  330. }
  331. var startPosition = reader.BaseStream.Position;
  332. var numLayers = reader.ReadInt16();
  333. // If numLayers < 0, then number of layers is absolute value,
  334. // and the first alpha channel contains the transparency data for
  335. // the merged result.
  336. if (numLayers < 0)
  337. {
  338. AbsoluteAlpha = true;
  339. numLayers = Math.Abs(numLayers);
  340. }
  341. for (int i = 0; i < numLayers; i++)
  342. {
  343. var layer = new Layer(reader, this);
  344. Layers.Add(layer);
  345. }
  346. // Header is complete just before loading pixel data
  347. LoadContext.OnLoadLayersHeader(this);
  348. //-----------------------------------------------------------------------
  349. // Load image data for all channels.
  350. foreach (var layer in Layers)
  351. {
  352. Util.DebugMessage(reader.BaseStream,
  353. "Load, Begin, Layer image, layer.Name");
  354. foreach (var channel in layer.Channels)
  355. {
  356. channel.LoadPixelData(reader);
  357. }
  358. Util.DebugMessage(reader.BaseStream,
  359. "Load, End, Layer image, layer.Name");
  360. }
  361. // Length is set to 0 when called on higher bitdepth layers.
  362. if (sectionLength > 0)
  363. {
  364. // Layers Info section is documented to be even-padded, but Photoshop
  365. // actually pads to 4 bytes.
  366. var endPosition = startPosition + sectionLength;
  367. var positionOffset = reader.BaseStream.Position - endPosition;
  368. Debug.Assert(positionOffset > -4,
  369. "LoadLayers did not read the full length of the Layers Info section.");
  370. Debug.Assert(positionOffset <= 0,
  371. "LoadLayers read past the end of the Layers Info section.");
  372. if (reader.BaseStream.Position < endPosition)
  373. reader.BaseStream.Position = endPosition;
  374. }
  375. Util.DebugMessage(reader.BaseStream, "Load, End, Layers");
  376. }
  377. ///////////////////////////////////////////////////////////////////////////
  378. /// <summary>
  379. /// Cleanup
  380. /// </summary>
  381. public void Cleanup()
  382. {
  383. var layersAndComposite = Layers.Concat(new[] { BaseLayer });
  384. foreach (var lac in layersAndComposite)
  385. {
  386. foreach (var c in lac.Channels)
  387. {
  388. c.Cleanup();
  389. }
  390. }
  391. }
  392. ///////////////////////////////////////////////////////////////////////////
  393. /// <summary>
  394. /// Decompress the document image data and all the layers' image data, in parallel.
  395. /// </summary>
  396. private void DecompressImages()
  397. {
  398. var layersAndComposite = Layers.Concat(new[] { BaseLayer });
  399. //var channels = layersAndComposite.SelectMany(x => x.Channels);
  400. //Parallel.ForEach(channels, channel =>
  401. //{
  402. // channel.DecodeImageData();
  403. //});
  404. foreach (var lac in layersAndComposite)
  405. {
  406. foreach (var c in lac.Channels)
  407. {
  408. c.DecodeImageData();
  409. }
  410. }
  411. foreach (var layer in Layers)
  412. {
  413. foreach (var channel in layer.Channels)
  414. {
  415. if (channel.ID == -2)
  416. layer.Masks.LayerMask.ImageData = channel.ImageData;
  417. else if (channel.ID == -3)
  418. layer.Masks.UserMask.ImageData = channel.ImageData;
  419. }
  420. }
  421. }
  422. /// <summary>
  423. /// Verifies that any Additional Info layers are consistent.
  424. /// </summary>
  425. private void VerifyInfoLayers()
  426. {
  427. var infoLayersCount = AdditionalInfo.Count(x => x is InfoLayers);
  428. if (infoLayersCount > 1)
  429. {
  430. throw new PsdInvalidException(
  431. "Cannot have more than one InfoLayers in a PSD file.");
  432. }
  433. if ((infoLayersCount > 0) && (Layers.Count == 0))
  434. {
  435. throw new PsdInvalidException(
  436. "InfoLayers cannot exist when there are 0 layers.");
  437. }
  438. }
  439. /// <summary>
  440. /// Verify validity of layer sections. Each start marker should have a
  441. /// matching end marker.
  442. /// </summary>
  443. internal void VerifyLayerSections()
  444. {
  445. int depth = 0;
  446. foreach (var layer in Enumerable.Reverse(Layers))
  447. {
  448. var layerSectionInfo = layer.AdditionalInfo.SingleOrDefault(
  449. x => x is LayerSectionInfo);
  450. if (layerSectionInfo == null)
  451. continue;
  452. var sectionInfo = (LayerSectionInfo)layerSectionInfo;
  453. switch (sectionInfo.SectionType)
  454. {
  455. case LayerSectionType.OpenFolder:
  456. case LayerSectionType.ClosedFolder:
  457. depth++;
  458. break;
  459. case LayerSectionType.SectionDivider:
  460. depth--;
  461. if (depth < 0)
  462. throw new PsdInvalidException("Layer section ended without matching start marker.");
  463. break;
  464. case LayerSectionType.Layer: // Nothing to do here yet.
  465. break;
  466. default:
  467. throw new PsdInvalidException("Unrecognized layer section type.");
  468. }
  469. }
  470. if (depth != 0)
  471. throw new PsdInvalidException("Layer section not closed by end marker.");
  472. }
  473. /// <summary>
  474. /// Set the VersionInfo resource on the file.
  475. /// </summary>
  476. public void SetVersionInfo()
  477. {
  478. var versionInfo = (VersionInfo)ImageResources.Get(ResourceID.VersionInfo);
  479. if (versionInfo == null)
  480. {
  481. versionInfo = new VersionInfo();
  482. ImageResources.Set(versionInfo);
  483. // Get the version string. We don't use the fourth part (revision).
  484. var assembly = System.Reflection.Assembly.GetExecutingAssembly();
  485. var version = assembly.GetName().Version;
  486. var versionString = version.Major + "." + version.Minor + "." + version.Build;
  487. // Strings are not localized since they are not shown to the user.
  488. versionInfo.Version = 1;
  489. versionInfo.HasRealMergedData = true;
  490. versionInfo.ReaderName = "Paint.NET PSD Plugin";
  491. versionInfo.WriterName = "Paint.NET PSD Plugin " + versionString;
  492. versionInfo.FileVersion = 1;
  493. }
  494. }
  495. ///////////////////////////////////////////////////////////////////////////
  496. byte[] m_GlobalLayerMaskData;
  497. private void LoadGlobalLayerMask(PsdBinaryReader reader)
  498. {
  499. Util.DebugMessage(reader.BaseStream, "Load, Begin, GlobalLayerMask");
  500. var maskLength = reader.ReadUInt32();
  501. if (maskLength <= 0)
  502. {
  503. Util.DebugMessage(reader.BaseStream, "Load, End, GlobalLayerMask");
  504. return;
  505. }
  506. m_GlobalLayerMaskData = reader.ReadBytes((int)maskLength);
  507. Util.DebugMessage(reader.BaseStream, "Load, End, GlobalLayerMask");
  508. }
  509. public byte[] globalLayerMaskData
  510. {
  511. get { return m_GlobalLayerMaskData; }
  512. }
  513. #endregion
  514. ///////////////////////////////////////////////////////////////////////////
  515. #region Composite image
  516. /// <summary>
  517. /// Represents the composite image.
  518. /// </summary>
  519. public Layer BaseLayer { get; set; }
  520. public ImageCompression ImageCompression { get; set; }
  521. private void LoadImage(PsdBinaryReader reader)
  522. {
  523. Util.DebugMessage(reader.BaseStream, "Load, Begin, Composite image");
  524. ImageCompression = (ImageCompression)reader.ReadInt16();
  525. // Create channels
  526. for (Int16 i = 0; i < ChannelCount; i++)
  527. {
  528. Util.DebugMessage(reader.BaseStream, "Load, Begin, Channel image data");
  529. var channel = new Channel(i, this.BaseLayer);
  530. channel.ImageCompression = ImageCompression;
  531. channel.Length = this.RowCount
  532. * Util.BytesPerRow(BaseLayer.Rect.Size, BitDepth);
  533. // The composite image stores all RLE headers up-front, rather than
  534. // with each channel.
  535. if (ImageCompression == ImageCompression.Rle)
  536. {
  537. channel.RleRowLengths = new RleRowLengths(reader, RowCount, IsLargeDocument);
  538. channel.Length = channel.RleRowLengths.Total;
  539. }
  540. BaseLayer.Channels.Add(channel);
  541. Util.DebugMessage(reader.BaseStream, "Load, End, Channel image data");
  542. }
  543. foreach (var channel in this.BaseLayer.Channels)
  544. {
  545. Util.DebugMessage(reader.BaseStream, "Load, Begin, Channel image data");
  546. Util.CheckByteArrayLength(channel.Length);
  547. channel.ImageDataRaw = reader.ReadBytes((int)channel.Length);
  548. Util.DebugMessage(reader.BaseStream, "Load, End, Channel image data");
  549. }
  550. // If there is exactly one more channel than we need, then it is the
  551. // alpha channel.
  552. if ((ColorMode != PsdColorMode.Multichannel)
  553. && (ChannelCount == ColorMode.MinChannelCount() + 1))
  554. {
  555. var alphaChannel = BaseLayer.Channels.Last();
  556. alphaChannel.ID = -1;
  557. }
  558. Util.DebugMessage(reader.BaseStream, "Load, End, Composite image");
  559. }
  560. #endregion
  561. }
  562. /// <summary>
  563. /// The possible Compression methods.
  564. /// </summary>
  565. internal enum ImageCompression
  566. {
  567. /// <summary>
  568. /// Raw data
  569. /// </summary>
  570. Raw = 0,
  571. /// <summary>
  572. /// RLE compressed
  573. /// </summary>
  574. Rle = 1,
  575. /// <summary>
  576. /// ZIP without prediction.
  577. /// </summary>
  578. Zip = 2,
  579. /// <summary>
  580. /// ZIP with prediction.
  581. /// </summary>
  582. ZipPrediction = 3
  583. }
  584. }