XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
namespace XFESystem.Browser;

/// <summary>A bounded RGBA canvas backing store. Pixels use straight ARGB, initially transparent.</summary>
public sealed class BrowserCanvasSurface
{
    private uint[] pixels;
    public BrowserCanvasSurface(int width, int height) { Width = width; Height = height; pixels = new uint[checked(width * height)]; }
    public int Width { get; private set; }
    public int Height { get; private set; }
    public ReadOnlyMemory<uint> Pixels => pixels;
    public long Revision { get; private set; }
    internal BrowserCanvasSurface CopyForPaintSnapshot()
    {
        var copy = new BrowserCanvasSurface(Width, Height);
        pixels.AsSpan().CopyTo(copy.pixels);
        copy.Revision = Revision;
        return copy;
    }
    internal void Resize(int width, int height) { Width = width; Height = height; pixels = new uint[checked(width * height)]; ++Revision; }
    internal void Fill(double x, double y, double width, double height, uint argb, bool clear = false)
    {
        if (!double.IsFinite(x + y + width + height)) return;
        if (width < 0) { x += width; width = -width; } if (height < 0) { y += height; height = -height; }
        var left = (int)Math.Clamp(Math.Floor(x), 0, Width); var top = (int)Math.Clamp(Math.Floor(y), 0, Height);
        var right = (int)Math.Clamp(Math.Ceiling(x + width), 0, Width); var bottom = (int)Math.Clamp(Math.Ceiling(y + height), 0, Height);
        for (var row = top; row < bottom; ++row) for (var column = left; column < right; ++column)
            pixels[row * Width + column] = clear ? 0 : Composite(pixels[row * Width + column], argb);
        ++Revision;
    }
    internal uint Pixel(int x, int y) => x >= 0 && y >= 0 && x < Width && y < Height ? pixels[y * Width + x] : 0;
    internal void PutPixel(int x, int y, uint value) { if (x >= 0 && y >= 0 && x < Width && y < Height) pixels[y * Width + x] = value; }
    internal void Commit() => ++Revision;
    internal void BlendMask(ReadOnlySpan<byte> mask, int width, int height, double x, double y, double scaleX, double scaleY, uint color, double alpha)
    {
        if (width < 0 || height < 0 || mask.Length != checked(width * height)) throw new ArgumentException("Invalid canvas text mask.");
        for (var row = 0; row < height; ++row) for (var col = 0; col < width; ++col)
        {
            uint coverage = (uint)Math.Clamp(Math.Round(mask[row * width + col] * alpha), 0, 255);
            if (coverage == 0) continue;
            Fill(x + col * scaleX, y + row * scaleY, scaleX, scaleY, coverage << 24 | color);
        }
        ++Revision;
    }
    private static uint Composite(uint destination, uint source)
    {
        uint alpha = source >> 24; if (alpha == 255) return source; if (alpha == 0) return destination;
        uint destinationAlpha = destination >> 24; uint outputAlpha = alpha + (destinationAlpha * (255 - alpha) + 127) / 255;
        if (outputAlpha == 0) return 0;
        uint result = outputAlpha << 24;
        for (var shift = 0; shift < 24; shift += 8)
        {
            uint premultiplied = ((source >> shift) & 255) * alpha + (((destination >> shift) & 255) * destinationAlpha * (255 - alpha) + 127) / 255;
            result |= ((premultiplied + outputAlpha / 2) / outputAlpha) << shift;
        }
        return result;
    }
}