Initial commit

This commit is contained in:
AbstractConcept 2022-09-13 00:36:34 -05:00
commit 3c7cc0c973
8391 changed files with 704313 additions and 0 deletions

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: cea9a02ce8d970946b9534af754bfda1
folderAsset: yes
timeCreated: 1495006553
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,50 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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;
namespace PhotoshopFile.Compression
{
internal class EndianReverser : ImageData
{
private ImageData imageData;
protected override bool AltersWrittenData
{
get { return true; }
}
public EndianReverser(ImageData imageData)
: base(imageData.Size, imageData.BitDepth)
{
this.imageData = imageData;
}
internal override void Read(byte[] buffer)
{
imageData.Read(buffer);
var numPixels = Size.Width * Size.Height;
if (numPixels == 0)
{
return;
}
Util.SwapByteArray(BitDepth, buffer, 0, numPixels);
}
public override byte[] ReadCompressed()
{
return imageData.ReadCompressed();
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1a1cbf26300dbdd4ebaf8e0a92b67db6
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,57 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 PDNWrapper;
using System.IO.Compression;
namespace PhotoshopFile.Compression
{
internal abstract class ImageData
{
public int BitDepth { get; private set; }
public int BytesPerRow { get; private set; }
public Size Size { get; private set; }
protected abstract bool AltersWrittenData { get; }
protected ImageData(Size size, int bitDepth)
{
Size = size;
BitDepth = bitDepth;
BytesPerRow = Util.BytesPerRow(size, bitDepth);
}
/// <summary>
/// Reads decompressed image data.
/// </summary>
public virtual byte[] Read()
{
var imageLongLength = (long)BytesPerRow * Size.Height;
Util.CheckByteArrayLength(imageLongLength);
var buffer = new byte[imageLongLength];
Read(buffer);
return buffer;
}
internal abstract void Read(byte[] buffer);
/// <summary>
/// Reads compressed image data.
/// </summary>
public abstract byte[] ReadCompressed();
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 23318de62729646489ccb1545e7c5bbf
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,96 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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.IO.Compression;
using PDNWrapper;
namespace PhotoshopFile.Compression
{
internal static class ImageDataFactory
{
/// <summary>
/// Creates an ImageData object to compress or decompress image data.
/// </summary>
/// <param name="channel">The Channel associated with the image data.</param>
/// <param name="data">The image data to be decompressed, or null if
/// image data is to be compressed.</param>
public static ImageData Create(Channel channel, byte[] data)
{
var bitDepth = channel.Layer.PsdFile.BitDepth;
ImageData imageData;
switch (channel.ImageCompression)
{
case ImageCompression.Raw:
imageData = new RawImage(data, channel.Rect.Size, bitDepth);
break;
case ImageCompression.Rle:
imageData = new RleImage(data, channel.RleRowLengths,
channel.Rect.Size, bitDepth);
break;
case ImageCompression.Zip:
// Photoshop treats 32-bit Zip as 32-bit ZipPrediction
imageData = (bitDepth == 32)
? CreateZipPredict(data, channel.Rect.Size, bitDepth)
: new ZipImage(data, channel.Rect.Size, bitDepth);
break;
case ImageCompression.ZipPrediction:
imageData = CreateZipPredict(data, channel.Rect.Size, bitDepth);
break;
default:
throw new PsdInvalidException("Unknown image compression method.");
}
// Reverse endianness of multi-byte image data
imageData = WrapEndianness(imageData);
return imageData;
}
private static ImageData CreateZipPredict(byte[] data, Size size,
int bitDepth)
{
switch (bitDepth)
{
case 16:
return new ZipPredict16Image(data, size);
case 32:
return new ZipPredict32Image(data, size);
default:
throw new PsdInvalidException(
"ZIP with prediction is only available for 16 and 32 bit depths.");
}
}
private static ImageData WrapEndianness(ImageData imageData)
{
// Single-byte image does not require endianness reversal
if (imageData.BitDepth <= 8)
{
return imageData;
}
// Bytes will be reordered by the compressor, so no wrapper is needed
if ((imageData is ZipPredict16Image) || (imageData is ZipPredict32Image))
{
return imageData;
}
return new EndianReverser(imageData);
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3a37c02e17e67be44b01952ad661991c
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,44 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 PDNWrapper;
namespace PhotoshopFile.Compression
{
internal class RawImage : ImageData
{
private byte[] data;
protected override bool AltersWrittenData
{
get { return false; }
}
public RawImage(byte[] data, Size size, int bitDepth)
: base(size, bitDepth)
{
this.data = data;
}
internal override void Read(byte[] buffer)
{
Array.Copy(data, buffer, data.Length);
}
public override byte[] ReadCompressed()
{
return data;
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b976ddccf1109574c922e1aa8cdd48fb
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,61 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Photoshop PSD FileType Plugin for Paint.NET
// http://psdplugin.codeplex.com/
//
// This software is ptortorovided 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.Diagnostics;
using PDNWrapper;
using System.IO;
using System.Linq;
namespace PhotoshopFile.Compression
{
internal class RleImage : ImageData
{
private byte[] rleData;
private RleRowLengths rleRowLengths;
protected override bool AltersWrittenData
{
get { return false; }
}
public RleImage(byte[] rleData, RleRowLengths rleRowLengths,
Size size, int bitDepth)
: base(size, bitDepth)
{
this.rleData = rleData;
this.rleRowLengths = rleRowLengths;
}
internal override void Read(byte[] buffer)
{
var rleStream = new MemoryStream(rleData);
var rleReader = new RleReader(rleStream);
var bufferIndex = 0;
for (int i = 0; i < Size.Height; i++)
{
var bytesRead = rleReader.Read(buffer, bufferIndex, BytesPerRow);
if (bytesRead != BytesPerRow)
{
throw new Exception("RLE row decompressed to unexpected length.");
}
bufferIndex += bytesRead;
}
}
public override byte[] ReadCompressed()
{
return rleData;
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 66819b54b3c858446a1518b135b6514b
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,101 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 PDNWrapper;
using System.IO;
using System.IO.Compression;
namespace PhotoshopFile.Compression
{
internal class ZipImage : ImageData
{
private MemoryStream zipDataStream;
private DeflateStream zipStream;
protected override bool AltersWrittenData
{
get { return false; }
}
public ZipImage(byte[] data, Size size, int bitDepth)
: base(size, bitDepth)
{
if (data == null)
{
InitCompress();
}
else
{
InitDecompress(data);
}
}
private void InitCompress()
{
zipDataStream = new MemoryStream();
// Write 2-byte zlib (RFC 1950) header
//
// CMF Compression Method and flags:
// CM 0:3 = 8 = deflate
// CINFO 4:7 = 4 = undefined, RFC 1950 only defines CINFO = 8
//
// FLG Flags:
// FCHECK 0:4 = 9 = check bits for CMF and FLG
// FDICT 5 = 0 = no preset dictionary
// FLEVEL 6:7 = 2 = default compression level
zipDataStream.WriteByte(0x48);
zipDataStream.WriteByte(0x89);
zipStream = new DeflateStream(zipDataStream, CompressionMode.Compress,
true);
}
private void InitDecompress(byte[] data)
{
zipDataStream = new MemoryStream(data);
// .NET implements Deflate (RFC 1951) but not zlib (RFC 1950),
// so we have to skip the first two bytes.
zipDataStream.ReadByte();
zipDataStream.ReadByte();
zipStream = new DeflateStream(zipDataStream, CompressionMode.Decompress,
true);
}
internal override void Read(byte[] buffer)
{
var bytesToRead = (long)Size.Height * BytesPerRow;
Util.CheckByteArrayLength(bytesToRead);
var bytesRead = zipStream.Read(buffer, 0, (int)bytesToRead);
if (bytesRead != bytesToRead)
{
throw new Exception("ZIP stream was not fully decompressed.");
}
}
public override byte[] ReadCompressed()
{
// Write out the last block. (Flush leaves the last block open.)
zipStream.Close();
// Do not write the zlib header when the image data is empty
var result = (zipDataStream.Length == 2)
? new byte[0]
: zipDataStream.ToArray();
return result;
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 87da90d1a63b1174cb56810e4d9f769e
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,113 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 PDNWrapper;
using System.IO.Compression;
namespace PhotoshopFile.Compression
{
internal class ZipPredict16Image : ImageData
{
private ImageData zipImage;
protected override bool AltersWrittenData
{
get { return true; }
}
public ZipPredict16Image(byte[] zipData, Size size)
: base(size, 16)
{
// 16-bitdepth images are delta-encoded word-by-word. The deltas
// are thus big-endian and must be reversed for further processing.
var zipRawImage = new ZipImage(zipData, size, 16);
zipImage = new EndianReverser(zipRawImage);
}
internal override void Read(byte[] buffer)
{
if (buffer.Length == 0)
{
return;
}
zipImage.Read(buffer);
{
{
Unpredict(buffer);
}
}
}
public override byte[] ReadCompressed()
{
return zipImage.ReadCompressed();
}
private void Predict(/*UInt16**/ byte[] ptrData)
{
int size = sizeof(UInt16);
// Delta-encode each row
for (int i = 0; i < Size.Height; i++)
{
int rowOffset = Size.Width * i * size;
//UInt16* ptrDataRow = ptrData;
var ptrDataRowEnd = Size.Width - 1;
// Start with the last column in the row
while (ptrDataRowEnd > 0)
{
var v = BitConverter.ToUInt16(ptrData, ptrDataRowEnd * size + rowOffset);
var v1 = BitConverter.ToUInt16(ptrData, (ptrDataRowEnd - 1) * size + rowOffset);
v -= v1;
var b = BitConverter.GetBytes(v);
for (int c = 0; c < b.Length; ++c)
{
ptrData[ptrDataRowEnd * size + rowOffset + c] = b[c];
}
ptrDataRowEnd--;
}
}
}
/// <summary>
/// Unpredicts the decompressed, native-endian image data.
/// </summary>
private void Unpredict(byte[] ptrData)
{
int size = sizeof(UInt16);
// Delta-decode each row
for (int i = 0; i < Size.Height; i++)
{
//UInt16* ptrDataRowEnd = ptrData + Size.Width;
int rowOffset = Size.Width * i * size;
// Start with column index 1 on each row
int start = 1;
while (start < Size.Width)
{
var v = BitConverter.ToUInt16(ptrData, start * size + rowOffset);
var v1 = BitConverter.ToUInt16(ptrData, (start - 1) * size + rowOffset);
v += v1;
var b = BitConverter.GetBytes(v);
for (int c = 0; c < b.Length; ++c)
{
ptrData[start * size + rowOffset + c] = b[c];
}
start++;
}
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6259913e8d526e447bf2ff536882fa6c
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,175 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 PDNWrapper;
namespace PhotoshopFile.Compression
{
internal class ZipPredict32Image : ImageData
{
private ZipImage zipImage;
protected override bool AltersWrittenData
{
// Prediction will pack the data into a temporary buffer, so the
// original data will remain unchanged.
get { return false; }
}
public ZipPredict32Image(byte[] zipData, Size size)
: base(size, 32)
{
zipImage = new ZipImage(zipData, size, 32);
}
internal override void Read(byte[] buffer)
{
if (buffer.Length == 0)
{
return;
}
var predictedData = new byte[buffer.Length];
zipImage.Read(predictedData);
{
//fixed (byte* ptrData = &predictedData[0])
//fixed (byte* ptrOutput = &buffer[0])
{
Unpredict(predictedData, buffer);
}
}
}
public override byte[] ReadCompressed()
{
return zipImage.ReadCompressed();
}
private void Predict(byte[] ptrData, byte[] ptrOutput /*Int32* ptrData, byte* ptrOutput*/)
{
int size = sizeof(Int32);
int inputIndex = 0;
int outputIndex = 0;
for (int i = 0; i < Size.Height; i++)
{
// Pack together the individual bytes of the 32-bit words, high-order
// bytes before low-order bytes.
int offset1 = Size.Width;
int offset2 = 2 * offset1;
int offset3 = 3 * offset1;
//Int32* ptrDataRow = ptrData;
//Int32* ptrDataRowEnd = ptrDataRow + Size.Width;
int start = 0, end = Size.Width;
//while (ptrData < ptrDataRowEnd)
while (start < end)
{
Int32 data = BitConverter.ToInt32(ptrData, inputIndex);
ptrOutput[start + outputIndex] = (byte)(data >> 24);
ptrOutput[start + outputIndex + offset1] = (byte)(data >> 16);
ptrOutput[start + outputIndex + offset2] = (byte)(data >> 8);
ptrOutput[start + outputIndex + offset3] = (byte)(data);
//ptrData++;
//ptrOutput++;
start++;
inputIndex += size;
}
// Delta-encode the row
//byte* ptrOutputRow = ptrOutput;
//byte* ptrOutputRowEnd = ptrOutputRow + BytesPerRow;
//ptrOutput = ptrOutputRowEnd - 1;
start = BytesPerRow - 1;
while (start > 0)
{
ptrOutput[start + outputIndex] -= ptrOutput[start + outputIndex - 1];
start--;
}
outputIndex += BytesPerRow;
// Advance pointer to next row
//ptrOutput = ptrOutputRowEnd;
//Debug.Assert(ptrData == ptrDataRowEnd);
}
}
/// <summary>
/// Unpredicts the raw decompressed image data into a 32-bpp bitmap with
/// native endianness.
/// </summary>
private void Unpredict(byte[] ptrData, byte[] ptrOutput /*byte* ptrData, Int32* ptrOutput*/)
{
int inputIndex = 0;
int outputIndex = 0;
for (int i = 0; i < Size.Height; i++)
{
//byte* ptrDataRow = ptrData;
//byte* ptrDataRowEnd = ptrDataRow + BytesPerRow;
// Delta-decode each row
//ptrData++;
//while (ptrData < ptrDataRowEnd)
int startIndex = 1;
while (startIndex < BytesPerRow)
{
//*ptrData += *(ptrData - 1);
//ptrData++;
ptrData[inputIndex + startIndex] += ptrData[inputIndex + startIndex - 1];
startIndex++;
}
// Within each row, the individual bytes of the 32-bit words are
// packed together, high-order bytes before low-order bytes.
// We now unpack them into words.
int offset1 = Size.Width;
int offset2 = 2 * offset1;
int offset3 = 3 * offset1;
//ptrData = ptrDataRow;
//Int32* ptrOutputRowEnd = ptrOutput + Size.Width;
//while (ptrOutput < ptrOutputRowEnd)
startIndex = 0;
while (startIndex < Size.Width)
{
Int32 pp = (Int32)ptrData[inputIndex + startIndex] << 24;
pp |= (Int32)ptrData[inputIndex + startIndex + offset1] << 16;
pp |= (Int32)ptrData[inputIndex + startIndex + offset2] << 8;
pp |= (Int32)ptrData[inputIndex + startIndex + offset3];
byte[] rr = BitConverter.GetBytes(pp);
for (int k = 0; k < rr.Length; ++k)
{
ptrOutput[outputIndex] = rr[k];
outputIndex++;
}
startIndex++;
//*ptrOutput = *(ptrData) << 24
// | *(ptrData + offset1) << 16
// | *(ptrData + offset2) << 8
// | *(ptrData + offset3);
//ptrData++;
//ptrOutput++;
}
// Advance pointer to next row
//ptrData = ptrDataRowEnd;
//Debug.Assert(ptrOutput == ptrOutputRowEnd);
inputIndex += BytesPerRow;
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2bb49ac2cee975745af2d48523e135c9
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,43 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Photoshop PSD FileType Plugin for Paint.NET
// http://psdplugin.codeplex.com/
//
// This software is perovided under the MIT License:
// Copyright (c) 2006-2007 Frank Blumenberg
// Copyright (c) 2010-2012 Tao Yue
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Diagnostics;
using PDNWrapper;
using System.Linq;
using System.Text;
namespace PhotoshopFile
{
[Serializable]
internal class PsdInvalidException : Exception
{
public PsdInvalidException()
{
}
public PsdInvalidException(string message)
: base(message)
{
}
}
[Serializable]
internal class RleException : Exception
{
public RleException() {}
public RleException(string message) : base(message) {}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1359900d9d213c641b1d44f49de5281d
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,234 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2015 Tao Yue
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
namespace PhotoshopFile
{
internal enum ResourceID
{
Undefined = 0,
MacPrintInfo = 1001,
ResolutionInfo = 1005,
AlphaChannelNames = 1006,
DisplayInfo = 1007,
Caption = 1008,
BorderInfo = 1009,
BackgroundColor = 1010,
PrintFlags = 1011,
MultichannelHalftoneInfo = 1012,
ColorHalftoneInfo = 1013,
DuotoneHalftoneInfo = 1014,
MultichannelTransferFunctions = 1015,
ColorTransferFunctions = 1016,
DuotoneTransferFunctions = 1017,
DuotoneImageInfo = 1018,
BlackWhiteRange = 1019,
EpsOptions = 1021,
QuickMaskInfo = 1022,
LayerStateInfo = 1024,
WorkingPathUnsaved = 1025,
LayersGroupInfo = 1026,
IptcNaa = 1028,
RawFormatImageMode = 1029,
JpegQuality = 1030,
GridGuidesInfo = 1032,
ThumbnailBgr = 1033,
CopyrightInfo = 1034,
Url = 1035,
ThumbnailRgb = 1036,
GlobalAngle = 1037,
ColorSamplersObsolete = 1038,
IccProfile = 1039,
Watermark = 1040,
IccUntagged = 1041,
EffectsVisible = 1042,
SpotHalftone = 1043,
DocumentSpecific = 1044,
UnicodeAlphaNames = 1045,
IndexedColorTableCount = 1046,
TransparentIndex = 1047,
GlobalAltitude = 1049,
Slices = 1050,
WorkflowUrl = 1051,
JumpToXpep = 1052,
AlphaIdentifiers = 1053,
UrlList = 1054,
VersionInfo = 1057,
ExifData1 = 1058,
ExifData3 = 1059,
XmpMetadata = 1060,
CaptionDigest = 1061,
PrintScale = 1062,
PixelAspectRatio = 1064,
LayerComps = 1065,
AlternateDuotoneColors = 1066,
AlternateSpotColors = 1067,
LayerSelectionIDs = 1069,
HdrToningInfo = 1070,
PrintInfo = 1071,
LayerGroupsEnabled = 1072,
ColorSamplers = 1073,
MeasurementScale = 1074,
TimelineInfo = 1075,
SheetDisclosure = 1076,
FloatDisplayInfo = 1077,
OnionSkins = 1078,
CountInfo = 1080,
PrintSettingsInfo = 1082,
PrintStyle = 1083,
MacNSPrintInfo = 1084,
WinDevMode = 1085,
AutoSaveFilePath = 1086,
AutoSaveFormat = 1087,
PathInfo = 2000, // 2000-2999: Path Information
ClippingPathName = 2999,
LightroomWorkflow = 8000,
PrintFlagsInfo = 10000
}
/// <summary>
/// Abstract class for Image Resources
/// </summary>
internal abstract class ImageResource
{
private string signature;
public string Signature
{
get { return signature; }
set
{
if (value.Length != 4)
{
throw new ArgumentException(
"Signature must be 4 characters in length.");
}
signature = value;
}
}
public string Name { get; set; }
public abstract ResourceID ID { get; }
protected ImageResource(string name)
{
Signature = "8BIM";
Name = name;
}
}
/// <summary>
/// Creates the appropriate subclass of ImageResource.
/// </summary>
internal static class ImageResourceFactory
{
public static ImageResource CreateImageResource(PsdBinaryReader reader)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, ImageResource");
var signature = reader.ReadAsciiChars(4);
var resourceIdInt = reader.ReadUInt16();
var name = reader.ReadPascalString(2);
var dataLength = (int)reader.ReadUInt32();
var dataPaddedLength = Util.RoundUp(dataLength, 2);
var endPosition = reader.BaseStream.Position + dataPaddedLength;
ImageResource resource = null;
var resourceId = (ResourceID)resourceIdInt;
switch (resourceId)
{
case ResourceID.ResolutionInfo:
resource = new ResolutionInfo(reader, name);
break;
case ResourceID.ThumbnailRgb:
case ResourceID.ThumbnailBgr:
resource = new Thumbnail(reader, resourceId, name, dataLength);
break;
case ResourceID.AlphaChannelNames:
resource = new AlphaChannelNames(reader, name, dataLength);
break;
case ResourceID.UnicodeAlphaNames:
resource = new UnicodeAlphaNames(reader, name, dataLength);
break;
case ResourceID.VersionInfo:
resource = new VersionInfo(reader, name);
break;
default:
resource = new RawImageResource(reader, signature, resourceId, name, dataLength);
break;
}
Util.DebugMessage(reader.BaseStream, "Load, End, ImageResource, {0}",
resourceId);
// Reposition the reader if we do not consume the full resource block.
// This takes care of the even-padding, and also preserves forward-
// compatibility in case a resource block is later extended with
// additional properties.
if (reader.BaseStream.Position < endPosition)
reader.BaseStream.Position = endPosition;
// However, overruns are definitely an error.
if (reader.BaseStream.Position > endPosition)
throw new PsdInvalidException("Corruption detected in resource.");
return resource;
}
}
internal class ImageResources : List<ImageResource>
{
public ImageResources() : base()
{
}
public ImageResource Get(ResourceID id)
{
return Find(x => x.ID == id);
}
public void Set(ImageResource resource)
{
Predicate<ImageResource> matchId = delegate(ImageResource res)
{
return res.ID == resource.ID;
};
var itemIdx = this.FindIndex(matchId);
var lastItemIdx = this.FindLastIndex(matchId);
if (itemIdx == -1)
{
Add(resource);
}
else if (itemIdx != lastItemIdx)
{
RemoveAll(matchId);
Insert(itemIdx, resource);
}
else
{
this[itemIdx] = resource;
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4bdfc57c0f4c65947a6971d815bd7ea7
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: f6b2d5715ac4ed8478b3819e8fe5918a
folderAsset: yes
timeCreated: 1495006553
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,52 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2013 Tao Yue
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
namespace PhotoshopFile
{
/// <summary>
/// The names of the alpha channels
/// </summary>
internal class AlphaChannelNames : ImageResource
{
public override ResourceID ID
{
get { return ResourceID.AlphaChannelNames; }
}
private List<string> channelNames = new List<string>();
public List<string> ChannelNames
{
get { return channelNames; }
}
public AlphaChannelNames() : base(String.Empty)
{
}
public AlphaChannelNames(PsdBinaryReader reader, string name, int resourceDataLength)
: base(name)
{
var endPosition = reader.BaseStream.Position + resourceDataLength;
// Alpha channel names are Pascal strings, with no padding in-between.
while (reader.BaseStream.Position < endPosition)
{
var channelName = reader.ReadPascalString(1);
ChannelNames.Add(channelName);
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3e90381151086314691cd7a595ba9a62
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,48 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2013 Tao Yue
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.IO;
using PDNWrapper;
namespace PhotoshopFile
{
/// <summary>
/// Stores the raw data for unimplemented image resource types.
/// </summary>
internal class RawImageResource : ImageResource
{
public byte[] Data { get; private set; }
private ResourceID id;
public override ResourceID ID
{
get { return id; }
}
public RawImageResource(ResourceID resourceId, string name)
: base(name)
{
this.id = resourceId;
}
public RawImageResource(PsdBinaryReader reader, string signature,
ResourceID resourceId, string name, int numBytes)
: base(name)
{
this.Signature = signature;
this.id = resourceId;
Data = reader.ReadBytes(numBytes);
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 764df10adc2353a47a5ae0e9649e51a6
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,94 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2012 Tao Yue
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
namespace PhotoshopFile
{
/// <summary>
/// Summary description for ResolutionInfo.
/// </summary>
internal class ResolutionInfo : ImageResource
{
public override ResourceID ID
{
get { return ResourceID.ResolutionInfo; }
}
/// <summary>
/// Horizontal DPI.
/// </summary>
public UFixed16_16 HDpi { get; set; }
/// <summary>
/// Vertical DPI.
/// </summary>
public UFixed16_16 VDpi { get; set; }
/// <summary>
/// 1 = pixels per inch, 2 = pixels per centimeter
/// </summary>
internal enum ResUnit
{
PxPerInch = 1,
PxPerCm = 2
}
/// <summary>
/// Display units for horizontal resolution. This only affects the
/// user interface; the resolution is still stored in the PSD file
/// as pixels/inch.
/// </summary>
public ResUnit HResDisplayUnit { get; set; }
/// <summary>
/// Display units for vertical resolution.
/// </summary>
public ResUnit VResDisplayUnit { get; set; }
/// <summary>
/// Physical units.
/// </summary>
internal enum Unit
{
Inches = 1,
Centimeters = 2,
Points = 3,
Picas = 4,
Columns = 5
}
public Unit WidthDisplayUnit { get; set; }
public Unit HeightDisplayUnit { get; set; }
public ResolutionInfo() : base(String.Empty)
{
}
public ResolutionInfo(PsdBinaryReader reader, string name)
: base(name)
{
this.HDpi = new UFixed16_16(reader.ReadUInt32());
this.HResDisplayUnit = (ResUnit)reader.ReadInt16();
this.WidthDisplayUnit = (Unit)reader.ReadInt16();
this.VDpi = new UFixed16_16(reader.ReadUInt32());
this.VResDisplayUnit = (ResUnit)reader.ReadInt16();
this.HeightDisplayUnit = (Unit)reader.ReadInt16();
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 285de09dadd2d3048a1028754eda2fa1
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,85 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2013 Tao Yue
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.IO;
using System.Diagnostics;
using PDNWrapper;
//using PDNWrapper.Imaging;
namespace PhotoshopFile
{
/// <summary>
/// Summary description for Thumbnail.
/// </summary>
internal class Thumbnail : RawImageResource
{
public Thumbnail(ResourceID id, string name)
: base(id, name)
{
}
public Thumbnail(PsdBinaryReader psdReader, ResourceID id, string name, int numBytes)
: base(psdReader, "8BIM", id, name, numBytes)
{
using (var memoryStream = new MemoryStream(Data))
using (var reader = new PsdBinaryReader(memoryStream, psdReader))
{
const int HEADER_LENGTH = 28;
var format = reader.ReadUInt32();
//var width = reader.ReadUInt32();
//var height = reader.ReadUInt32();
//var widthBytes = reader.ReadUInt32();
//var size = reader.ReadUInt32();
//var compressedSize = reader.ReadUInt32();
//var bitPerPixel = reader.ReadUInt16();
//var planes = reader.ReadUInt16();
// Raw RGB bitmap
if (format == 0)
{
//Image = new Bitmap((int)width, (int)height, PixelFormat.Format24bppRgb);
}
// JPEG bitmap
else if (format == 1)
{
byte[] imgData = reader.ReadBytes(numBytes - HEADER_LENGTH);
using (MemoryStream stream = new MemoryStream(imgData))
{
//var bitmap = new Bitmap(stream);
//Image = (Bitmap)bitmap.Clone();
}
// Reverse BGR pixels from old thumbnail format
if (id == ResourceID.ThumbnailBgr)
{
//for(int y=0;y<m_thumbnailImage.Height;y++)
// for (int x = 0; x < m_thumbnailImage.Width; x++)
// {
// Color c=m_thumbnailImage.GetPixel(x,y);
// Color c2=Color.FromArgb(c.B, c.G, c.R);
// m_thumbnailImage.SetPixel(x, y, c);
// }
}
}
else
{
throw new PsdInvalidException("Unknown thumbnail format.");
}
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 950af7a846d6ca046a6138dcc3735835
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,59 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2014 Tao Yue
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
namespace PhotoshopFile
{
/// <summary>
/// The names of the alpha channels.
/// </summary>
internal class UnicodeAlphaNames : ImageResource
{
public override ResourceID ID
{
get { return ResourceID.UnicodeAlphaNames; }
}
private List<string> channelNames = new List<string>();
public List<string> ChannelNames
{
get { return channelNames; }
}
public UnicodeAlphaNames()
: base(String.Empty)
{
}
public UnicodeAlphaNames(PsdBinaryReader reader, string name, int resourceDataLength)
: base(name)
{
var endPosition = reader.BaseStream.Position + resourceDataLength;
while (reader.BaseStream.Position < endPosition)
{
var channelName = reader.ReadUnicodeString();
// Photoshop writes out a null terminator for Unicode alpha names.
// There is no null terminator on other Unicode strings in PSD files.
if (channelName.EndsWith("\0"))
{
channelName = channelName.Substring(0, channelName.Length - 1);
}
ChannelNames.Add(channelName);
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8ba836b7a6b0c9a4a94c1d6c540b8962
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,53 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2012 Tao Yue
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PhotoshopFile
{
internal class VersionInfo : ImageResource
{
public override ResourceID ID
{
get { return ResourceID.VersionInfo; }
}
public UInt32 Version { get; set; }
public bool HasRealMergedData { get; set; }
public string ReaderName { get; set; }
public string WriterName { get; set; }
public UInt32 FileVersion { get; set; }
public VersionInfo() : base(String.Empty)
{
}
public VersionInfo(PsdBinaryReader reader, string name)
: base(name)
{
Version = reader.ReadUInt32();
HasRealMergedData = reader.ReadBoolean();
ReaderName = reader.ReadUnicodeString();
WriterName = reader.ReadUnicodeString();
FileVersion = reader.ReadUInt32();
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 948f26d9a8b009540b6574d5b592248d
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: a2b81847c6b672747861b07e47320717
folderAsset: yes
timeCreated: 1495006553
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,56 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2014 Tao Yue
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Diagnostics;
using System.Globalization;
namespace PhotoshopFile
{
internal class BlendingRanges
{
/// <summary>
/// The layer to which this channel belongs
/// </summary>
public Layer Layer { get; private set; }
public byte[] Data { get; set; }
///////////////////////////////////////////////////////////////////////////
public BlendingRanges(Layer layer)
{
Layer = layer;
Data = new byte[0];
}
///////////////////////////////////////////////////////////////////////////
public BlendingRanges(PsdBinaryReader reader, Layer layer)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, BlendingRanges");
Layer = layer;
var dataLength = reader.ReadInt32();
if (dataLength <= 0)
return;
Data = reader.ReadBytes(dataLength);
Util.DebugMessage(reader.BaseStream, "Load, End, BlendingRanges");
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9f4a47927e764174fa3984b07f9d4b58
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,232 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Diagnostics;
using PDNWrapper;
using System.Linq;
using Unity.Collections;
using PhotoshopFile.Compression;
namespace PhotoshopFile
{
internal class ChannelList : List<Channel>
{
/// <summary>
/// Returns channels with nonnegative IDs as an array, so that accessing
/// a channel by Id can be optimized into pointer arithmetic rather than
/// being implemented as a List scan.
/// </summary>
/// <remarks>
/// This optimization is crucial for blitting lots of pixels back and
/// forth between Photoshop's per-channel representation, and Paint.NET's
/// per-pixel BGRA representation.
/// </remarks>
public Channel[] ToIdArray()
{
var maxId = this.Max(x => x.ID);
var idArray = new Channel[maxId + 1];
foreach (var channel in this)
{
if (channel.ID >= 0)
idArray[channel.ID] = channel;
}
return idArray;
}
public ChannelList()
: base()
{
}
public Channel GetId(int id)
{
return this.Single(x => x.ID == id);
}
public bool ContainsId(int id)
{
return this.Exists(x => x.ID == id);
}
}
///////////////////////////////////////////////////////////////////////////
[DebuggerDisplay("ID = {ID}")]
internal class Channel
{
/// <summary>
/// The layer to which this channel belongs
/// </summary>
public Layer Layer { get; private set; }
/// <summary>
/// Channel ID.
/// <list type="bullet">
/// <item>-1 = transparency mask</item>
/// <item>-2 = user-supplied layer mask, or vector mask</item>
/// <item>-3 = user-supplied layer mask, if channel -2 contains a vector mask</item>
/// <item>
/// Nonnegative channel IDs give the actual image channels, in the
/// order defined by the colormode. For example, 0, 1, 2 = R, G, B.
/// </item>
/// </list>
/// </summary>
public short ID { get; set; }
public Rectangle Rect
{
get
{
switch (ID)
{
case -2:
return Layer.Masks.LayerMask.Rect;
case -3:
return Layer.Masks.UserMask.Rect;
default:
return Layer.Rect;
}
}
}
/// <summary>
/// Total length of the channel data, including compression headers.
/// </summary>
public long Length { get; set; }
/// <summary>
/// Raw image data for this color channel, in compressed on-disk format.
/// </summary>
/// <remarks>
/// If null, the ImageData will be automatically compressed during save.
/// </remarks>
public byte[] ImageDataRaw { get; set; }
/// <summary>
/// Decompressed image data for this color channel.
/// </summary>
/// <remarks>
/// When making changes to the ImageData, set ImageDataRaw to null so that
/// the correct data will be compressed during save.
/// </remarks>
public NativeArray<byte> ImageData { get; set; }
/// <summary>
/// Image compression method used.
/// </summary>
public ImageCompression ImageCompression { get; set; }
/// <summary>
/// RLE-compressed length of each row.
/// </summary>
public RleRowLengths RleRowLengths { get; set; }
//////////////////////////////////////////////////////////////////
internal Channel(short id, Layer layer)
{
ID = id;
Layer = layer;
}
internal Channel(PsdBinaryReader reader, Layer layer)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, Channel");
ID = reader.ReadInt16();
Length = (layer.PsdFile.IsLargeDocument)
? reader.ReadInt64()
: reader.ReadInt32();
Layer = layer;
Util.DebugMessage(reader.BaseStream, "Load, End, Channel, {0}", ID);
}
internal void Cleanup()
{
if (ImageData.IsCreated)
ImageData.Dispose();
}
//////////////////////////////////////////////////////////////////
internal void LoadPixelData(PsdBinaryReader reader)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, Channel image");
if (Length == 0)
{
ImageCompression = ImageCompression.Raw;
ImageDataRaw = new byte[0];
return;
}
var endPosition = reader.BaseStream.Position + this.Length;
ImageCompression = (ImageCompression)reader.ReadInt16();
var longDataLength = this.Length - 2;
Util.CheckByteArrayLength(longDataLength);
var dataLength = (int)longDataLength;
switch (ImageCompression)
{
case ImageCompression.Raw:
ImageDataRaw = reader.ReadBytes(dataLength);
break;
case ImageCompression.Rle:
// RLE row lengths
RleRowLengths = new RleRowLengths(reader, Rect.Height, Layer.PsdFile.IsLargeDocument);
var rleDataLength = (int)(endPosition - reader.BaseStream.Position);
Debug.Assert(rleDataLength == RleRowLengths.Total,
"RLE row lengths do not sum to length of channel image data.");
// The PSD specification states that rows are padded to even sizes.
// However, Photoshop doesn't actually do this. RLE rows can have
// odd lengths in the header, and there is no padding between rows.
ImageDataRaw = reader.ReadBytes(rleDataLength);
break;
case ImageCompression.Zip:
case ImageCompression.ZipPrediction:
ImageDataRaw = reader.ReadBytes(dataLength);
break;
}
Util.DebugMessage(reader.BaseStream, "Load, End, Channel image, {0}", ID, Layer.Name);
Debug.Assert(reader.BaseStream.Position == endPosition, "Pixel data was not fully read in.");
}
/// <summary>
/// Decodes the raw image data from the compressed on-disk format into
/// an uncompressed bitmap, in native byte order.
/// </summary>
public void DecodeImageData()
{
if ((ImageCompression == ImageCompression.Raw) && (Layer.PsdFile.BitDepth <= 8))
{
ImageData = new NativeArray<byte>(ImageDataRaw, Allocator.TempJob);
return;
}
var image = ImageDataFactory.Create(this, ImageDataRaw);
var longLength = (long)image.BytesPerRow * Rect.Height;
Util.CheckByteArrayLength(longLength);
var LocalImageData = new byte[longLength];
image.Read(LocalImageData);
ImageData = new NativeArray<byte>(LocalImageData, Allocator.TempJob);
ImageDataRaw = null; // no longer needed.
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c12f761461a76f44eab4f22f85e988b1
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,240 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using PDNWrapper;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace PhotoshopFile
{
[DebuggerDisplay("Name = {Name}")]
internal class Layer
{
internal PsdFile PsdFile { get; private set; }
/// <summary>
/// The rectangle containing the contents of the layer.
/// </summary>
public Rectangle Rect { get; set; }
public bool IsGroup { get; set; }
public bool IsEndGroupMarker { get; set; }
public Layer ParentLayer {get; set; }
// ID from Key "lyid"
public int LayerID { get; set; }
/// <summary>
/// Image channels.
/// </summary>
public ChannelList Channels { get; private set; }
/// <summary>
/// Returns alpha channel if it exists, otherwise null.
/// </summary>
public Channel AlphaChannel
{
get
{
if (Channels.ContainsId(-1))
return Channels.GetId(-1);
else
return null;
}
}
private string blendModeKey;
/// <summary>
/// Photoshop blend mode key for the layer
/// </summary>
public string BlendModeKey
{
get { return blendModeKey; }
set
{
if (value.Length != 4)
{
throw new ArgumentException(
"BlendModeKey must be 4 characters in length.");
}
blendModeKey = value;
}
}
/// <summary>
/// 0 = transparent ... 255 = opaque
/// </summary>
public byte Opacity { get; set; }
/// <summary>
/// false = base, true = non-base
/// </summary>
public bool Clipping { get; set; }
private static int protectTransBit = BitVector32.CreateMask();
private static int visibleBit = BitVector32.CreateMask(protectTransBit);
BitVector32 flags = new BitVector32();
/// <summary>
/// If true, the layer is visible.
/// </summary>
public bool Visible
{
get { return !flags[visibleBit]; }
set { flags[visibleBit] = !value; }
}
/// <summary>
/// Protect the transparency
/// </summary>
public bool ProtectTrans
{
get { return flags[protectTransBit]; }
set { flags[protectTransBit] = value; }
}
/// <summary>
/// The descriptive layer name
/// </summary>
public string Name { get; set; }
public BlendingRanges BlendingRangesData { get; set; }
public MaskInfo Masks { get; set; }
public List<LayerInfo> AdditionalInfo { get; set; }
///////////////////////////////////////////////////////////////////////////
public Layer(PsdFile psdFile)
{
PsdFile = psdFile;
Rect = Rectangle.Empty;
Channels = new ChannelList();
BlendModeKey = PsdBlendMode.Normal;
AdditionalInfo = new List<LayerInfo>();
IsGroup = false;
ParentLayer = null;
IsEndGroupMarker = false;
}
public Layer(PsdBinaryReader reader, PsdFile psdFile)
: this(psdFile)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, Layer");
Rect = reader.ReadRectangle();
//-----------------------------------------------------------------------
// Read channel headers. Image data comes later, after the layer header.
int numberOfChannels = reader.ReadUInt16();
for (int channel = 0; channel < numberOfChannels; channel++)
{
var ch = new Channel(reader, this);
Channels.Add(ch);
}
//-----------------------------------------------------------------------
//
var signature = reader.ReadAsciiChars(4);
if (signature != "8BIM")
throw (new PsdInvalidException("Invalid signature in layer header."));
BlendModeKey = reader.ReadAsciiChars(4);
Opacity = reader.ReadByte();
Clipping = reader.ReadBoolean();
var flagsByte = reader.ReadByte();
flags = new BitVector32(flagsByte);
reader.ReadByte(); //padding
//-----------------------------------------------------------------------
// This is the total size of the MaskData, the BlendingRangesData, the
// Name and the AdjustmentLayerInfo.
var extraDataSize = reader.ReadUInt32();
var extraDataStartPosition = reader.BaseStream.Position;
Masks = new MaskInfo(reader, this);
BlendingRangesData = new BlendingRanges(reader, this);
Name = reader.ReadPascalString(4);
//-----------------------------------------------------------------------
// Process Additional Layer Information
long adjustmentLayerEndPos = extraDataStartPosition + extraDataSize;
while (reader.BaseStream.Position < adjustmentLayerEndPos)
{
var layerInfo = LayerInfoFactory.Load(reader, this.PsdFile, false, adjustmentLayerEndPos);
AdditionalInfo.Add(layerInfo);
}
foreach (var adjustmentInfo in AdditionalInfo)
{
switch (adjustmentInfo.Key)
{
case "luni":
Name = ((LayerUnicodeName)adjustmentInfo).Name;
break;
case "lyid":
LayerID = ((LayerId)adjustmentInfo).ID;
break;
}
}
Util.DebugMessage(reader.BaseStream, "Load, End, Layer, {0}", Name);
PsdFile.LoadContext.OnLoadLayerHeader(this);
}
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Create ImageData for any missing channels.
/// </summary>
public void CreateMissingChannels()
{
var channelCount = this.PsdFile.ColorMode.MinChannelCount();
for (short id = 0; id < channelCount; id++)
{
if (!this.Channels.ContainsId(id))
{
var size = this.Rect.Height * this.Rect.Width;
var ch = new Channel(id, this);
ch.ImageData = new NativeArray<byte>(size, Allocator.TempJob);
unsafe
{
UnsafeUtility.MemSet(ch.ImageData.GetUnsafePtr(), (byte)255, size);
}
this.Channels.Add(ch);
}
}
}
///////////////////////////////////////////////////////////////////////////
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2dff4719950c8914097f7b77c9b07de9
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,168 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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.Diagnostics;
using System.IO;
namespace PhotoshopFile
{
internal static class LayerInfoFactory
{
/// <summary>
/// Loads the next LayerInfo record.
/// </summary>
/// <param name="reader">The file reader</param>
/// <param name="psdFile">The PSD file.</param>
/// <param name="globalLayerInfo">True if the LayerInfo record is being
/// loaded from the end of the Layer and Mask Information section;
/// false if it is being loaded from the end of a Layer record.</param>
public static LayerInfo Load(PsdBinaryReader reader, PsdFile psdFile,
bool globalLayerInfo, long fileEndPos)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, LayerInfo");
// Some keys use a signature of 8B64, but the identity of these keys
// is undocumented. We will therefore accept either signature.
var signature = reader.ReadAsciiChars(4);
if ((signature != "8BIM") && (signature != "8B64"))
{
throw new PsdInvalidException(
"LayerInfo signature invalid, must be 8BIM or 8B64.");
}
var key = reader.ReadAsciiChars(4);
var hasLongLength = LayerInfoUtil.HasLongLength(key, psdFile.IsLargeDocument);
LayerInfo result = new RawLayerInfo("dummy");
bool breakFromLoop = false;
while (!breakFromLoop)
{
var baseStartPosition = reader.BaseStream.Position;
var length = hasLongLength
? reader.ReadInt64()
: reader.ReadInt32();
var startPosition = reader.BaseStream.Position;
switch (key)
{
case "Layr":
case "Lr16":
case "Lr32":
result = new InfoLayers(reader, psdFile, key, length);
break;
case "lsct":
case "lsdk":
result = new LayerSectionInfo(reader, key, (int)length);
break;
case "luni":
result = new LayerUnicodeName(reader);
break;
case "lyid":
result = new LayerId(reader, key, length);
break;
default:
result = new RawLayerInfo(reader, signature, key, length);
break;
}
// May have additional padding applied.
var endPosition = startPosition + length;
if (reader.BaseStream.Position < endPosition)
reader.BaseStream.Position = endPosition;
// Documentation states that the length is even-padded. Actually:
// 1. Most keys have 4-padded lengths.
// 2. However, some keys (LMsk) have even-padded lengths.
// 3. Other keys (Txt2, Lr16, Lr32) have unpadded lengths.
//
// Photoshop writes data that is always 4-padded, even when the stated
// length is not a multiple of 4. The length mismatch seems to occur
// only on global layer info. We do not read extra padding in other
// cases because third-party programs are likely to follow the spec.
if (globalLayerInfo)
{
reader.ReadPadding(startPosition, 4);
}
//try if we can read the next signature
if (reader.BaseStream.Position < fileEndPos)
{
var nowPosition = reader.BaseStream.Position;
signature = reader.ReadAsciiChars(4);
if ((signature != "8BIM") && (signature != "8B64"))
{
hasLongLength = true;
reader.BaseStream.Position = baseStartPosition;
}
else
{
reader.BaseStream.Position = nowPosition;
breakFromLoop = true;
}
}
else
breakFromLoop = true;
}
Util.DebugMessage(reader.BaseStream, "Load, End, LayerInfo, {0}, {1}",
result.Signature, result.Key);
return result;
}
}
internal static class LayerInfoUtil
{
internal static bool HasLongLength(string key, bool isLargeDocument)
{
if (!isLargeDocument)
{
return false;
}
//return false;
switch (key)
{
case "LMsk":
case "Lr16":
case "Lr32":
case "Layr":
case "Mt16":
case "Mt32":
case "Mtrn":
case "Alph":
case "FMsk":
case "lnk2":
case "FEid":
case "FXid":
case "PxSD":
case "lnkE": // Undocumented
case "extn": // Undocumented
case "cinf": // Undocumented
return true;
default:
return false;
}
}
}
internal abstract class LayerInfo
{
public abstract string Signature { get; }
public abstract string Key { get; }
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 5bf52a9b627c3374385cbd0ccc5caacd
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 45831abf313417241b91683b30dedf45
folderAsset: yes
timeCreated: 1495006553
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,87 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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.Linq;
using System.Text;
namespace PhotoshopFile
{
/// <summary>
/// Layers that are stored as Additional Info, rather than in the main
/// Layers section of the PSD file.
/// </summary>
/// <remarks>
/// Photoshop stores layers in the Additional Info section for 16-bit and
/// 32-bit depth images. The Layers section in the PSD file is left empty.
///
/// This appears to be for backward-compatibility purposes, but it is not
/// required. Photoshop will successfully load a high-bitdepth image that
/// puts the layers in the Layers section.
/// </remarks>
internal class InfoLayers : LayerInfo
{
public override string Signature
{
get { return "8BIM"; }
}
private string key;
public override string Key
{
get { return key; }
}
public PsdFile PsdFile { get; set; }
public InfoLayers(PsdFile psdFile, string key)
{
PsdFile = psdFile;
switch (key)
{
// The key does not have to match the bit depth, but it does have to
// be one of the known values.
case "Layr":
case "Lr16":
case "Lr32":
this.key = key;
break;
default:
throw new PsdInvalidException(
"InfoLayers key must be Layr, Lr16, or Lr32.");
}
}
public InfoLayers(PsdBinaryReader reader, PsdFile psdFile,
string key, long dataLength)
: this(psdFile, key)
{
if (psdFile.Layers.Count > 0)
{
throw new PsdInvalidException(
"Cannot have both regular layers and Additional Info layers");
}
var endPosition = reader.BaseStream.Position + dataLength;
psdFile.LoadLayers(reader, false);
if (reader.BaseStream.Position != endPosition)
{
throw new PsdInvalidException(
"Incorrect length for InfoLayers.");
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 81ec2894805ff534395009ab7c13ea4c
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,71 @@
/////////////////////////////////////////////////////////////////////////////////
// Author : leoyaik@unity3d.com
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace PhotoshopFile
{
/// <summary>
/// Layers that are stored as Additional Info, rather than in the main
/// Layers section of the PSD file.
/// </summary>
/// <remarks>
/// Photoshop stores layers in the Additional Info section for 16-bit and
/// 32-bit depth images. The Layers section in the PSD file is left empty.
///
/// This appears to be for backward-compatibility purposes, but it is not
/// required. Photoshop will successfully load a high-bitdepth image that
/// puts the layers in the Layers section.
/// </remarks>
internal class LayerId : LayerInfo
{
public override string Signature
{
get { return "8BIM"; }
}
private string key;
public override string Key
{
get { return key; }
}
private int id = 0;
public int ID
{
get { return id; }
}
public LayerId(string key)
{
switch (key)
{
// The key does not have to match the bit depth, but it does have to
// be one of the known values.
case "lyid":
case "lnsr":
this.key = key;
break;
default:
throw new PsdInvalidException(
"LayerId key should be lyid or lnsr");
}
}
public LayerId(PsdBinaryReader reader,
string key, long dataLength)
: this(key)
{
if (dataLength == 4)
id = reader.ReadInt32();
else
throw new PsdInvalidException("LayerId data length should be 4");
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c40067c9a690e44ca098f5e4ed763e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,94 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2015 Tao Yue
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
namespace PhotoshopFile
{
internal enum LayerSectionType
{
Layer = 0,
OpenFolder = 1,
ClosedFolder = 2,
SectionDivider = 3
}
internal enum LayerSectionSubtype
{
Normal = 0,
SceneGroup = 1
}
/// <summary>
/// Layer sections are known as Groups in the Photoshop UI.
/// </summary>
internal class LayerSectionInfo : LayerInfo
{
public override string Signature
{
get { return "8BIM"; }
}
private string key;
public override string Key
{
get { return key; }
}
public LayerSectionType SectionType { get; set; }
private LayerSectionSubtype? subtype;
public LayerSectionSubtype Subtype
{
get { return subtype ?? LayerSectionSubtype.Normal; }
set { subtype = value; }
}
private string blendModeKey;
public string BlendModeKey
{
get { return blendModeKey; }
set
{
if (value.Length != 4)
{
throw new ArgumentException(
"BlendModeKey must be 4 characters in length.");
}
blendModeKey = value;
}
}
public LayerSectionInfo(PsdBinaryReader reader, string key, int dataLength)
{
// The key for layer section info is documented to be "lsct". However,
// some Photoshop files use the undocumented key "lsdk", with apparently
// the same data format.
this.key = key;
SectionType = (LayerSectionType)reader.ReadInt32();
if (dataLength >= 12)
{
var signature = reader.ReadAsciiChars(4);
if (signature != "8BIM")
throw new PsdInvalidException("Invalid section divider signature.");
BlendModeKey = reader.ReadAsciiChars(4);
if (dataLength >= 16)
{
Subtype = (LayerSectionSubtype)reader.ReadInt32();
}
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 4ca20fe44ab2bfd4d8a1c3547b4a136f
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,42 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2014 Tao Yue
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
namespace PhotoshopFile
{
internal class LayerUnicodeName : LayerInfo
{
public override string Signature
{
get { return "8BIM"; }
}
public override string Key
{
get { return "luni"; }
}
public string Name { get; set; }
public LayerUnicodeName(string name)
{
Name = name;
}
public LayerUnicodeName(PsdBinaryReader reader)
{
Name = reader.ReadUnicodeString();
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 277ac9726bb060c48860d92018fc06cf
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,52 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2014 Tao Yue
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Diagnostics;
namespace PhotoshopFile
{
[DebuggerDisplay("Layer Info: { key }")]
internal class RawLayerInfo : LayerInfo
{
private string signature;
public override string Signature
{
get { return signature; }
}
private string key;
public override string Key
{
get { return key; }
}
public byte[] Data { get; private set; }
public RawLayerInfo(string key, string signature = "8BIM")
{
this.signature = signature;
this.key = key;
}
public RawLayerInfo(PsdBinaryReader reader, string signature, string key,
long dataLength)
{
this.signature = signature;
this.key = key;
Util.CheckByteArrayLength(dataLength);
Data = reader.ReadBytes((int)dataLength);
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8b73fc997c117d641a1673cd96fc8a07
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,149 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2014 Tao Yue
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Specialized;
using System.Diagnostics;
using PDNWrapper;
using System.Globalization;
using Unity.Collections;
namespace PhotoshopFile
{
internal class Mask
{
/// <summary>
/// The layer to which this mask belongs
/// </summary>
public Layer Layer { get; private set; }
/// <summary>
/// The rectangle enclosing the mask.
/// </summary>
public Rectangle Rect { get; set; }
private byte backgroundColor;
public byte BackgroundColor
{
get { return backgroundColor; }
set
{
if ((value != 0) && (value != 255))
throw new PsdInvalidException("Mask background must be fully-opaque or fully-transparent.");
backgroundColor = value;
}
}
private static int positionVsLayerBit = BitVector32.CreateMask();
private static int disabledBit = BitVector32.CreateMask(positionVsLayerBit);
private static int invertOnBlendBit = BitVector32.CreateMask(disabledBit);
private BitVector32 flags;
public BitVector32 Flags { get { return flags; } }
/// <summary>
/// If true, the position of the mask is relative to the layer.
/// </summary>
public bool PositionVsLayer
{
get { return flags[positionVsLayerBit]; }
set { flags[positionVsLayerBit] = value; }
}
public bool Disabled
{
get { return flags[disabledBit]; }
set { flags[disabledBit] = value; }
}
/// <summary>
/// if true, invert the mask when blending.
/// </summary>
public bool InvertOnBlend
{
get { return flags[invertOnBlendBit]; }
set { flags[invertOnBlendBit] = value; }
}
/// <summary>
/// Mask image data.
/// </summary>
public NativeArray<byte> ImageData { get; set; }
public Mask(Layer layer)
{
Layer = layer;
this.flags = new BitVector32();
}
public Mask(Layer layer, Rectangle rect, byte color, BitVector32 flags)
{
Layer = layer;
Rect = rect;
BackgroundColor = color;
this.flags = flags;
}
}
/// <summary>
/// Mask info for a layer. Contains both the layer and user masks.
/// </summary>
internal class MaskInfo
{
public Mask LayerMask { get; set; }
public Mask UserMask { get; set; }
/// <summary>
/// Construct MaskInfo with null masks.
/// </summary>
public MaskInfo()
{
}
public MaskInfo(PsdBinaryReader reader, Layer layer)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, MaskInfo");
var maskLength = reader.ReadUInt32();
if (maskLength <= 0)
return;
var startPosition = reader.BaseStream.Position;
var endPosition = startPosition + maskLength;
// Read layer mask
var rectangle = reader.ReadRectangle();
var backgroundColor = reader.ReadByte();
var flagsByte = reader.ReadByte();
LayerMask = new Mask(layer, rectangle, backgroundColor, new BitVector32(flagsByte));
// User mask is supplied separately when there is also a vector mask.
if (maskLength == 36)
{
var userFlagsByte = reader.ReadByte();
var userBackgroundColor = reader.ReadByte();
var userRectangle = reader.ReadRectangle();
UserMask = new Mask(layer, userRectangle, userBackgroundColor, new BitVector32(userFlagsByte));
}
// 20-byte mask data will end with padding.
reader.BaseStream.Position = endPosition;
Util.DebugMessage(reader.BaseStream, "Load, End, MaskInfo");
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2b6148b9ac6738b4999b4686f3820560
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,44 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PhotoshopFile
{
/// <summary>
/// Contains settings and callbacks that affect the loading of a PSD file.
/// </summary>
internal class LoadContext
{
public Encoding Encoding { get; set; }
public LoadContext()
{
Encoding = Encoding.Default;
}
public virtual void OnLoadLayersHeader(PsdFile psdFile)
{
}
public virtual void OnLoadLayerHeader(Layer layer)
{
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3b268b1ee06ace349a872a7d95498aba
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,229 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2013 Tao Yue
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using PDNWrapper;
using System.IO;
using System.Text;
namespace PhotoshopFile
{
/// <summary>
/// Reads PSD data types in big-endian byte order.
/// </summary>
internal class PsdBinaryReader : IDisposable
{
private BinaryReader reader;
private Encoding encoding;
public Stream BaseStream
{
get { return reader.BaseStream; }
}
public PsdBinaryReader(Stream stream, PsdBinaryReader reader)
: this(stream, reader.encoding)
{
}
public PsdBinaryReader(Stream stream, Encoding encoding)
{
this.encoding = encoding;
// ReadPascalString and ReadUnicodeString handle encoding explicitly.
// BinaryReader.ReadString() is never called, so it is constructed with
// ASCII encoding to make accidental usage obvious.
reader = new BinaryReader(stream, Encoding.ASCII);
}
public byte ReadByte()
{
return reader.ReadByte();
}
public byte[] ReadBytes(int count)
{
return reader.ReadBytes(count);
}
public bool ReadBoolean()
{
return reader.ReadBoolean();
}
public Int16 ReadInt16()
{
var val = reader.ReadInt16();
byte[] b = BitConverter.GetBytes(val);
{
Util.SwapBytes(b, 0, 2);
}
val = BitConverter.ToInt16(b, 0);
return val;
}
public Int32 ReadInt32()
{
var val = reader.ReadInt32();
byte[] b = BitConverter.GetBytes(val);
{
Util.SwapBytes(b, 0, 4);
}
val = BitConverter.ToInt32(b, 0);
return val;
}
public Int64 ReadInt64()
{
var val = reader.ReadInt64();
var b = BitConverter.GetBytes(val);
{
Util.SwapBytes(b, 0, 8);
}
val = BitConverter.ToInt64(b, 0);
return val;
}
public UInt16 ReadUInt16()
{
var val = reader.ReadUInt16();
var b = BitConverter.GetBytes(val);
{
Util.SwapBytes(b, 0, 2);
}
val = BitConverter.ToUInt16(b, 0);
return val;
}
public UInt32 ReadUInt32()
{
var val = reader.ReadUInt32();
var b = BitConverter.GetBytes(val);
{
Util.SwapBytes(b, 0, 4);
}
val = BitConverter.ToUInt32(b, 0);
return val;
}
public UInt64 ReadUInt64()
{
var val = reader.ReadUInt64();
var b = BitConverter.GetBytes(val);
{
Util.SwapBytes(b, 0, 8);
}
val = BitConverter.ToUInt64(b, 0);
return val;
}
//////////////////////////////////////////////////////////////////
/// <summary>
/// Read padding to get to the byte multiple for the block.
/// </summary>
/// <param name="startPosition">Starting position of the padded block.</param>
/// <param name="padMultiple">Byte multiple that the block is padded to.</param>
public void ReadPadding(long startPosition, int padMultiple)
{
// Pad to specified byte multiple
var totalLength = reader.BaseStream.Position - startPosition;
var padBytes = Util.GetPadding((int)totalLength, padMultiple);
ReadBytes(padBytes);
}
public Rectangle ReadRectangle()
{
var rect = new Rectangle();
rect.Y = ReadInt32();
rect.X = ReadInt32();
rect.Height = ReadInt32() - rect.Y;
rect.Width = ReadInt32() - rect.X;
return rect;
}
/// <summary>
/// Read a fixed-length ASCII string.
/// </summary>
public string ReadAsciiChars(int count)
{
var bytes = reader.ReadBytes(count);
var s = Encoding.ASCII.GetString(bytes);
return s;
}
/// <summary>
/// Read a Pascal string using the specified encoding.
/// </summary>
/// <param name="padMultiple">Byte multiple that the Pascal string is padded to.</param>
public string ReadPascalString(int padMultiple)
{
var startPosition = reader.BaseStream.Position;
byte stringLength = ReadByte();
var bytes = ReadBytes(stringLength);
ReadPadding(startPosition, padMultiple);
// Default decoder uses best-fit fallback, so it will not throw any
// exceptions if unknown characters are encountered.
var str = encoding.GetString(bytes);
return str;
}
public string ReadUnicodeString()
{
var numChars = ReadInt32();
var length = 2 * numChars;
var data = ReadBytes(length);
var str = Encoding.BigEndianUnicode.GetString(data, 0, length);
return str;
}
//////////////////////////////////////////////////////////////////
#region IDisposable
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (disposed)
return;
if (disposing)
{
if (reader != null)
{
// BinaryReader.Dispose() is protected.
reader.Close();
reader = null;
}
}
disposed = true;
}
#endregion
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9614f1c9b6a2eb5499429bfab13a0c09
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,52 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2012 Tao Yue
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PhotoshopFile
{
internal static class PsdBlendMode
{
public const string Normal = "norm";
public const string Darken = "dark";
public const string Lighten = "lite";
public const string Hue = "hue ";
public const string Saturation = "sat ";
public const string Color = "colr";
public const string Luminosity = "lum ";
public const string Multiply = "mul ";
public const string Screen = "scrn";
public const string Dissolve = "diss";
public const string Overlay = "over";
public const string HardLight = "hLit";
public const string SoftLight = "sLit";
public const string Difference = "diff";
public const string Exclusion = "smud";
public const string ColorDodge = "div ";
public const string ColorBurn = "idiv";
public const string LinearBurn = "lbrn";
public const string LinearDodge = "lddg";
public const string VividLight = "vLit";
public const string LinearLight = "lLit";
public const string PinLight = "pLit";
public const string HardMix = "hMix";
public const string PassThrough = "pass";
public const string DarkerColor = "dkCl";
public const string LighterColor = "lgCl";
public const string Subtract = "fsub";
public const string Divide = "fdiv";
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c4e78ec955914854d9c9c68fa7af1742
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,708 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// 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 PaintDotNet.Data.PhotoshopFileType;
namespace PhotoshopFile
{
[Flags]
internal enum ELoadFlag
{
Header = 1,
ColorMode = Header | 1 << 2,
ImageData = ColorMode | 1 << 3,
All = Header | ColorMode | ImageData
}
internal enum PsdColorMode
{
Bitmap = 0,
Grayscale = 1,
Indexed = 2,
RGB = 3,
CMYK = 4,
Multichannel = 7,
Duotone = 8,
Lab = 9
};
internal enum PsdFileVersion : short
{
Psd = 1,
PsbLargeDocument = 2
}
internal class PsdFile
{
#region Constructors
ELoadFlag m_LoadFlag;
public PsdFile(PsdFileVersion version = PsdFileVersion.Psd)
{
Version = version;
BaseLayer = new Layer(this);
ImageResources = new ImageResources();
Layers = new List<Layer>();
AdditionalInfo = new List<LayerInfo>();
}
public PsdFile(string filename, LoadContext loadContext, ELoadFlag loadFlag = ELoadFlag.All)
: this()
{
using (var stream = new FileStream(filename, FileMode.Open))
{
Load(stream, loadContext, loadFlag);
}
}
public PsdFile(Stream stream, LoadContext loadContext, ELoadFlag loadFlag = ELoadFlag.All)
: this()
{
Load(stream, loadContext, loadFlag);
}
#endregion
#region Load and save
internal LoadContext LoadContext { get; private set; }
private void Load(Stream stream, LoadContext loadContext, ELoadFlag loadFlag)
{
LoadContext = loadContext;
var reader = new PsdBinaryReader(stream, loadContext.Encoding);
if ((loadFlag & ELoadFlag.Header) == ELoadFlag.Header)
LoadHeader(reader);
if ((loadFlag & ELoadFlag.ColorMode) == ELoadFlag.ColorMode)
LoadColorModeData(reader);
if ((loadFlag & ELoadFlag.ImageData) == ELoadFlag.ImageData)
{
LoadImageResources(reader);
LoadLayerAndMaskInfo(reader);
LoadImage(reader);
DecompressImages();
}
}
#endregion
#region Header
/// <summary>
/// Photoshop file format version.
/// </summary>
public PsdFileVersion Version { get; private set; }
public bool IsLargeDocument
{
get { return Version == PsdFileVersion.PsbLargeDocument; }
}
private Int16 channelCount;
/// <summary>
/// The number of channels in the image, including any alpha channels.
/// </summary>
public Int16 ChannelCount
{
get { return channelCount; }
set
{
if (value < 1 || value > 56)
throw new ArgumentException("Number of channels must be from 1 to 56.");
channelCount = value;
}
}
private void CheckDimension(int dimension)
{
if (dimension < 1)
{
throw new ArgumentException("Image dimension must be at least 1.");
}
if ((Version == PsdFileVersion.Psd) && (dimension > 30000))
{
throw new ArgumentException("PSD image dimension cannot exceed 30000.");
}
if ((Version == PsdFileVersion.PsbLargeDocument) && (dimension > 300000))
{
throw new ArgumentException("PSB image dimension cannot exceed 300000.");
}
}
/// <summary>
/// The height of the image in pixels.
/// </summary>
public int RowCount
{
get { return this.BaseLayer.Rect.Height; }
set
{
CheckDimension(value);
BaseLayer.Rect = new Rectangle(0, 0, BaseLayer.Rect.Width, value);
}
}
/// <summary>
/// The width of the image in pixels.
/// </summary>
public int ColumnCount
{
get { return this.BaseLayer.Rect.Width; }
set
{
CheckDimension(value);
BaseLayer.Rect = new Rectangle(0, 0, value, BaseLayer.Rect.Height);
}
}
private int bitDepth;
/// <summary>
/// The number of bits per channel. Supported values are 1, 8, 16, and 32.
/// </summary>
public int BitDepth
{
get { return bitDepth; }
set
{
switch (value)
{
case 1:
case 8:
case 16:
case 32:
bitDepth = value;
break;
default:
throw new NotImplementedException("Invalid bit depth.");
}
}
}
/// <summary>
/// The color mode of the file.
/// </summary>
public PsdColorMode ColorMode { get; set; }
///////////////////////////////////////////////////////////////////////////
private void LoadHeader(PsdBinaryReader reader)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, File header");
var signature = reader.ReadAsciiChars(4);
if (signature != "8BPS")
throw new PsdInvalidException("The given stream is not a valid PSD file");
Version = (PsdFileVersion)reader.ReadInt16();
Util.DebugMessage(reader.BaseStream, "Load, Info, Version {0}", (int)Version);
if ((Version != PsdFileVersion.Psd)
&& (Version != PsdFileVersion.PsbLargeDocument))
{
throw new PsdInvalidException("The PSD file has an unknown version");
}
//6 bytes reserved
reader.BaseStream.Position += 6;
this.ChannelCount = reader.ReadInt16();
this.RowCount = reader.ReadInt32();
this.ColumnCount = reader.ReadInt32();
BitDepth = reader.ReadInt16();
ColorMode = (PsdColorMode)reader.ReadInt16();
Util.DebugMessage(reader.BaseStream, "Load, End, File header");
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region ColorModeData
/// <summary>
/// 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.
/// </summary>
public byte[] ColorModeData = new byte[0];
private void LoadColorModeData(PsdBinaryReader reader)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, ColorModeData");
var paletteLength = reader.ReadUInt32();
if (paletteLength > 0)
{
ColorModeData = reader.ReadBytes((int)paletteLength);
}
Util.DebugMessage(reader.BaseStream, "Load, End, ColorModeData");
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region ImageResources
/// <summary>
/// The Image resource blocks for the file
/// </summary>
public ImageResources ImageResources { get; set; }
public ResolutionInfo Resolution
{
get
{
return (ResolutionInfo)ImageResources.Get(ResourceID.ResolutionInfo);
}
set
{
ImageResources.Set(value);
}
}
///////////////////////////////////////////////////////////////////////////
private void LoadImageResources(PsdBinaryReader reader)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, ImageResources");
var imageResourcesLength = reader.ReadUInt32();
if (imageResourcesLength <= 0)
return;
var startPosition = reader.BaseStream.Position;
var endPosition = startPosition + imageResourcesLength;
while (reader.BaseStream.Position < endPosition)
{
var imageResource = ImageResourceFactory.CreateImageResource(reader);
ImageResources.Add(imageResource);
}
Util.DebugMessage(reader.BaseStream, "Load, End, ImageResources");
//-----------------------------------------------------------------------
// make sure we are not on a wrong offset, so set the stream position
// manually
reader.BaseStream.Position = startPosition + imageResourcesLength;
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region LayerAndMaskInfo
public List<Layer> Layers { get; private set; }
public List<LayerInfo> AdditionalInfo { get; private set; }
public bool AbsoluteAlpha { get; set; }
///////////////////////////////////////////////////////////////////////////
private void LoadLayerAndMaskInfo(PsdBinaryReader reader)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, Layer and mask info");
var layersAndMaskLength = IsLargeDocument
? reader.ReadInt64()
: reader.ReadUInt32();
if (layersAndMaskLength <= 0)
return;
var startPosition = reader.BaseStream.Position;
var endPosition = startPosition + layersAndMaskLength;
LoadLayers(reader, true);
LoadGlobalLayerMask(reader);
//-----------------------------------------------------------------------
// Load Additional Layer Information
while (reader.BaseStream.Position < endPosition)
{
var info = LayerInfoFactory.Load(reader, this, true, endPosition);
AdditionalInfo.Add(info);
if (info is RawLayerInfo)
{
var layerInfo = (RawLayerInfo)info;
switch (info.Key)
{
case "LMsk":
m_GlobalLayerMaskData = layerInfo.Data;
break;
}
}
}
Util.DebugMessage(reader.BaseStream, "Load, End, Layer and mask info");
//-----------------------------------------------------------------------
// make sure we are not on a wrong offset, so set the stream position
// manually
reader.BaseStream.Position = startPosition + layersAndMaskLength;
}
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Load Layers Info section, including image data.
/// </summary>
/// <param name="reader">PSD reader.</param>
/// <param name="hasHeader">Whether the Layers Info section has a length header.</param>
internal void LoadLayers(PsdBinaryReader reader, bool hasHeader)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, Layers Info section");
long sectionLength = 0;
if (hasHeader)
{
sectionLength = IsLargeDocument
? reader.ReadInt64()
: reader.ReadUInt32();
if (sectionLength <= 0)
{
// The callback may take action when there are 0 layers, so it must
// be called even though the Layers Info section is empty.
LoadContext.OnLoadLayersHeader(this);
Util.DebugMessage(reader.BaseStream, "Load, End, Layers Info section");
return;
}
}
var startPosition = reader.BaseStream.Position;
var numLayers = reader.ReadInt16();
// If numLayers < 0, then number of layers is absolute value,
// and the first alpha channel contains the transparency data for
// the merged result.
if (numLayers < 0)
{
AbsoluteAlpha = true;
numLayers = Math.Abs(numLayers);
}
for (int i = 0; i < numLayers; i++)
{
var layer = new Layer(reader, this);
Layers.Add(layer);
}
// Header is complete just before loading pixel data
LoadContext.OnLoadLayersHeader(this);
//-----------------------------------------------------------------------
// Load image data for all channels.
foreach (var layer in Layers)
{
Util.DebugMessage(reader.BaseStream,
"Load, Begin, Layer image, layer.Name");
foreach (var channel in layer.Channels)
{
channel.LoadPixelData(reader);
}
Util.DebugMessage(reader.BaseStream,
"Load, End, Layer image, layer.Name");
}
// Length is set to 0 when called on higher bitdepth layers.
if (sectionLength > 0)
{
// Layers Info section is documented to be even-padded, but Photoshop
// actually pads to 4 bytes.
var endPosition = startPosition + sectionLength;
var positionOffset = reader.BaseStream.Position - endPosition;
Debug.Assert(positionOffset > -4,
"LoadLayers did not read the full length of the Layers Info section.");
Debug.Assert(positionOffset <= 0,
"LoadLayers read past the end of the Layers Info section.");
if (reader.BaseStream.Position < endPosition)
reader.BaseStream.Position = endPosition;
}
Util.DebugMessage(reader.BaseStream, "Load, End, Layers");
}
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Cleanup
/// </summary>
public void Cleanup()
{
var layersAndComposite = Layers.Concat(new[] { BaseLayer });
foreach (var lac in layersAndComposite)
{
foreach (var c in lac.Channels)
{
c.Cleanup();
}
}
}
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Decompress the document image data and all the layers' image data, in parallel.
/// </summary>
private void DecompressImages()
{
var layersAndComposite = Layers.Concat(new[] { BaseLayer });
//var channels = layersAndComposite.SelectMany(x => x.Channels);
//Parallel.ForEach(channels, channel =>
//{
// channel.DecodeImageData();
//});
foreach (var lac in layersAndComposite)
{
foreach (var c in lac.Channels)
{
c.DecodeImageData();
}
}
foreach (var layer in Layers)
{
foreach (var channel in layer.Channels)
{
if (channel.ID == -2)
layer.Masks.LayerMask.ImageData = channel.ImageData;
else if (channel.ID == -3)
layer.Masks.UserMask.ImageData = channel.ImageData;
}
}
}
/// <summary>
/// Verifies that any Additional Info layers are consistent.
/// </summary>
private void VerifyInfoLayers()
{
var infoLayersCount = AdditionalInfo.Count(x => x is InfoLayers);
if (infoLayersCount > 1)
{
throw new PsdInvalidException(
"Cannot have more than one InfoLayers in a PSD file.");
}
if ((infoLayersCount > 0) && (Layers.Count == 0))
{
throw new PsdInvalidException(
"InfoLayers cannot exist when there are 0 layers.");
}
}
/// <summary>
/// Verify validity of layer sections. Each start marker should have a
/// matching end marker.
/// </summary>
internal void VerifyLayerSections()
{
int depth = 0;
foreach (var layer in Enumerable.Reverse(Layers))
{
var layerSectionInfo = layer.AdditionalInfo.SingleOrDefault(
x => x is LayerSectionInfo);
if (layerSectionInfo == null)
continue;
var sectionInfo = (LayerSectionInfo)layerSectionInfo;
switch (sectionInfo.SectionType)
{
case LayerSectionType.OpenFolder:
case LayerSectionType.ClosedFolder:
depth++;
break;
case LayerSectionType.SectionDivider:
depth--;
if (depth < 0)
throw new PsdInvalidException("Layer section ended without matching start marker.");
break;
default:
throw new PsdInvalidException("Unrecognized layer section type.");
}
}
if (depth != 0)
throw new PsdInvalidException("Layer section not closed by end marker.");
}
/// <summary>
/// Set the VersionInfo resource on the file.
/// </summary>
public void SetVersionInfo()
{
var versionInfo = (VersionInfo)ImageResources.Get(ResourceID.VersionInfo);
if (versionInfo == null)
{
versionInfo = new VersionInfo();
ImageResources.Set(versionInfo);
// Get the version string. We don't use the fourth part (revision).
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var version = assembly.GetName().Version;
var versionString = version.Major + "." + version.Minor + "." + version.Build;
// Strings are not localized since they are not shown to the user.
versionInfo.Version = 1;
versionInfo.HasRealMergedData = true;
versionInfo.ReaderName = "Paint.NET PSD Plugin";
versionInfo.WriterName = "Paint.NET PSD Plugin " + versionString;
versionInfo.FileVersion = 1;
}
}
///////////////////////////////////////////////////////////////////////////
byte[] m_GlobalLayerMaskData;
private void LoadGlobalLayerMask(PsdBinaryReader reader)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, GlobalLayerMask");
var maskLength = reader.ReadUInt32();
if (maskLength <= 0)
{
Util.DebugMessage(reader.BaseStream, "Load, End, GlobalLayerMask");
return;
}
m_GlobalLayerMaskData = reader.ReadBytes((int)maskLength);
Util.DebugMessage(reader.BaseStream, "Load, End, GlobalLayerMask");
}
public byte[] globalLayerMaskData
{
get { return m_GlobalLayerMaskData; }
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region Composite image
/// <summary>
/// Represents the composite image.
/// </summary>
public Layer BaseLayer { get; set; }
public ImageCompression ImageCompression { get; set; }
private void LoadImage(PsdBinaryReader reader)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, Composite image");
ImageCompression = (ImageCompression)reader.ReadInt16();
// Create channels
for (Int16 i = 0; i < ChannelCount; i++)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, Channel image data");
var channel = new Channel(i, this.BaseLayer);
channel.ImageCompression = ImageCompression;
channel.Length = this.RowCount
* Util.BytesPerRow(BaseLayer.Rect.Size, BitDepth);
// The composite image stores all RLE headers up-front, rather than
// with each channel.
if (ImageCompression == ImageCompression.Rle)
{
channel.RleRowLengths = new RleRowLengths(reader, RowCount, IsLargeDocument);
channel.Length = channel.RleRowLengths.Total;
}
BaseLayer.Channels.Add(channel);
Util.DebugMessage(reader.BaseStream, "Load, End, Channel image data");
}
foreach (var channel in this.BaseLayer.Channels)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, Channel image data");
Util.CheckByteArrayLength(channel.Length);
channel.ImageDataRaw = reader.ReadBytes((int)channel.Length);
Util.DebugMessage(reader.BaseStream, "Load, End, Channel image data");
}
// If there is exactly one more channel than we need, then it is the
// alpha channel.
if ((ColorMode != PsdColorMode.Multichannel)
&& (ChannelCount == ColorMode.MinChannelCount() + 1))
{
var alphaChannel = BaseLayer.Channels.Last();
alphaChannel.ID = -1;
}
Util.DebugMessage(reader.BaseStream, "Load, End, Composite image");
}
#endregion
}
/// <summary>
/// The possible Compression methods.
/// </summary>
internal enum ImageCompression
{
/// <summary>
/// Raw data
/// </summary>
Raw = 0,
/// <summary>
/// RLE compressed
/// </summary>
Rle = 1,
/// <summary>
/// ZIP without prediction.
/// </summary>
Zip = 2,
/// <summary>
/// ZIP with prediction.
/// </summary>
ZipPrediction = 3
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3de656e186ce0704e8676716fcaf733f
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,106 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2013 Tao Yue
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Diagnostics;
using System.IO;
namespace PhotoshopFile
{
internal class RleReader
{
private Stream stream;
public RleReader(Stream stream)
{
this.stream = stream;
}
/// <summary>
/// Decodes a PackBits RLE stream.
/// </summary>
/// <param name="buffer">Output buffer for decoded data.</param>
/// <param name="offset">Offset at which to begin writing.</param>
/// <param name="count">Number of bytes to decode from the stream.</param>
public int Read(byte[] buffer, int offset, int count)
{
if (!Util.CheckBufferBounds(buffer, offset, count))
throw new ArgumentOutOfRangeException();
// Pin the entire buffer now, so that we don't keep pinning and unpinning
// for each RLE packet.
var ptrBuffer = buffer;
//fixed (byte* ptrBuffer = &buffer[0])
{
int bytesLeft = count;
int bufferIdx = offset;
while (bytesLeft > 0)
{
// ReadByte returns an unsigned byte, but we want a signed byte.
var flagCounter = unchecked((sbyte)stream.ReadByte());
// Raw packet
if (flagCounter > 0)
{
var readLength = flagCounter + 1;
if (bytesLeft < readLength)
throw new RleException("Raw packet overruns the decode window.");
stream.Read(buffer, bufferIdx, readLength);
bufferIdx += readLength;
bytesLeft -= readLength;
}
// RLE packet
else if (flagCounter > -128)
{
var runLength = 1 - flagCounter;
var byteValue = (byte)stream.ReadByte();
if (runLength > bytesLeft)
throw new RleException("RLE packet overruns the decode window.");
//byte* ptr = ptrBuffer + bufferIdx;
//byte* ptrEnd = ptr + runLength;
//while (ptr < ptrEnd)
//{
// *ptr = byteValue;
// ptr++;
//}
int start = 0;
while (start < runLength)
{
ptrBuffer[bufferIdx + start] = byteValue;
start++;
}
bufferIdx += runLength;
bytesLeft -= runLength;
}
else
{
// The canonical PackBits algorithm will never emit 0x80 (-128), but
// some programs do. Simply skip over the byte.
}
}
Debug.Assert(bytesLeft == 0);
return count - bytesLeft;
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f9404b28690a74b4f918e7006968dd80
timeCreated: 1495006554
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,50 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2014 Tao Yue
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Linq;
namespace PhotoshopFile
{
internal class RleRowLengths
{
public int[] Values { get; private set; }
public long Total
{
get { return Values.Sum(x => (long)x); }
}
public int this[int i]
{
get { return Values[i]; }
set { Values[i] = value; }
}
public RleRowLengths(int rowCount)
{
Values = new int[rowCount];
}
public RleRowLengths(PsdBinaryReader reader, int rowCount, bool isLargeDocument)
: this(rowCount)
{
for (int i = 0; i < rowCount; i++)
{
Values[i] = isLargeDocument
? reader.ReadInt32()
: reader.ReadUInt16();
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ef0d2c47c58545249b479bc1706d89b7
timeCreated: 1495006554
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,230 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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-2015 Tao Yue
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Diagnostics;
using System.IO;
using UnityEngine.Assertions;
namespace PhotoshopFile
{
internal class RleWriter
{
private int maxPacketLength = 128;
// Current task
private object rleLock;
private Stream stream;
private byte[] data;
private int offset;
// Current packet
private bool isRepeatPacket;
private int idxPacketStart;
private int packetLength;
private byte runValue;
private int runLength;
public RleWriter(Stream stream)
{
rleLock = new object();
this.stream = stream;
}
/// <summary>
/// Encodes byte data using PackBits RLE compression.
/// </summary>
/// <param name="data">Raw data to be encoded.</param>
/// <param name="offset">Offset at which to begin transferring data.</param>
/// <param name="count">Number of bytes of data to transfer.</param>
/// <returns>Number of compressed bytes written to the stream.</returns>
/// <remarks>
/// There are multiple ways to encode two-byte runs:
/// 1. Apple PackBits only encodes three-byte runs as repeats.
/// 2. Adobe Photoshop encodes two-byte runs as repeats, unless preceded
/// by literals.
/// 3. TIFF PackBits recommends that two-byte runs be encoded as repeats,
/// unless preceded *and* followed by literals.
///
/// This class adopts the Photoshop behavior, as it has slightly better
/// compression efficiency than Apple PackBits, and is easier to implement
/// than TIFF PackBits.
/// </remarks>
public int Write(byte[] data, int offset, int count)
{
if (!Util.CheckBufferBounds(data, offset, count))
throw new ArgumentOutOfRangeException();
// We cannot encode a count of 0, because the PackBits flag-counter byte
// uses 0 to indicate a length of 1.
if (count == 0)
{
throw new ArgumentOutOfRangeException("count");
}
lock (rleLock)
{
var startPosition = stream.Position;
this.data = data;
this.offset = offset;
//fixed (byte* ptrData = &data[0])
{
//byte* ptr = ptrData + offset;
//byte* ptrEnd = ptr + count;
//var bytesEncoded = EncodeToStream(ptr, ptrEnd);
//Debug.Assert(bytesEncoded == count, "Encoded byte count should match the argument.");
var bytesEncoded = EncodeToStream(data, offset, offset + count);
Assert.AreEqual(bytesEncoded, count, "Encoded byte count should match the argument.");
}
return (int)(stream.Position - startPosition);
}
}
private void ClearPacket()
{
this.isRepeatPacket = false;
this.packetLength = 0;
}
private void WriteRepeatPacket(int length)
{
var header = unchecked((byte)(1 - length));
stream.WriteByte(header);
stream.WriteByte(runValue);
}
private void WriteLiteralPacket(int length)
{
var header = unchecked((byte)(length - 1));
stream.WriteByte(header);
stream.Write(data, idxPacketStart, length);
}
private void WritePacket()
{
if (isRepeatPacket)
WriteRepeatPacket(packetLength);
else
WriteLiteralPacket(packetLength);
}
private void StartPacket(int count,
bool isRepeatPacket, int runLength, byte value)
{
this.isRepeatPacket = isRepeatPacket;
this.packetLength = runLength;
this.runLength = runLength;
this.runValue = value;
this.idxPacketStart = offset + count;
}
private void ExtendPacketAndRun(byte value)
{
packetLength++;
runLength++;
}
private void ExtendPacketStartNewRun(byte value)
{
packetLength++;
runLength = 1;
runValue = value;
}
private int EncodeToStream(byte[] ptr, int start, int end /*byte* ptr, byte* ptrEnd*/)
{
// Begin the first packet.
StartPacket(0, false, 1, ptr[start]);
int numBytesEncoded = 1;
start++;
// Loop invariant: Packet is never empty.
while (start < end)
{
var value = ptr[start];
if (packetLength == 1)
{
isRepeatPacket = (value == runValue);
if (isRepeatPacket)
ExtendPacketAndRun(value);
else
ExtendPacketStartNewRun(value);
}
else if (packetLength == maxPacketLength)
{
// Packet is full, so write it out and start a new one.
WritePacket();
StartPacket(numBytesEncoded, false, 1, value);
}
else if (isRepeatPacket)
{
// Decide whether to continue the repeat packet.
if (value == runValue)
ExtendPacketAndRun(value);
else
{
// Different color, so terminate the run and start a new packet.
WriteRepeatPacket(packetLength);
StartPacket(numBytesEncoded, false, 1, value);
}
}
else
{
// Decide whether to continue the literal packet.
if (value == runValue)
{
ExtendPacketAndRun(value);
// A 3-byte run terminates the literal and starts a new repeat
// packet. That's because the 3-byte run can be encoded as a
// 2-byte repeat. So even if the run ends at 3, we've already
// paid for the next flag-counter byte.
if (runLength == 3)
{
// The 3-byte run can come in the middle of a literal packet,
// but not at the beginning. The first 2 bytes of the run
// should've triggered a repeat packet.
Debug.Assert(packetLength > 3);
// -2 because numBytesEncoded has not yet been incremented
WriteLiteralPacket(packetLength - 3);
StartPacket(numBytesEncoded - 2, true, 3, value);
}
}
else
{
ExtendPacketStartNewRun(value);
}
}
start++;
numBytesEncoded++;
}
// Loop terminates with a non-empty packet waiting to be written out.
WritePacket();
ClearPacket();
return numBytesEncoded;
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6fcbb5d3e3f3adf479ac8a933b929a8a
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,390 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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;
}
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Fills a buffer with a byte value.
/// </summary>
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++;
}
}
/// <summary>
/// Fills a buffer with a byte value.
/// </summary>
static public void Fill(NativeArray<byte> ptr, int start, int end, byte value)
{
while (start < end)
{
ptr[start] = value;
start++;
}
}
static public void Invert(NativeArray<byte> ptr, int ptrStart, int ptrEnd)
{
while (ptrStart < ptrEnd)
{
ptr[ptrStart] = (byte)(ptr[ptrStart] ^ 0xff);
ptrStart++;
}
}
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Reverses the endianness of a 2-byte word.
/// </summary>
static public void SwapBytes2(byte[] ptr, int start)
{
byte byte0 = ptr[start];
ptr[start] = ptr[start + 1];
ptr[start + 1] = byte0;
}
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Reverses the endianness of a 4-byte word.
/// </summary>
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;
}
/// <summary>
/// Reverses the endianness of a word of arbitrary length.
/// </summary>
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.");
}
}
/// <summary>
/// Reverses the endianness of 2-byte words in a byte array.
/// </summary>
/// <param name="byteArray">Byte array containing the sequence on which to swap endianness</param>
/// <param name="startIdx">Byte index of the first word to swap</param>
/// <param name="count">Number of words to swap</param>
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;
}
}
}
}
/// <summary>
/// Reverses the endianness of 4-byte words in a byte array.
/// </summary>
/// <param name="byteArray">Byte array containing the sequence on which to swap endianness</param>
/// <param name="startIdx">Byte index of the first word to swap</param>
/// <param name="count">Number of words to swap</param>
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;
}
}
}
}
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Calculates the number of bytes required to store a row of an image
/// with the specified bit depth.
/// </summary>
/// <param name="size">The size of the image in pixels.</param>
/// <param name="bitDepth">The bit depth of the image.</param>
/// <returns>The number of bytes needed to store a row of the image.</returns>
public static int BytesPerRow(Size size, int bitDepth)
{
switch (bitDepth)
{
case 1:
return (size.Width + 7) / 8;
default:
return size.Width * BytesFromBitDepth(bitDepth);
}
}
/// <summary>
/// Round the integer to a multiple.
/// </summary>
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;
}
/// <summary>
/// Get number of bytes required to pad to the specified multiple.
/// </summary>
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;
}
/// <summary>
/// Returns the number of bytes needed to store a single pixel of the
/// specified bit depth.
/// </summary>
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.");
}
/// <summary>
/// Verify that the offset and count will remain within the bounds of the
/// buffer.
/// </summary>
/// <returns>True if in bounds, false if out of bounds.</returns>
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.");
}
}
/// <summary>
/// Writes a message to the debug console, indicating the current position
/// in the stream in both decimal and hexadecimal formats.
/// </summary>
[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);
}
}
/// <summary>
/// Fixed-point decimal, with 16-bit integer and 16-bit fraction.
/// </summary>
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;
}
/// <summary>
/// Split the high and low words of a 32-bit unsigned integer into a
/// fixed-point number.
/// </summary>
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;
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2bab0d96ebe77084d97602af7f3fd124
timeCreated: 1495006553
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: