using System.Globalization; namespace XFESystem.Desktop; /// Optional readback of the real presented framebuffer, in native pixels. public interface IDesktopPresentedPixelsCanvas { long FramesPresented { get; } long LastPresentTicks => 0; bool TryFingerprintPresentedPixels(DesktopBounds nativeBounds, out DesktopPresentedPixelsFingerprint result); } public readonly record struct DesktopPresentedPixelsFingerprint(DesktopBounds NativeBounds, long PresentationSequence, string Fingerprint); /// Bounded diagnostic fingerprint; never a substitute for GPU or screenshot evidence. public static class DesktopPresentedPixels { public const int MaximumFingerprintPixels = 262_144; public const string FingerprintAlgorithm = "RGB24-FNV1a64"; public static bool TryFingerprint(ReadOnlySpan framebuffer, int width, int height, int stride, DesktopBounds bounds, long presentationSequence, out DesktopPresentedPixelsFingerprint result) { result = default; if (presentationSequence <= 0 || width <= 0 || height <= 0 || stride < width || bounds.X < 0 || bounds.Y < 0 || bounds.Width <= 0 || bounds.Height <= 0 || (long)bounds.X + bounds.Width > width || (long)bounds.Y + bounds.Height > height || (long)bounds.Width * bounds.Height > MaximumFingerprintPixels || (long)(height - 1) * stride + width > framebuffer.Length) return false; ulong hash = 14695981039346656037; for (int y = bounds.Y; y < bounds.Y + bounds.Height; y++) for (int x = bounds.X; x < bounds.X + bounds.Width; x++) { uint pixel = unchecked((uint)framebuffer[y * stride + x]); hash = unchecked((hash ^ (byte)(pixel >> 16)) * 1099511628211); hash = unchecked((hash ^ (byte)(pixel >> 8)) * 1099511628211); hash = unchecked((hash ^ (byte)pixel) * 1099511628211); } result = new(bounds, presentationSequence, hash.ToString("x16", CultureInfo.InvariantCulture)); return true; } }