ImageResource.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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.Globalization;
  20. using System.IO;
  21. using System.Linq;
  22. //using static System.FormattableString;
  23. namespace PhotoshopFile
  24. {
  25. public enum ResourceID
  26. {
  27. Undefined = 0,
  28. MacPrintInfo = 1001,
  29. ResolutionInfo = 1005,
  30. AlphaChannelNames = 1006,
  31. DisplayInfo = 1007,
  32. Caption = 1008,
  33. BorderInfo = 1009,
  34. BackgroundColor = 1010,
  35. PrintFlags = 1011,
  36. MultichannelHalftoneInfo = 1012,
  37. ColorHalftoneInfo = 1013,
  38. DuotoneHalftoneInfo = 1014,
  39. MultichannelTransferFunctions = 1015,
  40. ColorTransferFunctions = 1016,
  41. DuotoneTransferFunctions = 1017,
  42. DuotoneImageInfo = 1018,
  43. BlackWhiteRange = 1019,
  44. EpsOptions = 1021,
  45. QuickMaskInfo = 1022,
  46. LayerStateInfo = 1024,
  47. WorkingPathUnsaved = 1025,
  48. LayersGroupInfo = 1026,
  49. IptcNaa = 1028,
  50. RawFormatImageMode = 1029,
  51. JpegQuality = 1030,
  52. GridGuidesInfo = 1032,
  53. ThumbnailBgr = 1033,
  54. CopyrightInfo = 1034,
  55. Url = 1035,
  56. ThumbnailRgb = 1036,
  57. GlobalAngle = 1037,
  58. ColorSamplersObsolete = 1038,
  59. IccProfile = 1039,
  60. Watermark = 1040,
  61. IccUntagged = 1041,
  62. EffectsVisible = 1042,
  63. SpotHalftone = 1043,
  64. DocumentSpecific = 1044,
  65. UnicodeAlphaNames = 1045,
  66. IndexedColorTableCount = 1046,
  67. TransparentIndex = 1047,
  68. GlobalAltitude = 1049,
  69. Slices = 1050,
  70. WorkflowUrl = 1051,
  71. JumpToXpep = 1052,
  72. AlphaIdentifiers = 1053,
  73. UrlList = 1054,
  74. VersionInfo = 1057,
  75. ExifData1 = 1058,
  76. ExifData3 = 1059,
  77. XmpMetadata = 1060,
  78. CaptionDigest = 1061,
  79. PrintScale = 1062,
  80. PixelAspectRatio = 1064,
  81. LayerComps = 1065,
  82. AlternateDuotoneColors = 1066,
  83. AlternateSpotColors = 1067,
  84. LayerSelectionIDs = 1069,
  85. HdrToningInfo = 1070,
  86. PrintInfo = 1071,
  87. LayerGroupsEnabled = 1072,
  88. ColorSamplers = 1073,
  89. MeasurementScale = 1074,
  90. TimelineInfo = 1075,
  91. SheetDisclosure = 1076,
  92. FloatDisplayInfo = 1077,
  93. OnionSkins = 1078,
  94. CountInfo = 1080,
  95. PrintSettingsInfo = 1082,
  96. PrintStyle = 1083,
  97. MacNSPrintInfo = 1084,
  98. WinDevMode = 1085,
  99. AutoSaveFilePath = 1086,
  100. AutoSaveFormat = 1087,
  101. PathInfo = 2000, // 2000-2999: Path Information
  102. ClippingPathName = 2999,
  103. LightroomWorkflow = 8000,
  104. PrintFlagsInfo = 10000
  105. }
  106. /// <summary>
  107. /// Abstract class for Image Resources
  108. /// </summary>
  109. public abstract class ImageResource
  110. {
  111. private string signature;
  112. public string Signature
  113. {
  114. get => signature;
  115. set
  116. {
  117. if (value.Length != 4)
  118. {
  119. throw new ArgumentException(
  120. $"{nameof(Signature)} must be 4 characters in length.");
  121. }
  122. signature = value;
  123. }
  124. }
  125. public string Name { get; set; }
  126. public abstract ResourceID ID { get; }
  127. protected ImageResource(string name)
  128. {
  129. Signature = "8BIM";
  130. Name = name;
  131. }
  132. /// <summary>
  133. /// Write out the image resource block: header and data.
  134. /// </summary>
  135. public void Save(PsdBinaryWriter writer)
  136. {
  137. Util.DebugMessage(writer.BaseStream, "Save, Begin, ImageResource");
  138. writer.WriteAsciiChars(Signature);
  139. writer.Write((UInt16)ID);
  140. writer.WritePascalString(Name, 2);
  141. // Length is unpadded, but data is even-padded
  142. var startPosition = writer.BaseStream.Position;
  143. using (new PsdBlockLengthWriter(writer))
  144. {
  145. WriteData(writer);
  146. }
  147. writer.WritePadding(startPosition, 2);
  148. Util.DebugMessage(writer.BaseStream, $"Save, End, ImageResource, {ID}");
  149. }
  150. /// <summary>
  151. /// Write the data for this image resource.
  152. /// </summary>
  153. protected abstract void WriteData(PsdBinaryWriter writer);
  154. public override string ToString() => String.Format("{0} {1}", ID, Name);
  155. }
  156. /// <summary>
  157. /// Creates the appropriate subclass of ImageResource.
  158. /// </summary>
  159. public static class ImageResourceFactory
  160. {
  161. public static ImageResource CreateImageResource(PsdBinaryReader reader)
  162. {
  163. Util.DebugMessage(reader.BaseStream, "Load, Begin, ImageResource");
  164. var signature = reader.ReadAsciiChars(4);
  165. var resourceIdInt = reader.ReadUInt16();
  166. var name = reader.ReadPascalString(2);
  167. var dataLength = (int)reader.ReadUInt32();
  168. var dataPaddedLength = Util.RoundUp(dataLength, 2);
  169. var endPosition = reader.BaseStream.Position + dataPaddedLength;
  170. ImageResource resource = null;
  171. var resourceId = (ResourceID)resourceIdInt;
  172. switch (resourceId)
  173. {
  174. case ResourceID.ResolutionInfo:
  175. resource = new ResolutionInfo(reader, name);
  176. break;
  177. case ResourceID.ThumbnailRgb:
  178. case ResourceID.ThumbnailBgr:
  179. resource = new Thumbnail(reader, resourceId, name, dataLength);
  180. break;
  181. case ResourceID.AlphaChannelNames:
  182. resource = new AlphaChannelNames(reader, name, dataLength);
  183. break;
  184. case ResourceID.UnicodeAlphaNames:
  185. resource = new UnicodeAlphaNames(reader, name, dataLength);
  186. break;
  187. case ResourceID.VersionInfo:
  188. resource = new VersionInfo(reader, name);
  189. break;
  190. default:
  191. resource = new RawImageResource(reader, signature, resourceId, name, dataLength);
  192. break;
  193. }
  194. Util.DebugMessage(reader.BaseStream,
  195. $"Load, End, ImageResource, {resourceId}");
  196. // Reposition the reader if we do not consume the full resource block.
  197. // This takes care of the even-padding, and also preserves forward-
  198. // compatibility in case a resource block is later extended with
  199. // additional properties.
  200. if (reader.BaseStream.Position < endPosition)
  201. reader.BaseStream.Position = endPosition;
  202. // However, overruns are definitely an error.
  203. if (reader.BaseStream.Position > endPosition)
  204. throw new PsdInvalidException("Corruption detected in resource.");
  205. return resource;
  206. }
  207. }
  208. public class ImageResources : List<ImageResource>
  209. {
  210. public ImageResources() : base()
  211. {
  212. }
  213. public ImageResource Get(ResourceID id)
  214. {
  215. return Find(x => x.ID == id);
  216. }
  217. public void Set(ImageResource resource)
  218. {
  219. Predicate<ImageResource> matchId = delegate(ImageResource res)
  220. {
  221. return res.ID == resource.ID;
  222. };
  223. var itemIdx = this.FindIndex(matchId);
  224. var lastItemIdx = this.FindLastIndex(matchId);
  225. if (itemIdx == -1)
  226. {
  227. Add(resource);
  228. }
  229. else if (itemIdx != lastItemIdx)
  230. {
  231. RemoveAll(matchId);
  232. Insert(itemIdx, resource);
  233. }
  234. else
  235. {
  236. this[itemIdx] = resource;
  237. }
  238. }
  239. }
  240. }