123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- using Unity.Collections;
- using Unity.Collections.LowLevel.Unsafe;
- using Unity.Jobs;
- using Unity.Mathematics;
- using Unity.Burst;
-
- namespace UnityEditor.U2D.PSD
- {
- internal static class ExtractLayerTask
- {
- struct LayerGroupData
- {
- public int startIndex { get; set; }
- public int endIndex { get; set; }
-
- /// <summary>
- /// The layer's bounding box in document space.
- /// </summary>
- public int4 documentRect { get; set; }
- }
-
- [BurstCompile]
- struct ConvertBufferJob : IJobParallelFor
- {
- [ReadOnly, DeallocateOnJobCompletion]
- public NativeArray<int> inputTextureBufferSizes;
- [ReadOnly, DeallocateOnJobCompletion]
- public NativeArray<IntPtr> inputTextures;
- [ReadOnly, DeallocateOnJobCompletion]
- public NativeArray<int4> inputLayerRects;
- [ReadOnly, DeallocateOnJobCompletion]
- public NativeArray<LayerGroupData> layerGroupDataData;
-
- [ReadOnly, DeallocateOnJobCompletion]
- public NativeArray<int4> outputLayerRect;
- [DeallocateOnJobCompletion]
- public NativeArray<IntPtr> outputTextures;
- public unsafe void Execute(int groupIndex)
- {
- var outputColor = (Color32*)outputTextures[groupIndex];
- var groupStartIndex = layerGroupDataData[groupIndex].startIndex;
- var groupEndIndex = layerGroupDataData[groupIndex].endIndex;
-
- var outStartX = outputLayerRect[groupIndex].x;
- var outStartY = outputLayerRect[groupIndex].y;
- var outWidth = outputLayerRect[groupIndex].z;
- var outHeight = outputLayerRect[groupIndex].w;
-
- for (var layerIndex = groupEndIndex; layerIndex >= groupStartIndex; --layerIndex)
- {
- if (inputTextures[layerIndex] == IntPtr.Zero)
- continue;
-
- var inputColor = (Color32*)inputTextures[layerIndex];
- var inX = inputLayerRects[layerIndex].x;
- var inY = inputLayerRects[layerIndex].y;
- var inWidth = inputLayerRects[layerIndex].z;
- var inHeight = inputLayerRects[layerIndex].w;
-
- for (var y = 0; y < inHeight; ++y)
- {
- var outPosY = (y + inY) - outStartY;
- // If pixel is outside of output texture's Y, move to the next pixel.
- if (outPosY < 0 || outPosY >= outHeight)
- continue;
-
- // Flip Y position on the input texture, because
- // PSDs textures are stored "upside-down"
- var inRow = ((inHeight - 1) - y) * inWidth;
- var outRow = outPosY * outWidth;
-
- for (var x = 0; x < inWidth; ++x)
- {
- var outPosX = (x + inX) - outStartX;
- // If pixel is outside of output texture's X, move to the next pixel.
- if (outPosX < 0 || outPosX >= outWidth)
- continue;
-
- var inBufferIndex = inRow + x;
- var outBufferIndex = outRow + outPosX;
- if (outBufferIndex < 0 || outBufferIndex > (outWidth * outHeight))
- continue;
-
- Color inColor = inputColor[inBufferIndex];
- Color prevOutColor = outputColor[outBufferIndex];
- var outColor = new Color();
-
- var destAlpha = prevOutColor.a * (1 - inColor.a);
- outColor.a = inColor.a + prevOutColor.a * (1 - inColor.a);
-
- var premultiplyAlpha = outColor.a > 0.0f ? 1 / outColor.a : 1f;
- outColor.r = (inColor.r * inColor.a + prevOutColor.r * destAlpha) * premultiplyAlpha;
- outColor.g = (inColor.g * inColor.a + prevOutColor.g * destAlpha) * premultiplyAlpha;
- outColor.b = (inColor.b * inColor.a + prevOutColor.b * destAlpha) * premultiplyAlpha;
-
- outputColor[outBufferIndex] = outColor;
- }
- }
- }
- }
- }
-
- public static unsafe void Execute(in PSDExtractLayerData[] psdExtractLayerData, out List<PSDLayer> outputLayers, bool importHiddenLayer, Vector2Int canvasSize)
- {
- outputLayers = new List<PSDLayer>();
- UnityEngine.Profiling.Profiler.BeginSample("ExtractLayer_PrepareJob");
-
- var inputLayers = new List<PSDLayer>();
- ExtractLayerData(in psdExtractLayerData, ref inputLayers, importHiddenLayer, false, true, canvasSize);
-
- var layerGroupData = new List<LayerGroupData>();
- GenerateOutputLayers(in inputLayers, ref outputLayers, ref layerGroupData, false, canvasSize);
-
- if (layerGroupData.Count == 0)
- {
- foreach (var layer in outputLayers)
- layer.texture = default;
- return;
- }
-
- var job = new ConvertBufferJob();
- job.inputTextureBufferSizes = new NativeArray<int>(inputLayers.Count, Allocator.TempJob);
- job.inputTextures = new NativeArray<IntPtr>(inputLayers.Count, Allocator.TempJob);
- job.inputLayerRects = new NativeArray<int4>(inputLayers.Count, Allocator.TempJob);
- job.outputLayerRect = new NativeArray<int4>(layerGroupData.Count, Allocator.TempJob);
- job.outputTextures = new NativeArray<IntPtr>(layerGroupData.Count, Allocator.TempJob);
-
- for (int i = 0, groupIndex = 0; i < inputLayers.Count; ++i)
- {
- var inputLayer = inputLayers[i];
- var outputLayer = outputLayers[i];
-
- job.inputTextures[i] = inputLayer.texture.IsCreated ? new IntPtr(inputLayer.texture.GetUnsafePtr()) : IntPtr.Zero;
-
- var isGroupOwner = groupIndex < layerGroupData.Count && layerGroupData[groupIndex].startIndex == i;
- if (isGroupOwner)
- {
- outputLayer.texture = new NativeArray<Color32>(outputLayer.width * outputLayer.height, Allocator.Persistent);
-
- job.outputLayerRect[groupIndex] = new int4((int)outputLayer.layerPosition.x, (int)outputLayer.layerPosition.y, outputLayer.width, outputLayer.height);
- job.outputTextures[groupIndex] = outputLayer.texture.IsCreated ? new IntPtr(outputLayer.texture.GetUnsafePtr()) : IntPtr.Zero;
- job.inputTextureBufferSizes[i] = inputLayer.texture.IsCreated ? inputLayer.texture.Length : -1;
- job.inputLayerRects[i] = layerGroupData[groupIndex].documentRect;
- ++groupIndex;
- }
- else
- {
- job.inputTextureBufferSizes[i] = inputLayer.texture.IsCreated ? inputLayer.texture.Length : -1;
- job.inputLayerRects[i] = new int4((int)inputLayer.layerPosition.x, (int)inputLayer.layerPosition.y, inputLayer.width, inputLayer.height);
- outputLayer.texture = default;
- }
- }
- job.layerGroupDataData = new NativeArray<LayerGroupData>(layerGroupData.ToArray(), Allocator.TempJob);
-
- var jobsPerThread = layerGroupData.Count / (SystemInfo.processorCount == 0 ? 8 : SystemInfo.processorCount);
- jobsPerThread = Mathf.Max(jobsPerThread, 1);
- var handle = job.Schedule(layerGroupData.Count, jobsPerThread);
- UnityEngine.Profiling.Profiler.EndSample();
- handle.Complete();
- }
-
- static void ExtractLayerData(in PSDExtractLayerData[] inputLayers, ref List<PSDLayer> extractedLayers, bool importHiddenLayer, bool flatten, bool parentGroupVisible, Vector2Int canvasSize)
- {
- var parentGroupIndex = extractedLayers.Count - 1;
-
- foreach (var inputLayer in inputLayers)
- {
- var bitmapLayer = inputLayer.bitmapLayer;
- var importSettings = inputLayer.importSetting;
- var layerVisible = bitmapLayer.Visible && parentGroupVisible;
-
- var layerRect = new RectInt(bitmapLayer.documentRect.X, bitmapLayer.documentRect.Y, bitmapLayer.Surface.width, bitmapLayer.Surface.height);
-
- if (!bitmapLayer.IsGroup)
- layerRect.y = (canvasSize.y - layerRect.y - layerRect.height);
-
- NativeArray<Color32> surface = default;
- if ((importHiddenLayer || bitmapLayer.Visible) &&
- importSettings.importLayer &&
- bitmapLayer.Surface.color.IsCreated &&
- bitmapLayer.Surface.color.Length > 0)
- surface = bitmapLayer.Surface.color;
-
- var extractedLayer = new PSDLayer(surface, parentGroupIndex, bitmapLayer.IsGroup, bitmapLayer.Name, layerRect.width, layerRect.height, bitmapLayer.LayerID, bitmapLayer.Visible)
- {
- spriteID = inputLayer.importSetting.spriteId,
- flatten = bitmapLayer.IsGroup && inputLayer.importSetting.flatten,
- layerPosition = bitmapLayer.IsGroup ? Vector2.zero : layerRect.position
- };
-
- extractedLayer.isImported = (importHiddenLayer || layerVisible) && !flatten && importSettings.importLayer;
- if (extractedLayer.isGroup)
- extractedLayer.isImported = extractedLayer.isImported && extractedLayer.flatten;
-
- extractedLayers.Add(extractedLayer);
-
- if (inputLayer.children.Length > 0)
- ExtractLayerData(in inputLayer.children, ref extractedLayers, importHiddenLayer, flatten || extractedLayer.flatten, layerVisible, canvasSize);
- }
- }
-
- static void GenerateOutputLayers(in List<PSDLayer> inputLayers, ref List<PSDLayer> outputLayers, ref List<LayerGroupData> layerGroupData, bool flatten, Vector2Int canvasSize)
- {
- var canvasRect = new RectInt(Vector2Int.zero, canvasSize);
-
- for (var i = 0; i < inputLayers.Count; ++i)
- {
- var inputLayer = inputLayers[i];
-
- var outputLayer = new PSDLayer(inputLayer);
- var outputRect = new RectInt((int)outputLayer.layerPosition.x, (int)outputLayer.layerPosition.y, outputLayer.width, outputLayer.height);
-
- if (inputLayer.isGroup)
- {
- var childIndices = FindAllChildrenOfParent(i, in inputLayers);
- childIndices.Sort();
-
- var startIndex = i;
- var endIndex = i + childIndices.Count;
-
- if (flatten == false && inputLayer.flatten && startIndex < endIndex)
- {
- var groupBoundingBox = CalculateLayerRectInChildren(in inputLayers, in childIndices);
- layerGroupData.Add(new LayerGroupData()
- {
- startIndex = startIndex,
- endIndex = endIndex,
- documentRect = new int4(groupBoundingBox.x, groupBoundingBox.y, groupBoundingBox.width, groupBoundingBox.height)
- });
- outputLayer.texture = default;
- outputRect = groupBoundingBox;
- }
- }
- else if(!inputLayer.isGroup && inputLayer.isImported)
- {
- var inputRect = new int4((int)inputLayer.layerPosition.x, (int)inputLayer.layerPosition.y, inputLayer.width, inputLayer.height);
- layerGroupData.Add(new LayerGroupData()
- {
- startIndex = i,
- endIndex = i,
- documentRect = inputRect
- });
- }
-
- CropRect(ref outputRect, canvasRect);
- outputLayer.layerPosition = outputRect.position;
- outputLayer.width = outputRect.width;
- outputLayer.height = outputRect.height;
-
- outputLayers.Add(outputLayer);
- }
- }
-
- static List<int> FindAllChildrenOfParent(int parentIndex, in List<PSDLayer> layers)
- {
- var childIndices = new List<int>();
- for (var i = parentIndex + 1; i < layers.Count; ++i)
- {
- if (layers[i].parentIndex == parentIndex)
- {
- childIndices.Add(i);
- if (layers[i].isGroup)
- childIndices.AddRange(FindAllChildrenOfParent(i, in layers));
- }
- }
- return childIndices;
- }
-
- static RectInt CalculateLayerRectInChildren(in List<PSDLayer> inputLayers, in List<int> childIndices)
- {
- var groupBoundingBox = default(RectInt);
- for (var m = 0; m < childIndices.Count; ++m)
- {
- var childLayer = inputLayers[childIndices[m]];
- if (childLayer.isGroup)
- continue;
-
- var layerRect = new RectInt((int) childLayer.layerPosition.x, (int) childLayer.layerPosition.y,
- childLayer.width, childLayer.height);
- if (IsRectIntDefault(groupBoundingBox))
- groupBoundingBox = layerRect;
- else
- FitRectInsideRect(ref groupBoundingBox, in layerRect);
- }
-
- return groupBoundingBox;
- }
-
- static bool IsRectIntDefault(RectInt rectInt)
- {
- return rectInt.x == 0 &&
- rectInt.y == 0 &&
- rectInt.width == 0 &&
- rectInt.height == 0;
- }
-
- static void CropRect(ref RectInt baseRect, in RectInt cropArea)
- {
- if (baseRect.x < cropArea.x)
- {
- baseRect.width = Mathf.Max(baseRect.width - (cropArea.x - baseRect.x), 0);
- baseRect.x = cropArea.x;
- }
- if (baseRect.xMax > cropArea.xMax)
- {
- baseRect.x = Mathf.Min(baseRect.x, cropArea.xMax);
- baseRect.width = Mathf.Max(cropArea.xMax - baseRect.x, 0);
- }
-
- if (baseRect.y < cropArea.y)
- {
- baseRect.height = Mathf.Max(baseRect.height - (cropArea.y - baseRect.y), 0);
- baseRect.y = cropArea.y;
- }
- if (baseRect.yMax > cropArea.yMax)
- {
- baseRect.y = Mathf.Min(baseRect.y, cropArea.yMax);
- baseRect.height = Mathf.Max(cropArea.yMax - baseRect.y, 0);
- }
- }
-
- static void FitRectInsideRect(ref RectInt baseRect, in RectInt rectToFitIn)
- {
- if (baseRect.xMin > rectToFitIn.xMin)
- baseRect.xMin = rectToFitIn.xMin;
- if (baseRect.yMin > rectToFitIn.yMin)
- baseRect.yMin = rectToFitIn.yMin;
- if (baseRect.xMax < rectToFitIn.xMax)
- baseRect.xMax = rectToFitIn.xMax;
- if (baseRect.yMax < rectToFitIn.yMax)
- baseRect.yMax = rectToFitIn.yMax;
- }
- }
- }
|