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,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.2D.PsdImporter.Editor")]

View file

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

View file

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

View file

@ -0,0 +1,50 @@
using System.Collections;
using System.Collections.Generic;
namespace PDNWrapper
{
internal static class Layer
{
public static BitmapLayer CreateBackgroundLayer(int w, int h)
{
return new BitmapLayer(w, h);
}
}
internal class BitmapLayer
{
int width, height;
public Rectangle Bounds
{
get {return new Rectangle(0, 0, width, height); }
}
public void Dispose()
{
Surface.Dispose();
foreach (var layer in ChildLayer)
layer.Dispose();
}
public BitmapLayer(int w, int h)
{
Surface = new Surface(w, h);
width = w;
height = h;
ChildLayer = new List<BitmapLayer>();
IsGroup = false;
}
public int LayerID { get; set; }
public bool IsGroup {get; set; }
public BitmapLayer ParentLayer {get; set; }
public List<BitmapLayer> ChildLayer { get; set; }
public string Name { get; set; }
public byte Opacity { get; set; }
public bool Visible { get; set; }
public LayerBlendMode BlendMode { get; set; }
public Surface Surface { get; set; }
}
}

View file

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

View file

@ -0,0 +1,30 @@
using System.Collections;
using System.Collections.Generic;
namespace PDNWrapper
{
internal class Document
{
public int width, height;
public Document(int w, int h)
{
width = w;
height = h;
Layers = new List<BitmapLayer>();
}
public void Dispose()
{
foreach (var layer in Layers)
layer.Dispose();
}
public List<BitmapLayer> Layers { get; set; }
public MeasurementUnit DpuUnit { get; set; }
public double DpuX { get; set; }
public double DpuY { get; set; }
}
}

View file

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

View file

@ -0,0 +1,27 @@
namespace PDNWrapper
{
internal enum MeasurementUnit
{
Pixel = 1,
Inch = 2,
Centimeter = 3
}
internal enum LayerBlendMode
{
Normal = 0,
Multiply = 1,
Additive = 2,
ColorBurn = 3,
ColorDodge = 4,
Reflect = 5,
Glow = 6,
Overlay = 7,
Difference = 8,
Negation = 9,
Lighten = 10,
Darken = 11,
Screen = 12,
Xor = 13
}
}

View file

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

View file

@ -0,0 +1,624 @@
using System.Collections;
using System.Collections.Generic;
using Unity.Jobs;
using UnityEngine;
using Unity.Collections;
using System;
using Unity.Collections.LowLevel.Unsafe;
namespace PaintDotNet.Data.PhotoshopFileType
{
#region PDNDecodeJob
internal struct PDNDecoderData
{
// Inputs.
public PDNWrapper.Rectangle Rect;
public PDNWrapper.Rectangle LayerRect;
public PDNWrapper.Rectangle ClippedRect;
public int SurfaceWidth;
public int SurfaceHeight;
public int SurfaceByteDepth;
public DecodeType DecoderType;
[NativeDisableParallelForRestriction]
[ReadOnly]
public NativeArray<byte> ColorChannel0;
[NativeDisableParallelForRestriction]
[ReadOnly]
public NativeArray<byte> ColorChannel1;
[NativeDisableParallelForRestriction]
[ReadOnly]
public NativeArray<byte> ColorChannel2;
[NativeDisableParallelForRestriction]
[ReadOnly]
public NativeArray<byte> ColorChannel3;
[NativeDisableParallelForRestriction]
[ReadOnly]
[DeallocateOnJobCompletion]
public NativeArray<byte> ColorModeData;
// Outputs
[NativeDisableParallelForRestriction]
public NativeArray<Color32> DecodedImage;
}
internal struct PDNDecoderJob : IJobParallelFor
{
public PDNDecoderData Data;
public void Execute(int index)
{
int idx = Data.Rect.Top + index;
{
// Calculate index into ImageData source from row and column.
int idxSrcPixel = (idx - Data.LayerRect.Top) * Data.LayerRect.Width + (Data.Rect.Left - Data.LayerRect.Left);
int idxSrcBytes = idxSrcPixel * Data.SurfaceByteDepth;
// Calculate pointers to destination Surface.
var idxDstStart = idx * Data.SurfaceWidth + Data.ClippedRect.Left;
var idxDstStops = idx * Data.SurfaceWidth + Data.ClippedRect.Right;
// For 16-bit images, take the higher-order byte from the image data, which is now in little-endian order.
if (Data.SurfaceByteDepth == 2)
{
idxSrcBytes++;
}
switch (Data.DecoderType)
{
case DecodeType.RGB32:
{
SetPDNRowRgb32(idxDstStart, idxDstStops, idxSrcBytes);
}
break;
case DecodeType.Grayscale32:
{
SetPDNRowGrayscale32(idxDstStart, idxDstStops, idxSrcBytes);
}
break;
case DecodeType.RGB:
{
SetPDNRowRgb(idxDstStart, idxDstStops, idxSrcBytes);
}
break;
case DecodeType.CMYK:
{
SetPDNRowCmyk(idxDstStart, idxDstStops, idxSrcBytes);
}
break;
case DecodeType.Bitmap:
{
SetPDNRowBitmap(idxDstStart, idxDstStops, idxSrcBytes);
}
break;
case DecodeType.Grayscale:
{
SetPDNRowGrayscale(idxDstStart, idxDstStops, idxSrcBytes);
}
break;
case DecodeType.Indexed:
{
SetPDNRowIndexed(idxDstStart, idxDstStops, idxSrcBytes);
}
break;
case DecodeType.Lab:
{
SetPDNRowLab(idxDstStart, idxDstStops, idxSrcBytes);
}
break;
}
}
}
// Case 0:
private void SetPDNRowRgb32(int dstStart, int dstStops, int idxSrc)
{
NativeArray<float> cR = Data.ColorChannel0.Reinterpret<float>(1);
NativeArray<float> cG = Data.ColorChannel1.Reinterpret<float>(1);
NativeArray<float> cB = Data.ColorChannel2.Reinterpret<float>(1);
var c = Data.DecodedImage[dstStart];
while (dstStart < dstStops)
{
c.r = ImageDecoderPdn.RGBByteFromHDRFloat(cR[idxSrc / 4]);
c.g = ImageDecoderPdn.RGBByteFromHDRFloat(cG[idxSrc / 4]);
c.b = ImageDecoderPdn.RGBByteFromHDRFloat(cB[idxSrc / 4]);
Data.DecodedImage[dstStart] = c;
dstStart++;
idxSrc += 4;
}
}
// Case 1:
private void SetPDNRowGrayscale32(int dstStart, int dstStops, int idxSrc)
{
NativeArray<float> channel = Data.ColorChannel0.Reinterpret<float>(1);
var c = Data.DecodedImage[dstStart];
while (dstStart < dstStops)
{
byte rgbValue = ImageDecoderPdn.RGBByteFromHDRFloat(channel[idxSrc / 4]);
c.r = rgbValue;
c.g = rgbValue;
c.b = rgbValue;
Data.DecodedImage[dstStart] = c;
dstStart++;
idxSrc += 4;
}
}
// Case 2:
private void SetPDNRowRgb(int dstStart, int dstStops, int idxSrc)
{
var c = Data.DecodedImage[dstStart];
while (dstStart < dstStops)
{
c.r = Data.ColorChannel0[idxSrc];
c.g = Data.ColorChannel1[idxSrc];
c.b = Data.ColorChannel2[idxSrc];
Data.DecodedImage[dstStart] = c;
dstStart++;
idxSrc += Data.SurfaceByteDepth;
}
}
// Case 3:
///////////////////////////////////////////////////////////////////////////////
//
// The color-conversion formulas come from the Colour Space Conversions FAQ:
// http://www.poynton.com/PDFs/coloureq.pdf
//
// RGB --> CMYK CMYK --> RGB
// --------------------------------------- --------------------------------------------
// Black = minimum(1-Red,1-Green,1-Blue) Red = 1-minimum(1,Cyan*(1-Black)+Black)
// Cyan = (1-Red-Black)/(1-Black) Green = 1-minimum(1,Magenta*(1-Black)+Black)
// Magenta = (1-Green-Black)/(1-Black) Blue = 1-minimum(1,Yellow*(1-Black)+Black)
// Yellow = (1-Blue-Black)/(1-Black)
//
///////////////////////////////////////////////////////////////////////////////
private void SetPDNRowCmyk(int dstStart, int dstStops, int idxSrc)
{
var c = Data.DecodedImage[dstStart];
while (dstStart < dstStops)
{
// CMYK values are stored as complements, presumably to allow for some
// measure of compatibility with RGB-only applications.
var C = 255 - Data.ColorChannel0[idxSrc];
var M = 255 - Data.ColorChannel1[idxSrc];
var Y = 255 - Data.ColorChannel2[idxSrc];
var K = 255 - Data.ColorChannel3[idxSrc];
int R = 255 - Math.Min(255, C * (255 - K) / 255 + K);
int G = 255 - Math.Min(255, M * (255 - K) / 255 + K);
int B = 255 - Math.Min(255, Y * (255 - K) / 255 + K);
c.r = (byte)R;
c.g = (byte)G;
c.b = (byte)B;
Data.DecodedImage[dstStart] = c;
dstStart++;
idxSrc += Data.SurfaceByteDepth;
}
}
// Case 4:
private void SetPDNRowBitmap(int dstStart, int dstStops, int idxSrc)
{
var c = Data.DecodedImage[dstStart];
while (dstStart < dstStops)
{
byte mask = (byte)(0x80 >> (idxSrc % 8));
byte bwValue = (byte)(Data.ColorChannel0[idxSrc / 8] & mask);
bwValue = (bwValue == 0) ? (byte)255 : (byte)0;
c.r = bwValue;
c.g = bwValue;
c.b = bwValue;
Data.DecodedImage[dstStart] = c;
dstStart++;
idxSrc += Data.SurfaceByteDepth;
}
}
// Case 5:
private void SetPDNRowGrayscale(int dstStart, int dstStops, int idxSrc)
{
var c = Data.DecodedImage[dstStart];
while (dstStart < dstStops)
{
c.r = Data.ColorChannel0[idxSrc];
c.g = Data.ColorChannel0[idxSrc];
c.b = Data.ColorChannel0[idxSrc];
Data.DecodedImage[dstStart] = c;
dstStart++;
idxSrc += Data.SurfaceByteDepth;
}
}
// Case 6:
private void SetPDNRowIndexed(int dstStart, int dstStops, int idxSrc)
{
var c = Data.DecodedImage[dstStart];
int index = (int)Data.ColorChannel0[idxSrc];
while (dstStart < dstStops)
{
c.r = Data.ColorModeData[index];
c.g = Data.ColorModeData[index + 256];
c.b = Data.ColorModeData[index + 2 * 256];
Data.DecodedImage[dstStart] = c;
dstStart++;
idxSrc += Data.SurfaceByteDepth;
}
}
// Case 7:
private void SetPDNRowLab(int dstStart, int dstStops, int idxSrc)
{
var c = Data.DecodedImage[dstStart];
while (dstStart < dstStops)
{
double exL, exA, exB;
exL = (double)Data.ColorChannel0[idxSrc];
exA = (double)Data.ColorChannel1[idxSrc];
exB = (double)Data.ColorChannel2[idxSrc];
int L = (int)(exL / 2.55);
int a = (int)(exA - 127.5);
int b = (int)(exB - 127.5);
// First, convert from Lab to XYZ.
// Standards used Observer = 2, Illuminant = D65
const double ref_X = 95.047;
const double ref_Y = 100.000;
const double ref_Z = 108.883;
double var_Y = ((double)L + 16.0) / 116.0;
double var_X = (double)a / 500.0 + var_Y;
double var_Z = var_Y - (double)b / 200.0;
double var_X3 = var_X * var_X * var_X;
double var_Y3 = var_Y * var_Y * var_Y;
double var_Z3 = var_Z * var_Z * var_Z;
if (var_Y3 > 0.008856)
var_Y = var_Y3;
else
var_Y = (var_Y - 16 / 116) / 7.787;
if (var_X3 > 0.008856)
var_X = var_X3;
else
var_X = (var_X - 16 / 116) / 7.787;
if (var_Z3 > 0.008856)
var_Z = var_Z3;
else
var_Z = (var_Z - 16 / 116) / 7.787;
double X = ref_X * var_X;
double Y = ref_Y * var_Y;
double Z = ref_Z * var_Z;
// Then, convert from XYZ to RGB.
// Standards used Observer = 2, Illuminant = D65
// ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883
double var_R = X * 0.032406 + Y * (-0.015372) + Z * (-0.004986);
double var_G = X * (-0.009689) + Y * 0.018758 + Z * 0.000415;
double var_B = X * 0.000557 + Y * (-0.002040) + Z * 0.010570;
if (var_R > 0.0031308)
var_R = 1.055 * (Math.Pow(var_R, 1 / 2.4)) - 0.055;
else
var_R = 12.92 * var_R;
if (var_G > 0.0031308)
var_G = 1.055 * (Math.Pow(var_G, 1 / 2.4)) - 0.055;
else
var_G = 12.92 * var_G;
if (var_B > 0.0031308)
var_B = 1.055 * (Math.Pow(var_B, 1 / 2.4)) - 0.055;
else
var_B = 12.92 * var_B;
int nRed = (int)(var_R * 256.0);
int nGreen = (int)(var_G * 256.0);
int nBlue = (int)(var_B * 256.0);
if (nRed < 0)
nRed = 0;
else if (nRed > 255)
nRed = 255;
if (nGreen < 0)
nGreen = 0;
else if (nGreen > 255)
nGreen = 255;
if (nBlue < 0)
nBlue = 0;
else if (nBlue > 255)
nBlue = 255;
c.r = (byte)nRed;
c.g = (byte)nGreen;
c.b = (byte)nBlue;
Data.DecodedImage[dstStart] = c;
dstStart++;
idxSrc += Data.SurfaceByteDepth;
}
}
}
#endregion
#region AlphaDecodeJob
internal struct PDNAlphaMaskData
{
// Inputs.
public PDNWrapper.Rectangle Rect;
public PDNWrapper.Rectangle LayerRect;
public PDNWrapper.Rectangle ClippedRect;
public int SurfaceWidth;
public int SurfaceHeight;
public int SurfaceByteDepth;
public int HasAlphaChannel;
public int HasUserAlphaMask;
public int UserMaskInvertOnBlend;
public PDNWrapper.Rectangle UserMaskRect;
public PDNWrapper.Rectangle UserMaskContextRect;
public int HasLayerAlphaMask;
public int LayerMaskInvertOnBlend;
public PDNWrapper.Rectangle LayerMaskRect;
public PDNWrapper.Rectangle LayerMaskContextRect;
[NativeDisableParallelForRestriction]
[ReadOnly]
[DeallocateOnJobCompletion]
public NativeArray<byte> AlphaChannel0;
[NativeDisableParallelForRestriction]
[ReadOnly]
public NativeArray<byte> UserMask;
[DeallocateOnJobCompletion]
[NativeDisableParallelForRestriction]
public NativeArray<byte> UserAlphaMask;
[DeallocateOnJobCompletion]
[NativeDisableParallelForRestriction]
public NativeArray<byte> UserAlphaMaskEmpty;
[NativeDisableParallelForRestriction]
[ReadOnly]
public NativeArray<byte> LayerMask;
[DeallocateOnJobCompletion]
[NativeDisableParallelForRestriction]
public NativeArray<byte> LayerAlphaMask;
[DeallocateOnJobCompletion]
[NativeDisableParallelForRestriction]
public NativeArray<byte> LayerAlphaMaskEmpty;
// Outputs
[NativeDisableParallelForRestriction]
public NativeArray<Color32> DecodedImage;
// Colors.
public byte UserMaskBackgroundColor;
public byte LayerMaskBackgroundColor;
}
internal struct PDNAlphaMaskJob : IJob
{
public PDNAlphaMaskData Data;
public void Execute()
{
for (int idx = Data.Rect.Top; idx < Data.Rect.Bottom; idx++)
{
// Calculate index into ImageData source from row and column.
int idxSrcPixel = (idx - Data.LayerRect.Top) * Data.LayerRect.Width + (Data.Rect.Left - Data.LayerRect.Left);
int idxSrcBytes = idxSrcPixel * Data.SurfaceByteDepth;
// Calculate pointers to destination Surface.
var idxDstStart = idx * Data.SurfaceWidth + Data.ClippedRect.Left;
var idxDstStops = idx * Data.SurfaceWidth + Data.ClippedRect.Right;
// For 16-bit images, take the higher-order byte from the image data, which is now in little-endian order.
if (Data.SurfaceByteDepth == 2)
{
idxSrcBytes++;
}
SetPDNAlphaRow(idxDstStart, idxDstStops, idxSrcBytes);
if (0 != Data.HasLayerAlphaMask)
{
GetMaskAlphaRow(idx, Data.LayerAlphaMask, Data.LayerAlphaMaskEmpty, Data.LayerMask, Data.LayerMaskInvertOnBlend, Data.LayerMaskBackgroundColor, Data.LayerMaskContextRect, Data.LayerMaskRect);
}
if (0 != Data.HasUserAlphaMask)
{
GetMaskAlphaRow(idx, Data.UserAlphaMask, Data.UserAlphaMaskEmpty, Data.UserMask, Data.UserMaskInvertOnBlend, Data.UserMaskBackgroundColor, Data.UserMaskContextRect, Data.UserMaskRect);
}
ApplyPDNMask(idxDstStart, idxDstStops);
}
}
private void SetPDNAlphaRow(int dstStart, int dstStops, int idxSrc)
{
// Set alpha to fully-opaque if there is no alpha channel
if (0 == Data.HasAlphaChannel)
{
while (dstStart < dstStops)
{
var c = Data.DecodedImage[dstStart];
c.a = 255;
Data.DecodedImage[dstStart] = c;
dstStart++;
}
}
// Set the alpha channel data
else
{
NativeArray<float> srcAlphaChannel = Data.AlphaChannel0.Reinterpret<float>(1);
{
while (dstStart < dstStops)
{
var c = Data.DecodedImage[dstStart];
c.a = (Data.SurfaceByteDepth < 4) ? Data.AlphaChannel0[idxSrc] : ImageDecoderPdn.RGBByteFromHDRFloat(srcAlphaChannel[idxSrc / 4]);
Data.DecodedImage[dstStart] = c;
dstStart++;
idxSrc += Data.SurfaceByteDepth;
}
}
}
}
private void ApplyPDNMask(int dstStart, int dstStops)
{
// Do nothing if there are no masks
if (0 == Data.HasLayerAlphaMask && 0 == Data.HasUserAlphaMask)
{
return;
}
// Apply one mask
else if (0 == Data.HasLayerAlphaMask || 0 == Data.HasUserAlphaMask)
{
var maskAlpha = (0 == Data.HasLayerAlphaMask) ? Data.UserAlphaMask : Data.LayerAlphaMask;
var maskStart = 0;
{
while (dstStart < dstStops)
{
var c = Data.DecodedImage[dstStart];
c.a = (byte)(Data.DecodedImage[dstStart].a * maskAlpha[maskStart] / 255);
Data.DecodedImage[dstStart] = c;
dstStart++;
maskStart++;
}
}
}
// Apply both masks in one pass, to minimize rounding error
else
{
var maskStart = 0;
{
while (dstStart < dstStops)
{
var c = Data.DecodedImage[dstStart];
var alphaFactor = (Data.LayerAlphaMask[maskStart]) * (Data.UserAlphaMask[maskStart]);
c.a = (byte)(Data.DecodedImage[dstStart].a * alphaFactor / 65025);
Data.DecodedImage[dstStart] = c;
dstStart++;
maskStart++;
}
}
}
}
private void DecodeMaskAlphaRow32(NativeArray<byte> Alpha, int dstStart, int dstStops, NativeArray<byte> Mask, int maskStart)
{
NativeArray<float> floatArray = Mask.Reinterpret<float>(1);
while (dstStart < dstStops)
{
Alpha[dstStart] = ImageDecoderPdn.RGBByteFromHDRFloat(floatArray[maskStart / 4]);
dstStart++;
maskStart += 4;
}
}
private void DecodeMaskAlphaRow(NativeArray<byte> Alpha, int dstStart, int dstStops, NativeArray<byte> Mask, int maskStart, int byteDepth)
{
while (dstStart < dstStops)
{
Alpha[dstStart] = Mask[maskStart];
dstStart++;
maskStart += byteDepth;
}
}
private unsafe void GetMaskAlphaRow(int idxSrc, NativeArray<byte> alphaBuffer, NativeArray<byte> alphaBufferEmpty, NativeArray<byte> maskChannel, int MaskInvertOnBlend, byte MaskBackgroundColor, PDNWrapper.Rectangle MaskContextRect, PDNWrapper.Rectangle MaskRect)
{
//////////////////////////////////////
// Transfer mask into the alpha array
// Background color for areas not covered by the mask
byte backgroundColor = (0 != MaskInvertOnBlend) ? (byte)(255 - MaskBackgroundColor) : MaskBackgroundColor;
{
var alphaBufferPtr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(alphaBuffer);
UnsafeUtility.MemSet(alphaBufferPtr, backgroundColor, alphaBuffer.Length);
}
// Only process if not Empty.
if (alphaBufferEmpty[idxSrc] == 0)
{
// Get pointers to starting positions
int alphaColumn = MaskContextRect.X;
// It's possible that the layer's rect is larger than the clip and it's offset.
// Since we only copy out the alpha based on the MaskContext size
// The copy will start from where the MaskContextRect is
if(Data.LayerRect.X > 0)
alphaColumn = MaskContextRect.X - Data.LayerRect.X;
var pAlpha = alphaColumn;
var pAlphaEnd = pAlpha + MaskContextRect.Width;
int maskRow = idxSrc - MaskRect.Y;
int maskColumn = MaskContextRect.X - MaskRect.X;
int idxMaskPixel = (maskRow * MaskRect.Width) + maskColumn;
var pMask = idxMaskPixel * Data.SurfaceByteDepth;
// Take the high-order byte if values are 16-bit (little-endian)
if (Data.SurfaceByteDepth == 2)
{
pMask++;
}
// Decode mask into the alpha array.
if (Data.SurfaceByteDepth == 4)
{
DecodeMaskAlphaRow32(alphaBuffer, pAlpha, pAlphaEnd, maskChannel, pMask);
}
else
{
DecodeMaskAlphaRow(alphaBuffer, pAlpha, pAlphaEnd, maskChannel, pMask, Data.SurfaceByteDepth);
}
// Obsolete since Photoshop CS6, but retained for compatibility with older versions. Note that the background has already been inverted.
if (0 != MaskInvertOnBlend)
{
PhotoshopFile.Util.Invert(alphaBuffer, pAlpha, pAlphaEnd);
}
}
}
}
#endregion
}

View file

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

View file

@ -0,0 +1,258 @@
using System;
namespace PDNWrapper
{
// Mimics System.Drawing.Rectangle
internal struct Rectangle
{
public static readonly Rectangle Empty = new Rectangle();
private int x;
private int y;
private int width;
private int height;
public Rectangle(int x, int y, int width, int height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public static Rectangle FromLTRB(int left, int top, int right, int bottom)
{
return new Rectangle(left,
top,
right - left,
bottom - top);
}
public Size Size
{
get
{
return new Size(Width, Height);
}
set
{
this.Width = value.Width;
this.Height = value.Height;
}
}
public int X
{
get
{
return x;
}
set
{
x = value;
}
}
public int Y
{
get
{
return y;
}
set
{
y = value;
}
}
public int Width
{
get
{
return width;
}
set
{
width = value;
}
}
public int Height
{
get
{
return height;
}
set
{
height = value;
}
}
public int Left
{
get
{
return X;
}
}
public int Top
{
get
{
return Y;
}
}
public int Right
{
get
{
return X + Width;
}
}
public int Bottom
{
get
{
return Y + Height;
}
}
public bool IsEmpty
{
get
{
return height == 0 && width == 0 && x == 0 && y == 0;
}
}
public override bool Equals(object obj)
{
if (!(obj is Rectangle))
return false;
Rectangle comp = (Rectangle)obj;
return (comp.X == this.X) &&
(comp.Y == this.Y) &&
(comp.Width == this.Width) &&
(comp.Height == this.Height);
}
public static bool operator==(Rectangle left, Rectangle right)
{
return (left.X == right.X
&& left.Y == right.Y
&& left.Width == right.Width
&& left.Height == right.Height);
}
public static bool operator!=(Rectangle left, Rectangle right)
{
return !(left == right);
}
public bool Contains(int x, int y)
{
return this.X <= x &&
x < this.X + this.Width &&
this.Y <= y &&
y < this.Y + this.Height;
}
public bool Contains(Rectangle rect)
{
return (this.X <= rect.X) &&
((rect.X + rect.Width) <= (this.X + this.Width)) &&
(this.Y <= rect.Y) &&
((rect.Y + rect.Height) <= (this.Y + this.Height));
}
public override int GetHashCode()
{
return (int)((UInt32)X ^
(((UInt32)Y << 13) | ((UInt32)Y >> 19)) ^
(((UInt32)Width << 26) | ((UInt32)Width >> 6)) ^
(((UInt32)Height << 7) | ((UInt32)Height >> 25)));
}
public void Inflate(int width, int height)
{
this.X -= width;
this.Y -= height;
this.Width += 2 * width;
this.Height += 2 * height;
}
public void Inflate(Size size)
{
Inflate(size.Width, size.Height);
}
public static Rectangle Inflate(Rectangle rect, int x, int y)
{
Rectangle r = rect;
r.Inflate(x, y);
return r;
}
public void Intersect(Rectangle rect)
{
Rectangle result = Rectangle.Intersect(rect, this);
this.X = result.X;
this.Y = result.Y;
this.Width = result.Width;
this.Height = result.Height;
}
public static Rectangle Intersect(Rectangle a, Rectangle b)
{
int x1 = Math.Max(a.X, b.X);
int x2 = Math.Min(a.X + a.Width, b.X + b.Width);
int y1 = Math.Max(a.Y, b.Y);
int y2 = Math.Min(a.Y + a.Height, b.Y + b.Height);
if (x2 >= x1
&& y2 >= y1)
{
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
}
return Rectangle.Empty;
}
public bool IntersectsWith(Rectangle rect)
{
return (rect.X < this.X + this.Width) &&
(this.X < (rect.X + rect.Width)) &&
(rect.Y < this.Y + this.Height) &&
(this.Y < rect.Y + rect.Height);
}
public static Rectangle Union(Rectangle a, Rectangle b)
{
int x1 = Math.Min(a.X, b.X);
int x2 = Math.Max(a.X + a.Width, b.X + b.Width);
int y1 = Math.Min(a.Y, b.Y);
int y2 = Math.Max(a.Y + a.Height, b.Y + b.Height);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
}
public void Offset(int x, int y)
{
this.X += x;
this.Y += y;
}
public override string ToString()
{
return "{X=" + X.ToString() + ",Y=" + Y.ToString() +
",Width=" + Width.ToString() +
",Height=" + Height.ToString() + "}";
}
}
}

View file

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

View file

@ -0,0 +1,49 @@
namespace PDNWrapper
{
// Mimics System.Drawing.Size
internal struct Size
{
public static readonly Size Empty = new Size();
private int width;
private int height;
public Size(int width, int height)
{
this.width = width;
this.height = height;
}
public bool IsEmpty
{
get
{
return width == 0 && height == 0;
}
}
public int Width
{
get
{
return width;
}
set
{
width = value;
}
}
public int Height
{
get
{
return height;
}
set
{
height = value;
}
}
}
}

View file

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

View file

@ -0,0 +1,29 @@
using Unity.Collections;
using UnityEngine;
namespace PDNWrapper
{
internal class Surface
{
NativeArray<Color32> m_Color;
public Surface(int w, int h)
{
width = w;
height = h;
m_Color = new NativeArray<Color32>(width * height, Allocator.Persistent);
}
public void Dispose()
{
m_Color.Dispose();
}
public NativeArray<Color32> color
{
get { return m_Color; }
}
public int width { get; private set; }
public int height { get; private set; }
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1b467712e18b40c4f90510e9d0364faf
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: 36a1cbeb5badf2a4abdcb1dcefa0efb6
folderAsset: yes
timeCreated: 1495006553
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,107 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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;
using System.Diagnostics;
using System.Linq;
using System.Text;
using PaintDotNet;
using PhotoshopFile;
using PDNWrapper;
namespace PaintDotNet.Data.PhotoshopFileType
{
internal static class BlendModeMapping
{
/// <summary>
/// Convert between Paint.NET and Photoshop blend modes.
/// </summary>
public static string ToPsdBlendMode(this LayerBlendMode pdnBlendMode)
{
switch (pdnBlendMode)
{
case LayerBlendMode.Normal:
return PsdBlendMode.Normal;
case LayerBlendMode.Multiply:
return PsdBlendMode.Multiply;
case LayerBlendMode.Additive:
return PsdBlendMode.LinearDodge;
case LayerBlendMode.ColorBurn:
return PsdBlendMode.ColorBurn;
case LayerBlendMode.ColorDodge:
return PsdBlendMode.ColorDodge;
case LayerBlendMode.Overlay:
return PsdBlendMode.Overlay;
case LayerBlendMode.Difference:
return PsdBlendMode.Difference;
case LayerBlendMode.Lighten:
return PsdBlendMode.Lighten;
case LayerBlendMode.Darken:
return PsdBlendMode.Darken;
case LayerBlendMode.Screen:
return PsdBlendMode.Screen;
// Paint.NET blend modes without a Photoshop equivalent are saved
// as Normal.
case LayerBlendMode.Glow:
case LayerBlendMode.Negation:
case LayerBlendMode.Reflect:
case LayerBlendMode.Xor:
return PsdBlendMode.Normal;
default:
Debug.Fail("Unknown Paint.NET blend mode.");
return PsdBlendMode.Normal;
}
}
/// <summary>
/// Convert a Photoshop blend mode to a Paint.NET BlendOp.
/// </summary>
public static LayerBlendMode FromPsdBlendMode(string blendModeKey)
{
switch (blendModeKey)
{
case PsdBlendMode.Normal:
return LayerBlendMode.Normal;
case PsdBlendMode.Multiply:
return LayerBlendMode.Multiply;
case PsdBlendMode.LinearDodge:
return LayerBlendMode.Additive;
case PsdBlendMode.ColorBurn:
return LayerBlendMode.ColorBurn;
case PsdBlendMode.ColorDodge:
return LayerBlendMode.ColorDodge;
case PsdBlendMode.Overlay:
return LayerBlendMode.Overlay;
case PsdBlendMode.Difference:
return LayerBlendMode.Difference;
case PsdBlendMode.Lighten:
return LayerBlendMode.Lighten;
case PsdBlendMode.Darken:
return LayerBlendMode.Darken;
case PsdBlendMode.Screen:
return LayerBlendMode.Screen;
// Photoshop blend modes without a Paint.NET equivalent are loaded
// as Normal.
default:
return LayerBlendMode.Normal;
}
}
}
}

View file

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

View file

@ -0,0 +1,46 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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;
using PhotoshopFile;
namespace PaintDotNet.Data.PhotoshopFileType
{
/// <summary>
/// Controls the loading of a PSD file into a Paint.NET Document.
/// </summary>
internal class DocumentLoadContext : LoadContext
{
public DocumentLoadContext() : base()
{
}
public override void OnLoadLayersHeader(PsdFile psdFile)
{
PsdLoad.CheckSufficientMemory(psdFile);
}
public override void OnLoadLayerHeader(PhotoshopFile.Layer layer)
{
var psdFile = layer.PsdFile;
if (psdFile.ColorMode == PsdColorMode.Multichannel)
{
PsdLoad.CheckSufficientMemory(psdFile);
}
}
}
}

View file

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

View file

@ -0,0 +1,807 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 PaintDotNet;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using PDNWrapper;
using System.Text;
using PhotoshopFile;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using Debug = UnityEngine.Debug;
using Unity.Jobs;
namespace PaintDotNet.Data.PhotoshopFileType
{
internal enum DecodeType
{
RGB32 = 0,
Grayscale32 = 1,
RGB = 2,
CMYK = 3,
Bitmap = 4,
Grayscale = 5,
Indexed = 6,
Lab = 7
};
internal static class ImageDecoderPdn
{
private static double rgbExponent = 1 / 2.19921875;
private class DecodeContext
{
public PhotoshopFile.Layer Layer { get; private set; }
public int ByteDepth { get; private set; }
public int HasAlphaChannel { get; private set; }
public Channel[] Channels { get; private set; }
public NativeArray<byte> AlphaChannel { get; private set; }
public PsdColorMode ColorMode { get; private set; }
public NativeArray<byte> ColorModeData { get; private set; }
public Rectangle Rectangle { get; private set; }
public MaskDecodeContext LayerMaskContext { get; private set; }
public MaskDecodeContext UserMaskContext { get; private set; }
public DecodeContext(PhotoshopFile.Layer layer, Rectangle bounds)
{
Layer = layer;
ByteDepth = Util.BytesFromBitDepth(layer.PsdFile.BitDepth);
HasAlphaChannel = 0;
Channels = layer.Channels.ToIdArray();
var alphaSize = 4;
if (layer.AlphaChannel != null && layer.AlphaChannel.ImageData.Length > 0)
{
HasAlphaChannel = 1;
alphaSize = layer.AlphaChannel.ImageData.Length;
alphaSize = (alphaSize / 4) + (alphaSize % 4 > 0 ? 1 : 0);
alphaSize = alphaSize * 4;
}
AlphaChannel = new NativeArray<byte>(alphaSize, Allocator.TempJob);
if (HasAlphaChannel > 0)
NativeArray<byte>.Copy(layer.AlphaChannel.ImageData, AlphaChannel, layer.AlphaChannel.ImageData.Length);
ColorMode = layer.PsdFile.ColorMode;
ColorModeData = new NativeArray<byte>(layer.PsdFile.ColorModeData, Allocator.TempJob);
// Clip the layer to the specified bounds
Rectangle = Layer.Rect.IntersectWith(bounds);
if (layer.Masks != null)
{
LayerMaskContext = GetMaskContext(layer.Masks.LayerMask);
UserMaskContext = GetMaskContext(layer.Masks.UserMask);
}
}
internal void Cleanup()
{
AlphaChannel.Dispose();
ColorModeData.Dispose();
}
private MaskDecodeContext GetMaskContext(Mask mask)
{
if ((mask == null) || (mask.Disabled))
{
return null;
}
return new MaskDecodeContext(mask, this);
}
}
private class MaskDecodeContext
{
public Mask Mask { get; private set; }
public Rectangle Rectangle { get; private set; }
public MaskDecodeContext(Mask mask, DecodeContext layerContext)
{
Mask = mask;
// The PositionVsLayer flag is documented to indicate a position
// relative to the layer, but Photoshop treats the position as
// absolute. So that's what we do, too.
Rectangle = mask.Rect.IntersectWith(layerContext.Rectangle);
}
public bool IsRowEmpty(int row)
{
return (Mask.ImageData == null)
|| (Mask.ImageData.Length == 0)
|| (Rectangle.Size.IsEmpty)
|| (row < Rectangle.Top)
|| (row >= Rectangle.Bottom);
}
}
///////////////////////////////////////////////////////////////////////////////
internal static byte RGBByteFromHDRFloat(float ptr)
{
var result = (byte)(255 * Math.Pow(ptr, rgbExponent));
return result;
}
private static DecodeDelegate GetDecodeDelegate(PsdColorMode psdColorMode, ref DecodeType decoderType)
{
switch (psdColorMode)
{
case PsdColorMode.Bitmap:
decoderType = DecodeType.Bitmap;
return SetPDNRowBitmap;
case PsdColorMode.Grayscale:
case PsdColorMode.Duotone:
decoderType = DecodeType.Grayscale;
return SetPDNRowGrayscale;
case PsdColorMode.Indexed:
decoderType = DecodeType.Indexed;
return SetPDNRowIndexed;
case PsdColorMode.RGB:
decoderType = DecodeType.RGB;
return SetPDNRowRgb;
case PsdColorMode.CMYK:
decoderType = DecodeType.CMYK;
return SetPDNRowCmyk;
case PsdColorMode.Lab:
decoderType = DecodeType.Lab;
return SetPDNRowLab;
case PsdColorMode.Multichannel:
throw new Exception("Cannot decode multichannel.");
default:
throw new Exception("Unknown color mode.");
}
}
private static DecodeDelegate GetDecodeDelegate32(PsdColorMode psdColorMode, ref DecodeType decoderType)
{
switch (psdColorMode)
{
case PsdColorMode.Grayscale:
decoderType = DecodeType.Grayscale32;
return SetPDNRowGrayscale32;
case PsdColorMode.RGB:
decoderType = DecodeType.RGB32;
return SetPDNRowRgb32;
default:
throw new PsdInvalidException(
"32-bit HDR images must be either RGB or grayscale.");
}
}
/// <summary>
/// Decode image from Photoshop's channel-separated formats to BGRA.
/// </summary>
public static JobHandle DecodeImage(BitmapLayer pdnLayer, PhotoshopFile.Layer psdLayer, JobHandle inputDeps)
{
UnityEngine.Profiling.Profiler.BeginSample("DecodeImage");
var decodeContext = new DecodeContext(psdLayer, pdnLayer.Bounds);
DecodeDelegate decoder = null;
DecodeType decoderType = 0;
if (decodeContext.ByteDepth == 4)
decoder = GetDecodeDelegate32(decodeContext.ColorMode, ref decoderType);
else
decoder = GetDecodeDelegate(decodeContext.ColorMode, ref decoderType);
JobHandle jobHandle = DecodeImage(pdnLayer, decodeContext, decoderType, inputDeps);
UnityEngine.Profiling.Profiler.EndSample();
return jobHandle;
}
/// <summary>
/// Decode image from Photoshop's channel-separated formats to BGRA,
/// using the specified decode delegate on each row.
/// </summary>
private static JobHandle DecodeImage(BitmapLayer pdnLayer, DecodeContext decodeContext, DecodeType decoderType, JobHandle inputDeps)
{
var psdLayer = decodeContext.Layer;
var surface = pdnLayer.Surface;
var rect = decodeContext.Rectangle;
// Convert rows from the Photoshop representation, writing the
// resulting ARGB values to to the Paint.NET Surface.
int jobCount = Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobWorkerMaximumCount;
int execCount = (rect.Bottom - rect.Top);
int sliceCount = execCount / jobCount;
PDNDecoderJob decoderJob = new PDNDecoderJob();
decoderJob.Data.Rect = rect;
decoderJob.Data.LayerRect = psdLayer.Rect;
decoderJob.Data.ClippedRect = rect;
decoderJob.Data.SurfaceWidth = surface.width;
decoderJob.Data.SurfaceHeight = surface.height;
decoderJob.Data.SurfaceByteDepth = decodeContext.ByteDepth;
decoderJob.Data.DecoderType = decoderType;
decoderJob.Data.ColorChannel0 = decodeContext.Channels[0].ImageData;
decoderJob.Data.ColorChannel1 = decodeContext.Channels.Length > 1 ? decodeContext.Channels[1].ImageData : decodeContext.Channels[0].ImageData;
decoderJob.Data.ColorChannel2 = decodeContext.Channels.Length > 2 ? decodeContext.Channels[2].ImageData : decodeContext.Channels[0].ImageData;
decoderJob.Data.ColorChannel3 = decodeContext.Channels.Length > 3 ? decodeContext.Channels[3].ImageData : decodeContext.Channels[0].ImageData;
decoderJob.Data.ColorModeData = decodeContext.ColorModeData;
decoderJob.Data.DecodedImage = surface.color;
// Schedule the job, returns the JobHandle which can be waited upon later on
JobHandle jobHandle = decoderJob.Schedule(execCount, sliceCount, inputDeps);
// Mask and Alpha.
int userMaskContextSize = decodeContext.UserMaskContext != null ? decodeContext.Rectangle.Width : 1;
int layerMaskContextSize = decodeContext.LayerMaskContext != null ? decodeContext.Rectangle.Width : 1;
var userAlphaMask = new NativeArray<byte>(userMaskContextSize, Allocator.TempJob);
var layerAlphaMask = new NativeArray<byte>(layerMaskContextSize, Allocator.TempJob);
var userAlphaMaskEmpty = new NativeArray<byte>(rect.Bottom, Allocator.TempJob);
var layerAlphaMaskEmpty = new NativeArray<byte>(rect.Bottom, Allocator.TempJob);
PDNAlphaMaskJob alphaMaskJob = new PDNAlphaMaskJob();
for (int y = rect.Top; y < rect.Bottom; ++y)
{
if (decodeContext.UserMaskContext != null)
userAlphaMaskEmpty[y] = decodeContext.UserMaskContext.IsRowEmpty(y) ? (byte)1 : (byte)0;
if (decodeContext.LayerMaskContext != null)
layerAlphaMaskEmpty[y] = decodeContext.LayerMaskContext.IsRowEmpty(y) ? (byte)1 : (byte)0;
}
alphaMaskJob.Data.Rect = rect;
alphaMaskJob.Data.LayerRect = psdLayer.Rect;
alphaMaskJob.Data.ClippedRect = rect;
alphaMaskJob.Data.SurfaceWidth = surface.width;
alphaMaskJob.Data.SurfaceHeight = surface.height;
alphaMaskJob.Data.SurfaceByteDepth = decodeContext.ByteDepth;
alphaMaskJob.Data.HasAlphaChannel = decodeContext.HasAlphaChannel;
alphaMaskJob.Data.HasUserAlphaMask = decodeContext.UserMaskContext != null ? 1 : 0;
alphaMaskJob.Data.UserMaskInvertOnBlend = decodeContext.UserMaskContext != null ? (decodeContext.UserMaskContext.Mask.InvertOnBlend ? 1 : 0) : 0;
alphaMaskJob.Data.UserMaskRect = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Mask.Rect : rect;
alphaMaskJob.Data.UserMaskContextRect = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Rectangle : rect;
alphaMaskJob.Data.HasLayerAlphaMask = decodeContext.LayerMaskContext != null ? 1 : 0;
alphaMaskJob.Data.LayerMaskInvertOnBlend = decodeContext.LayerMaskContext != null ? (decodeContext.LayerMaskContext.Mask.InvertOnBlend ? 1 : 0) : 0;
alphaMaskJob.Data.LayerMaskRect = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Mask.Rect : rect;
alphaMaskJob.Data.LayerMaskContextRect = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Rectangle : rect;
alphaMaskJob.Data.AlphaChannel0 = decodeContext.AlphaChannel;
alphaMaskJob.Data.UserMask = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Mask.ImageData : decodeContext.AlphaChannel;
alphaMaskJob.Data.UserAlphaMask = userAlphaMask;
alphaMaskJob.Data.UserAlphaMaskEmpty = userAlphaMaskEmpty;
alphaMaskJob.Data.LayerMask = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Mask.ImageData : decodeContext.AlphaChannel;
alphaMaskJob.Data.LayerAlphaMask = layerAlphaMask;
alphaMaskJob.Data.LayerAlphaMaskEmpty = layerAlphaMaskEmpty;
alphaMaskJob.Data.DecodedImage = surface.color;
alphaMaskJob.Data.UserMaskBackgroundColor = decodeContext.UserMaskContext != null ? decodeContext.UserMaskContext.Mask.BackgroundColor : (byte)0;
alphaMaskJob.Data.LayerMaskBackgroundColor = decodeContext.LayerMaskContext != null ? decodeContext.LayerMaskContext.Mask.BackgroundColor : (byte)0;
jobHandle = alphaMaskJob.Schedule(jobHandle);
return jobHandle;
}
///////////////////////////////////////////////////////////////////////////
/// SINGLE THREADED - KEPT FOR REFERENCE
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Decode image from Photoshop's channel-separated formats to BGRA.
/// </summary>
public static void DecodeImage(BitmapLayer pdnLayer, PhotoshopFile.Layer psdLayer)
{
UnityEngine.Profiling.Profiler.BeginSample("DecodeImage");
var decodeContext = new DecodeContext(psdLayer, pdnLayer.Bounds);
DecodeDelegate decoder = null;
DecodeType decoderType = 0;
if (decodeContext.ByteDepth == 4)
decoder = GetDecodeDelegate32(decodeContext.ColorMode, ref decoderType);
else
decoder = GetDecodeDelegate(decodeContext.ColorMode, ref decoderType);
DecodeImage(pdnLayer, decodeContext, decoder);
decodeContext.Cleanup();
UnityEngine.Profiling.Profiler.EndSample();
}
private delegate void DecodeDelegate(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, int idxSrc, DecodeContext context);
/// <summary>
/// Decode image from Photoshop's channel-separated formats to BGRA,
/// using the specified decode delegate on each row.
/// </summary>
private static void DecodeImage(BitmapLayer pdnLayer, DecodeContext decodeContext, DecodeDelegate decoder)
{
var psdLayer = decodeContext.Layer;
var surface = pdnLayer.Surface;
var rect = decodeContext.Rectangle;
// Convert rows from the Photoshop representation, writing the
// resulting ARGB values to to the Paint.NET Surface.
for (int y = rect.Top; y < rect.Bottom; y++)
{
// Calculate index into ImageData source from row and column.
int idxSrcPixel = (y - psdLayer.Rect.Top) * psdLayer.Rect.Width
+ (rect.Left - psdLayer.Rect.Left);
int idxSrcByte = idxSrcPixel * decodeContext.ByteDepth;
// Calculate pointers to destination Surface.
//var pDestRow = surface.GetRowAddress(y);
//var pDestStart = pDestRow + decodeContext.Rectangle.Left;
//var pDestEnd = pDestRow + decodeContext.Rectangle.Right;
var pDestStart = y * surface.width + decodeContext.Rectangle.Left;
var pDestEnd = y * surface.width + decodeContext.Rectangle.Right;
// For 16-bit images, take the higher-order byte from the image
// data, which is now in little-endian order.
if (decodeContext.ByteDepth == 2)
idxSrcByte++;
// Decode the color and alpha channels
decoder(pDestStart, pDestEnd, surface.width, surface.color, idxSrcByte, decodeContext);
}
// Mask and Alpha.
int userMaskContextSize = decodeContext.UserMaskContext != null ? decodeContext.Rectangle.Width : 1;
int layerMaskContextSize = decodeContext.LayerMaskContext != null ? decodeContext.Rectangle.Width : 1;
var userAlphaMask = new NativeArray<byte>(userMaskContextSize, Allocator.TempJob);
var layerAlphaMask = new NativeArray<byte>(layerMaskContextSize, Allocator.TempJob);
for (int y = rect.Top; y < rect.Bottom; y++)
{
// Calculate index into ImageData source from row and column.
int idxSrcPixel = (y - psdLayer.Rect.Top) * psdLayer.Rect.Width + (rect.Left - psdLayer.Rect.Left);
int idxSrcByte = idxSrcPixel * decodeContext.ByteDepth;
// Calculate pointers to destination Surface.
//var pDestRow = surface.GetRowAddress(y);
//var pDestStart = pDestRow + decodeContext.Rectangle.Left;
//var pDestEnd = pDestRow + decodeContext.Rectangle.Right;
var pDestStart = y * surface.width + decodeContext.Rectangle.Left;
var pDestEnd = y * surface.width + decodeContext.Rectangle.Right;
// For 16-bit images, take the higher-order byte from the image
// data, which is now in little-endian order.
if (decodeContext.ByteDepth == 2)
idxSrcByte++;
// Decode the color and alpha channels
SetPDNAlphaRow(pDestStart, pDestEnd, surface.width, surface.color, idxSrcByte, decodeContext.ByteDepth, decodeContext.HasAlphaChannel, decodeContext.AlphaChannel);
// Apply layer masks(s) to the alpha channel
GetMaskAlphaRow(y, decodeContext, decodeContext.LayerMaskContext, ref layerAlphaMask);
GetMaskAlphaRow(y, decodeContext, decodeContext.UserMaskContext, ref userAlphaMask);
ApplyPDNMask(pDestStart, pDestEnd, surface.width, surface.color, layerAlphaMask, userAlphaMask);
}
userAlphaMask.Dispose();
layerAlphaMask.Dispose();
}
private static unsafe void GetMaskAlphaRow(int y, DecodeContext layerContext, MaskDecodeContext maskContext, ref NativeArray<byte> alphaBuffer)
{
if (maskContext == null)
return;
var mask = maskContext.Mask;
// Background color for areas not covered by the mask
byte backgroundColor = mask.InvertOnBlend
? (byte)(255 - mask.BackgroundColor)
: mask.BackgroundColor;
{
var alphaBufferPtr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(alphaBuffer);
UnsafeUtility.MemSet(alphaBufferPtr, backgroundColor, alphaBuffer.Length);
}
if (maskContext.IsRowEmpty(y))
{
return;
}
//////////////////////////////////////
// Transfer mask into the alpha array
var pMaskData = mask.ImageData;
{
// Get pointers to starting positions
int alphaColumn = maskContext.Rectangle.X - layerContext.Rectangle.X;
var pAlpha = alphaColumn;
var pAlphaEnd = pAlpha + maskContext.Rectangle.Width;
int maskRow = y - mask.Rect.Y;
int maskColumn = maskContext.Rectangle.X - mask.Rect.X;
int idxMaskPixel = (maskRow * mask.Rect.Width) + maskColumn;
var pMask = idxMaskPixel * layerContext.ByteDepth;
// Take the high-order byte if values are 16-bit (little-endian)
if (layerContext.ByteDepth == 2)
pMask++;
// Decode mask into the alpha array.
if (layerContext.ByteDepth == 4)
{
DecodeMaskAlphaRow32(alphaBuffer, pAlpha, pAlphaEnd, pMaskData, pMask);
}
else
{
DecodeMaskAlphaRow(alphaBuffer, pAlpha, pAlphaEnd, pMaskData, pMask, layerContext.ByteDepth);
}
// Obsolete since Photoshop CS6, but retained for compatibility with
// older versions. Note that the background has already been inverted.
if (mask.InvertOnBlend)
{
Util.Invert(alphaBuffer, pAlpha, pAlphaEnd);
}
}
}
private static void SetPDNAlphaRow(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, int idxSrc, int byteDepth, int hasAlphaChannel, NativeArray<byte> alphaChannel)
{
// Set alpha to fully-opaque if there is no alpha channel
if (0 == hasAlphaChannel)
{
var pDest = pDestStart;
while (pDest < pDestEnd)
{
var c = color[pDest];
c.a = 255;
color[pDest] = c;
pDest++;
}
}
// Set the alpha channel data
else
{
NativeArray<float> srcAlphaChannel = alphaChannel.Reinterpret<float>(1);
{
var pDest = pDestStart;
while (pDest < pDestEnd)
{
var c = color[pDest];
c.a = (byteDepth < 4) ? alphaChannel[idxSrc] : RGBByteFromHDRFloat(srcAlphaChannel[idxSrc / 4]);
color[pDest] = c;
pDest++;
idxSrc += byteDepth;
}
}
}
}
private static void DecodeMaskAlphaRow32(NativeArray<byte> pAlpha, int pAlphaStart, int pAlphaEnd, NativeArray<byte> pMask, int pMaskStart)
{
NativeArray<float> floatArray = pMask.Reinterpret<float>(1);
while (pAlphaStart < pAlphaEnd)
{
pAlpha[pAlphaStart] = RGBByteFromHDRFloat(floatArray[pMaskStart * 4]);
pAlphaStart++;
pMaskStart += 4;
}
}
private static void DecodeMaskAlphaRow(NativeArray<byte> pAlpha, int pAlphaStart, int pAlphaEnd, NativeArray<byte> pMask, int pMaskStart, int byteDepth)
{
while (pAlphaStart < pAlphaEnd)
{
pAlpha[pAlphaStart] = pMask[pMaskStart];
pAlphaStart++;
pMaskStart += byteDepth;
}
}
private static void ApplyPDNMask(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, NativeArray<byte> layerMaskAlpha, NativeArray<byte> userMaskAlpha)
{
// Do nothing if there are no masks
if ((layerMaskAlpha.Length <= 1) && (userMaskAlpha.Length <= 1))
{
return;
}
// Apply one mask
else if ((layerMaskAlpha.Length <= 1) || (userMaskAlpha.Length <= 1))
{
var maskAlpha = (layerMaskAlpha.Length <= 1) ? userMaskAlpha : layerMaskAlpha;
var maskStart = 0;
{
while (pDestStart < pDestEnd)
{
var c = color[pDestStart];
c.a = (byte)(color[pDestStart].a * maskAlpha[maskStart] / 255);
color[pDestStart] = c;
pDestStart++;
maskStart++;
}
}
}
// Apply both masks in one pass, to minimize rounding error
else
{
//fixed (byte* pLayerMaskAlpha = &layerMaskAlpha[0],
// pUserMaskAlpha = &userMaskAlpha[0])
{
var maskStart = 0;
while (pDestStart < pDestEnd)
{
var alphaFactor = (layerMaskAlpha[maskStart]) * (userMaskAlpha[maskStart]);
var c = color[pDestStart];
c.a = (byte)(color[pDestStart].a * alphaFactor / 65025);
color[pDestStart] = c;
pDestStart++;
maskStart++;
}
}
}
}
///////////////////////////////////////////////////////////////////////////
#region Decode 32-bit HDR channels
private static void SetPDNRowRgb32(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, int idxSrc, DecodeContext context)
{
NativeArray<float> redChannel = context.Channels[0].ImageData.Reinterpret<float>(1);
NativeArray<float> greenChannel = context.Channels[1].ImageData.Reinterpret<float>(1);
NativeArray<float> blueChannel = context.Channels[2].ImageData.Reinterpret<float>(1);
{
while (pDestStart < pDestEnd)
{
var c = color[pDestStart];
c.r = RGBByteFromHDRFloat(redChannel[idxSrc / 4]);
c.g = RGBByteFromHDRFloat(greenChannel[idxSrc / 4]);
c.b = RGBByteFromHDRFloat(blueChannel[idxSrc / 4]);
color[pDestStart] = c;
pDestStart++;
idxSrc += 4;
}
}
}
private static void SetPDNRowGrayscale32(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, int idxSrc, DecodeContext context)
{
NativeArray<float> channel = context.Channels[0].ImageData.Reinterpret<float>(1);
{
while (pDestStart < pDestEnd)
{
byte rgbValue = RGBByteFromHDRFloat(channel[idxSrc / 4]);
var c = color[pDestStart];
c.r = rgbValue;
c.g = rgbValue;
c.b = rgbValue;
color[pDestStart] = c;
pDestStart++;
idxSrc += 4;
}
}
}
#endregion
///////////////////////////////////////////////////////////////////////////
#region Decode 8-bit and 16-bit channels
private static void SetPDNRowRgb(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, int idxSrc, DecodeContext context)
{
while (pDestStart < pDestEnd)
{
var c = color[pDestStart];
c.r = context.Channels[0].ImageData[idxSrc];
c.g = context.Channels[1].ImageData[idxSrc];
c.b = context.Channels[2].ImageData[idxSrc];
color[pDestStart] = c;
pDestStart++;
idxSrc += context.ByteDepth;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// The color-conversion formulas come from the Colour Space Conversions FAQ:
// http://www.poynton.com/PDFs/coloureq.pdf
//
// RGB --> CMYK CMYK --> RGB
// --------------------------------------- --------------------------------------------
// Black = minimum(1-Red,1-Green,1-Blue) Red = 1-minimum(1,Cyan*(1-Black)+Black)
// Cyan = (1-Red-Black)/(1-Black) Green = 1-minimum(1,Magenta*(1-Black)+Black)
// Magenta = (1-Green-Black)/(1-Black) Blue = 1-minimum(1,Yellow*(1-Black)+Black)
// Yellow = (1-Blue-Black)/(1-Black)
//
///////////////////////////////////////////////////////////////////////////////
private static void SetPDNRowCmyk(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, int idxSrc, DecodeContext context)
{
while (pDestStart < pDestEnd)
{
// CMYK values are stored as complements, presumably to allow for some
// measure of compatibility with RGB-only applications.
var C = 255 - context.Channels[0].ImageData[idxSrc];
var M = 255 - context.Channels[1].ImageData[idxSrc];
var Y = 255 - context.Channels[2].ImageData[idxSrc];
var K = 255 - context.Channels[3].ImageData[idxSrc];
int nRed = 255 - Math.Min(255, C * (255 - K) / 255 + K);
int nGreen = 255 - Math.Min(255, M * (255 - K) / 255 + K);
int nBlue = 255 - Math.Min(255, Y * (255 - K) / 255 + K);
var c = color[pDestStart];
c.r = (byte)nRed;
c.g = (byte)nGreen;
c.b = (byte)nBlue;
color[pDestStart] = c;
pDestStart++;
idxSrc += context.ByteDepth;
}
}
private static void SetPDNRowBitmap(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, int idxSrc, DecodeContext context)
{
var bitmap = context.Channels[0].ImageData;
while (pDestStart < pDestEnd)
{
byte mask = (byte)(0x80 >> (idxSrc % 8));
byte bwValue = (byte)(bitmap[idxSrc / 8] & mask);
bwValue = (bwValue == 0) ? (byte)255 : (byte)0;
var c = color[pDestStart];
c.r = bwValue;
c.g = bwValue;
c.b = bwValue;
color[pDestStart] = c;
pDestStart++;
idxSrc += context.ByteDepth;
}
}
private static void SetPDNRowGrayscale(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, int idxSrc, DecodeContext context)
{
while (pDestStart < pDestEnd)
{
var level = context.Channels[0].ImageData[idxSrc];
var c = color[pDestStart];
c.r = level;
c.g = level;
c.b = level;
color[pDestStart] = c;
pDestStart++;
idxSrc += context.ByteDepth;
}
}
private static void SetPDNRowIndexed(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, int idxSrc, DecodeContext context)
{
while (pDestStart < pDestEnd)
{
int index = (int)context.Channels[0].ImageData[idxSrc];
var c = color[pDestStart];
c.r = (byte)context.ColorModeData[index];
c.g = context.ColorModeData[index + 256];
c.b = context.ColorModeData[index + 2 * 256];
color[pDestStart] = c;
pDestStart++;
idxSrc += context.ByteDepth;
}
}
private static void SetPDNRowLab(int pDestStart, int pDestEnd, int width, NativeArray<Color32> color, int idxSrc, DecodeContext context)
{
while (pDestStart < pDestEnd)
{
double exL, exA, exB;
exL = (double)context.Channels[0].ImageData[idxSrc];
exA = (double)context.Channels[1].ImageData[idxSrc];
exB = (double)context.Channels[2].ImageData[idxSrc];
int L = (int)(exL / 2.55);
int a = (int)(exA - 127.5);
int b = (int)(exB - 127.5);
// First, convert from Lab to XYZ.
// Standards used Observer = 2, Illuminant = D65
const double ref_X = 95.047;
const double ref_Y = 100.000;
const double ref_Z = 108.883;
double var_Y = ((double)L + 16.0) / 116.0;
double var_X = (double)a / 500.0 + var_Y;
double var_Z = var_Y - (double)b / 200.0;
double var_X3 = var_X * var_X * var_X;
double var_Y3 = var_Y * var_Y * var_Y;
double var_Z3 = var_Z * var_Z * var_Z;
if (var_Y3 > 0.008856)
var_Y = var_Y3;
else
var_Y = (var_Y - 16 / 116) / 7.787;
if (var_X3 > 0.008856)
var_X = var_X3;
else
var_X = (var_X - 16 / 116) / 7.787;
if (var_Z3 > 0.008856)
var_Z = var_Z3;
else
var_Z = (var_Z - 16 / 116) / 7.787;
double X = ref_X * var_X;
double Y = ref_Y * var_Y;
double Z = ref_Z * var_Z;
// Then, convert from XYZ to RGB.
// Standards used Observer = 2, Illuminant = D65
// ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883
double var_R = X * 0.032406 + Y * (-0.015372) + Z * (-0.004986);
double var_G = X * (-0.009689) + Y * 0.018758 + Z * 0.000415;
double var_B = X * 0.000557 + Y * (-0.002040) + Z * 0.010570;
if (var_R > 0.0031308)
var_R = 1.055 * (Math.Pow(var_R, 1 / 2.4)) - 0.055;
else
var_R = 12.92 * var_R;
if (var_G > 0.0031308)
var_G = 1.055 * (Math.Pow(var_G, 1 / 2.4)) - 0.055;
else
var_G = 12.92 * var_G;
if (var_B > 0.0031308)
var_B = 1.055 * (Math.Pow(var_B, 1 / 2.4)) - 0.055;
else
var_B = 12.92 * var_B;
int nRed = (int)(var_R * 256.0);
int nGreen = (int)(var_G * 256.0);
int nBlue = (int)(var_B * 256.0);
if (nRed < 0)
nRed = 0;
else if (nRed > 255)
nRed = 255;
if (nGreen < 0)
nGreen = 0;
else if (nGreen > 255)
nGreen = 255;
if (nBlue < 0)
nBlue = 0;
else if (nBlue > 255)
nBlue = 255;
var c = color[pDestStart];
c.r = (byte)nRed;
c.g = (byte)nGreen;
c.b = (byte)nBlue;
color[pDestStart] = c;
pDestStart++;
idxSrc += context.ByteDepth;
}
}
#endregion
}
}

View file

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

View file

@ -0,0 +1,301 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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;
using System.Threading;
using PhotoshopFile;
using UnityEngine;
using PDNWrapper;
using Unity.Collections;
using Unity.Jobs;
namespace PaintDotNet.Data.PhotoshopFileType
{
internal static class PsdLoad
{
public static PsdFile Load(System.IO.Stream input, ELoadFlag loadFlag)
{
var loadContext = new DocumentLoadContext();
return new PsdFile(input, loadContext, loadFlag);
}
public static Document Load(System.IO.Stream input)
{
// Load and decompress Photoshop file structures
var loadContext = new DocumentLoadContext();
var psdFile = new PsdFile(input, loadContext);
// Multichannel images are loaded by processing each channel as a
// grayscale layer.
if (psdFile.ColorMode == PsdColorMode.Multichannel)
{
CreateLayersFromChannels(psdFile);
psdFile.ColorMode = PsdColorMode.Grayscale;
}
// Convert into Paint.NET internal representation
var document = new Document(psdFile.ColumnCount, psdFile.RowCount);
if (psdFile.Layers.Count == 0)
{
psdFile.BaseLayer.CreateMissingChannels();
var layer = PDNWrapper.Layer.CreateBackgroundLayer(psdFile.ColumnCount, psdFile.RowCount);
ImageDecoderPdn.DecodeImage(layer, psdFile.BaseLayer);
layer.Name = String.IsNullOrEmpty(psdFile.BaseLayer.Name)? "Background" : psdFile.BaseLayer.Name;
layer.Opacity = psdFile.BaseLayer.Opacity;
layer.Visible = psdFile.BaseLayer.Visible;
layer.IsGroup = psdFile.BaseLayer.IsGroup;
layer.LayerID = psdFile.BaseLayer.LayerID;
layer.BlendMode = BlendModeMapping.FromPsdBlendMode(psdFile.BaseLayer.BlendModeKey);
document.Layers.Add(layer);
}
else
{
psdFile.VerifyLayerSections();
ApplyLayerSections(psdFile.Layers);
//var pdnLayers = psdFile.Layers.AsParallel().AsOrdered()
// .Select(psdLayer => psdLayer.DecodeToPdnLayer())
// .ToList();
//document.Layers.AddRange(pdnLayers);
/*
foreach (var l in psdFile.Layers)
{
document.Layers.Add(l.DecodeToPdnLayer());
}
*/
BitmapLayer parent = null;
JobHandle jobHandle = default(JobHandle);
foreach (var l in Enumerable.Reverse(psdFile.Layers))
{
if (l.IsEndGroupMarker)
{
parent = parent != null ? parent.ParentLayer : null;
continue;
}
BitmapLayer b = null;
jobHandle = l.DecodeToPdnLayer(jobHandle, out b);
b.ParentLayer = parent;
if (parent != null)
parent.ChildLayer.Add(b);
else
document.Layers.Add(b);
if (b.IsGroup)
parent = b;
}
jobHandle.Complete();
}
SetPdnResolutionInfo(psdFile, document);
psdFile.Cleanup();
return document;
}
internal static JobHandle DecodeToPdnLayer(this PhotoshopFile.Layer psdLayer, JobHandle inputDeps, out BitmapLayer pdnLayer)
{
var psdFile = psdLayer.PsdFile;
psdLayer.CreateMissingChannels();
pdnLayer = new BitmapLayer(psdFile.ColumnCount, psdFile.RowCount);
pdnLayer.Name = psdLayer.Name;
pdnLayer.Opacity = psdLayer.Opacity;
pdnLayer.Visible = psdLayer.Visible;
pdnLayer.IsGroup = psdLayer.IsGroup;
pdnLayer.LayerID = psdLayer.LayerID;
pdnLayer.BlendMode = BlendModeMapping.FromPsdBlendMode(psdLayer.BlendModeKey);
return ImageDecoderPdn.DecodeImage(pdnLayer, psdLayer, inputDeps);
}
/// <summary>
/// Creates a layer for each channel in a multichannel image.
/// </summary>
private static void CreateLayersFromChannels(PsdFile psdFile)
{
if (psdFile.ColorMode != PsdColorMode.Multichannel)
throw new Exception("Not a multichannel image.");
if (psdFile.Layers.Count > 0)
throw new PsdInvalidException("Multichannel image should not have layers.");
// Get alpha channel names, preferably in Unicode.
var alphaChannelNames = (AlphaChannelNames)psdFile.ImageResources
.Get(ResourceID.AlphaChannelNames);
var unicodeAlphaNames = (UnicodeAlphaNames)psdFile.ImageResources
.Get(ResourceID.UnicodeAlphaNames);
if ((alphaChannelNames == null) && (unicodeAlphaNames == null))
throw new PsdInvalidException("No channel names found.");
var channelNames = (unicodeAlphaNames != null)
? unicodeAlphaNames.ChannelNames
: alphaChannelNames.ChannelNames;
var channels = psdFile.BaseLayer.Channels;
if (channels.Count > channelNames.Count)
throw new PsdInvalidException("More channels than channel names.");
// Channels are stored from top to bottom, but layers are stored from
// bottom to top.
for (int i = channels.Count - 1; i >= 0; i--)
{
var channel = channels[i];
var channelName = channelNames[i];
// Copy metadata over from base layer
var layer = new PhotoshopFile.Layer(psdFile);
layer.Rect = psdFile.BaseLayer.Rect;
layer.Visible = true;
layer.Masks = new MaskInfo();
layer.BlendingRangesData = new BlendingRanges(layer);
// We do not attempt to reconstruct the appearance of the image, but
// only to provide access to the channels image data.
layer.Name = channelName;
layer.BlendModeKey = PsdBlendMode.Darken;
layer.Opacity = 255;
// Copy channel image data into the new grayscale layer
var layerChannel = new Channel(0, layer);
layerChannel.ImageCompression = channel.ImageCompression;
layerChannel.ImageData = new NativeArray<byte>(channel.ImageData, Allocator.Persistent);
layer.Channels.Add(layerChannel);
psdFile.Layers.Add(layer);
}
}
/// <summary>
/// Transform Photoshop's layer tree to Paint.NET's flat layer list.
/// Indicate where layer sections begin and end, and hide all layers within
/// hidden layer sections.
/// </summary>
private static void ApplyLayerSections(List<PhotoshopFile.Layer> layers)
{
// BUG: PsdPluginResources.GetString will always return English resource,
// because Paint.NET does not set the CurrentUICulture when OnLoad is
// called. This situation should be resolved with Paint.NET 4.0, which
// will provide an alternative mechanism to retrieve the UI language.
// Track the depth of the topmost hidden section. Any nested sections
// will be hidden, whether or not they themselves have the flag set.
int topHiddenSectionDepth = Int32.MaxValue;
var layerSectionNames = new Stack<string>();
// Layers are stored bottom-to-top, but layer sections are specified
// top-to-bottom.
foreach (var layer in Enumerable.Reverse(layers))
{
// Leo: Since we are importing, we don't care if the group is collapsed
// Apply to all layers within the layer section, as well as the
// closing layer.
//if (layerSectionNames.Count > topHiddenSectionDepth)
// layer.Visible = false;
var sectionInfo = (LayerSectionInfo)layer.AdditionalInfo
.SingleOrDefault(x => x is LayerSectionInfo);
if (sectionInfo == null)
continue;
switch (sectionInfo.SectionType)
{
case LayerSectionType.OpenFolder:
case LayerSectionType.ClosedFolder:
// Start a new layer section
if ((!layer.Visible) && (topHiddenSectionDepth == Int32.MaxValue))
topHiddenSectionDepth = layerSectionNames.Count;
layerSectionNames.Push(layer.Name);
layer.IsGroup = true;
//layer.Name = String.Format(beginSectionWrapper, layer.Name);
break;
case LayerSectionType.SectionDivider:
// End the current layer section
//var layerSectionName = layerSectionNames.Pop ();
if (layerSectionNames.Count == topHiddenSectionDepth)
topHiddenSectionDepth = Int32.MaxValue;
layer.IsEndGroupMarker = true;
//layer.Name = String.Format(endSectionWrapper, layerSectionName);
break;
}
}
}
/// <summary>
/// Set the resolution on the Paint.NET Document to match the PSD file.
/// </summary>
private static void SetPdnResolutionInfo(PsdFile psdFile, Document document)
{
if (psdFile.Resolution != null)
{
// PSD files always specify the resolution in DPI. When loading and
// saving cm, we will have to round-trip the conversion, but doubles
// have plenty of precision to spare vs. PSD's 16/16 fixed-point.
if ((psdFile.Resolution.HResDisplayUnit == ResolutionInfo.ResUnit.PxPerCm)
&& (psdFile.Resolution.VResDisplayUnit == ResolutionInfo.ResUnit.PxPerCm))
{
document.DpuUnit = MeasurementUnit.Centimeter;
// HACK: Paint.NET truncates DpuX and DpuY to three decimal places,
// so add 0.0005 to get a rounded value instead.
document.DpuX = psdFile.Resolution.HDpi / 2.54 + 0.0005;
document.DpuY = psdFile.Resolution.VDpi / 2.54 + 0.0005;
}
else
{
document.DpuUnit = MeasurementUnit.Inch;
document.DpuX = psdFile.Resolution.HDpi;
document.DpuY = psdFile.Resolution.VDpi;
}
}
}
/// <summary>
/// Verify that the PSD file will fit into physical memory once loaded
/// and converted to Paint.NET format.
/// </summary>
/// <remarks>
/// This check is necessary because layers in Paint.NET have the same
/// dimensions as the canvas. Thus, PSD files that contain lots of
/// tiny adjustment layers may blow up in size by several
/// orders of magnitude.
/// </remarks>
internal static void CheckSufficientMemory(PsdFile psdFile)
{
// Multichannel images have channels converted to layers
var numLayers = (psdFile.ColorMode == PsdColorMode.Multichannel)
? psdFile.BaseLayer.Channels.Count
: Math.Max(psdFile.Layers.Count, 1);
// Paint.NET also requires a scratch layer and composite layer
numLayers += 2;
long numPixels = (long)psdFile.ColumnCount * psdFile.RowCount;
ulong bytesRequired = (ulong)(checked(4 * numPixels * numLayers));
// Check that the file will fit entirely into physical memory, so that we
// do not thrash and make the Paint.NET UI nonresponsive. We also have
// to check against virtual memory address space because 32-bit processes
// cannot access all 4 GB.
//var computerInfo = new Microsoft.VisualBasic.Devices.ComputerInfo();
//var accessibleMemory = Math.Min(computerInfo.TotalPhysicalMemory,
// computerInfo.TotalVirtualMemory);
var accessibleMemory = (ulong)SystemInfo.systemMemorySize * 1024 * 1024;
if (bytesRequired > accessibleMemory)
{
throw new OutOfMemoryException();
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: da02470271ab86841bf9d88cc0bed2ec
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: e59e9fe7abf273d468dc9e55f8a8acaa
folderAsset: yes
timeCreated: 1495006553
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

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:

View file

@ -0,0 +1,9 @@
{
"name": "PsdPlugin",
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true
}

Some files were not shown because too many files have changed in this diff Show more