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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using AngleSharp.Dom;
using AngleSharp.Html.Parser;

namespace XFESystem.Browser.Tests;

public sealed class BrowserCanvasTests
{
    [Fact]
    public void CanvasFillClearAndImageDataReadRealPixels()
    {
        var page = new CanvasPage();
        page.Run("canvas.width=4;canvas.height=3;ctx.fillStyle='#1256AB';ctx.fillRect(1,1,2,1);var data=ctx.getImageData(0,0,4,3);if(!(data.data instanceof Uint8ClampedArray))throw Error('type');");
        Assert.Equal("18,86,171,255", page.Value("Array.from(data.data.slice(20,24)).join(',')"));
        Assert.Equal(0xFF1256ABu, page.Surface.Pixels.Span[5]);
        page.Run("ctx.clearRect(1,1,1,1)"); Assert.Equal(0u, page.Surface.Pixels.Span[5]); Assert.Equal(0xFF1256ABu, page.Surface.Pixels.Span[6]);
    }

    [Fact]
    public void CanvasCompositePutImageDataAndTransformsAreNotNoOps()
    {
        var page = new CanvasPage();
        page.Run("canvas.width=4;canvas.height=4;ctx.fillStyle='red';ctx.fillRect(0,0,4,4);ctx.globalAlpha=.5;ctx.fillStyle='blue';ctx.fillRect(0,0,1,1);ctx.save();ctx.translate(1,1);ctx.scale(2,2);ctx.globalAlpha=1;ctx.fillStyle='white';ctx.fillRect(0,0,1,1);ctx.restore();var data=ctx.createImageData(1,1);data.data.set([1,2,3,4]);ctx.putImageData(data,3,0);");
        Assert.Equal(0xFF7F0080u, page.Surface.Pixels.Span[0]); Assert.Equal(0xFFFFFFFFu, page.Surface.Pixels.Span[5]); Assert.Equal(0x04010203u, page.Surface.Pixels.Span[3]);
    }

    [Fact]
    public void CanvasTextUsesHostRasterizedMaskAndMeasuredMetrics()
    {
        var page = new CanvasPage(new() { MeasureText = (_, _) => 2, RasterizeText = (_, _) => (2, 2, [255, 0, 128, 255]) });
        page.Run("canvas.width=4;canvas.height=4;ctx.fillStyle='#123456';ctx.textBaseline='top';ctx.fillText('X',1,1);var width=ctx.measureText('X').width;");
        Assert.Equal("2", page.Value("width")); Assert.Equal(0xFF123456u, page.Surface.Pixels.Span[5]); Assert.Equal(0u, page.Surface.Pixels.Span[6]); Assert.Equal(0x80123456u, page.Surface.Pixels.Span[9]);
        Assert.Equal("128", page.Value("ctx.getImageData(1,2,1,1).data[3]"));
    }

    [Fact]
    public void MissingTextRasterizerFailsExplicitlyAndResizeResetsBackingPixels()
    {
        var page = new CanvasPage(); Assert.False(page.Runtime.Evaluate("ctx.fillText('X',0,0)").Success);
        page.Run("ctx.fillStyle='red';ctx.fillRect(0,0,2,2);canvas.setAttribute('width','2');canvas.height=2;");
        Assert.Equal(2, page.Surface.Width); Assert.Equal(2, page.Surface.Height); Assert.All(page.Surface.Pixels.ToArray(), pixel => Assert.Equal(0u, pixel));
        Assert.Equal("#000000", page.Value("ctx.fillStyle"));
        Assert.False(page.Runtime.Evaluate("canvas.width=4096;canvas.height=4096").Success);
        Assert.True((long)page.Surface.Width * page.Surface.Height <= 1_048_576);
    }

    private sealed class CanvasPage
    {
        private readonly IDocument document;
        private readonly DomBridge dom;
        public CanvasPage(BrowserDomHost? host = null)
        {
            var limits = new BrowserLimits(); Runtime = new(limits); document = new HtmlParser().ParseDocument("<canvas id='Canvas'></canvas>");
            dom = new(Runtime, document, new("https://example.test/"), limits, host);
            Run("var canvas=document.getElementById('Canvas'),ctx=canvas.getContext('2d');if(canvas.getContext('2d')!==ctx||canvas.getContext('webgl')!==null)throw Error('identity');");
        }
        public JavaScriptRuntime Runtime { get; }
        public BrowserCanvasSurface Surface => dom.GetCanvasSurface(document.GetElementById("Canvas")!)!;
        public void Run(string source) { var result = Runtime.Evaluate(source); Assert.True(result.Success, result.Error); }
        public string Value(string source) { var result = Runtime.Evaluate(source); Assert.True(result.Success, result.Error); return result.Value; }
    }
}