From e33c510e359f2de624538b78fa699980cf7e9bbb Mon Sep 17 00:00:00 2001 From: Unity Technologies <@unity.com> Date: Thu, 9 Apr 2020 00:00:00 +0200 Subject: [PATCH] com.unity.2d.psdimporter@2.1.4 ## [2.1.4] - 2020-04-09 ### Fixed - Fix PSD import issues with PSD file without unique layer id - Fix crash on importing huge PSD files - Fix metafile not updated when reimporting - Fix error when importing PSB files with 32-bit color ### Changed - Improve PSD file import performance --- CHANGELOG.md | 9 + Editor/PSDImporter.cs | 108 ++- Editor/PSDImporterEditor.cs | 2 +- Editor/PSDPlugin/PDNWrapper/PDNDecodeJob.cs | 629 +++++++++++++++++- Editor/PSDPlugin/PDNWrapper/Surface.cs | 2 +- .../PhotoShopFileType/ImageDecoderPdn.cs | 413 ++++++++---- Editor/PSDPlugin/PhotoShopFileType/PsdLoad.cs | 22 +- .../PsdFile/Compression/EndianReverser.cs | 12 - .../PsdFile/Compression/ImageData.cs | 23 - .../PSDPlugin/PsdFile/Compression/RawImage.cs | 5 - .../PSDPlugin/PsdFile/Compression/RleImage.cs | 26 - .../PSDPlugin/PsdFile/Compression/ZipImage.cs | 5 - .../PsdFile/Compression/ZipPredict16Image.cs | 17 - .../PsdFile/Compression/ZipPredict32Image.cs | 21 +- Editor/PSDPlugin/PsdFile/ImageResource.cs | 32 - .../ImageResources/AlphaChannelNames.cs | 8 - .../ImageResources/RawImageResource.cs | 5 - .../PsdFile/ImageResources/ResolutionInfo.cs | 13 - .../ImageResources/UnicodeAlphaNames.cs | 10 - .../PsdFile/ImageResources/VersionInfo.cs | 9 - .../PsdFile/Layers/BlendingRanges.cs | 18 - Editor/PSDPlugin/PsdFile/Layers/Channel.cs | 97 +-- Editor/PSDPlugin/PsdFile/Layers/Layer.cs | 87 +-- Editor/PSDPlugin/PsdFile/Layers/LayerInfo.cs | 32 - .../PsdFile/Layers/LayerInfo/InfoLayers.cs | 5 - .../PsdFile/Layers/LayerInfo/LayerId.cs | 5 - .../Layers/LayerInfo/LayerSectionInfo.cs | 12 - .../Layers/LayerInfo/LayerUnicodeName.cs | 8 - .../PsdFile/Layers/LayerInfo/RawLayerInfo.cs | 5 - Editor/PSDPlugin/PsdFile/Layers/Mask.cs | 40 +- Editor/PSDPlugin/PsdFile/PsdFile.cs | 253 +------ Editor/PSDPlugin/PsdFile/RleRowLengths.cs | 15 - Editor/PSDPlugin/PsdFile/Util.cs | 22 + Editor/Tasks/ExtractLayerTask.cs | 4 +- package.json | 10 +- 35 files changed, 1081 insertions(+), 903 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 159f9e6..a08dc89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,13 @@ # Changelog +## [2.1.4] - 2020-04-09 +### Fixed +- Fix PSD import issues with PSD file without unique layer id +- Fix crash on importing huge PSD files +- Fix metafile not updated when reimporting +- Fix error when importing PSB files with 32-bit color + +### Changed +- Improve PSD file import performance ## [2.1.3] - 2020-03-20 ### Changed diff --git a/Editor/PSDImporter.cs b/Editor/PSDImporter.cs index 8015e86..278fa4d 100644 --- a/Editor/PSDImporter.cs +++ b/Editor/PSDImporter.cs @@ -13,6 +13,8 @@ using UnityEngine.Experimental.U2D.Animation; using UnityEngine.U2D; using UnityEngine.U2D.Animation; +using UnityEditor.MemoryProfiler; +using UnityEngine.Profiling.Memory.Experimental; namespace UnityEditor.U2D.PSD { @@ -20,16 +22,35 @@ namespace UnityEditor.U2D.PSD /// ScriptedImporter to import Photoshop files /// [ScriptedImporter(4, "psb")] - [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.psdimporter@2.1/manual/index.html")] + [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.psdimporter@3.1/manual/index.html")] public class PSDImporter : ScriptedImporter, ISpriteEditorDataProvider { - class GameObjectCreationFactory + class UniqueNameGenerator + { + List m_NameHash = new List(); + + public bool ContainHash(int i) + { + return m_NameHash.Contains(i); + } + + public void AddHash(int i) + { + m_NameHash.Add(i); + } + + public string GetUniqueName(string name) + { + return PSDImporter.GetUniqueName(name, m_NameHash); + } + } + + class GameObjectCreationFactory : UniqueNameGenerator { - List m_GameObjectNameHash = new List(); public GameObject CreateGameObject(string name, params System.Type[] components) { - var newName = GetUniqueName(name, m_GameObjectNameHash); + var newName = GetUniqueName(name); return new GameObject(newName, components); } } @@ -184,10 +205,6 @@ internal int textureActualHeight /// public override void OnImportAsset(AssetImportContext ctx) { - string ext = System.IO.Path.GetExtension(ctx.assetPath).ToLower(); - if (ext != ".psb") - throw new Exception("File does not have psb extension"); - FileStream fileStream = new FileStream(ctx.assetPath, FileMode.Open, FileAccess.Read); Document doc = null; try @@ -198,21 +215,8 @@ public override void OnImportAsset(AssetImportContext ctx) doc = PaintDotNet.Data.PhotoshopFileType.PsdLoad.Load(fileStream); UnityEngine.Profiling.Profiler.EndSample(); - // Is layer id truely unique? - for (int i = 0; i < doc.Layers.Count; ++i) - { - for (int j = 0; j < doc.Layers.Count; ++j) - { - if (i == j) - continue; - if (doc.Layers[i].LayerID == doc.Layers[j].LayerID) - { - Debug.LogWarning("File's Layer ID is not unique. Please report to developer. " + doc.Layers[i].LayerID + " " + doc.Layers[i].Name + "::" + doc.Layers[j].Name); - doc.Layers[i].LayerID = doc.Layers[i].Name.GetHashCode(); - } - } - } - + ValidatePSDLayerId(doc); + m_DocumentSize = new Vector2Int(doc.width, doc.height); bool singleSpriteMode = m_TextureImporterSettings.textureType == TextureImporterType.Sprite && m_TextureImporterSettings.spriteMode != (int)SpriteImportMode.Multiple; EnsureSingleSpriteExist(); @@ -220,7 +224,7 @@ public override void OnImportAsset(AssetImportContext ctx) if (m_TextureImporterSettings.textureType != TextureImporterType.Sprite || m_MosaicLayers == false || singleSpriteMode) { - var image = new NativeArray(doc.width * doc.height, Allocator.Temp); + var image = new NativeArray(doc.width * doc.height, Allocator.Persistent); try { var spriteImportData = GetSpriteImportData(); @@ -264,11 +268,49 @@ public override void OnImportAsset(AssetImportContext ctx) if (doc != null) doc.Dispose(); UnityEngine.Profiling.Profiler.EndSample(); + EditorUtility.SetDirty(this); + } + + + // Debug Profiler. + // UnityEngine.Profiling.Memory.Experimental.MemoryProfiler.TakeSnapshot("snapshot.snap", MemorySnapshotFinish, CaptureFlags.ManagedObjects | CaptureFlags.NativeObjects | CaptureFlags.NativeAllocations | CaptureFlags.NativeStackTraces); + } + + static void ValidatePSDLayerId(List layers, UniqueNameGenerator uniqueNameGenerator) + { + for (int i = 0; i < layers.Count; ++i) + { + if (uniqueNameGenerator.ContainHash(layers[i].LayerID)) + { + var importWarning = string.Format("Layer {0}: LayerId is not unique. Mapping will be done by Layer's name.", layers[i].Name); + var layerName = uniqueNameGenerator.GetUniqueName(layers[i].Name); + if (layerName != layers[i].Name) + importWarning += "\nLayer names are not unique. Please ensure they are unique to for SpriteRect to be mapped back correctly."; + layers[i].LayerID = layerName.GetHashCode(); + Debug.LogWarning(importWarning); + } + else + uniqueNameGenerator.AddHash(layers[i].LayerID); + if (layers[i].ChildLayer != null) + { + ValidatePSDLayerId(layers[i].ChildLayer, uniqueNameGenerator); + } } } + + + void ValidatePSDLayerId(Document doc) + { + UniqueNameGenerator uniqueNameGenerator = new UniqueNameGenerator(); + ValidatePSDLayerId(doc.Layers, uniqueNameGenerator); + } + TextureGenerationOutput ImportTexture(AssetImportContext ctx, NativeArray imageData, int textureWidth, int textureHeight, int spriteStart, int spriteCount) { + if (!imageData.IsCreated || imageData.Length == 0) + return new TextureGenerationOutput(); + UnityEngine.Profiling.Profiler.BeginSample("ImportTexture"); var platformSettings = GetPlatformTextureSettings(ctx.selectedBuildTarget); @@ -507,13 +549,21 @@ void ImportFromLayers(AssetImportContext ctx, Document doc) } } } + + foreach (var l in oldPsdLayers) + l.Dispose(); oldPsdLayers.Clear(); + oldPsdLayers.AddRange(psdLayers); m_ImportedTextureHeight = textureActualHeight = height; m_ImportedTextureWidth = textureActualWidth = width; var generatedTexture = ImportTexture(ctx, output, width, height, 0, spriteImportData.Count); - m_ImportedTextureHeight = generatedTexture.texture.height; - m_ImportedTextureWidth = generatedTexture.texture.width; + + if (generatedTexture.texture) + { + m_ImportedTextureHeight = generatedTexture.texture.height; + m_ImportedTextureWidth = generatedTexture.texture.width; + } RegisterAssets(ctx, generatedTexture); } @@ -524,6 +574,12 @@ void ImportFromLayers(AssetImportContext ctx, Document doc) foreach (var l in oldPsdLayers) l.Dispose(); } + + } + + void MemorySnapshotFinish(string path, bool done) + { + } void EnsureSingleSpriteExist() diff --git a/Editor/PSDImporterEditor.cs b/Editor/PSDImporterEditor.cs index de6b475..61d9d71 100644 --- a/Editor/PSDImporterEditor.cs +++ b/Editor/PSDImporterEditor.cs @@ -194,7 +194,7 @@ protected override void Apply() is_psd = IsPSD(doc), color_mode = FileColorMode(doc) }; - + doc.Cleanup(); AnalyticFactory.analytics.SendApplyEvent(evt); base.Apply(); } diff --git a/Editor/PSDPlugin/PDNWrapper/PDNDecodeJob.cs b/Editor/PSDPlugin/PDNWrapper/PDNDecodeJob.cs index deac2df..c50d074 100644 --- a/Editor/PSDPlugin/PDNWrapper/PDNDecodeJob.cs +++ b/Editor/PSDPlugin/PDNWrapper/PDNDecodeJob.cs @@ -1,22 +1,623 @@ -#if ENABLE_JOBS using System.Collections; using System.Collections.Generic; +using Unity.Jobs; using UnityEngine; -using UnityEngine.Collections; -using UnityEngine.Jobs; +using Unity.Collections; +using System; +using Unity.Collections.LowLevel.Unsafe; -struct PDNDecodeJobOut +namespace PaintDotNet.Data.PhotoshopFileType { - public int width; - public int height; - NativeArray buffer; -} -internal class PDNDecodeJob : IJob -{ - PDNDecodeJobOut output; - public void Execute() + #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 ColorChannel0; + + [NativeDisableParallelForRestriction] + [ReadOnly] + public NativeArray ColorChannel1; + + [NativeDisableParallelForRestriction] + [ReadOnly] + public NativeArray ColorChannel2; + + [NativeDisableParallelForRestriction] + [ReadOnly] + public NativeArray ColorChannel3; + + [NativeDisableParallelForRestriction] + [ReadOnly] + [DeallocateOnJobCompletion] + public NativeArray ColorModeData; + + // Outputs + [NativeDisableParallelForRestriction] + public NativeArray DecodedImage; + } -} -#endif + + 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 cR = Data.ColorChannel0.Reinterpret(1); + NativeArray cG = Data.ColorChannel1.Reinterpret(1); + NativeArray cB = Data.ColorChannel2.Reinterpret(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 channel = Data.ColorChannel0.Reinterpret(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 AlphaChannel0; + + [NativeDisableParallelForRestriction] + [ReadOnly] + public NativeArray UserMask; + + [DeallocateOnJobCompletion] + [NativeDisableParallelForRestriction] + public NativeArray UserAlphaMask; + + [DeallocateOnJobCompletion] + [NativeDisableParallelForRestriction] + public NativeArray UserAlphaMaskEmpty; + + [NativeDisableParallelForRestriction] + [ReadOnly] + public NativeArray LayerMask; + + [DeallocateOnJobCompletion] + [NativeDisableParallelForRestriction] + public NativeArray LayerAlphaMask; + + [DeallocateOnJobCompletion] + [NativeDisableParallelForRestriction] + public NativeArray LayerAlphaMaskEmpty; + + // Outputs + [NativeDisableParallelForRestriction] + public NativeArray 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 srcAlphaChannel = Data.AlphaChannel0.Reinterpret(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; + { + var c = Data.DecodedImage[dstStart]; + while (dstStart < dstStops) + { + 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; + var c = Data.DecodedImage[dstStart]; + { + while (dstStart < dstStops) + { + 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 Alpha, int dstStart, int dstStops, NativeArray Mask, int maskStart) + { + NativeArray floatArray = Mask.Reinterpret(1); + + while (dstStart < dstStops) + { + Alpha[dstStart] = ImageDecoderPdn.RGBByteFromHDRFloat(floatArray[maskStart / 4]); + + dstStart++; + maskStart += 4; + } + } + + private void DecodeMaskAlphaRow(NativeArray Alpha, int dstStart, int dstStops, NativeArray Mask, int maskStart, int byteDepth) + { + while (dstStart < dstStops) + { + Alpha[dstStart] = Mask[maskStart]; + + dstStart++; + maskStart += byteDepth; + } + } + + private unsafe void GetMaskAlphaRow(int idxSrc, NativeArray alphaBuffer, NativeArray alphaBufferEmpty, NativeArray 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 +} \ No newline at end of file diff --git a/Editor/PSDPlugin/PDNWrapper/Surface.cs b/Editor/PSDPlugin/PDNWrapper/Surface.cs index 659e6a7..f8daa56 100644 --- a/Editor/PSDPlugin/PDNWrapper/Surface.cs +++ b/Editor/PSDPlugin/PDNWrapper/Surface.cs @@ -10,7 +10,7 @@ public Surface(int w, int h) { width = w; height = h; - m_Color = new NativeArray(width * height, Allocator.Temp); + m_Color = new NativeArray(width * height, Allocator.Persistent); } public void Dispose() diff --git a/Editor/PSDPlugin/PhotoShopFileType/ImageDecoderPdn.cs b/Editor/PSDPlugin/PhotoShopFileType/ImageDecoderPdn.cs index 38c5717..9c3a0e2 100644 --- a/Editor/PSDPlugin/PhotoShopFileType/ImageDecoderPdn.cs +++ b/Editor/PSDPlugin/PhotoShopFileType/ImageDecoderPdn.cs @@ -21,20 +21,39 @@ 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 Channel AlphaChannel { get; private set; } + public NativeArray AlphaChannel { get; private set; } public PsdColorMode ColorMode { get; private set; } - public byte[] ColorModeData { get; private set; } + public NativeArray ColorModeData { get; private set; } public Rectangle Rectangle { get; private set; } public MaskDecodeContext LayerMaskContext { get; private set; } @@ -44,10 +63,22 @@ public DecodeContext(PhotoshopFile.Layer layer, Rectangle bounds) { Layer = layer; ByteDepth = Util.BytesFromBitDepth(layer.PsdFile.BitDepth); + HasAlphaChannel = 0; Channels = layer.Channels.ToIdArray(); - AlphaChannel = layer.AlphaChannel; + + 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(alphaSize, Allocator.TempJob); + if (HasAlphaChannel > 0) + NativeArray.Copy(layer.AlphaChannel.ImageData, AlphaChannel, layer.AlphaChannel.ImageData.Length); ColorMode = layer.PsdFile.ColorMode; - ColorModeData = layer.PsdFile.ColorModeData; + ColorModeData = new NativeArray(layer.PsdFile.ColorModeData, Allocator.TempJob); // Clip the layer to the specified bounds Rectangle = Layer.Rect.IntersectWith(bounds); @@ -59,6 +90,12 @@ public DecodeContext(PhotoshopFile.Layer layer, Rectangle bounds) } } + internal void Cleanup() + { + AlphaChannel.Dispose(); + ColorModeData.Dispose(); + } + private MaskDecodeContext GetMaskContext(Mask mask) { if ((mask == null) || (mask.Disabled)) @@ -74,8 +111,6 @@ private class MaskDecodeContext { public Mask Mask { get; private set; } public Rectangle Rectangle { get; private set; } - public byte[] AlphaBuffer { get; private set; } - public MaskDecodeContext(Mask mask, DecodeContext layerContext) { Mask = mask; @@ -84,7 +119,6 @@ public MaskDecodeContext(Mask mask, DecodeContext layerContext) // relative to the layer, but Photoshop treats the position as // absolute. So that's what we do, too. Rectangle = mask.Rect.IntersectWith(layerContext.Rectangle); - AlphaBuffer = new byte[layerContext.Rectangle.Width]; } public bool IsRowEmpty(int row) @@ -97,43 +131,36 @@ public bool IsRowEmpty(int row) } } - /// - /// Decode image from Photoshop's channel-separated formats to BGRA. - /// - public static void DecodeImage(BitmapLayer pdnLayer, - PhotoshopFile.Layer psdLayer) - { - UnityEngine.Profiling.Profiler.BeginSample("DecodeImage"); - var decodeContext = new DecodeContext(psdLayer, pdnLayer.Bounds); - DecodeDelegate decoder = null; - - if (decodeContext.ByteDepth == 4) - decoder = GetDecodeDelegate32(decodeContext.ColorMode); - else - decoder = GetDecodeDelegate(decodeContext.ColorMode); + /////////////////////////////////////////////////////////////////////////////// - DecodeImage(pdnLayer, decodeContext, decoder); - UnityEngine.Profiling.Profiler.EndSample(); + internal static byte RGBByteFromHDRFloat(float ptr) + { + var result = (byte)(255 * Math.Pow(ptr, rgbExponent)); + return result; } - private delegate void DecodeDelegate(int pDestStart, int pDestEnd, int width, NativeArray color, int idxSrc, DecodeContext context); - - private static DecodeDelegate GetDecodeDelegate(PsdColorMode psdColorMode) + 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."); @@ -142,13 +169,15 @@ private static DecodeDelegate GetDecodeDelegate(PsdColorMode psdColorMode) } } - private static DecodeDelegate GetDecodeDelegate32(PsdColorMode psdColorMode) + 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( @@ -156,12 +185,33 @@ private static DecodeDelegate GetDecodeDelegate32(PsdColorMode psdColorMode) } } + /// + /// Decode image from Photoshop's channel-separated formats to BGRA. + /// + 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; + } + /// /// Decode image from Photoshop's channel-separated formats to BGRA, /// using the specified decode delegate on each row. /// - private static void DecodeImage(BitmapLayer pdnLayer, DecodeContext decodeContext, DecodeDelegate decoder) + private static JobHandle DecodeImage(BitmapLayer pdnLayer, DecodeContext decodeContext, DecodeType decoderType, JobHandle inputDeps) { + var psdLayer = decodeContext.Layer; var surface = pdnLayer.Surface; var rect = decodeContext.Rectangle; @@ -169,6 +219,119 @@ private static void DecodeImage(BitmapLayer pdnLayer, DecodeContext decodeContex // 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(userMaskContextSize, Allocator.TempJob); + var layerAlphaMask = new NativeArray(layerMaskContextSize, Allocator.TempJob); + var userAlphaMaskEmpty = new NativeArray(rect.Bottom, Allocator.TempJob); + var layerAlphaMaskEmpty = new NativeArray(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 + /////////////////////////////////////////////////////////////////////////// + + /// + /// Decode image from Photoshop's channel-separated formats to BGRA. + /// + 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 color, int idxSrc, DecodeContext context); + + /// + /// Decode image from Photoshop's channel-separated formats to BGRA, + /// using the specified decode delegate on each row. + /// + 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. @@ -190,73 +353,47 @@ private static void DecodeImage(BitmapLayer pdnLayer, DecodeContext decodeContex // Decode the color and alpha channels decoder(pDestStart, pDestEnd, surface.width, surface.color, idxSrcByte, decodeContext); - SetPDNAlphaRow(pDestStart, pDestEnd, surface.width, surface.color, idxSrcByte, - decodeContext.ByteDepth, decodeContext.AlphaChannel); - - // Apply layer masks(s) to the alpha channel - var layerMaskAlphaRow = GetMaskAlphaRow(y, - decodeContext, decodeContext.LayerMaskContext); - var userMaskAlphaRow = GetMaskAlphaRow(y, - decodeContext, decodeContext.UserMaskContext); - ApplyPDNMask(pDestStart, pDestEnd, surface.width, surface.color, layerMaskAlphaRow, userMaskAlphaRow); } - } - /////////////////////////////////////////////////////////////////////////// + // 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(userMaskContextSize, Allocator.TempJob); + var layerAlphaMask = new NativeArray(layerMaskContextSize, Allocator.TempJob); - private static void SetPDNAlphaRow(int pDestStart, int pDestEnd, int width, NativeArray color, int idxSrc, int byteDepth, Channel alphaChannel) - { - // Set alpha to fully-opaque if there is no alpha channel - if (alphaChannel == null) - { - var pDest = pDestStart; - while (pDest < pDestEnd) - { - var c = color[pDest]; - c.a = 255; - color[pDest] = c; - pDest++; - } - } - // Set the alpha channel data - else + for (int y = rect.Top; y < rect.Bottom; y++) { - var pSrcAlphaChannel = alphaChannel.ImageData; - { - var pDest = pDestStart; - while (pDest < pDestEnd) - { - var c = color[pDest]; - c.a = (byteDepth < 4) - ? pSrcAlphaChannel[idxSrc] - : RGBByteFromHDRFloat(System.BitConverter.ToSingle(pSrcAlphaChannel, idxSrc)); + // 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; - color[pDest] = c; - pDest++; - idxSrc += 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(); } - /////////////////////////////////////////////////////////////////////////// - - /// - /// Gets one row of alpha values from the mask. - /// - /// The y-coordinate of the row. - /// The decode context for the layer containing - /// the mask. - /// The decode context for the mask. - /// An array of alpha values for the row, corresponding to the - /// width of the layer decode context. - private static byte[] GetMaskAlphaRow( - int y, DecodeContext layerContext, MaskDecodeContext maskContext) + private static unsafe void GetMaskAlphaRow(int y, DecodeContext layerContext, MaskDecodeContext maskContext, ref NativeArray alphaBuffer) { if (maskContext == null) - { - return null; - } + return; var mask = maskContext.Mask; // Background color for areas not covered by the mask @@ -264,17 +401,16 @@ private static byte[] GetMaskAlphaRow( ? (byte)(255 - mask.BackgroundColor) : mask.BackgroundColor; { - Util.Fill(maskContext.AlphaBuffer, 0, maskContext.AlphaBuffer.Length, - backgroundColor); + var alphaBufferPtr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(alphaBuffer); + UnsafeUtility.MemSet(alphaBufferPtr, backgroundColor, alphaBuffer.Length); } if (maskContext.IsRowEmpty(y)) { - return maskContext.AlphaBuffer; + return; } ////////////////////////////////////// // Transfer mask into the alpha array - var pAlphaRow = maskContext.AlphaBuffer; var pMaskData = mask.ImageData; { // Get pointers to starting positions @@ -282,8 +418,8 @@ private static byte[] GetMaskAlphaRow( var pAlpha = alphaColumn; var pAlphaEnd = pAlpha + maskContext.Rectangle.Width; - int maskRow = y - maskContext.Mask.Rect.Y; - int maskColumn = maskContext.Rectangle.X - maskContext.Mask.Rect.X; + 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; @@ -294,36 +430,70 @@ private static byte[] GetMaskAlphaRow( // Decode mask into the alpha array. if (layerContext.ByteDepth == 4) { - DecodeMaskAlphaRow32(pAlphaRow, pAlpha, pAlphaEnd, pMaskData, pMask); + DecodeMaskAlphaRow32(alphaBuffer, pAlpha, pAlphaEnd, pMaskData, pMask); } else { - DecodeMaskAlphaRow(pAlphaRow, pAlpha, pAlphaEnd, pMaskData, pMask, layerContext.ByteDepth); + 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(pAlphaRow, pAlpha, pAlphaEnd); + Util.Invert(alphaBuffer, pAlpha, pAlphaEnd); } } + } + + private static void SetPDNAlphaRow(int pDestStart, int pDestEnd, int width, NativeArray color, int idxSrc, int byteDepth, int hasAlphaChannel, NativeArray 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 srcAlphaChannel = alphaChannel.Reinterpret(1); + { + var pDest = pDestStart; + while (pDest < pDestEnd) + { + var c = color[pDest]; + c.a = (byteDepth < 4) ? alphaChannel[idxSrc] : RGBByteFromHDRFloat(srcAlphaChannel[idxSrc / 4]); - return maskContext.AlphaBuffer; + color[pDest] = c; + pDest++; + idxSrc += byteDepth; + } + } + } } - private static void DecodeMaskAlphaRow32(byte[] pAlpha, int pAlphaStart, int pAlphaEnd, byte[] pMask, int pMaskStart) + private static void DecodeMaskAlphaRow32(NativeArray pAlpha, int pAlphaStart, int pAlphaEnd, NativeArray pMask, int pMaskStart) { + + NativeArray floatArray = pMask.Reinterpret(1); + while (pAlphaStart < pAlphaEnd) { - pAlpha[pAlphaStart] = RGBByteFromHDRFloat(BitConverter.ToSingle(pMask, pMaskStart)); + pAlpha[pAlphaStart] = RGBByteFromHDRFloat(floatArray[pMaskStart * 4]); pAlphaStart++; pMaskStart += 4; } } - private static void DecodeMaskAlphaRow(byte[] pAlpha, int pAlphaStart, int pAlphaEnd, byte[] pMask, int pMaskStart, int byteDepth) + private static void DecodeMaskAlphaRow(NativeArray pAlpha, int pAlphaStart, int pAlphaEnd, NativeArray pMask, int pMaskStart, int byteDepth) { while (pAlphaStart < pAlphaEnd) { @@ -333,19 +503,18 @@ private static void DecodeMaskAlphaRow(byte[] pAlpha, int pAlphaStart, int pAlph pMaskStart += byteDepth; } } - - /////////////////////////////////////////////////////////////////////////// - - private static void ApplyPDNMask(int pDestStart, int pDestEnd, int width, NativeArray color, byte[] layerMaskAlpha, byte[] userMaskAlpha) + + private static void ApplyPDNMask(int pDestStart, int pDestEnd, int width, NativeArray color, NativeArray layerMaskAlpha, NativeArray userMaskAlpha) { // Do nothing if there are no masks - if ((layerMaskAlpha == null) && (userMaskAlpha == null)) + if ((layerMaskAlpha.Length <= 1) && (userMaskAlpha.Length <= 1)) + { return; - + } // Apply one mask - else if ((layerMaskAlpha == null) || (userMaskAlpha == null)) + else if ((layerMaskAlpha.Length <= 1) || (userMaskAlpha.Length <= 1)) { - var maskAlpha = layerMaskAlpha ?? userMaskAlpha; + var maskAlpha = (layerMaskAlpha.Length <= 1) ? userMaskAlpha : layerMaskAlpha; var maskStart = 0; { while (pDestStart < pDestEnd) @@ -385,16 +554,17 @@ private static void ApplyPDNMask(int pDestStart, int pDestEnd, int width, Native private static void SetPDNRowRgb32(int pDestStart, int pDestEnd, int width, NativeArray color, int idxSrc, DecodeContext context) { - var pSrcRedChannel = context.Channels[0].ImageData; - var pSrcGreenChannel = context.Channels[1].ImageData; - var pSrcBlueChannel = context.Channels[2].ImageData; + NativeArray redChannel = context.Channels[0].ImageData.Reinterpret(1); + NativeArray greenChannel = context.Channels[1].ImageData.Reinterpret(1); + NativeArray blueChannel = context.Channels[2].ImageData.Reinterpret(1); + { while (pDestStart < pDestEnd) { var c = color[pDestStart]; - c.r = RGBByteFromHDRFloat(BitConverter.ToSingle(pSrcRedChannel, idxSrc)); - c.g = RGBByteFromHDRFloat(BitConverter.ToSingle(pSrcGreenChannel, idxSrc)); - c.b = RGBByteFromHDRFloat(BitConverter.ToSingle(pSrcBlueChannel, idxSrc)); + c.r = RGBByteFromHDRFloat(redChannel[idxSrc / 4]); + c.g = RGBByteFromHDRFloat(greenChannel[idxSrc / 4]); + c.b = RGBByteFromHDRFloat(blueChannel[idxSrc / 4]); color[pDestStart] = c; pDestStart++; idxSrc += 4; @@ -404,11 +574,11 @@ private static void SetPDNRowRgb32(int pDestStart, int pDestEnd, int width, Nati private static void SetPDNRowGrayscale32(int pDestStart, int pDestEnd, int width, NativeArray color, int idxSrc, DecodeContext context) { - var channelPtr = context.Channels[0].ImageData; + NativeArray channel = context.Channels[0].ImageData.Reinterpret(1); { while (pDestStart < pDestEnd) { - byte rgbValue = RGBByteFromHDRFloat(BitConverter.ToSingle(channelPtr, idxSrc)); + byte rgbValue = RGBByteFromHDRFloat(channel[idxSrc / 4]); var c = color[pDestStart]; c.r = rgbValue; c.g = rgbValue; @@ -524,9 +694,9 @@ private static void SetPDNRowIndexed(int pDestStart, int pDestEnd, int width, Na { int index = (int)context.Channels[0].ImageData[idxSrc]; var c = color[pDestStart]; - c.r = (byte)context.Layer.PsdFile.ColorModeData[index]; - c.g = context.Layer.PsdFile.ColorModeData[index + 256]; - c.b = context.Layer.PsdFile.ColorModeData[index + 2 * 256]; + c.r = (byte)context.ColorModeData[index]; + c.g = context.ColorModeData[index + 256]; + c.b = context.ColorModeData[index + 2 * 256]; color[pDestStart] = c; pDestStart++; @@ -633,14 +803,5 @@ private static void SetPDNRowLab(int pDestStart, int pDestEnd, int width, Native } #endregion - - /////////////////////////////////////////////////////////////////////////////// - - private static double rgbExponent = 1 / 2.19921875; - private static byte RGBByteFromHDRFloat(float ptr) - { - var result = (byte)(255 * Math.Pow(ptr, rgbExponent)); - return result; - } } } diff --git a/Editor/PSDPlugin/PhotoShopFileType/PsdLoad.cs b/Editor/PSDPlugin/PhotoShopFileType/PsdLoad.cs index c66f387..7efbef9 100644 --- a/Editor/PSDPlugin/PhotoShopFileType/PsdLoad.cs +++ b/Editor/PSDPlugin/PhotoShopFileType/PsdLoad.cs @@ -20,6 +20,8 @@ using PhotoshopFile; using UnityEngine; using PDNWrapper; +using Unity.Collections; +using Unity.Jobs; namespace PaintDotNet.Data.PhotoshopFileType { @@ -30,7 +32,7 @@ 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 @@ -77,6 +79,7 @@ public static Document Load(System.IO.Stream input) } */ BitmapLayer parent = null; + JobHandle jobHandle = default(JobHandle); foreach (var l in Enumerable.Reverse(psdFile.Layers)) { if (l.IsEndGroupMarker) @@ -84,7 +87,8 @@ public static Document Load(System.IO.Stream input) parent = parent != null ? parent.ParentLayer : null; continue; } - var b = l.DecodeToPdnLayer(); + BitmapLayer b = null; + jobHandle = l.DecodeToPdnLayer(jobHandle, out b); b.ParentLayer = parent; if (parent != null) parent.ChildLayer.Add(b); @@ -94,28 +98,26 @@ public static Document Load(System.IO.Stream input) if (b.IsGroup) parent = b; } + jobHandle.Complete(); } SetPdnResolutionInfo(psdFile, document); - + psdFile.Cleanup(); return document; } - internal static BitmapLayer DecodeToPdnLayer( - this PhotoshopFile.Layer psdLayer) + internal static JobHandle DecodeToPdnLayer(this PhotoshopFile.Layer psdLayer, JobHandle inputDeps, out BitmapLayer pdnLayer) { var psdFile = psdLayer.PsdFile; psdLayer.CreateMissingChannels(); - var pdnLayer = new BitmapLayer(psdFile.ColumnCount, psdFile.RowCount); + 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); - ImageDecoderPdn.DecodeImage(pdnLayer, psdLayer); - - return pdnLayer; + return ImageDecoderPdn.DecodeImage(pdnLayer, psdLayer, inputDeps); } /// @@ -166,7 +168,7 @@ private static void CreateLayersFromChannels(PsdFile psdFile) // Copy channel image data into the new grayscale layer var layerChannel = new Channel(0, layer); layerChannel.ImageCompression = channel.ImageCompression; - layerChannel.ImageData = channel.ImageData; + layerChannel.ImageData = new NativeArray(channel.ImageData, Allocator.Persistent); layer.Channels.Add(layerChannel); psdFile.Layers.Add(layer); diff --git a/Editor/PSDPlugin/PsdFile/Compression/EndianReverser.cs b/Editor/PSDPlugin/PsdFile/Compression/EndianReverser.cs index fcf8cb0..38808eb 100644 --- a/Editor/PSDPlugin/PsdFile/Compression/EndianReverser.cs +++ b/Editor/PSDPlugin/PsdFile/Compression/EndianReverser.cs @@ -46,17 +46,5 @@ public override byte[] ReadCompressed() { return imageData.ReadCompressed(); } - - internal override void WriteInternal(byte[] array) - { - // Reverse endianness before passing on to underlying compressor - if (array.Length > 0) - { - var numPixels = array.Length / BytesPerRow * Size.Width; - Util.SwapByteArray(BitDepth, array, 0, numPixels); - } - - imageData.Write(array); - } } } diff --git a/Editor/PSDPlugin/PsdFile/Compression/ImageData.cs b/Editor/PSDPlugin/PsdFile/Compression/ImageData.cs index a9a7753..7b3b009 100644 --- a/Editor/PSDPlugin/PsdFile/Compression/ImageData.cs +++ b/Editor/PSDPlugin/PsdFile/Compression/ImageData.cs @@ -53,28 +53,5 @@ public virtual byte[] Read() /// Reads compressed image data. /// public abstract byte[] ReadCompressed(); - - /// - /// Writes rows of image data into compressed format. - /// - /// An array containing the data to be compressed. - public void Write(byte[] array) - { - var imageLength = (long)BytesPerRow * Size.Height; - if (array.Length != imageLength) - { - throw new ArgumentException( - "Array length is not equal to image length array."); - } - - if (AltersWrittenData) - { - array = (byte[])array.Clone(); - } - - WriteInternal(array); - } - - internal abstract void WriteInternal(byte[] array); } } diff --git a/Editor/PSDPlugin/PsdFile/Compression/RawImage.cs b/Editor/PSDPlugin/PsdFile/Compression/RawImage.cs index 24cc5be..4c4ddb3 100644 --- a/Editor/PSDPlugin/PsdFile/Compression/RawImage.cs +++ b/Editor/PSDPlugin/PsdFile/Compression/RawImage.cs @@ -40,10 +40,5 @@ public override byte[] ReadCompressed() { return data; } - - internal override void WriteInternal(byte[] array) - { - data = array; - } } } diff --git a/Editor/PSDPlugin/PsdFile/Compression/RleImage.cs b/Editor/PSDPlugin/PsdFile/Compression/RleImage.cs index d49ce75..35ed8f7 100644 --- a/Editor/PSDPlugin/PsdFile/Compression/RleImage.cs +++ b/Editor/PSDPlugin/PsdFile/Compression/RleImage.cs @@ -57,31 +57,5 @@ public override byte[] ReadCompressed() { return rleData; } - - internal override void WriteInternal(byte[] array) - { - if (rleData != null) - { - throw new Exception( - "Cannot write to RLE image in Decompress mode."); - } - - using (var dataStream = new MemoryStream()) - { - var rleWriter = new RleWriter(dataStream); - for (int row = 0; row < Size.Height; row++) - { - int rowIndex = row * BytesPerRow; - rleRowLengths[row] = rleWriter.Write( - array, rowIndex, BytesPerRow); - } - - // Save compressed data - dataStream.Flush(); - rleData = dataStream.ToArray(); - Debug.Assert(rleRowLengths.Total == rleData.Length, - "RLE row lengths do not sum to the compressed data length."); - } - } } } diff --git a/Editor/PSDPlugin/PsdFile/Compression/ZipImage.cs b/Editor/PSDPlugin/PsdFile/Compression/ZipImage.cs index 0de13a5..1a526f6 100644 --- a/Editor/PSDPlugin/PsdFile/Compression/ZipImage.cs +++ b/Editor/PSDPlugin/PsdFile/Compression/ZipImage.cs @@ -97,10 +97,5 @@ public override byte[] ReadCompressed() : zipDataStream.ToArray(); return result; } - - internal override void WriteInternal(byte[] array) - { - zipStream.Write(array, 0, array.Length); - } } } diff --git a/Editor/PSDPlugin/PsdFile/Compression/ZipPredict16Image.cs b/Editor/PSDPlugin/PsdFile/Compression/ZipPredict16Image.cs index 1a4808c..591a3fd 100644 --- a/Editor/PSDPlugin/PsdFile/Compression/ZipPredict16Image.cs +++ b/Editor/PSDPlugin/PsdFile/Compression/ZipPredict16Image.cs @@ -56,23 +56,6 @@ public override byte[] ReadCompressed() return zipImage.ReadCompressed(); } - internal override void WriteInternal(byte[] array) - { - if (array.Length == 0) - { - return; - } - - - { - { - Predict(array); - } - } - - zipImage.WriteInternal(array); - } - private void Predict(/*UInt16**/ byte[] ptrData) { int size = sizeof(UInt16); diff --git a/Editor/PSDPlugin/PsdFile/Compression/ZipPredict32Image.cs b/Editor/PSDPlugin/PsdFile/Compression/ZipPredict32Image.cs index 08b478f..f9f142f 100644 --- a/Editor/PSDPlugin/PsdFile/Compression/ZipPredict32Image.cs +++ b/Editor/PSDPlugin/PsdFile/Compression/ZipPredict32Image.cs @@ -58,26 +58,6 @@ public override byte[] ReadCompressed() return zipImage.ReadCompressed(); } - internal override void WriteInternal(byte[] array) - { - if (array.Length == 0) - { - return; - } - - var predictedData = new byte[array.Length]; - - { - //fixed (byte* ptrData = &array[0]) - //fixed (byte* ptrOutput = &predictedData[0]) - { - Predict(array, predictedData); - } - } - - zipImage.WriteInternal(predictedData); - } - private void Predict(byte[] ptrData, byte[] ptrOutput /*Int32* ptrData, byte* ptrOutput*/) { int size = sizeof(Int32); @@ -175,6 +155,7 @@ private void Unpredict(byte[] ptrData, byte[] ptrOutput /*byte* ptrData, Int32* ptrOutput[outputIndex] = rr[k]; outputIndex++; } + startIndex++; //*ptrOutput = *(ptrData) << 24 // | *(ptrData + offset1) << 16 // | *(ptrData + offset2) << 8 diff --git a/Editor/PSDPlugin/PsdFile/ImageResource.cs b/Editor/PSDPlugin/PsdFile/ImageResource.cs index 8bf8ee5..bc57e35 100644 --- a/Editor/PSDPlugin/PsdFile/ImageResource.cs +++ b/Editor/PSDPlugin/PsdFile/ImageResource.cs @@ -134,38 +134,6 @@ protected ImageResource(string name) Signature = "8BIM"; Name = name; } - - /// - /// Write out the image resource block: header and data. - /// - // public void Save(PsdBinaryWriter writer) - // { - // Util.DebugMessage(writer.BaseStream, "Save, Begin, ImageResource"); - - // writer.WriteAsciiChars(Signature); - // writer.Write((UInt16)ID); - // writer.WritePascalString(Name, 2); - - // // Length is unpadded, but data is even-padded - // var startPosition = writer.BaseStream.Position; - // using (new PsdBlockLengthWriter(writer)) - // { - // WriteData(writer); - // } - // writer.WritePadding(startPosition, 2); - - // Util.DebugMessage(writer.BaseStream, "Save, End, ImageResource, {0}", ID); - // } - - // /// - // /// Write the data for this image resource. - // /// - // protected abstract void WriteData(PsdBinaryWriter writer); - - // public override string ToString() - // { - // return String.Format(CultureInfo.InvariantCulture, "{0} {1}", ID, Name); - // } } /// diff --git a/Editor/PSDPlugin/PsdFile/ImageResources/AlphaChannelNames.cs b/Editor/PSDPlugin/PsdFile/ImageResources/AlphaChannelNames.cs index d039f22..d73a1e5 100644 --- a/Editor/PSDPlugin/PsdFile/ImageResources/AlphaChannelNames.cs +++ b/Editor/PSDPlugin/PsdFile/ImageResources/AlphaChannelNames.cs @@ -48,13 +48,5 @@ public AlphaChannelNames(PsdBinaryReader reader, string name, int resourceDataLe ChannelNames.Add(channelName); } } - - //protected override void WriteData(PsdBinaryWriter writer) - //{ - // foreach (var channelName in ChannelNames) - // { - // writer.WritePascalString(channelName, 1); - // } - //} } } diff --git a/Editor/PSDPlugin/PsdFile/ImageResources/RawImageResource.cs b/Editor/PSDPlugin/PsdFile/ImageResources/RawImageResource.cs index 7826a8a..efe54e5 100644 --- a/Editor/PSDPlugin/PsdFile/ImageResources/RawImageResource.cs +++ b/Editor/PSDPlugin/PsdFile/ImageResources/RawImageResource.cs @@ -44,10 +44,5 @@ public RawImageResource(PsdBinaryReader reader, string signature, this.id = resourceId; Data = reader.ReadBytes(numBytes); } - - //protected override void WriteData(PsdBinaryWriter writer) - //{ - // writer.Write(Data); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/ImageResources/ResolutionInfo.cs b/Editor/PSDPlugin/PsdFile/ImageResources/ResolutionInfo.cs index 916ec6f..ce86574 100644 --- a/Editor/PSDPlugin/PsdFile/ImageResources/ResolutionInfo.cs +++ b/Editor/PSDPlugin/PsdFile/ImageResources/ResolutionInfo.cs @@ -90,18 +90,5 @@ public ResolutionInfo(PsdBinaryReader reader, string name) this.VResDisplayUnit = (ResUnit)reader.ReadInt16(); this.HeightDisplayUnit = (Unit)reader.ReadInt16(); } - - //protected override void WriteData(PsdBinaryWriter writer) - //{ - // writer.Write(HDpi.Integer); - // writer.Write(HDpi.Fraction); - // writer.Write((Int16)HResDisplayUnit); - // writer.Write((Int16)WidthDisplayUnit); - - // writer.Write(VDpi.Integer); - // writer.Write(VDpi.Fraction); - // writer.Write((Int16)VResDisplayUnit); - // writer.Write((Int16)HeightDisplayUnit); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/ImageResources/UnicodeAlphaNames.cs b/Editor/PSDPlugin/PsdFile/ImageResources/UnicodeAlphaNames.cs index 61a31ca..aadcd92 100644 --- a/Editor/PSDPlugin/PsdFile/ImageResources/UnicodeAlphaNames.cs +++ b/Editor/PSDPlugin/PsdFile/ImageResources/UnicodeAlphaNames.cs @@ -55,15 +55,5 @@ public UnicodeAlphaNames(PsdBinaryReader reader, string name, int resourceDataLe ChannelNames.Add(channelName); } } - - //protected override void WriteData(PsdBinaryWriter writer) - //{ - // foreach (var channelName in ChannelNames) - // { - // // We must add a null terminator because Photoshop always strips the - // // last character of a Unicode alpha name, even if it is not null. - // writer.WriteUnicodeString(channelName + "\0"); - // } - //} } } diff --git a/Editor/PSDPlugin/PsdFile/ImageResources/VersionInfo.cs b/Editor/PSDPlugin/PsdFile/ImageResources/VersionInfo.cs index ad49a46..3e1e787 100644 --- a/Editor/PSDPlugin/PsdFile/ImageResources/VersionInfo.cs +++ b/Editor/PSDPlugin/PsdFile/ImageResources/VersionInfo.cs @@ -49,14 +49,5 @@ public VersionInfo(PsdBinaryReader reader, string name) WriterName = reader.ReadUnicodeString(); FileVersion = reader.ReadUInt32(); } - - //protected override void WriteData(PsdBinaryWriter writer) - //{ - // writer.Write(Version); - // writer.Write(HasRealMergedData); - // writer.WriteUnicodeString(ReaderName); - // writer.WriteUnicodeString(WriterName); - // writer.Write(FileVersion); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/Layers/BlendingRanges.cs b/Editor/PSDPlugin/PsdFile/Layers/BlendingRanges.cs index 8a77772..c62dc86 100644 --- a/Editor/PSDPlugin/PsdFile/Layers/BlendingRanges.cs +++ b/Editor/PSDPlugin/PsdFile/Layers/BlendingRanges.cs @@ -52,23 +52,5 @@ public BlendingRanges(PsdBinaryReader reader, Layer layer) Util.DebugMessage(reader.BaseStream, "Load, End, BlendingRanges"); } - - /////////////////////////////////////////////////////////////////////////// - - //public void Save(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, BlendingRanges"); - - // if (Data == null) - // { - // writer.Write((UInt32)0); - // return; - // } - - // writer.Write((UInt32)Data.Length); - // writer.Write(Data); - - // Util.DebugMessage(writer.BaseStream, "Save, End, BlendingRanges"); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/Layers/Channel.cs b/Editor/PSDPlugin/PsdFile/Layers/Channel.cs index 3f67a8e..0e49d48 100644 --- a/Editor/PSDPlugin/PsdFile/Layers/Channel.cs +++ b/Editor/PSDPlugin/PsdFile/Layers/Channel.cs @@ -19,7 +19,7 @@ using System.Diagnostics; using PDNWrapper; using System.Linq; - +using Unity.Collections; using PhotoshopFile.Compression; namespace PhotoshopFile @@ -124,7 +124,7 @@ public Rectangle Rect /// When making changes to the ImageData, set ImageDataRaw to null so that /// the correct data will be compressed during save. /// - public byte[] ImageData { get; set; } + public NativeArray ImageData { get; set; } /// /// Image compression method used. @@ -156,24 +156,12 @@ internal Channel(PsdBinaryReader reader, Layer layer) Util.DebugMessage(reader.BaseStream, "Load, End, Channel, {0}", ID); } - - //internal void Save(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, Channel"); - - // writer.Write(ID); - // if (Layer.PsdFile.IsLargeDocument) - // { - // writer.Write(Length); - // } - // else - // { - // writer.Write((Int32)Length); - // } - - // Util.DebugMessage(writer.BaseStream, "Save, End, Channel, {0}", ID); - //} - + + internal void Cleanup() + { + if (ImageData.IsCreated) + ImageData.Dispose(); + } ////////////////////////////////////////////////////////////////// internal void LoadPixelData(PsdBinaryReader reader) @@ -200,8 +188,7 @@ internal void LoadPixelData(PsdBinaryReader reader) break; case ImageCompression.Rle: // RLE row lengths - RleRowLengths = new RleRowLengths(reader, Rect.Height, - Layer.PsdFile.IsLargeDocument); + 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."); @@ -217,10 +204,8 @@ internal void LoadPixelData(PsdBinaryReader reader) 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."); + 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."); } /// @@ -229,67 +214,19 @@ internal void LoadPixelData(PsdBinaryReader reader) /// public void DecodeImageData() { - if ((ImageCompression == ImageCompression.Raw) - && (Layer.PsdFile.BitDepth <= 8)) + if ((ImageCompression == ImageCompression.Raw) && (Layer.PsdFile.BitDepth <= 8)) { - ImageData = ImageDataRaw; + ImageData = new NativeArray(ImageDataRaw, Allocator.TempJob); return; } var image = ImageDataFactory.Create(this, ImageDataRaw); var longLength = (long)image.BytesPerRow * Rect.Height; Util.CheckByteArrayLength(longLength); - ImageData = new byte[longLength]; - image.Read(ImageData); - } - - /// - /// Compresses the image data. - /// - public void CompressImageData() - { - // Do not recompress if compressed data is already present. - if (ImageDataRaw != null) - return; - - if (ImageData == null) - return; - - if (ImageCompression == ImageCompression.Rle) - { - RleRowLengths = new RleRowLengths(Rect.Height); - } - - var compressor = ImageDataFactory.Create(this, null); - compressor.Write(ImageData); - ImageDataRaw = compressor.ReadCompressed(); - - Length = 2 + ImageDataRaw.Length; - if (ImageCompression == ImageCompression.Rle) - { - var rowLengthSize = Layer.PsdFile.IsLargeDocument ? 4 : 2; - Length += rowLengthSize * Rect.Height; - } + var LocalImageData = new byte[longLength]; + image.Read(LocalImageData); + ImageData = new NativeArray(LocalImageData, Allocator.TempJob); + ImageDataRaw = null; // no longer needed. } - - //internal void SavePixelData(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, Channel image"); - - // writer.Write((short)ImageCompression); - // if (ImageDataRaw == null) - // { - // return; - // } - - // if (ImageCompression == PhotoshopFile.ImageCompression.Rle) - // { - // RleRowLengths.Write(writer, Layer.PsdFile.IsLargeDocument); - // } - // writer.Write(ImageDataRaw); - - // Util.DebugMessage(writer.BaseStream, "Save, End, Channel image, {0}", - // ID, Layer.Name); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/Layers/Layer.cs b/Editor/PSDPlugin/PsdFile/Layers/Layer.cs index c8a6bee..59a6e42 100644 --- a/Editor/PSDPlugin/PsdFile/Layers/Layer.cs +++ b/Editor/PSDPlugin/PsdFile/Layers/Layer.cs @@ -23,6 +23,8 @@ using System.IO; using System.Linq; using System.Threading; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; namespace PhotoshopFile { @@ -222,94 +224,17 @@ public void CreateMissingChannels() var size = this.Rect.Height * this.Rect.Width; var ch = new Channel(id, this); - ch.ImageData = new byte[size]; - + ch.ImageData = new NativeArray(size, Allocator.TempJob); + unsafe { - //fixed (byte* ptr = &ch.ImageData[0]) - { - Util.Fill(ch.ImageData, 0, size, (byte)255); - } + UnsafeUtility.MemSet(ch.ImageData.GetUnsafePtr(), (byte)255, size); } - this.Channels.Add(ch); } } } /////////////////////////////////////////////////////////////////////////// - - public void PrepareSave() - { - foreach (var ch in Channels) - { - ch.CompressImageData(); - } - - // Create or update the Unicode layer name to be consistent with the - // ANSI layer name. - var layerUnicodeNames = AdditionalInfo.Where(x => x is LayerUnicodeName); - if (layerUnicodeNames.Count() > 1) - { - throw new PsdInvalidException( - "Layer can only have one LayerUnicodeName."); - } - - var layerUnicodeName = (LayerUnicodeName)layerUnicodeNames.FirstOrDefault(); - if (layerUnicodeName == null) - { - layerUnicodeName = new LayerUnicodeName(Name); - AdditionalInfo.Add(layerUnicodeName); - } - else if (layerUnicodeName.Name != Name) - { - layerUnicodeName.Name = Name; - } - } - - //public void Save(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, Layer"); - - // writer.Write(Rect); - - // //----------------------------------------------------------------------- - - // writer.Write((short)Channels.Count); - // foreach (var ch in Channels) - // ch.Save(writer); - - // //----------------------------------------------------------------------- - - // writer.WriteAsciiChars("8BIM"); - // writer.WriteAsciiChars(BlendModeKey); - // writer.Write(Opacity); - // writer.Write(Clipping); - - // writer.Write((byte)flags.Data); - // writer.Write((byte)0); - - // //----------------------------------------------------------------------- - - // using (new PsdBlockLengthWriter(writer)) - // { - // Masks.Save(writer); - // BlendingRangesData.Save(writer); - - // var namePosition = writer.BaseStream.Position; - - // // Legacy layer name is limited to 31 bytes. Unicode layer name - // // can be much longer. - // writer.WritePascalString(Name, 4, 31); - - // foreach (LayerInfo info in AdditionalInfo) - // { - // info.Save(writer, - // globalLayerInfo: false, - // isLargeDocument: PsdFile.IsLargeDocument); - // } - // } - - // Util.DebugMessage(writer.BaseStream, "Save, End, Layer, {0}", Name); - //} + } } diff --git a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo.cs b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo.cs index 28380e8..f1af051 100644 --- a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo.cs +++ b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo.cs @@ -164,37 +164,5 @@ internal abstract class LayerInfo public abstract string Signature { get; } public abstract string Key { get; } - - //protected abstract void WriteData(PsdBinaryWriter writer); - - //public void Save(PsdBinaryWriter writer, bool globalLayerInfo, - // bool isLargeDocument) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, LayerInfo, {0}, {1}", - // Signature, Key); - - // writer.WriteAsciiChars(Signature); - // writer.WriteAsciiChars(Key); - - // var startPosition = writer.BaseStream.Position; - // using (var lengthWriter = new PsdBlockLengthWriter(writer, - // LayerInfoUtil.HasLongLength(Key, isLargeDocument))) - // { - // // Depending on the key, the length may be unpadded, 2-padded, or - // // 4-padded. Thus, it is up to each implementation of WriteData to - // // pad the length correctly. - // WriteData(writer); - // } - - // // Data for global layer info is always padded to a multiple of 4, - // // even if this causes the stated length to be incorrect. - // if (globalLayerInfo) - // { - // writer.WritePadding(startPosition, 4); - // } - - // Util.DebugMessage(writer.BaseStream, "Save, End, LayerInfo, {0}, {1}", - // Signature, Key); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/InfoLayers.cs b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/InfoLayers.cs index 2176793..43c3719 100644 --- a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/InfoLayers.cs +++ b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/InfoLayers.cs @@ -83,10 +83,5 @@ public InfoLayers(PsdBinaryReader reader, PsdFile psdFile, "Incorrect length for InfoLayers."); } } - - //protected override void WriteData(PsdBinaryWriter writer) - //{ - // PsdFile.SaveLayersData(writer); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerId.cs b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerId.cs index 1eb0ca2..5bb0e67 100644 --- a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerId.cs +++ b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerId.cs @@ -67,10 +67,5 @@ public LayerId(PsdBinaryReader reader, else throw new PsdInvalidException("LayerId data length should be 4"); } - - //protected override void WriteData(PsdBinaryWriter writer) - //{ - // PsdFile.SaveLayersData(writer); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerSectionInfo.cs b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerSectionInfo.cs index de8af51..425d53e 100644 --- a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerSectionInfo.cs +++ b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerSectionInfo.cs @@ -90,17 +90,5 @@ public LayerSectionInfo(PsdBinaryReader reader, string key, int dataLength) } } } - - //protected override void WriteData(PsdBinaryWriter writer) - //{ - // writer.Write((Int32)SectionType); - // if (BlendModeKey != null) - // { - // writer.WriteAsciiChars("8BIM"); - // writer.WriteAsciiChars(BlendModeKey); - // if (subtype != null) - // writer.Write((Int32)Subtype); - // } - //} } } diff --git a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerUnicodeName.cs b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerUnicodeName.cs index 58febf3..9de1c63 100644 --- a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerUnicodeName.cs +++ b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/LayerUnicodeName.cs @@ -38,13 +38,5 @@ public LayerUnicodeName(PsdBinaryReader reader) { Name = reader.ReadUnicodeString(); } - - //protected override void WriteData(PsdBinaryWriter writer) - //{ - // var startPosition = writer.BaseStream.Position; - - // writer.WriteUnicodeString(Name); - // writer.WritePadding(startPosition, 4); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/RawLayerInfo.cs b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/RawLayerInfo.cs index 77ba7ce..06f7a2a 100644 --- a/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/RawLayerInfo.cs +++ b/Editor/PSDPlugin/PsdFile/Layers/LayerInfo/RawLayerInfo.cs @@ -48,10 +48,5 @@ public RawLayerInfo(PsdBinaryReader reader, string signature, string key, Util.CheckByteArrayLength(dataLength); Data = reader.ReadBytes((int)dataLength); } - - //protected override void WriteData(PsdBinaryWriter writer) - //{ - // writer.Write(Data); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/Layers/Mask.cs b/Editor/PSDPlugin/PsdFile/Layers/Mask.cs index 14eff5b..b99a924 100644 --- a/Editor/PSDPlugin/PsdFile/Layers/Mask.cs +++ b/Editor/PSDPlugin/PsdFile/Layers/Mask.cs @@ -19,6 +19,7 @@ using System.Diagnostics; using PDNWrapper; using System.Globalization; +using Unity.Collections; namespace PhotoshopFile { @@ -80,7 +81,7 @@ public bool InvertOnBlend /// /// Mask image data. /// - public byte[] ImageData { get; set; } + public NativeArray ImageData { get; set; } public Mask(Layer layer) { @@ -136,8 +137,7 @@ public MaskInfo(PsdBinaryReader reader, Layer layer) var userFlagsByte = reader.ReadByte(); var userBackgroundColor = reader.ReadByte(); var userRectangle = reader.ReadRectangle(); - UserMask = new Mask(layer, userRectangle, userBackgroundColor, - new BitVector32(userFlagsByte)); + UserMask = new Mask(layer, userRectangle, userBackgroundColor, new BitVector32(userFlagsByte)); } // 20-byte mask data will end with padding. @@ -145,39 +145,5 @@ public MaskInfo(PsdBinaryReader reader, Layer layer) Util.DebugMessage(reader.BaseStream, "Load, End, MaskInfo"); } - - /////////////////////////////////////////////////////////////////////////// - - //public void Save(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, MaskInfo"); - - // if (LayerMask == null) - // { - // writer.Write((UInt32)0); - // return; - // } - - // using (new PsdBlockLengthWriter(writer)) - // { - // writer.Write(LayerMask.Rect); - // writer.Write(LayerMask.BackgroundColor); - // writer.Write((byte)LayerMask.Flags.Data); - - // if (UserMask == null) - // { - // // Pad by 2 bytes to make the block length 20 - // writer.Write((UInt16)0); - // } - // else - // { - // writer.Write((byte)UserMask.Flags.Data); - // writer.Write(UserMask.BackgroundColor); - // writer.Write(UserMask.Rect); - // } - // } - - // Util.DebugMessage(writer.BaseStream, "Save, End, MaskInfo"); - //} } } diff --git a/Editor/PSDPlugin/PsdFile/PsdFile.cs b/Editor/PSDPlugin/PsdFile/PsdFile.cs index 66e27a3..9e46e43 100644 --- a/Editor/PSDPlugin/PsdFile/PsdFile.cs +++ b/Editor/PSDPlugin/PsdFile/PsdFile.cs @@ -111,28 +111,6 @@ private void Load(Stream stream, LoadContext loadContext, ELoadFlag loadFlag) } } - public void Save(string fileName, Encoding encoding) - { - using (var stream = new FileStream(fileName, FileMode.Create)) - { - Save(stream, encoding); - } - } - - public void Save(Stream stream, Encoding encoding) - { - PrepareSave(); - - //using (var writer = new PsdBinaryWriter(stream, encoding)) - //{ - // SaveHeader(writer); - // SaveColorModeData(writer); - // SaveImageResources(writer); - // SaveLayerAndMaskInfo(writer); - // SaveImage(writer); - //} - } - #endregion #region Header @@ -263,25 +241,6 @@ private void LoadHeader(PsdBinaryReader reader) Util.DebugMessage(reader.BaseStream, "Load, End, File header"); } - /////////////////////////////////////////////////////////////////////////// - - //private void SaveHeader(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, File header"); - - // string signature = "8BPS"; - // writer.WriteAsciiChars(signature); - // writer.Write((Int16)Version); - // writer.Write(new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, }); - // writer.Write(ChannelCount); - // writer.Write(RowCount); - // writer.Write(ColumnCount); - // writer.Write((Int16)BitDepth); - // writer.Write((Int16)ColorMode); - - // Util.DebugMessage(writer.BaseStream, "Save, End, File header"); - //} - #endregion /////////////////////////////////////////////////////////////////////////// @@ -310,16 +269,6 @@ private void LoadColorModeData(PsdBinaryReader reader) Util.DebugMessage(reader.BaseStream, "Load, End, ColorModeData"); } - //private void SaveColorModeData(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, ColorModeData"); - - // writer.Write((UInt32)ColorModeData.Length); - // writer.Write(ColorModeData); - - // Util.DebugMessage(writer.BaseStream, "Save, End, ColorModeData"); - //} - #endregion /////////////////////////////////////////////////////////////////////////// @@ -371,21 +320,6 @@ private void LoadImageResources(PsdBinaryReader reader) reader.BaseStream.Position = startPosition + imageResourcesLength; } - /////////////////////////////////////////////////////////////////////////// - - //private void SaveImageResources(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, ImageResources"); - - // using (new PsdBlockLengthWriter(writer)) - // { - // foreach (var imgRes in ImageResources) - // imgRes.Save(writer); - // } - - // Util.DebugMessage(writer.BaseStream, "Save, End, ImageResources"); - //} - #endregion /////////////////////////////////////////////////////////////////////////// @@ -446,32 +380,6 @@ private void LoadLayerAndMaskInfo(PsdBinaryReader reader) /////////////////////////////////////////////////////////////////////////// - //private void SaveLayerAndMaskInfo(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, Layer and mask info"); - - // using (new PsdBlockLengthWriter(writer, IsLargeDocument)) - // { - // var startPosition = writer.BaseStream.Position; - - // SaveLayers(writer); - // SaveGlobalLayerMask(writer); - - // foreach (var info in AdditionalInfo) - // { - // info.Save(writer, - // globalLayerInfo: true, - // isLargeDocument: IsLargeDocument); - // } - - // writer.WritePadding(startPosition, 2); - // } - - // Util.DebugMessage(writer.BaseStream, "Save, End, Layer and mask info"); - //} - - /////////////////////////////////////////////////////////////////////////// - /// /// Load Layers Info section, including image data. /// @@ -556,6 +464,23 @@ internal void LoadLayers(PsdBinaryReader reader, bool hasHeader) /////////////////////////////////////////////////////////////////////////// + /// + /// Cleanup + /// + public void Cleanup() + { + var layersAndComposite = Layers.Concat(new[] { BaseLayer }); + + foreach (var lac in layersAndComposite) + { + foreach (var c in lac.Channels) + { + c.Cleanup(); + } + } + } + /////////////////////////////////////////////////////////////////////////// + /// /// Decompress the document image data and all the layers' image data, in parallel. /// @@ -587,26 +512,6 @@ private void DecompressImages() } } - /// - /// Check the validity of the PSD file and generate necessary data. - /// - public void PrepareSave() - { - CheckDimension(ColumnCount); - CheckDimension(RowCount); - VerifyInfoLayers(); - VerifyLayerSections(); - - var imageLayers = Layers.Concat(new List() { this.BaseLayer }).ToList(); - - foreach (var layer in imageLayers) - { - layer.PrepareSave(); - } - - SetVersionInfo(); - } - /// /// Verifies that any Additional Info layers are consistent. /// @@ -686,83 +591,7 @@ public void SetVersionInfo() versionInfo.FileVersion = 1; } } - - /// - /// Saves the Layers Info section, including headers and padding. - /// - /// The PSD writer. - //internal void SaveLayers(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, Layers Info section"); - - // using (new PsdBlockLengthWriter(writer, IsLargeDocument)) - // { - // var startPosition = writer.BaseStream.Position; - - // // Only one set of Layers can exist in the file. If layers will be - // // written to the Additional Info section, then the Layers section - // // must be empty to avoid conflict. - // var hasInfoLayers = AdditionalInfo.Exists(x => x is InfoLayers); - // if (!hasInfoLayers) - // { - // SaveLayersData(writer); - // } - - // // Documentation states that the Layers Info section is even-padded, - // // but it is actually padded to a multiple of 4. - // writer.WritePadding(startPosition, 4); - // } - - // Util.DebugMessage(writer.BaseStream, "Save, End, Layers Info section"); - //} - - ///// - ///// Saves the layer data, excluding headers and padding. - ///// - ///// The PSD writer. - //internal void SaveLayersData(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, Layers"); - - // var numLayers = (Int16)Layers.Count; - // if (AbsoluteAlpha) - // { - // numLayers = (Int16)(-numLayers); - // } - - // // Photoshop will not load files that have a layer count of 0 in the - // // compatible Layers section. Instead, the Layers section must be - // // entirely empty. - // if (numLayers == 0) - // { - // return; - // } - - // writer.Write(numLayers); - - // foreach (var layer in Layers) - // { - // layer.Save(writer); - // } - - // foreach (var layer in Layers) - // { - // Util.DebugMessage(writer.BaseStream, - // "Save, Begin, Layer image, layer.Name"); - // foreach (var channel in layer.Channels) - // { - // channel.SavePixelData(writer); - // } - // Util.DebugMessage(writer.BaseStream, - // "Save, End, Layer image, layer.Name"); - // } - - // // The caller is responsible for padding. Photoshop writes padded - // // lengths for compatible layers, but unpadded lengths for Additional - // // Info layers. - - // Util.DebugMessage(writer.BaseStream, "Save, End, Layers"); - //} + /////////////////////////////////////////////////////////////////////////// @@ -788,26 +617,6 @@ public byte[] globalLayerMaskData { get { return m_GlobalLayerMaskData; } } - /////////////////////////////////////////////////////////////////////////// - - //private void SaveGlobalLayerMask(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, GlobalLayerMask"); - - // if (AdditionalInfo.Exists(x => x.Key == "LMsk")) - // { - // writer.Write((UInt32)0); - // Util.DebugMessage(writer.BaseStream, "Save, End, GlobalLayerMask"); - // return; - // } - - // writer.Write((UInt32)GlobalLayerMaskData.Length); - // writer.Write(GlobalLayerMaskData); - - // Util.DebugMessage(writer.BaseStream, "Save, End, GlobalLayerMask"); - //} - - /////////////////////////////////////////////////////////////////////////// #endregion @@ -870,32 +679,6 @@ private void LoadImage(PsdBinaryReader reader) Util.DebugMessage(reader.BaseStream, "Load, End, Composite image"); } - /////////////////////////////////////////////////////////////////////////// - - //private void SaveImage(PsdBinaryWriter writer) - //{ - // Util.DebugMessage(writer.BaseStream, "Save, Begin, Composite image"); - - // writer.Write((short)this.ImageCompression); - // if (this.ImageCompression == PhotoshopFile.ImageCompression.Rle) - // { - // foreach (var channel in this.BaseLayer.Channels) - // { - // Util.DebugMessage(writer.BaseStream, "Save, Begin, RLE header"); - // channel.RleRowLengths.Write(writer, IsLargeDocument); - // Util.DebugMessage(writer.BaseStream, "Save, End, RLE header"); - // } - // } - // foreach (var channel in this.BaseLayer.Channels) - // { - // Util.DebugMessage(writer.BaseStream, "Save, Begin, Channel image data"); - // writer.Write(channel.ImageDataRaw); - // Util.DebugMessage(writer.BaseStream, "Save, End, Channel image data"); - // } - - // Util.DebugMessage(writer.BaseStream, "Save, End, Composite image"); - //} - #endregion } diff --git a/Editor/PSDPlugin/PsdFile/RleRowLengths.cs b/Editor/PSDPlugin/PsdFile/RleRowLengths.cs index 56a7918..4ef6c37 100644 --- a/Editor/PSDPlugin/PsdFile/RleRowLengths.cs +++ b/Editor/PSDPlugin/PsdFile/RleRowLengths.cs @@ -46,20 +46,5 @@ public RleRowLengths(PsdBinaryReader reader, int rowCount, bool isLargeDocument) : reader.ReadUInt16(); } } - - //public void Write(PsdBinaryWriter writer, bool isLargeDocument) - //{ - // for (int i = 0; i < Values.Length; i++) - // { - // if (isLargeDocument) - // { - // writer.Write(Values[i]); - // } - // else - // { - // writer.Write((UInt16)Values[i]); - // } - // } - //} } } diff --git a/Editor/PSDPlugin/PsdFile/Util.cs b/Editor/PSDPlugin/PsdFile/Util.cs index effb6be..c6fadc7 100644 --- a/Editor/PSDPlugin/PsdFile/Util.cs +++ b/Editor/PSDPlugin/PsdFile/Util.cs @@ -18,6 +18,7 @@ using System.IO; using System.Linq; using System.Text; +using Unity.Collections; namespace PhotoshopFile { @@ -62,6 +63,27 @@ static public void Invert(byte[] ptr, int ptrStart, int ptrEnd) } } + /// + /// Fills a buffer with a byte value. + /// + static public void Fill(NativeArray ptr, int start, int end, byte value) + { + while (start < end) + { + ptr[start] = value; + start++; + } + } + + static public void Invert(NativeArray ptr, int ptrStart, int ptrEnd) + { + while (ptrStart < ptrEnd) + { + ptr[ptrStart] = (byte)(ptr[ptrStart] ^ 0xff); + ptrStart++; + } + } + /////////////////////////////////////////////////////////////////////////// /// diff --git a/Editor/Tasks/ExtractLayerTask.cs b/Editor/Tasks/ExtractLayerTask.cs index 6e62292..24c0c0b 100644 --- a/Editor/Tasks/ExtractLayerTask.cs +++ b/Editor/Tasks/ExtractLayerTask.cs @@ -62,7 +62,7 @@ public static unsafe void Execute(List extractedLayer, List(el.texture.Length, Allocator.Temp); + el.texture = new NativeArray(el.texture.Length, Allocator.Persistent); extractedLayer.Add(el); job.output[jobIndex] = new IntPtr(el.texture.GetUnsafePtr()); job.width[jobIndex] = el.width; @@ -88,7 +88,7 @@ static int ExtractLayer(List extractedLayer, List layers, continue; if (l.IsGroup) { - extractedLayer.Add(new PSDLayer(new NativeArray(0, Allocator.Temp), parentGroupIndex, l.IsGroup, l.Name, 0, 0, l.LayerID)); + extractedLayer.Add(new PSDLayer(new NativeArray(0, Allocator.Persistent), parentGroupIndex, l.IsGroup, l.Name, 0, 0, l.LayerID)); actualLayerWithBuffer += ExtractLayer(extractedLayer, l.ChildLayer, importHiddenLayer); } else diff --git a/package.json b/package.json index 1f32279..3962a1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.unity.2d.psdimporter", - "version": "2.1.3", + "version": "2.1.4", "unity": "2019.3", "unityRelease": "0b11", "displayName": "2D PSD Importer", @@ -13,16 +13,16 @@ "category": "2D", "dependencies": { "com.unity.2d.common": "2.0.2", - "com.unity.2d.animation": "3.2.1", + "com.unity.2d.animation": "3.2.2", "com.unity.2d.sprite": "1.0.0" }, "relatedPackages": { - "com.unity.2d.psdimporter.tests": "2.1.2" + "com.unity.2d.psdimporter.tests": "2.1.4" }, "repository": { - "footprint": "32ca6d84b652c02ecea51af3169de4e171bfaa65", + "footprint": "1af17d197c48a824192b44f9e6416dd1f8e339ad", "type": "git", "url": "https://github.cds.internal.unity3d.com/unity/2d.git", - "revision": "fe91531ce08a1393a8bdfcd5344b47d2279e33c1" + "revision": "c9d241119572e3252d67d086d3849d6285af76dd" } }