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

XFESystem

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

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

/// <summary>
/// Copies unchanged scene pixels without including the software pointer. While
/// composing the rest of the frame, row segments exclude this opaque rectangle.
/// All coordinates here are native pixels, independent of layout scaling.
/// </summary>
public sealed class DesktopSceneMotionBuffer
{
    private readonly int[] _pixels, _scene;
    private readonly int _width, _height;
    public DesktopBounds PreservedBounds { get; private set; }
    public long PixelsReused { get; private set; }
    public long ReuseCount { get; private set; }

    public DesktopSceneMotionBuffer(int[] pixels, int[] scene, int width, int height)
    {
        if (width <= 0 || height <= 0 || (long)width * height > pixels.Length || (long)width * height > scene.Length)
            throw new ArgumentOutOfRangeException(nameof(width));
        if (ReferenceEquals(pixels, scene)) throw new ArgumentException("The pointer-free scene must have a separate buffer.", nameof(scene));
        _pixels = pixels; _scene = scene; _width = width; _height = height;
    }

    public void End() => PreservedBounds = default;

    public void Preserve(DesktopBounds source, int destinationX, int destinationY, bool pixelsAlreadyMatch = false)
    {
        End();
        if (pixelsAlreadyMatch && (source.X != destinationX || source.Y != destinationY))
            throw new ArgumentException("Only an unchanged region at the same position can skip the pixel copy.");
        long left = Math.Max(0, Math.Max(-(long)source.X, -(long)destinationX));
        long top = Math.Max(0, Math.Max(-(long)source.Y, -(long)destinationY));
        long right = Math.Min(Math.Max(0, source.Width), Math.Min((long)_width - source.X, (long)_width - destinationX));
        long bottom = Math.Min(Math.Max(0, source.Height), Math.Min((long)_height - source.Y, (long)_height - destinationY));
        if (right <= left || bottom <= top) return;
        int width = (int)(right - left), height = (int)(bottom - top);
        int sourceX = (int)(source.X + left), sourceY = (int)(source.Y + top);
        int targetX = (int)(destinationX + left), targetY = (int)(destinationY + top);
        if (!pixelsAlreadyMatch)
            for (int row = 0; row < height; row++)
                _scene.AsSpan((sourceY + row) * _width + sourceX, width)
                    .CopyTo(_pixels.AsSpan((targetY + row) * _width + targetX, width));
        PreservedBounds = new(targetX, targetY, width, height);
        PixelsReused += (long)width * height;
        ReuseCount++;
    }

    public bool Contains(DesktopBounds rectangle)
    {
        var p = PreservedBounds;
        return p.Width > 0 && p.Height > 0 && rectangle.X >= p.X && rectangle.Y >= p.Y &&
            (long)rectangle.X + rectangle.Width <= (long)p.X + p.Width &&
            (long)rectangle.Y + rectangle.Height <= (long)p.Y + p.Height;
    }

    /// <summary>Visible columns are [x,leftEnd) and [rightStart,x+width), without allocating a region list.</summary>
    public void RowSegments(int x, int width, int row, out int leftEnd, out int rightStart)
        => RowSegments(PreservedBounds, x, width, row, out leftEnd, out rightStart);

    public static void RowSegments(DesktopBounds excluded, int x, int width, int row, out int leftEnd, out int rightStart)
    {
        int right = x + width;
        var p = excluded;
        if (p.Width == 0 || row < p.Y || row >= p.Y + p.Height || right <= p.X || x >= p.X + p.Width)
        { leftEnd = right; rightStart = right; return; }
        leftEnd = Math.Clamp(p.X, x, right);
        rightStart = Math.Clamp(p.X + p.Width, x, right);
    }
}