/// <summary> /// Reads resolution information from a PSD file object. /// </summary> /// <param name="psd">The PSD file object from which to obtain resolution information.</param> /// <returns> /// The resolution information obtained from the PSD file, or <c>null</c> if the file contains no resolution /// information. /// </returns> public static ResolutionInfo FromPSD(PSDFile psd) { var resource = psd.ImageResources.FirstOrDefault(ir => ir.ID == ResolutionInfoResourceID); if (resource == null) { return(null); } var resInfo = new ResolutionInfo(); using (var ms = new MemoryStream(resource.Data, writable: false)) { int horizontalResolutionDPIFixedPoint = ms.ReadBigEndianInt32(); resInfo.HorizontalResolutionDPI = horizontalResolutionDPIFixedPoint / FixedPointDivisor; if (resInfo.HorizontalResolutionDPI <= 0.0) { throw new PSDFormatException($"horizontal resolution is {resInfo.HorizontalResolutionDPI}, expected more than 0.0"); } resInfo.HorizontalResolutionDisplayUnit = (ResolutionDisplayUnit)ms.ReadBigEndianInt16(); if (!Enum.IsDefined(typeof(ResolutionDisplayUnit), resInfo.HorizontalResolutionDisplayUnit)) { throw new PSDFormatException( $"horizontal resolution display unit is {resInfo.HorizontalResolutionDisplayUnit}, expected one of {string.Join(", ", EnumUtils.GetUnderlyingValues<ResolutionDisplayUnit, short>())}" ); } resInfo.WidthDisplayUnit = (SizeDisplayUnit)ms.ReadBigEndianInt16(); if (!Enum.IsDefined(typeof(SizeDisplayUnit), resInfo.WidthDisplayUnit)) { throw new PSDFormatException( $"width display unit is {resInfo.WidthDisplayUnit}, expected one of {string.Join(", ", EnumUtils.GetUnderlyingValues<SizeDisplayUnit, short>())}" ); } int verticalResolutionDPIFixedPoint = ms.ReadBigEndianInt32(); resInfo.VerticalResolutionDPI = verticalResolutionDPIFixedPoint / FixedPointDivisor; if (resInfo.VerticalResolutionDPI <= 0.0) { throw new PSDFormatException($"vertical resolution is {resInfo.VerticalResolutionDPI}, expected more than 0.0"); } resInfo.VerticalResolutionDisplayUnit = (ResolutionDisplayUnit)ms.ReadBigEndianInt16(); if (!Enum.IsDefined(typeof(ResolutionDisplayUnit), resInfo.VerticalResolutionDisplayUnit)) { throw new PSDFormatException( $"vertical resolution display unit is {resInfo.VerticalResolutionDisplayUnit}, expected one of {string.Join(", ", EnumUtils.GetUnderlyingValues<ResolutionDisplayUnit, short>())}" ); } resInfo.HeightDisplayUnit = (SizeDisplayUnit)ms.ReadBigEndianInt16(); if (!Enum.IsDefined(typeof(SizeDisplayUnit), resInfo.HeightDisplayUnit)) { throw new PSDFormatException( $"height display unit is {resInfo.HeightDisplayUnit}, expected one of {string.Join(", ", EnumUtils.GetUnderlyingValues<SizeDisplayUnit, short>())}" ); } } return(resInfo); }
/// <summary> /// Reads the PSD header from a stream and populates a <see cref="PSDFile"/> with the information. /// </summary> /// <param name="psd">The PSD file object to populate.</param> /// <param name="stream">The stream from which to read the PSD header.</param> public static void ReadHeader(PSDFile psd, Stream stream) { string magic = stream.ReadUsAsciiString(4); if (!string.Equals(magic, Magic, StringComparison.Ordinal)) { throw new PSDFormatException($"unexpected magic value (is \"{magic}\", expected \"{Magic}\")"); } short version = stream.ReadBigEndianInt16(); if (version < 1 || version > 2) { throw new PSDFormatException($"version is {version}, expected 1 or 2"); } psd.Version = version; byte[] reserved = stream.ReadBytes(6); if (reserved.Any(b => b != 0x00)) { throw new PSDFormatException("nonzero byte in reserved 6-byte value"); } short numberOfChannels = stream.ReadBigEndianInt16(); if (numberOfChannels < MinChannels || numberOfChannels > MaxChannels) { throw new PSDFormatException( $"number of channels is is {numberOfChannels}, expected at least {MinChannels} and at most {MaxChannels}"); } psd.NumberOfChannels = numberOfChannels; int height = stream.ReadBigEndianInt32(); if (height < 1) { throw new PSDFormatException($"height is {height}, expected at least 1"); } if (version == 1 && height > Version1MaxDimension) { throw new PSDFormatException( $"height is {height} and this is a version 1 document, expected at most {Version1MaxDimension}"); } if (version == 2 && height > Version2MaxDimension) { throw new PSDFormatException( $"height is {height} and this is a version 2 document, expected at most {Version2MaxDimension}"); } psd.Height = height; int width = stream.ReadBigEndianInt32(); if (width < 1) { throw new PSDFormatException($"width is {width}, expected at least 1"); } if (version == 1 && width > Version1MaxDimension) { throw new PSDFormatException( $"width is {width} and this is a version 1 document, expected at most {Version1MaxDimension}"); } if (version == 2 && width > Version2MaxDimension) { throw new PSDFormatException( $"width is {width} and this is a version 2 document, expected at most {Version2MaxDimension}"); } psd.Width = width; short depth = stream.ReadBigEndianInt16(); if (depth != 1 && depth != 8 && depth != 16 && depth != 32) { throw new PSDFormatException($"depth is {depth}, expected 1 or 8 or 16 or 32"); } psd.Depth = depth; short colorMode = stream.ReadBigEndianInt16(); if (!Enum.IsDefined(typeof(ColorMode), colorMode)) { throw new PSDFormatException( $"color mode is {colorMode}, expected one of {string.Join(", ", EnumUtils.GetUnderlyingValues<ColorMode, short>())}"); } psd.ColorMode = (ColorMode)colorMode; }
/// <summary> /// Reads metadata about a PSD file's precomposed image data and populates a <see cref="PSDFile"/> with it. /// </summary> /// <param name="psd">The PSD file object to populate.</param> /// <param name="stream">The stream from which to read the precomposed image data metadata.</param> public static void CreateImageDataPlaceholder(PSDFile psd, Stream stream) { var placeholder = new PSDImageDataPlaceholder(); // read compression method short compressionValue = stream.ReadBigEndianInt16(); if (!Enum.IsDefined(typeof(CompressionType), compressionValue)) { throw new PSDFormatException($"unexpected compression type {compressionValue}, expected one of {string.Join(", ", EnumUtils.GetUnderlyingValues<CompressionType, short>())}"); } placeholder.Compression = (CompressionType)compressionValue; placeholder.Offset = stream.Position; psd.PrecomposedImageData = placeholder; }
/// <summary> /// Reads the global mask information subsection of a PSD layer and mask information section from a stream /// and populates a <see cref="PSDFile"/> with the information. /// </summary> /// <param name="psd">The PSD file object to populate.</param> /// <param name="stream">The stream from which to read the layer information subsection.</param> public static void ReadGlobalMaskInformation(PSDFile psd, Stream stream) { int length = stream.ReadBigEndianInt32(); if (length == 0) { // no global mask info psd.GlobalLayerMask = null; return; } var mask = new PSDGlobalLayerMask(); mask.OverlayColorSpace = stream.ReadBigEndianInt16(); mask.ColorComponent1 = stream.ReadBigEndianInt16(); mask.ColorComponent2 = stream.ReadBigEndianInt16(); mask.ColorComponent3 = stream.ReadBigEndianInt16(); mask.ColorComponent4 = stream.ReadBigEndianInt16(); short opacity = stream.ReadBigEndianInt16(); if (opacity < 0 || opacity > 100) { throw new PSDFormatException($"global mask opacity must be at least 0 and at most 100, got {opacity}"); } mask.Opacity = opacity; byte kind = stream.ReadByteOrThrow(); if (!Enum.IsDefined(typeof(LayerMaskKind), kind)) { throw new PSDFormatException($"unknown layer mask kind {kind}, expected one of {string.Join(", ", EnumUtils.GetUnderlyingValues<LayerMaskKind, byte>())}"); } mask.Kind = (LayerMaskKind)kind; int paddingByteCount = length - 13; stream.ReadBytes(paddingByteCount); psd.GlobalLayerMask = mask; }