PsdFile.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  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-2017 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 System.Drawing;
  20. using System.Globalization;
  21. using System.IO;
  22. using System.Linq;
  23. using System.Text;
  24. using System.Threading.Tasks;
  25. namespace PhotoshopFile
  26. {
  27. public enum PsdColorMode
  28. {
  29. Bitmap = 0,
  30. Grayscale = 1,
  31. Indexed = 2,
  32. RGB = 3,
  33. CMYK = 4,
  34. Multichannel = 7,
  35. Duotone = 8,
  36. Lab = 9
  37. };
  38. public enum PsdFileVersion : short
  39. {
  40. Psd = 1,
  41. PsbLargeDocument = 2
  42. }
  43. public class PsdFile
  44. {
  45. #region Constructors
  46. public PsdFile(PsdFileVersion version = PsdFileVersion.Psd)
  47. {
  48. Version = version;
  49. BaseLayer = new Layer(this);
  50. ImageResources = new ImageResources();
  51. Layers = new List<Layer>();
  52. AdditionalInfo = new List<LayerInfo>();
  53. }
  54. public PsdFile(string filename, LoadContext loadContext)
  55. : this()
  56. {
  57. using (var stream = new FileStream(filename, FileMode.Open))
  58. {
  59. Load(stream, loadContext);
  60. }
  61. }
  62. public PsdFile(Stream stream, LoadContext loadContext)
  63. : this()
  64. {
  65. Load(stream, loadContext);
  66. }
  67. #endregion
  68. #region Load and save
  69. internal LoadContext LoadContext { get; private set; }
  70. private void Load(Stream stream, LoadContext loadContext)
  71. {
  72. LoadContext = loadContext;
  73. var reader = new PsdBinaryReader(stream, loadContext.Encoding);
  74. LoadHeader(reader);
  75. LoadColorModeData(reader);
  76. LoadImageResources(reader);
  77. LoadLayerAndMaskInfo(reader);
  78. LoadImage(reader);
  79. DecompressImages();
  80. }
  81. public void Save(string fileName, Encoding encoding)
  82. {
  83. using (var stream = new FileStream(fileName, FileMode.Create))
  84. {
  85. Save(stream, encoding);
  86. }
  87. }
  88. public void Save(Stream stream, Encoding encoding)
  89. {
  90. PrepareSave();
  91. using (var writer = new PsdBinaryWriter(stream, encoding))
  92. {
  93. SaveHeader(writer);
  94. SaveColorModeData(writer);
  95. SaveImageResources(writer);
  96. SaveLayerAndMaskInfo(writer);
  97. SaveImage(writer);
  98. }
  99. }
  100. #endregion
  101. #region Header
  102. /// <summary>
  103. /// Photoshop file format version.
  104. /// </summary>
  105. public PsdFileVersion Version { get; private set; }
  106. public bool IsLargeDocument =>
  107. (Version == PsdFileVersion.PsbLargeDocument);
  108. private Int16 channelCount;
  109. /// <summary>
  110. /// The number of channels in the image, including any alpha channels.
  111. /// </summary>
  112. public Int16 ChannelCount
  113. {
  114. get => channelCount;
  115. set
  116. {
  117. if (value < 1 || value > 56)
  118. throw new ArgumentException("Number of channels must be from 1 to 56.");
  119. channelCount = value;
  120. }
  121. }
  122. private void CheckDimension(int dimension)
  123. {
  124. if (dimension < 1)
  125. {
  126. throw new ArgumentException("Image dimension must be at least 1.");
  127. }
  128. if ((Version == PsdFileVersion.Psd) && (dimension > 30000))
  129. {
  130. throw new ArgumentException("PSD image dimension cannot exceed 30000.");
  131. }
  132. if ((Version == PsdFileVersion.PsbLargeDocument) && (dimension > 300000))
  133. {
  134. throw new ArgumentException("PSB image dimension cannot exceed 300000.");
  135. }
  136. }
  137. /// <summary>
  138. /// The height of the image in pixels.
  139. /// </summary>
  140. public int RowCount
  141. {
  142. get => this.BaseLayer.Rect.Height;
  143. set
  144. {
  145. CheckDimension(value);
  146. BaseLayer.Rect = new Rectangle(0, 0, BaseLayer.Rect.Width, value);
  147. }
  148. }
  149. /// <summary>
  150. /// The width of the image in pixels.
  151. /// </summary>
  152. public int ColumnCount
  153. {
  154. get => this.BaseLayer.Rect.Width;
  155. set
  156. {
  157. CheckDimension(value);
  158. BaseLayer.Rect = new Rectangle(0, 0, value, BaseLayer.Rect.Height);
  159. }
  160. }
  161. private int bitDepth;
  162. /// <summary>
  163. /// The number of bits per channel. Supported values are 1, 8, 16, and 32.
  164. /// </summary>
  165. public int BitDepth
  166. {
  167. get => bitDepth;
  168. set
  169. {
  170. switch (value)
  171. {
  172. case 1:
  173. case 8:
  174. case 16:
  175. case 32:
  176. bitDepth = value;
  177. break;
  178. default:
  179. throw new NotImplementedException("Invalid bit depth.");
  180. }
  181. }
  182. }
  183. /// <summary>
  184. /// The color mode of the file.
  185. /// </summary>
  186. public PsdColorMode ColorMode { get; set; }
  187. ///////////////////////////////////////////////////////////////////////////
  188. private void LoadHeader(PsdBinaryReader reader)
  189. {
  190. Util.DebugMessage(reader.BaseStream, "Load, Begin, File header");
  191. var signature = reader.ReadAsciiChars(4);
  192. if (signature != "8BPS")
  193. throw new PsdInvalidException("The given stream is not a valid PSD file");
  194. Version = (PsdFileVersion)reader.ReadInt16();
  195. Util.DebugMessage(reader.BaseStream, $"Load, Info, Version {(int)Version}");
  196. if ((Version != PsdFileVersion.Psd)
  197. && (Version != PsdFileVersion.PsbLargeDocument))
  198. {
  199. throw new PsdInvalidException("The PSD file has an unknown version");
  200. }
  201. //6 bytes reserved
  202. reader.BaseStream.Position += 6;
  203. this.ChannelCount = reader.ReadInt16();
  204. this.RowCount = reader.ReadInt32();
  205. this.ColumnCount = reader.ReadInt32();
  206. BitDepth = reader.ReadInt16();
  207. ColorMode = (PsdColorMode)reader.ReadInt16();
  208. Util.DebugMessage(reader.BaseStream, "Load, End, File header");
  209. }
  210. ///////////////////////////////////////////////////////////////////////////
  211. private void SaveHeader(PsdBinaryWriter writer)
  212. {
  213. Util.DebugMessage(writer.BaseStream, "Save, Begin, File header");
  214. string signature = "8BPS";
  215. writer.WriteAsciiChars(signature);
  216. writer.Write((Int16)Version);
  217. writer.Write(new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, });
  218. writer.Write(ChannelCount);
  219. writer.Write(RowCount);
  220. writer.Write(ColumnCount);
  221. writer.Write((Int16)BitDepth);
  222. writer.Write((Int16)ColorMode);
  223. Util.DebugMessage(writer.BaseStream, "Save, End, File header");
  224. }
  225. #endregion
  226. ///////////////////////////////////////////////////////////////////////////
  227. #region ColorModeData
  228. /// <summary>
  229. /// If ColorMode is ColorModes.Indexed, the following 768 bytes will contain
  230. /// a 256-color palette. If the ColorMode is ColorModes.Duotone, the data
  231. /// following presumably consists of screen parameters and other related information.
  232. /// Unfortunately, it is intentionally not documented by Adobe, and non-Photoshop
  233. /// readers are advised to treat duotone images as gray-scale images.
  234. /// </summary>
  235. public byte[] ColorModeData = new byte[0];
  236. private void LoadColorModeData(PsdBinaryReader reader)
  237. {
  238. Util.DebugMessage(reader.BaseStream, "Load, Begin, ColorModeData");
  239. var paletteLength = reader.ReadUInt32();
  240. if (paletteLength > 0)
  241. {
  242. ColorModeData = reader.ReadBytes((int)paletteLength);
  243. }
  244. Util.DebugMessage(reader.BaseStream, "Load, End, ColorModeData");
  245. }
  246. private void SaveColorModeData(PsdBinaryWriter writer)
  247. {
  248. Util.DebugMessage(writer.BaseStream, "Save, Begin, ColorModeData");
  249. writer.Write((UInt32)ColorModeData.Length);
  250. writer.Write(ColorModeData);
  251. Util.DebugMessage(writer.BaseStream, "Save, End, ColorModeData");
  252. }
  253. #endregion
  254. ///////////////////////////////////////////////////////////////////////////
  255. #region ImageResources
  256. /// <summary>
  257. /// The Image resource blocks for the file
  258. /// </summary>
  259. public ImageResources ImageResources { get; set; }
  260. public ResolutionInfo Resolution
  261. {
  262. get => (ResolutionInfo)ImageResources.Get(ResourceID.ResolutionInfo);
  263. set => ImageResources.Set(value);
  264. }
  265. ///////////////////////////////////////////////////////////////////////////
  266. private void LoadImageResources(PsdBinaryReader reader)
  267. {
  268. Util.DebugMessage(reader.BaseStream, "Load, Begin, ImageResources");
  269. var imageResourcesLength = reader.ReadUInt32();
  270. if (imageResourcesLength <= 0)
  271. return;
  272. var startPosition = reader.BaseStream.Position;
  273. var endPosition = startPosition + imageResourcesLength;
  274. while (reader.BaseStream.Position < endPosition)
  275. {
  276. var imageResource = ImageResourceFactory.CreateImageResource(reader);
  277. ImageResources.Add(imageResource);
  278. }
  279. Util.DebugMessage(reader.BaseStream, "Load, End, ImageResources");
  280. //-----------------------------------------------------------------------
  281. // make sure we are not on a wrong offset, so set the stream position
  282. // manually
  283. reader.BaseStream.Position = startPosition + imageResourcesLength;
  284. }
  285. ///////////////////////////////////////////////////////////////////////////
  286. private void SaveImageResources(PsdBinaryWriter writer)
  287. {
  288. Util.DebugMessage(writer.BaseStream, "Save, Begin, ImageResources");
  289. using (new PsdBlockLengthWriter(writer))
  290. {
  291. foreach (var imgRes in ImageResources)
  292. imgRes.Save(writer);
  293. }
  294. Util.DebugMessage(writer.BaseStream, "Save, End, ImageResources");
  295. }
  296. #endregion
  297. ///////////////////////////////////////////////////////////////////////////
  298. #region LayerAndMaskInfo
  299. public List<Layer> Layers { get; private set; }
  300. public List<LayerInfo> AdditionalInfo { get; private set; }
  301. public bool AbsoluteAlpha { get; set; }
  302. ///////////////////////////////////////////////////////////////////////////
  303. private void LoadLayerAndMaskInfo(PsdBinaryReader reader)
  304. {
  305. Util.DebugMessage(reader.BaseStream, "Load, Begin, Layer and mask info");
  306. var layersAndMaskLength = IsLargeDocument
  307. ? reader.ReadInt64()
  308. : reader.ReadUInt32();
  309. if (layersAndMaskLength <= 0)
  310. return;
  311. var startPosition = reader.BaseStream.Position;
  312. var endPosition = startPosition + layersAndMaskLength;
  313. LoadLayers(reader, true);
  314. LoadGlobalLayerMask(reader);
  315. //-----------------------------------------------------------------------
  316. // Load Additional Layer Information
  317. while (reader.BaseStream.Position < endPosition)
  318. {
  319. var info = LayerInfoFactory.Load(reader,
  320. psdFile: this,
  321. globalLayerInfo: true);
  322. AdditionalInfo.Add(info);
  323. if (info is RawLayerInfo layerInfo)
  324. {
  325. switch (layerInfo.Key)
  326. {
  327. case "LMsk":
  328. GlobalLayerMaskData = layerInfo.Data;
  329. break;
  330. }
  331. }
  332. }
  333. Util.DebugMessage(reader.BaseStream, "Load, End, Layer and mask info");
  334. //-----------------------------------------------------------------------
  335. // make sure we are not on a wrong offset, so set the stream position
  336. // manually
  337. reader.BaseStream.Position = startPosition + layersAndMaskLength;
  338. }
  339. ///////////////////////////////////////////////////////////////////////////
  340. private void SaveLayerAndMaskInfo(PsdBinaryWriter writer)
  341. {
  342. Util.DebugMessage(writer.BaseStream, "Save, Begin, Layer and mask info");
  343. using (new PsdBlockLengthWriter(writer, IsLargeDocument))
  344. {
  345. var startPosition = writer.BaseStream.Position;
  346. SaveLayers(writer);
  347. SaveGlobalLayerMask(writer);
  348. foreach (var info in AdditionalInfo)
  349. {
  350. info.Save(writer,
  351. globalLayerInfo: true,
  352. isLargeDocument: IsLargeDocument);
  353. }
  354. writer.WritePadding(startPosition, 2);
  355. }
  356. Util.DebugMessage(writer.BaseStream, "Save, End, Layer and mask info");
  357. }
  358. ///////////////////////////////////////////////////////////////////////////
  359. /// <summary>
  360. /// Load Layers Info section, including image data.
  361. /// </summary>
  362. /// <param name="reader">PSD reader.</param>
  363. /// <param name="hasHeader">Whether the Layers Info section has a length header.</param>
  364. internal void LoadLayers(PsdBinaryReader reader, bool hasHeader)
  365. {
  366. Util.DebugMessage(reader.BaseStream, "Load, Begin, Layers Info section");
  367. long sectionLength = 0;
  368. if (hasHeader)
  369. {
  370. sectionLength = IsLargeDocument
  371. ? reader.ReadInt64()
  372. : reader.ReadUInt32();
  373. if (sectionLength <= 0)
  374. {
  375. // The callback may take action when there are 0 layers, so it must
  376. // be called even though the Layers Info section is empty.
  377. LoadContext.OnLoadLayersHeader(this);
  378. Util.DebugMessage(reader.BaseStream, "Load, End, Layers Info section");
  379. return;
  380. }
  381. }
  382. var startPosition = reader.BaseStream.Position;
  383. var numLayers = reader.ReadInt16();
  384. // If numLayers < 0, then number of layers is absolute value,
  385. // and the first alpha channel contains the transparency data for
  386. // the merged result.
  387. if (numLayers < 0)
  388. {
  389. AbsoluteAlpha = true;
  390. numLayers = Math.Abs(numLayers);
  391. }
  392. for (int i = 0; i < numLayers; i++)
  393. {
  394. var layer = new Layer(reader, this);
  395. Layers.Add(layer);
  396. }
  397. // Header is complete just before loading pixel data
  398. LoadContext.OnLoadLayersHeader(this);
  399. //-----------------------------------------------------------------------
  400. // Load image data for all channels.
  401. foreach (var layer in Layers)
  402. {
  403. Util.DebugMessage(reader.BaseStream,
  404. $"Load, Begin, Layer image, {layer.Name}");
  405. foreach (var channel in layer.Channels)
  406. {
  407. channel.LoadPixelData(reader);
  408. }
  409. Util.DebugMessage(reader.BaseStream,
  410. $"Load, End, Layer image, {layer.Name}");
  411. }
  412. // Length is set to 0 when called on higher bitdepth layers.
  413. if (sectionLength > 0)
  414. {
  415. // Layers Info section is documented to be even-padded, but Photoshop
  416. // actually pads to 4 bytes.
  417. var endPosition = startPosition + sectionLength;
  418. var positionOffset = reader.BaseStream.Position - endPosition;
  419. Debug.Assert(positionOffset > -4,
  420. "LoadLayers did not read the full length of the Layers Info section.");
  421. Debug.Assert(positionOffset <= 0,
  422. "LoadLayers read past the end of the Layers Info section.");
  423. if (reader.BaseStream.Position < endPosition)
  424. reader.BaseStream.Position = endPosition;
  425. }
  426. Util.DebugMessage(reader.BaseStream, "Load, End, Layers");
  427. }
  428. ///////////////////////////////////////////////////////////////////////////
  429. /// <summary>
  430. /// Decompress the document image data and all the layers' image data, in parallel.
  431. /// </summary>
  432. private void DecompressImages()
  433. {
  434. var layersAndComposite = Layers.Concat(new[] { BaseLayer });
  435. var channels = layersAndComposite.SelectMany(x => x.Channels);
  436. Parallel.ForEach(channels, channel =>
  437. {
  438. channel.DecodeImageData();
  439. });
  440. foreach (var layer in Layers)
  441. {
  442. foreach (var channel in layer.Channels)
  443. {
  444. if (channel.ID == -2)
  445. layer.Masks.LayerMask.ImageData = channel.ImageData;
  446. else if (channel.ID == -3)
  447. layer.Masks.UserMask.ImageData = channel.ImageData;
  448. }
  449. }
  450. }
  451. /// <summary>
  452. /// Check the validity of the PSD file and generate necessary data.
  453. /// </summary>
  454. public void PrepareSave()
  455. {
  456. CheckDimension(ColumnCount);
  457. CheckDimension(RowCount);
  458. VerifyInfoLayers();
  459. VerifyLayerSections();
  460. var imageLayers = Layers.Concat(new List<Layer>() { this.BaseLayer }).ToList();
  461. foreach (var layer in imageLayers)
  462. {
  463. layer.PrepareSave();
  464. }
  465. SetVersionInfo();
  466. }
  467. /// <summary>
  468. /// Verifies that any Additional Info layers are consistent.
  469. /// </summary>
  470. private void VerifyInfoLayers()
  471. {
  472. var infoLayersCount = AdditionalInfo.Count(x => x is InfoLayers);
  473. if (infoLayersCount > 1)
  474. {
  475. throw new PsdInvalidException(
  476. $"Cannot have more than one {nameof(InfoLayers)} in a PSD file.");
  477. }
  478. if ((infoLayersCount > 0) && (Layers.Count == 0))
  479. {
  480. throw new PsdInvalidException(
  481. $"{nameof(InfoLayers)} cannot exist when there are 0 layers.");
  482. }
  483. }
  484. /// <summary>
  485. /// Verify validity of layer sections. Each start marker should have a
  486. /// matching end marker.
  487. /// </summary>
  488. internal void VerifyLayerSections()
  489. {
  490. int depth = 0;
  491. foreach (var layer in Enumerable.Reverse(Layers))
  492. {
  493. var layerSectionInfo = layer.AdditionalInfo.SingleOrDefault(
  494. x => x is LayerSectionInfo);
  495. if (layerSectionInfo == null)
  496. continue;
  497. var sectionInfo = (LayerSectionInfo)layerSectionInfo;
  498. switch (sectionInfo.SectionType)
  499. {
  500. case LayerSectionType.Layer:
  501. break;
  502. case LayerSectionType.OpenFolder:
  503. case LayerSectionType.ClosedFolder:
  504. depth++;
  505. break;
  506. case LayerSectionType.SectionDivider:
  507. depth--;
  508. if (depth < 0)
  509. throw new PsdInvalidException("Layer section ended without matching start marker.");
  510. break;
  511. default:
  512. throw new PsdInvalidException("Unrecognized layer section type.");
  513. }
  514. }
  515. if (depth != 0)
  516. throw new PsdInvalidException("Layer section not closed by end marker.");
  517. }
  518. /// <summary>
  519. /// Set the VersionInfo resource on the file.
  520. /// </summary>
  521. public void SetVersionInfo()
  522. {
  523. var versionInfo = (VersionInfo)ImageResources.Get(ResourceID.VersionInfo);
  524. if (versionInfo == null)
  525. {
  526. versionInfo = new VersionInfo();
  527. ImageResources.Set(versionInfo);
  528. // Get the version string. We don't use the fourth part (revision).
  529. var assembly = System.Reflection.Assembly.GetExecutingAssembly();
  530. var version = assembly.GetName().Version;
  531. var versionString = version.Major + "." + version.Minor + "." + version.Build;
  532. // Strings are not localized since they are not shown to the user.
  533. versionInfo.Version = 1;
  534. versionInfo.HasRealMergedData = true;
  535. versionInfo.ReaderName = "Paint.NET PSD Plugin";
  536. versionInfo.WriterName = "Paint.NET PSD Plugin " + versionString;
  537. versionInfo.FileVersion = 1;
  538. }
  539. }
  540. /// <summary>
  541. /// Saves the Layers Info section, including headers and padding.
  542. /// </summary>
  543. /// <param name="writer">The PSD writer.</param>
  544. internal void SaveLayers(PsdBinaryWriter writer)
  545. {
  546. Util.DebugMessage(writer.BaseStream, "Save, Begin, Layers Info section");
  547. using (new PsdBlockLengthWriter(writer, IsLargeDocument))
  548. {
  549. var startPosition = writer.BaseStream.Position;
  550. // Only one set of Layers can exist in the file. If layers will be
  551. // written to the Additional Info section, then the Layers section
  552. // must be empty to avoid conflict.
  553. var hasInfoLayers = AdditionalInfo.Exists(x => x is InfoLayers);
  554. if (!hasInfoLayers)
  555. {
  556. SaveLayersData(writer);
  557. }
  558. // Documentation states that the Layers Info section is even-padded,
  559. // but it is actually padded to a multiple of 4.
  560. writer.WritePadding(startPosition, 4);
  561. }
  562. Util.DebugMessage(writer.BaseStream, "Save, End, Layers Info section");
  563. }
  564. /// <summary>
  565. /// Saves the layer data, excluding headers and padding.
  566. /// </summary>
  567. /// <param name="writer">The PSD writer.</param>
  568. internal void SaveLayersData(PsdBinaryWriter writer)
  569. {
  570. Util.DebugMessage(writer.BaseStream, "Save, Begin, Layers");
  571. var numLayers = (Int16)Layers.Count;
  572. if (AbsoluteAlpha)
  573. {
  574. numLayers = (Int16)(-numLayers);
  575. }
  576. // Photoshop will not load files that have a layer count of 0 in the
  577. // compatible Layers section. Instead, the Layers section must be
  578. // entirely empty.
  579. if (numLayers == 0)
  580. {
  581. return;
  582. }
  583. writer.Write(numLayers);
  584. foreach (var layer in Layers)
  585. {
  586. layer.Save(writer);
  587. }
  588. foreach (var layer in Layers)
  589. {
  590. Util.DebugMessage(writer.BaseStream,
  591. $"Save, Begin, Layer image, {layer.Name}");
  592. foreach (var channel in layer.Channels)
  593. {
  594. channel.SavePixelData(writer);
  595. }
  596. Util.DebugMessage(writer.BaseStream,
  597. $"Save, End, Layer image, {layer.Name}");
  598. }
  599. // The caller is responsible for padding. Photoshop writes padded
  600. // lengths for compatible layers, but unpadded lengths for Additional
  601. // Info layers.
  602. Util.DebugMessage(writer.BaseStream, "Save, End, Layers");
  603. }
  604. ///////////////////////////////////////////////////////////////////////////
  605. byte[] GlobalLayerMaskData = new byte[0];
  606. private void LoadGlobalLayerMask(PsdBinaryReader reader)
  607. {
  608. Util.DebugMessage(reader.BaseStream, "Load, Begin, GlobalLayerMask");
  609. var maskLength = reader.ReadUInt32();
  610. if (maskLength <= 0)
  611. {
  612. Util.DebugMessage(reader.BaseStream, "Load, End, GlobalLayerMask");
  613. return;
  614. }
  615. GlobalLayerMaskData = reader.ReadBytes((int)maskLength);
  616. Util.DebugMessage(reader.BaseStream, "Load, End, GlobalLayerMask");
  617. }
  618. ///////////////////////////////////////////////////////////////////////////
  619. private void SaveGlobalLayerMask(PsdBinaryWriter writer)
  620. {
  621. Util.DebugMessage(writer.BaseStream, "Save, Begin, GlobalLayerMask");
  622. if (AdditionalInfo.Exists(x => x.Key == "LMsk"))
  623. {
  624. writer.Write((UInt32)0);
  625. Util.DebugMessage(writer.BaseStream, "Save, End, GlobalLayerMask");
  626. return;
  627. }
  628. writer.Write((UInt32)GlobalLayerMaskData.Length);
  629. writer.Write(GlobalLayerMaskData);
  630. Util.DebugMessage(writer.BaseStream, "Save, End, GlobalLayerMask");
  631. }
  632. ///////////////////////////////////////////////////////////////////////////
  633. #endregion
  634. ///////////////////////////////////////////////////////////////////////////
  635. #region Composite image
  636. /// <summary>
  637. /// Represents the composite image.
  638. /// </summary>
  639. public Layer BaseLayer { get; set; }
  640. public ImageCompression ImageCompression { get; set; }
  641. private void LoadImage(PsdBinaryReader reader)
  642. {
  643. Util.DebugMessage(reader.BaseStream, "Load, Begin, Composite image");
  644. ImageCompression = (ImageCompression)reader.ReadInt16();
  645. // Create channels
  646. for (Int16 i = 0; i < ChannelCount; i++)
  647. {
  648. Util.DebugMessage(reader.BaseStream, "Load, Begin, Channel image data");
  649. var channel = new Channel(i, this.BaseLayer);
  650. channel.ImageCompression = ImageCompression;
  651. channel.Length = this.RowCount
  652. * Util.BytesPerRow(BaseLayer.Rect.Size, BitDepth);
  653. // The composite image stores all RLE headers up-front, rather than
  654. // with each channel.
  655. if (ImageCompression == ImageCompression.Rle)
  656. {
  657. channel.RleRowLengths = new RleRowLengths(reader, RowCount, IsLargeDocument);
  658. channel.Length = channel.RleRowLengths.Total;
  659. }
  660. BaseLayer.Channels.Add(channel);
  661. Util.DebugMessage(reader.BaseStream, "Load, End, Channel image data");
  662. }
  663. foreach (var channel in this.BaseLayer.Channels)
  664. {
  665. Util.DebugMessage(reader.BaseStream, "Load, Begin, Channel image data");
  666. Util.CheckByteArrayLength(channel.Length);
  667. channel.ImageDataRaw = reader.ReadBytes((int)channel.Length);
  668. Util.DebugMessage(reader.BaseStream, "Load, End, Channel image data");
  669. }
  670. // If there is exactly one more channel than we need, then it is the
  671. // alpha channel.
  672. if ((ColorMode != PsdColorMode.Multichannel)
  673. && (ChannelCount == ColorMode.MinChannelCount() + 1))
  674. {
  675. var alphaChannel = BaseLayer.Channels.Last();
  676. alphaChannel.ID = -1;
  677. }
  678. Util.DebugMessage(reader.BaseStream, "Load, End, Composite image");
  679. }
  680. ///////////////////////////////////////////////////////////////////////////
  681. private void SaveImage(PsdBinaryWriter writer)
  682. {
  683. Util.DebugMessage(writer.BaseStream, "Save, Begin, Composite image");
  684. writer.Write((short)this.ImageCompression);
  685. if (this.ImageCompression == PhotoshopFile.ImageCompression.Rle)
  686. {
  687. foreach (var channel in this.BaseLayer.Channels)
  688. {
  689. Util.DebugMessage(writer.BaseStream, "Save, Begin, RLE header");
  690. channel.RleRowLengths.Write(writer, IsLargeDocument);
  691. Util.DebugMessage(writer.BaseStream, "Save, End, RLE header");
  692. }
  693. }
  694. foreach (var channel in this.BaseLayer.Channels)
  695. {
  696. Util.DebugMessage(writer.BaseStream, "Save, Begin, Channel image data");
  697. writer.Write(channel.ImageDataRaw);
  698. Util.DebugMessage(writer.BaseStream, "Save, End, Channel image data");
  699. }
  700. Util.DebugMessage(writer.BaseStream, "Save, End, Composite image");
  701. }
  702. #endregion
  703. }
  704. /// <summary>
  705. /// The possible Compression methods.
  706. /// </summary>
  707. public enum ImageCompression
  708. {
  709. /// <summary>
  710. /// Raw data
  711. /// </summary>
  712. Raw = 0,
  713. /// <summary>
  714. /// RLE compressed
  715. /// </summary>
  716. Rle = 1,
  717. /// <summary>
  718. /// ZIP without prediction.
  719. /// </summary>
  720. Zip = 2,
  721. /// <summary>
  722. /// ZIP with prediction.
  723. /// </summary>
  724. ZipPrediction = 3
  725. }
  726. }