diff --git a/Default.aspx b/Default.aspx
new file mode 100755
index 0000000..5e50d74
--- /dev/null
+++ b/Default.aspx
@@ -0,0 +1,19 @@
+<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="GetPsdLayers.Default" %>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Web.config b/Web.config
new file mode 100755
index 0000000..8f044cd
--- /dev/null
+++ b/Web.config
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/bin/App_Code/1bpp.cs b/bin/App_Code/1bpp.cs
new file mode 100755
index 0000000..6bb4f97
--- /dev/null
+++ b/bin/App_Code/1bpp.cs
@@ -0,0 +1,227 @@
+using System;
+
+
+namespace PhotoshopFiles
+{
+ internal class ImageBitChanger
+ {
+ //static void Main(string[] args)
+ //{
+ // System.Drawing.Bitmap b = new System.Drawing.Bitmap("test.jpg");
+ // SplashImage(b, 0, 0);
+ // //
+ // DateTime dtFaq = DateTime.Now;
+ // System.Drawing.Bitmap b0 = CopyToBpp(b, 1);
+ // TimeSpan tsFaq = DateTime.Now - dtFaq;
+ // Console.WriteLine("GDI conversion time: " + tsFaq.ToString());
+ // SplashImage(b0, 200, 100);
+ // //
+ // DateTime dtLu = DateTime.Now;
+ // System.Drawing.Bitmap b1 = FaqCopyTo1bpp(b);
+ // TimeSpan tsLu = DateTime.Now - dtLu;
+ // Console.WriteLine("FAQ conversion time: " + tsLu.ToString());
+ // SplashImage(b1, 400, 200);
+ // //
+ // System.Threading.Thread.Sleep(1000);
+ // InvalidateRect(IntPtr.Zero, IntPtr.Zero, 1);
+ //}
+
+
+ ///
+ /// Copies a bitmap into a 1bpp/8bpp bitmap of the same dimensions, fast
+ ///
+ /// original bitmap
+ /// 1 or 8, target bpp
+ /// a 1bpp copy of the bitmap
+ internal static System.Drawing.Bitmap CopyToBpp(System.Drawing.Bitmap b, int bpp)
+ {
+ //System.Drawing.Bitmap b = new System.Drawing.Bitmap(img);
+
+ if (bpp != 1 && bpp != 8) throw new System.ArgumentException("1 or 8", "bpp");
+
+ // Plan: built into Windows GDI is the ability to convert
+ // bitmaps from one format to another. Most of the time, this
+ // job is actually done by the graphics hardware accelerator card
+ // and so is extremely fast. The rest of the time, the job is done by
+ // very fast native code.
+ // We will call into this GDI functionality from C#. Our plan:
+ // (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)
+ // (2) Create a GDI monochrome hbitmap
+ // (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)
+ // (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)
+
+ int w = b.Width, h = b.Height;
+ IntPtr hbm = b.GetHbitmap(); // this is step (1)
+ //
+ // Step (2): create the monochrome bitmap.
+ // "BITMAPINFO" is an interop-struct which we define below.
+ // In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs
+ BITMAPINFO bmi = new BITMAPINFO();
+ bmi.biSize = 40; // the size of the BITMAPHEADERINFO struct
+ bmi.biWidth = w;
+ bmi.biHeight = h;
+ bmi.biPlanes = 1; // "planes" are confusing. We always use just 1. Read MSDN for more info.
+ bmi.biBitCount = (short)bpp; // ie. 1bpp or 8bpp
+ bmi.biCompression = BI_RGB; // ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes
+ bmi.biSizeImage = (uint)(((w + 7) & 0xFFFFFFF8) * h / 8);
+ bmi.biXPelsPerMeter = 1000000; // not really important
+ bmi.biYPelsPerMeter = 1000000; // not really important
+ // Now for the colour table.
+ uint ncols = (uint)1 << bpp; // 2 colours for 1bpp; 256 colours for 8bpp
+ bmi.biClrUsed = ncols;
+ bmi.biClrImportant = ncols;
+ bmi.cols = new uint[256]; // The structure always has fixed size 256, even if we end up using fewer colours
+ if (bpp == 1) { bmi.cols[0] = MAKERGB(0, 0, 0); bmi.cols[1] = MAKERGB(255, 255, 255); }
+ else { for (int i = 0; i < ncols; i++) bmi.cols[i] = MAKERGB(i, i, i); }
+ // For 8bpp we've created an palette with just greyscale colours.
+ // You can set up any palette you want here. Here are some possibilities:
+ // greyscale: for (int i=0; i<256; i++) bmi.cols[i]=MAKERGB(i,i,i);
+ // rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new int[6]{0,51,102,153,204,255};
+ // for (int i=0; i<216; i++) bmi.cols[i]=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);
+ // optimal: a difficult topic: http://en.wikipedia.org/wiki/Color_quantization
+ //
+ // Now create the indexed bitmap "hbm0"
+ IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
+ IntPtr hbm0 = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
+ //
+ // Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap
+ // GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".
+ IntPtr sdc = GetDC(IntPtr.Zero); // First we obtain the DC for the screen
+ // Next, create a DC for the original hbitmap
+ IntPtr hdc = CreateCompatibleDC(sdc); SelectObject(hdc, hbm);
+ // and create a DC for the monochrome hbitmap
+ IntPtr hdc0 = CreateCompatibleDC(sdc); SelectObject(hdc0, hbm0);
+ // Now we can do the BitBlt:
+ BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
+ // Step (4): convert this monochrome hbitmap back into a Bitmap:
+ System.Drawing.Bitmap b0 = System.Drawing.Bitmap.FromHbitmap(hbm0);
+ //
+ // Finally some cleanup.
+ DeleteDC(hdc);
+ DeleteDC(hdc0);
+ ReleaseDC(IntPtr.Zero, sdc);
+ DeleteObject(hbm);
+ DeleteObject(hbm0);
+ //
+ return b0;
+ }
+
+ ///
+ /// Draws a bitmap onto the screen. Note: this will be overpainted
+ /// by other windows when they come to draw themselves. Only use it
+ /// if you want to draw something quickly and can't be bothered with forms.
+ ///
+ /// the bitmap to draw on the screen
+ /// x screen coordinate
+ /// y screen coordinate
+ static void SplashImage(System.Drawing.Bitmap b, int x, int y)
+ { // Drawing onto the screen is supported by GDI, but not by the Bitmap/Graphics class.
+ // So we use interop:
+ // (1) Copy the Bitmap into a GDI hbitmap
+ IntPtr hbm = b.GetHbitmap();
+ // (2) obtain the GDI equivalent of a "Graphics" for the screen
+ IntPtr sdc = GetDC(IntPtr.Zero);
+ // (3) obtain the GDI equivalent of a "Graphics" for the hbitmap
+ IntPtr hdc = CreateCompatibleDC(sdc);
+ SelectObject(hdc, hbm);
+ // (4) Draw from the hbitmap's "Graphics" onto the screen's "Graphics"
+ BitBlt(sdc, x, y, b.Width, b.Height, hdc, 0, 0, SRCCOPY);
+ // and do boring GDI cleanup:
+ DeleteDC(hdc);
+ ReleaseDC(IntPtr.Zero, sdc);
+ DeleteObject(hbm);
+ }
+
+
+ [System.Runtime.InteropServices.DllImport("gdi32.dll")]
+ public static extern bool DeleteObject(IntPtr hObject);
+
+ [System.Runtime.InteropServices.DllImport("user32.dll")]
+ public static extern int InvalidateRect(IntPtr hwnd, IntPtr rect, int bErase);
+
+ [System.Runtime.InteropServices.DllImport("user32.dll")]
+ public static extern IntPtr GetDC(IntPtr hwnd);
+
+ [System.Runtime.InteropServices.DllImport("gdi32.dll")]
+ public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
+
+ [System.Runtime.InteropServices.DllImport("user32.dll")]
+ public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
+
+ [System.Runtime.InteropServices.DllImport("gdi32.dll")]
+ public static extern int DeleteDC(IntPtr hdc);
+
+ [System.Runtime.InteropServices.DllImport("gdi32.dll")]
+ public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
+
+ [System.Runtime.InteropServices.DllImport("gdi32.dll")]
+ public static extern int BitBlt(IntPtr hdcDst, int xDst, int yDst, int w, int h, IntPtr hdcSrc, int xSrc, int ySrc, int rop);
+ static int SRCCOPY = 0x00CC0020;
+
+ [System.Runtime.InteropServices.DllImport("gdi32.dll")]
+ static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset);
+ static uint BI_RGB = 0;
+ static uint DIB_RGB_COLORS = 0;
+ [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
+ public struct BITMAPINFO
+ {
+ public uint biSize;
+ public int biWidth, biHeight;
+ public short biPlanes, biBitCount;
+ public uint biCompression, biSizeImage;
+ public int biXPelsPerMeter, biYPelsPerMeter;
+ public uint biClrUsed, biClrImportant;
+ [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 256)]
+ public uint[] cols;
+ }
+
+ static uint MAKERGB(int r, int g, int b)
+ {
+ return ((uint)(b & 255)) | ((uint)((r & 255) << 8)) | ((uint)((g & 255) << 16));
+ }
+
+
+ ///
+ /// Copies a bitmap into a 1bpp bitmap of the same dimensions, slowly, using code from Bob Powell's GDI+ faq http://www.bobpowell.net/onebit.htm
+ ///
+ /// original bitmap
+ /// a 1bpp copy of the bitmap
+ static System.Drawing.Bitmap FaqCopyTo1bpp(System.Drawing.Bitmap b)
+ {
+ int w = b.Width, h = b.Height; System.Drawing.Rectangle r = new System.Drawing.Rectangle(0, 0, w, h);
+ if (b.PixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
+ {
+ System.Drawing.Bitmap temp = new System.Drawing.Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
+ System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(temp);
+ g.DrawImage(b, r, 0, 0, w, h, System.Drawing.GraphicsUnit.Pixel);
+ g.Dispose(); b = temp;
+ }
+ System.Drawing.Imaging.BitmapData bdat = b.LockBits(r, System.Drawing.Imaging.ImageLockMode.ReadOnly, b.PixelFormat);
+ System.Drawing.Bitmap b0 = new System.Drawing.Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format1bppIndexed);
+ System.Drawing.Imaging.BitmapData b0dat = b0.LockBits(r, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format1bppIndexed);
+ for (int y = 0; y < h; y++)
+ {
+ for (int x = 0; x < w; x++)
+ {
+ int index = y * bdat.Stride + (x * 4);
+ if (System.Drawing.Color.FromArgb(System.Runtime.InteropServices.Marshal.ReadByte(bdat.Scan0, index + 2), System.Runtime.InteropServices.Marshal.ReadByte(bdat.Scan0, index + 1), System.Runtime.InteropServices.Marshal.ReadByte(bdat.Scan0, index)).GetBrightness() > 0.5f)
+ {
+ int index0 = y * b0dat.Stride + (x >> 3);
+ byte p = System.Runtime.InteropServices.Marshal.ReadByte(b0dat.Scan0, index0);
+ byte mask = (byte)(0x80 >> (x & 0x7));
+ System.Runtime.InteropServices.Marshal.WriteByte(b0dat.Scan0, index0, (byte)(p | mask));
+ }
+ }
+ }
+ b0.UnlockBits(b0dat);
+ b.UnlockBits(bdat);
+ return b0;
+ }
+
+
+ //internal static void CopyToBpp(string p, int p_2)
+ //{
+ // CopyToBpp(p, p_2);
+ //}
+ }
+}
\ No newline at end of file
diff --git a/bin/App_Code/AlphaChannels.cs b/bin/App_Code/AlphaChannels.cs
new file mode 100755
index 0000000..82a4273
--- /dev/null
+++ b/bin/App_Code/AlphaChannels.cs
@@ -0,0 +1,78 @@
+/////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2006, Frank Blumenberg
+//
+// See License.txt for complete licensing and attribution information.
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+/////////////////////////////////////////////////////////////////////////////////
+using System;
+using System.Collections.Generic;
+
+namespace PhotoshopFiles
+{
+ ///
+ /// The names of the alpha channels
+ ///
+ public class AlphaChannels : ImageResource
+ {
+ private List m_channelNames = new List();
+ public List ChannelNames
+ {
+ get { return m_channelNames; }
+ }
+
+ public AlphaChannels()
+ : base((short)ResourceIDs.AlphaChannelNames)
+ {
+ }
+
+ public AlphaChannels(ImageResource imgRes)
+ : base(imgRes)
+ {
+
+ BinaryReverseReader reader = imgRes.DataReader;
+ // the names are pascal strings without padding!!!
+ while ((reader.BaseStream.Length - reader.BaseStream.Position) > 0)
+ {
+ byte stringLength = reader.ReadByte();
+ string s = new string(reader.ReadChars(stringLength));
+ if (s.Length > 0)
+ m_channelNames.Add(s);
+ }
+ reader.Close();
+ }
+
+ protected override void StoreData()
+ {
+ System.IO.MemoryStream stream = new System.IO.MemoryStream();
+ BinaryReverseWriter writer = new BinaryReverseWriter(stream);
+
+ foreach (string name in m_channelNames)
+ {
+ writer.Write((byte)name.Length);
+ writer.Write(name.ToCharArray());
+ }
+
+ writer.Close();
+ stream.Close();
+
+ Data = stream.ToArray();
+ }
+ }
+}
\ No newline at end of file
diff --git a/bin/App_Code/BinaryReverseReader.cs b/bin/App_Code/BinaryReverseReader.cs
new file mode 100755
index 0000000..ae1ca40
--- /dev/null
+++ b/bin/App_Code/BinaryReverseReader.cs
@@ -0,0 +1,293 @@
+/////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2006, Frank Blumenberg
+//
+// See License.txt for complete licensing and attribution information.
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// This code is adapted from code in the Endogine sprite engine by Jonas Beckeman.
+// http://www.endogine.com/CS/
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.IO;
+
+namespace PhotoshopFiles
+{
+ ///
+ /// Reads primitive data types as binary values in in big-endian format
+ ///
+ public class BinaryReverseReader : BinaryReader
+ {
+ public BinaryReverseReader(Stream a_stream)
+ : base(a_stream)
+ {
+ }
+
+ public override short ReadInt16()
+ {
+ short val = base.ReadInt16();
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 2);
+ }
+ return val;
+ }
+ public override int ReadInt32()
+ {
+ int val = base.ReadInt32();
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 4);
+ }
+ return val;
+ }
+ public override long ReadInt64()
+ {
+ long val = base.ReadInt64();
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 8);
+ }
+ return val;
+ }
+
+ public override ushort ReadUInt16()
+ {
+ ushort val = base.ReadUInt16();
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 2);
+ }
+ return val;
+ }
+
+ public override uint ReadUInt32()
+ {
+ uint val = base.ReadUInt32();
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 4);
+ }
+ return val;
+ }
+
+ public override ulong ReadUInt64()
+ {
+ ulong val = base.ReadUInt64();
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 8);
+ }
+ return val;
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ public string ReadPascalString()
+ {
+ byte stringLength = base.ReadByte();
+
+ char[] c = base.ReadChars(stringLength);
+
+ if ((stringLength % 2) == 0)
+ base.ReadByte();
+
+ return new string(c);
+ }
+
+ //////////////////////////////////////////////////////////////////
+ unsafe protected void SwapBytes(byte* ptr, int nLength)
+ {
+ for (long i = 0; i < nLength / 2; ++i)
+ {
+ byte t = *(ptr + i);
+ *(ptr + i) = *(ptr + nLength - i - 1);
+ *(ptr + nLength - i - 1) = t;
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ ///
+ /// Writes primitive data types as binary values in in big-endian format
+ ///
+ public class BinaryReverseWriter : BinaryWriter
+ {
+ public BinaryReverseWriter(Stream a_stream)
+ : base(a_stream)
+ {
+ }
+
+ public bool AutoFlush;
+
+ public void WritePascalString(string s)
+ {
+ char[] c;
+ if (s.Length > 255)
+ c = s.Substring(0, 255).ToCharArray();
+ else
+ c = s.ToCharArray();
+
+ base.Write((byte)c.Length);
+ base.Write(c);
+
+ int realLength = c.Length + 1;
+
+ if ((realLength % 2) == 0)
+ return;
+
+ for (int i = 0; i < (2 - (realLength % 2)); i++)
+ base.Write((byte)0);
+
+ if (AutoFlush)
+ Flush();
+ }
+
+ public override void Write(short val)
+ {
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 2);
+ }
+ base.Write(val);
+
+ if (AutoFlush)
+ Flush();
+ }
+ public override void Write(int val)
+ {
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 4);
+ }
+ base.Write(val);
+
+ if (AutoFlush)
+ Flush();
+ }
+ public override void Write(long val)
+ {
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 8);
+ }
+ base.Write(val);
+
+ if (AutoFlush)
+ Flush();
+ }
+
+ public override void Write(ushort val)
+ {
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 2);
+ }
+ base.Write(val);
+
+ if (AutoFlush)
+ Flush();
+ }
+
+ public override void Write(uint val)
+ {
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 4);
+ }
+ base.Write(val);
+
+ if (AutoFlush)
+ Flush();
+ }
+
+ public override void Write(ulong val)
+ {
+ unsafe
+ {
+ this.SwapBytes((byte*)&val, 8);
+ }
+ base.Write(val);
+
+ if (AutoFlush)
+ Flush();
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ unsafe protected void SwapBytes(byte* ptr, int nLength)
+ {
+ for (long i = 0; i < nLength / 2; ++i)
+ {
+ byte t = *(ptr + i);
+ *(ptr + i) = *(ptr + nLength - i - 1);
+ *(ptr + nLength - i - 1) = t;
+ }
+ }
+ }
+
+
+ class LengthWriter : IDisposable
+ {
+ long m_lengthPosition = long.MinValue;
+ long m_startPosition;
+ BinaryReverseWriter m_writer;
+
+ public LengthWriter(BinaryReverseWriter writer)
+ {
+ m_writer = writer;
+
+ // we will write the correct length later, so remember
+ // the position
+ m_lengthPosition = m_writer.BaseStream.Position;
+ m_writer.Write((uint)0xFEEDFEED);
+
+ // remember the start position for calculation Image
+ // resources length
+ m_startPosition = m_writer.BaseStream.Position;
+ }
+
+ public void Write()
+ {
+ if (m_lengthPosition != long.MinValue)
+ {
+ long endPosition = m_writer.BaseStream.Position;
+
+ m_writer.BaseStream.Position = m_lengthPosition;
+ long length = endPosition - m_startPosition;
+ m_writer.Write((uint)length);
+ m_writer.BaseStream.Position = endPosition;
+
+ m_lengthPosition = long.MinValue;
+ }
+ }
+
+ public void Dispose()
+ {
+ Write();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/bin/App_Code/ImageDecoderBitmaps.cs b/bin/App_Code/ImageDecoderBitmaps.cs
new file mode 100755
index 0000000..442337d
--- /dev/null
+++ b/bin/App_Code/ImageDecoderBitmaps.cs
@@ -0,0 +1,509 @@
+/////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2006, Frank Blumenberg
+//
+// See License.txt for complete licensing and attribution information.
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// This code contains code from SimplePsd class library by Igor Tolmachev.
+// http://www.codeproject.com/csharp/simplepsd.asp
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace PhotoshopFiles
+{
+ public class ImageDecoder
+ {
+ public ImageDecoder()
+ {
+
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !TEST
+ private struct PixelData
+ {
+ public byte Blue;
+ public byte Green;
+ public byte Red;
+ public byte Alpha;
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public static Bitmap DecodeImage(PsdFile psdFile)
+ {
+ Bitmap bitmap = new Bitmap(psdFile.Columns, psdFile.Rows, PixelFormat.Format32bppArgb);
+
+#if TEST
+ for (int y = 0; y < psdFile.Rows; y++)
+ {
+ int rowIndex = y * psdFile.Columns;
+
+ for (int x = 0; x < psdFile.Columns; x++)
+ {
+ int pos = rowIndex + x;
+
+ Color pixelColor=GetColor(psdFile,pos);
+
+ bitmap.SetPixel(x, y, pixelColor);
+ }
+ }
+
+#else
+
+ Rectangle r = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
+ BitmapData bd = bitmap.LockBits(r, ImageLockMode.ReadWrite, bitmap.PixelFormat);
+
+ unsafe
+ {
+ byte* pCurrRowPixel = (byte*)bd.Scan0.ToPointer();
+
+ for (int y = 0; y < psdFile.Rows; y++)
+ {
+ int rowIndex = y * psdFile.Columns;
+ PixelData* pCurrPixel = (PixelData*)pCurrRowPixel;
+ for (int x = 0; x < psdFile.Columns; x++)
+ {
+ int pos = rowIndex + x;
+
+ Color pixelColor = GetColor(psdFile, pos);
+
+ pCurrPixel->Alpha = 255;
+ pCurrPixel->Red = pixelColor.R;
+ pCurrPixel->Green = pixelColor.G;
+ pCurrPixel->Blue = pixelColor.B;
+
+ pCurrPixel += 1;
+ }
+ pCurrRowPixel += bd.Stride;
+ }
+ }
+
+ bitmap.UnlockBits(bd);
+#endif
+
+ return bitmap;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static Color GetColor(PsdFile psdFile, int pos)
+ {
+ Color c = Color.White;
+
+ switch (psdFile.ColorMode)
+ {
+ case PsdFile.ColorModes.RGB:
+ c = Color.FromArgb(psdFile.ImageData[0][pos],
+ psdFile.ImageData[1][pos],
+ psdFile.ImageData[2][pos]);
+ break;
+ case PsdFile.ColorModes.CMYK:
+ c = CMYKToRGB(psdFile.ImageData[0][pos],
+ psdFile.ImageData[1][pos],
+ psdFile.ImageData[2][pos],
+ psdFile.ImageData[3][pos]);
+ break;
+ case PsdFile.ColorModes.Multichannel:
+ c = CMYKToRGB(psdFile.ImageData[0][pos],
+ psdFile.ImageData[1][pos],
+ psdFile.ImageData[2][pos],
+ 0);
+ break;
+ case PsdFile.ColorModes.Grayscale:
+ case PsdFile.ColorModes.Duotone:
+ c = Color.FromArgb(psdFile.ImageData[0][pos],
+ psdFile.ImageData[0][pos],
+ psdFile.ImageData[0][pos]);
+ break;
+ case PsdFile.ColorModes.Indexed:
+ {
+ int index = (int)psdFile.ImageData[0][pos];
+ c = Color.FromArgb((int)psdFile.ColorModeData[index],
+ psdFile.ColorModeData[index + 256],
+ psdFile.ColorModeData[index + 2 * 256]);
+ }
+ break;
+ case PsdFile.ColorModes.Lab:
+ {
+ c = LabToRGB(psdFile.ImageData[0][pos],
+ psdFile.ImageData[1][pos],
+ psdFile.ImageData[2][pos]);
+ }
+ break;
+ }
+
+ return c;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public static Bitmap DecodeImage(Layer layer)
+ {
+ if (layer.Rect.Width == 0 || layer.Rect.Height == 0)
+ {
+ return null;
+ }
+
+ Bitmap bitmap = new Bitmap(layer.Rect.Width, layer.Rect.Height, PixelFormat.Format32bppArgb);
+
+#if TEST
+ for (int y = 0; y < layer.Rect.Height; y++)
+ {
+ int rowIndex = y * layer.Rect.Width;
+
+ for (int x = 0; x < layer.Rect.Width; x++)
+ {
+ int pos = rowIndex + x;
+
+ //Color pixelColor=GetColor(psdFile,pos);
+ Color pixelColor = Color.FromArgb(x % 255, Color.ForestGreen);// 255, 128, 0);
+
+ bitmap.SetPixel(x, y, pixelColor);
+ }
+ }
+
+#else
+
+ Rectangle r = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
+ BitmapData bd = bitmap.LockBits(r, ImageLockMode.ReadWrite, bitmap.PixelFormat);
+
+ unsafe
+ {
+ byte* pCurrRowPixel = (byte*)bd.Scan0.ToPointer();
+
+ for (int y = 0; y < layer.Rect.Height; y++)
+ {
+ int rowIndex = y * layer.Rect.Width;
+ PixelData* pCurrPixel = (PixelData*)pCurrRowPixel;
+ for (int x = 0; x < layer.Rect.Width; x++)
+ {
+ int pos = rowIndex + x;
+
+ Color pixelColor = GetColor(layer, pos);
+
+ if (layer.SortedChannels.ContainsKey(-2))
+ {
+ int maskAlpha = GetColor(layer.MaskData, x, y);
+ int oldAlpha = pixelColor.A;
+
+ int newAlpha = (oldAlpha * maskAlpha) / 255;
+ pixelColor = Color.FromArgb(newAlpha, pixelColor);
+ }
+
+ pCurrPixel->Alpha = pixelColor.A;
+ pCurrPixel->Red = pixelColor.R;
+ pCurrPixel->Green = pixelColor.G;
+ pCurrPixel->Blue = pixelColor.B;
+
+ pCurrPixel += 1;
+ }
+ pCurrRowPixel += bd.Stride;
+ }
+ }
+
+ bitmap.UnlockBits(bd);
+#endif
+
+ return bitmap;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static Color GetColor(Layer layer, int pos)
+ {
+ Color c = Color.White;
+
+ switch (layer.PsdFile.ColorMode)
+ {
+ case PsdFile.ColorModes.RGB:
+ c = Color.FromArgb(layer.SortedChannels[0].ImageData[pos],
+ layer.SortedChannels[1].ImageData[pos],
+ layer.SortedChannels[2].ImageData[pos]);
+ break;
+ case PsdFile.ColorModes.CMYK:
+ c = CMYKToRGB(layer.SortedChannels[0].ImageData[pos],
+ layer.SortedChannels[1].ImageData[pos],
+ layer.SortedChannels[2].ImageData[pos],
+ layer.SortedChannels[3].ImageData[pos]);
+ break;
+ case PsdFile.ColorModes.Multichannel:
+ c = CMYKToRGB(layer.SortedChannels[0].ImageData[pos],
+ layer.SortedChannels[1].ImageData[pos],
+ layer.SortedChannels[2].ImageData[pos],
+ 0);
+ break;
+ case PsdFile.ColorModes.Grayscale:
+ case PsdFile.ColorModes.Duotone:
+ c = Color.FromArgb(layer.SortedChannels[0].ImageData[pos],
+ layer.SortedChannels[0].ImageData[pos],
+ layer.SortedChannels[0].ImageData[pos]);
+ break;
+ case PsdFile.ColorModes.Indexed:
+ {
+ int index = (int)layer.SortedChannels[0].ImageData[pos];
+ c = Color.FromArgb((int)layer.PsdFile.ColorModeData[index],
+ layer.PsdFile.ColorModeData[index + 256],
+ layer.PsdFile.ColorModeData[index + 2 * 256]);
+ }
+ break;
+ case PsdFile.ColorModes.Lab:
+ {
+ c = LabToRGB(layer.SortedChannels[0].ImageData[pos],
+ layer.SortedChannels[1].ImageData[pos],
+ layer.SortedChannels[2].ImageData[pos]);
+ }
+ break;
+ }
+
+ if (layer.SortedChannels.ContainsKey(-1))
+ c = Color.FromArgb(layer.SortedChannels[-1].ImageData[pos], c);
+
+ return c;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static int GetColor(Layer.Mask mask, int x, int y)
+ {
+ int c = 255;
+
+ if (mask.PositionIsRelative)
+ {
+ x -= mask.Rect.X;
+ y -= mask.Rect.Y;
+ }
+ else
+ {
+ x = (x + mask.Layer.Rect.X) - mask.Rect.X;
+ y = (y + mask.Layer.Rect.Y) - mask.Rect.Y;
+ }
+
+ if (y >= 0 && y < mask.Rect.Height &&
+ x >= 0 && x < mask.Rect.Width)
+ {
+ int pos = y * mask.Rect.Width + x;
+ if (pos < mask.ImageData.Length)
+ c = mask.ImageData[pos];
+ else
+ c = 255;
+ }
+
+ return c;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public static Bitmap DecodeImage(Layer.Mask mask)
+ {
+ Layer layer = mask.Layer;
+
+ if (mask.Rect.Width == 0 || mask.Rect.Height == 0)
+ {
+ return null;
+ }
+
+ Bitmap bitmap = new Bitmap(mask.Rect.Width, mask.Rect.Height, PixelFormat.Format32bppArgb);
+
+ Rectangle r = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
+ BitmapData bd = bitmap.LockBits(r, ImageLockMode.ReadWrite, bitmap.PixelFormat);
+
+ unsafe
+ {
+ byte* pCurrRowPixel = (byte*)bd.Scan0.ToPointer();
+
+ for (int y = 0; y < mask.Rect.Height; y++)
+ {
+ int rowIndex = y * mask.Rect.Width;
+ PixelData* pCurrPixel = (PixelData*)pCurrRowPixel;
+ for (int x = 0; x < mask.Rect.Width; x++)
+ {
+ int pos = rowIndex + x;
+
+ Color pixelColor = Color.FromArgb(mask.ImageData[pos], mask.ImageData[pos], mask.ImageData[pos]);
+
+ pCurrPixel->Alpha = 255;
+ pCurrPixel->Red = pixelColor.R;
+ pCurrPixel->Green = pixelColor.G;
+ pCurrPixel->Blue = pixelColor.B;
+
+ pCurrPixel += 1;
+ }
+ pCurrRowPixel += bd.Stride;
+ }
+ }
+
+ bitmap.UnlockBits(bd);
+
+ return bitmap;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static Color LabToRGB(byte lb, byte ab, byte bb)
+ {
+ double exL, exA, exB;
+
+ exL = (double)lb;
+ exA = (double)ab;
+ exB = (double)bb;
+
+ double L_coef, a_coef, b_coef;
+ L_coef = 256.0 / 100.0;
+ a_coef = 256.0 / 256.0;
+ b_coef = 256.0 / 256.0;
+
+ int L = (int)(exL / L_coef);
+ int a = (int)(exA / a_coef - 128.0);
+ int b = (int)(exB / b_coef - 128.0);
+
+ // For the conversion we first convert values to XYZ and then to RGB
+ // Standards used Observer = 2, Illuminant = D65
+
+ const double ref_X = 95.047;
+ const double ref_Y = 100.000;
+ const double ref_Z = 108.883;
+
+ double var_Y = ((double)L + 16.0) / 116.0;
+ double var_X = (double)a / 500.0 + var_Y;
+ double var_Z = var_Y - (double)b / 200.0;
+
+ if (Math.Pow(var_Y, 3) > 0.008856)
+ var_Y = Math.Pow(var_Y, 3);
+ else
+ var_Y = (var_Y - 16 / 116) / 7.787;
+
+ if (Math.Pow(var_X, 3) > 0.008856)
+ var_X = Math.Pow(var_X, 3);
+ else
+ var_X = (var_X - 16 / 116) / 7.787;
+
+ if (Math.Pow(var_Z, 3) > 0.008856)
+ var_Z = Math.Pow(var_Z, 3);
+ else
+ var_Z = (var_Z - 16 / 116) / 7.787;
+
+ double X = ref_X * var_X;
+ double Y = ref_Y * var_Y;
+ double Z = ref_Z * var_Z;
+
+ return XYZToRGB(X, Y, Z);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private static Color XYZToRGB(double X, double Y, double Z)
+ {
+ // Standards used Observer = 2, Illuminant = D65
+ // ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883
+
+ double var_X = X / 100.0;
+ double var_Y = Y / 100.0;
+ double var_Z = Z / 100.0;
+
+ double var_R = var_X * 3.2406 + var_Y * (-1.5372) + var_Z * (-0.4986);
+ double var_G = var_X * (-0.9689) + var_Y * 1.8758 + var_Z * 0.0415;
+ double var_B = var_X * 0.0557 + var_Y * (-0.2040) + var_Z * 1.0570;
+
+ if (var_R > 0.0031308)
+ var_R = 1.055 * (Math.Pow(var_R, 1 / 2.4)) - 0.055;
+ else
+ var_R = 12.92 * var_R;
+
+ if (var_G > 0.0031308)
+ var_G = 1.055 * (Math.Pow(var_G, 1 / 2.4)) - 0.055;
+ else
+ var_G = 12.92 * var_G;
+
+ if (var_B > 0.0031308)
+ var_B = 1.055 * (Math.Pow(var_B, 1 / 2.4)) - 0.055;
+ else
+ var_B = 12.92 * var_B;
+
+ int nRed = (int)(var_R * 256.0);
+ int nGreen = (int)(var_G * 256.0);
+ int nBlue = (int)(var_B * 256.0);
+
+ if (nRed < 0) nRed = 0;
+ else if (nRed > 255) nRed = 255;
+ if (nGreen < 0) nGreen = 0;
+ else if (nGreen > 255) nGreen = 255;
+ if (nBlue < 0) nBlue = 0;
+ else if (nBlue > 255) nBlue = 255;
+
+ return Color.FromArgb(nRed, nGreen, nBlue);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+ //
+ // The algorithms for these routines were taken from:
+ // http://www.neuro.sfc.keio.ac.jp/~aly/polygon/info/color-space-faq.html
+ //
+ // RGB --> CMYK CMYK --> RGB
+ // --------------------------------------- --------------------------------------------
+ // Black = minimum(1-Red,1-Green,1-Blue) Red = 1-minimum(1,Cyan*(1-Black)+Black)
+ // Cyan = (1-Red-Black)/(1-Black) Green = 1-minimum(1,Magenta*(1-Black)+Black)
+ // Magenta = (1-Green-Black)/(1-Black) Blue = 1-minimum(1,Yellow*(1-Black)+Black)
+ // Yellow = (1-Blue-Black)/(1-Black)
+ //
+
+ private static Color CMYKToRGB(byte c, byte m, byte y, byte k)
+ {
+ double C, M, Y, K;
+
+ double exC, exM, exY, exK;
+ double dMaxColours = Math.Pow(2, 8);
+
+ exC = (double)c;
+ exM = (double)m;
+ exY = (double)y;
+ exK = (double)k;
+
+ C = (1.0 - exC / dMaxColours);
+ M = (1.0 - exM / dMaxColours);
+ Y = (1.0 - exY / dMaxColours);
+ K = (1.0 - exK / dMaxColours);
+
+ int nRed = (int)((1.0 - (C * (1 - K) + K)) * 255);
+ int nGreen = (int)((1.0 - (M * (1 - K) + K)) * 255);
+ int nBlue = (int)((1.0 - (Y * (1 - K) + K)) * 255);
+
+ if (nRed < 0) nRed = 0;
+ else if (nRed > 255) nRed = 255;
+ if (nGreen < 0) nGreen = 0;
+ else if (nGreen > 255) nGreen = 255;
+ if (nBlue < 0) nBlue = 0;
+ else if (nBlue > 255) nBlue = 255;
+
+ return Color.FromArgb(nRed, nGreen, nBlue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/bin/App_Code/ImageResource.cs b/bin/App_Code/ImageResource.cs
new file mode 100755
index 0000000..c01c425
--- /dev/null
+++ b/bin/App_Code/ImageResource.cs
@@ -0,0 +1,219 @@
+/////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2006, Frank Blumenberg
+//
+// See License.txt for complete licensing and attribution information.
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// This code is adapted from code in the Endogine sprite engine by Jonas Beckeman.
+// http://www.endogine.com/CS/
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Diagnostics;
+
+namespace PhotoshopFiles
+{
+
+ public enum ResourceIDs
+ {
+ Undefined = 0,
+ MacPrintInfo = 1001,
+ ResolutionInfo = 1005,
+ AlphaChannelNames = 1006,
+ DisplayInfo = 1007,
+ Caption = 1008,
+ BorderInfo = 1009,
+ BgColor = 1010,
+ PrintFlags = 1011,
+ MultiChannelHalftoneInfo = 1012,
+ ColorHalftoneInfo = 1013,
+ DuotoneHalftoneInfo = 1014,
+ MultiChannelTransferFunctions = 1015,
+ ColorTransferFunctions = 1016,
+ DuotoneTransferFunctions = 1017,
+ DuotoneImageInfo = 1018,
+ BlackWhiteRange = 1019,
+ EPSOptions = 1021,
+ QuickMaskInfo = 1022, //2 bytes containing Quick Mask channel ID, 1 byte boolean indicating whether the mask was initially empty.
+ LayerStateInfo = 1024, //2 bytes containing the index of target layer. 0=bottom layer.
+ WorkingPathUnsaved = 1025,
+ LayersGroupInfo = 1026, //2 bytes per layer containing a group ID for the dragging groups. Layers in a group have the same group ID.
+ IPTC_NAA = 1028,
+ RawFormatImageMode = 1029,
+ JPEGQuality = 1030,
+ GridGuidesInfo = 1032,
+ Thumbnail1 = 1033,
+ CopyrightInfo = 1034,
+ URL = 1035,
+ Thumbnail2 = 1036,
+ GlobalAngle = 1037,
+ ColorSamplers = 1038,
+ ICCProfile = 1039, //The raw bytes of an ICC format profile, see the ICC34.pdf and ICC34.h files from the Internation Color Consortium located in the documentation section
+ Watermark = 1040,
+ ICCUntagged = 1041, //1 byte that disables any assumed profile handling when opening the file. 1 = intentionally untagged.
+ EffectsVisible = 1042, //1 byte global flag to show/hide all the effects layer. Only present when they are hidden.
+ SpotHalftone = 1043, // 4 bytes for version, 4 bytes for length, and the variable length data.
+ DocumentSpecific = 1044,
+ UnicodeAlphaNames = 1045, // 4 bytes for length and the string as a unicode string
+ IndexedColorTableCount = 1046, // 2 bytes for the number of colors in table that are actually defined
+ TransparentIndex = 1047,
+ GlobalAltitude = 1049, // 4 byte entry for altitude
+ Slices = 1050,
+ WorkflowURL = 1051, //Unicode string, 4 bytes of length followed by unicode string
+ JumpToXPEP = 1052, //2 bytes major version, 2 bytes minor version,
+ //4 bytes count. Following is repeated for count: 4 bytes block size,
+ //4 bytes key, if key = 'jtDd' then next is a Boolean for the dirty flag
+ //otherwise it’s a 4 byte entry for the mod date
+ AlphaIdentifiers = 1053, //4 bytes of length, followed by 4 bytes each for every alpha identifier.
+ URLList = 1054, //4 byte count of URLs, followed by 4 byte long, 4 byte ID, and unicode string for each count.
+ VersionInfo = 1057, //4 byte version, 1 byte HasRealMergedData, unicode string of writer name, unicode string of reader name, 4 bytes of file version.
+ Unknown4 = 1058, //pretty long, 302 bytes in one file. Holds creation date, maybe Photoshop license number
+ XMLInfo = 1060, //some kind of XML definition of file. The xpacket tag seems to hold binary data
+ Unknown = 1061, //seems to be common!
+ Unknown2 = 1062, //seems to be common!
+ Unknown3 = 1064, //seems to be common!
+ PathInfo = 2000, //2000-2999 actually I think?
+ ClippingPathName = 2999,
+ PrintFlagsInfo = 10000
+ }
+
+
+ ///
+ /// Summary description for ImageResource.
+ ///
+ public class ImageResource
+ {
+ private short m_id;
+ public short ID
+ {
+ get { return m_id; }
+ set { m_id = value; }
+ }
+
+ private string m_name = String.Empty;
+ public string Name
+ {
+ get { return m_name; }
+ set { m_name = value; }
+ }
+
+ private byte[] m_data;
+ public byte[] Data
+ {
+ get { return m_data; }
+ set { m_data = value; }
+ }
+
+ private string m_osType = String.Empty;
+
+ public string OSType
+ {
+ get { return m_osType; }
+ set { m_osType = value; }
+ }
+
+ public ImageResource()
+ {
+ }
+
+ public ImageResource(short id)
+ {
+ m_id = id;
+ }
+
+ public ImageResource(ImageResource imgRes)
+ {
+ m_id = imgRes.m_id;
+ m_name = imgRes.m_name;
+
+ m_data = new byte[imgRes.m_data.Length];
+ imgRes.m_data.CopyTo(m_data, 0);
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ public ImageResource(BinaryReverseReader reader)
+ {
+ m_osType = new string(reader.ReadChars(4));
+ if (m_osType != "8BIM" && m_osType != "MeSa")
+ {
+ throw new InvalidOperationException("Could not read an image resource");
+ }
+
+ m_id = reader.ReadInt16();
+ m_name = reader.ReadPascalString();
+
+ uint settingLength = reader.ReadUInt32();
+ m_data = reader.ReadBytes((int)settingLength);
+
+ if (reader.BaseStream.Position % 2 == 1)
+ reader.ReadByte();
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ public void Save(BinaryReverseWriter writer)
+ {
+ StoreData();
+
+ if (m_osType == String.Empty)
+ m_osType = "8BIM";
+
+ writer.Write(m_osType.ToCharArray());
+ writer.Write(m_id);
+
+ writer.WritePascalString(m_name);
+
+ writer.Write((int)m_data.Length);
+ writer.Write(m_data);
+
+ if (writer.BaseStream.Position % 2 == 1)
+ writer.Write((byte)0);
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ protected virtual void StoreData()
+ {
+
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ public BinaryReverseReader DataReader
+ {
+ get
+ {
+ return new BinaryReverseReader(new System.IO.MemoryStream(this.m_data));
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ public override string ToString()
+ {
+ return String.Format("{0} {1}", (ResourceIDs)m_id, m_name);
+ }
+ }
+}
\ No newline at end of file
diff --git a/bin/App_Code/Layer.cs b/bin/App_Code/Layer.cs
new file mode 100755
index 0000000..a7d7cd2
--- /dev/null
+++ b/bin/App_Code/Layer.cs
@@ -0,0 +1,968 @@
+/////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2006, Frank Blumenberg
+//
+// See License.txt for complete licensing and attribution information.
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// This code contains code from the Endogine sprite engine by Jonas Beckeman.
+// http://www.endogine.com/CS/
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Text;
+using System.Drawing;
+using System.IO;
+using System.Diagnostics;
+
+namespace PhotoshopFiles
+{
+ public class Layer
+ {
+ ///////////////////////////////////////////////////////////////////////////
+
+ public class Channel
+ {
+ private Layer m_layer;
+ ///
+ /// The layer to which this channel belongs
+ ///
+ public Layer Layer
+ {
+ get { return m_layer; }
+ }
+
+
+ private short m_id;
+ ///
+ /// 0 = red, 1 = green, etc.
+ /// 1 = transparency mask
+ /// 2 = user supplied layer mask
+ ///
+ public short ID
+ {
+ get { return m_id; }
+ set { m_id = value; }
+ }
+
+ ///
+ /// The length of the compressed channel data.
+ ///
+ public int Length;
+
+ private byte[] m_data;
+ ///
+ /// The compressed raw channel data
+ ///
+ public byte[] Data
+ {
+ get { return m_data; }
+ set { m_data = value; }
+ }
+
+ ///
+ /// The raw image data from the channel.
+ ///
+ public byte[] m_imageData;
+
+ public byte[] ImageData
+ {
+ get { return m_imageData; }
+ set { m_imageData = value; }
+ }
+
+ private ImageCompression m_imageCompression;
+ public ImageCompression ImageCompression
+ {
+ get { return m_imageCompression; }
+ set { m_imageCompression = value; }
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ internal Channel(short id, Layer layer)
+ {
+ m_id = id;
+ m_layer = layer;
+ m_layer.Channels.Add(this);
+ m_layer.SortedChannels.Add(this.ID, this);
+ }
+
+ internal Channel(BinaryReverseReader reader, Layer layer)
+ {
+ Debug.WriteLine("Channel started at " + reader.BaseStream.Position.ToString());
+
+ m_id = reader.ReadInt16();
+ Length = reader.ReadInt32();
+
+ m_layer = layer;
+ }
+
+ internal void Save(BinaryReverseWriter writer)
+ {
+ Debug.WriteLine("Channel Save started at " + writer.BaseStream.Position.ToString());
+
+ writer.Write(m_id);
+
+ CompressImageData();
+
+ writer.Write(Data.Length + 2); // 2 bytes for the image compression
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ internal void LoadPixelData(BinaryReverseReader reader)
+ {
+ Debug.WriteLine("Channel.LoadPixelData started at " + reader.BaseStream.Position.ToString());
+
+ m_data = reader.ReadBytes((int)Length);
+
+ using (BinaryReverseReader readerImg = DataReader)
+ {
+ m_imageCompression = (ImageCompression)readerImg.ReadInt16();
+
+ int bytesPerRow = 0;
+
+ switch (m_layer.PsdFile.Depth)
+ {
+ case 1:
+ bytesPerRow = m_layer.m_rect.Width;//NOT Shure
+ break;
+ case 8:
+ bytesPerRow = m_layer.m_rect.Width;
+ break;
+ case 16:
+ bytesPerRow = m_layer.m_rect.Width * 2;
+ break;
+ }
+
+ m_imageData = new byte[m_layer.m_rect.Height * bytesPerRow];
+
+ switch (m_imageCompression)
+ {
+ case ImageCompression.Raw:
+ readerImg.Read(m_imageData, 0, m_imageData.Length);
+ break;
+ case ImageCompression.Rle:
+ {
+ int[] rowLenghtList = new int[m_layer.m_rect.Height];
+ for (int i = 0; i < rowLenghtList.Length; i++)
+ rowLenghtList[i] = readerImg.ReadInt16();
+
+ for (int i = 0; i < m_layer.m_rect.Height; i++)
+ {
+ int rowIndex = i * m_layer.m_rect.Width;
+ RleHelper.DecodedRow(readerImg.BaseStream, m_imageData, rowIndex, bytesPerRow);
+
+ //if (rowLenghtList[i] % 2 == 1)
+ // readerImg.ReadByte();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private void CompressImageData()
+ {
+ if (m_imageCompression == ImageCompression.Rle)
+ {
+ MemoryStream dataStream = new MemoryStream();
+ BinaryReverseWriter writer = new BinaryReverseWriter(dataStream);
+
+ // we will write the correct lengths later, so remember
+ // the position
+ long lengthPosition = writer.BaseStream.Position;
+
+ int[] rleRowLenghs = new int[m_layer.m_rect.Height];
+
+ if (m_imageCompression == ImageCompression.Rle)
+ {
+ for (int i = 0; i < rleRowLenghs.Length; i++)
+ {
+ writer.Write((short)0x1234);
+ }
+ }
+
+ //---------------------------------------------------------------
+
+ int bytesPerRow = 0;
+
+ switch (m_layer.PsdFile.Depth)
+ {
+ case 1:
+ bytesPerRow = m_layer.m_rect.Width;//NOT Shure
+ break;
+ case 8:
+ bytesPerRow = m_layer.m_rect.Width;
+ break;
+ case 16:
+ bytesPerRow = m_layer.m_rect.Width * 2;
+ break;
+ }
+
+ //---------------------------------------------------------------
+
+ for (int row = 0; row < m_layer.m_rect.Height; row++)
+ {
+ int rowIndex = row * m_layer.m_rect.Width;
+ rleRowLenghs[row] = RleHelper.EncodedRow(writer.BaseStream, m_imageData, rowIndex, bytesPerRow);
+ }
+
+ //---------------------------------------------------------------
+
+ long endPosition = writer.BaseStream.Position;
+
+ writer.BaseStream.Position = lengthPosition;
+
+ for (int i = 0; i < rleRowLenghs.Length; i++)
+ {
+ writer.Write((short)rleRowLenghs[i]);
+ }
+
+ writer.BaseStream.Position = endPosition;
+
+ dataStream.Close();
+
+ m_data = dataStream.ToArray();
+
+ dataStream.Dispose();
+
+ }
+ else
+ {
+ m_data = (byte[])m_imageData.Clone();
+ }
+ }
+
+ internal void SavePixelData(BinaryReverseWriter writer)
+ {
+ Debug.WriteLine("Channel SavePixelData started at " + writer.BaseStream.Position.ToString());
+
+ writer.Write((short)m_imageCompression);
+ writer.Write(m_imageData);
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ public BinaryReverseReader DataReader
+ {
+ get
+ {
+ if (m_data == null)
+ return null;
+
+ return new BinaryReverseReader(new System.IO.MemoryStream(this.m_data));
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public class Mask
+ {
+ private Layer m_layer;
+ ///
+ /// The layer to which this mask belongs
+ ///
+ public Layer Layer
+ {
+ get { return m_layer; }
+ }
+
+ private Rectangle m_rect = Rectangle.Empty;
+ ///
+ /// The rectangle enclosing the mask.
+ ///
+ public Rectangle Rect
+ {
+ get { return m_rect; }
+ set { m_rect = value; }
+ }
+
+ private byte m_defaultColor;
+ public byte DefaultColor
+ {
+ get { return m_defaultColor; }
+ set { m_defaultColor = value; }
+ }
+
+
+ private static int m_positionIsRelativeBit = BitVector32.CreateMask();
+ private static int m_disabledBit = BitVector32.CreateMask(m_positionIsRelativeBit);
+ private static int m_invertOnBlendBit = BitVector32.CreateMask(m_disabledBit);
+
+ private BitVector32 m_flags = new BitVector32();
+ ///
+ /// If true, the position of the mask is relative to the layer.
+ ///
+ public bool PositionIsRelative
+ {
+ get
+ {
+ return m_flags[m_positionIsRelativeBit];
+ }
+ set
+ {
+ m_flags[m_positionIsRelativeBit] = value;
+ }
+ }
+
+ public bool Disabled
+ {
+ get { return m_flags[m_disabledBit]; }
+ set { m_flags[m_disabledBit] = value; }
+ }
+
+ ///
+ /// if true, invert the mask when blending.
+ ///
+ public bool InvertOnBlendBit
+ {
+ get { return m_flags[m_invertOnBlendBit]; }
+ set { m_flags[m_invertOnBlendBit] = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ internal Mask(Layer layer)
+ {
+ m_layer = layer;
+ m_layer.MaskData = this;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ internal Mask(BinaryReverseReader reader, Layer layer)
+ {
+ Debug.WriteLine("Mask started at " + reader.BaseStream.Position.ToString());
+
+ m_layer = layer;
+
+ uint maskLength = reader.ReadUInt32();
+
+ if (maskLength <= 0)
+ return;
+
+ long startPosition = reader.BaseStream.Position;
+
+ //-----------------------------------------------------------------------
+
+ m_rect = new Rectangle();
+ m_rect.Y = reader.ReadInt32();
+ m_rect.X = reader.ReadInt32();
+ m_rect.Height = reader.ReadInt32() - m_rect.Y;
+ m_rect.Width = reader.ReadInt32() - m_rect.X;
+
+ m_defaultColor = reader.ReadByte();
+
+ //-----------------------------------------------------------------------
+
+ byte flags = reader.ReadByte();
+ m_flags = new BitVector32(flags);
+
+ //-----------------------------------------------------------------------
+
+ if (maskLength == 36)
+ {
+ BitVector32 realFlags = new BitVector32(reader.ReadByte());
+
+ byte realUserMaskBackground = reader.ReadByte();
+
+ Rectangle rect = new Rectangle();
+ rect.Y = reader.ReadInt32();
+ rect.X = reader.ReadInt32();
+ rect.Height = reader.ReadInt32() - m_rect.Y;
+ rect.Width = reader.ReadInt32() - m_rect.X;
+ }
+
+
+ // there is other stuff following, but we will ignore this.
+ reader.BaseStream.Position = startPosition + maskLength;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void Save(BinaryReverseWriter writer)
+ {
+ Debug.WriteLine("Mask Save started at " + writer.BaseStream.Position.ToString());
+
+ if (m_rect.IsEmpty)
+ {
+ writer.Write((uint)0);
+ return;
+ }
+
+ using (new LengthWriter(writer))
+ {
+ writer.Write(m_rect.Top);
+ writer.Write(m_rect.Left);
+ writer.Write(m_rect.Bottom);
+ writer.Write(m_rect.Right);
+
+ writer.Write(m_defaultColor);
+
+ writer.Write((byte)m_flags.Data);
+
+ // padding 2 bytes so that size is 20
+ writer.Write((int)0);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ ///
+ /// The raw image data from the channel.
+ ///
+ public byte[] m_imageData;
+
+ public byte[] ImageData
+ {
+ get { return m_imageData; }
+ set { m_imageData = value; }
+ }
+
+ internal void LoadPixelData(BinaryReverseReader reader)
+ {
+ Debug.WriteLine("Mask.LoadPixelData started at " + reader.BaseStream.Position.ToString());
+
+ if (m_rect.IsEmpty || m_layer.SortedChannels.ContainsKey(-2) == false)
+ return;
+
+ Channel maskChannel = m_layer.SortedChannels[-2];
+
+
+ maskChannel.Data = reader.ReadBytes((int)maskChannel.Length);
+
+
+ using (BinaryReverseReader readerImg = maskChannel.DataReader)
+ {
+ maskChannel.ImageCompression = (ImageCompression)readerImg.ReadInt16();
+
+ int bytesPerRow = 0;
+
+ switch (m_layer.PsdFile.Depth)
+ {
+ case 1:
+ bytesPerRow = m_rect.Width;//NOT Shure
+ break;
+ case 8:
+ bytesPerRow = m_rect.Width;
+ break;
+ case 16:
+ bytesPerRow = m_rect.Width * 2;
+ break;
+ }
+
+ maskChannel.ImageData = new byte[m_rect.Height * bytesPerRow];
+ // Fill Array
+ for (int i = 0; i < maskChannel.ImageData.Length; i++)
+ {
+ maskChannel.ImageData[i] = 0xAB;
+ }
+
+ m_imageData = (byte[])maskChannel.ImageData.Clone();
+
+ switch (maskChannel.ImageCompression)
+ {
+ case ImageCompression.Raw:
+ readerImg.Read(maskChannel.ImageData, 0, maskChannel.ImageData.Length);
+ break;
+ case ImageCompression.Rle:
+ {
+ int[] rowLenghtList = new int[m_rect.Height];
+
+ for (int i = 0; i < rowLenghtList.Length; i++)
+ rowLenghtList[i] = readerImg.ReadInt16();
+
+ for (int i = 0; i < m_rect.Height; i++)
+ {
+ int rowIndex = i * m_rect.Width;
+ RleHelper.DecodedRow(readerImg.BaseStream, maskChannel.ImageData, rowIndex, bytesPerRow);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ m_imageData = (byte[])maskChannel.ImageData.Clone();
+
+ }
+ }
+
+ internal void SavePixelData(BinaryReverseWriter writer)
+ {
+ //writer.Write(m_data);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public class BlendingRanges
+ {
+ private Layer m_layer;
+ ///
+ /// The layer to which this channel belongs
+ ///
+ public Layer Layer
+ {
+ get { return m_layer; }
+ }
+
+ private byte[] m_data = new byte[0];
+
+ public byte[] Data
+ {
+ get { return m_data; }
+ set { m_data = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public BlendingRanges(Layer layer)
+ {
+ m_layer = layer;
+ m_layer.BlendingRangesData = this;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public BlendingRanges(BinaryReverseReader reader, Layer layer)
+ {
+ Debug.WriteLine("BlendingRanges started at " + reader.BaseStream.Position.ToString());
+
+ m_layer = layer;
+ int dataLength = reader.ReadInt32();
+ if (dataLength <= 0)
+ return;
+
+ m_data = reader.ReadBytes(dataLength);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void Save(BinaryReverseWriter writer)
+ {
+ Debug.WriteLine("BlendingRanges Save started at " + writer.BaseStream.Position.ToString());
+
+ writer.Write((uint)m_data.Length);
+ writer.Write(m_data);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public class AdjusmentLayerInfo
+ {
+ private Layer m_layer;
+ ///
+ /// The layer to which this info belongs
+ ///
+ internal Layer Layer
+ {
+ get { return m_layer; }
+ }
+
+ private string m_key;
+ public string Key
+ {
+ get { return m_key; }
+ set { m_key = value; }
+ }
+
+ private byte[] m_data;
+ public byte[] Data
+ {
+ get { return m_data; }
+ set { m_data = value; }
+ }
+
+ public AdjusmentLayerInfo(string key, Layer layer)
+ {
+ m_key = key;
+ m_layer = layer;
+ m_layer.AdjustmentInfo.Add(this);
+ }
+
+ public AdjusmentLayerInfo(BinaryReverseReader reader, Layer layer)
+ {
+ Debug.WriteLine("AdjusmentLayerInfo started at " + reader.BaseStream.Position.ToString());
+
+ m_layer = layer;
+
+ string signature = new string(reader.ReadChars(4));
+ if (signature != "8BIM")
+ {
+ throw new IOException("Could not read an image resource");
+ }
+
+ m_key = new string(reader.ReadChars(4));
+
+ uint dataLength = reader.ReadUInt32();
+ m_data = reader.ReadBytes((int)dataLength);
+ }
+
+ public void Save(BinaryReverseWriter writer)
+ {
+ Debug.WriteLine("AdjusmentLayerInfo Save started at " + writer.BaseStream.Position.ToString());
+
+ string signature = "8BIM";
+
+ writer.Write(signature.ToCharArray());
+ writer.Write(m_key.ToCharArray());
+ writer.Write((uint)m_data.Length);
+ writer.Write(m_data);
+ }
+
+ //////////////////////////////////////////////////////////////////
+
+ public BinaryReverseReader DataReader
+ {
+ get
+ {
+ return new BinaryReverseReader(new System.IO.MemoryStream(this.m_data));
+ }
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private PsdFile m_psdFile;
+ internal PsdFile PsdFile
+ {
+ get { return m_psdFile; }
+ }
+
+ private Rectangle m_rect = Rectangle.Empty;
+ ///
+ /// The rectangle containing the contents of the layer.
+ ///
+ public Rectangle Rect
+ {
+ get { return m_rect; }
+ set { m_rect = value; }
+ }
+
+
+ ///
+ /// Channel information.
+ ///
+ private List m_channels = new List();
+
+ public List Channels
+ {
+ get { return m_channels; }
+ }
+
+ private SortedList m_sortedChannels = new SortedList();
+ public SortedList SortedChannels
+ {
+ get
+ {
+ return m_sortedChannels;
+ }
+ }
+
+ private string m_blendModeKey = "norm";
+ ///
+ /// The blend mode key for the layer
+ ///
+ ///
+ ///
+ ///
+ /// normnormal
+ /// darkdarken
+ /// litelighten
+ /// hue hue
+ /// sat saturation
+ /// colrcolor
+ /// lum luminosity
+ /// mul multiply
+ /// scrnscreen
+ /// dissdissolve
+ /// overoverlay
+ /// hLithard light
+ /// sLitsoft light
+ /// diffdifference
+ /// smudexlusion
+ /// div color dodge
+ /// idivcolor burn
+ ///
+ ///
+ public string BlendModeKey
+ {
+ get { return m_blendModeKey; }
+ set
+ {
+ if (value.Length != 4) throw new ArgumentException("Key length must be 4");
+ }
+ }
+
+
+ private byte m_opacity;
+ ///
+ /// 0 = transparent ... 255 = opaque
+ ///
+ public byte Opacity
+ {
+ get { return m_opacity; }
+ set { m_opacity = value; }
+ }
+
+
+ private bool m_clipping;
+ ///
+ /// false = base, true = nonbase
+ ///
+ public bool Clipping
+ {
+ get { return m_clipping; }
+ set { m_clipping = value; }
+ }
+
+ private static int m_protectTransBit = BitVector32.CreateMask();
+ private static int m_visibleBit = BitVector32.CreateMask(m_protectTransBit);
+
+ BitVector32 m_flags = new BitVector32();
+
+ ///
+ /// If true, the layer is visible.
+ ///
+ public bool Visible
+ {
+ get { return !m_flags[m_visibleBit]; }
+ set { m_flags[m_visibleBit] = !value; }
+ }
+
+
+ ///
+ /// Protect the transparency
+ ///
+ public bool ProtectTrans
+ {
+ get { return m_flags[m_protectTransBit]; }
+ set { m_flags[m_protectTransBit] = value; }
+ }
+
+
+ private string m_name;
+ ///
+ /// The descriptive layer name
+ ///
+ public string Name
+ {
+ get { return m_name; }
+ set { m_name = value; }
+ }
+
+ private BlendingRanges m_blendingRangesData;
+ public Layer.BlendingRanges BlendingRangesData
+ {
+ get { return m_blendingRangesData; }
+ set { m_blendingRangesData = value; }
+ }
+
+ private Mask m_maskData;
+ public Layer.Mask MaskData
+ {
+ get { return m_maskData; }
+ set { m_maskData = value; }
+ }
+
+ private List m_adjustmentInfo = new List();
+ public List AdjustmentInfo
+ {
+ get { return m_adjustmentInfo; }
+ set { m_adjustmentInfo = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public Layer(PsdFile psdFile)
+ {
+ m_psdFile = psdFile;
+ m_psdFile.Layers.Add(this);
+ }
+
+ public Layer(BinaryReverseReader reader, PsdFile psdFile)
+ {
+ Debug.WriteLine("Layer started at " + reader.BaseStream.Position.ToString());
+
+ m_psdFile = psdFile;
+ m_rect = new Rectangle();
+ m_rect.Y = reader.ReadInt32();
+ m_rect.X = reader.ReadInt32();
+ m_rect.Height = reader.ReadInt32() - m_rect.Y;
+ m_rect.Width = reader.ReadInt32() - m_rect.X;
+
+ //-----------------------------------------------------------------------
+
+ int numberOfChannels = reader.ReadUInt16();
+ this.m_channels.Clear();
+ for (int channel = 0; channel < numberOfChannels; channel++)
+ {
+ Channel ch = new Channel(reader, this);
+ m_channels.Add(ch);
+ m_sortedChannels.Add(ch.ID, ch);
+ }
+
+ //-----------------------------------------------------------------------
+
+ string signature = new string(reader.ReadChars(4));
+ if (signature != "8BIM")
+ throw (new IOException("Layer Channelheader error!"));
+
+ m_blendModeKey = new string(reader.ReadChars(4));
+ m_opacity = reader.ReadByte();
+
+ m_clipping = reader.ReadByte() > 0;
+
+ //-----------------------------------------------------------------------
+
+ byte flags = reader.ReadByte();
+ m_flags = new BitVector32(flags);
+
+ //-----------------------------------------------------------------------
+
+ reader.ReadByte(); //padding
+
+ //-----------------------------------------------------------------------
+
+ Debug.WriteLine("Layer extraDataSize started at " + reader.BaseStream.Position.ToString());
+
+ // this is the total size of the MaskData, the BlendingRangesData, the
+ // Name and the AdjustmenLayerInfo
+ uint extraDataSize = reader.ReadUInt32();
+
+
+
+ // remember the start position for calculation of the
+ // AdjustmenLayerInfo size
+ long extraDataStartPosition = reader.BaseStream.Position;
+
+ m_maskData = new Mask(reader, this);
+ m_blendingRangesData = new BlendingRanges(reader, this);
+
+ //-----------------------------------------------------------------------
+
+ long namePosition = reader.BaseStream.Position;
+
+ m_name = reader.ReadPascalString();
+
+ int paddingBytes = (int)((reader.BaseStream.Position - namePosition) % 4);
+
+ Debug.Print("Layer {0} padding bytes after name", paddingBytes);
+ reader.ReadBytes(paddingBytes);
+
+ //-----------------------------------------------------------------------
+
+ m_adjustmentInfo.Clear();
+
+ long adjustmenLayerEndPos = extraDataStartPosition + extraDataSize;
+ while (reader.BaseStream.Position < adjustmenLayerEndPos)
+ {
+ try
+ {
+ m_adjustmentInfo.Add(new AdjusmentLayerInfo(reader, this));
+ }
+ catch
+ {
+ reader.BaseStream.Position = adjustmenLayerEndPos;
+ }
+ }
+
+
+ //-----------------------------------------------------------------------
+ // make shure we are not on a wrong offset, so set the stream position
+ // manually
+ reader.BaseStream.Position = adjustmenLayerEndPos;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void Save(BinaryReverseWriter writer)
+ {
+ Debug.WriteLine("Layer Save started at " + writer.BaseStream.Position.ToString());
+
+ writer.Write(m_rect.Top);
+ writer.Write(m_rect.Left);
+ writer.Write(m_rect.Bottom);
+ writer.Write(m_rect.Right);
+
+ //-----------------------------------------------------------------------
+
+ writer.Write((short)m_channels.Count);
+ foreach (Channel ch in m_channels)
+ ch.Save(writer);
+
+ //-----------------------------------------------------------------------
+
+ string signature = "8BIM";
+ writer.Write(signature.ToCharArray());
+ writer.Write(m_blendModeKey.ToCharArray());
+ writer.Write(m_opacity);
+ writer.Write((byte)(m_clipping ? 1 : 0));
+
+ writer.Write((byte)m_flags.Data);
+
+ //-----------------------------------------------------------------------
+
+ writer.Write((byte)0);
+
+ //-----------------------------------------------------------------------
+
+ using (new LengthWriter(writer))
+ {
+ m_maskData.Save(writer);
+ m_blendingRangesData.Save(writer);
+
+ long namePosition = writer.BaseStream.Position;
+
+ writer.WritePascalString(m_name);
+
+ int paddingBytes = (int)((writer.BaseStream.Position - namePosition) % 4);
+ Debug.Print("Layer {0} write padding bytes after name", paddingBytes);
+
+ for (int i = 0; i < paddingBytes; i++)
+ writer.Write((byte)0);
+
+ foreach (AdjusmentLayerInfo info in m_adjustmentInfo)
+ {
+ info.Save(writer);
+ }
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/bin/App_Code/PSDFile.cs b/bin/App_Code/PSDFile.cs
new file mode 100755
index 0000000..1182f71
--- /dev/null
+++ b/bin/App_Code/PSDFile.cs
@@ -0,0 +1,622 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using System.Diagnostics;
+
+namespace PhotoshopFiles
+{
+ public class PsdFile
+ {
+ public enum ColorModes
+ {
+ Bitmap = 0, Grayscale = 1, Indexed = 2, RGB = 3, CMYK = 4, Multichannel = 7, Duotone = 8, Lab = 9
+ };
+
+ #region "Properties and Variables"
+
+ ///
+ /// If ColorMode is ColorModes.Indexed, the following 768 bytes will contain
+ /// a 256-color palette. If the ColorMode is ColorModes.Duotone, the data
+ /// following presumably consists of screen parameters and other related information.
+ /// Unfortunately, it is intentionally not documented by Adobe, and non-Photoshop
+ /// readers are advised to treat duotone images as gray-scale images.
+ ///
+ public byte[] ColorModeData = new byte[0];
+
+ //Masking data for the PSD
+ byte[] GlobalLayerMaskData = new byte[0];
+
+ ///
+ /// Always equal to 1.
+ ///
+ private short m_version = 1;
+ public short Version
+ {
+ get { return m_version; }
+ }
+
+ private short m_channels;
+ ///
+ /// The number of channels in the image, including any alpha channels.
+ /// Supported range is 1 to 24.
+ ///
+ public short Channels
+ {
+ get { return m_channels; }
+ set
+ {
+ if (value < 1 || value > 24)
+ throw new ArgumentException("Supported range is 1 to 24");
+ m_channels = value;
+ }
+ }
+
+
+ private int m_rows;
+ ///
+ /// The height of the image in pixels.
+ ///
+ public int Rows
+ {
+ get { return m_rows; }
+ set
+ {
+ if (value < 0 || value > 30000)
+ throw new ArgumentException("Supported range is 1 to 30000.");
+ m_rows = value;
+ }
+ }
+
+
+ private int m_columns;
+ ///
+ /// The width of the image in pixels.
+ ///
+ public int Columns
+ {
+ get { return m_columns; }
+ set
+ {
+ if (value < 0 || value > 30000)
+ throw new ArgumentException("Supported range is 1 to 30000.");
+ m_columns = value;
+ }
+ }
+
+
+ private int m_depth;
+ ///
+ /// The number of bits per channel. Supported values are 1, 8, and 16.
+ ///
+ public int Depth
+ {
+ get { return m_depth; }
+ set
+ {
+ if (value == 1 || value == 8 || value == 16)
+ m_depth = value;
+ else
+ throw new ArgumentException("Supported values are 1, 8, and 16.");
+ }
+ }
+
+ private ColorModes m_colorMode;
+ ///
+ /// The color mode of the file.
+ ///
+ public ColorModes ColorMode
+ {
+ get { return m_colorMode; }
+ set { m_colorMode = value; }
+ }
+
+
+ private List m_imageResources = new List();
+ ///
+ /// The Image resource blocks for the file
+ ///
+ ///
+ public List ImageResources
+ {
+ get { return m_imageResources; }
+ }
+
+ public ResolutionInfo Resolution
+ {
+ get
+ {
+ return (ResolutionInfo)m_imageResources.Find((ImageResource x) => x.ID == (int)ResourceIDs.ResolutionInfo);
+ }
+
+ set
+ {
+ ImageResource oldValue = m_imageResources.Find((ImageResource x) => x.ID == (int)ResourceIDs.ResolutionInfo);
+ if (oldValue != null)
+ m_imageResources.Remove(oldValue);
+
+ m_imageResources.Add(value);
+ }
+ }
+
+ List m_layers = new List();
+ public List Layers
+ {
+ get
+ {
+ return m_layers;
+ }
+ }
+
+ private bool m_absoluteAlpha;
+ public bool AbsoluteAlpha
+ {
+ get { return m_absoluteAlpha; }
+ set { m_absoluteAlpha = value; }
+ }
+
+ ///
+ /// The raw image data from the file, seperated by the channels.
+ ///
+ public byte[][] m_imageData;
+
+ public byte[][] ImageData
+ {
+ get { return m_imageData; }
+ set { m_imageData = value; }
+ }
+
+
+ private ImageCompression m_imageCompression;
+ public ImageCompression ImageCompression
+ {
+ get { return m_imageCompression; }
+ set { m_imageCompression = value; }
+ }
+ #endregion //End Properties
+
+ public void Load(string filename)
+ {
+ using (FileStream stream = new FileStream(filename, FileMode.Open))
+ {
+
+ //binary reverse reader reads data types in big-endian format.
+ BinaryReverseReader reader = new BinaryReverseReader(stream);
+
+ #region "Headers"
+ //The headers area is used to check for a valid PSD file
+ Debug.WriteLine("LoadHeader started at " + reader.BaseStream.Position.ToString());
+
+ string signature = new string(reader.ReadChars(4));
+ if (signature != "8BPS")
+ throw new IOException("Bad or invalid file stream supplied");
+
+ //get the version number, should be 1 always
+ if ((m_version = reader.ReadInt16()) != 1)
+ throw new IOException("Invalid version number supplied");
+
+ //get rid of the 6 bytes reserverd in PSD format
+ reader.BaseStream.Position += 6;
+
+ //get the rest of the information from the PSD file.
+ //Everytime ReadInt16() is called, it reads 2 bytes.
+ //Everytime ReadInt32() is called, it reads 4 bytes.
+ m_channels = reader.ReadInt16();
+ m_rows = reader.ReadInt32();
+ m_columns = reader.ReadInt32();
+ m_depth = reader.ReadInt16();
+ m_colorMode = (ColorModes)reader.ReadInt16();
+
+ //by end of headers, the reader has read 26 bytes into the file.
+ #endregion //End Headers
+
+ #region "ColorModeData"
+ ///
+ /// If ColorMode is ColorModes.Indexed, the following 768 bytes will contain
+ /// a 256-color palette. If the ColorMode is ColorModes.Duotone, the data
+ /// following presumably consists of screen parameters and other related information.
+ /// Unfortunately, it is intentionally not documented by Adobe, and non-Photoshop
+ /// readers are advised to treat duotone images as gray-scale images.
+ ///
+ Debug.WriteLine("LoadColorModeData started at " + reader.BaseStream.Position.ToString());
+
+ uint paletteLength = reader.ReadUInt32(); //readUint32() advances the reader 4 bytes.
+ if (paletteLength > 0)
+ {
+ ColorModeData = reader.ReadBytes((int)paletteLength);
+ }
+ #endregion //End ColorModeData
+
+
+ #region "Loading Image Resources"
+ //This part takes extensive use of classes that I didn't write therefore
+ //I can't document much on what they do.
+
+ Debug.WriteLine("LoadingImageResources started at " + reader.BaseStream.Position.ToString());
+
+ m_imageResources.Clear();
+
+ uint imgResLength = reader.ReadUInt32();
+ if (imgResLength <= 0)
+ return;
+
+ long startPosition = reader.BaseStream.Position;
+
+ while ((reader.BaseStream.Position - startPosition) < imgResLength)
+ {
+ ImageResource imgRes = new ImageResource(reader);
+
+ ResourceIDs resID = (ResourceIDs)imgRes.ID;
+ switch (resID)
+ {
+ case ResourceIDs.ResolutionInfo:
+ imgRes = new ResolutionInfo(imgRes);
+ break;
+ case ResourceIDs.Thumbnail1:
+ case ResourceIDs.Thumbnail2:
+ imgRes = new Thumbnail(imgRes);
+ break;
+ case ResourceIDs.AlphaChannelNames:
+ imgRes = new AlphaChannels(imgRes);
+ break;
+ }
+
+ m_imageResources.Add(imgRes);
+
+ }
+ // make sure we are not on a wrong offset, so set the stream position
+ // manually
+ reader.BaseStream.Position = startPosition + imgResLength;
+
+ #endregion //End LoadingImageResources
+
+
+ #region "Layer and Mask Info"
+ //We are gonna load up all the layers and masking of the PSD now.
+ Debug.WriteLine("LoadLayerAndMaskInfo - Part1 started at " + reader.BaseStream.Position.ToString());
+ uint layersAndMaskLength = reader.ReadUInt32();
+
+ if (layersAndMaskLength <= 0)
+ return;
+
+ //new start position
+ startPosition = reader.BaseStream.Position;
+
+ //Lets start by loading up all the layers
+ LoadLayers(reader);
+ //we are done the layers, load up the masks
+ LoadGlobalLayerMask(reader);
+
+ // make sure we are not on a wrong offset, so set the stream position
+ // manually
+ reader.BaseStream.Position = startPosition + layersAndMaskLength;
+ #endregion //End Layer and Mask info
+
+ #region "Loading Final Image"
+
+ //we have loaded up all the information from the PSD file
+ //into variables we can use later on.
+
+ //lets finish loading the raw data that defines the image
+ //in the picture.
+
+ Debug.WriteLine("LoadImage started at " + reader.BaseStream.Position.ToString());
+
+ m_imageCompression = (ImageCompression)reader.ReadInt16();
+
+ m_imageData = new byte[m_channels][];
+
+ //---------------------------------------------------------------
+
+ if (m_imageCompression == ImageCompression.Rle)
+ {
+ // The RLE-compressed data is proceeded by a 2-byte data count for each row in the data,
+ // which we're going to just skip.
+ reader.BaseStream.Position += m_rows * m_channels * 2;
+ }
+
+ //---------------------------------------------------------------
+
+ int bytesPerRow = 0;
+
+ switch (m_depth)
+ {
+ case 1:
+ bytesPerRow = m_columns;//NOT Shure
+ break;
+ case 8:
+ bytesPerRow = m_columns;
+ break;
+ case 16:
+ bytesPerRow = m_columns * 2;
+ break;
+ }
+
+ //---------------------------------------------------------------
+
+ for (int ch = 0; ch < m_channels; ch++)
+ {
+ m_imageData[ch] = new byte[m_rows * bytesPerRow];
+
+ switch (m_imageCompression)
+ {
+ case ImageCompression.Raw:
+ reader.Read(m_imageData[ch], 0, m_imageData[ch].Length);
+ break;
+ case ImageCompression.Rle:
+ {
+ for (int i = 0; i < m_rows; i++)
+ {
+ int rowIndex = i * m_columns;
+ RleHelper.DecodedRow(reader.BaseStream, m_imageData[ch], rowIndex, bytesPerRow);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ #endregion //End LoadingFinalImage
+ }
+ } //end Load()
+
+
+
+
+
+ ///
+ /// Loads up the Layers of the supplied PSD file
+ ///
+ private void LoadLayers(BinaryReverseReader reader)
+ {
+ Debug.WriteLine("LoadLayers started at " + reader.BaseStream.Position.ToString());
+
+ uint layersInfoSectionLength = reader.ReadUInt32();
+
+ if (layersInfoSectionLength <= 0)
+ return;
+
+ long startPosition = reader.BaseStream.Position;
+
+ short numberOfLayers = reader.ReadInt16();
+
+ // If <0, then number of layers is absolute value,
+ // and the first alpha channel contains the transparency data for
+ // the merged result.
+ if (numberOfLayers < 0)
+ {
+ AbsoluteAlpha = true;
+ numberOfLayers = Math.Abs(numberOfLayers);
+ }
+
+ m_layers.Clear();
+
+ if (numberOfLayers == 0)
+ return;
+
+ for (int i = 0; i < numberOfLayers; i++)
+ {
+ m_layers.Add(new Layer(reader, this));
+ }
+
+ foreach (Layer layer in m_layers)
+ {
+ foreach (Layer.Channel channel in layer.Channels)
+ {
+ if (channel.ID != -2)
+ channel.LoadPixelData(reader);
+ }
+ layer.MaskData.LoadPixelData(reader);
+ }
+
+
+ if (reader.BaseStream.Position % 2 == 1)
+ reader.ReadByte();
+
+ // make sure we are not on a wrong offset, so set the stream position
+ // manually
+ reader.BaseStream.Position = startPosition + layersInfoSectionLength;
+ }
+
+ ///
+ /// Load up the masking information of the supplied PSD
+ ///
+ private void LoadGlobalLayerMask(BinaryReverseReader reader)
+ {
+ Debug.WriteLine("LoadGlobalLayerMask started at " + reader.BaseStream.Position.ToString());
+
+ uint maskLength = reader.ReadUInt32();
+
+ if (maskLength <= 0)
+ return;
+
+ GlobalLayerMaskData = reader.ReadBytes((int)maskLength);
+ }
+
+ }
+
+ public enum ImageCompression
+ {
+ ///
+ /// Raw data
+ ///
+ Raw = 0,
+ ///
+ /// RLE compressed
+ ///
+ Rle = 1,
+ ///
+ /// ZIP without prediction.
+ ///
+ /// This is currently not supported since it is ot documented.
+ /// Loading will result in an image where all channels are set to zero.
+ ///
+ ///
+ Zip = 2,
+ ///
+ /// ZIP with prediction.
+ ///
+ /// This is currently not supported since it is ot documented.
+ /// Loading will result in an image where all channels are set to zero.
+ ///
+ ///
+ ZipPrediction = 3
+ }
+
+
+ class RleHelper
+ {
+ ////////////////////////////////////////////////////////////////////////
+
+ private class RlePacketStateMachine
+ {
+ private bool m_rlePacket = false;
+ private byte[] m_packetValues = new byte[128];
+ private int packetLength;
+ private Stream m_stream;
+
+ internal void Flush()
+ {
+ byte header;
+ if (m_rlePacket)
+ {
+ header = (byte)(-(packetLength - 1));
+ }
+ else
+ {
+ header = (byte)(packetLength - 1);
+ }
+
+ m_stream.WriteByte(header);
+
+ int length = (m_rlePacket ? 1 : packetLength);
+
+ m_stream.Write(m_packetValues, 0, length);
+
+ packetLength = 0;
+ }
+
+ internal void Push(byte color)
+ {
+ if (packetLength == 0)
+ {
+ // Starting a fresh packet.
+ m_rlePacket = false;
+ m_packetValues[0] = color;
+ packetLength = 1;
+ }
+ else if (packetLength == 1)
+ {
+ // 2nd byte of this packet... decide RLE or non-RLE.
+ m_rlePacket = (color == m_packetValues[0]);
+ m_packetValues[1] = color;
+ packetLength = 2;
+ }
+ else if (packetLength == m_packetValues.Length)
+ {
+ // Packet is full. Start a new one.
+ Flush();
+ Push(color);
+ }
+ else if (packetLength >= 2 && m_rlePacket && color != m_packetValues[packetLength - 1])
+ {
+ // We were filling in an RLE packet, and we got a non-repeated color.
+ // Emit the current packet and start a new one.
+ Flush();
+ Push(color);
+ }
+ else if (packetLength >= 2 && m_rlePacket && color == m_packetValues[packetLength - 1])
+ {
+ // We are filling in an RLE packet, and we got another repeated color.
+ // Add the new color to the current packet.
+ ++packetLength;
+ m_packetValues[packetLength - 1] = color;
+ }
+ else if (packetLength >= 2 && !m_rlePacket && color != m_packetValues[packetLength - 1])
+ {
+ // We are filling in a raw packet, and we got another random color.
+ // Add the new color to the current packet.
+ ++packetLength;
+ m_packetValues[packetLength - 1] = color;
+ }
+ else if (packetLength >= 2 && !m_rlePacket && color == m_packetValues[packetLength - 1])
+ {
+ // We were filling in a raw packet, but we got a repeated color.
+ // Emit the current packet without its last color, and start a
+ // new RLE packet that starts with a length of 2.
+ --packetLength;
+ Flush();
+ Push(color);
+ Push(color);
+ }
+ }
+
+ internal RlePacketStateMachine(Stream stream)
+ {
+ m_stream = stream;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+
+ public static int EncodedRow(Stream stream, byte[] imgData, int startIdx, int columns)
+ {
+ long startPosition = stream.Position;
+
+ RlePacketStateMachine machine = new RlePacketStateMachine(stream);
+
+ for (int x = 0; x < columns; ++x)
+ machine.Push(imgData[x + startIdx]);
+
+ machine.Flush();
+
+ return (int)(stream.Position - startPosition);
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+
+ public static void DecodedRow(Stream stream, byte[] imgData, int startIdx, int columns)
+ {
+ int count = 0;
+ while (count < columns)
+ {
+ byte byteValue = (byte)stream.ReadByte();
+
+ int len = (int)byteValue;
+ if (len < 128)
+ {
+ len++;
+ while (len != 0 && (startIdx + count) < imgData.Length)
+ {
+ byteValue = (byte)stream.ReadByte();
+
+ imgData[startIdx + count] = byteValue;
+ count++;
+ len--;
+ }
+ }
+ else if (len > 128)
+ {
+ // Next -len+1 bytes in the dest are replicated from next source byte.
+ // (Interpret len as a negative 8-bit int.)
+ len ^= 0x0FF;
+ len += 2;
+ byteValue = (byte)stream.ReadByte();
+
+ while (len != 0 && (startIdx + count) < imgData.Length)
+ {
+ imgData[startIdx + count] = byteValue;
+ count++;
+ len--;
+ }
+ }
+ else if (128 == len)
+ {
+ // Do nothing
+ }
+ }
+
+ }
+ }
+}
diff --git a/bin/App_Code/ResolutionInfo.cs b/bin/App_Code/ResolutionInfo.cs
new file mode 100755
index 0000000..35d4f4a
--- /dev/null
+++ b/bin/App_Code/ResolutionInfo.cs
@@ -0,0 +1,152 @@
+/////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2006, Frank Blumenberg
+//
+// See License.txt for complete licensing and attribution information.
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// This code is adapted from code in the Endogine sprite engine by Jonas Beckeman.
+// http://www.endogine.com/CS/
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+using System;
+
+namespace PhotoshopFiles
+{
+ ///
+ /// Summary description for ResolutionInfo.
+ ///
+ public class ResolutionInfo : ImageResource
+ {
+ ///
+ /// Fixed-point number: pixels per inch
+ ///
+ private short m_hRes;
+ public short HRes
+ {
+ get { return m_hRes; }
+ set { m_hRes = value; }
+ }
+
+ ///
+ /// Fixed-point number: pixels per inch
+ ///
+ private short m_vRes;
+ public short VRes
+ {
+ get { return m_vRes; }
+ set { m_vRes = value; }
+ }
+
+ ///
+ /// 1=pixels per inch, 2=pixels per centimeter
+ ///
+ public enum ResUnit
+ {
+ PxPerInch = 1,
+ PxPerCent = 2
+ }
+
+ private ResUnit m_hResUnit;
+ public ResUnit HResUnit
+ {
+ get { return m_hResUnit; }
+ set { m_hResUnit = value; }
+ }
+
+ private ResUnit m_vResUnit;
+ public ResUnit VResUnit
+ {
+ get { return m_vResUnit; }
+ set { m_vResUnit = value; }
+ }
+
+ ///
+ /// 1=in, 2=cm, 3=pt, 4=picas, 5=columns
+ ///
+ public enum Unit
+ {
+ In = 1,
+ Cm = 2,
+ Pt = 3,
+ Picas = 4,
+ Columns = 5
+ }
+ private Unit m_widthUnit;
+
+ public Unit WidthUnit
+ {
+ get { return m_widthUnit; }
+ set { m_widthUnit = value; }
+ }
+
+ private Unit m_heightUnit;
+
+ public Unit HeightUnit
+ {
+ get { return m_heightUnit; }
+ set { m_heightUnit = value; }
+ }
+
+ public ResolutionInfo()
+ : base()
+ {
+ base.ID = (short)ResourceIDs.ResolutionInfo;
+ }
+ public ResolutionInfo(ImageResource imgRes)
+ : base(imgRes)
+ {
+ BinaryReverseReader reader = imgRes.DataReader;
+
+ this.m_hRes = reader.ReadInt16();
+ this.m_hResUnit = (ResUnit)reader.ReadInt32();
+ this.m_widthUnit = (Unit)reader.ReadInt16();
+
+ this.m_vRes = reader.ReadInt16();
+ this.m_vResUnit = (ResUnit)reader.ReadInt32();
+ this.m_heightUnit = (Unit)reader.ReadInt16();
+
+ reader.Close();
+ }
+
+ protected override void StoreData()
+ {
+ System.IO.MemoryStream stream = new System.IO.MemoryStream();
+ BinaryReverseWriter writer = new BinaryReverseWriter(stream);
+
+ writer.Write((Int16)m_hRes);
+ writer.Write((Int32)m_hResUnit);
+ writer.Write((Int16)m_widthUnit);
+
+ writer.Write((Int16)m_vRes);
+ writer.Write((Int32)m_vResUnit);
+ writer.Write((Int16)m_heightUnit);
+
+ writer.Close();
+ stream.Close();
+
+ Data = stream.ToArray();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/bin/App_Code/Thumbnail.cs b/bin/App_Code/Thumbnail.cs
new file mode 100755
index 0000000..7c4b28b
--- /dev/null
+++ b/bin/App_Code/Thumbnail.cs
@@ -0,0 +1,94 @@
+/////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2006, Frank Blumenberg
+//
+// See License.txt for complete licensing and attribution information.
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// This code is adapted from code in the Endogine sprite engine by Jonas Beckeman.
+// http://www.endogine.com/CS/
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.IO;
+using System.Drawing;
+
+namespace PhotoshopFiles
+{
+ ///
+ /// Summary description for Thumbnail.
+ ///
+ public class Thumbnail : ImageResource
+ {
+ private Bitmap m_thumbnailImage;
+ public Bitmap Image
+ {
+ get { return m_thumbnailImage; }
+ set { m_thumbnailImage = value; }
+ }
+
+ public Thumbnail(ImageResource imgRes)
+ : base(imgRes)
+ {
+ using (BinaryReverseReader reader = DataReader)
+ {
+ int format = reader.ReadInt32();
+ int width = reader.ReadInt32();
+ int height = reader.ReadInt32();
+ int widthBytes = reader.ReadInt32();
+ int size = reader.ReadInt32();
+ int compressedSize = reader.ReadInt32();
+ short bitPerPixel = reader.ReadInt16();
+ short planes = reader.ReadInt16();
+
+ if (format == 1)
+ {
+
+ byte[] imgData = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));
+
+ using (MemoryStream strm = new MemoryStream(imgData))
+ {
+ m_thumbnailImage = (Bitmap)(Bitmap.FromStream(strm).Clone());
+ }
+
+ if (this.ID == 1033)
+ {
+ //// BGR
+ //for(int y=0;y