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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Drawing;
using System.Diagnostics;
using Cosmos.Kernel.HAL.Devices.Display;
using Cosmos.Kernel.System.Graphics;
using Cosmos.Kernel.System.Graphics.Fonts;
using XFESystem.Desktop;

namespace XFESystem.Kernel;

/// <summary>Native pixels, cached glyph coverage, scene restoration and bounded damaged scanout.</summary>
internal sealed class BaselineCanvasAdapter : IBrandedDesktopCanvas, IDesktopIconCanvas, IDesktopCoverageCanvas, IDesktopSceneMotionCanvas, IDesktopWebCanvas
{
    private readonly Canvas canvas = Canvas.GetFullScreen();
    private readonly TrueTypeFont font;
    private readonly int scale;
    private readonly int[] pixels, scene;
    private readonly DesktopSceneMotionBuffer motion;
    private readonly byte[] unicode = ResourceBytes("XFESystem.Kernel.Unifont.bin");
    private readonly byte[] brand = ResourceBytes("XFESystem.Kernel.Icon.rgba");
    private readonly byte[] atlas = ResourceBytes("XFESystem.Kernel.DesktopIcons.rgba");
    private readonly Dictionary<char, Glyph> glyphs = [];
    private readonly Dictionary<(int Icon, int Size), int[]> iconCache = [];
    private readonly IDisplay? scanout;
    private readonly DisplayDevice? device;
    private PixelRect clip;
    private readonly Stack<PixelRect> webClips = [];
    private readonly Dictionary<(char Character, int Size, int Face, bool Bold), WebGlyphCoverage> webGlyphs = [];
    private readonly Dictionary<int, WebFontFace> webFonts = [];
    private readonly Dictionary<(string Family,int Weight),WebFontFace> webFontRequests=[];
    private readonly Dictionary<(int Primary,char Character,bool Bold),WebFontFace> webFallbackFaces=[];
    private sealed record WebFontFace(int Id,string Family,TrueTypeFont Font,NativeWebFontMetrics Metrics,bool SyntheticBold);
    private sealed record WebGlyphCoverage(int Width,int Height,int OffsetX,byte[] Alpha);
    private DesktopBounds unchangedScanout;
    public long FramesPresented { get; private set; }
    public long PixelsPresented { get; private set; }
    public long PartialFrames { get; private set; }
    public long FullCopyFallbackFrames { get; private set; }
    public long LastPresentTicks { get; private set; }
    public int CachedGlyphCount => glyphs.Count;
    public long MotionReuseCount => motion.ReuseCount;
    public long MotionPixelsReused => motion.PixelsReused;
    public long ScanoutPixelsSaved { get; private set; }
    private sealed record Glyph(int Width, int Height, byte[] Alpha);
    private readonly record struct PixelRect(int X, int Y, int Width, int Height);
    private static readonly (int First, int Last, int Offset)[] GlyphRanges =
    [
        (128, 2047, 0), (8192, 8303, 63360), (8592, 8703, 67056),
        (12288, 12351, 70752), (13312, 40959, 72864), (65280, 65519, 985248),
        (65533, 65533, 993168)
    ];

    public BaselineCanvasAdapter()
    {
        using var resource = typeof(BaselineCanvasAdapter).Assembly.GetManifestResourceStream("XFESystem.Kernel.CascadiaMono.ttf")
            ?? throw new InvalidOperationException("The embedded desktop font is missing.");
        scale = canvas.Width >= 3000 || canvas.Height >= 1800 ? 2 : 1;
        font = new TrueTypeFont(resource, 13 * scale);
        pixels = canvas.GetBuffer() ?? throw new InvalidOperationException("The desktop canvas is not buffered.");
        scene = new int[pixels.Length];
        motion = new(pixels, scene, canvas.Width, canvas.Height);
        clip = new(0, 0, canvas.Width, canvas.Height);
        device = DisplayManager.Primary;
        if (device is { } primary && primary.TryGetFacet<IDisplay>(out var display)) scanout = display;
        if (atlas.Length != 9 * 64 * 64 * 4) throw new InvalidOperationException("Desktop icon atlas is invalid.");
    }

    public int Width => canvas.Width / scale;
    public int Height => canvas.Height / scale;
    public int NativeWidth => canvas.Width;
    public int NativeHeight => canvas.Height;
    public int ScalePercent => scale * 100;
    // Cosmos firmware fabricates a fallback 60 Hz and GOP provides no active
    // scanout clock. Only a real mode-setting driver's report can be confirmed.
    public int DriverRefreshHz => device?.IsFirmware == false ? scanout?.Mode.RefreshRate ?? 0 : 0;
    public bool SupportsSceneCache => true;
    public int ToLayout(int nativePosition) => nativePosition / scale;
    public int TextWidth(string text)
    {
        int width = 0;
        foreach (char character in text) width += character < 128 ? 8 : unicode[GlyphOffset(character)];
        return width;
    }

    public void BeginScene(DesktopBounds damage) { motion.End(); webClips.Clear(); clip = NativeRect(damage); }
    public void PushClip(DesktopBounds bounds) { webClips.Push(clip); clip = Intersect(clip, NativeRect(bounds)); }
    public void PopClip() { if (!webClips.TryPop(out clip)) throw new InvalidOperationException("Unbalanced web clipping."); }
    public double MeasureWebText(string text, double fontSize, string fontFamily, int fontWeight)
    {
        var face=WebFace(fontFamily,fontWeight);double size=Math.Clamp(fontSize,1,256),width=0;char previous='\0';
        foreach(char ch in text)
        {
            if(previous!='\0'&&face.Font.HasGlyph(previous)&&face.Font.HasGlyph(ch))width+=WebKerning(face,previous,ch,size);
            width+=WebAdvance(face,ch,size);previous=ch;
        }
        return width;
    }
    public double MeasureWebLineHeight(double fontSize,string fontFamily,int fontWeight)=>WebFace(fontFamily,fontWeight).Metrics.NaturalLineToEm*Math.Clamp(fontSize,1,256);
    public string DescribeWebFont(string requested,int weight)=>requested+" -> "+WebFace(requested,weight).Family;
    public string WebFontMetricsEvidence=>"Sans="+DescribeWebFont("system-ui",400)+";SansW="+MeasureWebText("WWW",16,"system-ui",400)+";SansI="+MeasureWebText("iii",16,"system-ui",400)+";Mono="+DescribeWebFont("Consolas",400)+";MonoW="+MeasureWebText("WWW",16,"Consolas",400)+";MonoI="+MeasureWebText("iii",16,"Consolas",400)+";Cjk="+WebCjkFace(false).Family+";CjkGlyph="+WebCjkFace(false).Font.HasGlyph('文')+";CjkBoldGlyph="+WebCjkFace(true).Font.HasGlyph('文')+";CjkAdvance="+MeasureWebText("中文",16,"system-ui",400);
    public (int Width, int Height, byte[] Alpha) RasterizeWebText(string text, double fontSize, string fontFamily, int fontWeight)
    {
        if (text.Length > 4096) throw new ArgumentOutOfRangeException(nameof(text));
        // Canvas bitmaps use CSS pixels, independently of desktop UI scale.
        int size = Math.Max(1, (int)Math.Round(Math.Clamp(fontSize, 1, 256)));var face=WebFace(fontFamily,fontWeight);
        int width = Math.Max(1, (int)Math.Ceiling(MeasureWebText(text, fontSize, fontFamily, fontWeight)));
        int height = Math.Max(1, (int)Math.Ceiling(MeasureWebLineHeight(size,fontFamily,fontWeight)));
        var alpha = new byte[checked(width * height)]; double left = 0;char previous='\0';
        foreach (char ch in text)
        {
            if(previous!='\0'&&face.Font.HasGlyph(previous)&&face.Font.HasGlyph(ch))left+=WebKerning(face,previous,ch,size);
            var glyph = WebGlyph(ch, size, face); int x = (int)Math.Round(left)+glyph.OffsetX;
            for (int row = 0; row < Math.Min(height, glyph.Height); row++) for (int col = Math.Max(0,-x); col < Math.Min(glyph.Width, width - x); col++)
                alpha[row * width + x + col] = Math.Max(alpha[row * width + x + col], glyph.Alpha[row * glyph.Width + col]);
            left += WebAdvance(face,ch,size);previous=ch;
        }
        return (width, height, alpha);
    }
    public void WebText(string text, uint rgb, double x, double y, double fontSize, string fontFamily, int fontWeight, double opacity = 1)
    {
        double size = Math.Clamp(fontSize, 1, 128), left = x * scale;var face=WebFace(fontFamily,fontWeight);char previous='\0';
        int nativeSize = Math.Max(1, (int)Math.Round(size * scale)), top = (int)Math.Round(y * scale);
        foreach (char ch in text)
        {
            if(previous!='\0'&&face.Font.HasGlyph(previous)&&face.Font.HasGlyph(ch))left+=WebKerning(face,previous,ch,size)*scale;
            double advance = WebAdvance(face,ch,size)*scale;
            if (left >= clip.X + clip.Width) break;
            if (left + advance > clip.X)
            {
                var glyph = WebGlyph(ch, nativeSize, face);
                if (opacity >= 1) BlendCoverage(rgb, (int)Math.Round(left)+glyph.OffsetX, top, glyph.Width, glyph.Height, glyph.Alpha);
                else if (opacity > 0)
                {
                    var alpha = new byte[glyph.Alpha.Length];
                    for (int i = 0; i < alpha.Length; i++) alpha[i] = (byte)(glyph.Alpha[i] * opacity);
                    BlendCoverage(rgb, (int)Math.Round(left)+glyph.OffsetX, top, glyph.Width, glyph.Height, alpha);
                }
            }
            left += advance;previous=ch;
        }
    }
    private WebFontFace WebFace(string family,int weight)
    {
        if(webFontRequests.TryGetValue((family,weight),out var cached))return cached;
        int id=1;
        foreach(string raw in family.Split(','))
        {
            string name=raw.Trim(' ','\'','"').ToLowerInvariant();
            if(name=="bootstrap-icons"){id=20;break;}
            if(name is "monospace" or "ui-monospace" or "cascadia mono" or "cascadia code" or "consolas" or "sfmono-regular" or "sf mono" or "menlo" or "liberation mono"){id=0;break;}
            if(name is "system-ui" or "sans-serif" or "-apple-system" or "segoe ui" or "lato" or "arial" or "helvetica neue" or "roboto" or "noto sans" or "liberation sans"){id=weight>=600?2:1;break;}
        }
        if(webFonts.TryGetValue(id,out var face))
        {if(face.SyntheticBold!=(id==0&&weight>=600))face=face with{SyntheticBold=id==0&&weight>=600};if(webFontRequests.Count<256)webFontRequests[(family,weight)]=face;return face;}
        string resource=id==20?"BootstrapIcons":id==0?"CascadiaMono":id==2?"LatoBold":"LatoRegular";
        byte[] bytes=ResourceBytes("XFESystem.Kernel."+resource+".ttf");
        face=new(id,id==20?"bootstrap-icons 1.11.3 (MIT full glyf font)":id==0?"Cascadia Mono Regular (OFL fallback)":id==2?"Lato Bold (OFL fallback)":"Lato Regular (OFL fallback)",new TrueTypeFont(bytes),new(bytes),id==0&&weight>=600);
        webFonts[id]=face;if(webFontRequests.Count<256)webFontRequests[(family,weight)]=face;return face;
    }
    private WebFontFace WebCjkFace(bool bold)
    {
        int id=bold?11:10;
        if(webFonts.TryGetValue(id,out var face))return face;
        byte[] bytes=ResourceBytes("XFESystem.Kernel.WebCjk"+(bold?"Bold":"Regular")+".ttf");
        face=new(id,"XFE Web CJK "+(bold?"Bold":"Regular")+" (Noto Sans SC 2.004 OFL glyf subset)",new TrueTypeFont(bytes),new(bytes),false);
        webFonts[id]=face;return face;
    }
    private WebFontFace ResolveWebGlyphFace(WebFontFace primary,char ch)
    {
        if(ch<128||primary.Font.HasGlyph(ch))return primary;
        var key=(primary.Id,ch,primary.SyntheticBold);
        if(webFallbackFaces.TryGetValue(key,out var found))return found;
        var candidate=WebCjkFace(primary.Id==2||primary.SyntheticBold);
        var resolved=candidate.Font.HasGlyph(ch)?candidate:primary;
        if(webFallbackFaces.Count<8192)webFallbackFaces[key]=resolved;return resolved;
    }
    private double WebAdvance(WebFontFace face,char ch,double cssSize)
    {
        if(char.IsControl(ch))return ch=='\t'?WebAdvance(face,' ',cssSize)*4:0;
        var selected=ResolveWebGlyphFace(face,ch);
        return selected.Font.HasGlyph(ch)?selected.Font.GetAdvance(ch,(int)Math.Clamp(Math.Round(cssSize*64*selected.Metrics.LineToEm),1,65535))/64d:unicode[GlyphOffset(ch)]*cssSize/16d;
    }
    private static double WebKerning(WebFontFace face,char left,char right,double cssSize)=>face.Font.GetKerning(left,right,(int)Math.Clamp(Math.Round(cssSize*64*face.Metrics.LineToEm),1,65535))/64d;
    private WebGlyphCoverage WebGlyph(char ch, int nativeSize, WebFontFace face)
    {
        var key = (ch, nativeSize, face.Id,face.SyntheticBold);
        if (webGlyphs.TryGetValue(key, out var value)) return value;
        var selected=ResolveWebGlyphFace(face,ch);
        int padding=Math.Max(1,nativeSize/4);
        int width = Math.Max(1, (int)Math.Ceiling(WebAdvance(face,ch,nativeSize)))+padding*2, height = Math.Max(1, (int)Math.Ceiling(face.Metrics.NaturalLineToEm*nativeSize));
        var alpha = new byte[width * height];
        if (selected.Font.HasGlyph(ch))
        {
            int rasterHeight=Math.Max(1,(int)Math.Round(nativeSize*selected.Metrics.LineToEm));
            int baselineOffset=selected.Id==face.Id?0:(int)Math.Round(face.Metrics.AscentToEm*nativeSize)-selected.Font.GetAscent(rasterHeight);
            var tile = new Canvas(width, height); tile.Clear(0); tile.DrawString(ch.ToString(), selected.Font, rasterHeight,Color.White, padding, baselineOffset);
            var source = tile.GetBuffer()!;
            for (int i = 0; i < source.Length; i++) alpha[i] = (byte)((uint)source[i] >> 24);
        }
        else
        {
            int offset=GlyphOffset(ch),glyphWidth=Math.Max(1,(int)Math.Ceiling(unicode[offset]*nativeSize/16d)),top=Math.Max(0,(int)Math.Round((face.Metrics.AscentToEm-.85)*nativeSize));
            for(int row=0;row<nativeSize&&row+top<height;row++)for(int col=0;col<glyphWidth;col++)
            {int sourceRow=Math.Min(15,row*16/nativeSize),sourceCol=Math.Min(unicode[offset]-1,col*unicode[offset]/glyphWidth);int bits=unicode[offset+1+sourceRow*2]<<8|unicode[offset+2+sourceRow*2];if((bits&(0x8000>>sourceCol))!=0)alpha[(row+top)*width+padding+col]=255;}
        }
        if (face.SyntheticBold&&selected.Id==face.Id) for (int row = 0; row < height; row++) for (int col = width - 1; col > 0; col--) alpha[row * width + col] = Math.Max(alpha[row * width + col], alpha[row * width + col - 1]);
        value = new(width, height,-padding,alpha); if (webGlyphs.Count < 4096) webGlyphs[key] = value; return value;
    }
    public void PreserveTranslatedSceneRegion(DesktopBounds source, int destinationX, int destinationY)
        => PreserveTranslatedSceneRegion(source, destinationX, destinationY, false);
    public void PreserveTranslatedSceneRegion(DesktopBounds source, int destinationX, int destinationY, bool unchangedInScanout)
    {
        motion.Preserve(new(source.X * scale, source.Y * scale, source.Width * scale, source.Height * scale), destinationX * scale, destinationY * scale, unchangedInScanout);
        unchangedScanout = unchangedInScanout ? motion.PreservedBounds : default;
    }
    public void EndScenePreservation() => motion.End();
    public void Fill(uint rgb, int x, int y, int width, int height)
    {
        var rectangle = Intersect(NativeRect(new(x, y, width, height)), clip);
        int argb = unchecked((int)(0xFF000000u | rgb));
        for (int row = rectangle.Y; row < rectangle.Y + rectangle.Height; row++)
        {
            motion.RowSegments(rectangle.X, rectangle.Width, row, out int leftEnd, out int rightStart);
            Array.Fill(pixels, argb, row * NativeWidth + rectangle.X, leftEnd - rectangle.X);
            Array.Fill(pixels, argb, row * NativeWidth + rightStart, rectangle.X + rectangle.Width - rightStart);
        }
    }

    public void BlendCoverage(uint rgb, int nativeX, int nativeY, int width, int height, ReadOnlySpan<byte> coverage)
    {
        if (width <= 0 || height <= 0) return;
        if ((long)width * height != coverage.Length) throw new ArgumentException("Coverage dimensions do not match the mask.", nameof(coverage));
        int left = (int)Math.Max(nativeX, (long)clip.X), top = (int)Math.Max(nativeY, (long)clip.Y);
        long right = Math.Min((long)nativeX + width, (long)clip.X + clip.Width);
        long bottom = Math.Min((long)nativeY + height, (long)clip.Y + clip.Height);
        if (right <= left || bottom <= top) return;
        if (motion.Contains(new(left, top, (int)right - left, (int)bottom - top))) return;
        for (int row = top; row < bottom; row++)
        {
            motion.RowSegments(left, (int)right - left, row, out int leftEnd, out int rightStart);
            for (int col = left; col < right; col = col + 1 == leftEnd ? rightStart : col + 1)
            {
                if (col == leftEnd && col < rightStart) { col = rightStart; if (col >= right) break; }
                byte alpha = coverage[(row - nativeY) * width + col - nativeX];
                if (alpha != 0) Blend(row * NativeWidth + col, rgb & 0xFFFFFF, alpha);
            }
        }
    }

    public void Line(uint rgb, int x1, int y1, int x2, int y2) => DesktopChromeRenderer.Line(this, rgb, x1, y1, x2, y2);

    public void Text(string text, uint rgb, int x, int y)
    {
        int left = x * scale, top = y * scale;
        if (top >= clip.Y + clip.Height || top + 20 * scale <= clip.Y) return;
        foreach (char character in text)
        {
            int width = (character < 128 ? 8 : unicode[GlyphOffset(character)]) * scale;
            if (left >= clip.X + clip.Width) break;
            if (left + width > clip.X && !motion.Contains(new(left, top, width, 20 * scale)))
            {
                var glyph = GlyphOf(character);
                var visible = Intersect(new(left, top, glyph.Width, glyph.Height), clip);
                for (int row = visible.Y; row < visible.Y + visible.Height; row++)
                {
                    motion.RowSegments(visible.X, visible.Width, row, out int leftEnd, out int rightStart);
                    int right = visible.X + visible.Width;
                    for (int col = visible.X; col < right; col = col + 1 == leftEnd ? rightStart : col + 1)
                    {
                        if (col == leftEnd && col < rightStart) { col = rightStart; if (col >= right) break; }
                        byte alpha = glyph.Alpha[(row - top) * glyph.Width + col - left];
                        if (alpha != 0) Blend(row * NativeWidth + col, rgb, alpha);
                    }
                }
            }
            left += width;
        }
    }

    private Glyph GlyphOf(char character)
    {
        if (glyphs.TryGetValue(character, out var cached)) return cached;
        int width = (character < 128 ? 8 : unicode[GlyphOffset(character)]) * scale, height = 20 * scale;
        var alpha = new byte[width * height];
        if (character < 128)
        {
            var tile = new Canvas(width, height);
            tile.Clear(0);
            tile.DrawString(character.ToString(), font, Color.White, 0, 0);
            int[] source = tile.GetBuffer()!;
            for (int i = 0; i < source.Length; i++) alpha[i] = (byte)((uint)source[i] >> 24);
        }
        else
        {
            int offset = GlyphOffset(character), logicalWidth = width / scale;
            for (int row = 0; row < 16; row++)
            {
                int bits = unicode[offset + 1 + row * 2] << 8 | unicode[offset + 2 + row * 2];
                for (int col = 0; col < logicalWidth; col++)
                    if ((bits & (0x8000 >> col)) != 0)
                        for (int sy = 0; sy < scale; sy++)
                            Array.Fill(alpha, (byte)255, (row * scale + sy) * width + col * scale, scale);
            }
        }
        var glyph = new Glyph(width, height, alpha);
        if (glyphs.Count < 4096) glyphs.Add(character, glyph);
        return glyph;
    }

    public void DrawIcon(DesktopIcon icon, int x, int y, int size)
    {
        if (icon == DesktopIcon.Settings)
        {
            if (size <= 0 || size > 256) return;
            // Code-native sliders are distinct from the nine generated atlas
            // entries. In particular Settings=9 must never index past atlas.
            int margin = Math.Max(2, size / 5), knob = Math.Max(3, size / 4);
            int radius = Math.Max(1, knob / 2);
            for (int row = 0; row < 3; row++)
            {
                int cy = y + margin + row * Math.Max(1, (size - 2 * margin) / 2);
                int cx = x + (row == 1 ? size * 2 / 3 : size / 3);
                DesktopChromeRenderer.Line(this, 0x4086B8, x + margin, cy, x + size - margin, cy);
                DesktopChromeRenderer.RoundedFill(this, 0x4086B8, cx - radius, cy - radius, knob, knob, radius);
                if (knob >= 5)
                    DesktopChromeRenderer.RoundedFill(this, 0x75C9F5, cx - radius + 1, cy - radius + 1, knob - 2, knob - 2, Math.Max(1, radius - 1));
            }
            return;
        }
        if ((uint)icon >= 9) throw new ArgumentOutOfRangeException(nameof(icon));
        DrawRaster((int)icon, atlas, (int)icon * 64 * 64 * 4, x, y, size);
    }
    public void BrandIcon(int x, int y, int size) => DrawRaster(10, brand, 0, x, y, size);
    private void DrawRaster(int icon, byte[] source, int offset, int x, int y, int size)
    {
        int edge = size * scale;
        if (edge <= 0 || edge > 512) return;
        int left = x * scale, top = y * scale;
        var visible = Intersect(new(left, top, edge, edge), clip);
        if (visible.Width <= 0 || visible.Height <= 0) return;
        if (motion.Contains(new(visible.X, visible.Y, visible.Width, visible.Height))) return;
        if (!iconCache.TryGetValue((icon, edge), out var scaled))
        {
            scaled = new int[edge * edge];
            for (int row = 0; row < edge; row++)
                for (int col = 0; col < edge; col++)
                {
                    int at = offset + ((row * 64 / edge) * 64 + col * 64 / edge) * 4;
                    scaled[row * edge + col] = source[at + 3] << 24 | source[at] << 16 | source[at + 1] << 8 | source[at + 2];
                }
            if (iconCache.Count < 64) iconCache.Add((icon, edge), scaled);
        }
        for (int row = visible.Y; row < visible.Y + visible.Height; row++)
        {
            motion.RowSegments(visible.X, visible.Width, row, out int leftEnd, out int rightStart);
            int right = visible.X + visible.Width;
            for (int col = visible.X; col < right; col = col + 1 == leftEnd ? rightStart : col + 1)
            {
                if (col == leftEnd && col < rightStart) { col = rightStart; if (col >= right) break; }
                uint argb = (uint)scaled[(row - top) * edge + col - left];
                if (argb >> 24 != 0) Blend(row * NativeWidth + col, argb & 0xFFFFFF, (byte)(argb >> 24));
            }
        }
    }

    private void Blend(int index, uint rgb, byte alpha)
    {
        if (alpha == 255) { pixels[index] = unchecked((int)(0xFF000000 | rgb)); return; }
        uint old = (uint)pixels[index];
        uint r = ((rgb >> 16 & 255) * alpha + (old >> 16 & 255) * (255u - alpha) + 127) / 255;
        uint g = ((rgb >> 8 & 255) * alpha + (old >> 8 & 255) * (255u - alpha) + 127) / 255;
        uint b = ((rgb & 255) * alpha + (old & 255) * (255u - alpha) + 127) / 255;
        pixels[index] = unchecked((int)(0xFF000000 | r << 16 | g << 8 | b));
    }

    public void SaveScene() { motion.End(); pixels.AsSpan().CopyTo(scene); }
    public void SaveScene(DesktopBounds damage) { motion.End(); CopyRegion(pixels, scene, NativeRect(damage)); }
    public void RestoreSceneRegion(DesktopBounds bounds) => CopyRegion(scene, pixels, NativeRect(bounds));
    private void CopyRegion(int[] source, int[] target, PixelRect rectangle)
    {
        for (int row = rectangle.Y; row < rectangle.Y + rectangle.Height; row++)
            source.AsSpan(row * NativeWidth + rectangle.X, rectangle.Width).CopyTo(target.AsSpan(row * NativeWidth + rectangle.X, rectangle.Width));
    }
    public void Present() => Present(new(0, 0, Width, Height));
    public void Present(DesktopBounds damage)
    {
        long begin = Stopwatch.GetTimestamp();
        var rectangle = NativeRect(damage);
        var unchanged = unchangedScanout; unchangedScanout = default;
        if (rectangle.Width <= 0 || rectangle.Height <= 0) return;
        if (device?.IsWithdrawn == true) return;
        if (scanout is not null && scanout.Framebuffer is { } framebuffer && scanout.Mode is var mode &&
            mode.Width == NativeWidth && mode.Height == NativeHeight && mode.BitsPerPixel == 32 && mode.Pitch % 4 == 0 && mode.Pitch >= NativeWidth * 4 &&
            framebuffer.Length >= (ulong)mode.Pitch * (ulong)NativeHeight)
        {
            Span<int> target = framebuffer.As<int>();
            int stride = mode.Pitch / 4;
            long copied = 0;
            for (int row = rectangle.Y; row < rectangle.Y + rectangle.Height; row++)
            {
                DesktopSceneMotionBuffer.RowSegments(unchanged, rectangle.X, rectangle.Width, row, out int leftEnd, out int rightStart);
                int leftCount = leftEnd - rectangle.X, rightCount = rectangle.X + rectangle.Width - rightStart;
                pixels.AsSpan(row * NativeWidth + rectangle.X, leftCount).CopyTo(target.Slice(row * stride + rectangle.X, leftCount));
                pixels.AsSpan(row * NativeWidth + rightStart, rightCount).CopyTo(target.Slice(row * stride + rightStart, rightCount));
                copied += leftCount + rightCount;
            }
            scanout.Flush(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
            PixelsPresented += copied;
            ScanoutPixelsSaved += (long)rectangle.Width * rectangle.Height - copied;
            if (rectangle.Width < NativeWidth || rectangle.Height < NativeHeight) PartialFrames++;
        }
        else
        {
            canvas.Display();
            PixelsPresented += (long)NativeWidth * NativeHeight;
            FullCopyFallbackFrames++;
        }
        FramesPresented++;
        LastPresentTicks = Stopwatch.GetTimestamp() - begin;
    }
    private PixelRect NativeRect(DesktopBounds value)
    {
        int x = Math.Clamp(value.X * scale, 0, NativeWidth), y = Math.Clamp(value.Y * scale, 0, NativeHeight);
        int right = Math.Clamp((value.X + Math.Max(0, value.Width)) * scale, x, NativeWidth);
        int bottom = Math.Clamp((value.Y + Math.Max(0, value.Height)) * scale, y, NativeHeight);
        return new(x, y, right - x, bottom - y);
    }
    private static PixelRect Intersect(PixelRect a, PixelRect b)
    {
        int x = Math.Max(a.X, b.X), y = Math.Max(a.Y, b.Y);
        return new(x, y, Math.Max(0, Math.Min(a.X + a.Width, b.X + b.Width) - x), Math.Max(0, Math.Min(a.Y + a.Height, b.Y + b.Height) - y));
    }
    private static byte[] ResourceBytes(string name)
    {
        using var resource = typeof(BaselineCanvasAdapter).Assembly.GetManifestResourceStream(name)
            ?? throw new InvalidOperationException("An embedded desktop asset is missing: " + name);
        var data = new byte[resource.Length]; resource.ReadExactly(data); return data;
    }
    private int GlyphOffset(int character)
    {
        foreach (var range in GlyphRanges)
            if (character >= range.First && character <= range.Last) return range.Offset + (character - range.First) * 33;
        return unicode.Length - 33;
    }
}