using Minint.Core.Models; namespace Minint.Core.Services.Impl; public sealed class Compositor : ICompositor { /// 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)); }