///////////////////////////////////////////////////////////////////////////////// // // Photoshop PSD FileType Plugin for Paint.NET // http://psdplugin.codeplex.com/ // // This software is provided under the MIT License: // Copyright (c) 2006-2007 Frank Blumenberg // Copyright (c) 2010-2016 Tao Yue // // See LICENSE.txt for complete licensing and attribution information. // ///////////////////////////////////////////////////////////////////////////////// using System; using System.Collections.Generic; using System.Diagnostics; using PDNWrapper; using System.IO; using System.Linq; using System.Text; using Unity.Collections; namespace PhotoshopFile { internal static class Util { [DebuggerDisplay("Top = {Top}, Bottom = {Bottom}, Left = {Left}, Right = {Right}")] internal struct RectanglePosition { public int Top { get; set; } public int Bottom { get; set; } public int Left { get; set; } public int Right { get; set; } } public static Rectangle IntersectWith( this Rectangle thisRect, Rectangle rect) { thisRect.Intersect(rect); return thisRect; } /////////////////////////////////////////////////////////////////////////// /// /// Fills a buffer with a byte value. /// static public void Fill(byte[] ptr, int start, int end, byte value) { while (start < end) { ptr[start] = value; start++; } } static public void Invert(byte[] ptr, int ptrStart, int ptrEnd) { while (ptrStart < ptrEnd) { ptr[ptrStart] = (byte)(ptr[ptrStart] ^ 0xff); ptrStart++; } } /// /// Fills a buffer with a byte value. /// static public void Fill(NativeArray ptr, int start, int end, byte value) { while (start < end) { ptr[start] = value; start++; } } static public void Invert(NativeArray ptr, int ptrStart, int ptrEnd) { while (ptrStart < ptrEnd) { ptr[ptrStart] = (byte)(ptr[ptrStart] ^ 0xff); ptrStart++; } } /////////////////////////////////////////////////////////////////////////// /// /// Reverses the endianness of a 2-byte word. /// static public void SwapBytes2(byte[] ptr, int start) { byte byte0 = ptr[start]; ptr[start] = ptr[start + 1]; ptr[start + 1] = byte0; } /////////////////////////////////////////////////////////////////////////// /// /// Reverses the endianness of a 4-byte word. /// static public void SwapBytes4(byte[] ptr, int start) { byte byte0 = ptr[start]; byte byte1 = ptr[start + 1]; ptr[start] = ptr[start + 3]; ptr[start + 1] = ptr[start + 2]; ptr[start + 2] = byte1; ptr[start + 3] = byte0; } /// /// Reverses the endianness of a word of arbitrary length. /// static public void SwapBytes(byte[] ptr, int start, int nLength) { for (long i = 0; i < nLength / 2; ++i) { byte t = ptr[start + i]; ptr[start + i] = ptr[start + nLength - i - 1]; ptr[start + nLength - i - 1] = t; } } /////////////////////////////////////////////////////////////////////////// public static void SwapByteArray(int bitDepth, byte[] byteArray, int startIdx, int count) { switch (bitDepth) { case 1: case 8: break; case 16: SwapByteArray2(byteArray, startIdx, count); break; case 32: SwapByteArray4(byteArray, startIdx, count); break; default: throw new Exception( "Byte-swapping implemented only for 16-bit and 32-bit depths."); } } /// /// Reverses the endianness of 2-byte words in a byte array. /// /// Byte array containing the sequence on which to swap endianness /// Byte index of the first word to swap /// Number of words to swap public static void SwapByteArray2(byte[] byteArray, int startIdx, int count) { int endIdx = startIdx + count * 2; if (byteArray.Length < endIdx) throw new IndexOutOfRangeException(); { //fixed (byte* arrayPtr = &byteArray[0]) { //byte* ptr = arrayPtr + startIdx; //byte* endPtr = arrayPtr + endIdx; while (startIdx < endIdx) { SwapBytes2(byteArray, startIdx); startIdx += 2; } } } } /// /// Reverses the endianness of 4-byte words in a byte array. /// /// Byte array containing the sequence on which to swap endianness /// Byte index of the first word to swap /// Number of words to swap public static void SwapByteArray4(byte[] byteArray, int startIdx, int count) { int endIdx = startIdx + count * 4; if (byteArray.Length < endIdx) throw new IndexOutOfRangeException(); { //fixed (byte* arrayPtr = &byteArray[0]) { //byte* ptr = arrayPtr + startIdx; //byte* endPtr = arrayPtr + endIdx; while (startIdx < endIdx) { SwapBytes4(byteArray, startIdx); startIdx += 4; } } } } /////////////////////////////////////////////////////////////////////////// /// /// Calculates the number of bytes required to store a row of an image /// with the specified bit depth. /// /// The size of the image in pixels. /// The bit depth of the image. /// The number of bytes needed to store a row of the image. public static int BytesPerRow(Size size, int bitDepth) { switch (bitDepth) { case 1: return (size.Width + 7) / 8; default: return size.Width * BytesFromBitDepth(bitDepth); } } /// /// Round the integer to a multiple. /// public static int RoundUp(int value, int multiple) { if (value == 0) return 0; if (Math.Sign(value) != Math.Sign(multiple)) { throw new ArgumentException( "value and multiple cannot have opposite signs."); } var remainder = value % multiple; if (remainder > 0) { value += (multiple - remainder); } return value; } /// /// Get number of bytes required to pad to the specified multiple. /// public static int GetPadding(int length, int padMultiple) { if ((length < 0) || (padMultiple < 0)) throw new ArgumentException(); var remainder = length % padMultiple; if (remainder == 0) return 0; var padding = padMultiple - remainder; return padding; } /// /// Returns the number of bytes needed to store a single pixel of the /// specified bit depth. /// public static int BytesFromBitDepth(int depth) { switch (depth) { case 1: case 8: return 1; case 16: return 2; case 32: return 4; default: throw new ArgumentException("Invalid bit depth."); } } public static short MinChannelCount(this PsdColorMode colorMode) { switch (colorMode) { case PsdColorMode.Bitmap: case PsdColorMode.Duotone: case PsdColorMode.Grayscale: case PsdColorMode.Indexed: case PsdColorMode.Multichannel: return 1; case PsdColorMode.Lab: case PsdColorMode.RGB: return 3; case PsdColorMode.CMYK: return 4; } throw new ArgumentException("Unknown color mode."); } /// /// Verify that the offset and count will remain within the bounds of the /// buffer. /// /// True if in bounds, false if out of bounds. public static bool CheckBufferBounds(byte[] data, int offset, int count) { if (offset < 0) return false; if (count < 0) return false; if (offset + count > data.Length) return false; return true; } public static void CheckByteArrayLength(long length) { if (length < 0) { throw new Exception("Byte array cannot have a negative length."); } if (length > 0x7fffffc7) { throw new OutOfMemoryException( "Byte array cannot exceed 2,147,483,591 in length."); } } /// /// Writes a message to the debug console, indicating the current position /// in the stream in both decimal and hexadecimal formats. /// [Conditional("DEBUG")] public static void DebugMessage(Stream stream, string message, params object[] args) { //var formattedMessage = String.Format(message, args); //Debug.WriteLine("0x{0:x}, {0}, {1}", //stream.Position, formattedMessage); } } /// /// Fixed-point decimal, with 16-bit integer and 16-bit fraction. /// internal class UFixed16_16 { public UInt16 Integer { get; set; } public UInt16 Fraction { get; set; } public UFixed16_16(UInt16 integer, UInt16 fraction) { Integer = integer; Fraction = fraction; } /// /// Split the high and low words of a 32-bit unsigned integer into a /// fixed-point number. /// public UFixed16_16(UInt32 value) { Integer = (UInt16)(value >> 16); Fraction = (UInt16)(value & 0x0000ffff); } public UFixed16_16(double value) { if (value >= 65536.0) throw new OverflowException(); if (value < 0) throw new OverflowException(); Integer = (UInt16)value; // Round instead of truncate, because doubles may not represent the // fraction exactly. Fraction = (UInt16)((value - Integer) * 65536 + 0.5); } public static implicit operator double(UFixed16_16 value) { return (double)value.Integer + value.Fraction / 65536.0; } } }