184 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			184 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using Unity.Burst;
 | |
| using Unity.Collections;
 | |
| using Unity.Collections.LowLevel.Unsafe;
 | |
| using Unity.Jobs;
 | |
| using Unity.Mathematics;
 | |
| using UnityEngine;
 | |
| 
 | |
| namespace UnityEditor.U2D.PSD
 | |
| {
 | |
|     static class FlattenImageTask
 | |
|     {
 | |
|         struct LayerData
 | |
|         {
 | |
|             public IntPtr layerBuffer;
 | |
|             public int4 layerRect;
 | |
|         }
 | |
| 
 | |
|         public static unsafe void Execute(in PSDExtractLayerData[] layer, ref NativeArray<Color32> output, bool importHiddenLayer, Vector2Int documentSize)
 | |
|         {
 | |
|             UnityEngine.Profiling.Profiler.BeginSample("FlattenImage");
 | |
| 
 | |
|             List<LayerData> layerData = new List<LayerData>();
 | |
|             for (int i = layer.Length - 1; i >= 0; --i)
 | |
|             {
 | |
|                 GetLayerDataToMerge(in layer[i], ref layerData, importHiddenLayer);
 | |
|             }
 | |
| 
 | |
|             if (layerData.Count == 0)
 | |
|                 return;
 | |
| 
 | |
|             int layersPerJob = layerData.Count / (SystemInfo.processorCount == 0 ? 8 : SystemInfo.processorCount);
 | |
|             layersPerJob = Mathf.Max(layersPerJob, 1);
 | |
| 
 | |
|             FlattenImageInternalJob job = new FlattenImageInternalJob();
 | |
|             FlattenImageInternalJob combineJob = new FlattenImageInternalJob();
 | |
| 
 | |
|             job.inputTextures = new NativeArray<IntPtr>(layerData.Count, Allocator.TempJob);
 | |
|             job.inputTextureRects = new NativeArray<int4>(layerData.Count, Allocator.TempJob);
 | |
| 
 | |
|             for (int i = 0; i < layerData.Count; ++i)
 | |
|             {
 | |
|                 job.inputTextures[i] = layerData[i].layerBuffer;
 | |
|                 job.inputTextureRects[i] = layerData[i].layerRect;
 | |
|             }
 | |
| 
 | |
|             job.layersPerJob = layersPerJob;
 | |
|             job.flipY = false;
 | |
|             combineJob.flipY = true;
 | |
| 
 | |
|             int jobCount = layerData.Count / layersPerJob + (layerData.Count % layersPerJob > 0 ? 1 : 0);
 | |
|             combineJob.layersPerJob = jobCount;
 | |
| 
 | |
|             NativeArray<byte>[] premergedBuffer = new NativeArray<byte>[jobCount];
 | |
|             job.outputTextureSizes = new NativeArray<int2>(jobCount, Allocator.TempJob);
 | |
|             job.outputTextures = new NativeArray<IntPtr>(jobCount, Allocator.TempJob);
 | |
|             combineJob.inputTextures = new NativeArray<IntPtr>(jobCount, Allocator.TempJob);
 | |
|             combineJob.inputTextureRects = new NativeArray<int4>(jobCount, Allocator.TempJob);
 | |
| 
 | |
|             for (int i = 0; i < jobCount; ++i)
 | |
|             {
 | |
|                 premergedBuffer[i] = new NativeArray<byte>(documentSize.x * documentSize.y * 4, Allocator.TempJob);
 | |
|                 job.outputTextureSizes[i] = new int2(documentSize.x, documentSize.y);
 | |
|                 job.outputTextures[i] = new IntPtr(premergedBuffer[i].GetUnsafePtr());
 | |
|                 combineJob.inputTextures[i] = new IntPtr(premergedBuffer[i].GetUnsafeReadOnlyPtr());
 | |
|                 combineJob.inputTextureRects[i] = new int4(0, 0, documentSize.x, documentSize.y);
 | |
|             }
 | |
| 
 | |
|             combineJob.outputTextureSizes = new NativeArray<int2>(new[] { new int2(documentSize.x, documentSize.y) }, Allocator.TempJob);
 | |
|             combineJob.outputTextures = new NativeArray<IntPtr>(new[] { new IntPtr(output.GetUnsafePtr()) }, Allocator.TempJob);
 | |
| 
 | |
|             JobHandle handle = job.Schedule(jobCount, 1);
 | |
|             combineJob.Schedule(1, 1, handle).Complete();
 | |
| 
 | |
|             foreach (NativeArray<byte> b in premergedBuffer)
 | |
|             {
 | |
|                 if (b.IsCreated)
 | |
|                     b.Dispose();
 | |
|             }
 | |
| 
 | |
|             UnityEngine.Profiling.Profiler.EndSample();
 | |
|         }
 | |
| 
 | |
|         static unsafe void GetLayerDataToMerge(in PSDExtractLayerData layer, ref List<LayerData> layerData, bool importHiddenLayer)
 | |
|         {
 | |
|             PDNWrapper.BitmapLayer bitmapLayer = layer.bitmapLayer;
 | |
|             PSDLayerImportSetting importSetting = layer.importSetting;
 | |
|             if (!bitmapLayer.Visible && importHiddenLayer == false || importSetting.importLayer == false)
 | |
|                 return;
 | |
| 
 | |
|             if (bitmapLayer.IsGroup)
 | |
|             {
 | |
|                 for (int i = layer.children.Length - 1; i >= 0; --i)
 | |
|                     GetLayerDataToMerge(layer.children[i], ref layerData, importHiddenLayer);
 | |
|             }
 | |
| 
 | |
|             if (bitmapLayer.Surface == null || bitmapLayer.localRect == default)
 | |
|                 return;
 | |
| 
 | |
|             PDNWrapper.Rectangle layerRect = bitmapLayer.documentRect;
 | |
|             LayerData data = new LayerData()
 | |
|             {
 | |
|                 layerBuffer = new IntPtr(bitmapLayer.Surface.color.GetUnsafeReadOnlyPtr()),
 | |
|                 layerRect = new int4(layerRect.X, layerRect.Y, layerRect.Width, layerRect.Height)
 | |
|             };
 | |
|             layerData.Add(data);
 | |
|         }
 | |
| 
 | |
|         [BurstCompile]
 | |
|         struct FlattenImageInternalJob : IJobParallelFor
 | |
|         {
 | |
|             [ReadOnly, DeallocateOnJobCompletion]
 | |
|             public NativeArray<IntPtr> inputTextures;
 | |
|             [ReadOnly, DeallocateOnJobCompletion]
 | |
|             public NativeArray<int4> inputTextureRects;
 | |
|             [ReadOnly]
 | |
|             public int layersPerJob;
 | |
|             [ReadOnly]
 | |
|             public bool flipY;
 | |
| 
 | |
|             [ReadOnly, DeallocateOnJobCompletion]
 | |
|             public NativeArray<int2> outputTextureSizes;
 | |
|             [DeallocateOnJobCompletion]
 | |
|             public NativeArray<IntPtr> outputTextures;
 | |
| 
 | |
|             public unsafe void Execute(int index)
 | |
|             {
 | |
|                 Color32* outputColor = (Color32*)outputTextures[index].ToPointer();
 | |
|                 for (int layerIndex = index * layersPerJob; layerIndex < (index * layersPerJob) + layersPerJob; ++layerIndex)
 | |
|                 {
 | |
|                     if (inputTextures.Length <= layerIndex)
 | |
|                         break;
 | |
| 
 | |
|                     Color32* inputColor = (Color32*)inputTextures[layerIndex].ToPointer();
 | |
|                     int inStartPosX = inputTextureRects[layerIndex].x;
 | |
|                     int inStartPosY = inputTextureRects[layerIndex].y;
 | |
|                     int inWidth = inputTextureRects[layerIndex].z;
 | |
|                     int inHeight = inputTextureRects[layerIndex].w;
 | |
| 
 | |
|                     int outWidth = outputTextureSizes[index].x;
 | |
|                     int outHeight = outputTextureSizes[index].y;
 | |
| 
 | |
|                     for (int y = 0; y < inHeight; ++y)
 | |
|                     {
 | |
|                         int outPosY = y + inStartPosY;
 | |
|                         // If pixel is outside of output texture's Y, move to the next pixel.
 | |
|                         if (outPosY < 0 || outPosY >= outHeight)
 | |
|                             continue;
 | |
| 
 | |
|                         int inRow = y * inWidth;
 | |
|                         int outRow = flipY ? (outHeight - 1 - y - inStartPosY) * outWidth : (y + inStartPosY) * outWidth;
 | |
| 
 | |
|                         for (int x = 0; x < inWidth; ++x)
 | |
|                         {
 | |
|                             int outPosX = x + inStartPosX;
 | |
|                             // If pixel is outside of output texture's X, move to the next pixel.
 | |
|                             if (outPosX < 0 || outPosX >= outWidth)
 | |
|                                 continue;
 | |
| 
 | |
|                             int inBufferIndex = inRow + x;
 | |
|                             int outBufferIndex = outRow + outPosX;
 | |
| 
 | |
|                             Color inColor = inputColor[inBufferIndex];
 | |
|                             Color prevOutColor = outputColor[outBufferIndex];
 | |
|                             Color outColor = new Color();
 | |
| 
 | |
|                             float destAlpha = prevOutColor.a * (1 - inColor.a);
 | |
|                             outColor.a = inColor.a + prevOutColor.a * (1 - inColor.a);
 | |
| 
 | |
|                             float 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;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |