using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace XFESystem.Browser; internal static class BrowserImageDecoder { private static readonly DecoderOptions options = new() { Configuration = new Configuration(new JpegConfigurationModule(), new WebpConfigurationModule(), new GifConfigurationModule()) { MaxDegreeOfParallelism = 1, MemoryAllocator = MemoryAllocator.Create(new MemoryAllocatorOptions { AllocationLimitMegabytes = 128, MaximumPoolSizeMegabytes = 0 }) }, SkipMetadata = true, MaxFrames = 1 }; internal static BrowserCanvasSurface Decode(byte[] bytes, int maximumPixels) { if (BrowserPngDecoder.HasSignature(bytes)) return BrowserPngDecoder.Decode(bytes, maximumPixels); if (System.Text.Encoding.UTF8.GetString(bytes.AsSpan(0, Math.Min(256, bytes.Length))).TrimStart('\uFEFF', ' ', '\r', '\n', '\t').StartsWith('<')) return BrowserSvgRasterizer.Decode(bytes, maximumPixels); using var stream = new MemoryStream(bytes, false); var info = Image.Identify(options, stream); if (info.Width <= 0 || info.Height <= 0 || (long)info.Width * info.Height > maximumPixels) throw new BrowserLimitException("The image dimensions exceed the decoded pixel budget."); stream.Position = 0; using var image = Image.Load(options, stream); if (image.Width != info.Width || image.Height != info.Height) throw new IOException("The decoded image dimensions differ from its header."); var surface = new BrowserCanvasSurface(image.Width, image.Height); image.ProcessPixelRows(accessor => { for (int y = 0; y < accessor.Height; y++) { var row = accessor.GetRowSpan(y); for (int x = 0; x < row.Length; x++) { var pixel = row[x]; surface.PutPixel(x, y, (uint)(pixel.A << 24 | pixel.R << 16 | pixel.G << 8 | pixel.B)); } } }); surface.Commit(); return surface; } }