201 lines
8.5 KiB
C#
201 lines
8.5 KiB
C#
|
using System;
|
||
|
using Unity.Burst;
|
||
|
using Unity.Collections;
|
||
|
using Unity.Mathematics;
|
||
|
using UnityEngine;
|
||
|
|
||
|
namespace UnityEditor.U2D.Aseprite
|
||
|
{
|
||
|
#if UNITY_2022_2_OR_NEWER
|
||
|
[BurstCompile]
|
||
|
#endif
|
||
|
internal static class TextureTasks
|
||
|
{
|
||
|
[BurstCompile]
|
||
|
public static void AddOpacity(ref NativeArray<Color32> texture, float opacity)
|
||
|
{
|
||
|
for (var i = 0; i < texture.Length; ++i)
|
||
|
{
|
||
|
var color = texture[i];
|
||
|
color.a = (byte)(color.a * opacity);
|
||
|
texture[i] = color;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[BurstCompile]
|
||
|
public static void FlipTextureY(ref NativeArray<Color32> texture, in int2 size)
|
||
|
{
|
||
|
if (size.x == 0 || size.y == 0)
|
||
|
return;
|
||
|
|
||
|
var outputTexture = new NativeArray<Color32>(texture.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||
|
for (var y = 0; y < size.y; ++y)
|
||
|
{
|
||
|
var inRow = ((size.y - 1) - y) * size.x;
|
||
|
var outRow = y * size.x;
|
||
|
|
||
|
for (var x = 0; x < size.x; ++x)
|
||
|
{
|
||
|
var inIndex = x + inRow;
|
||
|
var outIndex = x + outRow;
|
||
|
outputTexture[outIndex] = texture[inIndex];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
texture.DisposeIfCreated();
|
||
|
texture = outputTexture;
|
||
|
}
|
||
|
|
||
|
public struct MergeOutput
|
||
|
{
|
||
|
public RectInt rect;
|
||
|
public NativeArray<Color32> image;
|
||
|
}
|
||
|
|
||
|
[BurstCompile]
|
||
|
public static unsafe void MergeTextures(in NativeArray<IntPtr> textures, in NativeArray<RectInt> textureSizes, in NativeArray<BlendModes> blendModes, out MergeOutput output)
|
||
|
{
|
||
|
GetCombinedRect(in textureSizes, out var combinedRect);
|
||
|
var outputTexture = new NativeArray<Color32>(combinedRect.width * combinedRect.height, Allocator.Persistent);
|
||
|
|
||
|
var outStartX = combinedRect.x;
|
||
|
var outStartY = combinedRect.y;
|
||
|
var outWidth = combinedRect.width;
|
||
|
var outHeight = combinedRect.height;
|
||
|
for (var i = 0; i < textures.Length; ++i)
|
||
|
{
|
||
|
var inputColor = (Color32*)textures[i];
|
||
|
var inputBlend = blendModes[i];
|
||
|
var inX = textureSizes[i].x;
|
||
|
var inY = textureSizes[i].y;
|
||
|
var inWidth = textureSizes[i].width;
|
||
|
var inHeight = textureSizes[i].height;
|
||
|
|
||
|
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
|
||
|
// Aseprite 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;
|
||
|
|
||
|
var inColor = inputColor[inBufferIndex];
|
||
|
var prevOutColor = outputTexture[outBufferIndex];
|
||
|
|
||
|
Color32 outColor;
|
||
|
switch (inputBlend)
|
||
|
{
|
||
|
case BlendModes.Darken:
|
||
|
PixelBlends.Darken(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Multiply:
|
||
|
PixelBlends.Multiply(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.ColorBurn:
|
||
|
PixelBlends.ColorBurn(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Lighten:
|
||
|
PixelBlends.Lighten(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Screen:
|
||
|
PixelBlends.Screen(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.ColorDodge:
|
||
|
PixelBlends.ColorDodge(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Addition:
|
||
|
PixelBlends.Addition(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Overlay:
|
||
|
PixelBlends.Overlay(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.SoftLight:
|
||
|
PixelBlends.SoftLight(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.HardLight:
|
||
|
PixelBlends.HardLight(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Difference:
|
||
|
PixelBlends.Difference(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Exclusion:
|
||
|
PixelBlends.Exclusion(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Subtract:
|
||
|
PixelBlends.Subtract(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Divide:
|
||
|
PixelBlends.Divide(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Hue:
|
||
|
PixelBlends.Hue(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Saturation:
|
||
|
PixelBlends.Saturation(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Color:
|
||
|
PixelBlends.ColorBlend(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Luminosity:
|
||
|
PixelBlends.Luminosity(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
case BlendModes.Normal:
|
||
|
default:
|
||
|
PixelBlends.Normal(in prevOutColor, in inColor, out outColor);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
outputTexture[outBufferIndex] = outColor;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
output = new MergeOutput()
|
||
|
{
|
||
|
rect = combinedRect,
|
||
|
image = outputTexture
|
||
|
};
|
||
|
}
|
||
|
|
||
|
[BurstCompile]
|
||
|
static void GetCombinedRect(in NativeArray<RectInt> rects, out RectInt combinedRect)
|
||
|
{
|
||
|
combinedRect = rects[0];
|
||
|
for (var i = 1; i < rects.Length; ++i)
|
||
|
{
|
||
|
var rectToFitIn = rects[i];
|
||
|
FitRectInsideRect(ref combinedRect, in rectToFitIn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[BurstCompile]
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|