mirror of
https://gitgud.io/AbstractConcept/rimworld-animation-studio.git
synced 2024-08-15 00:43:27 +00:00
Initial commit
This commit is contained in:
commit
3c7cc0c973
8391 changed files with 704313 additions and 0 deletions
|
@ -0,0 +1,3 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.2D.PsdImporter.Editor")]
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 86edfd93afbfbfd4a9dd8afefbb6ba14
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9fcfe7284d9f34b4eae286b83227271d
|
||||
folderAsset: yes
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 43a7a92da0ad89c40b5a39ed87329536
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5d036cb10363b5f4b94b39c7afb3e2d0
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f396654a3007906488e747d54ae80913
|
||||
timeCreated: 1495006554
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 806458e67991c7449a38f7d942188a0e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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() + "}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e8fe32ddfd1506f47955dad6247df316
|
||||
timeCreated: 1495006554
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a81bd8318454c4f4f9e22a9e00bb9e3d
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1b467712e18b40c4f90510e9d0364faf
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 36a1cbeb5badf2a4abdcb1dcefa0efb6
|
||||
folderAsset: yes
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3045ca2469fb18a48a1b874587e1df9e
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 138d3cdf8c274dd4c995e7ec72539d1b
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 812b92369e1c26c4eb12b733b257bd85
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: da02470271ab86841bf9d88cc0bed2ec
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e59e9fe7abf273d468dc9e55f8a8acaa
|
||||
folderAsset: yes
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cea9a02ce8d970946b9534af754bfda1
|
||||
folderAsset: yes
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1a1cbf26300dbdd4ebaf8e0a92b67db6
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 23318de62729646489ccb1545e7c5bbf
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3a37c02e17e67be44b01952ad661991c
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b976ddccf1109574c922e1aa8cdd48fb
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 66819b54b3c858446a1518b135b6514b
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 87da90d1a63b1174cb56810e4d9f769e
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6259913e8d526e447bf2ff536882fa6c
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2bb49ac2cee975745af2d48523e135c9
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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) {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1359900d9d213c641b1d44f49de5281d
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4bdfc57c0f4c65947a6971d815bd7ea7
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f6b2d5715ac4ed8478b3819e8fe5918a
|
||||
folderAsset: yes
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3e90381151086314691cd7a595ba9a62
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 764df10adc2353a47a5ae0e9649e51a6
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 285de09dadd2d3048a1028754eda2fa1
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 950af7a846d6ca046a6138dcc3735835
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8ba836b7a6b0c9a4a94c1d6c540b8962
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 948f26d9a8b009540b6574d5b592248d
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a2b81847c6b672747861b07e47320717
|
||||
folderAsset: yes
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9f4a47927e764174fa3984b07f9d4b58
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c12f761461a76f44eab4f22f85e988b1
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2dff4719950c8914097f7b77c9b07de9
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5bf52a9b627c3374385cbd0ccc5caacd
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 45831abf313417241b91683b30dedf45
|
||||
folderAsset: yes
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 81ec2894805ff534395009ab7c13ea4c
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4c40067c9a690e44ca098f5e4ed763e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4ca20fe44ab2bfd4d8a1c3547b4a136f
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 277ac9726bb060c48860d92018fc06cf
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8b73fc997c117d641a1673cd96fc8a07
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2b6148b9ac6738b4999b4686f3820560
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3b268b1ee06ace349a872a7d95498aba
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9614f1c9b6a2eb5499429bfab13a0c09
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c4e78ec955914854d9c9c68fa7af1742
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3de656e186ce0704e8676716fcaf733f
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f9404b28690a74b4f918e7006968dd80
|
||||
timeCreated: 1495006554
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ef0d2c47c58545249b479bc1706d89b7
|
||||
timeCreated: 1495006554
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6fcbb5d3e3f3adf479ac8a933b929a8a
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2bab0d96ebe77084d97602af7f3fd124
|
||||
timeCreated: 1495006553
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue