74 lines
2.4 KiB
C#
74 lines
2.4 KiB
C#
using Minint.Core.Models;
|
|
|
|
namespace Minint.Core.Services.Impl;
|
|
|
|
public sealed class Compositor : ICompositor
|
|
{
|
|
/// <inheritdoc />
|
|
public uint[] Composite(MinintDocument document, int width, int height)
|
|
{
|
|
int pixelCount = width * height;
|
|
var result = new uint[pixelCount]; // starts as 0x00000000 (transparent black)
|
|
|
|
var palette = document.Palette;
|
|
|
|
foreach (var layer in document.Layers)
|
|
{
|
|
if (!layer.IsVisible)
|
|
continue;
|
|
|
|
byte layerOpacity = layer.Opacity;
|
|
if (layerOpacity == 0)
|
|
continue;
|
|
|
|
var pixels = layer.Pixels;
|
|
for (int i = 0; i < pixelCount; i++)
|
|
{
|
|
int idx = pixels[i];
|
|
if (idx == 0)
|
|
continue; // transparent — skip
|
|
|
|
var src = palette[idx];
|
|
|
|
// Effective source alpha = palette alpha * layer opacity / 255
|
|
int srcA = src.A * layerOpacity / 255;
|
|
if (srcA == 0)
|
|
continue;
|
|
|
|
if (srcA == 255)
|
|
{
|
|
// Fully opaque — fast path, no blending needed
|
|
result[i] = PackArgb(src.R, src.G, src.B, 255);
|
|
continue;
|
|
}
|
|
|
|
// Standard "over" alpha compositing
|
|
uint dst = result[i];
|
|
int dstA = (int)(dst >> 24);
|
|
int dstR = (int)((dst >> 16) & 0xFF);
|
|
int dstG = (int)((dst >> 8) & 0xFF);
|
|
int dstB = (int)(dst & 0xFF);
|
|
|
|
int outA = srcA + dstA * (255 - srcA) / 255;
|
|
if (outA == 0)
|
|
continue;
|
|
|
|
int outR = (src.R * srcA + dstR * dstA * (255 - srcA) / 255) / outA;
|
|
int outG = (src.G * srcA + dstG * dstA * (255 - srcA) / 255) / outA;
|
|
int outB = (src.B * srcA + dstB * dstA * (255 - srcA) / 255) / outA;
|
|
|
|
result[i] = PackArgb(
|
|
(byte)Math.Min(outR, 255),
|
|
(byte)Math.Min(outG, 255),
|
|
(byte)Math.Min(outB, 255),
|
|
(byte)Math.Min(outA, 255));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static uint PackArgb(byte r, byte g, byte b, byte a) =>
|
|
(uint)(b | (g << 8) | (r << 16) | (a << 24));
|
|
}
|