// Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using ImageMagick; using ImageMagick.Formats; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { public class MagickReferenceDecoder : IImageDecoder { private readonly bool validate; public MagickReferenceDecoder() : this(true) { } public MagickReferenceDecoder(bool validate) { this.validate = validate; } public static MagickReferenceDecoder Instance { get; } = new MagickReferenceDecoder(); private static void FromRgba32Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel { foreach (Memory m in destinationGroup) { Span destBuffer = m.Span; PixelOperations.Instance.FromRgba32Bytes( configuration, rgbaBytes, destBuffer, destBuffer.Length); rgbaBytes = rgbaBytes.Slice(destBuffer.Length * 4); } } private static void FromRgba64Bytes(Configuration configuration, Span rgbaBytes, IMemoryGroup destinationGroup) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel { foreach (Memory m in destinationGroup) { Span destBuffer = m.Span; PixelOperations.Instance.FromRgba64Bytes( configuration, rgbaBytes, destBuffer, destBuffer.Length); rgbaBytes = rgbaBytes.Slice(destBuffer.Length * 8); } } public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel => Task.FromResult(this.Decode(configuration, stream)); public Image Decode(Configuration configuration, Stream stream) where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel { var bmpReadDefines = new BmpReadDefines { IgnoreFileSize = !this.validate }; var settings = new MagickReadSettings(); settings.SetDefines(bmpReadDefines); using var magickImageCollection = new MagickImageCollection(stream, settings); var framesList = new List>(); foreach (IMagickImage magicFrame in magickImageCollection) { var frame = new ImageFrame(configuration, magicFrame.Width, magicFrame.Height); framesList.Add(frame); MemoryGroup framePixels = frame.PixelBuffer.FastMemoryGroup; using IUnsafePixelCollection pixels = magicFrame.GetPixelsUnsafe(); if (magicFrame.Depth == 8 || magicFrame.Depth == 4 || magicFrame.Depth == 2 || magicFrame.Depth == 1) { byte[] data = pixels.ToByteArray(PixelMapping.RGBA); FromRgba32Bytes(configuration, data, framePixels); } else if (magicFrame.Depth == 16) { ushort[] data = pixels.ToShortArray(PixelMapping.RGBA); Span bytes = MemoryMarshal.Cast(data.AsSpan()); FromRgba64Bytes(configuration, bytes, framePixels); } else { throw new InvalidOperationException(); } } return new Image(configuration, new ImageMetadata(), framesList); } public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); public async Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) => await this.DecodeAsync(configuration, stream, cancellationToken); } }