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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using AngleSharp.Dom;
using XFESystem.Desktop;
using XFESystem.Browser;
namespace XfePaintBaseline;
public static class BaselinePagePainter
{
    public static void Paint(IDesktopCanvas canvas, DesktopBounds viewport, BrowserDisplayList list, Func<INode, BrowserCanvasSurface?>? surfaces = null)
    {
        var web = canvas as IDesktopWebCanvas;
        var rounded = new BaselineClipCanvas(canvas);
        var clips = new Stack<BrowserRect>();
        var clip = new BrowserRect(viewport.X, viewport.Y, viewport.Width, viewport.Height);
        web?.PushClip(viewport);
        canvas.Fill(0xFFFFFF, viewport.X, viewport.Y, viewport.Width, viewport.Height);
        try
        {
            foreach (var item in list.Commands)
            {
                if (item.Opacity <= 0 && item.Kind is not (BrowserDrawKind.PushClip or BrowserDrawKind.PopClip)) continue;
                var r = item.Bounds.Translate(viewport.X - list.Viewport.ScrollX, viewport.Y - list.Viewport.ScrollY);
                if (item.Kind == BrowserDrawKind.PushClip) { clips.Push(clip); clip = clip.Intersect(r); rounded.Push(item.RoundedClip,viewport.X-list.Viewport.ScrollX,viewport.Y-list.Viewport.ScrollY); web?.PushClip(ToBounds(clip)); continue; }
                if (item.Kind == BrowserDrawKind.PopClip) { if (clips.TryPop(out var old)) { clip = old;rounded.Pop(); web?.PopClip(); } continue; }
                var visible = r.Intersect(clip); if (visible.Width <= 0 || visible.Height <= 0) continue;
                IDesktopCanvas target=rounded.Active&&canvas is IDesktopCoverageCanvas?rounded:canvas;
                if(item.Decoration is not null) BaselineBoxPainter.Paint(target,item,r,visible);
                else if (item.Kind == BrowserDrawKind.Canvas)
                { if (item.Node is not null && surfaces?.Invoke(item.Node) is { } surface) PaintCanvas(target, r, visible, surface, item.Opacity); }
                else if (item.Kind == BrowserDrawKind.Text)
                {
                    double leading=web is null?0:(r.Height-web.MeasureWebLineHeight(item.Font.Size,item.Font.Family,item.Font.Weight))/2;
                    if(web is not null&&rounded.Active&&canvas is IDesktopCoverageCanvas)rounded.WebText(web,item,r.Translate(0,leading));
                    else if (web is not null) web.WebText(item.Text, item.Color, r.X, r.Y+leading, item.Font.Size, item.Font.Family, item.Font.Weight, item.Opacity);
                    else if (visible == r) canvas.Text(item.Text, item.Color, (int)r.X, (int)r.Y);
                }
                else
                {
                    var b = ToBounds(visible);
                    if (item.Opacity < 1 && target is IDesktopCoverageCanvas coverage)
                    {
                        int left = (int)Math.Floor(visible.X * canvas.ScalePercent / 100d), top = (int)Math.Floor(visible.Y * canvas.ScalePercent / 100d);
                        int width = Math.Max(1, (int)Math.Ceiling(visible.Width * canvas.ScalePercent / 100d)), height = Math.Max(1, (int)Math.Ceiling(visible.Height * canvas.ScalePercent / 100d));
                        var row = new byte[width]; Array.Fill(row, (byte)Math.Clamp(item.Opacity * 255, 0, 255));
                        for (int y = 0; y < height; y++) coverage.BlendCoverage(item.Color, left, top + y, width, 1, row);
                    }
                    else target.Fill(item.Color, b.X, b.Y, b.Width, b.Height);
                }
            }
        }
        catch { while (clips.TryPop(out _)) web?.PopClip(); web?.PopClip(); throw; }
        while (clips.TryPop(out _)) web?.PopClip(); web?.PopClip();
    }
    private static void PaintCanvas(IDesktopCanvas canvas, BrowserRect bounds, BrowserRect visible, BrowserCanvasSurface surface, double opacity)
    {
        if (surface.Width <= 0 || surface.Height <= 0 || bounds.Width <= 0 || bounds.Height <= 0) return;
        var pixels = surface.Pixels.Span;
        int left = (int)Math.Floor(visible.Left), right = (int)Math.Ceiling(visible.Right), top = (int)Math.Floor(visible.Top), bottom = (int)Math.Ceiling(visible.Bottom);
        for (int row = top; row < bottom; row++)
        {
            int sourceY = Math.Clamp((int)((row - bounds.Y) * surface.Height / bounds.Height), 0, surface.Height - 1);
            for (int x = left; x < right;)
            {
                int sourceX = Math.Clamp((int)((x - bounds.X) * surface.Width / bounds.Width), 0, surface.Width - 1);
                uint color = pixels[sourceY * surface.Width + sourceX]; int end = x + 1;
                while (end < right && pixels[sourceY * surface.Width + Math.Clamp((int)((end - bounds.X) * surface.Width / bounds.Width), 0, surface.Width - 1)] == color) end++;
                int alpha = (int)((color >> 24) * opacity);
                if (alpha >= 255) canvas.Fill(color & 0xFFFFFF, x, row, end - x, 1);
                else if (alpha > 0 && canvas is IDesktopCoverageCanvas coverage)
                {
                    int nx = (int)Math.Round(x * canvas.ScalePercent / 100d), ny = (int)Math.Round(row * canvas.ScalePercent / 100d);
                    int nw = Math.Max(1,(int)Math.Round(end * canvas.ScalePercent / 100d)-nx), nh = Math.Max(1,(int)Math.Round((row+1) * canvas.ScalePercent / 100d)-ny);
                    var mask = new byte[nw]; Array.Fill(mask,(byte)alpha);
                    for (int sy = 0; sy < nh; sy++) coverage.BlendCoverage(color & 0xFFFFFF, nx, ny + sy, nw,1,mask);
                }
                x = end;
            }
        }
    }
    private static DesktopBounds ToBounds(BrowserRect r) => new((int)Math.Floor(r.X), (int)Math.Floor(r.Y), Math.Max(0, (int)Math.Ceiling(r.Right) - (int)Math.Floor(r.X)), Math.Max(0, (int)Math.Ceiling(r.Bottom) - (int)Math.Floor(r.Y)));
}