using System; using System.Buffers; using System.Buffers.Binary; using System.Buffers.Text; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Numerics; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.CodeAnalysis; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Companding; using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Diagnostics; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters; using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors; using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Writers; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Webp.BitReader; using SixLabors.ImageSharp.Formats.Webp.BitWriter; using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory.Internals; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.PixelFormats.Utils; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Binarization; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Processing.Processors.Effects; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.ImageSharp.Processing.Processors.Normalization; using SixLabors.ImageSharp.Processing.Processors.Overlays; using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Tuples; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v3.1", FrameworkDisplayName = ".NET Core 3.1")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] [assembly: InternalsVisibleTo("ImageSharp.Benchmarks, PublicKey=00240000048000009400000006020000002400005253413100040000010001000147e6fe6766715eec6cfed61f1e7dcdbf69748a3e355c67e9d8dfd953acab1d5e012ba34b23308166fdc61ee1d0390d5f36d814a6091dd4b5ed9eda5a26afced924c683b4bfb4b3d64b0586a57eff9f02b1f84e3cb0ddd518bd1697f2c84dcbb97eb8bb5c7801be12112ed0ec86db934b0e9a5171e6bb1384b6d2f7d54dfa97")] [assembly: InternalsVisibleTo("ImageSharp.Tests.ProfilingSandbox, PublicKey=00240000048000009400000006020000002400005253413100040000010001000147e6fe6766715eec6cfed61f1e7dcdbf69748a3e355c67e9d8dfd953acab1d5e012ba34b23308166fdc61ee1d0390d5f36d814a6091dd4b5ed9eda5a26afced924c683b4bfb4b3d64b0586a57eff9f02b1f84e3cb0ddd518bd1697f2c84dcbb97eb8bb5c7801be12112ed0ec86db934b0e9a5171e6bb1384b6d2f7d54dfa97")] [assembly: InternalsVisibleTo("SixLabors.ImageSharp.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001000147e6fe6766715eec6cfed61f1e7dcdbf69748a3e355c67e9d8dfd953acab1d5e012ba34b23308166fdc61ee1d0390d5f36d814a6091dd4b5ed9eda5a26afced924c683b4bfb4b3d64b0586a57eff9f02b1f84e3cb0ddd518bd1697f2c84dcbb97eb8bb5c7801be12112ed0ec86db934b0e9a5171e6bb1384b6d2f7d54dfa97")] [assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001000147e6fe6766715eec6cfed61f1e7dcdbf69748a3e355c67e9d8dfd953acab1d5e012ba34b23308166fdc61ee1d0390d5f36d814a6091dd4b5ed9eda5a26afced924c683b4bfb4b3d64b0586a57eff9f02b1f84e3cb0ddd518bd1697f2c84dcbb97eb8bb5c7801be12112ed0ec86db934b0e9a5171e6bb1384b6d2f7d54dfa97")] [assembly: AssemblyCompany("Six Labors")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © Six Labors")] [assembly: AssemblyDescription("A new, fully featured, fully managed, cross-platform, 2D graphics API for .NET")] [assembly: AssemblyFileVersion("2.1.9.0")] [assembly: AssemblyInformationalVersion("2.1.9+9816ca45016c5d3859986f3c600e8934bc450a56")] [assembly: AssemblyProduct("SixLabors.ImageSharp")] [assembly: AssemblyTitle("SixLabors.ImageSharp")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/SixLabors/ImageSharp")] [assembly: NeutralResourcesLanguage("en")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.0.0.0")] [assembly: TypeForwardedTo(typeof(ExcludeFromCodeCoverageAttribute))] [assembly: TypeForwardedTo(typeof(HashCode))] [assembly: TypeForwardedTo(typeof(MathF))] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsUnmanagedAttribute : Attribute { } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NativeIntegerAttribute : Attribute { public readonly bool[] TransformFlags; public NativeIntegerAttribute() { TransformFlags = new bool[1] { true }; } public NativeIntegerAttribute(bool[] P_0) { TransformFlags = P_0; } } } namespace SixLabors { [DebuggerStepThrough] internal static class DebugGuard { [Conditional("DEBUG")] public static void IsTrue(bool target, string message) { if (!target) { throw new InvalidOperationException(message); } } [Conditional("DEBUG")] public static void NotDisposed(bool isDisposed, string objectName) { if (isDisposed) { throw new ObjectDisposedException(objectName); } } [Conditional("DEBUG")] public static void MustBeSameSized(ReadOnlySpan target, ReadOnlySpan other, string parameterName) where T : struct { if (target.Length != other.Length) { throw new ArgumentException("Span-s must be the same size!", parameterName); } } [Conditional("DEBUG")] public static void MustBeSizedAtLeast(ReadOnlySpan target, ReadOnlySpan minSpan, string parameterName) where T : struct { if (target.Length < minSpan.Length) { throw new ArgumentException($"Span-s must be at least of length {minSpan.Length}!", parameterName); } } [Conditional("DEBUG")] public static void NotNull(TValue value, string parameterName) where TValue : class { if (value == null) { ThrowArgumentNullException(parameterName); } } [Conditional("DEBUG")] public static void NotNullOrWhiteSpace(string value, string parameterName) { if (value == null) { ThrowArgumentNullException(parameterName); } if (string.IsNullOrWhiteSpace(value)) { ThrowArgumentException("Must not be empty or whitespace.", parameterName); } } [Conditional("DEBUG")] public static void MustBeLessThan(TValue value, TValue max, string parameterName) where TValue : IComparable { if (value.CompareTo(max) >= 0) { ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be less than {max}."); } } [Conditional("DEBUG")] public static void MustBeLessThanOrEqualTo(TValue value, TValue max, string parameterName) where TValue : IComparable { if (value.CompareTo(max) > 0) { ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be less than or equal to {max}."); } } [Conditional("DEBUG")] public static void MustBeGreaterThan(TValue value, TValue min, string parameterName) where TValue : IComparable { if (value.CompareTo(min) <= 0) { ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than {min}."); } } [Conditional("DEBUG")] public static void MustBeGreaterThanOrEqualTo(TValue value, TValue min, string parameterName) where TValue : IComparable { if (value.CompareTo(min) < 0) { ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min}."); } } [Conditional("DEBUG")] public static void MustBeBetweenOrEqualTo(TValue value, TValue min, TValue max, string parameterName) where TValue : IComparable { if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0) { ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min} and less than or equal to {max}."); } } [Conditional("DEBUG")] public static void IsTrue(bool target, string parameterName, string message) { if (!target) { ThrowArgumentException(message, parameterName); } } [Conditional("DEBUG")] public static void IsFalse(bool target, string parameterName, string message) { if (target) { ThrowArgumentException(message, parameterName); } } [Conditional("DEBUG")] public static void MustBeSizedAtLeast(ReadOnlySpan source, int minLength, string parameterName) { if (source.Length < minLength) { ThrowArgumentException($"Span-s must be at least of length {minLength}!", parameterName); } } [Conditional("DEBUG")] public static void MustBeSizedAtLeast(Span source, int minLength, string parameterName) { if (source.Length < minLength) { ThrowArgumentException($"The size must be at least {minLength}.", parameterName); } } [Conditional("DEBUG")] public static void DestinationShouldNotBeTooShort(ReadOnlySpan source, Span destination, string destinationParamName) { if (destination.Length < source.Length) { ThrowArgumentException("Destination span is too short!", destinationParamName); } } [Conditional("DEBUG")] public static void DestinationShouldNotBeTooShort(Span source, Span destination, string destinationParamName) { if (destination.Length < source.Length) { ThrowArgumentException("Destination span is too short!", destinationParamName); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowArgumentException(string message, string parameterName) { throw new ArgumentException(message, parameterName); } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowArgumentOutOfRangeException(string parameterName, string message) { throw new ArgumentOutOfRangeException(parameterName, message); } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowArgumentNullException(string parameterName) { throw new ArgumentNullException(parameterName); } } [DebuggerStepThrough] internal static class Guard { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeValueType(TValue value, string parameterName) { if (!value.GetType().IsValueType) { ThrowHelper.ThrowArgumentException("Type must be a struct.", parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NotNull(TValue value, string parameterName) where TValue : class { if (value == null) { ThrowHelper.ThrowArgumentNullExceptionForNotNull(parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void NotNullOrWhiteSpace(string value, string parameterName) { if (string.IsNullOrWhiteSpace(value)) { ThrowHelper.ThrowArgumentExceptionForNotNullOrWhitespace(value, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(TValue value, TValue max, string parameterName) where TValue : IComparable { if (value.CompareTo(max) >= 0) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(TValue value, TValue max, string parameterName) where TValue : IComparable { if (value.CompareTo(max) > 0) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(TValue value, TValue min, string parameterName) where TValue : IComparable { if (value.CompareTo(min) <= 0) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(TValue value, TValue min, string parameterName) where TValue : IComparable { if (value.CompareTo(min) < 0) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(TValue value, TValue min, TValue max, string parameterName) where TValue : IComparable { if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IsTrue(bool target, string parameterName, string message) { if (!target) { ThrowHelper.ThrowArgumentException(message, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IsFalse(bool target, string parameterName, string message) { if (target) { ThrowHelper.ThrowArgumentException(message, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeSizedAtLeast(ReadOnlySpan source, int minLength, string parameterName) { if (source.Length < minLength) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeSizedAtLeast(minLength, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeSizedAtLeast(Span source, int minLength, string parameterName) { if (source.Length < minLength) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeSizedAtLeast(minLength, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void DestinationShouldNotBeTooShort(ReadOnlySpan source, Span destination, string destinationParamName) { if (destination.Length < source.Length) { ThrowHelper.ThrowArgumentException("Destination span is too short!", destinationParamName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void DestinationShouldNotBeTooShort(Span source, Span destination, string destinationParamName) { if (destination.Length < source.Length) { ThrowHelper.ThrowArgumentException("Destination span is too short!", destinationParamName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(byte value, byte max, string parameterName) { if (value >= max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(byte value, byte max, string parameterName) { if (value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(byte value, byte min, string parameterName) { if (value <= min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(byte value, byte min, string parameterName) { if (value < min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(byte value, byte min, byte max, string parameterName) { if (value < min || value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(sbyte value, sbyte max, string parameterName) { if (value >= max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(sbyte value, sbyte max, string parameterName) { if (value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(sbyte value, sbyte min, string parameterName) { if (value <= min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(sbyte value, sbyte min, string parameterName) { if (value < min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(sbyte value, sbyte min, sbyte max, string parameterName) { if (value < min || value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(short value, short max, string parameterName) { if (value >= max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(short value, short max, string parameterName) { if (value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(short value, short min, string parameterName) { if (value <= min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(short value, short min, string parameterName) { if (value < min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(short value, short min, short max, string parameterName) { if (value < min || value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(ushort value, ushort max, string parameterName) { if (value >= max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(ushort value, ushort max, string parameterName) { if (value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(ushort value, ushort min, string parameterName) { if (value <= min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(ushort value, ushort min, string parameterName) { if (value < min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(ushort value, ushort min, ushort max, string parameterName) { if (value < min || value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(char value, char max, string parameterName) { if (value >= max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(char value, char max, string parameterName) { if (value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(char value, char min, string parameterName) { if (value <= min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(char value, char min, string parameterName) { if (value < min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(char value, char min, char max, string parameterName) { if (value < min || value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(int value, int max, string parameterName) { if (value >= max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(int value, int max, string parameterName) { if (value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(int value, int min, string parameterName) { if (value <= min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(int value, int min, string parameterName) { if (value < min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(int value, int min, int max, string parameterName) { if (value < min || value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(uint value, uint max, string parameterName) { if (value >= max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(uint value, uint max, string parameterName) { if (value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(uint value, uint min, string parameterName) { if (value <= min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(uint value, uint min, string parameterName) { if (value < min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(uint value, uint min, uint max, string parameterName) { if (value < min || value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(float value, float max, string parameterName) { if (!(value < max)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(float value, float max, string parameterName) { if (!(value <= max)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(float value, float min, string parameterName) { if (!(value > min)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(float value, float min, string parameterName) { if (!(value >= min)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(float value, float min, float max, string parameterName) { if (!(value >= min) || !(value <= max)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(long value, long max, string parameterName) { if (value >= max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(long value, long max, string parameterName) { if (value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(long value, long min, string parameterName) { if (value <= min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(long value, long min, string parameterName) { if (value < min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(long value, long min, long max, string parameterName) { if (value < min || value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(ulong value, ulong max, string parameterName) { if (value >= max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(ulong value, ulong max, string parameterName) { if (value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(ulong value, ulong min, string parameterName) { if (value <= min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(ulong value, ulong min, string parameterName) { if (value < min) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(ulong value, ulong min, ulong max, string parameterName) { if (value < min || value > max) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(double value, double max, string parameterName) { if (!(value < max)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(double value, double max, string parameterName) { if (!(value <= max)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(double value, double min, string parameterName) { if (!(value > min)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(double value, double min, string parameterName) { if (!(value >= min)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(double value, double min, double max, string parameterName) { if (!(value >= min) || !(value <= max)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThan(decimal value, decimal max, string parameterName) { if (!(value < max)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThan(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeLessThanOrEqualTo(decimal value, decimal max, string parameterName) { if (!(value <= max)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(value, max, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThan(decimal value, decimal min, string parameterName) { if (!(value > min)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeGreaterThanOrEqualTo(decimal value, decimal min, string parameterName) { if (!(value >= min)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(value, min, parameterName); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MustBeBetweenOrEqualTo(decimal value, decimal min, decimal max, string parameterName) { if (!(value >= min) || !(value <= max)) { ThrowHelper.ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(value, min, max, parameterName); } } } internal static class ThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentNullExceptionForNotNull(string name) { ThrowArgumentNullException(name, "Parameter \"" + name + "\" must be not null."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentExceptionForNotNullOrWhitespace(string value, string name) { if (value == null) { ThrowArgumentNullException(name, "Parameter \"" + name + "\" must be not null."); } else { ThrowArgumentException(name, "Parameter \"" + name + "\" must not be empty or whitespace."); } } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentOutOfRangeExceptionForMustBeLessThan(T value, T max, string name) { ThrowArgumentOutOfRangeException(name, $"Parameter \"{name}\" ({typeof(T)}) must be less than {max}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentOutOfRangeExceptionForMustBeLessThanOrEqualTo(T value, T maximum, string name) { ThrowArgumentOutOfRangeException(name, $"Parameter \"{name}\" ({typeof(T)}) must be less than or equal to {maximum}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentOutOfRangeExceptionForMustBeGreaterThan(T value, T minimum, string name) { ThrowArgumentOutOfRangeException(name, $"Parameter \"{name}\" ({typeof(T)}) must be greater than {minimum}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentOutOfRangeExceptionForMustBeGreaterThanOrEqualTo(T value, T minimum, string name) { ThrowArgumentOutOfRangeException(name, $"Parameter \"{name}\" ({typeof(T)}) must be greater than or equal to {minimum}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentOutOfRangeExceptionForMustBeBetweenOrEqualTo(T value, T minimum, T maximum, string name) { ThrowArgumentOutOfRangeException(name, $"Parameter \"{name}\" ({typeof(T)}) must be between or equal to {minimum} and {maximum}, was {value}"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentOutOfRangeExceptionForMustBeSizedAtLeast(int minLength, string parameterName) { ThrowArgumentException($"Spans must be at least of length {minLength}!", parameterName); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentException(string message, string name) { throw new ArgumentException(message, name); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentNullException(string name, string message) { throw new ArgumentNullException(name, message); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentOutOfRangeException(string name, string message) { throw new ArgumentOutOfRangeException(name, message); } } } namespace SixLabors.ImageSharp { public readonly struct Color : IEquatable { private readonly Rgba64 data; private readonly IPixel boxedHighPrecisionPixel; private static readonly Lazy> NamedColorsLookupLazy = new Lazy>(CreateNamedColorsLookup, isThreadSafe: true); public static readonly Color AliceBlue = FromRgba(240, 248, byte.MaxValue, byte.MaxValue); public static readonly Color AntiqueWhite = FromRgba(250, 235, 215, byte.MaxValue); public static readonly Color Aqua = FromRgba(0, byte.MaxValue, byte.MaxValue, byte.MaxValue); public static readonly Color Aquamarine = FromRgba(127, byte.MaxValue, 212, byte.MaxValue); public static readonly Color Azure = FromRgba(240, byte.MaxValue, byte.MaxValue, byte.MaxValue); public static readonly Color Beige = FromRgba(245, 245, 220, byte.MaxValue); public static readonly Color Bisque = FromRgba(byte.MaxValue, 228, 196, byte.MaxValue); public static readonly Color Black = FromRgba(0, 0, 0, byte.MaxValue); public static readonly Color BlanchedAlmond = FromRgba(byte.MaxValue, 235, 205, byte.MaxValue); public static readonly Color Blue = FromRgba(0, 0, byte.MaxValue, byte.MaxValue); public static readonly Color BlueViolet = FromRgba(138, 43, 226, byte.MaxValue); public static readonly Color Brown = FromRgba(165, 42, 42, byte.MaxValue); public static readonly Color BurlyWood = FromRgba(222, 184, 135, byte.MaxValue); public static readonly Color CadetBlue = FromRgba(95, 158, 160, byte.MaxValue); public static readonly Color Chartreuse = FromRgba(127, byte.MaxValue, 0, byte.MaxValue); public static readonly Color Chocolate = FromRgba(210, 105, 30, byte.MaxValue); public static readonly Color Coral = FromRgba(byte.MaxValue, 127, 80, byte.MaxValue); public static readonly Color CornflowerBlue = FromRgba(100, 149, 237, byte.MaxValue); public static readonly Color Cornsilk = FromRgba(byte.MaxValue, 248, 220, byte.MaxValue); public static readonly Color Crimson = FromRgba(220, 20, 60, byte.MaxValue); public static readonly Color Cyan = Aqua; public static readonly Color DarkBlue = FromRgba(0, 0, 139, byte.MaxValue); public static readonly Color DarkCyan = FromRgba(0, 139, 139, byte.MaxValue); public static readonly Color DarkGoldenrod = FromRgba(184, 134, 11, byte.MaxValue); public static readonly Color DarkGray = FromRgba(169, 169, 169, byte.MaxValue); public static readonly Color DarkGreen = FromRgba(0, 100, 0, byte.MaxValue); public static readonly Color DarkGrey = DarkGray; public static readonly Color DarkKhaki = FromRgba(189, 183, 107, byte.MaxValue); public static readonly Color DarkMagenta = FromRgba(139, 0, 139, byte.MaxValue); public static readonly Color DarkOliveGreen = FromRgba(85, 107, 47, byte.MaxValue); public static readonly Color DarkOrange = FromRgba(byte.MaxValue, 140, 0, byte.MaxValue); public static readonly Color DarkOrchid = FromRgba(153, 50, 204, byte.MaxValue); public static readonly Color DarkRed = FromRgba(139, 0, 0, byte.MaxValue); public static readonly Color DarkSalmon = FromRgba(233, 150, 122, byte.MaxValue); public static readonly Color DarkSeaGreen = FromRgba(143, 188, 143, byte.MaxValue); public static readonly Color DarkSlateBlue = FromRgba(72, 61, 139, byte.MaxValue); public static readonly Color DarkSlateGray = FromRgba(47, 79, 79, byte.MaxValue); public static readonly Color DarkSlateGrey = DarkSlateGray; public static readonly Color DarkTurquoise = FromRgba(0, 206, 209, byte.MaxValue); public static readonly Color DarkViolet = FromRgba(148, 0, 211, byte.MaxValue); public static readonly Color DeepPink = FromRgba(byte.MaxValue, 20, 147, byte.MaxValue); public static readonly Color DeepSkyBlue = FromRgba(0, 191, byte.MaxValue, byte.MaxValue); public static readonly Color DimGray = FromRgba(105, 105, 105, byte.MaxValue); public static readonly Color DimGrey = DimGray; public static readonly Color DodgerBlue = FromRgba(30, 144, byte.MaxValue, byte.MaxValue); public static readonly Color Firebrick = FromRgba(178, 34, 34, byte.MaxValue); public static readonly Color FloralWhite = FromRgba(byte.MaxValue, 250, 240, byte.MaxValue); public static readonly Color ForestGreen = FromRgba(34, 139, 34, byte.MaxValue); public static readonly Color Fuchsia = FromRgba(byte.MaxValue, 0, byte.MaxValue, byte.MaxValue); public static readonly Color Gainsboro = FromRgba(220, 220, 220, byte.MaxValue); public static readonly Color GhostWhite = FromRgba(248, 248, byte.MaxValue, byte.MaxValue); public static readonly Color Gold = FromRgba(byte.MaxValue, 215, 0, byte.MaxValue); public static readonly Color Goldenrod = FromRgba(218, 165, 32, byte.MaxValue); public static readonly Color Gray = FromRgba(128, 128, 128, byte.MaxValue); public static readonly Color Green = FromRgba(0, 128, 0, byte.MaxValue); public static readonly Color GreenYellow = FromRgba(173, byte.MaxValue, 47, byte.MaxValue); public static readonly Color Grey = Gray; public static readonly Color Honeydew = FromRgba(240, byte.MaxValue, 240, byte.MaxValue); public static readonly Color HotPink = FromRgba(byte.MaxValue, 105, 180, byte.MaxValue); public static readonly Color IndianRed = FromRgba(205, 92, 92, byte.MaxValue); public static readonly Color Indigo = FromRgba(75, 0, 130, byte.MaxValue); public static readonly Color Ivory = FromRgba(byte.MaxValue, byte.MaxValue, 240, byte.MaxValue); public static readonly Color Khaki = FromRgba(240, 230, 140, byte.MaxValue); public static readonly Color Lavender = FromRgba(230, 230, 250, byte.MaxValue); public static readonly Color LavenderBlush = FromRgba(byte.MaxValue, 240, 245, byte.MaxValue); public static readonly Color LawnGreen = FromRgba(124, 252, 0, byte.MaxValue); public static readonly Color LemonChiffon = FromRgba(byte.MaxValue, 250, 205, byte.MaxValue); public static readonly Color LightBlue = FromRgba(173, 216, 230, byte.MaxValue); public static readonly Color LightCoral = FromRgba(240, 128, 128, byte.MaxValue); public static readonly Color LightCyan = FromRgba(224, byte.MaxValue, byte.MaxValue, byte.MaxValue); public static readonly Color LightGoldenrodYellow = FromRgba(250, 250, 210, byte.MaxValue); public static readonly Color LightGray = FromRgba(211, 211, 211, byte.MaxValue); public static readonly Color LightGreen = FromRgba(144, 238, 144, byte.MaxValue); public static readonly Color LightGrey = LightGray; public static readonly Color LightPink = FromRgba(byte.MaxValue, 182, 193, byte.MaxValue); public static readonly Color LightSalmon = FromRgba(byte.MaxValue, 160, 122, byte.MaxValue); public static readonly Color LightSeaGreen = FromRgba(32, 178, 170, byte.MaxValue); public static readonly Color LightSkyBlue = FromRgba(135, 206, 250, byte.MaxValue); public static readonly Color LightSlateGray = FromRgba(119, 136, 153, byte.MaxValue); public static readonly Color LightSlateGrey = LightSlateGray; public static readonly Color LightSteelBlue = FromRgba(176, 196, 222, byte.MaxValue); public static readonly Color LightYellow = FromRgba(byte.MaxValue, byte.MaxValue, 224, byte.MaxValue); public static readonly Color Lime = FromRgba(0, byte.MaxValue, 0, byte.MaxValue); public static readonly Color LimeGreen = FromRgba(50, 205, 50, byte.MaxValue); public static readonly Color Linen = FromRgba(250, 240, 230, byte.MaxValue); public static readonly Color Magenta = Fuchsia; public static readonly Color Maroon = FromRgba(128, 0, 0, byte.MaxValue); public static readonly Color MediumAquamarine = FromRgba(102, 205, 170, byte.MaxValue); public static readonly Color MediumBlue = FromRgba(0, 0, 205, byte.MaxValue); public static readonly Color MediumOrchid = FromRgba(186, 85, 211, byte.MaxValue); public static readonly Color MediumPurple = FromRgba(147, 112, 219, byte.MaxValue); public static readonly Color MediumSeaGreen = FromRgba(60, 179, 113, byte.MaxValue); public static readonly Color MediumSlateBlue = FromRgba(123, 104, 238, byte.MaxValue); public static readonly Color MediumSpringGreen = FromRgba(0, 250, 154, byte.MaxValue); public static readonly Color MediumTurquoise = FromRgba(72, 209, 204, byte.MaxValue); public static readonly Color MediumVioletRed = FromRgba(199, 21, 133, byte.MaxValue); public static readonly Color MidnightBlue = FromRgba(25, 25, 112, byte.MaxValue); public static readonly Color MintCream = FromRgba(245, byte.MaxValue, 250, byte.MaxValue); public static readonly Color MistyRose = FromRgba(byte.MaxValue, 228, 225, byte.MaxValue); public static readonly Color Moccasin = FromRgba(byte.MaxValue, 228, 181, byte.MaxValue); public static readonly Color NavajoWhite = FromRgba(byte.MaxValue, 222, 173, byte.MaxValue); public static readonly Color Navy = FromRgba(0, 0, 128, byte.MaxValue); public static readonly Color OldLace = FromRgba(253, 245, 230, byte.MaxValue); public static readonly Color Olive = FromRgba(128, 128, 0, byte.MaxValue); public static readonly Color OliveDrab = FromRgba(107, 142, 35, byte.MaxValue); public static readonly Color Orange = FromRgba(byte.MaxValue, 165, 0, byte.MaxValue); public static readonly Color OrangeRed = FromRgba(byte.MaxValue, 69, 0, byte.MaxValue); public static readonly Color Orchid = FromRgba(218, 112, 214, byte.MaxValue); public static readonly Color PaleGoldenrod = FromRgba(238, 232, 170, byte.MaxValue); public static readonly Color PaleGreen = FromRgba(152, 251, 152, byte.MaxValue); public static readonly Color PaleTurquoise = FromRgba(175, 238, 238, byte.MaxValue); public static readonly Color PaleVioletRed = FromRgba(219, 112, 147, byte.MaxValue); public static readonly Color PapayaWhip = FromRgba(byte.MaxValue, 239, 213, byte.MaxValue); public static readonly Color PeachPuff = FromRgba(byte.MaxValue, 218, 185, byte.MaxValue); public static readonly Color Peru = FromRgba(205, 133, 63, byte.MaxValue); public static readonly Color Pink = FromRgba(byte.MaxValue, 192, 203, byte.MaxValue); public static readonly Color Plum = FromRgba(221, 160, 221, byte.MaxValue); public static readonly Color PowderBlue = FromRgba(176, 224, 230, byte.MaxValue); public static readonly Color Purple = FromRgba(128, 0, 128, byte.MaxValue); public static readonly Color RebeccaPurple = FromRgba(102, 51, 153, byte.MaxValue); public static readonly Color Red = FromRgba(byte.MaxValue, 0, 0, byte.MaxValue); public static readonly Color RosyBrown = FromRgba(188, 143, 143, byte.MaxValue); public static readonly Color RoyalBlue = FromRgba(65, 105, 225, byte.MaxValue); public static readonly Color SaddleBrown = FromRgba(139, 69, 19, byte.MaxValue); public static readonly Color Salmon = FromRgba(250, 128, 114, byte.MaxValue); public static readonly Color SandyBrown = FromRgba(244, 164, 96, byte.MaxValue); public static readonly Color SeaGreen = FromRgba(46, 139, 87, byte.MaxValue); public static readonly Color SeaShell = FromRgba(byte.MaxValue, 245, 238, byte.MaxValue); public static readonly Color Sienna = FromRgba(160, 82, 45, byte.MaxValue); public static readonly Color Silver = FromRgba(192, 192, 192, byte.MaxValue); public static readonly Color SkyBlue = FromRgba(135, 206, 235, byte.MaxValue); public static readonly Color SlateBlue = FromRgba(106, 90, 205, byte.MaxValue); public static readonly Color SlateGray = FromRgba(112, 128, 144, byte.MaxValue); public static readonly Color SlateGrey = SlateGray; public static readonly Color Snow = FromRgba(byte.MaxValue, 250, 250, byte.MaxValue); public static readonly Color SpringGreen = FromRgba(0, byte.MaxValue, 127, byte.MaxValue); public static readonly Color SteelBlue = FromRgba(70, 130, 180, byte.MaxValue); public static readonly Color Tan = FromRgba(210, 180, 140, byte.MaxValue); public static readonly Color Teal = FromRgba(0, 128, 128, byte.MaxValue); public static readonly Color Thistle = FromRgba(216, 191, 216, byte.MaxValue); public static readonly Color Tomato = FromRgba(byte.MaxValue, 99, 71, byte.MaxValue); public static readonly Color Transparent = FromRgba(0, 0, 0, 0); public static readonly Color Turquoise = FromRgba(64, 224, 208, byte.MaxValue); public static readonly Color Violet = FromRgba(238, 130, 238, byte.MaxValue); public static readonly Color Wheat = FromRgba(245, 222, 179, byte.MaxValue); public static readonly Color White = FromRgba(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); public static readonly Color WhiteSmoke = FromRgba(245, 245, 245, byte.MaxValue); public static readonly Color Yellow = FromRgba(byte.MaxValue, byte.MaxValue, 0, byte.MaxValue); public static readonly Color YellowGreen = FromRgba(154, 205, 50, byte.MaxValue); private static readonly Lazy WebSafePaletteLazy = new Lazy(CreateWebSafePalette, isThreadSafe: true); private static readonly Lazy WernerPaletteLazy = new Lazy(CreateWernerPalette, isThreadSafe: true); public static ReadOnlyMemory WebSafePalette => WebSafePaletteLazy.Value; public static ReadOnlyMemory WernerPalette => WernerPaletteLazy.Value; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Rgba64 pixel) { data = pixel; boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Rgb48 pixel) { data = new Rgba64(pixel.R, pixel.G, pixel.B, ushort.MaxValue); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(La32 pixel) { data = new Rgba64(pixel.L, pixel.L, pixel.L, pixel.A); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(L16 pixel) { data = new Rgba64(pixel.PackedValue, pixel.PackedValue, pixel.PackedValue, ushort.MaxValue); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Rgba32 pixel) { data = new Rgba64(pixel); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Argb32 pixel) { data = new Rgba64(pixel); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Bgra32 pixel) { data = new Rgba64(pixel); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Abgr32 pixel) { data = new Rgba64(pixel); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Rgb24 pixel) { data = new Rgba64(pixel); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Bgr24 pixel) { data = new Rgba64(pixel); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); boxedHighPrecisionPixel = new RgbaVector(vector.X, vector.Y, vector.Z, vector.W); data = default(Rgba64); } public static explicit operator Vector4(Color color) { return color.ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Color(Vector4 source) { return new Color(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Rgba32 ToRgba32() { if (boxedHighPrecisionPixel == null) { return data.ToRgba32(); } Rgba32 dest = default(Rgba32); boxedHighPrecisionPixel.ToRgba32(ref dest); return dest; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Bgra32 ToBgra32() { if (boxedHighPrecisionPixel == null) { return data.ToBgra32(); } Bgra32 result = default(Bgra32); result.FromScaledVector4(boxedHighPrecisionPixel.ToScaledVector4()); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Argb32 ToArgb32() { if (boxedHighPrecisionPixel == null) { return data.ToArgb32(); } Argb32 result = default(Argb32); result.FromScaledVector4(boxedHighPrecisionPixel.ToScaledVector4()); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Abgr32 ToAbgr32() { if (boxedHighPrecisionPixel == null) { return data.ToAbgr32(); } Abgr32 result = default(Abgr32); result.FromScaledVector4(boxedHighPrecisionPixel.ToScaledVector4()); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Rgb24 ToRgb24() { if (boxedHighPrecisionPixel == null) { return data.ToRgb24(); } Rgb24 result = default(Rgb24); result.FromScaledVector4(boxedHighPrecisionPixel.ToScaledVector4()); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Bgr24 ToBgr24() { if (boxedHighPrecisionPixel == null) { return data.ToBgr24(); } Bgr24 result = default(Bgr24); result.FromScaledVector4(boxedHighPrecisionPixel.ToScaledVector4()); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Vector4 ToVector4() { if (boxedHighPrecisionPixel == null) { return data.ToScaledVector4(); } return boxedHighPrecisionPixel.ToScaledVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private Color(byte r, byte g, byte b, byte a) { data = new Rgba64(ColorNumerics.UpscaleFrom8BitTo16Bit(r), ColorNumerics.UpscaleFrom8BitTo16Bit(g), ColorNumerics.UpscaleFrom8BitTo16Bit(b), ColorNumerics.UpscaleFrom8BitTo16Bit(a)); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private Color(byte r, byte g, byte b) { data = new Rgba64(ColorNumerics.UpscaleFrom8BitTo16Bit(r), ColorNumerics.UpscaleFrom8BitTo16Bit(g), ColorNumerics.UpscaleFrom8BitTo16Bit(b), ushort.MaxValue); boxedHighPrecisionPixel = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private Color(IPixel pixel) { boxedHighPrecisionPixel = pixel; data = default(Rgba64); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Color left, Color right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Color left, Color right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color FromRgba(byte r, byte g, byte b, byte a) { return new Color(r, g, b, a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color FromRgb(byte r, byte g, byte b) { return new Color(r, g, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color FromPixel(TPixel pixel) where TPixel : unmanaged, IPixel { if (typeof(TPixel) == typeof(Rgba64)) { return new Color((Rgba64)(object)pixel); } if (typeof(TPixel) == typeof(Rgb48)) { return new Color((Rgb48)(object)pixel); } if (typeof(TPixel) == typeof(La32)) { return new Color((La32)(object)pixel); } if (typeof(TPixel) == typeof(L16)) { return new Color((L16)(object)pixel); } if (Unsafe.SizeOf() <= Unsafe.SizeOf()) { Rgba32 dest = default(Rgba32); pixel.ToRgba32(ref dest); return new Color(dest); } return new Color(pixel); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color ParseHex(string hex) { return new Color(Rgba32.ParseHex(hex)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryParseHex(string hex, out Color result) { result = default(Color); if (Rgba32.TryParseHex(hex, out var result2)) { result = new Color(result2); return true; } return false; } public static Color Parse(string input) { Guard.NotNull(input, "input"); if (!TryParse(input, out var result)) { throw new ArgumentException("Input string is not in the correct format.", "input"); } return result; } public static bool TryParse(string input, out Color result) { result = default(Color); if (string.IsNullOrWhiteSpace(input)) { return false; } if (NamedColorsLookupLazy.Value.TryGetValue(input, out result)) { return true; } return TryParseHex(input, out result); } public Color WithAlpha(float alpha) { Vector4 vector = (Vector4)this; vector.W = alpha; return new Color(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToHex() { return data.ToRgba32().ToHex(); } public override string ToString() { return ToHex(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TPixel ToPixel() where TPixel : unmanaged, IPixel { IPixel pixel = boxedHighPrecisionPixel; if (pixel is TPixel) { return (TPixel)pixel; } TPixel result; if (boxedHighPrecisionPixel == null) { result = default(TPixel); result.FromRgba64(data); return result; } result = default(TPixel); result.FromScaledVector4(boxedHighPrecisionPixel.ToScaledVector4()); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToPixel(Configuration configuration, ReadOnlySpan source, Span destination) where TPixel : unmanaged, IPixel { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); for (int i = 0; i < source.Length; i++) { destination[i] = source[i].ToPixel(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Color other) { if (boxedHighPrecisionPixel == null && other.boxedHighPrecisionPixel == null) { return data.PackedValue == other.data.PackedValue; } return boxedHighPrecisionPixel?.Equals(other.boxedHighPrecisionPixel) ?? false; } public override bool Equals(object obj) { if (obj is Color other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { if (boxedHighPrecisionPixel == null) { return data.PackedValue.GetHashCode(); } return boxedHighPrecisionPixel.GetHashCode(); } private static Dictionary CreateNamedColorsLookup() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { { "AliceBlue", AliceBlue }, { "AntiqueWhite", AntiqueWhite }, { "Aqua", Aqua }, { "Aquamarine", Aquamarine }, { "Azure", Azure }, { "Beige", Beige }, { "Bisque", Bisque }, { "Black", Black }, { "BlanchedAlmond", BlanchedAlmond }, { "Blue", Blue }, { "BlueViolet", BlueViolet }, { "Brown", Brown }, { "BurlyWood", BurlyWood }, { "CadetBlue", CadetBlue }, { "Chartreuse", Chartreuse }, { "Chocolate", Chocolate }, { "Coral", Coral }, { "CornflowerBlue", CornflowerBlue }, { "Cornsilk", Cornsilk }, { "Crimson", Crimson }, { "Cyan", Cyan }, { "DarkBlue", DarkBlue }, { "DarkCyan", DarkCyan }, { "DarkGoldenrod", DarkGoldenrod }, { "DarkGray", DarkGray }, { "DarkGreen", DarkGreen }, { "DarkGrey", DarkGrey }, { "DarkKhaki", DarkKhaki }, { "DarkMagenta", DarkMagenta }, { "DarkOliveGreen", DarkOliveGreen }, { "DarkOrange", DarkOrange }, { "DarkOrchid", DarkOrchid }, { "DarkRed", DarkRed }, { "DarkSalmon", DarkSalmon }, { "DarkSeaGreen", DarkSeaGreen }, { "DarkSlateBlue", DarkSlateBlue }, { "DarkSlateGray", DarkSlateGray }, { "DarkSlateGrey", DarkSlateGrey }, { "DarkTurquoise", DarkTurquoise }, { "DarkViolet", DarkViolet }, { "DeepPink", DeepPink }, { "DeepSkyBlue", DeepSkyBlue }, { "DimGray", DimGray }, { "DimGrey", DimGrey }, { "DodgerBlue", DodgerBlue }, { "Firebrick", Firebrick }, { "FloralWhite", FloralWhite }, { "ForestGreen", ForestGreen }, { "Fuchsia", Fuchsia }, { "Gainsboro", Gainsboro }, { "GhostWhite", GhostWhite }, { "Gold", Gold }, { "Goldenrod", Goldenrod }, { "Gray", Gray }, { "Green", Green }, { "GreenYellow", GreenYellow }, { "Grey", Grey }, { "Honeydew", Honeydew }, { "HotPink", HotPink }, { "IndianRed", IndianRed }, { "Indigo", Indigo }, { "Ivory", Ivory }, { "Khaki", Khaki }, { "Lavender", Lavender }, { "LavenderBlush", LavenderBlush }, { "LawnGreen", LawnGreen }, { "LemonChiffon", LemonChiffon }, { "LightBlue", LightBlue }, { "LightCoral", LightCoral }, { "LightCyan", LightCyan }, { "LightGoldenrodYellow", LightGoldenrodYellow }, { "LightGray", LightGray }, { "LightGreen", LightGreen }, { "LightGrey", LightGrey }, { "LightPink", LightPink }, { "LightSalmon", LightSalmon }, { "LightSeaGreen", LightSeaGreen }, { "LightSkyBlue", LightSkyBlue }, { "LightSlateGray", LightSlateGray }, { "LightSlateGrey", LightSlateGrey }, { "LightSteelBlue", LightSteelBlue }, { "LightYellow", LightYellow }, { "Lime", Lime }, { "LimeGreen", LimeGreen }, { "Linen", Linen }, { "Magenta", Magenta }, { "Maroon", Maroon }, { "MediumAquamarine", MediumAquamarine }, { "MediumBlue", MediumBlue }, { "MediumOrchid", MediumOrchid }, { "MediumPurple", MediumPurple }, { "MediumSeaGreen", MediumSeaGreen }, { "MediumSlateBlue", MediumSlateBlue }, { "MediumSpringGreen", MediumSpringGreen }, { "MediumTurquoise", MediumTurquoise }, { "MediumVioletRed", MediumVioletRed }, { "MidnightBlue", MidnightBlue }, { "MintCream", MintCream }, { "MistyRose", MistyRose }, { "Moccasin", Moccasin }, { "NavajoWhite", NavajoWhite }, { "Navy", Navy }, { "OldLace", OldLace }, { "Olive", Olive }, { "OliveDrab", OliveDrab }, { "Orange", Orange }, { "OrangeRed", OrangeRed }, { "Orchid", Orchid }, { "PaleGoldenrod", PaleGoldenrod }, { "PaleGreen", PaleGreen }, { "PaleTurquoise", PaleTurquoise }, { "PaleVioletRed", PaleVioletRed }, { "PapayaWhip", PapayaWhip }, { "PeachPuff", PeachPuff }, { "Peru", Peru }, { "Pink", Pink }, { "Plum", Plum }, { "PowderBlue", PowderBlue }, { "Purple", Purple }, { "RebeccaPurple", RebeccaPurple }, { "Red", Red }, { "RosyBrown", RosyBrown }, { "RoyalBlue", RoyalBlue }, { "SaddleBrown", SaddleBrown }, { "Salmon", Salmon }, { "SandyBrown", SandyBrown }, { "SeaGreen", SeaGreen }, { "SeaShell", SeaShell }, { "Sienna", Sienna }, { "Silver", Silver }, { "SkyBlue", SkyBlue }, { "SlateBlue", SlateBlue }, { "SlateGray", SlateGray }, { "SlateGrey", SlateGrey }, { "Snow", Snow }, { "SpringGreen", SpringGreen }, { "SteelBlue", SteelBlue }, { "Tan", Tan }, { "Teal", Teal }, { "Thistle", Thistle }, { "Tomato", Tomato }, { "Transparent", Transparent }, { "Turquoise", Turquoise }, { "Violet", Violet }, { "Wheat", Wheat }, { "White", White }, { "WhiteSmoke", WhiteSmoke }, { "Yellow", Yellow }, { "YellowGreen", YellowGreen } }; } private static Color[] CreateWebSafePalette() { return new Color[142] { AliceBlue, AntiqueWhite, Aqua, Aquamarine, Azure, Beige, Bisque, Black, BlanchedAlmond, Blue, BlueViolet, Brown, BurlyWood, CadetBlue, Chartreuse, Chocolate, Coral, CornflowerBlue, Cornsilk, Crimson, Cyan, DarkBlue, DarkCyan, DarkGoldenrod, DarkGray, DarkGreen, DarkKhaki, DarkMagenta, DarkOliveGreen, DarkOrange, DarkOrchid, DarkRed, DarkSalmon, DarkSeaGreen, DarkSlateBlue, DarkSlateGray, DarkTurquoise, DarkViolet, DeepPink, DeepSkyBlue, DimGray, DodgerBlue, Firebrick, FloralWhite, ForestGreen, Fuchsia, Gainsboro, GhostWhite, Gold, Goldenrod, Gray, Green, GreenYellow, Honeydew, HotPink, IndianRed, Indigo, Ivory, Khaki, Lavender, LavenderBlush, LawnGreen, LemonChiffon, LightBlue, LightCoral, LightCyan, LightGoldenrodYellow, LightGray, LightGreen, LightPink, LightSalmon, LightSeaGreen, LightSkyBlue, LightSlateGray, LightSteelBlue, LightYellow, Lime, LimeGreen, Linen, Magenta, Maroon, MediumAquamarine, MediumBlue, MediumOrchid, MediumPurple, MediumSeaGreen, MediumSlateBlue, MediumSpringGreen, MediumTurquoise, MediumVioletRed, MidnightBlue, MintCream, MistyRose, Moccasin, NavajoWhite, Navy, OldLace, Olive, OliveDrab, Orange, OrangeRed, Orchid, PaleGoldenrod, PaleGreen, PaleTurquoise, PaleVioletRed, PapayaWhip, PeachPuff, Peru, Pink, Plum, PowderBlue, Purple, RebeccaPurple, Red, RosyBrown, RoyalBlue, SaddleBrown, Salmon, SandyBrown, SeaGreen, SeaShell, Sienna, Silver, SkyBlue, SlateBlue, SlateGray, Snow, SpringGreen, SteelBlue, Tan, Teal, Thistle, Tomato, Transparent, Turquoise, Violet, Wheat, White, WhiteSmoke, Yellow, YellowGreen }; } private static Color[] CreateWernerPalette() { return new Color[110] { ParseHex("#f1e9cd"), ParseHex("#f2e7cf"), ParseHex("#ece6d0"), ParseHex("#f2eacc"), ParseHex("#f3e9ca"), ParseHex("#f2ebcd"), ParseHex("#e6e1c9"), ParseHex("#e2ddc6"), ParseHex("#cbc8b7"), ParseHex("#bfbbb0"), ParseHex("#bebeb3"), ParseHex("#b7b5ac"), ParseHex("#bab191"), ParseHex("#9c9d9a"), ParseHex("#8a8d84"), ParseHex("#5b5c61"), ParseHex("#555152"), ParseHex("#413f44"), ParseHex("#454445"), ParseHex("#423937"), ParseHex("#433635"), ParseHex("#252024"), ParseHex("#241f20"), ParseHex("#281f3f"), ParseHex("#1c1949"), ParseHex("#4f638d"), ParseHex("#383867"), ParseHex("#5c6b8f"), ParseHex("#657abb"), ParseHex("#6f88af"), ParseHex("#7994b5"), ParseHex("#6fb5a8"), ParseHex("#719ba2"), ParseHex("#8aa1a6"), ParseHex("#d0d5d3"), ParseHex("#8590ae"), ParseHex("#3a2f52"), ParseHex("#39334a"), ParseHex("#6c6d94"), ParseHex("#584c77"), ParseHex("#533552"), ParseHex("#463759"), ParseHex("#bfbac0"), ParseHex("#77747f"), ParseHex("#4a475c"), ParseHex("#b8bfaf"), ParseHex("#b2b599"), ParseHex("#979c84"), ParseHex("#5d6161"), ParseHex("#61ac86"), ParseHex("#a4b6a7"), ParseHex("#adba98"), ParseHex("#93b778"), ParseHex("#7d8c55"), ParseHex("#33431e"), ParseHex("#7c8635"), ParseHex("#8e9849"), ParseHex("#c2c190"), ParseHex("#67765b"), ParseHex("#ab924b"), ParseHex("#c8c76f"), ParseHex("#ccc050"), ParseHex("#ebdd99"), ParseHex("#ab9649"), ParseHex("#dbc364"), ParseHex("#e6d058"), ParseHex("#ead665"), ParseHex("#d09b2c"), ParseHex("#a36629"), ParseHex("#a77d35"), ParseHex("#f0d696"), ParseHex("#d7c485"), ParseHex("#f1d28c"), ParseHex("#efcc83"), ParseHex("#f3daa7"), ParseHex("#dfa837"), ParseHex("#ebbc71"), ParseHex("#d17c3f"), ParseHex("#92462f"), ParseHex("#be7249"), ParseHex("#bb603c"), ParseHex("#c76b4a"), ParseHex("#a75536"), ParseHex("#b63e36"), ParseHex("#b5493a"), ParseHex("#cd6d57"), ParseHex("#711518"), ParseHex("#e9c49d"), ParseHex("#eedac3"), ParseHex("#eecfbf"), ParseHex("#ce536b"), ParseHex("#b74a70"), ParseHex("#b7757c"), ParseHex("#612741"), ParseHex("#7a4848"), ParseHex("#3f3033"), ParseHex("#8d746f"), ParseHex("#4d3635"), ParseHex("#6e3b31"), ParseHex("#864735"), ParseHex("#553d3a"), ParseHex("#613936"), ParseHex("#7a4b3a"), ParseHex("#946943"), ParseHex("#c39e6d"), ParseHex("#513e32"), ParseHex("#8b7859"), ParseHex("#9b856b"), ParseHex("#766051"), ParseHex("#453b32") }; } } public enum ByteOrder { BigEndian, LittleEndian } internal static class Constants { public static readonly float Epsilon = 0.001f; public static readonly float EpsilonSquared = Epsilon * Epsilon; } public class ImageFormatException : Exception { internal ImageFormatException(string errorMessage) : base(errorMessage) { } internal ImageFormatException(string errorMessage, Exception innerException) : base(errorMessage, innerException) { } } public sealed class ImageProcessingException : Exception { public ImageProcessingException() { } public ImageProcessingException(string errorMessage) : base(errorMessage) { } public ImageProcessingException(string errorMessage, Exception innerException) : base(errorMessage, innerException) { } } public sealed class InvalidImageContentException : ImageFormatException { public InvalidImageContentException(string errorMessage) : base(errorMessage) { } public InvalidImageContentException(string errorMessage, Exception innerException) : base(errorMessage, innerException) { } internal InvalidImageContentException(Size size, InvalidMemoryOperationException memoryException) : this($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {size.Width}x{size.Height}.", memoryException) { } } public sealed class UnknownImageFormatException : ImageFormatException { public UnknownImageFormatException(string errorMessage) : base(errorMessage) { } } internal static class ConfigurationExtensions { public static ParallelOptions GetParallelOptions(this Configuration configuration) { return new ParallelOptions { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }; } } internal static class EnumerableExtensions { public static IEnumerable SteppedRange(int fromInclusive, Func toDelegate, int step) { return RangeIterator(fromInclusive, toDelegate, step); } private static IEnumerable RangeIterator(int fromInclusive, Func toDelegate, int step) { for (int i = fromInclusive; toDelegate(i); i += step) { yield return i; } } } internal static class StreamExtensions { public static void Write(this Stream stream, Span buffer, int offset, int count) { stream.Write(buffer.Slice(offset, count)); } public static int Read(this Stream stream, Span buffer, int offset, int count) { return stream.Read(buffer.Slice(offset, count)); } public static void Skip(this Stream stream, int count) { if (count < 1) { return; } if (stream.CanSeek) { stream.Seek(count, SeekOrigin.Current); return; } byte[] array = ArrayPool.Shared.Rent(count); try { while (count > 0) { int num = stream.Read(array, 0, count); if (num == 0) { break; } count -= num; } } finally { ArrayPool.Shared.Return(array); } } } internal static class ColorNumerics { private static readonly Vector4 Bt709 = new Vector4(0.2126f, 0.7152f, 0.0722f, 0f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetBT709Luminance(ref Vector4 vector, int luminanceLevels) { return (int)MathF.Round(Vector4.Dot(vector, Bt709) * (float)(luminanceLevels - 1)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Get8BitBT709Luminance(byte r, byte g, byte b) { return (byte)((float)(int)r * 0.2126f + (float)(int)g * 0.7152f + (float)(int)b * 0.0722f + 0.5f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) { return (ushort)((float)(int)r * 0.2126f + (float)(int)g * 0.7152f + (float)(int)b * 0.0722f + 0.5f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort Get16BitBT709Luminance(float r, float g, float b) { return (ushort)(r * 0.2126f + g * 0.7152f + b * 0.0722f + 0.5f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte DownScaleFrom16BitTo8Bit(ushort component) { return (byte)(component * 255 + 32895 >> 16); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort UpscaleFrom8BitTo16Bit(byte component) { return (ushort)(component * 257); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetBitsNeededForColorDepth(int colors) { return Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2.0))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetColorCountForBitDepth(int bitDepth) { return 1 << bitDepth; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Transform(ref Vector4 vector, ref ColorMatrix matrix) { float x = vector.X; float y = vector.Y; float z = vector.Z; float w = vector.W; vector.X = x * matrix.M11 + y * matrix.M21 + z * matrix.M31 + w * matrix.M41 + matrix.M51; vector.Y = x * matrix.M12 + y * matrix.M22 + z * matrix.M32 + w * matrix.M42 + matrix.M52; vector.Z = x * matrix.M13 + y * matrix.M23 + z * matrix.M33 + w * matrix.M43 + matrix.M53; vector.W = x * matrix.M14 + y * matrix.M24 + z * matrix.M34 + w * matrix.M44 + matrix.M54; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Transform(Span vectors, ref ColorMatrix matrix) { ref Vector4 reference = ref MemoryMarshal.GetReference(vectors); for (int i = 0; i < vectors.Length; i++) { Transform(ref Unsafe.Add(ref reference, i), ref matrix); } } } internal static class EnumUtils { public static TEnum Parse(int value, TEnum defaultValue) where TEnum : Enum { foreach (TEnum value2 in Enum.GetValues(typeof(TEnum))) { TEnum source = value2; if (value == Unsafe.As(ref source)) { return value2; } } return defaultValue; } public static bool HasFlag(TEnum value, TEnum flag) where TEnum : Enum { uint num = Unsafe.As(ref flag); return (Unsafe.As(ref value) & num) == num; } } internal static class InliningOptions { public const MethodImplOptions AlwaysInline = MethodImplOptions.AggressiveInlining; public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization; public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining; public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining; } internal static class Numerics { public const int BlendAlphaControl = 136; private const int ShuffleAlphaControl = 255; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GreatestCommonDivisor(int a, int b) { while (b != 0) { int num = b; b = a % b; a = num; } return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LeastCommonMultiple(int a, int b) { return a / GreatestCommonDivisor(a, b) * b; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Modulo2(int x) { return x & 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Modulo4(int x) { return x & 3; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Modulo8(int x) { return x & 7; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ModuloP2(int x, int m) { return x & (m - 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Abs(int x) { int num = x >> 31; return (x ^ num) - num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Pow2(float x) { return x * x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Pow3(float x) { return x * x * x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Gaussian(float x, float sigma) { float num = MathF.Sqrt((float)Math.PI * 2f) * sigma; float num2 = (0f - x) * x; float num3 = 2f * Pow2(sigma); float num4 = 1f / num; float num5 = MathF.Exp(num2 / num3); return num4 * num5; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float SinC(float f) { if (MathF.Abs(f) > Constants.Epsilon) { f *= (float)Math.PI; float num = MathF.Sin(f) / f; if (!(MathF.Abs(num) < Constants.Epsilon)) { return num; } return 0f; } return 1f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Clamp(byte value, byte min, byte max) { if (value > max) { return max; } if (value < min) { return min; } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Clamp(uint value, uint min, uint max) { if (value > max) { return max; } if (value < min) { return min; } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Clamp(int value, int min, int max) { if (value > max) { return max; } if (value < min) { return min; } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Clamp(float value, float min, float max) { if (value > max) { return max; } if (value < min) { return min; } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Clamp(double value, double min, double max) { if (value > max) { return max; } if (value < min) { return min; } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Clamp(Vector4 value, Vector4 min, Vector4 max) { return Vector4.Min(Vector4.Max(value, min), max); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Clamp(Span span, byte min, byte max) { Span span2 = span.Slice(ClampReduce(span, min, max)); if (span2.Length > 0) { ref byte reference = ref MemoryMarshal.GetReference(span2); ref byte right = ref Unsafe.Add(ref reference, span2.Length); while (Unsafe.IsAddressLessThan(in reference, in right)) { reference = Clamp(reference, min, max); reference = ref Unsafe.Add(ref reference, 1); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Clamp(Span span, uint min, uint max) { Span span2 = span.Slice(ClampReduce(span, min, max)); if (span2.Length > 0) { ref uint reference = ref MemoryMarshal.GetReference(span2); ref uint right = ref Unsafe.Add(ref reference, span2.Length); while (Unsafe.IsAddressLessThan(in reference, in right)) { reference = Clamp(reference, min, max); reference = ref Unsafe.Add(ref reference, 1); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Clamp(Span span, int min, int max) { Span span2 = span.Slice(ClampReduce(span, min, max)); if (span2.Length > 0) { ref int reference = ref MemoryMarshal.GetReference(span2); ref int right = ref Unsafe.Add(ref reference, span2.Length); while (Unsafe.IsAddressLessThan(in reference, in right)) { reference = Clamp(reference, min, max); reference = ref Unsafe.Add(ref reference, 1); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Clamp(Span span, float min, float max) { Span span2 = span.Slice(ClampReduce(span, min, max)); if (span2.Length > 0) { ref float reference = ref MemoryMarshal.GetReference(span2); ref float right = ref Unsafe.Add(ref reference, span2.Length); while (Unsafe.IsAddressLessThan(in reference, in right)) { reference = Clamp(reference, min, max); reference = ref Unsafe.Add(ref reference, 1); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Clamp(Span span, double min, double max) { Span span2 = span.Slice(ClampReduce(span, min, max)); if (span2.Length > 0) { ref double reference = ref MemoryMarshal.GetReference(span2); ref double right = ref Unsafe.Add(ref reference, span2.Length); while (Unsafe.IsAddressLessThan(in reference, in right)) { reference = Clamp(reference, min, max); reference = ref Unsafe.Add(ref reference, 1); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int ClampReduce(Span span, T min, T max) where T : unmanaged { if (Vector.IsHardwareAccelerated && span.Length >= Vector.Count) { int num = ModuloP2(span.Length, Vector.Count); int num2 = span.Length - num; if (num2 > 0) { ClampImpl(span.Slice(0, num2), min, max); } return num2; } return 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ClampImpl(Span span, T min, T max) where T : unmanaged { MemoryMarshal.GetReference(span); Vector left = new Vector(min); Vector right = new Vector(max); int num = span.Length / Vector.Count; int num2 = Modulo4(num); int elementOffset = num - num2; ref Vector reference = ref Unsafe.As>(ref MemoryMarshal.GetReference(span)); ref Vector reference2 = ref Unsafe.Add(ref reference, 1); ref Vector reference3 = ref Unsafe.Add(ref reference, 2); ref Vector reference4 = ref Unsafe.Add(ref reference, 3); ref Vector reference5 = ref Unsafe.Add(ref reference, elementOffset); while (Unsafe.IsAddressLessThan(in reference, in reference5)) { reference = Vector.Min(Vector.Max(left, reference), right); reference2 = Vector.Min(Vector.Max(left, reference2), right); reference3 = Vector.Min(Vector.Max(left, reference3), right); reference4 = Vector.Min(Vector.Max(left, reference4), right); reference = ref Unsafe.Add(ref reference, 4); reference2 = ref Unsafe.Add(ref reference2, 4); reference3 = ref Unsafe.Add(ref reference3, 4); reference4 = ref Unsafe.Add(ref reference4, 4); } if (num2 > 0) { reference = ref reference5; reference5 = ref Unsafe.Add(ref reference5, num2); while (Unsafe.IsAddressLessThan(in reference, in reference5)) { reference = Vector.Min(Vector.Max(left, reference), right); reference = ref Unsafe.Add(ref reference, 1); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Premultiply(ref Vector4 source) { float w = source.W; source *= w; source.W = w; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UnPremultiply(ref Vector4 source) { float w = source.W; source /= w; source.W = w; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Premultiply(Span vectors) { if (Avx2.IsSupported && vectors.Length >= 2) { ref Vector256 reference = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); ref Vector256 right = ref Unsafe.Add(ref reference, (IntPtr)((uint)vectors.Length / 2u)); while (Unsafe.IsAddressLessThan(in reference, in right)) { Vector256 vector = reference; Vector256 right2 = Avx.Shuffle(vector, vector, byte.MaxValue); reference = Avx.Blend(Avx.Multiply(vector, right2), vector, 136); reference = ref Unsafe.Add(ref reference, 1); } if (Modulo2(vectors.Length) != 0) { Premultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1))); } } else { ref Vector4 reference2 = ref MemoryMarshal.GetReference(vectors); ref Vector4 right3 = ref Unsafe.Add(ref reference2, vectors.Length); while (Unsafe.IsAddressLessThan(in reference2, in right3)) { Premultiply(ref reference2); reference2 = ref Unsafe.Add(ref reference2, 1); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UnPremultiply(Span vectors) { if (Avx2.IsSupported && vectors.Length >= 2) { ref Vector256 reference = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); ref Vector256 right = ref Unsafe.Add(ref reference, (IntPtr)((uint)vectors.Length / 2u)); while (Unsafe.IsAddressLessThan(in reference, in right)) { Vector256 vector = reference; Vector256 right2 = Avx.Shuffle(vector, vector, byte.MaxValue); reference = Avx.Blend(Avx.Divide(vector, right2), vector, 136); reference = ref Unsafe.Add(ref reference, 1); } if (Modulo2(vectors.Length) != 0) { UnPremultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1))); } } else { ref Vector4 reference2 = ref MemoryMarshal.GetReference(vectors); ref Vector4 right3 = ref Unsafe.Add(ref reference2, vectors.Length); while (Unsafe.IsAddressLessThan(in reference2, in right3)) { UnPremultiply(ref reference2); reference2 = ref Unsafe.Add(ref reference2, 1); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CubePowOnXYZ(Span vectors) { ref Vector4 reference = ref MemoryMarshal.GetReference(vectors); ref Vector4 right = ref Unsafe.Add(ref reference, vectors.Length); while (Unsafe.IsAddressLessThan(in reference, in right)) { Vector4 vector = reference; float w = vector.W; vector = vector * vector * vector; vector.W = w; reference = vector; reference = ref Unsafe.Add(ref reference, 1); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void CubeRootOnXYZ(Span vectors) { if (Sse41.IsSupported) { ref Vector128 reference = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); ref Vector128 right = ref Unsafe.Add(ref reference, vectors.Length); Vector128 right2 = Vector128.Create(341); Vector128 vector = Vector128.Create(-0f).AsInt32(); Vector128 right3 = Vector128.Create(1f).AsInt32(); Vector128 left = Vector128.Create(1f / 3f); Vector128 vector2 = Vector128.Create(2f / 3f); while (Unsafe.IsAddressLessThan(in reference, in right)) { Vector128 vector3 = reference; Vector128 right4 = vector3.AsInt32(); right4 = Sse2.AndNot(vector, right4); right4 = Sse2.Subtract(right4, right3); right4 = Sse2.ShiftRightArithmetic(right4, 10); right4 = Sse41.MultiplyLow(right4, right2); right4 = Sse2.Add(right4, right3); right4 = Sse2.AndNot(vector, right4); right4 = Sse2.Or(right4, Sse2.And(vector3.AsInt32(), vector)); Vector128 vector4 = right4.AsSingle(); if (Fma.IsSupported) { vector4 = Fma.MultiplyAdd(vector2, vector4, Sse.Multiply(left, Sse.Divide(vector3, Sse.Multiply(vector4, vector4)))); vector4 = Fma.MultiplyAdd(vector2, vector4, Sse.Multiply(left, Sse.Divide(vector3, Sse.Multiply(vector4, vector4)))); } else { vector4 = Sse.Add(Sse.Multiply(vector2, vector4), Sse.Multiply(left, Sse.Divide(vector3, Sse.Multiply(vector4, vector4)))); vector4 = Sse.Add(Sse.Multiply(vector2, vector4), Sse.Multiply(left, Sse.Divide(vector3, Sse.Multiply(vector4, vector4)))); } vector4 = Sse41.Insert(vector4, vector3, 240); reference = vector4; reference = ref Unsafe.Add(ref reference, 1); } } else { ref Vector4 reference2 = ref MemoryMarshal.GetReference(vectors); ref Vector4 right5 = ref Unsafe.Add(ref reference2, vectors.Length); Vector4 vector6 = default(Vector4); while (Unsafe.IsAddressLessThan(in reference2, in right5)) { Vector4 vector5 = reference2; double num = vector5.X; double num2 = vector5.Y; double num3 = vector5.Z; float w = vector5.W; ulong num4 = *(ulong*)(&num); ulong num5 = *(ulong*)(&num2); ulong num6 = *(ulong*)(&num3); num4 = 3071325735593686528L + num4 / 3; num5 = 3071325735593686528L + num5 / 3; num6 = 3071325735593686528L + num6 / 3; vector6.X = (float)(*(double*)(&num4)); vector6.Y = (float)(*(double*)(&num5)); vector6.Z = (float)(*(double*)(&num6)); vector6.W = 0f; vector6 = 2f / 3f * vector6 + 1f / 3f * (vector5 / (vector6 * vector6)); vector6 = 2f / 3f * vector6 + 1f / 3f * (vector5 / (vector6 * vector6)); vector6.W = w; reference2 = vector6; reference2 = ref Unsafe.Add(ref reference2, 1); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Lerp(in Vector256 value1, in Vector256 value2, in Vector256 amount) { Vector256 vector = Avx.Subtract(value2, value1); if (Fma.IsSupported) { return Fma.MultiplyAdd(vector, amount, value1); } return Avx.Add(Avx.Multiply(vector, amount), value1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Lerp(float value1, float value2, float amount) { return (value2 - value1) * amount + value1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Accumulate(ref Vector accumulator, Vector values) { Vector.Widen(values, out var low, out var high); Vector.Widen(low, out var low2, out var high2); accumulator += low2; accumulator += high2; Vector.Widen(high, out low2, out high2); accumulator += low2; accumulator += high2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ReduceSum(Vector128 accumulator) { if (Ssse3.IsSupported) { Vector128 vector = Ssse3.HorizontalAdd(accumulator, accumulator); Vector128 right = Sse2.Shuffle(vector, 1); return Sse2.ConvertToInt32(Sse2.Add(vector, right)); } int num = 0; for (int i = 0; i < Vector128.Count; i++) { num += accumulator.GetElement(i); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ReduceSum(Vector256 accumulator) { Vector128 vector = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); Vector128 vector2 = Sse2.Add(vector, Sse2.Shuffle(vector, 245)); return Sse2.ConvertToInt32(Sse2.Add(vector2, Sse2.Shuffle(vector2, 238))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int EvenReduceSum(Vector256 accumulator) { Vector128 vector = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); return Sse2.ConvertToInt32(Sse2.Add(vector, Sse2.Shuffle(vector, 238))); } public static int Log2(uint value) { return BitOperations.Log2(value); } public static uint DivideCeil(uint value, uint divisor) { return (value + divisor - 1) / divisor; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint RotateLeft(uint value, int offset) { return BitOperations.RotateLeft(value, offset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint RotateRight(uint value, int offset) { return BitOperations.RotateRight(value, offset); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsOutOfRange(int value, int min, int max) { return (uint)(value - min) > (uint)(max - min); } } internal static class RuntimeEnvironment { private static readonly Lazy IsNetCoreLazy = new Lazy(() => FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase)); public static bool IsNetCore => IsNetCoreLazy.Value; public static string FrameworkDescription => RuntimeInformation.FrameworkDescription; public static bool IsOSPlatform(OSPlatform osPlatform) { return RuntimeInformation.IsOSPlatform(osPlatform); } } internal interface IComponentShuffle { byte Control { get; } void RunFallbackShuffle(ReadOnlySpan source, Span dest); } internal interface IShuffle4 : IComponentShuffle { } internal readonly struct DefaultShuffle4 : IShuffle4, IComponentShuffle { private readonly byte p3; private readonly byte p2; private readonly byte p1; private readonly byte p0; public byte Control { get; } public DefaultShuffle4(byte p3, byte p2, byte p1, byte p0) { this.p3 = p3; this.p2 = p2; this.p1 = p1; this.p0 = p0; Control = SimdUtils.Shuffle.MmShuffle(p3, p2, p1, p0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref byte reference = ref MemoryMarshal.GetReference(source); ref byte reference2 = ref MemoryMarshal.GetReference(dest); int num = p3; int num2 = p2; int num3 = p1; int num4 = p0; for (int i = 0; i < source.Length; i += 4) { Unsafe.Add(ref reference2, i) = Unsafe.Add(ref reference, num4 + i); Unsafe.Add(ref reference2, i + 1) = Unsafe.Add(ref reference, num3 + i); Unsafe.Add(ref reference2, i + 2) = Unsafe.Add(ref reference, num2 + i); Unsafe.Add(ref reference2, i + 3) = Unsafe.Add(ref reference, num + i); } } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal readonly struct WXYZShuffle4 : IShuffle4, IComponentShuffle { public byte Control { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return SimdUtils.Shuffle.MmShuffle(2, 1, 0, 3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref uint source3 = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); int num = source.Length / 4; for (int i = 0; i < num; i++) { uint num2 = Unsafe.Add(ref source2, i); Unsafe.Add(ref source3, i) = (num2 << 8) | (num2 >> 24); } } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal readonly struct WZYXShuffle4 : IShuffle4, IComponentShuffle { public byte Control { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return SimdUtils.Shuffle.MmShuffle(0, 1, 2, 3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref uint source3 = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); int num = source.Length / 4; for (int i = 0; i < num; i++) { uint value = Unsafe.Add(ref source2, i); Unsafe.Add(ref source3, i) = BinaryPrimitives.ReverseEndianness(value); } } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal readonly struct YZWXShuffle4 : IShuffle4, IComponentShuffle { public byte Control { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return SimdUtils.Shuffle.MmShuffle(0, 3, 2, 1); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref uint source3 = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); int num = source.Length / 4; for (int i = 0; i < num; i++) { uint value = Unsafe.Add(ref source2, i); Unsafe.Add(ref source3, i) = Numerics.RotateRight(value, 8); } } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal readonly struct ZYXWShuffle4 : IShuffle4, IComponentShuffle { public byte Control { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return SimdUtils.Shuffle.MmShuffle(3, 0, 1, 2); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref uint source3 = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); int num = source.Length / 4; for (int i = 0; i < num; i++) { uint num2 = Unsafe.Add(ref source2, i); uint num3 = num2 & 0xFF00FF00u; uint num4 = Numerics.RotateLeft(num2 & 0xFF00FF, 16); Unsafe.Add(ref source3, i) = num3 + num4; } } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal readonly struct XWZYShuffle4 : IShuffle4, IComponentShuffle { public byte Control { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return SimdUtils.Shuffle.MmShuffle(1, 2, 3, 0); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref uint source3 = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); int num = source.Length / 4; for (int i = 0; i < num; i++) { uint num2 = Unsafe.Add(ref source2, i); uint num3 = num2 & 0xFF00FF; uint num4 = Numerics.RotateLeft(num2 & 0xFF00FF00u, 16); Unsafe.Add(ref source3, i) = num3 + num4; } } } internal interface IPad3Shuffle4 : IComponentShuffle { } internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4, IComponentShuffle { private readonly byte p3; private readonly byte p2; private readonly byte p1; private readonly byte p0; public byte Control { get; } public DefaultPad3Shuffle4(byte p3, byte p2, byte p1, byte p0) { this.p3 = p3; this.p2 = p2; this.p1 = p1; this.p0 = p0; Control = SimdUtils.Shuffle.MmShuffle(p3, p2, p1, p0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref byte reference = ref MemoryMarshal.GetReference(source); ref byte reference2 = ref MemoryMarshal.GetReference(dest); int elementOffset = p3; int elementOffset2 = p2; int elementOffset3 = p1; int elementOffset4 = p0; ref byte reference3 = ref MemoryMarshal.GetReference(stackalloc byte[4]); ref uint reference4 = ref Unsafe.As(ref reference3); int num = 0; int num2 = 0; while (num < source.Length) { reference4 = Unsafe.As(ref Unsafe.Add(ref reference, num)) | 0xFF000000u; Unsafe.Add(ref reference2, num2) = Unsafe.Add(ref reference3, elementOffset4); Unsafe.Add(ref reference2, num2 + 1) = Unsafe.Add(ref reference3, elementOffset3); Unsafe.Add(ref reference2, num2 + 2) = Unsafe.Add(ref reference3, elementOffset2); Unsafe.Add(ref reference2, num2 + 3) = Unsafe.Add(ref reference3, elementOffset); num += 3; num2 += 4; } } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal readonly struct XYZWPad3Shuffle4 : IPad3Shuffle4, IComponentShuffle { public byte Control { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return SimdUtils.Shuffle.MmShuffle(3, 2, 1, 0); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref byte reference = ref MemoryMarshal.GetReference(source); ref byte source2 = ref MemoryMarshal.GetReference(dest); ref byte reference2 = ref Unsafe.Add(ref reference, source.Length); ref byte right = ref Unsafe.Subtract(ref reference2, 4); while (Unsafe.IsAddressLessThan(in reference, in right)) { Unsafe.As(ref source2) = Unsafe.As(ref reference) | 0xFF000000u; reference = ref Unsafe.Add(ref reference, 3); source2 = ref Unsafe.Add(ref source2, 4); } while (Unsafe.IsAddressLessThan(in reference, in reference2)) { Unsafe.Add(ref source2, 0) = Unsafe.Add(ref reference, 0); Unsafe.Add(ref source2, 1) = Unsafe.Add(ref reference, 1); Unsafe.Add(ref source2, 2) = Unsafe.Add(ref reference, 2); Unsafe.Add(ref source2, 3) = byte.MaxValue; reference = ref Unsafe.Add(ref reference, 3); source2 = ref Unsafe.Add(ref source2, 4); } } } internal interface IShuffle3 : IComponentShuffle { } internal readonly struct DefaultShuffle3 : IShuffle3, IComponentShuffle { private readonly byte p2; private readonly byte p1; private readonly byte p0; public byte Control { get; } public DefaultShuffle3(byte p2, byte p1, byte p0) { this.p2 = p2; this.p1 = p1; this.p0 = p0; Control = SimdUtils.Shuffle.MmShuffle(3, p2, p1, p0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref byte reference = ref MemoryMarshal.GetReference(source); ref byte reference2 = ref MemoryMarshal.GetReference(dest); int num = p2; int num2 = p1; int num3 = p0; for (int i = 0; i < source.Length; i += 3) { Unsafe.Add(ref reference2, i) = Unsafe.Add(ref reference, num3 + i); Unsafe.Add(ref reference2, i + 1) = Unsafe.Add(ref reference, num2 + i); Unsafe.Add(ref reference2, i + 2) = Unsafe.Add(ref reference, num + i); } } } internal interface IShuffle4Slice3 : IComponentShuffle { } internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3, IComponentShuffle { private readonly byte p2; private readonly byte p1; private readonly byte p0; public byte Control { get; } public DefaultShuffle4Slice3(byte p3, byte p2, byte p1, byte p0) { this.p2 = p2; this.p1 = p1; this.p0 = p0; Control = SimdUtils.Shuffle.MmShuffle(p3, p2, p1, p0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref byte reference = ref MemoryMarshal.GetReference(source); ref byte reference2 = ref MemoryMarshal.GetReference(dest); int num = p2; int num2 = p1; int num3 = p0; int num4 = 0; int num5 = 0; while (num4 < dest.Length) { Unsafe.Add(ref reference2, num4) = Unsafe.Add(ref reference, num3 + num5); Unsafe.Add(ref reference2, num4 + 1) = Unsafe.Add(ref reference, num2 + num5); Unsafe.Add(ref reference2, num4 + 2) = Unsafe.Add(ref reference, num + num5); num4 += 3; num5 += 4; } } } [StructLayout(LayoutKind.Sequential, Size = 1)] internal readonly struct XYZWShuffle4Slice3 : IShuffle4Slice3, IComponentShuffle { public byte Control { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return SimdUtils.Shuffle.MmShuffle(3, 2, 1, 0); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RunFallbackShuffle(ReadOnlySpan source, Span dest) { ref uint reference = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref Byte3 source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); int num = source.Length / 4; int num2 = Numerics.Modulo4(num); int elementOffset = num - num2; ref uint right = ref Unsafe.Add(ref reference, elementOffset); ref uint right2 = ref Unsafe.Add(ref reference, num); while (Unsafe.IsAddressLessThan(in reference, in right)) { Unsafe.Add(ref source2, 0) = Unsafe.As(ref Unsafe.Add(ref reference, 0)); Unsafe.Add(ref source2, 1) = Unsafe.As(ref Unsafe.Add(ref reference, 1)); Unsafe.Add(ref source2, 2) = Unsafe.As(ref Unsafe.Add(ref reference, 2)); Unsafe.Add(ref source2, 3) = Unsafe.As(ref Unsafe.Add(ref reference, 3)); reference = ref Unsafe.Add(ref reference, 4); source2 = ref Unsafe.Add(ref source2, 4); } while (Unsafe.IsAddressLessThan(in reference, in right2)) { Unsafe.Add(ref source2, 0) = Unsafe.As(ref Unsafe.Add(ref reference, 0)); reference = ref Unsafe.Add(ref reference, 1); source2 = ref Unsafe.Add(ref source2, 1); } } } [StructLayout(LayoutKind.Explicit, Size = 3)] internal readonly struct Byte3 { } internal static class SimdUtils { public static class BasicIntrinsics256 { public static bool IsAvailable { get; } = HasVector8; internal static void ByteToNormalizedFloat(ReadOnlySpan source, Span dest) { Vector vector = new Vector(1.0039216f); Vector vector2 = new Vector(32768f); Vector vector3 = new Vector(1191182336u); Vector vector4 = new Vector(255u); ref Octet source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Octet source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); ref Vector source4 = ref Unsafe.As, Vector>(ref source3); int num = dest.Length / 8; for (int i = 0; i < num; i++) { ref Octet source5 = ref Unsafe.Add(ref source2, i); Unsafe.Add(ref source3, i).LoadFrom(ref source5); } for (int j = 0; j < num; j++) { ref Vector reference = ref Unsafe.Add(ref source4, j); Vector vector5 = Vector.AsVectorSingle((Vector.AsVectorUInt32(reference) & vector4) | vector3); vector5 = (vector5 - vector2) * vector; reference = vector5; } } internal static void NormalizedFloatToByteSaturate(ReadOnlySpan source, Span dest) { if (source.Length != 0) { ref Vector source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Octet source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); int num = source.Length / 8; Vector vector = new Vector(32768f); Vector vector2 = new Vector(255f) / new Vector(256f); Octet source4 = default(Octet); ref Vector reference = ref Unsafe.As, Vector>(ref source4); for (int i = 0; i < num; i++) { Vector left = Unsafe.Add(ref source2, i); left = Vector.Max(left, Vector.Zero); left = Vector.Min(left, Vector.One); left = left * vector2 + vector; reference = left; Unsafe.Add(ref source3, i).LoadFrom(ref source4); } } } internal static void BulkConvertNormalizedFloatToByte(ReadOnlySpan source, Span dest) { if (source.Length != 0) { ref Vector source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Octet source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); int num = source.Length / 8; Vector vector = new Vector(32768f); Vector vector2 = new Vector(255f) / new Vector(256f); Octet source4 = default(Octet); ref Vector reference = ref Unsafe.As, Vector>(ref source4); for (int i = 0; i < num; i++) { Vector vector3 = Unsafe.Add(ref source2, i); vector3 = vector3 * vector2 + vector; reference = vector3; Unsafe.Add(ref source3, i).LoadFrom(ref source4); } } } } private struct ByteTuple4 { public byte V0; public byte V1; public byte V2; public byte V3; } public static class ExtendedIntrinsics { public static bool IsAvailable { get; } = Vector.IsHardwareAccelerated; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ConvertToSingle(Vector source, out Vector dest1, out Vector dest2) { Vector.Widen(source, out var low, out var high); dest1 = Vector.ConvertToSingle(low); dest2 = Vector.ConvertToSingle(high); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ByteToNormalizedFloatReduce(ref ReadOnlySpan source, ref Span dest) { if (IsAvailable) { int num = Numerics.ModuloP2(source.Length, Vector.Count); int num2 = source.Length - num; if (num2 > 0) { ByteToNormalizedFloat(source.Slice(0, num2), dest.Slice(0, num2)); source = source.Slice(num2); dest = dest.Slice(num2); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void NormalizedFloatToByteSaturateReduce(ref ReadOnlySpan source, ref Span dest) { if (IsAvailable) { int num = Numerics.ModuloP2(source.Length, Vector.Count); int num2 = source.Length - num; if (num2 > 0) { NormalizedFloatToByteSaturate(source.Slice(0, num2), dest.Slice(0, num2)); source = source.Slice(num2); dest = dest.Slice(num2); } } } internal static void ByteToNormalizedFloat(ReadOnlySpan source, Span dest) { int num = dest.Length / Vector.Count; ref Vector source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); for (int i = 0; i < num; i++) { Vector.Widen(Unsafe.Add(ref source2, i), out var low, out var high); Vector.Widen(low, out var low2, out var high2); Vector.Widen(high, out var low3, out var high3); Vector vector = ConvertToSingle(low2); Vector vector2 = ConvertToSingle(high2); Vector vector3 = ConvertToSingle(low3); Vector vector4 = ConvertToSingle(high3); ref Vector reference = ref Unsafe.Add(ref source3, i * 4); reference = vector; Unsafe.Add(ref reference, 1) = vector2; Unsafe.Add(ref reference, 2) = vector3; Unsafe.Add(ref reference, 3) = vector4; } } internal static void NormalizedFloatToByteSaturate(ReadOnlySpan source, Span dest) { int num = dest.Length / Vector.Count; ref Vector source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); for (int i = 0; i < num; i++) { ref Vector reference = ref Unsafe.Add(ref source2, i * 4); Vector vf = reference; Vector vf2 = Unsafe.Add(ref reference, 1); Vector vf3 = Unsafe.Add(ref reference, 2); Vector vf4 = Unsafe.Add(ref reference, 3); Vector low = ConvertToUInt32(vf); Vector high = ConvertToUInt32(vf2); Vector low2 = ConvertToUInt32(vf3); Vector high2 = ConvertToUInt32(vf4); Vector low3 = Vector.Narrow(low, high); Vector high3 = Vector.Narrow(low2, high2); Vector vector = Vector.Narrow(low3, high3); Unsafe.Add(ref source3, i) = vector; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector ConvertToUInt32(Vector vf) { Vector vector = new Vector(255f); vf *= vector; vf += new Vector(0.5f); vf = Vector.Min(Vector.Max(vf, Vector.Zero), vector); return Vector.AsVectorUInt32(Vector.ConvertToInt32(vf)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector ConvertToSingle(Vector u) { return Vector.ConvertToSingle(Vector.AsVectorInt32(u)) * new Vector(0.003921569f); } } public static class FallbackIntrinsics128 { private struct ByteVector4 { public byte X; public byte Y; public byte Z; public byte W; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ByteToNormalizedFloatReduce(ref ReadOnlySpan source, ref Span dest) { int num = Numerics.Modulo4(source.Length); int num2 = source.Length - num; if (num2 > 0) { ByteToNormalizedFloat(source.Slice(0, num2), dest.Slice(0, num2)); source = source.Slice(num2); dest = dest.Slice(num2); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void NormalizedFloatToByteSaturateReduce(ref ReadOnlySpan source, ref Span dest) { int num = Numerics.Modulo4(source.Length); int num2 = source.Length - num; if (num2 > 0) { NormalizedFloatToByteSaturate(source.Slice(0, num2), dest.Slice(0, num2)); source = source.Slice(num2); dest = dest.Slice(num2); } } [MethodImpl(MethodImplOptions.NoInlining)] internal static void ByteToNormalizedFloat(ReadOnlySpan source, Span dest) { int num = dest.Length / 4; if (num != 0) { ref ByteVector4 source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref Vector4 source3 = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); Vector4 vector = default(Vector4); for (int i = 0; i < num; i++) { ref ByteVector4 reference = ref Unsafe.Add(ref source2, i); vector.X = (int)reference.X; vector.Y = (int)reference.Y; vector.Z = (int)reference.Z; vector.W = (int)reference.W; vector *= 0.003921569f; Unsafe.Add(ref source3, i) = vector; } } } [MethodImpl(MethodImplOptions.NoInlining)] internal static void NormalizedFloatToByteSaturate(ReadOnlySpan source, Span dest) { int num = source.Length / 4; if (num != 0) { ref Vector4 source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref ByteVector4 source3 = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); Vector4 vector = new Vector4(0.5f); Vector4 vector2 = new Vector4(255f); for (int i = 0; i < num; i++) { Vector4 value = Unsafe.Add(ref source2, i); value *= vector2; value += vector; value = Numerics.Clamp(value, Vector4.Zero, vector2); ref ByteVector4 reference = ref Unsafe.Add(ref source3, i); reference.X = (byte)value.X; reference.Y = (byte)value.Y; reference.Z = (byte)value.Z; reference.W = (byte)value.W; } } } } public static class HwIntrinsics { public static ReadOnlySpan PermuteMaskDeinterleave8x32 => new byte[32] { 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0 }; public static ReadOnlySpan PermuteMaskEvenOdd8x32 => new byte[32] { 0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0 }; public static ReadOnlySpan PermuteMaskSwitchInnerDWords8x32 => new byte[32] { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0 }; private static ReadOnlySpan ShuffleMaskPad4Nx16 => new byte[16] { 0, 1, 2, 128, 3, 4, 5, 128, 6, 7, 8, 128, 9, 10, 11, 128 }; private static ReadOnlySpan ShuffleMaskSlice4Nx16 => new byte[16] { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 128, 128, 128, 128 }; private static ReadOnlySpan ShuffleMaskShiftAlpha => new byte[32] { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15, 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15 }; public static ReadOnlySpan PermuteMaskShiftAlpha8x32 => new byte[32] { 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0 }; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Shuffle4Reduce(ref ReadOnlySpan source, ref Span dest, byte control) { if (Avx.IsSupported || Sse.IsSupported) { int num = (Avx.IsSupported ? Numerics.ModuloP2(source.Length, Vector256.Count) : Numerics.ModuloP2(source.Length, Vector128.Count)); int num2 = source.Length - num; if (num2 > 0) { Shuffle4(source.Slice(0, num2), dest.Slice(0, num2), control); source = source.Slice(num2); dest = dest.Slice(num2); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Shuffle4Reduce(ref ReadOnlySpan source, ref Span dest, byte control) { if (Avx2.IsSupported || Ssse3.IsSupported) { int num = (Avx2.IsSupported ? Numerics.ModuloP2(source.Length, Vector256.Count) : Numerics.ModuloP2(source.Length, Vector128.Count)); int num2 = source.Length - num; if (num2 > 0) { Shuffle4(source.Slice(0, num2), dest.Slice(0, num2), control); source = source.Slice(num2); dest = dest.Slice(num2); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Shuffle3Reduce(ref ReadOnlySpan source, ref Span dest, byte control) { if (Ssse3.IsSupported) { int num = source.Length % (Vector128.Count * 3); int num2 = source.Length - num; if (num2 > 0) { Shuffle3(source.Slice(0, num2), dest.Slice(0, num2), control); source = source.Slice(num2); dest = dest.Slice(num2); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pad3Shuffle4Reduce(ref ReadOnlySpan source, ref Span dest, byte control) { if (Ssse3.IsSupported) { int num = source.Length % (Vector128.Count * 3); int num2 = source.Length - num; int num3 = num2 * 4 / 3; if (num2 > 0) { Pad3Shuffle4(source.Slice(0, num2), dest.Slice(0, num3), control); source = source.Slice(num2); dest = dest.Slice(num3); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Shuffle4Slice3Reduce(ref ReadOnlySpan source, ref Span dest, byte control) { if (Ssse3.IsSupported) { int num = source.Length % (Vector128.Count * 4); int num2 = source.Length - num; int num3 = num2 * 3 / 4; if (num2 > 0) { Shuffle4Slice3(source.Slice(0, num2), dest.Slice(0, num3), control); source = source.Slice(num2); dest = dest.Slice(num3); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Shuffle4(ReadOnlySpan source, Span dest, byte control) { if (Avx.IsSupported) { ref Vector256 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector256 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); int num = dest.Length / Vector256.Count; int num2 = Numerics.Modulo4(num); int num3 = num - num2; for (int i = 0; i < num3; i += 4) { ref Vector256 reference = ref Unsafe.Add(ref source3, i); ref Vector256 reference2 = ref Unsafe.Add(ref source2, i); reference = Avx.Permute(reference2, control); Unsafe.Add(ref reference, 1) = Avx.Permute(Unsafe.Add(ref reference2, 1), control); Unsafe.Add(ref reference, 2) = Avx.Permute(Unsafe.Add(ref reference2, 2), control); Unsafe.Add(ref reference, 3) = Avx.Permute(Unsafe.Add(ref reference2, 3), control); } if (num2 > 0) { for (int j = num3; j < num; j++) { Unsafe.Add(ref source3, j) = Avx.Permute(Unsafe.Add(ref source2, j), control); } } return; } ref Vector128 source4 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector128 source5 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); int num4 = dest.Length / Vector128.Count; int num5 = Numerics.Modulo4(num4); int num6 = num4 - num5; for (int k = 0; k < num6; k += 4) { ref Vector128 reference3 = ref Unsafe.Add(ref source5, k); ref Vector128 reference4 = ref Unsafe.Add(ref source4, k); reference3 = Sse.Shuffle(reference4, reference4, control); Vector128 vector = Unsafe.Add(ref reference4, 1); Unsafe.Add(ref reference3, 1) = Sse.Shuffle(vector, vector, control); Vector128 vector2 = Unsafe.Add(ref reference4, 2); Unsafe.Add(ref reference3, 2) = Sse.Shuffle(vector2, vector2, control); Vector128 vector3 = Unsafe.Add(ref reference4, 3); Unsafe.Add(ref reference3, 3) = Sse.Shuffle(vector3, vector3, control); } if (num5 > 0) { for (int l = num6; l < num4; l++) { Vector128 vector4 = Unsafe.Add(ref source4, l); Unsafe.Add(ref source5, l) = Sse.Shuffle(vector4, vector4, control); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Shuffle4(ReadOnlySpan source, Span dest, byte control) { if (Avx2.IsSupported) { Span span = stackalloc byte[Vector256.Count]; Shuffle.MmShuffleSpan(ref span, control); Vector256 mask = Unsafe.As>(ref MemoryMarshal.GetReference(span)); ref Vector256 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector256 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); int num = dest.Length / Vector256.Count; int num2 = Numerics.Modulo4(num); int num3 = num - num2; for (int i = 0; i < num3; i += 4) { ref Vector256 reference = ref Unsafe.Add(ref source2, i); ref Vector256 reference2 = ref Unsafe.Add(ref source3, i); reference2 = Avx2.Shuffle(reference, mask); Unsafe.Add(ref reference2, 1) = Avx2.Shuffle(Unsafe.Add(ref reference, 1), mask); Unsafe.Add(ref reference2, 2) = Avx2.Shuffle(Unsafe.Add(ref reference, 2), mask); Unsafe.Add(ref reference2, 3) = Avx2.Shuffle(Unsafe.Add(ref reference, 3), mask); } if (num2 > 0) { for (int j = num3; j < num; j++) { Unsafe.Add(ref source3, j) = Avx2.Shuffle(Unsafe.Add(ref source2, j), mask); } } return; } Span span2 = stackalloc byte[Vector128.Count]; Shuffle.MmShuffleSpan(ref span2, control); Vector128 mask2 = Unsafe.As>(ref MemoryMarshal.GetReference(span2)); ref Vector128 source4 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector128 source5 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); int num4 = dest.Length / Vector128.Count; int num5 = Numerics.Modulo4(num4); int num6 = num4 - num5; for (int k = 0; k < num6; k += 4) { ref Vector128 reference3 = ref Unsafe.Add(ref source4, k); ref Vector128 reference4 = ref Unsafe.Add(ref source5, k); reference4 = Ssse3.Shuffle(reference3, mask2); Unsafe.Add(ref reference4, 1) = Ssse3.Shuffle(Unsafe.Add(ref reference3, 1), mask2); Unsafe.Add(ref reference4, 2) = Ssse3.Shuffle(Unsafe.Add(ref reference3, 2), mask2); Unsafe.Add(ref reference4, 3) = Ssse3.Shuffle(Unsafe.Add(ref reference3, 3), mask2); } if (num5 > 0) { for (int l = num6; l < num4; l++) { Unsafe.Add(ref source5, l) = Ssse3.Shuffle(Unsafe.Add(ref source4, l), mask2); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Shuffle3(ReadOnlySpan source, Span dest, byte control) { if (Ssse3.IsSupported) { Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(ShuffleMaskPad4Nx16)); Vector128 vector = Unsafe.As>(ref MemoryMarshal.GetReference(ShuffleMaskSlice4Nx16)); Vector128 mask2 = Ssse3.AlignRight(vector, vector, 12); Span span = stackalloc byte[Vector128.Count]; Shuffle.MmShuffleSpan(ref span, control); Vector128 mask3 = Unsafe.As>(ref MemoryMarshal.GetReference(span)); ref Vector128 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector128 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); int num = source.Length / Vector128.Count; for (int i = 0; i < num; i += 3) { ref Vector128 reference = ref Unsafe.Add(ref source2, i); Vector128 vector2 = reference; Vector128 vector3 = Unsafe.Add(ref reference, 1); Vector128 vector4 = Unsafe.Add(ref reference, 2); Vector128 value = Sse2.ShiftRightLogical128BitLane(vector4, 4); vector4 = Ssse3.AlignRight(vector4, vector3, 8); vector3 = Ssse3.AlignRight(vector3, vector2, 12); vector2 = Ssse3.Shuffle(Ssse3.Shuffle(vector2, mask), mask3); vector3 = Ssse3.Shuffle(Ssse3.Shuffle(vector3, mask), mask3); vector4 = Ssse3.Shuffle(Ssse3.Shuffle(vector4, mask), mask3); value = Ssse3.Shuffle(Ssse3.Shuffle(value, mask), mask3); vector2 = Ssse3.Shuffle(vector2, mask2); vector3 = Ssse3.Shuffle(vector3, vector); vector4 = Ssse3.Shuffle(vector4, mask2); value = Ssse3.Shuffle(value, vector); vector2 = Ssse3.AlignRight(vector3, vector2, 4); value = Ssse3.AlignRight(value, vector4, 12); vector3 = Sse2.ShiftLeftLogical128BitLane(vector3, 4); vector4 = Sse2.ShiftRightLogical128BitLane(vector4, 4); vector3 = Ssse3.AlignRight(vector4, vector3, 8); ref Vector128 reference2 = ref Unsafe.Add(ref source3, i); reference2 = vector2; Unsafe.Add(ref reference2, 1) = vector3; Unsafe.Add(ref reference2, 2) = value; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Pad3Shuffle4(ReadOnlySpan source, Span dest, byte control) { if (Ssse3.IsSupported) { Vector128 mask = Unsafe.As>(ref MemoryMarshal.GetReference(ShuffleMaskPad4Nx16)); Vector128 right = Vector128.Create(18374686483949813760uL).AsByte(); Span span = stackalloc byte[Vector128.Count]; Shuffle.MmShuffleSpan(ref span, control); Vector128 mask2 = Unsafe.As>(ref MemoryMarshal.GetReference(span)); ref Vector128 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector128 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); int num = source.Length / Vector128.Count; int num2 = 0; int num3 = 0; while (num2 < num) { ref Vector128 reference = ref Unsafe.Add(ref source2, num2); Vector128 vector = Unsafe.Add(ref reference, 1); Vector128 vector2 = Unsafe.Add(ref reference, 2); Vector128 value = Sse2.ShiftRightLogical128BitLane(vector2, 4); vector2 = Ssse3.AlignRight(vector2, vector, 8); vector = Ssse3.AlignRight(vector, reference, 12); ref Vector128 reference2 = ref Unsafe.Add(ref source3, num3); reference2 = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(reference, mask), right), mask2); Unsafe.Add(ref reference2, 1) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(vector, mask), right), mask2); Unsafe.Add(ref reference2, 2) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(vector2, mask), right), mask2); Unsafe.Add(ref reference2, 3) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(value, mask), right), mask2); num2 += 3; num3 += 4; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Shuffle4Slice3(ReadOnlySpan source, Span dest, byte control) { if (Ssse3.IsSupported) { Vector128 vector = Unsafe.As>(ref MemoryMarshal.GetReference(ShuffleMaskSlice4Nx16)); Vector128 mask = Ssse3.AlignRight(vector, vector, 12); Span span = stackalloc byte[Vector128.Count]; Shuffle.MmShuffleSpan(ref span, control); Vector128 mask2 = Unsafe.As>(ref MemoryMarshal.GetReference(span)); ref Vector128 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector128 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); int num = source.Length / Vector128.Count; int num2 = 0; int num3 = 0; while (num2 < num) { ref Vector128 reference = ref Unsafe.Add(ref source2, num2); Vector128 value = reference; Vector128 value2 = Unsafe.Add(ref reference, 1); Vector128 value3 = Unsafe.Add(ref reference, 2); Vector128 value4 = Unsafe.Add(ref reference, 3); value = Ssse3.Shuffle(Ssse3.Shuffle(value, mask2), mask); value2 = Ssse3.Shuffle(Ssse3.Shuffle(value2, mask2), vector); value3 = Ssse3.Shuffle(Ssse3.Shuffle(value3, mask2), mask); value4 = Ssse3.Shuffle(Ssse3.Shuffle(value4, mask2), vector); value = Ssse3.AlignRight(value2, value, 4); value4 = Ssse3.AlignRight(value4, value3, 12); value2 = Sse2.ShiftLeftLogical128BitLane(value2, 4); value3 = Sse2.ShiftRightLogical128BitLane(value3, 4); value2 = Ssse3.AlignRight(value3, value2, 8); ref Vector128 reference2 = ref Unsafe.Add(ref source3, num3); reference2 = value; Unsafe.Add(ref reference2, 1) = value2; Unsafe.Add(ref reference2, 2) = value4; num2 += 4; num3 += 3; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplyAdd(in Vector256 va, in Vector256 vm0, in Vector256 vm1) { if (Fma.IsSupported) { return Fma.MultiplyAdd(vm1, vm0, va); } return Avx.Add(Avx.Multiply(vm0, vm1), va); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 MultiplySubstract(in Vector256 vs, in Vector256 vm0, in Vector256 vm1) { if (Fma.IsSupported) { return Fma.MultiplySubtract(vm1, vm0, vs); } return Avx.Subtract(Avx.Multiply(vm0, vm1), vs); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ByteToNormalizedFloatReduce(ref ReadOnlySpan source, ref Span dest) { if (Avx2.IsSupported || Sse2.IsSupported) { int num = ((!Avx2.IsSupported) ? Numerics.ModuloP2(source.Length, Vector128.Count) : Numerics.ModuloP2(source.Length, Vector256.Count)); int num2 = source.Length - num; if (num2 > 0) { ByteToNormalizedFloat(source.Slice(0, num2), dest.Slice(0, num2)); source = source.Slice(num2); dest = dest.Slice(num2); } } } internal unsafe static void ByteToNormalizedFloat(ReadOnlySpan source, Span dest) { fixed (byte* ptr = source) { if (Avx2.IsSupported) { int num = dest.Length / Vector256.Count; ref Vector256 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); Vector256 left = Vector256.Create(0.003921569f); for (int i = 0; i < num; i++) { int num2 = Vector256.Count * i; Vector256 value = Avx2.ConvertToVector256Int32(ptr + num2); Vector256 value2 = Avx2.ConvertToVector256Int32(ptr + num2 + Vector256.Count); Vector256 value3 = Avx2.ConvertToVector256Int32(ptr + num2 + Vector256.Count * 2); Vector256 value4 = Avx2.ConvertToVector256Int32(ptr + num2 + Vector256.Count * 3); Vector256 vector = Avx.Multiply(left, Avx.ConvertToVector256Single(value)); Vector256 vector2 = Avx.Multiply(left, Avx.ConvertToVector256Single(value2)); Vector256 vector3 = Avx.Multiply(left, Avx.ConvertToVector256Single(value3)); Vector256 vector4 = Avx.Multiply(left, Avx.ConvertToVector256Single(value4)); ref Vector256 reference = ref Unsafe.Add(ref source2, i * 4); reference = vector; Unsafe.Add(ref reference, 1) = vector2; Unsafe.Add(ref reference, 2) = vector3; Unsafe.Add(ref reference, 3) = vector4; } return; } int num3 = dest.Length / Vector128.Count; ref Vector128 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); Vector128 left2 = Vector128.Create(0.003921569f); Vector128 zero = Vector128.Zero; for (int j = 0; j < num3; j++) { int num4 = Vector128.Count * j; Vector128 value5; Vector128 value6; Vector128 value7; Vector128 value8; if (Sse41.IsSupported) { value5 = Sse41.ConvertToVector128Int32(ptr + num4); value6 = Sse41.ConvertToVector128Int32(ptr + num4 + Vector128.Count); value7 = Sse41.ConvertToVector128Int32(ptr + num4 + Vector128.Count * 2); value8 = Sse41.ConvertToVector128Int32(ptr + num4 + Vector128.Count * 3); } else { Vector128 left3 = Sse2.LoadVector128(ptr + num4); Vector128 left4 = Sse2.UnpackLow(left3, zero).AsInt16(); Vector128 left5 = Sse2.UnpackHigh(left3, zero).AsInt16(); value5 = Sse2.UnpackLow(left4, zero.AsInt16()).AsInt32(); value6 = Sse2.UnpackHigh(left4, zero.AsInt16()).AsInt32(); value7 = Sse2.UnpackLow(left5, zero.AsInt16()).AsInt32(); value8 = Sse2.UnpackHigh(left5, zero.AsInt16()).AsInt32(); } Vector128 vector5 = Sse.Multiply(left2, Sse2.ConvertToVector128Single(value5)); Vector128 vector6 = Sse.Multiply(left2, Sse2.ConvertToVector128Single(value6)); Vector128 vector7 = Sse.Multiply(left2, Sse2.ConvertToVector128Single(value7)); Vector128 vector8 = Sse.Multiply(left2, Sse2.ConvertToVector128Single(value8)); ref Vector128 reference2 = ref Unsafe.Add(ref source3, j * 4); reference2 = vector5; Unsafe.Add(ref reference2, 1) = vector6; Unsafe.Add(ref reference2, 2) = vector7; Unsafe.Add(ref reference2, 3) = vector8; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void NormalizedFloatToByteSaturateReduce(ref ReadOnlySpan source, ref Span dest) { if (Avx2.IsSupported || Sse2.IsSupported) { int num = ((!Avx2.IsSupported) ? Numerics.ModuloP2(source.Length, Vector128.Count) : Numerics.ModuloP2(source.Length, Vector256.Count)); int num2 = source.Length - num; if (num2 > 0) { NormalizedFloatToByteSaturate(source.Slice(0, num2), dest.Slice(0, num2)); source = source.Slice(num2); dest = dest.Slice(num2); } } } internal static void NormalizedFloatToByteSaturate(ReadOnlySpan source, Span dest) { if (Avx2.IsSupported) { int num = dest.Length / Vector256.Count; ref Vector256 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector256 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); Vector256 left = Vector256.Create(255f); Vector256 control = Unsafe.As>(ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32)); for (int i = 0; i < num; i++) { ref Vector256 reference = ref Unsafe.Add(ref source2, i * 4); Vector256 value = Avx.Multiply(left, reference); Vector256 value2 = Avx.Multiply(left, Unsafe.Add(ref reference, 1)); Vector256 value3 = Avx.Multiply(left, Unsafe.Add(ref reference, 2)); Vector256 value4 = Avx.Multiply(left, Unsafe.Add(ref reference, 3)); Vector256 left2 = Avx.ConvertToVector256Int32(value); Vector256 right = Avx.ConvertToVector256Int32(value2); Vector256 left3 = Avx.ConvertToVector256Int32(value3); Vector256 right2 = Avx.ConvertToVector256Int32(value4); Vector256 left4 = Avx2.PackSignedSaturate(left2, right); Vector256 right3 = Avx2.PackSignedSaturate(left3, right2); Vector256 vector = Avx2.PackUnsignedSaturate(left4, right3); vector = Avx2.PermuteVar8x32(vector.AsInt32(), control).AsByte(); Unsafe.Add(ref source3, i) = vector; } } else { int num2 = dest.Length / Vector128.Count; ref Vector128 source4 = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); ref Vector128 source5 = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); Vector128 left5 = Vector128.Create(255f); for (int j = 0; j < num2; j++) { ref Vector128 reference2 = ref Unsafe.Add(ref source4, j * 4); Vector128 value5 = Sse.Multiply(left5, reference2); Vector128 value6 = Sse.Multiply(left5, Unsafe.Add(ref reference2, 1)); Vector128 value7 = Sse.Multiply(left5, Unsafe.Add(ref reference2, 2)); Vector128 value8 = Sse.Multiply(left5, Unsafe.Add(ref reference2, 3)); Vector128 left6 = Sse2.ConvertToVector128Int32(value5); Vector128 right4 = Sse2.ConvertToVector128Int32(value6); Vector128 left7 = Sse2.ConvertToVector128Int32(value7); Vector128 right5 = Sse2.ConvertToVector128Int32(value8); Vector128 left8 = Sse2.PackSignedSaturate(left6, right4); Vector128 right6 = Sse2.PackSignedSaturate(left7, right5); Unsafe.Add(ref source5, j) = Sse2.PackUnsignedSaturate(left8, right6); } } } internal static void PackFromRgbPlanesAvx2Reduce(ref ReadOnlySpan redChannel, ref ReadOnlySpan greenChannel, ref ReadOnlySpan blueChannel, ref Span destination) { ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(redChannel)); ref Vector256 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(greenChannel)); ref Vector256 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(blueChannel)); ref byte source4 = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); int num = redChannel.Length / Vector256.Count; Vector256 control = Unsafe.As>(ref MemoryMarshal.GetReference(PermuteMaskEvenOdd8x32)); Vector256 control2 = Unsafe.As>(ref MemoryMarshal.GetReference(PermuteMaskShiftAlpha8x32)); Vector256 right = Vector256.Create(byte.MaxValue); Vector256 mask = Unsafe.As>(ref MemoryMarshal.GetReference(ShuffleMaskShiftAlpha)); for (int i = 0; i < num; i++) { Vector256 vector = Unsafe.Add(ref source, i); Vector256 vector2 = Unsafe.Add(ref source2, i); Vector256 vector3 = Unsafe.Add(ref source3, i); vector = Avx2.PermuteVar8x32(vector.AsUInt32(), control).AsByte(); vector2 = Avx2.PermuteVar8x32(vector2.AsUInt32(), control).AsByte(); vector3 = Avx2.PermuteVar8x32(vector3.AsUInt32(), control).AsByte(); Vector256 vector4 = Avx2.UnpackLow(vector, vector2); Vector256 vector5 = Avx2.UnpackLow(vector3, right); Vector256 value = Avx2.UnpackLow(vector4.AsUInt16(), vector5.AsUInt16()).AsByte(); Vector256 value2 = Avx2.UnpackHigh(vector4.AsUInt16(), vector5.AsUInt16()).AsByte(); vector4 = Avx2.UnpackHigh(vector, vector2); vector5 = Avx2.UnpackHigh(vector3, right); Vector256 value3 = Avx2.UnpackLow(vector4.AsUInt16(), vector5.AsUInt16()).AsByte(); Vector256 value4 = Avx2.UnpackHigh(vector4.AsUInt16(), vector5.AsUInt16()).AsByte(); value = Avx2.Shuffle(value, mask); value2 = Avx2.Shuffle(value2, mask); value3 = Avx2.Shuffle(value3, mask); value4 = Avx2.Shuffle(value4, mask); value = Avx2.PermuteVar8x32(value.AsUInt32(), control2).AsByte(); value2 = Avx2.PermuteVar8x32(value2.AsUInt32(), control2).AsByte(); value3 = Avx2.PermuteVar8x32(value3.AsUInt32(), control2).AsByte(); value4 = Avx2.PermuteVar8x32(value4.AsUInt32(), control2).AsByte(); ref byte source5 = ref Unsafe.Add(ref source4, 96 * i); ref byte source6 = ref Unsafe.Add(ref source5, 24); ref byte source7 = ref Unsafe.Add(ref source6, 24); ref byte source8 = ref Unsafe.Add(ref source7, 24); Unsafe.As>(ref source5) = value; Unsafe.As>(ref source6) = value2; Unsafe.As>(ref source7) = value3; Unsafe.As>(ref source8) = value4; } int start = num * Vector256.Count; redChannel = redChannel.Slice(start); greenChannel = greenChannel.Slice(start); blueChannel = blueChannel.Slice(start); destination = destination.Slice(start); } internal static void PackFromRgbPlanesAvx2Reduce(ref ReadOnlySpan redChannel, ref ReadOnlySpan greenChannel, ref ReadOnlySpan blueChannel, ref Span destination) { ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(redChannel)); ref Vector256 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(greenChannel)); ref Vector256 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(blueChannel)); ref Vector256 source4 = ref Unsafe.As>(ref MemoryMarshal.GetReference(destination)); int num = redChannel.Length / Vector256.Count; Vector256 control = Unsafe.As>(ref MemoryMarshal.GetReference(PermuteMaskEvenOdd8x32)); Unsafe.As>(ref MemoryMarshal.GetReference(PermuteMaskShiftAlpha8x32)); Vector256 right = Vector256.Create(byte.MaxValue); Unsafe.As>(ref MemoryMarshal.GetReference(ShuffleMaskShiftAlpha)); for (int i = 0; i < num; i++) { Vector256 vector = Unsafe.Add(ref source, i); Vector256 vector2 = Unsafe.Add(ref source2, i); Vector256 vector3 = Unsafe.Add(ref source3, i); vector = Avx2.PermuteVar8x32(vector.AsUInt32(), control).AsByte(); vector2 = Avx2.PermuteVar8x32(vector2.AsUInt32(), control).AsByte(); vector3 = Avx2.PermuteVar8x32(vector3.AsUInt32(), control).AsByte(); Vector256 vector4 = Avx2.UnpackLow(vector, vector2); Vector256 vector5 = Avx2.UnpackLow(vector3, right); Vector256 vector6 = Avx2.UnpackLow(vector4.AsUInt16(), vector5.AsUInt16()).AsByte(); Vector256 vector7 = Avx2.UnpackHigh(vector4.AsUInt16(), vector5.AsUInt16()).AsByte(); vector4 = Avx2.UnpackHigh(vector, vector2); vector5 = Avx2.UnpackHigh(vector3, right); Vector256 vector8 = Avx2.UnpackLow(vector4.AsUInt16(), vector5.AsUInt16()).AsByte(); Vector256 vector9 = Avx2.UnpackHigh(vector4.AsUInt16(), vector5.AsUInt16()).AsByte(); ref Vector256 reference = ref Unsafe.Add(ref source4, i * 4); reference = vector6; Unsafe.Add(ref reference, 1) = vector7; Unsafe.Add(ref reference, 2) = vector8; Unsafe.Add(ref reference, 3) = vector9; } int start = num * Vector256.Count; redChannel = redChannel.Slice(start); greenChannel = greenChannel.Slice(start); blueChannel = blueChannel.Slice(start); destination = destination.Slice(start); } } public static class Shuffle { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte MmShuffle(byte p3, byte p2, byte p1, byte p0) { return (byte)((p3 << 6) | (p2 << 4) | (p1 << 2) | p0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void MmShuffleSpan(ref Span span, byte control) { InverseMmShuffle(control, out var p, out var p2, out var p3, out var p4); ref byte reference = ref MemoryMarshal.GetReference(span); for (int i = 0; i < span.Length; i += 4) { Unsafe.Add(ref reference, i) = (byte)(p4 + i); Unsafe.Add(ref reference, i + 1) = (byte)(p3 + i); Unsafe.Add(ref reference, i + 2) = (byte)(p2 + i); Unsafe.Add(ref reference, i + 3) = (byte)(p + i); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InverseMmShuffle(byte control, out int p3, out int p2, out int p1, out int p0) { p3 = (control >> 6) & 3; p2 = (control >> 4) & 3; p1 = (control >> 2) & 3; p0 = control & 3; } } public static bool HasVector8 { get; } = Vector.IsHardwareAccelerated && Vector.Count == 8 && Vector.Count == 8; public static bool HasVector4 { get; } = Vector.IsHardwareAccelerated && Vector.Count == 4; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector4 PseudoRound(this Vector4 v) { Vector4 vector = Numerics.Clamp(v, new Vector4(-1f), new Vector4(1f)); return v + vector * 0.5f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector FastRound(this Vector v) { if (Avx2.IsSupported) { Vector256 source = Avx.RoundToNearestInteger(Unsafe.As, Vector256>(ref v)); return Unsafe.As, Vector>(ref source); } Vector right = Vector.BitwiseOr(Vector.BitwiseAnd(Vector.AsVectorSingle(new Vector(int.MinValue)), v), new Vector(8388608f)); return Vector.Subtract(Vector.Add(v, right), right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ByteToNormalizedFloat(ReadOnlySpan source, Span dest) { HwIntrinsics.ByteToNormalizedFloatReduce(ref source, ref dest); FallbackIntrinsics128.ByteToNormalizedFloatReduce(ref source, ref dest); if (source.Length > 0) { ConvertByteToNormalizedFloatRemainder(source, dest); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void NormalizedFloatToByteSaturate(ReadOnlySpan source, Span dest) { HwIntrinsics.NormalizedFloatToByteSaturateReduce(ref source, ref dest); FallbackIntrinsics128.NormalizedFloatToByteSaturateReduce(ref source, ref dest); if (source.Length > 0) { ConvertNormalizedFloatToByteRemainder(source, dest); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan source, Span dest) { ref byte reference = ref MemoryMarshal.GetReference(source); ref float reference2 = ref MemoryMarshal.GetReference(dest); switch (source.Length) { default: return; case 3: Unsafe.Add(ref reference2, 2) = (float)(int)Unsafe.Add(ref reference, 2) / 255f; goto case 2; case 2: Unsafe.Add(ref reference2, 1) = (float)(int)Unsafe.Add(ref reference, 1) / 255f; break; case 1: break; } reference2 = (float)(int)reference / 255f; } [MethodImpl(MethodImplOptions.NoInlining)] private static void ConvertNormalizedFloatToByteRemainder(ReadOnlySpan source, Span dest) { ref float reference = ref MemoryMarshal.GetReference(source); ref byte reference2 = ref MemoryMarshal.GetReference(dest); switch (source.Length) { default: return; case 3: Unsafe.Add(ref reference2, 2) = ConvertToByte(Unsafe.Add(ref reference, 2)); goto case 2; case 2: Unsafe.Add(ref reference2, 1) = ConvertToByte(Unsafe.Add(ref reference, 1)); break; case 1: break; } reference2 = ConvertToByte(reference); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte ConvertToByte(float f) { return (byte)Numerics.Clamp(f * 255f + 0.5f, 0f, 255f); } [Conditional("DEBUG")] private static void VerifyHasVector8(string operation) { if (!HasVector8) { throw new NotSupportedException(operation + " is supported only on AVX2 CPU!"); } } [Conditional("DEBUG")] private static void VerifySpanInput(ReadOnlySpan source, Span dest, int shouldBeDivisibleBy) { } [Conditional("DEBUG")] private static void VerifySpanInput(ReadOnlySpan source, Span dest, int shouldBeDivisibleBy) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void PackFromRgbPlanes(Configuration configuration, ReadOnlySpan redChannel, ReadOnlySpan greenChannel, ReadOnlySpan blueChannel, Span destination) { if (Avx2.IsSupported) { HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination); } else { PackFromRgbPlanesScalarBatchedReduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination); } PackFromRgbPlanesRemainder(redChannel, greenChannel, blueChannel, destination); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void PackFromRgbPlanes(Configuration configuration, ReadOnlySpan redChannel, ReadOnlySpan greenChannel, ReadOnlySpan blueChannel, Span destination) { if (Avx2.IsSupported) { HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination); } else { PackFromRgbPlanesScalarBatchedReduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination); } PackFromRgbPlanesRemainder(redChannel, greenChannel, blueChannel, destination); } private static void PackFromRgbPlanesScalarBatchedReduce(ref ReadOnlySpan redChannel, ref ReadOnlySpan greenChannel, ref ReadOnlySpan blueChannel, ref Span destination) { ref ByteTuple4 source = ref Unsafe.As(ref MemoryMarshal.GetReference(redChannel)); ref ByteTuple4 source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(greenChannel)); ref ByteTuple4 source3 = ref Unsafe.As(ref MemoryMarshal.GetReference(blueChannel)); ref Rgb24 reference = ref MemoryMarshal.GetReference(destination); int num = redChannel.Length / 4; for (int i = 0; i < num; i++) { ref Rgb24 reference2 = ref Unsafe.Add(ref reference, i * 4); ref Rgb24 reference3 = ref Unsafe.Add(ref reference2, 1); ref Rgb24 reference4 = ref Unsafe.Add(ref reference2, 2); ref Rgb24 reference5 = ref Unsafe.Add(ref reference2, 3); ref ByteTuple4 reference6 = ref Unsafe.Add(ref source, i); ref ByteTuple4 reference7 = ref Unsafe.Add(ref source2, i); ref ByteTuple4 reference8 = ref Unsafe.Add(ref source3, i); reference2.R = reference6.V0; reference2.G = reference7.V0; reference2.B = reference8.V0; reference3.R = reference6.V1; reference3.G = reference7.V1; reference3.B = reference8.V1; reference4.R = reference6.V2; reference4.G = reference7.V2; reference4.B = reference8.V2; reference5.R = reference6.V3; reference5.G = reference7.V3; reference5.B = reference8.V3; } int start = num * 4; redChannel = redChannel.Slice(start); greenChannel = greenChannel.Slice(start); blueChannel = blueChannel.Slice(start); destination = destination.Slice(start); } private static void PackFromRgbPlanesScalarBatchedReduce(ref ReadOnlySpan redChannel, ref ReadOnlySpan greenChannel, ref ReadOnlySpan blueChannel, ref Span destination) { ref ByteTuple4 source = ref Unsafe.As(ref MemoryMarshal.GetReference(redChannel)); ref ByteTuple4 source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(greenChannel)); ref ByteTuple4 source3 = ref Unsafe.As(ref MemoryMarshal.GetReference(blueChannel)); ref Rgba32 reference = ref MemoryMarshal.GetReference(destination); int num = redChannel.Length / 4; destination.Fill(new Rgba32(0, 0, 0, byte.MaxValue)); for (int i = 0; i < num; i++) { ref Rgba32 reference2 = ref Unsafe.Add(ref reference, i * 4); ref Rgba32 reference3 = ref Unsafe.Add(ref reference2, 1); ref Rgba32 reference4 = ref Unsafe.Add(ref reference2, 2); ref Rgba32 reference5 = ref Unsafe.Add(ref reference2, 3); ref ByteTuple4 reference6 = ref Unsafe.Add(ref source, i); ref ByteTuple4 reference7 = ref Unsafe.Add(ref source2, i); ref ByteTuple4 reference8 = ref Unsafe.Add(ref source3, i); reference2.R = reference6.V0; reference2.G = reference7.V0; reference2.B = reference8.V0; reference3.R = reference6.V1; reference3.G = reference7.V1; reference3.B = reference8.V1; reference4.R = reference6.V2; reference4.G = reference7.V2; reference4.B = reference8.V2; reference5.R = reference6.V3; reference5.G = reference7.V3; reference5.B = reference8.V3; } int start = num * 4; redChannel = redChannel.Slice(start); greenChannel = greenChannel.Slice(start); blueChannel = blueChannel.Slice(start); destination = destination.Slice(start); } private static void PackFromRgbPlanesRemainder(ReadOnlySpan redChannel, ReadOnlySpan greenChannel, ReadOnlySpan blueChannel, Span destination) { ref byte reference = ref MemoryMarshal.GetReference(redChannel); ref byte reference2 = ref MemoryMarshal.GetReference(greenChannel); ref byte reference3 = ref MemoryMarshal.GetReference(blueChannel); ref Rgb24 reference4 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < destination.Length; i++) { ref Rgb24 reference5 = ref Unsafe.Add(ref reference4, i); reference5.R = Unsafe.Add(ref reference, i); reference5.G = Unsafe.Add(ref reference2, i); reference5.B = Unsafe.Add(ref reference3, i); } } private static void PackFromRgbPlanesRemainder(ReadOnlySpan redChannel, ReadOnlySpan greenChannel, ReadOnlySpan blueChannel, Span destination) { ref byte reference = ref MemoryMarshal.GetReference(redChannel); ref byte reference2 = ref MemoryMarshal.GetReference(greenChannel); ref byte reference3 = ref MemoryMarshal.GetReference(blueChannel); ref Rgba32 reference4 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < destination.Length; i++) { ref Rgba32 reference5 = ref Unsafe.Add(ref reference4, i); reference5.R = Unsafe.Add(ref reference, i); reference5.G = Unsafe.Add(ref reference2, i); reference5.B = Unsafe.Add(ref reference3, i); reference5.A = byte.MaxValue; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Shuffle4(ReadOnlySpan source, Span dest, byte control) { HwIntrinsics.Shuffle4Reduce(ref source, ref dest, control); if (source.Length > 0) { Shuffle4Remainder(source, dest, control); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Shuffle4(ReadOnlySpan source, Span dest, TShuffle shuffle) where TShuffle : struct, IShuffle4 { HwIntrinsics.Shuffle4Reduce(ref source, ref dest, shuffle.Control); if (source.Length > 0) { shuffle.RunFallbackShuffle(source, dest); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Shuffle3(ReadOnlySpan source, Span dest, TShuffle shuffle) where TShuffle : struct, IShuffle3 { HwIntrinsics.Shuffle3Reduce(ref source, ref dest, shuffle.Control); if (source.Length > 0) { shuffle.RunFallbackShuffle(source, dest); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Pad3Shuffle4(ReadOnlySpan source, Span dest, TShuffle shuffle) where TShuffle : struct, IPad3Shuffle4 { HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, shuffle.Control); if (source.Length > 0) { shuffle.RunFallbackShuffle(source, dest); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Shuffle4Slice3(ReadOnlySpan source, Span dest, TShuffle shuffle) where TShuffle : struct, IShuffle4Slice3 { HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, shuffle.Control); if (source.Length > 0) { shuffle.RunFallbackShuffle(source, dest); } } private static void Shuffle4Remainder(ReadOnlySpan source, Span dest, byte control) { ref float reference = ref MemoryMarshal.GetReference(source); ref float reference2 = ref MemoryMarshal.GetReference(dest); Shuffle.InverseMmShuffle(control, out var p, out var p2, out var p3, out var p4); for (int i = 0; i < source.Length; i += 4) { Unsafe.Add(ref reference2, i) = Unsafe.Add(ref reference, p4 + i); Unsafe.Add(ref reference2, i + 1) = Unsafe.Add(ref reference, p3 + i); Unsafe.Add(ref reference2, i + 2) = Unsafe.Add(ref reference, p2 + i); Unsafe.Add(ref reference2, i + 3) = Unsafe.Add(ref reference, p + i); } } [Conditional("DEBUG")] private static void VerifyShuffle4SpanInput(ReadOnlySpan source, Span dest) where T : struct { } [Conditional("DEBUG")] private static void VerifyShuffle3SpanInput(ReadOnlySpan source, Span dest) where T : struct { } [Conditional("DEBUG")] private static void VerifyPad3Shuffle4SpanInput(ReadOnlySpan source, Span dest) { } [Conditional("DEBUG")] private static void VerifyShuffle4Slice3SpanInput(ReadOnlySpan source, Span dest) { } } internal readonly struct TolerantMath { private readonly double epsilon; private readonly double negEpsilon; public static readonly TolerantMath Default = new TolerantMath(1E-08); public TolerantMath(double epsilon) { this.epsilon = epsilon; negEpsilon = 0.0 - epsilon; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsZero(double a) { if (a > negEpsilon) { return a < epsilon; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsPositive(double a) { return a > epsilon; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsNegative(double a) { return a < negEpsilon; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool AreEqual(double a, double b) { return IsZero(a - b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsGreater(double a, double b) { return a > b + epsilon; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsLess(double a, double b) { return a < b - epsilon; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsGreaterOrEqual(double a, double b) { return a >= b - epsilon; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsLessOrEqual(double a, double b) { return b >= a - epsilon; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Ceiling(double a) { double a2 = Math.IEEERemainder(a, 1.0); if (IsZero(a2)) { return Math.Round(a); } return Math.Ceiling(a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Floor(double a) { double a2 = Math.IEEERemainder(a, 1.0); if (IsZero(a2)) { return Math.Round(a); } return Math.Floor(a); } } public sealed class Configuration { private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance); private const int DefaultStreamProcessingBufferSize = 8096; private int streamProcessingBufferSize = 8096; private int maxDegreeOfParallelism = Environment.ProcessorCount; private MemoryAllocator memoryAllocator = MemoryAllocator.Default; public static Configuration Default { get; } = Lazy.Value; public int MaxDegreeOfParallelism { get { return maxDegreeOfParallelism; } set { if (value == 0 || value < -1) { throw new ArgumentOutOfRangeException("MaxDegreeOfParallelism"); } maxDegreeOfParallelism = value; } } public int StreamProcessingBufferSize { get { return streamProcessingBufferSize; } set { if (value <= 0) { throw new ArgumentOutOfRangeException("StreamProcessingBufferSize"); } streamProcessingBufferSize = value; } } public bool PreferContiguousImageBuffers { get; set; } public IDictionary Properties { get; } = new ConcurrentDictionary(); public IEnumerable ImageFormats => ImageFormatsManager.ImageFormats; public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current; public ImageFormatManager ImageFormatsManager { get; private set; } = new ImageFormatManager(); public MemoryAllocator MemoryAllocator { get { return memoryAllocator; } set { Guard.NotNull(value, "MemoryAllocator"); memoryAllocator = value; } } internal int MaxHeaderSize => ImageFormatsManager.MaxHeaderSize; internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); internal int WorkingBufferSizeHintInBytes { get; set; } = 1048576; internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); public Configuration() { } public Configuration(params IConfigurationModule[] configurationModules) { if (configurationModules != null) { for (int i = 0; i < configurationModules.Length; i++) { configurationModules[i].Configure(this); } } } public void Configure(IConfigurationModule configuration) { Guard.NotNull(configuration, "configuration"); configuration.Configure(this); } public Configuration Clone() { return new Configuration { MaxDegreeOfParallelism = MaxDegreeOfParallelism, StreamProcessingBufferSize = StreamProcessingBufferSize, ImageFormatsManager = ImageFormatsManager, memoryAllocator = memoryAllocator, ImageOperationsProvider = ImageOperationsProvider, ReadOrigin = ReadOrigin, FileSystem = FileSystem, WorkingBufferSizeHintInBytes = WorkingBufferSizeHintInBytes }; } internal static Configuration CreateDefaultInstance() { return new Configuration(new PngConfigurationModule(), new JpegConfigurationModule(), new GifConfigurationModule(), new BmpConfigurationModule(), new PbmConfigurationModule(), new TgaConfigurationModule(), new TiffConfigurationModule(), new WebpConfigurationModule()); } } public static class MetadataExtensions { public static BmpMetadata GetBmpMetadata(this ImageMetadata metadata) { return metadata.GetFormatMetadata(BmpFormat.Instance); } public static GifMetadata GetGifMetadata(this ImageMetadata metadata) { return metadata.GetFormatMetadata(GifFormat.Instance); } public static GifFrameMetadata GetGifMetadata(this ImageFrameMetadata metadata) { return metadata.GetFormatMetadata(GifFormat.Instance); } public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) { return metadata.GetFormatMetadata(JpegFormat.Instance); } public static PbmMetadata GetPbmMetadata(this ImageMetadata metadata) { return metadata.GetFormatMetadata(PbmFormat.Instance); } public static PngMetadata GetPngMetadata(this ImageMetadata metadata) { return metadata.GetFormatMetadata(PngFormat.Instance); } public static TgaMetadata GetTgaMetadata(this ImageMetadata metadata) { return metadata.GetFormatMetadata(TgaFormat.Instance); } public static TiffMetadata GetTiffMetadata(this ImageMetadata metadata) { return metadata.GetFormatMetadata(TiffFormat.Instance); } public static TiffFrameMetadata GetTiffMetadata(this ImageFrameMetadata metadata) { return metadata.GetFormatMetadata(TiffFormat.Instance); } public static WebpMetadata GetWebpMetadata(this ImageMetadata metadata) { return metadata.GetFormatMetadata(WebpFormat.Instance); } } public static class ImageExtensions { public static void SaveAsBmp(this Image source, string path) { source.SaveAsBmp(path, null); } public static Task SaveAsBmpAsync(this Image source, string path) { return source.SaveAsBmpAsync(path, null); } public static Task SaveAsBmpAsync(this Image source, string path, CancellationToken cancellationToken) { return source.SaveAsBmpAsync(path, null, cancellationToken); } public static void SaveAsBmp(this Image source, string path, BmpEncoder encoder) { source.Save(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance)); } public static Task SaveAsBmpAsync(this Image source, string path, BmpEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance), cancellationToken); } public static void SaveAsBmp(this Image source, Stream stream) { source.SaveAsBmp(stream, null); } public static Task SaveAsBmpAsync(this Image source, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsBmpAsync(stream, null, cancellationToken); } public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) { source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance)); } public static Task SaveAsBmpAsync(this Image source, Stream stream, BmpEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance), cancellationToken); } public static void SaveAsGif(this Image source, string path) { source.SaveAsGif(path, null); } public static Task SaveAsGifAsync(this Image source, string path) { return source.SaveAsGifAsync(path, null); } public static Task SaveAsGifAsync(this Image source, string path, CancellationToken cancellationToken) { return source.SaveAsGifAsync(path, null, cancellationToken); } public static void SaveAsGif(this Image source, string path, GifEncoder encoder) { source.Save(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance)); } public static Task SaveAsGifAsync(this Image source, string path, GifEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance), cancellationToken); } public static void SaveAsGif(this Image source, Stream stream) { source.SaveAsGif(stream, null); } public static Task SaveAsGifAsync(this Image source, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsGifAsync(stream, null, cancellationToken); } public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) { source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance)); } public static Task SaveAsGifAsync(this Image source, Stream stream, GifEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance), cancellationToken); } public static void SaveAsJpeg(this Image source, string path) { source.SaveAsJpeg(path, null); } public static Task SaveAsJpegAsync(this Image source, string path) { return source.SaveAsJpegAsync(path, null); } public static Task SaveAsJpegAsync(this Image source, string path, CancellationToken cancellationToken) { return source.SaveAsJpegAsync(path, null, cancellationToken); } public static void SaveAsJpeg(this Image source, string path, JpegEncoder encoder) { source.Save(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance)); } public static Task SaveAsJpegAsync(this Image source, string path, JpegEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance), cancellationToken); } public static void SaveAsJpeg(this Image source, Stream stream) { source.SaveAsJpeg(stream, null); } public static Task SaveAsJpegAsync(this Image source, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsJpegAsync(stream, null, cancellationToken); } public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) { source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance)); } public static Task SaveAsJpegAsync(this Image source, Stream stream, JpegEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance), cancellationToken); } public static void SaveAsPbm(this Image source, string path) { source.SaveAsPbm(path, null); } public static Task SaveAsPbmAsync(this Image source, string path) { return source.SaveAsPbmAsync(path, null); } public static Task SaveAsPbmAsync(this Image source, string path, CancellationToken cancellationToken) { return source.SaveAsPbmAsync(path, null, cancellationToken); } public static void SaveAsPbm(this Image source, string path, PbmEncoder encoder) { source.Save(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance)); } public static Task SaveAsPbmAsync(this Image source, string path, PbmEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance), cancellationToken); } public static void SaveAsPbm(this Image source, Stream stream) { source.SaveAsPbm(stream, null); } public static Task SaveAsPbmAsync(this Image source, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsPbmAsync(stream, null, cancellationToken); } public static void SaveAsPbm(this Image source, Stream stream, PbmEncoder encoder) { source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance)); } public static Task SaveAsPbmAsync(this Image source, Stream stream, PbmEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PbmFormat.Instance), cancellationToken); } public static void SaveAsPng(this Image source, string path) { source.SaveAsPng(path, null); } public static Task SaveAsPngAsync(this Image source, string path) { return source.SaveAsPngAsync(path, null); } public static Task SaveAsPngAsync(this Image source, string path, CancellationToken cancellationToken) { return source.SaveAsPngAsync(path, null, cancellationToken); } public static void SaveAsPng(this Image source, string path, PngEncoder encoder) { source.Save(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance)); } public static Task SaveAsPngAsync(this Image source, string path, PngEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance), cancellationToken); } public static void SaveAsPng(this Image source, Stream stream) { source.SaveAsPng(stream, null); } public static Task SaveAsPngAsync(this Image source, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsPngAsync(stream, null, cancellationToken); } public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) { source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance)); } public static Task SaveAsPngAsync(this Image source, Stream stream, PngEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance), cancellationToken); } public static void SaveAsTga(this Image source, string path) { source.SaveAsTga(path, null); } public static Task SaveAsTgaAsync(this Image source, string path) { return source.SaveAsTgaAsync(path, null); } public static Task SaveAsTgaAsync(this Image source, string path, CancellationToken cancellationToken) { return source.SaveAsTgaAsync(path, null, cancellationToken); } public static void SaveAsTga(this Image source, string path, TgaEncoder encoder) { source.Save(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance)); } public static Task SaveAsTgaAsync(this Image source, string path, TgaEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance), cancellationToken); } public static void SaveAsTga(this Image source, Stream stream) { source.SaveAsTga(stream, null); } public static Task SaveAsTgaAsync(this Image source, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsTgaAsync(stream, null, cancellationToken); } public static void SaveAsTga(this Image source, Stream stream, TgaEncoder encoder) { source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance)); } public static Task SaveAsTgaAsync(this Image source, Stream stream, TgaEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TgaFormat.Instance), cancellationToken); } public static void SaveAsWebp(this Image source, string path) { source.SaveAsWebp(path, null); } public static Task SaveAsWebpAsync(this Image source, string path) { return source.SaveAsWebpAsync(path, null); } public static Task SaveAsWebpAsync(this Image source, string path, CancellationToken cancellationToken) { return source.SaveAsWebpAsync(path, null, cancellationToken); } public static void SaveAsWebp(this Image source, string path, WebpEncoder encoder) { source.Save(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance)); } public static Task SaveAsWebpAsync(this Image source, string path, WebpEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance), cancellationToken); } public static void SaveAsWebp(this Image source, Stream stream) { source.SaveAsWebp(stream, null); } public static Task SaveAsWebpAsync(this Image source, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsWebpAsync(stream, null, cancellationToken); } public static void SaveAsWebp(this Image source, Stream stream, WebpEncoder encoder) { source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance)); } public static Task SaveAsWebpAsync(this Image source, Stream stream, WebpEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance), cancellationToken); } public static void SaveAsTiff(this Image source, string path) { source.SaveAsTiff(path, null); } public static Task SaveAsTiffAsync(this Image source, string path) { return source.SaveAsTiffAsync(path, null); } public static Task SaveAsTiffAsync(this Image source, string path, CancellationToken cancellationToken) { return source.SaveAsTiffAsync(path, null, cancellationToken); } public static void SaveAsTiff(this Image source, string path, TiffEncoder encoder) { source.Save(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance)); } public static Task SaveAsTiffAsync(this Image source, string path, TiffEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(path, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance), cancellationToken); } public static void SaveAsTiff(this Image source, Stream stream) { source.SaveAsTiff(stream, null); } public static Task SaveAsTiffAsync(this Image source, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsTiffAsync(stream, null, cancellationToken); } public static void SaveAsTiff(this Image source, Stream stream, TiffEncoder encoder) { source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance)); } public static Task SaveAsTiffAsync(this Image source, Stream stream, TiffEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance), cancellationToken); } public static void Save(this Image source, string path) { source.Save(path, source.DetectEncoder(path)); } public static Task SaveAsync(this Image source, string path, CancellationToken cancellationToken = default(CancellationToken)) { return source.SaveAsync(path, source.DetectEncoder(path), cancellationToken); } public static void Save(this Image source, string path, IImageEncoder encoder) { Guard.NotNull(path, "path"); Guard.NotNull(encoder, "encoder"); using Stream stream = source.GetConfiguration().FileSystem.Create(path); source.Save(stream, encoder); } public static async Task SaveAsync(this Image source, string path, IImageEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { Guard.NotNull(path, "path"); Guard.NotNull(encoder, "encoder"); using Stream fs = source.GetConfiguration().FileSystem.Create(path); await source.SaveAsync(fs, encoder, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static void Save(this Image source, Stream stream, IImageFormat format) { Guard.NotNull(stream, "stream"); Guard.NotNull(format, "format"); if (!stream.CanWrite) { throw new NotSupportedException("Cannot write to the stream."); } IImageEncoder imageEncoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format); if (imageEncoder == null) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("No encoder was found for the provided mime type. Registered encoders include:"); foreach (KeyValuePair imageEncoder2 in source.GetConfiguration().ImageFormatsManager.ImageEncoders) { stringBuilder.AppendFormat(" - {0} : {1}{2}", imageEncoder2.Key.Name, imageEncoder2.Value.GetType().Name, Environment.NewLine); } throw new NotSupportedException(stringBuilder.ToString()); } source.Save(stream, imageEncoder); } public static Task SaveAsync(this Image source, Stream stream, IImageFormat format, CancellationToken cancellationToken = default(CancellationToken)) { Guard.NotNull(stream, "stream"); Guard.NotNull(format, "format"); if (!stream.CanWrite) { throw new NotSupportedException("Cannot write to the stream."); } IImageEncoder imageEncoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format); if (imageEncoder == null) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("No encoder was found for the provided mime type. Registered encoders include:"); foreach (KeyValuePair imageEncoder2 in source.GetConfiguration().ImageFormatsManager.ImageEncoders) { stringBuilder.AppendFormat(" - {0} : {1}{2}", imageEncoder2.Key.Name, imageEncoder2.Value.GetType().Name, Environment.NewLine); } throw new NotSupportedException(stringBuilder.ToString()); } return source.SaveAsync(stream, imageEncoder, cancellationToken); } public static string ToBase64String(this Image source, IImageFormat format) { Guard.NotNull(format, "format"); using MemoryStream memoryStream = new MemoryStream(); source.Save(memoryStream, format); memoryStream.TryGetBuffer(out var buffer); return "data:" + format.DefaultMimeType + ";base64," + Convert.ToBase64String(buffer.Array, 0, (int)memoryStream.Length); } internal static Buffer2D GetRootFramePixelBuffer(this Image image) where TPixel : unmanaged, IPixel { return image.Frames.RootFrame.PixelBuffer; } } public static class GeometryUtilities { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float DegreeToRadian(float degree) { return degree * ((float)Math.PI / 180f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float RadianToDegree(float radian) { return radian / ((float)Math.PI / 180f); } } public static class GraphicOptionsDefaultsExtensions { public static IImageProcessingContext SetGraphicsOptions(this IImageProcessingContext context, Action optionsBuilder) { GraphicsOptions graphicsOptions = context.GetGraphicsOptions().DeepClone(); optionsBuilder(graphicsOptions); context.Properties[typeof(GraphicsOptions)] = graphicsOptions; return context; } public static void SetGraphicsOptions(this Configuration configuration, Action optionsBuilder) { GraphicsOptions graphicsOptions = configuration.GetGraphicsOptions().DeepClone(); optionsBuilder(graphicsOptions); configuration.Properties[typeof(GraphicsOptions)] = graphicsOptions; } public static IImageProcessingContext SetGraphicsOptions(this IImageProcessingContext context, GraphicsOptions options) { context.Properties[typeof(GraphicsOptions)] = options; return context; } public static void SetGraphicsOptions(this Configuration configuration, GraphicsOptions options) { configuration.Properties[typeof(GraphicsOptions)] = options; } public static GraphicsOptions GetGraphicsOptions(this IImageProcessingContext context) { if (context.Properties.TryGetValue(typeof(GraphicsOptions), out var value) && value is GraphicsOptions result) { return result; } return context.Configuration.GetGraphicsOptions(); } public static GraphicsOptions GetGraphicsOptions(this Configuration configuration) { if (configuration.Properties.TryGetValue(typeof(GraphicsOptions), out var value) && value is GraphicsOptions result) { return result; } GraphicsOptions graphicsOptions = new GraphicsOptions(); configuration.Properties[typeof(GraphicsOptions)] = graphicsOptions; return graphicsOptions; } } public class GraphicsOptions : IDeepCloneable { private int antialiasSubpixelDepth = 16; private float blendPercentage = 1f; public bool Antialias { get; set; } = true; public int AntialiasSubpixelDepth { get { return antialiasSubpixelDepth; } set { Guard.MustBeGreaterThanOrEqualTo(value, 0, "AntialiasSubpixelDepth"); antialiasSubpixelDepth = value; } } public float BlendPercentage { get { return blendPercentage; } set { Guard.MustBeBetweenOrEqualTo(value, 0f, 1f, "BlendPercentage"); blendPercentage = value; } } public PixelColorBlendingMode ColorBlendingMode { get; set; } public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } public GraphicsOptions() { } private GraphicsOptions(GraphicsOptions source) { AlphaCompositionMode = source.AlphaCompositionMode; Antialias = source.Antialias; AntialiasSubpixelDepth = source.AntialiasSubpixelDepth; BlendPercentage = source.BlendPercentage; ColorBlendingMode = source.ColorBlendingMode; } public GraphicsOptions DeepClone() { return new GraphicsOptions(this); } } public interface IConfigurationModule { void Configure(Configuration configuration); } public interface IDeepCloneable where T : class { T DeepClone(); } public interface IDeepCloneable { IDeepCloneable DeepClone(); } public interface IImage : IImageInfo, IDisposable { } public interface IImageInfo { PixelTypeInfo PixelType { get; } int Width { get; } int Height { get; } ImageMetadata Metadata { get; } } public abstract class Image : IImage, IImageInfo, IDisposable, IConfigurationProvider { private class EncodeVisitor : IImageVisitor, IImageVisitorAsync { private readonly IImageEncoder encoder; private readonly Stream stream; public EncodeVisitor(IImageEncoder encoder, Stream stream) { this.encoder = encoder; this.stream = stream; } public void Visit(Image image) where TPixel : unmanaged, IPixel { encoder.Encode(image, stream); } public Task VisitAsync(Image image, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return encoder.EncodeAsync(image, stream, cancellationToken); } } private bool isDisposed; private Size size; private readonly Configuration configuration; protected abstract ImageFrameCollection NonGenericFrameCollection { get; } public PixelTypeInfo PixelType { get; } public int Width => size.Width; public int Height => size.Height; public ImageMetadata Metadata { get; } public ImageFrameCollection Frames => NonGenericFrameCollection; Configuration IConfigurationProvider.Configuration => configuration; protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata, Size size) { this.configuration = configuration ?? Configuration.Default; PixelType = pixelType; this.size = size; Metadata = metadata ?? new ImageMetadata(); } internal Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata, int width, int height) : this(configuration, pixelType, metadata, new Size(width, height)) { } public void Dispose() { if (!isDisposed) { Dispose(disposing: true); GC.SuppressFinalize(this); isDisposed = true; } } public void Save(Stream stream, IImageEncoder encoder) { Guard.NotNull(stream, "stream"); Guard.NotNull(encoder, "encoder"); EnsureNotDisposed(); this.AcceptVisitor(new EncodeVisitor(encoder, stream)); } public Task SaveAsync(Stream stream, IImageEncoder encoder, CancellationToken cancellationToken = default(CancellationToken)) { Guard.NotNull(stream, "stream"); Guard.NotNull(encoder, "encoder"); EnsureNotDisposed(); return this.AcceptVisitorAsync(new EncodeVisitor(encoder, stream), cancellationToken); } public Image CloneAs() where TPixel2 : unmanaged, IPixel { return CloneAs(this.GetConfiguration()); } public abstract Image CloneAs(Configuration configuration) where TPixel2 : unmanaged, IPixel; protected void UpdateSize(Size size) { this.size = size; } protected abstract void Dispose(bool disposing); internal void EnsureNotDisposed() { if (isDisposed) { ThrowObjectDisposedException(GetType()); } } internal abstract void Accept(IImageVisitor visitor); internal abstract Task AcceptAsync(IImageVisitorAsync visitor, CancellationToken cancellationToken); [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowObjectDisposedException(Type type) { throw new ObjectDisposedException(type.Name); } internal static Image CreateUninitialized(Configuration configuration, int width, int height, ImageMetadata metadata) where TPixel : unmanaged, IPixel { Buffer2D buffer2D = configuration.MemoryAllocator.Allocate2D(width, height, configuration.PreferContiguousImageBuffers); return new Image(configuration, buffer2D.FastMemoryGroup, width, height, metadata); } private static IImageFormat InternalDetectFormat(Stream stream, Configuration config) { int num = (int)Math.Min(config.MaxHeaderSize, stream.Length); if (num <= 0) { return null; } Span span = ((num <= 512) ? stackalloc byte[num] : ((Span)new byte[num])); Span span2 = span; long position = stream.Position; int num2 = 0; int num3; do { num3 = stream.Read(span2, num2, num - num2); num2 += num3; } while (num2 < num && num3 > 0); stream.Position = position; IImageFormat result = null; foreach (IImageFormatDetector formatDetector in config.ImageFormatsManager.FormatDetectors) { if (formatDetector.HeaderSize <= num) { IImageFormat imageFormat = formatDetector.DetectFormat(span2); if (imageFormat != null) { result = imageFormat; } } } return result; } private static IImageDecoder DiscoverDecoder(Stream stream, Configuration config, out IImageFormat format) { format = InternalDetectFormat(stream, config); if (format == null) { return null; } return config.ImageFormatsManager.FindDecoder(format); } private static (Image Image, IImageFormat Format) Decode(Stream stream, Configuration config, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { IImageFormat format; IImageDecoder imageDecoder = DiscoverDecoder(stream, config, out format); if (imageDecoder == null) { return (Image: null, Format: null); } return (Image: imageDecoder.Decode(config, stream, cancellationToken), Format: format); } private static (Image Image, IImageFormat Format) Decode(Stream stream, Configuration config, CancellationToken cancellationToken = default(CancellationToken)) { IImageFormat format; IImageDecoder imageDecoder = DiscoverDecoder(stream, config, out format); if (imageDecoder == null) { return (Image: null, Format: null); } return (Image: imageDecoder.Decode(config, stream, cancellationToken), Format: format); } private static (IImageInfo ImageInfo, IImageFormat Format) InternalIdentity(Stream stream, Configuration config, CancellationToken cancellationToken = default(CancellationToken)) { if (!(DiscoverDecoder(stream, config, out var format) is IImageInfoDetector imageInfoDetector)) { return (ImageInfo: null, Format: null); } return (ImageInfo: imageInfoDetector?.Identify(config, stream, cancellationToken), Format: format); } public static IImageFormat DetectFormat(byte[] data) { return DetectFormat(Configuration.Default, data); } public static IImageFormat DetectFormat(Configuration configuration, byte[] data) { Guard.NotNull(data, "data"); using MemoryStream stream = new MemoryStream(data, 0, data.Length, writable: false, publiclyVisible: true); return DetectFormat(configuration, stream); } public static IImageInfo Identify(byte[] data) { IImageFormat format; return Identify(data, out format); } public static IImageInfo Identify(byte[] data, out IImageFormat format) { return Identify(Configuration.Default, data, out format); } public static IImageInfo Identify(Configuration configuration, byte[] data, out IImageFormat format) { Guard.NotNull(data, "data"); using MemoryStream stream = new MemoryStream(data, 0, data.Length, writable: false, publiclyVisible: true); return Identify(configuration, stream, out format); } public static Image Load(byte[] data) { return Load(Configuration.Default, data); } public static Image Load(byte[] data) where TPixel : unmanaged, IPixel { return Load(Configuration.Default, data); } public static Image Load(byte[] data, out IImageFormat format) where TPixel : unmanaged, IPixel { return Load(Configuration.Default, data, out format); } public static Image Load(Configuration configuration, byte[] data) where TPixel : unmanaged, IPixel { Guard.NotNull(data, "data"); using MemoryStream stream = new MemoryStream(data, 0, data.Length, writable: false, publiclyVisible: true); return Load(configuration, stream); } public static Image Load(Configuration configuration, byte[] data, out IImageFormat format) where TPixel : unmanaged, IPixel { Guard.NotNull(data, "data"); using MemoryStream stream = new MemoryStream(data, 0, data.Length, writable: false, publiclyVisible: true); return Load(configuration, stream, out format); } public static Image Load(byte[] data, IImageDecoder decoder) where TPixel : unmanaged, IPixel { Guard.NotNull(data, "data"); using MemoryStream stream = new MemoryStream(data, 0, data.Length, writable: false, publiclyVisible: true); return Load(stream, decoder); } public static Image Load(Configuration configuration, byte[] data, IImageDecoder decoder) where TPixel : unmanaged, IPixel { Guard.NotNull(data, "data"); using MemoryStream stream = new MemoryStream(data, 0, data.Length, writable: false, publiclyVisible: true); return Load(configuration, stream, decoder); } public static IImageFormat DetectFormat(ReadOnlySpan data) { return DetectFormat(Configuration.Default, data); } public static IImageFormat DetectFormat(Configuration configuration, ReadOnlySpan data) { Guard.NotNull(configuration, "configuration"); if (configuration.MaxHeaderSize <= 0) { return null; } foreach (IImageFormatDetector formatDetector in configuration.ImageFormatsManager.FormatDetectors) { IImageFormat imageFormat = formatDetector.DetectFormat(data); if (imageFormat != null) { return imageFormat; } } return null; } public static Image Load(ReadOnlySpan data) where TPixel : unmanaged, IPixel { return Load(Configuration.Default, data); } public static Image Load(ReadOnlySpan data, out IImageFormat format) where TPixel : unmanaged, IPixel { return Load(Configuration.Default, data, out format); } public static Image Load(ReadOnlySpan data, IImageDecoder decoder) where TPixel : unmanaged, IPixel { return Load(Configuration.Default, data, decoder); } public unsafe static Image Load(Configuration configuration, ReadOnlySpan data) where TPixel : unmanaged, IPixel { fixed (byte* pointer = data) { using UnmanagedMemoryStream stream = new UnmanagedMemoryStream(pointer, data.Length); return Load(configuration, stream); } } public unsafe static Image Load(Configuration configuration, ReadOnlySpan data, IImageDecoder decoder) where TPixel : unmanaged, IPixel { fixed (byte* pointer = data) { using UnmanagedMemoryStream stream = new UnmanagedMemoryStream(pointer, data.Length); return Load(configuration, stream, decoder); } } public unsafe static Image Load(Configuration configuration, ReadOnlySpan data, out IImageFormat format) where TPixel : unmanaged, IPixel { fixed (byte* pointer = data) { using UnmanagedMemoryStream stream = new UnmanagedMemoryStream(pointer, data.Length); return Load(configuration, stream, out format); } } public static Image Load(byte[] data, out IImageFormat format) { return Load(Configuration.Default, data, out format); } public static Image Load(byte[] data, IImageDecoder decoder) { return Load(Configuration.Default, data, decoder); } public static Image Load(Configuration configuration, byte[] data) { IImageFormat format; return Load(configuration, data, out format); } public static Image Load(Configuration configuration, byte[] data, IImageDecoder decoder) { using MemoryStream stream = new MemoryStream(data, 0, data.Length, writable: false, publiclyVisible: true); return Load(configuration, stream, decoder); } public static Image Load(Configuration configuration, byte[] data, out IImageFormat format) { using MemoryStream stream = new MemoryStream(data, 0, data.Length, writable: false, publiclyVisible: true); return Load(configuration, stream, out format); } public static Image Load(ReadOnlySpan data) { return Load(Configuration.Default, data); } public static Image Load(ReadOnlySpan data, IImageDecoder decoder) { return Load(Configuration.Default, data, decoder); } public static Image Load(ReadOnlySpan data, out IImageFormat format) { return Load(Configuration.Default, data, out format); } public static Image Load(Configuration configuration, ReadOnlySpan data) { IImageFormat format; return Load(configuration, data, out format); } public unsafe static Image Load(Configuration configuration, ReadOnlySpan data, IImageDecoder decoder) { fixed (byte* pointer = data) { using UnmanagedMemoryStream stream = new UnmanagedMemoryStream(pointer, data.Length); return Load(configuration, stream, decoder); } } public unsafe static Image Load(Configuration configuration, ReadOnlySpan data, out IImageFormat format) { fixed (byte* pointer = data) { using UnmanagedMemoryStream stream = new UnmanagedMemoryStream(pointer, data.Length); return Load(configuration, stream, out format); } } public static IImageFormat DetectFormat(string filePath) { return DetectFormat(Configuration.Default, filePath); } public static IImageFormat DetectFormat(Configuration configuration, string filePath) { Guard.NotNull(configuration, "configuration"); using Stream stream = configuration.FileSystem.OpenRead(filePath); return DetectFormat(configuration, stream); } public static IImageInfo Identify(string filePath) { IImageFormat format; return Identify(filePath, out format); } public static IImageInfo Identify(string filePath, out IImageFormat format) { return Identify(Configuration.Default, filePath, out format); } public static IImageInfo Identify(Configuration configuration, string filePath, out IImageFormat format) { Guard.NotNull(configuration, "configuration"); using Stream stream = configuration.FileSystem.OpenRead(filePath); return Identify(configuration, stream, out format); } public static Task IdentifyAsync(string filePath, CancellationToken cancellationToken = default(CancellationToken)) { return IdentifyAsync(Configuration.Default, filePath, cancellationToken); } public static async Task IdentifyAsync(Configuration configuration, string filePath, CancellationToken cancellationToken = default(CancellationToken)) { return (await IdentifyWithFormatAsync(configuration, filePath, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).Item1; } public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(string filePath, CancellationToken cancellationToken = default(CancellationToken)) { return IdentifyWithFormatAsync(Configuration.Default, filePath, cancellationToken); } public static async Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(Configuration configuration, string filePath, CancellationToken cancellationToken = default(CancellationToken)) { Guard.NotNull(configuration, "configuration"); using Stream stream = configuration.FileSystem.OpenRead(filePath); return await IdentifyWithFormatAsync(configuration, stream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static Image Load(string path) { return Load(Configuration.Default, path); } public static Image Load(string path, out IImageFormat format) { return Load(Configuration.Default, path, out format); } public static Image Load(Configuration configuration, string path) { IImageFormat format; return Load(configuration, path, out format); } public static async Task LoadAsync(Configuration configuration, string path, CancellationToken cancellationToken = default(CancellationToken)) { using Stream stream = configuration.FileSystem.OpenRead(path); return (await LoadWithFormatAsync(configuration, stream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).Item1; } public static Image Load(Configuration configuration, string path, IImageDecoder decoder) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(path, "path"); using Stream stream = configuration.FileSystem.OpenRead(path); return Load(configuration, stream, decoder); } public static Task LoadAsync(string path, CancellationToken cancellationToken = default(CancellationToken)) { return LoadAsync(Configuration.Default, path, cancellationToken); } public static Task LoadAsync(string path, IImageDecoder decoder, CancellationToken cancellationToken = default(CancellationToken)) { return LoadAsync(Configuration.Default, path, decoder, cancellationToken); } public static Task> LoadAsync(string path, IImageDecoder decoder, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { return LoadAsync(Configuration.Default, path, decoder, cancellationToken); } public static async Task LoadAsync(Configuration configuration, string path, IImageDecoder decoder, CancellationToken cancellationToken = default(CancellationToken)) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(path, "path"); using Stream stream = configuration.FileSystem.OpenRead(path); return await LoadAsync(configuration, stream, decoder, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static async Task> LoadAsync(Configuration configuration, string path, IImageDecoder decoder, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(path, "path"); using Stream stream = configuration.FileSystem.OpenRead(path); return await LoadAsync(configuration, stream, decoder, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } public static Task> LoadAsync(string path, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { return LoadAsync(Configuration.Default, path, cancellationToken); } public static async Task> LoadAsync(Configuration configuration, string path, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { using Stream stream = configuration.FileSystem.OpenRead(path); return (await LoadWithFormatAsync(configuration, stream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).Item1; } public static Image Load(string path, IImageDecoder decoder) { return Load(Configuration.Default, path, decoder); } public static Image Load(string path) where TPixel : unmanaged, IPixel { return Load(Configuration.Default, path); } public static Image Load(string path, out IImageFormat format) where TPixel : unmanaged, IPixel { return Load(Configuration.Default, path, out format); } public static Image Load(Configuration configuration, string path) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(path, "path"); using Stream stream = configuration.FileSystem.OpenRead(path); return Load(configuration, stream); } public static Image Load(Configuration configuration, string path, out IImageFormat format) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(path, "path"); using Stream stream = configuration.FileSystem.OpenRead(path); return Load(configuration, stream, out format); } public static Image Load(Configuration configuration, string path, out IImageFormat format) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(path, "path"); using Stream stream = configuration.FileSystem.OpenRead(path); return Load(configuration, stream, out format); } public static Image Load(string path, IImageDecoder decoder) where TPixel : unmanaged, IPixel { return Load(Configuration.Default, path, decoder); } public static Image Load(Configuration configuration, string path, IImageDecoder decoder) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(path, "path"); using Stream stream = configuration.FileSystem.OpenRead(path); return Load(configuration, stream, decoder); } public static IImageFormat DetectFormat(Stream stream) { return DetectFormat(Configuration.Default, stream); } public static IImageFormat DetectFormat(Configuration configuration, Stream stream) { return WithSeekableStream(configuration, stream, (Stream s) => InternalDetectFormat(s, configuration)); } public static Task DetectFormatAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return DetectFormatAsync(Configuration.Default, stream, cancellationToken); } public static Task DetectFormatAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return WithSeekableStreamAsync(configuration, stream, (Stream s, CancellationToken _) => InternalDetectFormat(s, configuration), cancellationToken); } public static IImageInfo Identify(Stream stream) { IImageFormat format; return Identify(stream, out format); } public static Task IdentifyAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return IdentifyAsync(Configuration.Default, stream, cancellationToken); } public static IImageInfo Identify(Stream stream, out IImageFormat format) { return Identify(Configuration.Default, stream, out format); } public static IImageInfo Identify(Configuration configuration, Stream stream) { IImageFormat format; return Identify(configuration, stream, out format); } public static async Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return (await IdentifyWithFormatAsync(configuration, stream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).Item1; } public static IImageInfo Identify(Configuration configuration, Stream stream, out IImageFormat format) { (IImageInfo, IImageFormat) tuple = WithSeekableStream(configuration, stream, (Stream s) => InternalIdentity(s, configuration ?? Configuration.Default)); format = tuple.Item2; return tuple.Item1; } public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return IdentifyWithFormatAsync(Configuration.Default, stream, cancellationToken); } public static Task<(IImageInfo ImageInfo, IImageFormat Format)> IdentifyWithFormatAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return WithSeekableStreamAsync(configuration, stream, (Stream s, CancellationToken ct) => InternalIdentity(s, configuration ?? Configuration.Default, ct), cancellationToken); } public static Image Load(Stream stream, out IImageFormat format) { return Load(Configuration.Default, stream, out format); } public static Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return LoadWithFormatAsync(Configuration.Default, stream, cancellationToken); } public static Image Load(Stream stream) { return Load(Configuration.Default, stream); } public static Task LoadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return LoadAsync(Configuration.Default, stream, cancellationToken); } public static Image Load(Stream stream, IImageDecoder decoder) { return Load(Configuration.Default, stream, decoder); } public static Task LoadAsync(Stream stream, IImageDecoder decoder, CancellationToken cancellationToken = default(CancellationToken)) { return LoadAsync(Configuration.Default, stream, decoder, cancellationToken); } public static Image Load(Configuration configuration, Stream stream, IImageDecoder decoder) { Guard.NotNull(decoder, "decoder"); return WithSeekableStream(configuration, stream, (Stream s) => decoder.Decode(configuration, s, default(CancellationToken))); } public static Task LoadAsync(Configuration configuration, Stream stream, IImageDecoder decoder, CancellationToken cancellationToken = default(CancellationToken)) { Guard.NotNull(decoder, "decoder"); return WithSeekableStreamAsync(configuration, stream, (Stream s, CancellationToken ct) => decoder.Decode(configuration, s, ct), cancellationToken); } public static Image Load(Configuration configuration, Stream stream) { IImageFormat format; return Load(configuration, stream, out format); } public static async Task LoadAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { return (await LoadWithFormatAsync(configuration, stream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).Item1; } public static Image Load(Stream stream) where TPixel : unmanaged, IPixel { return Load(Configuration.Default, stream); } public static Task> LoadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { return LoadAsync(Configuration.Default, stream, cancellationToken); } public static Image Load(Stream stream, out IImageFormat format) where TPixel : unmanaged, IPixel { return Load(Configuration.Default, stream, out format); } public static Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { return LoadWithFormatAsync(Configuration.Default, stream, cancellationToken); } public static Image Load(Stream stream, IImageDecoder decoder) where TPixel : unmanaged, IPixel { return WithSeekableStream(Configuration.Default, stream, (Stream s) => decoder.Decode(Configuration.Default, s, default(CancellationToken))); } public static Task> LoadAsync(Stream stream, IImageDecoder decoder, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { return WithSeekableStreamAsync(Configuration.Default, stream, (Stream s, CancellationToken ct) => decoder.Decode(Configuration.Default, s, ct), cancellationToken); } public static Image Load(Configuration configuration, Stream stream, IImageDecoder decoder) where TPixel : unmanaged, IPixel { return WithSeekableStream(configuration, stream, (Stream s) => decoder.Decode(configuration, s, default(CancellationToken))); } public static Task> LoadAsync(Configuration configuration, Stream stream, IImageDecoder decoder, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { return WithSeekableStreamAsync(configuration, stream, (Stream s, CancellationToken ct) => decoder.Decode(configuration, s, ct), cancellationToken); } public static Image Load(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel { IImageFormat format; return Load(configuration, stream, out format); } public static Image Load(Configuration configuration, Stream stream, out IImageFormat format) where TPixel : unmanaged, IPixel { (Image, IImageFormat) tuple = WithSeekableStream(configuration, stream, (Stream s) => Decode(s, configuration)); format = tuple.Item2; if (tuple.Item1 != null) { return tuple.Item1; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Image cannot be loaded. Available decoders:"); foreach (KeyValuePair imageDecoder in configuration.ImageFormatsManager.ImageDecoders) { stringBuilder.AppendFormat(" - {0} : {1}{2}", imageDecoder.Key.Name, imageDecoder.Value.GetType().Name, Environment.NewLine); } throw new UnknownImageFormatException(stringBuilder.ToString()); } public static async Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { (Image, IImageFormat) result = await WithSeekableStreamAsync(configuration, stream, (Stream s, CancellationToken ct) => Decode(s, configuration, ct), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); if (result.Item1 != null) { return result; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Image cannot be loaded. Available decoders:"); foreach (KeyValuePair imageDecoder in configuration.ImageFormatsManager.ImageDecoders) { stringBuilder.AppendFormat(" - {0} : {1}{2}", imageDecoder.Key.Name, imageDecoder.Value.GetType().Name, Environment.NewLine); } throw new UnknownImageFormatException(stringBuilder.ToString()); } public static async Task<(Image Image, IImageFormat Format)> LoadWithFormatAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { (Image, IImageFormat) result = await WithSeekableStreamAsync(configuration, stream, (Stream s, CancellationToken ct) => Decode(s, configuration, ct), cancellationToken).ConfigureAwait(continueOnCapturedContext: false); if (result.Item1 != null) { return result; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Image cannot be loaded. Available decoders:"); foreach (KeyValuePair imageDecoder in configuration.ImageFormatsManager.ImageDecoders) { stringBuilder.AppendFormat(" - {0} : {1}{2}", imageDecoder.Key.Name, imageDecoder.Value.GetType().Name, Environment.NewLine); } throw new UnknownImageFormatException(stringBuilder.ToString()); } public static async Task> LoadAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) where TPixel : unmanaged, IPixel { return (await LoadWithFormatAsync(configuration, stream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false)).Item1; } public static Image Load(Configuration configuration, Stream stream, out IImageFormat format) { (Image, IImageFormat) tuple = WithSeekableStream(configuration, stream, (Stream s) => Decode(s, configuration)); format = tuple.Item2; if (tuple.Item1 != null) { return tuple.Item1; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Image cannot be loaded. Available decoders:"); foreach (KeyValuePair imageDecoder in configuration.ImageFormatsManager.ImageDecoders) { stringBuilder.AppendFormat(" - {0} : {1}{2}", imageDecoder.Key.Name, imageDecoder.Value.GetType().Name, Environment.NewLine); } throw new UnknownImageFormatException(stringBuilder.ToString()); } private static T WithSeekableStream(Configuration configuration, Stream stream, Func action) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(stream, "stream"); if (!stream.CanRead) { throw new NotSupportedException("Cannot read from the stream."); } if (stream.CanSeek) { if (configuration.ReadOrigin == ReadOrigin.Begin) { stream.Position = 0L; } return action(stream); } using ChunkedMemoryStream chunkedMemoryStream = new ChunkedMemoryStream(configuration.MemoryAllocator); stream.CopyTo(chunkedMemoryStream, configuration.StreamProcessingBufferSize); chunkedMemoryStream.Position = 0L; return action(chunkedMemoryStream); } private static async Task WithSeekableStreamAsync(Configuration configuration, Stream stream, Func action, CancellationToken cancellationToken) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(stream, "stream"); if (!stream.CanRead) { throw new NotSupportedException("Cannot read from the stream."); } if (stream.CanSeek && configuration.ReadOrigin == ReadOrigin.Begin) { stream.Position = 0L; } using ChunkedMemoryStream memoryStream = new ChunkedMemoryStream(configuration.MemoryAllocator); await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); memoryStream.Position = 0L; return action(memoryStream, cancellationToken); } public static Image LoadPixelData(TPixel[] data, int width, int height) where TPixel : unmanaged, IPixel { return LoadPixelData(Configuration.Default, data, width, height); } public static Image LoadPixelData(ReadOnlySpan data, int width, int height) where TPixel : unmanaged, IPixel { return LoadPixelData(Configuration.Default, data, width, height); } public static Image LoadPixelData(byte[] data, int width, int height) where TPixel : unmanaged, IPixel { return LoadPixelData(Configuration.Default, data, width, height); } public static Image LoadPixelData(ReadOnlySpan data, int width, int height) where TPixel : unmanaged, IPixel { return LoadPixelData(Configuration.Default, data, width, height); } public static Image LoadPixelData(Configuration configuration, byte[] data, int width, int height) where TPixel : unmanaged, IPixel { return LoadPixelData(configuration, MemoryMarshal.Cast(new ReadOnlySpan(data)), width, height); } public static Image LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height) where TPixel : unmanaged, IPixel { return LoadPixelData(configuration, MemoryMarshal.Cast(data), width, height); } public static Image LoadPixelData(Configuration configuration, TPixel[] data, int width, int height) where TPixel : unmanaged, IPixel { return LoadPixelData(configuration, new ReadOnlySpan(data), width, height); } public static Image LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); int num = width * height; Guard.MustBeGreaterThanOrEqualTo(data.Length, num, "data"); Image image = new Image(configuration, width, height); data = data.Slice(0, num); data.CopyTo(image.Frames.RootFrame.PixelBuffer.FastMemoryGroup); return image; } public static Image WrapMemory(Configuration configuration, Memory pixelMemory, int width, int height, ImageMetadata metadata) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(metadata, "metadata"); Guard.IsTrue(pixelMemory.Length >= width * height, "pixelMemory", "The length of the input memory is less than the specified image size"); MemoryGroup memoryGroup = MemoryGroup.Wrap(pixelMemory); return new Image(configuration, memoryGroup, width, height, metadata); } public static Image WrapMemory(Configuration configuration, Memory pixelMemory, int width, int height) where TPixel : unmanaged, IPixel { return WrapMemory(configuration, pixelMemory, width, height, new ImageMetadata()); } public static Image WrapMemory(Memory pixelMemory, int width, int height) where TPixel : unmanaged, IPixel { return WrapMemory(Configuration.Default, pixelMemory, width, height); } public static Image WrapMemory(Configuration configuration, IMemoryOwner pixelMemoryOwner, int width, int height, ImageMetadata metadata) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(metadata, "metadata"); Guard.IsTrue(pixelMemoryOwner.Memory.Length >= width * height, "pixelMemoryOwner", "The length of the input memory is less than the specified image size"); MemoryGroup memoryGroup = MemoryGroup.Wrap(pixelMemoryOwner); return new Image(configuration, memoryGroup, width, height, metadata); } public static Image WrapMemory(Configuration configuration, IMemoryOwner pixelMemoryOwner, int width, int height) where TPixel : unmanaged, IPixel { return WrapMemory(configuration, pixelMemoryOwner, width, height, new ImageMetadata()); } public static Image WrapMemory(IMemoryOwner pixelMemoryOwner, int width, int height) where TPixel : unmanaged, IPixel { return WrapMemory(Configuration.Default, pixelMemoryOwner, width, height); } public static Image WrapMemory(Configuration configuration, Memory byteMemory, int width, int height, ImageMetadata metadata) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(metadata, "metadata"); ByteMemoryManager byteMemoryManager = new ByteMemoryManager(byteMemory); Guard.IsTrue(byteMemoryManager.Memory.Length >= width * height, "byteMemory", "The length of the input memory is less than the specified image size"); MemoryGroup memoryGroup = MemoryGroup.Wrap(byteMemoryManager.Memory); return new Image(configuration, memoryGroup, width, height, metadata); } public static Image WrapMemory(Configuration configuration, Memory byteMemory, int width, int height) where TPixel : unmanaged, IPixel { return WrapMemory(configuration, byteMemory, width, height, new ImageMetadata()); } public static Image WrapMemory(Memory byteMemory, int width, int height) where TPixel : unmanaged, IPixel { return WrapMemory(Configuration.Default, byteMemory, width, height); } public static Image WrapMemory(Configuration configuration, IMemoryOwner byteMemoryOwner, int width, int height, ImageMetadata metadata) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(metadata, "metadata"); ByteMemoryOwner byteMemoryOwner2 = new ByteMemoryOwner(byteMemoryOwner); Guard.IsTrue(byteMemoryOwner2.Memory.Length >= (long)width * (long)height, "pixelMemoryOwner", "The length of the input memory is less than the specified image size"); MemoryGroup memoryGroup = MemoryGroup.Wrap(byteMemoryOwner2); return new Image(configuration, memoryGroup, width, height, metadata); } public static Image WrapMemory(Configuration configuration, IMemoryOwner byteMemoryOwner, int width, int height) where TPixel : unmanaged, IPixel { return WrapMemory(configuration, byteMemoryOwner, width, height, new ImageMetadata()); } public static Image WrapMemory(IMemoryOwner byteMemoryOwner, int width, int height) where TPixel : unmanaged, IPixel { return WrapMemory(Configuration.Default, byteMemoryOwner, width, height); } public unsafe static Image WrapMemory(Configuration configuration, void* pointer, int width, int height, ImageMetadata metadata) where TPixel : unmanaged, IPixel { Guard.IsFalse(pointer == null, "pointer", "Pointer must be not null"); Guard.NotNull(configuration, "configuration"); Guard.NotNull(metadata, "metadata"); UnmanagedMemoryManager unmanagedMemoryManager = new UnmanagedMemoryManager(pointer, width * height); MemoryGroup memoryGroup = MemoryGroup.Wrap(unmanagedMemoryManager.Memory); return new Image(configuration, memoryGroup, width, height, metadata); } public unsafe static Image WrapMemory(Configuration configuration, void* pointer, int width, int height) where TPixel : unmanaged, IPixel { return WrapMemory(configuration, pointer, width, height, new ImageMetadata()); } public unsafe static Image WrapMemory(void* pointer, int width, int height) where TPixel : unmanaged, IPixel { return WrapMemory(Configuration.Default, pointer, width, height); } } public abstract class ImageFrame : IConfigurationProvider, IDisposable { private readonly Configuration configuration; public int Width { get; private set; } public int Height { get; private set; } public ImageFrameMetadata Metadata { get; } Configuration IConfigurationProvider.Configuration => configuration; protected ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(metadata, "metadata"); this.configuration = configuration ?? Configuration.Default; Width = width; Height = height; Metadata = metadata; } public Size Size() { return new Size(Width, Height); } public Rectangle Bounds() { return new Rectangle(0, 0, Width, Height); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected abstract void Dispose(bool disposing); internal abstract void CopyPixelsTo(MemoryGroup destination) where TDestinationPixel : unmanaged, IPixel; internal void UpdateSize(Size size) { Width = size.Width; Height = size.Height; } internal static ImageFrame LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height) where TPixel : unmanaged, IPixel { return LoadPixelData(configuration, MemoryMarshal.Cast(data), width, height); } internal static ImageFrame LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height) where TPixel : unmanaged, IPixel { int num = width * height; Guard.MustBeGreaterThanOrEqualTo(data.Length, num, "data"); ImageFrame imageFrame = new ImageFrame(configuration, width, height); data = data.Slice(0, num); data.CopyTo(imageFrame.PixelBuffer.FastMemoryGroup); return imageFrame; } } public abstract class ImageFrameCollection : IDisposable, IEnumerable, IEnumerable { private bool isDisposed; public abstract int Count { get; } public ImageFrame RootFrame { get { EnsureNotDisposed(); return NonGenericRootFrame; } } protected abstract ImageFrame NonGenericRootFrame { get; } public ImageFrame this[int index] { get { EnsureNotDisposed(); return NonGenericGetFrame(index); } } public abstract int IndexOf(ImageFrame frame); public ImageFrame InsertFrame(int index, ImageFrame source) { EnsureNotDisposed(); return NonGenericInsertFrame(index, source); } public ImageFrame AddFrame(ImageFrame source) { EnsureNotDisposed(); return NonGenericAddFrame(source); } public abstract void RemoveFrame(int index); public abstract bool Contains(ImageFrame frame); public abstract void MoveFrame(int sourceIndex, int destinationIndex); public Image ExportFrame(int index) { EnsureNotDisposed(); return NonGenericExportFrame(index); } public Image CloneFrame(int index) { EnsureNotDisposed(); return NonGenericCloneFrame(index); } public ImageFrame CreateFrame() { EnsureNotDisposed(); return NonGenericCreateFrame(); } public ImageFrame CreateFrame(Color backgroundColor) { EnsureNotDisposed(); return NonGenericCreateFrame(backgroundColor); } public void Dispose() { if (!isDisposed) { Dispose(disposing: true); GC.SuppressFinalize(this); isDisposed = true; } } public IEnumerator GetEnumerator() { EnsureNotDisposed(); return NonGenericGetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } protected void EnsureNotDisposed() { if (isDisposed) { ThrowObjectDisposedException(GetType()); } } protected abstract void Dispose(bool disposing); protected abstract IEnumerator NonGenericGetEnumerator(); protected abstract ImageFrame NonGenericGetFrame(int index); protected abstract ImageFrame NonGenericInsertFrame(int index, ImageFrame source); protected abstract ImageFrame NonGenericAddFrame(ImageFrame source); protected abstract Image NonGenericExportFrame(int index); protected abstract Image NonGenericCloneFrame(int index); protected abstract ImageFrame NonGenericCreateFrame(); protected abstract ImageFrame NonGenericCreateFrame(Color backgroundColor); [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowObjectDisposedException(Type type) { throw new ObjectDisposedException(type.Name); } } public sealed class ImageFrameCollection : ImageFrameCollection, IEnumerable>, IEnumerable where TPixel : unmanaged, IPixel { private readonly IList> frames = new List>(); private readonly Image parent; public override int Count => frames.Count; public new ImageFrame RootFrame { get { EnsureNotDisposed(); return frames[0]; } } internal ImageFrame RootFrameUnsafe => frames[0]; protected override ImageFrame NonGenericRootFrame => RootFrame; public new ImageFrame this[int index] { get { EnsureNotDisposed(); return frames[index]; } } internal ImageFrameCollection(Image parent, int width, int height, TPixel backgroundColor) { this.parent = parent ?? throw new ArgumentNullException("parent"); frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, backgroundColor)); } internal ImageFrameCollection(Image parent, int width, int height, MemoryGroup memorySource) { this.parent = parent ?? throw new ArgumentNullException("parent"); frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, memorySource)); } internal ImageFrameCollection(Image parent, IEnumerable> frames) { Guard.NotNull(parent, "parent"); Guard.NotNull(frames, "frames"); this.parent = parent; foreach (ImageFrame frame in frames) { ValidateFrame(frame); this.frames.Add(frame); } if (this.frames.Count == 0) { throw new ArgumentException("Must not be empty.", "frames"); } } public override int IndexOf(ImageFrame frame) { EnsureNotDisposed(); if (!(frame is ImageFrame item)) { return -1; } return frames.IndexOf(item); } public int IndexOf(ImageFrame frame) { EnsureNotDisposed(); return frames.IndexOf(frame); } public ImageFrame InsertFrame(int index, ImageFrame source) { EnsureNotDisposed(); ValidateFrame(source); ImageFrame imageFrame = source.Clone(parent.GetConfiguration()); frames.Insert(index, imageFrame); return imageFrame; } public ImageFrame AddFrame(ImageFrame source) { EnsureNotDisposed(); ValidateFrame(source); ImageFrame imageFrame = source.Clone(parent.GetConfiguration()); frames.Add(imageFrame); return imageFrame; } public ImageFrame AddFrame(ReadOnlySpan source) { EnsureNotDisposed(); ImageFrame imageFrame = ImageFrame.LoadPixelData(parent.GetConfiguration(), source, RootFrame.Width, RootFrame.Height); frames.Add(imageFrame); return imageFrame; } public ImageFrame AddFrame(TPixel[] source) { Guard.NotNull(source, "source"); return AddFrame(source.AsSpan()); } public override void RemoveFrame(int index) { EnsureNotDisposed(); if (index == 0 && Count == 1) { throw new InvalidOperationException("Cannot remove last frame."); } ImageFrame imageFrame = frames[index]; frames.RemoveAt(index); imageFrame.Dispose(); } public override bool Contains(ImageFrame frame) { EnsureNotDisposed(); if (frame is ImageFrame item) { return frames.Contains(item); } return false; } public bool Contains(ImageFrame frame) { EnsureNotDisposed(); return frames.Contains(frame); } public override void MoveFrame(int sourceIndex, int destinationIndex) { EnsureNotDisposed(); if (sourceIndex != destinationIndex) { ImageFrame item = frames[sourceIndex]; frames.RemoveAt(sourceIndex); frames.Insert(destinationIndex, item); } } public new Image ExportFrame(int index) { EnsureNotDisposed(); ImageFrame imageFrame = this[index]; if (Count == 1 && frames.Contains(imageFrame)) { throw new InvalidOperationException("Cannot remove last frame."); } frames.Remove(imageFrame); return new Image(parent.GetConfiguration(), parent.Metadata.DeepClone(), new ImageFrame[1] { imageFrame }); } public new Image CloneFrame(int index) { EnsureNotDisposed(); ImageFrame imageFrame = this[index].Clone(); return new Image(parent.GetConfiguration(), parent.Metadata.DeepClone(), new ImageFrame[1] { imageFrame }); } public new ImageFrame CreateFrame() { EnsureNotDisposed(); ImageFrame imageFrame = new ImageFrame(parent.GetConfiguration(), RootFrame.Width, RootFrame.Height); frames.Add(imageFrame); return imageFrame; } protected override IEnumerator NonGenericGetEnumerator() { return frames.GetEnumerator(); } protected override ImageFrame NonGenericGetFrame(int index) { return this[index]; } protected override ImageFrame NonGenericInsertFrame(int index, ImageFrame source) { Guard.NotNull(source, "source"); if (source is ImageFrame source2) { return InsertFrame(index, source2); } ImageFrame imageFrame = CopyNonCompatibleFrame(source); frames.Insert(index, imageFrame); return imageFrame; } protected override ImageFrame NonGenericAddFrame(ImageFrame source) { Guard.NotNull(source, "source"); if (source is ImageFrame source2) { return AddFrame(source2); } ImageFrame imageFrame = CopyNonCompatibleFrame(source); frames.Add(imageFrame); return imageFrame; } protected override Image NonGenericExportFrame(int index) { return ExportFrame(index); } protected override Image NonGenericCloneFrame(int index) { return CloneFrame(index); } protected override ImageFrame NonGenericCreateFrame(Color backgroundColor) { return CreateFrame(backgroundColor.ToPixel()); } protected override ImageFrame NonGenericCreateFrame() { return CreateFrame(); } public ImageFrame CreateFrame(TPixel backgroundColor) { ImageFrame imageFrame = new ImageFrame(parent.GetConfiguration(), RootFrame.Width, RootFrame.Height, backgroundColor); frames.Add(imageFrame); return imageFrame; } IEnumerator> IEnumerable>.GetEnumerator() { return frames.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)frames).GetEnumerator(); } private void ValidateFrame(ImageFrame frame) { Guard.NotNull(frame, "frame"); if (Count != 0 && (RootFrame.Width != frame.Width || RootFrame.Height != frame.Height)) { throw new ArgumentException("Frame must have the same dimensions as the image.", "frame"); } } protected override void Dispose(bool disposing) { if (!disposing) { return; } foreach (ImageFrame frame in frames) { frame.Dispose(); } frames.Clear(); } private ImageFrame CopyNonCompatibleFrame(ImageFrame source) { ImageFrame imageFrame = new ImageFrame(parent.GetConfiguration(), source.Size(), source.Metadata.DeepClone()); source.CopyPixelsTo(imageFrame.PixelBuffer.FastMemoryGroup); return imageFrame; } } public sealed class ImageFrame : ImageFrame, IPixelSource where TPixel : unmanaged, IPixel { private readonly struct RowIntervalOperation : IRowIntervalOperation where TPixel2 : unmanaged, IPixel { private readonly Buffer2D source; private readonly Buffer2D target; private readonly Configuration configuration; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowIntervalOperation(Buffer2D source, Buffer2D target, Configuration configuration) { this.source = source; this.target = target; this.configuration = configuration; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(in RowInterval rows) { for (int i = rows.Min; i < rows.Max; i++) { Span span = source.DangerousGetRowSpan(i); Span destinationPixels = target.DangerousGetRowSpan(i); PixelOperations.Instance.To(configuration, span, destinationPixels); } } void IRowIntervalOperation.Invoke(in RowInterval rows) { Invoke(in rows); } } private bool isDisposed; public Buffer2D PixelBuffer { get; private set; } public TPixel this[int x, int y] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { VerifyCoords(x, y); return PixelBuffer.GetElementUnsafe(x, y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { VerifyCoords(x, y); PixelBuffer.GetElementUnsafe(x, y) = value; } } internal ImageFrame(Configuration configuration, int width, int height) : this(configuration, width, height, new ImageFrameMetadata()) { } internal ImageFrame(Configuration configuration, Size size, ImageFrameMetadata metadata) : this(configuration, size.Width, size.Height, metadata) { } internal ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata) : base(configuration, width, height, metadata) { Guard.MustBeGreaterThan(width, 0, "width"); Guard.MustBeGreaterThan(height, 0, "height"); PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D(width, height, configuration.PreferContiguousImageBuffers, AllocationOptions.Clean); } internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor) : this(configuration, width, height, backgroundColor, new ImageFrameMetadata()) { } internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor, ImageFrameMetadata metadata) : base(configuration, width, height, metadata) { Guard.MustBeGreaterThan(width, 0, "width"); Guard.MustBeGreaterThan(height, 0, "height"); PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D(width, height, configuration.PreferContiguousImageBuffers); Clear(backgroundColor); } internal ImageFrame(Configuration configuration, int width, int height, MemoryGroup memorySource) : this(configuration, width, height, memorySource, new ImageFrameMetadata()) { } internal ImageFrame(Configuration configuration, int width, int height, MemoryGroup memorySource, ImageFrameMetadata metadata) : base(configuration, width, height, metadata) { Guard.MustBeGreaterThan(width, 0, "width"); Guard.MustBeGreaterThan(height, 0, "height"); PixelBuffer = new Buffer2D(memorySource, width, height); } internal ImageFrame(Configuration configuration, ImageFrame source) : base(configuration, source.Width, source.Height, source.Metadata.DeepClone()) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(source, "source"); PixelBuffer = this.GetConfiguration().MemoryAllocator.Allocate2D(source.PixelBuffer.Width, source.PixelBuffer.Height, configuration.PreferContiguousImageBuffers); source.PixelBuffer.FastMemoryGroup.CopyTo(PixelBuffer.FastMemoryGroup); } public void ProcessPixelRows(PixelAccessorAction processPixels) { Guard.NotNull(processPixels, "processPixels"); PixelBuffer.FastMemoryGroup.IncreaseRefCounts(); try { PixelAccessor pixelAccessor = new PixelAccessor(PixelBuffer); processPixels(pixelAccessor); } finally { PixelBuffer.FastMemoryGroup.DecreaseRefCounts(); } } public void ProcessPixelRows(ImageFrame frame2, PixelAccessorAction processPixels) where TPixel2 : unmanaged, IPixel { Guard.NotNull(frame2, "frame2"); Guard.NotNull(processPixels, "processPixels"); PixelBuffer.FastMemoryGroup.IncreaseRefCounts(); frame2.PixelBuffer.FastMemoryGroup.IncreaseRefCounts(); try { PixelAccessor pixelAccessor = new PixelAccessor(PixelBuffer); PixelAccessor pixelAccessor2 = new PixelAccessor(frame2.PixelBuffer); processPixels(pixelAccessor, pixelAccessor2); } finally { frame2.PixelBuffer.FastMemoryGroup.DecreaseRefCounts(); PixelBuffer.FastMemoryGroup.DecreaseRefCounts(); } } public void ProcessPixelRows(ImageFrame frame2, ImageFrame frame3, PixelAccessorAction processPixels) where TPixel2 : unmanaged, IPixel where TPixel3 : unmanaged, IPixel { Guard.NotNull(frame2, "frame2"); Guard.NotNull(frame3, "frame3"); Guard.NotNull(processPixels, "processPixels"); PixelBuffer.FastMemoryGroup.IncreaseRefCounts(); frame2.PixelBuffer.FastMemoryGroup.IncreaseRefCounts(); frame3.PixelBuffer.FastMemoryGroup.IncreaseRefCounts(); try { PixelAccessor pixelAccessor = new PixelAccessor(PixelBuffer); PixelAccessor pixelAccessor2 = new PixelAccessor(frame2.PixelBuffer); PixelAccessor pixelAccessor3 = new PixelAccessor(frame3.PixelBuffer); processPixels(pixelAccessor, pixelAccessor2, pixelAccessor3); } finally { frame3.PixelBuffer.FastMemoryGroup.DecreaseRefCounts(); frame2.PixelBuffer.FastMemoryGroup.DecreaseRefCounts(); PixelBuffer.FastMemoryGroup.DecreaseRefCounts(); } } public void CopyPixelDataTo(Span destination) { this.GetPixelMemoryGroup().CopyTo(destination); } public void CopyPixelDataTo(Span destination) { this.GetPixelMemoryGroup().CopyTo(MemoryMarshal.Cast(destination)); } public bool DangerousTryGetSinglePixelMemory(out Memory memory) { IMemoryGroup pixelMemoryGroup = this.GetPixelMemoryGroup(); if (pixelMemoryGroup.Count > 1) { memory = default(Memory); return false; } memory = pixelMemoryGroup.Single(); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ref TPixel GetPixelReference(int x, int y) { return ref PixelBuffer[x, y]; } internal void CopyTo(Buffer2D target) { if (Size() != target.Size()) { throw new ArgumentException("ImageFrame.CopyTo(): target must be of the same size!", "target"); } PixelBuffer.FastMemoryGroup.CopyTo(target.FastMemoryGroup); } internal void SwapOrCopyPixelsBufferFrom(ImageFrame pixelSource) { Guard.NotNull(pixelSource, "pixelSource"); Buffer2D.SwapOrCopyContent(PixelBuffer, pixelSource.PixelBuffer); UpdateSize(PixelBuffer.Size()); } protected override void Dispose(bool disposing) { if (!isDisposed) { if (disposing) { PixelBuffer?.Dispose(); PixelBuffer = null; } isDisposed = true; } } internal override void CopyPixelsTo(MemoryGroup destination) { if (typeof(TPixel) == typeof(TDestinationPixel)) { PixelBuffer.FastMemoryGroup.TransformTo(destination, delegate(ReadOnlySpan s, Span d) { Span destination2 = MemoryMarshal.Cast(d); s.CopyTo(destination2); }); } else { PixelBuffer.FastMemoryGroup.TransformTo(destination, delegate(ReadOnlySpan s, Span d) { PixelOperations.Instance.To(this.GetConfiguration(), s, d); }); } } public override string ToString() { return $"ImageFrame<{typeof(TPixel).Name}>({base.Width}x{base.Height})"; } internal ImageFrame Clone() { return Clone(this.GetConfiguration()); } internal ImageFrame Clone(Configuration configuration) { return new ImageFrame(configuration, this); } internal ImageFrame CloneAs() where TPixel2 : unmanaged, IPixel { return CloneAs(this.GetConfiguration()); } internal ImageFrame CloneAs(Configuration configuration) where TPixel2 : unmanaged, IPixel { if (typeof(TPixel2) == typeof(TPixel)) { return Clone(configuration) as ImageFrame; } ImageFrame imageFrame = new ImageFrame(configuration, base.Width, base.Height, base.Metadata.DeepClone()); RowIntervalOperation operation = new RowIntervalOperation(PixelBuffer, imageFrame.PixelBuffer, configuration); ParallelRowIterator.IterateRowIntervals(configuration, Bounds(), in operation); return imageFrame; } internal void Clear(TPixel value) { MemoryGroup fastMemoryGroup = PixelBuffer.FastMemoryGroup; if (value.Equals(default(TPixel))) { fastMemoryGroup.Clear(); } else { fastMemoryGroup.Fill(value); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void VerifyCoords(int x, int y) { if ((uint)x >= (uint)base.Width) { ThrowArgumentOutOfRangeException("x"); } if ((uint)y >= (uint)base.Height) { ThrowArgumentOutOfRangeException("y"); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowArgumentOutOfRangeException(string paramName) { throw new ArgumentOutOfRangeException(paramName); } } internal sealed class ImageInfo : IImageInfo { public PixelTypeInfo PixelType { get; } public int Width { get; } public int Height { get; } public ImageMetadata Metadata { get; } public ImageInfo(PixelTypeInfo pixelType, int width, int height, ImageMetadata metadata) { PixelType = pixelType; Width = width; Height = height; Metadata = metadata; } } public static class ImageInfoExtensions { public static Size Size(this IImageInfo info) { return new Size(info.Width, info.Height); } public static Rectangle Bounds(this IImageInfo info) { return new Rectangle(0, 0, info.Width, info.Height); } } public sealed class Image : Image where TPixel : unmanaged, IPixel { private readonly ImageFrameCollection frames; protected override ImageFrameCollection NonGenericFrameCollection => Frames; public new ImageFrameCollection Frames { get { EnsureNotDisposed(); return frames; } } private IPixelSource PixelSourceUnsafe => frames.RootFrameUnsafe; public TPixel this[int x, int y] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { EnsureNotDisposed(); VerifyCoords(x, y); return PixelSourceUnsafe.PixelBuffer.GetElementUnsafe(x, y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { EnsureNotDisposed(); VerifyCoords(x, y); PixelSourceUnsafe.PixelBuffer.GetElementUnsafe(x, y) = value; } } public Image(Configuration configuration, int width, int height) : this(configuration, width, height, new ImageMetadata()) { } public Image(Configuration configuration, int width, int height, TPixel backgroundColor) : this(configuration, width, height, backgroundColor, new ImageMetadata()) { } public Image(int width, int height, TPixel backgroundColor) : this(Configuration.Default, width, height, backgroundColor, new ImageMetadata()) { } public Image(int width, int height) : this(Configuration.Default, width, height) { } internal Image(Configuration configuration, int width, int height, ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { frames = new ImageFrameCollection(this, width, height, default(TPixel)); } internal Image(Configuration configuration, Buffer2D pixelBuffer, ImageMetadata metadata) : this(configuration, pixelBuffer.FastMemoryGroup, pixelBuffer.Width, pixelBuffer.Height, metadata) { } internal Image(Configuration configuration, MemoryGroup memoryGroup, int width, int height, ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { frames = new ImageFrameCollection(this, width, height, memoryGroup); } internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { frames = new ImageFrameCollection(this, width, height, backgroundColor); } internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames) : base(configuration, PixelTypeInfo.Create(), metadata, ValidateFramesAndGetSize(frames)) { this.frames = new ImageFrameCollection(this, frames); } public void ProcessPixelRows(PixelAccessorAction processPixels) { Guard.NotNull(processPixels, "processPixels"); Buffer2D pixelBuffer = Frames.RootFrame.PixelBuffer; pixelBuffer.FastMemoryGroup.IncreaseRefCounts(); try { PixelAccessor pixelAccessor = new PixelAccessor(pixelBuffer); processPixels(pixelAccessor); } finally { pixelBuffer.FastMemoryGroup.DecreaseRefCounts(); } } public void ProcessPixelRows(Image image2, PixelAccessorAction processPixels) where TPixel2 : unmanaged, IPixel { Guard.NotNull(image2, "image2"); Guard.NotNull(processPixels, "processPixels"); Buffer2D pixelBuffer = Frames.RootFrame.PixelBuffer; Buffer2D pixelBuffer2 = image2.Frames.RootFrame.PixelBuffer; pixelBuffer.FastMemoryGroup.IncreaseRefCounts(); pixelBuffer2.FastMemoryGroup.IncreaseRefCounts(); try { PixelAccessor pixelAccessor = new PixelAccessor(pixelBuffer); PixelAccessor pixelAccessor2 = new PixelAccessor(pixelBuffer2); processPixels(pixelAccessor, pixelAccessor2); } finally { pixelBuffer2.FastMemoryGroup.DecreaseRefCounts(); pixelBuffer.FastMemoryGroup.DecreaseRefCounts(); } } public void ProcessPixelRows(Image image2, Image image3, PixelAccessorAction processPixels) where TPixel2 : unmanaged, IPixel where TPixel3 : unmanaged, IPixel { Guard.NotNull(image2, "image2"); Guard.NotNull(image3, "image3"); Guard.NotNull(processPixels, "processPixels"); Buffer2D pixelBuffer = Frames.RootFrame.PixelBuffer; Buffer2D pixelBuffer2 = image2.Frames.RootFrame.PixelBuffer; Buffer2D pixelBuffer3 = image3.Frames.RootFrame.PixelBuffer; pixelBuffer.FastMemoryGroup.IncreaseRefCounts(); pixelBuffer2.FastMemoryGroup.IncreaseRefCounts(); pixelBuffer3.FastMemoryGroup.IncreaseRefCounts(); try { PixelAccessor pixelAccessor = new PixelAccessor(pixelBuffer); PixelAccessor pixelAccessor2 = new PixelAccessor(pixelBuffer2); PixelAccessor pixelAccessor3 = new PixelAccessor(pixelBuffer3); processPixels(pixelAccessor, pixelAccessor2, pixelAccessor3); } finally { pixelBuffer3.FastMemoryGroup.DecreaseRefCounts(); pixelBuffer2.FastMemoryGroup.DecreaseRefCounts(); pixelBuffer.FastMemoryGroup.DecreaseRefCounts(); } } public void CopyPixelDataTo(Span destination) { this.GetPixelMemoryGroup().CopyTo(destination); } public void CopyPixelDataTo(Span destination) { this.GetPixelMemoryGroup().CopyTo(MemoryMarshal.Cast(destination)); } public bool DangerousTryGetSinglePixelMemory(out Memory memory) { IMemoryGroup pixelMemoryGroup = this.GetPixelMemoryGroup(); if (pixelMemoryGroup.Count > 1) { memory = default(Memory); return false; } memory = pixelMemoryGroup.Single(); return true; } public Image Clone() { return Clone(this.GetConfiguration()); } public Image Clone(Configuration configuration) { EnsureNotDisposed(); ImageFrame[] array = new ImageFrame[frames.Count]; for (int i = 0; i < array.Length; i++) { array[i] = frames[i].Clone(configuration); } return new Image(configuration, base.Metadata.DeepClone(), array); } public override Image CloneAs(Configuration configuration) { EnsureNotDisposed(); ImageFrame[] array = new ImageFrame[frames.Count]; for (int i = 0; i < array.Length; i++) { array[i] = frames[i].CloneAs(configuration); } return new Image(configuration, base.Metadata.DeepClone(), array); } protected override void Dispose(bool disposing) { if (disposing) { frames.Dispose(); } } public override string ToString() { return $"Image<{typeof(TPixel).Name}>: {base.Width}x{base.Height}"; } internal override void Accept(IImageVisitor visitor) { EnsureNotDisposed(); visitor.Visit(this); } internal override Task AcceptAsync(IImageVisitorAsync visitor, CancellationToken cancellationToken) { EnsureNotDisposed(); return visitor.VisitAsync(this, cancellationToken); } internal void SwapOrCopyPixelsBuffersFrom(Image pixelSource) { Guard.NotNull(pixelSource, "pixelSource"); EnsureNotDisposed(); ImageFrameCollection imageFrameCollection = pixelSource.Frames; for (int i = 0; i < frames.Count; i++) { frames[i].SwapOrCopyPixelsBufferFrom(imageFrameCollection[i]); } UpdateSize(pixelSource.Size()); } private static Size ValidateFramesAndGetSize(IEnumerable> frames) { Guard.NotNull(frames, "frames"); ImageFrame imageFrame = frames.FirstOrDefault(); if (imageFrame == null) { throw new ArgumentException("Must not be empty.", "frames"); } Size rootSize = imageFrame.Size(); if (frames.Any((ImageFrame f) => f.Size() != rootSize)) { throw new ArgumentException("The provided frames must be of the same size.", "frames"); } return rootSize; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void VerifyCoords(int x, int y) { if ((uint)x >= (uint)base.Width) { ThrowArgumentOutOfRangeException("x"); } if ((uint)y >= (uint)base.Height) { ThrowArgumentOutOfRangeException("y"); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowArgumentOutOfRangeException(string paramName) { throw new ArgumentOutOfRangeException(paramName); } } public sealed class IndexedImageFrame : IPixelSource, IDisposable where TPixel : unmanaged, IPixel { private Buffer2D pixelBuffer; private IMemoryOwner paletteOwner; private bool isDisposed; public Configuration Configuration { get; } public int Width { get; } public int Height { get; } public ReadOnlyMemory Palette { get; } Buffer2D IPixelSource.PixelBuffer => pixelBuffer; internal IndexedImageFrame(Configuration configuration, int width, int height, ReadOnlyMemory palette) { Guard.NotNull(configuration, "configuration"); Guard.MustBeLessThanOrEqualTo(palette.Length, 256, "palette"); Guard.MustBeGreaterThan(width, 0, "width"); Guard.MustBeGreaterThan(height, 0, "height"); Configuration = configuration; Width = width; Height = height; pixelBuffer = configuration.MemoryAllocator.Allocate2D(width, height); paletteOwner = configuration.MemoryAllocator.Allocate(palette.Length); palette.Span.CopyTo(paletteOwner.GetSpan()); Palette = paletteOwner.Memory.Slice(0, palette.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan DangerousGetRowSpan(int rowIndex) { return GetWritablePixelRowSpanUnsafe(rowIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetWritablePixelRowSpanUnsafe(int rowIndex) { return pixelBuffer.DangerousGetRowSpan(rowIndex); } public void Dispose() { if (!isDisposed) { isDisposed = true; pixelBuffer.Dispose(); paletteOwner.Dispose(); pixelBuffer = null; paletteOwner = null; } } } public delegate void PixelAccessorAction(PixelAccessor pixelAccessor) where TPixel : unmanaged, IPixel; public delegate void PixelAccessorAction(PixelAccessor pixelAccessor1, PixelAccessor pixelAccessor2) where TPixel1 : unmanaged, IPixel where TPixel2 : unmanaged, IPixel; public delegate void PixelAccessorAction(PixelAccessor pixelAccessor1, PixelAccessor pixelAccessor2, PixelAccessor pixelAccessor3) where TPixel1 : unmanaged, IPixel where TPixel2 : unmanaged, IPixel where TPixel3 : unmanaged, IPixel; public ref struct PixelAccessor where TPixel : unmanaged, IPixel { private Buffer2D buffer; public int Width => buffer.Width; public int Height => buffer.Height; internal PixelAccessor(Buffer2D buffer) { this.buffer = buffer; } public Span GetRowSpan(int rowIndex) { return buffer.DangerousGetRowSpan(rowIndex); } } internal enum RgbaComponent { R, G, B, A } public struct ColorMatrix : IEquatable { public float M11; public float M12; public float M13; public float M14; public float M21; public float M22; public float M23; public float M24; public float M31; public float M32; public float M33; public float M34; public float M41; public float M42; public float M43; public float M44; public float M51; public float M52; public float M53; public float M54; public static ColorMatrix Identity { get; } = new ColorMatrix(1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f); public bool IsIdentity { get { if (M11 == 1f && M22 == 1f && M33 == 1f && M44 == 1f && M12 == 0f && M13 == 0f && M14 == 0f && M21 == 0f && M23 == 0f && M24 == 0f && M31 == 0f && M32 == 0f && M34 == 0f && M41 == 0f && M42 == 0f && M43 == 0f && M51 == 0f && M52 == 0f && M53 == 0f) { return M54 == 0f; } return false; } } public ColorMatrix(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44, float m51, float m52, float m53, float m54) { M11 = m11; M12 = m12; M13 = m13; M14 = m14; M21 = m21; M22 = m22; M23 = m23; M24 = m24; M31 = m31; M32 = m32; M33 = m33; M34 = m34; M41 = m41; M42 = m42; M43 = m43; M44 = m44; M51 = m51; M52 = m52; M53 = m53; M54 = m54; } public static ColorMatrix operator +(ColorMatrix value1, ColorMatrix value2) { return new ColorMatrix { M11 = value1.M11 + value2.M11, M12 = value1.M12 + value2.M12, M13 = value1.M13 + value2.M13, M14 = value1.M14 + value2.M14, M21 = value1.M21 + value2.M21, M22 = value1.M22 + value2.M22, M23 = value1.M23 + value2.M23, M24 = value1.M24 + value2.M24, M31 = value1.M31 + value2.M31, M32 = value1.M32 + value2.M32, M33 = value1.M33 + value2.M33, M34 = value1.M34 + value2.M34, M41 = value1.M41 + value2.M41, M42 = value1.M42 + value2.M42, M43 = value1.M43 + value2.M43, M44 = value1.M44 + value2.M44, M51 = value1.M51 + value2.M51, M52 = value1.M52 + value2.M52, M53 = value1.M53 + value2.M53, M54 = value1.M54 + value2.M54 }; } public static ColorMatrix operator -(ColorMatrix value1, ColorMatrix value2) { return new ColorMatrix { M11 = value1.M11 - value2.M11, M12 = value1.M12 - value2.M12, M13 = value1.M13 - value2.M13, M14 = value1.M14 - value2.M14, M21 = value1.M21 - value2.M21, M22 = value1.M22 - value2.M22, M23 = value1.M23 - value2.M23, M24 = value1.M24 - value2.M24, M31 = value1.M31 - value2.M31, M32 = value1.M32 - value2.M32, M33 = value1.M33 - value2.M33, M34 = value1.M34 - value2.M34, M41 = value1.M41 - value2.M41, M42 = value1.M42 - value2.M42, M43 = value1.M43 - value2.M43, M44 = value1.M44 - value2.M44, M51 = value1.M51 - value2.M51, M52 = value1.M52 - value2.M52, M53 = value1.M53 - value2.M53, M54 = value1.M54 - value2.M54 }; } public static ColorMatrix operator -(ColorMatrix value) { return new ColorMatrix { M11 = 0f - value.M11, M12 = 0f - value.M12, M13 = 0f - value.M13, M14 = 0f - value.M14, M21 = 0f - value.M21, M22 = 0f - value.M22, M23 = 0f - value.M23, M24 = 0f - value.M24, M31 = 0f - value.M31, M32 = 0f - value.M32, M33 = 0f - value.M33, M34 = 0f - value.M34, M41 = 0f - value.M41, M42 = 0f - value.M42, M43 = 0f - value.M43, M44 = 0f - value.M44, M51 = 0f - value.M51, M52 = 0f - value.M52, M53 = 0f - value.M53, M54 = 0f - value.M54 }; } public static ColorMatrix operator *(ColorMatrix value1, ColorMatrix value2) { return new ColorMatrix { M11 = value1.M11 * value2.M11 + value1.M12 * value2.M21 + value1.M13 * value2.M31 + value1.M14 * value2.M41, M12 = value1.M11 * value2.M12 + value1.M12 * value2.M22 + value1.M13 * value2.M32 + value1.M14 * value2.M42, M13 = value1.M11 * value2.M13 + value1.M12 * value2.M23 + value1.M13 * value2.M33 + value1.M14 * value2.M43, M14 = value1.M11 * value2.M14 + value1.M12 * value2.M24 + value1.M13 * value2.M34 + value1.M14 * value2.M44, M21 = value1.M21 * value2.M11 + value1.M22 * value2.M21 + value1.M23 * value2.M31 + value1.M24 * value2.M41, M22 = value1.M21 * value2.M12 + value1.M22 * value2.M22 + value1.M23 * value2.M32 + value1.M24 * value2.M42, M23 = value1.M21 * value2.M13 + value1.M22 * value2.M23 + value1.M23 * value2.M33 + value1.M24 * value2.M43, M24 = value1.M21 * value2.M14 + value1.M22 * value2.M24 + value1.M23 * value2.M34 + value1.M24 * value2.M44, M31 = value1.M31 * value2.M11 + value1.M32 * value2.M21 + value1.M33 * value2.M31 + value1.M34 * value2.M41, M32 = value1.M31 * value2.M12 + value1.M32 * value2.M22 + value1.M33 * value2.M32 + value1.M34 * value2.M42, M33 = value1.M31 * value2.M13 + value1.M32 * value2.M23 + value1.M33 * value2.M33 + value1.M34 * value2.M43, M34 = value1.M31 * value2.M14 + value1.M32 * value2.M24 + value1.M33 * value2.M34 + value1.M34 * value2.M44, M41 = value1.M41 * value2.M11 + value1.M42 * value2.M21 + value1.M43 * value2.M31 + value1.M44 * value2.M41, M42 = value1.M41 * value2.M12 + value1.M42 * value2.M22 + value1.M43 * value2.M32 + value1.M44 * value2.M42, M43 = value1.M41 * value2.M13 + value1.M42 * value2.M23 + value1.M43 * value2.M33 + value1.M44 * value2.M43, M44 = value1.M41 * value2.M14 + value1.M42 * value2.M24 + value1.M43 * value2.M34 + value1.M44 * value2.M44, M51 = value1.M51 * value2.M11 + value1.M52 * value2.M21 + value1.M53 * value2.M31 + value1.M54 * value2.M41 + value2.M51, M52 = value1.M51 * value2.M12 + value1.M52 * value2.M22 + value1.M53 * value2.M32 + value1.M54 * value2.M52 + value2.M52, M53 = value1.M51 * value2.M13 + value1.M52 * value2.M23 + value1.M53 * value2.M33 + value1.M54 * value2.M53 + value2.M53, M54 = value1.M51 * value2.M14 + value1.M52 * value2.M24 + value1.M53 * value2.M34 + value1.M54 * value2.M54 + value2.M54 }; } public static ColorMatrix operator *(ColorMatrix value1, float value2) { return new ColorMatrix { M11 = value1.M11 * value2, M12 = value1.M12 * value2, M13 = value1.M13 * value2, M14 = value1.M14 * value2, M21 = value1.M21 * value2, M22 = value1.M22 * value2, M23 = value1.M23 * value2, M24 = value1.M24 * value2, M31 = value1.M31 * value2, M32 = value1.M32 * value2, M33 = value1.M33 * value2, M34 = value1.M34 * value2, M41 = value1.M41 * value2, M42 = value1.M42 * value2, M43 = value1.M43 * value2, M44 = value1.M44 * value2, M51 = value1.M51 * value2, M52 = value1.M52 * value2, M53 = value1.M53 * value2, M54 = value1.M54 * value2 }; } public static bool operator ==(ColorMatrix value1, ColorMatrix value2) { return value1.Equals(value2); } public static bool operator !=(ColorMatrix value1, ColorMatrix value2) { return !value1.Equals(value2); } public override bool Equals(object obj) { if (obj is ColorMatrix other) { return Equals(other); } return false; } public bool Equals(ColorMatrix other) { if (M11 == other.M11 && M12 == other.M12 && M13 == other.M13 && M14 == other.M14 && M21 == other.M21 && M22 == other.M22 && M23 == other.M23 && M24 == other.M24 && M31 == other.M31 && M32 == other.M32 && M33 == other.M33 && M34 == other.M34 && M41 == other.M41 && M42 == other.M42 && M43 == other.M43 && M44 == other.M44 && M51 == other.M51 && M52 == other.M52 && M53 == other.M53) { return M54 == other.M54; } return false; } public override int GetHashCode() { HashCode hashCode = default(HashCode); hashCode.Add(M11); hashCode.Add(M12); hashCode.Add(M13); hashCode.Add(M14); hashCode.Add(M21); hashCode.Add(M22); hashCode.Add(M23); hashCode.Add(M24); hashCode.Add(M31); hashCode.Add(M32); hashCode.Add(M33); hashCode.Add(M34); hashCode.Add(M41); hashCode.Add(M42); hashCode.Add(M43); hashCode.Add(M44); hashCode.Add(M51); hashCode.Add(M52); hashCode.Add(M53); hashCode.Add(M54); return hashCode.ToHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} {{M51:{16} M52:{17} M53:{18} M54:{19}}} }}", M11.ToString(currentCulture), M12.ToString(currentCulture), M13.ToString(currentCulture), M14.ToString(currentCulture), M21.ToString(currentCulture), M22.ToString(currentCulture), M23.ToString(currentCulture), M24.ToString(currentCulture), M31.ToString(currentCulture), M32.ToString(currentCulture), M33.ToString(currentCulture), M34.ToString(currentCulture), M41.ToString(currentCulture), M42.ToString(currentCulture), M43.ToString(currentCulture), M44.ToString(currentCulture), M51.ToString(currentCulture), M52.ToString(currentCulture), M53.ToString(currentCulture), M54.ToString(currentCulture)); } } internal readonly struct Complex64 : IEquatable { public readonly float Real; public readonly float Imaginary; public Complex64(float real, float imaginary) { Real = real; Imaginary = imaginary; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Complex64 operator *(Complex64 value, float scalar) { return new Complex64(value.Real * scalar, value.Imaginary * scalar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ComplexVector4 operator *(Complex64 value, Vector4 vector) { return new ComplexVector4 { Real = vector * value.Real, Imaginary = vector * value.Imaginary }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ComplexVector4 operator *(Complex64 value, ComplexVector4 vector) { Vector4 real = value.Real * vector.Real - value.Imaginary * vector.Imaginary; Vector4 imaginary = value.Real * vector.Imaginary + value.Imaginary * vector.Real; return new ComplexVector4 { Real = real, Imaginary = imaginary }; } public bool Equals(Complex64 other) { if (Real.Equals(other.Real)) { return Imaginary.Equals(other.Imaginary); } return false; } public override bool Equals(object obj) { if (obj is Complex64 other) { return Equals(other); } return false; } public override int GetHashCode() { return (Real.GetHashCode() * 397) ^ Imaginary.GetHashCode(); } public override string ToString() { return string.Format("{0}{1}{2}j", Real, (Imaginary >= 0f) ? "+" : string.Empty, Imaginary); } } internal struct ComplexVector4 : IEquatable { public Vector4 Real; public Vector4 Imaginary; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Sum(ComplexVector4 value) { Real += value.Real; Imaginary += value.Imaginary; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 WeightedSum(float a, float b) { return Real * a + Imaginary * b; } public bool Equals(ComplexVector4 other) { if (Real.Equals(other.Real)) { return Imaginary.Equals(other.Imaginary); } return false; } public override bool Equals(object obj) { if (obj is ComplexVector4 other) { return Equals(other); } return false; } public override int GetHashCode() { return (Real.GetHashCode() * 397) ^ Imaginary.GetHashCode(); } } public readonly struct DenseMatrix : IEquatable> where T : struct, IEquatable { public readonly T[] Data; public readonly int Columns; public readonly int Rows; public readonly Size Size; public readonly int Count; public Span Span => new Span(Data); public ref T this[int row, int column] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return ref Data[row * Columns + column]; } } public DenseMatrix(int length) : this(length, length) { } public DenseMatrix(int columns, int rows) { Guard.MustBeGreaterThan(columns, 0, "columns"); Guard.MustBeGreaterThan(rows, 0, "rows"); Rows = rows; Columns = columns; Size = new Size(columns, rows); Count = columns * rows; Data = new T[Columns * Rows]; } public DenseMatrix(T[,] data) { Guard.NotNull(data, "data"); int length = data.GetLength(0); int length2 = data.GetLength(1); Guard.MustBeGreaterThan(length, 0, "Rows"); Guard.MustBeGreaterThan(length2, 0, "Columns"); Rows = length; Columns = length2; Size = new Size(length2, length); Count = Columns * Rows; Data = new T[Columns * Rows]; for (int i = 0; i < Rows; i++) { for (int j = 0; j < Columns; j++) { this[i, j] = data[i, j]; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator DenseMatrix(T[,] data) { return new DenseMatrix(data); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator T[,](in DenseMatrix data) { T[,] array = new T[data.Rows, data.Columns]; for (int i = 0; i < data.Rows; i++) { for (int j = 0; j < data.Columns; j++) { ref T reference = ref array[i, j]; reference = data[i, j]; } } return array; } public static bool operator ==(DenseMatrix left, DenseMatrix right) { return left.Equals(right); } public static bool operator !=(DenseMatrix left, DenseMatrix right) { return !(left == right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public DenseMatrix Transpose() { DenseMatrix result = new DenseMatrix(Rows, Columns); for (int i = 0; i < Rows; i++) { for (int j = 0; j < Columns; j++) { result[j, i] = this[i, j]; } } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Fill(T value) { Span.Fill(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { Span.Clear(); } [Conditional("DEBUG")] private void CheckCoordinates(int row, int column) { if (row < 0 || row >= Rows) { throw new ArgumentOutOfRangeException("row", row, $"{row} is outwith the matrix bounds."); } if (column < 0 || column >= Columns) { throw new ArgumentOutOfRangeException("column", column, $"{column} is outwith the matrix bounds."); } } public override bool Equals(object obj) { if (obj is DenseMatrix other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(DenseMatrix other) { if (Columns == other.Columns && Rows == other.Rows) { return Span.SequenceEqual(other.Span); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { HashCode hashCode = default(HashCode); hashCode.Add(Columns); hashCode.Add(Rows); Span span = Span; for (int i = 0; i < span.Length; i++) { hashCode.Add(span[i]); } return hashCode.ToHashCode(); } } internal readonly struct LongRational : IEquatable { public long Numerator { get; } public long Denominator { get; } public bool IsIndeterminate { get { if (Denominator == 0L) { return Numerator == 0; } return false; } } public bool IsInteger => Denominator == 1; public bool IsNegativeInfinity { get { if (Denominator == 0L) { return Numerator == -1; } return false; } } public bool IsPositiveInfinity { get { if (Denominator == 0L) { return Numerator == 1; } return false; } } public bool IsZero { get { if (Denominator == 1) { return Numerator == 0; } return false; } } public LongRational(long numerator, long denominator) { Numerator = numerator; Denominator = denominator; } public bool Equals(LongRational other) { if (Numerator == other.Numerator) { return Denominator == other.Denominator; } return false; } public override int GetHashCode() { return ((Numerator * 397) ^ Denominator).GetHashCode(); } public override string ToString() { return ToString(CultureInfo.InvariantCulture); } public string ToString(IFormatProvider provider) { if (IsIndeterminate) { return "[ Indeterminate ]"; } if (IsPositiveInfinity) { return "[ PositiveInfinity ]"; } if (IsNegativeInfinity) { return "[ NegativeInfinity ]"; } if (IsZero) { return "0"; } if (IsInteger) { return Numerator.ToString(provider); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Numerator.ToString(provider)); stringBuilder.Append('/'); stringBuilder.Append(Denominator.ToString(provider)); return stringBuilder.ToString(); } public static LongRational FromDouble(double value, bool bestPrecision) { if (double.IsNaN(value)) { return new LongRational(0L, 0L); } if (double.IsPositiveInfinity(value)) { return new LongRational(1L, 0L); } if (double.IsNegativeInfinity(value)) { return new LongRational(-1L, 0L); } long num = 1L; long num2 = 1L; double num3 = Math.Abs(value); double num4 = (double)num / (double)num2; double num5 = (bestPrecision ? double.Epsilon : 1E-06); while (Math.Abs(num4 - num3) > num5) { if (num4 < num3) { num++; } else { num2++; num = (int)(num3 * (double)num2); } num4 = (double)num / (double)num2; } if (value < 0.0) { num *= -1; } return new LongRational(num, num2).Simplify(); } private static long GreatestCommonDivisor(long left, long right) { if (right != 0L) { return GreatestCommonDivisor(right, left % right); } return left; } public LongRational Simplify() { if (IsIndeterminate || IsNegativeInfinity || IsPositiveInfinity || IsInteger || IsZero) { return this; } if (Numerator == 0L) { return new LongRational(0L, 0L); } if (Numerator == Denominator) { return new LongRational(1L, 1L); } long num = GreatestCommonDivisor(Math.Abs(Numerator), Math.Abs(Denominator)); if (num > 1) { return new LongRational(Numerator / num, Denominator / num); } return this; } } public static class Matrix3x2Extensions { public static Matrix3x2 CreateTranslation(PointF position) { return Matrix3x2.CreateTranslation(position); } public static Matrix3x2 CreateScale(float xScale, float yScale, PointF centerPoint) { return Matrix3x2.CreateScale(xScale, yScale, centerPoint); } public static Matrix3x2 CreateScale(SizeF scales) { return Matrix3x2.CreateScale(scales); } public static Matrix3x2 CreateScale(SizeF scales, PointF centerPoint) { return Matrix3x2.CreateScale(scales, centerPoint); } public static Matrix3x2 CreateScale(float scale, PointF centerPoint) { return Matrix3x2.CreateScale(scale, centerPoint); } public static Matrix3x2 CreateSkewDegrees(float degreesX, float degreesY) { return Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY)); } public static Matrix3x2 CreateSkew(float radiansX, float radiansY, PointF centerPoint) { return Matrix3x2.CreateSkew(radiansX, radiansY, centerPoint); } public static Matrix3x2 CreateSkewDegrees(float degreesX, float degreesY, PointF centerPoint) { return Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), centerPoint); } public static Matrix3x2 CreateRotationDegrees(float degrees) { return Matrix3x2.CreateRotation(GeometryUtilities.DegreeToRadian(degrees)); } public static Matrix3x2 CreateRotation(float radians, PointF centerPoint) { return Matrix3x2.CreateRotation(radians, centerPoint); } public static Matrix3x2 CreateRotationDegrees(float degrees, PointF centerPoint) { return Matrix3x2.CreateRotation(GeometryUtilities.DegreeToRadian(degrees), centerPoint); } } [StructLayout(LayoutKind.Explicit)] public struct Number : IEquatable, IComparable { [FieldOffset(0)] private readonly int signedValue; [FieldOffset(0)] private readonly uint unsignedValue; [FieldOffset(4)] private readonly bool isSigned; public Number(int value) { this = default(Number); signedValue = value; isSigned = true; } public Number(uint value) { this = default(Number); unsignedValue = value; isSigned = false; } public static implicit operator Number(int value) { return new Number(value); } public static implicit operator Number(uint value) { return new Number(value); } public static implicit operator Number(ushort value) { return new Number((uint)value); } public static explicit operator int(Number number) { if (!number.isSigned) { return (int)Numerics.Clamp(number.unsignedValue, 0u, 2147483647u); } return number.signedValue; } public static explicit operator uint(Number number) { if (!number.isSigned) { return number.unsignedValue; } return (uint)Numerics.Clamp(number.signedValue, 0, int.MaxValue); } public static explicit operator ushort(Number number) { if (!number.isSigned) { return (ushort)Numerics.Clamp(number.unsignedValue, 0u, 65535u); } return (ushort)Numerics.Clamp(number.signedValue, 0, 65535); } public static bool operator ==(Number left, Number right) { return object.Equals(left, right); } public static bool operator !=(Number left, Number right) { return !object.Equals(left, right); } public static bool operator >(Number left, Number right) { return left.CompareTo(right) == 1; } public static bool operator <(Number left, Number right) { return left.CompareTo(right) == -1; } public static bool operator >=(Number left, Number right) { return left.CompareTo(right) >= 0; } public static bool operator <=(Number left, Number right) { return left.CompareTo(right) <= 0; } public int CompareTo(Number other) { if (!isSigned) { return unsignedValue.CompareTo(other.unsignedValue); } return signedValue.CompareTo(other.signedValue); } public override bool Equals(object obj) { if (obj is Number other) { return Equals(other); } return false; } public bool Equals(Number other) { if (isSigned != other.isSigned) { return false; } if (!isSigned) { return unsignedValue.Equals(other.unsignedValue); } return signedValue.Equals(other.signedValue); } public override int GetHashCode() { if (!isSigned) { return unsignedValue.GetHashCode(); } return signedValue.GetHashCode(); } public override string ToString() { return ToString(CultureInfo.InvariantCulture); } public string ToString(IFormatProvider provider) { if (!isSigned) { return unsignedValue.ToString(provider); } return signedValue.ToString(provider); } } public struct Point : IEquatable { public static readonly Point Empty; public int X { get; set; } public int Y { get; set; } [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty => Equals(Empty); public Point(int value) { this = default(Point); X = LowInt16(value); Y = HighInt16(value); } public Point(int x, int y) { this = default(Point); X = x; Y = y; } public Point(Size size) { X = size.Width; Y = size.Height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator PointF(Point point) { return new PointF(point.X, point.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Vector2(Point point) { return new Vector2(point.X, point.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Size(Point point) { return new Size(point.X, point.Y); } public static Point operator -(Point value) { return new Point(-value.X, -value.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point operator +(Point point, Size size) { return Add(point, size); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point operator -(Point point, Size size) { return Subtract(point, size); } public static Point operator *(int left, Point right) { return Multiply(right, left); } public static Point operator *(Point left, int right) { return Multiply(left, right); } public static Point operator /(Point left, int right) { return new Point(left.X / right, left.Y / right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Point left, Point right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Point left, Point right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Add(Point point, Size size) { return new Point(point.X + size.Width, point.Y + size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Multiply(Point point, int value) { return new Point(point.X * value, point.Y * value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Subtract(Point point, Size size) { return new Point(point.X - size.Width, point.Y - size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Ceiling(PointF point) { return new Point((int)MathF.Ceiling(point.X), (int)MathF.Ceiling(point.Y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Round(PointF point) { return new Point((int)MathF.Round(point.X), (int)MathF.Round(point.Y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Round(Vector2 vector) { return new Point((int)MathF.Round(vector.X), (int)MathF.Round(vector.Y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Truncate(PointF point) { return new Point((int)point.X, (int)point.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Transform(Point point, Matrix3x2 matrix) { return Round(Vector2.Transform(new Vector2(point.X, point.Y), matrix)); } public void Deconstruct(out int x, out int y) { x = X; y = Y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(int dx, int dy) { X += dx; Y += dy; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(Point point) { Offset(point.X, point.Y); } public override int GetHashCode() { return HashCode.Combine(X, Y); } public override string ToString() { return $"Point [ X={X}, Y={Y} ]"; } public override bool Equals(object obj) { if (obj is Point other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Point other) { if (X.Equals(other.X)) { return Y.Equals(other.Y); } return false; } private static short HighInt16(int n) { return (short)((n >> 16) & 0xFFFF); } private static short LowInt16(int n) { return (short)(n & 0xFFFF); } } public struct PointF : IEquatable { public static readonly PointF Empty; public float X { get; set; } public float Y { get; set; } [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty => Equals(Empty); public PointF(float x, float y) { this = default(PointF); X = x; Y = y; } public PointF(SizeF size) { X = size.Width; Y = size.Height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator PointF(Vector2 vector) { return new PointF(vector.X, vector.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Vector2(PointF point) { return new Vector2(point.X, point.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Point(PointF point) { return Point.Truncate(point); } public static PointF operator -(PointF value) { return new PointF(0f - value.X, 0f - value.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF operator +(PointF point, SizeF size) { return Add(point, size); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF operator -(PointF point, PointF size) { return Subtract(point, size); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF operator +(PointF point, PointF size) { return Add(point, size); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF operator -(PointF point, SizeF size) { return Subtract(point, size); } public static PointF operator *(float left, PointF right) { return Multiply(right, left); } public static PointF operator *(PointF left, float right) { return Multiply(left, right); } public static PointF operator /(PointF left, float right) { return new PointF(left.X / right, left.Y / right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(PointF left, PointF right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(PointF left, PointF right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF Add(PointF point, SizeF size) { return new PointF(point.X + size.Width, point.Y + size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF Add(PointF point, PointF pointb) { return new PointF(point.X + pointb.X, point.Y + pointb.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF Subtract(PointF point, SizeF size) { return new PointF(point.X - size.Width, point.Y - size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF Subtract(PointF point, PointF pointb) { return new PointF(point.X - pointb.X, point.Y - pointb.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF Multiply(PointF point, float right) { return new PointF(point.X * right, point.Y * right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF Transform(PointF point, Matrix3x2 matrix) { return Vector2.Transform(point, matrix); } public void Deconstruct(out float x, out float y) { x = X; y = Y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(float dx, float dy) { X += dx; Y += dy; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(PointF point) { Offset(point.X, point.Y); } public override int GetHashCode() { return HashCode.Combine(X, Y); } public override string ToString() { return $"PointF [ X={X}, Y={Y} ]"; } public override bool Equals(object obj) { if (obj is PointF) { return Equals((PointF)obj); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(PointF other) { if (X.Equals(other.X)) { return Y.Equals(other.Y); } return false; } } public readonly struct Rational : IEquatable { public uint Numerator { get; } public uint Denominator { get; } public Rational(uint value) : this(value, 1u) { } public Rational(uint numerator, uint denominator) : this(numerator, denominator, simplify: true) { } public Rational(uint numerator, uint denominator, bool simplify) { if (simplify) { LongRational longRational = new LongRational(numerator, denominator).Simplify(); Numerator = (uint)longRational.Numerator; Denominator = (uint)longRational.Denominator; } else { Numerator = numerator; Denominator = denominator; } } public Rational(double value) : this(value, bestPrecision: false) { } public Rational(double value, bool bestPrecision) { LongRational longRational = LongRational.FromDouble(Math.Abs(value), bestPrecision); Numerator = (uint)longRational.Numerator; Denominator = (uint)longRational.Denominator; } public static bool operator ==(Rational left, Rational right) { return left.Equals(right); } public static bool operator !=(Rational left, Rational right) { return !left.Equals(right); } public static Rational FromDouble(double value) { return new Rational(value, bestPrecision: false); } public static Rational FromDouble(double value, bool bestPrecision) { return new Rational(value, bestPrecision); } public override bool Equals(object obj) { if (obj is Rational other) { return Equals(other); } return false; } public bool Equals(Rational other) { LongRational longRational = new LongRational(Numerator, Denominator); LongRational other2 = new LongRational(other.Numerator, other.Denominator); return longRational.Equals(other2); } public override int GetHashCode() { return new LongRational(Numerator, Denominator).GetHashCode(); } public double ToDouble() { return (double)Numerator / (double)Denominator; } public float ToSingle() { return (float)Numerator / (float)Denominator; } public override string ToString() { return ToString(CultureInfo.InvariantCulture); } public string ToString(IFormatProvider provider) { return new LongRational(Numerator, Denominator).ToString(provider); } } public struct Rectangle : IEquatable { public static readonly Rectangle Empty; public int X { get; set; } public int Y { get; set; } public int Width { get; set; } public int Height { get; set; } [EditorBrowsable(EditorBrowsableState.Never)] public Point Location { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return new Point(X, Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { X = value.X; Y = value.Y; } } [EditorBrowsable(EditorBrowsableState.Never)] public Size Size { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return new Size(Width, Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Width = value.Width; Height = value.Height; } } [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty => Equals(Empty); public int Top => Y; public int Right { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return X + Width; } } public int Bottom { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Y + Height; } } public int Left => X; public Rectangle(int x, int y, int width, int height) { X = x; Y = y; Width = width; Height = height; } public Rectangle(Point point, Size size) { X = point.X; Y = point.Y; Width = size.Width; Height = size.Height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator RectangleF(Rectangle rectangle) { return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Vector4(Rectangle rectangle) { return new Vector4(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rectangle left, Rectangle right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rectangle left, Rectangle right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle FromLTRB(int left, int top, int right, int bottom) { return new Rectangle(left, top, right - left, bottom - top); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Center(Rectangle rectangle) { return new Point(rectangle.Left + rectangle.Width / 2, rectangle.Top + rectangle.Height / 2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle Intersect(Rectangle a, Rectangle b) { int num = Math.Max(a.X, b.X); int num2 = Math.Min(a.Right, b.Right); int num3 = Math.Max(a.Y, b.Y); int num4 = Math.Min(a.Bottom, b.Bottom); if (num2 >= num && num4 >= num3) { return new Rectangle(num, num3, num2 - num, num4 - num3); } return Empty; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle Inflate(Rectangle rectangle, int x, int y) { Rectangle result = rectangle; result.Inflate(x, y); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle Ceiling(RectangleF rectangle) { return new Rectangle((int)MathF.Ceiling(rectangle.X), (int)MathF.Ceiling(rectangle.Y), (int)MathF.Ceiling(rectangle.Width), (int)MathF.Ceiling(rectangle.Height)); } public static RectangleF Transform(Rectangle rectangle, Matrix3x2 matrix) { PointF pointF = Point.Transform(new Point(rectangle.Right, rectangle.Bottom), matrix); PointF pointF2 = Point.Transform(rectangle.Location, matrix); return new RectangleF(pointF2, new SizeF(pointF - pointF2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle Truncate(RectangleF rectangle) { return new Rectangle((int)rectangle.X, (int)rectangle.Y, (int)rectangle.Width, (int)rectangle.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle Round(RectangleF rectangle) { return new Rectangle((int)MathF.Round(rectangle.X), (int)MathF.Round(rectangle.Y), (int)MathF.Round(rectangle.Width), (int)MathF.Round(rectangle.Height)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle Union(Rectangle a, Rectangle b) { int num = Math.Min(a.X, b.X); int num2 = Math.Max(a.Right, b.Right); int num3 = Math.Min(a.Y, b.Y); int num4 = Math.Max(a.Bottom, b.Bottom); return new Rectangle(num, num3, num2 - num, num4 - num3); } public void Deconstruct(out int x, out int y, out int width, out int height) { x = X; y = Y; width = Width; height = Height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Intersect(Rectangle rectangle) { Rectangle rectangle2 = Intersect(rectangle, this); X = rectangle2.X; Y = rectangle2.Y; Width = rectangle2.Width; Height = rectangle2.Height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Inflate(int width, int height) { X -= width; Y -= height; Width += 2 * width; Height += 2 * height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Inflate(Size size) { Inflate(size.Width, size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(int x, int y) { if (X <= x && x < Right && Y <= y) { return y < Bottom; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(Point point) { return Contains(point.X, point.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(Rectangle rectangle) { if (X <= rectangle.X && rectangle.Right <= Right && Y <= rectangle.Y) { return rectangle.Bottom <= Bottom; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IntersectsWith(Rectangle rectangle) { if (rectangle.X < Right && X < rectangle.Right && rectangle.Y < Bottom) { return Y < rectangle.Bottom; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(Point point) { Offset(point.X, point.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(int dx, int dy) { X += dx; Y += dy; } public override int GetHashCode() { return HashCode.Combine(X, Y, Width, Height); } public override string ToString() { return $"Rectangle [ X={X}, Y={Y}, Width={Width}, Height={Height} ]"; } public override bool Equals(object obj) { if (obj is Rectangle other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Rectangle other) { if (X.Equals(other.X) && Y.Equals(other.Y) && Width.Equals(other.Width)) { return Height.Equals(other.Height); } return false; } } public struct RectangleF : IEquatable { public static readonly RectangleF Empty; public float X { get; set; } public float Y { get; set; } public float Width { get; set; } public float Height { get; set; } [EditorBrowsable(EditorBrowsableState.Never)] public PointF Location { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return new PointF(X, Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { X = value.X; Y = value.Y; } } [EditorBrowsable(EditorBrowsableState.Never)] public SizeF Size { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return new SizeF(Width, Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Width = value.Width; Height = value.Height; } } [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty { get { if (!(Width <= 0f)) { return Height <= 0f; } return true; } } public float Top => Y; public float Right { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return X + Width; } } public float Bottom { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Y + Height; } } public float Left => X; public RectangleF(float x, float y, float width, float height) { X = x; Y = y; Width = width; Height = height; } public RectangleF(PointF point, SizeF size) { X = point.X; Y = point.Y; Width = size.Width; Height = size.Height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Rectangle(RectangleF rectangle) { return Rectangle.Truncate(rectangle); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(RectangleF left, RectangleF right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(RectangleF left, RectangleF right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RectangleF FromLTRB(float left, float top, float right, float bottom) { return new RectangleF(left, top, right - left, bottom - top); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PointF Center(RectangleF rectangle) { return new PointF(rectangle.Left + rectangle.Width / 2f, rectangle.Top + rectangle.Height / 2f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RectangleF Intersect(RectangleF a, RectangleF b) { float num = MathF.Max(a.X, b.X); float num2 = MathF.Min(a.Right, b.Right); float num3 = MathF.Max(a.Y, b.Y); float num4 = MathF.Min(a.Bottom, b.Bottom); if (num2 >= num && num4 >= num3) { return new RectangleF(num, num3, num2 - num, num4 - num3); } return Empty; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RectangleF Inflate(RectangleF rectangle, float x, float y) { RectangleF result = rectangle; result.Inflate(x, y); return result; } public static RectangleF Transform(RectangleF rectangle, Matrix3x2 matrix) { PointF pointF = PointF.Transform(new PointF(rectangle.Right, rectangle.Bottom), matrix); PointF pointF2 = PointF.Transform(rectangle.Location, matrix); return new RectangleF(pointF2, new SizeF(pointF - pointF2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RectangleF Union(RectangleF a, RectangleF b) { float num = MathF.Min(a.X, b.X); float num2 = MathF.Max(a.Right, b.Right); float num3 = MathF.Min(a.Y, b.Y); float num4 = MathF.Max(a.Bottom, b.Bottom); return new RectangleF(num, num3, num2 - num, num4 - num3); } public void Deconstruct(out float x, out float y, out float width, out float height) { x = X; y = Y; width = Width; height = Height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Intersect(RectangleF rectangle) { RectangleF rectangleF = Intersect(rectangle, this); X = rectangleF.X; Y = rectangleF.Y; Width = rectangleF.Width; Height = rectangleF.Height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Inflate(float width, float height) { X -= width; Y -= height; Width += 2f * width; Height += 2f * height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Inflate(SizeF size) { Inflate(size.Width, size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(float x, float y) { if (X <= x && x < Right && Y <= y) { return y < Bottom; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(PointF point) { return Contains(point.X, point.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(RectangleF rectangle) { if (X <= rectangle.X && rectangle.Right <= Right && Y <= rectangle.Y) { return rectangle.Bottom <= Bottom; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IntersectsWith(RectangleF rectangle) { if (rectangle.X < Right && X < rectangle.Right && rectangle.Y < Bottom) { return Y < rectangle.Bottom; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(PointF point) { Offset(point.X, point.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(float dx, float dy) { X += dx; Y += dy; } public override int GetHashCode() { return HashCode.Combine(X, Y, Width, Height); } public override string ToString() { return $"RectangleF [ X={X}, Y={Y}, Width={Width}, Height={Height} ]"; } public override bool Equals(object obj) { if (obj is RectangleF other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(RectangleF other) { if (X.Equals(other.X) && Y.Equals(other.Y) && Width.Equals(other.Width)) { return Height.Equals(other.Height); } return false; } } public readonly struct SignedRational : IEquatable { public int Numerator { get; } public int Denominator { get; } public SignedRational(int value) : this(value, 1) { } public SignedRational(int numerator, int denominator) : this(numerator, denominator, simplify: true) { } public SignedRational(int numerator, int denominator, bool simplify) { if (simplify) { LongRational longRational = new LongRational(numerator, denominator).Simplify(); Numerator = (int)longRational.Numerator; Denominator = (int)longRational.Denominator; } else { Numerator = numerator; Denominator = denominator; } } public SignedRational(double value) : this(value, bestPrecision: false) { } public SignedRational(double value, bool bestPrecision) { LongRational longRational = LongRational.FromDouble(value, bestPrecision); Numerator = (int)longRational.Numerator; Denominator = (int)longRational.Denominator; } public static bool operator ==(SignedRational left, SignedRational right) { return left.Equals(right); } public static bool operator !=(SignedRational left, SignedRational right) { return !left.Equals(right); } public static SignedRational FromDouble(double value) { return new SignedRational(value, bestPrecision: false); } public static SignedRational FromDouble(double value, bool bestPrecision) { return new SignedRational(value, bestPrecision); } public override bool Equals(object obj) { if (obj is SignedRational other) { return Equals(other); } return false; } public bool Equals(SignedRational other) { LongRational longRational = new LongRational(Numerator, Denominator); LongRational other2 = new LongRational(other.Numerator, other.Denominator); return longRational.Equals(other2); } public override int GetHashCode() { return new LongRational(Numerator, Denominator).GetHashCode(); } public double ToDouble() { return (double)Numerator / (double)Denominator; } public override string ToString() { return ToString(CultureInfo.InvariantCulture); } public string ToString(IFormatProvider provider) { return new LongRational(Numerator, Denominator).ToString(provider); } } public struct Size : IEquatable { public static readonly Size Empty; public int Width { get; set; } public int Height { get; set; } [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty => Equals(Empty); public Size(int value) { this = default(Size); Width = value; Height = value; } public Size(int width, int height) { Width = width; Height = height; } public Size(Size size) { this = default(Size); Width = size.Width; Height = size.Height; } public Size(Point point) { Width = point.X; Height = point.Y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator SizeF(Size size) { return new SizeF(size.Width, size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Point(Size size) { return new Point(size.Width, size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size operator +(Size left, Size right) { return Add(left, right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size operator -(Size left, Size right) { return Subtract(left, right); } public static Size operator *(int left, Size right) { return Multiply(right, left); } public static Size operator *(Size left, int right) { return Multiply(left, right); } public static Size operator /(Size left, int right) { return new Size(left.Width / right, left.Height / right); } public static SizeF operator *(float left, Size right) { return Multiply(right, left); } public static SizeF operator *(Size left, float right) { return Multiply(left, right); } public static SizeF operator /(Size left, float right) { return new SizeF((float)left.Width / right, (float)left.Height / right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Size left, Size right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Size left, Size right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size Add(Size left, Size right) { return new Size(left.Width + right.Width, left.Height + right.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size Subtract(Size left, Size right) { return new Size(left.Width - right.Width, left.Height - right.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size Ceiling(SizeF size) { return new Size((int)MathF.Ceiling(size.Width), (int)MathF.Ceiling(size.Height)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size Round(SizeF size) { return new Size((int)MathF.Round(size.Width), (int)MathF.Round(size.Height)); } public static SizeF Transform(Size size, Matrix3x2 matrix) { Vector2 vector = Vector2.Transform(new Vector2(size.Width, size.Height), matrix); return new SizeF(vector.X, vector.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size Truncate(SizeF size) { return new Size((int)size.Width, (int)size.Height); } public void Deconstruct(out int width, out int height) { width = Width; height = Height; } public override int GetHashCode() { return HashCode.Combine(Width, Height); } public override string ToString() { return $"Size [ Width={Width}, Height={Height} ]"; } public override bool Equals(object obj) { if (obj is Size other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Size other) { if (Width.Equals(other.Width)) { return Height.Equals(other.Height); } return false; } private static Size Multiply(Size size, int multiplier) { return new Size(size.Width * multiplier, size.Height * multiplier); } private static SizeF Multiply(Size size, float multiplier) { return new SizeF((float)size.Width * multiplier, (float)size.Height * multiplier); } } public struct SizeF : IEquatable { public static readonly SizeF Empty; public float Width { get; set; } public float Height { get; set; } [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty => Equals(Empty); public SizeF(float width, float height) { Width = width; Height = height; } public SizeF(SizeF size) { this = default(SizeF); Width = size.Width; Height = size.Height; } public SizeF(PointF point) { Width = point.X; Height = point.Y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Vector2(SizeF point) { return new Vector2(point.Width, point.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Size(SizeF size) { return new Size((int)size.Width, (int)size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator PointF(SizeF size) { return new PointF(size.Width, size.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SizeF operator +(SizeF left, SizeF right) { return Add(left, right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SizeF operator -(SizeF left, SizeF right) { return Subtract(left, right); } public static SizeF operator *(float left, SizeF right) { return Multiply(right, left); } public static SizeF operator *(SizeF left, float right) { return Multiply(left, right); } public static SizeF operator /(SizeF left, float right) { return new SizeF(left.Width / right, left.Height / right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(SizeF left, SizeF right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(SizeF left, SizeF right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SizeF Add(SizeF left, SizeF right) { return new SizeF(left.Width + right.Width, left.Height + right.Height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SizeF Subtract(SizeF left, SizeF right) { return new SizeF(left.Width - right.Width, left.Height - right.Height); } public static SizeF Transform(SizeF size, Matrix3x2 matrix) { Vector2 vector = Vector2.Transform(new Vector2(size.Width, size.Height), matrix); return new SizeF(vector.X, vector.Y); } public void Deconstruct(out float width, out float height) { width = Width; height = Height; } public override int GetHashCode() { return HashCode.Combine(Width, Height); } public override string ToString() { return $"SizeF [ Width={Width}, Height={Height} ]"; } public override bool Equals(object obj) { if (obj is SizeF) { return Equals((SizeF)obj); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(SizeF other) { if (Width.Equals(other.Width)) { return Height.Equals(other.Height); } return false; } private static SizeF Multiply(SizeF size, float multiplier) { return new SizeF(size.Width * multiplier, size.Height * multiplier); } } internal readonly struct ValueSize : IEquatable { public enum ValueSizeType { Absolute, PercentageOfWidth, PercentageOfHeight } public float Value { get; } public ValueSizeType Type { get; } public ValueSize(float value, ValueSizeType type) { if (type != ValueSizeType.Absolute) { Guard.MustBeBetweenOrEqualTo(value, 0f, 1f, "value"); } Value = value; Type = type; } public static implicit operator ValueSize(float f) { return Absolute(f); } public static ValueSize PercentageOfWidth(float percentage) { return new ValueSize(percentage, ValueSizeType.PercentageOfWidth); } public static ValueSize PercentageOfHeight(float percentage) { return new ValueSize(percentage, ValueSizeType.PercentageOfHeight); } public static ValueSize Absolute(float value) { return new ValueSize(value, ValueSizeType.Absolute); } public float Calculate(Size size) { return Type switch { ValueSizeType.PercentageOfWidth => Value * (float)size.Width, ValueSizeType.PercentageOfHeight => Value * (float)size.Height, _ => Value, }; } public override string ToString() { return $"{Value} - {Type}"; } public override bool Equals(object obj) { if (obj is ValueSize other) { return Equals(other); } return false; } public bool Equals(ValueSize other) { if (Type == other.Type) { return Value.Equals(other.Value); } return false; } public override int GetHashCode() { return HashCode.Combine(Value, Type); } } public enum ReadOrigin { Begin, Current } } namespace SixLabors.ImageSharp.Processing { public static class AdaptiveThresholdExtensions { public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source) { return source.ApplyProcessor(new AdaptiveThresholdProcessor()); } public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, float thresholdLimit) { return source.ApplyProcessor(new AdaptiveThresholdProcessor(thresholdLimit)); } public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower) { return source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower)); } public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, float thresholdLimit) { return source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit)); } public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, Rectangle rectangle) { return source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower), rectangle); } public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, float thresholdLimit, Rectangle rectangle) { return source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit), rectangle); } } public class AffineTransformBuilder { private readonly List> matrixFactories = new List>(); public AffineTransformBuilder PrependRotationDegrees(float degrees) { return PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); } public AffineTransformBuilder PrependRotationRadians(float radians) { return Prepend((Size size) => TransformUtils.CreateRotationMatrixRadians(radians, size)); } public AffineTransformBuilder PrependRotationDegrees(float degrees, Vector2 origin) { return PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees), origin); } public AffineTransformBuilder PrependRotationRadians(float radians, Vector2 origin) { return PrependMatrix(Matrix3x2.CreateRotation(radians, origin)); } public AffineTransformBuilder AppendRotationDegrees(float degrees) { return AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); } public AffineTransformBuilder AppendRotationRadians(float radians) { return Append((Size size) => TransformUtils.CreateRotationMatrixRadians(radians, size)); } public AffineTransformBuilder AppendRotationDegrees(float degrees, Vector2 origin) { return AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees), origin); } public AffineTransformBuilder AppendRotationRadians(float radians, Vector2 origin) { return AppendMatrix(Matrix3x2.CreateRotation(radians, origin)); } public AffineTransformBuilder PrependScale(float scale) { return PrependMatrix(Matrix3x2.CreateScale(scale)); } public AffineTransformBuilder PrependScale(SizeF scale) { return PrependScale((Vector2)scale); } public AffineTransformBuilder PrependScale(Vector2 scales) { return PrependMatrix(Matrix3x2.CreateScale(scales)); } public AffineTransformBuilder AppendScale(float scale) { return AppendMatrix(Matrix3x2.CreateScale(scale)); } public AffineTransformBuilder AppendScale(SizeF scales) { return AppendScale((Vector2)scales); } public AffineTransformBuilder AppendScale(Vector2 scales) { return AppendMatrix(Matrix3x2.CreateScale(scales)); } public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY) { return Prepend((Size size) => TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size)); } public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY) { return Prepend((Size size) => TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size)); } public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY, Vector2 origin) { return PrependSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), origin); } public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY, Vector2 origin) { return PrependMatrix(Matrix3x2.CreateSkew(radiansX, radiansY, origin)); } public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY) { return Append((Size size) => TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size)); } public AffineTransformBuilder AppendSkewRadians(float radiansX, float radiansY) { return Append((Size size) => TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size)); } public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY, Vector2 origin) { return AppendSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), origin); } public AffineTransformBuilder AppendSkewRadians(float radiansX, float radiansY, Vector2 origin) { return AppendMatrix(Matrix3x2.CreateSkew(radiansX, radiansY, origin)); } public AffineTransformBuilder PrependTranslation(PointF position) { return PrependTranslation((Vector2)position); } public AffineTransformBuilder PrependTranslation(Vector2 position) { return PrependMatrix(Matrix3x2.CreateTranslation(position)); } public AffineTransformBuilder AppendTranslation(PointF position) { return AppendTranslation((Vector2)position); } public AffineTransformBuilder AppendTranslation(Vector2 position) { return AppendMatrix(Matrix3x2.CreateTranslation(position)); } public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix) { CheckDegenerate(matrix); return Prepend((Size _) => matrix); } public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix) { CheckDegenerate(matrix); return Append((Size _) => matrix); } public Matrix3x2 BuildMatrix(Size sourceSize) { return BuildMatrix(new Rectangle(Point.Empty, sourceSize)); } public Matrix3x2 BuildMatrix(Rectangle sourceRectangle) { Guard.MustBeGreaterThan(sourceRectangle.Width, 0, "sourceRectangle"); Guard.MustBeGreaterThan(sourceRectangle.Height, 0, "sourceRectangle"); Matrix3x2 matrix3x = Matrix3x2.CreateTranslation(-sourceRectangle.Location); Size size = sourceRectangle.Size; foreach (Func matrixFactory in matrixFactories) { matrix3x *= matrixFactory(size); } CheckDegenerate(matrix3x); return matrix3x; } private static void CheckDegenerate(Matrix3x2 matrix) { if (TransformUtils.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } } private AffineTransformBuilder Prepend(Func factory) { matrixFactories.Insert(0, factory); return this; } private AffineTransformBuilder Append(Func factory) { matrixFactories.Add(factory); return this; } } public enum AnchorPositionMode { Center, Top, Bottom, Left, Right, TopLeft, TopRight, BottomRight, BottomLeft } public enum BinaryThresholdMode { Luminance, Saturation, MaxChroma } public enum ColorBlindnessMode { Achromatomaly, Achromatopsia, Deuteranomaly, Deuteranopia, Protanomaly, Protanopia, Tritanomaly, Tritanopia } internal class DefaultImageProcessorContext : IInternalImageProcessingContext, IImageProcessingContext where TPixel : unmanaged, IPixel { private readonly bool mutate; private readonly Image source; private Image destination; public Configuration Configuration { get; } public IDictionary Properties { get; } = new ConcurrentDictionary(); public DefaultImageProcessorContext(Configuration configuration, Image source, bool mutate) { Configuration = configuration; this.mutate = mutate; this.source = source; if (this.mutate) { destination = source; } } public Image GetResultImage() { if (!mutate && destination == null) { destination = source.Clone(); } return destination; } public Size GetCurrentSize() { return GetCurrentBounds().Size; } public IImageProcessingContext ApplyProcessor(IImageProcessor processor) { return ApplyProcessor(processor, GetCurrentBounds()); } public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { if (!mutate && destination == null) { if (processor is ICloningImageProcessor cloningImageProcessor) { using ICloningImageProcessor cloningImageProcessor2 = cloningImageProcessor.CreatePixelSpecificCloningProcessor(Configuration, source, rectangle); destination = cloningImageProcessor2.CloneAndExecute(); return this; } destination = source.Clone(); } using IImageProcessor imageProcessor = processor.CreatePixelSpecificProcessor(Configuration, destination, rectangle); imageProcessor.Execute(); return this; } private Rectangle GetCurrentBounds() { return destination?.Bounds() ?? source.Bounds(); } } public static class BinaryDitherExtensions { public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IDither dither) { return source.BinaryDither(dither, Color.White, Color.Black); } public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IDither dither, Color upperColor, Color lowerColor) { return source.ApplyProcessor(new PaletteDitherProcessor(dither, new Color[2] { upperColor, lowerColor })); } public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IDither dither, Rectangle rectangle) { return source.BinaryDither(dither, Color.White, Color.Black, rectangle); } public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IDither dither, Color upperColor, Color lowerColor, Rectangle rectangle) { return source.ApplyProcessor(new PaletteDitherProcessor(dither, new Color[2] { upperColor, lowerColor }), rectangle); } } public static class BinaryThresholdExtensions { public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold) { return source.ApplyProcessor(new BinaryThresholdProcessor(threshold, BinaryThresholdMode.Luminance)); } public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, BinaryThresholdMode mode) { return source.ApplyProcessor(new BinaryThresholdProcessor(threshold, mode)); } public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, Rectangle rectangle) { return source.ApplyProcessor(new BinaryThresholdProcessor(threshold, BinaryThresholdMode.Luminance), rectangle); } public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, BinaryThresholdMode mode, Rectangle rectangle) { return source.ApplyProcessor(new BinaryThresholdProcessor(threshold, mode), rectangle); } public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, Color upperColor, Color lowerColor) { return source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor, BinaryThresholdMode.Luminance)); } public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, Color upperColor, Color lowerColor, BinaryThresholdMode mode) { return source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor, mode)); } public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, Color upperColor, Color lowerColor, Rectangle rectangle) { return source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor, BinaryThresholdMode.Luminance), rectangle); } public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, Color upperColor, Color lowerColor, BinaryThresholdMode mode, Rectangle rectangle) { return source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor, mode), rectangle); } } public static class BokehBlurExtensions { public static IImageProcessingContext BokehBlur(this IImageProcessingContext source) { return source.ApplyProcessor(new BokehBlurProcessor()); } public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma) { return source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma)); } public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle) { return source.ApplyProcessor(new BokehBlurProcessor(), rectangle); } public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle) { return source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle); } } public static class BoxBlurExtensions { public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) { return source.ApplyProcessor(new BoxBlurProcessor()); } public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) { return source.ApplyProcessor(new BoxBlurProcessor(radius)); } public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) { return source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); } } public static class DetectEdgesExtensions { public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) { return source.DetectEdges(KnownEdgeDetectorKernels.Sobel); } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) { return source.DetectEdges(KnownEdgeDetectorKernels.Sobel, rectangle); } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetector2DKernel kernel) { return source.DetectEdges(kernel, grayscale: true); } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetector2DKernel kernel, bool grayscale) { EdgeDetector2DProcessor processor = new EdgeDetector2DProcessor(kernel, grayscale); source.ApplyProcessor(processor); return source; } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetector2DKernel kernel, Rectangle rectangle) { return source.DetectEdges(kernel, grayscale: true, rectangle); } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetector2DKernel kernel, bool grayscale, Rectangle rectangle) { EdgeDetector2DProcessor processor = new EdgeDetector2DProcessor(kernel, grayscale); source.ApplyProcessor(processor, rectangle); return source; } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorKernel kernel) { return source.DetectEdges(kernel, grayscale: true); } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorKernel kernel, bool grayscale) { EdgeDetectorProcessor processor = new EdgeDetectorProcessor(kernel, grayscale); source.ApplyProcessor(processor); return source; } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorKernel kernel, Rectangle rectangle) { return source.DetectEdges(kernel, grayscale: true, rectangle); } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorKernel kernel, bool grayscale, Rectangle rectangle) { EdgeDetectorProcessor processor = new EdgeDetectorProcessor(kernel, grayscale); source.ApplyProcessor(processor, rectangle); return source; } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorCompassKernel kernel) { return source.DetectEdges(kernel, grayscale: true); } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorCompassKernel kernel, bool grayscale) { EdgeDetectorCompassProcessor processor = new EdgeDetectorCompassProcessor(kernel, grayscale); source.ApplyProcessor(processor); return source; } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorCompassKernel kernel, Rectangle rectangle) { return source.DetectEdges(kernel, grayscale: true, rectangle); } public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectorCompassKernel kernel, bool grayscale, Rectangle rectangle) { EdgeDetectorCompassProcessor processor = new EdgeDetectorCompassProcessor(kernel, grayscale); source.ApplyProcessor(processor, rectangle); return source; } } public static class GaussianBlurExtensions { public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) { return source.ApplyProcessor(new GaussianBlurProcessor()); } public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) { return source.ApplyProcessor(new GaussianBlurProcessor(sigma)); } public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) { return source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); } } public static class GaussianSharpenExtensions { public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) { return source.ApplyProcessor(new GaussianSharpenProcessor()); } public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) { return source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); } public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle) { return source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); } } public static class DitherExtensions { public static IImageProcessingContext Dither(this IImageProcessingContext source) { return source.Dither(KnownDitherings.Bayer8x8); } public static IImageProcessingContext Dither(this IImageProcessingContext source, IDither dither) { return source.ApplyProcessor(new PaletteDitherProcessor(dither)); } public static IImageProcessingContext Dither(this IImageProcessingContext source, IDither dither, float ditherScale) { return source.ApplyProcessor(new PaletteDitherProcessor(dither, ditherScale)); } public static IImageProcessingContext Dither(this IImageProcessingContext source, IDither dither, ReadOnlyMemory palette) { return source.ApplyProcessor(new PaletteDitherProcessor(dither, palette)); } public static IImageProcessingContext Dither(this IImageProcessingContext source, IDither dither, float ditherScale, ReadOnlyMemory palette) { return source.ApplyProcessor(new PaletteDitherProcessor(dither, ditherScale, palette)); } public static IImageProcessingContext Dither(this IImageProcessingContext source, Rectangle rectangle) { return source.Dither(KnownDitherings.Bayer8x8, rectangle); } public static IImageProcessingContext Dither(this IImageProcessingContext source, IDither dither, Rectangle rectangle) { return source.ApplyProcessor(new PaletteDitherProcessor(dither), rectangle); } public static IImageProcessingContext Dither(this IImageProcessingContext source, IDither dither, float ditherScale, Rectangle rectangle) { return source.ApplyProcessor(new PaletteDitherProcessor(dither, ditherScale), rectangle); } public static IImageProcessingContext Dither(this IImageProcessingContext source, IDither dither, ReadOnlyMemory palette, Rectangle rectangle) { return source.ApplyProcessor(new PaletteDitherProcessor(dither, palette), rectangle); } public static IImageProcessingContext Dither(this IImageProcessingContext source, IDither dither, float ditherScale, ReadOnlyMemory palette, Rectangle rectangle) { return source.ApplyProcessor(new PaletteDitherProcessor(dither, ditherScale, palette), rectangle); } } public static class DrawImageExtensions { public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float opacity) { GraphicsOptions graphicsOptions = source.GetGraphicsOptions(); return source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, graphicsOptions.ColorBlendingMode, graphicsOptions.AlphaCompositionMode, opacity)); } public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode colorBlending, float opacity) { return source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity)); } public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) { return source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, colorBlending, alphaComposition, opacity)); } public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, GraphicsOptions options) { return source.ApplyProcessor(new DrawImageProcessor(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); } public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, float opacity) { GraphicsOptions graphicsOptions = source.GetGraphicsOptions(); return source.ApplyProcessor(new DrawImageProcessor(image, location, graphicsOptions.ColorBlendingMode, graphicsOptions.AlphaCompositionMode, opacity)); } public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, PixelColorBlendingMode colorBlending, float opacity) { return source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, source.GetGraphicsOptions().AlphaCompositionMode, opacity)); } public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) { return source.ApplyProcessor(new DrawImageProcessor(image, location, colorBlending, alphaComposition, opacity)); } public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, GraphicsOptions options) { return source.ApplyProcessor(new DrawImageProcessor(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); } } public static class OilPaintExtensions { public static IImageProcessingContext OilPaint(this IImageProcessingContext source) { return source.OilPaint(10, 15); } public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) { return source.OilPaint(10, 15, rectangle); } public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize) { return source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); } public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize, Rectangle rectangle) { return source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); } } public static class PixelateExtensions { public static IImageProcessingContext Pixelate(this IImageProcessingContext source) { return source.Pixelate(4); } public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) { return source.ApplyProcessor(new PixelateProcessor(size)); } public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size, Rectangle rectangle) { return source.ApplyProcessor(new PixelateProcessor(size), rectangle); } } public static class PixelRowDelegateExtensions { public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation) { return source.ProcessPixelRowsAsVector4(rowOperation, PixelConversionModifiers.None); } public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, PixelConversionModifiers modifiers) { return source.ApplyProcessor(new PixelRowDelegateProcessor(rowOperation, modifiers)); } public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, Rectangle rectangle) { return source.ProcessPixelRowsAsVector4(rowOperation, rectangle, PixelConversionModifiers.None); } public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, Rectangle rectangle, PixelConversionModifiers modifiers) { return source.ApplyProcessor(new PixelRowDelegateProcessor(rowOperation, modifiers), rectangle); } public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation) { return source.ProcessPixelRowsAsVector4(rowOperation, PixelConversionModifiers.None); } public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, PixelConversionModifiers modifiers) { return source.ApplyProcessor(new PositionAwarePixelRowDelegateProcessor(rowOperation, modifiers)); } public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, Rectangle rectangle) { return source.ProcessPixelRowsAsVector4(rowOperation, rectangle, PixelConversionModifiers.None); } public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, Rectangle rectangle, PixelConversionModifiers modifiers) { return source.ApplyProcessor(new PositionAwarePixelRowDelegateProcessor(rowOperation, modifiers), rectangle); } } public static class BlackWhiteExtensions { public static IImageProcessingContext BlackWhite(this IImageProcessingContext source) { return source.ApplyProcessor(new BlackWhiteProcessor()); } public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle) { return source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); } } public static class BrightnessExtensions { public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount) { return source.ApplyProcessor(new BrightnessProcessor(amount)); } public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle) { return source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); } } public static class ColorBlindnessExtensions { public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindness) { return source.ApplyProcessor(GetProcessor(colorBlindness)); } public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle) { return source.ApplyProcessor(GetProcessor(colorBlindnessMode), rectangle); } private static IImageProcessor GetProcessor(ColorBlindnessMode colorBlindness) { return colorBlindness switch { ColorBlindnessMode.Achromatomaly => new AchromatomalyProcessor(), ColorBlindnessMode.Achromatopsia => new AchromatopsiaProcessor(), ColorBlindnessMode.Deuteranomaly => new DeuteranomalyProcessor(), ColorBlindnessMode.Deuteranopia => new DeuteranopiaProcessor(), ColorBlindnessMode.Protanomaly => new ProtanomalyProcessor(), ColorBlindnessMode.Protanopia => new ProtanopiaProcessor(), ColorBlindnessMode.Tritanomaly => new TritanomalyProcessor(), _ => new TritanopiaProcessor(), }; } } public static class ContrastExtensions { public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount) { return source.ApplyProcessor(new ContrastProcessor(amount)); } public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle) { return source.ApplyProcessor(new ContrastProcessor(amount), rectangle); } } public static class FilterExtensions { public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix) { return source.ApplyProcessor(new FilterProcessor(matrix)); } public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle) { return source.ApplyProcessor(new FilterProcessor(matrix), rectangle); } } public static class GrayscaleExtensions { public static IImageProcessingContext Grayscale(this IImageProcessingContext source) { return source.Grayscale(GrayscaleMode.Bt709); } public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount) { return source.Grayscale(GrayscaleMode.Bt709, amount); } public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode) { return source.Grayscale(mode, 1f); } public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount) { IImageProcessor imageProcessor2; if (mode != GrayscaleMode.Bt709) { IImageProcessor imageProcessor = new GrayscaleBt601Processor(amount); imageProcessor2 = imageProcessor; } else { IImageProcessor imageProcessor = new GrayscaleBt709Processor(amount); imageProcessor2 = imageProcessor; } IImageProcessor processor = imageProcessor2; source.ApplyProcessor(processor); return source; } public static IImageProcessingContext Grayscale(this IImageProcessingContext source, Rectangle rectangle) { return source.Grayscale(1f, rectangle); } public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount, Rectangle rectangle) { return source.Grayscale(GrayscaleMode.Bt709, amount, rectangle); } public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, Rectangle rectangle) { return source.Grayscale(mode, 1f, rectangle); } public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount, Rectangle rectangle) { IImageProcessor imageProcessor2; if (mode != GrayscaleMode.Bt709) { IImageProcessor imageProcessor = new GrayscaleBt601Processor(amount); imageProcessor2 = imageProcessor; } else { IImageProcessor imageProcessor = new GrayscaleBt709Processor(amount); imageProcessor2 = imageProcessor; } IImageProcessor processor = imageProcessor2; source.ApplyProcessor(processor, rectangle); return source; } } public static class HueExtensions { public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees) { return source.ApplyProcessor(new HueProcessor(degrees)); } public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle) { return source.ApplyProcessor(new HueProcessor(degrees), rectangle); } } public static class InvertExtensions { public static IImageProcessingContext Invert(this IImageProcessingContext source) { return source.ApplyProcessor(new InvertProcessor(1f)); } public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle) { return source.ApplyProcessor(new InvertProcessor(1f), rectangle); } } public static class KodachromeExtensions { public static IImageProcessingContext Kodachrome(this IImageProcessingContext source) { return source.ApplyProcessor(new KodachromeProcessor()); } public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle) { return source.ApplyProcessor(new KodachromeProcessor(), rectangle); } } public static class LightnessExtensions { public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount) { return source.ApplyProcessor(new LightnessProcessor(amount)); } public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount, Rectangle rectangle) { return source.ApplyProcessor(new LightnessProcessor(amount), rectangle); } } public static class LomographExtensions { public static IImageProcessingContext Lomograph(this IImageProcessingContext source) { return source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions())); } public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle) { return source.ApplyProcessor(new LomographProcessor(source.GetGraphicsOptions()), rectangle); } } public static class OpacityExtensions { public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount) { return source.ApplyProcessor(new OpacityProcessor(amount)); } public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle) { return source.ApplyProcessor(new OpacityProcessor(amount), rectangle); } } public static class PolaroidExtensions { public static IImageProcessingContext Polaroid(this IImageProcessingContext source) { return source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions())); } public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle) { return source.ApplyProcessor(new PolaroidProcessor(source.GetGraphicsOptions()), rectangle); } } public static class SaturateExtensions { public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount) { return source.ApplyProcessor(new SaturateProcessor(amount)); } public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle) { return source.ApplyProcessor(new SaturateProcessor(amount), rectangle); } } public static class SepiaExtensions { public static IImageProcessingContext Sepia(this IImageProcessingContext source) { return source.Sepia(1f); } public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount) { return source.ApplyProcessor(new SepiaProcessor(amount)); } public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle) { return source.Sepia(1f, rectangle); } public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle) { return source.ApplyProcessor(new SepiaProcessor(amount), rectangle); } } public static class HistogramEqualizationExtensions { public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) { return source.HistogramEqualization(new HistogramEqualizationOptions()); } public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source, HistogramEqualizationOptions options) { return source.ApplyProcessor(HistogramEqualizationProcessor.FromOptions(options)); } } public static class BackgroundColorExtensions { public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color) { return source.BackgroundColor(source.GetGraphicsOptions(), color); } public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color, Rectangle rectangle) { return source.BackgroundColor(source.GetGraphicsOptions(), color, rectangle); } public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, GraphicsOptions options, Color color) { return source.ApplyProcessor(new BackgroundColorProcessor(options, color)); } public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, GraphicsOptions options, Color color, Rectangle rectangle) { return source.ApplyProcessor(new BackgroundColorProcessor(options, color), rectangle); } } public static class GlowExtensions { public static IImageProcessingContext Glow(this IImageProcessingContext source) { return source.Glow(source.GetGraphicsOptions()); } public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color) { return source.Glow(source.GetGraphicsOptions(), color); } public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) { return source.Glow(source.GetGraphicsOptions(), radius); } public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) { return source.Glow(source.GetGraphicsOptions(), rectangle); } public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color, float radius, Rectangle rectangle) { return source.Glow(source.GetGraphicsOptions(), color, ValueSize.Absolute(radius), rectangle); } public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options) { return source.Glow(options, Color.Black, ValueSize.PercentageOfWidth(0.5f)); } public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, Color color) { return source.Glow(options, color, ValueSize.PercentageOfWidth(0.5f)); } public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, float radius) { return source.Glow(options, Color.Black, ValueSize.Absolute(radius)); } public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, Rectangle rectangle) { return source.Glow(options, Color.Black, ValueSize.PercentageOfWidth(0.5f), rectangle); } public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, Color color, float radius, Rectangle rectangle) { return source.Glow(options, color, ValueSize.Absolute(radius), rectangle); } private static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, Color color, ValueSize radius, Rectangle rectangle) { return source.ApplyProcessor(new GlowProcessor(options, color, radius), rectangle); } private static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, Color color, ValueSize radius) { return source.ApplyProcessor(new GlowProcessor(options, color, radius)); } } public static class VignetteExtensions { public static IImageProcessingContext Vignette(this IImageProcessingContext source) { return source.Vignette(source.GetGraphicsOptions()); } public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color) { return source.Vignette(source.GetGraphicsOptions(), color); } public static IImageProcessingContext Vignette(this IImageProcessingContext source, float radiusX, float radiusY) { return source.Vignette(source.GetGraphicsOptions(), radiusX, radiusY); } public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) { return source.Vignette(source.GetGraphicsOptions(), rectangle); } public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color, float radiusX, float radiusY, Rectangle rectangle) { return source.Vignette(source.GetGraphicsOptions(), color, radiusX, radiusY, rectangle); } public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options) { return source.VignetteInternal(options, Color.Black, ValueSize.PercentageOfWidth(0.5f), ValueSize.PercentageOfHeight(0.5f)); } public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, Color color) { return source.VignetteInternal(options, color, ValueSize.PercentageOfWidth(0.5f), ValueSize.PercentageOfHeight(0.5f)); } public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, float radiusX, float radiusY) { return source.VignetteInternal(options, Color.Black, radiusX, radiusY); } public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, Rectangle rectangle) { return source.VignetteInternal(options, Color.Black, ValueSize.PercentageOfWidth(0.5f), ValueSize.PercentageOfHeight(0.5f), rectangle); } public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, Color color, float radiusX, float radiusY, Rectangle rectangle) { return source.VignetteInternal(options, color, radiusX, radiusY, rectangle); } private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, GraphicsOptions options, Color color, ValueSize radiusX, ValueSize radiusY, Rectangle rectangle) { return source.ApplyProcessor(new VignetteProcessor(options, color, radiusX, radiusY), rectangle); } private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, GraphicsOptions options, Color color, ValueSize radiusX, ValueSize radiusY) { return source.ApplyProcessor(new VignetteProcessor(options, color, radiusX, radiusY)); } } public static class ProcessingExtensions { private class ProcessingVisitor : IImageVisitor { private readonly Configuration configuration; private readonly Action operation; private readonly bool mutate; public Image ResultImage { get; private set; } public ProcessingVisitor(Configuration configuration, Action operation, bool mutate) { this.configuration = configuration; this.operation = operation; this.mutate = mutate; } public void Visit(Image image) where TPixel : unmanaged, IPixel { IInternalImageProcessingContext internalImageProcessingContext = configuration.ImageOperationsProvider.CreateImageProcessingContext(configuration, image, mutate); operation(internalImageProcessingContext); ResultImage = internalImageProcessingContext.GetResultImage(); } } public static void Mutate(this Image source, Action operation) { source.Mutate(source.GetConfiguration(), operation); } public static void Mutate(this Image source, Configuration configuration, Action operation) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(source, "source"); Guard.NotNull(operation, "operation"); source.EnsureNotDisposed(); source.AcceptVisitor(new ProcessingVisitor(configuration, operation, mutate: true)); } public static void Mutate(this Image source, Action operation) where TPixel : unmanaged, IPixel { source.Mutate(source.GetConfiguration(), operation); } public static void Mutate(this Image source, Configuration configuration, Action operation) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(source, "source"); Guard.NotNull(operation, "operation"); source.EnsureNotDisposed(); IInternalImageProcessingContext obj = configuration.ImageOperationsProvider.CreateImageProcessingContext(configuration, source, mutate: true); operation(obj); } public static void Mutate(this Image source, params IImageProcessor[] operations) where TPixel : unmanaged, IPixel { source.Mutate(source.GetConfiguration(), operations); } public static void Mutate(this Image source, Configuration configuration, params IImageProcessor[] operations) where TPixel : unmanaged, IPixel { Guard.NotNull(source, "source"); Guard.NotNull(operations, "operations"); source.EnsureNotDisposed(); configuration.ImageOperationsProvider.CreateImageProcessingContext(configuration, source, mutate: true).ApplyProcessors(operations); } public static Image Clone(this Image source, Action operation) { return source.Clone(source.GetConfiguration(), operation); } public static Image Clone(this Image source, Configuration configuration, Action operation) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(source, "source"); Guard.NotNull(operation, "operation"); source.EnsureNotDisposed(); ProcessingVisitor processingVisitor = new ProcessingVisitor(configuration, operation, mutate: false); source.AcceptVisitor(processingVisitor); return processingVisitor.ResultImage; } public static Image Clone(this Image source, Action operation) where TPixel : unmanaged, IPixel { return source.Clone(source.GetConfiguration(), operation); } public static Image Clone(this Image source, Configuration configuration, Action operation) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(source, "source"); Guard.NotNull(operation, "operation"); source.EnsureNotDisposed(); IInternalImageProcessingContext internalImageProcessingContext = configuration.ImageOperationsProvider.CreateImageProcessingContext(configuration, source, mutate: false); operation(internalImageProcessingContext); return internalImageProcessingContext.GetResultImage(); } public static Image Clone(this Image source, params IImageProcessor[] operations) where TPixel : unmanaged, IPixel { return source.Clone(source.GetConfiguration(), operations); } public static Image Clone(this Image source, Configuration configuration, params IImageProcessor[] operations) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.NotNull(source, "source"); Guard.NotNull(operations, "operations"); source.EnsureNotDisposed(); IInternalImageProcessingContext internalImageProcessingContext = configuration.ImageOperationsProvider.CreateImageProcessingContext(configuration, source, mutate: false); internalImageProcessingContext.ApplyProcessors(operations); return internalImageProcessingContext.GetResultImage(); } public static IImageProcessingContext ApplyProcessors(this IImageProcessingContext source, params IImageProcessor[] operations) { foreach (IImageProcessor processor in operations) { source = source.ApplyProcessor(processor); } return source; } public static Buffer2D CalculateIntegralImage(this Image source) where TPixel : unmanaged, IPixel { Configuration configuration = source.GetConfiguration(); int height = source.Height; int width = source.Width; Buffer2D buffer2D = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height); ulong num = 0uL; Buffer2D pixelBuffer = source.Frames.RootFrame.PixelBuffer; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(source.Width); Span span = buffer.GetSpan(); Span span2 = pixelBuffer.DangerousGetRowSpan(0); Span span3 = buffer2D.DangerousGetRowSpan(0); PixelOperations.Instance.ToL8(configuration, span2, span); for (int i = 0; i < width; i++) { num += span[i].PackedValue; span3[i] = num; } Span span4 = span3; for (int j = 1; j < height; j++) { span2 = pixelBuffer.DangerousGetRowSpan(j); span3 = buffer2D.DangerousGetRowSpan(j); PixelOperations.Instance.ToL8(configuration, span2, span); num = span[0].PackedValue; span3[0] = num + span4[0]; for (int k = 1; k < width; k++) { num += span[k].PackedValue; span3[k] = num + span4[k]; } span4 = span3; } return buffer2D; } } public static class QuantizeExtensions { public static IImageProcessingContext Quantize(this IImageProcessingContext source) { return source.Quantize(KnownQuantizers.Octree); } public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer) { return source.ApplyProcessor(new QuantizeProcessor(quantizer)); } public static IImageProcessingContext Quantize(this IImageProcessingContext source, Rectangle rectangle) { return source.Quantize(KnownQuantizers.Octree, rectangle); } public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer, Rectangle rectangle) { return source.ApplyProcessor(new QuantizeProcessor(quantizer), rectangle); } } public static class AutoOrientExtensions { public static IImageProcessingContext AutoOrient(this IImageProcessingContext source) { return source.ApplyProcessor(new AutoOrientProcessor()); } } public static class CropExtensions { public static IImageProcessingContext Crop(this IImageProcessingContext source, int width, int height) { return source.Crop(new Rectangle(0, 0, width, height)); } public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) { return source.ApplyProcessor(new CropProcessor(cropRectangle, source.GetCurrentSize())); } } public static class EntropyCropExtensions { public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source) { return source.ApplyProcessor(new EntropyCropProcessor()); } public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source, float threshold) { return source.ApplyProcessor(new EntropyCropProcessor(threshold)); } } public static class FlipExtensions { public static IImageProcessingContext Flip(this IImageProcessingContext source, FlipMode flipMode) { return source.ApplyProcessor(new FlipProcessor(flipMode)); } } public static class PadExtensions { public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) { return source.Pad(width, height, default(Color)); } public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height, Color color) { Size currentSize = source.GetCurrentSize(); ResizeOptions options = new ResizeOptions { Size = new Size(Math.Max(width, currentSize.Width), Math.Max(height, currentSize.Height)), Mode = ResizeMode.BoxPad, Sampler = KnownResamplers.NearestNeighbor, PadColor = color }; return source.Resize(options); } } public static class ResizeExtensions { public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) { return source.Resize(size.Width, size.Height, KnownResamplers.Bicubic, compand: false); } public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) { return source.Resize(size.Width, size.Height, KnownResamplers.Bicubic, compand); } public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) { return source.Resize(width, height, KnownResamplers.Bicubic, compand: false); } public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) { return source.Resize(width, height, KnownResamplers.Bicubic, compand); } public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) { return source.Resize(width, height, sampler, compand: false); } public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) { return source.Resize(size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); } public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) { return source.Resize(width, height, sampler, new Rectangle(0, 0, width, height), compand); } public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand) { ResizeOptions options = new ResizeOptions { Size = new Size(width, height), Mode = ResizeMode.Manual, Sampler = sampler, TargetRectangle = targetRectangle, Compand = compand }; return source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize()), sourceRectangle); } public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle targetRectangle, bool compand) { ResizeOptions options = new ResizeOptions { Size = new Size(width, height), Mode = ResizeMode.Manual, Sampler = sampler, TargetRectangle = targetRectangle, Compand = compand }; return source.Resize(options); } public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) { return source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); } } public static class RotateExtensions { public static IImageProcessingContext Rotate(this IImageProcessingContext source, RotateMode rotateMode) { return source.Rotate((float)rotateMode); } public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) { return source.Rotate(degrees, KnownResamplers.Bicubic); } public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees, IResampler sampler) { return source.ApplyProcessor(new RotateProcessor(degrees, sampler, source.GetCurrentSize())); } } public static class RotateFlipExtensions { public static IImageProcessingContext RotateFlip(this IImageProcessingContext source, RotateMode rotateMode, FlipMode flipMode) { return source.Rotate(rotateMode).Flip(flipMode); } } public static class SkewExtensions { public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY) { return source.Skew(degreesX, degreesY, KnownResamplers.Bicubic); } public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY, IResampler sampler) { return source.ApplyProcessor(new SkewProcessor(degreesX, degreesY, sampler, source.GetCurrentSize())); } } public static class TransformExtensions { public static IImageProcessingContext Transform(this IImageProcessingContext source, AffineTransformBuilder builder) { return source.Transform(builder, KnownResamplers.Bicubic); } public static IImageProcessingContext Transform(this IImageProcessingContext ctx, AffineTransformBuilder builder, IResampler sampler) { return ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); } public static IImageProcessingContext Transform(this IImageProcessingContext ctx, Rectangle sourceRectangle, AffineTransformBuilder builder, IResampler sampler) { Matrix3x2 matrix3x = builder.BuildMatrix(sourceRectangle); Size transformedSize = TransformUtils.GetTransformedSize(sourceRectangle.Size, matrix3x); return ctx.Transform(sourceRectangle, matrix3x, transformedSize, sampler); } public static IImageProcessingContext Transform(this IImageProcessingContext ctx, Rectangle sourceRectangle, Matrix3x2 transform, Size targetDimensions, IResampler sampler) { return ctx.ApplyProcessor(new AffineTransformProcessor(transform, sampler, targetDimensions), sourceRectangle); } public static IImageProcessingContext Transform(this IImageProcessingContext source, ProjectiveTransformBuilder builder) { return source.Transform(builder, KnownResamplers.Bicubic); } public static IImageProcessingContext Transform(this IImageProcessingContext ctx, ProjectiveTransformBuilder builder, IResampler sampler) { return ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); } public static IImageProcessingContext Transform(this IImageProcessingContext ctx, Rectangle sourceRectangle, ProjectiveTransformBuilder builder, IResampler sampler) { Matrix4x4 matrix4x = builder.BuildMatrix(sourceRectangle); Size transformedSize = TransformUtils.GetTransformedSize(sourceRectangle.Size, matrix4x); return ctx.Transform(sourceRectangle, matrix4x, transformedSize, sampler); } public static IImageProcessingContext Transform(this IImageProcessingContext ctx, Rectangle sourceRectangle, Matrix4x4 transform, Size targetDimensions, IResampler sampler) { return ctx.ApplyProcessor(new ProjectiveTransformProcessor(transform, sampler, targetDimensions), sourceRectangle); } } public enum FlipMode { None, Horizontal, Vertical } public enum GrayscaleMode { Bt709, Bt601 } public interface IImageProcessingContext { Configuration Configuration { get; } IDictionary Properties { get; } Size GetCurrentSize(); IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); IImageProcessingContext ApplyProcessor(IImageProcessor processor); } internal interface IImageProcessingContextFactory { IInternalImageProcessingContext CreateImageProcessingContext(Configuration configuration, Image source, bool mutate) where TPixel : unmanaged, IPixel; } internal class DefaultImageOperationsProviderFactory : IImageProcessingContextFactory { public IInternalImageProcessingContext CreateImageProcessingContext(Configuration configuration, Image source, bool mutate) where TPixel : unmanaged, IPixel { return new DefaultImageProcessorContext(configuration, source, mutate); } } internal interface IInternalImageProcessingContext : IImageProcessingContext where TPixel : unmanaged, IPixel { Image GetResultImage(); } public static class KnownDitherings { public static IDither Bayer2x2 { get; } = OrderedDither.Bayer2x2; public static IDither Ordered3x3 { get; } = OrderedDither.Ordered3x3; public static IDither Bayer4x4 { get; } = OrderedDither.Bayer4x4; public static IDither Bayer8x8 { get; } = OrderedDither.Bayer8x8; public static IDither Bayer16x16 { get; } = OrderedDither.Bayer16x16; public static IDither Atkinson { get; } = ErrorDither.Atkinson; public static IDither Burks { get; } = ErrorDither.Burkes; public static IDither FloydSteinberg { get; } = ErrorDither.FloydSteinberg; public static IDither JarvisJudiceNinke { get; } = ErrorDither.JarvisJudiceNinke; public static IDither Sierra2 { get; } = ErrorDither.Sierra2; public static IDither Sierra3 { get; } = ErrorDither.Sierra3; public static IDither SierraLite { get; } = ErrorDither.SierraLite; public static IDither StevensonArce { get; } = ErrorDither.StevensonArce; public static IDither Stucki { get; } = ErrorDither.Stucki; } public static class KnownEdgeDetectorKernels { public static EdgeDetector2DKernel Kayyali { get; } = EdgeDetector2DKernel.KayyaliKernel; public static EdgeDetectorCompassKernel Kirsch { get; } = EdgeDetectorCompassKernel.Kirsch; public static EdgeDetectorKernel Laplacian3x3 { get; } = EdgeDetectorKernel.Laplacian3x3; public static EdgeDetectorKernel Laplacian5x5 { get; } = EdgeDetectorKernel.Laplacian5x5; public static EdgeDetectorKernel LaplacianOfGaussian { get; } = EdgeDetectorKernel.LaplacianOfGaussian; public static EdgeDetector2DKernel Prewitt { get; } = EdgeDetector2DKernel.PrewittKernel; public static EdgeDetector2DKernel RobertsCross { get; } = EdgeDetector2DKernel.RobertsCrossKernel; public static EdgeDetectorCompassKernel Robinson { get; } = EdgeDetectorCompassKernel.Robinson; public static EdgeDetector2DKernel Scharr { get; } = EdgeDetector2DKernel.ScharrKernel; public static EdgeDetector2DKernel Sobel { get; } = EdgeDetector2DKernel.SobelKernel; } public static class KnownFilterMatrices { public static ColorMatrix AchromatomalyFilter { get; } = new ColorMatrix { M11 = 0.618f, M12 = 0.163f, M13 = 0.163f, M21 = 0.32f, M22 = 0.775f, M23 = 0.32f, M31 = 0.062f, M32 = 0.062f, M33 = 0.516f, M44 = 1f }; public static ColorMatrix AchromatopsiaFilter { get; } = new ColorMatrix { M11 = 0.299f, M12 = 0.299f, M13 = 0.299f, M21 = 0.587f, M22 = 0.587f, M23 = 0.587f, M31 = 0.114f, M32 = 0.114f, M33 = 0.114f, M44 = 1f }; public static ColorMatrix DeuteranomalyFilter { get; } = new ColorMatrix { M11 = 0.8f, M12 = 0.258f, M21 = 0.2f, M22 = 0.742f, M23 = 0.142f, M33 = 0.858f, M44 = 1f }; public static ColorMatrix DeuteranopiaFilter { get; } = new ColorMatrix { M11 = 0.625f, M12 = 0.7f, M21 = 0.375f, M22 = 0.3f, M23 = 0.3f, M33 = 0.7f, M44 = 1f }; public static ColorMatrix ProtanomalyFilter { get; } = new ColorMatrix { M11 = 0.817f, M12 = 0.333f, M21 = 0.183f, M22 = 0.667f, M23 = 0.125f, M33 = 0.875f, M44 = 1f }; public static ColorMatrix ProtanopiaFilter { get; } = new ColorMatrix { M11 = 0.567f, M12 = 0.558f, M21 = 0.433f, M22 = 0.442f, M23 = 0.242f, M33 = 0.758f, M44 = 1f }; public static ColorMatrix TritanomalyFilter { get; } = new ColorMatrix { M11 = 0.967f, M21 = 0.33f, M22 = 0.733f, M23 = 0.183f, M32 = 0.267f, M33 = 0.817f, M44 = 1f }; public static ColorMatrix TritanopiaFilter { get; } = new ColorMatrix { M11 = 0.95f, M21 = 0.05f, M22 = 0.433f, M23 = 0.475f, M32 = 0.567f, M33 = 0.525f, M44 = 1f }; public static ColorMatrix BlackWhiteFilter { get; } = new ColorMatrix { M11 = 1.5f, M12 = 1.5f, M13 = 1.5f, M21 = 1.5f, M22 = 1.5f, M23 = 1.5f, M31 = 1.5f, M32 = 1.5f, M33 = 1.5f, M44 = 1f, M51 = -1f, M52 = -1f, M53 = -1f }; public static ColorMatrix KodachromeFilter { get; } = new ColorMatrix { M11 = 0.7297023f, M22 = 0.6109577f, M33 = 0.597218f, M44 = 1f, M51 = 0.105f, M52 = 0.145f, M53 = 0.155f } * CreateSaturateFilter(1.2f) * CreateContrastFilter(1.35f); public static ColorMatrix LomographFilter { get; } = new ColorMatrix { M11 = 1.5f, M22 = 1.45f, M33 = 1.16f, M44 = 1f, M51 = -0.1f, M52 = -0.02f, M53 = -0.07f } * CreateSaturateFilter(1.1f) * CreateContrastFilter(1.33f); public static ColorMatrix PolaroidFilter { get; } = new ColorMatrix { M11 = 1.538f, M12 = -0.062f, M13 = -0.262f, M21 = -0.022f, M22 = 1.578f, M23 = -0.022f, M31 = 0.216f, M32 = -0.16f, M33 = 1.5831f, M44 = 1f, M51 = 0.02f, M52 = -0.05f, M53 = -0.05f }; public static ColorMatrix CreateBrightnessFilter(float amount) { Guard.MustBeGreaterThanOrEqualTo(amount, 0f, "amount"); return new ColorMatrix { M11 = amount, M22 = amount, M33 = amount, M44 = 1f }; } public static ColorMatrix CreateContrastFilter(float amount) { Guard.MustBeGreaterThanOrEqualTo(amount, 0f, "amount"); float num = -0.5f * amount + 0.5f; return new ColorMatrix { M11 = amount, M22 = amount, M33 = amount, M44 = 1f, M51 = num, M52 = num, M53 = num }; } public static ColorMatrix CreateGrayscaleBt601Filter(float amount) { Guard.MustBeBetweenOrEqualTo(amount, 0f, 1f, "amount"); amount = 1f - amount; ColorMatrix result = default(ColorMatrix); result.M11 = 0.299f + 0.701f * amount; result.M21 = 0.587f - 0.587f * amount; result.M31 = 1f - (result.M11 + result.M21); result.M12 = 0.299f - 0.299f * amount; result.M22 = 0.587f + 0.2848f * amount; result.M32 = 1f - (result.M12 + result.M22); result.M13 = 0.299f - 0.299f * amount; result.M23 = 0.587f - 0.587f * amount; result.M33 = 1f - (result.M13 + result.M23); result.M44 = 1f; return result; } public static ColorMatrix CreateGrayscaleBt709Filter(float amount) { Guard.MustBeBetweenOrEqualTo(amount, 0f, 1f, "amount"); amount = 1f - amount; ColorMatrix result = default(ColorMatrix); result.M11 = 0.2126f + 0.7874f * amount; result.M21 = 0.7152f - 0.7152f * amount; result.M31 = 1f - (result.M11 + result.M21); result.M12 = 0.2126f - 0.2126f * amount; result.M22 = 0.7152f + 0.2848f * amount; result.M32 = 1f - (result.M12 + result.M22); result.M13 = 0.2126f - 0.2126f * amount; result.M23 = 0.7152f - 0.7152f * amount; result.M33 = 1f - (result.M13 + result.M23); result.M44 = 1f; return result; } public static ColorMatrix CreateHueFilter(float degrees) { for (degrees %= 360f; degrees < 0f; degrees += 360f) { } float x = GeometryUtilities.DegreeToRadian(degrees); float num = MathF.Cos(x); float num2 = MathF.Sin(x); return new ColorMatrix { M11 = 0.213f + num * 0.787f - num2 * 0.213f, M21 = 0.715f - num * 0.715f - num2 * 0.715f, M31 = 0.072f - num * 0.072f + num2 * 0.928f, M12 = 0.213f - num * 0.213f + num2 * 0.143f, M22 = 0.715f + num * 0.285f + num2 * 0.14f, M32 = 0.072f - num * 0.072f - num2 * 0.283f, M13 = 0.213f - num * 0.213f - num2 * 0.787f, M23 = 0.715f - num * 0.715f + num2 * 0.715f, M33 = 0.072f + num * 0.928f + num2 * 0.072f, M44 = 1f }; } public static ColorMatrix CreateInvertFilter(float amount) { Guard.MustBeBetweenOrEqualTo(amount, 0f, 1f, "amount"); float num = 1f - 2f * amount; return new ColorMatrix { M11 = num, M22 = num, M33 = num, M44 = 1f, M51 = amount, M52 = amount, M53 = amount }; } public static ColorMatrix CreateOpacityFilter(float amount) { Guard.MustBeBetweenOrEqualTo(amount, 0f, 1f, "amount"); return new ColorMatrix { M11 = 1f, M22 = 1f, M33 = 1f, M44 = amount }; } public static ColorMatrix CreateSaturateFilter(float amount) { Guard.MustBeGreaterThanOrEqualTo(amount, 0f, "amount"); ColorMatrix result = default(ColorMatrix); result.M11 = 0.213f + 0.787f * amount; result.M21 = 0.715f - 0.715f * amount; result.M31 = 1f - (result.M11 + result.M21); result.M12 = 0.213f - 0.213f * amount; result.M22 = 0.715f + 0.285f * amount; result.M32 = 1f - (result.M12 + result.M22); result.M13 = 0.213f - 0.213f * amount; result.M23 = 0.715f - 0.715f * amount; result.M33 = 1f - (result.M13 + result.M23); result.M44 = 1f; return result; } public static ColorMatrix CreateLightnessFilter(float amount) { Guard.MustBeGreaterThanOrEqualTo(amount, 0f, "amount"); amount -= 1f; return new ColorMatrix { M11 = 1f, M22 = 1f, M33 = 1f, M44 = 1f, M51 = amount, M52 = amount, M53 = amount }; } public static ColorMatrix CreateSepiaFilter(float amount) { Guard.MustBeBetweenOrEqualTo(amount, 0f, 1f, "amount"); amount = 1f - amount; return new ColorMatrix { M11 = 0.393f + 0.607f * amount, M21 = 0.769f - 0.769f * amount, M31 = 0.189f - 0.189f * amount, M12 = 0.349f - 0.349f * amount, M22 = 0.686f + 0.314f * amount, M32 = 0.168f - 0.168f * amount, M13 = 0.272f - 0.272f * amount, M23 = 0.534f - 0.534f * amount, M33 = 0.131f + 0.869f * amount, M44 = 1f }; } } public static class KnownQuantizers { public static IQuantizer Octree { get; } = new OctreeQuantizer(); public static IQuantizer Wu { get; } = new WuQuantizer(); public static IQuantizer WebSafe { get; } = new WebSafePaletteQuantizer(); public static IQuantizer Werner { get; } = new WernerPaletteQuantizer(); } public static class KnownResamplers { public static IResampler Bicubic { get; } = default(BicubicResampler); public static IResampler Box { get; } = default(BoxResampler); public static IResampler CatmullRom { get; } = CubicResampler.CatmullRom; public static IResampler Hermite { get; } = CubicResampler.Hermite; public static IResampler Lanczos2 { get; } = LanczosResampler.Lanczos2; public static IResampler Lanczos3 { get; } = LanczosResampler.Lanczos3; public static IResampler Lanczos5 { get; } = LanczosResampler.Lanczos5; public static IResampler Lanczos8 { get; } = LanczosResampler.Lanczos8; public static IResampler MitchellNetravali { get; } = CubicResampler.MitchellNetravali; public static IResampler NearestNeighbor { get; } = default(NearestNeighborResampler); public static IResampler Robidoux { get; } = CubicResampler.Robidoux; public static IResampler RobidouxSharp { get; } = CubicResampler.RobidouxSharp; public static IResampler Spline { get; } = CubicResampler.Spline; public static IResampler Triangle { get; } = default(TriangleResampler); public static IResampler Welch { get; } = default(WelchResampler); } public delegate void PixelRowOperation(Span span); public delegate void PixelRowOperation(Span span, T value); public class ProjectiveTransformBuilder { private readonly List> matrixFactories = new List>(); public ProjectiveTransformBuilder PrependTaper(TaperSide side, TaperCorner corner, float fraction) { return Prepend((Size size) => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); } public ProjectiveTransformBuilder AppendTaper(TaperSide side, TaperCorner corner, float fraction) { return Append((Size size) => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); } public ProjectiveTransformBuilder PrependRotationDegrees(float degrees) { return PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); } public ProjectiveTransformBuilder PrependRotationRadians(float radians) { return Prepend((Size size) => new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, size))); } internal ProjectiveTransformBuilder PrependRotationDegrees(float degrees, Vector2 origin) { return PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees), origin); } internal ProjectiveTransformBuilder PrependRotationRadians(float radians, Vector2 origin) { return PrependMatrix(Matrix4x4.CreateRotationZ(radians, new Vector3(origin, 0f))); } public ProjectiveTransformBuilder AppendRotationDegrees(float degrees) { return AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); } public ProjectiveTransformBuilder AppendRotationRadians(float radians) { return Append((Size size) => new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, size))); } internal ProjectiveTransformBuilder AppendRotationDegrees(float degrees, Vector2 origin) { return AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees), origin); } internal ProjectiveTransformBuilder AppendRotationRadians(float radians, Vector2 origin) { return AppendMatrix(Matrix4x4.CreateRotationZ(radians, new Vector3(origin, 0f))); } public ProjectiveTransformBuilder PrependScale(float scale) { return PrependMatrix(Matrix4x4.CreateScale(scale)); } public ProjectiveTransformBuilder PrependScale(SizeF scale) { return PrependScale((Vector2)scale); } public ProjectiveTransformBuilder PrependScale(Vector2 scales) { return PrependMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1f))); } public ProjectiveTransformBuilder AppendScale(float scale) { return AppendMatrix(Matrix4x4.CreateScale(scale)); } public ProjectiveTransformBuilder AppendScale(SizeF scales) { return AppendScale((Vector2)scales); } public ProjectiveTransformBuilder AppendScale(Vector2 scales) { return AppendMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1f))); } internal ProjectiveTransformBuilder PrependSkewDegrees(float degreesX, float degreesY) { return PrependSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY)); } public ProjectiveTransformBuilder PrependSkewRadians(float radiansX, float radiansY) { return Prepend((Size size) => new Matrix4x4(TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size))); } public ProjectiveTransformBuilder PrependSkewDegrees(float degreesX, float degreesY, Vector2 origin) { return PrependSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), origin); } public ProjectiveTransformBuilder PrependSkewRadians(float radiansX, float radiansY, Vector2 origin) { return PrependMatrix(new Matrix4x4(Matrix3x2.CreateSkew(radiansX, radiansY, origin))); } internal ProjectiveTransformBuilder AppendSkewDegrees(float degreesX, float degreesY) { return AppendSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY)); } public ProjectiveTransformBuilder AppendSkewRadians(float radiansX, float radiansY) { return Append((Size size) => new Matrix4x4(TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size))); } public ProjectiveTransformBuilder AppendSkewDegrees(float degreesX, float degreesY, Vector2 origin) { return AppendSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), origin); } public ProjectiveTransformBuilder AppendSkewRadians(float radiansX, float radiansY, Vector2 origin) { return AppendMatrix(new Matrix4x4(Matrix3x2.CreateSkew(radiansX, radiansY, origin))); } public ProjectiveTransformBuilder PrependTranslation(PointF position) { return PrependTranslation((Vector2)position); } public ProjectiveTransformBuilder PrependTranslation(Vector2 position) { return PrependMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0f))); } public ProjectiveTransformBuilder AppendTranslation(PointF position) { return AppendTranslation((Vector2)position); } public ProjectiveTransformBuilder AppendTranslation(Vector2 position) { return AppendMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0f))); } public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix) { CheckDegenerate(matrix); return Prepend((Size _) => matrix); } public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix) { CheckDegenerate(matrix); return Append((Size _) => matrix); } public Matrix4x4 BuildMatrix(Size sourceSize) { return BuildMatrix(new Rectangle(Point.Empty, sourceSize)); } public Matrix4x4 BuildMatrix(Rectangle sourceRectangle) { Guard.MustBeGreaterThan(sourceRectangle.Width, 0, "sourceRectangle"); Guard.MustBeGreaterThan(sourceRectangle.Height, 0, "sourceRectangle"); Matrix4x4 matrix4x = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0f)); Size size = sourceRectangle.Size; foreach (Func matrixFactory in matrixFactories) { matrix4x *= matrixFactory(size); } CheckDegenerate(matrix4x); return matrix4x; } private static void CheckDegenerate(Matrix4x4 matrix) { if (TransformUtils.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } } private ProjectiveTransformBuilder Prepend(Func factory) { matrixFactories.Insert(0, factory); return this; } private ProjectiveTransformBuilder Append(Func factory) { matrixFactories.Add(factory); return this; } } public enum ResizeMode { Crop, Pad, BoxPad, Max, Min, Stretch, Manual } public class ResizeOptions { public ResizeMode Mode { get; set; } public AnchorPositionMode Position { get; set; } public PointF? CenterCoordinates { get; set; } public Size Size { get; set; } public IResampler Sampler { get; set; } = KnownResamplers.Bicubic; public bool Compand { get; set; } public Rectangle? TargetRectangle { get; set; } public bool PremultiplyAlpha { get; set; } = true; public Color PadColor { get; set; } } public enum RotateMode { None = 0, Rotate90 = 90, Rotate180 = 180, Rotate270 = 270 } public enum TaperCorner { LeftOrTop, RightOrBottom, Both } public enum TaperSide { Left, Top, Right, Bottom } } namespace SixLabors.ImageSharp.Processing.Processors { public abstract class CloningImageProcessor : ICloningImageProcessor, IImageProcessor { public abstract ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel; IImageProcessor IImageProcessor.CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return CreatePixelSpecificCloningProcessor(configuration, source, sourceRectangle); } } public abstract class CloningImageProcessor : ICloningImageProcessor, IImageProcessor, IDisposable where TPixel : unmanaged, IPixel { protected Image Source { get; } protected Rectangle SourceRectangle { get; } protected Configuration Configuration { get; } protected CloningImageProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { Configuration = configuration; Source = source; SourceRectangle = sourceRectangle; } Image ICloningImageProcessor.CloneAndExecute() { Image image = CreateTarget(); CheckFrameCount(Source, image); _ = Configuration; BeforeImageApply(image); for (int i = 0; i < Source.Frames.Count; i++) { ImageFrame source = Source.Frames[i]; ImageFrame destination = image.Frames[i]; BeforeFrameApply(source, destination); OnFrameApply(source, destination); AfterFrameApply(source, destination); } AfterImageApply(image); return image; } void IImageProcessor.Execute() { Image image = null; try { image = ((ICloningImageProcessor)this).CloneAndExecute(); CheckFrameCount(Source, image); Source.SwapOrCopyPixelsBuffersFrom(image); } finally { image?.Dispose(); } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected abstract Size GetDestinationSize(); protected virtual void BeforeImageApply(Image destination) { } protected virtual void BeforeFrameApply(ImageFrame source, ImageFrame destination) { } protected abstract void OnFrameApply(ImageFrame source, ImageFrame destination); protected virtual void AfterFrameApply(ImageFrame source, ImageFrame destination) { } protected virtual void AfterImageApply(Image destination) { } protected virtual void Dispose(bool disposing) { } private Image CreateTarget() { Image source = Source; Size destinationSize = GetDestinationSize(); ImageFrame[] array = new ImageFrame[source.Frames.Count]; for (int i = 0; i < array.Length; i++) { array[i] = new ImageFrame(Configuration, destinationSize.Width, destinationSize.Height, source.Frames[i].Metadata.DeepClone()); } return new Image(Configuration, source.Metadata.DeepClone(), array); } private void CheckFrameCount(Image a, Image b) { if (a.Frames.Count != b.Frames.Count) { throw new ImageProcessingException("An error occurred when processing the image using " + GetType().Name + ". The processor changed the number of frames."); } } } public interface ICloningImageProcessor : IImageProcessor { ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel; } public interface ICloningImageProcessor : IImageProcessor, IDisposable where TPixel : unmanaged, IPixel { Image CloneAndExecute(); } public interface IImageProcessor { IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel; } public interface IImageProcessor : IDisposable where TPixel : unmanaged, IPixel { void Execute(); } internal static class ImageProcessorExtensions { private class ExecuteVisitor : IImageVisitor { private readonly Configuration configuration; private readonly IImageProcessor processor; private readonly Rectangle sourceRectangle; public ExecuteVisitor(Configuration configuration, IImageProcessor processor, Rectangle sourceRectangle) { this.configuration = configuration; this.processor = processor; this.sourceRectangle = sourceRectangle; } public void Visit(Image image) where TPixel : unmanaged, IPixel { using IImageProcessor imageProcessor = processor.CreatePixelSpecificProcessor(configuration, image, sourceRectangle); imageProcessor.Execute(); } } public static void Execute(this IImageProcessor processor, Configuration configuration, Image source, Rectangle sourceRectangle) { source.AcceptVisitor(new ExecuteVisitor(configuration, processor, sourceRectangle)); } } public abstract class ImageProcessor : IImageProcessor, IDisposable where TPixel : unmanaged, IPixel { protected Image Source { get; } protected Rectangle SourceRectangle { get; } protected Configuration Configuration { get; } protected ImageProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { Configuration = configuration; Source = source; SourceRectangle = sourceRectangle; } void IImageProcessor.Execute() { BeforeImageApply(); foreach (ImageFrame frame in Source.Frames) { Apply(frame); } AfterImageApply(); } public void Apply(ImageFrame source) { BeforeFrameApply(source); OnFrameApply(source); AfterFrameApply(source); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void BeforeImageApply() { } protected virtual void BeforeFrameApply(ImageFrame source) { } protected abstract void OnFrameApply(ImageFrame source); protected virtual void AfterFrameApply(ImageFrame source) { } protected virtual void AfterImageApply() { } protected virtual void Dispose(bool disposing) { } } } namespace SixLabors.ImageSharp.Processing.Processors.Transforms { public sealed class CropProcessor : CloningImageProcessor { public Rectangle CropRectangle { get; } public CropProcessor(Rectangle cropRectangle, Size sourceSize) { Guard.IsTrue(new Rectangle(Point.Empty, sourceSize).Contains(cropRectangle), "cropRectangle", "Crop rectangle should be smaller than the source bounds."); CropRectangle = cropRectangle; } public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new CropProcessor(configuration, this, source, sourceRectangle); } } internal class CropProcessor : TransformProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D source; private readonly Buffer2D destination; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(Rectangle bounds, Buffer2D source, Buffer2D destination) { this.bounds = bounds; this.source = source; this.destination = destination; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Span span = source.DangerousGetRowSpan(y); Span span2 = span.Slice(bounds.Left); Span span3 = destination.DangerousGetRowSpan(y - bounds.Top); span = span2.Slice(0, bounds.Width); span.CopyTo(span3); } } private readonly Rectangle cropRectangle; public CropProcessor(Configuration configuration, CropProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { cropRectangle = definition.CropRectangle; } protected override Size GetDestinationSize() { return new Size(cropRectangle.Width, cropRectangle.Height); } protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { if (source.Width == destination.Width && source.Height == destination.Height && base.SourceRectangle == cropRectangle) { source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup()); return; } Rectangle rectangle = cropRectangle; ParallelRowIterator.IterateRows(rectangle, ParallelExecutionSettings.FromConfiguration(base.Configuration).MultiplyMinimumPixelsPerTask(4), new RowOperation(rectangle, source.PixelBuffer, destination.PixelBuffer)); } } public sealed class DegenerateTransformException : Exception { public DegenerateTransformException() { } public DegenerateTransformException(string message) : base(message) { } public DegenerateTransformException(string message, Exception innerException) : base(message, innerException) { } } public sealed class EntropyCropProcessor : IImageProcessor { public float Threshold { get; } public EntropyCropProcessor() : this(0.5f) { } public EntropyCropProcessor(float threshold) { Guard.MustBeBetweenOrEqualTo(threshold, 0f, 1f, "threshold"); Threshold = threshold; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new EntropyCropProcessor(configuration, this, source, sourceRectangle); } } internal class EntropyCropProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly EntropyCropProcessor definition; public EntropyCropProcessor(Configuration configuration, EntropyCropProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.definition = definition; } protected override void BeforeImageApply() { Rectangle filteredBoundingRectangle; using (Image image = new Image(base.Configuration, base.Source.Metadata.DeepClone(), new ImageFrame[1] { base.Source.Frames.RootFrame.Clone() })) { base.Source.GetConfiguration(); new EdgeDetector2DProcessor(KnownEdgeDetectorKernels.Sobel, grayscale: false).Execute(base.Configuration, image, base.SourceRectangle); new BinaryThresholdProcessor(definition.Threshold).Execute(base.Configuration, image, base.SourceRectangle); filteredBoundingRectangle = GetFilteredBoundingRectangle(image.Frames.RootFrame, 0f); } new CropProcessor(filteredBoundingRectangle, base.Source.Size()).Execute(base.Configuration, base.Source, base.SourceRectangle); base.BeforeImageApply(); } protected override void OnFrameApply(ImageFrame source) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight) { return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); } private static Rectangle GetFilteredBoundingRectangle(ImageFrame bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) { int width = bitmap.Width; int height = bitmap.Height; Point topLeft = default(Point); Point bottomRight = default(Point); Func, int, int, float, bool> delegateFunc; switch (channel) { case RgbaComponent.R: delegateFunc = (ImageFrame pixels, int x, int y, float b) => MathF.Abs(pixels[x, y].ToVector4().X - b) > Constants.Epsilon; break; case RgbaComponent.G: delegateFunc = (ImageFrame pixels, int x, int y, float b) => MathF.Abs(pixels[x, y].ToVector4().Y - b) > Constants.Epsilon; break; case RgbaComponent.B: delegateFunc = (ImageFrame pixels, int x, int y, float b) => MathF.Abs(pixels[x, y].ToVector4().Z - b) > Constants.Epsilon; break; default: delegateFunc = (ImageFrame pixels, int x, int y, float b) => MathF.Abs(pixels[x, y].ToVector4().W - b) > Constants.Epsilon; break; } topLeft.Y = GetMinY(bitmap); topLeft.X = GetMinX(bitmap); bottomRight.Y = Numerics.Clamp(GetMaxY(bitmap) + 1, 0, height); bottomRight.X = Numerics.Clamp(GetMaxX(bitmap) + 1, 0, width); return GetBoundingRectangle(topLeft, bottomRight); int GetMaxX(ImageFrame pixels) { for (int num = width - 1; num > -1; num--) { for (int i = 0; i < height; i++) { if (delegateFunc(pixels, num, i, componentValue)) { return num; } } } return width; } int GetMaxY(ImageFrame pixels) { for (int num = height - 1; num > -1; num--) { for (int i = 0; i < width; i++) { if (delegateFunc(pixels, i, num, componentValue)) { return num; } } } return height; } int GetMinX(ImageFrame pixels) { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (delegateFunc(pixels, i, j, componentValue)) { return i; } } } return 0; } int GetMinY(ImageFrame pixels) { for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (delegateFunc(pixels, j, i, componentValue)) { return i; } } } return 0; } } } public interface IResampler { float Radius { get; } float GetValue(float x); void ApplyTransform(IResamplingTransformImageProcessor processor) where TPixel : unmanaged, IPixel; } public interface IResamplingTransformImageProcessor : IImageProcessor, IDisposable where TPixel : unmanaged, IPixel { void ApplyTransform(in TResampler sampler) where TResampler : struct, IResampler; } public interface ISwizzler { Size DestinationSize { get; } Point Transform(Point point); } public class AffineTransformProcessor : CloningImageProcessor { public IResampler Sampler { get; } public Matrix3x2 TransformMatrix { get; } public Size DestinationSize { get; } public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targetDimensions) { Guard.NotNull(sampler, "sampler"); Guard.MustBeValueType(sampler, "sampler"); if (TransformUtils.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } Sampler = sampler; TransformMatrix = matrix; DestinationSize = targetDimensions; } public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new AffineTransformProcessor(configuration, this, source, sourceRectangle); } } internal class AffineTransformProcessor : TransformProcessor, IResamplingTransformImageProcessor, IImageProcessor, IDisposable where TPixel : unmanaged, IPixel { private readonly struct NNAffineOperation : IRowOperation { private readonly Buffer2D source; private readonly Buffer2D destination; private readonly Rectangle bounds; private readonly Matrix3x2 matrix; [MethodImpl(MethodImplOptions.AggressiveInlining)] public NNAffineOperation(Buffer2D source, Rectangle bounds, Buffer2D destination, Matrix3x2 matrix) { this.source = source; this.bounds = bounds; this.destination = destination; this.matrix = matrix; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Span span = destination.DangerousGetRowSpan(y); for (int i = 0; i < span.Length; i++) { Vector2 vector = Vector2.Transform(new Vector2(i, y), matrix); int x = (int)MathF.Round(vector.X); int y2 = (int)MathF.Round(vector.Y); if (bounds.Contains(x, y2)) { span[i] = source.GetElementUnsafe(x, y2); } } } } private readonly struct AffineOperation : IRowIntervalOperation where TResampler : struct, IResampler { private readonly Configuration configuration; private readonly Buffer2D source; private readonly Rectangle bounds; private readonly Buffer2D destination; private readonly TResampler sampler; private readonly Matrix3x2 matrix; private readonly float yRadius; private readonly float xRadius; [MethodImpl(MethodImplOptions.AggressiveInlining)] public AffineOperation(Configuration configuration, Buffer2D source, Rectangle bounds, Buffer2D destination, in TResampler sampler, Matrix3x2 matrix) { this.configuration = configuration; this.source = source; this.bounds = bounds; this.destination = destination; this.sampler = sampler; this.matrix = matrix; yRadius = LinearTransformUtility.GetSamplingRadius(in sampler, source.Height, destination.Height); xRadius = LinearTransformUtility.GetSamplingRadius(in sampler, source.Width, destination.Width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(in RowInterval rows, Span span) { if (RuntimeEnvironment.IsOSPlatform(OSPlatform.OSX) && RuntimeEnvironment.IsNetCore) { InvokeMacOS(in rows, span); return; } Matrix3x2 matrix3x = matrix; TResampler val = sampler; float radius = yRadius; float radius2 = xRadius; int y = bounds.Y; int max = bounds.Bottom - 1; int x = bounds.X; int max2 = bounds.Right - 1; for (int i = rows.Min; i < rows.Max; i++) { Span span2 = destination.DangerousGetRowSpan(i); PixelOperations.Instance.ToVector4(configuration, span2, span, PixelConversionModifiers.Scale); for (int j = 0; j < span.Length; j++) { Vector2 vector = Vector2.Transform(new Vector2(j, i), matrix3x); float y2 = vector.Y; float x2 = vector.X; int rangeStart = LinearTransformUtility.GetRangeStart(radius, y2, y, max); int rangeEnd = LinearTransformUtility.GetRangeEnd(radius, y2, y, max); int rangeStart2 = LinearTransformUtility.GetRangeStart(radius2, x2, x, max2); int rangeEnd2 = LinearTransformUtility.GetRangeEnd(radius2, x2, x, max2); if (rangeEnd == rangeStart || rangeEnd2 == rangeStart2) { continue; } Vector4 zero = Vector4.Zero; for (int k = rangeStart; k <= rangeEnd; k++) { float value = val.GetValue((float)k - y2); for (int l = rangeStart2; l <= rangeEnd2; l++) { float value2 = val.GetValue((float)l - x2); Vector4 vector2 = source.GetElementUnsafe(l, k).ToScaledVector4(); Numerics.Premultiply(ref vector2); zero += vector2 * value2 * value; } } span[j] = zero; } Numerics.UnPremultiply(span); PixelOperations.Instance.FromVector4Destructive(configuration, span, span2, PixelConversionModifiers.Scale); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [ExcludeFromCodeCoverage] private void InvokeMacOS(in RowInterval rows, Span span) { Matrix3x2 matrix3x = matrix; TResampler val = sampler; float radius = yRadius; float radius2 = xRadius; int y = bounds.Y; int max = bounds.Bottom - 1; int x = bounds.X; int max2 = bounds.Right - 1; for (int i = rows.Min; i < rows.Max; i++) { Span span2 = destination.DangerousGetRowSpan(i); PixelOperations.Instance.ToVector4(configuration, span2, span, PixelConversionModifiers.Scale); for (int j = 0; j < span.Length; j++) { Vector2 vector = Vector2.Transform(new Vector2(j, i), matrix3x); float y2 = vector.Y; float x2 = vector.X; int rangeStart = LinearTransformUtility.GetRangeStart(radius, y2, y, max); int rangeEnd = LinearTransformUtility.GetRangeEnd(radius, y2, y, max); int rangeStart2 = LinearTransformUtility.GetRangeStart(radius2, x2, x, max2); int rangeEnd2 = LinearTransformUtility.GetRangeEnd(radius2, x2, x, max2); if (rangeEnd == rangeStart || rangeEnd2 == rangeStart2) { continue; } Vector4 zero = Vector4.Zero; for (int k = rangeStart; k <= rangeEnd; k++) { float value = val.GetValue((float)k - y2); for (int l = rangeStart2; l <= rangeEnd2; l++) { float value2 = val.GetValue((float)l - x2); Vector4 vector2 = source.GetElementUnsafe(l, k).ToScaledVector4(); Numerics.Premultiply(ref vector2); zero += vector2 * value2 * value; } } Numerics.UnPremultiply(ref zero); span[j] = zero; } PixelOperations.Instance.FromVector4Destructive(configuration, span, span2, PixelConversionModifiers.Scale); } } void IRowIntervalOperation.Invoke(in RowInterval rows, Span span) { Invoke(in rows, span); } } private readonly Size destinationSize; private readonly Matrix3x2 transformMatrix; private readonly IResampler resampler; private ImageFrame source; private ImageFrame destination; public AffineTransformProcessor(Configuration configuration, AffineTransformProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { destinationSize = definition.DestinationSize; transformMatrix = definition.TransformMatrix; resampler = definition.Sampler; } protected override Size GetDestinationSize() { return destinationSize; } protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { this.source = source; this.destination = destination; resampler.ApplyTransform(this); } public void ApplyTransform(in TResampler sampler) where TResampler : struct, IResampler { Configuration configuration = base.Configuration; ImageFrame imageFrame = source; ImageFrame imageFrame2 = destination; Matrix3x2 result = transformMatrix; if (result.Equals(Matrix3x2.Identity)) { Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, imageFrame2.Bounds()); Buffer2DRegion region = imageFrame.PixelBuffer.GetRegion(rectangle); Buffer2DRegion region2 = imageFrame2.PixelBuffer.GetRegion(rectangle); for (int i = 0; i < region.Height; i++) { region.DangerousGetRowSpan(i).CopyTo(region2.DangerousGetRowSpan(i)); } } else { Matrix3x2.Invert(result, out result); if (sampler is NearestNeighborResampler) { NNAffineOperation operation = new NNAffineOperation(imageFrame.PixelBuffer, Rectangle.Intersect(base.SourceRectangle, imageFrame.Bounds()), imageFrame2.PixelBuffer, result); ParallelRowIterator.IterateRows(configuration, imageFrame2.Bounds(), in operation); } else { AffineOperation operation2 = new AffineOperation(configuration, imageFrame.PixelBuffer, Rectangle.Intersect(base.SourceRectangle, imageFrame.Bounds()), imageFrame2.PixelBuffer, in sampler, result); ParallelRowIterator.IterateRowIntervals, Vector4>(configuration, imageFrame2.Bounds(), in operation2); } } } void IResamplingTransformImageProcessor.ApplyTransform(in TResampler sampler) { ApplyTransform(in sampler); } } public sealed class AutoOrientProcessor : IImageProcessor { public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new AutoOrientProcessor(configuration, source, sourceRectangle); } } internal class AutoOrientProcessor : ImageProcessor where TPixel : unmanaged, IPixel { public AutoOrientProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { } protected override void BeforeImageApply() { ushort exifOrientation = GetExifOrientation(base.Source); Size size = base.SourceRectangle.Size; switch (exifOrientation) { case 2: new FlipProcessor(FlipMode.Horizontal).Execute(base.Configuration, base.Source, base.SourceRectangle); break; case 3: new RotateProcessor(180f, size).Execute(base.Configuration, base.Source, base.SourceRectangle); break; case 4: new FlipProcessor(FlipMode.Vertical).Execute(base.Configuration, base.Source, base.SourceRectangle); break; case 5: new RotateProcessor(90f, size).Execute(base.Configuration, base.Source, base.SourceRectangle); new FlipProcessor(FlipMode.Horizontal).Execute(base.Configuration, base.Source, base.SourceRectangle); break; case 6: new RotateProcessor(90f, size).Execute(base.Configuration, base.Source, base.SourceRectangle); break; case 7: new FlipProcessor(FlipMode.Vertical).Execute(base.Configuration, base.Source, base.SourceRectangle); new RotateProcessor(270f, size).Execute(base.Configuration, base.Source, base.SourceRectangle); break; case 8: new RotateProcessor(270f, size).Execute(base.Configuration, base.Source, base.SourceRectangle); break; } base.BeforeImageApply(); } protected override void OnFrameApply(ImageFrame sourceBase) { } private static ushort GetExifOrientation(Image source) { if (source.Metadata.ExifProfile == null) { return 0; } IExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); if (value == null) { return 0; } ushort result; if (value.DataType == ExifDataType.Short) { result = value.Value; } else { result = Convert.ToUInt16(value.Value); source.Metadata.ExifProfile.RemoveValue(ExifTag.Orientation); } source.Metadata.ExifProfile.SetValue(ExifTag.Orientation, 1); return result; } } public sealed class FlipProcessor : IImageProcessor { public FlipMode FlipMode { get; } public FlipProcessor(FlipMode flipMode) { FlipMode = flipMode; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new FlipProcessor(configuration, this, source, sourceRectangle); } } internal class FlipProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly Buffer2D source; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(Buffer2D source) { this.source = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { source.DangerousGetRowSpan(y).Reverse(); } } private readonly FlipProcessor definition; public FlipProcessor(Configuration configuration, FlipProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.definition = definition; } protected override void OnFrameApply(ImageFrame source) { switch (definition.FlipMode) { case FlipMode.Vertical: FlipX(source.PixelBuffer, base.Configuration); break; case FlipMode.Horizontal: FlipY(source, base.Configuration); break; } } private void FlipX(Buffer2D source, Configuration configuration) { int height = source.Height; using IMemoryOwner memoryOwner = configuration.MemoryAllocator.Allocate(source.Width); Span span = memoryOwner.Memory.Span; for (int i = 0; i < height / 2; i++) { int y = height - i - 1; Span destination = source.DangerousGetRowSpan(y); Span destination2 = source.DangerousGetRowSpan(i); destination.CopyTo(span); destination2.CopyTo(destination); span.CopyTo(destination2); } } private void FlipY(ImageFrame source, Configuration configuration) { RowOperation operation = new RowOperation(source.PixelBuffer); ParallelRowIterator.IterateRows(configuration, source.Bounds(), in operation); } } internal static class LinearTransformUtility { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float GetSamplingRadius(in TResampler sampler, int sourceSize, int destinationSize) where TResampler : struct, IResampler { float num = (float)sourceSize / (float)destinationSize; if (num < 1f) { num = 1f; } return MathF.Ceiling(sampler.Radius * num); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetRangeStart(float radius, float center, int min, int max) { return Numerics.Clamp((int)MathF.Ceiling(center - radius), min, max); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetRangeEnd(float radius, float center, int min, int max) { return Numerics.Clamp((int)MathF.Floor(center + radius), min, max); } } public sealed class ProjectiveTransformProcessor : CloningImageProcessor { public IResampler Sampler { get; } public Matrix4x4 TransformMatrix { get; } public Size DestinationSize { get; } public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size targetDimensions) { Guard.NotNull(sampler, "sampler"); Guard.MustBeValueType(sampler, "sampler"); if (TransformUtils.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } Sampler = sampler; TransformMatrix = matrix; DestinationSize = targetDimensions; } public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new ProjectiveTransformProcessor(configuration, this, source, sourceRectangle); } } internal class ProjectiveTransformProcessor : TransformProcessor, IResamplingTransformImageProcessor, IImageProcessor, IDisposable where TPixel : unmanaged, IPixel { private readonly struct NNProjectiveOperation : IRowOperation { private readonly Buffer2D source; private readonly Buffer2D destination; private readonly Rectangle bounds; private readonly Matrix4x4 matrix; [MethodImpl(MethodImplOptions.AggressiveInlining)] public NNProjectiveOperation(Buffer2D source, Rectangle bounds, Buffer2D destination, Matrix4x4 matrix) { this.source = source; this.bounds = bounds; this.destination = destination; this.matrix = matrix; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Span span = destination.DangerousGetRowSpan(y); for (int i = 0; i < span.Length; i++) { Vector2 vector = TransformUtils.ProjectiveTransform2D(i, y, matrix); int x = (int)MathF.Round(vector.X); int y2 = (int)MathF.Round(vector.Y); if (bounds.Contains(x, y2)) { span[i] = source.GetElementUnsafe(x, y2); } } } } private readonly struct ProjectiveOperation : IRowIntervalOperation where TResampler : struct, IResampler { private readonly Configuration configuration; private readonly Buffer2D source; private readonly Rectangle bounds; private readonly Buffer2D destination; private readonly TResampler sampler; private readonly Matrix4x4 matrix; private readonly float yRadius; private readonly float xRadius; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ProjectiveOperation(Configuration configuration, Buffer2D source, Rectangle bounds, Buffer2D destination, in TResampler sampler, Matrix4x4 matrix) { this.configuration = configuration; this.source = source; this.bounds = bounds; this.destination = destination; this.sampler = sampler; this.matrix = matrix; yRadius = LinearTransformUtility.GetSamplingRadius(in sampler, bounds.Height, destination.Height); xRadius = LinearTransformUtility.GetSamplingRadius(in sampler, bounds.Width, destination.Width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(in RowInterval rows, Span span) { if (RuntimeEnvironment.IsOSPlatform(OSPlatform.OSX) && RuntimeEnvironment.IsNetCore) { InvokeMacOS(in rows, span); return; } Matrix4x4 matrix4x = matrix; TResampler val = sampler; float radius = yRadius; float radius2 = xRadius; int y = bounds.Y; int max = bounds.Bottom - 1; int x = bounds.X; int max2 = bounds.Right - 1; for (int i = rows.Min; i < rows.Max; i++) { Span span2 = destination.DangerousGetRowSpan(i); PixelOperations.Instance.ToVector4(configuration, span2, span, PixelConversionModifiers.Scale); for (int j = 0; j < span.Length; j++) { Vector2 vector = TransformUtils.ProjectiveTransform2D(j, i, matrix4x); float y2 = vector.Y; float x2 = vector.X; int rangeStart = LinearTransformUtility.GetRangeStart(radius, y2, y, max); int rangeEnd = LinearTransformUtility.GetRangeEnd(radius, y2, y, max); int rangeStart2 = LinearTransformUtility.GetRangeStart(radius2, x2, x, max2); int rangeEnd2 = LinearTransformUtility.GetRangeEnd(radius2, x2, x, max2); if (rangeEnd <= rangeStart || rangeEnd2 <= rangeStart2) { continue; } Vector4 zero = Vector4.Zero; for (int k = rangeStart; k <= rangeEnd; k++) { float value = val.GetValue((float)k - y2); for (int l = rangeStart2; l <= rangeEnd2; l++) { float value2 = val.GetValue((float)l - x2); Vector4 vector2 = source.GetElementUnsafe(l, k).ToScaledVector4(); Numerics.Premultiply(ref vector2); zero += vector2 * value2 * value; } } span[j] = zero; } Numerics.UnPremultiply(span); PixelOperations.Instance.FromVector4Destructive(configuration, span, span2, PixelConversionModifiers.Scale); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [ExcludeFromCodeCoverage] public void InvokeMacOS(in RowInterval rows, Span span) { Matrix4x4 matrix4x = matrix; TResampler val = sampler; float radius = yRadius; float radius2 = xRadius; int y = bounds.Y; int max = bounds.Bottom - 1; int x = bounds.X; int max2 = bounds.Right - 1; for (int i = rows.Min; i < rows.Max; i++) { Span span2 = destination.DangerousGetRowSpan(i); PixelOperations.Instance.ToVector4(configuration, span2, span, PixelConversionModifiers.Scale); for (int j = 0; j < span.Length; j++) { Vector2 vector = TransformUtils.ProjectiveTransform2D(j, i, matrix4x); float y2 = vector.Y; float x2 = vector.X; int rangeStart = LinearTransformUtility.GetRangeStart(radius, y2, y, max); int rangeEnd = LinearTransformUtility.GetRangeEnd(radius, y2, y, max); int rangeStart2 = LinearTransformUtility.GetRangeStart(radius2, x2, x, max2); int rangeEnd2 = LinearTransformUtility.GetRangeEnd(radius2, x2, x, max2); if (rangeEnd <= rangeStart || rangeEnd2 <= rangeStart2) { continue; } Vector4 zero = Vector4.Zero; for (int k = rangeStart; k <= rangeEnd; k++) { float value = val.GetValue((float)k - y2); for (int l = rangeStart2; l <= rangeEnd2; l++) { float value2 = val.GetValue((float)l - x2); Vector4 vector2 = source.GetElementUnsafe(l, k).ToScaledVector4(); Numerics.Premultiply(ref vector2); zero += vector2 * value2 * value; } } Numerics.UnPremultiply(ref zero); span[j] = zero; } PixelOperations.Instance.FromVector4Destructive(configuration, span, span2, PixelConversionModifiers.Scale); } } void IRowIntervalOperation.Invoke(in RowInterval rows, Span span) { Invoke(in rows, span); } } private readonly Size destinationSize; private readonly IResampler resampler; private readonly Matrix4x4 transformMatrix; private ImageFrame source; private ImageFrame destination; public ProjectiveTransformProcessor(Configuration configuration, ProjectiveTransformProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { destinationSize = definition.DestinationSize; transformMatrix = definition.TransformMatrix; resampler = definition.Sampler; } protected override Size GetDestinationSize() { return destinationSize; } protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { this.source = source; this.destination = destination; resampler.ApplyTransform(this); } public void ApplyTransform(in TResampler sampler) where TResampler : struct, IResampler { Configuration configuration = base.Configuration; ImageFrame imageFrame = source; ImageFrame imageFrame2 = destination; Matrix4x4 result = transformMatrix; if (result.Equals(Matrix4x4.Identity)) { Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, imageFrame2.Bounds()); Buffer2DRegion region = imageFrame.PixelBuffer.GetRegion(rectangle); Buffer2DRegion region2 = imageFrame2.PixelBuffer.GetRegion(rectangle); for (int i = 0; i < region.Height; i++) { region.DangerousGetRowSpan(i).CopyTo(region2.DangerousGetRowSpan(i)); } } else { Matrix4x4.Invert(result, out result); if (sampler is NearestNeighborResampler) { NNProjectiveOperation operation = new NNProjectiveOperation(imageFrame.PixelBuffer, Rectangle.Intersect(base.SourceRectangle, imageFrame.Bounds()), imageFrame2.PixelBuffer, result); ParallelRowIterator.IterateRows(configuration, imageFrame2.Bounds(), in operation); } else { ProjectiveOperation operation2 = new ProjectiveOperation(configuration, imageFrame.PixelBuffer, Rectangle.Intersect(base.SourceRectangle, imageFrame.Bounds()), imageFrame2.PixelBuffer, in sampler, result); ParallelRowIterator.IterateRowIntervals, Vector4>(configuration, imageFrame2.Bounds(), in operation2); } } } void IResamplingTransformImageProcessor.ApplyTransform(in TResampler sampler) { ApplyTransform(in sampler); } } public sealed class RotateProcessor : AffineTransformProcessor { public float Degrees { get; } public RotateProcessor(float degrees, Size sourceSize) : this(degrees, KnownResamplers.Bicubic, sourceSize) { } public RotateProcessor(float degrees, IResampler sampler, Size sourceSize) : this(TransformUtils.CreateRotationMatrixDegrees(degrees, sourceSize), sampler, sourceSize) { Degrees = degrees; } private RotateProcessor(Matrix3x2 rotationMatrix, IResampler sampler, Size sourceSize) : base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, rotationMatrix)) { } public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new RotateProcessor(configuration, this, source, sourceRectangle); } } internal class RotateProcessor : AffineTransformProcessor where TPixel : unmanaged, IPixel { private readonly struct Rotate180RowOperation : IRowOperation { private readonly int width; private readonly int height; private readonly Buffer2D source; private readonly Buffer2D destination; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rotate180RowOperation(int width, int height, Buffer2D source, Buffer2D destination) { this.width = width; this.height = height; this.source = source; this.destination = destination; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Span span = source.DangerousGetRowSpan(y); Span span2 = destination.DangerousGetRowSpan(height - y - 1); for (int i = 0; i < width; i++) { span2[width - i - 1] = span[i]; } } } private readonly struct Rotate270RowIntervalOperation : IRowIntervalOperation { private readonly Rectangle bounds; private readonly int width; private readonly int height; private readonly Buffer2D source; private readonly Buffer2D destination; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rotate270RowIntervalOperation(Rectangle bounds, int width, int height, Buffer2D source, Buffer2D destination) { this.bounds = bounds; this.width = width; this.height = height; this.source = source; this.destination = destination; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(in RowInterval rows) { for (int i = rows.Min; i < rows.Max; i++) { Span span = source.DangerousGetRowSpan(i); for (int j = 0; j < width; j++) { int num = height - i - 1; num = height - num - 1; int y = width - j - 1; if (bounds.Contains(num, y)) { destination[num, y] = span[j]; } } } } void IRowIntervalOperation.Invoke(in RowInterval rows) { Invoke(in rows); } } private readonly struct Rotate90RowOperation : IRowOperation { private readonly Rectangle bounds; private readonly int width; private readonly int height; private readonly Buffer2D source; private readonly Buffer2D destination; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rotate90RowOperation(Rectangle bounds, int width, int height, Buffer2D source, Buffer2D destination) { this.bounds = bounds; this.width = width; this.height = height; this.source = source; this.destination = destination; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Span span = source.DangerousGetRowSpan(y); int x = height - y - 1; for (int i = 0; i < width; i++) { if (bounds.Contains(x, i)) { destination[x, i] = span[i]; } } } } private readonly float degrees; public RotateProcessor(Configuration configuration, RotateProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, (AffineTransformProcessor)definition, source, sourceRectangle) { degrees = definition.Degrees; } protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { if (!OptimizedApply(source, destination, base.Configuration)) { base.OnFrameApply(source, destination); } } protected override void AfterImageApply(Image destination) { ExifProfile exifProfile = destination.Metadata.ExifProfile; if (exifProfile != null && !(MathF.Abs(WrapDegrees(degrees)) < Constants.Epsilon)) { exifProfile.RemoveValue(ExifTag.Orientation); base.AfterImageApply(destination); } } private static float WrapDegrees(float degrees) { for (degrees %= 360f; degrees < 0f; degrees += 360f) { } return degrees; } private bool OptimizedApply(ImageFrame source, ImageFrame destination, Configuration configuration) { float num = WrapDegrees(degrees); if (MathF.Abs(num) < Constants.Epsilon) { source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup()); return true; } if (MathF.Abs(num - 90f) < Constants.Epsilon) { Rotate90(source, destination, configuration); return true; } if (MathF.Abs(num - 180f) < Constants.Epsilon) { Rotate180(source, destination, configuration); return true; } if (MathF.Abs(num - 270f) < Constants.Epsilon) { Rotate270(source, destination, configuration); return true; } return false; } private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) { Rotate180RowOperation operation = new Rotate180RowOperation(source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer); ParallelRowIterator.IterateRows(configuration, source.Bounds(), in operation); } private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) { Rotate270RowIntervalOperation operation = new Rotate270RowIntervalOperation(destination.Bounds(), source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer); ParallelRowIterator.IterateRowIntervals(configuration, source.Bounds(), in operation); } private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration) { Rotate90RowOperation operation = new Rotate90RowOperation(destination.Bounds(), source.Width, source.Height, source.PixelBuffer, destination.PixelBuffer); ParallelRowIterator.IterateRows(configuration, source.Bounds(), in operation); } } public sealed class SkewProcessor : AffineTransformProcessor { public float DegreesX { get; } public float DegreesY { get; } public SkewProcessor(float degreesX, float degreesY, Size sourceSize) : this(degreesX, degreesY, KnownResamplers.Bicubic, sourceSize) { } public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size sourceSize) : this(TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, sourceSize), sampler, sourceSize) { DegreesX = degreesX; DegreesY = degreesY; } private SkewProcessor(Matrix3x2 skewMatrix, IResampler sampler, Size sourceSize) : base(skewMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, skewMatrix)) { } } [StructLayout(LayoutKind.Sequential, Size = 1)] public readonly struct BicubicResampler : IResampler { public float Radius => 2f; [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetValue(float x) { if (x < 0f) { x = 0f - x; } if (x <= 1f) { return (1.5f * x - 2.5f) * x * x + 1f; } if (x < 2f) { return ((-0.5f * x + 2.5f) * x - 4f) * x + 2f; } return 0f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyTransform(IResamplingTransformImageProcessor processor) where TPixel : unmanaged, IPixel { processor.ApplyTransform(in this); } } [StructLayout(LayoutKind.Sequential, Size = 1)] public readonly struct BoxResampler : IResampler { public float Radius => 0.5f; [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetValue(float x) { if (x > -0.5f && x <= 0.5f) { return 1f; } return 0f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyTransform(IResamplingTransformImageProcessor processor) where TPixel : unmanaged, IPixel { processor.ApplyTransform(in this); } } public readonly struct CubicResampler : IResampler { private readonly float bspline; private readonly float cardinal; public static CubicResampler CatmullRom = new CubicResampler(2f, 0f, 0.5f); public static CubicResampler Hermite = new CubicResampler(2f, 0f, 0f); public static CubicResampler MitchellNetravali = new CubicResampler(2f, 0.3333333f, 0.3333333f); public static CubicResampler Robidoux = new CubicResampler(2f, 0.37821576f, 0.31089213f); public static CubicResampler RobidouxSharp = new CubicResampler(2f, 0.2620145f, 0.36899275f); public static CubicResampler Spline = new CubicResampler(2f, 1f, 0f); public float Radius { get; } public CubicResampler(float radius, float bspline, float cardinal) { Radius = radius; this.bspline = bspline; this.cardinal = cardinal; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetValue(float x) { float num = bspline; float num2 = cardinal; if (x < 0f) { x = 0f - x; } float num3 = x * x; if (x < 1f) { x = (12f - 9f * num - 6f * num2) * (x * num3) + (-18f + 12f * num + 6f * num2) * num3 + (6f - 2f * num); return x / 6f; } if (x < 2f) { x = (0f - num - 6f * num2) * (x * num3) + (6f * num + 30f * num2) * num3 + (-12f * num - 48f * num2) * x + (8f * num + 24f * num2); return x / 6f; } return 0f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyTransform(IResamplingTransformImageProcessor processor) where TPixel : unmanaged, IPixel { processor.ApplyTransform(in this); } } public readonly struct LanczosResampler : IResampler { public static LanczosResampler Lanczos2 = new LanczosResampler(2f); public static LanczosResampler Lanczos3 = new LanczosResampler(3f); public static LanczosResampler Lanczos5 = new LanczosResampler(5f); public static LanczosResampler Lanczos8 = new LanczosResampler(8f); public float Radius { get; } public LanczosResampler(float radius) { Radius = radius; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetValue(float x) { if (x < 0f) { x = 0f - x; } float radius = Radius; if (x < radius) { return Numerics.SinC(x) * Numerics.SinC(x / radius); } return 0f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyTransform(IResamplingTransformImageProcessor processor) where TPixel : unmanaged, IPixel { processor.ApplyTransform(in this); } } [StructLayout(LayoutKind.Sequential, Size = 1)] public readonly struct NearestNeighborResampler : IResampler { public float Radius => 1f; [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetValue(float x) { return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyTransform(IResamplingTransformImageProcessor processor) where TPixel : unmanaged, IPixel { processor.ApplyTransform(in this); } } [StructLayout(LayoutKind.Sequential, Size = 1)] public readonly struct TriangleResampler : IResampler { public float Radius => 1f; [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetValue(float x) { if (x < 0f) { x = 0f - x; } if (x < 1f) { return 1f - x; } return 0f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyTransform(IResamplingTransformImageProcessor processor) where TPixel : unmanaged, IPixel { processor.ApplyTransform(in this); } } [StructLayout(LayoutKind.Sequential, Size = 1)] public readonly struct WelchResampler : IResampler { public float Radius => 3f; [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetValue(float x) { if (x < 0f) { x = 0f - x; } if (x < 3f) { return Numerics.SinC(x) * (1f - x * x / 9f); } return 0f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyTransform(IResamplingTransformImageProcessor processor) where TPixel : unmanaged, IPixel { processor.ApplyTransform(in this); } } internal static class ResizeHelper { public unsafe static int CalculateResizeWorkerHeightInWindowBands(int windowBandHeight, int width, int sizeLimitHintInBytes) { int num = sizeLimitHintInBytes / sizeof(Vector4); int num2 = windowBandHeight * width; return Math.Max(2, num / num2); } public static (Size Size, Rectangle Rectangle) CalculateTargetLocationAndBounds(Size sourceSize, ResizeOptions options) { int num = options.Size.Width; int num2 = options.Size.Height; if (num <= 0 && num2 <= 0) { ThrowInvalid($"Target width {num} and height {num2} must be greater than zero."); } if (num == 0 && num2 > 0) { num = (int)MathF.Max(1f, MathF.Round((float)(sourceSize.Width * num2) / (float)sourceSize.Height)); } if (num2 == 0 && num > 0) { num2 = (int)MathF.Max(1f, MathF.Round((float)(sourceSize.Height * num) / (float)sourceSize.Width)); } return options.Mode switch { ResizeMode.Crop => CalculateCropRectangle(sourceSize, options, num, num2), ResizeMode.Pad => CalculatePadRectangle(sourceSize, options, num, num2), ResizeMode.BoxPad => CalculateBoxPadRectangle(sourceSize, options, num, num2), ResizeMode.Max => CalculateMaxRectangle(sourceSize, num, num2), ResizeMode.Min => CalculateMinRectangle(sourceSize, num, num2), ResizeMode.Manual => CalculateManualRectangle(options, num, num2), _ => (Size: new Size(Sanitize(num), Sanitize(num2)), Rectangle: new Rectangle(0, 0, Sanitize(num), Sanitize(num2))), }; } private static (Size Size, Rectangle Rectangle) CalculateBoxPadRectangle(Size source, ResizeOptions options, int width, int height) { int width2 = source.Width; int height2 = source.Height; float num = MathF.Abs((float)height / (float)height2); float num2 = MathF.Abs((float)width / (float)width2); int num3 = ((height > 0) ? height : ((int)MathF.Round((float)height2 * num2))); int num4 = ((width > 0) ? width : ((int)MathF.Round((float)width2 * num))); if (width2 < num4 && height2 < num3) { int input = width2; int input2 = height2; width = num4; height = num3; int y; int x; switch (options.Position) { case AnchorPositionMode.Left: y = (height - height2) / 2; x = 0; break; case AnchorPositionMode.Right: y = (height - height2) / 2; x = width - width2; break; case AnchorPositionMode.TopRight: y = 0; x = width - width2; break; case AnchorPositionMode.Top: y = 0; x = (width - width2) / 2; break; case AnchorPositionMode.TopLeft: y = 0; x = 0; break; case AnchorPositionMode.BottomRight: y = height - height2; x = width - width2; break; case AnchorPositionMode.Bottom: y = height - height2; x = (width - width2) / 2; break; case AnchorPositionMode.BottomLeft: y = height - height2; x = 0; break; default: y = (height - height2) / 2; x = (width - width2) / 2; break; } return (Size: new Size(Sanitize(width), Sanitize(height)), Rectangle: new Rectangle(x, y, Sanitize(input), Sanitize(input2))); } return CalculatePadRectangle(source, options, width, height); } private static (Size Size, Rectangle Rectangle) CalculateCropRectangle(Size source, ResizeOptions options, int width, int height) { int width2 = source.Width; int height2 = source.Height; int num = 0; int num2 = 0; int input = width; int input2 = height; float num3 = MathF.Abs((float)height / (float)height2); float num4 = MathF.Abs((float)width / (float)width2); if (num3 < num4) { float num5 = num4; if (options.CenterCoordinates.HasValue) { num2 = (int)MathF.Round((0f - num5 * (float)height2) * options.CenterCoordinates.Value.Y + (float)height / 2f); if (num2 > 0) { num2 = 0; } if (num2 < (int)MathF.Round((float)height - (float)height2 * num5)) { num2 = (int)MathF.Round((float)height - (float)height2 * num5); } } else { switch (options.Position) { case AnchorPositionMode.Top: case AnchorPositionMode.TopLeft: case AnchorPositionMode.TopRight: num2 = 0; break; case AnchorPositionMode.Bottom: case AnchorPositionMode.BottomRight: case AnchorPositionMode.BottomLeft: num2 = (int)MathF.Round((float)height - (float)height2 * num5); break; default: num2 = (int)MathF.Round(((float)height - (float)height2 * num5) / 2f); break; } } input2 = (int)MathF.Ceiling((float)height2 * num4); } else { float num5 = num3; if (options.CenterCoordinates.HasValue) { num = (int)MathF.Round((0f - num5 * (float)width2) * options.CenterCoordinates.Value.X + (float)width / 2f); if (num > 0) { num = 0; } if (num < (int)MathF.Round((float)width - (float)width2 * num5)) { num = (int)MathF.Round((float)width - (float)width2 * num5); } } else { switch (options.Position) { case AnchorPositionMode.Left: case AnchorPositionMode.TopLeft: case AnchorPositionMode.BottomLeft: num = 0; break; case AnchorPositionMode.Right: case AnchorPositionMode.TopRight: case AnchorPositionMode.BottomRight: num = (int)MathF.Round((float)width - (float)width2 * num5); break; default: num = (int)MathF.Round(((float)width - (float)width2 * num5) / 2f); break; } } input = (int)MathF.Ceiling((float)width2 * num3); } return (Size: new Size(Sanitize(width), Sanitize(height)), Rectangle: new Rectangle(num, num2, Sanitize(input), Sanitize(input2))); } private static (Size Size, Rectangle Rectangle) CalculateMaxRectangle(Size source, int width, int height) { int input = width; int input2 = height; float num = MathF.Abs((float)height / (float)source.Height); float num2 = MathF.Abs((float)width / (float)source.Width); float num3 = (float)height / (float)width; if ((float)source.Height / (float)source.Width < num3) { input2 = (int)MathF.Round((float)source.Height * num2); } else { input = (int)MathF.Round((float)source.Width * num); } return (Size: new Size(Sanitize(input), Sanitize(input2)), Rectangle: new Rectangle(0, 0, Sanitize(input), Sanitize(input2))); } private static (Size Size, Rectangle Rectangle) CalculateMinRectangle(Size source, int width, int height) { int width2 = source.Width; int height2 = source.Height; int input = width; int input2 = height; if (width > width2 || height > height2) { return (Size: new Size(width2, height2), Rectangle: new Rectangle(0, 0, width2, height2)); } int num = width2 - width; int num2 = height2 - height; if (num < num2) { float num3 = (float)height2 / (float)width2; input2 = (int)MathF.Round((float)width * num3); } else if (num > num2) { float num4 = (float)width2 / (float)height2; input = (int)MathF.Round((float)height * num4); } else if (height > width) { float num5 = MathF.Abs((float)width / (float)width2); input2 = (int)MathF.Round((float)height2 * num5); } else { float num6 = MathF.Abs((float)height / (float)height2); input = (int)MathF.Round((float)width2 * num6); } return (Size: new Size(Sanitize(input), Sanitize(input2)), Rectangle: new Rectangle(0, 0, Sanitize(input), Sanitize(input2))); } private static (Size Size, Rectangle Rectangle) CalculatePadRectangle(Size sourceSize, ResizeOptions options, int width, int height) { int width2 = sourceSize.Width; int height2 = sourceSize.Height; int x = 0; int y = 0; int input = width; int input2 = height; float num = MathF.Abs((float)height / (float)height2); float num2 = MathF.Abs((float)width / (float)width2); if (num < num2) { float num3 = num; input = (int)MathF.Round((float)width2 * num); switch (options.Position) { case AnchorPositionMode.Left: case AnchorPositionMode.TopLeft: case AnchorPositionMode.BottomLeft: x = 0; break; case AnchorPositionMode.Right: case AnchorPositionMode.TopRight: case AnchorPositionMode.BottomRight: x = (int)MathF.Round((float)width - (float)width2 * num3); break; default: x = (int)MathF.Round(((float)width - (float)width2 * num3) / 2f); break; } } else { float num3 = num2; input2 = (int)MathF.Round((float)height2 * num2); switch (options.Position) { case AnchorPositionMode.Top: case AnchorPositionMode.TopLeft: case AnchorPositionMode.TopRight: y = 0; break; case AnchorPositionMode.Bottom: case AnchorPositionMode.BottomRight: case AnchorPositionMode.BottomLeft: y = (int)MathF.Round((float)height - (float)height2 * num3); break; default: y = (int)MathF.Round(((float)height - (float)height2 * num3) / 2f); break; } } return (Size: new Size(Sanitize(width), Sanitize(height)), Rectangle: new Rectangle(x, y, Sanitize(input), Sanitize(input2))); } private static (Size Size, Rectangle Rectangle) CalculateManualRectangle(ResizeOptions options, int width, int height) { if (!options.TargetRectangle.HasValue) { ThrowInvalid("Manual resizing requires a target location and size."); } Rectangle value = options.TargetRectangle.Value; int x = value.X; int y = value.Y; int input = ((value.Width > 0) ? value.Width : width); int input2 = ((value.Height > 0) ? value.Height : height); return (Size: new Size(Sanitize(width), Sanitize(height)), Rectangle: new Rectangle(x, y, Sanitize(input), Sanitize(input2))); } private static void ThrowInvalid(string message) { throw new InvalidOperationException(message); } private static int Sanitize(int input) { return Math.Max(1, input); } } internal readonly struct ResizeKernel { private unsafe readonly float* bufferPtr; public int StartIndex { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public unsafe Span Values { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return new Span(bufferPtr, Length); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal unsafe ResizeKernel(int startIndex, float* bufferPtr, int length) { StartIndex = startIndex; this.bufferPtr = bufferPtr; Length = length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 Convolve(Span rowSpan) { return ConvolveCore(ref rowSpan[StartIndex]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe Vector4 ConvolveCore(ref Vector4 rowStartRef) { if (Avx2.IsSupported && Fma.IsSupported) { float* ptr = bufferPtr; float* ptr2 = ptr + (Length & -4); Vector256 vector = Vector256.Zero; Vector256 vector2 = Vector256.Zero; Vector256 control = Unsafe.ReadUnaligned>(in MemoryMarshal.GetReference((ReadOnlySpan)new byte[32] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 })); while (ptr < ptr2) { vector = Fma.MultiplyAdd(Unsafe.As>(ref rowStartRef), Avx2.PermuteVar8x32(Vector256.CreateScalarUnsafe(*(double*)ptr).AsSingle(), control), vector); vector2 = Fma.MultiplyAdd(Unsafe.As>(ref Unsafe.Add(ref rowStartRef, 2)), Avx2.PermuteVar8x32(Vector256.CreateScalarUnsafe(*(double*)(ptr + 2)).AsSingle(), control), vector2); ptr += 4; rowStartRef = ref Unsafe.Add(ref rowStartRef, 4); } vector = Avx.Add(vector, vector2); if ((Length & 3) >= 2) { vector = Fma.MultiplyAdd(Unsafe.As>(ref rowStartRef), Avx2.PermuteVar8x32(Vector256.CreateScalarUnsafe(*(double*)ptr).AsSingle(), control), vector); ptr += 2; rowStartRef = ref Unsafe.Add(ref rowStartRef, 2); } Vector128 c = Sse.Add(vector.GetLower(), vector.GetUpper()); if ((Length & 1) != 0) { c = Fma.MultiplyAdd(Unsafe.As>(ref rowStartRef), Vector128.Create(*ptr), c); } return *(Vector4*)(&c); } Vector4 zero = Vector4.Zero; float* ptr3 = bufferPtr; float* ptr4 = bufferPtr + Length; while (ptr3 < ptr4) { zero += rowStartRef * *ptr3; ptr3++; rowStartRef = ref Unsafe.Add(ref rowStartRef, 1); } return zero; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal unsafe ResizeKernel AlterLeftValue(int left) { return new ResizeKernel(left, bufferPtr, Length); } internal void Fill(Span values) { for (int i = 0; i < Length; i++) { Values[i] = (float)values[i]; } } } internal class ResizeKernelMap : IDisposable { private sealed class PeriodicKernelMap : ResizeKernelMap { private readonly int period; private readonly int cornerInterval; internal override string Info => base.Info + $"|period:{period}|cornerInterval:{cornerInterval}"; public PeriodicKernelMap(MemoryAllocator memoryAllocator, int sourceLength, int destinationLength, double ratio, double scale, int radius, int period, int cornerInterval) : base(memoryAllocator, sourceLength, destinationLength, cornerInterval * 2 + period, ratio, scale, radius) { this.cornerInterval = cornerInterval; this.period = period; } protected internal override void Initialize(in TResampler sampler) { int num = cornerInterval + period; for (int i = 0; i < num; i++) { kernels[i] = BuildKernel(in sampler, i, i); } int num2 = base.DestinationLength - cornerInterval; for (int j = num; j < num2; j++) { double num3 = ((double)j + 0.5) * ratio - 0.5; int left = (int)TolerantMath.Ceiling(num3 - (double)radius); ResizeKernel resizeKernel = kernels[j - period]; kernels[j] = resizeKernel.AlterLeftValue(left); } int num4 = cornerInterval + period; for (int k = 0; k < cornerInterval; k++) { kernels[num2 + k] = BuildKernel(in sampler, num2 + k, num4 + k); } } } private static readonly TolerantMath TolerantMath = TolerantMath.Default; private readonly int sourceLength; private readonly double ratio; private readonly double scale; private readonly int radius; private readonly MemoryHandle pinHandle; private readonly Buffer2D data; private readonly ResizeKernel[] kernels; private bool isDisposed; private readonly double[] tempValues; public int DestinationLength { get; } public int MaxDiameter { get; } internal virtual string Info => $"radius:{radius}|sourceSize:{sourceLength}|destinationSize:{DestinationLength}|ratio:{ratio}|scale:{scale}"; private ResizeKernelMap(MemoryAllocator memoryAllocator, int sourceLength, int destinationLength, int bufferHeight, double ratio, double scale, int radius) { this.ratio = ratio; this.scale = scale; this.radius = radius; this.sourceLength = sourceLength; DestinationLength = destinationLength; MaxDiameter = radius * 2 + 1; data = memoryAllocator.Allocate2D(MaxDiameter, bufferHeight, preferContiguosImageBuffers: true, AllocationOptions.Clean); pinHandle = data.DangerousGetSingleMemory().Pin(); kernels = new ResizeKernel[destinationLength]; tempValues = new double[MaxDiameter]; } public void Dispose() { Dispose(disposing: true); } protected virtual void Dispose(bool disposing) { if (!isDisposed) { isDisposed = true; if (disposing) { pinHandle.Dispose(); data.Dispose(); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ref ResizeKernel GetKernel(nint destIdx) { return ref kernels[destIdx]; } public static ResizeKernelMap Calculate(in TResampler sampler, int destinationSize, int sourceSize, MemoryAllocator memoryAllocator) where TResampler : struct, IResampler { double num = (double)sourceSize / (double)destinationSize; double num2 = num; if (num2 < 1.0) { num2 = 1.0; } int num3 = (int)TolerantMath.Ceiling(num2 * (double)sampler.Radius); int num4 = destinationSize / Numerics.GreatestCommonDivisor(sourceSize, destinationSize); double num5 = (num - 1.0) * 0.5; double a = ((double)num3 - num5 - 1.0) / num; int num6 = (int)TolerantMath.Ceiling(a); if (TolerantMath.AreEqual(a, num6)) { num6++; } ResizeKernelMap obj = ((2 * (num6 + num4) < destinationSize) ? new PeriodicKernelMap(memoryAllocator, sourceSize, destinationSize, num, num2, num3, num4, num6) : new ResizeKernelMap(memoryAllocator, sourceSize, destinationSize, destinationSize, num, num2, num3)); obj.Initialize(in sampler); return obj; } protected internal virtual void Initialize(in TResampler sampler) where TResampler : struct, IResampler { for (int i = 0; i < DestinationLength; i++) { kernels[i] = BuildKernel(in sampler, i, i); } } private ResizeKernel BuildKernel(in TResampler sampler, int destRowIndex, int dataRowIndex) where TResampler : struct, IResampler { double num = ((double)destRowIndex + 0.5) * ratio - 0.5; int num2 = (int)TolerantMath.Ceiling(num - (double)radius); if (num2 < 0) { num2 = 0; } int num3 = (int)TolerantMath.Floor(num + (double)radius); if (num3 > sourceLength - 1) { num3 = sourceLength - 1; } ResizeKernel result = CreateKernel(dataRowIndex, num2, num3); Span values = tempValues.AsSpan(0, result.Length); double num4 = 0.0; for (int i = num2; i <= num3; i++) { double num5 = sampler.GetValue((float)(((double)i - num) / scale)); num4 += num5; values[i - num2] = num5; } if (num4 > 0.0) { for (int j = 0; j < result.Length; j++) { values[j] /= num4; } } result.Fill(values); return result; } private unsafe ResizeKernel CreateKernel(int dataRowIndex, int left, int right) { int length = right - left + 1; float* bufferPtr = (float*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(data.DangerousGetRowSpan(dataRowIndex))); return new ResizeKernel(left, bufferPtr, length); } [Conditional("DEBUG")] private void ValidateSizesForCreateKernel(int length, int dataRowIndex, int left, int right) { if (length > data.Width) { throw new InvalidOperationException($"Error in KernelMap.CreateKernel({dataRowIndex},{left},{right}): left > this.data.Width"); } } } public class ResizeProcessor : CloningImageProcessor { public int DestinationWidth { get; } public int DestinationHeight { get; } public Rectangle DestinationRectangle { get; } public ResizeOptions Options { get; } public ResizeProcessor(ResizeOptions options, Size sourceSize) { Guard.NotNull(options, "options"); Guard.NotNull(options.Sampler, "Sampler"); Guard.MustBeValueType(options.Sampler, "Sampler"); (Size Size, Rectangle Rectangle) tuple = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options); Size item = tuple.Size; Rectangle item2 = tuple.Rectangle; Options = options; DestinationWidth = item.Width; DestinationHeight = item.Height; DestinationRectangle = item2; } public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new ResizeProcessor(configuration, this, source, sourceRectangle); } } internal class ResizeProcessor : TransformProcessor, IResamplingTransformImageProcessor, IImageProcessor, IDisposable where TPixel : unmanaged, IPixel { private readonly struct NNRowOperation : IRowOperation { private readonly Rectangle sourceBounds; private readonly Rectangle destinationBounds; private readonly Rectangle interest; private readonly float widthFactor; private readonly float heightFactor; private readonly Buffer2D source; private readonly Buffer2D destination; [MethodImpl(MethodImplOptions.AggressiveInlining)] public NNRowOperation(Rectangle sourceBounds, Rectangle destinationBounds, Rectangle interest, float widthFactor, float heightFactor, Buffer2D source, Buffer2D destination) { this.sourceBounds = sourceBounds; this.destinationBounds = destinationBounds; this.interest = interest; this.widthFactor = widthFactor; this.heightFactor = heightFactor; this.source = source; this.destination = destination; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { int x = sourceBounds.X; int y2 = sourceBounds.Y; int x2 = destinationBounds.X; int y3 = destinationBounds.Y; int left = interest.Left; int right = interest.Right; Span span = source.DangerousGetRowSpan((int)((float)(y - y3) * heightFactor + (float)y2)); Span span2 = destination.DangerousGetRowSpan(y); for (int i = left; i < right; i++) { span2[i] = span[(int)((float)(i - x2) * widthFactor + (float)x)]; } } } private readonly ResizeOptions options; private readonly int destinationWidth; private readonly int destinationHeight; private readonly IResampler resampler; private readonly Rectangle destinationRectangle; private Image destination; public ResizeProcessor(Configuration configuration, ResizeProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { destinationWidth = definition.DestinationWidth; destinationHeight = definition.DestinationHeight; destinationRectangle = definition.DestinationRectangle; options = definition.Options; resampler = definition.Options.Sampler; } protected override Size GetDestinationSize() { return new Size(destinationWidth, destinationHeight); } protected override void BeforeImageApply(Image destination) { this.destination = destination; resampler.ApplyTransform(this); base.BeforeImageApply(destination); } protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { } public void ApplyTransform(in TResampler sampler) where TResampler : struct, IResampler { Configuration configuration = base.Configuration; Image source = base.Source; Image image = destination; Rectangle sourceRectangle = base.SourceRectangle; Rectangle rectangle = destinationRectangle; bool compand = options.Compand; bool premultiplyAlpha = options.PremultiplyAlpha; TPixel value = options.PadColor.ToPixel(); bool flag = (options.Mode == ResizeMode.BoxPad || options.Mode == ResizeMode.Pad) && options.PadColor != default(Color); if (source.Width == image.Width && source.Height == image.Height && sourceRectangle == rectangle) { for (int i = 0; i < source.Frames.Count; i++) { ImageFrame source2 = source.Frames[i]; MemoryGroupExtensions.CopyTo(target: image.Frames[i].GetPixelMemoryGroup(), source: source2.GetPixelMemoryGroup()); } return; } Rectangle interest = Rectangle.Intersect(rectangle, image.Bounds()); if (sampler is NearestNeighborResampler) { for (int j = 0; j < source.Frames.Count; j++) { ImageFrame source3 = source.Frames[j]; ImageFrame imageFrame = image.Frames[j]; if (flag) { imageFrame.Clear(value); } ApplyNNResizeFrameTransform(configuration, source3, imageFrame, sourceRectangle, rectangle, interest); } return; } MemoryAllocator memoryAllocator = configuration.MemoryAllocator; using ResizeKernelMap horizontalKernelMap = ResizeKernelMap.Calculate(in sampler, rectangle.Width, sourceRectangle.Width, memoryAllocator); using ResizeKernelMap verticalKernelMap = ResizeKernelMap.Calculate(in sampler, rectangle.Height, sourceRectangle.Height, memoryAllocator); for (int k = 0; k < source.Frames.Count; k++) { ImageFrame source4 = source.Frames[k]; ImageFrame imageFrame2 = image.Frames[k]; if (flag) { imageFrame2.Clear(value); } ApplyResizeFrameTransform(configuration, source4, imageFrame2, horizontalKernelMap, verticalKernelMap, sourceRectangle, rectangle, interest, compand, premultiplyAlpha); } } private static void ApplyNNResizeFrameTransform(Configuration configuration, ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Rectangle destinationRectangle, Rectangle interest) { float widthFactor = (float)sourceRectangle.Width / (float)destinationRectangle.Width; float heightFactor = (float)sourceRectangle.Height / (float)destinationRectangle.Height; ParallelRowIterator.IterateRows(configuration, interest, new NNRowOperation(sourceRectangle, destinationRectangle, interest, widthFactor, heightFactor, source.PixelBuffer, destination.PixelBuffer)); } private static PixelConversionModifiers GetModifiers(bool compand, bool premultiplyAlpha) { if (premultiplyAlpha) { return PixelConversionModifiers.Premultiply.ApplyCompanding(compand); } return PixelConversionModifiers.None.ApplyCompanding(compand); } private static void ApplyResizeFrameTransform(Configuration configuration, ImageFrame source, ImageFrame destination, ResizeKernelMap horizontalKernelMap, ResizeKernelMap verticalKernelMap, Rectangle sourceRectangle, Rectangle destinationRectangle, Rectangle interest, bool compand, bool premultiplyAlpha) { PixelAlphaRepresentation? pixelAlphaRepresentation = PixelOperations.Instance.GetPixelTypeInfo()?.AlphaRepresentation; bool flag = !pixelAlphaRepresentation.HasValue || pixelAlphaRepresentation.Value == PixelAlphaRepresentation.Unassociated; premultiplyAlpha = premultiplyAlpha && flag; PixelConversionModifiers modifiers = GetModifiers(compand, premultiplyAlpha); Buffer2DRegion region = source.PixelBuffer.GetRegion(sourceRectangle); using ResizeWorker resizeWorker = new ResizeWorker(configuration, region, modifiers, horizontalKernelMap, verticalKernelMap, interest, destinationRectangle.Location); resizeWorker.Initialize(); RowInterval rowInterval = new RowInterval(interest.Top, interest.Bottom); resizeWorker.FillDestinationPixels(rowInterval, destination.PixelBuffer); } void IResamplingTransformImageProcessor.ApplyTransform(in TResampler sampler) { ApplyTransform(in sampler); } } internal sealed class ResizeWorker : IDisposable where TPixel : unmanaged, IPixel { private readonly Buffer2D transposedFirstPassBuffer; private readonly Configuration configuration; private readonly PixelConversionModifiers conversionModifiers; private readonly ResizeKernelMap horizontalKernelMap; private readonly Buffer2DRegion source; private readonly Rectangle sourceRectangle; private readonly IMemoryOwner tempRowBuffer; private readonly IMemoryOwner tempColumnBuffer; private readonly ResizeKernelMap verticalKernelMap; private readonly Rectangle targetWorkingRect; private readonly Point targetOrigin; private readonly int windowBandHeight; private readonly int workerHeight; private RowInterval currentWindow; public ResizeWorker(Configuration configuration, Buffer2DRegion source, PixelConversionModifiers conversionModifiers, ResizeKernelMap horizontalKernelMap, ResizeKernelMap verticalKernelMap, Rectangle targetWorkingRect, Point targetOrigin) { this.configuration = configuration; this.source = source; sourceRectangle = source.Rectangle; this.conversionModifiers = conversionModifiers; this.horizontalKernelMap = horizontalKernelMap; this.verticalKernelMap = verticalKernelMap; this.targetWorkingRect = targetWorkingRect; this.targetOrigin = targetOrigin; windowBandHeight = verticalKernelMap.MaxDiameter; int sizeLimitHintInBytes = Math.Min(configuration.WorkingBufferSizeHintInBytes, configuration.MemoryAllocator.GetBufferCapacityInBytes()); int num = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(windowBandHeight, targetWorkingRect.Width, sizeLimitHintInBytes); workerHeight = Math.Min(sourceRectangle.Height, num * windowBandHeight); transposedFirstPassBuffer = configuration.MemoryAllocator.Allocate2D(workerHeight, targetWorkingRect.Width, preferContiguosImageBuffers: true, AllocationOptions.Clean); tempRowBuffer = configuration.MemoryAllocator.Allocate(sourceRectangle.Width); tempColumnBuffer = configuration.MemoryAllocator.Allocate(targetWorkingRect.Width); currentWindow = new RowInterval(0, workerHeight); } public void Dispose() { transposedFirstPassBuffer.Dispose(); tempRowBuffer.Dispose(); tempColumnBuffer.Dispose(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetColumnSpan(int x, int startY) { return transposedFirstPassBuffer.DangerousGetRowSpan(x).Slice(startY - currentWindow.Min); } public void Initialize() { CalculateFirstPassValues(currentWindow); } public void FillDestinationPixels(RowInterval rowInterval, Buffer2D destination) { Span span = tempColumnBuffer.GetSpan(); Span span2 = transposedFirstPassBuffer.DangerousGetSingleSpan(); int left = targetWorkingRect.Left; int right = targetWorkingRect.Right; int width = targetWorkingRect.Width; for (int i = rowInterval.Min; i < rowInterval.Max; i++) { ResizeKernel kernel = verticalKernelMap.GetKernel(i - targetOrigin.Y); while (kernel.StartIndex + kernel.Length > currentWindow.Max) { Slide(); } ref Vector4 reference = ref MemoryMarshal.GetReference(span); int index = kernel.StartIndex - currentWindow.Min; ref Vector4 reference2 = ref span2[index]; for (nint num = 0; num < right - left; num++) { ref Vector4 rowStartRef = ref Unsafe.Add(ref reference2, num * workerHeight); Unsafe.Add(ref reference, num) = kernel.ConvolveCore(ref rowStartRef); } Span destinationPixels = destination.DangerousGetRowSpan(i).Slice(left, width); PixelOperations.Instance.FromVector4Destructive(configuration, span, destinationPixels, conversionModifiers); } } private void Slide() { int num = currentWindow.Max - windowBandHeight; int max = Math.Min(num + workerHeight, sourceRectangle.Height); transposedFirstPassBuffer.DangerousCopyColumns(workerHeight - windowBandHeight, 0, windowBandHeight); currentWindow = new RowInterval(num, max); CalculateFirstPassValues(currentWindow.Slice(windowBandHeight)); } private void CalculateFirstPassValues(RowInterval calculationInterval) { Span span = tempRowBuffer.GetSpan(); Span span2 = transposedFirstPassBuffer.DangerousGetSingleSpan(); int left = targetWorkingRect.Left; int right = targetWorkingRect.Right; int x = targetOrigin.X; for (int i = calculationInterval.Min; i < calculationInterval.Max; i++) { Span span3 = source.DangerousGetRowSpan(i); PixelOperations.Instance.ToVector4(configuration, span3, span, conversionModifiers); ref Vector4 reference = ref span2[i - currentWindow.Min]; nint num = left; nint num2 = 0; while (num < right) { ResizeKernel kernel = horizontalKernelMap.GetKernel(num - x); Unsafe.Add(ref reference, num2 * workerHeight) = kernel.Convolve(span); num++; num2++; } } } } internal class SwizzleProcessor : TransformProcessor where TSwizzler : struct, ISwizzler where TPixel : unmanaged, IPixel { private readonly TSwizzler swizzler; private readonly Size destinationSize; public SwizzleProcessor(Configuration configuration, TSwizzler swizzler, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.swizzler = swizzler; destinationSize = swizzler.DestinationSize; } protected override Size GetDestinationSize() { return destinationSize; } protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { Point point = default(Point); Buffer2D pixelBuffer = source.PixelBuffer; point.Y = 0; while (point.Y < source.Height) { Span span = pixelBuffer.DangerousGetRowSpan(point.Y); point.X = 0; while (point.X < source.Width) { Point point2 = swizzler.Transform(point); destination[point2.X, point2.Y] = span[point.X]; point.X++; } point.Y++; } } } public sealed class SwizzleProcessor : IImageProcessor where TSwizzler : struct, ISwizzler { public TSwizzler Swizzler { get; } public SwizzleProcessor(TSwizzler swizzler) { Swizzler = swizzler; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new SwizzleProcessor(configuration, Swizzler, source, sourceRectangle); } } internal abstract class TransformProcessor : CloningImageProcessor where TPixel : unmanaged, IPixel { protected TransformProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { } protected override void AfterImageApply(Image destination) { TransformProcessorHelpers.UpdateDimensionalMetadata(destination); base.AfterImageApply(destination); } } internal static class TransformProcessorHelpers { public static void UpdateDimensionalMetadata(Image image) where TPixel : unmanaged, IPixel { ExifProfile exifProfile = image.Metadata.ExifProfile; if (exifProfile != null) { if (exifProfile.GetValue(ExifTag.PixelXDimension) != null) { exifProfile.SetValue(ExifTag.PixelXDimension, image.Width); } if (exifProfile.GetValue(ExifTag.PixelYDimension) != null) { exifProfile.SetValue(ExifTag.PixelYDimension, image.Height); } } } } internal static class TransformUtils { public static bool IsDegenerate(Matrix3x2 matrix) { if (!IsNaN(matrix)) { return IsZero(matrix.GetDeterminant()); } return true; } public static bool IsDegenerate(Matrix4x4 matrix) { if (!IsNaN(matrix)) { return IsZero(matrix.GetDeterminant()); } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsZero(float a) { if (a > 0f - Constants.EpsilonSquared) { return a < Constants.EpsilonSquared; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNaN(Matrix3x2 matrix) { if (!float.IsNaN(matrix.M11) && !float.IsNaN(matrix.M12) && !float.IsNaN(matrix.M21) && !float.IsNaN(matrix.M22) && !float.IsNaN(matrix.M31)) { return float.IsNaN(matrix.M32); } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNaN(Matrix4x4 matrix) { if (!float.IsNaN(matrix.M11) && !float.IsNaN(matrix.M12) && !float.IsNaN(matrix.M13) && !float.IsNaN(matrix.M14) && !float.IsNaN(matrix.M21) && !float.IsNaN(matrix.M22) && !float.IsNaN(matrix.M23) && !float.IsNaN(matrix.M24) && !float.IsNaN(matrix.M31) && !float.IsNaN(matrix.M32) && !float.IsNaN(matrix.M33) && !float.IsNaN(matrix.M34) && !float.IsNaN(matrix.M41) && !float.IsNaN(matrix.M42) && !float.IsNaN(matrix.M43)) { return float.IsNaN(matrix.M44); } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 ProjectiveTransform2D(float x, float y, Matrix4x4 matrix) { Vector4 vector = Vector4.Transform(new Vector4(x, y, 0f, 1f), matrix); return new Vector2(vector.X, vector.Y) / MathF.Max(vector.W, 1E-07f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Matrix3x2 CreateRotationMatrixDegrees(float degrees, Size size) { return CreateCenteredTransformMatrix(new Rectangle(Point.Empty, size), Matrix3x2Extensions.CreateRotationDegrees(degrees, PointF.Empty)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Matrix3x2 CreateRotationMatrixRadians(float radians, Size size) { return CreateCenteredTransformMatrix(new Rectangle(Point.Empty, size), Matrix3x2Extensions.CreateRotation(radians, PointF.Empty)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Matrix3x2 CreateSkewMatrixDegrees(float degreesX, float degreesY, Size size) { return CreateCenteredTransformMatrix(new Rectangle(Point.Empty, size), Matrix3x2Extensions.CreateSkewDegrees(degreesX, degreesY, PointF.Empty)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Matrix3x2 CreateSkewMatrixRadians(float radiansX, float radiansY, Size size) { return CreateCenteredTransformMatrix(new Rectangle(Point.Empty, size), Matrix3x2Extensions.CreateSkew(radiansX, radiansY, PointF.Empty)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Matrix3x2 CreateCenteredTransformMatrix(Rectangle sourceRectangle, Matrix3x2 matrix) { Rectangle transformedBoundingRectangle = GetTransformedBoundingRectangle(sourceRectangle, matrix); Matrix3x2.Invert(matrix, out var result); Matrix3x2 matrix3x = Matrix3x2.CreateTranslation(new Vector2(-transformedBoundingRectangle.Width, -transformedBoundingRectangle.Height) * 0.5f); Matrix3x2 matrix3x2 = Matrix3x2.CreateTranslation(new Vector2(sourceRectangle.Width, sourceRectangle.Height) * 0.5f); Matrix3x2.Invert(matrix3x * result * matrix3x2, out var result2); return result2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Matrix4x4 CreateTaperMatrix(Size size, TaperSide side, TaperCorner corner, float fraction) { Matrix4x4 identity = Matrix4x4.Identity; switch (side) { case TaperSide.Left: identity.M11 = fraction; identity.M22 = fraction; identity.M14 = (fraction - 1f) / (float)size.Width; switch (corner) { case TaperCorner.LeftOrTop: identity.M12 = (float)size.Height * identity.M14; identity.M42 = (float)size.Height * (1f - fraction); break; case TaperCorner.Both: identity.M12 = (float)size.Height * 0.5f * identity.M14; identity.M42 = (float)size.Height * (1f - fraction) / 2f; break; } break; case TaperSide.Top: identity.M11 = fraction; identity.M22 = fraction; identity.M24 = (fraction - 1f) / (float)size.Height; switch (corner) { case TaperCorner.LeftOrTop: identity.M21 = (float)size.Width * identity.M24; identity.M41 = (float)size.Width * (1f - fraction); break; case TaperCorner.Both: identity.M21 = (float)size.Width * 0.5f * identity.M24; identity.M41 = (float)size.Width * (1f - fraction) * 0.5f; break; } break; case TaperSide.Right: identity.M11 = 1f / fraction; identity.M14 = (1f - fraction) / ((float)size.Width * fraction); switch (corner) { case TaperCorner.LeftOrTop: identity.M12 = (float)size.Height * identity.M14; break; case TaperCorner.Both: identity.M12 = (float)size.Height * 0.5f * identity.M14; break; } break; case TaperSide.Bottom: identity.M22 = 1f / fraction; identity.M24 = (1f - fraction) / ((float)size.Height * fraction); switch (corner) { case TaperCorner.LeftOrTop: identity.M21 = (float)size.Width * identity.M24; break; case TaperCorner.Both: identity.M21 = (float)size.Width * 0.5f * identity.M24; break; } break; } return identity; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle GetTransformedBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix) { Rectangle transformedRectangle = GetTransformedRectangle(rectangle, matrix); return new Rectangle(0, 0, transformedRectangle.Width, transformedRectangle.Height); } public static Rectangle GetTransformedRectangle(Rectangle rectangle, Matrix3x2 matrix) { if (rectangle.Equals(default(Rectangle)) || Matrix3x2.Identity.Equals(matrix)) { return rectangle; } Vector2 tl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix); Vector2 tr = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix); Vector2 bl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix); Vector2 br = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix); return GetBoundingRectangle(tl, tr, bl, br); } public static Size GetTransformedSize(Size size, Matrix3x2 matrix) { Guard.IsTrue(size.Width > 0 && size.Height > 0, "size", "Source size dimensions cannot be 0!"); if (matrix.Equals(default(Matrix3x2)) || matrix.Equals(Matrix3x2.Identity)) { return size; } return ConstrainSize(GetTransformedRectangle(new Rectangle(Point.Empty, size), matrix)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle GetTransformedRectangle(Rectangle rectangle, Matrix4x4 matrix) { if (rectangle.Equals(default(Rectangle)) || Matrix4x4.Identity.Equals(matrix)) { return rectangle; } Vector2 tl = ProjectiveTransform2D(rectangle.Left, rectangle.Top, matrix); Vector2 tr = ProjectiveTransform2D(rectangle.Right, rectangle.Top, matrix); Vector2 bl = ProjectiveTransform2D(rectangle.Left, rectangle.Bottom, matrix); Vector2 br = ProjectiveTransform2D(rectangle.Right, rectangle.Bottom, matrix); return GetBoundingRectangle(tl, tr, bl, br); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size GetTransformedSize(Size size, Matrix4x4 matrix) { Guard.IsTrue(size.Width > 0 && size.Height > 0, "size", "Source size dimensions cannot be 0!"); if (matrix.Equals(default(Matrix4x4)) || matrix.Equals(Matrix4x4.Identity)) { return size; } return ConstrainSize(GetTransformedRectangle(new Rectangle(Point.Empty, size), matrix)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Size ConstrainSize(Rectangle rectangle) { int num = ((rectangle.Top < 0) ? rectangle.Bottom : Math.Max(rectangle.Height, rectangle.Bottom)); int num2 = ((rectangle.Left < 0) ? rectangle.Right : Math.Max(rectangle.Width, rectangle.Right)); if (num <= 0) { num = rectangle.Height; } if (num2 <= 0) { num2 = rectangle.Width; } return new Size(num2, num); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Rectangle GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br) { float left = MathF.Min(tl.X, MathF.Min(tr.X, MathF.Min(bl.X, br.X))); float top = MathF.Min(tl.Y, MathF.Min(tr.Y, MathF.Min(bl.Y, br.Y))); float right = MathF.Max(tl.X, MathF.Max(tr.X, MathF.Max(bl.X, br.X))); float bottom = MathF.Max(tl.Y, MathF.Max(tr.Y, MathF.Max(bl.Y, br.Y))); return Rectangle.Round(RectangleF.FromLTRB(left, top, right, bottom)); } } } namespace SixLabors.ImageSharp.Processing.Processors.Quantization { public class DefaultPixelSamplingStrategy : IPixelSamplingStrategy { private const int DefaultMaximumPixels = 16777216; public long MaximumPixels { get; } public double MinimumScanRatio { get; } public DefaultPixelSamplingStrategy() : this(16777216, 0.1) { } public DefaultPixelSamplingStrategy(int maximumPixels, double minimumScanRatio) { Guard.MustBeGreaterThan(maximumPixels, 0, "maximumPixels"); MaximumPixels = maximumPixels; MinimumScanRatio = minimumScanRatio; } public IEnumerable> EnumeratePixelRegions(Image image) where TPixel : unmanaged, IPixel { long num = Math.Min(MaximumPixels, (long)image.Width * (long)image.Height * image.Frames.Count) / image.Width; long totalNumberOfRows = (long)image.Height * (long)image.Frames.Count; if (totalNumberOfRows <= num) { foreach (ImageFrame frame in image.Frames) { yield return frame.PixelBuffer.GetRegion(); } yield break; } double value = (double)num / (double)totalNumberOfRows; value = Math.Max(val2: (num <= 200) ? Math.Round(value, 1) : Math.Round(value, 2), val1: MinimumScanRatio); Rational rational = new Rational(value); int denom = (int)rational.Denominator; int num2 = (int)rational.Numerator; for (int pos = 0; pos < totalNumberOfRows; pos++) { if (pos % denom < num2) { yield return GetRow(pos); } } Buffer2DRegion GetRow(int num3) { int index = num3 / image.Height; int y = num3 % image.Height; return image.Frames[index].PixelBuffer.GetRegion(0, y, image.Width, 1); } } } internal sealed class EuclideanPixelMap : IDisposable where TPixel : unmanaged, IPixel { private struct ColorDistanceCache : IDisposable { private const int IndexBits = 5; private const int IndexAlphaBits = 5; private const int IndexCount = 33; private const int IndexAlphaCount = 33; private const int RgbShift = 3; private const int AlphaShift = 3; private const int Entries = 1185921; private MemoryHandle tableHandle; private readonly IMemoryOwner table; private unsafe readonly short* tablePointer; public unsafe ColorDistanceCache(MemoryAllocator allocator) { table = allocator.Allocate(1185921); table.GetSpan().Fill(-1); tableHandle = table.Memory.Pin(); tablePointer = (short*)tableHandle.Pointer; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void Add(Rgba32 rgba, byte index) { int r = rgba.R >> 3; int g = rgba.G >> 3; int b = rgba.B >> 3; int a = rgba.A >> 3; int paletteIndex = GetPaletteIndex(r, g, b, a); tablePointer[paletteIndex] = index; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe bool TryGetValue(Rgba32 rgba, out short match) { int r = rgba.R >> 3; int g = rgba.G >> 3; int b = rgba.B >> 3; int a = rgba.A >> 3; int paletteIndex = GetPaletteIndex(r, g, b, a); match = tablePointer[paletteIndex]; return match > -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { table.GetSpan().Fill(-1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetPaletteIndex(int r, int g, int b, int a) { return (r << 15) + (r << 11) + (g << 10) + (r << 10) + (r << 6) + (g << 5) + (r + g + b << 5) + r + g + b + a; } public void Dispose() { if (table != null) { tableHandle.Dispose(); table.Dispose(); } } } private Rgba32[] rgbaPalette; private ColorDistanceCache cache; private readonly Configuration configuration; public ReadOnlyMemory Palette { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; [MethodImpl(MethodImplOptions.AggressiveInlining)] private set; } public EuclideanPixelMap(Configuration configuration, ReadOnlyMemory palette) { this.configuration = configuration; Palette = palette; rgbaPalette = new Rgba32[palette.Length]; cache = new ColorDistanceCache(configuration.MemoryAllocator); PixelOperations.Instance.ToRgba32(configuration, Palette.Span, rgbaPalette); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetClosestColor(TPixel color, out TPixel match) { ref TPixel reference = ref MemoryMarshal.GetReference(Palette.Span); Unsafe.SkipInit(out var value); color.ToRgba32(ref value); if (!cache.TryGetValue(value, out var match2)) { return GetClosestColorSlow(value, ref reference, out match); } match = Unsafe.Add(ref reference, match2); return match2; } public void Clear(ReadOnlyMemory palette) { Palette = palette; rgbaPalette = new Rgba32[palette.Length]; PixelOperations.Instance.ToRgba32(configuration, Palette.Span, rgbaPalette); cache.Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetClosestColorSlow(Rgba32 rgba, ref TPixel paletteRef, out TPixel match) { int num = 0; float num2 = float.MaxValue; for (int i = 0; i < rgbaPalette.Length; i++) { Rgba32 b = rgbaPalette[i]; int num3 = DistanceSquared(rgba, b); if (num3 == 0) { num = i; break; } if ((float)num3 < num2) { num = i; num2 = num3; } } cache.Add(rgba, (byte)num); match = Unsafe.Add(ref paletteRef, num); return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int DistanceSquared(Rgba32 a, Rgba32 b) { int num = a.R - b.R; int num2 = a.G - b.G; int num3 = a.B - b.B; int num4 = a.A - b.A; return num * num + num2 * num2 + num3 * num3 + num4 * num4; } public void Dispose() { cache.Dispose(); } } public class ExtensivePixelSamplingStrategy : IPixelSamplingStrategy { public IEnumerable> EnumeratePixelRegions(Image image) where TPixel : unmanaged, IPixel { foreach (ImageFrame frame in image.Frames) { yield return frame.PixelBuffer.GetRegion(); } } } public interface IPixelSamplingStrategy { IEnumerable> EnumeratePixelRegions(Image image) where TPixel : unmanaged, IPixel; } public interface IQuantizer { QuantizerOptions Options { get; } IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) where TPixel : unmanaged, IPixel; IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel; } public interface IQuantizer : IDisposable where TPixel : unmanaged, IPixel { Configuration Configuration { get; } QuantizerOptions Options { get; } ReadOnlyMemory Palette { get; } void AddPaletteColors(Buffer2DRegion pixelRegion); IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds); byte GetQuantizedColor(TPixel color, out TPixel match); } public class OctreeQuantizer : IQuantizer { public QuantizerOptions Options { get; } public OctreeQuantizer() : this(new QuantizerOptions()) { } public OctreeQuantizer(QuantizerOptions options) { Guard.NotNull(options, "options"); Options = options; } public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) where TPixel : unmanaged, IPixel { return CreatePixelSpecificQuantizer(configuration, Options); } public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel { return new OctreeQuantizer(configuration, options); } } public struct OctreeQuantizer : IQuantizer, IDisposable where TPixel : unmanaged, IPixel { private sealed class Octree { public sealed class OctreeNode { private readonly OctreeNode[] children; private bool leaf; private int pixelCount; private int red; private int green; private int blue; private int paletteIndex; public OctreeNode NextReducible { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public OctreeNode(int level, int colorBits, Octree octree) { leaf = level == colorBits; red = (green = (blue = 0)); pixelCount = 0; if (leaf) { octree.Leaves++; NextReducible = null; children = null; } else { NextReducible = octree.ReducibleNodes[level]; octree.ReducibleNodes[level] = this; children = new OctreeNode[8]; } } public void AddColor(ref Rgba32 color, int colorBits, int level, Octree octree) { if (leaf) { Increment(ref color); octree.TrackPrevious(this); return; } int colorIndex = GetColorIndex(ref color, level); OctreeNode octreeNode = children[colorIndex]; if (octreeNode == null) { octreeNode = new OctreeNode(level + 1, colorBits, octree); children[colorIndex] = octreeNode; } octreeNode.AddColor(ref color, colorBits, level + 1, octree); } public int Reduce() { red = (green = (blue = 0)); int num = 0; for (int i = 0; i < 8; i++) { OctreeNode octreeNode = children[i]; if (octreeNode != null) { red += octreeNode.red; green += octreeNode.green; blue += octreeNode.blue; pixelCount += octreeNode.pixelCount; num++; children[i] = null; } } leaf = true; return num - 1; } [MethodImpl(MethodImplOptions.NoInlining)] public void ConstructPalette(Span palette, ref int index) { if (leaf) { Vector3 vector = Vector3.Clamp(new Vector3(red, green, blue) / pixelCount, Vector3.Zero, new Vector3(255f)); Unsafe.SkipInit(out var value); value.FromRgba32(new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, byte.MaxValue)); palette[index] = value; paletteIndex = index++; } else { for (int i = 0; i < 8; i++) { children[i]?.ConstructPalette(palette, ref index); } } } [MethodImpl(MethodImplOptions.NoInlining)] public int GetPaletteIndex(ref Rgba32 pixel, int level) { if (leaf) { return paletteIndex; } int colorIndex = GetColorIndex(ref pixel, level); OctreeNode octreeNode = children[colorIndex]; int result = 0; if (octreeNode != null) { result = octreeNode.GetPaletteIndex(ref pixel, level + 1); } else { for (int i = 0; i < children.Length; i++) { octreeNode = children[i]; if (octreeNode != null) { int num = octreeNode.GetPaletteIndex(ref pixel, level + 1); if (num != 0) { return num; } } } } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetColorIndex(ref Rgba32 color, int level) { int num = 7 - level; byte b = (byte)(1 << num); return ((color.R & b) >> num) | ((color.G & b) >> num << 1) | ((color.B & b) >> num << 2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Increment(ref Rgba32 color) { pixelCount++; red += color.R; green += color.G; blue += color.B; } } private readonly OctreeNode root; private readonly int maxColorBits; private OctreeNode previousNode; private Rgba32 previousColor; public int Leaves { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; [MethodImpl(MethodImplOptions.AggressiveInlining)] set; } private OctreeNode[] ReducibleNodes { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public Octree(int maxColorBits) { this.maxColorBits = maxColorBits; Leaves = 0; ReducibleNodes = new OctreeNode[9]; root = new OctreeNode(0, this.maxColorBits, this); previousColor = default(Rgba32); previousNode = null; } public void AddColor(Rgba32 color) { if (previousColor.Equals(color)) { if (previousNode == null) { previousColor = color; root.AddColor(ref color, maxColorBits, 0, this); } else { previousNode.Increment(ref color); } } else { previousColor = color; root.AddColor(ref color, maxColorBits, 0, this); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Palletize(Span palette, int colorCount, ref int paletteIndex) { while (Leaves > colorCount) { Reduce(); } root.ConstructPalette(palette, ref paletteIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetPaletteIndex(TPixel color) { Unsafe.SkipInit(out var value); color.ToRgba32(ref value); return root.GetPaletteIndex(ref value, 0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TrackPrevious(OctreeNode node) { previousNode = node; } private void Reduce() { int num = maxColorBits - 1; while (num > 0 && ReducibleNodes[num] == null) { num--; } OctreeNode octreeNode = ReducibleNodes[num]; ReducibleNodes[num] = octreeNode.NextReducible; Leaves -= octreeNode.Reduce(); previousNode = null; } } private readonly int maxColors; private readonly int bitDepth; private readonly Octree octree; private IMemoryOwner paletteOwner; private ReadOnlyMemory palette; private EuclideanPixelMap pixelMap; private readonly bool isDithering; private bool isDisposed; public Configuration Configuration { get; } public QuantizerOptions Options { get; } public ReadOnlyMemory Palette { get { QuantizerUtilities.CheckPaletteState(in palette); return palette; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public OctreeQuantizer(Configuration configuration, QuantizerOptions options) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(options, "options"); Configuration = configuration; Options = options; maxColors = Options.MaxColors; bitDepth = Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(maxColors), 1, 8); octree = new Octree(bitDepth); paletteOwner = configuration.MemoryAllocator.Allocate(maxColors, AllocationOptions.Clean); pixelMap = null; palette = default(ReadOnlyMemory); isDithering = Options.Dither != null; isDisposed = false; } public void AddPaletteColors(Buffer2DRegion pixelRegion) { Rectangle rectangle = pixelRegion.Rectangle; Buffer2D buffer = pixelRegion.Buffer; using (IMemoryOwner buffer2 = Configuration.MemoryAllocator.Allocate(rectangle.Width)) { Span span = buffer2.GetSpan(); for (int i = rectangle.Top; i < rectangle.Bottom; i++) { Span span2 = buffer.DangerousGetRowSpan(i).Slice(rectangle.Left, rectangle.Width); PixelOperations.Instance.ToRgba32(Configuration, span2, span); for (int j = 0; j < span.Length; j++) { Rgba32 color = span[j]; octree.AddColor(color); } } } int paletteIndex = 0; Span span3 = paletteOwner.GetSpan(); int num = maxColors; if (bitDepth == 8) { num--; } octree.Palletize(span3, num, ref paletteIndex); ReadOnlyMemory readOnlyMemory = paletteOwner.Memory.Slice(0, span3.Length); if (pixelMap == null) { pixelMap = new EuclideanPixelMap(Configuration, readOnlyMemory); } else { pixelMap.Clear(readOnlyMemory); } palette = readOnlyMemory; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds) { return QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly byte GetQuantizedColor(TPixel color, out TPixel match) { if (isDithering || color.Equals(default(TPixel))) { return (byte)pixelMap.GetClosestColor(color, out match); } ref TPixel reference = ref MemoryMarshal.GetReference(palette.Span); byte b = (byte)octree.GetPaletteIndex(color); match = Unsafe.Add(ref reference, b); return b; } public void Dispose() { if (!isDisposed) { isDisposed = true; paletteOwner?.Dispose(); paletteOwner = null; pixelMap?.Dispose(); pixelMap = null; } } } public class PaletteQuantizer : IQuantizer { private readonly ReadOnlyMemory colorPalette; public QuantizerOptions Options { get; } public PaletteQuantizer(ReadOnlyMemory palette) : this(palette, new QuantizerOptions()) { } public PaletteQuantizer(ReadOnlyMemory palette, QuantizerOptions options) { Guard.MustBeGreaterThan(palette.Length, 0, "palette"); Guard.NotNull(options, "options"); colorPalette = palette; Options = options; } public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) where TPixel : unmanaged, IPixel { return CreatePixelSpecificQuantizer(configuration, Options); } public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel { Guard.NotNull(options, "options"); TPixel[] array = new TPixel[Math.Min(colorPalette.Length, options.MaxColors)]; Color.ToPixel(configuration, colorPalette.Span, array.AsSpan()); return new PaletteQuantizer(configuration, options, array); } } internal struct PaletteQuantizer : IQuantizer, IDisposable where TPixel : unmanaged, IPixel { private EuclideanPixelMap pixelMap; public Configuration Configuration { get; } public QuantizerOptions Options { get; } public ReadOnlyMemory Palette => pixelMap.Palette; [MethodImpl(MethodImplOptions.AggressiveInlining)] public PaletteQuantizer(Configuration configuration, QuantizerOptions options, ReadOnlyMemory palette) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(options, "options"); Configuration = configuration; Options = options; pixelMap = new EuclideanPixelMap(configuration, palette); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds) { return QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddPaletteColors(Buffer2DRegion pixelRegion) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly byte GetQuantizedColor(TPixel color, out TPixel match) { return (byte)pixelMap.GetClosestColor(color, out match); } public void Dispose() { pixelMap?.Dispose(); pixelMap = null; } } public class QuantizeProcessor : IImageProcessor { public IQuantizer Quantizer { get; } public QuantizeProcessor(IQuantizer quantizer) { Quantizer = quantizer; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new QuantizeProcessor(configuration, Quantizer, source, sourceRectangle); } } internal class QuantizeProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly IQuantizer quantizer; public QuantizeProcessor(Configuration configuration, IQuantizer quantizer, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { Guard.NotNull(quantizer, "quantizer"); this.quantizer = quantizer; } protected override void OnFrameApply(ImageFrame source) { Rectangle bounds = Rectangle.Intersect(source.Bounds(), base.SourceRectangle); Configuration configuration = base.Configuration; using IQuantizer quantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration); using IndexedImageFrame indexedImageFrame = quantizer.BuildPaletteAndQuantizeFrame(source, bounds); ReadOnlySpan span = indexedImageFrame.Palette.Span; int top = bounds.Top; int left = bounds.Left; Buffer2D pixelBuffer = source.PixelBuffer; for (int i = bounds.Y; i < bounds.Height; i++) { Span span2 = pixelBuffer.DangerousGetRowSpan(i); ReadOnlySpan readOnlySpan = indexedImageFrame.DangerousGetRowSpan(i - top); for (int j = bounds.Left; j < bounds.Right; j++) { span2[j] = span[readOnlySpan[j - left]]; } } } } public static class QuantizerConstants { public const int MinColors = 1; public const int MaxColors = 256; public const float MinDitherScale = 0f; public const float MaxDitherScale = 1f; public static IDither DefaultDither { get; } = KnownDitherings.FloydSteinberg; } public class QuantizerOptions { private float ditherScale = 1f; private int maxColors = 256; public IDither Dither { get; set; } = QuantizerConstants.DefaultDither; public float DitherScale { get { return ditherScale; } set { ditherScale = Numerics.Clamp(value, 0f, 1f); } } public int MaxColors { get { return maxColors; } set { maxColors = Numerics.Clamp(value, 1, 256); } } } public static class QuantizerUtilities { public static void CheckPaletteState(in ReadOnlyMemory palette) where TPixel : unmanaged, IPixel { if (palette.Equals(default(ReadOnlyMemory))) { throw new InvalidOperationException("Frame Quantizer palette has not been built."); } } public static IndexedImageFrame BuildPaletteAndQuantizeFrame(this IQuantizer quantizer, ImageFrame source, Rectangle bounds) where TPixel : unmanaged, IPixel { Guard.NotNull(quantizer, "quantizer"); Guard.NotNull(source, "source"); Rectangle rectangle = Rectangle.Intersect(source.Bounds(), bounds); Buffer2DRegion region = source.PixelBuffer.GetRegion(rectangle); quantizer.AddPaletteColors(region); return quantizer.QuantizeFrame(source, bounds); } public static IndexedImageFrame QuantizeFrame(ref TFrameQuantizer quantizer, ImageFrame source, Rectangle bounds) where TFrameQuantizer : struct, IQuantizer where TPixel : unmanaged, IPixel { Guard.NotNull(source, "source"); Rectangle bounds2 = Rectangle.Intersect(source.Bounds(), bounds); IndexedImageFrame indexedImageFrame = new IndexedImageFrame(quantizer.Configuration, bounds2.Width, bounds2.Height, quantizer.Palette); if (quantizer.Options.Dither == null) { SecondPass(ref quantizer, source, indexedImageFrame, bounds2); } else { using ImageFrame source2 = source.Clone(); SecondPass(ref quantizer, source2, indexedImageFrame, bounds2); } return indexedImageFrame; } internal static void BuildPalette(this IQuantizer quantizer, IPixelSamplingStrategy pixelSamplingStrategy, Image image) where TPixel : unmanaged, IPixel { foreach (Buffer2DRegion item in pixelSamplingStrategy.EnumeratePixelRegions(image)) { quantizer.AddPaletteColors(item); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void SecondPass(ref TFrameQuantizer quantizer, ImageFrame source, IndexedImageFrame destination, Rectangle bounds) where TFrameQuantizer : struct, IQuantizer where TPixel : unmanaged, IPixel { IDither dither = quantizer.Options.Dither; Buffer2D pixelBuffer = source.PixelBuffer; if (dither == null) { int top = bounds.Top; int left = bounds.Left; for (int i = bounds.Y; i < bounds.Height; i++) { Span span = pixelBuffer.DangerousGetRowSpan(i); Span writablePixelRowSpanUnsafe = destination.GetWritablePixelRowSpanUnsafe(i - top); for (int j = bounds.Left; j < bounds.Right; j++) { writablePixelRowSpanUnsafe[j - left] = Unsafe.AsRef(in quantizer).GetQuantizedColor(span[j], out var _); } } } else { dither.ApplyQuantizationDither(ref quantizer, source, destination, bounds); } } } public class WebSafePaletteQuantizer : PaletteQuantizer { public WebSafePaletteQuantizer() : this(new QuantizerOptions()) { } public WebSafePaletteQuantizer(QuantizerOptions options) : base(Color.WebSafePalette, options) { } } public class WernerPaletteQuantizer : PaletteQuantizer { public WernerPaletteQuantizer() : this(new QuantizerOptions()) { } public WernerPaletteQuantizer(QuantizerOptions options) : base(Color.WernerPalette, options) { } } public class WuQuantizer : IQuantizer { public QuantizerOptions Options { get; } public WuQuantizer() : this(new QuantizerOptions()) { } public WuQuantizer(QuantizerOptions options) { Guard.NotNull(options, "options"); Options = options; } public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration) where TPixel : unmanaged, IPixel { return CreatePixelSpecificQuantizer(configuration, Options); } public IQuantizer CreatePixelSpecificQuantizer(Configuration configuration, QuantizerOptions options) where TPixel : unmanaged, IPixel { return new WuQuantizer(configuration, options); } } internal struct WuQuantizer : IQuantizer, IDisposable where TPixel : unmanaged, IPixel { private struct Moment { public long R; public long G; public long B; public long A; public long Weight; public double Moment2; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Moment operator +(Moment x, Moment y) { x.R += y.R; x.G += y.G; x.B += y.B; x.A += y.A; x.Weight += y.Weight; x.Moment2 += y.Moment2; return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Moment operator -(Moment x, Moment y) { x.R -= y.R; x.G -= y.G; x.B -= y.B; x.A -= y.A; x.Weight -= y.Weight; x.Moment2 -= y.Moment2; return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Moment operator -(Moment x) { x.R = -x.R; x.G = -x.G; x.B = -x.B; x.A = -x.A; x.Weight = -x.Weight; x.Moment2 = 0.0 - x.Moment2; return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Moment operator +(Moment x, Rgba32 y) { x.R += y.R; x.G += y.G; x.B += y.B; x.A += y.A; x.Weight++; Vector4 vector = new Vector4((int)y.R, (int)y.G, (int)y.B, (int)y.A); x.Moment2 += Vector4.Dot(vector, vector); return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 Normalize() { return new Vector4(R, G, B, A) / Weight / 255f; } } private struct Box : IEquatable { public int RMin; public int RMax; public int GMin; public int GMax; public int BMin; public int BMax; public int AMin; public int AMax; public int Volume; public override readonly bool Equals(object obj) { if (obj is Box other) { return Equals(other); } return false; } public readonly bool Equals(Box other) { if (RMin == other.RMin && RMax == other.RMax && GMin == other.GMin && GMax == other.GMax && BMin == other.BMin && BMax == other.BMax && AMin == other.AMin && AMax == other.AMax) { return Volume == other.Volume; } return false; } public override readonly int GetHashCode() { HashCode hashCode = default(HashCode); hashCode.Add(RMin); hashCode.Add(RMax); hashCode.Add(GMin); hashCode.Add(GMax); hashCode.Add(BMin); hashCode.Add(BMax); hashCode.Add(AMin); hashCode.Add(AMax); hashCode.Add(Volume); return hashCode.ToHashCode(); } } private readonly MemoryAllocator memoryAllocator; private const int IndexBits = 5; private const int IndexAlphaBits = 5; private const int IndexCount = 33; private const int IndexAlphaCount = 33; private const int TableLength = 1185921; private IMemoryOwner momentsOwner; private IMemoryOwner tagsOwner; private IMemoryOwner paletteOwner; private ReadOnlyMemory palette; private int maxColors; private readonly Box[] colorCube; private EuclideanPixelMap pixelMap; private readonly bool isDithering; private bool isDisposed; public Configuration Configuration { get; } public QuantizerOptions Options { get; } public ReadOnlyMemory Palette { get { QuantizerUtilities.CheckPaletteState(in palette); return palette; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public WuQuantizer(Configuration configuration, QuantizerOptions options) { Guard.NotNull(configuration, "configuration"); Guard.NotNull(options, "options"); Configuration = configuration; Options = options; maxColors = Options.MaxColors; memoryAllocator = Configuration.MemoryAllocator; momentsOwner = memoryAllocator.Allocate(1185921, AllocationOptions.Clean); tagsOwner = memoryAllocator.Allocate(1185921, AllocationOptions.Clean); paletteOwner = memoryAllocator.Allocate(maxColors, AllocationOptions.Clean); colorCube = new Box[maxColors]; isDisposed = false; pixelMap = null; palette = default(ReadOnlyMemory); isDithering = (isDithering = Options.Dither != null); } public void AddPaletteColors(Buffer2DRegion pixelRegion) { Rectangle rectangle = pixelRegion.Rectangle; Buffer2D buffer = pixelRegion.Buffer; Build3DHistogram(buffer, rectangle); Get3DMoments(memoryAllocator); BuildCube(); Span span = paletteOwner.GetSpan().Slice(0, maxColors); ReadOnlySpan moments = momentsOwner.GetSpan(); for (int i = 0; i < span.Length; i++) { Mark(ref colorCube[i], (byte)i); Moment moment = Volume(ref colorCube[i], moments); if (moment.Weight > 0) { span[i].FromScaledVector4(moment.Normalize()); } } ReadOnlyMemory readOnlyMemory = paletteOwner.Memory.Slice(0, span.Length); if (isDithering) { if (pixelMap == null) { pixelMap = new EuclideanPixelMap(Configuration, readOnlyMemory); } else { pixelMap.Clear(readOnlyMemory); } } palette = readOnlyMemory; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly IndexedImageFrame QuantizeFrame(ImageFrame source, Rectangle bounds) { return QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds); } public readonly byte GetQuantizedColor(TPixel color, out TPixel match) { if (isDithering) { return (byte)pixelMap.GetClosestColor(color, out match); } Rgba32 dest = default(Rgba32); color.ToRgba32(ref dest); int num = dest.R >> 3; int num2 = dest.G >> 3; int num3 = dest.B >> 3; int num4 = dest.A >> 3; byte b = ((ReadOnlySpan)tagsOwner.GetSpan())[GetPaletteIndex(num + 1, num2 + 1, num3 + 1, num4 + 1)]; match = Unsafe.Add(ref MemoryMarshal.GetReference(palette.Span), b); return b; } public void Dispose() { if (!isDisposed) { isDisposed = true; momentsOwner?.Dispose(); tagsOwner?.Dispose(); paletteOwner?.Dispose(); momentsOwner = null; tagsOwner = null; paletteOwner = null; pixelMap?.Dispose(); pixelMap = null; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetPaletteIndex(int r, int g, int b, int a) { return (r << 15) + (r << 11) + (g << 10) + (r << 10) + (r << 6) + (g << 5) + (r + g + b << 5) + r + g + b + a; } private static Moment Volume(ref Box cube, ReadOnlySpan moments) { return moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMax)] - moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)] - moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)] + moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] - moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)] + moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] + moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] - moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)] + moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] - moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; } private static Moment Bottom(ref Box cube, int direction, ReadOnlySpan moments) { return direction switch { 3 => -moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)] + moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] - moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)], 2 => -moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)] + moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] + moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] - moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)], 1 => -moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)] + moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] + moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] - moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] - moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)], 0 => -moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)] + moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] + moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] - moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)], _ => throw new ArgumentOutOfRangeException("direction"), }; } private static Moment Top(ref Box cube, int direction, int position, ReadOnlySpan moments) { return direction switch { 3 => moments[GetPaletteIndex(position, cube.GMax, cube.BMax, cube.AMax)] - moments[GetPaletteIndex(position, cube.GMax, cube.BMax, cube.AMin)] - moments[GetPaletteIndex(position, cube.GMax, cube.BMin, cube.AMax)] + moments[GetPaletteIndex(position, cube.GMax, cube.BMin, cube.AMin)] - moments[GetPaletteIndex(position, cube.GMin, cube.BMax, cube.AMax)] + moments[GetPaletteIndex(position, cube.GMin, cube.BMax, cube.AMin)] + moments[GetPaletteIndex(position, cube.GMin, cube.BMin, cube.AMax)] - moments[GetPaletteIndex(position, cube.GMin, cube.BMin, cube.AMin)], 2 => moments[GetPaletteIndex(cube.RMax, position, cube.BMax, cube.AMax)] - moments[GetPaletteIndex(cube.RMax, position, cube.BMax, cube.AMin)] - moments[GetPaletteIndex(cube.RMax, position, cube.BMin, cube.AMax)] + moments[GetPaletteIndex(cube.RMax, position, cube.BMin, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, position, cube.BMax, cube.AMax)] + moments[GetPaletteIndex(cube.RMin, position, cube.BMax, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, position, cube.BMin, cube.AMax)] - moments[GetPaletteIndex(cube.RMin, position, cube.BMin, cube.AMin)], 1 => moments[GetPaletteIndex(cube.RMax, cube.GMax, position, cube.AMax)] - moments[GetPaletteIndex(cube.RMax, cube.GMax, position, cube.AMin)] - moments[GetPaletteIndex(cube.RMax, cube.GMin, position, cube.AMax)] + moments[GetPaletteIndex(cube.RMax, cube.GMin, position, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, cube.GMax, position, cube.AMax)] + moments[GetPaletteIndex(cube.RMin, cube.GMax, position, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, position, cube.AMax)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, position, cube.AMin)], 0 => moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, position)] - moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, position)] - moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, position)] + moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, position)] - moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, position)] + moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, position)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, position)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, position)], _ => throw new ArgumentOutOfRangeException("direction"), }; } private void Build3DHistogram(Buffer2D source, Rectangle bounds) { Span span = momentsOwner.GetSpan(); using IMemoryOwner buffer = memoryAllocator.Allocate(bounds.Width); Span span2 = buffer.GetSpan(); for (int i = bounds.Top; i < bounds.Bottom; i++) { Span span3 = source.DangerousGetRowSpan(i).Slice(bounds.Left, bounds.Width); PixelOperations.Instance.ToRgba32(Configuration, span3, span2); for (int j = 0; j < span2.Length; j++) { Rgba32 rgba = span2[j]; int r = (rgba.R >> 3) + 1; int g = (rgba.G >> 3) + 1; int b = (rgba.B >> 3) + 1; int a = (rgba.A >> 3) + 1; span[GetPaletteIndex(r, g, b, a)] += rgba; } } } private void Get3DMoments(MemoryAllocator allocator) { using IMemoryOwner buffer = allocator.Allocate(1089); using IMemoryOwner buffer2 = allocator.Allocate(33); Span span = momentsOwner.GetSpan(); Span span2 = buffer.GetSpan(); Span span3 = buffer2.GetSpan(); int paletteIndex = GetPaletteIndex(1, 0, 0, 0); for (int i = 1; i < 33; i++) { int num = (i << 15) + (i << 11) + (i << 10) + (i << 6) + i; span2.Clear(); for (int j = 1; j < 33; j++) { int num2 = num + (j << 10) + (j << 5) + j; int num3 = i + j; span3.Clear(); for (int k = 1; k < 33; k++) { int num4 = num2 + (num3 + k << 5) + k; Moment moment = default(Moment); int num5 = k * 33; for (int l = 1; l < 33; l++) { int num6 = num4 + l; moment += span[num6]; span3[l] += moment; int index = num5 + l; span2[index] += span3[l]; int index2 = num6 - paletteIndex; span[num6] = span[index2] + span2[index]; } } } } } private double Variance(ref Box cube) { ReadOnlySpan moments = momentsOwner.GetSpan(); Moment moment = Volume(ref cube, moments); Moment moment2 = moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMax)] - moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)] - moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)] + moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] - moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)] + moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] + moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] - moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)] + moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] - moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] - moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] + moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; Vector4 vector = new Vector4(moment.R, moment.G, moment.B, moment.A); return moment2.Moment2 - (double)(Vector4.Dot(vector, vector) / (float)moment.Weight); } private float Maximize(ref Box cube, int direction, int first, int last, out int cut, Moment whole) { ReadOnlySpan moments = momentsOwner.GetSpan(); Moment moment = Bottom(ref cube, direction, moments); float num = 0f; cut = -1; for (int i = first; i < last; i++) { Moment moment2 = moment + Top(ref cube, direction, i, moments); if (moment2.Weight == 0L) { continue; } Vector4 vector = new Vector4(moment2.R, moment2.G, moment2.B, moment2.A); float num2 = Vector4.Dot(vector, vector) / (float)moment2.Weight; moment2 = whole - moment2; if (moment2.Weight != 0L) { vector = new Vector4(moment2.R, moment2.G, moment2.B, moment2.A); num2 += Vector4.Dot(vector, vector) / (float)moment2.Weight; if (num2 > num) { num = num2; cut = i; } } } return num; } private bool Cut(ref Box set1, ref Box set2) { ReadOnlySpan moments = momentsOwner.GetSpan(); Moment whole = Volume(ref set1, moments); int cut; float num = Maximize(ref set1, 3, set1.RMin + 1, set1.RMax, out cut, whole); int cut2; float num2 = Maximize(ref set1, 2, set1.GMin + 1, set1.GMax, out cut2, whole); int cut3; float num3 = Maximize(ref set1, 1, set1.BMin + 1, set1.BMax, out cut3, whole); int cut4; float num4 = Maximize(ref set1, 0, set1.AMin + 1, set1.AMax, out cut4, whole); int num5; if (!(num >= num2) || !(num >= num3) || !(num >= num4)) { num5 = ((num2 >= num && num2 >= num3 && num2 >= num4) ? 2 : ((num3 >= num && num3 >= num2 && num3 >= num4) ? 1 : 0)); } else { num5 = 3; if (cut < 0) { return false; } } set2.RMax = set1.RMax; set2.GMax = set1.GMax; set2.BMax = set1.BMax; set2.AMax = set1.AMax; switch (num5) { case 3: set2.RMin = (set1.RMax = cut); set2.GMin = set1.GMin; set2.BMin = set1.BMin; set2.AMin = set1.AMin; break; case 2: set2.GMin = (set1.GMax = cut2); set2.RMin = set1.RMin; set2.BMin = set1.BMin; set2.AMin = set1.AMin; break; case 1: set2.BMin = (set1.BMax = cut3); set2.RMin = set1.RMin; set2.GMin = set1.GMin; set2.AMin = set1.AMin; break; case 0: set2.AMin = (set1.AMax = cut4); set2.RMin = set1.RMin; set2.GMin = set1.GMin; set2.BMin = set1.BMin; break; } set1.Volume = (set1.RMax - set1.RMin) * (set1.GMax - set1.GMin) * (set1.BMax - set1.BMin) * (set1.AMax - set1.AMin); set2.Volume = (set2.RMax - set2.RMin) * (set2.GMax - set2.GMin) * (set2.BMax - set2.BMin) * (set2.AMax - set2.AMin); return true; } private void Mark(ref Box cube, byte label) { Span span = tagsOwner.GetSpan(); for (int i = cube.RMin + 1; i <= cube.RMax; i++) { int num = (i << 15) + (i << 11) + (i << 10) + (i << 6) + i; for (int j = cube.GMin + 1; j <= cube.GMax; j++) { int num2 = num + (j << 10) + (j << 5) + j; int num3 = i + j; for (int k = cube.BMin + 1; k <= cube.BMax; k++) { int num4 = num2 + (num3 + k << 5) + k; for (int l = cube.AMin + 1; l <= cube.AMax; l++) { int index = num4 + l; span[index] = label; } } } } } private void BuildCube() { using IMemoryOwner buffer = Configuration.MemoryAllocator.Allocate(maxColors); Span span = buffer.GetSpan(); ref Box reference = ref colorCube[0]; reference.RMin = (reference.GMin = (reference.BMin = (reference.AMin = 0))); reference.RMax = (reference.GMax = (reference.BMax = 32)); reference.AMax = 32; int num = 0; for (int i = 1; i < maxColors; i++) { ref Box reference2 = ref colorCube[num]; ref Box reference3 = ref colorCube[i]; if (Cut(ref reference2, ref reference3)) { span[num] = ((reference2.Volume > 1) ? Variance(ref reference2) : 0.0); span[i] = ((reference3.Volume > 1) ? Variance(ref reference3) : 0.0); } else { span[num] = 0.0; i--; } num = 0; double num2 = span[0]; for (int j = 1; j <= i; j++) { if (span[j] > num2) { num2 = span[j]; num = j; } } if (num2 <= 0.0) { maxColors = i + 1; break; } } } } } namespace SixLabors.ImageSharp.Processing.Processors.Overlays { public sealed class BackgroundColorProcessor : IImageProcessor { public GraphicsOptions GraphicsOptions { get; } public Color Color { get; } public BackgroundColorProcessor(GraphicsOptions options, Color color) { Color = color; GraphicsOptions = options; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new BackgroundColorProcessor(configuration, this, source, sourceRectangle); } } internal class BackgroundColorProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly Configuration configuration; private readonly Rectangle bounds; private readonly PixelBlender blender; private readonly IMemoryOwner amount; private readonly IMemoryOwner colors; private readonly Buffer2D source; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(Configuration configuration, Rectangle bounds, PixelBlender blender, IMemoryOwner amount, IMemoryOwner colors, Buffer2D source) { this.configuration = configuration; this.bounds = bounds; this.blender = blender; this.amount = amount; this.colors = colors; this.source = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Span span = source.DangerousGetRowSpan(y).Slice(bounds.X, bounds.Width); blender.Blend(configuration, span, colors.GetSpan(), span, amount.GetSpan()); } } private readonly BackgroundColorProcessor definition; public BackgroundColorProcessor(Configuration configuration, BackgroundColorProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.definition = definition; } protected override void OnFrameApply(ImageFrame source) { TPixel value = definition.Color.ToPixel(); GraphicsOptions graphicsOptions = definition.GraphicsOptions; Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); Configuration configuration = base.Configuration; MemoryAllocator memoryAllocator = configuration.MemoryAllocator; using IMemoryOwner memoryOwner = memoryAllocator.Allocate(rectangle.Width); using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(rectangle.Width); memoryOwner.GetSpan().Fill(value); memoryOwner2.GetSpan().Fill(graphicsOptions.BlendPercentage); PixelBlender pixelBlender = PixelOperations.Instance.GetPixelBlender(graphicsOptions); RowOperation operation = new RowOperation(configuration, rectangle, pixelBlender, memoryOwner2, memoryOwner, source.PixelBuffer); ParallelRowIterator.IterateRows(configuration, rectangle, in operation); } } public sealed class GlowProcessor : IImageProcessor { public GraphicsOptions GraphicsOptions { get; } public Color GlowColor { get; } internal ValueSize Radius { get; } public GlowProcessor(GraphicsOptions options, Color color) : this(options, color, 0f) { } internal GlowProcessor(GraphicsOptions options, Color color, ValueSize radius) { GlowColor = color; Radius = radius; GraphicsOptions = options; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new GlowProcessor(configuration, this, source, sourceRectangle); } } internal class GlowProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly Configuration configuration; private readonly Rectangle bounds; private readonly PixelBlender blender; private readonly Vector2 center; private readonly float maxDistance; private readonly float blendPercent; private readonly IMemoryOwner colors; private readonly Buffer2D source; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(Configuration configuration, Rectangle bounds, IMemoryOwner colors, PixelBlender blender, Vector2 center, float maxDistance, float blendPercent, Buffer2D source) { this.configuration = configuration; this.bounds = bounds; this.colors = colors; this.blender = blender; this.center = center; this.maxDistance = maxDistance; this.blendPercent = blendPercent; this.source = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { Span span2 = colors.GetSpan(); for (int i = 0; i < bounds.Width; i++) { float num = Vector2.Distance(center, new Vector2(i + bounds.X, y)); span[i] = Numerics.Clamp(blendPercent * (1f - 0.95f * (num / maxDistance)), 0f, 1f); } Span span3 = source.DangerousGetRowSpan(y).Slice(bounds.X, bounds.Width); blender.Blend(configuration, span3, span3, span2, span); } } private readonly PixelBlender blender; private readonly GlowProcessor definition; public GlowProcessor(Configuration configuration, GlowProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.definition = definition; blender = PixelOperations.Instance.GetPixelBlender(definition.GraphicsOptions); } protected override void OnFrameApply(ImageFrame source) { TPixel value = definition.GlowColor.ToPixel(); float blendPercentage = definition.GraphicsOptions.BlendPercentage; Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); Vector2 center = Rectangle.Center(rectangle); float num = definition.Radius.Calculate(rectangle.Size); float maxDistance = ((num > 0f) ? MathF.Min(num, (float)rectangle.Width * 0.5f) : ((float)rectangle.Width * 0.5f)); Configuration configuration = base.Configuration; using IMemoryOwner memoryOwner = configuration.MemoryAllocator.Allocate(rectangle.Width); memoryOwner.GetSpan().Fill(value); RowOperation operation = new RowOperation(configuration, rectangle, memoryOwner, blender, center, maxDistance, blendPercentage, source.PixelBuffer); ParallelRowIterator.IterateRows(configuration, rectangle, in operation); } } public sealed class VignetteProcessor : IImageProcessor { public GraphicsOptions GraphicsOptions { get; } public Color VignetteColor { get; } internal ValueSize RadiusX { get; } internal ValueSize RadiusY { get; } public VignetteProcessor(GraphicsOptions options, Color color) { VignetteColor = color; GraphicsOptions = options; } internal VignetteProcessor(GraphicsOptions options, Color color, ValueSize radiusX, ValueSize radiusY) { VignetteColor = color; RadiusX = radiusX; RadiusY = radiusY; GraphicsOptions = options; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new VignetteProcessor(configuration, this, source, sourceRectangle); } } internal class VignetteProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly Configuration configuration; private readonly Rectangle bounds; private readonly PixelBlender blender; private readonly Vector2 center; private readonly float maxDistance; private readonly float blendPercent; private readonly IMemoryOwner colors; private readonly Buffer2D source; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(Configuration configuration, Rectangle bounds, IMemoryOwner colors, PixelBlender blender, Vector2 center, float maxDistance, float blendPercent, Buffer2D source) { this.configuration = configuration; this.bounds = bounds; this.colors = colors; this.blender = blender; this.center = center; this.maxDistance = maxDistance; this.blendPercent = blendPercent; this.source = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { Span span2 = colors.GetSpan(); for (int i = 0; i < bounds.Width; i++) { float num = Vector2.Distance(center, new Vector2(i + bounds.X, y)); span[i] = Numerics.Clamp(blendPercent * (0.9f * (num / maxDistance)), 0f, 1f); } Span span3 = source.DangerousGetRowSpan(y).Slice(bounds.X, bounds.Width); blender.Blend(configuration, span3, span3, span2, span); } } private readonly PixelBlender blender; private readonly VignetteProcessor definition; public VignetteProcessor(Configuration configuration, VignetteProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.definition = definition; blender = PixelOperations.Instance.GetPixelBlender(definition.GraphicsOptions); } protected override void OnFrameApply(ImageFrame source) { TPixel value = definition.VignetteColor.ToPixel(); float blendPercentage = definition.GraphicsOptions.BlendPercentage; Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); Vector2 center = Rectangle.Center(rectangle); float num = definition.RadiusX.Calculate(rectangle.Size); float num2 = definition.RadiusY.Calculate(rectangle.Size); float num3 = ((num > 0f) ? MathF.Min(num, (float)rectangle.Width * 0.5f) : ((float)rectangle.Width * 0.5f)); float num4 = ((num2 > 0f) ? MathF.Min(num2, (float)rectangle.Height * 0.5f) : ((float)rectangle.Height * 0.5f)); float maxDistance = MathF.Sqrt(num3 * num3 + num4 * num4); Configuration configuration = base.Configuration; using IMemoryOwner memoryOwner = configuration.MemoryAllocator.Allocate(rectangle.Width); memoryOwner.GetSpan().Fill(value); RowOperation operation = new RowOperation(configuration, rectangle, memoryOwner, blender, center, maxDistance, blendPercentage, source.PixelBuffer); ParallelRowIterator.IterateRows(configuration, rectangle, in operation); } } } namespace SixLabors.ImageSharp.Processing.Processors.Normalization { public class AdaptiveHistogramEqualizationProcessor : HistogramEqualizationProcessor { public int NumberOfTiles { get; } public AdaptiveHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, int clipLimit, int numberOfTiles) : base(luminanceLevels, clipHistogram, clipLimit) { NumberOfTiles = numberOfTiles; } public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new AdaptiveHistogramEqualizationProcessor(configuration, base.LuminanceLevels, base.ClipHistogram, base.ClipLimit, NumberOfTiles, source, sourceRectangle); } } internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualizationProcessor where TPixel : unmanaged, IPixel { private readonly struct RowIntervalOperation : IRowIntervalOperation { private readonly CdfTileData cdfData; private readonly List<(int Y, int CdfY)> tileYStartPositions; private readonly int tileWidth; private readonly int tileHeight; private readonly int tileCount; private readonly int halfTileWidth; private readonly int luminanceLevels; private readonly Buffer2D source; private readonly int sourceWidth; private readonly int sourceHeight; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowIntervalOperation(CdfTileData cdfData, List<(int Y, int CdfY)> tileYStartPositions, int tileWidth, int tileHeight, int tileCount, int halfTileWidth, int luminanceLevels, Buffer2D source) { this.cdfData = cdfData; this.tileYStartPositions = tileYStartPositions; this.tileWidth = tileWidth; this.tileHeight = tileHeight; this.tileCount = tileCount; this.halfTileWidth = halfTileWidth; this.luminanceLevels = luminanceLevels; this.source = source; sourceWidth = source.Width; sourceHeight = source.Height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(in RowInterval rows) { for (int i = rows.Min; i < rows.Max; i++) { (int Y, int CdfY) tuple = tileYStartPositions[i]; int item = tuple.Y; int item2 = tuple.CdfY; int num = 0; int num2 = halfTileWidth; for (int j = 0; j < tileCount - 1; j++) { int num3 = 0; int num4 = Math.Min(item + tileHeight, sourceHeight); int num5 = Math.Min(num2 + tileWidth, sourceWidth); for (int k = item; k < num4; k++) { Span span = source.DangerousGetRowSpan(k); int num6 = 0; for (int l = num2; l < num5; l++) { ref TPixel reference = ref span[l]; float num7 = AdaptiveHistogramEqualizationProcessor.InterpolateBetweenFourTiles(reference, cdfData, tileCount, tileCount, num6, num3, num, item2, tileWidth, tileHeight, luminanceLevels); reference.FromVector4(new Vector4(num7, num7, num7, reference.ToVector4().W)); num6++; } num3++; } num++; num2 += tileWidth; } } } void IRowIntervalOperation.Invoke(in RowInterval rows) { Invoke(in rows); } } private sealed class CdfTileData : IDisposable { private readonly struct RowIntervalOperation : IRowIntervalOperation { private readonly HistogramEqualizationProcessor processor; private readonly MemoryAllocator allocator; private readonly Buffer2D cdfMinBuffer2D; private readonly Buffer2D cdfLutBuffer2D; private readonly List<(int Y, int CdfY)> tileYStartPositions; private readonly int tileWidth; private readonly int tileHeight; private readonly int luminanceLevels; private readonly Buffer2D source; private readonly int sourceWidth; private readonly int sourceHeight; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowIntervalOperation(HistogramEqualizationProcessor processor, MemoryAllocator allocator, Buffer2D cdfMinBuffer2D, Buffer2D cdfLutBuffer2D, List<(int Y, int CdfY)> tileYStartPositions, int tileWidth, int tileHeight, int luminanceLevels, Buffer2D source) { this.processor = processor; this.allocator = allocator; this.cdfMinBuffer2D = cdfMinBuffer2D; this.cdfLutBuffer2D = cdfLutBuffer2D; this.tileYStartPositions = tileYStartPositions; this.tileWidth = tileWidth; this.tileHeight = tileHeight; this.luminanceLevels = luminanceLevels; this.source = source; sourceWidth = source.Width; sourceHeight = source.Height; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(in RowInterval rows) { for (int i = rows.Min; i < rows.Max; i++) { int num = 0; int item = tileYStartPositions[i].CdfY; int item2 = tileYStartPositions[i].Y; int num2 = Math.Min(item2 + tileHeight, sourceHeight); Span span = cdfMinBuffer2D.DangerousGetRowSpan(item); span.Clear(); using IMemoryOwner buffer = allocator.Allocate(luminanceLevels); Span span2 = buffer.GetSpan(); ref int reference = ref MemoryMarshal.GetReference(span2); for (int j = 0; j < sourceWidth; j += tileWidth) { span2.Clear(); ref int reference2 = ref MemoryMarshal.GetReference(cdfLutBuffer2D.DangerousGetRowSpan(i).Slice(num * luminanceLevels, luminanceLevels)); int num3 = Math.Min(j + tileWidth, sourceWidth); for (int k = item2; k < num2; k++) { Span span3 = source.DangerousGetRowSpan(k); for (int l = j; l < num3; l++) { span2[HistogramEqualizationProcessor.GetLuminance(span3[l], luminanceLevels)]++; } } if (processor.ClipHistogramEnabled) { processor.ClipHistogram(span2, processor.ClipLimit); } span[num] += processor.CalculateCdf(ref reference2, ref reference, span2.Length - 1); num++; } } } void IRowIntervalOperation.Invoke(in RowInterval rows) { Invoke(in rows); } } private readonly Configuration configuration; private readonly MemoryAllocator memoryAllocator; private readonly Buffer2D cdfMinBuffer2D; private readonly Buffer2D cdfLutBuffer2D; private readonly int pixelsInTile; private readonly int sourceWidth; private readonly int tileWidth; private readonly int tileHeight; private readonly int luminanceLevels; private readonly List<(int Y, int CdfY)> tileYStartPositions; public CdfTileData(Configuration configuration, int sourceWidth, int sourceHeight, int tileCountX, int tileCountY, int tileWidth, int tileHeight, int luminanceLevels) { this.configuration = configuration; memoryAllocator = configuration.MemoryAllocator; this.luminanceLevels = luminanceLevels; cdfMinBuffer2D = memoryAllocator.Allocate2D(tileCountX, tileCountY); cdfLutBuffer2D = memoryAllocator.Allocate2D(tileCountX * luminanceLevels, tileCountY); this.sourceWidth = sourceWidth; this.tileWidth = tileWidth; this.tileHeight = tileHeight; pixelsInTile = tileWidth * tileHeight; tileYStartPositions = new List<(int, int)>(); int num = 0; for (int i = 0; i < sourceHeight; i += tileHeight) { tileYStartPositions.Add((i, num)); num++; } } public void CalculateLookupTables(ImageFrame source, HistogramEqualizationProcessor processor) { RowIntervalOperation operation = new RowIntervalOperation(processor, memoryAllocator, cdfMinBuffer2D, cdfLutBuffer2D, tileYStartPositions, tileWidth, tileHeight, luminanceLevels, source.PixelBuffer); ParallelRowIterator.IterateRowIntervals(configuration, new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count), in operation); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetCdfLutSpan(int tileX, int tileY) { return cdfLutBuffer2D.DangerousGetRowSpan(tileY).Slice(tileX * luminanceLevels, luminanceLevels); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public float RemapGreyValue(int tilesX, int tilesY, int luminance) { int num = cdfMinBuffer2D[tilesX, tilesY]; Span cdfLutSpan = GetCdfLutSpan(tilesX, tilesY); if (pixelsInTile - num != 0) { return (float)cdfLutSpan[luminance] / (float)(pixelsInTile - num); } return cdfLutSpan[luminance] / pixelsInTile; } public void Dispose() { cdfMinBuffer2D.Dispose(); cdfLutBuffer2D.Dispose(); } } private int Tiles { get; } public AdaptiveHistogramEqualizationProcessor(Configuration configuration, int luminanceLevels, bool clipHistogram, int clipLimit, int tiles, Image source, Rectangle sourceRectangle) : base(configuration, luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, "tiles"); Guard.MustBeLessThanOrEqualTo(tiles, 100, "tiles"); Tiles = tiles; } protected override void OnFrameApply(ImageFrame source) { int width = source.Width; int height = source.Height; int num = (int)MathF.Ceiling((float)width / (float)Tiles); int num2 = (int)MathF.Ceiling((float)height / (float)Tiles); int tiles = Tiles; int num3 = num / 2; int num4 = num2 / 2; int luminanceLevels = base.LuminanceLevels; using CdfTileData cdfTileData = new CdfTileData(base.Configuration, width, height, Tiles, Tiles, num, num2, luminanceLevels); cdfTileData.CalculateLookupTables(source, this); List<(int, int)> list = new List<(int, int)>(); int num5 = 0; int num6 = num4; for (int i = 0; i < tiles - 1; i++) { list.Add((num6, num5)); num5++; num6 += num2; } RowIntervalOperation operation = new RowIntervalOperation(cdfTileData, list, num, num2, tiles, num3, luminanceLevels, source.PixelBuffer); ParallelRowIterator.IterateRowIntervals(base.Configuration, new Rectangle(0, 0, width, list.Count), in operation); ProcessBorderColumn(source.PixelBuffer, cdfTileData, 0, height, Tiles, num2, 0, num3, luminanceLevels); int xStart = (Tiles - 1) * num + num3; ProcessBorderColumn(source.PixelBuffer, cdfTileData, Tiles - 1, height, Tiles, num2, xStart, width, luminanceLevels); ProcessBorderRow(source.PixelBuffer, cdfTileData, 0, width, Tiles, num, 0, num4, luminanceLevels); int yStart = (Tiles - 1) * num2 + num4; ProcessBorderRow(source.PixelBuffer, cdfTileData, Tiles - 1, width, Tiles, num, yStart, height, luminanceLevels); ProcessCornerTile(source.PixelBuffer, cdfTileData, 0, 0, 0, num3, 0, num4, luminanceLevels); ProcessCornerTile(source.PixelBuffer, cdfTileData, 0, Tiles - 1, 0, num3, yStart, height, luminanceLevels); ProcessCornerTile(source.PixelBuffer, cdfTileData, Tiles - 1, 0, xStart, width, 0, num4, luminanceLevels); ProcessCornerTile(source.PixelBuffer, cdfTileData, Tiles - 1, Tiles - 1, xStart, width, yStart, height, luminanceLevels); } private static void ProcessCornerTile(Buffer2D source, CdfTileData cdfData, int cdfX, int cdfY, int xStart, int xEnd, int yStart, int yEnd, int luminanceLevels) { for (int i = yStart; i < yEnd; i++) { Span span = source.DangerousGetRowSpan(i); for (int j = xStart; j < xEnd; j++) { ref TPixel reference = ref span[j]; float num = cdfData.RemapGreyValue(cdfX, cdfY, HistogramEqualizationProcessor.GetLuminance(reference, luminanceLevels)); reference.FromVector4(new Vector4(num, num, num, reference.ToVector4().W)); } } } private static void ProcessBorderColumn(Buffer2D source, CdfTileData cdfData, int cdfX, int sourceHeight, int tileCount, int tileHeight, int xStart, int xEnd, int luminanceLevels) { int num = tileHeight / 2; int num2 = 0; int num3 = num; for (int i = 0; i < tileCount - 1; i++) { int num4 = Math.Min(num3 + tileHeight, sourceHeight - 1); int num5 = 0; for (int j = num3; j < num4; j++) { Span span = source.DangerousGetRowSpan(j); for (int k = xStart; k < xEnd; k++) { ref TPixel reference = ref span[k]; float num6 = InterpolateBetweenTwoTiles(reference, cdfData, cdfX, num2, cdfX, num2 + 1, num5, tileHeight, luminanceLevels); reference.FromVector4(new Vector4(num6, num6, num6, reference.ToVector4().W)); } num5++; } num2++; num3 += tileHeight; } } private static void ProcessBorderRow(Buffer2D source, CdfTileData cdfData, int cdfY, int sourceWidth, int tileCount, int tileWidth, int yStart, int yEnd, int luminanceLevels) { int num = tileWidth / 2; int num2 = 0; int num3 = num; for (int i = 0; i < tileCount - 1; i++) { for (int j = yStart; j < yEnd; j++) { Span span = source.DangerousGetRowSpan(j); int num4 = 0; int num5 = Math.Min(num3 + tileWidth, sourceWidth - 1); for (int k = num3; k < num5; k++) { ref TPixel reference = ref span[k]; float num6 = InterpolateBetweenTwoTiles(reference, cdfData, num2, cdfY, num2 + 1, cdfY, num4, tileWidth, luminanceLevels); reference.FromVector4(new Vector4(num6, num6, num6, reference.ToVector4().W)); num4++; } } num2++; num3 += tileWidth; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float InterpolateBetweenFourTiles(TPixel sourcePixel, CdfTileData cdfData, int tileCountX, int tileCountY, int tileX, int tileY, int cdfX, int cdfY, int tileWidth, int tileHeight, int luminanceLevels) { int luminance = HistogramEqualizationProcessor.GetLuminance(sourcePixel, luminanceLevels); float tx = (float)tileX / (float)(tileWidth - 1); float ty = (float)tileY / (float)(tileHeight - 1); int tilesY = Math.Min(tileCountY - 1, cdfY + 1); int tilesX = Math.Min(tileCountX - 1, cdfX + 1); float lt = cdfData.RemapGreyValue(cdfX, cdfY, luminance); float rt = cdfData.RemapGreyValue(tilesX, cdfY, luminance); float lb = cdfData.RemapGreyValue(cdfX, tilesY, luminance); float rb = cdfData.RemapGreyValue(tilesX, tilesY, luminance); return BilinearInterpolation(tx, ty, lt, rt, lb, rb); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float InterpolateBetweenTwoTiles(TPixel sourcePixel, CdfTileData cdfData, int tileX1, int tileY1, int tileX2, int tileY2, int tilePos, int tileWidth, int luminanceLevels) { int luminance = HistogramEqualizationProcessor.GetLuminance(sourcePixel, luminanceLevels); float t = (float)tilePos / (float)(tileWidth - 1); float left = cdfData.RemapGreyValue(tileX1, tileY1, luminance); float right = cdfData.RemapGreyValue(tileX2, tileY2, luminance); return LinearInterpolation(left, right, t); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float BilinearInterpolation(float tx, float ty, float lt, float rt, float lb, float rb) { return LinearInterpolation(LinearInterpolation(lt, rt, tx), LinearInterpolation(lb, rb, tx), ty); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float LinearInterpolation(float left, float right, float t) { return left + (right - left) * t; } } public class AdaptiveHistogramEqualizationSlidingWindowProcessor : HistogramEqualizationProcessor { public int NumberOfTiles { get; } public AdaptiveHistogramEqualizationSlidingWindowProcessor(int luminanceLevels, bool clipHistogram, int clipLimit, int numberOfTiles) : base(luminanceLevels, clipHistogram, clipLimit) { NumberOfTiles = numberOfTiles; } public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new AdaptiveHistogramEqualizationSlidingWindowProcessor(configuration, base.LuminanceLevels, base.ClipHistogram, base.ClipLimit, NumberOfTiles, source, sourceRectangle); } } internal class AdaptiveHistogramEqualizationSlidingWindowProcessor : HistogramEqualizationProcessor where TPixel : unmanaged, IPixel { private readonly struct SlidingWindowOperation { private readonly Configuration configuration; private readonly AdaptiveHistogramEqualizationSlidingWindowProcessor processor; private readonly ImageFrame source; private readonly MemoryAllocator memoryAllocator; private readonly Buffer2D targetPixels; private readonly SlidingWindowInfos swInfos; private readonly int yStart; private readonly int yEnd; private readonly bool useFastPath; [MethodImpl(MethodImplOptions.AggressiveInlining)] public SlidingWindowOperation(Configuration configuration, AdaptiveHistogramEqualizationSlidingWindowProcessor processor, ImageFrame source, MemoryAllocator memoryAllocator, Buffer2D targetPixels, SlidingWindowInfos swInfos, int yStart, int yEnd, bool useFastPath) { this.configuration = configuration; this.processor = processor; this.source = source; this.memoryAllocator = memoryAllocator; this.targetPixels = targetPixels; this.swInfos = swInfos; this.yStart = yStart; this.yEnd = yEnd; this.useFastPath = useFastPath; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int x) { using IMemoryOwner buffer = memoryAllocator.Allocate(processor.LuminanceLevels, AllocationOptions.Clean); using IMemoryOwner buffer2 = memoryAllocator.Allocate(processor.LuminanceLevels, AllocationOptions.Clean); using IMemoryOwner buffer3 = memoryAllocator.Allocate(processor.LuminanceLevels, AllocationOptions.Clean); using IMemoryOwner buffer4 = memoryAllocator.Allocate(swInfos.TileWidth, AllocationOptions.Clean); Span span = buffer.GetSpan(); ref int reference = ref MemoryMarshal.GetReference(span); Span span2 = buffer2.GetSpan(); ref int reference2 = ref MemoryMarshal.GetReference(span2); ref int reference3 = ref MemoryMarshal.GetReference(buffer3.GetSpan()); Span span3 = buffer4.GetSpan(); ref Vector4 reference4 = ref MemoryMarshal.GetReference(span3); for (int i = yStart - swInfos.HalfTileHeight; i < yStart + swInfos.HalfTileHeight; i++) { if (useFastPath) { processor.CopyPixelRowFast(source.PixelBuffer, span3, x - swInfos.HalfTileWidth, i, swInfos.TileWidth, configuration); } else { processor.CopyPixelRow(source, span3, x - swInfos.HalfTileWidth, i, swInfos.TileWidth, configuration); } processor.AddPixelsToHistogram(ref reference4, ref reference, processor.LuminanceLevels, span3.Length); } for (int j = yStart; j < yEnd; j++) { if (processor.ClipHistogramEnabled) { span.CopyTo(span2); processor.ClipHistogram(span2, processor.ClipLimit); } int num = (processor.ClipHistogramEnabled ? processor.CalculateCdf(ref reference3, ref reference2, span.Length - 1) : processor.CalculateCdf(ref reference3, ref reference, span.Length - 1)); float num2 = swInfos.PixelInTile - num; int luminance = HistogramEqualizationProcessor.GetLuminance(source[x, j], processor.LuminanceLevels); float num3 = (float)Unsafe.Add(ref reference3, luminance) / num2; targetPixels[x, j].FromVector4(new Vector4(num3, num3, num3, source[x, j].ToVector4().W)); if (useFastPath) { processor.CopyPixelRowFast(source.PixelBuffer, span3, x - swInfos.HalfTileWidth, j - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); } else { processor.CopyPixelRow(source, span3, x - swInfos.HalfTileWidth, j - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); } processor.RemovePixelsFromHistogram(ref reference4, ref reference, processor.LuminanceLevels, span3.Length); if (useFastPath) { processor.CopyPixelRowFast(source.PixelBuffer, span3, x - swInfos.HalfTileWidth, j + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); } else { processor.CopyPixelRow(source, span3, x - swInfos.HalfTileWidth, j + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); } processor.AddPixelsToHistogram(ref reference4, ref reference, processor.LuminanceLevels, span3.Length); } } } private class SlidingWindowInfos { public int TileWidth { get; } public int TileHeight { get; } public int PixelInTile { get; } public int HalfTileWidth { get; } public int HalfTileHeight { get; } public SlidingWindowInfos(int tileWidth, int tileHeight, int halfTileWidth, int halfTileHeight, int pixelInTile) { TileWidth = tileWidth; TileHeight = tileHeight; HalfTileWidth = halfTileWidth; HalfTileHeight = halfTileHeight; PixelInTile = pixelInTile; } } private int Tiles { get; } public AdaptiveHistogramEqualizationSlidingWindowProcessor(Configuration configuration, int luminanceLevels, bool clipHistogram, int clipLimit, int tiles, Image source, Rectangle sourceRectangle) : base(configuration, luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, "tiles"); Guard.MustBeLessThanOrEqualTo(tiles, 100, "tiles"); Tiles = tiles; } protected override void OnFrameApply(ImageFrame source) { MemoryAllocator memoryAllocator = base.Configuration.MemoryAllocator; ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = base.Configuration.MaxDegreeOfParallelism }; int num2; int num = (num2 = source.Width / Tiles); int pixelInTile = num * num2; int num3 = num2 / 2; int num4 = num3; SlidingWindowInfos swInfos = new SlidingWindowInfos(num, num2, num4, num3, pixelInTile); using Buffer2D buffer2D = base.Configuration.MemoryAllocator.Allocate2D(source.Width, source.Height); SlidingWindowOperation slidingWindowOperation = new SlidingWindowOperation(base.Configuration, this, source, memoryAllocator, buffer2D, swInfos, num3, source.Height - num3, useFastPath: true); Parallel.For(num4, source.Width - num4, parallelOptions, ((SlidingWindowOperation)slidingWindowOperation).Invoke); SlidingWindowOperation slidingWindowOperation2 = new SlidingWindowOperation(base.Configuration, this, source, memoryAllocator, buffer2D, swInfos, 0, source.Height, useFastPath: false); Parallel.For(0, num4, parallelOptions, ((SlidingWindowOperation)slidingWindowOperation2).Invoke); SlidingWindowOperation slidingWindowOperation3 = new SlidingWindowOperation(base.Configuration, this, source, memoryAllocator, buffer2D, swInfos, 0, source.Height, useFastPath: false); Parallel.For(source.Width - num4, source.Width, parallelOptions, ((SlidingWindowOperation)slidingWindowOperation3).Invoke); SlidingWindowOperation slidingWindowOperation4 = new SlidingWindowOperation(base.Configuration, this, source, memoryAllocator, buffer2D, swInfos, 0, num3, useFastPath: false); Parallel.For(num4, source.Width - num4, parallelOptions, ((SlidingWindowOperation)slidingWindowOperation4).Invoke); SlidingWindowOperation slidingWindowOperation5 = new SlidingWindowOperation(base.Configuration, this, source, memoryAllocator, buffer2D, swInfos, source.Height - num3, source.Height, useFastPath: false); Parallel.For(num4, source.Width - num4, parallelOptions, ((SlidingWindowOperation)slidingWindowOperation5).Invoke); Buffer2D.SwapOrCopyContent(source.PixelBuffer, buffer2D); } private void CopyPixelRow(ImageFrame source, Span rowPixels, int x, int y, int tileWidth, Configuration configuration) { if (y < 0) { y = Numerics.Abs(y); } else if (y >= source.Height) { int num = y - source.Height; y = source.Height - num - 1; } if (x < 0) { rowPixels.Clear(); int num2 = 0; for (int i = x; i < x + tileWidth; i++) { rowPixels[num2] = source[Numerics.Abs(i), y].ToVector4(); num2++; } } else if (x + tileWidth > source.Width) { rowPixels.Clear(); int num3 = 0; for (int j = x; j < x + tileWidth; j++) { if (j >= source.Width) { int num4 = j - source.Width; rowPixels[num3] = source[j - num4 - 1, y].ToVector4(); } else { rowPixels[num3] = source[j, y].ToVector4(); } num3++; } } else { CopyPixelRowFast(source.PixelBuffer, rowPixels, x, y, tileWidth, configuration); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CopyPixelRowFast(Buffer2D source, Span rowPixels, int x, int y, int tileWidth, Configuration configuration) { PixelOperations.Instance.ToVector4(configuration, source.DangerousGetRowSpan(y).Slice(x, tileWidth), rowPixels); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AddPixelsToHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) { for (nint num = 0; num < length; num++) { Unsafe.Add(ref histogramBase, ColorNumerics.GetBT709Luminance(ref Unsafe.Add(ref greyValuesBase, num), luminanceLevels))++; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void RemovePixelsFromHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) { for (int i = 0; i < length; i++) { Unsafe.Add(ref histogramBase, ColorNumerics.GetBT709Luminance(ref Unsafe.Add(ref greyValuesBase, i), luminanceLevels))--; } } } public class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor { public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, int clipLimit) : base(luminanceLevels, clipHistogram, clipLimit) { } public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new GlobalHistogramEqualizationProcessor(configuration, base.LuminanceLevels, base.ClipHistogram, base.ClipLimit, source, sourceRectangle); } } internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor where TPixel : unmanaged, IPixel { private readonly struct GrayscaleLevelsRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly IMemoryOwner histogramBuffer; private readonly Buffer2D source; private readonly int luminanceLevels; [MethodImpl(MethodImplOptions.AggressiveInlining)] public GrayscaleLevelsRowOperation(Rectangle bounds, IMemoryOwner histogramBuffer, Buffer2D source, int luminanceLevels) { this.bounds = bounds; this.histogramBuffer = histogramBuffer; this.source = source; this.luminanceLevels = luminanceLevels; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { ref int reference = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan()); Span span = source.DangerousGetRowSpan(y); int num = luminanceLevels; for (int i = 0; i < bounds.Width; i++) { Vector4 vector = span[i].ToVector4(); int bT709Luminance = ColorNumerics.GetBT709Luminance(ref vector, num); Interlocked.Increment(ref Unsafe.Add(ref reference, bT709Luminance)); } } } private readonly struct CdfApplicationRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly IMemoryOwner cdfBuffer; private readonly Buffer2D source; private readonly int luminanceLevels; private readonly float numberOfPixelsMinusCdfMin; [MethodImpl(MethodImplOptions.AggressiveInlining)] public CdfApplicationRowOperation(Rectangle bounds, IMemoryOwner cdfBuffer, Buffer2D source, int luminanceLevels, float numberOfPixelsMinusCdfMin) { this.bounds = bounds; this.cdfBuffer = cdfBuffer; this.source = source; this.luminanceLevels = luminanceLevels; this.numberOfPixelsMinusCdfMin = numberOfPixelsMinusCdfMin; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { ref int reference = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); Span span = source.DangerousGetRowSpan(y); int num = luminanceLevels; float num2 = numberOfPixelsMinusCdfMin; for (int i = 0; i < bounds.Width; i++) { ref TPixel reference2 = ref span[i]; Vector4 vector = reference2.ToVector4(); int bT709Luminance = ColorNumerics.GetBT709Luminance(ref vector, num); float num3 = (float)Unsafe.Add(ref reference, bT709Luminance) / num2; reference2.FromVector4(new Vector4(num3, num3, num3, vector.W)); } } } public GlobalHistogramEqualizationProcessor(Configuration configuration, int luminanceLevels, bool clipHistogram, int clipLimit, Image source, Rectangle sourceRectangle) : base(configuration, luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { } protected override void OnFrameApply(ImageFrame source) { MemoryAllocator memoryAllocator = base.Configuration.MemoryAllocator; int num = source.Width * source.Height; Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); using IMemoryOwner memoryOwner = memoryAllocator.Allocate(base.LuminanceLevels, AllocationOptions.Clean); GrayscaleLevelsRowOperation operation = new GrayscaleLevelsRowOperation(rectangle, memoryOwner, source.PixelBuffer, base.LuminanceLevels); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation); Span span = memoryOwner.GetSpan(); if (base.ClipHistogramEnabled) { ClipHistogram(span, base.ClipLimit); } using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(base.LuminanceLevels, AllocationOptions.Clean); int num2 = CalculateCdf(ref MemoryMarshal.GetReference(memoryOwner2.GetSpan()), ref MemoryMarshal.GetReference(span), span.Length - 1); float numberOfPixelsMinusCdfMin = num - num2; CdfApplicationRowOperation operation2 = new CdfApplicationRowOperation(rectangle, memoryOwner2, source.PixelBuffer, base.LuminanceLevels, numberOfPixelsMinusCdfMin); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation2); } } public enum HistogramEqualizationMethod { Global, AdaptiveTileInterpolation, AdaptiveSlidingWindow } public class HistogramEqualizationOptions { public HistogramEqualizationMethod Method { get; set; } public int LuminanceLevels { get; set; } = 256; public bool ClipHistogram { get; set; } public int ClipLimit { get; set; } = 350; public int NumberOfTiles { get; set; } = 8; } public abstract class HistogramEqualizationProcessor : IImageProcessor { public int LuminanceLevels { get; } public bool ClipHistogram { get; } public int ClipLimit { get; } protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, int clipLimit) { LuminanceLevels = luminanceLevels; ClipHistogram = clipHistogram; ClipLimit = clipLimit; } public abstract IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel; public static HistogramEqualizationProcessor FromOptions(HistogramEqualizationOptions options) { return options.Method switch { HistogramEqualizationMethod.Global => new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit), HistogramEqualizationMethod.AdaptiveTileInterpolation => new AdaptiveHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit, options.NumberOfTiles), HistogramEqualizationMethod.AdaptiveSlidingWindow => new AdaptiveHistogramEqualizationSlidingWindowProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit, options.NumberOfTiles), _ => new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimit), }; } } internal abstract class HistogramEqualizationProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly float luminanceLevelsFloat; public int LuminanceLevels { get; } public bool ClipHistogramEnabled { get; } public int ClipLimit { get; } protected HistogramEqualizationProcessor(Configuration configuration, int luminanceLevels, bool clipHistogram, int clipLimit, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { Guard.MustBeGreaterThan(luminanceLevels, 0, "luminanceLevels"); Guard.MustBeGreaterThan(clipLimit, 1, "clipLimit"); LuminanceLevels = luminanceLevels; luminanceLevelsFloat = luminanceLevels; ClipHistogramEnabled = clipHistogram; ClipLimit = clipLimit; } public int CalculateCdf(ref int cdfBase, ref int histogramBase, int maxIdx) { int num = 0; int num2 = 0; bool flag = false; for (int i = 0; i <= maxIdx; i++) { num += Unsafe.Add(ref histogramBase, i); if (!flag && num != 0) { num2 = num; flag = true; } Unsafe.Add(ref cdfBase, i) = Math.Max(0, num - num2); } return num2; } public void ClipHistogram(Span histogram, int clipLimit) { int num = 0; ref int reference = ref MemoryMarshal.GetReference(histogram); for (int i = 0; i < histogram.Length; i++) { ref int reference2 = ref Unsafe.Add(ref reference, i); if (reference2 > clipLimit) { num += reference2 - clipLimit; reference2 = clipLimit; } } int num2 = ((num > 0) ? ((int)MathF.Floor((float)num / luminanceLevelsFloat)) : 0); if (num2 > 0) { for (int j = 0; j < histogram.Length; j++) { Unsafe.Add(ref reference, j) += num2; } } int num3 = num - num2 * LuminanceLevels; if (num3 != 0) { int num4 = Math.Max(LuminanceLevels / num3, 1); int num5 = 0; while (num5 < LuminanceLevels && num3 > 0) { Unsafe.Add(ref reference, num5)++; num5 += num4; num3--; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetLuminance(TPixel sourcePixel, int luminanceLevels) { Vector4 vector = sourcePixel.ToVector4(); return ColorNumerics.GetBT709Luminance(ref vector, luminanceLevels); } } } namespace SixLabors.ImageSharp.Processing.Processors.Filters { public sealed class AchromatomalyProcessor : FilterProcessor { public AchromatomalyProcessor() : base(KnownFilterMatrices.AchromatomalyFilter) { } } public sealed class AchromatopsiaProcessor : FilterProcessor { public AchromatopsiaProcessor() : base(KnownFilterMatrices.AchromatopsiaFilter) { } } public sealed class BlackWhiteProcessor : FilterProcessor { public BlackWhiteProcessor() : base(KnownFilterMatrices.BlackWhiteFilter) { } } public sealed class BrightnessProcessor : FilterProcessor { public float Amount { get; } public BrightnessProcessor(float amount) : base(KnownFilterMatrices.CreateBrightnessFilter(amount)) { Amount = amount; } } public sealed class ContrastProcessor : FilterProcessor { public float Amount { get; } public ContrastProcessor(float amount) : base(KnownFilterMatrices.CreateContrastFilter(amount)) { Amount = amount; } } public sealed class DeuteranomalyProcessor : FilterProcessor { public DeuteranomalyProcessor() : base(KnownFilterMatrices.DeuteranomalyFilter) { } } public sealed class DeuteranopiaProcessor : FilterProcessor { public DeuteranopiaProcessor() : base(KnownFilterMatrices.DeuteranopiaFilter) { } } public class FilterProcessor : IImageProcessor { public ColorMatrix Matrix { get; } public FilterProcessor(ColorMatrix matrix) { Matrix = matrix; } public virtual IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new FilterProcessor(configuration, this, source, sourceRectangle); } } internal class FilterProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly int startX; private readonly Buffer2D source; private readonly ColorMatrix matrix; private readonly Configuration configuration; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(int startX, Buffer2D source, ColorMatrix matrix, Configuration configuration) { this.startX = startX; this.source = source; this.matrix = matrix; this.configuration = configuration; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { Span span2 = source.DangerousGetRowSpan(y).Slice(startX, span.Length); PixelOperations.Instance.ToVector4(configuration, span2, span, PixelConversionModifiers.Scale); ColorNumerics.Transform(span, ref Unsafe.AsRef(in matrix)); PixelOperations.Instance.FromVector4Destructive(configuration, span, span2, PixelConversionModifiers.Scale); } } private readonly FilterProcessor definition; public FilterProcessor(Configuration configuration, FilterProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.definition = definition; } protected override void OnFrameApply(ImageFrame source) { Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); RowOperation operation = new RowOperation(rectangle.X, source.PixelBuffer, definition.Matrix, base.Configuration); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation); } } public sealed class GrayscaleBt601Processor : FilterProcessor { public float Amount { get; } public GrayscaleBt601Processor(float amount) : base(KnownFilterMatrices.CreateGrayscaleBt601Filter(amount)) { Amount = amount; } } public sealed class GrayscaleBt709Processor : FilterProcessor { public float Amount { get; } public GrayscaleBt709Processor(float amount) : base(KnownFilterMatrices.CreateGrayscaleBt709Filter(amount)) { Amount = amount; } } public sealed class HueProcessor : FilterProcessor { public float Degrees { get; } public HueProcessor(float degrees) : base(KnownFilterMatrices.CreateHueFilter(degrees)) { Degrees = degrees; } } public sealed class InvertProcessor : FilterProcessor { public float Amount { get; } public InvertProcessor(float amount) : base(KnownFilterMatrices.CreateInvertFilter(amount)) { Amount = amount; } } public sealed class KodachromeProcessor : FilterProcessor { public KodachromeProcessor() : base(KnownFilterMatrices.KodachromeFilter) { } } public sealed class LightnessProcessor : FilterProcessor { public float Amount { get; } public LightnessProcessor(float amount) : base(KnownFilterMatrices.CreateLightnessFilter(amount)) { Amount = amount; } } public sealed class LomographProcessor : FilterProcessor { public GraphicsOptions GraphicsOptions { get; } public LomographProcessor(GraphicsOptions graphicsOptions) : base(KnownFilterMatrices.LomographFilter) { GraphicsOptions = graphicsOptions; } public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new LomographProcessor(configuration, this, source, sourceRectangle); } } internal class LomographProcessor : FilterProcessor where TPixel : unmanaged, IPixel { private static readonly Color VeryDarkGreen = Color.FromRgba(0, 10, 0, byte.MaxValue); private readonly LomographProcessor definition; public LomographProcessor(Configuration configuration, LomographProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, (FilterProcessor)definition, source, sourceRectangle) { this.definition = definition; } protected override void AfterImageApply() { new VignetteProcessor(definition.GraphicsOptions, VeryDarkGreen).Execute(base.Configuration, base.Source, base.SourceRectangle); base.AfterImageApply(); } } public sealed class OpacityProcessor : FilterProcessor { public float Amount { get; } public OpacityProcessor(float amount) : base(KnownFilterMatrices.CreateOpacityFilter(amount)) { Amount = amount; } } internal sealed class OpaqueProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct OpaqueRowOperation : IRowOperation { private readonly Configuration configuration; private readonly Buffer2D target; private readonly Rectangle bounds; [MethodImpl(MethodImplOptions.AggressiveInlining)] public OpaqueRowOperation(Configuration configuration, Buffer2D target, Rectangle bounds) { this.configuration = configuration; this.target = target; this.bounds = bounds; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { Span destinationPixels = target.DangerousGetRowSpan(y).Slice(bounds.X); PixelOperations.Instance.ToVector4(configuration, destinationPixels.Slice(0, span.Length), span, PixelConversionModifiers.Scale); ref Vector4 reference = ref MemoryMarshal.GetReference(span); for (int i = 0; i < bounds.Width; i++) { Unsafe.Add(ref reference, i).W = 1f; } PixelOperations.Instance.FromVector4Destructive(configuration, span, destinationPixels, PixelConversionModifiers.Scale); } } public OpaqueProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { } protected override void OnFrameApply(ImageFrame source) { Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); OpaqueRowOperation operation = new OpaqueRowOperation(base.Configuration, source.PixelBuffer, rectangle); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation); } } public sealed class PolaroidProcessor : FilterProcessor { public GraphicsOptions GraphicsOptions { get; } public PolaroidProcessor(GraphicsOptions graphicsOptions) : base(KnownFilterMatrices.PolaroidFilter) { GraphicsOptions = graphicsOptions; } public override IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) { return new PolaroidProcessor(configuration, this, source, sourceRectangle); } } internal class PolaroidProcessor : FilterProcessor where TPixel : unmanaged, IPixel { private static readonly Color LightOrange = Color.FromRgba(byte.MaxValue, 153, 102, 128); private static readonly Color VeryDarkOrange = Color.FromRgb(102, 34, 0); private readonly PolaroidProcessor definition; public PolaroidProcessor(Configuration configuration, PolaroidProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, (FilterProcessor)definition, source, sourceRectangle) { this.definition = definition; } protected override void AfterImageApply() { new VignetteProcessor(definition.GraphicsOptions, VeryDarkOrange).Execute(base.Configuration, base.Source, base.SourceRectangle); new GlowProcessor(definition.GraphicsOptions, LightOrange, (float)base.Source.Width / 4f).Execute(base.Configuration, base.Source, base.SourceRectangle); base.AfterImageApply(); } } public sealed class ProtanomalyProcessor : FilterProcessor { public ProtanomalyProcessor() : base(KnownFilterMatrices.ProtanomalyFilter) { } } public sealed class ProtanopiaProcessor : FilterProcessor { public ProtanopiaProcessor() : base(KnownFilterMatrices.ProtanopiaFilter) { } } public sealed class SaturateProcessor : FilterProcessor { public float Amount { get; } public SaturateProcessor(float amount) : base(KnownFilterMatrices.CreateSaturateFilter(amount)) { Amount = amount; } } public sealed class SepiaProcessor : FilterProcessor { public float Amount { get; } public SepiaProcessor(float amount) : base(KnownFilterMatrices.CreateSepiaFilter(amount)) { Amount = amount; } } public sealed class TritanomalyProcessor : FilterProcessor { public TritanomalyProcessor() : base(KnownFilterMatrices.TritanomalyFilter) { } } public sealed class TritanopiaProcessor : FilterProcessor { public TritanopiaProcessor() : base(KnownFilterMatrices.TritanopiaFilter) { } } } namespace SixLabors.ImageSharp.Processing.Processors.Effects { public interface IPixelRowDelegate { void Invoke(Span span, Point offset); } public sealed class OilPaintingProcessor : IImageProcessor { public int Levels { get; } public int BrushSize { get; } public OilPaintingProcessor(int levels, int brushSize) { Guard.MustBeGreaterThan(levels, 0, "levels"); Guard.MustBeGreaterThan(brushSize, 0, "brushSize"); Levels = levels; BrushSize = brushSize; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new OilPaintingProcessor(configuration, this, source, sourceRectangle); } } internal class OilPaintingProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowIntervalOperation : IRowIntervalOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; private readonly Buffer2D source; private readonly Configuration configuration; private readonly int radius; private readonly int levels; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowIntervalOperation(Rectangle bounds, Buffer2D targetPixels, Buffer2D source, Configuration configuration, int radius, int levels) { this.bounds = bounds; this.targetPixels = targetPixels; this.source = source; this.configuration = configuration; this.radius = radius; this.levels = levels; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(in RowInterval rows) { int max = bounds.Bottom - 1; int max2 = bounds.Right - 1; using IMemoryOwner memoryOwner = configuration.MemoryAllocator.Allocate(source.Width); using IMemoryOwner memoryOwner2 = configuration.MemoryAllocator.Allocate(source.Width); using IMemoryOwner memoryOwner3 = configuration.MemoryAllocator.Allocate(levels * 4); Memory memory = memoryOwner.Memory; Span span = memory.Span; Span destinationVectors = span.Slice(bounds.X, bounds.Width); memory = memoryOwner2.Memory; Span span2 = memory.Span; Span sourceVectors = span2.Slice(bounds.X, bounds.Width); ref float reference = ref memoryOwner3.GetReference(); ref int reference2 = ref Unsafe.As(ref reference); ref float reference3 = ref Unsafe.Add(ref reference, levels); ref float reference4 = ref Unsafe.Add(ref reference3, levels); ref float reference5 = ref Unsafe.Add(ref reference4, levels); for (int i = rows.Min; i < rows.Max; i++) { Span span3 = source.DangerousGetRowSpan(i).Slice(bounds.X, bounds.Width); PixelOperations.Instance.ToVector4(configuration, span3, destinationVectors); for (int j = bounds.X; j < bounds.Right; j++) { int num = 0; int elementOffset = 0; memoryOwner3.Memory.Span.Clear(); for (int k = 0; k <= radius; k++) { int num2 = k - radius; int value = i + num2; value = Numerics.Clamp(value, 0, max); Span span4 = source.DangerousGetRowSpan(value); for (int l = 0; l <= radius; l++) { int num3 = l - radius; int value2 = j + num3; value2 = Numerics.Clamp(value2, 0, max2); Vector4 vector = span4[value2].ToVector4(); float x = vector.X; float z = vector.Z; float y = vector.Y; int num4 = (int)MathF.Round((z + y + x) / 3f * (float)(levels - 1)); Unsafe.Add(ref reference2, num4)++; Unsafe.Add(ref reference3, num4) += x; Unsafe.Add(ref reference4, num4) += z; Unsafe.Add(ref reference5, num4) += y; if (Unsafe.Add(ref reference2, num4) > num) { num = Unsafe.Add(ref reference2, num4); elementOffset = num4; } } float x2 = MathF.Abs(Unsafe.Add(ref reference3, elementOffset) / (float)num); float z2 = MathF.Abs(Unsafe.Add(ref reference4, elementOffset) / (float)num); float y2 = MathF.Abs(Unsafe.Add(ref reference5, elementOffset) / (float)num); float w = span[j].W; span2[j] = new Vector4(x2, y2, z2, w); } } Span destinationPixels = targetPixels.DangerousGetRowSpan(i).Slice(bounds.X, bounds.Width); PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destinationPixels); } } void IRowIntervalOperation.Invoke(in RowInterval rows) { Invoke(in rows); } } private readonly OilPaintingProcessor definition; public OilPaintingProcessor(Configuration configuration, OilPaintingProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.definition = definition; } protected override void OnFrameApply(ImageFrame source) { int brushSize = definition.BrushSize; if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width) { throw new ArgumentOutOfRangeException("brushSize"); } using Buffer2D buffer2D = base.Configuration.MemoryAllocator.Allocate2D(source.Size()); source.CopyTo(buffer2D); RowIntervalOperation operation = new RowIntervalOperation(base.SourceRectangle, buffer2D, source.PixelBuffer, base.Configuration, brushSize >> 1, definition.Levels); ParallelRowIterator.IterateRowIntervals(base.Configuration, base.SourceRectangle, in operation); Buffer2D.SwapOrCopyContent(source.PixelBuffer, buffer2D); } } public sealed class PixelateProcessor : IImageProcessor { public int Size { get; } public PixelateProcessor(int size) { Guard.MustBeGreaterThan(size, 0, "size"); Size = size; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new PixelateProcessor(configuration, this, source, sourceRectangle); } } internal class PixelateProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation { private readonly int minX; private readonly int maxX; private readonly int maxXIndex; private readonly int maxY; private readonly int maxYIndex; private readonly int size; private readonly int radius; private readonly Buffer2D source; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(Rectangle bounds, int size, Buffer2D source) { minX = bounds.X; maxX = bounds.Right; maxXIndex = bounds.Right - 1; maxY = bounds.Bottom; maxYIndex = bounds.Bottom - 1; this.size = size; radius = size >> 1; this.source = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Span span = source.DangerousGetRowSpan(Math.Min(y + radius, maxYIndex)); for (int i = minX; i < maxX; i += size) { TPixel val = span[Math.Min(i + radius, maxXIndex)]; for (int j = y; j < y + size && j < maxY; j++) { for (int k = i; k < i + size && k < maxX; k++) { source[k, j] = val; } } } } } private readonly PixelateProcessor definition; private int Size => definition.Size; public PixelateProcessor(Configuration configuration, PixelateProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.definition = definition; } protected override void OnFrameApply(ImageFrame source) { Rectangle interest = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); int size = Size; Guard.MustBeBetweenOrEqualTo(size, 0, interest.Width, "size"); Guard.MustBeBetweenOrEqualTo(size, 0, interest.Height, "size"); Parallel.ForEach(EnumerableExtensions.SteppedRange(interest.Y, (int i) => i < interest.Bottom, size), base.Configuration.GetParallelOptions(), new RowOperation(interest, size, source.PixelBuffer).Invoke); } } internal sealed class PixelRowDelegateProcessor : IImageProcessor { public readonly struct PixelRowDelegate : IPixelRowDelegate { private readonly PixelRowOperation pixelRowOperation; [MethodImpl(MethodImplOptions.AggressiveInlining)] public PixelRowDelegate(PixelRowOperation pixelRowOperation) { this.pixelRowOperation = pixelRowOperation; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(Span span, Point offset) { pixelRowOperation(span); } } public PixelRowOperation PixelRowOperation { get; } public PixelConversionModifiers Modifiers { get; } public PixelRowDelegateProcessor(PixelRowOperation pixelRowOperation, PixelConversionModifiers modifiers) { PixelRowOperation = pixelRowOperation; Modifiers = modifiers; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new PixelRowDelegateProcessor(new PixelRowDelegate(PixelRowOperation), configuration, Modifiers, source, sourceRectangle); } } internal sealed class PixelRowDelegateProcessor : ImageProcessor where TPixel : unmanaged, IPixel where TDelegate : struct, IPixelRowDelegate { private readonly struct RowOperation : IRowOperation { private readonly int startX; private readonly Buffer2D source; private readonly Configuration configuration; private readonly PixelConversionModifiers modifiers; private readonly TDelegate rowProcessor; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(int startX, Buffer2D source, Configuration configuration, PixelConversionModifiers modifiers, in TDelegate rowProcessor) { this.startX = startX; this.source = source; this.configuration = configuration; this.modifiers = modifiers; this.rowProcessor = rowProcessor; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { Span span2 = source.DangerousGetRowSpan(y).Slice(startX, span.Length); PixelOperations.Instance.ToVector4(configuration, span2, span, modifiers); Unsafe.AsRef(in rowProcessor).Invoke(span, new Point(startX, y)); PixelOperations.Instance.FromVector4Destructive(configuration, span, span2, modifiers); } } private readonly TDelegate rowDelegate; private readonly PixelConversionModifiers modifiers; public PixelRowDelegateProcessor(in TDelegate rowDelegate, Configuration configuration, PixelConversionModifiers modifiers, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.rowDelegate = rowDelegate; this.modifiers = modifiers; } protected override void OnFrameApply(ImageFrame source) { Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); RowOperation operation = new RowOperation(rectangle.X, source.PixelBuffer, base.Configuration, modifiers, in rowDelegate); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation); } } internal sealed class PositionAwarePixelRowDelegateProcessor : IImageProcessor { public readonly struct PixelRowDelegate : IPixelRowDelegate { private readonly PixelRowOperation pixelRowOperation; [MethodImpl(MethodImplOptions.AggressiveInlining)] public PixelRowDelegate(PixelRowOperation pixelRowOperation) { this.pixelRowOperation = pixelRowOperation; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(Span span, Point offset) { pixelRowOperation(span, offset); } } public PixelRowOperation PixelRowOperation { get; } public PixelConversionModifiers Modifiers { get; } public PositionAwarePixelRowDelegateProcessor(PixelRowOperation pixelRowOperation, PixelConversionModifiers modifiers) { PixelRowOperation = pixelRowOperation; Modifiers = modifiers; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new PixelRowDelegateProcessor(new PixelRowDelegate(PixelRowOperation), configuration, Modifiers, source, sourceRectangle); } } } namespace SixLabors.ImageSharp.Processing.Processors.Drawing { public class DrawImageProcessor : IImageProcessor { private class ProcessorFactoryVisitor : IImageVisitor where TPixelBg : unmanaged, IPixel { private readonly Configuration configuration; private readonly DrawImageProcessor definition; private readonly Image source; private readonly Rectangle sourceRectangle; public IImageProcessor Result { get; private set; } public ProcessorFactoryVisitor(Configuration configuration, DrawImageProcessor definition, Image source, Rectangle sourceRectangle) { this.configuration = configuration; this.definition = definition; this.source = source; this.sourceRectangle = sourceRectangle; } public void Visit(Image image) where TPixelFg : unmanaged, IPixel { Result = new DrawImageProcessor(configuration, image, source, sourceRectangle, definition.Location, definition.ColorBlendingMode, definition.AlphaCompositionMode, definition.Opacity); } } public Image Image { get; } public Point Location { get; } public PixelColorBlendingMode ColorBlendingMode { get; } public PixelAlphaCompositionMode AlphaCompositionMode { get; } public float Opacity { get; } public DrawImageProcessor(Image image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity) { Image = image; Location = location; ColorBlendingMode = colorBlendingMode; AlphaCompositionMode = alphaCompositionMode; Opacity = opacity; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixelBg : unmanaged, IPixel { ProcessorFactoryVisitor processorFactoryVisitor = new ProcessorFactoryVisitor(configuration, this, source, sourceRectangle); Image.AcceptVisitor(processorFactoryVisitor); return processorFactoryVisitor.Result; } } internal class DrawImageProcessor : ImageProcessor where TPixelBg : unmanaged, IPixel where TPixelFg : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly Buffer2D source; private readonly Buffer2D target; private readonly PixelBlender blender; private readonly Configuration configuration; private readonly int minX; private readonly int width; private readonly int locationY; private readonly int targetX; private readonly float opacity; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(Buffer2D source, Buffer2D target, PixelBlender blender, Configuration configuration, int minX, int width, int locationY, int targetX, float opacity) { this.source = source; this.target = target; this.blender = blender; this.configuration = configuration; this.minX = minX; this.width = width; this.locationY = locationY; this.targetX = targetX; this.opacity = opacity; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Span span = source.DangerousGetRowSpan(y).Slice(minX, width); Span span2 = target.DangerousGetRowSpan(y - locationY).Slice(targetX, width); blender.Blend(configuration, span, span, span2, opacity); } } public Image Image { get; } public float Opacity { get; } public PixelBlender Blender { get; } public Point Location { get; } public DrawImageProcessor(Configuration configuration, Image image, Image source, Rectangle sourceRectangle, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity) : base(configuration, source, sourceRectangle) { Guard.MustBeBetweenOrEqualTo(opacity, 0f, 1f, "opacity"); Image = image; Opacity = opacity; Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); Location = location; } protected override void OnFrameApply(ImageFrame source) { Rectangle sourceRectangle = base.SourceRectangle; Configuration configuration = base.Configuration; Image image = Image; PixelBlender blender = Blender; int y = Location.Y; Rectangle rectangle = image.Bounds(); int num = Math.Max(Location.X, sourceRectangle.X); int num2 = Math.Min(Location.X + rectangle.Width, sourceRectangle.Right); int targetX = num - Location.X; int top = Math.Max(Location.Y, sourceRectangle.Y); int bottom = Math.Min(Location.Y + rectangle.Height, sourceRectangle.Bottom); int width = num2 - num; Rectangle rectangle2 = Rectangle.FromLTRB(num, top, num2, bottom); if (rectangle2.Width <= 0 || rectangle2.Height <= 0) { throw new ImageProcessingException("Cannot draw image because the source image does not overlap the target image."); } RowOperation operation = new RowOperation(source.PixelBuffer, image.Frames.RootFrame.PixelBuffer, blender, configuration, num, width, y, targetX, Opacity); ParallelRowIterator.IterateRows(configuration, rectangle2, in operation); } } } namespace SixLabors.ImageSharp.Processing.Processors.Dithering { public readonly struct ErrorDither : IDither, IEquatable, IEquatable { public static ErrorDither Atkinson = CreateAtkinson(); public static ErrorDither Burkes = CreateBurks(); public static ErrorDither FloydSteinberg = CreateFloydSteinberg(); public static ErrorDither JarvisJudiceNinke = CreateJarvisJudiceNinke(); public static ErrorDither Sierra2 = CreateSierra2(); public static ErrorDither Sierra3 = CreateSierra3(); public static ErrorDither SierraLite = CreateSierraLite(); public static ErrorDither StevensonArce = CreateStevensonArce(); public static ErrorDither Stucki = CreateStucki(); private readonly int offset; private readonly DenseMatrix matrix; private static ErrorDither CreateAtkinson() { return new ErrorDither((DenseMatrix)new float[3, 4] { { 0f, 0f, 0.125f, 0.125f }, { 0.125f, 0.125f, 0.125f, 0f }, { 0f, 0.125f, 0f, 0f } }, 1); } private static ErrorDither CreateBurks() { return new ErrorDither((DenseMatrix)new float[2, 5] { { 0f, 0f, 0f, 0.25f, 0.125f }, { 0.0625f, 0.125f, 0.25f, 0.125f, 0.0625f } }, 2); } private static ErrorDither CreateFloydSteinberg() { return new ErrorDither((DenseMatrix)new float[2, 3] { { 0f, 0f, 0.4375f }, { 0.1875f, 0.3125f, 0.0625f } }, 1); } private static ErrorDither CreateJarvisJudiceNinke() { return new ErrorDither((DenseMatrix)new float[3, 5] { { 0f, 0f, 0f, 7f / 48f, 5f / 48f }, { 0.0625f, 5f / 48f, 7f / 48f, 5f / 48f, 0.0625f }, { 1f / 48f, 0.0625f, 5f / 48f, 0.0625f, 1f / 48f } }, 2); } private static ErrorDither CreateSierra2() { return new ErrorDither((DenseMatrix)new float[2, 5] { { 0f, 0f, 0f, 0.25f, 0.1875f }, { 0.0625f, 0.125f, 0.1875f, 0.125f, 0.0625f } }, 2); } private static ErrorDither CreateSierra3() { return new ErrorDither((DenseMatrix)new float[3, 5] { { 0f, 0f, 0f, 5f / 32f, 3f / 32f }, { 0.0625f, 0.125f, 5f / 32f, 0.125f, 0.0625f }, { 0f, 0.0625f, 3f / 32f, 0.0625f, 0f } }, 2); } private static ErrorDither CreateSierraLite() { return new ErrorDither((DenseMatrix)new float[2, 3] { { 0f, 0f, 0.5f }, { 0.25f, 0.25f, 0f } }, 1); } private static ErrorDither CreateStevensonArce() { return new ErrorDither((DenseMatrix)new float[4, 7] { { 0f, 0f, 0f, 0f, 0f, 0.16f, 0f }, { 0.06f, 0f, 0.13f, 0f, 0.15f, 0f, 0.08f }, { 0f, 0.06f, 0f, 0.13f, 0f, 0.06f, 0f }, { 0.025f, 0f, 0.06f, 0f, 0.06f, 0f, 0.025f } }, 3); } private static ErrorDither CreateStucki() { return new ErrorDither((DenseMatrix)new float[3, 5] { { 0f, 0f, 0f, 4f / 21f, 2f / 21f }, { 1f / 21f, 2f / 21f, 4f / 21f, 2f / 21f, 1f / 21f }, { 1f / 42f, 1f / 21f, 2f / 21f, 1f / 21f, 1f / 42f } }, 2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ErrorDither(in DenseMatrix matrix, int offset) { Guard.MustBeGreaterThan(offset, 0, "offset"); this.matrix = matrix; this.offset = offset; } public static bool operator ==(IDither left, ErrorDither right) { return right == left; } public static bool operator !=(IDither left, ErrorDither right) { return !(right == left); } public static bool operator ==(ErrorDither left, IDither right) { return left.Equals(right); } public static bool operator !=(ErrorDither left, IDither right) { return !(left == right); } public static bool operator ==(ErrorDither left, ErrorDither right) { return left.Equals(right); } public static bool operator !=(ErrorDither left, ErrorDither right) { return !(left == right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyQuantizationDither(ref TFrameQuantizer quantizer, ImageFrame source, IndexedImageFrame destination, Rectangle bounds) where TFrameQuantizer : struct, IQuantizer where TPixel : unmanaged, IPixel { if (this == default(ErrorDither)) { ThrowDefaultInstance(); } int top = bounds.Top; int left = bounds.Left; float ditherScale = quantizer.Options.DitherScale; Buffer2D pixelBuffer = source.PixelBuffer; for (int i = bounds.Top; i < bounds.Bottom; i++) { ref TPixel reference = ref MemoryMarshal.GetReference(pixelBuffer.DangerousGetRowSpan(i)); ref byte reference2 = ref MemoryMarshal.GetReference(destination.GetWritablePixelRowSpanUnsafe(i - top)); for (int j = bounds.Left; j < bounds.Right; j++) { TPixel val = Unsafe.Add(ref reference, j); Unsafe.Add(ref reference2, j - left) = quantizer.GetQuantizedColor(val, out var match); Dither(source, bounds, val, match, j, i, ditherScale); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyPaletteDither(in TPaletteDitherImageProcessor processor, ImageFrame source, Rectangle bounds) where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor where TPixel : unmanaged, IPixel { if (this == default(ErrorDither)) { ThrowDefaultInstance(); } Buffer2D pixelBuffer = source.PixelBuffer; float ditherScale = processor.DitherScale; for (int i = bounds.Top; i < bounds.Bottom; i++) { ref TPixel reference = ref MemoryMarshal.GetReference(pixelBuffer.DangerousGetRowSpan(i)); for (int j = bounds.Left; j < bounds.Right; j++) { ref TPixel reference2 = ref Unsafe.Add(ref reference, j); TPixel paletteColor = Unsafe.AsRef(in processor).GetPaletteColor(reference2); Dither(source, bounds, reference2, paletteColor, j, i, ditherScale); reference2 = paletteColor; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal TPixel Dither(ImageFrame image, Rectangle bounds, TPixel source, TPixel transformed, int x, int y, float scale) where TPixel : unmanaged, IPixel { if (source.Equals(transformed)) { return transformed; } Vector4 vector = (source.ToVector4() - transformed.ToVector4()) * scale; int num = offset; DenseMatrix denseMatrix = matrix; Buffer2D pixelBuffer = image.PixelBuffer; int num2 = 0; int num3 = y; while (num2 < denseMatrix.Rows) { if (num3 < bounds.Bottom) { Span span = pixelBuffer.DangerousGetRowSpan(num3); for (int i = 0; i < denseMatrix.Columns; i++) { int num4 = x + (i - num); if (num4 >= bounds.Left && num4 < bounds.Right) { float num5 = denseMatrix[num2, i]; if (num5 != 0f) { ref TPixel reference = ref span[num4]; Vector4 vector2 = reference.ToVector4(); vector2 += vector * num5; reference.FromVector4(vector2); } } } } num2++; num3++; } return transformed; } public override bool Equals(object obj) { if (obj is ErrorDither other) { return Equals(other); } return false; } public bool Equals(ErrorDither other) { if (offset == other.offset) { return matrix.Equals(other.matrix); } return false; } public bool Equals(IDither other) { return Equals((object?)other); } public override int GetHashCode() { return HashCode.Combine(offset, matrix); } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowDefaultInstance() { throw new ImageProcessingException("Cannot use the default value type instance to dither."); } void IDither.ApplyPaletteDither(in TPaletteDitherImageProcessor processor, ImageFrame source, Rectangle bounds) { ApplyPaletteDither(in processor, source, bounds); } } public interface IDither { void ApplyQuantizationDither(ref TFrameQuantizer quantizer, ImageFrame source, IndexedImageFrame destination, Rectangle bounds) where TFrameQuantizer : struct, IQuantizer where TPixel : unmanaged, IPixel; void ApplyPaletteDither(in TPaletteDitherImageProcessor processor, ImageFrame source, Rectangle bounds) where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor where TPixel : unmanaged, IPixel; } public interface IPaletteDitherImageProcessor where TPixel : unmanaged, IPixel { Configuration Configuration { get; } ReadOnlyMemory Palette { get; } float DitherScale { get; } TPixel GetPaletteColor(TPixel color); } public readonly struct OrderedDither : IDither, IEquatable, IEquatable { private readonly DenseMatrix thresholdMatrix; private readonly int modulusX; private readonly int modulusY; public static OrderedDither Bayer2x2 = new OrderedDither(2u); public static OrderedDither Bayer4x4 = new OrderedDither(4u); public static OrderedDither Bayer8x8 = new OrderedDither(8u); public static OrderedDither Bayer16x16 = new OrderedDither(16u); public static OrderedDither Ordered3x3 = new OrderedDither(3u); [MethodImpl(MethodImplOptions.AggressiveInlining)] public OrderedDither(uint length) { Guard.MustBeGreaterThan(length, 0u, "length"); DenseMatrix denseMatrix = OrderedDitherFactory.CreateDitherMatrix(length); DenseMatrix denseMatrix2 = new DenseMatrix((int)length); float num = length * length; for (int i = 0; i < length; i++) { for (int j = 0; j < length; j++) { denseMatrix2[i, j] = (float)(denseMatrix[i, j] + 1) / num - 0.5f; } } modulusX = denseMatrix.Columns; modulusY = denseMatrix.Rows; thresholdMatrix = denseMatrix2; } public static bool operator ==(IDither left, OrderedDither right) { return right == left; } public static bool operator !=(IDither left, OrderedDither right) { return !(right == left); } public static bool operator ==(OrderedDither left, IDither right) { return left.Equals(right); } public static bool operator !=(OrderedDither left, IDither right) { return !(left == right); } public static bool operator ==(OrderedDither left, OrderedDither right) { return left.Equals(right); } public static bool operator !=(OrderedDither left, OrderedDither right) { return !(left == right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyQuantizationDither(ref TFrameQuantizer quantizer, ImageFrame source, IndexedImageFrame destination, Rectangle bounds) where TFrameQuantizer : struct, IQuantizer where TPixel : unmanaged, IPixel { if (this == default(OrderedDither)) { ThrowDefaultInstance(); } int spread = CalculatePaletteSpread(destination.Palette.Length); float ditherScale = quantizer.Options.DitherScale; Buffer2D pixelBuffer = source.PixelBuffer; for (int i = bounds.Top; i < bounds.Bottom; i++) { ReadOnlySpan readOnlySpan = pixelBuffer.DangerousGetRowSpan(i).Slice(bounds.X, bounds.Width); Span span = destination.GetWritablePixelRowSpanUnsafe(i - bounds.Y).Slice(0, readOnlySpan.Length); for (int j = 0; j < readOnlySpan.Length; j++) { TPixel color = Dither(readOnlySpan[j], j, i, spread, ditherScale); span[j] = quantizer.GetQuantizedColor(color, out var _); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ApplyPaletteDither(in TPaletteDitherImageProcessor processor, ImageFrame source, Rectangle bounds) where TPaletteDitherImageProcessor : struct, IPaletteDitherImageProcessor where TPixel : unmanaged, IPixel { if (this == default(OrderedDither)) { ThrowDefaultInstance(); } int spread = CalculatePaletteSpread(processor.Palette.Length); float ditherScale = processor.DitherScale; Buffer2D pixelBuffer = source.PixelBuffer; for (int i = bounds.Top; i < bounds.Bottom; i++) { Span span = pixelBuffer.DangerousGetRowSpan(i).Slice(bounds.X, bounds.Width); for (int j = 0; j < span.Length; j++) { ref TPixel reference = ref span[j]; TPixel color = Dither(reference, j, i, spread, ditherScale); reference = processor.GetPaletteColor(color); } } } internal static int CalculatePaletteSpread(int colors) { return (int)(255.0 / Math.Max(1.0, Math.Pow(colors, 1.0 / 3.0) - 1.0)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal TPixel Dither(TPixel source, int x, int y, int spread, float scale) where TPixel : unmanaged, IPixel { Unsafe.SkipInit(out var value); source.ToRgba32(ref value); Unsafe.SkipInit(out var value2); float num = (float)spread * thresholdMatrix[y % modulusY, x % modulusX] * scale; value2.R = (byte)Numerics.Clamp((float)(int)value.R + num, 0f, 255f); value2.G = (byte)Numerics.Clamp((float)(int)value.G + num, 0f, 255f); value2.B = (byte)Numerics.Clamp((float)(int)value.B + num, 0f, 255f); value2.A = (byte)Numerics.Clamp((float)(int)value.A + num, 0f, 255f); TPixel result = default(TPixel); result.FromRgba32(value2); return result; } public override bool Equals(object obj) { if (obj is OrderedDither other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(OrderedDither other) { if (thresholdMatrix.Equals(other.thresholdMatrix) && modulusX == other.modulusX) { return modulusY == other.modulusY; } return false; } public bool Equals(IDither other) { return Equals((object?)other); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(thresholdMatrix, modulusX, modulusY); } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowDefaultInstance() { throw new ImageProcessingException("Cannot use the default value type instance to dither."); } void IDither.ApplyPaletteDither(in TPaletteDitherImageProcessor processor, ImageFrame source, Rectangle bounds) { ApplyPaletteDither(in processor, source, bounds); } } internal static class OrderedDitherFactory { public static DenseMatrix CreateDitherMatrix(uint length) { uint num = 0u; uint num2; do { num++; num2 = (uint)(1 << (int)num); } while (length > num2); DenseMatrix result = new DenseMatrix((int)length); uint num3 = 0u; for (int i = 0; i < length; i++) { for (int j = 0; j < length; j++) { result[i, j] = Bayer(num3 / length, num3 % length, num); num3++; } } uint num4 = num2 * num2; uint num5 = 0u; for (uint num6 = 0u; num6 < num4; num6++) { bool flag = false; for (int k = 0; k < length; k++) { for (int l = 0; l < length; l++) { if (result[k, l] == num6) { result[k, l] -= num5; flag = true; break; } } } if (!flag) { num5++; } } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Bayer(uint x, uint y, uint order) { uint num = 0u; for (uint num2 = 0u; num2 < order; num2++) { uint num3 = (x & 1) ^ (y & 1); uint num4 = x & 1; num = (((num << 1) | num3) << 1) | num4; x >>= 1; y >>= 1; } return num; } } public sealed class PaletteDitherProcessor : IImageProcessor { public IDither Dither { get; } public float DitherScale { get; } public ReadOnlyMemory Palette { get; } public PaletteDitherProcessor(IDither dither) : this(dither, 1f) { } public PaletteDitherProcessor(IDither dither, float ditherScale) : this(dither, ditherScale, Color.WebSafePalette) { } public PaletteDitherProcessor(IDither dither, ReadOnlyMemory palette) : this(dither, 1f, palette) { } public PaletteDitherProcessor(IDither dither, float ditherScale, ReadOnlyMemory palette) { Guard.MustBeGreaterThan(palette.Length, 0, "palette"); Guard.NotNull(dither, "dither"); Dither = dither; DitherScale = Numerics.Clamp(ditherScale, 0f, 1f); Palette = palette; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new PaletteDitherProcessor(configuration, this, source, sourceRectangle); } } internal sealed class PaletteDitherProcessor : ImageProcessor where TPixel : unmanaged, IPixel { internal readonly struct DitherProcessor : IPaletteDitherImageProcessor, IDisposable { private readonly EuclideanPixelMap pixelMap; public Configuration Configuration { get; } public ReadOnlyMemory Palette { get; } public float DitherScale { get; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public DitherProcessor(Configuration configuration, ReadOnlyMemory palette, float ditherScale) { Configuration = configuration; pixelMap = new EuclideanPixelMap(configuration, palette); Palette = palette; DitherScale = ditherScale; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TPixel GetPaletteColor(TPixel color) { pixelMap.GetClosestColor(color, out var match); return match; } public void Dispose() { pixelMap.Dispose(); } } private readonly DitherProcessor ditherProcessor; private readonly IDither dither; private IMemoryOwner paletteOwner; private bool isDisposed; public PaletteDitherProcessor(Configuration configuration, PaletteDitherProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { dither = definition.Dither; ReadOnlySpan span = definition.Palette.Span; paletteOwner = base.Configuration.MemoryAllocator.Allocate(span.Length); Color.ToPixel(base.Configuration, span, paletteOwner.Memory.Span); ditherProcessor = new DitherProcessor(base.Configuration, paletteOwner.Memory, definition.DitherScale); } protected override void OnFrameApply(ImageFrame source) { Rectangle bounds = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); dither.ApplyPaletteDither(in ditherProcessor, source, bounds); } protected override void Dispose(bool disposing) { if (!isDisposed) { isDisposed = true; if (disposing) { paletteOwner.Dispose(); ditherProcessor.Dispose(); } paletteOwner = null; base.Dispose(disposing); } } } } namespace SixLabors.ImageSharp.Processing.Processors.Convolution { public sealed class BokehBlurProcessor : IImageProcessor { internal readonly struct SecondPassConvolutionRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetValues; private readonly Buffer2D sourceValues; private readonly KernelSamplingMap map; private readonly Complex64[] kernel; private readonly float z; private readonly float w; [MethodImpl(MethodImplOptions.AggressiveInlining)] public SecondPassConvolutionRowOperation(Rectangle bounds, Buffer2D targetValues, Buffer2D sourceValues, KernelSamplingMap map, Complex64[] kernel, float z, float w) { this.bounds = bounds; this.targetValues = targetValues; this.sourceValues = sourceValues; this.map = map; this.kernel = kernel; this.z = z; this.w = w; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { int x = bounds.X; int width = bounds.Width; int num = kernel.Length; ref int reference = ref Unsafe.Add(ref MemoryMarshal.GetReference(map.GetRowOffsetSpan()), (y - bounds.Y) * num); ref Vector4 elementUnsafe = ref targetValues.GetElementUnsafe(x, y); ref Complex64 reference2 = ref kernel[0]; ref Complex64 right = ref Unsafe.Add(ref reference2, num); while (Unsafe.IsAddressLessThan(in reference2, in right)) { ref ComplexVector4 reference3 = ref sourceValues.GetElementUnsafe(0, reference); ref ComplexVector4 right2 = ref Unsafe.Add(ref reference3, width); ref Vector4 reference4 = ref elementUnsafe; Complex64 complex = reference2; while (Unsafe.IsAddressLessThan(in reference3, in right2)) { reference4 += (complex * reference3).WeightedSum(z, w); reference3 = ref Unsafe.Add(ref reference3, 1); reference4 = ref Unsafe.Add(ref reference4, 1); } reference2 = ref Unsafe.Add(ref reference2, 1); reference = ref Unsafe.Add(ref reference, 1); } } } public const int DefaultRadius = 32; public const int DefaultComponents = 2; public const float DefaultGamma = 3f; public int Radius { get; } public int Components { get; } public float Gamma { get; } public BokehBlurProcessor() { Radius = 32; Components = 2; Gamma = 3f; } public BokehBlurProcessor(int radius, int components, float gamma) { Guard.MustBeGreaterThan(radius, 0, "radius"); Guard.MustBeBetweenOrEqualTo(components, 1, 6, "components"); Guard.MustBeGreaterThanOrEqualTo(gamma, 1f, "gamma"); Radius = radius; Components = components; Gamma = gamma; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new BokehBlurProcessor(configuration, this, source, sourceRectangle); } } internal class BokehBlurProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct FirstPassConvolutionRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetValues; private readonly Buffer2D sourcePixels; private readonly KernelSamplingMap map; private readonly Complex64[] kernel; private readonly Configuration configuration; [MethodImpl(MethodImplOptions.AggressiveInlining)] public FirstPassConvolutionRowOperation(Rectangle bounds, Buffer2D targetValues, Buffer2D sourcePixels, KernelSamplingMap map, Complex64[] kernel, Configuration configuration) { this.bounds = bounds; this.targetValues = targetValues; this.sourcePixels = sourcePixels; this.map = map; this.kernel = kernel; this.configuration = configuration; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { int x = bounds.X; int width = bounds.Width; int elementOffset = kernel.Length; Span span2 = targetValues.DangerousGetRowSpan(y); span2.Clear(); Span span3 = sourcePixels.DangerousGetRowSpan(y).Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span3, span); ref Vector4 reference = ref MemoryMarshal.GetReference(span); ref ComplexVector4 reference2 = ref MemoryMarshal.GetReference(span2); ref ComplexVector4 right = ref Unsafe.Add(ref reference2, span.Length); ref Complex64 reference3 = ref kernel[0]; ref Complex64 right2 = ref Unsafe.Add(ref reference3, elementOffset); ref int reference4 = ref MemoryMarshal.GetReference(map.GetColumnOffsetSpan()); while (Unsafe.IsAddressLessThan(in reference2, in right)) { ref Complex64 reference5 = ref reference3; ref int reference6 = ref reference4; while (Unsafe.IsAddressLessThan(in reference5, in right2)) { Vector4 vector = Unsafe.Add(ref reference, reference6 - x); reference2.Sum(reference5 * vector); reference5 = ref Unsafe.Add(ref reference5, 1); reference6 = ref Unsafe.Add(ref reference6, 1); } reference4 = ref Unsafe.Add(ref reference4, elementOffset); reference2 = ref Unsafe.Add(ref reference2, 1); } } } private readonly struct ApplyGammaExposureRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; private readonly Configuration configuration; private readonly float gamma; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ApplyGammaExposureRowOperation(Rectangle bounds, Buffer2D targetPixels, Configuration configuration, float gamma) { this.bounds = bounds; this.targetPixels = targetPixels; this.configuration = configuration; this.gamma = gamma; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { Span destinationPixels = targetPixels.DangerousGetRowSpan(y).Slice(bounds.X); PixelOperations.Instance.ToVector4(configuration, destinationPixels.Slice(0, span.Length), span, PixelConversionModifiers.Premultiply); ref Vector4 reference = ref MemoryMarshal.GetReference(span); for (int i = 0; i < bounds.Width; i++) { ref Vector4 reference2 = ref Unsafe.Add(ref reference, i); reference2.X = MathF.Pow(reference2.X, gamma); reference2.Y = MathF.Pow(reference2.Y, gamma); reference2.Z = MathF.Pow(reference2.Z, gamma); } PixelOperations.Instance.FromVector4Destructive(configuration, span, destinationPixels); } } private readonly struct ApplyGamma3ExposureRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; private readonly Configuration configuration; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ApplyGamma3ExposureRowOperation(Rectangle bounds, Buffer2D targetPixels, Configuration configuration) { this.bounds = bounds; this.targetPixels = targetPixels; this.configuration = configuration; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { Span destinationPixels = targetPixels.DangerousGetRowSpan(y).Slice(bounds.X); PixelOperations.Instance.ToVector4(configuration, destinationPixels.Slice(0, span.Length), span, PixelConversionModifiers.Premultiply); Numerics.CubePowOnXYZ(span); PixelOperations.Instance.FromVector4Destructive(configuration, span, destinationPixels); } } private readonly struct ApplyInverseGammaExposureRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; private readonly Buffer2D sourceValues; private readonly Configuration configuration; private readonly float inverseGamma; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ApplyInverseGammaExposureRowOperation(Rectangle bounds, Buffer2D targetPixels, Buffer2D sourceValues, Configuration configuration, float inverseGamma) { this.bounds = bounds; this.targetPixels = targetPixels; this.sourceValues = sourceValues; this.configuration = configuration; this.inverseGamma = inverseGamma; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Vector4 zero = Vector4.Zero; Vector4 max = new Vector4(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); Span destinationPixels = targetPixels.DangerousGetRowSpan(y).Slice(bounds.X); Span span = sourceValues.DangerousGetRowSpan(y).Slice(bounds.X); ref Vector4 reference = ref MemoryMarshal.GetReference(span); for (int i = 0; i < bounds.Width; i++) { ref Vector4 reference2 = ref Unsafe.Add(ref reference, i); Vector4 vector = Numerics.Clamp(reference2, zero, max); reference2.X = MathF.Pow(vector.X, inverseGamma); reference2.Y = MathF.Pow(vector.Y, inverseGamma); reference2.Z = MathF.Pow(vector.Z, inverseGamma); } PixelOperations.Instance.FromVector4Destructive(configuration, span.Slice(0, bounds.Width), destinationPixels, PixelConversionModifiers.Premultiply); } } private readonly struct ApplyInverseGamma3ExposureRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; private readonly Buffer2D sourceValues; private readonly Configuration configuration; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ApplyInverseGamma3ExposureRowOperation(Rectangle bounds, Buffer2D targetPixels, Buffer2D sourceValues, Configuration configuration) { this.bounds = bounds; this.targetPixels = targetPixels; this.sourceValues = sourceValues; this.configuration = configuration; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Span span = sourceValues.DangerousGetRowSpan(y).Slice(bounds.X, bounds.Width); MemoryMarshal.GetReference(span); Numerics.Clamp(MemoryMarshal.Cast(span), 0f, float.PositiveInfinity); Numerics.CubeRootOnXYZ(span); Span destinationPixels = targetPixels.DangerousGetRowSpan(y).Slice(bounds.X); PixelOperations.Instance.FromVector4Destructive(configuration, span.Slice(0, bounds.Width), destinationPixels, PixelConversionModifiers.Premultiply); } } private readonly float gamma; private readonly int kernelSize; private readonly Vector4[] kernelParameters; private readonly Complex64[][] kernels; public IReadOnlyList Kernels => kernels; public IReadOnlyList KernelParameters => kernelParameters; public BokehBlurProcessor(Configuration configuration, BokehBlurProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { gamma = definition.Gamma; kernelSize = definition.Radius * 2 + 1; BokehBlurKernelData bokehBlurKernelData = BokehBlurKernelDataProvider.GetBokehBlurKernelData(definition.Radius, kernelSize, definition.Components); kernelParameters = bokehBlurKernelData.Parameters; kernels = bokehBlurKernelData.Kernels; } protected override void OnFrameApply(ImageFrame source) { Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); if (gamma == 3f) { ApplyGamma3ExposureRowOperation operation = new ApplyGamma3ExposureRowOperation(rectangle, source.PixelBuffer, base.Configuration); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation); } else { ApplyGammaExposureRowOperation operation2 = new ApplyGammaExposureRowOperation(rectangle, source.PixelBuffer, base.Configuration, gamma); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation2); } using Buffer2D buffer2D = base.Configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean); OnFrameApplyCore(source, rectangle, base.Configuration, buffer2D); if (gamma == 3f) { ApplyInverseGamma3ExposureRowOperation operation3 = new ApplyInverseGamma3ExposureRowOperation(rectangle, source.PixelBuffer, buffer2D, base.Configuration); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation3); } else { ApplyInverseGammaExposureRowOperation operation4 = new ApplyInverseGammaExposureRowOperation(rectangle, source.PixelBuffer, buffer2D, base.Configuration, 1f / gamma); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation4); } } private void OnFrameApplyCore(ImageFrame source, Rectangle sourceRectangle, Configuration configuration, Buffer2D processingBuffer) { using Buffer2D buffer2D = configuration.MemoryAllocator.Allocate2D(source.Size()); using KernelSamplingMap kernelSamplingMap = new KernelSamplingMap(configuration.MemoryAllocator); kernelSamplingMap.BuildSamplingOffsetMap(kernelSize, kernelSize, sourceRectangle); ref Complex64[] reference = ref MemoryMarshal.GetReference(kernels.AsSpan()); ref Vector4 reference2 = ref MemoryMarshal.GetReference(kernelParameters.AsSpan()); for (int i = 0; i < kernels.Length; i++) { Complex64[] kernel = Unsafe.Add(ref reference, i); Vector4 vector = Unsafe.Add(ref reference2, i); ParallelRowIterator.IterateRows(configuration, sourceRectangle, new FirstPassConvolutionRowOperation(sourceRectangle, buffer2D, source.PixelBuffer, kernelSamplingMap, kernel, configuration)); ParallelRowIterator.IterateRows(configuration, sourceRectangle, new BokehBlurProcessor.SecondPassConvolutionRowOperation(sourceRectangle, processingBuffer, buffer2D, kernelSamplingMap, kernel, vector.Z, vector.W)); } } } public sealed class BoxBlurProcessor : IImageProcessor { public const int DefaultRadius = 7; public int Radius { get; } public BoxBlurProcessor(int radius) { Radius = radius; } public BoxBlurProcessor() : this(7) { } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new BoxBlurProcessor(configuration, this, source, sourceRectangle); } } internal class BoxBlurProcessor : ImageProcessor where TPixel : unmanaged, IPixel { public float[] Kernel { get; } public BoxBlurProcessor(Configuration configuration, BoxBlurProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { int kernelSize = definition.Radius * 2 + 1; Kernel = CreateBoxKernel(kernelSize); } protected override void OnFrameApply(ImageFrame source) { using Convolution2PassProcessor convolution2PassProcessor = new Convolution2PassProcessor(base.Configuration, Kernel, preserveAlpha: false, base.Source, base.SourceRectangle); convolution2PassProcessor.Apply(source); } private static float[] CreateBoxKernel(int kernelSize) { float[] array = new float[kernelSize]; array.AsSpan().Fill(1f / (float)kernelSize); return array; } } internal class Convolution2DProcessor : ImageProcessor where TPixel : unmanaged, IPixel { public DenseMatrix KernelX { get; } public DenseMatrix KernelY { get; } public bool PreserveAlpha { get; } public Convolution2DProcessor(Configuration configuration, in DenseMatrix kernelX, in DenseMatrix kernelY, bool preserveAlpha, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), "kernelX kernelY", "Kernel sizes must be the same."); KernelX = kernelX; KernelY = kernelY; PreserveAlpha = preserveAlpha; } protected override void OnFrameApply(ImageFrame source) { MemoryAllocator memoryAllocator = base.Configuration.MemoryAllocator; using Buffer2D buffer2D = memoryAllocator.Allocate2D(source.Width, source.Height); source.CopyTo(buffer2D); Rectangle bounds = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); Rectangle rectangle = new Rectangle(bounds.X, bounds.Y, bounds.Width * 3, bounds.Height); using (KernelSamplingMap kernelSamplingMap = new KernelSamplingMap(memoryAllocator)) { kernelSamplingMap.BuildSamplingOffsetMap(KernelY, bounds); Convolution2DRowOperation operation = new Convolution2DRowOperation(bounds, buffer2D, source.PixelBuffer, kernelSamplingMap, KernelY, KernelX, base.Configuration, PreserveAlpha); ParallelRowIterator.IterateRows, Vector4>(base.Configuration, rectangle, in operation); } Buffer2D.SwapOrCopyContent(source.PixelBuffer, buffer2D); } } internal readonly struct Convolution2DRowOperation : IRowOperation where TPixel : unmanaged, IPixel { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; private readonly Buffer2D sourcePixels; private readonly KernelSamplingMap map; private readonly DenseMatrix kernelMatrixY; private readonly DenseMatrix kernelMatrixX; private readonly Configuration configuration; private readonly bool preserveAlpha; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Convolution2DRowOperation(Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, KernelSamplingMap map, DenseMatrix kernelMatrixY, DenseMatrix kernelMatrixX, Configuration configuration, bool preserveAlpha) { this.bounds = bounds; this.targetPixels = targetPixels; this.sourcePixels = sourcePixels; this.map = map; this.kernelMatrixY = kernelMatrixY; this.kernelMatrixX = kernelMatrixX; this.configuration = configuration; this.preserveAlpha = preserveAlpha; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { if (preserveAlpha) { Convolve3(y, span); } else { Convolve4(y, span); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Convolve3(int y, Span span) { int x = bounds.X; int width = bounds.Width; Span span2 = span.Slice(0, width); Span span3 = span.Slice(width, width); Span span4 = span.Slice(width * 2, width); Convolution2DState convolution2DState = new Convolution2DState(in kernelMatrixY, in kernelMatrixX, map); ref int sampleRow = ref convolution2DState.GetSampleRow(y - bounds.Y); span3.Clear(); span4.Clear(); ref Vector4 reference = ref MemoryMarshal.GetReference(span3); ref Vector4 reference2 = ref MemoryMarshal.GetReference(span4); ReadOnlyKernel kernelY = convolution2DState.KernelY; ReadOnlyKernel kernelX = convolution2DState.KernelX; Span span5; Span span6; for (int i = 0; i < kernelY.Rows; i++) { int y2 = Unsafe.Add(ref sampleRow, i); span5 = sourcePixels.DangerousGetRowSpan(y2); span6 = span5.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span6, span2); ref Vector4 reference3 = ref MemoryMarshal.GetReference(span2); for (int j = 0; j < span2.Length; j++) { ref int sampleColumn = ref convolution2DState.GetSampleColumn(j); ref Vector4 reference4 = ref Unsafe.Add(ref reference, j); ref Vector4 reference5 = ref Unsafe.Add(ref reference2, j); for (int k = 0; k < kernelY.Columns; k++) { int elementOffset = Unsafe.Add(ref sampleColumn, k) - x; Vector4 vector = Unsafe.Add(ref reference3, elementOffset); reference4 += kernelX[i, k] * vector; reference5 += kernelY[i, k] * vector; } } } span5 = sourcePixels.DangerousGetRowSpan(y); span6 = span5.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span6, span2); for (int l = 0; l < span6.Length; l++) { ref Vector4 reference6 = ref Unsafe.Add(ref reference, l); Vector4 vector2 = reference6; Vector4 vector3 = Unsafe.Add(ref reference2, l); reference6 = Vector4.SquareRoot(vector3 * vector3 + vector2 * vector2); reference6.W = Unsafe.Add(ref MemoryMarshal.GetReference(span2), l).W; } span5 = targetPixels.DangerousGetRowSpan(y); Span destinationPixels = span5.Slice(x, width); PixelOperations.Instance.FromVector4Destructive(configuration, span3, destinationPixels); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Convolve4(int y, Span span) { int x = bounds.X; int width = bounds.Width; Span span2 = span.Slice(0, width); Span span3 = span.Slice(width, width); Span span4 = span.Slice(width * 2, width); Convolution2DState convolution2DState = new Convolution2DState(in kernelMatrixY, in kernelMatrixX, map); ref int sampleRow = ref convolution2DState.GetSampleRow(y - bounds.Y); span3.Clear(); span4.Clear(); ref Vector4 reference = ref MemoryMarshal.GetReference(span3); ref Vector4 reference2 = ref MemoryMarshal.GetReference(span4); ReadOnlyKernel kernelY = convolution2DState.KernelY; ReadOnlyKernel kernelX = convolution2DState.KernelX; Span span5; for (int i = 0; i < kernelY.Rows; i++) { int y2 = Unsafe.Add(ref sampleRow, i); span5 = sourcePixels.DangerousGetRowSpan(y2); Span span6 = span5.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span6, span2); Numerics.Premultiply(span2); ref Vector4 reference3 = ref MemoryMarshal.GetReference(span2); for (int j = 0; j < span2.Length; j++) { ref int sampleColumn = ref convolution2DState.GetSampleColumn(j); ref Vector4 reference4 = ref Unsafe.Add(ref reference, j); ref Vector4 reference5 = ref Unsafe.Add(ref reference2, j); for (int k = 0; k < kernelY.Columns; k++) { int elementOffset = Unsafe.Add(ref sampleColumn, k) - x; Vector4 vector = Unsafe.Add(ref reference3, elementOffset); reference4 += kernelX[i, k] * vector; reference5 += kernelY[i, k] * vector; } } } for (int l = 0; l < span3.Length; l++) { ref Vector4 reference6 = ref Unsafe.Add(ref reference, l); Vector4 vector2 = reference6; Vector4 vector3 = Unsafe.Add(ref reference2, l); reference6 = Vector4.SquareRoot(vector3 * vector3 + vector2 * vector2); } Numerics.UnPremultiply(span3); span5 = targetPixels.DangerousGetRowSpan(y); Span destinationPixels = span5.Slice(x, width); PixelOperations.Instance.FromVector4Destructive(configuration, span3, destinationPixels); } } internal readonly ref struct Convolution2DState { private readonly Span rowOffsetMap; private readonly Span columnOffsetMap; private readonly int kernelHeight; private readonly int kernelWidth; public ReadOnlyKernel KernelY { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public ReadOnlyKernel KernelX { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public Convolution2DState(in DenseMatrix kernelY, in DenseMatrix kernelX, KernelSamplingMap map) { KernelY = new ReadOnlyKernel(kernelY); KernelX = new ReadOnlyKernel(kernelX); kernelHeight = kernelY.Rows; kernelWidth = kernelY.Columns; rowOffsetMap = map.GetRowOffsetSpan(); columnOffsetMap = map.GetColumnOffsetSpan(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref int GetSampleRow(int row) { return ref Unsafe.Add(ref MemoryMarshal.GetReference(rowOffsetMap), row * kernelHeight); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref int GetSampleColumn(int column) { return ref Unsafe.Add(ref MemoryMarshal.GetReference(columnOffsetMap), column * kernelWidth); } } internal class Convolution2PassProcessor : ImageProcessor where TPixel : unmanaged, IPixel { internal readonly struct HorizontalConvolutionRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; private readonly Buffer2D sourcePixels; private readonly KernelSamplingMap map; private readonly float[] kernel; private readonly Configuration configuration; private readonly bool preserveAlpha; [MethodImpl(MethodImplOptions.AggressiveInlining)] public HorizontalConvolutionRowOperation(Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, KernelSamplingMap map, float[] kernel, Configuration configuration, bool preserveAlpha) { this.bounds = bounds; this.targetPixels = targetPixels; this.sourcePixels = sourcePixels; this.map = map; this.kernel = kernel; this.configuration = configuration; this.preserveAlpha = preserveAlpha; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { if (preserveAlpha) { Convolve3(y, span); } else { Convolve4(y, span); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Convolve3(int y, Span span) { int x = bounds.X; int width = bounds.Width; int elementOffset = kernel.Length; Span span2 = span.Slice(0, bounds.Width); Span span3 = span.Slice(bounds.Width); span3.Clear(); Span span4 = sourcePixels.DangerousGetRowSpan(y); Span span5 = span4.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span5, span2); ref Vector4 reference = ref MemoryMarshal.GetReference(span2); ref Vector4 reference2 = ref MemoryMarshal.GetReference(span3); ref Vector4 right = ref Unsafe.Add(ref reference2, span2.Length); ref float reference3 = ref kernel[0]; ref float right2 = ref Unsafe.Add(ref reference3, elementOffset); ref int reference4 = ref MemoryMarshal.GetReference(map.GetColumnOffsetSpan()); while (Unsafe.IsAddressLessThan(in reference2, in right)) { ref float reference5 = ref reference3; ref int reference6 = ref reference4; while (Unsafe.IsAddressLessThan(in reference5, in right2)) { Vector4 vector = Unsafe.Add(ref reference, reference6 - x); reference2 += reference5 * vector; reference5 = ref Unsafe.Add(ref reference5, 1); reference6 = ref Unsafe.Add(ref reference6, 1); } reference2 = ref Unsafe.Add(ref reference2, 1); reference4 = ref Unsafe.Add(ref reference4, elementOffset); } span4 = sourcePixels.DangerousGetRowSpan(y); span5 = span4.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span5, span2); reference2 = ref MemoryMarshal.GetReference(span3); while (Unsafe.IsAddressLessThan(in reference2, in right)) { reference2.W = reference.W; reference2 = ref Unsafe.Add(ref reference2, 1); reference = ref Unsafe.Add(ref reference, 1); } span4 = targetPixels.DangerousGetRowSpan(y); Span destinationPixels = span4.Slice(x, width); PixelOperations.Instance.FromVector4Destructive(configuration, span3, destinationPixels); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Convolve4(int y, Span span) { int x = bounds.X; int width = bounds.Width; int elementOffset = kernel.Length; Span span2 = span.Slice(0, bounds.Width); Span span3 = span.Slice(bounds.Width); span3.Clear(); Span span4 = sourcePixels.DangerousGetRowSpan(y); Span span5 = span4.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span5, span2); Numerics.Premultiply(span2); ref Vector4 reference = ref MemoryMarshal.GetReference(span2); ref Vector4 reference2 = ref MemoryMarshal.GetReference(span3); ref Vector4 right = ref Unsafe.Add(ref reference2, span2.Length); ref float reference3 = ref kernel[0]; ref float right2 = ref Unsafe.Add(ref reference3, elementOffset); ref int reference4 = ref MemoryMarshal.GetReference(map.GetColumnOffsetSpan()); while (Unsafe.IsAddressLessThan(in reference2, in right)) { ref float reference5 = ref reference3; ref int reference6 = ref reference4; while (Unsafe.IsAddressLessThan(in reference5, in right2)) { Vector4 vector = Unsafe.Add(ref reference, reference6 - x); reference2 += reference5 * vector; reference5 = ref Unsafe.Add(ref reference5, 1); reference6 = ref Unsafe.Add(ref reference6, 1); } reference2 = ref Unsafe.Add(ref reference2, 1); reference4 = ref Unsafe.Add(ref reference4, elementOffset); } Numerics.UnPremultiply(span3); span4 = targetPixels.DangerousGetRowSpan(y); Span destinationPixels = span4.Slice(x, width); PixelOperations.Instance.FromVector4Destructive(configuration, span3, destinationPixels); } } internal readonly struct VerticalConvolutionRowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; private readonly Buffer2D sourcePixels; private readonly KernelSamplingMap map; private readonly float[] kernel; private readonly Configuration configuration; private readonly bool preserveAlpha; [MethodImpl(MethodImplOptions.AggressiveInlining)] public VerticalConvolutionRowOperation(Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, KernelSamplingMap map, float[] kernel, Configuration configuration, bool preserveAlpha) { this.bounds = bounds; this.targetPixels = targetPixels; this.sourcePixels = sourcePixels; this.map = map; this.kernel = kernel; this.configuration = configuration; this.preserveAlpha = preserveAlpha; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { if (preserveAlpha) { Convolve3(y, span); } else { Convolve4(y, span); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Convolve3(int y, Span span) { int x = bounds.X; int width = bounds.Width; int num = kernel.Length; Span span2 = span.Slice(0, bounds.Width); Span span3 = span.Slice(bounds.Width); ref int reference = ref Unsafe.Add(ref MemoryMarshal.GetReference(map.GetRowOffsetSpan()), (y - bounds.Y) * num); span3.Clear(); ref Vector4 reference2 = ref MemoryMarshal.GetReference(span3); ref float reference3 = ref kernel[0]; ref float right = ref Unsafe.Add(ref reference3, num); Span span4; Span span5; while (Unsafe.IsAddressLessThan(in reference3, in right)) { span4 = sourcePixels.DangerousGetRowSpan(reference); span5 = span4.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span5, span2); ref Vector4 reference4 = ref MemoryMarshal.GetReference(span2); ref Vector4 right2 = ref Unsafe.Add(ref reference4, span2.Length); ref Vector4 reference5 = ref reference2; float num2 = reference3; while (Unsafe.IsAddressLessThan(in reference4, in right2)) { reference5 += num2 * reference4; reference4 = ref Unsafe.Add(ref reference4, 1); reference5 = ref Unsafe.Add(ref reference5, 1); } reference3 = ref Unsafe.Add(ref reference3, 1); reference = ref Unsafe.Add(ref reference, 1); } span4 = sourcePixels.DangerousGetRowSpan(y); span5 = span4.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span5, span2); ref Vector4 reference6 = ref MemoryMarshal.GetReference(span2); ref Vector4 right3 = ref Unsafe.Add(ref reference6, span2.Length); while (Unsafe.IsAddressLessThan(in reference6, in right3)) { reference2.W = reference6.W; reference2 = ref Unsafe.Add(ref reference2, 1); reference6 = ref Unsafe.Add(ref reference6, 1); } span4 = targetPixels.DangerousGetRowSpan(y); Span destinationPixels = span4.Slice(x, width); PixelOperations.Instance.FromVector4Destructive(configuration, span3, destinationPixels); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Convolve4(int y, Span span) { int x = bounds.X; int width = bounds.Width; int num = kernel.Length; Span span2 = span.Slice(0, bounds.Width); Span span3 = span.Slice(bounds.Width); ref int reference = ref Unsafe.Add(ref MemoryMarshal.GetReference(map.GetRowOffsetSpan()), (y - bounds.Y) * num); span3.Clear(); ref Vector4 reference2 = ref MemoryMarshal.GetReference(span3); ref float reference3 = ref kernel[0]; ref float right = ref Unsafe.Add(ref reference3, num); Span span4; while (Unsafe.IsAddressLessThan(in reference3, in right)) { span4 = sourcePixels.DangerousGetRowSpan(reference); Span span5 = span4.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span5, span2); Numerics.Premultiply(span2); ref Vector4 reference4 = ref MemoryMarshal.GetReference(span2); ref Vector4 right2 = ref Unsafe.Add(ref reference4, span2.Length); ref Vector4 reference5 = ref reference2; float num2 = reference3; while (Unsafe.IsAddressLessThan(in reference4, in right2)) { reference5 += num2 * reference4; reference4 = ref Unsafe.Add(ref reference4, 1); reference5 = ref Unsafe.Add(ref reference5, 1); } reference3 = ref Unsafe.Add(ref reference3, 1); reference = ref Unsafe.Add(ref reference, 1); } Numerics.UnPremultiply(span3); span4 = targetPixels.DangerousGetRowSpan(y); Span destinationPixels = span4.Slice(x, width); PixelOperations.Instance.FromVector4Destructive(configuration, span3, destinationPixels); } } public float[] Kernel { get; } public bool PreserveAlpha { get; } public Convolution2PassProcessor(Configuration configuration, float[] kernel, bool preserveAlpha, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { Kernel = kernel; PreserveAlpha = preserveAlpha; } protected override void OnFrameApply(ImageFrame source) { using Buffer2D buffer2D = base.Configuration.MemoryAllocator.Allocate2D(source.Size()); Rectangle bounds = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); Rectangle rectangle = new Rectangle(bounds.X, bounds.Y, bounds.Width * 2, bounds.Height); using KernelSamplingMap kernelSamplingMap = new KernelSamplingMap(base.Configuration.MemoryAllocator); kernelSamplingMap.BuildSamplingOffsetMap(Kernel.Length, Kernel.Length, bounds); HorizontalConvolutionRowOperation operation = new HorizontalConvolutionRowOperation(bounds, buffer2D, source.PixelBuffer, kernelSamplingMap, Kernel, base.Configuration, PreserveAlpha); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation); VerticalConvolutionRowOperation operation2 = new VerticalConvolutionRowOperation(bounds, source.PixelBuffer, buffer2D, kernelSamplingMap, Kernel, base.Configuration, PreserveAlpha); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation2); } } internal static class ConvolutionProcessorHelpers { internal static int GetDefaultGaussianRadius(float sigma) { return (int)MathF.Ceiling(sigma * 3f); } internal static float[] CreateGaussianBlurKernel(int size, float weight) { float[] array = new float[size]; float num = 0f; float num2 = (float)(size - 1) / 2f; for (int i = 0; i < size; i++) { float num3 = Numerics.Gaussian((float)i - num2, weight); num += num3; array[i] = num3; } for (int j = 0; j < size; j++) { array[j] /= num; } return array; } internal static float[] CreateGaussianSharpenKernel(int size, float weight) { float[] array = new float[size]; float num = 0f; float num2 = (float)(size - 1) / 2f; for (int i = 0; i < size; i++) { float num3 = Numerics.Gaussian((float)i - num2, weight); num += num3; array[i] = num3; } int num4 = (int)num2; for (int j = 0; j < size; j++) { if (j == num4) { array[j] = 2f * num - array[j]; } else { array[j] = 0f - array[j]; } } for (int k = 0; k < size; k++) { array[k] /= num; } return array; } } internal class ConvolutionProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D targetPixels; private readonly Buffer2D sourcePixels; private readonly KernelSamplingMap map; private readonly DenseMatrix kernel; private readonly Configuration configuration; private readonly bool preserveAlpha; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(Rectangle bounds, Buffer2D targetPixels, Buffer2D sourcePixels, KernelSamplingMap map, DenseMatrix kernel, Configuration configuration, bool preserveAlpha) { this.bounds = bounds; this.targetPixels = targetPixels; this.sourcePixels = sourcePixels; this.map = map; this.kernel = kernel; this.configuration = configuration; this.preserveAlpha = preserveAlpha; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { int x = bounds.X; int width = bounds.Width; Span span2 = span.Slice(0, bounds.Width); Span span3 = span.Slice(bounds.Width); MemoryMarshal.GetReference(span); Span span4 = targetPixels.DangerousGetRowSpan(y); Span destinationPixels = span4.Slice(x, width); ConvolutionState convolutionState = new ConvolutionState(in kernel, map); int row = y - bounds.Y; ref int sampleRow = ref convolutionState.GetSampleRow(row); if (preserveAlpha) { span3.Clear(); ref Vector4 reference = ref MemoryMarshal.GetReference(span3); Span span5; for (int i = 0; i < convolutionState.Kernel.Rows; i++) { int y2 = Unsafe.Add(ref sampleRow, i); span4 = sourcePixels.DangerousGetRowSpan(y2); span5 = span4.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span5, span2); ref Vector4 reference2 = ref MemoryMarshal.GetReference(span2); for (int j = 0; j < span2.Length; j++) { ref int sampleColumn = ref convolutionState.GetSampleColumn(j); ref Vector4 reference3 = ref Unsafe.Add(ref reference, j); for (int k = 0; k < convolutionState.Kernel.Columns; k++) { int elementOffset = Unsafe.Add(ref sampleColumn, k) - x; Vector4 vector = Unsafe.Add(ref reference2, elementOffset); reference3 += convolutionState.Kernel[i, k] * vector; } } } span4 = sourcePixels.DangerousGetRowSpan(y); span5 = span4.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span5, span2); for (int l = 0; l < span5.Length; l++) { Unsafe.Add(ref reference, l).W = Unsafe.Add(ref MemoryMarshal.GetReference(span2), l).W; } } else { span3.Clear(); ref Vector4 reference4 = ref MemoryMarshal.GetReference(span3); for (int m = 0; m < convolutionState.Kernel.Rows; m++) { int y3 = Unsafe.Add(ref sampleRow, m); span4 = sourcePixels.DangerousGetRowSpan(y3); Span span6 = span4.Slice(x, width); PixelOperations.Instance.ToVector4(configuration, span6, span2); Numerics.Premultiply(span2); ref Vector4 reference5 = ref MemoryMarshal.GetReference(span2); for (int n = 0; n < span2.Length; n++) { ref int sampleColumn2 = ref convolutionState.GetSampleColumn(n); ref Vector4 reference6 = ref Unsafe.Add(ref reference4, n); for (int num = 0; num < convolutionState.Kernel.Columns; num++) { int elementOffset2 = Unsafe.Add(ref sampleColumn2, num) - x; Vector4 vector2 = Unsafe.Add(ref reference5, elementOffset2); reference6 += convolutionState.Kernel[m, num] * vector2; } } } Numerics.UnPremultiply(span3); } PixelOperations.Instance.FromVector4Destructive(configuration, span3, destinationPixels); } } public DenseMatrix KernelXY { get; } public bool PreserveAlpha { get; } public ConvolutionProcessor(Configuration configuration, in DenseMatrix kernelXY, bool preserveAlpha, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { KernelXY = kernelXY; PreserveAlpha = preserveAlpha; } protected override void OnFrameApply(ImageFrame source) { MemoryAllocator memoryAllocator = base.Configuration.MemoryAllocator; using Buffer2D buffer2D = memoryAllocator.Allocate2D(source.Size()); source.CopyTo(buffer2D); Rectangle bounds = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); Rectangle rectangle = new Rectangle(bounds.X, bounds.Y, bounds.Width * 2, bounds.Height); using (KernelSamplingMap kernelSamplingMap = new KernelSamplingMap(memoryAllocator)) { kernelSamplingMap.BuildSamplingOffsetMap(KernelXY, bounds); RowOperation operation = new RowOperation(bounds, buffer2D, source.PixelBuffer, kernelSamplingMap, KernelXY, base.Configuration, PreserveAlpha); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation); } Buffer2D.SwapOrCopyContent(source.PixelBuffer, buffer2D); } } internal readonly ref struct ConvolutionState { private readonly Span rowOffsetMap; private readonly Span columnOffsetMap; private readonly int kernelHeight; private readonly int kernelWidth; public ReadOnlyKernel Kernel { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public ConvolutionState(in DenseMatrix kernel, KernelSamplingMap map) { Kernel = new ReadOnlyKernel(kernel); kernelHeight = kernel.Rows; kernelWidth = kernel.Columns; rowOffsetMap = map.GetRowOffsetSpan(); columnOffsetMap = map.GetColumnOffsetSpan(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref int GetSampleRow(int row) { return ref Unsafe.Add(ref MemoryMarshal.GetReference(rowOffsetMap), row * kernelHeight); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref int GetSampleColumn(int column) { return ref Unsafe.Add(ref MemoryMarshal.GetReference(columnOffsetMap), column * kernelWidth); } } public sealed class EdgeDetector2DProcessor : IImageProcessor { public EdgeDetector2DKernel Kernel { get; } public bool Grayscale { get; } public EdgeDetector2DProcessor(EdgeDetector2DKernel kernel, bool grayscale) { Kernel = kernel; Grayscale = grayscale; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new EdgeDetector2DProcessor(configuration, this, source, sourceRectangle); } } internal class EdgeDetector2DProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly DenseMatrix kernelX; private readonly DenseMatrix kernelY; private readonly bool grayscale; public EdgeDetector2DProcessor(Configuration configuration, EdgeDetector2DProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { kernelX = definition.Kernel.KernelX; kernelY = definition.Kernel.KernelY; grayscale = definition.Grayscale; } protected override void BeforeImageApply() { using (IImageProcessor imageProcessor = new OpaqueProcessor(base.Configuration, base.Source, base.SourceRectangle)) { imageProcessor.Execute(); } if (grayscale) { new GrayscaleBt709Processor(1f).Execute(base.Configuration, base.Source, base.SourceRectangle); } base.BeforeImageApply(); } protected override void OnFrameApply(ImageFrame source) { using Convolution2DProcessor convolution2DProcessor = new Convolution2DProcessor(base.Configuration, in kernelX, in kernelY, preserveAlpha: true, base.Source, base.SourceRectangle); convolution2DProcessor.Apply(source); } } public sealed class EdgeDetectorCompassProcessor : IImageProcessor { public EdgeDetectorCompassKernel Kernel { get; } public bool Grayscale { get; } public EdgeDetectorCompassProcessor(EdgeDetectorCompassKernel kernel, bool grayscale) { Kernel = kernel; Grayscale = grayscale; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new EdgeDetectorCompassProcessor(configuration, this, source, sourceRectangle); } } internal class EdgeDetectorCompassProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly Buffer2D targetPixels; private readonly Buffer2D passPixels; private readonly int minX; private readonly int maxX; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(Buffer2D targetPixels, Buffer2D passPixels, Rectangle bounds) { this.targetPixels = targetPixels; this.passPixels = passPixels; minX = bounds.X; maxX = bounds.Right; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { ref TPixel reference = ref MemoryMarshal.GetReference(passPixels.DangerousGetRowSpan(y)); ref TPixel reference2 = ref MemoryMarshal.GetReference(targetPixels.DangerousGetRowSpan(y)); for (int i = minX; i < maxX; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); ref TPixel reference4 = ref Unsafe.Add(ref reference2, i); Vector4 vector = Vector4.Max(reference3.ToVector4(), reference4.ToVector4()); reference4.FromVector4(vector); } } } private readonly DenseMatrix[] kernels; private readonly bool grayscale; internal EdgeDetectorCompassProcessor(Configuration configuration, EdgeDetectorCompassProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { grayscale = definition.Grayscale; kernels = definition.Kernel.Flatten(); } protected override void BeforeImageApply() { using (IImageProcessor imageProcessor = new OpaqueProcessor(base.Configuration, base.Source, base.SourceRectangle)) { imageProcessor.Execute(); } if (grayscale) { new GrayscaleBt709Processor(1f).Execute(base.Configuration, base.Source, base.SourceRectangle); } base.BeforeImageApply(); } protected override void OnFrameApply(ImageFrame source) { Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); using ImageFrame imageFrame = source.Clone(); using (ConvolutionProcessor convolutionProcessor = new ConvolutionProcessor(base.Configuration, in kernels[0], preserveAlpha: true, base.Source, rectangle)) { convolutionProcessor.Apply(source); } if (kernels.Length == 1) { return; } for (int i = 1; i < kernels.Length; i++) { using ImageFrame imageFrame2 = imageFrame.Clone(); using (ConvolutionProcessor convolutionProcessor2 = new ConvolutionProcessor(base.Configuration, in kernels[i], preserveAlpha: true, base.Source, rectangle)) { convolutionProcessor2.Apply(imageFrame2); } RowOperation operation = new RowOperation(source.PixelBuffer, imageFrame2.PixelBuffer, rectangle); ParallelRowIterator.IterateRows(base.Configuration, rectangle, in operation); } } } public sealed class EdgeDetectorProcessor : IImageProcessor { public EdgeDetectorKernel Kernel { get; } public bool Grayscale { get; } public EdgeDetectorProcessor(EdgeDetectorKernel kernel, bool grayscale) { Kernel = kernel; Grayscale = grayscale; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new EdgeDetectorProcessor(configuration, this, source, sourceRectangle); } } internal class EdgeDetectorProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly bool grayscale; private readonly DenseMatrix kernelXY; public EdgeDetectorProcessor(Configuration configuration, EdgeDetectorProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { kernelXY = definition.Kernel.KernelXY; grayscale = definition.Grayscale; } protected override void BeforeImageApply() { using (IImageProcessor imageProcessor = new OpaqueProcessor(base.Configuration, base.Source, base.SourceRectangle)) { imageProcessor.Execute(); } if (grayscale) { new GrayscaleBt709Processor(1f).Execute(base.Configuration, base.Source, base.SourceRectangle); } base.BeforeImageApply(); } protected override void OnFrameApply(ImageFrame source) { using ConvolutionProcessor convolutionProcessor = new ConvolutionProcessor(base.Configuration, in kernelXY, preserveAlpha: true, base.Source, base.SourceRectangle); convolutionProcessor.Apply(source); } } public sealed class GaussianBlurProcessor : IImageProcessor { public const float DefaultSigma = 3f; public float Sigma { get; } public int Radius { get; } public GaussianBlurProcessor() : this(3f, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(3f)) { } public GaussianBlurProcessor(float sigma) : this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(sigma)) { } public GaussianBlurProcessor(int radius) : this((float)radius / 3f, radius) { } public GaussianBlurProcessor(float sigma, int radius) { Sigma = sigma; Radius = radius; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new GaussianBlurProcessor(configuration, this, source, sourceRectangle); } } internal class GaussianBlurProcessor : ImageProcessor where TPixel : unmanaged, IPixel { public float[] Kernel { get; } public GaussianBlurProcessor(Configuration configuration, GaussianBlurProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { int size = definition.Radius * 2 + 1; Kernel = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(size, definition.Sigma); } protected override void OnFrameApply(ImageFrame source) { using Convolution2PassProcessor convolution2PassProcessor = new Convolution2PassProcessor(base.Configuration, Kernel, preserveAlpha: false, base.Source, base.SourceRectangle); convolution2PassProcessor.Apply(source); } } public sealed class GaussianSharpenProcessor : IImageProcessor { public const float DefaultSigma = 3f; public float Sigma { get; } public int Radius { get; } public GaussianSharpenProcessor() : this(3f, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(3f)) { } public GaussianSharpenProcessor(float sigma) : this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(sigma)) { } public GaussianSharpenProcessor(int radius) : this((float)radius / 3f, radius) { } public GaussianSharpenProcessor(float sigma, int radius) { Sigma = sigma; Radius = radius; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new GaussianSharpenProcessor(configuration, this, source, sourceRectangle); } } internal class GaussianSharpenProcessor : ImageProcessor where TPixel : unmanaged, IPixel { public float[] Kernel { get; } public GaussianSharpenProcessor(Configuration configuration, GaussianSharpenProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { int size = definition.Radius * 2 + 1; Kernel = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(size, definition.Sigma); } protected override void OnFrameApply(ImageFrame source) { using Convolution2PassProcessor convolution2PassProcessor = new Convolution2PassProcessor(base.Configuration, Kernel, preserveAlpha: false, base.Source, base.SourceRectangle); convolution2PassProcessor.Apply(source); } } public readonly struct EdgeDetector2DKernel : IEquatable { public static EdgeDetector2DKernel KayyaliKernel = new EdgeDetector2DKernel(KayyaliKernels.KayyaliX, KayyaliKernels.KayyaliY); public static EdgeDetector2DKernel PrewittKernel = new EdgeDetector2DKernel(PrewittKernels.PrewittX, PrewittKernels.PrewittY); public static EdgeDetector2DKernel RobertsCrossKernel = new EdgeDetector2DKernel(RobertsCrossKernels.RobertsCrossX, RobertsCrossKernels.RobertsCrossY); public static EdgeDetector2DKernel ScharrKernel = new EdgeDetector2DKernel(ScharrKernels.ScharrX, ScharrKernels.ScharrY); public static EdgeDetector2DKernel SobelKernel = new EdgeDetector2DKernel(SobelKernels.SobelX, SobelKernels.SobelY); public DenseMatrix KernelX { get; } public DenseMatrix KernelY { get; } public EdgeDetector2DKernel(DenseMatrix kernelX, DenseMatrix kernelY) { Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), "kernelX kernelY", "Kernel sizes must be the same."); KernelX = kernelX; KernelY = kernelY; } public static bool operator ==(EdgeDetector2DKernel left, EdgeDetector2DKernel right) { return left.Equals(right); } public static bool operator !=(EdgeDetector2DKernel left, EdgeDetector2DKernel right) { return !(left == right); } public override bool Equals(object obj) { if (obj is EdgeDetector2DKernel other) { return Equals(other); } return false; } public bool Equals(EdgeDetector2DKernel other) { if (KernelX.Equals(other.KernelX)) { return KernelY.Equals(other.KernelY); } return false; } public override int GetHashCode() { return HashCode.Combine(KernelX, KernelY); } } public readonly struct EdgeDetectorCompassKernel : IEquatable { public static EdgeDetectorCompassKernel Kirsch = new EdgeDetectorCompassKernel(KirschKernels.North, KirschKernels.NorthWest, KirschKernels.West, KirschKernels.SouthWest, KirschKernels.South, KirschKernels.SouthEast, KirschKernels.East, KirschKernels.NorthEast); public static EdgeDetectorCompassKernel Robinson = new EdgeDetectorCompassKernel(RobinsonKernels.North, RobinsonKernels.NorthWest, RobinsonKernels.West, RobinsonKernels.SouthWest, RobinsonKernels.South, RobinsonKernels.SouthEast, RobinsonKernels.East, RobinsonKernels.NorthEast); public DenseMatrix North { get; } public DenseMatrix NorthWest { get; } public DenseMatrix West { get; } public DenseMatrix SouthWest { get; } public DenseMatrix South { get; } public DenseMatrix SouthEast { get; } public DenseMatrix East { get; } public DenseMatrix NorthEast { get; } public EdgeDetectorCompassKernel(DenseMatrix north, DenseMatrix northWest, DenseMatrix west, DenseMatrix southWest, DenseMatrix south, DenseMatrix southEast, DenseMatrix east, DenseMatrix northEast) { North = north; NorthWest = northWest; West = west; SouthWest = southWest; South = south; SouthEast = southEast; East = east; NorthEast = northEast; } public static bool operator ==(EdgeDetectorCompassKernel left, EdgeDetectorCompassKernel right) { return left.Equals(right); } public static bool operator !=(EdgeDetectorCompassKernel left, EdgeDetectorCompassKernel right) { return !(left == right); } public override bool Equals(object obj) { if (obj is EdgeDetectorCompassKernel other) { return Equals(other); } return false; } public bool Equals(EdgeDetectorCompassKernel other) { if (North.Equals(other.North) && NorthWest.Equals(other.NorthWest) && West.Equals(other.West) && SouthWest.Equals(other.SouthWest) && South.Equals(other.South) && SouthEast.Equals(other.SouthEast) && East.Equals(other.East)) { return NorthEast.Equals(other.NorthEast); } return false; } public override int GetHashCode() { return HashCode.Combine(North, NorthWest, West, SouthWest, South, SouthEast, East, NorthEast); } internal DenseMatrix[] Flatten() { return new DenseMatrix[8] { North, NorthWest, West, SouthWest, South, SouthEast, East, NorthEast }; } } public readonly struct EdgeDetectorKernel : IEquatable { public static EdgeDetectorKernel Laplacian3x3 = new EdgeDetectorKernel(LaplacianKernels.Laplacian3x3); public static EdgeDetectorKernel Laplacian5x5 = new EdgeDetectorKernel(LaplacianKernels.Laplacian5x5); public static EdgeDetectorKernel LaplacianOfGaussian = new EdgeDetectorKernel(LaplacianKernels.LaplacianOfGaussianXY); public DenseMatrix KernelXY { get; } public EdgeDetectorKernel(DenseMatrix kernelXY) { KernelXY = kernelXY; } public static bool operator ==(EdgeDetectorKernel left, EdgeDetectorKernel right) { return left.Equals(right); } public static bool operator !=(EdgeDetectorKernel left, EdgeDetectorKernel right) { return !(left == right); } public override bool Equals(object obj) { if (obj is EdgeDetectorKernel other) { return Equals(other); } return false; } public bool Equals(EdgeDetectorKernel other) { return KernelXY.Equals(other.KernelXY); } public override int GetHashCode() { return KernelXY.GetHashCode(); } } internal static class KayyaliKernels { public static DenseMatrix KayyaliX => new float[3, 3] { { 6f, 0f, -6f }, { 0f, 0f, 0f }, { -6f, 0f, 6f } }; public static DenseMatrix KayyaliY => new float[3, 3] { { -6f, 0f, 6f }, { 0f, 0f, 0f }, { 6f, 0f, -6f } }; } internal static class KirschKernels { public static DenseMatrix North => new float[3, 3] { { 5f, 5f, 5f }, { -3f, 0f, -3f }, { -3f, -3f, -3f } }; public static DenseMatrix NorthWest => new float[3, 3] { { 5f, 5f, -3f }, { 5f, 0f, -3f }, { -3f, -3f, -3f } }; public static DenseMatrix West => new float[3, 3] { { 5f, -3f, -3f }, { 5f, 0f, -3f }, { 5f, -3f, -3f } }; public static DenseMatrix SouthWest => new float[3, 3] { { -3f, -3f, -3f }, { 5f, 0f, -3f }, { 5f, 5f, -3f } }; public static DenseMatrix South => new float[3, 3] { { -3f, -3f, -3f }, { -3f, 0f, -3f }, { 5f, 5f, 5f } }; public static DenseMatrix SouthEast => new float[3, 3] { { -3f, -3f, -3f }, { -3f, 0f, 5f }, { -3f, 5f, 5f } }; public static DenseMatrix East => new float[3, 3] { { -3f, -3f, 5f }, { -3f, 0f, 5f }, { -3f, -3f, 5f } }; public static DenseMatrix NorthEast => new float[3, 3] { { -3f, 5f, 5f }, { -3f, 0f, 5f }, { -3f, -3f, -3f } }; } internal static class LaplacianKernelFactory { public static DenseMatrix CreateKernel(uint length) { Guard.MustBeGreaterThanOrEqualTo(length, 3u, "length"); Guard.IsFalse(length % 2 == 0, "length", "The kernel length must be an odd number."); DenseMatrix result = new DenseMatrix((int)length); result.Fill(-1f); int num = (int)(length / 2); result[num, num] = length * length - 1; return result; } } internal static class LaplacianKernels { public static DenseMatrix Laplacian3x3 => LaplacianKernelFactory.CreateKernel(3u); public static DenseMatrix Laplacian5x5 => LaplacianKernelFactory.CreateKernel(5u); public static DenseMatrix LaplacianOfGaussianXY => new float[5, 5] { { 0f, 0f, -1f, 0f, 0f }, { 0f, -1f, -2f, -1f, 0f }, { -1f, -2f, 16f, -2f, -1f }, { 0f, -1f, -2f, -1f, 0f }, { 0f, 0f, -1f, 0f, 0f } }; } internal static class PrewittKernels { public static DenseMatrix PrewittX => new float[3, 3] { { -1f, 0f, 1f }, { -1f, 0f, 1f }, { -1f, 0f, 1f } }; public static DenseMatrix PrewittY => new float[3, 3] { { 1f, 1f, 1f }, { 0f, 0f, 0f }, { -1f, -1f, -1f } }; } internal static class RobertsCrossKernels { public static DenseMatrix RobertsCrossX => new float[2, 2] { { 1f, 0f }, { 0f, -1f } }; public static DenseMatrix RobertsCrossY => new float[2, 2] { { 0f, 1f }, { -1f, 0f } }; } internal static class RobinsonKernels { public static DenseMatrix North => new float[3, 3] { { 1f, 2f, 1f }, { 0f, 0f, 0f }, { -1f, -2f, -1f } }; public static DenseMatrix NorthWest => new float[3, 3] { { 2f, 1f, 0f }, { 1f, 0f, -1f }, { 0f, -1f, -2f } }; public static DenseMatrix West => new float[3, 3] { { 1f, 0f, -1f }, { 2f, 0f, -2f }, { 1f, 0f, -1f } }; public static DenseMatrix SouthWest => new float[3, 3] { { 0f, -1f, -2f }, { 1f, 0f, -1f }, { 2f, 1f, 0f } }; public static DenseMatrix South => new float[3, 3] { { -1f, -2f, -1f }, { 0f, 0f, 0f }, { 1f, 2f, 1f } }; public static DenseMatrix SouthEast => new float[3, 3] { { -2f, -1f, 0f }, { -1f, 0f, 1f }, { 0f, 1f, 2f } }; public static DenseMatrix East => new float[3, 3] { { -1f, 0f, 1f }, { -2f, 0f, 2f }, { -1f, 0f, 1f } }; public static DenseMatrix NorthEast => new float[3, 3] { { 0f, 1f, 2f }, { -1f, 0f, 1f }, { -2f, -1f, 0f } }; } internal static class ScharrKernels { public static DenseMatrix ScharrX => new float[3, 3] { { -3f, 0f, 3f }, { -10f, 0f, 10f }, { -3f, 0f, 3f } }; public static DenseMatrix ScharrY => new float[3, 3] { { 3f, 10f, 3f }, { 0f, 0f, 0f }, { -3f, -10f, -3f } }; } internal static class SobelKernels { public static DenseMatrix SobelX => new float[3, 3] { { -1f, 0f, 1f }, { -2f, 0f, 2f }, { -1f, 0f, 1f } }; public static DenseMatrix SobelY => new float[3, 3] { { -1f, -2f, -1f }, { 0f, 0f, 0f }, { 1f, 2f, 1f } }; } internal sealed class KernelSamplingMap : IDisposable { private readonly MemoryAllocator allocator; private bool isDisposed; private IMemoryOwner yOffsets; private IMemoryOwner xOffsets; public KernelSamplingMap(MemoryAllocator allocator) { this.allocator = allocator; } public void BuildSamplingOffsetMap(DenseMatrix kernel, Rectangle bounds) { BuildSamplingOffsetMap(kernel.Rows, kernel.Columns, bounds); } public void BuildSamplingOffsetMap(int kernelHeight, int kernelWidth, Rectangle bounds) { yOffsets = allocator.Allocate(bounds.Height * kernelHeight); xOffsets = allocator.Allocate(bounds.Width * kernelWidth); int y = bounds.Y; int max = bounds.Bottom - 1; int x = bounds.X; int max2 = bounds.Right - 1; int num = kernelHeight >> 1; int num2 = kernelWidth >> 1; Span span = yOffsets.GetSpan(); ref int reference = ref MemoryMarshal.GetReference(span); for (int i = 0; i < bounds.Height; i++) { int num3 = i * kernelHeight; for (int j = 0; j < kernelHeight; j++) { Unsafe.Add(ref reference, num3 + j) = i + j + y - num; } } if (kernelHeight > 1) { Numerics.Clamp(span, y, max); } Span span2 = xOffsets.GetSpan(); ref int reference2 = ref MemoryMarshal.GetReference(span2); for (int k = 0; k < bounds.Width; k++) { int num4 = k * kernelWidth; for (int l = 0; l < kernelWidth; l++) { Unsafe.Add(ref reference2, num4 + l) = k + l + x - num2; } } if (kernelWidth > 1) { Numerics.Clamp(span2, x, max2); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetRowOffsetSpan() { return yOffsets.GetSpan(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetColumnOffsetSpan() { return xOffsets.GetSpan(); } public void Dispose() { if (!isDisposed) { yOffsets?.Dispose(); xOffsets?.Dispose(); isDisposed = true; } } } internal readonly ref struct ReadOnlyKernel { private readonly ReadOnlySpan values; public int Columns { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public int Rows { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public float this[int row, int column] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Unsafe.Add(ref MemoryMarshal.GetReference(values), row * Columns + column); } } public ReadOnlyKernel(DenseMatrix matrix) { Columns = matrix.Columns; Rows = matrix.Rows; values = matrix.Span; } [Conditional("DEBUG")] private void CheckCoordinates(int row, int column) { if (row < 0 || row >= Rows) { throw new ArgumentOutOfRangeException("row", row, $"{row} is outwith the matrix bounds."); } if (column < 0 || column >= Columns) { throw new ArgumentOutOfRangeException("column", column, $"{column} is outwith the matrix bounds."); } } } } namespace SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters { internal readonly struct BokehBlurKernelData { public readonly Vector4[] Parameters; public readonly Complex64[][] Kernels; public BokehBlurKernelData(Vector4[] parameters, Complex64[][] kernels) { Parameters = parameters; Kernels = kernels; } } internal static class BokehBlurKernelDataProvider { private static readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); private static IReadOnlyList KernelScales { get; } = new float[6] { 1.4f, 1.2f, 1.2f, 1.2f, 1.2f, 1.2f }; private static IReadOnlyList KernelComponents { get; } = new Vector4[6][] { new Vector4[1] { new Vector4(0.862325f, 1.624835f, 0.767583f, 1.862321f) }, new Vector4[2] { new Vector4(0.886528f, 5.268909f, 0.411259f, -0.548794f), new Vector4(1.960518f, 1.558213f, 0.513282f, 4.56111f) }, new Vector4[3] { new Vector4(2.17649f, 5.043495f, 1.621035f, -2.105439f), new Vector4(1.019306f, 9.027613f, -0.28086f, -0.162882f), new Vector4(2.81511f, 1.597273f, -0.366471f, 10.300301f) }, new Vector4[4] { new Vector4(4.338459f, 1.553635f, -5.767909f, 46.1644f), new Vector4(3.839993f, 4.693183f, 9.795391f, -15.227561f), new Vector4(2.79188f, 8.178137f, -3.048324f, 0.302959f), new Vector4(1.34219f, 12.328289f, 0.010001f, 0.24465f) }, new Vector4[5] { new Vector4(4.892608f, 1.685979f, -22.356787f, 85.91246f), new Vector4(4.71187f, 4.998496f, 35.918938f, -28.875618f), new Vector4(4.052795f, 8.244168f, -13.212253f, -1.578428f), new Vector4(2.929212f, 11.900859f, 0.507991f, 1.816328f), new Vector4(1.512961f, 16.116383f, 0.138051f, -0.01f) }, new Vector4[6] { new Vector4(5.143778f, 2.079813f, -82.3266f, 111.231026f), new Vector4(5.612426f, 6.153387f, 113.87866f, 58.00488f), new Vector4(5.982921f, 9.802895f, 39.479084f, -162.02888f), new Vector4(6.505167f, 11.059237f, -71.286026f, 95.02707f), new Vector4(3.869579f, 14.81052f, 1.405746f, -3.704914f), new Vector4(2.201904f, 19.03291f, -0.152784f, -0.107988f) } }; public static BokehBlurKernelData GetBokehBlurKernelData(int radius, int kernelSize, int componentsCount) { BokehBlurParameters key = new BokehBlurParameters(radius, componentsCount); if (!Cache.TryGetValue(key, out var value)) { (Vector4[] Parameters, float Scale) parameters = GetParameters(componentsCount); Vector4[] item = parameters.Parameters; float item2 = parameters.Scale; Complex64[][] kernels = CreateComplexKernels(item, radius, kernelSize, item2); NormalizeKernels(kernels, item); value = new BokehBlurKernelData(item, kernels); Cache.TryAdd(key, value); } return value; } private static (Vector4[] Parameters, float Scale) GetParameters(int componentsCount) { int index = Math.Max(0, Math.Min(componentsCount - 1, KernelComponents.Count)); return (Parameters: KernelComponents[index], Scale: KernelScales[index]); } private static Complex64[][] CreateComplexKernels(Vector4[] kernelParameters, int radius, int kernelSize, float kernelsScale) { Complex64[][] array = new Complex64[kernelParameters.Length][]; ref Vector4 reference = ref MemoryMarshal.GetReference(kernelParameters.AsSpan()); for (int i = 0; i < kernelParameters.Length; i++) { ref Vector4 reference2 = ref Unsafe.Add(ref reference, i); array[i] = CreateComplex1DKernel(radius, kernelSize, kernelsScale, reference2.X, reference2.Y); } return array; } private static Complex64[] CreateComplex1DKernel(int radius, int kernelSize, float kernelsScale, float a, float b) { Complex64[] array = new Complex64[kernelSize]; ref Complex64 reference = ref MemoryMarshal.GetReference(array.AsSpan()); int num = -radius; int num2 = 0; while (num2 < kernelSize) { float num3 = (float)num * kernelsScale * (1f / (float)radius); num3 *= num3; Unsafe.Add(ref reference, num2) = new Complex64(MathF.Exp((0f - a) * num3) * MathF.Cos(b * num3), MathF.Exp((0f - a) * num3) * MathF.Sin(b * num3)); num2++; num++; } return array; } private static void NormalizeKernels(Complex64[][] kernels, Vector4[] kernelParameters) { float num = 0f; Span span = kernels.AsSpan(); ref Complex64[] reference = ref MemoryMarshal.GetReference(span); ref Vector4 reference2 = ref MemoryMarshal.GetReference(kernelParameters.AsSpan()); for (int i = 0; i < kernelParameters.Length; i++) { ref Complex64[] reference3 = ref Unsafe.Add(ref reference, i); int num2 = reference3.Length; ref Complex64 source = ref reference3[0]; ref Vector4 reference4 = ref Unsafe.Add(ref reference2, i); for (int j = 0; j < num2; j++) { for (int k = 0; k < num2; k++) { ref Complex64 reference5 = ref Unsafe.Add(ref source, j); ref Complex64 reference6 = ref Unsafe.Add(ref source, k); num += reference4.Z * (reference5.Real * reference6.Real - reference5.Imaginary * reference6.Imaginary) + reference4.W * (reference5.Real * reference6.Imaginary + reference5.Imaginary * reference6.Real); } } } float num3 = 1f / MathF.Sqrt(num); for (int l = 0; l < span.Length; l++) { ref Complex64[] reference7 = ref Unsafe.Add(ref reference, l); int num4 = reference7.Length; ref Complex64 source2 = ref reference7[0]; for (int m = 0; m < num4; m++) { Unsafe.Add(ref source2, m) *= num3; } } } } internal readonly struct BokehBlurParameters : IEquatable { public readonly int Radius; public readonly int Components; public BokehBlurParameters(int radius, int components) { Radius = radius; Components = components; } public bool Equals(BokehBlurParameters other) { if (Radius.Equals(other.Radius)) { return Components.Equals(other.Components); } return false; } public override bool Equals(object obj) { if (obj is BokehBlurParameters other) { return Equals(other); } return false; } public override int GetHashCode() { return (Radius.GetHashCode() * 397) ^ Components.GetHashCode(); } } } namespace SixLabors.ImageSharp.Processing.Processors.Binarization { public class AdaptiveThresholdProcessor : IImageProcessor { public Color Upper { get; set; } public Color Lower { get; set; } public float ThresholdLimit { get; set; } public AdaptiveThresholdProcessor() : this(Color.White, Color.Black, 0.85f) { } public AdaptiveThresholdProcessor(float thresholdLimit) : this(Color.White, Color.Black, thresholdLimit) { } public AdaptiveThresholdProcessor(Color upper, Color lower) : this(upper, lower, 0.85f) { } public AdaptiveThresholdProcessor(Color upper, Color lower, float thresholdLimit) { Upper = upper; Lower = lower; ThresholdLimit = thresholdLimit; } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new AdaptiveThresholdProcessor(configuration, this, source, sourceRectangle); } } internal class AdaptiveThresholdProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly Rectangle bounds; private readonly Buffer2D source; private readonly Buffer2D intImage; private readonly TPixel upper; private readonly TPixel lower; private readonly float thresholdLimit; private readonly int startX; private readonly int endX; private readonly int startY; private readonly byte clusterSize; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(Rectangle bounds, Buffer2D source, Buffer2D intImage, TPixel upper, TPixel lower, float thresholdLimit, byte clusterSize, int startX, int endX, int startY) { this.bounds = bounds; this.source = source; this.intImage = intImage; this.upper = upper; this.lower = lower; this.thresholdLimit = thresholdLimit; this.startX = startX; this.endX = endX; this.startY = startY; this.clusterSize = clusterSize; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y) { Rgba32 dest = default(Rgba32); Span span = source.DangerousGetRowSpan(y); for (int i = startX; i < endX; i++) { TPixel val = span[i]; val.ToRgba32(ref dest); int num = Math.Max(i - startX - clusterSize + 1, 0); int num2 = Math.Min(i - startX + clusterSize + 1, bounds.Width - 1); int num3 = Math.Max(y - startY - clusterSize + 1, 0); int num4 = Math.Min(y - startY + clusterSize + 1, bounds.Height - 1); uint num5 = (uint)((num2 - num) * (num4 - num3)); long num6 = (long)Math.Min(intImage[num2, num4] - intImage[num, num4] - intImage[num2, num3] + intImage[num, num3], 9223372036854775807uL); if ((float)((dest.R + dest.G + dest.B) * num5) <= (float)num6 * thresholdLimit) { source[i, y] = lower; } else { source[i, y] = upper; } } } } private readonly AdaptiveThresholdProcessor definition; public AdaptiveThresholdProcessor(Configuration configuration, AdaptiveThresholdProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.definition = definition; } protected override void OnFrameApply(ImageFrame source) { Rectangle rectangle = Rectangle.Intersect(base.SourceRectangle, source.Bounds()); Configuration configuration = base.Configuration; TPixel upper = definition.Upper.ToPixel(); TPixel lower = definition.Lower.ToPixel(); float thresholdLimit = definition.ThresholdLimit; int y = rectangle.Y; int bottom = rectangle.Bottom; int x = rectangle.X; int right = rectangle.Right; int width = rectangle.Width; int height = rectangle.Height; byte clusterSize = (byte)Math.Truncate((float)width / 16f - 1f); Buffer2D pixelBuffer = source.PixelBuffer; using Buffer2D buffer2D = base.Configuration.MemoryAllocator.Allocate2D(width, height); Rgba32 dest = default(Rgba32); for (int i = x; i < right; i++) { ulong num = 0uL; for (int j = y; j < bottom; j++) { Unsafe.Add(ref MemoryMarshal.GetReference(pixelBuffer.DangerousGetRowSpan(j)), i).ToRgba32(ref dest); num += (ulong)(dest.R + dest.G + dest.B); if (i - x != 0) { buffer2D[i - x, j - y] = buffer2D[i - x - 1, j - y] + num; } else { buffer2D[i - x, j - y] = num; } } } RowOperation operation = new RowOperation(rectangle, source.PixelBuffer, buffer2D, upper, lower, thresholdLimit, clusterSize, x, right, y); ParallelRowIterator.IterateRows(configuration, rectangle, in operation); } } public class BinaryThresholdProcessor : IImageProcessor { public float Threshold { get; } public Color UpperColor { get; } public Color LowerColor { get; } public BinaryThresholdMode Mode { get; } public BinaryThresholdProcessor(float threshold, BinaryThresholdMode mode) : this(threshold, Color.White, Color.Black, mode) { } public BinaryThresholdProcessor(float threshold) : this(threshold, Color.White, Color.Black, BinaryThresholdMode.Luminance) { } public BinaryThresholdProcessor(float threshold, Color upperColor, Color lowerColor, BinaryThresholdMode mode) { Guard.MustBeBetweenOrEqualTo(threshold, 0f, 1f, "threshold"); Threshold = threshold; UpperColor = upperColor; LowerColor = lowerColor; Mode = mode; } public BinaryThresholdProcessor(float threshold, Color upperColor, Color lowerColor) : this(threshold, upperColor, lowerColor, BinaryThresholdMode.Luminance) { } public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel { return new BinaryThresholdProcessor(configuration, this, source, sourceRectangle); } } internal class BinaryThresholdProcessor : ImageProcessor where TPixel : unmanaged, IPixel { private readonly struct RowOperation : IRowOperation { private readonly Buffer2D source; private readonly TPixel upper; private readonly TPixel lower; private readonly byte threshold; private readonly BinaryThresholdMode mode; private readonly int startX; private readonly Configuration configuration; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperation(int startX, Buffer2D source, TPixel upper, TPixel lower, byte threshold, BinaryThresholdMode mode, Configuration configuration) { this.startX = startX; this.source = source; this.upper = upper; this.lower = lower; this.threshold = threshold; this.mode = mode; this.configuration = configuration; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int y, Span span) { TPixel val = upper; TPixel val2 = lower; Span span2 = source.DangerousGetRowSpan(y).Slice(startX, span.Length); PixelOperations.Instance.ToRgb24(configuration, span2, span); switch (mode) { case BinaryThresholdMode.Luminance: { byte b = threshold; for (int j = 0; j < span2.Length; j++) { Rgb24 rgb = span[j]; byte b2 = ColorNumerics.Get8BitBT709Luminance(rgb.R, rgb.G, rgb.B); span2[j] = ((b2 >= b) ? val : val2); } break; } case BinaryThresholdMode.Saturation: { float num2 = (float)(int)threshold / 255f; for (int k = 0; k < span2.Length; k++) { float saturation = GetSaturation(span[k]); span2[k] = ((saturation >= num2) ? val : val2); } break; } case BinaryThresholdMode.MaxChroma: { float num = (float)(int)threshold / 2f; for (int i = 0; i < span2.Length; i++) { float maxChroma = GetMaxChroma(span[i]); span2[i] = ((maxChroma >= num) ? val : val2); } break; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float GetSaturation(Rgb24 rgb) { float x = (float)(int)rgb.R / 255f; float x2 = (float)(int)rgb.G / 255f; float y = (float)(int)rgb.B / 255f; float num = MathF.Max(x, MathF.Max(x2, y)); float num2 = MathF.Min(x, MathF.Min(x2, y)); float num3 = num - num2; if (MathF.Abs(num3) < Constants.Epsilon) { return 0f; } if ((num + num2) / 2f <= 0.5f) { return num3 / (num + num2); } return num3 / (2f - num - num2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float GetMaxChroma(Rgb24 rgb) { float num = (int)rgb.R; float num2 = (int)rgb.G; float num3 = (int)rgb.B; float num4 = 128f + (-0.168736f * num - 0.331264f * num2 + 0.5f * num3); float num5 = 128f + (0.5f * num - 0.418688f * num2 - 0.081312f * num3); return MathF.Max(MathF.Abs(num4 - 127.5f), MathF.Abs(num5 - 127.5f)); } } private readonly BinaryThresholdProcessor definition; public BinaryThresholdProcessor(Configuration configuration, BinaryThresholdProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.definition = definition; } protected override void OnFrameApply(ImageFrame source) { byte threshold = (byte)MathF.Round(definition.Threshold * 255f); TPixel upper = definition.UpperColor.ToPixel(); TPixel lower = definition.LowerColor.ToPixel(); Rectangle sourceRectangle = base.SourceRectangle; Configuration configuration = base.Configuration; Rectangle rectangle = Rectangle.Intersect(sourceRectangle, source.Bounds()); RowOperation operation = new RowOperation(rectangle.X, source.PixelBuffer, upper, lower, threshold, definition.Mode, configuration); ParallelRowIterator.IterateRows(configuration, rectangle, in operation); } } } namespace SixLabors.ImageSharp.Processing.Extensions.Transforms { public static class SwizzleExtensions { public static IImageProcessingContext Swizzle(this IImageProcessingContext source, TSwizzler swizzler) where TSwizzler : struct, ISwizzler { return source.ApplyProcessor(new SwizzleProcessor(swizzler)); } } } namespace SixLabors.ImageSharp.PixelFormats { internal static class HalfTypeHelper { [StructLayout(LayoutKind.Explicit)] private struct Uif { [FieldOffset(0)] public float F; [FieldOffset(0)] public int I; [FieldOffset(0)] public uint U; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ushort Pack(float value) { Uif uif = new Uif { F = value }; return Pack(uif.I); } internal static ushort Pack(int value) { int num = (value >> 16) & 0x8000; int num2 = ((value >> 23) & 0xFF) - 112; int num3 = value & 0x7FFFFF; if (num2 <= 0) { if (num2 < -10) { return (ushort)num; } num3 |= 0x800000; int num4 = 14 - num2; int num5 = (1 << num4 - 1) - 1; int num6 = (num3 >> num4) & 1; num3 = num3 + num5 + num6 >> num4; return (ushort)(num | num3); } if (num2 == 143) { if (num3 == 0) { return (ushort)(num | 0x7C00); } num3 >>= 13; return (ushort)((uint)(num | 0x7C00 | num3) | ((num3 == 0) ? 1u : 0u)); } num3 = num3 + 4095 + ((num3 >> 13) & 1); if ((num3 & 0x800000) != 0) { num3 = 0; num2++; } if (num2 > 30) { return (ushort)(num | 0x7C00); } return (ushort)(num | (num2 << 10) | (num3 >> 13)); } internal static float Unpack(ushort value) { uint num = (uint)(value & 0x3FF); uint num2 = 4294967282u; uint u; if ((value & -33792) == 0) { if (num != 0) { while ((num & 0x400) == 0) { num2--; num <<= 1; } num &= 0xFFFFFBFFu; u = (uint)((value & 0x8000) << 16) | (num2 + 127 << 23) | (num << 13); } else { u = (uint)((value & 0x8000) << 16); } } else { u = (uint)(((value & 0x8000) << 16) | (((value >>> 10) & 0x1F) - 15 + 127 << 23)) | (num << 13); } return new Uif { U = u }.F; } } public interface IPackedVector : IPixel where TPacked : struct, IEquatable { TPacked PackedValue { get; set; } } public interface IPixel : IPixel, IEquatable where TSelf : unmanaged, IPixel { PixelOperations CreatePixelOperations(); } public interface IPixel { void FromScaledVector4(Vector4 vector); Vector4 ToScaledVector4(); void FromVector4(Vector4 vector); Vector4 ToVector4(); void FromArgb32(Argb32 source); void FromBgra5551(Bgra5551 source); void FromBgr24(Bgr24 source); void FromBgra32(Bgra32 source); void FromAbgr32(Abgr32 source); void FromL8(L8 source); void FromL16(L16 source); void FromLa16(La16 source); void FromLa32(La32 source); void FromRgb24(Rgb24 source); void FromRgba32(Rgba32 source); void ToRgba32(ref Rgba32 dest); void FromRgb48(Rgb48 source); void FromRgba64(Rgba64 source); } public enum PixelAlphaCompositionMode { SrcOver, Src, SrcAtop, SrcIn, SrcOut, Dest, DestAtop, DestOver, DestIn, DestOut, Clear, Xor } public enum PixelAlphaRepresentation { None, Associated, Unassociated } public abstract class PixelBlender where TPixel : unmanaged, IPixel { public abstract TPixel Blend(TPixel background, TPixel source, float amount); public void Blend(Configuration configuration, Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) where TPixelSrc : unmanaged, IPixel { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, "Length"); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, "Length"); Guard.MustBeBetweenOrEqualTo(amount, 0f, 1f, "amount"); using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(destination.Length * 3); Span destination2 = buffer.Slice(0, destination.Length); Span span = buffer.Slice(destination.Length, destination.Length); Span span2 = buffer.Slice(destination.Length * 2, destination.Length); ReadOnlySpan sourcePixels = background.Slice(0, background.Length); PixelOperations.Instance.ToVector4(configuration, sourcePixels, span, PixelConversionModifiers.Scale); ReadOnlySpan sourcePixels2 = source.Slice(0, background.Length); PixelOperations.Instance.ToVector4(configuration, sourcePixels2, span2, PixelConversionModifiers.Scale); BlendFunction(destination2, span, span2, amount); Span sourceVectors = destination2.Slice(0, background.Length); PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); } public void Blend(Configuration configuration, Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { this.Blend(configuration, destination, background, source, amount); } public void Blend(Configuration configuration, Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) where TPixelSrc : unmanaged, IPixel { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, "Length"); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, "Length"); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, "Length"); using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(destination.Length * 3); Span destination2 = buffer.Slice(0, destination.Length); Span span = buffer.Slice(destination.Length, destination.Length); Span span2 = buffer.Slice(destination.Length * 2, destination.Length); ReadOnlySpan sourcePixels = background.Slice(0, background.Length); PixelOperations.Instance.ToVector4(configuration, sourcePixels, span, PixelConversionModifiers.Scale); ReadOnlySpan sourcePixels2 = source.Slice(0, background.Length); PixelOperations.Instance.ToVector4(configuration, sourcePixels2, span2, PixelConversionModifiers.Scale); BlendFunction(destination2, span, span2, amount); Span sourceVectors = destination2.Slice(0, background.Length); PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); } protected abstract void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount); protected abstract void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount); } public enum PixelColorBlendingMode { Normal, Multiply, Add, Subtract, Screen, Darken, Lighten, Overlay, HardLight } [Flags] public enum PixelConversionModifiers { None = 0, Scale = 1, Premultiply = 2, SRgbCompand = 4 } internal static class PixelConversionModifiersExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsDefined(this PixelConversionModifiers modifiers, PixelConversionModifiers expected) { return (modifiers & expected) == expected; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PixelConversionModifiers Remove(this PixelConversionModifiers modifiers, PixelConversionModifiers removeThis) { return modifiers & ~removeThis; } public static PixelConversionModifiers ApplyCompanding(this PixelConversionModifiers originalModifiers, bool compand) { if (!compand) { return originalModifiers; } return originalModifiers | PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand; } } public struct A8 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } public byte PackedValue { get; set; } public A8(byte alpha) { PackedValue = alpha; } public A8(float alpha) { PackedValue = Pack(alpha); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(A8 left, A8 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(A8 left, A8 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = Pack(vector.W); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4(0f, 0f, 0f, (float)(int)PackedValue / 255f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { PackedValue = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { PackedValue = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { PackedValue = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { PackedValue = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { PackedValue = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { PackedValue = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { PackedValue = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { PackedValue = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { PackedValue = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { PackedValue = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest = default(Rgba32); dest.A = PackedValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { PackedValue = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } public override readonly bool Equals(object obj) { if (obj is A8 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(A8 other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { return $"A8({PackedValue})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte Pack(float alpha) { return (byte)Math.Round(Numerics.Clamp(alpha, 0f, 1f) * 255f); } } public struct Abgr32 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } public override void FromAbgr32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destinationPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToRgba32(source, dest); } public override void FromRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToAbgr32(source, dest); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToArgb32(source, dest); } public override void FromArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToAbgr32(source, dest); } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToBgra32(source, dest); } public override void FromBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToAbgr32(source, dest); } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToRgb24(source, dest); } public override void FromRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToAbgr32(source, dest); } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToBgr24(source, dest); } public override void FromBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToAbgr32(source, dest); } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Abgr32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Abgr32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromAbgr32(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Abgr32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Abgr32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromAbgr32(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Abgr32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Abgr32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromAbgr32(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Abgr32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Abgr32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromAbgr32(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Abgr32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Abgr32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromAbgr32(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Abgr32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Abgr32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromAbgr32(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Abgr32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Abgr32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromAbgr32(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToAbgr32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } } public byte A; public byte B; public byte G; public byte R; private static readonly Vector4 MaxBytes = new Vector4(255f); private static readonly Vector4 Half = new Vector4(0.5f); public uint Abgr { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return Unsafe.As(ref Unsafe.AsRef(in this)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Unsafe.As(ref this) = value; } } public uint PackedValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return Abgr; } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Abgr = value; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(byte r, byte g, byte b) { R = r; G = g; B = b; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(byte r, byte g, byte b, byte a) { R = r; G = g; B = b; A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(float r, float g, float b, float a = 1f) { this = default(Abgr32); Pack(r, g, b, a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(Vector3 vector) { this = default(Abgr32); Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(Vector4 vector) { this = default(Abgr32); Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Abgr32(uint packed) { this = default(Abgr32); Abgr = packed; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Color(Abgr32 source) { return new Color(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Abgr32(Color color) { return color.ToAbgr32(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Abgr32 left, Abgr32 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Abgr32 left, Abgr32 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((int)R, (int)G, (int)B, (int)A) / MaxBytes; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { Unsafe.As(ref Unsafe.AddByteOffset(ref Unsafe.As(ref this), new IntPtr(1))) = source; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { R = source.PackedValue; G = source.PackedValue; B = source.PackedValue; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue))); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { R = source.L; G = source.L; B = source.L; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L))); A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { R = source.R; G = source.G; B = source.B; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.R = R; dest.G = G; dest.B = B; dest.A = A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } public override readonly bool Equals(object obj) { if (obj is Abgr32 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Abgr32 other) { return Abgr == other.Abgr; } public override readonly string ToString() { return $"Abgr({A}, {B}, {G}, {R})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return Abgr.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(float x, float y, float z, float w) { Vector4 vector = new Vector4(x, y, z, w); Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(ref Vector3 vector) { Vector4 vector2 = new Vector4(vector, 1f); Pack(ref vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(ref Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); R = (byte)vector.X; G = (byte)vector.Y; B = (byte)vector.Z; A = (byte)vector.W; } } public struct Argb32 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } public override void FromArgb32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destinationPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToRgba32(source, dest); } public override void FromRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToArgb32(source, dest); } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToAbgr32(source, dest); } public override void FromAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToArgb32(source, dest); } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToBgra32(source, dest); } public override void FromBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToArgb32(source, dest); } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToRgb24(source, dest); } public override void FromRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToArgb32(source, dest); } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToBgr24(source, dest); } public override void FromBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToArgb32(source, dest); } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Argb32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Argb32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromArgb32(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Argb32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Argb32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromArgb32(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Argb32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Argb32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromArgb32(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Argb32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Argb32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromArgb32(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Argb32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Argb32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromArgb32(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Argb32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Argb32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromArgb32(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Argb32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Argb32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromArgb32(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToArgb32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } } public byte A; public byte R; public byte G; public byte B; private static readonly Vector4 MaxBytes = new Vector4(255f); private static readonly Vector4 Half = new Vector4(0.5f); public uint Argb { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return Unsafe.As(ref Unsafe.AsRef(in this)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Unsafe.As(ref this) = value; } } public uint PackedValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return Argb; } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Argb = value; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(byte r, byte g, byte b) { R = r; G = g; B = b; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(byte r, byte g, byte b, byte a) { R = r; G = g; B = b; A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(float r, float g, float b, float a = 1f) { this = default(Argb32); Pack(r, g, b, a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(Vector3 vector) { this = default(Argb32); Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(Vector4 vector) { this = default(Argb32); Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Argb32(uint packed) { this = default(Argb32); Argb = packed; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Color(Argb32 source) { return new Color(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Argb32(Color color) { return color.ToArgb32(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Argb32 left, Argb32 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Argb32 left, Argb32 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((int)R, (int)G, (int)B, (int)A) / MaxBytes; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { PackedValue = source.PackedValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { R = source.R; G = source.G; B = source.B; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { R = source.PackedValue; G = source.PackedValue; B = source.PackedValue; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue))); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { R = source.L; G = source.L; B = source.L; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L))); A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { R = source.R; G = source.G; B = source.B; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.R = R; dest.G = G; dest.B = B; dest.A = A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } public override readonly bool Equals(object obj) { if (obj is Argb32 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Argb32 other) { return Argb == other.Argb; } public override readonly string ToString() { return $"Argb({A}, {R}, {G}, {B})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return Argb.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(float x, float y, float z, float w) { Vector4 vector = new Vector4(x, y, z, w); Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(ref Vector3 vector) { Vector4 vector2 = new Vector4(vector, 1f); Pack(ref vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(ref Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); R = (byte)vector.X; G = (byte)vector.Y; B = (byte)vector.Z; A = (byte)vector.W; } } [StructLayout(LayoutKind.Explicit)] public struct Bgr24 : IPixel, IPixel, IEquatable { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } public override void FromBgr24(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destinationPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToRgba32(source, dest); } public override void FromRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToBgr24(source, dest); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToArgb32(source, dest); } public override void FromArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToBgr24(source, dest); } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToAbgr32(source, dest); } public override void FromAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToBgr24(source, dest); } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToBgra32(source, dest); } public override void FromBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToBgr24(source, dest); } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToRgb24(source, dest); } public override void FromRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToBgr24(source, dest); } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgr24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgr24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgr24(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgr24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgr24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgr24(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgr24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgr24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgr24(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgr24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgr24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgr24(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgr24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgr24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgr24(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgr24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgr24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgr24(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgr24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgr24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgr24(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToBgr24(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } } [FieldOffset(0)] public byte B; [FieldOffset(1)] public byte G; [FieldOffset(2)] public byte R; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Bgr24(byte r, byte g, byte b) { R = r; G = g; B = b; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Color(Bgr24 source) { return new Color(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Bgr24(Color color) { return color.ToBgr24(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgr24 left, Bgr24 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgr24 left, Bgr24 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Rgba32 source = default(Rgba32); source.FromVector4(vector); FromRgba32(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Rgba32(R, G, B, byte.MaxValue).ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { R = source.R; G = source.G; B = source.B; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { R = source.R; G = source.G; B = source.B; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { R = source.PackedValue; G = source.PackedValue; B = source.PackedValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { R = source.L; G = source.L; B = source.L; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { R = source.R; G = source.G; B = source.B; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { this = Unsafe.As(ref Unsafe.AddByteOffset(ref Unsafe.As(ref source), new IntPtr(1))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { this = source.Bgr; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.R = R; dest.G = G; dest.B = B; dest.A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Bgr24 other) { if (R.Equals(other.R) && G.Equals(other.G)) { return B.Equals(other.B); } return false; } public override readonly bool Equals(object obj) { if (obj is Bgr24 other) { return Equals(other); } return false; } public override readonly string ToString() { return $"Bgr24({B}, {G}, {R})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return HashCode.Combine(R, B, G); } } public struct Bgr565 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } public ushort PackedValue { get; set; } public Bgr565(float x, float y, float z) : this(new Vector3(x, y, z)) { } public Bgr565(Vector3 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgr565 left, Bgr565 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgr565 left, Bgr565 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Vector3 vector2 = new Vector3(vector.X, vector.Y, vector.Z); PackedValue = Pack(ref vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4(ToVector3(), 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromVector4(source.ToVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromVector4(source.ToVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromVector4(source.ToVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromVector4(source.ToVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromVector4(source.ToVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector3 ToVector3() { return new Vector3((float)((PackedValue >> 11) & 0x1F) * (1f / 31f), (float)((PackedValue >> 5) & 0x3F) * (1f / 63f), (float)(PackedValue & 0x1F) * (1f / 31f)); } public override readonly bool Equals(object obj) { if (obj is Bgr565 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Bgr565 other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { Vector3 vector = ToVector3(); return FormattableString.Invariant($"Bgr565({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort Pack(ref Vector3 vector) { vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); return (ushort)((((int)Math.Round(vector.X * 31f) & 0x1F) << 11) | (((int)Math.Round(vector.Y * 63f) & 0x3F) << 5) | ((int)Math.Round(vector.Z * 31f) & 0x1F)); } } public struct Bgra32 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } public override void FromBgra32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destinationPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale)); } public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToRgba32(source, dest); } public override void FromRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToBgra32(source, dest); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToArgb32(source, dest); } public override void FromArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToBgra32(source, dest); } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToAbgr32(source, dest); } public override void FromAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToBgra32(source, dest); } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToRgb24(source, dest); } public override void FromRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToBgra32(source, dest); } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToBgr24(source, dest); } public override void FromBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToBgra32(source, dest); } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra32(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra32(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra32(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra32(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra32(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra32(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra32(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToBgra32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } } public byte B; public byte G; public byte R; public byte A; private static readonly Vector4 MaxBytes = new Vector4(255f); private static readonly Vector4 Half = new Vector4(0.5f); public uint Bgra { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return Unsafe.As(ref Unsafe.AsRef(in this)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Unsafe.As(ref this) = value; } } public uint PackedValue { readonly get { return Bgra; } set { Bgra = value; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Bgra32(byte r, byte g, byte b) { R = r; G = g; B = b; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Bgra32(byte r, byte g, byte b, byte a) { R = r; G = g; B = b; A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Color(Bgra32 source) { return new Color(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Bgra32(Color color) { return color.ToBgra32(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgra32 left, Bgra32 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgra32 left, Bgra32 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((int)R, (int)G, (int)B, (int)A) / MaxBytes; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { R = source.R; G = source.G; B = source.B; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { R = source.PackedValue; G = source.PackedValue; B = source.PackedValue; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue))); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { R = source.L; G = source.L; B = source.L; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L))); A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { R = source.R; G = source.G; B = source.B; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.R = R; dest.G = G; dest.B = B; dest.A = A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } public override readonly bool Equals(object obj) { if (obj is Bgra32 other) { return Equals(other); } return false; } public readonly bool Equals(Bgra32 other) { return Bgra.Equals(other.Bgra); } public override readonly int GetHashCode() { return Bgra.GetHashCode(); } public override readonly string ToString() { return $"Bgra32({B}, {G}, {R}, {A})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(ref Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); R = (byte)vector.X; G = (byte)vector.Y; B = (byte)vector.Z; A = (byte)vector.W; } } public struct Bgra4444 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } public ushort PackedValue { get; set; } public Bgra4444(float x, float y, float z, float w) : this(new Vector4(x, y, z, w)) { } public Bgra4444(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgra4444 left, Bgra4444 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgra4444 left, Bgra4444 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((PackedValue >> 8) & 0xF, (PackedValue >> 4) & 0xF, PackedValue & 0xF, (PackedValue >> 12) & 0xF) * (1f / 15f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } public override readonly bool Equals(object obj) { if (obj is Bgra4444 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Bgra4444 other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { Vector4 vector = ToVector4(); return FormattableString.Invariant($"Bgra4444({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##}, {vector.W:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort Pack(ref Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); return (ushort)((((int)Math.Round(vector.W * 15f) & 0xF) << 12) | (((int)Math.Round(vector.X * 15f) & 0xF) << 8) | (((int)Math.Round(vector.Y * 15f) & 0xF) << 4) | ((int)Math.Round(vector.Z * 15f) & 0xF)); } } public struct Bgra5551 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } public override void FromBgra5551(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromBgra5551(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToBgra5551(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } } public ushort PackedValue { get; set; } public Bgra5551(float x, float y, float z, float w) : this(new Vector4(x, y, z, w)) { } public Bgra5551(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgra5551 left, Bgra5551 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgra5551 left, Bgra5551 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((float)((PackedValue >> 10) & 0x1F) / 31f, (float)((PackedValue >> 5) & 0x1F) / 31f, (float)(PackedValue & 0x1F) / 31f, (PackedValue >> 15) & 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } public override bool Equals(object obj) { if (obj is Bgra5551 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Bgra5551 other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { Vector4 vector = ToVector4(); return FormattableString.Invariant($"Bgra5551({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##}, {vector.W:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort Pack(ref Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); return (ushort)((((int)Math.Round(vector.X * 31f) & 0x1F) << 10) | (((int)Math.Round(vector.Y * 31f) & 0x1F) << 5) | ((int)Math.Round(vector.Z * 31f) & 0x1F) | (((int)Math.Round(vector.W) & 1) << 15)); } } public struct Byte4 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } public uint PackedValue { get; set; } public Byte4(Vector4 vector) { PackedValue = Pack(ref vector); } public Byte4(float x, float y, float z, float w) { Vector4 vector = new Vector4(x, y, z, w); PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Byte4 left, Byte4 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Byte4 left, Byte4 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector * 255f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4() / 255f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4(PackedValue & 0xFF, (PackedValue >> 8) & 0xFF, (PackedValue >> 16) & 0xFF, (PackedValue >> 24) & 0xFF); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } public override readonly bool Equals(object obj) { if (obj is Byte4 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Byte4 other) { return PackedValue.Equals(other.PackedValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } public override readonly string ToString() { Vector4 vector = ToVector4(); return FormattableString.Invariant($"Byte4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(ref Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, new Vector4(255f)); uint num = (uint)Math.Round(vector.X) & 0xFF; uint num2 = ((uint)Math.Round(vector.Y) & 0xFF) << 8; uint num3 = ((uint)Math.Round(vector.Z) & 0xFF) << 16; uint num4 = ((uint)Math.Round(vector.W) & 0xFF) << 24; return num | num2 | num3 | num4; } } public struct HalfSingle : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } public ushort PackedValue { get; set; } public HalfSingle(float single) { PackedValue = HalfTypeHelper.Pack(single); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HalfSingle left, HalfSingle right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfSingle left, HalfSingle right) { return !left.Equals(right); } public PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { float x = vector.X; x *= 2f; x -= 1f; PackedValue = HalfTypeHelper.Pack(x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return new Vector4((ToSingle() + 1f) / 2f, 0f, 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = HalfTypeHelper.Pack(vector.X); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4(ToSingle(), 0f, 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly float ToSingle() { return HalfTypeHelper.Unpack(PackedValue); } public override readonly bool Equals(object obj) { if (obj is HalfSingle other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(HalfSingle other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { return FormattableString.Invariant($"HalfSingle({ToSingle():#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } } public struct HalfVector2 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } public uint PackedValue { get; set; } public HalfVector2(float x, float y) { PackedValue = Pack(x, y); } public HalfVector2(Vector2 vector) { PackedValue = Pack(vector.X, vector.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HalfVector2 left, HalfVector2 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfVector2 left, HalfVector2 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { Vector2 vector2 = new Vector2(vector.X, vector.Y) * 2f; vector2 -= Vector2.One; PackedValue = Pack(vector2.X, vector2.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return new Vector4((ToVector2() + Vector2.One) / 2f, 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = Pack(vector.X, vector.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { Vector2 vector = ToVector2(); return new Vector4(vector.X, vector.Y, 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector2 ToVector2() { Vector2 result = default(Vector2); result.X = HalfTypeHelper.Unpack((ushort)PackedValue); result.Y = HalfTypeHelper.Unpack((ushort)(PackedValue >> 16)); return result; } public override readonly bool Equals(object obj) { if (obj is HalfVector2 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(HalfVector2 other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { Vector2 vector = ToVector2(); return FormattableString.Invariant($"HalfVector2({vector.X:#0.##}, {vector.Y:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(float x, float y) { ushort num = HalfTypeHelper.Pack(x); uint num2 = (uint)(HalfTypeHelper.Pack(y) << 16); return num | num2; } } public struct HalfVector4 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } public ulong PackedValue { get; set; } public HalfVector4(float x, float y, float z, float w) : this(new Vector4(x, y, z, w)) { } public HalfVector4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HalfVector4 left, HalfVector4 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfVector4 left, HalfVector4 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { vector *= 2f; vector -= Vector4.One; FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return (ToVector4() + Vector4.One) / 2f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4(HalfTypeHelper.Unpack((ushort)PackedValue), HalfTypeHelper.Unpack((ushort)(PackedValue >> 16)), HalfTypeHelper.Unpack((ushort)(PackedValue >> 32)), HalfTypeHelper.Unpack((ushort)(PackedValue >> 48))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } public override readonly bool Equals(object obj) { if (obj is HalfVector4 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(HalfVector4 other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { Vector4 vector = ToVector4(); return FormattableString.Invariant($"HalfVector4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Pack(ref Vector4 vector) { long num = HalfTypeHelper.Pack(vector.X); ulong num2 = (ulong)HalfTypeHelper.Pack(vector.Y) << 16; ulong num3 = (ulong)HalfTypeHelper.Pack(vector.Z) << 32; ulong num4 = (ulong)HalfTypeHelper.Pack(vector.W) << 48; return (ulong)num | num2 | num3 | num4; } } public struct L16 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override void FromL16(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL16(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToL16(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private const float Max = 65535f; public ushort PackedValue { get; set; } public L16(ushort luminance) { PackedValue = luminance; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(L16 left, L16 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(L16 left, L16 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { ConvertFromRgbaScaledVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { ConvertFromRgbaScaledVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { float num = (float)(int)PackedValue / 65535f; return new Vector4(num, num, num, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { PackedValue = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { PackedValue = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { PackedValue = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { PackedValue = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { PackedValue = ColorNumerics.UpscaleFrom8BitTo16Bit(source.PackedValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { PackedValue = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { PackedValue = source.L; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { PackedValue = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { PackedValue = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.B = (dest.G = (dest.R = ColorNumerics.DownScaleFrom16BitTo8Bit(PackedValue))); dest.A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { PackedValue = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { PackedValue = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); } public override readonly bool Equals(object obj) { if (obj is L16 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(L16 other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { return $"L16({PackedValue})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ConvertFromRgbaScaledVector4(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * 65535f; PackedValue = ColorNumerics.Get16BitBT709Luminance(vector.X, vector.Y, vector.Z); } } public struct L8 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override void FromL8(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref L8 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromL8(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToL8(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private static readonly Vector4 MaxBytes = new Vector4(255f); private static readonly Vector4 Half = new Vector4(0.5f); public byte PackedValue { get; set; } public L8(byte luminance) { PackedValue = luminance; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(L8 left, L8 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(L8 left, L8 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { ConvertFromRgbaScaledVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { ConvertFromRgbaScaledVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { float num = (float)(int)PackedValue / 255f; return new Vector4(num, num, num, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { PackedValue = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { PackedValue = source.L; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { PackedValue = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { PackedValue = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.R = PackedValue; dest.G = PackedValue; dest.B = PackedValue; dest.A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { PackedValue = ColorNumerics.Get8BitBT709Luminance(ColorNumerics.DownScaleFrom16BitTo8Bit(source.R), ColorNumerics.DownScaleFrom16BitTo8Bit(source.G), ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { PackedValue = ColorNumerics.Get8BitBT709Luminance(ColorNumerics.DownScaleFrom16BitTo8Bit(source.R), ColorNumerics.DownScaleFrom16BitTo8Bit(source.G), ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); } public override readonly bool Equals(object obj) { if (obj is L8 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(L8 other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { return $"L8({PackedValue})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ConvertFromRgbaScaledVector4(Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); PackedValue = ColorNumerics.Get8BitBT709Luminance((byte)vector.X, (byte)vector.Y, (byte)vector.Z); } } [StructLayout(LayoutKind.Explicit)] public struct La16 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override void FromLa16(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La16 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa16(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToLa16(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private static readonly Vector4 MaxBytes = new Vector4(255f); private static readonly Vector4 Half = new Vector4(0.5f); [FieldOffset(0)] public byte L; [FieldOffset(1)] public byte A; public ushort PackedValue { readonly get { return Unsafe.As(ref Unsafe.AsRef(in this)); } set { Unsafe.As(ref this) = value; } } public La16(byte l, byte a) { L = l; A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(La16 left, La16 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(La16 left, La16 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(La16 other) { return PackedValue.Equals(other.PackedValue); } public override readonly bool Equals(object obj) { if (obj is La16 other) { return Equals(other); } return false; } public override readonly string ToString() { return $"La16({L}, {A})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { L = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { L = source.PackedValue; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { L = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L); A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { L = ColorNumerics.Get8BitBT709Luminance(ColorNumerics.DownScaleFrom16BitTo8Bit(source.R), ColorNumerics.DownScaleFrom16BitTo8Bit(source.G), ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { L = ColorNumerics.Get8BitBT709Luminance(source.R, source.G, source.B); A = source.A; } public void FromRgba64(Rgba64 source) { L = ColorNumerics.Get8BitBT709Luminance(ColorNumerics.DownScaleFrom16BitTo8Bit(source.R), ColorNumerics.DownScaleFrom16BitTo8Bit(source.G), ColorNumerics.DownScaleFrom16BitTo8Bit(source.B)); A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { ConvertFromRgbaScaledVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { ConvertFromRgbaScaledVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.R = L; dest.G = L; dest.B = L; dest.A = A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { float num = (float)(int)L / 255f; return new Vector4(num, num, num, (float)(int)A / 255f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ConvertFromRgbaScaledVector4(Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); L = ColorNumerics.Get8BitBT709Luminance((byte)vector.X, (byte)vector.Y, (byte)vector.Z); A = (byte)vector.W; } } [StructLayout(LayoutKind.Explicit)] public struct La32 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override void FromLa32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref La32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromLa32(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToLa32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private const float Max = 65535f; [FieldOffset(0)] public ushort L; [FieldOffset(2)] public ushort A; public uint PackedValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return Unsafe.As(ref Unsafe.AsRef(in this)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Unsafe.As(ref this) = value; } } public La32(ushort l, ushort a) { L = l; A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(La32 left, La32 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(La32 left, La32 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(La32 other) { return PackedValue.Equals(other.PackedValue); } public override readonly bool Equals(object obj) { if (obj is La32 other) { return Equals(other); } return false; } public override readonly string ToString() { return $"La32({L}, {A})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { L = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { L = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { L = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { L = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { L = source.PackedValue; A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { L = ColorNumerics.UpscaleFrom8BitTo16Bit(source.PackedValue); A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { L = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { L = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { L = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { L = ColorNumerics.Get16BitBT709Luminance(ColorNumerics.UpscaleFrom8BitTo16Bit(source.R), ColorNumerics.UpscaleFrom8BitTo16Bit(source.G), ColorNumerics.UpscaleFrom8BitTo16Bit(source.B)); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { L = ColorNumerics.Get16BitBT709Luminance(source.R, source.G, source.B); A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { ConvertFromRgbaScaledVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { ConvertFromRgbaScaledVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.B = (dest.G = (dest.R = ColorNumerics.DownScaleFrom16BitTo8Bit(L))); dest.A = ColorNumerics.DownScaleFrom16BitTo8Bit(A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { float num = (float)(int)L / 65535f; return new Vector4(num, num, num, (float)(int)A / 65535f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ConvertFromRgbaScaledVector4(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * 65535f; L = ColorNumerics.Get16BitBT709Luminance(vector.X, vector.Y, vector.Z); A = (ushort)MathF.Round(vector.W); } } public struct NormalizedByte2 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private const float MaxPos = 127f; private static readonly Vector2 Half = new Vector2(127f); private static readonly Vector2 MinusOne = new Vector2(-1f); public ushort PackedValue { get; set; } public NormalizedByte2(float x, float y) : this(new Vector2(x, y)) { } public NormalizedByte2(Vector2 vector) { PackedValue = Pack(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedByte2 left, NormalizedByte2 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedByte2 left, NormalizedByte2 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { Vector2 vector2 = new Vector2(vector.X, vector.Y) * 2f; vector2 -= Vector2.One; PackedValue = Pack(vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return new Vector4((ToVector2() + Vector2.One) / 2f, 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Vector2 vector2 = new Vector2(vector.X, vector.Y); PackedValue = Pack(vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4(ToVector2(), 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector2 ToVector2() { return new Vector2((float)(sbyte)(PackedValue & 0xFF) / 127f, (float)(sbyte)((PackedValue >> 8) & 0xFF) / 127f); } public override readonly bool Equals(object obj) { if (obj is NormalizedByte2 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(NormalizedByte2 other) { return PackedValue.Equals(other.PackedValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } public override readonly string ToString() { Vector2 vector = ToVector2(); return FormattableString.Invariant($"NormalizedByte2({vector.X:#0.##}, {vector.Y:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort Pack(Vector2 vector) { vector = Vector2.Clamp(vector, MinusOne, Vector2.One) * Half; int num = (ushort)Convert.ToInt16(Math.Round(vector.X)) & 0xFF; int num2 = ((ushort)Convert.ToInt16(Math.Round(vector.Y)) & 0xFF) << 8; return (ushort)(num | num2); } } public struct NormalizedByte4 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private const float MaxPos = 127f; private static readonly Vector4 Half = new Vector4(127f); private static readonly Vector4 MinusOne = new Vector4(-1f); public uint PackedValue { get; set; } public NormalizedByte4(float x, float y, float z, float w) : this(new Vector4(x, y, z, w)) { } public NormalizedByte4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedByte4 left, NormalizedByte4 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedByte4 left, NormalizedByte4 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { vector *= 2f; vector -= Vector4.One; FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return (ToVector4() + Vector4.One) / 2f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((float)(sbyte)(PackedValue & 0xFF) / 127f, (float)(sbyte)((PackedValue >> 8) & 0xFF) / 127f, (float)(sbyte)((PackedValue >> 16) & 0xFF) / 127f, (float)(sbyte)((PackedValue >> 24) & 0xFF) / 127f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } public override readonly bool Equals(object obj) { if (obj is NormalizedByte4 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(NormalizedByte4 other) { return PackedValue.Equals(other.PackedValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } public override readonly string ToString() { Vector4 vector = ToVector4(); return FormattableString.Invariant($"NormalizedByte4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(ref Vector4 vector) { vector = Numerics.Clamp(vector, MinusOne, Vector4.One) * Half; int num = Convert.ToInt16(MathF.Round(vector.X)) & 0xFF; uint num2 = (uint)((Convert.ToInt16(MathF.Round(vector.Y)) & 0xFF) << 8); uint num3 = (uint)((Convert.ToInt16(MathF.Round(vector.Z)) & 0xFF) << 16); uint num4 = (uint)((Convert.ToInt16(MathF.Round(vector.W)) & 0xFF) << 24); return (uint)num | num2 | num3 | num4; } } public struct NormalizedShort2 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private const float MaxPos = 32767f; private static readonly Vector2 Max = new Vector2(32767f); private static readonly Vector2 Min = Vector2.Negate(Max); public uint PackedValue { get; set; } public NormalizedShort2(float x, float y) : this(new Vector2(x, y)) { } public NormalizedShort2(Vector2 vector) { PackedValue = Pack(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedShort2 left, NormalizedShort2 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { Vector2 vector2 = new Vector2(vector.X, vector.Y) * 2f; vector2 -= Vector2.One; PackedValue = Pack(vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return new Vector4((ToVector2() + Vector2.One) / 2f, 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Vector2 vector2 = new Vector2(vector.X, vector.Y); PackedValue = Pack(vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4(ToVector2(), 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector2 ToVector2() { return new Vector2((float)(short)(PackedValue & 0xFFFF) / 32767f, (float)(short)(PackedValue >> 16) / 32767f); } public override readonly bool Equals(object obj) { if (obj is NormalizedShort2 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(NormalizedShort2 other) { return PackedValue.Equals(other.PackedValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } public override readonly string ToString() { Vector2 vector = ToVector2(); return FormattableString.Invariant($"NormalizedShort2({vector.X:#0.##}, {vector.Y:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(Vector2 vector) { vector *= Max; vector = Vector2.Clamp(vector, Min, Max); int num = (int)MathF.Round(vector.X) & 0xFFFF; uint num2 = (uint)(((int)MathF.Round(vector.Y) & 0xFFFF) << 16); return (uint)num | num2; } } public struct NormalizedShort4 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private const float MaxPos = 32767f; private static readonly Vector4 Max = new Vector4(32767f); private static readonly Vector4 Min = Vector4.Negate(Max); public ulong PackedValue { get; set; } public NormalizedShort4(float x, float y, float z, float w) : this(new Vector4(x, y, z, w)) { } public NormalizedShort4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedShort4 left, NormalizedShort4 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { vector *= 2f; vector -= Vector4.One; FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return (ToVector4() + Vector4.One) / 2f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((float)(short)(PackedValue & 0xFFFF) / 32767f, (float)(short)((PackedValue >> 16) & 0xFFFF) / 32767f, (float)(short)((PackedValue >> 32) & 0xFFFF) / 32767f, (float)(short)((PackedValue >> 48) & 0xFFFF) / 32767f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } public override readonly bool Equals(object obj) { if (obj is NormalizedShort4 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(NormalizedShort4 other) { return PackedValue.Equals(other.PackedValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } public override readonly string ToString() { Vector4 vector = ToVector4(); return FormattableString.Invariant($"NormalizedShort4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Pack(ref Vector4 vector) { vector *= Max; vector = Numerics.Clamp(vector, Min, Max); long num = (long)Convert.ToInt32(MathF.Round(vector.X)) & 0xFFFFL; ulong num2 = ((ulong)Convert.ToInt32(MathF.Round(vector.Y)) & 0xFFFFuL) << 16; ulong num3 = ((ulong)Convert.ToInt32(MathF.Round(vector.Z)) & 0xFFFFuL) << 32; ulong num4 = ((ulong)Convert.ToInt32(MathF.Round(vector.W)) & 0xFFFFuL) << 48; return (ulong)num | num2 | num3 | num4; } } [StructLayout(LayoutKind.Explicit)] public struct Rgb24 : IPixel, IPixel, IEquatable { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override void FromRgb24(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destinationPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToRgba32(source, dest); } public override void FromRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToRgb24(source, dest); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToArgb32(source, dest); } public override void FromArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToRgb24(source, dest); } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToAbgr32(source, dest); } public override void FromAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToRgb24(source, dest); } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToBgra32(source, dest); } public override void FromBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToRgb24(source, dest); } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToBgr24(source, dest); } public override void FromBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToRgb24(source, dest); } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb24(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb24(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb24(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb24(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb24(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb24(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb24 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb24 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb24(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToRgb24(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } internal override void PackFromRgbPlanes(Configuration configuration, ReadOnlySpan redChannel, ReadOnlySpan greenChannel, ReadOnlySpan blueChannel, Span destination) { Guard.NotNull(configuration, "configuration"); int length = redChannel.Length; PixelOperations.GuardPackFromRgbPlanes(greenChannel, blueChannel, destination, length); SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination); } } [FieldOffset(0)] public byte R; [FieldOffset(1)] public byte G; [FieldOffset(2)] public byte B; private static readonly Vector4 MaxBytes = new Vector4(255f); private static readonly Vector4 Half = new Vector4(0.5f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb24(byte r, byte g, byte b) { R = r; G = g; B = b; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Color(Rgb24 source) { return new Color(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Rgb24(Color color) { return color.ToRgb24(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Rgb24(Rgb color) { Vector4 vector = new Vector4(color.ToVector3(), 1f); Rgb24 result = default(Rgb24); result.FromScaledVector4(vector); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgb24 left, Rgb24 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgb24 left, Rgb24 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Rgba32(R, G, B, byte.MaxValue).ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { R = source.R; G = source.G; B = source.B; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { R = source.R; G = source.G; B = source.B; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { R = source.R; G = source.G; B = source.B; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { R = source.R; G = source.G; B = source.B; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { R = source.PackedValue; G = source.PackedValue; B = source.PackedValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { R = source.L; G = source.L; B = source.L; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { this = source.Rgb; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.R = R; dest.G = G; dest.B = B; dest.A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); } public override readonly bool Equals(object obj) { if (obj is Rgb24 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Rgb24 other) { if (R.Equals(other.R) && G.Equals(other.G)) { return B.Equals(other.B); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return HashCode.Combine(R, B, G); } public override readonly string ToString() { return $"Rgb24({R}, {G}, {B})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(ref Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); R = (byte)vector.X; G = (byte)vector.Y; B = (byte)vector.Z; } } public struct Rgb48 : IPixel, IPixel, IEquatable { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override void FromRgb48(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgb48(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToRgb48(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private const float Max = 65535f; public ushort R; public ushort G; public ushort B; public Rgb48(ushort r, ushort g, ushort b) { this = default(Rgb48); R = r; G = g; B = b; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgb48 left, Rgb48 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgb48 left, Rgb48 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * 65535f; R = (ushort)MathF.Round(vector.X); G = (ushort)MathF.Round(vector.Y); B = (ushort)MathF.Round(vector.Z); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((float)(int)R / 65535f, (float)(int)G / 65535f, (float)(int)B / 65535f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { this = source.Rgb; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { B = (G = (R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.PackedValue))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { R = source.PackedValue; G = source.PackedValue; B = source.PackedValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { B = (G = (R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { R = source.L; G = source.L; B = source.L; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.R = ColorNumerics.DownScaleFrom16BitTo8Bit(R); dest.G = ColorNumerics.DownScaleFrom16BitTo8Bit(G); dest.B = ColorNumerics.DownScaleFrom16BitTo8Bit(B); dest.A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { this = source; } public override readonly bool Equals(object obj) { if (obj is Rgb48 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Rgb48 other) { if (R.Equals(other.R) && G.Equals(other.G)) { return B.Equals(other.B); } return false; } public override readonly string ToString() { return $"Rgb48({R}, {G}, {B})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return HashCode.Combine(R, G, B); } } public struct Rgba32 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override void FromRgba32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToArgb32(source, dest); } public override void FromArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromArgb32.ToRgba32(source, dest); } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToAbgr32(source, dest); } public override void FromAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromAbgr32.ToRgba32(source, dest); } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToBgra32(source, dest); } public override void FromBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgra32.ToRgba32(source, dest); } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToRgb24(source, dest); } public override void FromRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgb24.ToRgba32(source, dest); } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromRgba32.ToBgr24(source, dest); } public override void FromBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ReadOnlySpan source = MemoryMarshal.Cast(sourcePixels); Span dest = MemoryMarshal.Cast(destinationPixels); PixelConverter.FromBgr24.ToRgba32(source, dest); } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba32(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba32(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba32(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba32(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba32(reference3); } } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba32(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba32 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba32 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba32(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToRgba32(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationVectors, PixelConversionModifiers modifiers) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationVectors, "destinationVectors"); destinationVectors = destinationVectors.Slice(0, sourcePixels.Length); SimdUtils.ByteToNormalizedFloat(MemoryMarshal.Cast(sourcePixels), MemoryMarshal.Cast(destinationVectors)); Vector4Converters.ApplyForwardConversionModifiers(destinationVectors, modifiers); } public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destinationPixels, PixelConversionModifiers modifiers) { Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationPixels, "destinationPixels"); destinationPixels = destinationPixels.Slice(0, sourceVectors.Length); Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers); SimdUtils.NormalizedFloatToByteSaturate(MemoryMarshal.Cast(sourceVectors), MemoryMarshal.Cast(destinationPixels)); } internal override void PackFromRgbPlanes(Configuration configuration, ReadOnlySpan redChannel, ReadOnlySpan greenChannel, ReadOnlySpan blueChannel, Span destination) { Guard.NotNull(configuration, "configuration"); int length = redChannel.Length; PixelOperations.GuardPackFromRgbPlanes(greenChannel, blueChannel, destination, length); SimdUtils.PackFromRgbPlanes(configuration, redChannel, greenChannel, blueChannel, destination); } } public byte R; public byte G; public byte B; public byte A; private static readonly Vector4 MaxBytes = new Vector4(255f); private static readonly Vector4 Half = new Vector4(0.5f); public uint Rgba { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return Unsafe.As(ref Unsafe.AsRef(in this)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Unsafe.As(ref this) = value; } } public Rgb24 Rgb { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return new Rgb24(R, G, B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { R = value.R; G = value.G; B = value.B; } } public Bgr24 Bgr { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return new Bgr24(R, G, B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { R = value.R; G = value.G; B = value.B; } } public uint PackedValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return Rgba; } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Rgba = value; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(byte r, byte g, byte b) { R = r; G = g; B = b; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(byte r, byte g, byte b, byte a) { R = r; G = g; B = b; A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(float r, float g, float b, float a = 1f) { this = default(Rgba32); Pack(r, g, b, a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(Vector3 vector) { this = default(Rgba32); Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(Vector4 vector) { this = default(Rgba32); this = PackNew(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32(uint packed) { this = default(Rgba32); Rgba = packed; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Color(Rgba32 source) { return new Color(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Rgba32(Color color) { return color.ToRgba32(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Rgba32(Rgb color) { Vector4 vector = new Vector4(color.ToVector3(), 1f); Rgba32 result = default(Rgba32); result.FromScaledVector4(vector); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgba32 left, Rgba32 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgba32 left, Rgba32 right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rgba32 ParseHex(string hex) { Guard.NotNull(hex, "hex"); if (!TryParseHex(hex, out var result)) { throw new ArgumentException("Hexadecimal string is not in the correct format.", "hex"); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryParseHex(string hex, out Rgba32 result) { result = default(Rgba32); if (string.IsNullOrWhiteSpace(hex)) { return false; } hex = ToRgbaHex(hex); if (hex == null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result2)) { return false; } result2 = BinaryPrimitives.ReverseEndianness(result2); result = Unsafe.As(ref result2); return true; } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((int)R, (int)G, (int)B, (int)A) / MaxBytes; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { Bgr = source; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { R = source.R; G = source.G; B = source.B; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { R = source.PackedValue; G = source.PackedValue; B = source.PackedValue; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.PackedValue))); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { R = source.L; G = source.L; B = source.L; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { B = (G = (R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.L))); A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { Rgb = source; A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest = this; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); A = byte.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { R = ColorNumerics.DownScaleFrom16BitTo8Bit(source.R); G = ColorNumerics.DownScaleFrom16BitTo8Bit(source.G); B = ColorNumerics.DownScaleFrom16BitTo8Bit(source.B); A = ColorNumerics.DownScaleFrom16BitTo8Bit(source.A); } public readonly string ToHex() { return ((uint)(A | (B << 8) | (G << 16) | (R << 24))).ToString("X8"); } public override readonly bool Equals(object obj) { if (obj is Rgba32 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Rgba32 other) { return Rgba.Equals(other.Rgba); } public override readonly string ToString() { return $"Rgba32({R}, {G}, {B}, {A})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return Rgba.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Rgba32 PackNew(ref Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); return new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, (byte)vector.W); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(float x, float y, float z, float w) { Vector4 vector = new Vector4(x, y, z, w); Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(ref Vector3 vector) { Vector4 vector2 = new Vector4(vector, 1f); Pack(ref vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(ref Vector4 vector) { vector *= MaxBytes; vector += Half; vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); R = (byte)vector.X; G = (byte)vector.Y; B = (byte)vector.Z; A = (byte)vector.W; } private static string ToRgbaHex(string hex) { if (hex[0] == '#') { hex = hex.Substring(1); } if (hex.Length == 8) { return hex; } if (hex.Length == 6) { return hex + "FF"; } int length = hex.Length; if ((length < 3 || length > 4) ? true : false) { return null; } char c = hex[0]; char c2 = hex[1]; char c3 = hex[2]; char c4 = ((hex.Length == 3) ? 'F' : hex[3]); return new string(new char[8] { c, c, c2, c2, c3, c3, c4, c4 }); } } public struct Rgba64 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override void FromRgba64(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); source.CopyTo(destinationPixels.Slice(0, source.Length)); } public override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); sourcePixels.CopyTo(destinationPixels.Slice(0, sourcePixels.Length)); } public override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (nint num = 0; num < sourcePixels.Length; num++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, num); Unsafe.Add(ref reference2, num).FromRgba64(reference3); } } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { PixelOperations.Instance.ToRgba64(configuration, sourcePixels, destinationPixels.Slice(0, sourcePixels.Length)); } public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private const float Max = 65535f; public ushort R; public ushort G; public ushort B; public ushort A; public Rgb48 Rgb { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return Unsafe.As(ref Unsafe.AsRef(in this)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Unsafe.As(ref this) = value; } } public ulong PackedValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return Unsafe.As(ref Unsafe.AsRef(in this)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Unsafe.As(ref this) = value; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(ushort r, ushort g, ushort b, ushort a) { R = r; G = g; B = b; A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Rgba32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Bgra32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Argb32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Abgr32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Rgb24 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Bgr24 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba64(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * 65535f; R = (ushort)MathF.Round(vector.X); G = (ushort)MathF.Round(vector.Y); B = (ushort)MathF.Round(vector.Z); A = (ushort)MathF.Round(vector.W); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Color(Rgba64 source) { return new Color(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Rgba64(Color color) { return color.ToPixel(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgba64 left, Rgba64 right) { return left.PackedValue == right.PackedValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgba64 left, Rgba64 right) { return left.PackedValue != right.PackedValue; } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * 65535f; R = (ushort)MathF.Round(vector.X); G = (ushort)MathF.Round(vector.Y); B = (ushort)MathF.Round(vector.Z); A = (ushort)MathF.Round(vector.W); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((int)R, (int)G, (int)B, (int)A) / 65535f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { B = (G = (R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.PackedValue))); A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { R = source.PackedValue; G = source.PackedValue; B = source.PackedValue; A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { B = (G = (R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.L))); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { R = source.L; G = source.L; B = source.L; A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { R = ColorNumerics.UpscaleFrom8BitTo16Bit(source.R); G = ColorNumerics.UpscaleFrom8BitTo16Bit(source.G); B = ColorNumerics.UpscaleFrom8BitTo16Bit(source.B); A = ColorNumerics.UpscaleFrom8BitTo16Bit(source.A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.R = ColorNumerics.DownScaleFrom16BitTo8Bit(R); dest.G = ColorNumerics.DownScaleFrom16BitTo8Bit(G); dest.B = ColorNumerics.DownScaleFrom16BitTo8Bit(B); dest.A = ColorNumerics.DownScaleFrom16BitTo8Bit(A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { Rgb = source; A = ushort.MaxValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { this = source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Rgba32 ToRgba32() { byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(R); byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(G); byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(B); byte a = ColorNumerics.DownScaleFrom16BitTo8Bit(A); return new Rgba32(r, g, b, a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Bgra32 ToBgra32() { byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(R); byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(G); byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(B); byte a = ColorNumerics.DownScaleFrom16BitTo8Bit(A); return new Bgra32(r, g, b, a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Argb32 ToArgb32() { byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(R); byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(G); byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(B); byte a = ColorNumerics.DownScaleFrom16BitTo8Bit(A); return new Argb32(r, g, b, a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Abgr32 ToAbgr32() { byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(R); byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(G); byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(B); byte a = ColorNumerics.DownScaleFrom16BitTo8Bit(A); return new Abgr32(r, g, b, a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Rgb24 ToRgb24() { byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(R); byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(G); byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(B); return new Rgb24(r, g, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Bgr24 ToBgr24() { byte r = ColorNumerics.DownScaleFrom16BitTo8Bit(R); byte g = ColorNumerics.DownScaleFrom16BitTo8Bit(G); byte b = ColorNumerics.DownScaleFrom16BitTo8Bit(B); return new Bgr24(r, g, b); } public override readonly bool Equals(object obj) { if (obj is Rgba64 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Rgba64 other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { return $"Rgba64({R}, {G}, {B}, {A})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } } public struct Rg32 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private static readonly Vector2 Max = new Vector2(65535f); public uint PackedValue { get; set; } public Rg32(float x, float y) : this(new Vector2(x, y)) { } public Rg32(Vector2 vector) { PackedValue = Pack(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rg32 left, Rg32 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rg32 left, Rg32 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Vector2 vector2 = new Vector2(vector.X, vector.Y); PackedValue = Pack(vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4(ToVector2(), 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector2 ToVector2() { return new Vector2(PackedValue & 0xFFFF, (PackedValue >> 16) & 0xFFFF) / Max; } public override readonly bool Equals(object obj) { if (obj is Rg32 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Rg32 other) { return PackedValue.Equals(other.PackedValue); } public override readonly string ToString() { Vector2 vector = ToVector2(); return FormattableString.Invariant($"Rg32({vector.X:#0.##}, {vector.Y:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(Vector2 vector) { vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One) * Max; return (uint)(((int)Math.Round(vector.X) & 0xFFFF) | (((int)Math.Round(vector.Y) & 0xFFFF) << 16)); } } public struct Rgba1010102 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private static readonly Vector4 Multiplier = new Vector4(1023f, 1023f, 1023f, 3f); public uint PackedValue { get; set; } public Rgba1010102(float x, float y, float z, float w) : this(new Vector4(x, y, z, w)) { } public Rgba1010102(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgba1010102 left, Rgba1010102 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgba1010102 left, Rgba1010102 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4(PackedValue & 0x3FF, (PackedValue >> 10) & 0x3FF, (PackedValue >> 20) & 0x3FF, (PackedValue >> 30) & 3) / Multiplier; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } public override readonly bool Equals(object obj) { if (obj is Rgba1010102 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Rgba1010102 other) { return PackedValue == other.PackedValue; } public override readonly string ToString() { Vector4 vector = ToVector4(); return FormattableString.Invariant($"Rgba1010102({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(ref Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One) * Multiplier; return (uint)(((int)Math.Round(vector.X) & 0x3FF) | (((int)Math.Round(vector.Y) & 0x3FF) << 10) | (((int)Math.Round(vector.Z) & 0x3FF) << 20) | (((int)Math.Round(vector.W) & 3) << 30)); } } public struct RgbaVector : IPixel, IPixel, IEquatable { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } public override void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Span destinationVectors = MemoryMarshal.Cast(destinationPixels); PixelOperations.Instance.ToVector4(configuration, sourcePixels, destinationVectors, PixelConversionModifiers.Scale); } public override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destinationPixels, PixelConversionModifiers modifiers) { Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationPixels, "destinationPixels"); Vector4Converters.ApplyBackwardConversionModifiers(sourceVectors, modifiers); MemoryMarshal.Cast(sourceVectors).CopyTo(destinationPixels); } public override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationVectors, PixelConversionModifiers modifiers) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationVectors, "destinationVectors"); MemoryMarshal.Cast(sourcePixels).CopyTo(destinationVectors); Vector4Converters.ApplyForwardConversionModifiers(destinationVectors, modifiers); } public override void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Vector4 source = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); ref L8 reference = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Vector4 reference2 = ref Unsafe.Add(ref source, i); Unsafe.Add(ref reference, i).ConvertFromRgbaScaledVector4(reference2); } } public override void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref Vector4 source = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); ref L16 reference = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref Vector4 reference2 = ref Unsafe.Add(ref source, i); Unsafe.Add(ref reference, i).ConvertFromRgbaScaledVector4(reference2); } } } public float R; public float G; public float B; public float A; private const float MaxBytes = 255f; private static readonly Vector4 Max = new Vector4(255f); private static readonly Vector4 Half = new Vector4(0.5f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public RgbaVector(float r, float g, float b, float a = 1f) { R = r; G = g; B = b; A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(RgbaVector left, RgbaVector right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(RgbaVector left, RgbaVector right) { return !left.Equals(right); } public static RgbaVector FromHex(string hex) { return Color.ParseHex(hex).ToPixel(); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return ToVector4(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One); R = vector.X; G = vector.Y; B = vector.Z; A = vector.W; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4(R, G, B, A); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } public readonly string ToHex() { Vector4 vector = ToVector4() * Max; vector += Half; return ((uint)((byte)vector.W | ((byte)vector.Z << 8) | ((byte)vector.Y << 16) | ((byte)vector.X << 24))).ToString("X8"); } public override readonly bool Equals(object obj) { if (obj is RgbaVector other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(RgbaVector other) { if (R.Equals(other.R) && G.Equals(other.G) && B.Equals(other.B)) { return A.Equals(other.A); } return false; } public override readonly string ToString() { return FormattableString.Invariant($"RgbaVector({R:#0.##}, {G:#0.##}, {B:#0.##}, {A:#0.##})"); } public override readonly int GetHashCode() { return HashCode.Combine(R, G, B, A); } } public struct Short2 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.None), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private const float MaxPos = 32767f; private const float MinNeg = -32768f; private static readonly Vector2 Max = new Vector2(32767f); private static readonly Vector2 Min = new Vector2(-32768f); public uint PackedValue { get; set; } public Short2(float x, float y) : this(new Vector2(x, y)) { } public Short2(Vector2 vector) { PackedValue = Pack(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Short2 left, Short2 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Short2 left, Short2 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { Vector2 vector2 = new Vector2(vector.X, vector.Y) * 65534f; vector2 -= new Vector2(32767f); PackedValue = Pack(vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return new Vector4((ToVector2() + new Vector2(32767f)) / 65534f, 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { Vector2 vector2 = new Vector2(vector.X, vector.Y); PackedValue = Pack(vector2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((short)(PackedValue & 0xFFFF), (short)(PackedValue >> 16), 0f, 1f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector2 ToVector2() { return new Vector2((short)(PackedValue & 0xFFFF), (short)(PackedValue >> 16)); } public override readonly bool Equals(object obj) { if (obj is Short2 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Short2 other) { return PackedValue.Equals(other.PackedValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } public override readonly string ToString() { Vector2 vector = ToVector2(); return FormattableString.Invariant($"Short2({vector.X:#0.##}, {vector.Y:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(Vector2 vector) { vector = Vector2.Clamp(vector, Min, Max); int num = Convert.ToInt32(Math.Round(vector.X)) & 0xFFFF; uint num2 = (uint)((Convert.ToInt32(Math.Round(vector.Y)) & 0xFFFF) << 16); return (uint)num | num2; } } public struct Short4 : IPixel, IPixel, IEquatable, IPackedVector { internal class PixelOperations : PixelOperations { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(PixelAlphaRepresentation.Unassociated), isThreadSafe: true); public override PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } } private const float MaxPos = 32767f; private const float MinNeg = -32768f; private static readonly Vector4 Max = new Vector4(32767f); private static readonly Vector4 Min = new Vector4(-32768f); public ulong PackedValue { get; set; } public Short4(float x, float y, float z, float w) : this(new Vector4(x, y, z, w)) { } public Short4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Short4 left, Short4 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Short4 left, Short4 right) { return !left.Equals(right); } public readonly PixelOperations CreatePixelOperations() { return new PixelOperations(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromScaledVector4(Vector4 vector) { vector *= 65534f; vector -= new Vector4(32767f); FromVector4(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToScaledVector4() { return (ToVector4() + new Vector4(32767f)) / 65534f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromVector4(Vector4 vector) { PackedValue = Pack(ref vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector4 ToVector4() { return new Vector4((short)(PackedValue & 0xFFFF), (short)((PackedValue >> 16) & 0xFFFF), (short)((PackedValue >> 32) & 0xFFFF), (short)((PackedValue >> 48) & 0xFFFF)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32(Argb32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24(Bgr24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32(Bgra32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32(Abgr32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551(Bgra5551 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8(L8 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16(L16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16(La16 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32(La32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24(Rgb24 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32(Rgba32 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32(ref Rgba32 dest) { dest.FromScaledVector4(ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48(Rgb48 source) { FromScaledVector4(source.ToScaledVector4()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64(Rgba64 source) { FromScaledVector4(source.ToScaledVector4()); } public override readonly bool Equals(object obj) { if (obj is Short4 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(Short4 other) { return PackedValue.Equals(other.PackedValue); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly int GetHashCode() { return PackedValue.GetHashCode(); } public override readonly string ToString() { Vector4 vector = ToVector4(); return FormattableString.Invariant($"Short4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Pack(ref Vector4 vector) { vector = Numerics.Clamp(vector, Min, Max); long num = (long)Convert.ToInt32(Math.Round(vector.X)) & 0xFFFFL; ulong num2 = ((ulong)Convert.ToInt32(Math.Round(vector.Y)) & 0xFFFFuL) << 16; ulong num3 = ((ulong)Convert.ToInt32(Math.Round(vector.Z)) & 0xFFFFuL) << 32; ulong num4 = ((ulong)Convert.ToInt32(Math.Round(vector.W)) & 0xFFFFuL) << 48; return (ulong)num | num2 | num3 | num4; } } public class PixelOperations where TPixel : unmanaged, IPixel { private static readonly Lazy LazyInfo = new Lazy(() => PixelTypeInfo.Create(), isThreadSafe: true); public static PixelOperations Instance { get; } = default(TPixel).CreatePixelOperations(); public virtual PixelTypeInfo GetPixelTypeInfo() { return LazyInfo.Value; } public virtual void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destinationPixels, PixelConversionModifiers modifiers) { Guard.NotNull(configuration, "configuration"); Vector4Converters.Default.FromVector4(sourceVectors, destinationPixels, modifiers); } public void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span destinationPixels) { FromVector4Destructive(configuration, sourceVectors, destinationPixels, PixelConversionModifiers.None); } public virtual void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationVectors, PixelConversionModifiers modifiers) { Guard.NotNull(configuration, "configuration"); Vector4Converters.Default.ToVector4(sourcePixels, destinationVectors, modifiers); } public void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationVectors) { ToVector4(configuration, sourcePixels, destinationVectors, PixelConversionModifiers.None); } public virtual void From(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) where TSourcePixel : unmanaged, IPixel { int num = sourcePixels.Length / 1024; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(1024); Span span = buffer.GetSpan(); for (int i = 0; i < num; i++) { int start = i * 1024; ReadOnlySpan sourcePixels2 = sourcePixels.Slice(start, 1024); Span destinationPixels2 = destinationPixels.Slice(start, 1024); PixelOperations.Instance.ToVector4(configuration, sourcePixels2, span, PixelConversionModifiers.Scale); FromVector4Destructive(configuration, span, destinationPixels2, PixelConversionModifiers.Scale); } int num2 = num * 1024; int num3 = sourcePixels.Length - num2; if (num3 > 0) { ReadOnlySpan sourcePixels3 = sourcePixels.Slice(num2); Span destinationPixels3 = destinationPixels.Slice(num2); span = span.Slice(0, num3); PixelOperations.Instance.ToVector4(configuration, sourcePixels3, span, PixelConversionModifiers.Scale); FromVector4Destructive(configuration, span, destinationPixels3, PixelConversionModifiers.Scale); } } public virtual void To(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) where TDestinationPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); PixelOperations.Instance.From(configuration, sourcePixels, destinationPixels); } internal virtual void PackFromRgbPlanes(Configuration configuration, ReadOnlySpan redChannel, ReadOnlySpan greenChannel, ReadOnlySpan blueChannel, Span destination) { Guard.NotNull(configuration, "configuration"); int length = redChannel.Length; GuardPackFromRgbPlanes(greenChannel, blueChannel, destination, length); Rgb24 source = default(Rgb24); ref byte reference = ref MemoryMarshal.GetReference(redChannel); ref byte reference2 = ref MemoryMarshal.GetReference(greenChannel); ref byte reference3 = ref MemoryMarshal.GetReference(blueChannel); ref TPixel reference4 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { source.R = Unsafe.Add(ref reference, i); source.G = Unsafe.Add(ref reference2, i); source.B = Unsafe.Add(ref reference3, i); Unsafe.Add(ref reference4, i).FromRgb24(source); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void GuardPackFromRgbPlanes(ReadOnlySpan greenChannel, ReadOnlySpan blueChannel, Span destination, int count) { Guard.IsTrue(greenChannel.Length == count, "greenChannel", "Channels must be of same size!"); Guard.IsTrue(blueChannel.Length == count, "blueChannel", "Channels must be of same size!"); Guard.IsTrue(destination.Length > count + 2, "destination", "'destination' must contain a padding of 3 elements!"); } public virtual void FromArgb32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref Argb32 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref Argb32 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromArgb32(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromArgb32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromArgb32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref Argb32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToArgb32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToArgb32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromAbgr32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref Abgr32 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref Abgr32 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromAbgr32(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromAbgr32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromAbgr32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToAbgr32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref Abgr32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToAbgr32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToAbgr32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromBgr24(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref Bgr24 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref Bgr24 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromBgr24(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgr24Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromBgr24(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgr24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToBgr24Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToBgr24(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromBgra32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref Bgra32 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref Bgra32 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromBgra32(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromBgra32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToBgra32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToBgra32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromL8(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref L8 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref L8 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromL8(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL8Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromL8(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToL8(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref L8 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToL8Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToL8(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromL16(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref L16 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref L16 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromL16(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromL16Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromL16(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToL16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref L16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToL16Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToL16(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromLa16(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref La16 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref La16 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromLa16(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa16Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromLa16(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToLa16(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref La16 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToLa16Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToLa16(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromLa32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref La32 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref La32 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromLa32(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromLa32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromLa32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToLa32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref La32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToLa32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToLa32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromRgb24(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref Rgb24 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref Rgb24 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromRgb24(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb24Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromRgb24(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb24 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgb24Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToRgb24(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromRgba32(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref Rgba32 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref Rgba32 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromRgba32(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromRgba32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba32 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToRgba32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromRgb48(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref Rgb48 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref Rgb48 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromRgb48(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgb48Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromRgb48(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgb48Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToRgb48(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromRgba64(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref Rgba64 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref Rgba64 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromRgba64(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromRgba64Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromRgba64(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref Rgba64 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToRgba64Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToRgba64(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public virtual void FromBgra5551(Configuration configuration, ReadOnlySpan source, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(source, destinationPixels, "destinationPixels"); ref Bgra5551 reference = ref MemoryMarshal.GetReference(source); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < source.Length; i++) { ref Bgra5551 reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromBgra5551(reference3); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBgra5551Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destinationPixels, int count) { FromBgra5551(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destinationPixels); } public virtual void ToBgra5551(Configuration configuration, ReadOnlySpan sourcePixels, Span destinationPixels) { Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, "destinationPixels"); ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref Bgra5551 reference2 = ref MemoryMarshal.GetReference(destinationPixels); for (int i = 0; i < sourcePixels.Length; i++) { ref TPixel reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i).FromScaledVector4(reference3.ToScaledVector4()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToBgra5551Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) { ToBgra5551(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); } public PixelBlender GetPixelBlender(GraphicsOptions options) { return GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode); } public virtual PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) { return alphaMode switch { PixelAlphaCompositionMode.Clear => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplyClear.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddClear.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractClear.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenClear.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenClear.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenClear.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlayClear.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightClear.Instance, _ => DefaultPixelBlenders.NormalClear.Instance, }, PixelAlphaCompositionMode.Xor => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplyXor.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddXor.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractXor.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenXor.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenXor.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenXor.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlayXor.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightXor.Instance, _ => DefaultPixelBlenders.NormalXor.Instance, }, PixelAlphaCompositionMode.Src => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplySrc.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddSrc.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractSrc.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenSrc.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenSrc.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenSrc.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlaySrc.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightSrc.Instance, _ => DefaultPixelBlenders.NormalSrc.Instance, }, PixelAlphaCompositionMode.SrcAtop => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplySrcAtop.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddSrcAtop.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractSrcAtop.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenSrcAtop.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenSrcAtop.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenSrcAtop.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlaySrcAtop.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightSrcAtop.Instance, _ => DefaultPixelBlenders.NormalSrcAtop.Instance, }, PixelAlphaCompositionMode.SrcIn => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplySrcIn.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddSrcIn.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractSrcIn.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenSrcIn.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenSrcIn.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenSrcIn.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlaySrcIn.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightSrcIn.Instance, _ => DefaultPixelBlenders.NormalSrcIn.Instance, }, PixelAlphaCompositionMode.SrcOut => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplySrcOut.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddSrcOut.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractSrcOut.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenSrcOut.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenSrcOut.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenSrcOut.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlaySrcOut.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightSrcOut.Instance, _ => DefaultPixelBlenders.NormalSrcOut.Instance, }, PixelAlphaCompositionMode.Dest => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplyDest.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddDest.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractDest.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenDest.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenDest.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenDest.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlayDest.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightDest.Instance, _ => DefaultPixelBlenders.NormalDest.Instance, }, PixelAlphaCompositionMode.DestAtop => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplyDestAtop.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddDestAtop.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractDestAtop.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenDestAtop.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenDestAtop.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenDestAtop.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlayDestAtop.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightDestAtop.Instance, _ => DefaultPixelBlenders.NormalDestAtop.Instance, }, PixelAlphaCompositionMode.DestIn => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplyDestIn.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddDestIn.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractDestIn.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenDestIn.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenDestIn.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenDestIn.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlayDestIn.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightDestIn.Instance, _ => DefaultPixelBlenders.NormalDestIn.Instance, }, PixelAlphaCompositionMode.DestOut => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplyDestOut.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddDestOut.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractDestOut.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenDestOut.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenDestOut.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenDestOut.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlayDestOut.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightDestOut.Instance, _ => DefaultPixelBlenders.NormalDestOut.Instance, }, PixelAlphaCompositionMode.DestOver => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplyDestOver.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddDestOver.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractDestOver.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenDestOver.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenDestOver.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenDestOver.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlayDestOver.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightDestOver.Instance, _ => DefaultPixelBlenders.NormalDestOver.Instance, }, _ => colorMode switch { PixelColorBlendingMode.Multiply => DefaultPixelBlenders.MultiplySrcOver.Instance, PixelColorBlendingMode.Add => DefaultPixelBlenders.AddSrcOver.Instance, PixelColorBlendingMode.Subtract => DefaultPixelBlenders.SubtractSrcOver.Instance, PixelColorBlendingMode.Screen => DefaultPixelBlenders.ScreenSrcOver.Instance, PixelColorBlendingMode.Darken => DefaultPixelBlenders.DarkenSrcOver.Instance, PixelColorBlendingMode.Lighten => DefaultPixelBlenders.LightenSrcOver.Instance, PixelColorBlendingMode.Overlay => DefaultPixelBlenders.OverlaySrcOver.Instance, PixelColorBlendingMode.HardLight => DefaultPixelBlenders.HardLightSrcOver.Instance, _ => DefaultPixelBlenders.NormalSrcOver.Instance, }, }; } } } namespace SixLabors.ImageSharp.PixelFormats.Utils { internal static class PixelConverter { public static class FromRgba32 { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToArgb32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(WXYZShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBgra32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(ZYXWShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToAbgr32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(WZYXShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToRgb24(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4Slice3(source, dest, default(XYZWShuffle4Slice3)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBgr24(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(3, 0, 1, 2)); } } public static class FromArgb32 { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToRgba32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(YZWXShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBgra32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(WZYXShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToAbgr32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(XWZYShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToRgb24(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 3, 2, 1)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBgr24(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 1, 2, 3)); } } public static class FromBgra32 { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToArgb32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(WZYXShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToRgba32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(ZYXWShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToAbgr32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(WXYZShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToRgb24(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(3, 0, 1, 2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBgr24(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4Slice3(source, dest, default(XYZWShuffle4Slice3)); } } public static class FromAbgr32 { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToArgb32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(XWZYShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToRgba32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(WZYXShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBgra32(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4(source, dest, default(YZWXShuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToRgb24(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 1, 2, 3)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBgr24(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle4Slice3(source, dest, new DefaultShuffle4Slice3(0, 3, 2, 1)); } } public static class FromRgb24 { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToRgba32(ReadOnlySpan source, Span dest) { SimdUtils.Pad3Shuffle4(source, dest, default(XYZWPad3Shuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToArgb32(ReadOnlySpan source, Span dest) { SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(2, 1, 0, 3)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBgra32(ReadOnlySpan source, Span dest) { SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(3, 0, 1, 2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToAbgr32(ReadOnlySpan source, Span dest) { SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(0, 1, 2, 3)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBgr24(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle3(source, dest, new DefaultShuffle3(0, 1, 2)); } } public static class FromBgr24 { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToArgb32(ReadOnlySpan source, Span dest) { SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(0, 1, 2, 3)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToRgba32(ReadOnlySpan source, Span dest) { SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(3, 0, 1, 2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBgra32(ReadOnlySpan source, Span dest) { SimdUtils.Pad3Shuffle4(source, dest, default(XYZWPad3Shuffle4)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToAbgr32(ReadOnlySpan source, Span dest) { SimdUtils.Pad3Shuffle4(source, dest, new DefaultPad3Shuffle4(2, 1, 0, 3)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToRgb24(ReadOnlySpan source, Span dest) { SimdUtils.Shuffle3(source, dest, new DefaultShuffle3(0, 1, 2)); } } } internal static class Vector4Converters { public static class Default { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void FromVector4(Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, "destPixels"); UnsafeFromVector4(sourceVectors, destPixels, modifiers); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToVector4(ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, "destVectors"); UnsafeToVector4(sourcePixels, destVectors, modifiers); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UnsafeFromVector4(Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { ApplyBackwardConversionModifiers(sourceVectors, modifiers); if (PixelConversionModifiersExtensions.IsDefined(modifiers, PixelConversionModifiers.Scale)) { UnsafeFromScaledVector4Core(sourceVectors, destPixels); } else { UnsafeFromVector4Core(sourceVectors, destPixels); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void UnsafeToVector4(ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { if (PixelConversionModifiersExtensions.IsDefined(modifiers, PixelConversionModifiers.Scale)) { UnsafeToScaledVector4Core(sourcePixels, destVectors); } else { UnsafeToVector4Core(sourcePixels, destVectors); } ApplyForwardConversionModifiers(destVectors, modifiers); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void UnsafeFromVector4Core(ReadOnlySpan sourceVectors, Span destPixels) where TPixel : unmanaged, IPixel { ref Vector4 reference = ref MemoryMarshal.GetReference(sourceVectors); ref Vector4 right = ref Unsafe.Add(ref reference, sourceVectors.Length); ref TPixel reference2 = ref MemoryMarshal.GetReference(destPixels); while (Unsafe.IsAddressLessThan(in reference, in right)) { reference2.FromVector4(reference); reference = ref Unsafe.Add(ref reference, 1); reference2 = ref Unsafe.Add(ref reference2, 1); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void UnsafeToVector4Core(ReadOnlySpan sourcePixels, Span destVectors) where TPixel : unmanaged, IPixel { ref TPixel reference = ref MemoryMarshal.GetReference(sourcePixels); ref TPixel right = ref Unsafe.Add(ref reference, sourcePixels.Length); ref Vector4 reference2 = ref MemoryMarshal.GetReference(destVectors); while (Unsafe.IsAddressLessThan(in reference, in right)) { reference2 = reference.ToVector4(); reference = ref Unsafe.Add(ref reference, 1); reference2 = ref Unsafe.Add(ref reference2, 1); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void UnsafeFromScaledVector4Core(ReadOnlySpan sourceVectors, Span destinationColors) where TPixel : unmanaged, IPixel { ref Vector4 reference = ref MemoryMarshal.GetReference(sourceVectors); ref Vector4 right = ref Unsafe.Add(ref reference, sourceVectors.Length); ref TPixel reference2 = ref MemoryMarshal.GetReference(destinationColors); while (Unsafe.IsAddressLessThan(in reference, in right)) { reference2.FromScaledVector4(reference); reference = ref Unsafe.Add(ref reference, 1); reference2 = ref Unsafe.Add(ref reference2, 1); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void UnsafeToScaledVector4Core(ReadOnlySpan sourceColors, Span destinationVectors) where TPixel : unmanaged, IPixel { ref TPixel reference = ref MemoryMarshal.GetReference(sourceColors); ref TPixel right = ref Unsafe.Add(ref reference, sourceColors.Length); ref Vector4 reference2 = ref MemoryMarshal.GetReference(destinationVectors); while (Unsafe.IsAddressLessThan(in reference, in right)) { reference2 = reference.ToScaledVector4(); reference = ref Unsafe.Add(ref reference, 1); reference2 = ref Unsafe.Add(ref reference2, 1); } } } public static class RgbaCompatible { private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold(); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ToVector4(Configuration configuration, PixelOperations pixelOperations, ReadOnlySpan sourcePixels, Span destVectors, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, "destVectors"); int length = sourcePixels.Length; if (length < Vector4ConversionThreshold) { Default.UnsafeToVector4(sourcePixels, destVectors, modifiers); return; } int num = length - 1; ReadOnlySpan sourcePixels2 = sourcePixels.Slice(0, num); Span span = MemoryMarshal.Cast(destVectors).Slice(3 * length + 1, num); pixelOperations.ToRgba32(configuration, sourcePixels2, span); SimdUtils.ByteToNormalizedFloat(MemoryMarshal.Cast(span), MemoryMarshal.Cast(destVectors.Slice(0, num))); destVectors[num] = sourcePixels[num].ToVector4(); ApplyForwardConversionModifiers(destVectors, modifiers); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void FromVector4(Configuration configuration, PixelOperations pixelOperations, Span sourceVectors, Span destPixels, PixelConversionModifiers modifiers) where TPixel : unmanaged, IPixel { Guard.NotNull(configuration, "configuration"); Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, "destPixels"); int length = sourceVectors.Length; if (length < Vector4ConversionThreshold) { Default.UnsafeFromVector4(sourceVectors, destPixels, modifiers); return; } ApplyBackwardConversionModifiers(sourceVectors, modifiers); using IMemoryOwner memoryOwner = configuration.MemoryAllocator.Allocate(length); Span span = memoryOwner.Memory.Span; SimdUtils.NormalizedFloatToByteSaturate(MemoryMarshal.Cast(sourceVectors), MemoryMarshal.Cast(span)); pixelOperations.FromRgba32(configuration, span, destPixels); } private static int CalculateVector4ConversionThreshold() { if (!Vector.IsHardwareAccelerated) { return int.MaxValue; } if (!SimdUtils.ExtendedIntrinsics.IsAvailable || !SimdUtils.HasVector8) { return 128; } return 256; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ApplyForwardConversionModifiers(Span vectors, PixelConversionModifiers modifiers) { if (PixelConversionModifiersExtensions.IsDefined(modifiers, PixelConversionModifiers.SRgbCompand)) { SRgbCompanding.Expand(vectors); } if (PixelConversionModifiersExtensions.IsDefined(modifiers, PixelConversionModifiers.Premultiply)) { Numerics.Premultiply(vectors); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ApplyBackwardConversionModifiers(Span vectors, PixelConversionModifiers modifiers) { if (PixelConversionModifiersExtensions.IsDefined(modifiers, PixelConversionModifiers.Premultiply)) { Numerics.UnPremultiply(vectors); } if (PixelConversionModifiersExtensions.IsDefined(modifiers, PixelConversionModifiers.SRgbCompand)) { SRgbCompanding.Compress(vectors); } } } } namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { internal static class DefaultPixelBlenders where TPixel : unmanaged, IPixel { public class NormalSrc : PixelBlender { public static NormalSrc Instance { get; } = new NormalSrc(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalSrc(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplySrc : PixelBlender { public static MultiplySrc Instance { get; } = new MultiplySrc(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplySrc(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddSrc : PixelBlender { public static AddSrc Instance { get; } = new AddSrc(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddSrc(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractSrc : PixelBlender { public static SubtractSrc Instance { get; } = new SubtractSrc(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractSrc(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenSrc : PixelBlender { public static ScreenSrc Instance { get; } = new ScreenSrc(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenSrc : PixelBlender { public static DarkenSrc Instance { get; } = new DarkenSrc(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenSrc : PixelBlender { public static LightenSrc Instance { get; } = new LightenSrc(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenSrc(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlaySrc : PixelBlender { public static OverlaySrc Instance { get; } = new OverlaySrc(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlaySrc(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightSrc : PixelBlender { public static HardLightSrc Instance { get; } = new HardLightSrc(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightSrc(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalSrcAtop : PixelBlender { public static NormalSrcAtop Instance { get; } = new NormalSrcAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplySrcAtop : PixelBlender { public static MultiplySrcAtop Instance { get; } = new MultiplySrcAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddSrcAtop : PixelBlender { public static AddSrcAtop Instance { get; } = new AddSrcAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractSrcAtop : PixelBlender { public static SubtractSrcAtop Instance { get; } = new SubtractSrcAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenSrcAtop : PixelBlender { public static ScreenSrcAtop Instance { get; } = new ScreenSrcAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenSrcAtop : PixelBlender { public static DarkenSrcAtop Instance { get; } = new DarkenSrcAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenSrcAtop : PixelBlender { public static LightenSrcAtop Instance { get; } = new LightenSrcAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlaySrcAtop : PixelBlender { public static OverlaySrcAtop Instance { get; } = new OverlaySrcAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlaySrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightSrcAtop : PixelBlender { public static HardLightSrcAtop Instance { get; } = new HardLightSrcAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightSrcAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalSrcOver : PixelBlender { public static NormalSrcOver Instance { get; } = new NormalSrcOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplySrcOver : PixelBlender { public static MultiplySrcOver Instance { get; } = new MultiplySrcOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddSrcOver : PixelBlender { public static AddSrcOver Instance { get; } = new AddSrcOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractSrcOver : PixelBlender { public static SubtractSrcOver Instance { get; } = new SubtractSrcOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenSrcOver : PixelBlender { public static ScreenSrcOver Instance { get; } = new ScreenSrcOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenSrcOver : PixelBlender { public static DarkenSrcOver Instance { get; } = new DarkenSrcOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenSrcOver : PixelBlender { public static LightenSrcOver Instance { get; } = new LightenSrcOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlaySrcOver : PixelBlender { public static OverlaySrcOver Instance { get; } = new OverlaySrcOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlaySrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightSrcOver : PixelBlender { public static HardLightSrcOver Instance { get; } = new HardLightSrcOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightSrcOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalSrcIn : PixelBlender { public static NormalSrcIn Instance { get; } = new NormalSrcIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplySrcIn : PixelBlender { public static MultiplySrcIn Instance { get; } = new MultiplySrcIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddSrcIn : PixelBlender { public static AddSrcIn Instance { get; } = new AddSrcIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractSrcIn : PixelBlender { public static SubtractSrcIn Instance { get; } = new SubtractSrcIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenSrcIn : PixelBlender { public static ScreenSrcIn Instance { get; } = new ScreenSrcIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenSrcIn : PixelBlender { public static DarkenSrcIn Instance { get; } = new DarkenSrcIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenSrcIn : PixelBlender { public static LightenSrcIn Instance { get; } = new LightenSrcIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlaySrcIn : PixelBlender { public static OverlaySrcIn Instance { get; } = new OverlaySrcIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlaySrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightSrcIn : PixelBlender { public static HardLightSrcIn Instance { get; } = new HardLightSrcIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightSrcIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalSrcOut : PixelBlender { public static NormalSrcOut Instance { get; } = new NormalSrcOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplySrcOut : PixelBlender { public static MultiplySrcOut Instance { get; } = new MultiplySrcOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddSrcOut : PixelBlender { public static AddSrcOut Instance { get; } = new AddSrcOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractSrcOut : PixelBlender { public static SubtractSrcOut Instance { get; } = new SubtractSrcOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenSrcOut : PixelBlender { public static ScreenSrcOut Instance { get; } = new ScreenSrcOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenSrcOut : PixelBlender { public static DarkenSrcOut Instance { get; } = new DarkenSrcOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenSrcOut : PixelBlender { public static LightenSrcOut Instance { get; } = new LightenSrcOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlaySrcOut : PixelBlender { public static OverlaySrcOut Instance { get; } = new OverlaySrcOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlaySrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightSrcOut : PixelBlender { public static HardLightSrcOut Instance { get; } = new HardLightSrcOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightSrcOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalDest : PixelBlender { public static NormalDest Instance { get; } = new NormalDest(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalDest(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplyDest : PixelBlender { public static MultiplyDest Instance { get; } = new MultiplyDest(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyDest(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddDest : PixelBlender { public static AddDest Instance { get; } = new AddDest(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddDest(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractDest : PixelBlender { public static SubtractDest Instance { get; } = new SubtractDest(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractDest(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenDest : PixelBlender { public static ScreenDest Instance { get; } = new ScreenDest(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenDest(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenDest : PixelBlender { public static DarkenDest Instance { get; } = new DarkenDest(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenDest(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenDest : PixelBlender { public static LightenDest Instance { get; } = new LightenDest(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenDest(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlayDest : PixelBlender { public static OverlayDest Instance { get; } = new OverlayDest(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlayDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayDest(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightDest : PixelBlender { public static HardLightDest Instance { get; } = new HardLightDest(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightDest(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightDest(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalDestAtop : PixelBlender { public static NormalDestAtop Instance { get; } = new NormalDestAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplyDestAtop : PixelBlender { public static MultiplyDestAtop Instance { get; } = new MultiplyDestAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddDestAtop : PixelBlender { public static AddDestAtop Instance { get; } = new AddDestAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractDestAtop : PixelBlender { public static SubtractDestAtop Instance { get; } = new SubtractDestAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenDestAtop : PixelBlender { public static ScreenDestAtop Instance { get; } = new ScreenDestAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenDestAtop : PixelBlender { public static DarkenDestAtop Instance { get; } = new DarkenDestAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenDestAtop : PixelBlender { public static LightenDestAtop Instance { get; } = new LightenDestAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlayDestAtop : PixelBlender { public static OverlayDestAtop Instance { get; } = new OverlayDestAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightDestAtop : PixelBlender { public static HardLightDestAtop Instance { get; } = new HardLightDestAtop(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightDestAtop(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalDestOver : PixelBlender { public static NormalDestOver Instance { get; } = new NormalDestOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplyDestOver : PixelBlender { public static MultiplyDestOver Instance { get; } = new MultiplyDestOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddDestOver : PixelBlender { public static AddDestOver Instance { get; } = new AddDestOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractDestOver : PixelBlender { public static SubtractDestOver Instance { get; } = new SubtractDestOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenDestOver : PixelBlender { public static ScreenDestOver Instance { get; } = new ScreenDestOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenDestOver : PixelBlender { public static DarkenDestOver Instance { get; } = new DarkenDestOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenDestOver : PixelBlender { public static LightenDestOver Instance { get; } = new LightenDestOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlayDestOver : PixelBlender { public static OverlayDestOver Instance { get; } = new OverlayDestOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightDestOver : PixelBlender { public static HardLightDestOver Instance { get; } = new HardLightDestOver(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightDestOver(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalDestIn : PixelBlender { public static NormalDestIn Instance { get; } = new NormalDestIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplyDestIn : PixelBlender { public static MultiplyDestIn Instance { get; } = new MultiplyDestIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddDestIn : PixelBlender { public static AddDestIn Instance { get; } = new AddDestIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractDestIn : PixelBlender { public static SubtractDestIn Instance { get; } = new SubtractDestIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenDestIn : PixelBlender { public static ScreenDestIn Instance { get; } = new ScreenDestIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenDestIn : PixelBlender { public static DarkenDestIn Instance { get; } = new DarkenDestIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenDestIn : PixelBlender { public static LightenDestIn Instance { get; } = new LightenDestIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlayDestIn : PixelBlender { public static OverlayDestIn Instance { get; } = new OverlayDestIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightDestIn : PixelBlender { public static HardLightDestIn Instance { get; } = new HardLightDestIn(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightDestIn(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalDestOut : PixelBlender { public static NormalDestOut Instance { get; } = new NormalDestOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplyDestOut : PixelBlender { public static MultiplyDestOut Instance { get; } = new MultiplyDestOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddDestOut : PixelBlender { public static AddDestOut Instance { get; } = new AddDestOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractDestOut : PixelBlender { public static SubtractDestOut Instance { get; } = new SubtractDestOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenDestOut : PixelBlender { public static ScreenDestOut Instance { get; } = new ScreenDestOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenDestOut : PixelBlender { public static DarkenDestOut Instance { get; } = new DarkenDestOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenDestOut : PixelBlender { public static LightenDestOut Instance { get; } = new LightenDestOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlayDestOut : PixelBlender { public static OverlayDestOut Instance { get; } = new OverlayDestOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightDestOut : PixelBlender { public static HardLightDestOut Instance { get; } = new HardLightDestOut(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightDestOut(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalClear : PixelBlender { public static NormalClear Instance { get; } = new NormalClear(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalClear(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplyClear : PixelBlender { public static MultiplyClear Instance { get; } = new MultiplyClear(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyClear(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddClear : PixelBlender { public static AddClear Instance { get; } = new AddClear(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddClear(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractClear : PixelBlender { public static SubtractClear Instance { get; } = new SubtractClear(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractClear(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenClear : PixelBlender { public static ScreenClear Instance { get; } = new ScreenClear(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenClear(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenClear : PixelBlender { public static DarkenClear Instance { get; } = new DarkenClear(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenClear(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenClear : PixelBlender { public static LightenClear Instance { get; } = new LightenClear(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenClear(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlayClear : PixelBlender { public static OverlayClear Instance { get; } = new OverlayClear(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayClear(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightClear : PixelBlender { public static HardLightClear Instance { get; } = new HardLightClear(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightClear(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class NormalXor : PixelBlender { public static NormalXor Instance { get; } = new NormalXor(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.NormalXor(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class MultiplyXor : PixelBlender { public static MultiplyXor Instance { get; } = new MultiplyXor(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.MultiplyXor(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class AddXor : PixelBlender { public static AddXor Instance { get; } = new AddXor(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.AddXor(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class SubtractXor : PixelBlender { public static SubtractXor Instance { get; } = new SubtractXor(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.SubtractXor(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class ScreenXor : PixelBlender { public static ScreenXor Instance { get; } = new ScreenXor(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.ScreenXor(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class DarkenXor : PixelBlender { public static DarkenXor Instance { get; } = new DarkenXor(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.DarkenXor(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class LightenXor : PixelBlender { public static LightenXor Instance { get; } = new LightenXor(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.LightenXor(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class OverlayXor : PixelBlender { public static OverlayXor Instance { get; } = new OverlayXor(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.OverlayXor(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } public class HardLightXor : PixelBlender { public static HardLightXor Instance { get; } = new HardLightXor(); public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel result = default(TPixel); result.FromScaledVector4(PorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), Numerics.Clamp(amount, 0f, 1f))); return result; } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) { amount = Numerics.Clamp(amount, 0f, 1f); for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], amount); } } protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) { for (int i = 0; i < destination.Length; i++) { destination[i] = PorterDuffFunctions.HardLightXor(background[i], source[i], Numerics.Clamp(amount[i], 0f, 1f)); } } } } internal static class PorterDuffFunctions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Normal(Vector4 backdrop, Vector4 source) { return source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Multiply(Vector4 backdrop, Vector4 source) { return backdrop * source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Add(Vector4 backdrop, Vector4 source) { return Vector4.Min(Vector4.One, backdrop + source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Subtract(Vector4 backdrop, Vector4 source) { return Vector4.Max(Vector4.Zero, backdrop - source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Screen(Vector4 backdrop, Vector4 source) { return Vector4.One - (Vector4.One - backdrop) * (Vector4.One - source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Darken(Vector4 backdrop, Vector4 source) { return Vector4.Min(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Lighten(Vector4 backdrop, Vector4 source) { return Vector4.Max(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Overlay(Vector4 backdrop, Vector4 source) { float x = OverlayValueFunction(backdrop.X, source.X); float y = OverlayValueFunction(backdrop.Y, source.Y); float z = OverlayValueFunction(backdrop.Z, source.Z); return Vector4.Min(Vector4.One, new Vector4(x, y, z, 0f)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLight(Vector4 backdrop, Vector4 source) { float x = OverlayValueFunction(source.X, backdrop.X); float y = OverlayValueFunction(source.Y, backdrop.Y); float z = OverlayValueFunction(source.Z, backdrop.Z); return Vector4.Min(Vector4.One, new Vector4(x, y, z, 0f)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float OverlayValueFunction(float backdrop, float source) { if (!(backdrop <= 0.5f)) { return 1f - 2f * (1f - source) * (1f - backdrop); } return 2f * backdrop * source; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Over(Vector4 destination, Vector4 source, Vector4 blend) { float num = destination.W * source.W; float num2 = destination.W - num; float num3 = source.W - num; float num4 = num2 + source.W; Vector4 result = destination * num2 + source * num3 + blend * num; result /= MathF.Max(num4, Constants.Epsilon); result.W = num4; return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Atop(Vector4 destination, Vector4 source, Vector4 blend) { float num = destination.W * source.W; float num2 = destination.W - num; float w = destination.W; Vector4 result = destination * num2 + blend * num; result /= MathF.Max(w, Constants.Epsilon); result.W = w; return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 In(Vector4 destination, Vector4 source) { float num = destination.W * source.W; Vector4 result = source * num; result /= MathF.Max(num, Constants.Epsilon); result.W = num; return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Out(Vector4 destination, Vector4 source) { float num = (1f - destination.W) * source.W; Vector4 result = source * num; result /= MathF.Max(num, Constants.Epsilon); result.W = num; return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Xor(Vector4 destination, Vector4 source) { float num = 1f - destination.W; float num2 = 1f - source.W; float num3 = source.W * num + destination.W * num2; Vector4 result = source.W * source * num + destination.W * destination * num2; result /= MathF.Max(num3, Constants.Epsilon); result.W = num3; return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector4 Clear(Vector4 backdrop, Vector4 source) { return Vector4.Zero; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrc(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return source; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(backdrop, source, Normal(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(backdrop, source, Normal(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(source, backdrop, Normal(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(source, backdrop, Normal(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalXor(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Xor(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalClear(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Clear(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDest(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalClear(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalXor(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(NormalXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrc(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return source; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(backdrop, source, Multiply(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(backdrop, source, Multiply(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(source, backdrop, Multiply(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(source, backdrop, Multiply(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Xor(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyClear(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Clear(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrc(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDest(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplyDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplyDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplyDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplyDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplyDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyClear(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplyClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyXor(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(MultiplyXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrc(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return source; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(backdrop, source, Add(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(backdrop, source, Add(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(source, backdrop, Add(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(source, backdrop, Add(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddXor(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Xor(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddClear(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Clear(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDest(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddClear(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddXor(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(AddXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrc(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return source; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(backdrop, source, Subtract(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(backdrop, source, Subtract(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(source, backdrop, Subtract(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(source, backdrop, Subtract(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractXor(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Xor(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractClear(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Clear(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDest(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractClear(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractXor(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(SubtractXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrc(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return source; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(backdrop, source, Screen(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(backdrop, source, Screen(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(source, backdrop, Screen(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(source, backdrop, Screen(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenXor(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Xor(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenClear(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Clear(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDest(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenClear(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenXor(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(ScreenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrc(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return source; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(backdrop, source, Darken(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(backdrop, source, Darken(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(source, backdrop, Darken(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(source, backdrop, Darken(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenXor(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Xor(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenClear(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Clear(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDest(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenClear(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenXor(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(DarkenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrc(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return source; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(backdrop, source, Lighten(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(backdrop, source, Lighten(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(source, backdrop, Lighten(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(source, backdrop, Lighten(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenXor(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Xor(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenClear(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Clear(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDest(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenClear(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenXor(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(LightenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrc(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return source; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(backdrop, source, Overlay(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(backdrop, source, Overlay(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(source, backdrop, Overlay(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(source, backdrop, Overlay(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayXor(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Xor(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayClear(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Clear(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrc(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlaySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlaySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlaySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlaySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlaySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDest(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlayDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlayDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlayDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlayDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlayDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayClear(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlayClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayXor(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(OverlayXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrc(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return source; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(backdrop, source, HardLight(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(backdrop, source, HardLight(backdrop, source)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestAtop(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Atop(source, backdrop, HardLight(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestOver(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Over(source, backdrop, HardLight(source, backdrop)); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return In(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestOut(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Out(source, backdrop); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightXor(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Xor(backdrop, source); } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightClear(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; return Clear(backdrop, source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDest(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightClear(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightXor(TPixel backdrop, TPixel source, float opacity) where TPixel : unmanaged, IPixel { opacity = Numerics.Clamp(opacity, 0f, 1f); TPixel result = default(TPixel); result.FromScaledVector4(HardLightXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return result; } } } namespace SixLabors.ImageSharp.Metadata { public enum FrameDecodingMode { All, First } public sealed class ImageFrameMetadata : IDeepCloneable { private readonly Dictionary formatMetadata = new Dictionary(); public ExifProfile ExifProfile { get; set; } public XmpProfile XmpProfile { get; set; } public IccProfile IccProfile { get; set; } public IptcProfile IptcProfile { get; set; } internal ImageFrameMetadata() { } internal ImageFrameMetadata(ImageFrameMetadata other) { foreach (KeyValuePair formatMetadatum in other.formatMetadata) { formatMetadata.Add(formatMetadatum.Key, formatMetadatum.Value.DeepClone()); } ExifProfile = other.ExifProfile?.DeepClone(); IccProfile = other.IccProfile?.DeepClone(); IptcProfile = other.IptcProfile?.DeepClone(); XmpProfile = other.XmpProfile?.DeepClone(); } public ImageFrameMetadata DeepClone() { return new ImageFrameMetadata(this); } public TFormatFrameMetadata GetFormatMetadata(IImageFormat key) where TFormatMetadata : class where TFormatFrameMetadata : class, IDeepCloneable { if (formatMetadata.TryGetValue(key, out var value)) { return (TFormatFrameMetadata)value; } TFormatFrameMetadata val = key.CreateDefaultFormatFrameMetadata(); formatMetadata[key] = val; return val; } } public sealed class ImageMetadata : IDeepCloneable { public const double DefaultHorizontalResolution = 96.0; public const double DefaultVerticalResolution = 96.0; public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch; private readonly Dictionary formatMetadata = new Dictionary(); private double horizontalResolution; private double verticalResolution; public double HorizontalResolution { get { return horizontalResolution; } set { if (value > 0.0) { horizontalResolution = value; } } } public double VerticalResolution { get { return verticalResolution; } set { if (value > 0.0) { verticalResolution = value; } } } public PixelResolutionUnit ResolutionUnits { get; set; } public ExifProfile ExifProfile { get; set; } public XmpProfile XmpProfile { get; set; } public IccProfile IccProfile { get; set; } public IptcProfile IptcProfile { get; set; } public ImageMetadata() { horizontalResolution = 96.0; verticalResolution = 96.0; ResolutionUnits = PixelResolutionUnit.PixelsPerInch; } private ImageMetadata(ImageMetadata other) { HorizontalResolution = other.HorizontalResolution; VerticalResolution = other.VerticalResolution; ResolutionUnits = other.ResolutionUnits; foreach (KeyValuePair formatMetadatum in other.formatMetadata) { formatMetadata.Add(formatMetadatum.Key, formatMetadatum.Value.DeepClone()); } ExifProfile = other.ExifProfile?.DeepClone(); IccProfile = other.IccProfile?.DeepClone(); IptcProfile = other.IptcProfile?.DeepClone(); XmpProfile = other.XmpProfile?.DeepClone(); } public TFormatMetadata GetFormatMetadata(IImageFormat key) where TFormatMetadata : class, IDeepCloneable { if (formatMetadata.TryGetValue(key, out var value)) { return (TFormatMetadata)value; } TFormatMetadata val = key.CreateDefaultFormatMetadata(); formatMetadata[key] = val; return val; } public ImageMetadata DeepClone() { return new ImageMetadata(this); } internal void SyncProfiles() { ExifProfile?.Sync(this); } } public enum PixelResolutionUnit : byte { AspectRatio, PixelsPerInch, PixelsPerCentimeter, PixelsPerMeter } } namespace SixLabors.ImageSharp.Metadata.Profiles.Xmp { public sealed class XmpProfile : IDeepCloneable { internal byte[] Data { get; private set; } public XmpProfile() : this((byte[])null) { } public XmpProfile(byte[] data) { Data = data; } private XmpProfile(XmpProfile other) { Guard.NotNull(other, "other"); Data = other.Data; } public XDocument GetDocument() { byte[] data = Data; if (data == null) { return null; } int num = data.Length; for (int num2 = num - 1; num2 > 0; num2--) { byte b = data[num2]; if ((b == 0 || b == 15) ? true : false) { num--; } } using MemoryStream stream = new MemoryStream(data, 0, num); using StreamReader textReader = new StreamReader(stream, Encoding.UTF8); return XDocument.Load((TextReader)textReader); } public byte[] ToByteArray() { byte[] array = new byte[Data.Length]; Data.AsSpan().CopyTo(array); return array; } public XmpProfile DeepClone() { return new XmpProfile(this); } } } namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc { public sealed class IptcProfile : IDeepCloneable { private Collection values; private const byte IptcTagMarkerByte = 28; private const uint MaxStandardDataTagSize = 32767u; public byte[] Data { get; private set; } public IEnumerable Values { get { Initialize(); return values; } } public IptcProfile() : this((byte[])null) { } public IptcProfile(byte[] data) { Data = data; Initialize(); } private IptcProfile(IptcProfile other) { Guard.NotNull(other, "other"); if (other.values != null) { values = new Collection(); foreach (IptcValue value in other.Values) { values.Add(value.DeepClone()); } } if (other.Data != null) { Data = new byte[other.Data.Length]; other.Data.AsSpan().CopyTo(Data); } } public IptcProfile DeepClone() { return new IptcProfile(this); } public List GetValues(IptcTag tag) { List list = new List(); foreach (IptcValue value in Values) { if (value.Tag == tag) { list.Add(value); } } return list; } public bool RemoveValue(IptcTag tag) { Initialize(); bool result = false; for (int num = values.Count - 1; num >= 0; num--) { if (values[num].Tag == tag) { values.RemoveAt(num); result = true; } } return result; } public bool RemoveValue(IptcTag tag, string value) { Initialize(); bool result = false; for (int num = values.Count - 1; num >= 0; num--) { if (values[num].Tag == tag && values[num].Value.Equals(value)) { values.RemoveAt(num); result = true; } } return result; } public void SetEncoding(Encoding encoding) { Guard.NotNull(encoding, "encoding"); foreach (IptcValue value in Values) { value.Encoding = encoding; } } public void SetValue(IptcTag tag, Encoding encoding, string value, bool strict = true) { Guard.NotNull(encoding, "encoding"); Guard.NotNull(value, "value"); if (!tag.IsRepeatable()) { foreach (IptcValue value2 in Values) { if (value2.Tag == tag) { value2.Strict = strict; value2.Encoding = encoding; value2.Value = value; return; } } } values.Add(new IptcValue(tag, encoding, value, strict)); } public void SetDateTimeValue(IptcTag tag, DateTimeOffset dateTimeOffset) { if (!tag.IsDate() && !tag.IsTime()) { throw new ArgumentException("iptc tag is not a time or date type"); } string value = (tag.IsDate() ? dateTimeOffset.ToString("yyyyMMdd", CultureInfo.InvariantCulture) : dateTimeOffset.ToString("HHmmsszzzz", CultureInfo.InvariantCulture).Replace(":", string.Empty)); SetValue(tag, Encoding.UTF8, value); } public void SetValue(IptcTag tag, string value, bool strict = true) { SetValue(tag, Encoding.UTF8, value, strict); } public void UpdateData() { int num = 0; foreach (IptcValue value in Values) { num += value.Length + 5; } Data = new byte[num]; int num2 = 0; foreach (IptcValue value2 in Values) { Data[num2++] = 28; Data[num2++] = 2; Data[num2++] = (byte)value2.Tag; Data[num2++] = (byte)(value2.Length >> 8); Data[num2++] = (byte)value2.Length; if (value2.Length > 0) { Buffer.BlockCopy(value2.ToByteArray(), 0, Data, num2, value2.Length); num2 += value2.Length; } } } private void Initialize() { if (values != null) { return; } values = new Collection(); if (Data == null || Data[0] != 28) { return; } int num = 0; while (num < Data.Length - 4) { bool num2 = Data[num++] == 28; byte b = Data[num++]; bool flag = b >= 1 && b <= 9; IptcTag tag = (IptcTag)Data[num++]; bool flag2 = num2 && flag; uint num3 = BinaryPrimitives.ReadUInt16BigEndian(Data.AsSpan(num, 2)); num += 2; if (num3 <= 32767) { if (flag2 && num3 != 0 && num <= Data.Length - num3) { byte[] array = new byte[num3]; Buffer.BlockCopy(Data, num, array, 0, (int)num3); values.Add(new IptcValue(tag, array, strict: false)); } num += (int)num3; continue; } break; } } } public enum IptcTag { Unknown = -1, RecordVersion = 0, ObjectType = 3, ObjectAttribute = 4, Name = 5, EditStatus = 7, EditorialUpdate = 8, Urgency = 10, SubjectReference = 12, Category = 15, SupplementalCategories = 20, FixtureIdentifier = 22, Keywords = 25, LocationCode = 26, LocationName = 27, ReleaseDate = 30, ReleaseTime = 35, ExpirationDate = 37, ExpirationTime = 38, SpecialInstructions = 40, ActionAdvised = 42, ReferenceService = 45, ReferenceDate = 47, ReferenceNumber = 50, CreatedDate = 55, CreatedTime = 60, DigitalCreationDate = 62, DigitalCreationTime = 63, OriginatingProgram = 65, ProgramVersion = 70, ObjectCycle = 75, Byline = 80, BylineTitle = 85, City = 90, SubLocation = 92, ProvinceState = 95, CountryCode = 100, Country = 101, OriginalTransmissionReference = 103, Headline = 105, Credit = 110, Source = 115, CopyrightNotice = 116, Contact = 118, Caption = 120, LocalCaption = 121, CaptionWriter = 122, ImageType = 130, ImageOrientation = 131, CustomField1 = 200, CustomField2 = 201, CustomField3 = 202, CustomField4 = 203, CustomField5 = 204, CustomField6 = 205, CustomField7 = 206, CustomField8 = 207, CustomField9 = 208, CustomField10 = 209, CustomField11 = 210, CustomField12 = 211, CustomField13 = 212, CustomField14 = 213, CustomField15 = 214, CustomField16 = 215, CustomField17 = 216, CustomField18 = 217, CustomField19 = 218, CustomField20 = 219 } public static class IptcTagExtensions { public static int MaxLength(this IptcTag tag) { return tag switch { IptcTag.RecordVersion => 2, IptcTag.ObjectType => 67, IptcTag.ObjectAttribute => 68, IptcTag.Name => 64, IptcTag.EditStatus => 64, IptcTag.EditorialUpdate => 2, IptcTag.Urgency => 1, IptcTag.SubjectReference => 236, IptcTag.Category => 3, IptcTag.SupplementalCategories => 32, IptcTag.FixtureIdentifier => 32, IptcTag.Keywords => 64, IptcTag.LocationCode => 3, IptcTag.LocationName => 64, IptcTag.ReleaseDate => 8, IptcTag.ReleaseTime => 11, IptcTag.ExpirationDate => 8, IptcTag.ExpirationTime => 11, IptcTag.SpecialInstructions => 256, IptcTag.ActionAdvised => 2, IptcTag.ReferenceService => 10, IptcTag.ReferenceDate => 8, IptcTag.ReferenceNumber => 8, IptcTag.CreatedDate => 8, IptcTag.CreatedTime => 11, IptcTag.DigitalCreationDate => 8, IptcTag.DigitalCreationTime => 11, IptcTag.OriginatingProgram => 32, IptcTag.ProgramVersion => 10, IptcTag.ObjectCycle => 1, IptcTag.Byline => 32, IptcTag.BylineTitle => 32, IptcTag.City => 32, IptcTag.SubLocation => 32, IptcTag.ProvinceState => 32, IptcTag.CountryCode => 3, IptcTag.Country => 64, IptcTag.OriginalTransmissionReference => 32, IptcTag.Headline => 256, IptcTag.Credit => 32, IptcTag.Source => 32, IptcTag.CopyrightNotice => 128, IptcTag.Contact => 128, IptcTag.Caption => 2000, IptcTag.CaptionWriter => 32, IptcTag.ImageType => 2, IptcTag.ImageOrientation => 1, _ => 256, }; } public static bool IsRepeatable(this IptcTag tag) { switch (tag) { case IptcTag.RecordVersion: case IptcTag.ObjectType: case IptcTag.Name: case IptcTag.EditStatus: case IptcTag.EditorialUpdate: case IptcTag.Urgency: case IptcTag.Category: case IptcTag.FixtureIdentifier: case IptcTag.ReleaseDate: case IptcTag.ReleaseTime: case IptcTag.ExpirationDate: case IptcTag.ExpirationTime: case IptcTag.SpecialInstructions: case IptcTag.ActionAdvised: case IptcTag.CreatedDate: case IptcTag.CreatedTime: case IptcTag.DigitalCreationDate: case IptcTag.DigitalCreationTime: case IptcTag.OriginatingProgram: case IptcTag.ProgramVersion: case IptcTag.ObjectCycle: case IptcTag.City: case IptcTag.SubLocation: case IptcTag.ProvinceState: case IptcTag.CountryCode: case IptcTag.Country: case IptcTag.OriginalTransmissionReference: case IptcTag.Headline: case IptcTag.Credit: case IptcTag.Source: case IptcTag.CopyrightNotice: case IptcTag.Caption: case IptcTag.ImageType: case IptcTag.ImageOrientation: return false; default: return true; } } public static bool IsDate(this IptcTag tag) { switch (tag) { case IptcTag.ReleaseDate: case IptcTag.ExpirationDate: case IptcTag.ReferenceDate: case IptcTag.CreatedDate: case IptcTag.DigitalCreationDate: return true; default: return false; } } public static bool IsTime(this IptcTag tag) { switch (tag) { case IptcTag.ReleaseTime: case IptcTag.ExpirationTime: case IptcTag.CreatedTime: case IptcTag.DigitalCreationTime: return true; default: return false; } } } public sealed class IptcValue : IDeepCloneable { private byte[] data = Array.Empty(); private Encoding encoding; public Encoding Encoding { get { return encoding; } set { if (value != null) { encoding = value; } } } public IptcTag Tag { get; } public bool Strict { get; set; } public string Value { get { return encoding.GetString(data); } set { if (string.IsNullOrEmpty(value)) { data = Array.Empty(); return; } int num = Tag.MaxLength(); byte[] bytes; if (Strict && value.Length > num) { string s = value.Substring(0, num); bytes = encoding.GetBytes(s); if (bytes.Length > num) { throw new ArgumentException($"The iptc value exceeds the limit of {num} bytes for the tag {Tag}"); } } else { bytes = encoding.GetBytes(value); } data = bytes; } } public int Length => data.Length; internal IptcValue(IptcValue other) { if (other.data != null) { data = new byte[other.data.Length]; other.data.AsSpan().CopyTo(data); } if (other.Encoding != null) { Encoding = (Encoding)other.Encoding.Clone(); } Tag = other.Tag; Strict = other.Strict; } internal IptcValue(IptcTag tag, byte[] value, bool strict) { Guard.NotNull(value, "value"); Strict = strict; Tag = tag; data = value; encoding = Encoding.UTF8; } internal IptcValue(IptcTag tag, Encoding encoding, string value, bool strict) { Strict = strict; Tag = tag; this.encoding = encoding; Value = value; } internal IptcValue(IptcTag tag, string value, bool strict) { Strict = strict; Tag = tag; encoding = Encoding.UTF8; Value = value; } public IptcValue DeepClone() { return new IptcValue(this); } public override bool Equals(object obj) { if (this == obj) { return true; } return Equals(obj as IptcValue); } public bool Equals(IptcValue other) { if (other == null) { return false; } if (this == other) { return true; } if (Tag != other.Tag) { return false; } if (data.Length != other.data.Length) { return false; } for (int i = 0; i < data.Length; i++) { if (data[i] != other.data[i]) { return false; } } return true; } public override int GetHashCode() { return HashCode.Combine(data, Tag); } public byte[] ToByteArray() { byte[] array = new byte[data.Length]; data.CopyTo(array, 0); return array; } public override string ToString() { return Value; } public string ToString(Encoding encoding) { Guard.NotNull(encoding, "encoding"); return encoding.GetString(data); } } } namespace SixLabors.ImageSharp.Metadata.Profiles.Icc { internal abstract class IccCurveSegment : IEquatable { public IccCurveSegmentSignature Signature { get; } protected IccCurveSegment(IccCurveSegmentSignature signature) { Signature = signature; } public virtual bool Equals(IccCurveSegment other) { if (other == null) { return false; } if (this == other) { return true; } return Signature == other.Signature; } } internal sealed class IccFormulaCurveElement : IccCurveSegment, IEquatable { public IccFormulaCurveType Type { get; } public float Gamma { get; } public float A { get; } public float B { get; } public float C { get; } public float D { get; } public float E { get; } public IccFormulaCurveElement(IccFormulaCurveType type, float gamma, float a, float b, float c, float d, float e) : base(IccCurveSegmentSignature.FormulaCurve) { Type = type; Gamma = gamma; A = a; B = b; C = c; D = d; E = e; } public override bool Equals(IccCurveSegment other) { if (base.Equals(other) && other is IccFormulaCurveElement iccFormulaCurveElement) { if (Type == iccFormulaCurveElement.Type && Gamma == iccFormulaCurveElement.Gamma && A == iccFormulaCurveElement.A && B == iccFormulaCurveElement.B && C == iccFormulaCurveElement.C && D == iccFormulaCurveElement.D) { return E == iccFormulaCurveElement.E; } return false; } return false; } public bool Equals(IccFormulaCurveElement other) { return Equals((IccCurveSegment)other); } } internal sealed class IccOneDimensionalCurve : IEquatable { public float[] BreakPoints { get; } public IccCurveSegment[] Segments { get; } public IccOneDimensionalCurve(float[] breakPoints, IccCurveSegment[] segments) { Guard.NotNull(breakPoints, "breakPoints"); Guard.NotNull(segments, "segments"); Guard.IsTrue(breakPoints.Length == segments.Length - 1, "breakPoints,segments", "Number of BreakPoints must be one less than number of Segments"); BreakPoints = breakPoints; Segments = segments; } public bool Equals(IccOneDimensionalCurve other) { if (other == null) { return false; } if (this == other) { return true; } if (BreakPoints.AsSpan().SequenceEqual(other.BreakPoints)) { return Segments.AsSpan().SequenceEqual(other.Segments); } return false; } } internal sealed class IccParametricCurve : IEquatable { public IccParametricCurveType Type { get; } public float G { get; } public float A { get; } public float B { get; } public float C { get; } public float D { get; } public float E { get; } public float F { get; } public IccParametricCurve(float g) : this(IccParametricCurveType.Type1, g, 0f, 0f, 0f, 0f, 0f, 0f) { } public IccParametricCurve(float g, float a, float b) : this(IccParametricCurveType.Cie122_1996, g, a, b, 0f, 0f, 0f, 0f) { } public IccParametricCurve(float g, float a, float b, float c) : this(IccParametricCurveType.Iec61966_3, g, a, b, c, 0f, 0f, 0f) { } public IccParametricCurve(float g, float a, float b, float c, float d) : this(IccParametricCurveType.SRgb, g, a, b, c, d, 0f, 0f) { } public IccParametricCurve(float g, float a, float b, float c, float d, float e, float f) : this(IccParametricCurveType.Type5, g, a, b, c, d, e, f) { } private IccParametricCurve(IccParametricCurveType type, float g, float a, float b, float c, float d, float e, float f) { Type = type; G = g; A = a; B = b; C = c; D = d; E = e; F = f; } public bool Equals(IccParametricCurve other) { if (other == null) { return false; } if (this == other) { return true; } if (Type == other.Type && G.Equals(other.G) && A.Equals(other.A) && B.Equals(other.B) && C.Equals(other.C) && D.Equals(other.D) && E.Equals(other.E)) { return F.Equals(other.F); } return false; } public override bool Equals(object obj) { if (obj is IccParametricCurve other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(Type, G.GetHashCode(), A.GetHashCode(), B.GetHashCode(), C.GetHashCode(), D.GetHashCode(), E.GetHashCode(), F.GetHashCode()); } } internal sealed class IccResponseCurve : IEquatable { public IccCurveMeasurementEncodings CurveType { get; } public Vector3[] XyzValues { get; } public IccResponseNumber[][] ResponseArrays { get; } public IccResponseCurve(IccCurveMeasurementEncodings curveType, Vector3[] xyzValues, IccResponseNumber[][] responseArrays) { Guard.NotNull(xyzValues, "xyzValues"); Guard.NotNull(responseArrays, "responseArrays"); Guard.IsTrue(xyzValues.Length == responseArrays.Length, "xyzValues,responseArrays", "Arrays must have same length"); Guard.MustBeBetweenOrEqualTo(xyzValues.Length, 1, 15, "xyzValues"); CurveType = curveType; XyzValues = xyzValues; ResponseArrays = responseArrays; } public bool Equals(IccResponseCurve other) { if (other == null) { return false; } if (this == other) { return true; } if (CurveType == other.CurveType && XyzValues.AsSpan().SequenceEqual(other.XyzValues)) { return EqualsResponseArray(other); } return false; } public override bool Equals(object obj) { if (obj is IccResponseCurve other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(CurveType, XyzValues, ResponseArrays); } private bool EqualsResponseArray(IccResponseCurve other) { if (ResponseArrays.Length != other.ResponseArrays.Length) { return false; } for (int i = 0; i < ResponseArrays.Length; i++) { if (!ResponseArrays[i].AsSpan().SequenceEqual(other.ResponseArrays[i])) { return false; } } return true; } } internal sealed class IccSampledCurveElement : IccCurveSegment, IEquatable { public float[] CurveEntries { get; } public IccSampledCurveElement(float[] curveEntries) : base(IccCurveSegmentSignature.SampledCurve) { Guard.NotNull(curveEntries, "curveEntries"); Guard.IsTrue(curveEntries.Length != 0, "curveEntries", "There must be at least one value"); CurveEntries = curveEntries; } public override bool Equals(IccCurveSegment other) { if (base.Equals(other) && other is IccSampledCurveElement iccSampledCurveElement) { return CurveEntries.AsSpan().SequenceEqual(iccSampledCurveElement.CurveEntries); } return false; } public bool Equals(IccSampledCurveElement other) { return Equals((IccCurveSegment)other); } } internal sealed class IccDataReader { private readonly byte[] data; private int currentIndex; public int DataLength => data.Length; public IccDataReader(byte[] data) { this.data = data ?? throw new ArgumentNullException("data"); } public void SetIndex(int index) { currentIndex = Numerics.Clamp(index, 0, data.Length); } private int AddIndex(int increment) { int result = currentIndex; currentIndex += increment; return result; } private void AddPadding() { currentIndex += CalcPadding(); } private int CalcPadding() { int num = 4 - currentIndex % 4; if (num < 4) { return num; } return 0; } private bool GetBit(byte value, int position) { return ((value >> 7 - position) & 1) == 1; } public IccOneDimensionalCurve ReadOneDimensionalCurve() { ushort num = ReadUInt16(); AddIndex(2); float[] array = new float[num - 1]; for (int i = 0; i < array.Length; i++) { array[i] = ReadSingle(); } IccCurveSegment[] array2 = new IccCurveSegment[num]; for (int j = 0; j < num; j++) { array2[j] = ReadCurveSegment(); } return new IccOneDimensionalCurve(array, array2); } public IccResponseCurve ReadResponseCurve(int channelCount) { IccCurveMeasurementEncodings curveType = (IccCurveMeasurementEncodings)ReadUInt32(); uint[] array = new uint[channelCount]; for (int i = 0; i < channelCount; i++) { array[i] = ReadUInt32(); } Vector3[] array2 = new Vector3[channelCount]; for (int j = 0; j < channelCount; j++) { array2[j] = ReadXyzNumber(); } IccResponseNumber[][] array3 = new IccResponseNumber[channelCount][]; for (int k = 0; k < channelCount; k++) { array3[k] = new IccResponseNumber[array[k]]; for (uint num = 0u; num < array[k]; num++) { array3[k][num] = ReadResponseNumber(); } } return new IccResponseCurve(curveType, array2, array3); } public IccParametricCurve ReadParametricCurve() { ushort num = ReadUInt16(); AddIndex(2); float a; float b; float c; float d; float e; float f; float g = (a = (b = (c = (d = (e = (f = 0f)))))); if (num <= 4) { g = ReadFix16(); } if (num > 0 && num <= 4) { a = ReadFix16(); b = ReadFix16(); } if (num > 1 && num <= 4) { c = ReadFix16(); } if (num > 2 && num <= 4) { d = ReadFix16(); } if (num == 4) { e = ReadFix16(); f = ReadFix16(); } return num switch { 0 => new IccParametricCurve(g), 1 => new IccParametricCurve(g, a, b), 2 => new IccParametricCurve(g, a, b, c), 3 => new IccParametricCurve(g, a, b, c, d), 4 => new IccParametricCurve(g, a, b, c, d, e, f), _ => throw new InvalidIccProfileException($"Invalid parametric curve type of {num}"), }; } public IccCurveSegment ReadCurveSegment() { IccCurveSegmentSignature iccCurveSegmentSignature = (IccCurveSegmentSignature)ReadUInt32(); AddIndex(4); return iccCurveSegmentSignature switch { IccCurveSegmentSignature.FormulaCurve => ReadFormulaCurveElement(), IccCurveSegmentSignature.SampledCurve => ReadSampledCurveElement(), _ => throw new InvalidIccProfileException($"Invalid curve segment type of {iccCurveSegmentSignature}"), }; } public IccFormulaCurveElement ReadFormulaCurveElement() { IccFormulaCurveType iccFormulaCurveType = (IccFormulaCurveType)ReadUInt16(); AddIndex(2); float d; float e; float gamma = (d = (e = 0f)); if (iccFormulaCurveType == IccFormulaCurveType.Type1 || iccFormulaCurveType == IccFormulaCurveType.Type2) { gamma = ReadSingle(); } float a = ReadSingle(); float b = ReadSingle(); float c = ReadSingle(); if (iccFormulaCurveType == IccFormulaCurveType.Type2 || iccFormulaCurveType == IccFormulaCurveType.Type3) { d = ReadSingle(); } if (iccFormulaCurveType == IccFormulaCurveType.Type3) { e = ReadSingle(); } return new IccFormulaCurveElement(iccFormulaCurveType, gamma, a, b, c, d, e); } public IccSampledCurveElement ReadSampledCurveElement() { uint num = ReadUInt32(); float[] array = new float[num]; for (int i = 0; i < num; i++) { array[i] = ReadSingle(); } return new IccSampledCurveElement(array); } private IccTagDataEntry[] ReadCurves(int count) { IccTagDataEntry[] array = new IccTagDataEntry[count]; for (int i = 0; i < count; i++) { IccTypeSignature iccTypeSignature = ReadTagDataEntryHeader(); if (iccTypeSignature != IccTypeSignature.Curve && iccTypeSignature != IccTypeSignature.ParametricCurve) { throw new InvalidIccProfileException("Curve has to be either \"IccTypeSignature.Curve\" or \"IccTypeSignature.ParametricCurve\" for LutAToB- and LutBToA-TagDataEntries"); } switch (iccTypeSignature) { case IccTypeSignature.Curve: array[i] = ReadCurveTagDataEntry(); break; case IccTypeSignature.ParametricCurve: array[i] = ReadParametricCurveTagDataEntry(); break; } AddPadding(); } return array; } public IccLut ReadLut8() { return new IccLut(ReadBytes(256)); } public IccLut ReadLut16(int count) { ushort[] array = new ushort[count]; for (int i = 0; i < count; i++) { array[i] = ReadUInt16(); } return new IccLut(array); } public IccClut ReadClut(int inChannelCount, int outChannelCount, bool isFloat) { byte[] array = new byte[inChannelCount]; Buffer.BlockCopy(data, AddIndex(16), array, 0, inChannelCount); if (!isFloat) { byte b = data[AddIndex(4)]; return b switch { 1 => ReadClut8(inChannelCount, outChannelCount, array), 2 => ReadClut16(inChannelCount, outChannelCount, array), _ => throw new InvalidIccProfileException($"Invalid CLUT size of {b}"), }; } return ReadClutF32(inChannelCount, outChannelCount, array); } public IccClut ReadClut8(int inChannelCount, int outChannelCount, byte[] gridPointCount) { int num = currentIndex; int num2 = 0; for (int i = 0; i < inChannelCount; i++) { num2 += (int)Math.Pow((int)gridPointCount[i], inChannelCount); } num2 /= inChannelCount; float[][] array = new float[num2][]; for (int j = 0; j < num2; j++) { array[j] = new float[outChannelCount]; for (int k = 0; k < outChannelCount; k++) { array[j][k] = (float)(int)data[currentIndex++] / 255f; } } currentIndex = num + num2 * outChannelCount; return new IccClut(array, gridPointCount, IccClutDataType.UInt8); } public IccClut ReadClut16(int inChannelCount, int outChannelCount, byte[] gridPointCount) { int num = currentIndex; int num2 = 0; for (int i = 0; i < inChannelCount; i++) { num2 += (int)Math.Pow((int)gridPointCount[i], inChannelCount); } num2 /= inChannelCount; float[][] array = new float[num2][]; for (int j = 0; j < num2; j++) { array[j] = new float[outChannelCount]; for (int k = 0; k < outChannelCount; k++) { array[j][k] = (float)(int)ReadUInt16() / 65535f; } } currentIndex = num + num2 * outChannelCount * 2; return new IccClut(array, gridPointCount, IccClutDataType.UInt16); } public IccClut ReadClutF32(int inChCount, int outChCount, byte[] gridPointCount) { int num = currentIndex; int num2 = 0; for (int i = 0; i < inChCount; i++) { num2 += (int)Math.Pow((int)gridPointCount[i], inChCount); } num2 /= inChCount; float[][] array = new float[num2][]; for (int j = 0; j < num2; j++) { array[j] = new float[outChCount]; for (int k = 0; k < outChCount; k++) { array[j][k] = ReadSingle(); } } currentIndex = num + num2 * outChCount * 4; return new IccClut(array, gridPointCount, IccClutDataType.Float); } public float[,] ReadMatrix(int xCount, int yCount, bool isSingle) { float[,] array = new float[xCount, yCount]; for (int i = 0; i < yCount; i++) { for (int j = 0; j < xCount; j++) { if (isSingle) { array[j, i] = ReadSingle(); } else { array[j, i] = ReadFix16(); } } } return array; } public float[] ReadMatrix(int yCount, bool isSingle) { float[] array = new float[yCount]; for (int i = 0; i < yCount; i++) { if (isSingle) { array[i] = ReadSingle(); } else { array[i] = ReadFix16(); } } return array; } public IccMultiProcessElement ReadMultiProcessElement() { IccMultiProcessElementSignature iccMultiProcessElementSignature = (IccMultiProcessElementSignature)ReadUInt32(); ushort inChannelCount = ReadUInt16(); ushort outChannelCount = ReadUInt16(); switch (iccMultiProcessElementSignature) { case IccMultiProcessElementSignature.CurveSet: return ReadCurveSetProcessElement(inChannelCount, outChannelCount); case IccMultiProcessElementSignature.Matrix: return ReadMatrixProcessElement(inChannelCount, outChannelCount); case IccMultiProcessElementSignature.Clut: return ReadClutProcessElement(inChannelCount, outChannelCount); case IccMultiProcessElementSignature.BAcs: AddIndex(8); return new IccBAcsProcessElement(inChannelCount, outChannelCount); case IccMultiProcessElementSignature.EAcs: AddIndex(8); return new IccEAcsProcessElement(inChannelCount, outChannelCount); default: throw new InvalidIccProfileException($"Invalid MultiProcessElement type of {iccMultiProcessElementSignature}"); } } public IccCurveSetProcessElement ReadCurveSetProcessElement(int inChannelCount, int outChannelCount) { IccOneDimensionalCurve[] array = new IccOneDimensionalCurve[inChannelCount]; for (int i = 0; i < inChannelCount; i++) { array[i] = ReadOneDimensionalCurve(); AddPadding(); } return new IccCurveSetProcessElement(array); } public IccMatrixProcessElement ReadMatrixProcessElement(int inChannelCount, int outChannelCount) { return new IccMatrixProcessElement(ReadMatrix(inChannelCount, outChannelCount, isSingle: true), ReadMatrix(outChannelCount, isSingle: true)); } public IccClutProcessElement ReadClutProcessElement(int inChannelCount, int outChannelCount) { return new IccClutProcessElement(ReadClut(inChannelCount, outChannelCount, isFloat: true)); } public DateTime ReadDateTime() { try { return new DateTime(ReadUInt16(), ReadUInt16(), ReadUInt16(), ReadUInt16(), ReadUInt16(), ReadUInt16(), DateTimeKind.Utc); } catch (ArgumentOutOfRangeException) { return DateTime.MinValue; } } public IccVersion ReadVersionNumber() { int num = ReadInt32(); int major = (num >> 24) & 0xFF; int minor = (num >> 20) & 0xF; int patch = (num >> 16) & 0xF; return new IccVersion(major, minor, patch); } public Vector3 ReadXyzNumber() { return new Vector3(ReadFix16(), ReadFix16(), ReadFix16()); } public IccProfileId ReadProfileId() { return new IccProfileId(ReadUInt32(), ReadUInt32(), ReadUInt32(), ReadUInt32()); } public IccPositionNumber ReadPositionNumber() { return new IccPositionNumber(ReadUInt32(), ReadUInt32()); } public IccResponseNumber ReadResponseNumber() { return new IccResponseNumber(ReadUInt16(), ReadFix16()); } public IccNamedColor ReadNamedColor(uint deviceCoordCount) { string name = ReadAsciiString(32); ushort[] pcsCoordinates = new ushort[3] { ReadUInt16(), ReadUInt16(), ReadUInt16() }; ushort[] array = new ushort[deviceCoordCount]; for (int i = 0; i < deviceCoordCount; i++) { array[i] = ReadUInt16(); } return new IccNamedColor(name, pcsCoordinates, array); } public IccProfileDescription ReadProfileDescription() { uint deviceManufacturer = ReadUInt32(); uint deviceModel = ReadUInt32(); IccDeviceAttribute deviceAttributes = (IccDeviceAttribute)ReadInt64(); IccProfileTag technologyInformation = (IccProfileTag)ReadUInt32(); IccMultiLocalizedUnicodeTagDataEntry iccMultiLocalizedUnicodeTagDataEntry = ReadText(); return new IccProfileDescription(deviceModelInfo: ReadText().Texts, deviceManufacturer: deviceManufacturer, deviceModel: deviceModel, deviceAttributes: deviceAttributes, technologyInformation: technologyInformation, deviceManufacturerInfo: iccMultiLocalizedUnicodeTagDataEntry.Texts); IccMultiLocalizedUnicodeTagDataEntry ReadText() { return ReadTagDataEntryHeader() switch { IccTypeSignature.MultiLocalizedUnicode => ReadMultiLocalizedUnicodeTagDataEntry(), IccTypeSignature.TextDescription => (IccMultiLocalizedUnicodeTagDataEntry)ReadTextDescriptionTagDataEntry(), _ => throw new InvalidIccProfileException("Profile description can only have multi-localized Unicode or text description entries"), }; } } public IccColorantTableEntry ReadColorantTableEntry() { return new IccColorantTableEntry(ReadAsciiString(32), ReadUInt16(), ReadUInt16(), ReadUInt16()); } public IccScreeningChannel ReadScreeningChannel() { return new IccScreeningChannel(ReadFix16(), ReadFix16(), (IccScreeningSpotType)ReadInt32()); } public ushort ReadUInt16() { return BinaryPrimitives.ReadUInt16BigEndian(data.AsSpan(AddIndex(2), 2)); } public short ReadInt16() { return BinaryPrimitives.ReadInt16BigEndian(data.AsSpan(AddIndex(2), 2)); } public uint ReadUInt32() { return BinaryPrimitives.ReadUInt32BigEndian(data.AsSpan(AddIndex(4), 4)); } public int ReadInt32() { return BinaryPrimitives.ReadInt32BigEndian(data.AsSpan(AddIndex(4), 4)); } public ulong ReadUInt64() { return BinaryPrimitives.ReadUInt64BigEndian(data.AsSpan(AddIndex(8), 8)); } public long ReadInt64() { return BinaryPrimitives.ReadInt64BigEndian(data.AsSpan(AddIndex(8), 8)); } public float ReadSingle() { int source = ReadInt32(); return Unsafe.As(ref source); } public double ReadDouble() { long source = ReadInt64(); return Unsafe.As(ref source); } public string ReadAsciiString(int length) { if (length == 0) { return string.Empty; } Guard.MustBeGreaterThan(length, 0, "length"); string text = Encoding.ASCII.GetString(data, AddIndex(length), length); int num = text.IndexOf('\0'); if (num >= 0) { text = text.Substring(0, num); } return text; } public string ReadUnicodeString(int length) { if (length == 0) { return string.Empty; } Guard.MustBeGreaterThan(length, 0, "length"); return Encoding.BigEndianUnicode.GetString(data, AddIndex(length), length); } public float ReadFix16() { return (float)ReadInt32() / 65536f; } public float ReadUFix16() { return (float)ReadUInt32() / 65536f; } public float ReadU1Fix15() { return (float)(int)ReadUInt16() / 32768f; } public float ReadUFix8() { return (float)(int)ReadUInt16() / 256f; } public byte[] ReadBytes(int count) { byte[] array = new byte[count]; Buffer.BlockCopy(data, AddIndex(count), array, 0, count); return array; } public IccTagDataEntry ReadTagDataEntry(IccTagTableEntry info) { currentIndex = (int)info.Offset; return ReadTagDataEntryHeader() switch { IccTypeSignature.Chromaticity => ReadChromaticityTagDataEntry(), IccTypeSignature.ColorantOrder => ReadColorantOrderTagDataEntry(), IccTypeSignature.ColorantTable => ReadColorantTableTagDataEntry(), IccTypeSignature.Curve => ReadCurveTagDataEntry(), IccTypeSignature.Data => ReadDataTagDataEntry(info.DataSize), IccTypeSignature.DateTime => ReadDateTimeTagDataEntry(), IccTypeSignature.Lut16 => ReadLut16TagDataEntry(), IccTypeSignature.Lut8 => ReadLut8TagDataEntry(), IccTypeSignature.LutAToB => ReadLutAtoBTagDataEntry(), IccTypeSignature.LutBToA => ReadLutBtoATagDataEntry(), IccTypeSignature.Measurement => ReadMeasurementTagDataEntry(), IccTypeSignature.MultiLocalizedUnicode => ReadMultiLocalizedUnicodeTagDataEntry(), IccTypeSignature.MultiProcessElements => ReadMultiProcessElementsTagDataEntry(), IccTypeSignature.NamedColor2 => ReadNamedColor2TagDataEntry(), IccTypeSignature.ParametricCurve => ReadParametricCurveTagDataEntry(), IccTypeSignature.ProfileSequenceDesc => ReadProfileSequenceDescTagDataEntry(), IccTypeSignature.ProfileSequenceIdentifier => ReadProfileSequenceIdentifierTagDataEntry(), IccTypeSignature.ResponseCurveSet16 => ReadResponseCurveSet16TagDataEntry(), IccTypeSignature.S15Fixed16Array => ReadFix16ArrayTagDataEntry(info.DataSize), IccTypeSignature.Signature => ReadSignatureTagDataEntry(), IccTypeSignature.Text => ReadTextTagDataEntry(info.DataSize), IccTypeSignature.U16Fixed16Array => ReadUFix16ArrayTagDataEntry(info.DataSize), IccTypeSignature.UInt16Array => ReadUInt16ArrayTagDataEntry(info.DataSize), IccTypeSignature.UInt32Array => ReadUInt32ArrayTagDataEntry(info.DataSize), IccTypeSignature.UInt64Array => ReadUInt64ArrayTagDataEntry(info.DataSize), IccTypeSignature.UInt8Array => ReadUInt8ArrayTagDataEntry(info.DataSize), IccTypeSignature.ViewingConditions => ReadViewingConditionsTagDataEntry(), IccTypeSignature.Xyz => ReadXyzTagDataEntry(info.DataSize), IccTypeSignature.TextDescription => ReadTextDescriptionTagDataEntry(), IccTypeSignature.CrdInfo => ReadCrdInfoTagDataEntry(), IccTypeSignature.Screening => ReadScreeningTagDataEntry(), IccTypeSignature.UcrBg => ReadUcrBgTagDataEntry(info.DataSize), _ => ReadUnknownTagDataEntry(info.DataSize), }; } public IccTypeSignature ReadTagDataEntryHeader() { uint result = ReadUInt32(); AddIndex(4); return (IccTypeSignature)result; } public void ReadCheckTagDataEntryHeader(IccTypeSignature expected) { IccTypeSignature iccTypeSignature = ReadTagDataEntryHeader(); if (expected != (IccTypeSignature)4294967295u && iccTypeSignature != expected) { throw new InvalidIccProfileException($"Read signature {iccTypeSignature} is not the expected {expected}"); } } public IccUnknownTagDataEntry ReadUnknownTagDataEntry(uint size) { int count = (int)(size - 8); return new IccUnknownTagDataEntry(ReadBytes(count)); } public IccChromaticityTagDataEntry ReadChromaticityTagDataEntry() { ushort num = ReadUInt16(); IccColorantEncoding iccColorantEncoding = (IccColorantEncoding)ReadUInt16(); if (Enum.IsDefined(typeof(IccColorantEncoding), iccColorantEncoding) && iccColorantEncoding != IccColorantEncoding.Unknown) { return new IccChromaticityTagDataEntry(iccColorantEncoding); } double[][] array = new double[num][]; for (int i = 0; i < num; i++) { array[i] = new double[2] { ReadUFix16(), ReadUFix16() }; } return new IccChromaticityTagDataEntry(array); } public IccColorantOrderTagDataEntry ReadColorantOrderTagDataEntry() { uint count = ReadUInt32(); return new IccColorantOrderTagDataEntry(ReadBytes((int)count)); } public IccColorantTableTagDataEntry ReadColorantTableTagDataEntry() { uint num = ReadUInt32(); IccColorantTableEntry[] array = new IccColorantTableEntry[num]; for (int i = 0; i < num; i++) { array[i] = ReadColorantTableEntry(); } return new IccColorantTableTagDataEntry(array); } public IccCurveTagDataEntry ReadCurveTagDataEntry() { uint num = ReadUInt32(); switch (num) { case 0u: return new IccCurveTagDataEntry(); case 1u: return new IccCurveTagDataEntry(ReadUFix8()); default: { float[] array = new float[num]; for (int i = 0; i < num; i++) { array[i] = (float)(int)ReadUInt16() / 65535f; } return new IccCurveTagDataEntry(array); } } } public IccDataTagDataEntry ReadDataTagDataEntry(uint size) { AddIndex(3); byte value = data[AddIndex(1)]; bool bit = GetBit(value, 7); int count = (int)(size - 12); return new IccDataTagDataEntry(ReadBytes(count), bit); } public IccDateTimeTagDataEntry ReadDateTimeTagDataEntry() { return new IccDateTimeTagDataEntry(ReadDateTime()); } public IccLut16TagDataEntry ReadLut16TagDataEntry() { byte b = data[AddIndex(1)]; byte b2 = data[AddIndex(1)]; byte b3 = data[AddIndex(1)]; AddIndex(1); float[,] matrix = ReadMatrix(3, 3, isSingle: false); ushort count = ReadUInt16(); ushort count2 = ReadUInt16(); IccLut[] array = new IccLut[b]; byte[] array2 = new byte[b]; for (int i = 0; i < b; i++) { array[i] = ReadLut16(count); array2[i] = b3; } IccClut clutValues = ReadClut16(b, b2, array2); IccLut[] array3 = new IccLut[b2]; for (int j = 0; j < b2; j++) { array3[j] = ReadLut16(count2); } return new IccLut16TagDataEntry(matrix, array, clutValues, array3); } public IccLut8TagDataEntry ReadLut8TagDataEntry() { byte b = data[AddIndex(1)]; byte b2 = data[AddIndex(1)]; byte b3 = data[AddIndex(1)]; AddIndex(1); float[,] matrix = ReadMatrix(3, 3, isSingle: false); IccLut[] array = new IccLut[b]; byte[] array2 = new byte[b]; for (int i = 0; i < b; i++) { array[i] = ReadLut8(); array2[i] = b3; } IccClut clutValues = ReadClut8(b, b2, array2); IccLut[] array3 = new IccLut[b2]; for (int j = 0; j < b2; j++) { array3[j] = ReadLut8(); } return new IccLut8TagDataEntry(matrix, array, clutValues, array3); } public IccLutAToBTagDataEntry ReadLutAtoBTagDataEntry() { int num = currentIndex - 8; byte b = data[AddIndex(1)]; byte b2 = data[AddIndex(1)]; AddIndex(2); uint num2 = ReadUInt32(); uint num3 = ReadUInt32(); uint num4 = ReadUInt32(); uint num5 = ReadUInt32(); uint num6 = ReadUInt32(); IccTagDataEntry[] curveB = null; IccTagDataEntry[] curveM = null; IccTagDataEntry[] curveA = null; IccClut clutValues = null; float[,] matrix3x = null; float[] matrix3x2 = null; if (num2 != 0) { currentIndex = (int)num2 + num; curveB = ReadCurves(b2); } if (num4 != 0) { currentIndex = (int)num4 + num; curveM = ReadCurves(b2); } if (num6 != 0) { currentIndex = (int)num6 + num; curveA = ReadCurves(b); } if (num5 != 0) { currentIndex = (int)num5 + num; clutValues = ReadClut(b, b2, isFloat: false); } if (num3 != 0) { currentIndex = (int)num3 + num; matrix3x = ReadMatrix(3, 3, isSingle: false); matrix3x2 = ReadMatrix(3, isSingle: false); } return new IccLutAToBTagDataEntry(curveB, matrix3x, matrix3x2, curveM, clutValues, curveA); } public IccLutBToATagDataEntry ReadLutBtoATagDataEntry() { int num = currentIndex - 8; byte b = data[AddIndex(1)]; byte b2 = data[AddIndex(1)]; AddIndex(2); uint num2 = ReadUInt32(); uint num3 = ReadUInt32(); uint num4 = ReadUInt32(); uint num5 = ReadUInt32(); uint num6 = ReadUInt32(); IccTagDataEntry[] curveB = null; IccTagDataEntry[] curveM = null; IccTagDataEntry[] curveA = null; IccClut clutValues = null; float[,] matrix3x = null; float[] matrix3x2 = null; if (num2 != 0) { currentIndex = (int)num2 + num; curveB = ReadCurves(b); } if (num4 != 0) { currentIndex = (int)num4 + num; curveM = ReadCurves(b); } if (num6 != 0) { currentIndex = (int)num6 + num; curveA = ReadCurves(b2); } if (num5 != 0) { currentIndex = (int)num5 + num; clutValues = ReadClut(b, b2, isFloat: false); } if (num3 != 0) { currentIndex = (int)num3 + num; matrix3x = ReadMatrix(3, 3, isSingle: false); matrix3x2 = ReadMatrix(3, isSingle: false); } return new IccLutBToATagDataEntry(curveB, matrix3x, matrix3x2, curveM, clutValues, curveA); } public IccMeasurementTagDataEntry ReadMeasurementTagDataEntry() { return new IccMeasurementTagDataEntry((IccStandardObserver)ReadUInt32(), ReadXyzNumber(), (IccMeasurementGeometry)ReadUInt32(), ReadUFix16(), (IccStandardIlluminant)ReadUInt32()); } public IccMultiLocalizedUnicodeTagDataEntry ReadMultiLocalizedUnicodeTagDataEntry() { int num = currentIndex - 8; uint num2 = ReadUInt32(); ReadUInt32(); IccLocalizedString[] array = new IccLocalizedString[num2]; CultureInfo[] array2 = new CultureInfo[num2]; uint[] array3 = new uint[num2]; uint[] array4 = new uint[num2]; for (int i = 0; i < num2; i++) { string language = ReadAsciiString(2); string country = ReadAsciiString(2); array2[i] = ReadCulture(language, country); array3[i] = ReadUInt32(); array4[i] = ReadUInt32(); } for (int j = 0; j < num2; j++) { currentIndex = (int)(num + array4[j]); array[j] = new IccLocalizedString(array2[j], ReadUnicodeString((int)array3[j])); } return new IccMultiLocalizedUnicodeTagDataEntry(array); static CultureInfo ReadCulture(string text, string text2) { if (string.IsNullOrWhiteSpace(text)) { return CultureInfo.InvariantCulture; } if (string.IsNullOrWhiteSpace(text2)) { try { return new CultureInfo(text); } catch (CultureNotFoundException) { return CultureInfo.InvariantCulture; } } try { return new CultureInfo(text + "-" + text2); } catch (CultureNotFoundException) { return ReadCulture(text, null); } } } public IccMultiProcessElementsTagDataEntry ReadMultiProcessElementsTagDataEntry() { int num = currentIndex - 8; ReadUInt16(); ReadUInt16(); uint num2 = ReadUInt32(); IccPositionNumber[] array = new IccPositionNumber[num2]; for (int i = 0; i < num2; i++) { array[i] = ReadPositionNumber(); } IccMultiProcessElement[] array2 = new IccMultiProcessElement[num2]; for (int j = 0; j < num2; j++) { currentIndex = (int)array[j].Offset + num; array2[j] = ReadMultiProcessElement(); } return new IccMultiProcessElementsTagDataEntry(array2); } public IccNamedColor2TagDataEntry ReadNamedColor2TagDataEntry() { int vendorFlags = ReadInt32(); uint num = ReadUInt32(); uint deviceCoordCount = ReadUInt32(); string prefix = ReadAsciiString(32); string suffix = ReadAsciiString(32); IccNamedColor[] array = new IccNamedColor[num]; for (int i = 0; i < num; i++) { array[i] = ReadNamedColor(deviceCoordCount); } return new IccNamedColor2TagDataEntry(vendorFlags, prefix, suffix, array); } public IccParametricCurveTagDataEntry ReadParametricCurveTagDataEntry() { return new IccParametricCurveTagDataEntry(ReadParametricCurve()); } public IccProfileSequenceDescTagDataEntry ReadProfileSequenceDescTagDataEntry() { uint num = ReadUInt32(); IccProfileDescription[] array = new IccProfileDescription[num]; for (int i = 0; i < num; i++) { array[i] = ReadProfileDescription(); } return new IccProfileSequenceDescTagDataEntry(array); } public IccProfileSequenceIdentifierTagDataEntry ReadProfileSequenceIdentifierTagDataEntry() { int num = currentIndex - 8; uint num2 = ReadUInt32(); IccPositionNumber[] array = new IccPositionNumber[num2]; for (int i = 0; i < num2; i++) { array[i] = ReadPositionNumber(); } IccProfileSequenceIdentifier[] array2 = new IccProfileSequenceIdentifier[num2]; for (int j = 0; j < num2; j++) { currentIndex = (int)(num + array[j].Offset); IccProfileId id = ReadProfileId(); ReadCheckTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode); IccMultiLocalizedUnicodeTagDataEntry iccMultiLocalizedUnicodeTagDataEntry = ReadMultiLocalizedUnicodeTagDataEntry(); array2[j] = new IccProfileSequenceIdentifier(id, iccMultiLocalizedUnicodeTagDataEntry.Texts); } return new IccProfileSequenceIdentifierTagDataEntry(array2); } public IccResponseCurveSet16TagDataEntry ReadResponseCurveSet16TagDataEntry() { int num = currentIndex - 8; ushort channelCount = ReadUInt16(); ushort num2 = ReadUInt16(); uint[] array = new uint[num2]; for (int i = 0; i < num2; i++) { array[i] = ReadUInt32(); } IccResponseCurve[] array2 = new IccResponseCurve[num2]; for (int j = 0; j < num2; j++) { currentIndex = (int)(num + array[j]); array2[j] = ReadResponseCurve(channelCount); } return new IccResponseCurveSet16TagDataEntry(array2); } public IccFix16ArrayTagDataEntry ReadFix16ArrayTagDataEntry(uint size) { uint num = (size - 8) / 4; float[] array = new float[num]; for (int i = 0; i < num; i++) { array[i] = ReadFix16() / 256f; } return new IccFix16ArrayTagDataEntry(array); } public IccSignatureTagDataEntry ReadSignatureTagDataEntry() { return new IccSignatureTagDataEntry(ReadAsciiString(4)); } public IccTextTagDataEntry ReadTextTagDataEntry(uint size) { return new IccTextTagDataEntry(ReadAsciiString((int)(size - 8))); } public IccUFix16ArrayTagDataEntry ReadUFix16ArrayTagDataEntry(uint size) { uint num = (size - 8) / 4; float[] array = new float[num]; for (int i = 0; i < num; i++) { array[i] = ReadUFix16(); } return new IccUFix16ArrayTagDataEntry(array); } public IccUInt16ArrayTagDataEntry ReadUInt16ArrayTagDataEntry(uint size) { uint num = (size - 8) / 2; ushort[] array = new ushort[num]; for (int i = 0; i < num; i++) { array[i] = ReadUInt16(); } return new IccUInt16ArrayTagDataEntry(array); } public IccUInt32ArrayTagDataEntry ReadUInt32ArrayTagDataEntry(uint size) { uint num = (size - 8) / 4; uint[] array = new uint[num]; for (int i = 0; i < num; i++) { array[i] = ReadUInt32(); } return new IccUInt32ArrayTagDataEntry(array); } public IccUInt64ArrayTagDataEntry ReadUInt64ArrayTagDataEntry(uint size) { uint num = (size - 8) / 8; ulong[] array = new ulong[num]; for (int i = 0; i < num; i++) { array[i] = ReadUInt64(); } return new IccUInt64ArrayTagDataEntry(array); } public IccUInt8ArrayTagDataEntry ReadUInt8ArrayTagDataEntry(uint size) { int count = (int)(size - 8); return new IccUInt8ArrayTagDataEntry(ReadBytes(count)); } public IccViewingConditionsTagDataEntry ReadViewingConditionsTagDataEntry() { return new IccViewingConditionsTagDataEntry(ReadXyzNumber(), ReadXyzNumber(), (IccStandardIlluminant)ReadUInt32()); } public IccXyzTagDataEntry ReadXyzTagDataEntry(uint size) { uint num = (size - 8) / 12; Vector3[] array = new Vector3[num]; for (int i = 0; i < num; i++) { array[i] = ReadXyzNumber(); } return new IccXyzTagDataEntry(array); } public IccTextDescriptionTagDataEntry ReadTextDescriptionTagDataEntry() { string unicode; string scriptCode; string ascii = (unicode = (scriptCode = null)); int num = (int)ReadUInt32(); if (num > 0) { ascii = ReadAsciiString(num - 1); AddIndex(1); } uint unicodeLanguageCode = ReadUInt32(); int num2 = (int)ReadUInt32(); if (num2 > 0) { unicode = ReadUnicodeString(num2 * 2 - 2); AddIndex(2); } ushort scriptCodeCode = ReadUInt16(); int num3 = Math.Min(data[AddIndex(1)], (byte)67); if (num3 > 0) { scriptCode = ReadAsciiString(num3 - 1); AddIndex(1); } return new IccTextDescriptionTagDataEntry(ascii, unicode, scriptCode, unicodeLanguageCode, scriptCodeCode); } public IccCrdInfoTagDataEntry ReadCrdInfoTagDataEntry() { uint length = ReadUInt32(); string postScriptProductName = ReadAsciiString((int)length); uint length2 = ReadUInt32(); string renderingIntent0Crd = ReadAsciiString((int)length2); uint length3 = ReadUInt32(); string renderingIntent1Crd = ReadAsciiString((int)length3); uint length4 = ReadUInt32(); string renderingIntent2Crd = ReadAsciiString((int)length4); uint length5 = ReadUInt32(); string renderingIntent3Crd = ReadAsciiString((int)length5); return new IccCrdInfoTagDataEntry(postScriptProductName, renderingIntent0Crd, renderingIntent1Crd, renderingIntent2Crd, renderingIntent3Crd); } public IccScreeningTagDataEntry ReadScreeningTagDataEntry() { IccScreeningFlag flags = (IccScreeningFlag)ReadInt32(); IccScreeningChannel[] array = new IccScreeningChannel[ReadUInt32()]; for (int i = 0; i < array.Length; i++) { array[i] = ReadScreeningChannel(); } return new IccScreeningTagDataEntry(flags, array); } public IccUcrBgTagDataEntry ReadUcrBgTagDataEntry(uint size) { uint num = ReadUInt32(); ushort[] array = new ushort[num]; for (int i = 0; i < array.Length; i++) { array[i] = ReadUInt16(); } uint num2 = ReadUInt32(); ushort[] array2 = new ushort[num2]; for (int j = 0; j < array2.Length; j++) { array2[j] = ReadUInt16(); } uint num3 = (num + num2) * 2 + 8; int length = (int)(size - 8 - num3); string description = ReadAsciiString(length); return new IccUcrBgTagDataEntry(array, array2, description); } } internal sealed class IccDataWriter : IDisposable { private readonly MemoryStream dataStream; private bool isDisposed; public uint Length => (uint)dataStream.Length; public IccDataWriter() { dataStream = new MemoryStream(); } public byte[] GetData() { return dataStream.ToArray(); } public void SetIndex(int index) { dataStream.Position = index; } public int WriteArray(byte[] data) { dataStream.Write(data, 0, data.Length); return data.Length; } public int WriteArray(ushort[] data) { for (int i = 0; i < data.Length; i++) { WriteUInt16(data[i]); } return data.Length * 2; } public int WriteArray(short[] data) { for (int i = 0; i < data.Length; i++) { WriteInt16(data[i]); } return data.Length * 2; } public int WriteArray(uint[] data) { for (int i = 0; i < data.Length; i++) { WriteUInt32(data[i]); } return data.Length * 4; } public int WriteArray(int[] data) { for (int i = 0; i < data.Length; i++) { WriteInt32(data[i]); } return data.Length * 4; } public int WriteArray(ulong[] data) { for (int i = 0; i < data.Length; i++) { WriteUInt64(data[i]); } return data.Length * 8; } public int WriteEmpty(int length) { for (int i = 0; i < length; i++) { dataStream.WriteByte(0); } return length; } public int WritePadding() { int num = 4 - (int)dataStream.Position % 4; return WriteEmpty((num < 4) ? num : 0); } public void Dispose() { Dispose(disposing: true); } private unsafe int WriteBytes(byte* data, int length) { if (BitConverter.IsLittleEndian) { for (int num = length - 1; num >= 0; num--) { dataStream.WriteByte(data[num]); } } else { WriteBytesDirect(data, length); } return length; } private unsafe int WriteBytesDirect(byte* data, int length) { for (int i = 0; i < length; i++) { dataStream.WriteByte(data[i]); } return length; } private int WriteCurves(IccTagDataEntry[] curves) { int num = 0; foreach (IccTagDataEntry iccTagDataEntry in curves) { if (iccTagDataEntry.Signature != IccTypeSignature.Curve && iccTagDataEntry.Signature != IccTypeSignature.ParametricCurve) { throw new InvalidIccProfileException("Curve has to be either \"IccTypeSignature.Curve\" or \"IccTypeSignature.ParametricCurve\" for LutAToB- and LutBToA-TagDataEntries"); } num += WriteTagDataEntry(iccTagDataEntry); num += WritePadding(); } return num; } private void Dispose(bool disposing) { if (!isDisposed) { if (disposing) { dataStream?.Dispose(); } isDisposed = true; } } public int WriteOneDimensionalCurve(IccOneDimensionalCurve value) { int num = WriteUInt16((ushort)value.Segments.Length); num += WriteEmpty(2); float[] breakPoints = value.BreakPoints; foreach (float value2 in breakPoints) { num += WriteSingle(value2); } IccCurveSegment[] segments = value.Segments; foreach (IccCurveSegment value3 in segments) { num += WriteCurveSegment(value3); } return num; } public int WriteResponseCurve(IccResponseCurve value) { int num = WriteUInt32((uint)value.CurveType); IccResponseNumber[][] responseArrays = value.ResponseArrays; foreach (IccResponseNumber[] array in responseArrays) { num += WriteUInt32((uint)array.Length); } Vector3[] xyzValues = value.XyzValues; foreach (Vector3 value2 in xyzValues) { num += WriteXyzNumber(value2); } responseArrays = value.ResponseArrays; foreach (IccResponseNumber[] array2 in responseArrays) { for (int j = 0; j < array2.Length; j++) { IccResponseNumber value3 = array2[j]; num += WriteResponseNumber(in value3); } } return num; } public int WriteParametricCurve(IccParametricCurve value) { ushort type = (ushort)value.Type; int num = WriteUInt16(type); num += WriteEmpty(2); if (type <= 4) { num += WriteFix16(value.G); } if (type > 0 && type <= 4) { num += WriteFix16(value.A); num += WriteFix16(value.B); } if (type > 1 && type <= 4) { num += WriteFix16(value.C); } if (type > 2 && type <= 4) { num += WriteFix16(value.D); } if (type == 4) { num += WriteFix16(value.E); num += WriteFix16(value.F); } return num; } public int WriteCurveSegment(IccCurveSegment value) { int num = WriteUInt32((uint)value.Signature); num += WriteEmpty(4); return value.Signature switch { IccCurveSegmentSignature.FormulaCurve => num + WriteFormulaCurveElement((IccFormulaCurveElement)value), IccCurveSegmentSignature.SampledCurve => num + WriteSampledCurveElement((IccSampledCurveElement)value), _ => throw new InvalidIccProfileException($"Invalid CurveSegment type of {value.Signature}"), }; } public int WriteFormulaCurveElement(IccFormulaCurveElement value) { int num = WriteUInt16((ushort)value.Type); num += WriteEmpty(2); if (value.Type == IccFormulaCurveType.Type1 || value.Type == IccFormulaCurveType.Type2) { num += WriteSingle(value.Gamma); } num += WriteSingle(value.A); num += WriteSingle(value.B); num += WriteSingle(value.C); if (value.Type == IccFormulaCurveType.Type2 || value.Type == IccFormulaCurveType.Type3) { num += WriteSingle(value.D); } if (value.Type == IccFormulaCurveType.Type3) { num += WriteSingle(value.E); } return num; } public int WriteSampledCurveElement(IccSampledCurveElement value) { int num = WriteUInt32((uint)value.CurveEntries.Length); float[] curveEntries = value.CurveEntries; foreach (float value2 in curveEntries) { num += WriteSingle(value2); } return num; } public int WriteLut8(IccLut value) { float[] values = value.Values; foreach (float num in values) { WriteByte((byte)Numerics.Clamp(num * 255f + 0.5f, 0f, 255f)); } return value.Values.Length; } public int WriteLut16(IccLut value) { float[] values = value.Values; foreach (float num in values) { WriteUInt16((ushort)Numerics.Clamp(num * 65535f + 0.5f, 0f, 65535f)); } return value.Values.Length * 2; } public int WriteClut(IccClut value) { int num = WriteArray(value.GridPointCount); num += WriteEmpty(16 - value.GridPointCount.Length); switch (value.DataType) { case IccClutDataType.Float: return num + WriteClutF32(value); case IccClutDataType.UInt8: num += WriteByte(1); num += WriteEmpty(3); return num + WriteClut8(value); case IccClutDataType.UInt16: num += WriteByte(2); num += WriteEmpty(3); return num + WriteClut16(value); default: throw new InvalidIccProfileException($"Invalid CLUT data type of {value.DataType}"); } } public int WriteClut8(IccClut value) { int num = 0; float[][] values = value.Values; foreach (float[] array in values) { foreach (float num2 in array) { num += WriteByte((byte)Numerics.Clamp(num2 * 255f + 0.5f, 0f, 255f)); } } return num; } public int WriteClut16(IccClut value) { int num = 0; float[][] values = value.Values; foreach (float[] array in values) { foreach (float num2 in array) { num += WriteUInt16((ushort)Numerics.Clamp(num2 * 65535f + 0.5f, 0f, 65535f)); } } return num; } public int WriteClutF32(IccClut value) { int num = 0; float[][] values = value.Values; foreach (float[] array in values) { foreach (float value2 in array) { num += WriteSingle(value2); } } return num; } public int WriteMatrix(Matrix4x4 value, bool isSingle) { int num = 0; if (isSingle) { num += WriteSingle(value.M11); num += WriteSingle(value.M21); num += WriteSingle(value.M31); num += WriteSingle(value.M12); num += WriteSingle(value.M22); num += WriteSingle(value.M32); num += WriteSingle(value.M13); num += WriteSingle(value.M23); return num + WriteSingle(value.M33); } num += WriteFix16(value.M11); num += WriteFix16(value.M21); num += WriteFix16(value.M31); num += WriteFix16(value.M12); num += WriteFix16(value.M22); num += WriteFix16(value.M32); num += WriteFix16(value.M13); num += WriteFix16(value.M23); return num + WriteFix16(value.M33); } public int WriteMatrix(in DenseMatrix value, bool isSingle) { int num = 0; for (int i = 0; i < value.Rows; i++) { for (int j = 0; j < value.Columns; j++) { num = ((!isSingle) ? (num + WriteFix16(value[j, i])) : (num + WriteSingle(value[j, i]))); } } return num; } public int WriteMatrix(float[,] value, bool isSingle) { int num = 0; for (int i = 0; i < value.GetLength(1); i++) { for (int j = 0; j < value.GetLength(0); j++) { num = ((!isSingle) ? (num + WriteFix16(value[j, i])) : (num + WriteSingle(value[j, i]))); } } return num; } public int WriteMatrix(Vector3 value, bool isSingle) { int num = 0; if (isSingle) { num += WriteSingle(value.X); num += WriteSingle(value.Y); return num + WriteSingle(value.Z); } num += WriteFix16(value.X); num += WriteFix16(value.Y); return num + WriteFix16(value.Z); } public int WriteMatrix(float[] value, bool isSingle) { int num = 0; for (int i = 0; i < value.Length; i++) { num = ((!isSingle) ? (num + WriteFix16(value[i])) : (num + WriteSingle(value[i]))); } return num; } public int WriteMultiProcessElement(IccMultiProcessElement value) { int num = WriteUInt32((uint)value.Signature); num += WriteUInt16((ushort)value.InputChannelCount); num += WriteUInt16((ushort)value.OutputChannelCount); switch (value.Signature) { case IccMultiProcessElementSignature.CurveSet: return num + WriteCurveSetProcessElement((IccCurveSetProcessElement)value); case IccMultiProcessElementSignature.Matrix: return num + WriteMatrixProcessElement((IccMatrixProcessElement)value); case IccMultiProcessElementSignature.Clut: return num + WriteClutProcessElement((IccClutProcessElement)value); case IccMultiProcessElementSignature.BAcs: case IccMultiProcessElementSignature.EAcs: return num + WriteEmpty(8); default: throw new InvalidIccProfileException($"Invalid MultiProcessElement type of {value.Signature}"); } } public int WriteCurveSetProcessElement(IccCurveSetProcessElement value) { int num = 0; IccOneDimensionalCurve[] curves = value.Curves; foreach (IccOneDimensionalCurve value2 in curves) { num += WriteOneDimensionalCurve(value2); num += WritePadding(); } return num; } public int WriteMatrixProcessElement(IccMatrixProcessElement value) { return WriteMatrix(value.MatrixIxO, isSingle: true) + WriteMatrix(value.MatrixOx1, isSingle: true); } public int WriteClutProcessElement(IccClutProcessElement value) { return WriteClut(value.ClutValue); } public int WriteDateTime(DateTime value) { return WriteUInt16((ushort)value.Year) + WriteUInt16((ushort)value.Month) + WriteUInt16((ushort)value.Day) + WriteUInt16((ushort)value.Hour) + WriteUInt16((ushort)value.Minute) + WriteUInt16((ushort)value.Second); } public int WriteVersionNumber(in IccVersion value) { int num = Numerics.Clamp(value.Major, 0, 255); int num2 = Numerics.Clamp(value.Minor, 0, 15); int num3 = Numerics.Clamp(value.Patch, 0, 15); int value2 = (num << 24) | (num2 << 20) | (num3 << 16); return WriteInt32(value2); } public int WriteXyzNumber(Vector3 value) { return WriteFix16(value.X) + WriteFix16(value.Y) + WriteFix16(value.Z); } public int WriteProfileId(in IccProfileId value) { return WriteUInt32(value.Part1) + WriteUInt32(value.Part2) + WriteUInt32(value.Part3) + WriteUInt32(value.Part4); } public int WritePositionNumber(in IccPositionNumber value) { return WriteUInt32(value.Offset) + WriteUInt32(value.Size); } public int WriteResponseNumber(in IccResponseNumber value) { return WriteUInt16(value.DeviceCode) + WriteFix16(value.MeasurementValue); } public int WriteNamedColor(in IccNamedColor value) { return WriteAsciiString(value.Name, 32, ensureNullTerminator: true) + WriteArray(value.PcsCoordinates) + WriteArray(value.DeviceCoordinates); } public int WriteProfileDescription(in IccProfileDescription value) { return WriteUInt32(value.DeviceManufacturer) + WriteUInt32(value.DeviceModel) + WriteInt64((long)value.DeviceAttributes) + WriteUInt32((uint)value.TechnologyInformation) + WriteTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode) + WriteMultiLocalizedUnicodeTagDataEntry(new IccMultiLocalizedUnicodeTagDataEntry(value.DeviceManufacturerInfo)) + WriteTagDataEntryHeader(IccTypeSignature.MultiLocalizedUnicode) + WriteMultiLocalizedUnicodeTagDataEntry(new IccMultiLocalizedUnicodeTagDataEntry(value.DeviceModelInfo)); } public int WriteScreeningChannel(in IccScreeningChannel value) { return WriteFix16(value.Frequency) + WriteFix16(value.Angle) + WriteInt32((int)value.SpotShape); } public int WriteByte(byte value) { dataStream.WriteByte(value); return 1; } public unsafe int WriteUInt16(ushort value) { return WriteBytes((byte*)(&value), 2); } public unsafe int WriteInt16(short value) { return WriteBytes((byte*)(&value), 2); } public unsafe int WriteUInt32(uint value) { return WriteBytes((byte*)(&value), 4); } public unsafe int WriteInt32(int value) { return WriteBytes((byte*)(&value), 4); } public unsafe int WriteUInt64(ulong value) { return WriteBytes((byte*)(&value), 8); } public unsafe int WriteInt64(long value) { return WriteBytes((byte*)(&value), 8); } public unsafe int WriteSingle(float value) { return WriteBytes((byte*)(&value), 4); } public unsafe int WriteDouble(double value) { return WriteBytes((byte*)(&value), 8); } public int WriteFix16(double value) { value = Numerics.Clamp(value, -32768.0, 32767.99998474121); value *= 65536.0; return WriteInt32((int)Math.Round(value, MidpointRounding.AwayFromZero)); } public int WriteUFix16(double value) { value = Numerics.Clamp(value, 0.0, 65535.99998474121); value *= 65536.0; return WriteUInt32((uint)Math.Round(value, MidpointRounding.AwayFromZero)); } public int WriteU1Fix15(double value) { value = Numerics.Clamp(value, 0.0, 1.999969482421875); value *= 32768.0; return WriteUInt16((ushort)Math.Round(value, MidpointRounding.AwayFromZero)); } public int WriteUFix8(double value) { value = Numerics.Clamp(value, 0.0, 255.99609375); value *= 256.0; return WriteUInt16((ushort)Math.Round(value, MidpointRounding.AwayFromZero)); } public int WriteAsciiString(string value) { if (string.IsNullOrEmpty(value)) { return 0; } byte[] bytes = Encoding.ASCII.GetBytes(value); dataStream.Write(bytes, 0, bytes.Length); return bytes.Length; } public int WriteAsciiString(string value, int length, bool ensureNullTerminator) { if (length == 0) { return 0; } Guard.MustBeGreaterThan(length, 0, "length"); if (value == null) { value = string.Empty; } byte value2 = 32; int num = 0; if (ensureNullTerminator) { value2 = 0; num = 1; } value = value.Substring(0, Math.Min(length - num, value.Length)); byte[] bytes = Encoding.ASCII.GetBytes(value); int num2 = Math.Min(length - num, bytes.Length); dataStream.Write(bytes, 0, num2); for (int i = 0; i < length - num2; i++) { dataStream.WriteByte(value2); } return length; } public int WriteUnicodeString(string value) { if (string.IsNullOrEmpty(value)) { return 0; } byte[] bytes = Encoding.BigEndianUnicode.GetBytes(value); dataStream.Write(bytes, 0, bytes.Length); return bytes.Length; } public int WriteTagDataEntry(IccTagDataEntry data, out IccTagTableEntry table) { uint offset = (uint)dataStream.Position; int num = WriteTagDataEntry(data); WritePadding(); table = new IccTagTableEntry(data.TagSignature, offset, (uint)num); return num; } public int WriteTagDataEntry(IccTagDataEntry entry) { int num = WriteTagDataEntryHeader(entry.Signature); return entry.Signature switch { IccTypeSignature.Chromaticity => num + WriteChromaticityTagDataEntry((IccChromaticityTagDataEntry)entry), IccTypeSignature.ColorantOrder => num + WriteColorantOrderTagDataEntry((IccColorantOrderTagDataEntry)entry), IccTypeSignature.ColorantTable => num + WriteColorantTableTagDataEntry((IccColorantTableTagDataEntry)entry), IccTypeSignature.Curve => num + WriteCurveTagDataEntry((IccCurveTagDataEntry)entry), IccTypeSignature.Data => num + WriteDataTagDataEntry((IccDataTagDataEntry)entry), IccTypeSignature.DateTime => num + WriteDateTimeTagDataEntry((IccDateTimeTagDataEntry)entry), IccTypeSignature.Lut16 => num + WriteLut16TagDataEntry((IccLut16TagDataEntry)entry), IccTypeSignature.Lut8 => num + WriteLut8TagDataEntry((IccLut8TagDataEntry)entry), IccTypeSignature.LutAToB => num + WriteLutAtoBTagDataEntry((IccLutAToBTagDataEntry)entry), IccTypeSignature.LutBToA => num + WriteLutBtoATagDataEntry((IccLutBToATagDataEntry)entry), IccTypeSignature.Measurement => num + WriteMeasurementTagDataEntry((IccMeasurementTagDataEntry)entry), IccTypeSignature.MultiLocalizedUnicode => num + WriteMultiLocalizedUnicodeTagDataEntry((IccMultiLocalizedUnicodeTagDataEntry)entry), IccTypeSignature.MultiProcessElements => num + WriteMultiProcessElementsTagDataEntry((IccMultiProcessElementsTagDataEntry)entry), IccTypeSignature.NamedColor2 => num + WriteNamedColor2TagDataEntry((IccNamedColor2TagDataEntry)entry), IccTypeSignature.ParametricCurve => num + WriteParametricCurveTagDataEntry((IccParametricCurveTagDataEntry)entry), IccTypeSignature.ProfileSequenceDesc => num + WriteProfileSequenceDescTagDataEntry((IccProfileSequenceDescTagDataEntry)entry), IccTypeSignature.ProfileSequenceIdentifier => num + WriteProfileSequenceIdentifierTagDataEntry((IccProfileSequenceIdentifierTagDataEntry)entry), IccTypeSignature.ResponseCurveSet16 => num + WriteResponseCurveSet16TagDataEntry((IccResponseCurveSet16TagDataEntry)entry), IccTypeSignature.S15Fixed16Array => num + WriteFix16ArrayTagDataEntry((IccFix16ArrayTagDataEntry)entry), IccTypeSignature.Signature => num + WriteSignatureTagDataEntry((IccSignatureTagDataEntry)entry), IccTypeSignature.Text => num + WriteTextTagDataEntry((IccTextTagDataEntry)entry), IccTypeSignature.U16Fixed16Array => num + WriteUFix16ArrayTagDataEntry((IccUFix16ArrayTagDataEntry)entry), IccTypeSignature.UInt16Array => num + WriteUInt16ArrayTagDataEntry((IccUInt16ArrayTagDataEntry)entry), IccTypeSignature.UInt32Array => num + WriteUInt32ArrayTagDataEntry((IccUInt32ArrayTagDataEntry)entry), IccTypeSignature.UInt64Array => num + WriteUInt64ArrayTagDataEntry((IccUInt64ArrayTagDataEntry)entry), IccTypeSignature.UInt8Array => num + WriteUInt8ArrayTagDataEntry((IccUInt8ArrayTagDataEntry)entry), IccTypeSignature.ViewingConditions => num + WriteViewingConditionsTagDataEntry((IccViewingConditionsTagDataEntry)entry), IccTypeSignature.Xyz => num + WriteXyzTagDataEntry((IccXyzTagDataEntry)entry), IccTypeSignature.TextDescription => num + WriteTextDescriptionTagDataEntry((IccTextDescriptionTagDataEntry)entry), IccTypeSignature.CrdInfo => num + WriteCrdInfoTagDataEntry((IccCrdInfoTagDataEntry)entry), IccTypeSignature.Screening => num + WriteScreeningTagDataEntry((IccScreeningTagDataEntry)entry), IccTypeSignature.UcrBg => num + WriteUcrBgTagDataEntry((IccUcrBgTagDataEntry)entry), _ => num + WriteUnknownTagDataEntry(entry as IccUnknownTagDataEntry), }; } public int WriteTagDataEntryHeader(IccTypeSignature signature) { return WriteUInt32((uint)signature) + WriteEmpty(4); } public int WriteUnknownTagDataEntry(IccUnknownTagDataEntry value) { return WriteArray(value.Data); } public int WriteChromaticityTagDataEntry(IccChromaticityTagDataEntry value) { int num = WriteUInt16((ushort)value.ChannelCount); num += WriteUInt16((ushort)value.ColorantType); for (int i = 0; i < value.ChannelCount; i++) { num += WriteUFix16(value.ChannelValues[i][0]); num += WriteUFix16(value.ChannelValues[i][1]); } return num; } public int WriteColorantOrderTagDataEntry(IccColorantOrderTagDataEntry value) { return WriteUInt32((uint)value.ColorantNumber.Length) + WriteArray(value.ColorantNumber); } public int WriteColorantTableTagDataEntry(IccColorantTableTagDataEntry value) { int num = WriteUInt32((uint)value.ColorantData.Length); for (int i = 0; i < value.ColorantData.Length; i++) { ref IccColorantTableEntry reference = ref value.ColorantData[i]; num += WriteAsciiString(reference.Name, 32, ensureNullTerminator: true); num += WriteUInt16(reference.Pcs1); num += WriteUInt16(reference.Pcs2); num += WriteUInt16(reference.Pcs3); } return num; } public int WriteCurveTagDataEntry(IccCurveTagDataEntry value) { int num = 0; if (value.IsIdentityResponse) { num += WriteUInt32(0u); } else if (value.IsGamma) { num += WriteUInt32(1u); num += WriteUFix8(value.Gamma); } else { num += WriteUInt32((uint)value.CurveData.Length); for (int i = 0; i < value.CurveData.Length; i++) { num += WriteUInt16((ushort)Numerics.Clamp(value.CurveData[i] * 65535f + 0.5f, 0f, 65535f)); } } return num; } public int WriteDataTagDataEntry(IccDataTagDataEntry value) { return WriteEmpty(3) + WriteByte(value.IsAscii ? ((byte)1) : ((byte)0)) + WriteArray(value.Data); } public int WriteDateTimeTagDataEntry(IccDateTimeTagDataEntry value) { return WriteDateTime(value.Value); } public int WriteLut16TagDataEntry(IccLut16TagDataEntry value) { int num = WriteByte((byte)value.InputValues.Length); num += WriteByte((byte)value.OutputValues.Length); num += WriteByte(value.ClutValues.GridPointCount[0]); num += WriteEmpty(1); num += WriteMatrix(value.Matrix, isSingle: false); num += WriteUInt16((ushort)value.InputValues[0].Values.Length); num += WriteUInt16((ushort)value.OutputValues[0].Values.Length); IccLut[] inputValues = value.InputValues; foreach (IccLut value2 in inputValues) { num += WriteLut16(value2); } num += WriteClut16(value.ClutValues); inputValues = value.OutputValues; foreach (IccLut value3 in inputValues) { num += WriteLut16(value3); } return num; } public int WriteLut8TagDataEntry(IccLut8TagDataEntry value) { int num = WriteByte((byte)value.InputChannelCount); num += WriteByte((byte)value.OutputChannelCount); num += WriteByte((byte)value.ClutValues.Values[0].Length); num += WriteEmpty(1); num += WriteMatrix(value.Matrix, isSingle: false); IccLut[] inputValues = value.InputValues; foreach (IccLut value2 in inputValues) { num += WriteLut8(value2); } num += WriteClut8(value.ClutValues); inputValues = value.OutputValues; foreach (IccLut value3 in inputValues) { num += WriteLut8(value3); } return num; } public int WriteLutAtoBTagDataEntry(IccLutAToBTagDataEntry value) { long num = dataStream.Position - 8; int num2 = WriteByte((byte)value.InputChannelCount); num2 += WriteByte((byte)value.OutputChannelCount); num2 += WriteEmpty(2); long num3 = 0L; long num4 = 0L; long num5 = 0L; long num6 = 0L; long num7 = 0L; long position = dataStream.Position; dataStream.Position += 20L; if (value.CurveB != null) { num3 = dataStream.Position; num2 += WriteCurves(value.CurveB); num2 += WritePadding(); } if (value.Matrix3x1.HasValue && value.Matrix3x3.HasValue) { num4 = dataStream.Position; num2 += WriteMatrix(value.Matrix3x3.Value, isSingle: false); num2 += WriteMatrix(value.Matrix3x1.Value, isSingle: false); num2 += WritePadding(); } if (value.CurveM != null) { num5 = dataStream.Position; num2 += WriteCurves(value.CurveM); num2 += WritePadding(); } if (value.ClutValues != null) { num6 = dataStream.Position; num2 += WriteClut(value.ClutValues); num2 += WritePadding(); } if (value.CurveA != null) { num7 = dataStream.Position; num2 += WriteCurves(value.CurveA); num2 += WritePadding(); } long position2 = dataStream.Position; dataStream.Position = position; if (num3 != 0L) { num3 -= num; } if (num4 != 0L) { num4 -= num; } if (num5 != 0L) { num5 -= num; } if (num6 != 0L) { num6 -= num; } if (num7 != 0L) { num7 -= num; } num2 += WriteUInt32((uint)num3); num2 += WriteUInt32((uint)num4); num2 += WriteUInt32((uint)num5); num2 += WriteUInt32((uint)num6); num2 += WriteUInt32((uint)num7); dataStream.Position = position2; return num2; } public int WriteLutBtoATagDataEntry(IccLutBToATagDataEntry value) { long num = dataStream.Position - 8; int num2 = WriteByte((byte)value.InputChannelCount); num2 += WriteByte((byte)value.OutputChannelCount); num2 += WriteEmpty(2); long num3 = 0L; long num4 = 0L; long num5 = 0L; long num6 = 0L; long num7 = 0L; long position = dataStream.Position; dataStream.Position += 20L; if (value.CurveB != null) { num3 = dataStream.Position; num2 += WriteCurves(value.CurveB); num2 += WritePadding(); } if (value.Matrix3x1.HasValue && value.Matrix3x3.HasValue) { num4 = dataStream.Position; num2 += WriteMatrix(value.Matrix3x3.Value, isSingle: false); num2 += WriteMatrix(value.Matrix3x1.Value, isSingle: false); num2 += WritePadding(); } if (value.CurveM != null) { num5 = dataStream.Position; num2 += WriteCurves(value.CurveM); num2 += WritePadding(); } if (value.ClutValues != null) { num6 = dataStream.Position; num2 += WriteClut(value.ClutValues); num2 += WritePadding(); } if (value.CurveA != null) { num7 = dataStream.Position; num2 += WriteCurves(value.CurveA); num2 += WritePadding(); } long position2 = dataStream.Position; dataStream.Position = position; if (num3 != 0L) { num3 -= num; } if (num4 != 0L) { num4 -= num; } if (num5 != 0L) { num5 -= num; } if (num6 != 0L) { num6 -= num; } if (num7 != 0L) { num7 -= num; } num2 += WriteUInt32((uint)num3); num2 += WriteUInt32((uint)num4); num2 += WriteUInt32((uint)num5); num2 += WriteUInt32((uint)num6); num2 += WriteUInt32((uint)num7); dataStream.Position = position2; return num2; } public int WriteMeasurementTagDataEntry(IccMeasurementTagDataEntry value) { return WriteUInt32((uint)value.Observer) + WriteXyzNumber(value.XyzBacking) + WriteUInt32((uint)value.Geometry) + WriteUFix16(value.Flare) + WriteUInt32((uint)value.Illuminant); } public int WriteMultiLocalizedUnicodeTagDataEntry(IccMultiLocalizedUnicodeTagDataEntry value) { long num = dataStream.Position - 8; int num2 = value.Texts.Length; int num3 = WriteUInt32((uint)num2); num3 += WriteUInt32(12u); long position = dataStream.Position; dataStream.Position += num2 * 12; IGrouping[] array = (from t in value.Texts group t by t.Text).ToArray(); uint[] array2 = new uint[array.Length]; int[] array3 = new int[array.Length]; for (int num4 = 0; num4 < array.Length; num4++) { array2[num4] = (uint)(dataStream.Position - num); num3 += (array3[num4] = WriteUnicodeString(array[num4].Key)); } long position2 = dataStream.Position; dataStream.Position = position; for (int num5 = 0; num5 < array.Length; num5++) { foreach (IccLocalizedString item in array[num5]) { string name = item.Culture.Name; if (string.IsNullOrEmpty(name)) { num3 += WriteAsciiString("xx", 2, ensureNullTerminator: false); num3 += WriteAsciiString("\0\0", 2, ensureNullTerminator: false); } else if (name.Contains("-")) { string[] array4 = name.Split('-'); num3 += WriteAsciiString(array4[0].ToLower(), 2, ensureNullTerminator: false); num3 += WriteAsciiString(array4[1].ToUpper(), 2, ensureNullTerminator: false); } else { num3 += WriteAsciiString(name, 2, ensureNullTerminator: false); num3 += WriteAsciiString("\0\0", 2, ensureNullTerminator: false); } num3 += WriteUInt32((uint)array3[num5]); num3 += WriteUInt32(array2[num5]); } } dataStream.Position = position2; return num3; } public int WriteMultiProcessElementsTagDataEntry(IccMultiProcessElementsTagDataEntry value) { long num = dataStream.Position - 8; int num2 = WriteUInt16((ushort)value.InputChannelCount); num2 += WriteUInt16((ushort)value.OutputChannelCount); num2 += WriteUInt32((uint)value.Data.Length); long position = dataStream.Position; dataStream.Position += value.Data.Length * 8; IccPositionNumber[] array = new IccPositionNumber[value.Data.Length]; for (int i = 0; i < value.Data.Length; i++) { uint offset = (uint)(dataStream.Position - num); int num3 = WriteMultiProcessElement(value.Data[i]); num2 += WritePadding(); array[i] = new IccPositionNumber(offset, (uint)num3); num2 += num3; } long position2 = dataStream.Position; dataStream.Position = position; IccPositionNumber[] array2 = array; for (int j = 0; j < array2.Length; j++) { IccPositionNumber value2 = array2[j]; num2 += WritePositionNumber(in value2); } dataStream.Position = position2; return num2; } public int WriteNamedColor2TagDataEntry(IccNamedColor2TagDataEntry value) { int num = WriteInt32(value.VendorFlags) + WriteUInt32((uint)value.Colors.Length) + WriteUInt32((uint)value.CoordinateCount) + WriteAsciiString(value.Prefix, 32, ensureNullTerminator: true) + WriteAsciiString(value.Suffix, 32, ensureNullTerminator: true); IccNamedColor[] colors = value.Colors; for (int i = 0; i < colors.Length; i++) { IccNamedColor value2 = colors[i]; num += WriteNamedColor(in value2); } return num; } public int WriteParametricCurveTagDataEntry(IccParametricCurveTagDataEntry value) { return WriteParametricCurve(value.Curve); } public int WriteProfileSequenceDescTagDataEntry(IccProfileSequenceDescTagDataEntry value) { int num = WriteUInt32((uint)value.Descriptions.Length); for (int i = 0; i < value.Descriptions.Length; i++) { num += WriteProfileDescription(in value.Descriptions[i]); } return num; } public int WriteProfileSequenceIdentifierTagDataEntry(IccProfileSequenceIdentifierTagDataEntry value) { long num = dataStream.Position - 8; int num2 = value.Data.Length; int num3 = WriteUInt32((uint)num2); long position = dataStream.Position; dataStream.Position += num2 * 8; IccPositionNumber[] array = new IccPositionNumber[num2]; for (int i = 0; i < num2; i++) { ref IccProfileSequenceIdentifier reference = ref value.Data[i]; uint offset = (uint)(dataStream.Position - num); int num4 = WriteProfileId(reference.Id); num4 += WriteTagDataEntry(new IccMultiLocalizedUnicodeTagDataEntry(reference.Description)); num4 += WritePadding(); array[i] = new IccPositionNumber(offset, (uint)num4); num3 += num4; } long position2 = dataStream.Position; dataStream.Position = position; IccPositionNumber[] array2 = array; for (int j = 0; j < array2.Length; j++) { IccPositionNumber value2 = array2[j]; num3 += WritePositionNumber(in value2); } dataStream.Position = position2; return num3; } public int WriteResponseCurveSet16TagDataEntry(IccResponseCurveSet16TagDataEntry value) { long num = dataStream.Position - 8; int num2 = WriteUInt16(value.ChannelCount); num2 += WriteUInt16((ushort)value.Curves.Length); long position = dataStream.Position; dataStream.Position += value.Curves.Length * 4; uint[] array = new uint[value.Curves.Length]; for (int i = 0; i < value.Curves.Length; i++) { array[i] = (uint)(dataStream.Position - num); num2 += WriteResponseCurve(value.Curves[i]); num2 += WritePadding(); } long position2 = dataStream.Position; dataStream.Position = position; num2 += WriteArray(array); dataStream.Position = position2; return num2; } public int WriteFix16ArrayTagDataEntry(IccFix16ArrayTagDataEntry value) { int num = 0; for (int i = 0; i < value.Data.Length; i++) { num += WriteFix16((double)value.Data[i] * 256.0); } return num; } public int WriteSignatureTagDataEntry(IccSignatureTagDataEntry value) { return WriteAsciiString(value.SignatureData, 4, ensureNullTerminator: false); } public int WriteTextTagDataEntry(IccTextTagDataEntry value) { return WriteAsciiString(value.Text); } public int WriteUFix16ArrayTagDataEntry(IccUFix16ArrayTagDataEntry value) { int num = 0; for (int i = 0; i < value.Data.Length; i++) { num += WriteUFix16(value.Data[i]); } return num; } public int WriteUInt16ArrayTagDataEntry(IccUInt16ArrayTagDataEntry value) { return WriteArray(value.Data); } public int WriteUInt32ArrayTagDataEntry(IccUInt32ArrayTagDataEntry value) { return WriteArray(value.Data); } public int WriteUInt64ArrayTagDataEntry(IccUInt64ArrayTagDataEntry value) { return WriteArray(value.Data); } public int WriteUInt8ArrayTagDataEntry(IccUInt8ArrayTagDataEntry value) { return WriteArray(value.Data); } public int WriteViewingConditionsTagDataEntry(IccViewingConditionsTagDataEntry value) { return WriteXyzNumber(value.IlluminantXyz) + WriteXyzNumber(value.SurroundXyz) + WriteUInt32((uint)value.Illuminant); } public int WriteXyzTagDataEntry(IccXyzTagDataEntry value) { int num = 0; for (int i = 0; i < value.Data.Length; i++) { num += WriteXyzNumber(value.Data[i]); } return num; } public int WriteTextDescriptionTagDataEntry(IccTextDescriptionTagDataEntry value) { int num = 0; if (value.Ascii == null) { num += WriteUInt32(0u); } else { dataStream.Position += 4L; int num2; num += (num2 = WriteAsciiString(value.Ascii + "\0")); dataStream.Position -= num2 + 4; num += WriteUInt32((uint)num2); dataStream.Position += num2; } if (value.Unicode == null) { num += WriteUInt32(0u); num += WriteUInt32(0u); } else { dataStream.Position += 8L; int num2; num += (num2 = WriteUnicodeString(value.Unicode + "\0")); dataStream.Position -= num2 + 8; num += WriteUInt32(value.UnicodeLanguageCode); num += WriteUInt32((uint)(value.Unicode.Length + 1)); dataStream.Position += num2; } if (value.ScriptCode == null) { num += WriteUInt16(0); num += WriteByte(0); num += WriteEmpty(67); } else { dataStream.Position += 3L; int num2; num += (num2 = WriteAsciiString(value.ScriptCode, 67, ensureNullTerminator: true)); dataStream.Position -= num2 + 3; num += WriteUInt16(value.ScriptCodeCode); num += WriteByte((byte)((value.ScriptCode.Length > 66) ? 67u : ((uint)(value.ScriptCode.Length + 1)))); dataStream.Position += num2; } return num; } public int WriteCrdInfoTagDataEntry(IccCrdInfoTagDataEntry value) { int count = 0; WriteString(value.PostScriptProductName); WriteString(value.RenderingIntent0Crd); WriteString(value.RenderingIntent1Crd); WriteString(value.RenderingIntent2Crd); WriteString(value.RenderingIntent3Crd); return count; void WriteString(string text) { int num = ((!string.IsNullOrEmpty(text)) ? (text.Length + 1) : 0); count += WriteUInt32((uint)num); count += WriteAsciiString(text, num, ensureNullTerminator: true); } } public int WriteScreeningTagDataEntry(IccScreeningTagDataEntry value) { int num = 0; num += WriteInt32((int)value.Flags); num += WriteUInt32((uint)value.Channels.Length); for (int i = 0; i < value.Channels.Length; i++) { num += WriteScreeningChannel(in value.Channels[i]); } return num; } public int WriteUcrBgTagDataEntry(IccUcrBgTagDataEntry value) { int num = 0; num += WriteUInt32((uint)value.UcrCurve.Length); for (int i = 0; i < value.UcrCurve.Length; i++) { num += WriteUInt16(value.UcrCurve[i]); } num += WriteUInt32((uint)value.BgCurve.Length); for (int j = 0; j < value.BgCurve.Length; j++) { num += WriteUInt16(value.BgCurve[j]); } return num + WriteAsciiString(value.Description + "\0"); } } internal enum IccClutDataType { Float, UInt8, UInt16 } internal enum IccColorantEncoding : ushort { Unknown, ItuRBt709_2, SmpteRp145, EbuTech3213E, P22 } public enum IccColorSpaceType : uint { CieXyz = 1482250784u, CieLab = 1281450528u, CieLuv = 1282766368u, YCbCr = 1497588338u, CieYxy = 1501067552u, Rgb = 1380401696u, Gray = 1196573017u, Hsv = 1213421088u, Hls = 1212961568u, Cmyk = 1129142603u, Cmy = 1129142560u, Color2 = 843271250u, Color3 = 860048466u, Color4 = 876825682u, Color5 = 893602898u, Color6 = 910380114u, Color7 = 927157330u, Color8 = 943934546u, Color9 = 960711762u, Color10 = 1094929490u, Color11 = 1111706706u, Color12 = 1128483922u, Color13 = 1145261138u, Color14 = 1162038354u, Color15 = 1178815570u } internal enum IccCurveMeasurementEncodings : uint { StatusA = 1400136001u, StatusE = 1400136005u, StatusI = 1400136009u, StatusT = 1400136020u, StatusM = 1400136013u, DinE = 1129193504u, DinEPol = 1129193552u, DinI = 1129205280u, DinIPol = 1129205328u } internal enum IccCurveSegmentSignature : uint { FormulaCurve = 1885434470u, SampledCurve = 1935764838u } internal enum IccDataType { DateTime, Float32, Position, Response16, S15Fixed16, U16Fixed16, U1Fixed15, U8Fixed8, UInt16, UInt32, UInt64, UInt8, Xyz, Ascii } [Flags] public enum IccDeviceAttribute : long { OpacityTransparent = 1L, OpacityReflective = 0L, ReflectivityMatte = 2L, ReflectivityGlossy = 0L, PolarityNegative = 4L, PolarityPositive = 0L, ChromaBlackWhite = 8L, ChromaColor = 0L } internal enum IccFormulaCurveType : ushort { Type1, Type2, Type3 } internal enum IccMeasurementGeometry : uint { Unknown, Degree0To45Or45To0, Degree0ToDOrDTo0 } internal enum IccMultiProcessElementSignature : uint { CurveSet = 1835428980u, Matrix = 1835103334u, Clut = 1668052340u, BAcs = 1648444243u, EAcs = 1698775891u } internal enum IccParametricCurveType : ushort { Type1, Cie122_1996, Iec61966_3, SRgb, Type5 } public enum IccPrimaryPlatformType : uint { NotIdentified = 0u, AppleComputerInc = 1095782476u, MicrosoftCorporation = 1297303124u, SiliconGraphicsInc = 1397180704u, SunMicrosystemsInc = 1398099543u } public enum IccProfileClass : uint { InputDevice = 1935896178u, DisplayDevice = 1835955314u, OutputDevice = 1886549106u, DeviceLink = 1818848875u, ColorSpace = 1936744803u, Abstract = 1633842036u, NamedColor = 1852662636u } [Flags] public enum IccProfileFlag { None = 0, Embedded = 1, NotEmbedded = 0, NotIndependent = 2, Independent = 0 } public enum IccProfileTag : uint { Unknown = 0u, AToB0 = 1093812784u, AToB1 = 1093812785u, AToB2 = 1093812786u, BlueMatrixColumn = 1649957210u, BlueTrc = 1649693251u, BToA0 = 1110589744u, BToA1 = 1110589745u, BToA2 = 1110589746u, BToD0 = 1110590512u, BToD1 = 1110590513u, BToD2 = 1110590514u, BToD3 = 1110590515u, CalibrationDateTime = 1667329140u, CharTarget = 1952543335u, ChromaticAdaptation = 1667785060u, Chromaticity = 1667789421u, ColorantOrder = 1668051567u, ColorantTable = 1668051572u, ColorantTableOut = 1668050804u, ColorimetricIntentImageStat = 1667852659u, Copyright = 1668313716u, CrdInfo = 1668441193u, Data = 1684108385u, DateTime = 1685350765u, DeviceManufacturerDescription = 1684893284u, DeviceModelDescription = 1684890724u, DeviceSettings = 1684371059u, DToB0 = 1144144432u, DToB1 = 1144144432u, DToB2 = 1144144432u, DToB3 = 1144144432u, Gamut = 1734438260u, GrayTrc = 1800688195u, GreenMatrixColumn = 1733843290u, GreenTrc = 1733579331u, Luminance = 1819635049u, Measurement = 1835360627u, MediaBlackPoint = 1651208308u, MediaWhitePoint = 2004119668u, NamedColor = 1852010348u, NamedColor2 = 1852009522u, OutputResponse = 1919251312u, PerceptualRenderingIntentGamut = 1919510320u, Preview0 = 1886545200u, Preview1 = 1886545201u, Preview2 = 1886545202u, ProfileDescription = 1684370275u, ProfileSequenceDescription = 1886610801u, PostScript2Crd0 = 1886610480u, PostScript2Crd1 = 1886610481u, PostScript2Crd2 = 1886610482u, PostScript2Crd3 = 1886610483u, PostScript2Csa = 1886597747u, PostScript2RenderingIntent = 1886597737u, RedMatrixColumn = 1918392666u, RedTrc = 1918128707u, SaturationRenderingIntentGamut = 1919510322u, ScreeningDescription = 1935897188u, Screening = 1935897198u, Technology = 1952801640u, UcrBgSpecification = 1650877472u, ViewingCondDescription = 1987405156u, ViewingConditions = 1986618743u } public enum IccRenderingIntent : uint { Perceptual, MediaRelativeColorimetric, Saturation, AbsoluteColorimetric } [Flags] internal enum IccScreeningFlag { None = 0, DefaultScreens = 1, NotDefaultScreens = 0, UnitLinesPerInch = 2, UnitLinesPerCm = 0 } internal enum IccScreeningSpotType { Unknown, PrinterDefault, Round, Diamond, Ellipse, Line, Square, Cross } internal enum IccSignatureName : uint { Unknown = 0u, SceneColorimetryEstimates = 1935896421u, SceneAppearanceEstimates = 1935765605u, FocalPlaneColorimetryEstimates = 1718641509u, ReflectionHardcopyOriginalColorimetry = 1919446883u, ReflectionPrintOutputColorimetry = 1919971171u, PerceptualReferenceMediumGamut = 1886547303u, FilmScanner = 1718838126u, DigitalCamera = 1684234605u, ReflectiveScanner = 1920164718u, InkJetPrinter = 1768580468u, ThermalWaxPrinter = 1953980792u, ElectrophotographicPrinter = 1701865583u, ElectrostaticPrinter = 1702065249u, DyeSublimationPrinter = 1685288290u, PhotographicPaperPrinter = 1919969391u, FilmWriter = 1718645358u, VideoMonitor = 1986618477u, VideoCamera = 1986618467u, ProjectionTelevision = 1886024822u, CathodeRayTubeDisplay = 1129468960u, PassiveMatrixDisplay = 1347240992u, ActiveMatrixDisplay = 1095582752u, PhotoCD = 1263551300u, PhotographicImageSetter = 1768777587u, Gravure = 1735549302u, OffsetLithography = 1868981875u, Silkscreen = 1936288875u, Flexography = 1718379896u, MotionPictureFilmScanner = 1836082803u, MotionPictureFilmRecorder = 1836082802u, DigitalMotionPictureCamera = 1684893795u, DigitalCinemaProjector = 1684236912u } internal enum IccStandardIlluminant : uint { Unknown, D50, D65, D93, F2, D55, A, EquiPowerE, F8 } internal enum IccStandardObserver : uint { Unknown, Cie1931Observer, Cie1964Observer } public enum IccTypeSignature : uint { Unknown = 0u, Chromaticity = 1667789421u, ColorantOrder = 1668051567u, ColorantTable = 1668051572u, Curve = 1668641398u, Data = 1684108385u, DateTime = 1685350765u, Lut16 = 1835430962u, Lut8 = 1835430961u, LutAToB = 1832993312u, LutBToA = 1833058592u, Measurement = 1835360627u, MultiLocalizedUnicode = 1835824483u, MultiProcessElements = 1836082548u, NamedColor2 = 1852009522u, ParametricCurve = 1885434465u, ProfileSequenceDesc = 1886610801u, ProfileSequenceIdentifier = 1886611812u, ResponseCurveSet16 = 1919120178u, S15Fixed16Array = 1936077618u, Signature = 1936287520u, Text = 1952807028u, U16Fixed16Array = 1969632050u, UInt16Array = 1969828150u, UInt32Array = 1969828658u, UInt64Array = 1969829428u, UInt8Array = 1969827896u, ViewingConditions = 1986618743u, Xyz = 1482250784u, TextDescription = 1684370275u, CrdInfo = 1668441193u, Screening = 1935897198u, UcrBg = 1650877472u, DeviceSettings = 1684371059u, NamedColor = 1852010348u } public class InvalidIccProfileException : Exception { public InvalidIccProfileException(string message) : base(message) { } public InvalidIccProfileException(string message, Exception inner) : base(message, inner) { } } public sealed class IccProfile : IDeepCloneable { private readonly byte[] data; private IccTagDataEntry[] entries; private IccProfileHeader header; public IccProfileHeader Header { get { InitializeHeader(); return header; } set { header = value; } } public IccTagDataEntry[] Entries { get { InitializeEntries(); return entries; } } public IccProfile() : this((byte[])null) { } public IccProfile(byte[] data) { this.data = data; } internal IccProfile(IccProfileHeader header, IccTagDataEntry[] entries) { this.header = header ?? throw new ArgumentNullException("header"); this.entries = entries ?? throw new ArgumentNullException("entries"); } private IccProfile(IccProfile other) { Guard.NotNull(other, "other"); data = other.ToByteArray(); } public IccProfile DeepClone() { return new IccProfile(this); } public static IccProfileId CalculateHash(byte[] data) { Guard.NotNull(data, "data"); Guard.IsTrue(data.Length >= 128, "data", "Data length must be at least 128 to be a valid profile header"); byte[] array = new byte[24]; Buffer.BlockCopy(data, 44, array, 0, 4); Buffer.BlockCopy(data, 64, array, 4, 4); Buffer.BlockCopy(data, 84, array, 8, 16); using MD5 mD = MD5.Create(); try { Array.Clear(data, 44, 4); Array.Clear(data, 64, 4); Array.Clear(data, 84, 16); return new IccDataReader(mD.ComputeHash(data)).ReadProfileId(); } finally { Buffer.BlockCopy(array, 0, data, 44, 4); Buffer.BlockCopy(array, 4, data, 64, 4); Buffer.BlockCopy(array, 8, data, 84, 16); } } public bool CheckIsValid() { bool flag = true; if (data != null) { flag = data.Length >= 128 && data.Length >= Header.Size; } if (flag && Enum.IsDefined(typeof(IccColorSpaceType), Header.DataColorSpace) && Enum.IsDefined(typeof(IccColorSpaceType), Header.ProfileConnectionSpace) && Enum.IsDefined(typeof(IccRenderingIntent), Header.RenderingIntent) && Header.Size >= 128) { return Header.Size < 50000000; } return false; } public byte[] ToByteArray() { if (data != null) { byte[] array = new byte[data.Length]; Buffer.BlockCopy(data, 0, array, 0, array.Length); return array; } return new IccWriter().Write(this); } private void InitializeHeader() { if (header == null) { if (data == null) { header = new IccProfileHeader(); return; } IccReader iccReader = new IccReader(); header = iccReader.ReadHeader(data); } } private void InitializeEntries() { if (entries == null) { if (data == null) { entries = Array.Empty(); return; } IccReader iccReader = new IccReader(); entries = iccReader.ReadTagData(data); } } } public sealed class IccProfileHeader { public uint Size { get; set; } public string CmmType { get; set; } public IccVersion Version { get; set; } public IccProfileClass Class { get; set; } public IccColorSpaceType DataColorSpace { get; set; } public IccColorSpaceType ProfileConnectionSpace { get; set; } public DateTime CreationDate { get; set; } public string FileSignature { get; set; } public IccPrimaryPlatformType PrimaryPlatformSignature { get; set; } public IccProfileFlag Flags { get; set; } public uint DeviceManufacturer { get; set; } public uint DeviceModel { get; set; } public IccDeviceAttribute DeviceAttributes { get; set; } public IccRenderingIntent RenderingIntent { get; set; } public Vector3 PcsIlluminant { get; set; } public string CreatorSignature { get; set; } public IccProfileId Id { get; set; } } internal sealed class IccReader { public IccProfile Read(byte[] data) { Guard.NotNull(data, "data"); Guard.IsTrue(data.Length >= 128, "data", "Data length must be at least 128 to be a valid ICC profile"); IccDataReader reader = new IccDataReader(data); IccProfileHeader header = ReadHeader(reader); IccTagDataEntry[] entries = ReadTagData(reader); return new IccProfile(header, entries); } public IccProfileHeader ReadHeader(byte[] data) { Guard.NotNull(data, "data"); Guard.IsTrue(data.Length >= 128, "data", "Data length must be at least 128 to be a valid profile header"); IccDataReader reader = new IccDataReader(data); return ReadHeader(reader); } public IccTagDataEntry[] ReadTagData(byte[] data) { Guard.NotNull(data, "data"); Guard.IsTrue(data.Length >= 128, "data", "Data length must be at least 128 to be a valid ICC profile"); IccDataReader reader = new IccDataReader(data); return ReadTagData(reader); } private IccProfileHeader ReadHeader(IccDataReader reader) { reader.SetIndex(0); return new IccProfileHeader { Size = reader.ReadUInt32(), CmmType = reader.ReadAsciiString(4), Version = reader.ReadVersionNumber(), Class = (IccProfileClass)reader.ReadUInt32(), DataColorSpace = (IccColorSpaceType)reader.ReadUInt32(), ProfileConnectionSpace = (IccColorSpaceType)reader.ReadUInt32(), CreationDate = reader.ReadDateTime(), FileSignature = reader.ReadAsciiString(4), PrimaryPlatformSignature = (IccPrimaryPlatformType)reader.ReadUInt32(), Flags = (IccProfileFlag)reader.ReadInt32(), DeviceManufacturer = reader.ReadUInt32(), DeviceModel = reader.ReadUInt32(), DeviceAttributes = (IccDeviceAttribute)reader.ReadInt64(), RenderingIntent = (IccRenderingIntent)reader.ReadUInt32(), PcsIlluminant = reader.ReadXyzNumber(), CreatorSignature = reader.ReadAsciiString(4), Id = reader.ReadProfileId() }; } private IccTagDataEntry[] ReadTagData(IccDataReader reader) { IccTagTableEntry[] array = ReadTagTable(reader); List list = new List(array.Length); Dictionary dictionary = new Dictionary(); IccTagTableEntry[] array2 = array; for (int i = 0; i < array2.Length; i++) { IccTagTableEntry info = array2[i]; IccTagDataEntry iccTagDataEntry; if (dictionary.ContainsKey(info.Offset)) { iccTagDataEntry = dictionary[info.Offset]; } else { try { iccTagDataEntry = reader.ReadTagDataEntry(info); } catch { continue; } dictionary.Add(info.Offset, iccTagDataEntry); } iccTagDataEntry.TagSignature = info.Signature; list.Add(iccTagDataEntry); } return list.ToArray(); } private IccTagTableEntry[] ReadTagTable(IccDataReader reader) { reader.SetIndex(128); uint num = reader.ReadUInt32(); if (num > 100) { return Array.Empty(); } List list = new List((int)num); for (int i = 0; i < num; i++) { uint signature = reader.ReadUInt32(); uint num2 = reader.ReadUInt32(); uint num3 = reader.ReadUInt32(); if (num2 < reader.DataLength && num3 < reader.DataLength - 128) { list.Add(new IccTagTableEntry((IccProfileTag)signature, num2, num3)); } } return list.ToArray(); } } public abstract class IccTagDataEntry : IEquatable { public IccTypeSignature Signature { get; } public IccProfileTag TagSignature { get; set; } protected IccTagDataEntry(IccTypeSignature signature) : this(signature, IccProfileTag.Unknown) { } protected IccTagDataEntry(IccTypeSignature signature, IccProfileTag tagSignature) { Signature = signature; TagSignature = tagSignature; } public override bool Equals(object obj) { if (obj is IccTagDataEntry other) { return Equals(other); } return false; } public virtual bool Equals(IccTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } return Signature == other.Signature; } public override int GetHashCode() { return Signature.GetHashCode(); } } internal sealed class IccWriter { public byte[] Write(IccProfile profile) { Guard.NotNull(profile, "profile"); using IccDataWriter iccDataWriter = new IccDataWriter(); IccTagTableEntry[] table = WriteTagData(iccDataWriter, profile.Entries); WriteTagTable(iccDataWriter, table); WriteHeader(iccDataWriter, profile.Header); return iccDataWriter.GetData(); } private void WriteHeader(IccDataWriter writer, IccProfileHeader header) { writer.SetIndex(0); writer.WriteUInt32(writer.Length); writer.WriteAsciiString(header.CmmType, 4, ensureNullTerminator: false); writer.WriteVersionNumber(header.Version); writer.WriteUInt32((uint)header.Class); writer.WriteUInt32((uint)header.DataColorSpace); writer.WriteUInt32((uint)header.ProfileConnectionSpace); writer.WriteDateTime(header.CreationDate); writer.WriteAsciiString("acsp"); writer.WriteUInt32((uint)header.PrimaryPlatformSignature); writer.WriteInt32((int)header.Flags); writer.WriteUInt32(header.DeviceManufacturer); writer.WriteUInt32(header.DeviceModel); writer.WriteInt64((long)header.DeviceAttributes); writer.WriteUInt32((uint)header.RenderingIntent); writer.WriteXyzNumber(header.PcsIlluminant); writer.WriteAsciiString(header.CreatorSignature, 4, ensureNullTerminator: false); writer.WriteProfileId(IccProfile.CalculateHash(writer.GetData())); } private void WriteTagTable(IccDataWriter writer, IccTagTableEntry[] table) { writer.SetIndex(128); writer.WriteUInt32((uint)table.Length); for (int i = 0; i < table.Length; i++) { IccTagTableEntry iccTagTableEntry = table[i]; writer.WriteUInt32((uint)iccTagTableEntry.Signature); writer.WriteUInt32(iccTagTableEntry.Offset); writer.WriteUInt32(iccTagTableEntry.DataSize); } } private IccTagTableEntry[] WriteTagData(IccDataWriter writer, IccTagDataEntry[] entries) { IEnumerable> enumerable = from t in entries group t by t; writer.SetIndex(132 + entries.Length * 12); List list = new List(); foreach (IGrouping item in enumerable) { writer.WriteTagDataEntry(item.Key, out var table); foreach (IccTagDataEntry item2 in item) { list.Add(new IccTagTableEntry(item2.TagSignature, table.Offset, table.DataSize)); } } return list.ToArray(); } } internal sealed class IccBAcsProcessElement : IccMultiProcessElement, IEquatable { public IccBAcsProcessElement(int inChannelCount, int outChannelCount) : base(IccMultiProcessElementSignature.BAcs, inChannelCount, outChannelCount) { } public bool Equals(IccBAcsProcessElement other) { return base.Equals(other); } } internal sealed class IccClutProcessElement : IccMultiProcessElement, IEquatable { public IccClut ClutValue { get; } public IccClutProcessElement(IccClut clutValue) : base(IccMultiProcessElementSignature.Clut, clutValue?.InputChannelCount ?? 1, clutValue?.OutputChannelCount ?? 1) { ClutValue = clutValue ?? throw new ArgumentNullException("clutValue"); } public override bool Equals(IccMultiProcessElement other) { if (base.Equals(other) && other is IccClutProcessElement iccClutProcessElement) { return ClutValue.Equals(iccClutProcessElement.ClutValue); } return false; } public bool Equals(IccClutProcessElement other) { return Equals((IccMultiProcessElement)other); } } internal sealed class IccCurveSetProcessElement : IccMultiProcessElement, IEquatable { public IccOneDimensionalCurve[] Curves { get; } public IccCurveSetProcessElement(IccOneDimensionalCurve[] curves) : base(IccMultiProcessElementSignature.CurveSet, (curves == null) ? 1 : curves.Length, (curves == null) ? 1 : curves.Length) { Curves = curves ?? throw new ArgumentNullException("curves"); } public override bool Equals(IccMultiProcessElement other) { if (base.Equals(other) && other is IccCurveSetProcessElement iccCurveSetProcessElement) { return Curves.AsSpan().SequenceEqual(iccCurveSetProcessElement.Curves); } return false; } public bool Equals(IccCurveSetProcessElement other) { return Equals((IccMultiProcessElement)other); } } internal sealed class IccEAcsProcessElement : IccMultiProcessElement, IEquatable { public IccEAcsProcessElement(int inChannelCount, int outChannelCount) : base(IccMultiProcessElementSignature.EAcs, inChannelCount, outChannelCount) { } public bool Equals(IccEAcsProcessElement other) { return base.Equals(other); } } internal sealed class IccMatrixProcessElement : IccMultiProcessElement, IEquatable { public DenseMatrix MatrixIxO { get; } public float[] MatrixOx1 { get; } public IccMatrixProcessElement(float[,] matrixIxO, float[] matrixOx1) : base(IccMultiProcessElementSignature.Matrix, matrixIxO?.GetLength(0) ?? 1, matrixIxO?.GetLength(1) ?? 1) { Guard.NotNull(matrixIxO, "matrixIxO"); Guard.NotNull(matrixOx1, "matrixOx1"); Guard.IsTrue(matrixIxO.GetLength(1) == matrixOx1.Length, "matrixIxO,matrixIxO", "Output channel length must match"); MatrixIxO = matrixIxO; MatrixOx1 = matrixOx1; } public override bool Equals(IccMultiProcessElement other) { if (base.Equals(other) && other is IccMatrixProcessElement iccMatrixProcessElement) { if (EqualsMatrix(iccMatrixProcessElement)) { return MatrixOx1.AsSpan().SequenceEqual(iccMatrixProcessElement.MatrixOx1); } return false; } return false; } public bool Equals(IccMatrixProcessElement other) { return Equals((IccMultiProcessElement)other); } private bool EqualsMatrix(IccMatrixProcessElement element) { return MatrixIxO.Equals(element.MatrixIxO); } } internal abstract class IccMultiProcessElement : IEquatable { public IccMultiProcessElementSignature Signature { get; } public int InputChannelCount { get; } public int OutputChannelCount { get; } protected IccMultiProcessElement(IccMultiProcessElementSignature signature, int inChannelCount, int outChannelCount) { Guard.MustBeBetweenOrEqualTo(inChannelCount, 1, 15, "inChannelCount"); Guard.MustBeBetweenOrEqualTo(outChannelCount, 1, 15, "outChannelCount"); Signature = signature; InputChannelCount = inChannelCount; OutputChannelCount = outChannelCount; } public virtual bool Equals(IccMultiProcessElement other) { if (other == null) { return false; } if (this == other) { return true; } if (Signature == other.Signature && InputChannelCount == other.InputChannelCount) { return OutputChannelCount == other.OutputChannelCount; } return false; } } internal sealed class IccChromaticityTagDataEntry : IccTagDataEntry, IEquatable { public int ChannelCount => ChannelValues.Length; public IccColorantEncoding ColorantType { get; } public double[][] ChannelValues { get; } public IccChromaticityTagDataEntry(IccColorantEncoding colorantType) : this(colorantType, GetColorantArray(colorantType), IccProfileTag.Unknown) { } public IccChromaticityTagDataEntry(double[][] channelValues) : this(IccColorantEncoding.Unknown, channelValues, IccProfileTag.Unknown) { } public IccChromaticityTagDataEntry(IccColorantEncoding colorantType, IccProfileTag tagSignature) : this(colorantType, GetColorantArray(colorantType), tagSignature) { } public IccChromaticityTagDataEntry(double[][] channelValues, IccProfileTag tagSignature) : this(IccColorantEncoding.Unknown, channelValues, tagSignature) { } private IccChromaticityTagDataEntry(IccColorantEncoding colorantType, double[][] channelValues, IccProfileTag tagSignature) : base(IccTypeSignature.Chromaticity, tagSignature) { Guard.NotNull(channelValues, "channelValues"); Guard.MustBeBetweenOrEqualTo(channelValues.Length, 1, 15, "channelValues"); ColorantType = colorantType; ChannelValues = channelValues; int channelLength = channelValues[0].Length; Guard.IsFalse(channelValues.Any((double[] t) => t == null || t.Length != channelLength), "channelValues", "The number of values per channel is not the same for all channels"); } public override bool Equals(IccTagDataEntry other) { if (other is IccChromaticityTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccChromaticityTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && ColorantType == other.ColorantType) { return EqualsChannelValues(other); } return false; } public override bool Equals(object obj) { if (obj is IccChromaticityTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, ColorantType, ChannelValues); } private static double[][] GetColorantArray(IccColorantEncoding colorantType) { return colorantType switch { IccColorantEncoding.EbuTech3213E => new double[3][] { new double[2] { 0.64, 0.33 }, new double[2] { 0.29, 0.6 }, new double[2] { 0.15, 0.06 } }, IccColorantEncoding.ItuRBt709_2 => new double[3][] { new double[2] { 0.64, 0.33 }, new double[2] { 0.3, 0.6 }, new double[2] { 0.15, 0.06 } }, IccColorantEncoding.P22 => new double[3][] { new double[2] { 0.625, 0.34 }, new double[2] { 0.28, 0.605 }, new double[2] { 0.155, 0.07 } }, IccColorantEncoding.SmpteRp145 => new double[3][] { new double[2] { 0.63, 0.34 }, new double[2] { 0.31, 0.595 }, new double[2] { 0.155, 0.07 } }, _ => throw new ArgumentException("Unrecognized colorant encoding"), }; } private bool EqualsChannelValues(IccChromaticityTagDataEntry entry) { if (ChannelValues.Length != entry.ChannelValues.Length) { return false; } for (int i = 0; i < ChannelValues.Length; i++) { if (!ChannelValues[i].AsSpan().SequenceEqual(entry.ChannelValues[i])) { return false; } } return true; } } internal sealed class IccColorantOrderTagDataEntry : IccTagDataEntry, IEquatable { public byte[] ColorantNumber { get; } public IccColorantOrderTagDataEntry(byte[] colorantNumber) : this(colorantNumber, IccProfileTag.Unknown) { } public IccColorantOrderTagDataEntry(byte[] colorantNumber, IccProfileTag tagSignature) : base(IccTypeSignature.ColorantOrder, tagSignature) { Guard.NotNull(colorantNumber, "colorantNumber"); Guard.MustBeBetweenOrEqualTo(colorantNumber.Length, 1, 15, "colorantNumber"); ColorantNumber = colorantNumber; } public override bool Equals(IccTagDataEntry other) { if (other is IccColorantOrderTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccColorantOrderTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return ColorantNumber.AsSpan().SequenceEqual(other.ColorantNumber); } return false; } public override bool Equals(object obj) { if (obj is IccColorantOrderTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, ColorantNumber); } } internal sealed class IccColorantTableTagDataEntry : IccTagDataEntry, IEquatable { public IccColorantTableEntry[] ColorantData { get; } public IccColorantTableTagDataEntry(IccColorantTableEntry[] colorantData) : this(colorantData, IccProfileTag.Unknown) { } public IccColorantTableTagDataEntry(IccColorantTableEntry[] colorantData, IccProfileTag tagSignature) : base(IccTypeSignature.ColorantTable, tagSignature) { Guard.NotNull(colorantData, "colorantData"); Guard.MustBeBetweenOrEqualTo(colorantData.Length, 1, 15, "colorantData"); ColorantData = colorantData; } public override bool Equals(IccTagDataEntry other) { if (other is IccColorantTableTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccColorantTableTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return ColorantData.AsSpan().SequenceEqual(other.ColorantData); } return false; } public override bool Equals(object obj) { if (obj is IccColorantTableTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, ColorantData); } } internal sealed class IccCrdInfoTagDataEntry : IccTagDataEntry, IEquatable { public string PostScriptProductName { get; } public string RenderingIntent0Crd { get; } public string RenderingIntent1Crd { get; } public string RenderingIntent2Crd { get; } public string RenderingIntent3Crd { get; } public IccCrdInfoTagDataEntry(string postScriptProductName, string renderingIntent0Crd, string renderingIntent1Crd, string renderingIntent2Crd, string renderingIntent3Crd) : this(postScriptProductName, renderingIntent0Crd, renderingIntent1Crd, renderingIntent2Crd, renderingIntent3Crd, IccProfileTag.Unknown) { } public IccCrdInfoTagDataEntry(string postScriptProductName, string renderingIntent0Crd, string renderingIntent1Crd, string renderingIntent2Crd, string renderingIntent3Crd, IccProfileTag tagSignature) : base(IccTypeSignature.CrdInfo, tagSignature) { PostScriptProductName = postScriptProductName; RenderingIntent0Crd = renderingIntent0Crd; RenderingIntent1Crd = renderingIntent1Crd; RenderingIntent2Crd = renderingIntent2Crd; RenderingIntent3Crd = renderingIntent3Crd; } public override bool Equals(IccTagDataEntry other) { if (other is IccCrdInfoTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccCrdInfoTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && string.Equals(PostScriptProductName, other.PostScriptProductName) && string.Equals(RenderingIntent0Crd, other.RenderingIntent0Crd) && string.Equals(RenderingIntent1Crd, other.RenderingIntent1Crd) && string.Equals(RenderingIntent2Crd, other.RenderingIntent2Crd)) { return string.Equals(RenderingIntent3Crd, other.RenderingIntent3Crd); } return false; } public override bool Equals(object obj) { if (obj is IccCrdInfoTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, PostScriptProductName, RenderingIntent0Crd, RenderingIntent1Crd, RenderingIntent2Crd, RenderingIntent3Crd); } } internal sealed class IccCurveTagDataEntry : IccTagDataEntry, IEquatable { public float[] CurveData { get; } public float Gamma { get { if (!IsGamma) { return 0f; } return CurveData[0]; } } public bool IsIdentityResponse => CurveData.Length == 0; public bool IsGamma => CurveData.Length == 1; public IccCurveTagDataEntry() : this(Array.Empty(), IccProfileTag.Unknown) { } public IccCurveTagDataEntry(float gamma) : this(new float[1] { gamma }, IccProfileTag.Unknown) { } public IccCurveTagDataEntry(float[] curveData) : this(curveData, IccProfileTag.Unknown) { } public IccCurveTagDataEntry(IccProfileTag tagSignature) : this(Array.Empty(), tagSignature) { } public IccCurveTagDataEntry(float gamma, IccProfileTag tagSignature) : this(new float[1] { gamma }, tagSignature) { } public IccCurveTagDataEntry(float[] curveData, IccProfileTag tagSignature) : base(IccTypeSignature.Curve, tagSignature) { CurveData = curveData ?? Array.Empty(); } public override bool Equals(IccTagDataEntry other) { if (other is IccCurveTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccCurveTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return CurveData.AsSpan().SequenceEqual(other.CurveData); } return false; } public override bool Equals(object obj) { if (obj is IccCurveTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, CurveData); } } internal sealed class IccDataTagDataEntry : IccTagDataEntry, IEquatable { public byte[] Data { get; } public bool IsAscii { get; } public string AsciiString { get { if (!IsAscii) { return null; } return Encoding.ASCII.GetString(Data, 0, Data.Length); } } public IccDataTagDataEntry(byte[] data) : this(data, isAscii: false, IccProfileTag.Unknown) { } public IccDataTagDataEntry(byte[] data, bool isAscii) : this(data, isAscii, IccProfileTag.Unknown) { } public IccDataTagDataEntry(byte[] data, bool isAscii, IccProfileTag tagSignature) : base(IccTypeSignature.Data, tagSignature) { Data = data ?? throw new ArgumentException("data"); IsAscii = isAscii; } public override bool Equals(IccTagDataEntry other) { if (other is IccDataTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccDataTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && Data.AsSpan().SequenceEqual(other.Data)) { return IsAscii == other.IsAscii; } return false; } public override bool Equals(object obj) { if (obj is IccDataTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Data, IsAscii); } } internal sealed class IccDateTimeTagDataEntry : IccTagDataEntry, IEquatable { public DateTime Value { get; } public IccDateTimeTagDataEntry(DateTime value) : this(value, IccProfileTag.Unknown) { } public IccDateTimeTagDataEntry(DateTime value, IccProfileTag tagSignature) : base(IccTypeSignature.DateTime, tagSignature) { Value = value; } public override bool Equals(IccTagDataEntry other) { if (other is IccDateTimeTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccDateTimeTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Value.Equals(other.Value); } return false; } public override bool Equals(object obj) { if (obj is IccDateTimeTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Value); } } internal sealed class IccFix16ArrayTagDataEntry : IccTagDataEntry, IEquatable { public float[] Data { get; } public IccFix16ArrayTagDataEntry(float[] data) : this(data, IccProfileTag.Unknown) { } public IccFix16ArrayTagDataEntry(float[] data, IccProfileTag tagSignature) : base(IccTypeSignature.S15Fixed16Array, tagSignature) { Data = data ?? throw new ArgumentNullException("data"); } public override bool Equals(IccTagDataEntry other) { if (other is IccFix16ArrayTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccFix16ArrayTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Data.AsSpan().SequenceEqual(other.Data); } return false; } public override bool Equals(object obj) { if (obj is IccFix16ArrayTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Data); } } internal sealed class IccLut16TagDataEntry : IccTagDataEntry, IEquatable { private static readonly float[,] IdentityMatrix = new float[3, 3] { { 1f, 0f, 0f }, { 0f, 1f, 0f }, { 0f, 0f, 1f } }; public int InputChannelCount => InputValues.Length; public int OutputChannelCount => OutputValues.Length; public Matrix4x4 Matrix { get; } public IccLut[] InputValues { get; } public IccClut ClutValues { get; } public IccLut[] OutputValues { get; } public IccLut16TagDataEntry(IccLut[] inputValues, IccClut clutValues, IccLut[] outputValues) : this(IdentityMatrix, inputValues, clutValues, outputValues, IccProfileTag.Unknown) { } public IccLut16TagDataEntry(IccLut[] inputValues, IccClut clutValues, IccLut[] outputValues, IccProfileTag tagSignature) : this(IdentityMatrix, inputValues, clutValues, outputValues, tagSignature) { } public IccLut16TagDataEntry(float[,] matrix, IccLut[] inputValues, IccClut clutValues, IccLut[] outputValues) : this(matrix, inputValues, clutValues, outputValues, IccProfileTag.Unknown) { } public IccLut16TagDataEntry(float[,] matrix, IccLut[] inputValues, IccClut clutValues, IccLut[] outputValues, IccProfileTag tagSignature) : base(IccTypeSignature.Lut16, tagSignature) { Guard.NotNull(matrix, "matrix"); Guard.IsTrue(matrix.GetLength(0) == 3 && matrix.GetLength(1) == 3, "matrix", "Matrix must have a size of three by three"); Matrix = CreateMatrix(matrix); InputValues = inputValues ?? throw new ArgumentNullException("inputValues"); ClutValues = clutValues ?? throw new ArgumentNullException("clutValues"); OutputValues = outputValues ?? throw new ArgumentNullException("outputValues"); Guard.IsTrue(InputChannelCount == clutValues.InputChannelCount, "clutValues", "Input channel count does not match the CLUT size"); Guard.IsTrue(OutputChannelCount == clutValues.OutputChannelCount, "clutValues", "Output channel count does not match the CLUT size"); } public override bool Equals(IccTagDataEntry other) { if (other is IccLut16TagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccLut16TagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && Matrix.Equals(other.Matrix) && InputValues.AsSpan().SequenceEqual(other.InputValues) && ClutValues.Equals(other.ClutValues)) { return OutputValues.AsSpan().SequenceEqual(other.OutputValues); } return false; } public override bool Equals(object obj) { if (obj is IccLut16TagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Matrix, InputValues, ClutValues, OutputValues); } private static Matrix4x4 CreateMatrix(float[,] matrix) { return new Matrix4x4(matrix[0, 0], matrix[0, 1], matrix[0, 2], 0f, matrix[1, 0], matrix[1, 1], matrix[1, 2], 0f, matrix[2, 0], matrix[2, 1], matrix[2, 2], 0f, 0f, 0f, 0f, 1f); } } internal sealed class IccLut8TagDataEntry : IccTagDataEntry, IEquatable { private static readonly float[,] IdentityMatrix = new float[3, 3] { { 1f, 0f, 0f }, { 0f, 1f, 0f }, { 0f, 0f, 1f } }; public int InputChannelCount => InputValues.Length; public int OutputChannelCount => OutputValues.Length; public Matrix4x4 Matrix { get; } public IccLut[] InputValues { get; } public IccClut ClutValues { get; } public IccLut[] OutputValues { get; } public IccLut8TagDataEntry(IccLut[] inputValues, IccClut clutValues, IccLut[] outputValues) : this(IdentityMatrix, inputValues, clutValues, outputValues, IccProfileTag.Unknown) { } public IccLut8TagDataEntry(IccLut[] inputValues, IccClut clutValues, IccLut[] outputValues, IccProfileTag tagSignature) : this(IdentityMatrix, inputValues, clutValues, outputValues, tagSignature) { } public IccLut8TagDataEntry(float[,] matrix, IccLut[] inputValues, IccClut clutValues, IccLut[] outputValues) : this(matrix, inputValues, clutValues, outputValues, IccProfileTag.Unknown) { } public IccLut8TagDataEntry(float[,] matrix, IccLut[] inputValues, IccClut clutValues, IccLut[] outputValues, IccProfileTag tagSignature) : base(IccTypeSignature.Lut8, tagSignature) { Guard.NotNull(matrix, "matrix"); Guard.IsTrue(matrix.GetLength(0) == 3 && matrix.GetLength(1) == 3, "matrix", "Matrix must have a size of three by three"); Matrix = CreateMatrix(matrix); InputValues = inputValues ?? throw new ArgumentNullException("inputValues"); ClutValues = clutValues ?? throw new ArgumentNullException("clutValues"); OutputValues = outputValues ?? throw new ArgumentNullException("outputValues"); Guard.IsTrue(InputChannelCount == clutValues.InputChannelCount, "clutValues", "Input channel count does not match the CLUT size"); Guard.IsTrue(OutputChannelCount == clutValues.OutputChannelCount, "clutValues", "Output channel count does not match the CLUT size"); Guard.IsFalse(inputValues.Any((IccLut t) => t.Values.Length != 256), "inputValues", "Input lookup table has to have a length of 256"); Guard.IsFalse(outputValues.Any((IccLut t) => t.Values.Length != 256), "outputValues", "Output lookup table has to have a length of 256"); } public override bool Equals(IccTagDataEntry other) { if (other is IccLut8TagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccLut8TagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && Matrix.Equals(other.Matrix) && InputValues.AsSpan().SequenceEqual(other.InputValues) && ClutValues.Equals(other.ClutValues)) { return OutputValues.AsSpan().SequenceEqual(other.OutputValues); } return false; } public override bool Equals(object obj) { if (obj is IccLut8TagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Matrix, InputValues, ClutValues, OutputValues); } private Matrix4x4 CreateMatrix(float[,] matrix) { return new Matrix4x4(matrix[0, 0], matrix[0, 1], matrix[0, 2], 0f, matrix[1, 0], matrix[1, 1], matrix[1, 2], 0f, matrix[2, 0], matrix[2, 1], matrix[2, 2], 0f, 0f, 0f, 0f, 1f); } } internal sealed class IccLutAToBTagDataEntry : IccTagDataEntry, IEquatable { public int InputChannelCount { get; } public int OutputChannelCount { get; } public Matrix4x4? Matrix3x3 { get; } public Vector3? Matrix3x1 { get; } public IccClut ClutValues { get; } public IccTagDataEntry[] CurveB { get; } public IccTagDataEntry[] CurveM { get; } public IccTagDataEntry[] CurveA { get; } public IccLutAToBTagDataEntry(IccTagDataEntry[] curveB, float[,] matrix3x3, float[] matrix3x1, IccTagDataEntry[] curveM, IccClut clutValues, IccTagDataEntry[] curveA) : this(curveB, matrix3x3, matrix3x1, curveM, clutValues, curveA, IccProfileTag.Unknown) { } public IccLutAToBTagDataEntry(IccTagDataEntry[] curveB, float[,] matrix3x3, float[] matrix3x1, IccTagDataEntry[] curveM, IccClut clutValues, IccTagDataEntry[] curveA, IccProfileTag tagSignature) : base(IccTypeSignature.LutAToB, tagSignature) { VerifyMatrix(matrix3x3, matrix3x1); VerifyCurve(curveA, "curveA"); VerifyCurve(curveB, "curveB"); VerifyCurve(curveM, "curveM"); Matrix3x3 = CreateMatrix3x3(matrix3x3); Matrix3x1 = CreateMatrix3x1(matrix3x1); CurveA = curveA; CurveB = curveB; CurveM = curveM; ClutValues = clutValues; if (IsAClutMMatrixB()) { Guard.IsTrue(CurveB.Length == 3, "CurveB", "CurveB must have a length of three"); Guard.IsTrue(CurveM.Length == 3, "CurveM", "CurveM must have a length of three"); Guard.MustBeBetweenOrEqualTo(CurveA.Length, 1, 15, "CurveA"); InputChannelCount = curveA.Length; OutputChannelCount = 3; Guard.IsTrue(InputChannelCount == clutValues.InputChannelCount, "clutValues", "Input channel count does not match the CLUT size"); Guard.IsTrue(OutputChannelCount == clutValues.OutputChannelCount, "clutValues", "Output channel count does not match the CLUT size"); } else if (IsMMatrixB()) { Guard.IsTrue(CurveB.Length == 3, "CurveB", "CurveB must have a length of three"); Guard.IsTrue(CurveM.Length == 3, "CurveM", "CurveM must have a length of three"); InputChannelCount = (OutputChannelCount = 3); } else if (IsAClutB()) { Guard.MustBeBetweenOrEqualTo(CurveA.Length, 1, 15, "CurveA"); Guard.MustBeBetweenOrEqualTo(CurveB.Length, 1, 15, "CurveB"); InputChannelCount = curveA.Length; OutputChannelCount = curveB.Length; Guard.IsTrue(InputChannelCount == clutValues.InputChannelCount, "clutValues", "Input channel count does not match the CLUT size"); Guard.IsTrue(OutputChannelCount == clutValues.OutputChannelCount, "clutValues", "Output channel count does not match the CLUT size"); } else { if (!IsB()) { throw new ArgumentException("Invalid combination of values given"); } InputChannelCount = (OutputChannelCount = CurveB.Length); } } public override bool Equals(IccTagDataEntry other) { if (other is IccLutAToBTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccLutAToBTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && InputChannelCount == other.InputChannelCount && OutputChannelCount == other.OutputChannelCount && Matrix3x3.Equals(other.Matrix3x3) && Matrix3x1.Equals(other.Matrix3x1) && ClutValues.Equals(other.ClutValues) && EqualsCurve(CurveB, other.CurveB) && EqualsCurve(CurveM, other.CurveM)) { return EqualsCurve(CurveA, other.CurveA); } return false; } public override bool Equals(object obj) { if (obj is IccLutAToBTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { HashCode hashCode = default(HashCode); hashCode.Add(base.Signature); hashCode.Add(InputChannelCount); hashCode.Add(OutputChannelCount); hashCode.Add(Matrix3x3); hashCode.Add(Matrix3x1); hashCode.Add(ClutValues); hashCode.Add(CurveB); hashCode.Add(CurveM); hashCode.Add(CurveA); return hashCode.ToHashCode(); } private static bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves) { bool num = thisCurves == null; bool flag = entryCurves == null; if (num && flag) { return true; } if (flag) { return false; } return thisCurves.SequenceEqual(entryCurves); } private bool IsAClutMMatrixB() { if (CurveB != null && Matrix3x3.HasValue && Matrix3x1.HasValue && CurveM != null && ClutValues != null) { return CurveA != null; } return false; } private bool IsMMatrixB() { if (CurveB != null && Matrix3x3.HasValue && Matrix3x1.HasValue) { return CurveM != null; } return false; } private bool IsAClutB() { if (CurveB != null && ClutValues != null) { return CurveA != null; } return false; } private bool IsB() { return CurveB != null; } private void VerifyCurve(IccTagDataEntry[] curves, string name) { if (curves != null) { Guard.IsFalse(curves.Any((IccTagDataEntry t) => !(t is IccParametricCurveTagDataEntry) && !(t is IccCurveTagDataEntry)), "name", "name must be of type IccParametricCurveTagDataEntry or IccCurveTagDataEntry"); } } private void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) { if (matrix3x1 != null) { Guard.IsTrue(matrix3x1.Length == 3, "matrix3x1", "Matrix must have a size of three"); } if (matrix3x3 != null) { Guard.IsTrue(matrix3x3.GetLength(0) == 3 && matrix3x3.GetLength(1) == 3, "matrix3x3", "Matrix must have a size of three by three"); } } private Vector3? CreateMatrix3x1(float[] matrix) { if (matrix == null) { return null; } return new Vector3(matrix[0], matrix[1], matrix[2]); } private Matrix4x4? CreateMatrix3x3(float[,] matrix) { if (matrix == null) { return null; } return new Matrix4x4(matrix[0, 0], matrix[0, 1], matrix[0, 2], 0f, matrix[1, 0], matrix[1, 1], matrix[1, 2], 0f, matrix[2, 0], matrix[2, 1], matrix[2, 2], 0f, 0f, 0f, 0f, 1f); } } internal sealed class IccLutBToATagDataEntry : IccTagDataEntry, IEquatable { public int InputChannelCount { get; } public int OutputChannelCount { get; } public Matrix4x4? Matrix3x3 { get; } public Vector3? Matrix3x1 { get; } public IccClut ClutValues { get; } public IccTagDataEntry[] CurveB { get; } public IccTagDataEntry[] CurveM { get; } public IccTagDataEntry[] CurveA { get; } public IccLutBToATagDataEntry(IccTagDataEntry[] curveB, float[,] matrix3x3, float[] matrix3x1, IccTagDataEntry[] curveM, IccClut clutValues, IccTagDataEntry[] curveA) : this(curveB, matrix3x3, matrix3x1, curveM, clutValues, curveA, IccProfileTag.Unknown) { } public IccLutBToATagDataEntry(IccTagDataEntry[] curveB, float[,] matrix3x3, float[] matrix3x1, IccTagDataEntry[] curveM, IccClut clutValues, IccTagDataEntry[] curveA, IccProfileTag tagSignature) : base(IccTypeSignature.LutBToA, tagSignature) { VerifyMatrix(matrix3x3, matrix3x1); VerifyCurve(curveA, "curveA"); VerifyCurve(curveB, "curveB"); VerifyCurve(curveM, "curveM"); Matrix3x3 = CreateMatrix3x3(matrix3x3); Matrix3x1 = CreateMatrix3x1(matrix3x1); CurveA = curveA; CurveB = curveB; CurveM = curveM; ClutValues = clutValues; if (IsBMatrixMClutA()) { Guard.IsTrue(CurveB.Length == 3, "CurveB", "CurveB must have a length of three"); Guard.IsTrue(CurveM.Length == 3, "CurveM", "CurveM must have a length of three"); Guard.MustBeBetweenOrEqualTo(CurveA.Length, 1, 15, "CurveA"); InputChannelCount = 3; OutputChannelCount = curveA.Length; Guard.IsTrue(InputChannelCount == clutValues.InputChannelCount, "clutValues", "Input channel count does not match the CLUT size"); Guard.IsTrue(OutputChannelCount == clutValues.OutputChannelCount, "clutValues", "Output channel count does not match the CLUT size"); } else if (IsBMatrixM()) { Guard.IsTrue(CurveB.Length == 3, "CurveB", "CurveB must have a length of three"); Guard.IsTrue(CurveM.Length == 3, "CurveM", "CurveM must have a length of three"); InputChannelCount = (OutputChannelCount = 3); } else if (IsBClutA()) { Guard.MustBeBetweenOrEqualTo(CurveA.Length, 1, 15, "CurveA"); Guard.MustBeBetweenOrEqualTo(CurveB.Length, 1, 15, "CurveB"); InputChannelCount = curveB.Length; OutputChannelCount = curveA.Length; Guard.IsTrue(InputChannelCount == clutValues.InputChannelCount, "clutValues", "Input channel count does not match the CLUT size"); Guard.IsTrue(OutputChannelCount == clutValues.OutputChannelCount, "clutValues", "Output channel count does not match the CLUT size"); } else { if (!IsB()) { throw new ArgumentException("Invalid combination of values given"); } InputChannelCount = (OutputChannelCount = CurveB.Length); } } public override bool Equals(IccTagDataEntry other) { if (other is IccLutBToATagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccLutBToATagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && InputChannelCount == other.InputChannelCount && OutputChannelCount == other.OutputChannelCount && Matrix3x3.Equals(other.Matrix3x3) && Matrix3x1.Equals(other.Matrix3x1) && ClutValues.Equals(other.ClutValues) && EqualsCurve(CurveB, other.CurveB) && EqualsCurve(CurveM, other.CurveM)) { return EqualsCurve(CurveA, other.CurveA); } return false; } public override bool Equals(object obj) { if (obj is IccLutBToATagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { HashCode hashCode = default(HashCode); hashCode.Add(base.Signature); hashCode.Add(InputChannelCount); hashCode.Add(OutputChannelCount); hashCode.Add(Matrix3x3); hashCode.Add(Matrix3x1); hashCode.Add(ClutValues); hashCode.Add(CurveB); hashCode.Add(CurveM); hashCode.Add(CurveA); return hashCode.ToHashCode(); } private bool EqualsCurve(IccTagDataEntry[] thisCurves, IccTagDataEntry[] entryCurves) { bool num = thisCurves == null; bool flag = entryCurves == null; if (num && flag) { return true; } if (flag) { return false; } return thisCurves.SequenceEqual(entryCurves); } private bool IsBMatrixMClutA() { if (CurveB != null && Matrix3x3.HasValue && Matrix3x1.HasValue && CurveM != null && ClutValues != null) { return CurveA != null; } return false; } private bool IsBMatrixM() { if (CurveB != null && Matrix3x3.HasValue && Matrix3x1.HasValue) { return CurveM != null; } return false; } private bool IsBClutA() { if (CurveB != null && ClutValues != null) { return CurveA != null; } return false; } private bool IsB() { return CurveB != null; } private void VerifyCurve(IccTagDataEntry[] curves, string name) { if (curves != null) { Guard.IsFalse(curves.Any((IccTagDataEntry t) => !(t is IccParametricCurveTagDataEntry) && !(t is IccCurveTagDataEntry)), "name", "name must be of type IccParametricCurveTagDataEntry or IccCurveTagDataEntry"); } } private void VerifyMatrix(float[,] matrix3x3, float[] matrix3x1) { if (matrix3x1 != null) { Guard.IsTrue(matrix3x1.Length == 3, "matrix3x1", "Matrix must have a size of three"); } if (matrix3x3 != null) { Guard.IsTrue(matrix3x3.GetLength(0) == 3 && matrix3x3.GetLength(1) == 3, "matrix3x3", "Matrix must have a size of three by three"); } } private Vector3? CreateMatrix3x1(float[] matrix) { if (matrix == null) { return null; } return new Vector3(matrix[0], matrix[1], matrix[2]); } private Matrix4x4? CreateMatrix3x3(float[,] matrix) { if (matrix == null) { return null; } return new Matrix4x4(matrix[0, 0], matrix[0, 1], matrix[0, 2], 0f, matrix[1, 0], matrix[1, 1], matrix[1, 2], 0f, matrix[2, 0], matrix[2, 1], matrix[2, 2], 0f, 0f, 0f, 0f, 1f); } } internal sealed class IccMeasurementTagDataEntry : IccTagDataEntry, IEquatable { public IccStandardObserver Observer { get; } public Vector3 XyzBacking { get; } public IccMeasurementGeometry Geometry { get; } public float Flare { get; } public IccStandardIlluminant Illuminant { get; } public IccMeasurementTagDataEntry(IccStandardObserver observer, Vector3 xyzBacking, IccMeasurementGeometry geometry, float flare, IccStandardIlluminant illuminant) : this(observer, xyzBacking, geometry, flare, illuminant, IccProfileTag.Unknown) { } public IccMeasurementTagDataEntry(IccStandardObserver observer, Vector3 xyzBacking, IccMeasurementGeometry geometry, float flare, IccStandardIlluminant illuminant, IccProfileTag tagSignature) : base(IccTypeSignature.Measurement, tagSignature) { Observer = observer; XyzBacking = xyzBacking; Geometry = geometry; Flare = flare; Illuminant = illuminant; } public override bool Equals(IccTagDataEntry other) { if (other is IccMeasurementTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccMeasurementTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && Observer == other.Observer && XyzBacking.Equals(other.XyzBacking) && Geometry == other.Geometry && Flare.Equals(other.Flare)) { return Illuminant == other.Illuminant; } return false; } public override bool Equals(object obj) { if (obj is IccMeasurementTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Observer, XyzBacking, Geometry, Flare, Illuminant); } } internal sealed class IccMultiLocalizedUnicodeTagDataEntry : IccTagDataEntry, IEquatable { public IccLocalizedString[] Texts { get; } public IccMultiLocalizedUnicodeTagDataEntry(IccLocalizedString[] texts) : this(texts, IccProfileTag.Unknown) { } public IccMultiLocalizedUnicodeTagDataEntry(IccLocalizedString[] texts, IccProfileTag tagSignature) : base(IccTypeSignature.MultiLocalizedUnicode, tagSignature) { Texts = texts ?? throw new ArgumentNullException("texts"); } public override bool Equals(IccTagDataEntry other) { if (other is IccMultiLocalizedUnicodeTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccMultiLocalizedUnicodeTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Texts.AsSpan().SequenceEqual(other.Texts); } return false; } public override bool Equals(object obj) { if (obj is IccMultiLocalizedUnicodeTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Texts); } } internal sealed class IccMultiProcessElementsTagDataEntry : IccTagDataEntry, IEquatable { public int InputChannelCount { get; } public int OutputChannelCount { get; } public IccMultiProcessElement[] Data { get; } public IccMultiProcessElementsTagDataEntry(IccMultiProcessElement[] data) : this(data, IccProfileTag.Unknown) { } public IccMultiProcessElementsTagDataEntry(IccMultiProcessElement[] data, IccProfileTag tagSignature) : base(IccTypeSignature.MultiProcessElements, tagSignature) { Guard.NotNull(data, "data"); Guard.IsTrue(data.Length != 0, "data", "data must have at least one element"); InputChannelCount = data[0].InputChannelCount; OutputChannelCount = data[0].OutputChannelCount; Data = data; Guard.IsFalse(data.Any((IccMultiProcessElement t) => t.InputChannelCount != InputChannelCount || t.OutputChannelCount != OutputChannelCount), "data", "The number of input and output channels are not the same for all elements"); } public override bool Equals(IccTagDataEntry other) { if (other is IccMultiProcessElementsTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccMultiProcessElementsTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && InputChannelCount == other.InputChannelCount && OutputChannelCount == other.OutputChannelCount) { return Data.AsSpan().SequenceEqual(other.Data); } return false; } public override bool Equals(object obj) { if (obj is IccMultiProcessElementsTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, InputChannelCount, OutputChannelCount, Data); } } internal sealed class IccNamedColor2TagDataEntry : IccTagDataEntry, IEquatable { public int CoordinateCount { get; } public string Prefix { get; } public string Suffix { get; } public int VendorFlags { get; } public IccNamedColor[] Colors { get; } public IccNamedColor2TagDataEntry(IccNamedColor[] colors) : this(0, null, null, colors, IccProfileTag.Unknown) { } public IccNamedColor2TagDataEntry(string prefix, string suffix, IccNamedColor[] colors) : this(0, prefix, suffix, colors, IccProfileTag.Unknown) { } public IccNamedColor2TagDataEntry(int vendorFlags, string prefix, string suffix, IccNamedColor[] colors) : this(vendorFlags, prefix, suffix, colors, IccProfileTag.Unknown) { } public IccNamedColor2TagDataEntry(IccNamedColor[] colors, IccProfileTag tagSignature) : this(0, null, null, colors, tagSignature) { } public IccNamedColor2TagDataEntry(string prefix, string suffix, IccNamedColor[] colors, IccProfileTag tagSignature) : this(0, prefix, suffix, colors, tagSignature) { } public IccNamedColor2TagDataEntry(int vendorFlags, string prefix, string suffix, IccNamedColor[] colors, IccProfileTag tagSignature) : base(IccTypeSignature.NamedColor2, tagSignature) { Guard.NotNull(colors, "colors"); int coordinateCount = 0; if (colors.Length != 0) { ushort[] deviceCoordinates = colors[0].DeviceCoordinates; coordinateCount = ((deviceCoordinates != null) ? deviceCoordinates.Length : 0); Guard.IsFalse(colors.Any(delegate(IccNamedColor t) { ushort[] deviceCoordinates2 = t.DeviceCoordinates; return ((deviceCoordinates2 != null) ? deviceCoordinates2.Length : 0) != coordinateCount; }), "colors", "Device coordinate count must be the same for all colors"); } VendorFlags = vendorFlags; CoordinateCount = coordinateCount; Prefix = prefix; Suffix = suffix; Colors = colors; } public override bool Equals(IccTagDataEntry other) { if (other is IccNamedColor2TagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccNamedColor2TagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && CoordinateCount == other.CoordinateCount && string.Equals(Prefix, other.Prefix) && string.Equals(Suffix, other.Suffix) && VendorFlags == other.VendorFlags) { return Colors.AsSpan().SequenceEqual(other.Colors); } return false; } public override bool Equals(object obj) { if (obj is IccNamedColor2TagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, CoordinateCount, Prefix, Suffix, VendorFlags, Colors); } } internal sealed class IccParametricCurveTagDataEntry : IccTagDataEntry, IEquatable { public IccParametricCurve Curve { get; } public IccParametricCurveTagDataEntry(IccParametricCurve curve) : this(curve, IccProfileTag.Unknown) { } public IccParametricCurveTagDataEntry(IccParametricCurve curve, IccProfileTag tagSignature) : base(IccTypeSignature.ParametricCurve, tagSignature) { Curve = curve ?? throw new ArgumentNullException("curve"); } public override bool Equals(IccTagDataEntry other) { if (other is IccParametricCurveTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccParametricCurveTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Curve.Equals(other.Curve); } return false; } public override bool Equals(object obj) { if (obj is IccParametricCurveTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Curve); } } internal sealed class IccProfileSequenceDescTagDataEntry : IccTagDataEntry, IEquatable { public IccProfileDescription[] Descriptions { get; } public IccProfileSequenceDescTagDataEntry(IccProfileDescription[] descriptions) : this(descriptions, IccProfileTag.Unknown) { } public IccProfileSequenceDescTagDataEntry(IccProfileDescription[] descriptions, IccProfileTag tagSignature) : base(IccTypeSignature.ProfileSequenceDesc, tagSignature) { Descriptions = descriptions ?? throw new ArgumentNullException("descriptions"); } public override bool Equals(IccTagDataEntry other) { if (other is IccProfileSequenceDescTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccProfileSequenceDescTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Descriptions.AsSpan().SequenceEqual(other.Descriptions); } return false; } public override bool Equals(object obj) { if (obj is IccProfileSequenceDescTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Descriptions); } } internal sealed class IccProfileSequenceIdentifierTagDataEntry : IccTagDataEntry, IEquatable { public IccProfileSequenceIdentifier[] Data { get; } public IccProfileSequenceIdentifierTagDataEntry(IccProfileSequenceIdentifier[] data) : this(data, IccProfileTag.Unknown) { } public IccProfileSequenceIdentifierTagDataEntry(IccProfileSequenceIdentifier[] data, IccProfileTag tagSignature) : base(IccTypeSignature.ProfileSequenceIdentifier, tagSignature) { Data = data ?? throw new ArgumentNullException("data"); } public override bool Equals(IccTagDataEntry other) { if (other is IccProfileSequenceIdentifierTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccProfileSequenceIdentifierTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Data.AsSpan().SequenceEqual(other.Data); } return false; } public override bool Equals(object obj) { if (obj is IccProfileSequenceIdentifierTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Data); } } internal sealed class IccResponseCurveSet16TagDataEntry : IccTagDataEntry, IEquatable { public ushort ChannelCount { get; } public IccResponseCurve[] Curves { get; } public IccResponseCurveSet16TagDataEntry(IccResponseCurve[] curves) : this(curves, IccProfileTag.Unknown) { } public IccResponseCurveSet16TagDataEntry(IccResponseCurve[] curves, IccProfileTag tagSignature) : base(IccTypeSignature.ResponseCurveSet16, tagSignature) { Guard.NotNull(curves, "curves"); Guard.IsTrue(curves.Length != 0, "curves", "curves needs at least one element"); Curves = curves; ChannelCount = (ushort)curves[0].ResponseArrays.Length; Guard.IsFalse(curves.Any((IccResponseCurve t) => t.ResponseArrays.Length != ChannelCount), "curves", "All curves need to have the same number of channels"); } public override bool Equals(IccTagDataEntry other) { if (other is IccResponseCurveSet16TagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccResponseCurveSet16TagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && ChannelCount == other.ChannelCount) { return Curves.AsSpan().SequenceEqual(other.Curves); } return false; } public override bool Equals(object obj) { if (obj is IccResponseCurveSet16TagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, ChannelCount, Curves); } } internal sealed class IccScreeningTagDataEntry : IccTagDataEntry, IEquatable { public IccScreeningFlag Flags { get; } public IccScreeningChannel[] Channels { get; } public IccScreeningTagDataEntry(IccScreeningFlag flags, IccScreeningChannel[] channels) : this(flags, channels, IccProfileTag.Unknown) { } public IccScreeningTagDataEntry(IccScreeningFlag flags, IccScreeningChannel[] channels, IccProfileTag tagSignature) : base(IccTypeSignature.Screening, tagSignature) { Flags = flags; Channels = channels ?? throw new ArgumentNullException("channels"); } public override bool Equals(IccTagDataEntry other) { if (other is IccScreeningTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccScreeningTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && Flags == other.Flags) { return Channels.AsSpan().SequenceEqual(other.Channels); } return false; } public override bool Equals(object obj) { if (obj is IccScreeningTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Flags, Channels); } } internal sealed class IccSignatureTagDataEntry : IccTagDataEntry, IEquatable { public string SignatureData { get; } public IccSignatureTagDataEntry(string signatureData) : this(signatureData, IccProfileTag.Unknown) { } public IccSignatureTagDataEntry(string signatureData, IccProfileTag tagSignature) : base(IccTypeSignature.Signature, tagSignature) { SignatureData = signatureData ?? throw new ArgumentNullException("signatureData"); } public override bool Equals(IccTagDataEntry other) { if (other is IccSignatureTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccSignatureTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return string.Equals(SignatureData, other.SignatureData); } return false; } public override bool Equals(object obj) { if (obj is IccSignatureTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, SignatureData); } } internal sealed class IccTextDescriptionTagDataEntry : IccTagDataEntry, IEquatable { public string Ascii { get; } public string Unicode { get; } public string ScriptCode { get; } public uint UnicodeLanguageCode { get; } public ushort ScriptCodeCode { get; } public IccTextDescriptionTagDataEntry(string ascii, string unicode, string scriptCode, uint unicodeLanguageCode, ushort scriptCodeCode) : this(ascii, unicode, scriptCode, unicodeLanguageCode, scriptCodeCode, IccProfileTag.Unknown) { } public IccTextDescriptionTagDataEntry(string ascii, string unicode, string scriptCode, uint unicodeLanguageCode, ushort scriptCodeCode, IccProfileTag tagSignature) : base(IccTypeSignature.TextDescription, tagSignature) { Ascii = ascii; Unicode = unicode; ScriptCode = scriptCode; UnicodeLanguageCode = unicodeLanguageCode; ScriptCodeCode = scriptCodeCode; } public static explicit operator IccMultiLocalizedUnicodeTagDataEntry(IccTextDescriptionTagDataEntry textEntry) { if (textEntry == null) { return null; } IccLocalizedString iccLocalizedString; if (string.IsNullOrEmpty(textEntry.Unicode)) { iccLocalizedString = ((!string.IsNullOrEmpty(textEntry.Ascii)) ? new IccLocalizedString(textEntry.Ascii) : (string.IsNullOrEmpty(textEntry.ScriptCode) ? new IccLocalizedString(string.Empty) : new IccLocalizedString(textEntry.ScriptCode))); } else { CultureInfo cultureInfo = GetCulture(textEntry.UnicodeLanguageCode); iccLocalizedString = ((cultureInfo != null) ? new IccLocalizedString(cultureInfo, textEntry.Unicode) : new IccLocalizedString(textEntry.Unicode)); } return new IccMultiLocalizedUnicodeTagDataEntry(new IccLocalizedString[1] { iccLocalizedString }, textEntry.TagSignature); static CultureInfo GetCulture(uint value) { if (value == 0) { return null; } byte b = (byte)(value >> 24); byte b2 = (byte)(value >> 16); byte b3 = (byte)(value >> 8); byte b4 = (byte)value; if (b >= 97 && b <= 122 && b2 >= 97 && b2 <= 122 && b3 >= 65 && b3 <= 90 && b4 >= 65 && b4 <= 90) { return new CultureInfo(new string(new char[5] { (char)b, (char)b2, '-', (char)b3, (char)b4 })); } return null; } } public override bool Equals(IccTagDataEntry other) { if (other is IccTextDescriptionTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccTextDescriptionTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && string.Equals(Ascii, other.Ascii) && string.Equals(Unicode, other.Unicode) && string.Equals(ScriptCode, other.ScriptCode) && UnicodeLanguageCode == other.UnicodeLanguageCode) { return ScriptCodeCode == other.ScriptCodeCode; } return false; } public override bool Equals(object obj) { if (obj is IccTextDescriptionTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Ascii, Unicode, ScriptCode, UnicodeLanguageCode, ScriptCodeCode); } } internal sealed class IccTextTagDataEntry : IccTagDataEntry, IEquatable { public string Text { get; } public IccTextTagDataEntry(string text) : this(text, IccProfileTag.Unknown) { } public IccTextTagDataEntry(string text, IccProfileTag tagSignature) : base(IccTypeSignature.Text, tagSignature) { Text = text ?? throw new ArgumentNullException("text"); } public override bool Equals(IccTagDataEntry other) { if (other is IccTextTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccTextTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return string.Equals(Text, other.Text); } return false; } public override bool Equals(object obj) { if (obj is IccTextTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Text); } } internal sealed class IccUcrBgTagDataEntry : IccTagDataEntry, IEquatable { public ushort[] UcrCurve { get; } public ushort[] BgCurve { get; } public string Description { get; } public IccUcrBgTagDataEntry(ushort[] ucrCurve, ushort[] bgCurve, string description) : this(ucrCurve, bgCurve, description, IccProfileTag.Unknown) { } public IccUcrBgTagDataEntry(ushort[] ucrCurve, ushort[] bgCurve, string description, IccProfileTag tagSignature) : base(IccTypeSignature.UcrBg, tagSignature) { UcrCurve = ucrCurve ?? throw new ArgumentNullException("ucrCurve"); BgCurve = bgCurve ?? throw new ArgumentNullException("bgCurve"); Description = description ?? throw new ArgumentNullException("description"); } public override bool Equals(IccTagDataEntry other) { if (other is IccUcrBgTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccUcrBgTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && UcrCurve.AsSpan().SequenceEqual(other.UcrCurve) && BgCurve.AsSpan().SequenceEqual(other.BgCurve)) { return string.Equals(Description, other.Description); } return false; } public override bool Equals(object obj) { if (obj is IccUcrBgTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, UcrCurve, BgCurve, Description); } } internal sealed class IccUFix16ArrayTagDataEntry : IccTagDataEntry, IEquatable { public float[] Data { get; } public IccUFix16ArrayTagDataEntry(float[] data) : this(data, IccProfileTag.Unknown) { } public IccUFix16ArrayTagDataEntry(float[] data, IccProfileTag tagSignature) : base(IccTypeSignature.U16Fixed16Array, tagSignature) { Data = data ?? throw new ArgumentNullException("data"); } public override bool Equals(IccTagDataEntry other) { if (other is IccUFix16ArrayTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccUFix16ArrayTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Data.AsSpan().SequenceEqual(other.Data); } return false; } public override bool Equals(object obj) { if (obj is IccUFix16ArrayTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Data); } } internal sealed class IccUInt16ArrayTagDataEntry : IccTagDataEntry, IEquatable { public ushort[] Data { get; } public IccUInt16ArrayTagDataEntry(ushort[] data) : this(data, IccProfileTag.Unknown) { } public IccUInt16ArrayTagDataEntry(ushort[] data, IccProfileTag tagSignature) : base(IccTypeSignature.UInt16Array, tagSignature) { Data = data ?? throw new ArgumentNullException("data"); } public override bool Equals(IccTagDataEntry other) { if (other is IccUInt16ArrayTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccUInt16ArrayTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Data.AsSpan().SequenceEqual(other.Data); } return false; } public override bool Equals(object obj) { if (obj is IccUInt16ArrayTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Data); } } internal sealed class IccUInt32ArrayTagDataEntry : IccTagDataEntry, IEquatable { public uint[] Data { get; } public IccUInt32ArrayTagDataEntry(uint[] data) : this(data, IccProfileTag.Unknown) { } public IccUInt32ArrayTagDataEntry(uint[] data, IccProfileTag tagSignature) : base(IccTypeSignature.UInt32Array, tagSignature) { Data = data ?? throw new ArgumentNullException("data"); } public override bool Equals(IccTagDataEntry other) { if (other is IccUInt32ArrayTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccUInt32ArrayTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Data.AsSpan().SequenceEqual(other.Data); } return false; } public override bool Equals(object obj) { if (obj is IccUInt32ArrayTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Data); } } internal sealed class IccUInt64ArrayTagDataEntry : IccTagDataEntry, IEquatable { public ulong[] Data { get; } public IccUInt64ArrayTagDataEntry(ulong[] data) : this(data, IccProfileTag.Unknown) { } public IccUInt64ArrayTagDataEntry(ulong[] data, IccProfileTag tagSignature) : base(IccTypeSignature.UInt64Array, tagSignature) { Data = data ?? throw new ArgumentNullException("data"); } public override bool Equals(IccTagDataEntry other) { if (other is IccUInt64ArrayTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccUInt64ArrayTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Data.AsSpan().SequenceEqual(other.Data); } return false; } public override bool Equals(object obj) { if (obj is IccUInt64ArrayTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Data); } } internal sealed class IccUInt8ArrayTagDataEntry : IccTagDataEntry, IEquatable { public byte[] Data { get; } public IccUInt8ArrayTagDataEntry(byte[] data) : this(data, IccProfileTag.Unknown) { } public IccUInt8ArrayTagDataEntry(byte[] data, IccProfileTag tagSignature) : base(IccTypeSignature.UInt8Array, tagSignature) { Data = data ?? throw new ArgumentNullException("data"); } public override bool Equals(IccTagDataEntry other) { if (other is IccUInt8ArrayTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccUInt8ArrayTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Data.AsSpan().SequenceEqual(other.Data); } return false; } public override bool Equals(object obj) { if (obj is IccUInt8ArrayTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Data); } } internal sealed class IccUnknownTagDataEntry : IccTagDataEntry, IEquatable { public byte[] Data { get; } public IccUnknownTagDataEntry(byte[] data) : this(data, IccProfileTag.Unknown) { } public IccUnknownTagDataEntry(byte[] data, IccProfileTag tagSignature) : base(IccTypeSignature.Unknown, tagSignature) { Data = data ?? throw new ArgumentNullException("data"); } public override bool Equals(IccTagDataEntry other) { if (other is IccUnknownTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccUnknownTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other)) { return Data.AsSpan().SequenceEqual(other.Data); } return false; } public override bool Equals(object obj) { if (obj is IccUnknownTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, Data); } } internal sealed class IccViewingConditionsTagDataEntry : IccTagDataEntry, IEquatable { public Vector3 IlluminantXyz { get; } public Vector3 SurroundXyz { get; } public IccStandardIlluminant Illuminant { get; } public IccViewingConditionsTagDataEntry(Vector3 illuminantXyz, Vector3 surroundXyz, IccStandardIlluminant illuminant) : this(illuminantXyz, surroundXyz, illuminant, IccProfileTag.Unknown) { } public IccViewingConditionsTagDataEntry(Vector3 illuminantXyz, Vector3 surroundXyz, IccStandardIlluminant illuminant, IccProfileTag tagSignature) : base(IccTypeSignature.ViewingConditions, tagSignature) { IlluminantXyz = illuminantXyz; SurroundXyz = surroundXyz; Illuminant = illuminant; } public override bool Equals(IccTagDataEntry other) { if (other is IccViewingConditionsTagDataEntry other2) { return Equals(other2); } return false; } public bool Equals(IccViewingConditionsTagDataEntry other) { if (other == null) { return false; } if (this == other) { return true; } if (base.Equals(other) && IlluminantXyz.Equals(other.IlluminantXyz) && SurroundXyz.Equals(other.SurroundXyz)) { return Illuminant == other.Illuminant; } return false; } public override bool Equals(object obj) { if (obj is IccViewingConditionsTagDataEntry other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(base.Signature, IlluminantXyz, SurroundXyz, Illuminant); } } internal sealed class IccXyzTagDataEntry : IccTagDataEntry, IEquatable { public Vector3[] Data { get; } public IccXyzTagDataEntry(Vector3[] data) : this(data, IccProfileTag.Unknown) { } public IccXyzTagDataEntry(Vector3[] data, IccProfileTag tagSignature) : base(IccTypeSignature.Xyz, tagSignature) { Data = data ?? throw new ArgumentNullException("data"); } public override bool Equals(IccTagDataEntry other) { if (base.Equals(other) && other is IccXyzTagDataEntry iccXyzTagDataEntry) { return Data.AsSpan().SequenceEqual(iccXyzTagDataEntry.Data); } return false; } public bool Equals(IccXyzTagDataEntry other) { return Equals((IccTagDataEntry)other); } } internal sealed class IccClut : IEquatable { public float[][] Values { get; } public IccClutDataType DataType { get; } public int InputChannelCount { get; } public int OutputChannelCount { get; } public byte[] GridPointCount { get; } public IccClut(float[][] values, byte[] gridPointCount, IccClutDataType type) { Guard.NotNull(values, "values"); Guard.NotNull(gridPointCount, "gridPointCount"); Values = values; DataType = type; InputChannelCount = gridPointCount.Length; OutputChannelCount = values[0].Length; GridPointCount = gridPointCount; CheckValues(); } public IccClut(ushort[][] values, byte[] gridPointCount) { Guard.NotNull(values, "values"); Guard.NotNull(gridPointCount, "gridPointCount"); Values = new float[values.Length][]; for (int i = 0; i < values.Length; i++) { Values[i] = new float[values[i].Length]; for (int j = 0; j < values[i].Length; j++) { Values[i][j] = (float)(int)values[i][j] / 65535f; } } DataType = IccClutDataType.UInt16; InputChannelCount = gridPointCount.Length; OutputChannelCount = values[0].Length; GridPointCount = gridPointCount; CheckValues(); } public IccClut(byte[][] values, byte[] gridPointCount) { Guard.NotNull(values, "values"); Guard.NotNull(gridPointCount, "gridPointCount"); Values = new float[values.Length][]; for (int i = 0; i < values.Length; i++) { Values[i] = new float[values[i].Length]; for (int j = 0; j < values[i].Length; j++) { Values[i][j] = (float)(int)values[i][j] / 255f; } } DataType = IccClutDataType.UInt8; InputChannelCount = gridPointCount.Length; OutputChannelCount = values[0].Length; GridPointCount = gridPointCount; CheckValues(); } public bool Equals(IccClut other) { if (other == null) { return false; } if (this == other) { return true; } if (EqualsValuesArray(other) && DataType == other.DataType && InputChannelCount == other.InputChannelCount && OutputChannelCount == other.OutputChannelCount) { return GridPointCount.AsSpan().SequenceEqual(other.GridPointCount); } return false; } public override bool Equals(object obj) { if (obj is IccClut other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(Values, DataType, InputChannelCount, OutputChannelCount, GridPointCount); } private bool EqualsValuesArray(IccClut other) { if (Values.Length != other.Values.Length) { return false; } for (int i = 0; i < Values.Length; i++) { if (!Values[i].AsSpan().SequenceEqual(other.Values[i])) { return false; } } return true; } private void CheckValues() { Guard.MustBeBetweenOrEqualTo(InputChannelCount, 1, 15, "InputChannelCount"); Guard.MustBeBetweenOrEqualTo(OutputChannelCount, 1, 15, "OutputChannelCount"); Guard.IsFalse(Values.Any((float[] t) => t.Length != OutputChannelCount), "Values", "The number of output values varies"); int num = 0; for (int num2 = 0; num2 < InputChannelCount; num2++) { num += (int)Math.Pow((int)GridPointCount[num2], InputChannelCount); } num /= InputChannelCount; Guard.IsTrue(Values.Length == num, "Values", "Length of values array does not match the grid points"); } } internal readonly struct IccColorantTableEntry : IEquatable { public string Name { get; } public ushort Pcs1 { get; } public ushort Pcs2 { get; } public ushort Pcs3 { get; } public IccColorantTableEntry(string name) : this(name, 0, 0, 0) { } public IccColorantTableEntry(string name, ushort pcs1, ushort pcs2, ushort pcs3) { Name = name ?? throw new ArgumentNullException("name"); Pcs1 = pcs1; Pcs2 = pcs2; Pcs3 = pcs3; } public static bool operator ==(IccColorantTableEntry left, IccColorantTableEntry right) { return left.Equals(right); } public static bool operator !=(IccColorantTableEntry left, IccColorantTableEntry right) { return !left.Equals(right); } public override bool Equals(object obj) { if (obj is IccColorantTableEntry other) { return Equals(other); } return false; } public bool Equals(IccColorantTableEntry other) { if (Name == other.Name && Pcs1 == other.Pcs1 && Pcs2 == other.Pcs2) { return Pcs3 == other.Pcs3; } return false; } public override int GetHashCode() { return HashCode.Combine(Name, Pcs1, Pcs2, Pcs3); } public override string ToString() { return $"{Name}: {Pcs1}; {Pcs2}; {Pcs3}"; } } internal readonly struct IccLocalizedString : IEquatable { public string Text { get; } public CultureInfo Culture { get; } public IccLocalizedString(string text) : this(CultureInfo.CurrentCulture, text) { } public IccLocalizedString(CultureInfo culture, string text) { Culture = culture ?? throw new ArgumentNullException("culture"); Text = text ?? throw new ArgumentNullException("text"); } public bool Equals(IccLocalizedString other) { if (Culture.Equals(other.Culture)) { return Text == other.Text; } return false; } public override string ToString() { return Culture.Name + ": " + Text; } } internal readonly struct IccLut : IEquatable { public float[] Values { get; } public IccLut(float[] values) { Values = values ?? throw new ArgumentNullException("values"); } public IccLut(ushort[] values) { Guard.NotNull(values, "values"); Values = new float[values.Length]; for (int i = 0; i < values.Length; i++) { Values[i] = (float)(int)values[i] / 65535f; } } public IccLut(byte[] values) { Guard.NotNull(values, "values"); Values = new float[values.Length]; for (int i = 0; i < values.Length; i++) { Values[i] = (float)(int)values[i] / 255f; } } public bool Equals(IccLut other) { if (Values == other.Values) { return true; } return Values.AsSpan().SequenceEqual(other.Values); } } internal readonly struct IccNamedColor : IEquatable { public string Name { get; } public ushort[] PcsCoordinates { get; } public ushort[] DeviceCoordinates { get; } public IccNamedColor(string name, ushort[] pcsCoordinates, ushort[] deviceCoordinates) { Guard.NotNull(name, "name"); Guard.NotNull(pcsCoordinates, "pcsCoordinates"); Guard.IsTrue(pcsCoordinates.Length == 3, "pcsCoordinates", "Must have a length of 3"); Name = name; PcsCoordinates = pcsCoordinates; DeviceCoordinates = deviceCoordinates; } public static bool operator ==(IccNamedColor left, IccNamedColor right) { return left.Equals(right); } public static bool operator !=(IccNamedColor left, IccNamedColor right) { return !left.Equals(right); } public override bool Equals(object obj) { if (obj is IccNamedColor other) { return Equals(other); } return false; } public bool Equals(IccNamedColor other) { if (Name.Equals(other.Name) && PcsCoordinates.AsSpan().SequenceEqual(other.PcsCoordinates)) { return DeviceCoordinates.AsSpan().SequenceEqual(other.DeviceCoordinates); } return false; } public override int GetHashCode() { return HashCode.Combine(Name, PcsCoordinates, DeviceCoordinates); } public override string ToString() { return Name; } } internal readonly struct IccPositionNumber : IEquatable { public uint Offset { get; } public uint Size { get; } public IccPositionNumber(uint offset, uint size) { Offset = offset; Size = size; } public static bool operator ==(IccPositionNumber left, IccPositionNumber right) { return left.Equals(right); } public static bool operator !=(IccPositionNumber left, IccPositionNumber right) { return !left.Equals(right); } public override bool Equals(object obj) { if (obj is IccPositionNumber other) { return Equals(other); } return false; } public bool Equals(IccPositionNumber other) { if (Offset == other.Offset) { return Size == other.Size; } return false; } public override int GetHashCode() { return (int)(Offset ^ Size); } public override string ToString() { return $"{Offset}; {Size}"; } } internal readonly struct IccProfileDescription : IEquatable { public uint DeviceManufacturer { get; } public uint DeviceModel { get; } public IccDeviceAttribute DeviceAttributes { get; } public IccProfileTag TechnologyInformation { get; } public IccLocalizedString[] DeviceManufacturerInfo { get; } public IccLocalizedString[] DeviceModelInfo { get; } public IccProfileDescription(uint deviceManufacturer, uint deviceModel, IccDeviceAttribute deviceAttributes, IccProfileTag technologyInformation, IccLocalizedString[] deviceManufacturerInfo, IccLocalizedString[] deviceModelInfo) { DeviceManufacturer = deviceManufacturer; DeviceModel = deviceModel; DeviceAttributes = deviceAttributes; TechnologyInformation = technologyInformation; DeviceManufacturerInfo = deviceManufacturerInfo ?? throw new ArgumentNullException("deviceManufacturerInfo"); DeviceModelInfo = deviceModelInfo ?? throw new ArgumentNullException("deviceModelInfo"); } public bool Equals(IccProfileDescription other) { if (DeviceManufacturer == other.DeviceManufacturer && DeviceModel == other.DeviceModel && DeviceAttributes == other.DeviceAttributes && TechnologyInformation == other.TechnologyInformation && DeviceManufacturerInfo.AsSpan().SequenceEqual(other.DeviceManufacturerInfo)) { return DeviceModelInfo.AsSpan().SequenceEqual(other.DeviceModelInfo); } return false; } public override bool Equals(object obj) { if (obj is IccProfileDescription other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(DeviceManufacturer, DeviceModel, DeviceAttributes, TechnologyInformation, DeviceManufacturerInfo, DeviceModelInfo); } } public readonly struct IccProfileId : IEquatable { public static readonly IccProfileId Zero; public uint Part1 { get; } public uint Part2 { get; } public uint Part3 { get; } public uint Part4 { get; } public bool IsSet => !Equals(Zero); public IccProfileId(uint p1, uint p2, uint p3, uint p4) { Part1 = p1; Part2 = p2; Part3 = p3; Part4 = p4; } public static bool operator ==(IccProfileId left, IccProfileId right) { return left.Equals(right); } public static bool operator !=(IccProfileId left, IccProfileId right) { return !left.Equals(right); } public override bool Equals(object obj) { if (obj is IccProfileId other) { return Equals(other); } return false; } public bool Equals(IccProfileId other) { if (Part1 == other.Part1 && Part2 == other.Part2 && Part3 == other.Part3) { return Part4 == other.Part4; } return false; } public override int GetHashCode() { return HashCode.Combine(Part1, Part2, Part3, Part4); } public override string ToString() { return ToHex(Part1) + "-" + ToHex(Part2) + "-" + ToHex(Part3) + "-" + ToHex(Part4); } private static string ToHex(uint value) { return value.ToString("X").PadLeft(8, '0'); } } internal readonly struct IccProfileSequenceIdentifier : IEquatable { public IccProfileId Id { get; } public IccLocalizedString[] Description { get; } public IccProfileSequenceIdentifier(IccProfileId id, IccLocalizedString[] description) { Id = id; Description = description ?? throw new ArgumentNullException("description"); } public bool Equals(IccProfileSequenceIdentifier other) { if (Id.Equals(other.Id)) { return Description.AsSpan().SequenceEqual(other.Description); } return false; } public override bool Equals(object obj) { if (obj is IccProfileSequenceIdentifier other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(Id, Description); } } internal readonly struct IccResponseNumber : IEquatable { public ushort DeviceCode { get; } public float MeasurementValue { get; } public IccResponseNumber(ushort deviceCode, float measurementValue) { DeviceCode = deviceCode; MeasurementValue = measurementValue; } public static bool operator ==(IccResponseNumber left, IccResponseNumber right) { return left.Equals(right); } public static bool operator !=(IccResponseNumber left, IccResponseNumber right) { return !left.Equals(right); } public override bool Equals(object obj) { if (obj is IccResponseNumber other) { return Equals(other); } return false; } public bool Equals(IccResponseNumber other) { if (DeviceCode == other.DeviceCode) { return MeasurementValue == other.MeasurementValue; } return false; } public override int GetHashCode() { return HashCode.Combine(DeviceCode, MeasurementValue); } public override string ToString() { return $"Code: {DeviceCode}; Value: {MeasurementValue}"; } } internal readonly struct IccScreeningChannel : IEquatable { public float Frequency { get; } public float Angle { get; } public IccScreeningSpotType SpotShape { get; } public IccScreeningChannel(float frequency, float angle, IccScreeningSpotType spotShape) { Frequency = frequency; Angle = angle; SpotShape = spotShape; } public static bool operator ==(IccScreeningChannel left, IccScreeningChannel right) { return left.Equals(right); } public static bool operator !=(IccScreeningChannel left, IccScreeningChannel right) { return !left.Equals(right); } public bool Equals(IccScreeningChannel other) { if (Frequency == other.Frequency && Angle == other.Angle) { return SpotShape == other.SpotShape; } return false; } public override bool Equals(object obj) { if (obj is IccScreeningChannel other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(Frequency, Angle, SpotShape); } public override string ToString() { return $"{Frequency}Hz; {Angle}°; {SpotShape}"; } } internal readonly struct IccTagTableEntry : IEquatable { public IccProfileTag Signature { get; } public uint Offset { get; } public uint DataSize { get; } public IccTagTableEntry(IccProfileTag signature, uint offset, uint dataSize) { Signature = signature; Offset = offset; DataSize = dataSize; } public static bool operator ==(IccTagTableEntry left, IccTagTableEntry right) { return left.Equals(right); } public static bool operator !=(IccTagTableEntry left, IccTagTableEntry right) { return !left.Equals(right); } public override bool Equals(object obj) { if (obj is IccTagTableEntry other) { return Equals(other); } return false; } public bool Equals(IccTagTableEntry other) { if (Signature.Equals(other.Signature) && Offset.Equals(other.Offset)) { return DataSize.Equals(other.DataSize); } return false; } public override int GetHashCode() { return HashCode.Combine(Signature, Offset, DataSize); } public override string ToString() { return $"{Signature} (Offset: {Offset}; Size: {DataSize})"; } } public readonly struct IccVersion : IEquatable { public int Major { get; } public int Minor { get; } public int Patch { get; } public IccVersion(int major, int minor, int patch) { Major = major; Minor = minor; Patch = patch; } public bool Equals(IccVersion other) { if (Major == other.Major && Minor == other.Minor) { return Patch == other.Patch; } return false; } public override string ToString() { return string.Join(".", Major, Minor, Patch); } } } namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { internal static class ExifConstants { public static ReadOnlySpan LittleEndianByteOrderMarker => "II*\0"u8; public static ReadOnlySpan BigEndianByteOrderMarker => new byte[4] { 77, 77, 0, 42 }; public static Encoding DefaultEncoding => Encoding.UTF8; } public enum ExifDataType { Unknown = 0, Byte = 1, Ascii = 2, Short = 3, Long = 4, Rational = 5, SignedByte = 6, Undefined = 7, SignedShort = 8, SignedLong = 9, SignedRational = 10, SingleFloat = 11, DoubleFloat = 12, Ifd = 13, Long8 = 16, SignedLong8 = 17, Ifd8 = 18 } internal static class ExifDataTypes { public static uint GetSize(ExifDataType dataType) { switch (dataType) { case ExifDataType.Byte: case ExifDataType.Ascii: case ExifDataType.SignedByte: case ExifDataType.Undefined: return 1u; case ExifDataType.Short: case ExifDataType.SignedShort: return 2u; case ExifDataType.Long: case ExifDataType.SignedLong: case ExifDataType.SingleFloat: case ExifDataType.Ifd: return 4u; case ExifDataType.Rational: case ExifDataType.SignedRational: case ExifDataType.DoubleFloat: case ExifDataType.Long8: case ExifDataType.SignedLong8: case ExifDataType.Ifd8: return 8u; default: throw new NotSupportedException(dataType.ToString()); } } } internal static class ExifEncodedStringHelpers { public const int CharacterCodeBytesLength = 8; private const ulong AsciiCode = 314761761601uL; private const ulong JISCode = 5458250uL; private const ulong UnicodeCode = 19496880615018069uL; private const ulong UndefinedCode = 0uL; private static ReadOnlySpan AsciiCodeBytes => new byte[8] { 65, 83, 67, 73, 73, 0, 0, 0 }; private static ReadOnlySpan JISCodeBytes => new byte[8] { 74, 73, 83, 0, 0, 0, 0, 0 }; private static ReadOnlySpan UnicodeCodeBytes => "UNICODE\0"u8; private static ReadOnlySpan UndefinedCodeBytes => new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 }; private static Encoding JIS0208Encoding => CodePagesEncodingProvider.Instance.GetEncoding(20932); public static bool IsEncodedString(ExifTagValue tag) { if ((uint)(tag - 27) <= 1u || tag == ExifTagValue.UserComment) { return true; } return false; } public static ReadOnlySpan GetCodeBytes(EncodedString.CharacterCode code) { return code switch { EncodedString.CharacterCode.ASCII => AsciiCodeBytes, EncodedString.CharacterCode.JIS => JISCodeBytes, EncodedString.CharacterCode.Unicode => UnicodeCodeBytes, EncodedString.CharacterCode.Undefined => UndefinedCodeBytes, _ => UndefinedCodeBytes, }; } public static Encoding GetEncoding(EncodedString.CharacterCode code) { return code switch { EncodedString.CharacterCode.ASCII => Encoding.ASCII, EncodedString.CharacterCode.JIS => JIS0208Encoding, EncodedString.CharacterCode.Unicode => Encoding.Unicode, EncodedString.CharacterCode.Undefined => Encoding.UTF8, _ => Encoding.UTF8, }; } public static bool TryParse(ReadOnlySpan buffer, out EncodedString encodedString) { if (TryDetect(buffer, out var code)) { string text = GetEncoding(code).GetString(buffer.Slice(8)); encodedString = new EncodedString(code, text); return true; } encodedString = default(EncodedString); return false; } public static uint GetDataLength(EncodedString encodedString) { return (uint)(GetEncoding(encodedString.Code).GetByteCount(encodedString.Text) + 8); } public static int Write(EncodedString encodedString, Span destination) { GetCodeBytes(encodedString.Code).CopyTo(destination); string text = encodedString.Text; int num = Write(GetEncoding(encodedString.Code), text, destination.Slice(8)); return 8 + num; } public static int Write(Encoding encoding, string value, Span destination) { return encoding.GetBytes(value.AsSpan(), destination); } private static bool TryDetect(ReadOnlySpan buffer, out EncodedString.CharacterCode code) { if (buffer.Length >= 8) { switch (BinaryPrimitives.ReadUInt64LittleEndian(buffer)) { case 314761761601uL: code = EncodedString.CharacterCode.ASCII; return true; case 5458250uL: code = EncodedString.CharacterCode.JIS; return true; case 19496880615018069uL: code = EncodedString.CharacterCode.Unicode; return true; case 0uL: code = EncodedString.CharacterCode.Undefined; return true; } } code = EncodedString.CharacterCode.ASCII; return false; } } [Flags] public enum ExifParts { None = 0, IfdTags = 1, ExifTags = 2, GpsTags = 4, All = 7 } public sealed class ExifProfile : IDeepCloneable { private readonly byte[] data; private List values; private int thumbnailOffset; private int thumbnailLength; public ExifParts Parts { get; set; } public IReadOnlyList InvalidTags { get; private set; } public IReadOnlyList Values { get { InitializeValues(); return values; } } public ExifProfile() : this((byte[])null) { } public ExifProfile(byte[] data) { Parts = ExifParts.All; this.data = data; InvalidTags = Array.Empty(); } internal ExifProfile(List values, IReadOnlyList invalidTags) { Parts = ExifParts.All; this.values = values; InvalidTags = invalidTags; } private ExifProfile(ExifProfile other) { Guard.NotNull(other, "other"); Parts = other.Parts; thumbnailLength = other.thumbnailLength; thumbnailOffset = other.thumbnailOffset; IReadOnlyList invalidTags; if (other.InvalidTags.Count <= 0) { IReadOnlyList readOnlyList = Array.Empty(); invalidTags = readOnlyList; } else { IReadOnlyList readOnlyList = new List(other.InvalidTags); invalidTags = readOnlyList; } InvalidTags = invalidTags; if (other.values != null) { values = new List(other.Values.Count); foreach (IExifValue value in other.Values) { values.Add(value.DeepClone()); } } if (other.data != null) { data = new byte[other.data.Length]; other.data.AsSpan().CopyTo(data); } } public Image CreateThumbnail() { return CreateThumbnail(); } public Image CreateThumbnail() where TPixel : unmanaged, IPixel { InitializeValues(); if (thumbnailOffset == 0 || thumbnailLength == 0) { return null; } if (data == null || data.Length < thumbnailOffset + thumbnailLength) { return null; } using MemoryStream stream = new MemoryStream(data, thumbnailOffset, thumbnailLength); return Image.Load(stream); } public IExifValue GetValue(ExifTag tag) { IExifValue valueInternal = GetValueInternal(tag); if (valueInternal != null) { return (IExifValue)valueInternal; } return null; } public bool RemoveValue(ExifTag tag) { InitializeValues(); for (int i = 0; i < values.Count; i++) { if (values[i].Tag == tag) { values.RemoveAt(i); return true; } } return false; } public void SetValue(ExifTag tag, TValueType value) { SetValueInternal(tag, value); } public byte[] ToByteArray() { if (values == null) { return data; } if (values.Count == 0) { return Array.Empty(); } return new ExifWriter(values, Parts).GetData(); } public ExifProfile DeepClone() { return new ExifProfile(this); } internal IExifValue GetValueInternal(ExifTag tag) { foreach (IExifValue value in Values) { if (value.Tag == tag) { return value; } } return null; } internal void SetValueInternal(ExifTag tag, object value) { foreach (IExifValue value2 in Values) { if (value2.Tag == tag) { value2.TrySetValue(value); return; } } ExifValue exifValue = ExifValues.Create(tag); if ((object)exifValue == null) { throw new NotSupportedException(); } exifValue.TrySetValue(value); values.Add(exifValue); } internal void Sync(ImageMetadata metadata) { SyncResolution(ExifTag.XResolution, metadata.HorizontalResolution); SyncResolution(ExifTag.YResolution, metadata.VerticalResolution); } private void SyncResolution(ExifTag tag, double resolution) { IExifValue value = GetValue(tag); if (value != null) { if (value.IsArray || value.DataType != ExifDataType.Rational) { RemoveValue(value.Tag); } Rational value2 = new Rational(resolution, bestPrecision: false); SetValue(tag, value2); } } private void InitializeValues() { if (values != null) { return; } if (data == null) { values = new List(); return; } ExifReader exifReader = new ExifReader(data); values = exifReader.ReadValues(); IReadOnlyList invalidTags; if (exifReader.InvalidTags.Count <= 0) { IReadOnlyList readOnlyList = Array.Empty(); invalidTags = readOnlyList; } else { IReadOnlyList readOnlyList = new List(exifReader.InvalidTags); invalidTags = readOnlyList; } InvalidTags = invalidTags; thumbnailOffset = (int)exifReader.ThumbnailOffset; thumbnailLength = (int)exifReader.ThumbnailLength; } } internal class ExifReader : BaseExifReader { public ExifReader(byte[] exifData) : this(exifData, null) { } public ExifReader(byte[] exifData, MemoryAllocator allocator) : base(new MemoryStream(exifData ?? throw new ArgumentNullException("exifData")), allocator) { } public List ReadValues() { List list = new List(); base.IsBigEndian = ReadUInt16() != 18761; if (ReadUInt16() != 42) { return list; } uint offset = ReadUInt32(); ReadValues(list, offset); uint offset2 = ReadUInt32(); GetThumbnail(offset2); ReadSubIfd(list); ReadBigValues(list); return list; } private void GetThumbnail(uint offset) { if (offset == 0) { return; } List list = new List(); ReadValues(list, offset); foreach (ExifValue item in list) { if (item == ExifTag.JPEGInterchangeFormat) { base.ThumbnailOffset = ((ExifLong)item).Value; } else if (item == ExifTag.JPEGInterchangeFormatLength) { base.ThumbnailLength = ((ExifLong)item).Value; } } } } internal abstract class BaseExifReader { private delegate TDataType ConverterMethod(ReadOnlySpan data); private readonly byte[] buf8 = new byte[8]; private readonly byte[] buf4 = new byte[4]; private readonly byte[] buf2 = new byte[2]; private readonly MemoryAllocator allocator; private readonly Stream data; private List invalidTags; private List subIfds; public IReadOnlyList InvalidTags { get { IReadOnlyList readOnlyList = invalidTags; return readOnlyList ?? Array.Empty(); } } public uint ThumbnailLength { get; protected set; } public uint ThumbnailOffset { get; protected set; } public bool IsBigEndian { get; protected set; } public List<(ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif)> BigValues { get; } = new List<(ulong, ExifDataType, ulong, ExifValue)>(); protected BaseExifReader(Stream stream, MemoryAllocator allocator) { data = stream ?? throw new ArgumentNullException("stream"); this.allocator = allocator; } protected void ReadBigValues(List values) { if (BigValues.Count == 0) { return; } int num = 0; foreach (var bigValue in BigValues) { ExifDataType item = bigValue.DataType; ulong item2 = bigValue.NumberOfComponents; ulong num2 = item2 * ExifDataTypes.GetSize(item); if ((int)num2 > num) { num = (int)num2; } } if (allocator != null) { using IMemoryOwner buffer = allocator.Allocate(num); Span span = buffer.GetSpan(); foreach (var bigValue2 in BigValues) { ulong num3 = bigValue2.NumberOfComponents * ExifDataTypes.GetSize(bigValue2.DataType); ReadBigValue(values, bigValue2, span.Slice(0, (int)num3)); } } else { Span span2 = ((num > 256) ? ((Span)new byte[num]) : stackalloc byte[256]); Span span3 = span2; foreach (var bigValue3 in BigValues) { ulong num4 = bigValue3.NumberOfComponents * ExifDataTypes.GetSize(bigValue3.DataType); ReadBigValue(values, bigValue3, span3.Slice(0, (int)num4)); } } BigValues.Clear(); } protected void ReadValues(List values, uint offset) { if (offset <= data.Length) { Seek(offset); int num = ReadUInt16(); Span offsetBuffer = stackalloc byte[4]; for (int i = 0; i < num; i++) { ReadValue(values, offsetBuffer); } } } protected void ReadSubIfd(List values) { if (subIfds == null) { return; } foreach (ulong subIfd in subIfds) { ReadValues(values, (uint)subIfd); } } protected void ReadValues64(List values, ulong offset) { Seek(offset); ulong num = ReadUInt64(); Span offsetBuffer = stackalloc byte[8]; for (ulong num2 = 0uL; num2 < num; num2++) { ReadValue64(values, offsetBuffer); } } protected void ReadBigValue(IList values, (ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif) tag, Span buffer) { Seek(tag.Offset); if (TryReadSpan(buffer)) { object value = ConvertValue(tag.DataType, buffer, tag.NumberOfComponents > 1 || tag.Exif.IsArray); Add(values, tag.Exif, value); } } private static TDataType[] ToArray(ExifDataType dataType, ReadOnlySpan data, ConverterMethod converter) { int size = (int)ExifDataTypes.GetSize(dataType); int num = data.Length / size; TDataType[] array = new TDataType[num]; for (int i = 0; i < num; i++) { ReadOnlySpan readOnlySpan = data.Slice(i * size, size); array.SetValue(converter(readOnlySpan), i); } return array; } private static string ConvertToString(Encoding encoding, ReadOnlySpan buffer) { int num = buffer.IndexOf(0); if (num > -1) { buffer = buffer.Slice(0, num); } return encoding.GetString(buffer); } private byte ConvertToByte(ReadOnlySpan buffer) { return buffer[0]; } private object ConvertValue(ExifDataType dataType, ReadOnlySpan buffer, bool isArray) { if (buffer.Length == 0) { return null; } switch (dataType) { case ExifDataType.Unknown: return null; case ExifDataType.Ascii: return ConvertToString(ExifConstants.DefaultEncoding, buffer); case ExifDataType.Byte: case ExifDataType.Undefined: if (!isArray) { return ConvertToByte(buffer); } return buffer.ToArray(); case ExifDataType.DoubleFloat: if (!isArray) { return ConvertToDouble(buffer); } return ToArray(dataType, buffer, ConvertToDouble); case ExifDataType.Long: case ExifDataType.Ifd: if (!isArray) { return ConvertToUInt32(buffer); } return ToArray(dataType, buffer, ConvertToUInt32); case ExifDataType.Rational: if (!isArray) { return ToRational(buffer); } return ToArray(dataType, buffer, ToRational); case ExifDataType.Short: if (!isArray) { return ConvertToShort(buffer); } return ToArray(dataType, buffer, ConvertToShort); case ExifDataType.SignedByte: if (!isArray) { return ConvertToSignedByte(buffer); } return ToArray(dataType, buffer, ConvertToSignedByte); case ExifDataType.SignedLong: if (!isArray) { return ConvertToInt32(buffer); } return ToArray(dataType, buffer, ConvertToInt32); case ExifDataType.SignedRational: if (!isArray) { return ToSignedRational(buffer); } return ToArray(dataType, buffer, ToSignedRational); case ExifDataType.SignedShort: if (!isArray) { return ConvertToSignedShort(buffer); } return ToArray(dataType, buffer, ConvertToSignedShort); case ExifDataType.SingleFloat: if (!isArray) { return ConvertToSingle(buffer); } return ToArray(dataType, buffer, ConvertToSingle); case ExifDataType.Long8: case ExifDataType.Ifd8: if (!isArray) { return ConvertToUInt64(buffer); } return ToArray(dataType, buffer, ConvertToUInt64); case ExifDataType.SignedLong8: if (!isArray) { return ConvertToInt64(buffer); } return ToArray(dataType, buffer, ConvertToUInt64); default: throw new NotSupportedException($"Data type {dataType} is not supported."); } } private void ReadValue(List values, Span offsetBuffer) { if (data.Length - data.Position < 12) { return; } ExifTagValue exifTagValue = (ExifTagValue)ReadUInt16(); ExifDataType exifDataType = EnumUtils.Parse(ReadUInt16(), ExifDataType.Unknown); uint num = ReadUInt32(); TryReadSpan(offsetBuffer); if (exifDataType == ExifDataType.Unknown) { return; } if (num == 0) { num = 4 / ExifDataTypes.GetSize(exifDataType); } ExifValue exifValue = ExifValues.Create(exifTagValue) ?? ExifValues.Create(exifTagValue, exifDataType, num); if ((object)exifValue == null) { AddInvalidTag(new UnkownExifTag(exifTagValue)); return; } uint num2 = num * ExifDataTypes.GetSize(exifDataType); if (num2 > 4) { uint num3 = ConvertToUInt32(offsetBuffer); if (num3 > int.MaxValue || num3 + num2 > data.Length) { AddInvalidTag(new UnkownExifTag(exifTagValue)); } else { BigValues.Add((num3, exifDataType, num, exifValue)); } } else { object value = ConvertValue(exifDataType, offsetBuffer.Slice(0, (int)num2), num > 1 || exifValue.IsArray); Add(values, exifValue, value); } } private void ReadValue64(List values, Span offsetBuffer) { if (data.Length - data.Position < 20) { return; } ExifTagValue exifTagValue = (ExifTagValue)ReadUInt16(); ExifDataType exifDataType = EnumUtils.Parse(ReadUInt16(), ExifDataType.Unknown); ulong num = ReadUInt64(); TryReadSpan(offsetBuffer); if (exifDataType == ExifDataType.Unknown) { return; } if (num == 0L) { num = 8 / ExifDataTypes.GetSize(exifDataType); } ExifValue exifValue = exifTagValue switch { ExifTagValue.StripOffsets => new ExifLong8Array(ExifTagValue.StripOffsets), ExifTagValue.StripByteCounts => new ExifLong8Array(ExifTagValue.StripByteCounts), ExifTagValue.TileOffsets => new ExifLong8Array(ExifTagValue.TileOffsets), ExifTagValue.TileByteCounts => new ExifLong8Array(ExifTagValue.TileByteCounts), _ => ExifValues.Create(exifTagValue) ?? ExifValues.Create(exifTagValue, exifDataType, num), }; if ((object)exifValue == null) { AddInvalidTag(new UnkownExifTag(exifTagValue)); return; } ulong num2 = num * ExifDataTypes.GetSize(exifDataType); if (num2 > 8) { ulong num3 = ConvertToUInt64(offsetBuffer); if (num3 > ulong.MaxValue || num3 > (ulong)(data.Length - (long)num2)) { AddInvalidTag(new UnkownExifTag(exifTagValue)); } else { BigValues.Add((num3, exifDataType, num, exifValue)); } } else { object value = ConvertValue(exifDataType, offsetBuffer.Slice(0, (int)num2), num > 1 || exifValue.IsArray); Add(values, exifValue, value); } } private void Add(IList values, IExifValue exif, object value) { if (!exif.TrySetValue(value)) { return; } foreach (IExifValue value2 in values) { if (value2 == exif) { return; } } if (exif.Tag == ExifTag.SubIFDOffset) { AddSubIfd(value); } else if (exif.Tag == ExifTag.GPSIFDOffset) { AddSubIfd(value); } else { values.Add(exif); } } private void AddInvalidTag(ExifTag tag) { (invalidTags ?? (invalidTags = new List())).Add(tag); } private void AddSubIfd(object val) { (subIfds ?? (subIfds = new List())).Add(Convert.ToUInt64(val)); } private void Seek(ulong pos) { data.Seek((long)pos, SeekOrigin.Begin); } private bool TryReadSpan(Span span) { int length = span.Length; if (data.Length - data.Position < length) { return false; } return data.Read(span) == length; } protected ulong ReadUInt64() { if (!TryReadSpan(buf8)) { return 0uL; } return ConvertToUInt64(buf8); } protected uint ReadUInt32() { if (!TryReadSpan(buf4)) { return 0u; } return ConvertToUInt32(buf4); } protected ushort ReadUInt16() { if (!TryReadSpan(buf2)) { return 0; } return ConvertToShort(buf2); } private long ConvertToInt64(ReadOnlySpan buffer) { if (buffer.Length < 8) { return 0L; } if (!IsBigEndian) { return BinaryPrimitives.ReadInt64LittleEndian(buffer); } return BinaryPrimitives.ReadInt64BigEndian(buffer); } private ulong ConvertToUInt64(ReadOnlySpan buffer) { if (buffer.Length < 8) { return 0uL; } if (!IsBigEndian) { return BinaryPrimitives.ReadUInt64LittleEndian(buffer); } return BinaryPrimitives.ReadUInt64BigEndian(buffer); } private double ConvertToDouble(ReadOnlySpan buffer) { if (buffer.Length < 8) { return 0.0; } long source = (IsBigEndian ? BinaryPrimitives.ReadInt64BigEndian(buffer) : BinaryPrimitives.ReadInt64LittleEndian(buffer)); return Unsafe.As(ref source); } private uint ConvertToUInt32(ReadOnlySpan buffer) { if (buffer.Length < 4) { return 0u; } if (!IsBigEndian) { return BinaryPrimitives.ReadUInt32LittleEndian(buffer); } return BinaryPrimitives.ReadUInt32BigEndian(buffer); } private ushort ConvertToShort(ReadOnlySpan buffer) { if (buffer.Length < 2) { return 0; } if (!IsBigEndian) { return BinaryPrimitives.ReadUInt16LittleEndian(buffer); } return BinaryPrimitives.ReadUInt16BigEndian(buffer); } private float ConvertToSingle(ReadOnlySpan buffer) { if (buffer.Length < 4) { return 0f; } int source = (IsBigEndian ? BinaryPrimitives.ReadInt32BigEndian(buffer) : BinaryPrimitives.ReadInt32LittleEndian(buffer)); return Unsafe.As(ref source); } private Rational ToRational(ReadOnlySpan buffer) { if (buffer.Length < 8) { return default(Rational); } uint numerator = ConvertToUInt32(buffer.Slice(0, 4)); uint denominator = ConvertToUInt32(buffer.Slice(4, 4)); return new Rational(numerator, denominator, simplify: false); } private sbyte ConvertToSignedByte(ReadOnlySpan buffer) { return (sbyte)buffer[0]; } private int ConvertToInt32(ReadOnlySpan buffer) { if (buffer.Length < 4) { return 0; } if (!IsBigEndian) { return BinaryPrimitives.ReadInt32LittleEndian(buffer); } return BinaryPrimitives.ReadInt32BigEndian(buffer); } private SignedRational ToSignedRational(ReadOnlySpan buffer) { if (buffer.Length < 8) { return default(SignedRational); } int numerator = ConvertToInt32(buffer.Slice(0, 4)); int denominator = ConvertToInt32(buffer.Slice(4, 4)); return new SignedRational(numerator, denominator, simplify: false); } private short ConvertToSignedShort(ReadOnlySpan buffer) { if (buffer.Length < 2) { return 0; } if (!IsBigEndian) { return BinaryPrimitives.ReadInt16LittleEndian(buffer); } return BinaryPrimitives.ReadInt16BigEndian(buffer); } } [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] internal sealed class ExifTagDescriptionAttribute : Attribute { public ExifTagDescriptionAttribute(object value, string description) { } public static string GetDescription(ExifTag tag, object value) { ExifTagValue exifTagValue = (ExifTagValue)(ushort)tag; FieldInfo declaredField = exifTagValue.GetType().GetTypeInfo().GetDeclaredField(exifTagValue.ToString()); if ((object)declaredField == null) { return null; } foreach (CustomAttributeData customAttribute in declaredField.CustomAttributes) { if (object.Equals(customAttribute.ConstructorArguments[0].Value, value)) { return (string)customAttribute.ConstructorArguments[1].Value; } } return null; } } internal static class ExifTags { public static ExifParts GetPart(ExifTag tag) { ExifTagValue exifTagValue = (ExifTagValue)(ushort)tag; if (exifTagValue <= ExifTagValue.SubIFDOffset) { if (exifTagValue > ExifTagValue.BatteryLevel) { if (exifTagValue <= ExifTagValue.MDFileUnits) { if (exifTagValue <= ExifTagValue.ExposureTime) { if (exifTagValue == ExifTagValue.Copyright) { goto IL_0536; } if (exifTagValue == ExifTagValue.ExposureTime) { goto IL_0538; } } else { if (exifTagValue == ExifTagValue.FNumber) { goto IL_0538; } if ((uint)(exifTagValue - 33445) <= 1u || (uint)(exifTagValue - 33448) <= 4u) { goto IL_0536; } } } else if (exifTagValue <= ExifTagValue.ModelTiePoint) { if (exifTagValue == ExifTagValue.PixelScale || (uint)(exifTagValue - 33918) <= 2u || exifTagValue == ExifTagValue.ModelTiePoint) { goto IL_0536; } } else { if (exifTagValue == ExifTagValue.SEMInfo || exifTagValue == ExifTagValue.ModelTransform) { goto IL_0536; } if (exifTagValue == ExifTagValue.SubIFDOffset) { } } goto IL_053c; } switch (exifTagValue) { case ExifTagValue.SubfileType: case ExifTagValue.OldSubfileType: case ExifTagValue.ImageWidth: case ExifTagValue.ImageLength: case ExifTagValue.BitsPerSample: case ExifTagValue.Compression: case ExifTagValue.PhotometricInterpretation: case ExifTagValue.Thresholding: case ExifTagValue.CellWidth: case ExifTagValue.CellLength: case ExifTagValue.FillOrder: case ExifTagValue.DocumentName: case ExifTagValue.ImageDescription: case ExifTagValue.Make: case ExifTagValue.Model: case ExifTagValue.StripOffsets: case ExifTagValue.Orientation: case ExifTagValue.SamplesPerPixel: case ExifTagValue.RowsPerStrip: case ExifTagValue.StripByteCounts: case ExifTagValue.MinSampleValue: case ExifTagValue.MaxSampleValue: case ExifTagValue.XResolution: case ExifTagValue.YResolution: case ExifTagValue.PlanarConfiguration: case ExifTagValue.PageName: case ExifTagValue.XPosition: case ExifTagValue.YPosition: case ExifTagValue.FreeOffsets: case ExifTagValue.FreeByteCounts: case ExifTagValue.GrayResponseUnit: case ExifTagValue.GrayResponseCurve: case ExifTagValue.T4Options: case ExifTagValue.T6Options: case ExifTagValue.ResolutionUnit: case ExifTagValue.PageNumber: case ExifTagValue.ColorResponseUnit: case ExifTagValue.TransferFunction: case ExifTagValue.Software: case ExifTagValue.DateTime: case ExifTagValue.Artist: case ExifTagValue.HostComputer: case ExifTagValue.Predictor: case ExifTagValue.WhitePoint: case ExifTagValue.PrimaryChromaticities: case ExifTagValue.ColorMap: case ExifTagValue.HalftoneHints: case ExifTagValue.TileWidth: case ExifTagValue.TileLength: case ExifTagValue.TileOffsets: case ExifTagValue.TileByteCounts: case ExifTagValue.BadFaxLines: case ExifTagValue.CleanFaxData: case ExifTagValue.ConsecutiveBadFaxLines: case ExifTagValue.InkSet: case ExifTagValue.InkNames: case ExifTagValue.NumberOfInks: case ExifTagValue.DotRange: case ExifTagValue.TargetPrinter: case ExifTagValue.ExtraSamples: case ExifTagValue.SampleFormat: case ExifTagValue.SMinSampleValue: case ExifTagValue.SMaxSampleValue: case ExifTagValue.TransferRange: case ExifTagValue.ClipPath: case ExifTagValue.XClipPathUnits: case ExifTagValue.YClipPathUnits: case ExifTagValue.Indexed: case ExifTagValue.JPEGTables: case ExifTagValue.OPIProxy: case ExifTagValue.ProfileType: case ExifTagValue.FaxProfile: case ExifTagValue.CodingMethods: case ExifTagValue.VersionYear: case ExifTagValue.ModeNumber: case ExifTagValue.Decode: case ExifTagValue.DefaultImageColor: case ExifTagValue.T82ptions: case ExifTagValue.JPEGProc: case ExifTagValue.JPEGInterchangeFormat: case ExifTagValue.JPEGInterchangeFormatLength: case ExifTagValue.JPEGRestartInterval: case ExifTagValue.JPEGLosslessPredictors: case ExifTagValue.JPEGPointTransforms: case ExifTagValue.JPEGQTables: case ExifTagValue.JPEGDCTables: case ExifTagValue.JPEGACTables: case ExifTagValue.YCbCrCoefficients: case ExifTagValue.YCbCrSubsampling: case ExifTagValue.YCbCrPositioning: case ExifTagValue.ReferenceBlackWhite: case ExifTagValue.StripRowCounts: case ExifTagValue.XMP: case ExifTagValue.Rating: case ExifTagValue.RatingPercent: case ExifTagValue.ImageID: case ExifTagValue.CFARepeatPatternDim: case ExifTagValue.CFAPattern2: case ExifTagValue.BatteryLevel: break; case ExifTagValue.GPSVersionID: case ExifTagValue.InteroperabilityIndex: case ExifTagValue.GPSLatitude: case ExifTagValue.GPSLongitudeRef: case ExifTagValue.GPSLongitude: case ExifTagValue.GPSAltitudeRef: case ExifTagValue.GPSAltitude: case ExifTagValue.GPSTimestamp: case ExifTagValue.GPSSatellites: case ExifTagValue.GPSStatus: case ExifTagValue.GPSMeasureMode: case ExifTagValue.GPSDOP: case ExifTagValue.GPSSpeedRef: case ExifTagValue.GPSSpeed: case ExifTagValue.GPSTrackRef: case ExifTagValue.GPSTrack: case ExifTagValue.GPSImgDirectionRef: case ExifTagValue.GPSImgDirection: case ExifTagValue.GPSMapDatum: case ExifTagValue.GPSDestLatitudeRef: case ExifTagValue.GPSDestLatitude: case ExifTagValue.GPSDestLongitudeRef: case ExifTagValue.GPSDestLongitude: case ExifTagValue.GPSDestBearingRef: case ExifTagValue.GPSDestBearing: case ExifTagValue.GPSDestDistanceRef: case ExifTagValue.GPSDestDistance: case ExifTagValue.GPSProcessingMethod: case ExifTagValue.GPSAreaInformation: case ExifTagValue.GPSDateStamp: case ExifTagValue.GPSDifferential: return ExifParts.GpsTags; default: goto IL_053c; } } else { switch (exifTagValue) { case ExifTagValue.ImageLayer: case ExifTagValue.FaxRecvParams: case ExifTagValue.FaxSubaddress: case ExifTagValue.FaxRecvTime: case ExifTagValue.ImageSourceData: case ExifTagValue.XPTitle: case ExifTagValue.XPComment: case ExifTagValue.XPAuthor: case ExifTagValue.XPKeywords: case ExifTagValue.XPSubject: case ExifTagValue.GDALMetadata: case ExifTagValue.GDALNoData: break; case ExifTagValue.ExposureProgram: case ExifTagValue.SpectralSensitivity: case ExifTagValue.ISOSpeedRatings: case ExifTagValue.OECF: case ExifTagValue.Interlace: case ExifTagValue.TimeZoneOffset: case ExifTagValue.SelfTimerMode: case ExifTagValue.SensitivityType: case ExifTagValue.StandardOutputSensitivity: case ExifTagValue.RecommendedExposureIndex: case ExifTagValue.ISOSpeed: case ExifTagValue.ISOSpeedLatitudeyyy: case ExifTagValue.ISOSpeedLatitudezzz: case ExifTagValue.ExifVersion: case ExifTagValue.DateTimeOriginal: case ExifTagValue.DateTimeDigitized: case ExifTagValue.OffsetTime: case ExifTagValue.OffsetTimeOriginal: case ExifTagValue.OffsetTimeDigitized: case ExifTagValue.ComponentsConfiguration: case ExifTagValue.CompressedBitsPerPixel: case ExifTagValue.ShutterSpeedValue: case ExifTagValue.ApertureValue: case ExifTagValue.BrightnessValue: case ExifTagValue.ExposureBiasValue: case ExifTagValue.MaxApertureValue: case ExifTagValue.SubjectDistance: case ExifTagValue.MeteringMode: case ExifTagValue.LightSource: case ExifTagValue.Flash: case ExifTagValue.FocalLength: case ExifTagValue.FlashEnergy2: case ExifTagValue.SpatialFrequencyResponse2: case ExifTagValue.Noise: case ExifTagValue.FocalPlaneXResolution2: case ExifTagValue.FocalPlaneYResolution2: case ExifTagValue.FocalPlaneResolutionUnit2: case ExifTagValue.ImageNumber: case ExifTagValue.SecurityClassification: case ExifTagValue.ImageHistory: case ExifTagValue.SubjectArea: case ExifTagValue.ExposureIndex2: case ExifTagValue.TIFFEPStandardID: case ExifTagValue.SensingMethod2: case ExifTagValue.MakerNote: case ExifTagValue.UserComment: case ExifTagValue.SubsecTime: case ExifTagValue.SubsecTimeOriginal: case ExifTagValue.SubsecTimeDigitized: case ExifTagValue.AmbientTemperature: case ExifTagValue.Humidity: case ExifTagValue.Pressure: case ExifTagValue.WaterDepth: case ExifTagValue.Acceleration: case ExifTagValue.CameraElevationAngle: case ExifTagValue.FlashpixVersion: case ExifTagValue.ColorSpace: case ExifTagValue.PixelXDimension: case ExifTagValue.PixelYDimension: case ExifTagValue.RelatedSoundFile: case ExifTagValue.FlashEnergy: case ExifTagValue.SpatialFrequencyResponse: case ExifTagValue.FocalPlaneXResolution: case ExifTagValue.FocalPlaneYResolution: case ExifTagValue.FocalPlaneResolutionUnit: case ExifTagValue.SubjectLocation: case ExifTagValue.ExposureIndex: case ExifTagValue.SensingMethod: case ExifTagValue.FileSource: case ExifTagValue.SceneType: case ExifTagValue.CFAPattern: case ExifTagValue.CustomRendered: case ExifTagValue.ExposureMode: case ExifTagValue.WhiteBalance: case ExifTagValue.DigitalZoomRatio: case ExifTagValue.FocalLengthIn35mmFilm: case ExifTagValue.SceneCaptureType: case ExifTagValue.GainControl: case ExifTagValue.Contrast: case ExifTagValue.Saturation: case ExifTagValue.Sharpness: case ExifTagValue.DeviceSettingDescription: case ExifTagValue.SubjectDistanceRange: case ExifTagValue.ImageUniqueID: case ExifTagValue.OwnerName: case ExifTagValue.SerialNumber: case ExifTagValue.LensSpecification: case ExifTagValue.LensMake: case ExifTagValue.LensModel: case ExifTagValue.LensSerialNumber: goto IL_0538; default: goto IL_053c; } } goto IL_0536; IL_053c: return ExifParts.None; IL_0538: return ExifParts.ExifTags; IL_0536: return ExifParts.IfdTags; } } internal static class ExifUcs2StringHelpers { public static Encoding Ucs2Encoding => Encoding.GetEncoding("UCS-2"); public static bool IsUcs2Tag(ExifTagValue tag) { if ((uint)(tag - 40091) <= 4u) { return true; } return false; } public static int Write(string value, Span destination) { return ExifEncodedStringHelpers.Write(Ucs2Encoding, value, destination); } } internal sealed class ExifWriter { private readonly ExifParts allowedParts; private readonly IList values; private List dataOffsets; private readonly List ifdValues; private readonly List exifValues; private readonly List gpsValues; public ExifWriter(IList values, ExifParts allowedParts) { this.values = values; this.allowedParts = allowedParts; ifdValues = GetPartValues(ExifParts.IfdTags); exifValues = GetPartValues(ExifParts.ExifTags); gpsValues = GetPartValues(ExifParts.GpsTags); } public byte[] GetData() { IExifValue offsetValue = GetOffsetValue(ifdValues, exifValues, ExifTag.SubIFDOffset); IExifValue offsetValue2 = GetOffsetValue(ifdValues, gpsValues, ExifTag.GPSIFDOffset); uint length = GetLength(ifdValues); uint length2 = GetLength(exifValues); uint length3 = GetLength(gpsValues); uint num = length + length2 + length3; if (num == 0) { return Array.Empty(); } num += (uint)ExifConstants.LittleEndianByteOrderMarker.Length; num += 4; byte[] array = new byte[num]; int num2 = 0; ExifConstants.LittleEndianByteOrderMarker.CopyTo(array.AsSpan(num2)); num2 += ExifConstants.LittleEndianByteOrderMarker.Length; uint num3 = (uint)(num2 + 4); offsetValue?.TrySetValue(num3 + length); offsetValue2?.TrySetValue(num3 + length + length2); num2 = WriteUInt32(num3, array, num2); num2 = WriteHeaders(ifdValues, array, num2); num2 = WriteData(0u, ifdValues, array, num2); if (length2 != 0) { num2 = WriteHeaders(exifValues, array, num2); num2 = WriteData(0u, exifValues, array, num2); } if (length3 != 0) { num2 = WriteHeaders(gpsValues, array, num2); num2 = WriteData(0u, gpsValues, array, num2); } return array; } private unsafe static int WriteSingle(float value, Span destination, int offset) { BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), *(int*)(&value)); return offset + 4; } private unsafe static int WriteDouble(double value, Span destination, int offset) { BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(offset, 8), *(long*)(&value)); return offset + 8; } private static int Write(ReadOnlySpan source, Span destination, int offset) { source.CopyTo(destination.Slice(offset, source.Length)); return offset + source.Length; } private static int WriteInt16(short value, Span destination, int offset) { BinaryPrimitives.WriteInt16LittleEndian(destination.Slice(offset, 2), value); return offset + 2; } private static int WriteUInt16(ushort value, Span destination, int offset) { BinaryPrimitives.WriteUInt16LittleEndian(destination.Slice(offset, 2), value); return offset + 2; } private static int WriteUInt32(uint value, Span destination, int offset) { BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(offset, 4), value); return offset + 4; } private static int WriteInt64(long value, Span destination, int offset) { BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(offset, 8), value); return offset + 8; } private static int WriteUInt64(ulong value, Span destination, int offset) { BinaryPrimitives.WriteUInt64LittleEndian(destination.Slice(offset, 8), value); return offset + 8; } private static int WriteInt32(int value, Span destination, int offset) { BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), value); return offset + 4; } private static IExifValue GetOffsetValue(List ifdValues, List values, ExifTag offset) { int num = -1; for (int i = 0; i < ifdValues.Count; i++) { if (ifdValues[i].Tag == offset) { num = i; } } if (values.Count > 0) { if (num != -1) { return ifdValues[num]; } ExifValue exifValue = ExifValues.Create(offset); ifdValues.Add(exifValue); return exifValue; } if (num != -1) { ifdValues.RemoveAt(num); } return null; } private List GetPartValues(ExifParts part) { List list = new List(); if (!EnumUtils.HasFlag(allowedParts, part)) { return list; } foreach (IExifValue value in values) { if (HasValue(value) && ExifTags.GetPart(value.Tag) == part) { list.Add(value); } } return list; } private static bool HasValue(IExifValue exifValue) { object value = exifValue.GetValue(); if (value == null) { return false; } if (exifValue.DataType == ExifDataType.Ascii) { return ((string)value).Length > 0; } if (value is Array array) { return array.Length > 0; } return true; } private uint GetLength(IList values) { if (values.Count == 0) { return 0u; } uint num = 2u; foreach (IExifValue value in values) { uint length = GetLength(value); num += 12; if (length > 4) { num += length; } } return num + 4; } internal static uint GetLength(IExifValue value) { return GetNumberOfComponents(value) * ExifDataTypes.GetSize(value.DataType); } internal static uint GetNumberOfComponents(IExifValue exifValue) { object value = exifValue.GetValue(); if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag)) { return (uint)ExifUcs2StringHelpers.Ucs2Encoding.GetByteCount((string)value); } if (value is EncodedString encodedString) { return ExifEncodedStringHelpers.GetDataLength(encodedString); } if (exifValue.DataType == ExifDataType.Ascii) { return (uint)(ExifConstants.DefaultEncoding.GetByteCount((string)value) + 1); } if (value is Array array) { return (uint)array.Length; } return 1u; } private static int WriteArray(IExifValue value, Span destination, int offset) { int num = offset; foreach (object item in (Array)value.GetValue()) { num = WriteValue(value.DataType, item, destination, num); } return num; } private int WriteData(uint startIndex, List values, Span destination, int offset) { if (dataOffsets.Count == 0) { return offset; } int num = offset; int num2 = 0; foreach (IExifValue value in values) { if (GetLength(value) > 4) { WriteUInt32((uint)(num - startIndex), destination, dataOffsets[num2++]); num = WriteValue(value, destination, num); } } return num; } private int WriteHeaders(List values, Span destination, int offset) { dataOffsets = new List(); int num = WriteUInt16((ushort)values.Count, destination, offset); if (values.Count == 0) { return num; } foreach (IExifValue value in values) { num = WriteUInt16((ushort)value.Tag, destination, num); num = WriteUInt16((ushort)value.DataType, destination, num); num = WriteUInt32(GetNumberOfComponents(value), destination, num); if (GetLength(value) > 4) { dataOffsets.Add(num); } else { WriteValue(value, destination, num); } num += 4; } return WriteUInt32(0u, destination, num); } private static void WriteRational(Span destination, in Rational value) { BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(0, 4), value.Numerator); BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(4, 4), value.Denominator); } private static void WriteSignedRational(Span destination, in SignedRational value) { BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(0, 4), value.Numerator); BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(4, 4), value.Denominator); } private static int WriteValue(ExifDataType dataType, object value, Span destination, int offset) { switch (dataType) { case ExifDataType.Ascii: offset = Write(ExifConstants.DefaultEncoding.GetBytes((string)value), destination, offset); destination[offset] = 0; return offset + 1; case ExifDataType.Byte: case ExifDataType.Undefined: destination[offset] = (byte)value; return offset + 1; case ExifDataType.DoubleFloat: return WriteDouble((double)value, destination, offset); case ExifDataType.Short: if (value is Number number2) { return WriteUInt16((ushort)number2, destination, offset); } return WriteUInt16((ushort)value, destination, offset); case ExifDataType.Long: if (value is Number number) { return WriteUInt32((uint)number, destination, offset); } return WriteUInt32((uint)value, destination, offset); case ExifDataType.Long8: return WriteUInt64((ulong)value, destination, offset); case ExifDataType.SignedLong8: return WriteInt64((long)value, destination, offset); case ExifDataType.Rational: WriteRational(destination.Slice(offset, 8), (Rational)value); return offset + 8; case ExifDataType.SignedByte: destination[offset] = (byte)(sbyte)value; return offset + 1; case ExifDataType.SignedLong: return WriteInt32((int)value, destination, offset); case ExifDataType.SignedShort: return WriteInt16((short)value, destination, offset); case ExifDataType.SignedRational: WriteSignedRational(destination.Slice(offset, 8), (SignedRational)value); return offset + 8; case ExifDataType.SingleFloat: return WriteSingle((float)value, destination, offset); default: throw new NotImplementedException(); } } internal static int WriteValue(IExifValue exifValue, Span destination, int offset) { object value = exifValue.GetValue(); if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag)) { return offset + ExifUcs2StringHelpers.Write((string)value, destination.Slice(offset)); } if (value is EncodedString encodedString) { return offset + ExifEncodedStringHelpers.Write(encodedString, destination.Slice(offset)); } if (exifValue.IsArray) { return WriteArray(exifValue, destination, offset); } return WriteValue(exifValue.DataType, value, destination, offset); } } public abstract class ExifTag : IEquatable { private readonly ushort value; public static ExifTag FaxProfile { get; } = new ExifTag(ExifTagValue.FaxProfile); public static ExifTag ModeNumber { get; } = new ExifTag(ExifTagValue.ModeNumber); public static ExifTag GPSAltitudeRef { get; } = new ExifTag(ExifTagValue.GPSAltitudeRef); public static ExifTag ClipPath => new ExifTag(ExifTagValue.ClipPath); public static ExifTag VersionYear => new ExifTag(ExifTagValue.VersionYear); public static ExifTag XMP => new ExifTag(ExifTagValue.XMP); public static ExifTag IPTC => new ExifTag(ExifTagValue.IPTC); public static ExifTag IccProfile => new ExifTag(ExifTagValue.IccProfile); public static ExifTag CFAPattern2 => new ExifTag(ExifTagValue.CFAPattern2); public static ExifTag TIFFEPStandardID => new ExifTag(ExifTagValue.TIFFEPStandardID); public static ExifTag GPSVersionID => new ExifTag(ExifTagValue.GPSVersionID); public static ExifTag PixelScale { get; } = new ExifTag(ExifTagValue.PixelScale); public static ExifTag IntergraphMatrix { get; } = new ExifTag(ExifTagValue.IntergraphMatrix); public static ExifTag ModelTiePoint { get; } = new ExifTag(ExifTagValue.ModelTiePoint); public static ExifTag ModelTransform { get; } = new ExifTag(ExifTagValue.ModelTransform); public static ExifTag UserComment { get; } = new ExifTag(ExifTagValue.UserComment); public static ExifTag GPSProcessingMethod { get; } = new ExifTag(ExifTagValue.GPSProcessingMethod); public static ExifTag GPSAreaInformation { get; } = new ExifTag(ExifTagValue.GPSAreaInformation); public static ExifTag SubfileType { get; } = new ExifTag(ExifTagValue.SubfileType); public static ExifTag SubIFDOffset { get; } = new ExifTag(ExifTagValue.SubIFDOffset); public static ExifTag GPSIFDOffset { get; } = new ExifTag(ExifTagValue.GPSIFDOffset); public static ExifTag T4Options { get; } = new ExifTag(ExifTagValue.T4Options); public static ExifTag T6Options { get; } = new ExifTag(ExifTagValue.T6Options); public static ExifTag XClipPathUnits { get; } = new ExifTag(ExifTagValue.XClipPathUnits); public static ExifTag YClipPathUnits { get; } = new ExifTag(ExifTagValue.YClipPathUnits); public static ExifTag ProfileType { get; } = new ExifTag(ExifTagValue.ProfileType); public static ExifTag CodingMethods { get; } = new ExifTag(ExifTagValue.CodingMethods); public static ExifTag T82ptions { get; } = new ExifTag(ExifTagValue.T82ptions); public static ExifTag JPEGInterchangeFormat { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormat); public static ExifTag JPEGInterchangeFormatLength { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormatLength); public static ExifTag MDFileTag { get; } = new ExifTag(ExifTagValue.MDFileTag); public static ExifTag StandardOutputSensitivity { get; } = new ExifTag(ExifTagValue.StandardOutputSensitivity); public static ExifTag RecommendedExposureIndex { get; } = new ExifTag(ExifTagValue.RecommendedExposureIndex); public static ExifTag ISOSpeed { get; } = new ExifTag(ExifTagValue.ISOSpeed); public static ExifTag ISOSpeedLatitudeyyy { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudeyyy); public static ExifTag ISOSpeedLatitudezzz { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudezzz); public static ExifTag FaxRecvParams { get; } = new ExifTag(ExifTagValue.FaxRecvParams); public static ExifTag FaxRecvTime { get; } = new ExifTag(ExifTagValue.FaxRecvTime); public static ExifTag ImageNumber { get; } = new ExifTag(ExifTagValue.ImageNumber); public static ExifTag FreeOffsets { get; } = new ExifTag(ExifTagValue.FreeOffsets); public static ExifTag FreeByteCounts { get; } = new ExifTag(ExifTagValue.FreeByteCounts); public static ExifTag ColorResponseUnit { get; } = new ExifTag(ExifTagValue.ColorResponseUnit); public static ExifTag TileOffsets { get; } = new ExifTag(ExifTagValue.TileOffsets); public static ExifTag SMinSampleValue { get; } = new ExifTag(ExifTagValue.SMinSampleValue); public static ExifTag SMaxSampleValue { get; } = new ExifTag(ExifTagValue.SMaxSampleValue); public static ExifTag JPEGQTables { get; } = new ExifTag(ExifTagValue.JPEGQTables); public static ExifTag JPEGDCTables { get; } = new ExifTag(ExifTagValue.JPEGDCTables); public static ExifTag JPEGACTables { get; } = new ExifTag(ExifTagValue.JPEGACTables); public static ExifTag StripRowCounts { get; } = new ExifTag(ExifTagValue.StripRowCounts); public static ExifTag IntergraphRegisters { get; } = new ExifTag(ExifTagValue.IntergraphRegisters); public static ExifTag TimeZoneOffset { get; } = new ExifTag(ExifTagValue.TimeZoneOffset); public static ExifTag SubIFDs { get; } = new ExifTag(ExifTagValue.SubIFDs); public static ExifTag ImageWidth { get; } = new ExifTag(ExifTagValue.ImageWidth); public static ExifTag ImageLength { get; } = new ExifTag(ExifTagValue.ImageLength); public static ExifTag RowsPerStrip { get; } = new ExifTag(ExifTagValue.RowsPerStrip); public static ExifTag TileWidth { get; } = new ExifTag(ExifTagValue.TileWidth); public static ExifTag TileLength { get; } = new ExifTag(ExifTagValue.TileLength); public static ExifTag BadFaxLines { get; } = new ExifTag(ExifTagValue.BadFaxLines); public static ExifTag ConsecutiveBadFaxLines { get; } = new ExifTag(ExifTagValue.ConsecutiveBadFaxLines); public static ExifTag PixelXDimension { get; } = new ExifTag(ExifTagValue.PixelXDimension); public static ExifTag PixelYDimension { get; } = new ExifTag(ExifTagValue.PixelYDimension); public static ExifTag StripOffsets { get; } = new ExifTag(ExifTagValue.StripOffsets); public static ExifTag StripByteCounts { get; } = new ExifTag(ExifTagValue.StripByteCounts); public static ExifTag TileByteCounts { get; } = new ExifTag(ExifTagValue.TileByteCounts); public static ExifTag ImageLayer { get; } = new ExifTag(ExifTagValue.ImageLayer); public static ExifTag XPosition { get; } = new ExifTag(ExifTagValue.XPosition); public static ExifTag YPosition { get; } = new ExifTag(ExifTagValue.YPosition); public static ExifTag XResolution { get; } = new ExifTag(ExifTagValue.XResolution); public static ExifTag YResolution { get; } = new ExifTag(ExifTagValue.YResolution); public static ExifTag BatteryLevel { get; } = new ExifTag(ExifTagValue.BatteryLevel); public static ExifTag ExposureTime { get; } = new ExifTag(ExifTagValue.ExposureTime); public static ExifTag FNumber { get; } = new ExifTag(ExifTagValue.FNumber); public static ExifTag MDScalePixel { get; } = new ExifTag(ExifTagValue.MDScalePixel); public static ExifTag CompressedBitsPerPixel { get; } = new ExifTag(ExifTagValue.CompressedBitsPerPixel); public static ExifTag ApertureValue { get; } = new ExifTag(ExifTagValue.ApertureValue); public static ExifTag MaxApertureValue { get; } = new ExifTag(ExifTagValue.MaxApertureValue); public static ExifTag SubjectDistance { get; } = new ExifTag(ExifTagValue.SubjectDistance); public static ExifTag FocalLength { get; } = new ExifTag(ExifTagValue.FocalLength); public static ExifTag FlashEnergy2 { get; } = new ExifTag(ExifTagValue.FlashEnergy2); public static ExifTag FocalPlaneXResolution2 { get; } = new ExifTag(ExifTagValue.FocalPlaneXResolution2); public static ExifTag FocalPlaneYResolution2 { get; } = new ExifTag(ExifTagValue.FocalPlaneYResolution2); public static ExifTag ExposureIndex2 { get; } = new ExifTag(ExifTagValue.ExposureIndex2); public static ExifTag Humidity { get; } = new ExifTag(ExifTagValue.Humidity); public static ExifTag Pressure { get; } = new ExifTag(ExifTagValue.Pressure); public static ExifTag Acceleration { get; } = new ExifTag(ExifTagValue.Acceleration); public static ExifTag FlashEnergy { get; } = new ExifTag(ExifTagValue.FlashEnergy); public static ExifTag FocalPlaneXResolution { get; } = new ExifTag(ExifTagValue.FocalPlaneXResolution); public static ExifTag FocalPlaneYResolution { get; } = new ExifTag(ExifTagValue.FocalPlaneYResolution); public static ExifTag ExposureIndex { get; } = new ExifTag(ExifTagValue.ExposureIndex); public static ExifTag DigitalZoomRatio { get; } = new ExifTag(ExifTagValue.DigitalZoomRatio); public static ExifTag GPSAltitude { get; } = new ExifTag(ExifTagValue.GPSAltitude); public static ExifTag GPSDOP { get; } = new ExifTag(ExifTagValue.GPSDOP); public static ExifTag GPSSpeed { get; } = new ExifTag(ExifTagValue.GPSSpeed); public static ExifTag GPSTrack { get; } = new ExifTag(ExifTagValue.GPSTrack); public static ExifTag GPSImgDirection { get; } = new ExifTag(ExifTagValue.GPSImgDirection); public static ExifTag GPSDestBearing { get; } = new ExifTag(ExifTagValue.GPSDestBearing); public static ExifTag GPSDestDistance { get; } = new ExifTag(ExifTagValue.GPSDestDistance); public static ExifTag WhitePoint { get; } = new ExifTag(ExifTagValue.WhitePoint); public static ExifTag PrimaryChromaticities { get; } = new ExifTag(ExifTagValue.PrimaryChromaticities); public static ExifTag YCbCrCoefficients { get; } = new ExifTag(ExifTagValue.YCbCrCoefficients); public static ExifTag ReferenceBlackWhite { get; } = new ExifTag(ExifTagValue.ReferenceBlackWhite); public static ExifTag GPSLatitude { get; } = new ExifTag(ExifTagValue.GPSLatitude); public static ExifTag GPSLongitude { get; } = new ExifTag(ExifTagValue.GPSLongitude); public static ExifTag GPSTimestamp { get; } = new ExifTag(ExifTagValue.GPSTimestamp); public static ExifTag GPSDestLatitude { get; } = new ExifTag(ExifTagValue.GPSDestLatitude); public static ExifTag GPSDestLongitude { get; } = new ExifTag(ExifTagValue.GPSDestLongitude); public static ExifTag LensSpecification { get; } = new ExifTag(ExifTagValue.LensSpecification); public static ExifTag OldSubfileType { get; } = new ExifTag(ExifTagValue.OldSubfileType); public static ExifTag Compression { get; } = new ExifTag(ExifTagValue.Compression); public static ExifTag PhotometricInterpretation { get; } = new ExifTag(ExifTagValue.PhotometricInterpretation); public static ExifTag Thresholding { get; } = new ExifTag(ExifTagValue.Thresholding); public static ExifTag CellWidth { get; } = new ExifTag(ExifTagValue.CellWidth); public static ExifTag CellLength { get; } = new ExifTag(ExifTagValue.CellLength); public static ExifTag FillOrder { get; } = new ExifTag(ExifTagValue.FillOrder); public static ExifTag Orientation { get; } = new ExifTag(ExifTagValue.Orientation); public static ExifTag SamplesPerPixel { get; } = new ExifTag(ExifTagValue.SamplesPerPixel); public static ExifTag PlanarConfiguration { get; } = new ExifTag(ExifTagValue.PlanarConfiguration); public static ExifTag Predictor { get; } = new ExifTag(ExifTagValue.Predictor); public static ExifTag GrayResponseUnit { get; } = new ExifTag(ExifTagValue.GrayResponseUnit); public static ExifTag ResolutionUnit { get; } = new ExifTag(ExifTagValue.ResolutionUnit); public static ExifTag CleanFaxData { get; } = new ExifTag(ExifTagValue.CleanFaxData); public static ExifTag InkSet { get; } = new ExifTag(ExifTagValue.InkSet); public static ExifTag NumberOfInks { get; } = new ExifTag(ExifTagValue.NumberOfInks); public static ExifTag DotRange { get; } = new ExifTag(ExifTagValue.DotRange); public static ExifTag Indexed { get; } = new ExifTag(ExifTagValue.Indexed); public static ExifTag OPIProxy { get; } = new ExifTag(ExifTagValue.OPIProxy); public static ExifTag JPEGProc { get; } = new ExifTag(ExifTagValue.JPEGProc); public static ExifTag JPEGRestartInterval { get; } = new ExifTag(ExifTagValue.JPEGRestartInterval); public static ExifTag YCbCrPositioning { get; } = new ExifTag(ExifTagValue.YCbCrPositioning); public static ExifTag Rating { get; } = new ExifTag(ExifTagValue.Rating); public static ExifTag RatingPercent { get; } = new ExifTag(ExifTagValue.RatingPercent); public static ExifTag ExposureProgram { get; } = new ExifTag(ExifTagValue.ExposureProgram); public static ExifTag Interlace { get; } = new ExifTag(ExifTagValue.Interlace); public static ExifTag SelfTimerMode { get; } = new ExifTag(ExifTagValue.SelfTimerMode); public static ExifTag SensitivityType { get; } = new ExifTag(ExifTagValue.SensitivityType); public static ExifTag MeteringMode { get; } = new ExifTag(ExifTagValue.MeteringMode); public static ExifTag LightSource { get; } = new ExifTag(ExifTagValue.LightSource); public static ExifTag FocalPlaneResolutionUnit2 { get; } = new ExifTag(ExifTagValue.FocalPlaneResolutionUnit2); public static ExifTag SensingMethod2 { get; } = new ExifTag(ExifTagValue.SensingMethod2); public static ExifTag Flash { get; } = new ExifTag(ExifTagValue.Flash); public static ExifTag ColorSpace { get; } = new ExifTag(ExifTagValue.ColorSpace); public static ExifTag FocalPlaneResolutionUnit { get; } = new ExifTag(ExifTagValue.FocalPlaneResolutionUnit); public static ExifTag SensingMethod { get; } = new ExifTag(ExifTagValue.SensingMethod); public static ExifTag CustomRendered { get; } = new ExifTag(ExifTagValue.CustomRendered); public static ExifTag ExposureMode { get; } = new ExifTag(ExifTagValue.ExposureMode); public static ExifTag WhiteBalance { get; } = new ExifTag(ExifTagValue.WhiteBalance); public static ExifTag FocalLengthIn35mmFilm { get; } = new ExifTag(ExifTagValue.FocalLengthIn35mmFilm); public static ExifTag SceneCaptureType { get; } = new ExifTag(ExifTagValue.SceneCaptureType); public static ExifTag GainControl { get; } = new ExifTag(ExifTagValue.GainControl); public static ExifTag Contrast { get; } = new ExifTag(ExifTagValue.Contrast); public static ExifTag Saturation { get; } = new ExifTag(ExifTagValue.Saturation); public static ExifTag Sharpness { get; } = new ExifTag(ExifTagValue.Sharpness); public static ExifTag SubjectDistanceRange { get; } = new ExifTag(ExifTagValue.SubjectDistanceRange); public static ExifTag GPSDifferential { get; } = new ExifTag(ExifTagValue.GPSDifferential); public static ExifTag BitsPerSample { get; } = new ExifTag(ExifTagValue.BitsPerSample); public static ExifTag MinSampleValue { get; } = new ExifTag(ExifTagValue.MinSampleValue); public static ExifTag MaxSampleValue { get; } = new ExifTag(ExifTagValue.MaxSampleValue); public static ExifTag GrayResponseCurve { get; } = new ExifTag(ExifTagValue.GrayResponseCurve); public static ExifTag ColorMap { get; } = new ExifTag(ExifTagValue.ColorMap); public static ExifTag ExtraSamples { get; } = new ExifTag(ExifTagValue.ExtraSamples); public static ExifTag PageNumber { get; } = new ExifTag(ExifTagValue.PageNumber); public static ExifTag TransferFunction { get; } = new ExifTag(ExifTagValue.TransferFunction); public static ExifTag HalftoneHints { get; } = new ExifTag(ExifTagValue.HalftoneHints); public static ExifTag SampleFormat { get; } = new ExifTag(ExifTagValue.SampleFormat); public static ExifTag TransferRange { get; } = new ExifTag(ExifTagValue.TransferRange); public static ExifTag DefaultImageColor { get; } = new ExifTag(ExifTagValue.DefaultImageColor); public static ExifTag JPEGLosslessPredictors { get; } = new ExifTag(ExifTagValue.JPEGLosslessPredictors); public static ExifTag JPEGPointTransforms { get; } = new ExifTag(ExifTagValue.JPEGPointTransforms); public static ExifTag YCbCrSubsampling { get; } = new ExifTag(ExifTagValue.YCbCrSubsampling); public static ExifTag CFARepeatPatternDim { get; } = new ExifTag(ExifTagValue.CFARepeatPatternDim); public static ExifTag IntergraphPacketData { get; } = new ExifTag(ExifTagValue.IntergraphPacketData); public static ExifTag ISOSpeedRatings { get; } = new ExifTag(ExifTagValue.ISOSpeedRatings); public static ExifTag SubjectArea { get; } = new ExifTag(ExifTagValue.SubjectArea); public static ExifTag SubjectLocation { get; } = new ExifTag(ExifTagValue.SubjectLocation); public static ExifTag ShutterSpeedValue { get; } = new ExifTag(ExifTagValue.ShutterSpeedValue); public static ExifTag BrightnessValue { get; } = new ExifTag(ExifTagValue.BrightnessValue); public static ExifTag ExposureBiasValue { get; } = new ExifTag(ExifTagValue.ExposureBiasValue); public static ExifTag AmbientTemperature { get; } = new ExifTag(ExifTagValue.AmbientTemperature); public static ExifTag WaterDepth { get; } = new ExifTag(ExifTagValue.WaterDepth); public static ExifTag CameraElevationAngle { get; } = new ExifTag(ExifTagValue.CameraElevationAngle); public static ExifTag Decode { get; } = new ExifTag(ExifTagValue.Decode); public static ExifTag ImageDescription { get; } = new ExifTag(ExifTagValue.ImageDescription); public static ExifTag Make { get; } = new ExifTag(ExifTagValue.Make); public static ExifTag Model { get; } = new ExifTag(ExifTagValue.Model); public static ExifTag Software { get; } = new ExifTag(ExifTagValue.Software); public static ExifTag DateTime { get; } = new ExifTag(ExifTagValue.DateTime); public static ExifTag Artist { get; } = new ExifTag(ExifTagValue.Artist); public static ExifTag HostComputer { get; } = new ExifTag(ExifTagValue.HostComputer); public static ExifTag Copyright { get; } = new ExifTag(ExifTagValue.Copyright); public static ExifTag DocumentName { get; } = new ExifTag(ExifTagValue.DocumentName); public static ExifTag PageName { get; } = new ExifTag(ExifTagValue.PageName); public static ExifTag InkNames { get; } = new ExifTag(ExifTagValue.InkNames); public static ExifTag TargetPrinter { get; } = new ExifTag(ExifTagValue.TargetPrinter); public static ExifTag ImageID { get; } = new ExifTag(ExifTagValue.ImageID); public static ExifTag MDLabName { get; } = new ExifTag(ExifTagValue.MDLabName); public static ExifTag MDSampleInfo { get; } = new ExifTag(ExifTagValue.MDSampleInfo); public static ExifTag MDPrepDate { get; } = new ExifTag(ExifTagValue.MDPrepDate); public static ExifTag MDPrepTime { get; } = new ExifTag(ExifTagValue.MDPrepTime); public static ExifTag MDFileUnits { get; } = new ExifTag(ExifTagValue.MDFileUnits); public static ExifTag SEMInfo { get; } = new ExifTag(ExifTagValue.SEMInfo); public static ExifTag SpectralSensitivity { get; } = new ExifTag(ExifTagValue.SpectralSensitivity); public static ExifTag DateTimeOriginal { get; } = new ExifTag(ExifTagValue.DateTimeOriginal); public static ExifTag DateTimeDigitized { get; } = new ExifTag(ExifTagValue.DateTimeDigitized); public static ExifTag SubsecTime { get; } = new ExifTag(ExifTagValue.SubsecTime); public static ExifTag SubsecTimeOriginal { get; } = new ExifTag(ExifTagValue.SubsecTimeOriginal); public static ExifTag SubsecTimeDigitized { get; } = new ExifTag(ExifTagValue.SubsecTimeDigitized); public static ExifTag RelatedSoundFile { get; } = new ExifTag(ExifTagValue.RelatedSoundFile); public static ExifTag FaxSubaddress { get; } = new ExifTag(ExifTagValue.FaxSubaddress); public static ExifTag OffsetTime { get; } = new ExifTag(ExifTagValue.OffsetTime); public static ExifTag OffsetTimeOriginal { get; } = new ExifTag(ExifTagValue.OffsetTimeOriginal); public static ExifTag OffsetTimeDigitized { get; } = new ExifTag(ExifTagValue.OffsetTimeDigitized); public static ExifTag SecurityClassification { get; } = new ExifTag(ExifTagValue.SecurityClassification); public static ExifTag ImageHistory { get; } = new ExifTag(ExifTagValue.ImageHistory); public static ExifTag ImageUniqueID { get; } = new ExifTag(ExifTagValue.ImageUniqueID); public static ExifTag OwnerName { get; } = new ExifTag(ExifTagValue.OwnerName); public static ExifTag SerialNumber { get; } = new ExifTag(ExifTagValue.SerialNumber); public static ExifTag LensMake { get; } = new ExifTag(ExifTagValue.LensMake); public static ExifTag LensModel { get; } = new ExifTag(ExifTagValue.LensModel); public static ExifTag LensSerialNumber { get; } = new ExifTag(ExifTagValue.LensSerialNumber); public static ExifTag GDALMetadata { get; } = new ExifTag(ExifTagValue.GDALMetadata); public static ExifTag GDALNoData { get; } = new ExifTag(ExifTagValue.GDALNoData); public static ExifTag GPSLatitudeRef { get; } = new ExifTag(ExifTagValue.InteroperabilityIndex); public static ExifTag GPSLongitudeRef { get; } = new ExifTag(ExifTagValue.GPSLongitudeRef); public static ExifTag GPSSatellites { get; } = new ExifTag(ExifTagValue.GPSSatellites); public static ExifTag GPSStatus { get; } = new ExifTag(ExifTagValue.GPSStatus); public static ExifTag GPSMeasureMode { get; } = new ExifTag(ExifTagValue.GPSMeasureMode); public static ExifTag GPSSpeedRef { get; } = new ExifTag(ExifTagValue.GPSSpeedRef); public static ExifTag GPSTrackRef { get; } = new ExifTag(ExifTagValue.GPSTrackRef); public static ExifTag GPSImgDirectionRef { get; } = new ExifTag(ExifTagValue.GPSImgDirectionRef); public static ExifTag GPSMapDatum { get; } = new ExifTag(ExifTagValue.GPSMapDatum); public static ExifTag GPSDestLatitudeRef { get; } = new ExifTag(ExifTagValue.GPSDestLatitudeRef); public static ExifTag GPSDestLongitudeRef { get; } = new ExifTag(ExifTagValue.GPSDestLongitudeRef); public static ExifTag GPSDestBearingRef { get; } = new ExifTag(ExifTagValue.GPSDestBearingRef); public static ExifTag GPSDestDistanceRef { get; } = new ExifTag(ExifTagValue.GPSDestDistanceRef); public static ExifTag GPSDateStamp { get; } = new ExifTag(ExifTagValue.GPSDateStamp); public static ExifTag XPTitle => new ExifTag(ExifTagValue.XPTitle); public static ExifTag XPComment => new ExifTag(ExifTagValue.XPComment); public static ExifTag XPAuthor => new ExifTag(ExifTagValue.XPAuthor); public static ExifTag XPKeywords => new ExifTag(ExifTagValue.XPKeywords); public static ExifTag XPSubject => new ExifTag(ExifTagValue.XPSubject); public static ExifTag JPEGTables { get; } = new ExifTag(ExifTagValue.JPEGTables); public static ExifTag OECF { get; } = new ExifTag(ExifTagValue.OECF); public static ExifTag ExifVersion { get; } = new ExifTag(ExifTagValue.ExifVersion); public static ExifTag ComponentsConfiguration { get; } = new ExifTag(ExifTagValue.ComponentsConfiguration); public static ExifTag MakerNote { get; } = new ExifTag(ExifTagValue.MakerNote); public static ExifTag FlashpixVersion { get; } = new ExifTag(ExifTagValue.FlashpixVersion); public static ExifTag SpatialFrequencyResponse { get; } = new ExifTag(ExifTagValue.SpatialFrequencyResponse); public static ExifTag SpatialFrequencyResponse2 { get; } = new ExifTag(ExifTagValue.SpatialFrequencyResponse2); public static ExifTag Noise { get; } = new ExifTag(ExifTagValue.Noise); public static ExifTag CFAPattern { get; } = new ExifTag(ExifTagValue.CFAPattern); public static ExifTag DeviceSettingDescription { get; } = new ExifTag(ExifTagValue.DeviceSettingDescription); public static ExifTag ImageSourceData { get; } = new ExifTag(ExifTagValue.ImageSourceData); public static ExifTag FileSource { get; } = new ExifTag(ExifTagValue.FileSource); public static ExifTag SceneType { get; } = new ExifTag(ExifTagValue.SceneType); internal ExifTag(ushort value) { this.value = value; } public static explicit operator ushort(ExifTag tag) { return tag?.value ?? ushort.MaxValue; } public static bool operator ==(ExifTag left, ExifTag right) { return object.Equals(left, right); } public static bool operator !=(ExifTag left, ExifTag right) { return !object.Equals(left, right); } public override bool Equals(object obj) { if (obj is ExifTag other) { return Equals(other); } return false; } public bool Equals(ExifTag other) { if ((object)other == null) { return false; } if ((object)this == other) { return true; } return value == other.value; } public override int GetHashCode() { return value.GetHashCode(); } public override string ToString() { return ((ExifTagValue)value/*cast due to .constrained prefix*/).ToString(); } } internal enum ExifTagValue { Unknown = 65535, SubIFDOffset = 34665, GPSIFDOffset = 34853, [ExifTagDescription("R98", "Indicates a file conforming to R98 file specification of Recommended Exif Interoperability Rules (ExifR98) or to DCF basic file stipulated by Design Rule for Camera File System.")] [ExifTagDescription("THM", "Indicates a file conforming to DCF thumbnail file stipulated by Design rule for Camera File System.")] InteroperabilityIndex = 1, [ExifTagDescription(0u, "Full-resolution Image")] [ExifTagDescription(1u, "Reduced-resolution image")] [ExifTagDescription(2u, "Single page of multi-page image")] [ExifTagDescription(3u, "Single page of multi-page reduced-resolution image")] [ExifTagDescription(4u, "Transparency mask")] [ExifTagDescription(5u, "Transparency mask of reduced-resolution image")] [ExifTagDescription(6u, "Transparency mask of multi-page image")] [ExifTagDescription(7u, "Transparency mask of reduced-resolution multi-page image")] [ExifTagDescription(65537u, "Alternate reduced-resolution image ")] SubfileType = 254, [ExifTagDescription((ushort)1, "Full-resolution Image")] [ExifTagDescription((ushort)2, "Reduced-resolution image")] [ExifTagDescription((ushort)3, "Single page of multi-page image")] OldSubfileType = 255, ImageWidth = 256, ImageLength = 257, BitsPerSample = 258, [ExifTagDescription((ushort)1, "Uncompressed")] [ExifTagDescription((ushort)2, "CCITT 1D")] [ExifTagDescription((ushort)3, "T4/Group 3 Fax")] [ExifTagDescription((ushort)4, "T6/Group 4 Fax")] [ExifTagDescription((ushort)5, "LZW")] [ExifTagDescription((ushort)6, "JPEG (old-style)")] [ExifTagDescription((ushort)7, "JPEG")] [ExifTagDescription((ushort)8, "Adobe Deflate")] [ExifTagDescription((ushort)9, "JBIG B&W")] [ExifTagDescription((ushort)10, "JBIG Color")] [ExifTagDescription((ushort)99, "JPEG")] [ExifTagDescription((ushort)262, "Kodak 262")] [ExifTagDescription((ushort)32766, "Next")] [ExifTagDescription((ushort)32767, "Sony ARW Compressed")] [ExifTagDescription((ushort)32769, "Packed RAW")] [ExifTagDescription((ushort)32770, "Samsung SRW Compressed")] [ExifTagDescription((ushort)32771, "CCIRLEW")] [ExifTagDescription((ushort)32772, "Samsung SRW Compressed 2")] [ExifTagDescription((ushort)32773, "PackBits")] [ExifTagDescription((ushort)32809, "Thunderscan")] [ExifTagDescription((ushort)32867, "Kodak KDC Compressed")] [ExifTagDescription((ushort)32895, "IT8CTPAD")] [ExifTagDescription((ushort)32896, "IT8LW")] [ExifTagDescription((ushort)32897, "IT8MP")] [ExifTagDescription((ushort)32898, "IT8BL")] [ExifTagDescription((ushort)32908, "PixarFilm")] [ExifTagDescription((ushort)32909, "PixarLog")] [ExifTagDescription((ushort)32946, "Deflate")] [ExifTagDescription((ushort)32947, "DCS")] [ExifTagDescription((ushort)34661, "JBIG")] [ExifTagDescription((ushort)34676, "SGILog")] [ExifTagDescription((ushort)34677, "SGILog24")] [ExifTagDescription((ushort)34712, "JPEG 2000")] [ExifTagDescription((ushort)34713, "Nikon NEF Compressed")] [ExifTagDescription((ushort)34715, "JBIG2 TIFF FX")] [ExifTagDescription((ushort)34718, "Microsoft Document Imaging (MDI) Binary Level Codec")] [ExifTagDescription((ushort)34719, "Microsoft Document Imaging (MDI) Progressive Transform Codec")] [ExifTagDescription((ushort)34720, "Microsoft Document Imaging (MDI) Vector")] [ExifTagDescription((ushort)34892, "Lossy JPEG")] [ExifTagDescription((ushort)65000, "Kodak DCR Compressed")] [ExifTagDescription(ushort.MaxValue, "Pentax PEF Compressed")] Compression = 259, [ExifTagDescription((ushort)0, "WhiteIsZero")] [ExifTagDescription((ushort)1, "BlackIsZero")] [ExifTagDescription((ushort)2, "RGB")] [ExifTagDescription((ushort)3, "RGB Palette")] [ExifTagDescription((ushort)4, "Transparency Mask")] [ExifTagDescription((ushort)5, "CMYK")] [ExifTagDescription((ushort)6, "YCbCr")] [ExifTagDescription((ushort)8, "CIELab")] [ExifTagDescription((ushort)9, "ICCLab")] [ExifTagDescription((ushort)10, "TULab")] [ExifTagDescription((ushort)32803, "Color Filter Array")] [ExifTagDescription((ushort)32844, "Pixar LogL")] [ExifTagDescription((ushort)32845, "Pixar LogLuv")] [ExifTagDescription((ushort)34892, "Linear Raw")] PhotometricInterpretation = 262, [ExifTagDescription((ushort)1, "No dithering or halftoning")] [ExifTagDescription((ushort)2, "Ordered dither or halftone")] [ExifTagDescription((ushort)3, "Randomized dither")] Thresholding = 263, CellWidth = 264, CellLength = 265, [ExifTagDescription((ushort)1, "Normal")] [ExifTagDescription((ushort)2, "Reversed")] FillOrder = 266, DocumentName = 269, ImageDescription = 270, Make = 271, Model = 272, StripOffsets = 273, [ExifTagDescription((ushort)1, "Horizontal (normal)")] [ExifTagDescription((ushort)2, "Mirror horizontal")] [ExifTagDescription((ushort)3, "Rotate 180")] [ExifTagDescription((ushort)4, "Mirror vertical")] [ExifTagDescription((ushort)5, "Mirror horizontal and rotate 270 CW")] [ExifTagDescription((ushort)6, "Rotate 90 CW")] [ExifTagDescription((ushort)7, "Mirror horizontal and rotate 90 CW")] [ExifTagDescription((ushort)8, "Rotate 270 CW")] Orientation = 274, SamplesPerPixel = 277, RowsPerStrip = 278, StripByteCounts = 279, MinSampleValue = 280, MaxSampleValue = 281, XResolution = 282, YResolution = 283, [ExifTagDescription((ushort)1, "Chunky")] [ExifTagDescription((ushort)2, "Planar")] PlanarConfiguration = 284, PageName = 285, XPosition = 286, YPosition = 287, FreeOffsets = 288, FreeByteCounts = 289, [ExifTagDescription((ushort)1, "0.1")] [ExifTagDescription((ushort)2, "0.001")] [ExifTagDescription((ushort)3, "0.0001")] [ExifTagDescription((ushort)4, "1e-05")] [ExifTagDescription((ushort)5, "1e-06")] GrayResponseUnit = 290, GrayResponseCurve = 291, [ExifTagDescription(0u, "2-Dimensional encoding")] [ExifTagDescription(1u, "Uncompressed")] [ExifTagDescription(2u, "Fill bits added")] T4Options = 292, [ExifTagDescription(1u, "Uncompressed")] T6Options = 293, [ExifTagDescription((ushort)1, "None")] [ExifTagDescription((ushort)2, "Inches")] [ExifTagDescription((ushort)3, "Centimeter")] ResolutionUnit = 296, PageNumber = 297, ColorResponseUnit = 300, TransferFunction = 301, Software = 305, DateTime = 306, Artist = 315, HostComputer = 316, Predictor = 317, WhitePoint = 318, PrimaryChromaticities = 319, ColorMap = 320, HalftoneHints = 321, TileWidth = 322, TileLength = 323, TileOffsets = 324, TileByteCounts = 325, BadFaxLines = 326, [ExifTagDescription(0u, "Clean")] [ExifTagDescription(1u, "Regenerated")] [ExifTagDescription(2u, "Unclean")] CleanFaxData = 327, ConsecutiveBadFaxLines = 328, SubIFDs = 330, [ExifTagDescription((ushort)1, "CMYK")] [ExifTagDescription((ushort)2, "Not CMYK")] InkSet = 332, InkNames = 333, NumberOfInks = 334, DotRange = 336, TargetPrinter = 337, [ExifTagDescription((ushort)0, "Unspecified")] [ExifTagDescription((ushort)1, "Associated Alpha")] [ExifTagDescription((ushort)2, "Unassociated Alpha")] ExtraSamples = 338, [ExifTagDescription((ushort)1, "Unsigned")] [ExifTagDescription((ushort)2, "Signed")] [ExifTagDescription((ushort)3, "Float")] [ExifTagDescription((ushort)4, "Undefined")] [ExifTagDescription((ushort)5, "Complex int")] [ExifTagDescription((ushort)6, "Complex float")] SampleFormat = 339, SMinSampleValue = 340, SMaxSampleValue = 341, TransferRange = 342, ClipPath = 343, XClipPathUnits = 344, YClipPathUnits = 345, [ExifTagDescription((ushort)0, "Not indexed")] [ExifTagDescription((ushort)1, "Indexed")] Indexed = 346, JPEGTables = 347, [ExifTagDescription((ushort)0, "Higher resolution image does not exist")] [ExifTagDescription((ushort)1, "Higher resolution image exists")] OPIProxy = 351, GlobalParametersIFD = 400, [ExifTagDescription(0u, "Unspecified")] [ExifTagDescription(1u, "Group 3 FAX")] ProfileType = 401, [ExifTagDescription((byte)0, "Unknown")] [ExifTagDescription((byte)1, "Minimal B&W lossless, S")] [ExifTagDescription((byte)2, "Extended B&W lossless, F")] [ExifTagDescription((byte)3, "Lossless JBIG B&W, J")] [ExifTagDescription((byte)4, "Lossy color and grayscale, C")] [ExifTagDescription((byte)5, "Lossless color and grayscale, L")] [ExifTagDescription((byte)6, "Mixed raster content, M")] [ExifTagDescription((byte)7, "Profile T")] [ExifTagDescription(byte.MaxValue, "Multi Profiles")] FaxProfile = 402, [ExifTagDescription(0uL, "Unspecified compression")] [ExifTagDescription(1uL, "Modified Huffman")] [ExifTagDescription(2uL, "Modified Read")] [ExifTagDescription(4uL, "Modified MR")] [ExifTagDescription(8uL, "JBIG")] [ExifTagDescription(16uL, "Baseline JPEG")] [ExifTagDescription(32uL, "JBIG color")] CodingMethods = 403, VersionYear = 404, ModeNumber = 405, Decode = 433, DefaultImageColor = 434, T82ptions = 435, [ExifTagDescription((ushort)1, "Baseline")] [ExifTagDescription((ushort)14, "Lossless")] JPEGProc = 512, JPEGInterchangeFormat = 513, JPEGInterchangeFormatLength = 514, JPEGRestartInterval = 515, JPEGLosslessPredictors = 517, JPEGPointTransforms = 518, JPEGQTables = 519, JPEGDCTables = 520, JPEGACTables = 521, YCbCrCoefficients = 529, YCbCrSubsampling = 530, [ExifTagDescription((ushort)1, "Centered")] [ExifTagDescription((ushort)2, "Co-sited")] YCbCrPositioning = 531, ReferenceBlackWhite = 532, StripRowCounts = 559, XMP = 700, Rating = 18246, RatingPercent = 18249, ImageID = 32781, WangAnnotation = 32932, CFARepeatPatternDim = 33421, CFAPattern2 = 33422, BatteryLevel = 33423, Copyright = 33432, ExposureTime = 33434, FNumber = 33437, [ExifTagDescription((ushort)2, "Squary root data format")] [ExifTagDescription((ushort)128, "Linear data format")] MDFileTag = 33445, MDScalePixel = 33446, [ExifTagDescription((ushort)0, "lowest possible")] [ExifTagDescription((ushort)1, "low range")] [ExifTagDescription("n-2", "high range")] [ExifTagDescription("n-1", "highest possible")] MDColorTable = 33447, MDLabName = 33448, MDSampleInfo = 33449, MDPrepDate = 33450, MDPrepTime = 33451, [ExifTagDescription("O.D.", "Densitometer")] [ExifTagDescription("Counts", "PhosphorImager")] [ExifTagDescription("RFU", "FluorImager")] MDFileUnits = 33452, PixelScale = 33550, IPTC = 33723, IntergraphPacketData = 33918, IntergraphRegisters = 33919, IntergraphMatrix = 33920, ModelTiePoint = 33922, SEMInfo = 34118, ModelTransform = 34264, Photoshop = 34377, IccProfile = 34675, GeoKeyDirectoryTag = 34735, GeoDoubleParamsTag = 34736, GeoAsciiParamsTag = 34737, ImageLayer = 34732, [ExifTagDescription((ushort)0, "Not Defined")] [ExifTagDescription((ushort)1, "Manual")] [ExifTagDescription((ushort)2, "Program AE")] [ExifTagDescription((ushort)3, "Aperture-priority AE")] [ExifTagDescription((ushort)4, "Shutter speed priority AE")] [ExifTagDescription((ushort)5, "Creative (Slow speed)")] [ExifTagDescription((ushort)6, "Action (High speed)")] [ExifTagDescription((ushort)7, "Portrait")] [ExifTagDescription((ushort)8, "Landscape")] [ExifTagDescription((ushort)9, "Bulb")] ExposureProgram = 34850, SpectralSensitivity = 34852, ISOSpeedRatings = 34855, OECF = 34856, Interlace = 34857, TimeZoneOffset = 34858, SelfTimerMode = 34859, [ExifTagDescription((ushort)0, "Unknown")] [ExifTagDescription((ushort)1, "Standard Output Sensitivity")] [ExifTagDescription((ushort)2, "Recommended Exposure Index")] [ExifTagDescription((ushort)3, "ISO Speed")] [ExifTagDescription((ushort)4, "Standard Output Sensitivity and Recommended Exposure Index")] [ExifTagDescription((ushort)5, "Standard Output Sensitivity and ISO Speed")] [ExifTagDescription((ushort)6, "Recommended Exposure Index and ISO Speed")] [ExifTagDescription((ushort)7, "Standard Output Sensitivity, Recommended Exposure Index and ISO Speed")] SensitivityType = 34864, StandardOutputSensitivity = 34865, RecommendedExposureIndex = 34866, ISOSpeed = 34867, ISOSpeedLatitudeyyy = 34868, ISOSpeedLatitudezzz = 34869, FaxRecvParams = 34908, FaxSubaddress = 34909, FaxRecvTime = 34910, ExifVersion = 36864, DateTimeOriginal = 36867, DateTimeDigitized = 36868, OffsetTime = 36880, OffsetTimeOriginal = 36881, OffsetTimeDigitized = 36882, ComponentsConfiguration = 37121, CompressedBitsPerPixel = 37122, ShutterSpeedValue = 37377, ApertureValue = 37378, BrightnessValue = 37379, ExposureBiasValue = 37380, MaxApertureValue = 37381, SubjectDistance = 37382, [ExifTagDescription((ushort)0, "Unknown")] [ExifTagDescription((ushort)1, "Average")] [ExifTagDescription((ushort)2, "Center-weighted average")] [ExifTagDescription((ushort)3, "Spot")] [ExifTagDescription((ushort)4, "Multi-spot")] [ExifTagDescription((ushort)5, "Multi-segment")] [ExifTagDescription((ushort)6, "Partial")] [ExifTagDescription((ushort)255, "Other")] MeteringMode = 37383, [ExifTagDescription((ushort)0, "Unknown")] [ExifTagDescription((ushort)1, "Daylight")] [ExifTagDescription((ushort)2, "Fluorescent")] [ExifTagDescription((ushort)3, "Tungsten (Incandescent)")] [ExifTagDescription((ushort)4, "Flash")] [ExifTagDescription((ushort)9, "Fine Weather")] [ExifTagDescription((ushort)10, "Cloudy")] [ExifTagDescription((ushort)11, "Shade")] [ExifTagDescription((ushort)12, "Daylight Fluorescent")] [ExifTagDescription((ushort)13, "Day White Fluorescent")] [ExifTagDescription((ushort)14, "Cool White Fluorescent")] [ExifTagDescription((ushort)15, "White Fluorescent")] [ExifTagDescription((ushort)16, "Warm White Fluorescent")] [ExifTagDescription((ushort)17, "Standard Light A")] [ExifTagDescription((ushort)18, "Standard Light B")] [ExifTagDescription((ushort)19, "Standard Light C")] [ExifTagDescription((ushort)20, "D55")] [ExifTagDescription((ushort)21, "D65")] [ExifTagDescription((ushort)22, "D75")] [ExifTagDescription((ushort)23, "D50")] [ExifTagDescription((ushort)24, "ISO Studio Tungsten")] [ExifTagDescription((ushort)255, "Other")] LightSource = 37384, [ExifTagDescription((ushort)0, "No Flash")] [ExifTagDescription((ushort)1, "Fired")] [ExifTagDescription((ushort)5, "Fired, Return not detected")] [ExifTagDescription((ushort)7, "Fired, Return detected")] [ExifTagDescription((ushort)8, "On, Did not fire")] [ExifTagDescription((ushort)9, "On, Fired")] [ExifTagDescription((ushort)13, "On, Return not detected")] [ExifTagDescription((ushort)15, "On, Return detected")] [ExifTagDescription((ushort)16, "Off, Did not fire")] [ExifTagDescription((ushort)20, "Off, Did not fire, Return not detected")] [ExifTagDescription((ushort)24, "Auto, Did not fire")] [ExifTagDescription((ushort)25, "Auto, Fired")] [ExifTagDescription((ushort)29, "Auto, Fired, Return not detected")] [ExifTagDescription((ushort)31, "Auto, Fired, Return detected")] [ExifTagDescription((ushort)32, "No flash function")] [ExifTagDescription((ushort)48, "Off, No flash function")] [ExifTagDescription((ushort)65, "Fired, Red-eye reduction")] [ExifTagDescription((ushort)69, "Fired, Red-eye reduction, Return not detected")] [ExifTagDescription((ushort)71, "Fired, Red-eye reduction, Return detected")] [ExifTagDescription((ushort)73, "On, Red-eye reduction")] [ExifTagDescription((ushort)77, "On, Red-eye reduction, Return not detected")] [ExifTagDescription((ushort)79, "On, Red-eye reduction, Return detected")] [ExifTagDescription((ushort)80, "Off, Red-eye reduction")] [ExifTagDescription((ushort)88, "Auto, Did not fire, Red-eye reduction")] [ExifTagDescription((ushort)89, "Auto, Fired, Red-eye reduction")] [ExifTagDescription((ushort)93, "Auto, Fired, Red-eye reduction, Return not detected")] [ExifTagDescription((ushort)95, "Auto, Fired, Red-eye reduction, Return detected")] Flash = 37385, FocalLength = 37386, FlashEnergy2 = 37387, SpatialFrequencyResponse2 = 37388, Noise = 37389, FocalPlaneXResolution2 = 37390, FocalPlaneYResolution2 = 37391, [ExifTagDescription((ushort)1, "None")] [ExifTagDescription((ushort)2, "Inches")] [ExifTagDescription((ushort)3, "Centimeter")] [ExifTagDescription((ushort)4, "Millimeter")] [ExifTagDescription((ushort)5, "Micrometer")] FocalPlaneResolutionUnit2 = 37392, ImageNumber = 37393, [ExifTagDescription("C", "Confidential")] [ExifTagDescription("R", "Restricted")] [ExifTagDescription("S", "Secret")] [ExifTagDescription("T", "Top Secret")] [ExifTagDescription("U", "Unclassified")] SecurityClassification = 37394, ImageHistory = 37395, SubjectArea = 37396, ExposureIndex2 = 37397, TIFFEPStandardID = 37398, [ExifTagDescription((ushort)1, "Not defined")] [ExifTagDescription((ushort)2, "One-chip color area")] [ExifTagDescription((ushort)3, "Two-chip color area")] [ExifTagDescription((ushort)4, "Three-chip color area")] [ExifTagDescription((ushort)5, "Color sequential area")] [ExifTagDescription((ushort)7, "Trilinear")] [ExifTagDescription((ushort)8, "Color sequential linear")] SensingMethod2 = 37399, MakerNote = 37500, UserComment = 37510, SubsecTime = 37520, SubsecTimeOriginal = 37521, SubsecTimeDigitized = 37522, ImageSourceData = 37724, AmbientTemperature = 37888, Humidity = 37889, Pressure = 37890, WaterDepth = 37891, Acceleration = 37892, CameraElevationAngle = 37893, XPTitle = 40091, XPComment = 40092, XPAuthor = 40093, XPKeywords = 40094, XPSubject = 40095, FlashpixVersion = 40960, [ExifTagDescription((ushort)1, "sRGB")] [ExifTagDescription((ushort)2, "Adobe RGB")] [ExifTagDescription((ushort)4093, "Wide Gamut RGB")] [ExifTagDescription((ushort)65534, "ICC Profile")] [ExifTagDescription(ushort.MaxValue, "Uncalibrated")] ColorSpace = 40961, PixelXDimension = 40962, PixelYDimension = 40963, RelatedSoundFile = 40964, InteroperabilityIFD = 40965, FlashEnergy = 41483, SpatialFrequencyResponse = 41484, FocalPlaneXResolution = 41486, FocalPlaneYResolution = 41487, [ExifTagDescription((ushort)1, "None")] [ExifTagDescription((ushort)2, "Inches")] [ExifTagDescription((ushort)3, "Centimeter")] [ExifTagDescription((ushort)4, "Millimeter")] [ExifTagDescription((ushort)5, "Micrometer")] FocalPlaneResolutionUnit = 41488, SubjectLocation = 41492, ExposureIndex = 41493, [ExifTagDescription((ushort)1, "Not defined")] [ExifTagDescription((ushort)2, "One-chip color area")] [ExifTagDescription((ushort)3, "Two-chip color area")] [ExifTagDescription((ushort)4, "Three-chip color area")] [ExifTagDescription((ushort)5, "Color sequential area")] [ExifTagDescription((ushort)7, "Trilinear")] [ExifTagDescription((ushort)8, "Color sequential linear")] SensingMethod = 41495, FileSource = 41728, SceneType = 41729, CFAPattern = 41730, [ExifTagDescription((ushort)1, "Normal")] [ExifTagDescription((ushort)2, "Custom")] CustomRendered = 41985, [ExifTagDescription((ushort)0, "Auto")] [ExifTagDescription((ushort)1, "Manual")] [ExifTagDescription((ushort)2, "Auto bracket")] ExposureMode = 41986, [ExifTagDescription((ushort)0, "Auto")] [ExifTagDescription((ushort)1, "Manual")] WhiteBalance = 41987, DigitalZoomRatio = 41988, FocalLengthIn35mmFilm = 41989, [ExifTagDescription((ushort)0, "Standard")] [ExifTagDescription((ushort)1, "Landscape")] [ExifTagDescription((ushort)2, "Portrait")] [ExifTagDescription((ushort)3, "Night")] SceneCaptureType = 41990, [ExifTagDescription((ushort)0, "None")] [ExifTagDescription((ushort)1, "Low gain up")] [ExifTagDescription((ushort)2, "High gain up")] [ExifTagDescription((ushort)3, "Low gain down")] [ExifTagDescription((ushort)4, "High gain down")] GainControl = 41991, [ExifTagDescription((ushort)0, "Normal")] [ExifTagDescription((ushort)1, "Low")] [ExifTagDescription((ushort)2, "High")] Contrast = 41992, [ExifTagDescription((ushort)0, "Normal")] [ExifTagDescription((ushort)1, "Low")] [ExifTagDescription((ushort)2, "High")] Saturation = 41993, [ExifTagDescription((ushort)0, "Normal")] [ExifTagDescription((ushort)1, "Soft")] [ExifTagDescription((ushort)2, "Hard")] Sharpness = 41994, DeviceSettingDescription = 41995, [ExifTagDescription((ushort)0, "Unknown")] [ExifTagDescription((ushort)1, "Macro")] [ExifTagDescription((ushort)2, "Close")] [ExifTagDescription((ushort)3, "Distant")] SubjectDistanceRange = 41996, ImageUniqueID = 42016, OwnerName = 42032, SerialNumber = 42033, LensSpecification = 42034, LensMake = 42035, LensModel = 42036, LensSerialNumber = 42037, GDALMetadata = 42112, GDALNoData = 42113, GPSVersionID = 0, GPSLatitudeRef = 1, GPSLatitude = 2, GPSLongitudeRef = 3, GPSLongitude = 4, GPSAltitudeRef = 5, GPSAltitude = 6, GPSTimestamp = 7, GPSSatellites = 8, GPSStatus = 9, GPSMeasureMode = 10, GPSDOP = 11, GPSSpeedRef = 12, GPSSpeed = 13, GPSTrackRef = 14, GPSTrack = 15, GPSImgDirectionRef = 16, GPSImgDirection = 17, GPSMapDatum = 18, GPSDestLatitudeRef = 19, GPSDestLatitude = 20, GPSDestLongitudeRef = 21, GPSDestLongitude = 22, GPSDestBearingRef = 23, GPSDestBearing = 24, GPSDestDistanceRef = 25, GPSDestDistance = 26, GPSProcessingMethod = 27, GPSAreaInformation = 28, GPSDateStamp = 29, GPSDifferential = 30, OceScanjobDescription = 50215, OceApplicationSelector = 50216, OceIdentificationNumber = 50217, OceImageLogicCharacteristics = 50218, AliasLayerMetadata = 50784 } public sealed class ExifTag : ExifTag { internal ExifTag(ExifTagValue value) : base((ushort)value) { } } internal sealed class UnkownExifTag : ExifTag { internal UnkownExifTag(ExifTagValue value) : base((ushort)value) { } } public readonly struct EncodedString : IEquatable { public enum CharacterCode { ASCII, JIS, Unicode, Undefined } public CharacterCode Code { get; } public string Text { get; } public EncodedString(string text) : this(CharacterCode.Unicode, text) { } public EncodedString(CharacterCode code, string text) { Text = text; Code = code; } public static implicit operator EncodedString(string text) { return new EncodedString(text); } public static explicit operator string(EncodedString encodedString) { return encodedString.Text; } public override bool Equals(object obj) { if (obj is EncodedString other) { return Equals(other); } return false; } public bool Equals(EncodedString other) { if (Text == other.Text) { return Code == other.Code; } return false; } public override int GetHashCode() { return HashCode.Combine(Text, Code); } public override string ToString() { return Text; } } internal abstract class ExifArrayValue : ExifValue, IExifValue, IExifValue, IDeepCloneable { public override bool IsArray => true; public TValueType[] Value { get; set; } protected ExifArrayValue(ExifTag tag) : base(tag) { } protected ExifArrayValue(ExifTagValue tag) : base(tag) { } internal ExifArrayValue(ExifArrayValue value) : base(value) { } public override object GetValue() { return Value; } public override bool TrySetValue(object value) { if (value == null) { Value = null; return true; } Type type = value.GetType(); if (value.GetType() == typeof(TValueType[])) { Value = (TValueType[])value; return true; } if (type == typeof(TValueType)) { Value = new TValueType[1] { (TValueType)value }; return true; } return false; } } internal sealed class ExifByte : ExifValue { public override ExifDataType DataType { get; } protected override string StringValue => base.Value.ToString("X2", CultureInfo.InvariantCulture); public ExifByte(ExifTag tag, ExifDataType dataType) : base(tag) { DataType = dataType; } public ExifByte(ExifTagValue tag, ExifDataType dataType) : base(tag) { DataType = dataType; } private ExifByte(ExifByte value) : base((ExifValue)value) { DataType = value.DataType; } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is int num) { if (num >= 0 && num <= 255) { base.Value = (byte)num; return true; } return false; } return base.TrySetValue(value); } public override IExifValue DeepClone() { return new ExifByte(this); } } internal sealed class ExifByteArray : ExifArrayValue { public override ExifDataType DataType { get; } public ExifByteArray(ExifTag tag, ExifDataType dataType) : base(tag) { DataType = dataType; } public ExifByteArray(ExifTagValue tag, ExifDataType dataType) : base(tag) { DataType = dataType; } private ExifByteArray(ExifByteArray value) : base((ExifArrayValue)value) { DataType = value.DataType; } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is int[] intArrayValue) { return TrySetSignedIntArray(intArrayValue); } if (value is int num) { if (num >= 0 && num <= 255) { base.Value = new byte[1] { (byte)num }; } return true; } return false; } public override IExifValue DeepClone() { return new ExifByteArray(this); } private bool TrySetSignedIntArray(int[] intArrayValue) { if (Array.FindIndex(intArrayValue, (int x) => x < 0 || x > 255) > -1) { return false; } byte[] array = new byte[intArrayValue.Length]; for (int num = 0; num < intArrayValue.Length; num++) { int num2 = intArrayValue[num]; array[num] = (byte)num2; } base.Value = array; return true; } } internal sealed class ExifDouble : ExifValue { public override ExifDataType DataType => ExifDataType.DoubleFloat; protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); public ExifDouble(ExifTag tag) : base(tag) { } public ExifDouble(ExifTagValue tag) : base(tag) { } private ExifDouble(ExifDouble value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is int num) { base.Value = num; return true; } return false; } public override IExifValue DeepClone() { return new ExifDouble(this); } } internal sealed class ExifDoubleArray : ExifArrayValue { public override ExifDataType DataType => ExifDataType.DoubleFloat; public ExifDoubleArray(ExifTag tag) : base(tag) { } public ExifDoubleArray(ExifTagValue tag) : base(tag) { } private ExifDoubleArray(ExifDoubleArray value) : base((ExifArrayValue)value) { } public override IExifValue DeepClone() { return new ExifDoubleArray(this); } } internal sealed class ExifEncodedString : ExifValue { public override ExifDataType DataType => ExifDataType.Undefined; protected override string StringValue => base.Value.Text; public ExifEncodedString(ExifTag tag) : base(tag) { } public ExifEncodedString(ExifTagValue tag) : base(tag) { } private ExifEncodedString(ExifEncodedString value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is string text) { base.Value = new EncodedString(text); return true; } if (value is byte[] array && ExifEncodedStringHelpers.TryParse(array, out var encodedString)) { base.Value = encodedString; return true; } return false; } public override IExifValue DeepClone() { return new ExifEncodedString(this); } } internal sealed class ExifFloat : ExifValue { public override ExifDataType DataType => ExifDataType.SingleFloat; protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); public ExifFloat(ExifTagValue tag) : base(tag) { } private ExifFloat(ExifFloat value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is int num) { base.Value = num; return true; } return false; } public override IExifValue DeepClone() { return new ExifFloat(this); } } internal sealed class ExifFloatArray : ExifArrayValue { public override ExifDataType DataType => ExifDataType.SingleFloat; public ExifFloatArray(ExifTagValue tag) : base(tag) { } private ExifFloatArray(ExifFloatArray value) : base((ExifArrayValue)value) { } public override IExifValue DeepClone() { return new ExifFloatArray(this); } } internal sealed class ExifLong : ExifValue { public override ExifDataType DataType => ExifDataType.Long; protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); public ExifLong(ExifTag tag) : base(tag) { } public ExifLong(ExifTagValue tag) : base(tag) { } private ExifLong(ExifLong value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is int num) { if ((long)num >= 0L) { base.Value = (uint)num; return true; } return false; } return false; } public override IExifValue DeepClone() { return new ExifLong(this); } } internal sealed class ExifLong8 : ExifValue { public override ExifDataType DataType => ExifDataType.Long8; protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); public ExifLong8(ExifTag tag) : base(tag) { } public ExifLong8(ExifTagValue tag) : base(tag) { } private ExifLong8(ExifLong8 value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (!(value is int num)) { if (!(value is uint num2)) { if (value is long num3) { if (num3 >= 0) { base.Value = (ulong)num3; return true; } return false; } return false; } base.Value = num2; return true; } if ((long)num >= 0L) { base.Value = (uint)num; return true; } return false; } public override IExifValue DeepClone() { return new ExifLong8(this); } } internal sealed class ExifLong8Array : ExifArrayValue { public override ExifDataType DataType { get { if (base.Value != null) { ulong[] value = base.Value; for (int i = 0; i < value.Length; i++) { if (value[i] > uint.MaxValue) { return ExifDataType.Long8; } } } return ExifDataType.Long; } } public ExifLong8Array(ExifTagValue tag) : base(tag) { } private ExifLong8Array(ExifLong8Array value) : base((ExifArrayValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (!(value is int value2)) { if (!(value is uint num)) { if (!(value is short value3)) { if (!(value is ushort num2)) { if (!(value is long num3)) { if (!(value is long[] array)) { if (!(value is int[] array2)) { if (value is short[] array3) { if (value.GetType() == typeof(ushort[])) { return SetArray((ushort[])value); } return SetArray(array3); } return false; } if (value.GetType() == typeof(uint[])) { return SetArray((uint[])value); } return SetArray(array2); } if (value.GetType() == typeof(ulong[])) { return SetArray((ulong[])value); } return SetArray(array); } return SetSingle((ulong)Numerics.Clamp(num3, 0f, 9.223372E+18f)); } return SetSingle(num2); } return SetSingle((ulong)Numerics.Clamp(value3, 0, 32767)); } return SetSingle(num); } return SetSingle((ulong)Numerics.Clamp(value2, 0, int.MaxValue)); } public override IExifValue DeepClone() { return new ExifLong8Array(this); } private bool SetSingle(ulong value) { base.Value = new ulong[1] { value }; return true; } private bool SetArray(long[] values) { ulong[] array = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { array[i] = (ulong)((values[i] < 0) ? 0 : values[i]); } base.Value = array; return true; } private bool SetArray(ulong[] values) { base.Value = values; return true; } private bool SetArray(int[] values) { ulong[] array = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { array[i] = (ulong)Numerics.Clamp(values[i], 0, int.MaxValue); } base.Value = array; return true; } private bool SetArray(uint[] values) { ulong[] array = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { array[i] = values[i]; } base.Value = array; return true; } private bool SetArray(short[] values) { ulong[] array = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { array[i] = (ulong)Numerics.Clamp(values[i], 0, 32767); } base.Value = array; return true; } private bool SetArray(ushort[] values) { ulong[] array = new ulong[values.Length]; for (int i = 0; i < values.Length; i++) { array[i] = values[i]; } base.Value = array; return true; } } internal sealed class ExifLongArray : ExifArrayValue { public override ExifDataType DataType => ExifDataType.Long; public ExifLongArray(ExifTag tag) : base(tag) { } public ExifLongArray(ExifTagValue tag) : base(tag) { } private ExifLongArray(ExifLongArray value) : base((ExifArrayValue)value) { } public override IExifValue DeepClone() { return new ExifLongArray(this); } } internal sealed class ExifNumber : ExifValue { public override ExifDataType DataType { get { if (base.Value > ushort.MaxValue) { return ExifDataType.Long; } return ExifDataType.Short; } } protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); public ExifNumber(ExifTag tag) : base(tag) { } private ExifNumber(ExifNumber value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (!(value is int num)) { if (!(value is uint num2)) { if (!(value is short num3)) { if (value is ushort num4) { base.Value = num4; return true; } return false; } if ((long)num3 >= 0L) { base.Value = (uint)num3; return true; } return false; } base.Value = num2; return true; } if ((long)num >= 0L) { base.Value = (uint)num; return true; } return false; } public override IExifValue DeepClone() { return new ExifNumber(this); } } internal sealed class ExifNumberArray : ExifArrayValue { public override ExifDataType DataType { get { if (base.Value != null) { Number[] value = base.Value; for (int i = 0; i < value.Length; i++) { if (value[i] > ushort.MaxValue) { return ExifDataType.Long; } } } return ExifDataType.Short; } } public ExifNumberArray(ExifTag tag) : base(tag) { } private ExifNumberArray(ExifNumberArray value) : base((ExifArrayValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (!(value is int num)) { if (!(value is uint num2)) { if (!(value is short num3)) { if (!(value is ushort num4)) { if (!(value is int[] array)) { if (value is short[] array2) { if (value.GetType() == typeof(ushort[])) { return SetArray((ushort[])value); } return SetArray(array2); } return false; } if (value.GetType() == typeof(uint[])) { return SetArray((uint[])value); } return SetArray(array); } return SetSingle(num4); } return SetSingle(num3); } return SetSingle(num2); } return SetSingle(num); } public override IExifValue DeepClone() { return new ExifNumberArray(this); } private bool SetSingle(Number value) { base.Value = new Number[1] { value }; return true; } private bool SetArray(int[] values) { Number[] array = new Number[values.Length]; for (int i = 0; i < values.Length; i++) { array[i] = values[i]; } base.Value = array; return true; } private bool SetArray(uint[] values) { Number[] array = new Number[values.Length]; for (int i = 0; i < values.Length; i++) { array[i] = values[i]; } base.Value = array; return true; } private bool SetArray(short[] values) { Number[] array = new Number[values.Length]; for (int i = 0; i < values.Length; i++) { array[i] = values[i]; } base.Value = array; return true; } private bool SetArray(ushort[] values) { Number[] array = new Number[values.Length]; for (int i = 0; i < values.Length; i++) { array[i] = values[i]; } base.Value = array; return true; } } public static class ExifOrientationMode { public const ushort Unknown = 0; public const ushort TopLeft = 1; public const ushort TopRight = 2; public const ushort BottomRight = 3; public const ushort BottomLeft = 4; public const ushort LeftTop = 5; public const ushort RightTop = 6; public const ushort RightBottom = 7; public const ushort LeftBottom = 8; } internal sealed class ExifRational : ExifValue { public override ExifDataType DataType => ExifDataType.Rational; protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); public ExifRational(ExifTag tag) : base(tag) { } public ExifRational(ExifTagValue tag) : base(tag) { } private ExifRational(ExifRational value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is SignedRational signedRational) { if ((long)signedRational.Numerator >= 0L && (long)signedRational.Denominator >= 0L) { base.Value = new Rational((uint)signedRational.Numerator, (uint)signedRational.Denominator); } return true; } return false; } public override IExifValue DeepClone() { return new ExifRational(this); } } internal sealed class ExifRationalArray : ExifArrayValue { public override ExifDataType DataType => ExifDataType.Rational; public ExifRationalArray(ExifTag tag) : base(tag) { } public ExifRationalArray(ExifTagValue tag) : base(tag) { } private ExifRationalArray(ExifRationalArray value) : base((ExifArrayValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is SignedRational[] signed) { return TrySetSignedArray(signed); } if (value is SignedRational signedRational) { if (signedRational.Numerator >= 0 && signedRational.Denominator >= 0) { base.Value = new Rational[1] { new Rational((uint)signedRational.Numerator, (uint)signedRational.Denominator) }; } return true; } return false; } public override IExifValue DeepClone() { return new ExifRationalArray(this); } private bool TrySetSignedArray(SignedRational[] signed) { if (Array.FindIndex(signed, (SignedRational x) => x.Numerator < 0 || x.Denominator < 0) > -1) { return false; } Rational[] array = new Rational[signed.Length]; for (int num = 0; num < signed.Length; num++) { SignedRational signedRational = signed[num]; array[num] = new Rational((uint)signedRational.Numerator, (uint)signedRational.Denominator); } base.Value = array; return true; } } internal sealed class ExifShort : ExifValue { public override ExifDataType DataType => ExifDataType.Short; protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); public ExifShort(ExifTag tag) : base(tag) { } public ExifShort(ExifTagValue tag) : base(tag) { } private ExifShort(ExifShort value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (!(value is int num)) { if (value is short num2) { if (num2 >= 0) { base.Value = (ushort)num2; return true; } return false; } return false; } if (num >= 0 && num <= 65535) { base.Value = (ushort)num; return true; } return false; } public override IExifValue DeepClone() { return new ExifShort(this); } } internal sealed class ExifShortArray : ExifArrayValue { public override ExifDataType DataType => ExifDataType.Short; public ExifShortArray(ExifTag tag) : base(tag) { } public ExifShortArray(ExifTagValue tag) : base(tag) { } private ExifShortArray(ExifShortArray value) : base((ExifArrayValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is int[] signed) { return TrySetSignedIntArray(signed); } if (value is short[] signed2) { return TrySetSignedShortArray(signed2); } if (value is int num) { if (num >= 0 && num <= 65535) { base.Value = new ushort[1] { (ushort)num }; } return true; } if (value is short num2) { if (num2 >= 0) { base.Value = new ushort[1] { (ushort)num2 }; } return true; } return false; } public override IExifValue DeepClone() { return new ExifShortArray(this); } private bool TrySetSignedIntArray(int[] signed) { if (Array.FindIndex(signed, (int x) => x < 0 || x > 65535) > -1) { return false; } ushort[] array = new ushort[signed.Length]; for (int num = 0; num < signed.Length; num++) { int num2 = signed[num]; array[num] = (ushort)num2; } base.Value = array; return true; } private bool TrySetSignedShortArray(short[] signed) { if (Array.FindIndex(signed, (short x) => x < 0) > -1) { return false; } ushort[] array = new ushort[signed.Length]; for (int num = 0; num < signed.Length; num++) { short num2 = signed[num]; array[num] = (ushort)num2; } base.Value = array; return true; } } internal sealed class ExifSignedByte : ExifValue { public override ExifDataType DataType => ExifDataType.SignedByte; protected override string StringValue => base.Value.ToString("X2", CultureInfo.InvariantCulture); public ExifSignedByte(ExifTagValue tag) : base(tag) { } private ExifSignedByte(ExifSignedByte value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is int num) { if (num >= -128 && num <= 127) { base.Value = (sbyte)num; return true; } return false; } return false; } public override IExifValue DeepClone() { return new ExifSignedByte(this); } } internal sealed class ExifSignedByteArray : ExifArrayValue { public override ExifDataType DataType => ExifDataType.SignedByte; public ExifSignedByteArray(ExifTagValue tag) : base(tag) { } private ExifSignedByteArray(ExifSignedByteArray value) : base((ExifArrayValue)value) { } public override IExifValue DeepClone() { return new ExifSignedByteArray(this); } } internal sealed class ExifSignedLong : ExifValue { public override ExifDataType DataType => ExifDataType.SignedLong; protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); public ExifSignedLong(ExifTagValue tag) : base(tag) { } private ExifSignedLong(ExifSignedLong value) : base((ExifValue)value) { } public override IExifValue DeepClone() { return new ExifSignedLong(this); } } internal sealed class ExifSignedLong8 : ExifValue { public override ExifDataType DataType => ExifDataType.SignedLong8; protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); public ExifSignedLong8(ExifTagValue tag) : base(tag) { } private ExifSignedLong8(ExifSignedLong8 value) : base((ExifValue)value) { } public override IExifValue DeepClone() { return new ExifSignedLong8(this); } } internal sealed class ExifSignedLong8Array : ExifArrayValue { public override ExifDataType DataType => ExifDataType.SignedLong8; public ExifSignedLong8Array(ExifTagValue tag) : base(tag) { } private ExifSignedLong8Array(ExifSignedLong8Array value) : base((ExifArrayValue)value) { } public override IExifValue DeepClone() { return new ExifSignedLong8Array(this); } } internal sealed class ExifSignedLongArray : ExifArrayValue { public override ExifDataType DataType => ExifDataType.SignedLong; public ExifSignedLongArray(ExifTagValue tag) : base(tag) { } private ExifSignedLongArray(ExifSignedLongArray value) : base((ExifArrayValue)value) { } public override IExifValue DeepClone() { return new ExifSignedLongArray(this); } } internal sealed class ExifSignedRational : ExifValue { public override ExifDataType DataType => ExifDataType.SignedRational; protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); internal ExifSignedRational(ExifTag tag) : base(tag) { } internal ExifSignedRational(ExifTagValue tag) : base(tag) { } private ExifSignedRational(ExifSignedRational value) : base((ExifValue)value) { } public override IExifValue DeepClone() { return new ExifSignedRational(this); } } internal sealed class ExifSignedRationalArray : ExifArrayValue { public override ExifDataType DataType => ExifDataType.SignedRational; public ExifSignedRationalArray(ExifTag tag) : base(tag) { } public ExifSignedRationalArray(ExifTagValue tag) : base(tag) { } private ExifSignedRationalArray(ExifSignedRationalArray value) : base((ExifArrayValue)value) { } public override IExifValue DeepClone() { return new ExifSignedRationalArray(this); } } internal sealed class ExifSignedShort : ExifValue { public override ExifDataType DataType => ExifDataType.SignedShort; protected override string StringValue => base.Value.ToString(CultureInfo.InvariantCulture); public ExifSignedShort(ExifTagValue tag) : base(tag) { } private ExifSignedShort(ExifSignedShort value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is int num) { if (num >= -32768 && num <= 32767) { base.Value = (short)num; return true; } return false; } return false; } public override IExifValue DeepClone() { return new ExifSignedShort(this); } } internal sealed class ExifSignedShortArray : ExifArrayValue { public override ExifDataType DataType => ExifDataType.SignedShort; public ExifSignedShortArray(ExifTagValue tag) : base(tag) { } private ExifSignedShortArray(ExifSignedShortArray value) : base((ExifArrayValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is int[] intArray) { return TrySetSignedArray(intArray); } if (value is int num) { if (num >= -32768 && num <= 32767) { base.Value = new short[1] { (short)num }; } return true; } return false; } public override IExifValue DeepClone() { return new ExifSignedShortArray(this); } private bool TrySetSignedArray(int[] intArray) { if (Array.FindIndex(intArray, (int x) => x < -32768 || x > 32767) > -1) { return false; } short[] array = new short[intArray.Length]; for (int num = 0; num < intArray.Length; num++) { int num2 = intArray[num]; array[num] = (short)num2; } base.Value = array; return true; } } internal sealed class ExifString : ExifValue { public override ExifDataType DataType => ExifDataType.Ascii; protected override string StringValue => base.Value; public ExifString(ExifTag tag) : base(tag) { } public ExifString(ExifTagValue tag) : base(tag) { } private ExifString(ExifString value) : base((ExifValue)value) { } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is int num) { base.Value = num.ToString(CultureInfo.InvariantCulture); return true; } return false; } public override IExifValue DeepClone() { return new ExifString(this); } } internal sealed class ExifUcs2String : ExifValue { public override ExifDataType DataType => ExifDataType.Byte; protected override string StringValue => base.Value; public ExifUcs2String(ExifTag tag) : base(tag) { } public ExifUcs2String(ExifTagValue tag) : base(tag) { } private ExifUcs2String(ExifUcs2String value) : base((ExifValue)value) { } public override object GetValue() { return base.Value; } public override bool TrySetValue(object value) { if (base.TrySetValue(value)) { return true; } if (value is byte[] bytes) { base.Value = ExifUcs2StringHelpers.Ucs2Encoding.GetString(bytes); return true; } return false; } public override IExifValue DeepClone() { return new ExifUcs2String(this); } } internal abstract class ExifValue : IExifValue, IDeepCloneable, IEquatable { public virtual ExifDataType DataType { get; } public virtual bool IsArray { get; } public ExifTag Tag { get; } protected ExifValue(ExifTag tag) { Tag = tag; } protected ExifValue(ExifTagValue tag) { Tag = new UnkownExifTag(tag); } internal ExifValue(ExifValue other) { Guard.NotNull(other, "other"); DataType = other.DataType; IsArray = other.IsArray; Tag = other.Tag; if (!other.IsArray) { TrySetValue(other.GetValue()); } else { TrySetValue(((Array)other.GetValue())?.Clone()); } } public static bool operator ==(ExifValue left, ExifTag right) { return object.Equals(left, right); } public static bool operator !=(ExifValue left, ExifTag right) { return !object.Equals(left, right); } public override bool Equals(object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (obj is ExifTag other) { return Equals(other); } if (obj is ExifValue exifValue) { if (Tag.Equals(exifValue.Tag)) { return object.Equals(GetValue(), exifValue.GetValue()); } return false; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ExifTag other) { return Tag.Equals(other); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(Tag, GetValue()); } public abstract object GetValue(); public abstract bool TrySetValue(object value); public abstract IExifValue DeepClone(); } internal static class ExifValues { public static ExifValue Create(ExifTagValue tag) { return (ExifValue)CreateValue(tag); } public static ExifValue Create(ExifTag tag) { return (ExifValue)CreateValue((ExifTagValue)(ushort)tag); } public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, ulong numberOfComponents) { return Create(tag, dataType, numberOfComponents != 1); } public static ExifValue Create(ExifTagValue tag, ExifDataType dataType, bool isArray) { switch (dataType) { case ExifDataType.Byte: if (!isArray) { return new ExifByte(tag, dataType); } return new ExifByteArray(tag, dataType); case ExifDataType.DoubleFloat: if (!isArray) { return new ExifDouble(tag); } return new ExifDoubleArray(tag); case ExifDataType.SingleFloat: if (!isArray) { return new ExifFloat(tag); } return new ExifFloatArray(tag); case ExifDataType.Long: if (!isArray) { return new ExifLong(tag); } return new ExifLongArray(tag); case ExifDataType.Long8: if (!isArray) { return new ExifLong8(tag); } return new ExifLong8Array(tag); case ExifDataType.Rational: if (!isArray) { return new ExifRational(tag); } return new ExifRationalArray(tag); case ExifDataType.Short: if (!isArray) { return new ExifShort(tag); } return new ExifShortArray(tag); case ExifDataType.SignedByte: if (!isArray) { return new ExifSignedByte(tag); } return new ExifSignedByteArray(tag); case ExifDataType.SignedLong: if (!isArray) { return new ExifSignedLong(tag); } return new ExifSignedLongArray(tag); case ExifDataType.SignedLong8: if (!isArray) { return new ExifSignedLong8(tag); } return new ExifSignedLong8Array(tag); case ExifDataType.SignedRational: if (!isArray) { return new ExifSignedRational(tag); } return new ExifSignedRationalArray(tag); case ExifDataType.SignedShort: if (!isArray) { return new ExifSignedShort(tag); } return new ExifSignedShortArray(tag); case ExifDataType.Ascii: return new ExifString(tag); case ExifDataType.Undefined: if (!isArray) { return new ExifByte(tag, dataType); } return new ExifByteArray(tag, dataType); default: return null; } } private static object CreateValue(ExifTagValue tag) { return tag switch { ExifTagValue.FaxProfile => new ExifByte(ExifTag.FaxProfile, ExifDataType.Byte), ExifTagValue.ModeNumber => new ExifByte(ExifTag.ModeNumber, ExifDataType.Byte), ExifTagValue.GPSAltitudeRef => new ExifByte(ExifTag.GPSAltitudeRef, ExifDataType.Byte), ExifTagValue.ClipPath => new ExifByteArray(ExifTag.ClipPath, ExifDataType.Byte), ExifTagValue.VersionYear => new ExifByteArray(ExifTag.VersionYear, ExifDataType.Byte), ExifTagValue.XMP => new ExifByteArray(ExifTag.XMP, ExifDataType.Byte), ExifTagValue.CFAPattern2 => new ExifByteArray(ExifTag.CFAPattern2, ExifDataType.Byte), ExifTagValue.TIFFEPStandardID => new ExifByteArray(ExifTag.TIFFEPStandardID, ExifDataType.Byte), ExifTagValue.GPSVersionID => new ExifByteArray(ExifTag.GPSVersionID, ExifDataType.Byte), ExifTagValue.PixelScale => new ExifDoubleArray(ExifTag.PixelScale), ExifTagValue.IntergraphMatrix => new ExifDoubleArray(ExifTag.IntergraphMatrix), ExifTagValue.ModelTiePoint => new ExifDoubleArray(ExifTag.ModelTiePoint), ExifTagValue.ModelTransform => new ExifDoubleArray(ExifTag.ModelTransform), ExifTagValue.SubfileType => new ExifLong(ExifTag.SubfileType), ExifTagValue.SubIFDOffset => new ExifLong(ExifTag.SubIFDOffset), ExifTagValue.GPSIFDOffset => new ExifLong(ExifTag.GPSIFDOffset), ExifTagValue.T4Options => new ExifLong(ExifTag.T4Options), ExifTagValue.T6Options => new ExifLong(ExifTag.T6Options), ExifTagValue.XClipPathUnits => new ExifLong(ExifTag.XClipPathUnits), ExifTagValue.YClipPathUnits => new ExifLong(ExifTag.YClipPathUnits), ExifTagValue.ProfileType => new ExifLong(ExifTag.ProfileType), ExifTagValue.CodingMethods => new ExifLong(ExifTag.CodingMethods), ExifTagValue.T82ptions => new ExifLong(ExifTag.T82ptions), ExifTagValue.JPEGInterchangeFormat => new ExifLong(ExifTag.JPEGInterchangeFormat), ExifTagValue.JPEGInterchangeFormatLength => new ExifLong(ExifTag.JPEGInterchangeFormatLength), ExifTagValue.MDFileTag => new ExifLong(ExifTag.MDFileTag), ExifTagValue.StandardOutputSensitivity => new ExifLong(ExifTag.StandardOutputSensitivity), ExifTagValue.RecommendedExposureIndex => new ExifLong(ExifTag.RecommendedExposureIndex), ExifTagValue.ISOSpeed => new ExifLong(ExifTag.ISOSpeed), ExifTagValue.ISOSpeedLatitudeyyy => new ExifLong(ExifTag.ISOSpeedLatitudeyyy), ExifTagValue.ISOSpeedLatitudezzz => new ExifLong(ExifTag.ISOSpeedLatitudezzz), ExifTagValue.FaxRecvParams => new ExifLong(ExifTag.FaxRecvParams), ExifTagValue.FaxRecvTime => new ExifLong(ExifTag.FaxRecvTime), ExifTagValue.ImageNumber => new ExifLong(ExifTag.ImageNumber), ExifTagValue.FreeOffsets => new ExifLongArray(ExifTag.FreeOffsets), ExifTagValue.FreeByteCounts => new ExifLongArray(ExifTag.FreeByteCounts), ExifTagValue.ColorResponseUnit => new ExifLongArray(ExifTag.ColorResponseUnit), ExifTagValue.TileOffsets => new ExifLongArray(ExifTag.TileOffsets), ExifTagValue.SMinSampleValue => new ExifLongArray(ExifTag.SMinSampleValue), ExifTagValue.SMaxSampleValue => new ExifLongArray(ExifTag.SMaxSampleValue), ExifTagValue.JPEGQTables => new ExifLongArray(ExifTag.JPEGQTables), ExifTagValue.JPEGDCTables => new ExifLongArray(ExifTag.JPEGDCTables), ExifTagValue.JPEGACTables => new ExifLongArray(ExifTag.JPEGACTables), ExifTagValue.StripRowCounts => new ExifLongArray(ExifTag.StripRowCounts), ExifTagValue.IntergraphRegisters => new ExifLongArray(ExifTag.IntergraphRegisters), ExifTagValue.TimeZoneOffset => new ExifLongArray(ExifTag.TimeZoneOffset), ExifTagValue.SubIFDs => new ExifLongArray(ExifTag.SubIFDs), ExifTagValue.ImageWidth => new ExifNumber(ExifTag.ImageWidth), ExifTagValue.ImageLength => new ExifNumber(ExifTag.ImageLength), ExifTagValue.RowsPerStrip => new ExifNumber(ExifTag.RowsPerStrip), ExifTagValue.TileWidth => new ExifNumber(ExifTag.TileWidth), ExifTagValue.TileLength => new ExifNumber(ExifTag.TileLength), ExifTagValue.BadFaxLines => new ExifNumber(ExifTag.BadFaxLines), ExifTagValue.ConsecutiveBadFaxLines => new ExifNumber(ExifTag.ConsecutiveBadFaxLines), ExifTagValue.PixelXDimension => new ExifNumber(ExifTag.PixelXDimension), ExifTagValue.PixelYDimension => new ExifNumber(ExifTag.PixelYDimension), ExifTagValue.StripByteCounts => new ExifNumberArray(ExifTag.StripByteCounts), ExifTagValue.StripOffsets => new ExifNumberArray(ExifTag.StripOffsets), ExifTagValue.TileByteCounts => new ExifNumberArray(ExifTag.TileByteCounts), ExifTagValue.ImageLayer => new ExifNumberArray(ExifTag.ImageLayer), ExifTagValue.XPosition => new ExifRational(ExifTag.XPosition), ExifTagValue.YPosition => new ExifRational(ExifTag.YPosition), ExifTagValue.XResolution => new ExifRational(ExifTag.XResolution), ExifTagValue.YResolution => new ExifRational(ExifTag.YResolution), ExifTagValue.BatteryLevel => new ExifRational(ExifTag.BatteryLevel), ExifTagValue.ExposureTime => new ExifRational(ExifTag.ExposureTime), ExifTagValue.FNumber => new ExifRational(ExifTag.FNumber), ExifTagValue.MDScalePixel => new ExifRational(ExifTag.MDScalePixel), ExifTagValue.CompressedBitsPerPixel => new ExifRational(ExifTag.CompressedBitsPerPixel), ExifTagValue.ApertureValue => new ExifRational(ExifTag.ApertureValue), ExifTagValue.MaxApertureValue => new ExifRational(ExifTag.MaxApertureValue), ExifTagValue.SubjectDistance => new ExifRational(ExifTag.SubjectDistance), ExifTagValue.FocalLength => new ExifRational(ExifTag.FocalLength), ExifTagValue.FlashEnergy2 => new ExifRational(ExifTag.FlashEnergy2), ExifTagValue.FocalPlaneXResolution2 => new ExifRational(ExifTag.FocalPlaneXResolution2), ExifTagValue.FocalPlaneYResolution2 => new ExifRational(ExifTag.FocalPlaneYResolution2), ExifTagValue.ExposureIndex2 => new ExifRational(ExifTag.ExposureIndex2), ExifTagValue.Humidity => new ExifRational(ExifTag.Humidity), ExifTagValue.Pressure => new ExifRational(ExifTag.Pressure), ExifTagValue.Acceleration => new ExifRational(ExifTag.Acceleration), ExifTagValue.FlashEnergy => new ExifRational(ExifTag.FlashEnergy), ExifTagValue.FocalPlaneXResolution => new ExifRational(ExifTag.FocalPlaneXResolution), ExifTagValue.FocalPlaneYResolution => new ExifRational(ExifTag.FocalPlaneYResolution), ExifTagValue.ExposureIndex => new ExifRational(ExifTag.ExposureIndex), ExifTagValue.DigitalZoomRatio => new ExifRational(ExifTag.DigitalZoomRatio), ExifTagValue.GPSAltitude => new ExifRational(ExifTag.GPSAltitude), ExifTagValue.GPSDOP => new ExifRational(ExifTag.GPSDOP), ExifTagValue.GPSSpeed => new ExifRational(ExifTag.GPSSpeed), ExifTagValue.GPSTrack => new ExifRational(ExifTag.GPSTrack), ExifTagValue.GPSImgDirection => new ExifRational(ExifTag.GPSImgDirection), ExifTagValue.GPSDestBearing => new ExifRational(ExifTag.GPSDestBearing), ExifTagValue.GPSDestDistance => new ExifRational(ExifTag.GPSDestDistance), ExifTagValue.WhitePoint => new ExifRationalArray(ExifTag.WhitePoint), ExifTagValue.PrimaryChromaticities => new ExifRationalArray(ExifTag.PrimaryChromaticities), ExifTagValue.YCbCrCoefficients => new ExifRationalArray(ExifTag.YCbCrCoefficients), ExifTagValue.ReferenceBlackWhite => new ExifRationalArray(ExifTag.ReferenceBlackWhite), ExifTagValue.GPSLatitude => new ExifRationalArray(ExifTag.GPSLatitude), ExifTagValue.GPSLongitude => new ExifRationalArray(ExifTag.GPSLongitude), ExifTagValue.GPSTimestamp => new ExifRationalArray(ExifTag.GPSTimestamp), ExifTagValue.GPSDestLatitude => new ExifRationalArray(ExifTag.GPSDestLatitude), ExifTagValue.GPSDestLongitude => new ExifRationalArray(ExifTag.GPSDestLongitude), ExifTagValue.LensSpecification => new ExifRationalArray(ExifTag.LensSpecification), ExifTagValue.OldSubfileType => new ExifShort(ExifTag.OldSubfileType), ExifTagValue.Compression => new ExifShort(ExifTag.Compression), ExifTagValue.PhotometricInterpretation => new ExifShort(ExifTag.PhotometricInterpretation), ExifTagValue.Thresholding => new ExifShort(ExifTag.Thresholding), ExifTagValue.CellWidth => new ExifShort(ExifTag.CellWidth), ExifTagValue.CellLength => new ExifShort(ExifTag.CellLength), ExifTagValue.FillOrder => new ExifShort(ExifTag.FillOrder), ExifTagValue.Orientation => new ExifShort(ExifTag.Orientation), ExifTagValue.SamplesPerPixel => new ExifShort(ExifTag.SamplesPerPixel), ExifTagValue.PlanarConfiguration => new ExifShort(ExifTag.PlanarConfiguration), ExifTagValue.Predictor => new ExifShort(ExifTag.Predictor), ExifTagValue.GrayResponseUnit => new ExifShort(ExifTag.GrayResponseUnit), ExifTagValue.ResolutionUnit => new ExifShort(ExifTag.ResolutionUnit), ExifTagValue.CleanFaxData => new ExifShort(ExifTag.CleanFaxData), ExifTagValue.InkSet => new ExifShort(ExifTag.InkSet), ExifTagValue.NumberOfInks => new ExifShort(ExifTag.NumberOfInks), ExifTagValue.DotRange => new ExifShort(ExifTag.DotRange), ExifTagValue.Indexed => new ExifShort(ExifTag.Indexed), ExifTagValue.OPIProxy => new ExifShort(ExifTag.OPIProxy), ExifTagValue.JPEGProc => new ExifShort(ExifTag.JPEGProc), ExifTagValue.JPEGRestartInterval => new ExifShort(ExifTag.JPEGRestartInterval), ExifTagValue.YCbCrPositioning => new ExifShort(ExifTag.YCbCrPositioning), ExifTagValue.Rating => new ExifShort(ExifTag.Rating), ExifTagValue.RatingPercent => new ExifShort(ExifTag.RatingPercent), ExifTagValue.ExposureProgram => new ExifShort(ExifTag.ExposureProgram), ExifTagValue.Interlace => new ExifShort(ExifTag.Interlace), ExifTagValue.SelfTimerMode => new ExifShort(ExifTag.SelfTimerMode), ExifTagValue.SensitivityType => new ExifShort(ExifTag.SensitivityType), ExifTagValue.MeteringMode => new ExifShort(ExifTag.MeteringMode), ExifTagValue.LightSource => new ExifShort(ExifTag.LightSource), ExifTagValue.FocalPlaneResolutionUnit2 => new ExifShort(ExifTag.FocalPlaneResolutionUnit2), ExifTagValue.SensingMethod2 => new ExifShort(ExifTag.SensingMethod2), ExifTagValue.Flash => new ExifShort(ExifTag.Flash), ExifTagValue.ColorSpace => new ExifShort(ExifTag.ColorSpace), ExifTagValue.FocalPlaneResolutionUnit => new ExifShort(ExifTag.FocalPlaneResolutionUnit), ExifTagValue.SensingMethod => new ExifShort(ExifTag.SensingMethod), ExifTagValue.CustomRendered => new ExifShort(ExifTag.CustomRendered), ExifTagValue.ExposureMode => new ExifShort(ExifTag.ExposureMode), ExifTagValue.WhiteBalance => new ExifShort(ExifTag.WhiteBalance), ExifTagValue.FocalLengthIn35mmFilm => new ExifShort(ExifTag.FocalLengthIn35mmFilm), ExifTagValue.SceneCaptureType => new ExifShort(ExifTag.SceneCaptureType), ExifTagValue.GainControl => new ExifShort(ExifTag.GainControl), ExifTagValue.Contrast => new ExifShort(ExifTag.Contrast), ExifTagValue.Saturation => new ExifShort(ExifTag.Saturation), ExifTagValue.Sharpness => new ExifShort(ExifTag.Sharpness), ExifTagValue.SubjectDistanceRange => new ExifShort(ExifTag.SubjectDistanceRange), ExifTagValue.GPSDifferential => new ExifShort(ExifTag.GPSDifferential), ExifTagValue.BitsPerSample => new ExifShortArray(ExifTag.BitsPerSample), ExifTagValue.MinSampleValue => new ExifShortArray(ExifTag.MinSampleValue), ExifTagValue.MaxSampleValue => new ExifShortArray(ExifTag.MaxSampleValue), ExifTagValue.GrayResponseCurve => new ExifShortArray(ExifTag.GrayResponseCurve), ExifTagValue.ColorMap => new ExifShortArray(ExifTag.ColorMap), ExifTagValue.ExtraSamples => new ExifShortArray(ExifTag.ExtraSamples), ExifTagValue.PageNumber => new ExifShortArray(ExifTag.PageNumber), ExifTagValue.TransferFunction => new ExifShortArray(ExifTag.TransferFunction), ExifTagValue.HalftoneHints => new ExifShortArray(ExifTag.HalftoneHints), ExifTagValue.SampleFormat => new ExifShortArray(ExifTag.SampleFormat), ExifTagValue.TransferRange => new ExifShortArray(ExifTag.TransferRange), ExifTagValue.DefaultImageColor => new ExifShortArray(ExifTag.DefaultImageColor), ExifTagValue.JPEGLosslessPredictors => new ExifShortArray(ExifTag.JPEGLosslessPredictors), ExifTagValue.JPEGPointTransforms => new ExifShortArray(ExifTag.JPEGPointTransforms), ExifTagValue.YCbCrSubsampling => new ExifShortArray(ExifTag.YCbCrSubsampling), ExifTagValue.CFARepeatPatternDim => new ExifShortArray(ExifTag.CFARepeatPatternDim), ExifTagValue.IntergraphPacketData => new ExifShortArray(ExifTag.IntergraphPacketData), ExifTagValue.ISOSpeedRatings => new ExifShortArray(ExifTag.ISOSpeedRatings), ExifTagValue.SubjectArea => new ExifShortArray(ExifTag.SubjectArea), ExifTagValue.SubjectLocation => new ExifShortArray(ExifTag.SubjectLocation), ExifTagValue.ShutterSpeedValue => new ExifSignedRational(ExifTag.ShutterSpeedValue), ExifTagValue.BrightnessValue => new ExifSignedRational(ExifTag.BrightnessValue), ExifTagValue.ExposureBiasValue => new ExifSignedRational(ExifTag.ExposureBiasValue), ExifTagValue.AmbientTemperature => new ExifSignedRational(ExifTag.AmbientTemperature), ExifTagValue.WaterDepth => new ExifSignedRational(ExifTag.WaterDepth), ExifTagValue.CameraElevationAngle => new ExifSignedRational(ExifTag.CameraElevationAngle), ExifTagValue.Decode => new ExifSignedRationalArray(ExifTag.Decode), ExifTagValue.ImageDescription => new ExifString(ExifTag.ImageDescription), ExifTagValue.Make => new ExifString(ExifTag.Make), ExifTagValue.Model => new ExifString(ExifTag.Model), ExifTagValue.Software => new ExifString(ExifTag.Software), ExifTagValue.DateTime => new ExifString(ExifTag.DateTime), ExifTagValue.Artist => new ExifString(ExifTag.Artist), ExifTagValue.HostComputer => new ExifString(ExifTag.HostComputer), ExifTagValue.Copyright => new ExifString(ExifTag.Copyright), ExifTagValue.DocumentName => new ExifString(ExifTag.DocumentName), ExifTagValue.PageName => new ExifString(ExifTag.PageName), ExifTagValue.InkNames => new ExifString(ExifTag.InkNames), ExifTagValue.TargetPrinter => new ExifString(ExifTag.TargetPrinter), ExifTagValue.ImageID => new ExifString(ExifTag.ImageID), ExifTagValue.MDLabName => new ExifString(ExifTag.MDLabName), ExifTagValue.MDSampleInfo => new ExifString(ExifTag.MDSampleInfo), ExifTagValue.MDPrepDate => new ExifString(ExifTag.MDPrepDate), ExifTagValue.MDPrepTime => new ExifString(ExifTag.MDPrepTime), ExifTagValue.MDFileUnits => new ExifString(ExifTag.MDFileUnits), ExifTagValue.SEMInfo => new ExifString(ExifTag.SEMInfo), ExifTagValue.SpectralSensitivity => new ExifString(ExifTag.SpectralSensitivity), ExifTagValue.DateTimeOriginal => new ExifString(ExifTag.DateTimeOriginal), ExifTagValue.DateTimeDigitized => new ExifString(ExifTag.DateTimeDigitized), ExifTagValue.SubsecTime => new ExifString(ExifTag.SubsecTime), ExifTagValue.SubsecTimeOriginal => new ExifString(ExifTag.SubsecTimeOriginal), ExifTagValue.SubsecTimeDigitized => new ExifString(ExifTag.SubsecTimeDigitized), ExifTagValue.RelatedSoundFile => new ExifString(ExifTag.RelatedSoundFile), ExifTagValue.FaxSubaddress => new ExifString(ExifTag.FaxSubaddress), ExifTagValue.OffsetTime => new ExifString(ExifTag.OffsetTime), ExifTagValue.OffsetTimeOriginal => new ExifString(ExifTag.OffsetTimeOriginal), ExifTagValue.OffsetTimeDigitized => new ExifString(ExifTag.OffsetTimeDigitized), ExifTagValue.SecurityClassification => new ExifString(ExifTag.SecurityClassification), ExifTagValue.ImageHistory => new ExifString(ExifTag.ImageHistory), ExifTagValue.ImageUniqueID => new ExifString(ExifTag.ImageUniqueID), ExifTagValue.OwnerName => new ExifString(ExifTag.OwnerName), ExifTagValue.SerialNumber => new ExifString(ExifTag.SerialNumber), ExifTagValue.LensMake => new ExifString(ExifTag.LensMake), ExifTagValue.LensModel => new ExifString(ExifTag.LensModel), ExifTagValue.LensSerialNumber => new ExifString(ExifTag.LensSerialNumber), ExifTagValue.GDALMetadata => new ExifString(ExifTag.GDALMetadata), ExifTagValue.GDALNoData => new ExifString(ExifTag.GDALNoData), ExifTagValue.InteroperabilityIndex => new ExifString(ExifTag.GPSLatitudeRef), ExifTagValue.GPSLongitudeRef => new ExifString(ExifTag.GPSLongitudeRef), ExifTagValue.GPSSatellites => new ExifString(ExifTag.GPSSatellites), ExifTagValue.GPSStatus => new ExifString(ExifTag.GPSStatus), ExifTagValue.GPSMeasureMode => new ExifString(ExifTag.GPSMeasureMode), ExifTagValue.GPSSpeedRef => new ExifString(ExifTag.GPSSpeedRef), ExifTagValue.GPSTrackRef => new ExifString(ExifTag.GPSTrackRef), ExifTagValue.GPSImgDirectionRef => new ExifString(ExifTag.GPSImgDirectionRef), ExifTagValue.GPSMapDatum => new ExifString(ExifTag.GPSMapDatum), ExifTagValue.GPSDestLatitudeRef => new ExifString(ExifTag.GPSDestLatitudeRef), ExifTagValue.GPSDestLongitudeRef => new ExifString(ExifTag.GPSDestLongitudeRef), ExifTagValue.GPSDestBearingRef => new ExifString(ExifTag.GPSDestBearingRef), ExifTagValue.GPSDestDistanceRef => new ExifString(ExifTag.GPSDestDistanceRef), ExifTagValue.GPSDateStamp => new ExifString(ExifTag.GPSDateStamp), ExifTagValue.FileSource => new ExifByte(ExifTag.FileSource, ExifDataType.Undefined), ExifTagValue.SceneType => new ExifByte(ExifTag.SceneType, ExifDataType.Undefined), ExifTagValue.JPEGTables => new ExifByteArray(ExifTag.JPEGTables, ExifDataType.Undefined), ExifTagValue.OECF => new ExifByteArray(ExifTag.OECF, ExifDataType.Undefined), ExifTagValue.ExifVersion => new ExifByteArray(ExifTag.ExifVersion, ExifDataType.Undefined), ExifTagValue.ComponentsConfiguration => new ExifByteArray(ExifTag.ComponentsConfiguration, ExifDataType.Undefined), ExifTagValue.MakerNote => new ExifByteArray(ExifTag.MakerNote, ExifDataType.Undefined), ExifTagValue.FlashpixVersion => new ExifByteArray(ExifTag.FlashpixVersion, ExifDataType.Undefined), ExifTagValue.SpatialFrequencyResponse => new ExifByteArray(ExifTag.SpatialFrequencyResponse, ExifDataType.Undefined), ExifTagValue.SpatialFrequencyResponse2 => new ExifByteArray(ExifTag.SpatialFrequencyResponse2, ExifDataType.Undefined), ExifTagValue.Noise => new ExifByteArray(ExifTag.Noise, ExifDataType.Undefined), ExifTagValue.CFAPattern => new ExifByteArray(ExifTag.CFAPattern, ExifDataType.Undefined), ExifTagValue.DeviceSettingDescription => new ExifByteArray(ExifTag.DeviceSettingDescription, ExifDataType.Undefined), ExifTagValue.ImageSourceData => new ExifByteArray(ExifTag.ImageSourceData, ExifDataType.Undefined), ExifTagValue.XPTitle => new ExifUcs2String(ExifTag.XPTitle), ExifTagValue.XPComment => new ExifUcs2String(ExifTag.XPComment), ExifTagValue.XPAuthor => new ExifUcs2String(ExifTag.XPAuthor), ExifTagValue.XPKeywords => new ExifUcs2String(ExifTag.XPKeywords), ExifTagValue.XPSubject => new ExifUcs2String(ExifTag.XPSubject), ExifTagValue.UserComment => new ExifEncodedString(ExifTag.UserComment), ExifTagValue.GPSProcessingMethod => new ExifEncodedString(ExifTag.GPSProcessingMethod), ExifTagValue.GPSAreaInformation => new ExifEncodedString(ExifTag.GPSAreaInformation), _ => null, }; } } internal abstract class ExifValue : ExifValue, IExifValue, IExifValue, IDeepCloneable { public TValueType Value { get; set; } protected abstract string StringValue { get; } protected ExifValue(ExifTag tag) : base(tag) { } protected ExifValue(ExifTagValue tag) : base(tag) { } internal ExifValue(ExifValue value) : base(value) { } public override object GetValue() { return Value; } public override bool TrySetValue(object value) { if (value == null) { Value = default(TValueType); return true; } if (value.GetType() == typeof(TValueType)) { Value = (TValueType)value; return true; } return false; } public override string ToString() { if (Value == null) { return null; } return ExifTagDescriptionAttribute.GetDescription(base.Tag, Value) ?? StringValue; } } public interface IExifValue : IDeepCloneable { ExifDataType DataType { get; } bool IsArray { get; } ExifTag Tag { get; } object GetValue(); bool TrySetValue(object value); } public interface IExifValue : IExifValue, IDeepCloneable { TValueType Value { get; set; } } } namespace SixLabors.ImageSharp.Memory { [Flags] public enum AllocationOptions { None = 0, Clean = 1 } internal static class AllocationOptionsExtensions { public static bool Has(this AllocationOptions options, AllocationOptions flag) { return (options & flag) == flag; } } public abstract class MemoryAllocator { private const int OneGigabyte = 1073741824; public static MemoryAllocator Default { get; } = Create(); internal long MemoryGroupAllocationLimitBytes { get; private set; } = Environment.Is64BitProcess ? 4294967296L : 1073741824; internal int SingleBufferAllocationLimitBytes { get; private set; } = 1073741824; protected internal abstract int GetBufferCapacityInBytes(); public static MemoryAllocator Create() { return Create(default(MemoryAllocatorOptions)); } public static MemoryAllocator Create(MemoryAllocatorOptions options) { UniformUnmanagedMemoryPoolMemoryAllocator uniformUnmanagedMemoryPoolMemoryAllocator = new UniformUnmanagedMemoryPoolMemoryAllocator(options.MaximumPoolSizeMegabytes); if (options.AllocationLimitMegabytes.HasValue) { uniformUnmanagedMemoryPoolMemoryAllocator.MemoryGroupAllocationLimitBytes = (long)options.AllocationLimitMegabytes.Value * 1024L * 1024; uniformUnmanagedMemoryPoolMemoryAllocator.SingleBufferAllocationLimitBytes = (int)Math.Min(uniformUnmanagedMemoryPoolMemoryAllocator.SingleBufferAllocationLimitBytes, uniformUnmanagedMemoryPoolMemoryAllocator.MemoryGroupAllocationLimitBytes); } return uniformUnmanagedMemoryPoolMemoryAllocator; } public abstract IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) where T : struct; public virtual void ReleaseRetainedResources() { } internal MemoryGroup AllocateGroup(long totalLength, int bufferAlignment, AllocationOptions options = AllocationOptions.None) where T : struct { if (totalLength < 0) { throw new InvalidMemoryOperationException($"Attempted to allocate a buffer of negative length={totalLength}."); } ulong num = (ulong)(totalLength * Unsafe.SizeOf()); if (num > (ulong)MemoryGroupAllocationLimitBytes) { throw new InvalidMemoryOperationException($"Attempted to allocate a buffer of length={num} that exceeded the limit {MemoryGroupAllocationLimitBytes}."); } return AllocateGroupCore(totalLength, (long)num, bufferAlignment, options); } internal virtual MemoryGroup AllocateGroupCore(long totalLengthInElements, long totalLengthInBytes, int bufferAlignment, AllocationOptions options) where T : struct { return MemoryGroup.Allocate(this, totalLengthInElements, bufferAlignment, options); } } public struct MemoryAllocatorOptions { private int? maximumPoolSizeMegabytes; private int? allocationLimitMegabytes; public int? MaximumPoolSizeMegabytes { get { return maximumPoolSizeMegabytes; } set { if (value.HasValue) { Guard.MustBeGreaterThanOrEqualTo(value.Value, 0, "MaximumPoolSizeMegabytes"); } maximumPoolSizeMegabytes = value; } } public int? AllocationLimitMegabytes { readonly get { return allocationLimitMegabytes; } set { if (value.HasValue) { Guard.MustBeGreaterThan(value.Value, 0, "AllocationLimitMegabytes"); } allocationLimitMegabytes = value; } } } public sealed class SimpleGcMemoryAllocator : MemoryAllocator { protected internal override int GetBufferCapacityInBytes() { return int.MaxValue; } public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) { if (length < 0) { throw new InvalidMemoryOperationException($"Attempted to allocate a buffer of negative length={length}."); } ulong num = (ulong)length * (ulong)Unsafe.SizeOf(); if (num > (ulong)base.SingleBufferAllocationLimitBytes) { throw new InvalidMemoryOperationException($"Attempted to allocate a buffer of length={num} that exceeded the limit {base.SingleBufferAllocationLimitBytes}."); } return new BasicArrayBuffer(new T[length]); } } internal sealed class UniformUnmanagedMemoryPoolMemoryAllocator : MemoryAllocator { private const int OneMegabyte = 1048576; private const int DefaultContiguousPoolBlockSizeBytes = 4194304; private const int DefaultNonPoolBlockSizeBytes = 33554432; private readonly int sharedArrayPoolThresholdInBytes; private readonly int poolBufferSizeInBytes; private readonly int poolCapacity; private readonly UniformUnmanagedMemoryPool.TrimSettings trimSettings; private readonly UniformUnmanagedMemoryPool pool; private readonly UnmanagedMemoryAllocator nonPoolAllocator; internal static Func GetTotalAvailableMemoryBytes { get; set; } = () => GC.GetGCMemoryInfo().TotalAvailableMemoryBytes; public UniformUnmanagedMemoryPoolMemoryAllocator(int? maxPoolSizeMegabytes) : this(4194304, maxPoolSizeMegabytes.HasValue ? ((long)maxPoolSizeMegabytes.Value * 1048576L) : GetDefaultMaxPoolSizeBytes(), 33554432) { } public UniformUnmanagedMemoryPoolMemoryAllocator(int poolBufferSizeInBytes, long maxPoolSizeInBytes, int unmanagedBufferSizeInBytes) : this(1048576, poolBufferSizeInBytes, maxPoolSizeInBytes, unmanagedBufferSizeInBytes) { } internal UniformUnmanagedMemoryPoolMemoryAllocator(int sharedArrayPoolThresholdInBytes, int poolBufferSizeInBytes, long maxPoolSizeInBytes, int unmanagedBufferSizeInBytes) : this(sharedArrayPoolThresholdInBytes, poolBufferSizeInBytes, maxPoolSizeInBytes, unmanagedBufferSizeInBytes, UniformUnmanagedMemoryPool.TrimSettings.Default) { } internal UniformUnmanagedMemoryPoolMemoryAllocator(int sharedArrayPoolThresholdInBytes, int poolBufferSizeInBytes, long maxPoolSizeInBytes, int unmanagedBufferSizeInBytes, UniformUnmanagedMemoryPool.TrimSettings trimSettings) { this.sharedArrayPoolThresholdInBytes = sharedArrayPoolThresholdInBytes; this.poolBufferSizeInBytes = poolBufferSizeInBytes; poolCapacity = (int)(maxPoolSizeInBytes / poolBufferSizeInBytes); this.trimSettings = trimSettings; pool = new UniformUnmanagedMemoryPool(this.poolBufferSizeInBytes, poolCapacity, this.trimSettings); nonPoolAllocator = new UnmanagedMemoryAllocator(unmanagedBufferSizeInBytes); } protected internal override int GetBufferCapacityInBytes() { return poolBufferSizeInBytes; } public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) { if (length < 0) { throw new InvalidMemoryOperationException($"Attempted to allocate a buffer of negative length={length}."); } ulong num = (ulong)length * (ulong)Unsafe.SizeOf(); if (num > (ulong)base.SingleBufferAllocationLimitBytes) { throw new InvalidMemoryOperationException($"Attempted to allocate a buffer of length={num} that exceeded the limit {base.SingleBufferAllocationLimitBytes}."); } if (num <= (ulong)sharedArrayPoolThresholdInBytes) { SharedArrayPoolBuffer sharedArrayPoolBuffer = new SharedArrayPoolBuffer(length); if (options.Has(AllocationOptions.Clean)) { sharedArrayPoolBuffer.GetSpan().Clear(); } return sharedArrayPoolBuffer; } if (num <= (ulong)poolBufferSizeInBytes) { UnmanagedMemoryHandle handle = pool.Rent(); if (handle.IsValid) { return pool.CreateGuardedBuffer(handle, length, options.Has(AllocationOptions.Clean)); } } return nonPoolAllocator.Allocate(length, options); } internal override MemoryGroup AllocateGroupCore(long totalLengthInElements, long totalLengthInBytes, int bufferAlignment, AllocationOptions options = AllocationOptions.None) { if (totalLengthInBytes <= sharedArrayPoolThresholdInBytes) { return MemoryGroup.CreateContiguous(new SharedArrayPoolBuffer((int)totalLengthInElements), options.Has(AllocationOptions.Clean)); } if (totalLengthInBytes <= poolBufferSizeInBytes) { UnmanagedMemoryHandle handle = pool.Rent(); if (handle.IsValid) { return MemoryGroup.CreateContiguous(pool.CreateGuardedBuffer(handle, (int)totalLengthInElements, options.Has(AllocationOptions.Clean)), options.Has(AllocationOptions.Clean)); } } if (MemoryGroup.TryAllocate(pool, totalLengthInElements, bufferAlignment, options, out var memoryGroup)) { return memoryGroup; } return MemoryGroup.Allocate(nonPoolAllocator, totalLengthInElements, bufferAlignment, options); } public override void ReleaseRetainedResources() { pool.Release(); } private static long GetDefaultMaxPoolSizeBytes() { if (Environment.Is64BitProcess || !RuntimeInformation.FrameworkDescription.StartsWith(".NET 5.0")) { long num = GetTotalAvailableMemoryBytes(); if (num > 0) { return num / 8; } } return 134217728L; } } internal class UnmanagedMemoryAllocator : MemoryAllocator { private readonly int bufferCapacityInBytes; public UnmanagedMemoryAllocator(int bufferCapacityInBytes) { this.bufferCapacityInBytes = bufferCapacityInBytes; } protected internal override int GetBufferCapacityInBytes() { return bufferCapacityInBytes; } public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) { UnmanagedBuffer unmanagedBuffer = UnmanagedBuffer.Allocate(length); if (options.Has(AllocationOptions.Clean)) { unmanagedBuffer.GetSpan().Clear(); } return unmanagedBuffer; } } public static class Buffer2DExtensions { public static IMemoryGroup GetMemoryGroup(this Buffer2D buffer) where T : struct { Guard.NotNull(buffer, "buffer"); return buffer.FastMemoryGroup.View; } internal unsafe static void DangerousCopyColumns(this Buffer2D buffer, int sourceIndex, int destIndex, int columnCount) where T : struct { int num = Unsafe.SizeOf(); int num2 = buffer.Width * num; int num3 = sourceIndex * num; int num4 = destIndex * num; long num5 = columnCount * num; fixed (byte* ptr = MemoryMarshal.AsBytes(buffer.DangerousGetSingleMemory().Span)) { byte* ptr2 = ptr; for (int i = 0; i < buffer.Height; i++) { byte* source = ptr2 + num3; byte* destination = ptr2 + num4; Buffer.MemoryCopy(source, destination, num5, num5); ptr2 += num2; } } } internal static Rectangle FullRectangle(this Buffer2D buffer) where T : struct { return new Rectangle(0, 0, buffer.Width, buffer.Height); } internal static Buffer2DRegion GetRegion(this Buffer2D buffer, Rectangle rectangle) where T : unmanaged { return new Buffer2DRegion(buffer, rectangle); } internal static Buffer2DRegion GetRegion(this Buffer2D buffer, int x, int y, int width, int height) where T : unmanaged { return new Buffer2DRegion(buffer, new Rectangle(x, y, width, height)); } internal static Buffer2DRegion GetRegion(this Buffer2D buffer) where T : unmanaged { return new Buffer2DRegion(buffer); } internal static Size Size(this Buffer2D buffer) where T : struct { return new Size(buffer.Width, buffer.Height); } internal static Rectangle Bounds(this Buffer2D buffer) where T : struct { return new Rectangle(0, 0, buffer.Width, buffer.Height); } [Conditional("DEBUG")] private static void CheckColumnRegionsDoNotOverlap(Buffer2D buffer, int sourceIndex, int destIndex, int columnCount) where T : struct { int num = Math.Min(sourceIndex, destIndex); int num2 = Math.Max(sourceIndex, destIndex); if (num2 < num + columnCount || num2 > buffer.Width - columnCount) { throw new InvalidOperationException("Column regions should not overlap!"); } } } public readonly struct Buffer2DRegion where T : unmanaged { public Rectangle Rectangle { get; } public Buffer2D Buffer { get; } public int Width => Rectangle.Width; public int Height => Rectangle.Height; public int Stride => Buffer.Width; internal Size Size => Rectangle.Size; internal bool IsFullBufferArea => Size == Buffer.Size(); internal ref T this[int x, int y] => ref Buffer[x + Rectangle.X, y + Rectangle.Y]; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Buffer2DRegion(Buffer2D buffer, Rectangle rectangle) { Buffer = buffer; Rectangle = rectangle; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Buffer2DRegion(Buffer2D buffer) : this(buffer, buffer.FullRectangle()) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span DangerousGetRowSpan(int y) { int y2 = Rectangle.Y + y; int x = Rectangle.X; int width = Rectangle.Width; return Buffer.DangerousGetRowSpan(y2).Slice(x, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Buffer2DRegion GetSubRegion(int x, int y, int width, int height) { Rectangle rectangle = new Rectangle(x, y, width, height); return GetSubRegion(rectangle); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Buffer2DRegion GetSubRegion(Rectangle rectangle) { int x = Rectangle.X + rectangle.X; int y = Rectangle.Y + rectangle.Y; rectangle = new Rectangle(x, y, rectangle.Width, rectangle.Height); return new Buffer2DRegion(Buffer, rectangle); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ref T GetReferenceToOrigin() { int y = Rectangle.Y; int x = Rectangle.X; return ref Buffer.DangerousGetRowSpan(y)[x]; } internal void Clear() { if (IsFullBufferArea) { Buffer.FastMemoryGroup.Clear(); return; } for (int i = 0; i < Rectangle.Height; i++) { DangerousGetRowSpan(i).Clear(); } } } public sealed class Buffer2D : IDisposable where T : struct { public int Width { get; private set; } public int Height { get; private set; } public IMemoryGroup MemoryGroup => FastMemoryGroup.View; internal MemoryGroup FastMemoryGroup { get; private set; } public ref T this[int x, int y] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return ref DangerousGetRowSpan(y)[x]; } } internal Buffer2D(MemoryGroup memoryGroup, int width, int height) { FastMemoryGroup = memoryGroup; Width = width; Height = height; } public void Dispose() { FastMemoryGroup.Dispose(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span DangerousGetRowSpan(int y) { if ((uint)y >= (uint)Height) { ThrowYOutOfRangeException(y); } return FastMemoryGroup.GetRowSpanCoreUnsafe(y, Width); } internal bool DangerousTryGetPaddedRowSpan(int y, int padding, out Span paddedSpan) { int num = Width + padding; Span remainingSliceOfBuffer = FastMemoryGroup.GetRemainingSliceOfBuffer((long)y * (long)Width); if (remainingSliceOfBuffer.Length < num) { paddedSpan = default(Span); return false; } paddedSpan = remainingSliceOfBuffer.Slice(0, num); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ref T GetElementUnsafe(int x, int y) { return ref FastMemoryGroup.GetRowSpanCoreUnsafe(y, Width)[x]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Memory GetSafeRowMemory(int y) { return FastMemoryGroup.View.GetBoundedMemorySlice((long)y * (long)Width, Width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Span DangerousGetSingleSpan() { return FastMemoryGroup.Single().Span; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Memory DangerousGetSingleMemory() { return FastMemoryGroup.Single(); } internal static bool SwapOrCopyContent(Buffer2D destination, Buffer2D source) { bool result = false; Buffer2D buffer2D; if (MemoryGroup.CanSwapContent(destination.FastMemoryGroup, source.FastMemoryGroup)) { buffer2D = source; MemoryGroup fastMemoryGroup = source.FastMemoryGroup; MemoryGroup fastMemoryGroup2 = destination.FastMemoryGroup; destination.FastMemoryGroup = fastMemoryGroup; buffer2D.FastMemoryGroup = fastMemoryGroup2; destination.FastMemoryGroup.RecreateViewAfterSwap(); source.FastMemoryGroup.RecreateViewAfterSwap(); result = true; } else { if (destination.FastMemoryGroup.TotalLength != source.FastMemoryGroup.TotalLength) { throw new InvalidMemoryOperationException("Trying to copy/swap incompatible buffers. This is most likely caused by applying an unsupported processor to wrapped-memory images."); } source.FastMemoryGroup.CopyTo(destination.MemoryGroup); } buffer2D = source; int width = source.Width; int width2 = destination.Width; destination.Width = width; buffer2D.Width = width2; buffer2D = source; width2 = source.Height; width = destination.Height; destination.Height = width2; buffer2D.Height = width; return result; } [MethodImpl(MethodImplOptions.NoInlining)] private void ThrowYOutOfRangeException(int y) { throw new ArgumentOutOfRangeException($"DangerousGetRowSpan({y}). Y was out of range. Height={Height}"); } } internal sealed class ByteMemoryManager : MemoryManager where T : unmanaged { private readonly Memory memory; public ByteMemoryManager(Memory memory) { this.memory = memory; } protected override void Dispose(bool disposing) { } public override Span GetSpan() { return MemoryMarshal.Cast(memory.Span); } public override MemoryHandle Pin(int elementIndex = 0) { return memory.Slice(elementIndex * Unsafe.SizeOf()).Pin(); } public override void Unpin() { } } internal sealed class ByteMemoryOwner : IMemoryOwner, IDisposable where T : unmanaged { private readonly IMemoryOwner memoryOwner; private readonly ByteMemoryManager memoryManager; private bool disposedValue; public Memory Memory => memoryManager.Memory; public ByteMemoryOwner(IMemoryOwner memoryOwner) { this.memoryOwner = memoryOwner; memoryManager = new ByteMemoryManager(memoryOwner.Memory); } private void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { memoryOwner.Dispose(); } disposedValue = true; } } public void Dispose() { Dispose(disposing: true); } } public interface IMemoryGroup : IReadOnlyList>, IEnumerable>, IEnumerable, IReadOnlyCollection> where T : struct { int BufferLength { get; } long TotalLength { get; } bool IsValid { get; } new MemoryGroupEnumerator GetEnumerator(); } [EditorBrowsable(EditorBrowsableState.Never)] public ref struct MemoryGroupEnumerator where T : struct { private readonly IMemoryGroup memoryGroup; private readonly int count; private int index; public Memory Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return memoryGroup[index]; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MemoryGroupEnumerator(MemoryGroup.Owned memoryGroup) { this.memoryGroup = memoryGroup; count = memoryGroup.Count; index = -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MemoryGroupEnumerator(MemoryGroup.Consumed memoryGroup) { this.memoryGroup = memoryGroup; count = memoryGroup.Count; index = -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MemoryGroupEnumerator(MemoryGroupView memoryGroup) { this.memoryGroup = memoryGroup; count = memoryGroup.Count; index = -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { int num = index + 1; if (num < count) { index = num; return true; } return false; } } internal static class MemoryGroupExtensions { private struct MemoryGroupCursor where T : struct { private readonly IMemoryGroup memoryGroup; private int bufferIndex; private int elementIndex; private bool IsAtLastBuffer => bufferIndex == memoryGroup.Count - 1; private int CurrentBufferLength => memoryGroup[bufferIndex].Length; public MemoryGroupCursor(IMemoryGroup memoryGroup) { this.memoryGroup = memoryGroup; bufferIndex = 0; elementIndex = 0; } public Span GetSpan(int length) { return memoryGroup[bufferIndex].Span.Slice(elementIndex, length); } public int LookAhead() { return CurrentBufferLength - elementIndex; } public void Forward(int steps) { int num = elementIndex + steps; int currentBufferLength = CurrentBufferLength; if (num < currentBufferLength) { elementIndex = num; return; } if (num == currentBufferLength) { bufferIndex++; elementIndex = 0; return; } throw new ArgumentException("Can't forward multiple buffers!", "steps"); } } internal static void Fill(this IMemoryGroup group, T value) where T : struct { MemoryGroupEnumerator enumerator = group.GetEnumerator(); while (enumerator.MoveNext()) { enumerator.Current.Span.Fill(value); } } internal static void Clear(this IMemoryGroup group) where T : struct { MemoryGroupEnumerator enumerator = group.GetEnumerator(); while (enumerator.MoveNext()) { enumerator.Current.Span.Clear(); } } internal static Memory GetBoundedMemorySlice(this IMemoryGroup group, long start, int length) where T : struct { Guard.NotNull(group, "group"); Guard.IsTrue(group.IsValid, "group", "Group must be valid!"); Guard.MustBeGreaterThanOrEqualTo(length, 0, "length"); Guard.MustBeLessThan(start, group.TotalLength, "start"); long result; int num = (int)Math.DivRem(start, group.BufferLength, out result); int num2 = (int)result; if ((uint)num >= group.Count) { throw new ArgumentOutOfRangeException("start"); } int num3 = num2 + length; Memory memory = group[num]; if (num3 > memory.Length) { throw new ArgumentOutOfRangeException("length"); } return memory.Slice(num2, length); } internal static void CopyTo(this IMemoryGroup source, Span target) where T : struct { Guard.NotNull(source, "source"); Guard.MustBeGreaterThanOrEqualTo(target.Length, source.TotalLength, "target"); MemoryGroupCursor memoryGroupCursor = new MemoryGroupCursor(source); int num2; for (long num = 0L; num < source.TotalLength; num += num2) { num2 = Math.Min(memoryGroupCursor.LookAhead(), target.Length); memoryGroupCursor.GetSpan(num2).CopyTo(target); memoryGroupCursor.Forward(num2); target = target.Slice(num2); } } internal static void CopyTo(this Span source, IMemoryGroup target) where T : struct { ((ReadOnlySpan)source).CopyTo(target); } internal static void CopyTo(this ReadOnlySpan source, IMemoryGroup target) where T : struct { Guard.NotNull(target, "target"); Guard.MustBeGreaterThanOrEqualTo(target.TotalLength, source.Length, "target"); MemoryGroupCursor memoryGroupCursor = new MemoryGroupCursor(target); while (!source.IsEmpty) { int num = Math.Min(memoryGroupCursor.LookAhead(), source.Length); source.Slice(0, num).CopyTo(memoryGroupCursor.GetSpan(num)); memoryGroupCursor.Forward(num); source = source.Slice(num); } } internal static void CopyTo(this IMemoryGroup source, IMemoryGroup target) where T : struct { Guard.NotNull(source, "source"); Guard.NotNull(target, "target"); Guard.IsTrue(source.IsValid, "source", "Source group must be valid."); Guard.IsTrue(target.IsValid, "target", "Target group must be valid."); Guard.MustBeLessThanOrEqualTo(source.TotalLength, target.TotalLength, "Destination buffer too short!"); if (!source.IsEmpty()) { long num = 0L; MemoryGroupCursor memoryGroupCursor = new MemoryGroupCursor(source); MemoryGroupCursor memoryGroupCursor2 = new MemoryGroupCursor(target); int num2; for (; num < source.TotalLength; num += num2) { num2 = Math.Min(memoryGroupCursor.LookAhead(), memoryGroupCursor2.LookAhead()); Span span = memoryGroupCursor.GetSpan(num2); Span span2 = memoryGroupCursor2.GetSpan(num2); span.CopyTo(span2); memoryGroupCursor.Forward(num2); memoryGroupCursor2.Forward(num2); } } } internal static void TransformTo(this IMemoryGroup source, IMemoryGroup target, TransformItemsDelegate transform) where TSource : struct where TTarget : struct { Guard.NotNull(source, "source"); Guard.NotNull(target, "target"); Guard.NotNull(transform, "transform"); Guard.IsTrue(source.IsValid, "source", "Source group must be valid."); Guard.IsTrue(target.IsValid, "target", "Target group must be valid."); Guard.MustBeLessThanOrEqualTo(source.TotalLength, target.TotalLength, "Destination buffer too short!"); if (!source.IsEmpty()) { long num = 0L; MemoryGroupCursor memoryGroupCursor = new MemoryGroupCursor(source); MemoryGroupCursor memoryGroupCursor2 = new MemoryGroupCursor(target); int num2; for (; num < source.TotalLength; num += num2) { num2 = Math.Min(memoryGroupCursor.LookAhead(), memoryGroupCursor2.LookAhead()); Span span = memoryGroupCursor.GetSpan(num2); Span span2 = memoryGroupCursor2.GetSpan(num2); transform(span, span2); memoryGroupCursor.Forward(num2); memoryGroupCursor2.Forward(num2); } } } internal static void TransformInplace(this IMemoryGroup memoryGroup, TransformItemsInplaceDelegate transform) where T : struct { MemoryGroupEnumerator enumerator = memoryGroup.GetEnumerator(); while (enumerator.MoveNext()) { transform(enumerator.Current.Span); } } internal static bool IsEmpty(this IMemoryGroup group) where T : struct { return group.Count == 0; } } internal struct MemoryGroupSpanCache { public SpanCacheMode Mode; public byte[] SingleArray; public unsafe void* SinglePointer; public unsafe void*[] MultiPointer; public unsafe static MemoryGroupSpanCache Create(IMemoryOwner[] memoryOwners) where T : struct { IMemoryOwner memoryOwner = memoryOwners[0]; MemoryGroupSpanCache result = default(MemoryGroupSpanCache); if (memoryOwners.Length == 1) { if (memoryOwner is SharedArrayPoolBuffer sharedArrayPoolBuffer) { result.Mode = SpanCacheMode.SingleArray; result.SingleArray = sharedArrayPoolBuffer.Array; } else if (memoryOwner is UnmanagedBuffer unmanagedBuffer) { result.Mode = SpanCacheMode.SinglePointer; result.SinglePointer = unmanagedBuffer.Pointer; } } else if (memoryOwner is UnmanagedBuffer) { result.Mode = SpanCacheMode.MultiPointer; result.MultiPointer = new void*[memoryOwners.Length]; for (int i = 0; i < memoryOwners.Length; i++) { result.MultiPointer[i] = ((UnmanagedBuffer)memoryOwners[i]).Pointer; } } return result; } } internal class MemoryGroupView : IMemoryGroup, IReadOnlyList>, IEnumerable>, IEnumerable, IReadOnlyCollection> where T : struct { private class MemoryOwnerWrapper : MemoryManager { private readonly MemoryGroupView view; private readonly int index; public MemoryOwnerWrapper(MemoryGroupView view, int index) { this.view = view; this.index = index; } protected override void Dispose(bool disposing) { } public override Span GetSpan() { view.EnsureIsValid(); return view.owner[index].Span; } public override MemoryHandle Pin(int elementIndex = 0) { view.EnsureIsValid(); return view.owner[index].Pin(); } public override void Unpin() { throw new NotSupportedException(); } } private MemoryGroup owner; private readonly MemoryOwnerWrapper[] memoryWrappers; public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { EnsureIsValid(); return owner.Count; } } public int BufferLength { get { EnsureIsValid(); return owner.BufferLength; } } public long TotalLength { get { EnsureIsValid(); return owner.TotalLength; } } public bool IsValid => owner != null; public Memory this[int index] { get { EnsureIsValid(); return memoryWrappers[index].Memory; } } public MemoryGroupView(MemoryGroup owner) { this.owner = owner; memoryWrappers = new MemoryOwnerWrapper[owner.Count]; for (int i = 0; i < owner.Count; i++) { memoryWrappers[i] = new MemoryOwnerWrapper(this, i); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public MemoryGroupEnumerator GetEnumerator() { return new MemoryGroupEnumerator(this); } IEnumerator> IEnumerable>.GetEnumerator() { EnsureIsValid(); for (int i = 0; i < Count; i++) { yield return memoryWrappers[i].Memory; } } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable>)this).GetEnumerator(); } internal void Invalidate() { owner = null; } private void EnsureIsValid() { if (!IsValid) { throw new InvalidMemoryOperationException("Can not access an invalidated MemoryGroupView!"); } } } internal abstract class MemoryGroup : IMemoryGroup, IReadOnlyList>, IEnumerable>, IEnumerable, IReadOnlyCollection>, IDisposable where T : struct { public sealed class Consumed : MemoryGroup, IEnumerable>, IEnumerable { private readonly Memory[] source; public override int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return source.Length; } } public override Memory this[int index] => source[index]; public Consumed(Memory[] source, int bufferLength, long totalLength) : base(bufferLength, totalLength) { this.source = source; base.View = new MemoryGroupView(this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override MemoryGroupEnumerator GetEnumerator() { return new MemoryGroupEnumerator(this); } IEnumerator> IEnumerable>.GetEnumerator() { return ((IEnumerable>)source).GetEnumerator(); } public override void Dispose() { base.View.Invalidate(); } } public sealed class Owned : MemoryGroup, IEnumerable>, IEnumerable { private sealed class ObservedBuffer : MemoryManager { private readonly UnmanagedMemoryHandle handle; private readonly int lengthInElements; private ObservedBuffer(UnmanagedMemoryHandle handle, int lengthInElements) { this.handle = handle; this.lengthInElements = lengthInElements; } public static ObservedBuffer Create(UnmanagedMemoryHandle handle, int lengthInElements, AllocationOptions options) { ObservedBuffer observedBuffer = new ObservedBuffer(handle, lengthInElements); if (options.Has(AllocationOptions.Clean)) { observedBuffer.GetSpan().Clear(); } return observedBuffer; } protected override void Dispose(bool disposing) { } public unsafe override Span GetSpan() { return new Span(handle.Pointer, lengthInElements); } public unsafe override MemoryHandle Pin(int elementIndex = 0) { return new MemoryHandle(Unsafe.Add(handle.Pointer, elementIndex)); } public override void Unpin() { } } private IMemoryOwner[] memoryOwners; private RefCountedMemoryLifetimeGuard groupLifetimeGuard; public bool Swappable { get; } private bool IsDisposed => memoryOwners == null; public override int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { EnsureNotDisposed(); return memoryOwners.Length; } } public override Memory this[int index] { get { EnsureNotDisposed(); return memoryOwners[index].Memory; } } public Owned(IMemoryOwner[] memoryOwners, int bufferLength, long totalLength, bool swappable) : base(bufferLength, totalLength) { this.memoryOwners = memoryOwners; Swappable = swappable; base.View = new MemoryGroupView(this); memoryGroupSpanCache = MemoryGroupSpanCache.Create(memoryOwners); } public Owned(UniformUnmanagedMemoryPool pool, UnmanagedMemoryHandle[] pooledHandles, int bufferLength, long totalLength, int sizeOfLastBuffer, AllocationOptions options) : this(CreateBuffers(pooledHandles, bufferLength, sizeOfLastBuffer, options), bufferLength, totalLength, swappable: true) { groupLifetimeGuard = pool.CreateGroupLifetimeGuard(pooledHandles); } private static IMemoryOwner[] CreateBuffers(UnmanagedMemoryHandle[] pooledBuffers, int bufferLength, int sizeOfLastBuffer, AllocationOptions options) { IMemoryOwner[] array = new IMemoryOwner[pooledBuffers.Length]; for (int i = 0; i < pooledBuffers.Length - 1; i++) { ObservedBuffer observedBuffer = ObservedBuffer.Create(pooledBuffers[i], bufferLength, options); array[i] = observedBuffer; } ObservedBuffer observedBuffer2 = ObservedBuffer.Create(pooledBuffers[^1], sizeOfLastBuffer, options); array[^1] = observedBuffer2; return array; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override MemoryGroupEnumerator GetEnumerator() { return new MemoryGroupEnumerator(this); } public override void IncreaseRefCounts() { EnsureNotDisposed(); if (groupLifetimeGuard != null) { groupLifetimeGuard.AddRef(); return; } IMemoryOwner[] array = memoryOwners; for (int i = 0; i < array.Length; i++) { if (array[i] is IRefCounted refCounted) { refCounted.AddRef(); } } } public override void DecreaseRefCounts() { EnsureNotDisposed(); if (groupLifetimeGuard != null) { groupLifetimeGuard.ReleaseRef(); return; } IMemoryOwner[] array = memoryOwners; for (int i = 0; i < array.Length; i++) { if (array[i] is IRefCounted refCounted) { refCounted.ReleaseRef(); } } } public override void RecreateViewAfterSwap() { base.View.Invalidate(); base.View = new MemoryGroupView(this); } IEnumerator> IEnumerable>.GetEnumerator() { EnsureNotDisposed(); return memoryOwners.Select((IMemoryOwner mo) => mo.Memory).GetEnumerator(); } public override void Dispose() { if (IsDisposed) { return; } base.View.Invalidate(); if (groupLifetimeGuard != null) { groupLifetimeGuard.Dispose(); } else { IMemoryOwner[] array = memoryOwners; for (int i = 0; i < array.Length; i++) { array[i].Dispose(); } } memoryOwners = null; base.IsValid = false; groupLifetimeGuard = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnsureNotDisposed() { if (memoryOwners == null) { ThrowObjectDisposedException(); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowObjectDisposedException() { throw new ObjectDisposedException("MemoryGroup"); } } private static readonly int ElementSize = Unsafe.SizeOf(); private MemoryGroupSpanCache memoryGroupSpanCache; public abstract int Count { get; } public int BufferLength { get; } public long TotalLength { get; } public bool IsValid { get; private set; } = true; public MemoryGroupView View { get; private set; } public abstract Memory this[int index] { get; } private MemoryGroup(int bufferLength, long totalLength) { BufferLength = bufferLength; TotalLength = totalLength; } public abstract void Dispose(); public abstract MemoryGroupEnumerator GetEnumerator(); IEnumerator> IEnumerable>.GetEnumerator() { throw new NotImplementedException($"The type {GetType()} needs to override IEnumerable>.GetEnumerator()"); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable>)this).GetEnumerator(); } public static MemoryGroup Allocate(MemoryAllocator allocator, long totalLengthInElements, int bufferAlignmentInElements, AllocationOptions options = AllocationOptions.None) { int bufferCapacityInBytes = allocator.GetBufferCapacityInBytes(); Guard.NotNull(allocator, "allocator"); if (totalLengthInElements < 0) { throw new InvalidMemoryOperationException($"Attempted to allocate a buffer of negative length={totalLengthInElements}."); } int num = bufferCapacityInBytes / ElementSize; if (bufferAlignmentInElements < 0 || bufferAlignmentInElements > num) { throw new InvalidMemoryOperationException($"The buffer capacity of the provided MemoryAllocator is insufficient for the requested buffer alignment: {bufferAlignmentInElements}."); } if (totalLengthInElements == 0L) { return new Owned(new IMemoryOwner[1] { allocator.Allocate(0, options) }, 0, 0L, swappable: true); } int num2 = num / bufferAlignmentInElements * bufferAlignmentInElements; if (totalLengthInElements > 0 && totalLengthInElements < num2) { num2 = (int)totalLengthInElements; } int num3 = (int)(totalLengthInElements % num2); long num4 = totalLengthInElements / num2; if (num3 == 0) { num3 = num2; } else { num4++; } IMemoryOwner[] array = new IMemoryOwner[num4]; for (int i = 0; i < array.Length - 1; i++) { array[i] = allocator.Allocate(num2, options); } if (num4 > 0) { array[^1] = allocator.Allocate(num3, options); } return new Owned(array, num2, totalLengthInElements, swappable: true); } public static MemoryGroup CreateContiguous(IMemoryOwner buffer, bool clear) { if (clear) { buffer.GetSpan().Clear(); } int length = buffer.Memory.Length; return new Owned(new IMemoryOwner[1] { buffer }, length, length, swappable: true); } public static bool TryAllocate(UniformUnmanagedMemoryPool pool, long totalLengthInElements, int bufferAlignmentInElements, AllocationOptions options, out MemoryGroup memoryGroup) { Guard.NotNull(pool, "pool"); Guard.MustBeGreaterThanOrEqualTo(totalLengthInElements, 0L, "totalLengthInElements"); Guard.MustBeGreaterThanOrEqualTo(bufferAlignmentInElements, 0, "bufferAlignmentInElements"); int num = pool.BufferLength / ElementSize; if (bufferAlignmentInElements > num) { memoryGroup = null; return false; } if (totalLengthInElements == 0L) { throw new InvalidMemoryOperationException("Allocating 0 length buffer from UniformByteArrayPool is disallowed"); } int num2 = num / bufferAlignmentInElements * bufferAlignmentInElements; if (totalLengthInElements > 0 && totalLengthInElements < num2) { num2 = (int)totalLengthInElements; } int num3 = (int)(totalLengthInElements % num2); int num4 = (int)(totalLengthInElements / num2); if (num3 == 0) { num3 = num2; } else { num4++; } UnmanagedMemoryHandle[] array = pool.Rent(num4); if (array == null) { memoryGroup = null; return false; } memoryGroup = new Owned(pool, array, num2, totalLengthInElements, num3, options); return true; } public static MemoryGroup Wrap(params Memory[] source) { int num = ((source.Length != 0) ? source[0].Length : 0); for (int i = 1; i < source.Length - 1; i++) { if (source[i].Length != num) { throw new InvalidMemoryOperationException("Wrap: buffers should be uniformly sized!"); } } if (source.Length != 0 && source[^1].Length > num) { throw new InvalidMemoryOperationException("Wrap: the last buffer is too large!"); } long totalLength = ((num > 0) ? ((long)num * (long)(source.Length - 1) + source[^1].Length) : 0); return new Consumed(source, num, totalLength); } public static MemoryGroup Wrap(params IMemoryOwner[] source) { int num = ((source.Length != 0) ? source[0].Memory.Length : 0); for (int i = 1; i < source.Length - 1; i++) { if (source[i].Memory.Length != num) { throw new InvalidMemoryOperationException("Wrap: buffers should be uniformly sized!"); } } if (source.Length != 0 && source[^1].Memory.Length > num) { throw new InvalidMemoryOperationException("Wrap: the last buffer is too large!"); } long totalLength = ((num > 0) ? ((long)num * (long)(source.Length - 1) + source[^1].Memory.Length) : 0); return new Owned(source, num, totalLength, swappable: false); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe Span GetRowSpanCoreUnsafe(int y, int width) { switch (memoryGroupSpanCache.Mode) { case SpanCacheMode.SingleArray: return MemoryMarshal.CreateSpan(ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetReference(memoryGroupSpanCache.SingleArray)), y * width), width); case SpanCacheMode.SinglePointer: return new Span(Unsafe.Add(memoryGroupSpanCache.SinglePointer, y * width), width); case SpanCacheMode.MultiPointer: { GetMultiBufferPosition(y, width, out var bufferIdx2, out var bufferStart2); return new Span(Unsafe.Add(memoryGroupSpanCache.MultiPointer[bufferIdx2], bufferStart2), width); } default: { GetMultiBufferPosition(y, width, out var bufferIdx, out var bufferStart); return this[bufferIdx].Span.Slice(bufferStart, width); } } } public Span GetRemainingSliceOfBuffer(long start) { long result; long num = Math.DivRem(start, BufferLength, out result); return this[(int)num].Span.Slice((int)result); } public static bool CanSwapContent(MemoryGroup target, MemoryGroup source) { if (source is Owned { Swappable: not false }) { if (target is Owned owned2) { return owned2.Swappable; } return false; } return false; } public virtual void RecreateViewAfterSwap() { } public virtual void IncreaseRefCounts() { } public virtual void DecreaseRefCounts() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void GetMultiBufferPosition(int y, int width, out int bufferIdx, out int bufferStart) { long result; long num = Math.DivRem((long)y * (long)width, BufferLength, out result); bufferIdx = (int)num; bufferStart = (int)result; } } internal enum SpanCacheMode { Default, SingleArray, SinglePointer, MultiPointer } public class InvalidMemoryOperationException : InvalidOperationException { public InvalidMemoryOperationException(string message) : base(message) { } public InvalidMemoryOperationException() { } } public static class MemoryAllocatorExtensions { public static Buffer2D Allocate2D(this MemoryAllocator memoryAllocator, int width, int height, bool preferContiguosImageBuffers, AllocationOptions options = AllocationOptions.None) where T : struct { long num = (long)width * (long)height; MemoryGroup memoryGroup = ((!preferContiguosImageBuffers || num >= int.MaxValue) ? memoryAllocator.AllocateGroup(num, width, options) : MemoryGroup.CreateContiguous(memoryAllocator.Allocate((int)num, options), clear: false)); return new Buffer2D(memoryGroup, width, height); } public static Buffer2D Allocate2D(this MemoryAllocator memoryAllocator, int width, int height, AllocationOptions options = AllocationOptions.None) where T : struct { return memoryAllocator.Allocate2D(width, height, preferContiguosImageBuffers: false, options); } public static Buffer2D Allocate2D(this MemoryAllocator memoryAllocator, Size size, bool preferContiguosImageBuffers, AllocationOptions options = AllocationOptions.None) where T : struct { return memoryAllocator.Allocate2D(size.Width, size.Height, preferContiguosImageBuffers, options); } public static Buffer2D Allocate2D(this MemoryAllocator memoryAllocator, Size size, AllocationOptions options = AllocationOptions.None) where T : struct { return memoryAllocator.Allocate2D(size.Width, size.Height, preferContiguosImageBuffers: false, options); } internal static Buffer2D Allocate2DOveraligned(this MemoryAllocator memoryAllocator, int width, int height, int alignmentMultiplier, AllocationOptions options = AllocationOptions.None) where T : struct { long totalLength = (long)width * (long)height; return new Buffer2D(memoryAllocator.AllocateGroup(totalLength, width * alignmentMultiplier, options), width, height); } internal static IMemoryOwner AllocatePaddedPixelRowBuffer(this MemoryAllocator memoryAllocator, int width, int pixelSizeInBytes, int paddingInBytes) { int length = width * pixelSizeInBytes + paddingInBytes; return memoryAllocator.Allocate(length); } } internal static class MemoryOwnerExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span GetSpan(this IMemoryOwner buffer) { return buffer.Memory.Span; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Length(this IMemoryOwner buffer) { return buffer.Memory.Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span Slice(this IMemoryOwner buffer, int start) { return buffer.GetSpan().Slice(start); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span Slice(this IMemoryOwner buffer, int start, int length) { return buffer.GetSpan().Slice(start, length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Clear(this IMemoryOwner buffer) { buffer.GetSpan().Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T GetReference(this IMemoryOwner buffer) where T : struct { return ref MemoryMarshal.GetReference(buffer.GetSpan()); } } public readonly struct RowInterval : IEquatable { public int Min { get; } public int Max { get; } public int Height => Max - Min; public RowInterval(int min, int max) { Guard.MustBeLessThan(min, max, "min"); Min = min; Max = max; } public static bool operator ==(RowInterval left, RowInterval right) { return left.Equals(right); } public static bool operator !=(RowInterval left, RowInterval right) { return !left.Equals(right); } public bool Equals(RowInterval other) { if (Min == other.Min) { return Max == other.Max; } return false; } public override bool Equals(object obj) { if (obj != null && obj is RowInterval other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(Min, Max); } public override string ToString() { return $"RowInterval [{Min}->{Max}]"; } internal RowInterval Slice(int start) { return new RowInterval(Min + start, Max); } internal RowInterval Slice(int start, int length) { return new RowInterval(Min + start, Min + start + length); } } internal delegate void TransformItemsDelegate(ReadOnlySpan source, Span target); internal delegate void TransformItemsInplaceDelegate(Span data); internal sealed class UnmanagedMemoryManager : MemoryManager where T : unmanaged { private unsafe readonly void* pointer; private readonly int length; public unsafe UnmanagedMemoryManager(void* pointer, int length) { this.pointer = pointer; this.length = length; } protected override void Dispose(bool disposing) { } public unsafe override Span GetSpan() { return new Span(pointer, length); } public unsafe override MemoryHandle Pin(int elementIndex = 0) { return new MemoryHandle((byte*)pointer + (nint)elementIndex * (nint)sizeof(T), default(GCHandle), this); } public override void Unpin() { } } } namespace SixLabors.ImageSharp.Memory.Internals { internal class BasicArrayBuffer : ManagedBufferBase where T : struct { public T[] Array { get; } public int Length { get; } public BasicArrayBuffer(T[] array, int length) { Array = array; Length = length; } public BasicArrayBuffer(T[] array) : this(array, array.Length) { } public override Span GetSpan() { return Array.AsSpan(0, Length); } protected override void Dispose(bool disposing) { } protected override object GetPinnableObject() { return Array; } } internal sealed class Gen2GcCallback : CriticalFinalizerObject { private readonly Func callback0; private readonly Func callback1; private GCHandle weakTargetObj; private Gen2GcCallback(Func callback) { callback0 = callback; } private Gen2GcCallback(Func callback, object targetObj) { callback1 = callback; weakTargetObj = GCHandle.Alloc(targetObj, GCHandleType.Weak); } ~Gen2GcCallback() { if (weakTargetObj.IsAllocated) { object target = weakTargetObj.Target; if (target == null) { weakTargetObj.Free(); return; } try { if (!callback1(target)) { weakTargetObj.Free(); return; } } catch { } } else { try { if (!callback0()) { return; } } catch { } } GC.ReRegisterForFinalize(this); } public static void Register(Func callback) { new Gen2GcCallback(callback); } public static void Register(Func callback, object targetObj) { new Gen2GcCallback(callback, targetObj); } } internal interface IRefCounted { void AddRef(); void ReleaseRef(); } internal abstract class ManagedBufferBase : MemoryManager where T : struct { private GCHandle pinHandle; public unsafe override MemoryHandle Pin(int elementIndex = 0) { if (!pinHandle.IsAllocated) { pinHandle = GCHandle.Alloc(GetPinnableObject(), GCHandleType.Pinned); } return new MemoryHandle(Unsafe.Add((void*)pinHandle.AddrOfPinnedObject(), elementIndex), default(GCHandle), this); } public override void Unpin() { if (pinHandle.IsAllocated) { pinHandle.Free(); } } protected abstract object GetPinnableObject(); } internal abstract class RefCountedMemoryLifetimeGuard : IDisposable { private int refCount = 1; private int disposed; private int released; private string allocationStackTrace; public bool IsDisposed => disposed == 1; protected RefCountedMemoryLifetimeGuard() { if (MemoryDiagnostics.UndisposedAllocationSubscribed) { allocationStackTrace = Environment.StackTrace; } MemoryDiagnostics.IncrementTotalUndisposedAllocationCount(); } ~RefCountedMemoryLifetimeGuard() { Interlocked.Exchange(ref disposed, 1); ReleaseRef(finalizing: true); } public void AddRef() { Interlocked.Increment(ref refCount); } public void ReleaseRef() { ReleaseRef(finalizing: false); } public void Dispose() { if (Interlocked.Exchange(ref disposed, 1) == 0) { ReleaseRef(); GC.SuppressFinalize(this); } } protected abstract void Release(); private void ReleaseRef(bool finalizing) { Interlocked.Decrement(ref refCount); if (refCount == 0 && Interlocked.Exchange(ref released, 1) == 0) { if (!finalizing) { MemoryDiagnostics.DecrementTotalUndisposedAllocationCount(); } else if (allocationStackTrace != null) { MemoryDiagnostics.RaiseUndisposedMemoryResource(allocationStackTrace); } Release(); } } } internal class SharedArrayPoolBuffer : ManagedBufferBase, IRefCounted where T : struct { private sealed class LifetimeGuard : RefCountedMemoryLifetimeGuard { private byte[] array; public LifetimeGuard(byte[] array) { this.array = array; } protected override void Release() { ArrayPool.Shared.Return(array); array = null; } } private readonly int lengthInBytes; private LifetimeGuard lifetimeGuard; public byte[] Array { get; private set; } public SharedArrayPoolBuffer(int lengthInElements) { lengthInBytes = lengthInElements * Unsafe.SizeOf(); Array = ArrayPool.Shared.Rent(lengthInBytes); lifetimeGuard = new LifetimeGuard(Array); } protected override void Dispose(bool disposing) { if (Array != null) { lifetimeGuard.Dispose(); Array = null; } } public override Span GetSpan() { return MemoryMarshal.Cast(Array.AsSpan(0, lengthInBytes)); } protected override object GetPinnableObject() { return Array; } public void AddRef() { lifetimeGuard.AddRef(); } public void ReleaseRef() { lifetimeGuard.ReleaseRef(); } [Conditional("DEBUG")] private void CheckDisposed() { if (Array == null) { throw new ObjectDisposedException("SharedArrayPoolBuffer"); } } } internal class UniformUnmanagedMemoryPool : CriticalFinalizerObject { public class TrimSettings { public int TrimPeriodMilliseconds { get; set; } = 60000; public float Rate { get; set; } = 0.5f; public unsafe float HighPressureThresholdRate { get; set; } = (sizeof(IntPtr) == 8) ? 0.9f : 0.6f; public bool Enabled => Rate > 0f; public static TrimSettings Default => new TrimSettings(); } private sealed class GroupLifetimeGuard : RefCountedMemoryLifetimeGuard { private readonly UniformUnmanagedMemoryPool pool; private readonly UnmanagedMemoryHandle[] handles; public GroupLifetimeGuard(UniformUnmanagedMemoryPool pool, UnmanagedMemoryHandle[] handles) { this.pool = pool; this.handles = handles; } protected override void Release() { if (!pool.Return(handles)) { UnmanagedMemoryHandle[] array = handles; foreach (UnmanagedMemoryHandle unmanagedMemoryHandle in array) { unmanagedMemoryHandle.Free(); } } } } private sealed class ReturnToPoolBufferLifetimeGuard : UnmanagedBufferLifetimeGuard { private readonly UniformUnmanagedMemoryPool pool; public ReturnToPoolBufferLifetimeGuard(UniformUnmanagedMemoryPool pool, UnmanagedMemoryHandle handle) : base(handle) { this.pool = pool; } protected override void Release() { if (!pool.Return(base.Handle)) { base.Handle.Free(); } } } private static int minTrimPeriodMilliseconds = int.MaxValue; private static readonly List> AllPools = new List>(); private static Timer trimTimer; private static readonly Stopwatch Stopwatch = Stopwatch.StartNew(); private readonly TrimSettings trimSettings; private readonly UnmanagedMemoryHandle[] buffers; private int index; private long lastTrimTimestamp; private int finalized; public int BufferLength { get; } public int Capacity { get; } private bool Finalized => finalized == 1; public UniformUnmanagedMemoryPool(int bufferLength, int capacity) : this(bufferLength, capacity, TrimSettings.Default) { } public UniformUnmanagedMemoryPool(int bufferLength, int capacity, TrimSettings trimSettings) { this.trimSettings = trimSettings; Capacity = capacity; BufferLength = bufferLength; buffers = new UnmanagedMemoryHandle[capacity]; if (trimSettings.Enabled) { UpdateTimer(trimSettings, this); Gen2GcCallback.Register((object s) => ((UniformUnmanagedMemoryPool)s).Trim(), this); lastTrimTimestamp = Stopwatch.ElapsedMilliseconds; } } ~UniformUnmanagedMemoryPool() { Interlocked.Exchange(ref finalized, 1); TrimAll(buffers); } public UnmanagedMemoryHandle Rent() { UnmanagedMemoryHandle[] array = buffers; if (index == array.Length || Finalized) { return UnmanagedMemoryHandle.NullHandle; } UnmanagedMemoryHandle result; lock (array) { if (index == array.Length || Finalized) { return UnmanagedMemoryHandle.NullHandle; } result = array[index]; array[index++] = default(UnmanagedMemoryHandle); } if (result.IsInvalid) { result = UnmanagedMemoryHandle.Allocate(BufferLength); } return result; } public UnmanagedMemoryHandle[] Rent(int bufferCount) { UnmanagedMemoryHandle[] array = buffers; if (index + bufferCount >= array.Length + 1 || Finalized) { return null; } UnmanagedMemoryHandle[] array2; lock (array) { if (index + bufferCount >= array.Length + 1 || Finalized) { return null; } array2 = new UnmanagedMemoryHandle[bufferCount]; for (int i = 0; i < bufferCount; i++) { array2[i] = array[index]; array[index++] = UnmanagedMemoryHandle.NullHandle; } } for (int j = 0; j < array2.Length; j++) { if (array2[j].IsInvalid) { array2[j] = UnmanagedMemoryHandle.Allocate(BufferLength); } } return array2; } public bool Return(UnmanagedMemoryHandle bufferHandle) { Guard.IsTrue(bufferHandle.IsValid, "bufferHandle", "Returning NullHandle to the pool is not allowed."); lock (buffers) { if (Finalized || index == 0) { return false; } buffers[--index] = bufferHandle; } return true; } public bool Return(Span bufferHandles) { lock (buffers) { if (Finalized || index - bufferHandles.Length + 1 <= 0) { return false; } for (int num = bufferHandles.Length - 1; num >= 0; num--) { ref UnmanagedMemoryHandle reference = ref bufferHandles[num]; Guard.IsTrue(reference.IsValid, "bufferHandles", "Returning NullHandle to the pool is not allowed."); buffers[--index] = reference; } } return true; } public void Release() { lock (buffers) { for (int i = index; i < buffers.Length; i++) { ref UnmanagedMemoryHandle reference = ref buffers[i]; if (reference.IsInvalid) { break; } reference.Free(); } } } [Conditional("DEBUG")] private void DebugThrowInvalidReturn() { if (Finalized) { throw new ObjectDisposedException("UniformUnmanagedMemoryPool", "Invalid handle return to the pool! The pool has been finalized."); } throw new InvalidOperationException("Invalid handle return to the pool! Returning more buffers than rented."); } private static void UpdateTimer(TrimSettings settings, UniformUnmanagedMemoryPool pool) { lock (AllPools) { AllPools.Add(new WeakReference(pool)); int num = settings.TrimPeriodMilliseconds / 4; if (trimTimer == null) { trimTimer = new Timer(delegate { TimerCallback(); }, null, num, num); } else if (settings.TrimPeriodMilliseconds < minTrimPeriodMilliseconds) { trimTimer.Change(num, num); } minTrimPeriodMilliseconds = Math.Min(minTrimPeriodMilliseconds, settings.TrimPeriodMilliseconds); } } private static void TimerCallback() { lock (AllPools) { for (int num = AllPools.Count - 1; num >= 0; num--) { if (!AllPools[num].TryGetTarget(out var _)) { AllPools.RemoveAt(num); } } foreach (WeakReference allPool in AllPools) { if (allPool.TryGetTarget(out var target2)) { target2.Trim(); } } } } private bool Trim() { if (Finalized) { return false; } UnmanagedMemoryHandle[] buffersLocal = buffers; if (IsHighMemoryPressure()) { TrimAll(buffersLocal); return true; } if (Stopwatch.ElapsedMilliseconds - lastTrimTimestamp > trimSettings.TrimPeriodMilliseconds) { return TrimLowPressure(buffersLocal); } return true; } private void TrimAll(UnmanagedMemoryHandle[] buffersLocal) { lock (buffersLocal) { for (int i = index; i < buffersLocal.Length && buffersLocal[i].IsValid; i++) { buffersLocal[i].Free(); } } } private bool TrimLowPressure(UnmanagedMemoryHandle[] buffersLocal) { lock (buffersLocal) { int num = 0; for (int i = index; i < buffersLocal.Length && buffersLocal[i].IsValid; i++) { num++; } int num2 = (int)Math.Ceiling((float)num * trimSettings.Rate); int num3 = index + num - 1; int num4 = index + num - num2; for (int num5 = num3; num5 >= num4; num5--) { buffersLocal[num5].Free(); } lastTrimTimestamp = Stopwatch.ElapsedMilliseconds; } return true; } private bool IsHighMemoryPressure() { GCMemoryInfo gCMemoryInfo = GC.GetGCMemoryInfo(); return (float)gCMemoryInfo.MemoryLoadBytes >= (float)gCMemoryInfo.HighMemoryLoadThresholdBytes * trimSettings.HighPressureThresholdRate; } public UnmanagedBuffer CreateGuardedBuffer(UnmanagedMemoryHandle handle, int lengthInElements, bool clear) where T : struct { UnmanagedBuffer unmanagedBuffer = new UnmanagedBuffer(lengthInElements, new ReturnToPoolBufferLifetimeGuard(this, handle)); if (clear) { unmanagedBuffer.Clear(); } return unmanagedBuffer; } public RefCountedMemoryLifetimeGuard CreateGroupLifetimeGuard(UnmanagedMemoryHandle[] handles) { return new GroupLifetimeGuard(this, handles); } } internal abstract class UnmanagedBufferLifetimeGuard : RefCountedMemoryLifetimeGuard { public sealed class FreeHandle : UnmanagedBufferLifetimeGuard { public FreeHandle(UnmanagedMemoryHandle handle) : base(handle) { } protected override void Release() { base.Handle.Free(); } } private UnmanagedMemoryHandle handle; public ref UnmanagedMemoryHandle Handle => ref handle; protected UnmanagedBufferLifetimeGuard(UnmanagedMemoryHandle handle) { this.handle = handle; } } internal sealed class UnmanagedBuffer : MemoryManager, IRefCounted where T : struct { private readonly int lengthInElements; private readonly UnmanagedBufferLifetimeGuard lifetimeGuard; private int disposed; public unsafe void* Pointer => lifetimeGuard.Handle.Pointer; public UnmanagedBuffer(int lengthInElements, UnmanagedBufferLifetimeGuard lifetimeGuard) { this.lengthInElements = lengthInElements; this.lifetimeGuard = lifetimeGuard; } public unsafe override Span GetSpan() { return new Span(Pointer, lengthInElements); } public unsafe override MemoryHandle Pin(int elementIndex = 0) { lifetimeGuard.AddRef(); return new MemoryHandle(Unsafe.Add(Pointer, elementIndex), default(GCHandle), this); } protected override void Dispose(bool disposing) { if (Interlocked.Exchange(ref disposed, 1) != 1) { lifetimeGuard.Dispose(); } } public override void Unpin() { lifetimeGuard.ReleaseRef(); } public void AddRef() { lifetimeGuard.AddRef(); } public void ReleaseRef() { lifetimeGuard.ReleaseRef(); } public static UnmanagedBuffer Allocate(int lengthInElements) { return new UnmanagedBuffer(lengthInElements, new UnmanagedBufferLifetimeGuard.FreeHandle(UnmanagedMemoryHandle.Allocate(lengthInElements * Unsafe.SizeOf()))); } } internal struct UnmanagedMemoryHandle : IEquatable { private const int MaxAllocationAttempts = 10; private static int totalOutstandingHandles; private static long totalOomRetries; private static object lowMemoryMonitor; public static readonly UnmanagedMemoryHandle NullHandle; private IntPtr handle; private int lengthInBytes; public IntPtr Handle => handle; public bool IsInvalid => Handle == IntPtr.Zero; public bool IsValid => Handle != IntPtr.Zero; public unsafe void* Pointer => (void*)Handle; internal static int TotalOutstandingHandles => totalOutstandingHandles; internal static long TotalOomRetries => totalOomRetries; private UnmanagedMemoryHandle(IntPtr handle, int lengthInBytes) { this.handle = handle; this.lengthInBytes = lengthInBytes; if (lengthInBytes > 0) { GC.AddMemoryPressure(lengthInBytes); } Interlocked.Increment(ref totalOutstandingHandles); } public static bool operator ==(UnmanagedMemoryHandle a, UnmanagedMemoryHandle b) { return a.Equals(b); } public static bool operator !=(UnmanagedMemoryHandle a, UnmanagedMemoryHandle b) { return !a.Equals(b); } public static UnmanagedMemoryHandle Allocate(int lengthInBytes) { return new UnmanagedMemoryHandle(AllocateHandle(lengthInBytes), lengthInBytes); } private static IntPtr AllocateHandle(int lengthInBytes) { int num = 0; IntPtr intPtr = IntPtr.Zero; while (intPtr == IntPtr.Zero) { try { intPtr = Marshal.AllocHGlobal(lengthInBytes); } catch (OutOfMemoryException) { if (num < 10) { num++; Interlocked.Increment(ref totalOomRetries); Interlocked.CompareExchange(ref lowMemoryMonitor, new object(), null); Monitor.Enter(lowMemoryMonitor); Monitor.Wait(lowMemoryMonitor, 1); Monitor.Exit(lowMemoryMonitor); continue; } throw; } } return intPtr; } public void Free() { IntPtr intPtr = Interlocked.Exchange(ref handle, IntPtr.Zero); if (!(intPtr == IntPtr.Zero)) { Marshal.FreeHGlobal(intPtr); Interlocked.Decrement(ref totalOutstandingHandles); if (lengthInBytes > 0) { GC.RemoveMemoryPressure(lengthInBytes); } if (Volatile.Read(in lowMemoryMonitor) != null) { Monitor.Enter(lowMemoryMonitor); Monitor.PulseAll(lowMemoryMonitor); Monitor.Exit(lowMemoryMonitor); } lengthInBytes = 0; } } public bool Equals(UnmanagedMemoryHandle other) { return handle.Equals((object?)(nint)other.handle); } public override bool Equals(object obj) { if (obj is UnmanagedMemoryHandle other) { return Equals(other); } return false; } public override int GetHashCode() { return handle.GetHashCode(); } } } namespace SixLabors.ImageSharp.IO { internal sealed class BufferedReadStream : Stream { private readonly int maxBufferIndex; private readonly byte[] readBuffer; private MemoryHandle readBufferHandle; private unsafe readonly byte* pinnedReadBuffer; private int readBufferIndex; private long readerPosition; private bool isDisposed; public int EofHitCount { get; private set; } public int BufferSize { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public override long Length { get; } public override long Position { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return readerPosition; } [MethodImpl(MethodImplOptions.NoInlining)] set { Guard.MustBeGreaterThanOrEqualTo(value, 0L, "Position"); if (IsInReadBuffer(value, out var index)) { readBufferIndex = (int)index; readerPosition = value; } else { BaseStream.Seek(value, SeekOrigin.Begin); readerPosition = value; readBufferIndex = BufferSize; } } } public override bool CanRead { get; } = true; public override bool CanSeek { get; } = true; public override bool CanWrite { get; } public long RemainingBytes { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Length - Position; } } public Stream BaseStream { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public unsafe BufferedReadStream(Configuration configuration, Stream stream) { Guard.NotNull(configuration, "configuration"); Guard.IsTrue(stream.CanRead, "stream", "Stream must be readable."); Guard.IsTrue(stream.CanSeek, "stream", "Stream must be seekable."); if (stream.CanWrite) { stream.Flush(); } BaseStream = stream; Length = stream.Length; Position = (int)stream.Position; BufferSize = configuration.StreamProcessingBufferSize; maxBufferIndex = BufferSize - 1; readBuffer = ArrayPool.Shared.Rent(BufferSize); readBufferHandle = new Memory(readBuffer).Pin(); pinnedReadBuffer = (byte*)readBufferHandle.Pointer; readBufferIndex = BufferSize; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe override int ReadByte() { if (readerPosition >= Length) { EofHitCount++; return -1; } if (readBufferIndex > maxBufferIndex) { FillReadBuffer(); } readerPosition++; return pinnedReadBuffer[readBufferIndex++]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int Read(byte[] buffer, int offset, int count) { if (count > BufferSize) { return ReadToBufferDirectSlow(buffer, offset, count); } if (count + readBufferIndex > BufferSize) { return ReadToBufferViaCopySlow(buffer, offset, count); } return ReadToBufferViaCopyFast(buffer, offset, count); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int Read(Span buffer) { int length = buffer.Length; if (length > BufferSize) { return ReadToBufferDirectSlow(buffer); } if (length + readBufferIndex > BufferSize) { return ReadToBufferViaCopySlow(buffer); } return ReadToBufferViaCopyFast(buffer); } public override void Flush() { Stream baseStream = BaseStream; if (readerPosition != baseStream.Position) { baseStream.Seek(readerPosition, SeekOrigin.Begin); readerPosition = (int)baseStream.Position; } readBufferIndex = BufferSize; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override long Seek(long offset, SeekOrigin origin) { switch (origin) { case SeekOrigin.Begin: Position = offset; break; case SeekOrigin.Current: Position += offset; break; case SeekOrigin.End: Position = Length - offset; break; } return readerPosition; } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } protected override void Dispose(bool disposing) { if (!isDisposed) { isDisposed = true; readBufferHandle.Dispose(); ArrayPool.Shared.Return(readBuffer); Flush(); base.Dispose(disposing: true); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool IsInReadBuffer(long newPosition, out long index) { index = newPosition - readerPosition + readBufferIndex; if (index > -1) { return index < BufferSize; } return false; } [MethodImpl(MethodImplOptions.NoInlining)] private void FillReadBuffer() { Stream baseStream = BaseStream; if (readerPosition != baseStream.Position) { baseStream.Seek(readerPosition, SeekOrigin.Begin); } int num = 0; int num2; do { num2 = baseStream.Read(readBuffer, num, BufferSize - num); num += num2; } while (num < BufferSize && num2 > 0); readBufferIndex = 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int ReadToBufferViaCopyFast(Span buffer) { int copyCount = GetCopyCount(buffer.Length); readBuffer.AsSpan(readBufferIndex, copyCount).CopyTo(buffer); readerPosition += copyCount; readBufferIndex += copyCount; CheckEof(copyCount); return copyCount; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int ReadToBufferViaCopyFast(byte[] buffer, int offset, int count) { int copyCount = GetCopyCount(count); CopyBytes(buffer, offset, copyCount); readerPosition += copyCount; readBufferIndex += copyCount; return copyCount; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int ReadToBufferViaCopySlow(Span buffer) { FillReadBuffer(); return ReadToBufferViaCopyFast(buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int ReadToBufferViaCopySlow(byte[] buffer, int offset, int count) { FillReadBuffer(); return ReadToBufferViaCopyFast(buffer, offset, count); } [MethodImpl(MethodImplOptions.NoInlining)] private int ReadToBufferDirectSlow(Span buffer) { Stream baseStream = BaseStream; if (readerPosition != baseStream.Position) { baseStream.Seek(readerPosition, SeekOrigin.Begin); } int length = buffer.Length; int num = 0; int num2; do { num2 = baseStream.Read(buffer.Slice(num, length - num)); num += num2; } while (num < length && num2 > 0); Position += num; CheckEof(num); return num; } [MethodImpl(MethodImplOptions.NoInlining)] private int ReadToBufferDirectSlow(byte[] buffer, int offset, int count) { Stream baseStream = BaseStream; if (readerPosition != baseStream.Position) { baseStream.Seek(readerPosition, SeekOrigin.Begin); } int num = 0; int num2; do { num2 = baseStream.Read(buffer, num + offset, count - num); num += num2; } while (num < count && num2 > 0); Position += num; return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetCopyCount(int count) { long num = Length - readerPosition; if (num > count) { return count; } if (num < 0) { return 0; } return (int)num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void CopyBytes(byte[] buffer, int offset, int count) { if (count < 9) { int num = count; int num2 = readBufferIndex; byte* ptr = pinnedReadBuffer; while (--num > -1) { buffer[offset + num] = ptr[num2 + num]; } } else { Buffer.BlockCopy(readBuffer, readBufferIndex, buffer, offset, count); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CheckEof(int read) { if (read == 0) { EofHitCount++; } } } internal sealed class ChunkedMemoryStream : Stream { private sealed class MemoryChunk : IDisposable { private bool isDisposed; public IMemoryOwner Buffer { get; set; } public MemoryChunk Next { get; set; } public int Length { get; set; } private void Dispose(bool disposing) { if (!isDisposed) { if (disposing) { Buffer.Dispose(); } Buffer = null; isDisposed = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } private readonly MemoryAllocator allocator; private MemoryChunk memoryChunk; private int chunkCount; private readonly int allocatorCapacity; private bool isDisposed; private MemoryChunk writeChunk; private int writeOffset; private MemoryChunk readChunk; private int readOffset; public override bool CanRead => !isDisposed; public override bool CanSeek => !isDisposed; public override bool CanWrite => !isDisposed; public override long Length { get { EnsureNotDisposed(); int num = 0; MemoryChunk memoryChunk = this.memoryChunk; while (memoryChunk != null) { MemoryChunk next = memoryChunk.Next; num = ((next == null) ? (num + writeOffset) : (num + memoryChunk.Length)); memoryChunk = next; } return num; } } public override long Position { get { EnsureNotDisposed(); if (readChunk == null) { return 0L; } int num = 0; for (MemoryChunk next = memoryChunk; next != readChunk; next = next.Next) { num += next.Length; } num += readOffset; return num; } set { EnsureNotDisposed(); if (value < 0) { ThrowArgumentOutOfRange("value"); } MemoryChunk memoryChunk = readChunk; int num = readOffset; readChunk = null; readOffset = 0; int num2 = (int)value; for (MemoryChunk next = this.memoryChunk; next != null; next = next.Next) { if (num2 < next.Length || (num2 == next.Length && next.Next == null)) { readChunk = next; readOffset = num2; break; } num2 -= next.Length; } if (readChunk == null) { readChunk = memoryChunk; readOffset = num; } } } public ChunkedMemoryStream(MemoryAllocator allocator) { Guard.NotNull(allocator, "allocator"); allocatorCapacity = allocator.GetBufferCapacityInBytes(); this.allocator = allocator; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override long Seek(long offset, SeekOrigin origin) { EnsureNotDisposed(); switch (origin) { case SeekOrigin.Begin: Position = offset; break; case SeekOrigin.Current: Position += offset; break; case SeekOrigin.End: Position = Length + offset; break; default: ThrowInvalidSeek(); break; } return Position; } public override void SetLength(long value) { throw new NotSupportedException(); } protected override void Dispose(bool disposing) { if (isDisposed) { return; } try { isDisposed = true; if (disposing) { ReleaseMemoryChunks(memoryChunk); } memoryChunk = null; writeChunk = null; readChunk = null; chunkCount = 0; } finally { base.Dispose(disposing); } } public override void Flush() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int Read(byte[] buffer, int offset, int count) { Guard.NotNull(buffer, "buffer"); Guard.MustBeGreaterThanOrEqualTo(offset, 0, "offset"); Guard.MustBeGreaterThanOrEqualTo(count, 0, "count"); Guard.IsFalse(buffer.Length - offset < count, "buffer", "Offset subtracted from the buffer length is less than count."); return ReadImpl(buffer.AsSpan(offset, count)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int Read(Span buffer) { return ReadImpl(buffer); } private int ReadImpl(Span buffer) { EnsureNotDisposed(); if (readChunk == null) { if (memoryChunk == null) { return 0; } readChunk = memoryChunk; readOffset = 0; } IMemoryOwner buffer2 = readChunk.Buffer; int length = readChunk.Length; if (readChunk.Next == null) { length = writeOffset; } int num = 0; int num2 = 0; int num3 = buffer.Length; while (num3 > 0) { if (readOffset == length) { if (readChunk.Next == null) { break; } readChunk = readChunk.Next; readOffset = 0; buffer2 = readChunk.Buffer; length = readChunk.Length; if (readChunk.Next == null) { length = writeOffset; } } int num4 = Math.Min(num3, length - readOffset); buffer2.Slice(readOffset, num4).CopyTo(buffer.Slice(num2)); num2 += num4; num3 -= num4; readOffset += num4; num += num4; } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int ReadByte() { EnsureNotDisposed(); if (readChunk == null) { if (memoryChunk == null) { return 0; } readChunk = memoryChunk; readOffset = 0; } IMemoryOwner buffer = readChunk.Buffer; int length = readChunk.Length; if (readChunk.Next == null) { length = writeOffset; } if (readOffset == length) { if (readChunk.Next == null) { return -1; } readChunk = readChunk.Next; readOffset = 0; buffer = readChunk.Buffer; } return buffer.GetSpan()[readOffset++]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void Write(byte[] buffer, int offset, int count) { Guard.NotNull(buffer, "buffer"); Guard.MustBeGreaterThanOrEqualTo(offset, 0, "offset"); Guard.MustBeGreaterThanOrEqualTo(count, 0, "count"); Guard.IsFalse(buffer.Length - offset < count, "buffer", "Offset subtracted from the buffer length is less than count."); WriteImpl(buffer.AsSpan(offset, count)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void Write(ReadOnlySpan buffer) { WriteImpl(buffer); } private void WriteImpl(ReadOnlySpan buffer) { EnsureNotDisposed(); if (memoryChunk == null) { memoryChunk = AllocateMemoryChunk(); writeChunk = memoryChunk; writeOffset = 0; } Span span = writeChunk.Buffer.GetSpan(); int length = writeChunk.Length; int num = buffer.Length; int num2 = 0; while (num > 0) { if (writeOffset == length) { writeChunk.Next = AllocateMemoryChunk(); writeChunk = writeChunk.Next; writeOffset = 0; span = writeChunk.Buffer.GetSpan(); length = writeChunk.Length; } int num3 = Math.Min(num, length - writeOffset); buffer.Slice(num2, num3).CopyTo(span.Slice(writeOffset)); num2 += num3; num -= num3; writeOffset += num3; } } public override void WriteByte(byte value) { EnsureNotDisposed(); if (memoryChunk == null) { memoryChunk = AllocateMemoryChunk(); writeChunk = memoryChunk; writeOffset = 0; } IMemoryOwner buffer = writeChunk.Buffer; int length = writeChunk.Length; if (writeOffset == length) { writeChunk.Next = AllocateMemoryChunk(); writeChunk = writeChunk.Next; writeOffset = 0; buffer = writeChunk.Buffer; } buffer.GetSpan()[writeOffset++] = value; } public byte[] ToArray() { int count = (int)Length; byte[] array = new byte[Length]; MemoryChunk memoryChunk = readChunk; int num = readOffset; readChunk = this.memoryChunk; readOffset = 0; Read(array, 0, count); readChunk = memoryChunk; readOffset = num; return array; } public void WriteTo(Stream stream) { EnsureNotDisposed(); Guard.NotNull(stream, "stream"); if (readChunk == null) { if (memoryChunk == null) { return; } readChunk = memoryChunk; readOffset = 0; } IMemoryOwner buffer = readChunk.Buffer; int length = readChunk.Length; if (readChunk.Next == null) { length = writeOffset; } while (true) { if (readOffset == length) { if (readChunk.Next == null) { break; } readChunk = readChunk.Next; readOffset = 0; buffer = readChunk.Buffer; length = readChunk.Length; if (readChunk.Next == null) { length = writeOffset; } } int count = length - readOffset; stream.Write(buffer.GetSpan(), readOffset, count); readOffset = length; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnsureNotDisposed() { if (isDisposed) { ThrowDisposed(); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowDisposed() { throw new ObjectDisposedException(null, "The stream is closed."); } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowArgumentOutOfRange(string value) { throw new ArgumentOutOfRangeException(value); } [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowInvalidSeek() { throw new ArgumentException("Invalid seek origin."); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private MemoryChunk AllocateMemoryChunk() { IMemoryOwner buffer = allocator.Allocate(Math.Min(allocatorCapacity, GetChunkSize(chunkCount++))); return new MemoryChunk { Buffer = buffer, Next = null, Length = buffer.Length() }; } private void ReleaseMemoryChunks(MemoryChunk chunk) { while (chunk != null) { chunk.Dispose(); chunk = chunk.Next; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetChunkSize(int i) { if (i >= 16) { return 4194304; } return 131072 * (1 << i / 4); } } internal interface IFileSystem { Stream OpenRead(string path); Stream Create(string path); } internal sealed class LocalFileSystem : IFileSystem { public Stream OpenRead(string path) { return File.OpenRead(path); } public Stream Create(string path) { return File.Create(path); } } } namespace SixLabors.ImageSharp.Formats { public interface IImageDecoder { Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel; Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken); } internal interface IImageDecoderInternals { Configuration Configuration { get; } Size Dimensions { get; } Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel; IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken); } public interface IImageEncoder { void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel; Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel; } internal interface IImageEncoderInternals { void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel; } public interface IImageFormat { string Name { get; } string DefaultMimeType { get; } IEnumerable MimeTypes { get; } IEnumerable FileExtensions { get; } } public interface IImageFormat : IImageFormat where TFormatMetadata : class { TFormatMetadata CreateDefaultFormatMetadata(); } public interface IImageFormat : IImageFormat, IImageFormat where TFormatMetadata : class where TFormatFrameMetadata : class { TFormatFrameMetadata CreateDefaultFormatFrameMetadata(); } public interface IImageFormatDetector { int HeaderSize { get; } IImageFormat DetectFormat(ReadOnlySpan header); } public interface IImageInfoDetector { IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken); } internal static class ImageDecoderUtilities { public static IImageInfo Identify(this IImageDecoderInternals decoder, Configuration configuration, Stream stream, CancellationToken cancellationToken) { using BufferedReadStream stream2 = new BufferedReadStream(configuration, stream); try { return decoder.Identify(stream2, cancellationToken); } catch (InvalidMemoryOperationException memoryException) { throw new InvalidImageContentException(decoder.Dimensions, memoryException); } } public static Image Decode(this IImageDecoderInternals decoder, Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return decoder.Decode(configuration, stream, DefaultLargeImageExceptionFactory, cancellationToken); } public static Image Decode(this IImageDecoderInternals decoder, Configuration configuration, Stream stream, Func largeImageExceptionFactory, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { BufferedReadStream bufferedReadStream = (stream as BufferedReadStream) ?? new BufferedReadStream(configuration, stream); try { return decoder.Decode(bufferedReadStream, cancellationToken); } catch (InvalidMemoryOperationException arg) { throw largeImageExceptionFactory(arg, decoder.Dimensions); } finally { if (bufferedReadStream != stream) { bufferedReadStream.Dispose(); } } } private static InvalidImageContentException DefaultLargeImageExceptionFactory(InvalidMemoryOperationException memoryOperationException, Size dimensions) { return new InvalidImageContentException(dimensions, memoryOperationException); } } internal static class ImageEncoderUtilities { public static async Task EncodeAsync(this IImageEncoderInternals encoder, Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Configuration configuration = image.GetConfiguration(); if (stream.CanSeek) { await DoEncodeAsync(stream).ConfigureAwait(continueOnCapturedContext: false); return; } using (MemoryStream ms = new MemoryStream()) { await DoEncodeAsync(ms); ms.Position = 0L; await ms.CopyToAsync(stream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } Task DoEncodeAsync(Stream innerStream) { try { encoder.Encode(image, innerStream, cancellationToken); return Task.CompletedTask; } catch (OperationCanceledException) { return Task.FromCanceled(cancellationToken); } catch (Exception exception) { return Task.FromException(exception); } } } public static void Encode(this IImageEncoderInternals encoder, Image image, Stream stream) where TPixel : unmanaged, IPixel { encoder.Encode(image, stream, default(CancellationToken)); } } public class ImageFormatManager { private static readonly object HashLock = new object(); private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); private readonly HashSet imageFormats = new HashSet(); private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); internal int MaxHeaderSize { get; private set; } public IEnumerable ImageFormats => imageFormats; internal IEnumerable FormatDetectors => imageFormatDetectors; internal IEnumerable> ImageDecoders => mimeTypeDecoders; internal IEnumerable> ImageEncoders => mimeTypeEncoders; public void AddImageFormat(IImageFormat format) { Guard.NotNull(format, "format"); Guard.NotNull(format.MimeTypes, "MimeTypes"); Guard.NotNull(format.FileExtensions, "FileExtensions"); lock (HashLock) { if (!imageFormats.Contains(format)) { imageFormats.Add(format); } } } public IImageFormat FindFormatByFileExtension(string extension) { Guard.NotNullOrWhiteSpace(extension, "extension"); if (extension[0] == '.') { extension = extension.Substring(1); } return imageFormats.FirstOrDefault((IImageFormat x) => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); } public IImageFormat FindFormatByMimeType(string mimeType) { return imageFormats.FirstOrDefault((IImageFormat x) => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); } public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) { Guard.NotNull(imageFormat, "imageFormat"); Guard.NotNull(encoder, "encoder"); AddImageFormat(imageFormat); mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (IImageFormat s, IImageEncoder e) => encoder); } public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) { Guard.NotNull(imageFormat, "imageFormat"); Guard.NotNull(decoder, "decoder"); AddImageFormat(imageFormat); mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (IImageFormat s, IImageDecoder e) => decoder); } public void ClearImageFormatDetectors() { imageFormatDetectors = new ConcurrentBag(); } public void AddImageFormatDetector(IImageFormatDetector detector) { Guard.NotNull(detector, "detector"); imageFormatDetectors.Add(detector); SetMaxHeaderSize(); } public IImageDecoder FindDecoder(IImageFormat format) { Guard.NotNull(format, "format"); if (!mimeTypeDecoders.TryGetValue(format, out var value)) { return null; } return value; } public IImageEncoder FindEncoder(IImageFormat format) { Guard.NotNull(format, "format"); if (!mimeTypeEncoders.TryGetValue(format, out var value)) { return null; } return value; } private void SetMaxHeaderSize() { MaxHeaderSize = imageFormatDetectors.Max((IImageFormatDetector x) => x.HeaderSize); } } public class PixelTypeInfo { public int BitsPerPixel { get; } public PixelAlphaRepresentation? AlphaRepresentation { get; } public PixelTypeInfo(int bitsPerPixel) { BitsPerPixel = bitsPerPixel; } public PixelTypeInfo(int bitsPerPixel, PixelAlphaRepresentation alpha) { BitsPerPixel = bitsPerPixel; AlphaRepresentation = alpha; } internal static PixelTypeInfo Create() where TPixel : unmanaged, IPixel { return new PixelTypeInfo(Unsafe.SizeOf() * 8); } internal static PixelTypeInfo Create(PixelAlphaRepresentation alpha) where TPixel : unmanaged, IPixel { return new PixelTypeInfo(Unsafe.SizeOf() * 8, alpha); } } } namespace SixLabors.ImageSharp.Formats.Webp { internal class AlphaDecoder : IDisposable { private readonly MemoryAllocator memoryAllocator; public int Width { get; } public int Height { get; } public WebpAlphaFilterType AlphaFilterType { get; } public int LastRow { get; set; } public int PrevRow { get; set; } public Vp8LDecoder Vp8LDec { get; } public IMemoryOwner Alpha { get; } private bool Compressed { get; } private IMemoryOwner Data { get; } private WebpLosslessDecoder LosslessDecoder { get; } public bool Use8BDecode { get; } public AlphaDecoder(int width, int height, IMemoryOwner data, byte alphaChunkHeader, MemoryAllocator memoryAllocator, Configuration configuration) { Width = width; Height = height; Data = data; this.memoryAllocator = memoryAllocator; LastRow = 0; int length = width * height; WebpAlphaCompressionMethod webpAlphaCompressionMethod = (WebpAlphaCompressionMethod)(alphaChunkHeader & 3); if (webpAlphaCompressionMethod != WebpAlphaCompressionMethod.NoCompression && webpAlphaCompressionMethod != WebpAlphaCompressionMethod.WebpLosslessCompression) { WebpThrowHelper.ThrowImageFormatException($"unexpected alpha compression method {webpAlphaCompressionMethod} found"); } Compressed = webpAlphaCompressionMethod == WebpAlphaCompressionMethod.WebpLosslessCompression; int num = (alphaChunkHeader >> 2) & 3; if ((num < 0 || num > 3) ? true : false) { WebpThrowHelper.ThrowImageFormatException($"unexpected alpha filter method {num} found"); } Alpha = memoryAllocator.Allocate(length); AlphaFilterType = (WebpAlphaFilterType)num; Vp8LDec = new Vp8LDecoder(width, height, memoryAllocator); if (Compressed) { Vp8LBitReader bitReader = new Vp8LBitReader(data); LosslessDecoder = new WebpLosslessDecoder(bitReader, memoryAllocator, configuration); LosslessDecoder.DecodeImageStream(Vp8LDec, width, height, isLevel0: true); Use8BDecode = Vp8LDec.Transforms.Count > 0 && Is8BOptimizable(Vp8LDec.Metadata); } } public void Decode() { if (!Compressed) { Memory memory = Data.Memory; Span span = memory.Span; int num = Width * Height; if (span.Length < num) { WebpThrowHelper.ThrowImageFormatException("not enough data in the ALPH chunk"); } memory = Alpha.Memory; Span span2 = memory.Span; if (AlphaFilterType == WebpAlphaFilterType.None) { span.Slice(0, num).CopyTo(span2); return; } Span input = span; Span span3 = span2; Span prev = default(Span); for (int i = 0; i < Height; i++) { switch (AlphaFilterType) { case WebpAlphaFilterType.Horizontal: HorizontalUnfilter(prev, input, span3, Width); break; case WebpAlphaFilterType.Vertical: VerticalUnfilter(prev, input, span3, Width); break; case WebpAlphaFilterType.Gradient: GradientUnfilter(prev, input, span3, Width); break; } prev = span3; input = input.Slice(Width); span3 = span3.Slice(Width); } } else if (Use8BDecode) { LosslessDecoder.DecodeAlphaData(this); } else { LosslessDecoder.DecodeImageData(Vp8LDec, Vp8LDec.Pixels.Memory.Span); ExtractAlphaRows(Vp8LDec); } } public void AlphaApplyFilter(int firstRow, int lastRow, Span dst, int stride) { if (AlphaFilterType == WebpAlphaFilterType.None) { return; } Span span = Alpha.Memory.Span; Span prev = ((PrevRow == 0) ? ((Span)null) : span.Slice(Width * PrevRow)); for (int i = firstRow; i < lastRow; i++) { switch (AlphaFilterType) { case WebpAlphaFilterType.Horizontal: HorizontalUnfilter(prev, dst, dst, Width); break; case WebpAlphaFilterType.Vertical: VerticalUnfilter(prev, dst, dst, Width); break; case WebpAlphaFilterType.Gradient: GradientUnfilter(prev, dst, dst, Width); break; } prev = dst; dst = dst.Slice(stride); } PrevRow = lastRow - 1; } public void ExtractPalettedAlphaRows(int lastRow) { WebpAlphaFilterType alphaFilterType = AlphaFilterType; bool flag = (uint)alphaFilterType <= 1u; int num = ((!flag) ? LastRow : 0); int num2 = ((LastRow < num) ? num : LastRow); if (lastRow > num2) { Span span = Alpha.Memory.Span; Span span2 = MemoryMarshal.Cast(Vp8LDec.Pixels.Memory.Span); Span dst = span.Slice(Width * num2); Span src = span2.Slice(Vp8LDec.Width * num2); if (Vp8LDec.Transforms.Count == 0 || Vp8LDec.Transforms[0].TransformType != Vp8LTransformType.ColorIndexingTransform) { WebpThrowHelper.ThrowImageFormatException("error while decoding alpha channel, expected color index transform data is missing"); } ColorIndexInverseTransformAlpha(Vp8LDec.Transforms[0], num2, lastRow, src, dst); AlphaApplyFilter(num2, lastRow, dst, Width); } LastRow = lastRow; } private void ExtractAlphaRows(Vp8LDecoder dec) { int height = dec.Height; int width = dec.Width; Span span = dec.Pixels.Memory.Span; Span span2 = Alpha.Memory.Span; int size = width * height; WebpLosslessDecoder.ApplyInverseTransforms(dec, span, memoryAllocator); ExtractGreen(span, span2, size); AlphaApplyFilter(0, height, span2, width); } private static void ColorIndexInverseTransformAlpha(Vp8LTransform transform, int yStart, int yEnd, Span src, Span dst) { int num = 8 >> transform.Bits; int xSize = transform.XSize; Span span = transform.Data.Memory.Span; if (num < 8) { int num2 = 0; int num3 = 0; int num4 = (1 << transform.Bits) - 1; int num5 = (1 << num) - 1; for (int i = yStart; i < yEnd; i++) { int num6 = 0; for (int j = 0; j < xSize; j++) { if ((j & num4) == 0) { num6 = src[num2]; num2++; } dst[num3] = GetAlphaValue((int)span[num6 & num5]); num3++; num6 >>= num; } } } else { MapAlpha(src, span, dst, yStart, yEnd, xSize); } } private static void HorizontalUnfilter(Span prev, Span input, Span dst, int width) { if (Sse2.IsSupported) { dst[0] = (byte)(input[0] + ((!prev.IsEmpty) ? prev[0] : 0)); if (width > 1) { Vector128 vector = Vector128.Zero.WithElement(0, dst[0]); ref byte reference = ref MemoryMarshal.GetReference(input); nint num; for (num = 1; num + 8 <= width; num += 8) { Vector128 vector2 = Sse2.Add(Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, num)), 0L).AsByte(), vector.AsByte()); Vector128 right = Sse2.ShiftLeftLogical128BitLane(vector2, 1); Vector128 vector3 = Sse2.Add(vector2, right); Vector128 right2 = Sse2.ShiftLeftLogical128BitLane(vector3, 2); Vector128 vector4 = Sse2.Add(vector3, right2); Vector128 right3 = Sse2.ShiftLeftLogical128BitLane(vector4, 4); Vector128 vector5 = Sse2.Add(vector4, right3); Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(dst), num)) = vector5.GetLower(); vector = Sse2.ShiftRightLogical(vector5.AsInt64(), 56).AsInt32(); } for (; num < width; num++) { dst[(int)num] = (byte)(input[(int)num] + dst[(int)num - 1]); } } } else { byte b = (byte)((!prev.IsEmpty) ? prev[0] : 0); for (int i = 0; i < width; i++) { byte b2 = (byte)(b + input[i]); b = b2; dst[i] = b2; } } } private static void VerticalUnfilter(Span prev, Span input, Span dst, int width) { if (prev.IsEmpty) { HorizontalUnfilter(null, input, dst, width); } else if (Avx2.IsSupported) { int num = width & -32; nint num2; for (num2 = 0; num2 < num; num2 += 32) { Vector256 vector = Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(input), num2)); Vector256 vector2 = Avx2.Add(right: Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(prev), num2)).AsByte(), left: vector.AsByte()); Unsafe.As>(ref Unsafe.Add(ref MemoryMarshal.GetReference(dst), num2)) = vector2; } for (; num2 < width; num2++) { dst[(int)num2] = (byte)(prev[(int)num2] + input[(int)num2]); } } else { for (int i = 0; i < width; i++) { dst[i] = (byte)(prev[i] + input[i]); } } } private static void GradientUnfilter(Span prev, Span input, Span dst, int width) { if (prev.IsEmpty) { HorizontalUnfilter(null, input, dst, width); return; } byte c; byte b = (c = prev[0]); for (int i = 0; i < width; i++) { byte b2 = prev[i]; b = (byte)(input[i] + GradientPredictor(b, b2, c)); c = b2; dst[i] = b; } } private static bool Is8BOptimizable(Vp8LMetadata hdr) { if (hdr.ColorCacheSize > 0) { return false; } for (int i = 0; i < hdr.NumHTreeGroups; i++) { List hTrees = hdr.HTreeGroups[i].HTrees; if (hTrees[1][0].BitsUsed > 0) { return false; } if (hTrees[2][0].BitsUsed > 0) { return false; } if (hTrees[3][0].BitsUsed > 0) { return false; } } return true; } private static void MapAlpha(Span src, Span colorMap, Span dst, int yStart, int yEnd, int width) { int num = 0; for (int i = yStart; i < yEnd; i++) { for (int j = 0; j < width; j++) { dst[num] = GetAlphaValue((int)colorMap[src[num]]); num++; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte GetAlphaValue(int val) { return (byte)((val >> 8) & 0xFF); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GradientPredictor(byte a, byte b, byte c) { int num = a + b - c; if ((num & -256) != 0) { if (num >= 0) { return 255; } return 0; } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ExtractGreen(Span argb, Span alpha, int size) { for (int i = 0; i < size; i++) { alpha[i] = (byte)(argb[i] >> 8); } } public void Dispose() { Vp8LDec?.Dispose(); Data.Dispose(); Alpha?.Dispose(); } } internal class AlphaEncoder : IDisposable { private IMemoryOwner alphaData; public IMemoryOwner EncodeAlpha(Image image, Configuration configuration, MemoryAllocator memoryAllocator, bool compress, out int size) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator); if (compress) { WebpEncodingMethod webpEncodingMethod = WebpEncodingMethod.Level4; int quality = 8 * (int)webpEncodingMethod; using Vp8LEncoder vp8LEncoder = new Vp8LEncoder(memoryAllocator, configuration, width, height, quality, webpEncodingMethod, WebpTransparentColorMode.Preserve, nearLossless: false, 0); using Image image2 = DispatchAlphaToGreen(image, alphaData.GetSpan()); size = vp8LEncoder.EncodeAlphaImageData(image2, alphaData); return alphaData; } size = width * height; return alphaData; } private static Image DispatchAlphaToGreen(Image image, Span alphaData) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Image image2 = new Image(width, height); for (int i = 0; i < height; i++) { Span span = image2.DangerousGetPixelRowMemory(i).Span; Span span2 = alphaData.Slice(i * width, width); for (int j = 0; j < width; j++) { span[j] = new Rgba32(0, span2[j], 0, 0); } } return image2; } private static IMemoryOwner ExtractAlphaChannel(Image image, Configuration configuration, MemoryAllocator memoryAllocator) where TPixel : unmanaged, IPixel { Buffer2D pixelBuffer = image.Frames.RootFrame.PixelBuffer; int height = image.Height; int width = image.Width; IMemoryOwner memoryOwner = memoryAllocator.Allocate(width * height); Span span = memoryOwner.GetSpan(); using IMemoryOwner buffer = memoryAllocator.Allocate(width); Span span2 = buffer.GetSpan(); for (int i = 0; i < height; i++) { Span span3 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToRgba32(configuration, span3, span2); int num = i * width; for (int j = 0; j < width; j++) { span[num + j] = span2[j].A; } } return memoryOwner; } public void Dispose() { alphaData?.Dispose(); } } internal enum EntropyIx : byte { Direct, Spatial, SubGreen, SpatialSubGreen, Palette, PaletteAndSpatial, NumEntropyIx } internal enum HistoIx : byte { HistoAlpha, HistoAlphaPred, HistoGreen, HistoGreenPred, HistoRed, HistoRedPred, HistoBlue, HistoBluePred, HistoRedSubGreen, HistoRedPredSubGreen, HistoBlueSubGreen, HistoBluePredSubGreen, HistoPalette, HistoTotal } internal interface IWebpDecoderOptions { bool IgnoreMetadata { get; } } internal interface IWebpEncoderOptions { WebpFileFormatType? FileFormat { get; } int Quality { get; } WebpEncodingMethod Method { get; } bool UseAlphaCompression { get; } int EntropyPasses { get; } int SpatialNoiseShaping { get; } int FilterStrength { get; } WebpTransparentColorMode TransparentColorMode { get; } bool NearLossless { get; } int NearLosslessQuality { get; } } internal enum WebpAlphaCompressionMethod { NoCompression, WebpLosslessCompression } internal enum WebpAlphaFilterType { None, Horizontal, Vertical, Gradient } public enum WebpBitsPerPixel : short { Pixel24 = 24, Pixel32 = 32 } internal enum WebpChunkType : uint { Vp8 = 1448097824u, Vp8L = 1448097868u, Vp8X = 1448097880u, Alpha = 1095520328u, Iccp = 1229144912u, Exif = 1163413830u, Xmp = 1481461792u, AnimationParameter = 1095649613u, Animation = 1095650630u } internal static class WebpCommonUtils { private static readonly Vector256 AlphaMaskVector256 = Vector256.Create(0, 0, 0, byte.MaxValue, 0, 0, 0, byte.MaxValue, 0, 0, 0, byte.MaxValue, 0, 0, 0, byte.MaxValue, 0, 0, 0, byte.MaxValue, 0, 0, 0, byte.MaxValue, 0, 0, 0, byte.MaxValue, 0, 0, 0, byte.MaxValue); private static readonly Vector256 All0x80Vector256 = Vector256.Create((byte)128).AsByte(); private static readonly Vector128 AlphaMask = Vector128.Create(0, 0, 0, byte.MaxValue, 0, 0, 0, byte.MaxValue, 0, 0, 0, byte.MaxValue, 0, 0, 0, byte.MaxValue); private static readonly Vector128 All0x80 = Vector128.Create((byte)128).AsByte(); public unsafe static bool CheckNonOpaque(Span row) { if (Avx2.IsSupported) { ReadOnlySpan readOnlySpan = MemoryMarshal.AsBytes(row); int i = 0; int num = row.Length * 4 - 3; fixed (byte* ptr = readOnlySpan) { for (; i + 128 <= num; i += 128) { Vector256 left = Avx.LoadVector256(ptr + i).AsByte(); Vector256 left2 = Avx.LoadVector256(ptr + i + 32).AsByte(); Vector256 left3 = Avx.LoadVector256(ptr + i + 64).AsByte(); Vector256 left4 = Avx.LoadVector256(ptr + i + 96).AsByte(); Vector256 left5 = Avx2.And(left, AlphaMaskVector256).AsInt32(); Vector256 right = Avx2.And(left2, AlphaMaskVector256).AsInt32(); Vector256 left6 = Avx2.And(left3, AlphaMaskVector256).AsInt32(); Vector256 right2 = Avx2.And(left4, AlphaMaskVector256).AsInt32(); Vector256 left7 = Avx2.PackSignedSaturate(left5, right).AsInt16(); Vector256 right3 = Avx2.PackSignedSaturate(left6, right2).AsInt16(); if (Avx2.MoveMask(Avx2.CompareEqual(Avx2.PackSignedSaturate(left7, right3).AsByte(), All0x80Vector256)) != -1) { return true; } } for (; i + 64 <= num; i += 64) { if (IsNoneOpaque64Bytes(ptr, i)) { return true; } } for (; i + 32 <= num; i += 32) { if (IsNoneOpaque32Bytes(ptr, i)) { return true; } } for (; i <= num; i += 4) { if (ptr[i + 3] != byte.MaxValue) { return true; } } } } else if (Sse2.IsSupported) { ReadOnlySpan readOnlySpan2 = MemoryMarshal.AsBytes(row); int j = 0; int num2 = row.Length * 4 - 3; fixed (byte* ptr2 = readOnlySpan2) { for (; j + 64 <= num2; j += 64) { if (IsNoneOpaque64Bytes(ptr2, j)) { return true; } } for (; j + 32 <= num2; j += 32) { if (IsNoneOpaque32Bytes(ptr2, j)) { return true; } } for (; j <= num2; j += 4) { if (ptr2[j + 3] != byte.MaxValue) { return true; } } } } else { for (int k = 0; k < row.Length; k++) { if (row[k].A != byte.MaxValue) { return true; } } } return false; } private unsafe static bool IsNoneOpaque64Bytes(byte* src, int i) { Vector128 left = Sse2.LoadVector128(src + i).AsByte(); Vector128 left2 = Sse2.LoadVector128(src + i + 16).AsByte(); Vector128 left3 = Sse2.LoadVector128(src + i + 32).AsByte(); Vector128 left4 = Sse2.LoadVector128(src + i + 48).AsByte(); Vector128 left5 = Sse2.And(left, AlphaMask).AsInt32(); Vector128 right = Sse2.And(left2, AlphaMask).AsInt32(); Vector128 left6 = Sse2.And(left3, AlphaMask).AsInt32(); Vector128 right2 = Sse2.And(left4, AlphaMask).AsInt32(); Vector128 left7 = Sse2.PackSignedSaturate(left5, right).AsInt16(); Vector128 right3 = Sse2.PackSignedSaturate(left6, right2).AsInt16(); if (Sse2.MoveMask(Sse2.CompareEqual(Sse2.PackSignedSaturate(left7, right3).AsByte(), All0x80)) != 65535) { return true; } return false; } private unsafe static bool IsNoneOpaque32Bytes(byte* src, int i) { Vector128 left = Sse2.LoadVector128(src + i).AsByte(); Vector128 left2 = Sse2.LoadVector128(src + i + 16).AsByte(); Vector128 left3 = Sse2.And(left, AlphaMask).AsInt32(); Vector128 right = Sse2.And(left2, AlphaMask).AsInt32(); Vector128 vector = Sse2.PackSignedSaturate(left3, right).AsInt16(); if (Sse2.MoveMask(Sse2.CompareEqual(Sse2.PackSignedSaturate(vector, vector).AsByte(), All0x80)) != 65535) { return true; } return false; } } public sealed class WebpConfigurationModule : IConfigurationModule { public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetDecoder(WebpFormat.Instance, new WebpDecoder()); configuration.ImageFormatsManager.SetEncoder(WebpFormat.Instance, new WebpEncoder()); configuration.ImageFormatsManager.AddImageFormatDetector(new WebpImageFormatDetector()); } } internal static class WebpConstants { public static readonly IEnumerable FileExtensions = new string[1] { "webp" }; public static readonly IEnumerable MimeTypes = new string[1] { "image/webp" }; public static readonly byte[] Vp8HeaderMagicBytes = new byte[3] { 157, 1, 42 }; public const byte Vp8LHeaderMagicByte = 47; public static readonly byte[] Vp8MagicBytes = new byte[4] { 86, 80, 56, 32 }; public static readonly byte[] Vp8LMagicBytes = new byte[4] { 86, 80, 56, 76 }; public static readonly byte[] Vp8XMagicBytes = new byte[4] { 86, 80, 56, 88 }; public static readonly byte[] RiffFourCc = new byte[4] { 82, 73, 70, 70 }; public static readonly byte[] WebpHeader = new byte[4] { 87, 69, 66, 80 }; public const int Vp8LVersionBits = 3; public const int Vp8LImageSizeBits = 14; public const int Vp8FrameHeaderSize = 10; public const int Vp8XChunkSize = 10; public const int ChunkHeaderSize = 8; public const int RiffHeaderSize = 12; public const int TagSize = 4; public const int Vp8LVersion = 0; public const int MaxHuffImageSize = 2600; public const int MinHuffmanBits = 2; public const int MaxHuffmanBits = 9; public const int MaxPaletteSize = 256; public const int MaxColorCacheBits = 10; public const int MaxNumberOfTransforms = 4; public const int MaxTransformBits = 6; public const int TransformPresent = 1; public const int MaxDimension = 16383; public const int MaxAllowedCodeLength = 15; public const int DefaultCodeLength = 8; public const int HuffmanCodesPerMetaCode = 5; public const uint ArgbBlack = 4278190080u; public const int NumArgbCacheRows = 16; public const int NumLiteralCodes = 256; public const int NumLengthCodes = 24; public const int NumDistanceCodes = 40; public const int CodeLengthCodes = 19; public const int LengthTableBits = 7; public const uint CodeLengthLiterals = 16u; public const int CodeLengthRepeatCode = 16; public static readonly int[] CodeLengthExtraBits = new int[3] { 2, 3, 7 }; public static readonly int[] CodeLengthRepeatOffsets = new int[3] { 3, 3, 11 }; public static readonly int[] AlphabetSize = new int[5] { 280, 256, 256, 256, 40 }; public const int NumMbSegments = 4; public const int MaxNumPartitions = 8; public const int NumTypes = 4; public const int NumBands = 8; public const int NumProbas = 11; public const int NumPredModes = 4; public const int NumBModes = 10; public const int NumCtx = 3; public const int MaxVariableLevel = 67; public const int FlatnessLimitI16 = 0; public const int FlatnessLimitIUv = 2; public const int FlatnessLimitI4 = 3; public const int FlatnessPenality = 140; public const int Bps = 32; public const double Gamma = 0.8; public const int GammaFix = 12; public const int GammaScale = 4095; public const int GammaTabFix = 7; public const int GammaTabSize = 32; public const int GammaTabScale = 128; public const int GammaTabRounder = 64; public const int AlphaFix = 19; public const int MaxAlpha = 255; public const int AlphaScale = 510; public const int QuantEncMidAlpha = 64; public const int QuantEncMinAlpha = 30; public const int QuantEncMaxAlpha = 100; public const double SnsToDq = 0.9; public const int QuantEncMaxDqUv = 6; public const int QuantEncMinDqUv = -4; public const int QFix = 17; public const int MaxDelzaSize = 64; public const int FilterStrengthCutoff = 2; public const int Vp8MaxPartition0Size = 524288; public static readonly short[] Vp8FixedCostsUv = new short[4] { 302, 984, 439, 642 }; public static readonly short[] Vp8FixedCostsI16 = new short[4] { 663, 919, 872, 919 }; public const int RdDistoMult = 256; public static readonly byte[] FilterExtraRows = new byte[3] { 0, 2, 8 }; public static readonly int[] Vp8EncBands = new int[17] { 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0 }; public static readonly short[] Scan = new short[16] { 0, 4, 8, 12, 128, 132, 136, 140, 256, 260, 264, 268, 384, 388, 392, 396 }; public static readonly byte[] Cat3 = new byte[3] { 173, 148, 140 }; public static readonly byte[] Cat4 = new byte[4] { 176, 155, 140, 135 }; public static readonly byte[] Cat5 = new byte[5] { 180, 157, 141, 134, 130 }; public static readonly byte[] Cat6 = new byte[11] { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 }; public static readonly byte[] Zigzag = new byte[16] { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 }; public static readonly sbyte[] YModesIntra4 = new sbyte[18] { 0, 1, -1, 2, -2, 3, 4, 6, -3, 5, -4, -5, -6, 7, -7, 8, -8, -9 }; } public sealed class WebpDecoder : IImageDecoder, IWebpDecoderOptions, IImageInfoDetector { public bool IgnoreMetadata { get; set; } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(stream, "stream"); WebpDecoderCore webpDecoderCore = new WebpDecoderCore(configuration, this); try { return webpDecoderCore.Decode(configuration, stream, cancellationToken); } catch (InvalidMemoryOperationException innerException) { Size dimensions = webpDecoderCore.Dimensions; throw new InvalidImageContentException($"Cannot decode image. Failed to allocate buffers for possibly degenerate dimensions: {dimensions.Width}x{dimensions.Height}.", innerException); } } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) { return Decode(configuration, stream, cancellationToken); } public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, "stream"); return new WebpDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); } } internal sealed class WebpDecoderCore : IImageDecoderInternals { private readonly byte[] buffer = new byte[4]; private readonly MemoryAllocator memoryAllocator; private Stream currentStream; private WebpMetadata webpMetadata; private WebpImageInfo webImageInfo; public bool IgnoreMetadata { get; } public ImageMetadata Metadata { get; private set; } public Configuration Configuration { get; } public Size Dimensions => new Size((int)webImageInfo.Width, (int)webImageInfo.Height); public WebpDecoderCore(Configuration configuration, IWebpDecoderOptions options) { Configuration = configuration; memoryAllocator = configuration.MemoryAllocator; IgnoreMetadata = options.IgnoreMetadata; } public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Image image = null; try { Metadata = new ImageMetadata(); currentStream = stream; ReadImageHeader(); using (webImageInfo = ReadVp8Info()) { WebpFeatures features = webImageInfo.Features; if (features != null && features.Animation) { WebpThrowHelper.ThrowNotSupportedException("Animations are not supported"); } image = new Image(Configuration, (int)webImageInfo.Width, (int)webImageInfo.Height, Metadata); Buffer2D rootFramePixelBuffer = image.GetRootFramePixelBuffer(); if (webImageInfo.IsLossless) { new WebpLosslessDecoder(webImageInfo.Vp8LBitReader, memoryAllocator, Configuration).Decode(rootFramePixelBuffer, image.Width, image.Height); } else { new WebpLossyDecoder(webImageInfo.Vp8BitReader, memoryAllocator, Configuration).Decode(rootFramePixelBuffer, image.Width, image.Height, webImageInfo); } if (webImageInfo.Features != null) { ParseOptionalChunks(webImageInfo.Features); } return image; } } catch { image?.Dispose(); throw; } } public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { currentStream = stream; ReadImageHeader(); using (webImageInfo = ReadVp8Info()) { return new ImageInfo(new PixelTypeInfo((int)webImageInfo.BitsPerPixel), (int)webImageInfo.Width, (int)webImageInfo.Height, Metadata); } } private uint ReadImageHeader() { currentStream.Skip(4); uint result = ReadChunkSize(); currentStream.Skip(4); return result; } private WebpImageInfo ReadVp8Info() { Metadata = new ImageMetadata(); webpMetadata = Metadata.GetFormatMetadata(WebpFormat.Instance); switch (ReadChunkType()) { case WebpChunkType.Vp8: return ReadVp8Header(); case WebpChunkType.Vp8L: return ReadVp8LHeader(); case WebpChunkType.Vp8X: return ReadVp8XHeader(); default: WebpThrowHelper.ThrowImageFormatException("Unrecognized VP8 header"); return new WebpImageInfo(); } } private WebpImageInfo ReadVp8XHeader() { WebpFeatures webpFeatures = new WebpFeatures(); ReadChunkSize(); int num = currentStream.ReadByte(); if (num == -1) { WebpThrowHelper.ThrowInvalidImageContentException("VP8X header doe not contain enough data"); } if (num >> 6 != 0) { WebpThrowHelper.ThrowImageFormatException("first two bits of the VP8X header are expected to be zero"); } webpFeatures.IccProfile = (num & 0x20) != 0; webpFeatures.Alpha = (num & 0x10) != 0; webpFeatures.ExifProfile = (num & 8) != 0; webpFeatures.XmpMetaData = (num & 4) != 0; webpFeatures.Animation = (num & 2) != 0; if (currentStream.Read(buffer, 0, 3) != 3) { WebpThrowHelper.ThrowInvalidImageContentException("VP8X header does not contain enough data"); } if (buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0) { WebpThrowHelper.ThrowImageFormatException("reserved bytes should be zero"); } if (currentStream.Read(buffer, 0, 3) != 3) { WebpThrowHelper.ThrowInvalidImageContentException("VP8 header does not contain enough data to read the width"); } buffer[3] = 0; uint width = (uint)(BinaryPrimitives.ReadInt32LittleEndian(buffer) + 1); if (currentStream.Read(buffer, 0, 3) != 3) { WebpThrowHelper.ThrowInvalidImageContentException("VP8 header does not contain enough data to read the height"); } buffer[3] = 0; uint height = (uint)(BinaryPrimitives.ReadInt32LittleEndian(buffer) + 1); WebpImageInfo result = new WebpImageInfo(); while (currentStream.Position < currentStream.Length) { WebpChunkType webpChunkType = ReadChunkType(); switch (webpChunkType) { case WebpChunkType.Vp8: result = ReadVp8Header(webpFeatures); continue; case WebpChunkType.Vp8L: result = ReadVp8LHeader(webpFeatures); continue; } if (IsOptionalVp8XChunk(webpChunkType)) { ParseOptionalExtendedChunks(webpChunkType, webpFeatures); } else { WebpThrowHelper.ThrowImageFormatException("Unexpected chunk followed VP8X header"); } } if (webpFeatures.Animation) { return new WebpImageInfo { Width = width, Height = height, Features = webpFeatures }; } return result; } private WebpImageInfo ReadVp8Header(WebpFeatures features = null) { webpMetadata.FileFormat = WebpFileFormatType.Lossy; if (currentStream.Read(buffer, 0, 4) != 4) { WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 data size"); } uint num = BinaryPrimitives.ReadUInt32LittleEndian(buffer); uint num2 = num; if (currentStream.Read(buffer, 0, 3) != 3) { WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 frame tag"); } int num3 = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16); num2 -= 3; if ((num3 & 1) == 1) { WebpThrowHelper.ThrowImageFormatException("VP8 header indicates the image is not a key frame"); } uint num4 = (uint)((num3 >>> 1) & 7); if (num4 > 3) { WebpThrowHelper.ThrowImageFormatException($"VP8 header indicates unknown profile {num4}"); } if (((num3 >>> 4) & 1) == 0) { WebpThrowHelper.ThrowImageFormatException("VP8 header indicates that the first frame is invisible"); } uint num5 = (uint)num3 >> 5; if (num5 > num) { WebpThrowHelper.ThrowImageFormatException("VP8 header contains inconsistent size information"); } if (currentStream.Read(buffer, 0, 3) != 3) { WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 magic bytes"); } if (!buffer.AsSpan(0, 3).SequenceEqual(WebpConstants.Vp8HeaderMagicBytes)) { WebpThrowHelper.ThrowImageFormatException("VP8 magic bytes not found"); } if (currentStream.Read(buffer, 0, 4) != 4) { WebpThrowHelper.ThrowInvalidImageContentException("VP8 header does not contain enough data to read the image width and height"); } short num6 = BinaryPrimitives.ReadInt16LittleEndian(buffer); uint num7 = (uint)(num6 & 0x3FFF); sbyte xScale = (sbyte)(num6 >>> 6); short num8 = BinaryPrimitives.ReadInt16LittleEndian(buffer.AsSpan(2)); uint num9 = (uint)(num8 & 0x3FFF); sbyte yScale = (sbyte)(num8 >>> 6); num2 -= 7; if (num7 == 0 || num9 == 0) { WebpThrowHelper.ThrowImageFormatException("width or height can not be zero"); } if (num5 > num2) { WebpThrowHelper.ThrowImageFormatException("bad partition length"); } Vp8FrameHeader vp8FrameHeader = new Vp8FrameHeader { KeyFrame = true, Profile = (sbyte)num4, PartitionLength = num5 }; Vp8BitReader vp8BitReader = new Vp8BitReader(currentStream, num2, memoryAllocator, num5) { Remaining = num2 }; return new WebpImageInfo { Width = num7, Height = num9, XScale = xScale, YScale = yScale, BitsPerPixel = ((features != null && features.Alpha) ? WebpBitsPerPixel.Pixel32 : WebpBitsPerPixel.Pixel24), IsLossless = false, Features = features, Vp8Profile = (sbyte)num4, Vp8FrameHeader = vp8FrameHeader, Vp8BitReader = vp8BitReader }; } private WebpImageInfo ReadVp8LHeader(WebpFeatures features = null) { webpMetadata.FileFormat = WebpFileFormatType.Lossless; uint imageDataSize = ReadChunkSize(); Vp8LBitReader vp8LBitReader = new Vp8LBitReader(currentStream, imageDataSize, memoryAllocator); if (vp8LBitReader.ReadValue(8) != 47) { WebpThrowHelper.ThrowImageFormatException("Invalid VP8L signature"); } uint num = vp8LBitReader.ReadValue(14) + 1; uint num2 = vp8LBitReader.ReadValue(14) + 1; if (num == 0 || num2 == 0) { WebpThrowHelper.ThrowImageFormatException("invalid width or height read"); } vp8LBitReader.ReadBit(); uint num3 = vp8LBitReader.ReadValue(3); if (num3 != 0) { WebpThrowHelper.ThrowNotSupportedException($"Unexpected version number {num3} found in VP8L header"); } return new WebpImageInfo { Width = num, Height = num2, BitsPerPixel = WebpBitsPerPixel.Pixel32, IsLossless = true, Features = features, Vp8LBitReader = vp8LBitReader }; } private void ParseOptionalExtendedChunks(WebpChunkType chunkType, WebpFeatures features) { switch (chunkType) { case WebpChunkType.Iccp: ReadIccProfile(); break; case WebpChunkType.Exif: ReadExifProfile(); break; case WebpChunkType.Xmp: ReadXmpProfile(); break; case WebpChunkType.Alpha: { uint num = ReadChunkSize(); features.AlphaChunkHeader = (byte)currentStream.ReadByte(); int num2 = (int)(num - 1); features.AlphaData = memoryAllocator.Allocate(num2); if (currentStream.Read(features.AlphaData.Memory.Span, 0, num2) != num2) { WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the alpha chunk"); } break; } default: WebpThrowHelper.ThrowImageFormatException("Unexpected chunk followed VP8X header"); break; case WebpChunkType.Animation: break; } } private void ParseOptionalChunks(WebpFeatures features) { if (IgnoreMetadata || (!features.ExifProfile && !features.XmpMetaData)) { return; } long length = currentStream.Length; while (currentStream.Position < length) { WebpChunkType webpChunkType = ReadChunkType(); if (webpChunkType == WebpChunkType.Exif && Metadata.ExifProfile == null) { ReadExifProfile(); continue; } if (webpChunkType == WebpChunkType.Xmp && Metadata.XmpProfile == null) { ReadXmpProfile(); continue; } uint count = ReadChunkSize(); currentStream.Skip((int)count); } } private void ReadExifProfile() { uint num = ReadChunkSize(); if (IgnoreMetadata) { currentStream.Skip((int)num); return; } byte[] data = new byte[num]; if (currentStream.Read(data, 0, (int)num) == num) { ExifProfile exifProfile = new ExifProfile(data); Metadata.ExifProfile = exifProfile; } } private void ReadXmpProfile() { uint num = ReadChunkSize(); if (IgnoreMetadata) { currentStream.Skip((int)num); return; } byte[] data = new byte[num]; if (currentStream.Read(data, 0, (int)num) == num) { XmpProfile xmpProfile = new XmpProfile(data); Metadata.XmpProfile = xmpProfile; } } private void ReadIccProfile() { uint num = ReadChunkSize(); if (IgnoreMetadata) { currentStream.Skip((int)num); return; } byte[] data = new byte[num]; if (currentStream.Read(data, 0, (int)num) != num) { WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the iccp chunk"); } IccProfile iccProfile = new IccProfile(data); if (iccProfile.CheckIsValid()) { Metadata.IccProfile = iccProfile; } } private WebpChunkType ReadChunkType() { if (currentStream.Read(buffer, 0, 4) == 4) { return (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer); } throw new ImageFormatException("Invalid Webp data."); } private uint ReadChunkSize() { if (currentStream.Read(buffer, 0, 4) == 4) { uint num = BinaryPrimitives.ReadUInt32LittleEndian(buffer); if (num % 2 != 0) { return num + 1; } return num; } throw new ImageFormatException("Invalid Webp data."); } private static bool IsOptionalVp8XChunk(WebpChunkType chunkType) { switch (chunkType) { case WebpChunkType.Alpha: case WebpChunkType.Exif: case WebpChunkType.Iccp: case WebpChunkType.Xmp: return true; case WebpChunkType.AnimationParameter: case WebpChunkType.Animation: WebpThrowHelper.ThrowNotSupportedException("Animated webp are not yet supported."); return false; default: return false; } } } public sealed class WebpEncoder : IImageEncoder, IWebpEncoderOptions { public WebpFileFormatType? FileFormat { get; set; } public int Quality { get; set; } = 75; public WebpEncodingMethod Method { get; set; } = WebpEncodingMethod.Level4; public bool UseAlphaCompression { get; set; } = true; public int EntropyPasses { get; set; } = 1; public int SpatialNoiseShaping { get; set; } = 50; public int FilterStrength { get; set; } = 60; public WebpTransparentColorMode TransparentColorMode { get; set; } public bool NearLossless { get; set; } public int NearLosslessQuality { get; set; } = 100; public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { new WebpEncoderCore(this, image.GetMemoryAllocator()).Encode(image, stream); } public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return new WebpEncoderCore(this, image.GetMemoryAllocator()).EncodeAsync(image, stream, cancellationToken); } } internal sealed class WebpEncoderCore : IImageEncoderInternals { private readonly MemoryAllocator memoryAllocator; private readonly bool alphaCompression; private readonly int quality; private readonly WebpEncodingMethod method; private readonly int entropyPasses; private readonly int spatialNoiseShaping; private readonly int filterStrength; private readonly WebpTransparentColorMode transparentColorMode; private readonly bool nearLossless; private readonly int nearLosslessQuality; private readonly WebpFileFormatType? fileFormat; private Configuration configuration; public WebpEncoderCore(IWebpEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; alphaCompression = options.UseAlphaCompression; fileFormat = options.FileFormat; quality = options.Quality; method = options.Method; entropyPasses = options.EntropyPasses; spatialNoiseShaping = options.SpatialNoiseShaping; filterStrength = options.FilterStrength; transparentColorMode = options.TransparentColorMode; nearLossless = options.NearLossless; nearLosslessQuality = options.NearLosslessQuality; } public void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); configuration = image.GetConfiguration(); WebpFileFormatType? webpFileFormatType = fileFormat; if ((!webpFileFormatType.HasValue) ? (image.Metadata.GetWebpMetadata().FileFormat == WebpFileFormatType.Lossless) : (fileFormat == WebpFileFormatType.Lossless)) { using (Vp8LEncoder vp8LEncoder = new Vp8LEncoder(memoryAllocator, configuration, image.Width, image.Height, quality, method, transparentColorMode, nearLossless, nearLosslessQuality)) { vp8LEncoder.Encode(image, stream); return; } } using Vp8Encoder vp8Encoder = new Vp8Encoder(memoryAllocator, configuration, image.Width, image.Height, quality, method, entropyPasses, filterStrength, spatialNoiseShaping, alphaCompression); vp8Encoder.Encode(image, stream); } } public enum WebpEncodingMethod { Level0 = 0, Fastest = 0, Level1 = 1, Level2 = 2, Level3 = 3, Level4 = 4, Default = 4, Level5 = 5, Level6 = 6, BestQuality = 6 } internal class WebpFeatures : IDisposable { public bool IccProfile { get; set; } public bool Alpha { get; set; } public IMemoryOwner AlphaData { get; set; } public byte AlphaChunkHeader { get; set; } public bool ExifProfile { get; set; } public bool XmpMetaData { get; set; } public bool Animation { get; set; } public void Dispose() { AlphaData?.Dispose(); } } public enum WebpFileFormatType { Lossless, Lossy } public sealed class WebpFormat : IImageFormat, IImageFormat { public static WebpFormat Instance { get; } = new WebpFormat(); public string Name => "Webp"; public string DefaultMimeType => "image/webp"; public IEnumerable MimeTypes => WebpConstants.MimeTypes; public IEnumerable FileExtensions => WebpConstants.FileExtensions; private WebpFormat() { } public WebpMetadata CreateDefaultFormatMetadata() { return new WebpMetadata(); } } public sealed class WebpImageFormatDetector : IImageFormatDetector { public int HeaderSize => 12; public IImageFormat DetectFormat(ReadOnlySpan header) { if (!IsSupportedFileFormat(header)) { return null; } return WebpFormat.Instance; } private bool IsSupportedFileFormat(ReadOnlySpan header) { if (header.Length >= HeaderSize && IsRiffContainer(header)) { return IsWebpFile(header); } return false; } private bool IsRiffContainer(ReadOnlySpan header) { return header.Slice(0, 4).SequenceEqual(WebpConstants.RiffFourCc); } private bool IsWebpFile(ReadOnlySpan header) { return header.Slice(8, 4).SequenceEqual(WebpConstants.WebpHeader); } } internal class WebpImageInfo : IDisposable { public uint Width { get; set; } public uint Height { get; set; } public sbyte XScale { get; set; } public sbyte YScale { get; set; } public WebpBitsPerPixel BitsPerPixel { get; set; } public bool IsLossless { get; set; } public WebpFeatures Features { get; set; } public int Vp8Profile { get; set; } = -1; public Vp8FrameHeader Vp8FrameHeader { get; set; } public Vp8LBitReader Vp8LBitReader { get; set; } public Vp8BitReader Vp8BitReader { get; set; } public void Dispose() { Vp8BitReader?.Dispose(); Vp8LBitReader?.Dispose(); Features?.AlphaData?.Dispose(); } } internal static class WebpLookupTables { public static readonly byte[,][] ModesProba; public static readonly ushort[] GammaToLinearTab; public static readonly int[] LinearToGammaTab; public static readonly short[,][] Vp8FixedCostsI4; public static readonly int[] Vp8DspScan; public static readonly short[] Vp8Scan; public static readonly short[] Vp8ScanUv; public static readonly short[] Vp8LevelFixedCosts; public static readonly byte[,] LevelsFromDelta; public static readonly ushort[] Vp8EntropyCost; public static readonly ushort[][] Vp8LevelCodes; public static readonly float[] Log2Table; public static readonly float[] SLog2Table; public static readonly int[] CodeToPlane; public static readonly uint[] PlaneToCodeLut; public static readonly ushort[] AcTable; public static readonly ushort[] AcTable2; public static readonly byte[,,,] CoeffsUpdateProba; public static readonly byte[,,,] DefaultCoeffsProba; public static readonly (int Code, int ExtraBits)[] PrefixEncodeCode; public static readonly uint[] InvAlpha; public static ReadOnlySpan Norm => new byte[128] { 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }; public static ReadOnlySpan NewRange => new byte[128] { 127, 127, 191, 127, 159, 191, 223, 127, 143, 159, 175, 191, 207, 223, 239, 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239, 247, 127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, 183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 127 }; public static ReadOnlySpan LogTable8Bit => new byte[256] { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; public static ReadOnlySpan DcTable => new byte[128] { 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 91, 93, 95, 96, 98, 100, 101, 102, 104, 106, 108, 110, 112, 114, 116, 118, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 143, 145, 148, 151, 154, 157 }; public static ReadOnlySpan PrefixEncodeExtraBitsValue => new byte[512] { 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126 }; private static ReadOnlySpan Abs0Table => new byte[511] { 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, 214, 213, 212, 211, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 195, 194, 193, 192, 191, 190, 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, 159, 158, 157, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 133, 132, 131, 130, 129, 128, 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }; private static ReadOnlySpan Clip1Table => new byte[766] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; private static ReadOnlySpan Sclip1Table => new sbyte[2041] { -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 }; private static ReadOnlySpan Sclip2Table => new sbyte[225] { -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 }; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Abs0(int x) { return Abs0Table[x + 255]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static sbyte Sclip1(int x) { return Sclip1Table[x + 1020]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static sbyte Sclip2(int x) { return Sclip2Table[x + 112]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Clip1(int x) { return Clip1Table[x + 255]; } static WebpLookupTables() { ModesProba = new byte[10, 10][]; GammaToLinearTab = new ushort[256]; LinearToGammaTab = new int[33]; Vp8FixedCostsI4 = new short[10, 10][]; Vp8DspScan = new int[24] { 0, 4, 8, 12, 128, 132, 136, 140, 256, 260, 264, 268, 384, 388, 392, 396, 0, 4, 128, 132, 8, 12, 136, 140 }; Vp8Scan = new short[16] { 0, 4, 8, 12, 128, 132, 136, 140, 256, 260, 264, 268, 384, 388, 392, 396 }; Vp8ScanUv = new short[8] { 0, 4, 128, 132, 8, 12, 136, 140 }; Vp8LevelFixedCosts = new short[2048] { 0, 256, 256, 256, 256, 432, 618, 630, 731, 640, 640, 828, 901, 948, 1021, 1101, 1174, 1221, 1294, 1042, 1085, 1115, 1158, 1202, 1245, 1275, 1318, 1337, 1380, 1410, 1453, 1497, 1540, 1570, 1613, 1280, 1295, 1317, 1332, 1358, 1373, 1395, 1410, 1454, 1469, 1491, 1506, 1532, 1547, 1569, 1584, 1601, 1616, 1638, 1653, 1679, 1694, 1716, 1731, 1775, 1790, 1812, 1827, 1853, 1868, 1890, 1905, 1727, 1733, 1742, 1748, 1759, 1765, 1774, 1780, 1800, 1806, 1815, 1821, 1832, 1838, 1847, 1853, 1878, 1884, 1893, 1899, 1910, 1916, 1925, 1931, 1951, 1957, 1966, 1972, 1983, 1989, 1998, 2004, 2027, 2033, 2042, 2048, 2059, 2065, 2074, 2080, 2100, 2106, 2115, 2121, 2132, 2138, 2147, 2153, 2178, 2184, 2193, 2199, 2210, 2216, 2225, 2231, 2251, 2257, 2266, 2272, 2283, 2289, 2298, 2304, 2168, 2174, 2183, 2189, 2200, 2206, 2215, 2221, 2241, 2247, 2256, 2262, 2273, 2279, 2288, 2294, 2319, 2325, 2334, 2340, 2351, 2357, 2366, 2372, 2392, 2398, 2407, 2413, 2424, 2430, 2439, 2445, 2468, 2474, 2483, 2489, 2500, 2506, 2515, 2521, 2541, 2547, 2556, 2562, 2573, 2579, 2588, 2594, 2619, 2625, 2634, 2640, 2651, 2657, 2666, 2672, 2692, 2698, 2707, 2713, 2724, 2730, 2739, 2745, 2540, 2546, 2555, 2561, 2572, 2578, 2587, 2593, 2613, 2619, 2628, 2634, 2645, 2651, 2660, 2666, 2691, 2697, 2706, 2712, 2723, 2729, 2738, 2744, 2764, 2770, 2779, 2785, 2796, 2802, 2811, 2817, 2840, 2846, 2855, 2861, 2872, 2878, 2887, 2893, 2913, 2919, 2928, 2934, 2945, 2951, 2960, 2966, 2991, 2997, 3006, 3012, 3023, 3029, 3038, 3044, 3064, 3070, 3079, 3085, 3096, 3102, 3111, 3117, 2981, 2987, 2996, 3002, 3013, 3019, 3028, 3034, 3054, 3060, 3069, 3075, 3086, 3092, 3101, 3107, 3132, 3138, 3147, 3153, 3164, 3170, 3179, 3185, 3205, 3211, 3220, 3226, 3237, 3243, 3252, 3258, 3281, 3287, 3296, 3302, 3313, 3319, 3328, 3334, 3354, 3360, 3369, 3375, 3386, 3392, 3401, 3407, 3432, 3438, 3447, 3453, 3464, 3470, 3479, 3485, 3505, 3511, 3520, 3526, 3537, 3543, 3552, 3558, 2816, 2822, 2831, 2837, 2848, 2854, 2863, 2869, 2889, 2895, 2904, 2910, 2921, 2927, 2936, 2942, 2967, 2973, 2982, 2988, 2999, 3005, 3014, 3020, 3040, 3046, 3055, 3061, 3072, 3078, 3087, 3093, 3116, 3122, 3131, 3137, 3148, 3154, 3163, 3169, 3189, 3195, 3204, 3210, 3221, 3227, 3236, 3242, 3267, 3273, 3282, 3288, 3299, 3305, 3314, 3320, 3340, 3346, 3355, 3361, 3372, 3378, 3387, 3393, 3257, 3263, 3272, 3278, 3289, 3295, 3304, 3310, 3330, 3336, 3345, 3351, 3362, 3368, 3377, 3383, 3408, 3414, 3423, 3429, 3440, 3446, 3455, 3461, 3481, 3487, 3496, 3502, 3513, 3519, 3528, 3534, 3557, 3563, 3572, 3578, 3589, 3595, 3604, 3610, 3630, 3636, 3645, 3651, 3662, 3668, 3677, 3683, 3708, 3714, 3723, 3729, 3740, 3746, 3755, 3761, 3781, 3787, 3796, 3802, 3813, 3819, 3828, 3834, 3629, 3635, 3644, 3650, 3661, 3667, 3676, 3682, 3702, 3708, 3717, 3723, 3734, 3740, 3749, 3755, 3780, 3786, 3795, 3801, 3812, 3818, 3827, 3833, 3853, 3859, 3868, 3874, 3885, 3891, 3900, 3906, 3929, 3935, 3944, 3950, 3961, 3967, 3976, 3982, 4002, 4008, 4017, 4023, 4034, 4040, 4049, 4055, 4080, 4086, 4095, 4101, 4112, 4118, 4127, 4133, 4153, 4159, 4168, 4174, 4185, 4191, 4200, 4206, 4070, 4076, 4085, 4091, 4102, 4108, 4117, 4123, 4143, 4149, 4158, 4164, 4175, 4181, 4190, 4196, 4221, 4227, 4236, 4242, 4253, 4259, 4268, 4274, 4294, 4300, 4309, 4315, 4326, 4332, 4341, 4347, 4370, 4376, 4385, 4391, 4402, 4408, 4417, 4423, 4443, 4449, 4458, 4464, 4475, 4481, 4490, 4496, 4521, 4527, 4536, 4542, 4553, 4559, 4568, 4574, 4594, 4600, 4609, 4615, 4626, 4632, 4641, 4647, 3515, 3521, 3530, 3536, 3547, 3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620, 3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698, 3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771, 3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847, 3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920, 3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998, 4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071, 4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988, 3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061, 4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139, 4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212, 4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288, 4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361, 4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439, 4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512, 4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360, 4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433, 4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511, 4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584, 4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660, 4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733, 4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811, 4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884, 4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801, 4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874, 4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952, 4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025, 5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101, 5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174, 5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252, 5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325, 5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636, 4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709, 4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787, 4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860, 4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936, 4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009, 5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087, 5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160, 5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077, 5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150, 5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228, 5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301, 5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377, 5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450, 5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528, 5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601, 5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449, 5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522, 5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600, 5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673, 5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749, 5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822, 5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900, 5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973, 5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890, 5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963, 5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041, 6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114, 6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190, 6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263, 6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341, 6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414, 6420, 6429, 6435, 3515, 3521, 3530, 3536, 3547, 3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620, 3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698, 3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771, 3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847, 3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920, 3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998, 4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071, 4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988, 3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061, 4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139, 4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212, 4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288, 4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361, 4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439, 4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512, 4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360, 4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433, 4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511, 4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584, 4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660, 4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733, 4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811, 4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884, 4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801, 4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874, 4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952, 4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025, 5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101, 5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174, 5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252, 5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325, 5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636, 4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709, 4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787, 4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860, 4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936, 4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009, 5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087, 5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160, 5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077, 5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150, 5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228, 5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301, 5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377, 5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450, 5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528, 5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601, 5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449, 5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522, 5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600, 5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673, 5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749, 5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822, 5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900, 5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973, 5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890, 5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963, 5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041, 6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114, 6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190, 6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263, 6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341, 6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414, 6420, 6429, 6435, 5303, 5309, 5318, 5324, 5335, 5341, 5350, 5356, 5376, 5382, 5391, 5397, 5408, 5414, 5423, 5429, 5454, 5460, 5469, 5475, 5486, 5492, 5501, 5507, 5527, 5533, 5542, 5548, 5559, 5565, 5574, 5580, 5603, 5609, 5618, 5624, 5635, 5641, 5650, 5656, 5676, 5682, 5691, 5697, 5708, 5714, 5723, 5729, 5754, 5760, 5769, 5775, 5786, 5792, 5801, 5807, 5827, 5833, 5842, 5848, 5859, 5865, 5874, 5880, 5744, 5750, 5759, 5765, 5776, 5782, 5791, 5797, 5817, 5823, 5832, 5838, 5849, 5855, 5864, 5870, 5895, 5901, 5910, 5916, 5927, 5933, 5942, 5948, 5968, 5974, 5983, 5989, 6000, 6006, 6015, 6021, 6044, 6050, 6059, 6065, 6076, 6082, 6091, 6097, 6117, 6123, 6132, 6138, 6149, 6155, 6164, 6170, 6195, 6201, 6210, 6216, 6227, 6233, 6242, 6248, 6268, 6274, 6283, 6289, 6300, 6306, 6315, 6321, 6116, 6122, 6131, 6137, 6148, 6154, 6163, 6169, 6189, 6195, 6204, 6210, 6221, 6227, 6236, 6242, 6267, 6273, 6282, 6288, 6299, 6305, 6314, 6320, 6340, 6346, 6355, 6361, 6372, 6378, 6387, 6393, 6416, 6422, 6431, 6437, 6448, 6454, 6463, 6469, 6489, 6495, 6504, 6510, 6521, 6527, 6536, 6542, 6567, 6573, 6582, 6588, 6599, 6605, 6614, 6620, 6640, 6646, 6655, 6661, 6672, 6678, 6687, 6693, 6557, 6563, 6572, 6578, 6589, 6595, 6604, 6610, 6630, 6636, 6645, 6651, 6662, 6668, 6677, 6683, 6708, 6714, 6723, 6729, 6740, 6746, 6755, 6761, 6781, 6787, 6796, 6802, 6813, 6819, 6828, 6834, 6857, 6863, 6872, 6878, 6889, 6895, 6904, 6910, 6930, 6936, 6945, 6951, 6962, 6968, 6977, 6983, 7008, 7014, 7023, 7029, 7040, 7046, 7055, 7061, 7081, 7087, 7096, 7102, 7113, 7119, 7128, 7134, 6392, 6398, 6407, 6413, 6424, 6430, 6439, 6445, 6465, 6471, 6480, 6486, 6497, 6503, 6512, 6518, 6543, 6549, 6558, 6564, 6575, 6581, 6590, 6596, 6616, 6622, 6631, 6637, 6648, 6654, 6663, 6669, 6692, 6698, 6707, 6713, 6724, 6730, 6739, 6745, 6765, 6771, 6780, 6786, 6797, 6803, 6812, 6818, 6843, 6849, 6858, 6864, 6875, 6881, 6890, 6896, 6916, 6922, 6931, 6937, 6948, 6954, 6963, 6969, 6833, 6839, 6848, 6854, 6865, 6871, 6880, 6886, 6906, 6912, 6921, 6927, 6938, 6944, 6953, 6959, 6984, 6990, 6999, 7005, 7016, 7022, 7031, 7037, 7057, 7063, 7072, 7078, 7089, 7095, 7104, 7110, 7133, 7139, 7148, 7154, 7165, 7171, 7180, 7186, 7206, 7212, 7221, 7227, 7238, 7244, 7253, 7259, 7284, 7290, 7299, 7305, 7316, 7322, 7331, 7337, 7357, 7363, 7372, 7378, 7389, 7395, 7404, 7410, 7205, 7211, 7220, 7226, 7237, 7243, 7252, 7258, 7278, 7284, 7293, 7299, 7310, 7316, 7325, 7331, 7356, 7362, 7371, 7377, 7388, 7394, 7403, 7409, 7429, 7435, 7444, 7450, 7461, 7467, 7476, 7482, 7505, 7511, 7520, 7526, 7537, 7543, 7552, 7558, 7578, 7584, 7593, 7599, 7610, 7616, 7625, 7631, 7656, 7662, 7671, 7677, 7688, 7694, 7703, 7709, 7729, 7735, 7744, 7750, 7761 }; LevelsFromDelta = new byte[8, 64] { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 } }; Vp8EntropyCost = new ushort[256] { 1792, 1792, 1792, 1536, 1536, 1408, 1366, 1280, 1280, 1216, 1178, 1152, 1110, 1076, 1061, 1024, 1024, 992, 968, 951, 939, 911, 896, 878, 871, 854, 838, 820, 811, 794, 786, 768, 768, 752, 740, 732, 720, 709, 704, 690, 683, 672, 666, 655, 647, 640, 631, 622, 615, 607, 598, 592, 586, 576, 572, 564, 559, 555, 547, 541, 534, 528, 522, 512, 512, 504, 500, 494, 488, 483, 477, 473, 467, 461, 458, 452, 448, 443, 438, 434, 427, 424, 419, 415, 410, 406, 403, 399, 394, 390, 384, 384, 377, 374, 370, 366, 362, 359, 355, 351, 347, 342, 342, 336, 333, 330, 326, 323, 320, 316, 312, 308, 305, 302, 299, 296, 293, 288, 287, 283, 280, 277, 274, 272, 268, 266, 262, 256, 256, 256, 251, 248, 245, 242, 240, 237, 234, 232, 228, 226, 223, 221, 218, 216, 214, 211, 208, 205, 203, 201, 198, 196, 192, 191, 188, 187, 183, 181, 179, 176, 175, 171, 171, 168, 165, 163, 160, 159, 156, 154, 152, 150, 148, 146, 144, 142, 139, 138, 135, 133, 131, 128, 128, 125, 123, 121, 119, 117, 115, 113, 111, 110, 107, 105, 103, 102, 100, 98, 96, 94, 92, 91, 89, 86, 86, 83, 82, 80, 77, 76, 74, 73, 71, 69, 67, 66, 64, 63, 61, 59, 57, 55, 54, 52, 51, 49, 47, 46, 44, 43, 41, 40, 38, 36, 35, 33, 32, 30, 29, 27, 25, 24, 22, 21, 19, 18, 16, 15, 13, 12, 10, 9, 7, 6, 4, 3 }; Vp8LevelCodes = new ushort[67][] { new ushort[2] { 1, 0 }, new ushort[2] { 7, 1 }, new ushort[2] { 15, 5 }, new ushort[2] { 15, 13 }, new ushort[2] { 51, 3 }, new ushort[2] { 51, 3 }, new ushort[2] { 51, 35 }, new ushort[2] { 51, 35 }, new ushort[2] { 51, 35 }, new ushort[2] { 51, 35 }, new ushort[2] { 211, 19 }, new ushort[2] { 211, 19 }, new ushort[2] { 211, 19 }, new ushort[2] { 211, 19 }, new ushort[2] { 211, 19 }, new ushort[2] { 211, 19 }, new ushort[2] { 211, 19 }, new ushort[2] { 211, 19 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 211, 147 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 83 }, new ushort[2] { 339, 339 } }; Log2Table = new float[256] { 0f, 0f, 1f, 1.5849625f, 2f, 2.321928f, 2.5849626f, 2.807355f, 3f, 3.169925f, 3.321928f, 3.4594316f, 3.5849626f, 3.7004397f, 3.807355f, 3.9068906f, 4f, 4.087463f, 4.169925f, 4.2479277f, 4.321928f, 4.3923173f, 4.4594316f, 4.523562f, 4.5849624f, 4.643856f, 4.70044f, 4.7548876f, 4.807355f, 4.857981f, 4.9068904f, 4.9541965f, 5f, 5.044394f, 5.087463f, 5.129283f, 5.169925f, 5.2094536f, 5.2479277f, 5.2854023f, 5.321928f, 5.357552f, 5.3923173f, 5.426265f, 5.4594316f, 5.491853f, 5.523562f, 5.554589f, 5.5849624f, 5.61471f, 5.643856f, 5.6724253f, 5.70044f, 5.7279205f, 5.7548876f, 5.7813597f, 5.807355f, 5.83289f, 5.857981f, 5.882643f, 5.9068904f, 5.9307375f, 5.9541965f, 5.97728f, 6f, 6.022368f, 6.044394f, 6.066089f, 6.087463f, 6.1085243f, 6.129283f, 6.149747f, 6.169925f, 6.1898246f, 6.2094536f, 6.228819f, 6.2479277f, 6.2667866f, 6.2854023f, 6.3037806f, 6.321928f, 6.33985f, 6.357552f, 6.3750396f, 6.3923173f, 6.409391f, 6.426265f, 6.4429436f, 6.4594316f, 6.4757333f, 6.491853f, 6.507795f, 6.523562f, 6.539159f, 6.554589f, 6.5698557f, 6.5849624f, 6.5999126f, 6.61471f, 6.6293564f, 6.643856f, 6.6582117f, 6.6724253f, 6.6865005f, 6.70044f, 6.7142453f, 6.7279205f, 6.741467f, 6.7548876f, 6.768184f, 6.7813597f, 6.794416f, 6.807355f, 6.820179f, 6.83289f, 6.84549f, 6.857981f, 6.8703647f, 6.882643f, 6.894818f, 6.9068904f, 6.9188633f, 6.9307375f, 6.9425144f, 6.9541965f, 6.965784f, 6.97728f, 6.9886847f, 7f, 7.011227f, 7.022368f, 7.033423f, 7.044394f, 7.0552826f, 7.066089f, 7.0768156f, 7.087463f, 7.098032f, 7.1085243f, 7.1189413f, 7.129283f, 7.139551f, 7.149747f, 7.159871f, 7.169925f, 7.179909f, 7.1898246f, 7.199672f, 7.2094536f, 7.2191687f, 7.228819f, 7.2384048f, 7.2479277f, 7.2573876f, 7.2667866f, 7.2761245f, 7.2854023f, 7.2946205f, 7.3037806f, 7.312883f, 7.321928f, 7.330917f, 7.33985f, 7.348728f, 7.357552f, 7.366322f, 7.3750396f, 7.383704f, 7.3923173f, 7.4008794f, 7.409391f, 7.4178524f, 7.426265f, 7.434628f, 7.4429436f, 7.451211f, 7.4594316f, 7.4676056f, 7.4757333f, 7.4838157f, 7.491853f, 7.499846f, 7.507795f, 7.5157f, 7.523562f, 7.5313816f, 7.539159f, 7.5468946f, 7.554589f, 7.5622425f, 7.5698557f, 7.577429f, 7.5849624f, 7.592457f, 7.5999126f, 7.6073303f, 7.61471f, 7.6220517f, 7.6293564f, 7.636625f, 7.643856f, 7.6510515f, 7.6582117f, 7.665336f, 7.6724253f, 7.67948f, 7.6865005f, 7.693487f, 7.70044f, 7.7073593f, 7.7142453f, 7.7210994f, 7.7279205f, 7.7347097f, 7.741467f, 7.748193f, 7.7548876f, 7.7615514f, 7.768184f, 7.774787f, 7.7813597f, 7.7879024f, 7.794416f, 7.8009f, 7.807355f, 7.8137813f, 7.820179f, 7.8265486f, 7.83289f, 7.839204f, 7.84549f, 7.851749f, 7.857981f, 7.8641863f, 7.8703647f, 7.876517f, 7.882643f, 7.8887434f, 7.894818f, 7.900867f, 7.9068904f, 7.9128895f, 7.9188633f, 7.9248123f, 7.9307375f, 7.936638f, 7.9425144f, 7.948367f, 7.9541965f, 7.960002f, 7.965784f, 7.971544f, 7.97728f, 7.9829936f, 7.9886847f, 7.9943533f }; SLog2Table = new float[256] { 0f, 0f, 2f, 4.7548876f, 8f, 11.60964f, 15.509775f, 19.651484f, 24f, 28.529325f, 33.21928f, 38.05375f, 43.01955f, 48.105717f, 53.302967f, 58.60336f, 64f, 69.48687f, 75.05865f, 80.710625f, 86.43856f, 92.23866f, 98.1075f, 104.04192f, 110.0391f, 116.096405f, 122.21143f, 128.38196f, 134.60594f, 140.88145f, 147.20671f, 153.58008f, 160f, 166.46501f, 172.97374f, 179.5249f, 186.1173f, 192.74977f, 199.42125f, 206.13069f, 212.87712f, 219.65964f, 226.47733f, 233.32939f, 240.215f, 247.13339f, 254.08385f, 261.06567f, 268.0782f, 275.1208f, 282.1928f, 289.2937f, 296.42285f, 303.57977f, 310.76392f, 317.9748f, 325.21188f, 332.47473f, 339.7629f, 347.07593f, 354.41342f, 361.77496f, 369.16016f, 376.56863f, 384f, 391.45392f, 398.93002f, 406.42798f, 413.94748f, 421.4882f, 429.0498f, 436.63205f, 444.2346f, 451.85718f, 459.49954f, 467.1614f, 474.8425f, 482.54257f, 490.26138f, 497.9987f, 505.75424f, 513.52783f, 521.3193f, 529.1283f, 536.95465f, 544.7982f, 552.65875f, 560.5361f, 568.43f, 576.3403f, 584.2668f, 592.2093f, 600.1677f, 608.1418f, 616.13135f, 624.1363f, 632.1564f, 640.1915f, 648.2416f, 656.30634f, 664.3856f, 672.4794f, 680.5874f, 688.70953f, 696.8457f, 704.9958f, 713.15955f, 721.337f, 729.52783f, 737.7321f, 745.9496f, 754.1802f, 762.42377f, 770.68024f, 778.94946f, 787.2314f, 795.5258f, 803.8327f, 812.15186f, 820.48334f, 828.82684f, 837.18243f, 845.5499f, 853.92926f, 862.3203f, 870.723f, 879.13727f, 887.5629f, 896f, 904.4483f, 912.90784f, 921.3784f, 929.86005f, 938.35254f, 946.85596f, 955.3701f, 963.89496f, 972.4304f, 980.9764f, 989.53284f, 998.0996f, 1006.67676f, 1015.2641f, 1023.8616f, 1032.4692f, 1041.0868f, 1049.7144f, 1058.3518f, 1066.9991f, 1075.6561f, 1084.3228f, 1092.9991f, 1101.6849f, 1110.3804f, 1119.0851f, 1127.7993f, 1136.5227f, 1145.2555f, 1153.9973f, 1162.7484f, 1171.5085f, 1180.2776f, 1189.0557f, 1197.8427f, 1206.6385f, 1215.4431f, 1224.2566f, 1233.0786f, 1241.9093f, 1250.7487f, 1259.5964f, 1268.4528f, 1277.3175f, 1286.1907f, 1295.0721f, 1303.9619f, 1312.86f, 1321.7662f, 1330.6805f, 1339.603f, 1348.5336f, 1357.472f, 1366.4186f, 1375.373f, 1384.3354f, 1393.3055f, 1402.2836f, 1411.2693f, 1420.2627f, 1429.2638f, 1438.2726f, 1447.289f, 1456.3127f, 1465.3442f, 1474.383f, 1483.4294f, 1492.4832f, 1501.5442f, 1510.6127f, 1519.6884f, 1528.7712f, 1537.8615f, 1546.9587f, 1556.0632f, 1565.1748f, 1574.2935f, 1583.4191f, 1592.5518f, 1601.6914f, 1610.838f, 1619.9916f, 1629.152f, 1638.3191f, 1647.4932f, 1656.674f, 1665.8615f, 1675.0557f, 1684.2566f, 1693.4642f, 1702.6783f, 1711.8992f, 1721.1265f, 1730.3604f, 1739.6007f, 1748.8475f, 1758.1007f, 1767.3605f, 1776.6265f, 1785.8989f, 1795.1776f, 1804.4628f, 1813.754f, 1823.0516f, 1832.3553f, 1841.6654f, 1850.9814f, 1860.3037f, 1869.6322f, 1878.9667f, 1888.3071f, 1897.6537f, 1907.0063f, 1916.3649f, 1925.7295f, 1935.0999f, 1944.4763f, 1953.8585f, 1963.2467f, 1972.6406f, 1982.0405f, 1991.446f, 2000.8574f, 2010.2745f, 2019.6974f, 2029.1259f, 2038.5602f }; CodeToPlane = new int[120] { 24, 7, 23, 25, 40, 6, 39, 41, 22, 26, 38, 42, 56, 5, 55, 57, 21, 27, 54, 58, 37, 43, 72, 4, 71, 73, 20, 28, 53, 59, 70, 74, 36, 44, 88, 69, 75, 52, 60, 3, 87, 89, 19, 29, 86, 90, 35, 45, 68, 76, 85, 91, 51, 61, 104, 2, 103, 105, 18, 30, 102, 106, 34, 46, 84, 92, 67, 77, 101, 107, 50, 62, 120, 1, 119, 121, 83, 93, 17, 31, 100, 108, 66, 78, 118, 122, 33, 47, 117, 123, 49, 63, 99, 109, 82, 94, 0, 116, 124, 65, 79, 16, 32, 98, 110, 48, 115, 125, 81, 95, 64, 114, 126, 97, 111, 80, 113, 127, 96, 112 }; PlaneToCodeLut = new uint[128] { 96u, 73u, 55u, 39u, 23u, 13u, 5u, 1u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 101u, 78u, 58u, 42u, 26u, 16u, 8u, 2u, 0u, 3u, 9u, 17u, 27u, 43u, 59u, 79u, 102u, 86u, 62u, 46u, 32u, 20u, 10u, 6u, 4u, 7u, 11u, 21u, 33u, 47u, 63u, 87u, 105u, 90u, 70u, 52u, 37u, 28u, 18u, 14u, 12u, 15u, 19u, 29u, 38u, 53u, 71u, 91u, 110u, 99u, 82u, 66u, 48u, 35u, 30u, 24u, 22u, 25u, 31u, 36u, 49u, 67u, 83u, 100u, 115u, 108u, 94u, 76u, 64u, 50u, 44u, 40u, 34u, 41u, 45u, 51u, 65u, 77u, 95u, 109u, 118u, 113u, 103u, 92u, 80u, 68u, 60u, 56u, 54u, 57u, 61u, 69u, 81u, 93u, 104u, 114u, 119u, 116u, 111u, 106u, 97u, 88u, 84u, 74u, 72u, 75u, 85u, 89u, 98u, 107u, 112u, 117u }; AcTable = new ushort[128] { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 119, 122, 125, 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 161, 164, 167, 170, 173, 177, 181, 185, 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 234, 239, 245, 249, 254, 259, 264, 269, 274, 279, 284 }; AcTable2 = new ushort[128] { 8, 8, 9, 10, 12, 13, 15, 17, 18, 20, 21, 23, 24, 26, 27, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 62, 63, 65, 66, 68, 69, 71, 72, 74, 75, 77, 79, 80, 82, 83, 85, 86, 88, 89, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 124, 127, 130, 133, 136, 139, 142, 145, 148, 151, 155, 158, 161, 164, 167, 170, 173, 176, 179, 184, 189, 193, 198, 203, 207, 212, 217, 221, 226, 230, 235, 240, 244, 249, 254, 258, 263, 268, 274, 280, 286, 292, 299, 305, 311, 317, 323, 330, 336, 342, 348, 354, 362, 370, 379, 385, 393, 401, 409, 416, 424, 432, 440 }; CoeffsUpdateProba = new byte[4, 8, 3, 11] { { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 }, { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 }, { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } } }, { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 }, { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 } }, { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } } }, { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 }, { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 }, { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 } }, { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } } }, { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 }, { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 } }, { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }, { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } } } }; DefaultCoeffsProba = new byte[4, 8, 3, 11] { { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } }, { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 }, { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 }, { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 } }, { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 }, { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 }, { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 } }, { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 }, { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 }, { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 } }, { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 }, { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 }, { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 } }, { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 }, { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 }, { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 } }, { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 }, { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 }, { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 } }, { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } } }, { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 }, { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 }, { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 } }, { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 }, { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 }, { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 } }, { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 }, { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 }, { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 } }, { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 }, { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 }, { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 } }, { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 }, { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 }, { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 } }, { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 }, { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 }, { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 } }, { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 }, { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 }, { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 } }, { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 }, { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 } } }, { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 }, { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 }, { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 } }, { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 }, { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 }, { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 } }, { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 }, { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 }, { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 } }, { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 }, { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 }, { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 } }, { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 }, { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 }, { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } }, { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 } }, { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 }, { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 }, { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 } }, { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 } } }, { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 }, { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 }, { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 } }, { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 }, { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 }, { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 } }, { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 }, { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 }, { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 } }, { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 }, { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 }, { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 } }, { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 }, { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 }, { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 } }, { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 }, { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 }, { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 } }, { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 }, { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 }, { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 } }, { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }, { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 } } } }; PrefixEncodeCode = new(int, int)[512] { (0, 0), (0, 0), (1, 0), (2, 0), (3, 0), (4, 1), (4, 1), (5, 1), (5, 1), (6, 2), (6, 2), (6, 2), (6, 2), (7, 2), (7, 2), (7, 2), (7, 2), (8, 3), (8, 3), (8, 3), (8, 3), (8, 3), (8, 3), (8, 3), (8, 3), (9, 3), (9, 3), (9, 3), (9, 3), (9, 3), (9, 3), (9, 3), (9, 3), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (10, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (11, 4), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (12, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (13, 5), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (14, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (15, 6), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (16, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7), (17, 7) }; InvAlpha = new uint[1021] { 0u, 524288u, 262144u, 174762u, 131072u, 104857u, 87381u, 74898u, 65536u, 58254u, 52428u, 47662u, 43690u, 40329u, 37449u, 34952u, 32768u, 30840u, 29127u, 27594u, 26214u, 24966u, 23831u, 22795u, 21845u, 20971u, 20164u, 19418u, 18724u, 18078u, 17476u, 16912u, 16384u, 15887u, 15420u, 14979u, 14563u, 14169u, 13797u, 13443u, 13107u, 12787u, 12483u, 12192u, 11915u, 11650u, 11397u, 11155u, 10922u, 10699u, 10485u, 10280u, 10082u, 9892u, 9709u, 9532u, 9362u, 9198u, 9039u, 8886u, 8738u, 8594u, 8456u, 8322u, 8192u, 8065u, 7943u, 7825u, 7710u, 7598u, 7489u, 7384u, 7281u, 7182u, 7084u, 6990u, 6898u, 6808u, 6721u, 6636u, 6553u, 6472u, 6393u, 6316u, 6241u, 6168u, 6096u, 6026u, 5957u, 5890u, 5825u, 5761u, 5698u, 5637u, 5577u, 5518u, 5461u, 5405u, 5349u, 5295u, 5242u, 5190u, 5140u, 5090u, 5041u, 4993u, 4946u, 4899u, 4854u, 4809u, 4766u, 4723u, 4681u, 4639u, 4599u, 4559u, 4519u, 4481u, 4443u, 4405u, 4369u, 4332u, 4297u, 4262u, 4228u, 4194u, 4161u, 4128u, 4096u, 4064u, 4032u, 4002u, 3971u, 3942u, 3912u, 3883u, 3855u, 3826u, 3799u, 3771u, 3744u, 3718u, 3692u, 3666u, 3640u, 3615u, 3591u, 3566u, 3542u, 3518u, 3495u, 3472u, 3449u, 3426u, 3404u, 3382u, 3360u, 3339u, 3318u, 3297u, 3276u, 3256u, 3236u, 3216u, 3196u, 3177u, 3158u, 3139u, 3120u, 3102u, 3084u, 3066u, 3048u, 3030u, 3013u, 2995u, 2978u, 2962u, 2945u, 2928u, 2912u, 2896u, 2880u, 2864u, 2849u, 2833u, 2818u, 2803u, 2788u, 2774u, 2759u, 2744u, 2730u, 2716u, 2702u, 2688u, 2674u, 2661u, 2647u, 2634u, 2621u, 2608u, 2595u, 2582u, 2570u, 2557u, 2545u, 2532u, 2520u, 2508u, 2496u, 2484u, 2473u, 2461u, 2449u, 2438u, 2427u, 2416u, 2404u, 2394u, 2383u, 2372u, 2361u, 2351u, 2340u, 2330u, 2319u, 2309u, 2299u, 2289u, 2279u, 2269u, 2259u, 2250u, 2240u, 2231u, 2221u, 2212u, 2202u, 2193u, 2184u, 2175u, 2166u, 2157u, 2148u, 2139u, 2131u, 2122u, 2114u, 2105u, 2097u, 2088u, 2080u, 2072u, 2064u, 2056u, 2048u, 2040u, 2032u, 2024u, 2016u, 2008u, 2001u, 1993u, 1985u, 1978u, 1971u, 1963u, 1956u, 1949u, 1941u, 1934u, 1927u, 1920u, 1913u, 1906u, 1899u, 1892u, 1885u, 1879u, 1872u, 1865u, 1859u, 1852u, 1846u, 1839u, 1833u, 1826u, 1820u, 1814u, 1807u, 1801u, 1795u, 1789u, 1783u, 1777u, 1771u, 1765u, 1759u, 1753u, 1747u, 1741u, 1736u, 1730u, 1724u, 1718u, 1713u, 1707u, 1702u, 1696u, 1691u, 1685u, 1680u, 1675u, 1669u, 1664u, 1659u, 1653u, 1648u, 1643u, 1638u, 1633u, 1628u, 1623u, 1618u, 1613u, 1608u, 1603u, 1598u, 1593u, 1588u, 1583u, 1579u, 1574u, 1569u, 1565u, 1560u, 1555u, 1551u, 1546u, 1542u, 1537u, 1533u, 1528u, 1524u, 1519u, 1515u, 1510u, 1506u, 1502u, 1497u, 1493u, 1489u, 1485u, 1481u, 1476u, 1472u, 1468u, 1464u, 1460u, 1456u, 1452u, 1448u, 1444u, 1440u, 1436u, 1432u, 1428u, 1424u, 1420u, 1416u, 1413u, 1409u, 1405u, 1401u, 1398u, 1394u, 1390u, 1387u, 1383u, 1379u, 1376u, 1372u, 1368u, 1365u, 1361u, 1358u, 1354u, 1351u, 1347u, 1344u, 1340u, 1337u, 1334u, 1330u, 1327u, 1323u, 1320u, 1317u, 1314u, 1310u, 1307u, 1304u, 1300u, 1297u, 1294u, 1291u, 1288u, 1285u, 1281u, 1278u, 1275u, 1272u, 1269u, 1266u, 1263u, 1260u, 1257u, 1254u, 1251u, 1248u, 1245u, 1242u, 1239u, 1236u, 1233u, 1230u, 1227u, 1224u, 1222u, 1219u, 1216u, 1213u, 1210u, 1208u, 1205u, 1202u, 1199u, 1197u, 1194u, 1191u, 1188u, 1186u, 1183u, 1180u, 1178u, 1175u, 1172u, 1170u, 1167u, 1165u, 1162u, 1159u, 1157u, 1154u, 1152u, 1149u, 1147u, 1144u, 1142u, 1139u, 1137u, 1134u, 1132u, 1129u, 1127u, 1125u, 1122u, 1120u, 1117u, 1115u, 1113u, 1110u, 1108u, 1106u, 1103u, 1101u, 1099u, 1096u, 1094u, 1092u, 1089u, 1087u, 1085u, 1083u, 1081u, 1078u, 1076u, 1074u, 1072u, 1069u, 1067u, 1065u, 1063u, 1061u, 1059u, 1057u, 1054u, 1052u, 1050u, 1048u, 1046u, 1044u, 1042u, 1040u, 1038u, 1036u, 1034u, 1032u, 1030u, 1028u, 1026u, 1024u, 1022u, 1020u, 1018u, 1016u, 1014u, 1012u, 1010u, 1008u, 1006u, 1004u, 1002u, 1000u, 998u, 996u, 994u, 992u, 991u, 989u, 987u, 985u, 983u, 981u, 979u, 978u, 976u, 974u, 972u, 970u, 969u, 967u, 965u, 963u, 961u, 960u, 958u, 956u, 954u, 953u, 951u, 949u, 948u, 946u, 944u, 942u, 941u, 939u, 937u, 936u, 934u, 932u, 931u, 929u, 927u, 926u, 924u, 923u, 921u, 919u, 918u, 916u, 914u, 913u, 911u, 910u, 908u, 907u, 905u, 903u, 902u, 900u, 899u, 897u, 896u, 894u, 893u, 891u, 890u, 888u, 887u, 885u, 884u, 882u, 881u, 879u, 878u, 876u, 875u, 873u, 872u, 870u, 869u, 868u, 866u, 865u, 863u, 862u, 860u, 859u, 858u, 856u, 855u, 853u, 852u, 851u, 849u, 848u, 846u, 845u, 844u, 842u, 841u, 840u, 838u, 837u, 836u, 834u, 833u, 832u, 830u, 829u, 828u, 826u, 825u, 824u, 823u, 821u, 820u, 819u, 817u, 816u, 815u, 814u, 812u, 811u, 810u, 809u, 807u, 806u, 805u, 804u, 802u, 801u, 800u, 799u, 798u, 796u, 795u, 794u, 793u, 791u, 790u, 789u, 788u, 787u, 786u, 784u, 783u, 782u, 781u, 780u, 779u, 777u, 776u, 775u, 774u, 773u, 772u, 771u, 769u, 768u, 767u, 766u, 765u, 764u, 763u, 762u, 760u, 759u, 758u, 757u, 756u, 755u, 754u, 753u, 752u, 751u, 750u, 748u, 747u, 746u, 745u, 744u, 743u, 742u, 741u, 740u, 739u, 738u, 737u, 736u, 735u, 734u, 733u, 732u, 731u, 730u, 729u, 728u, 727u, 726u, 725u, 724u, 723u, 722u, 721u, 720u, 719u, 718u, 717u, 716u, 715u, 714u, 713u, 712u, 711u, 710u, 709u, 708u, 707u, 706u, 705u, 704u, 703u, 702u, 701u, 700u, 699u, 699u, 698u, 697u, 696u, 695u, 694u, 693u, 692u, 691u, 690u, 689u, 688u, 688u, 687u, 686u, 685u, 684u, 683u, 682u, 681u, 680u, 680u, 679u, 678u, 677u, 676u, 675u, 674u, 673u, 673u, 672u, 671u, 670u, 669u, 668u, 667u, 667u, 666u, 665u, 664u, 663u, 662u, 661u, 661u, 660u, 659u, 658u, 657u, 657u, 656u, 655u, 654u, 653u, 652u, 652u, 651u, 650u, 649u, 648u, 648u, 647u, 646u, 645u, 644u, 644u, 643u, 642u, 641u, 640u, 640u, 639u, 638u, 637u, 637u, 636u, 635u, 634u, 633u, 633u, 632u, 631u, 630u, 630u, 629u, 628u, 627u, 627u, 626u, 625u, 624u, 624u, 623u, 622u, 621u, 621u, 620u, 619u, 618u, 618u, 617u, 616u, 616u, 615u, 614u, 613u, 613u, 612u, 611u, 611u, 610u, 609u, 608u, 608u, 607u, 606u, 606u, 605u, 604u, 604u, 603u, 602u, 601u, 601u, 600u, 599u, 599u, 598u, 597u, 597u, 596u, 595u, 595u, 594u, 593u, 593u, 592u, 591u, 591u, 590u, 589u, 589u, 588u, 587u, 587u, 586u, 585u, 585u, 584u, 583u, 583u, 582u, 581u, 581u, 580u, 579u, 579u, 578u, 578u, 577u, 576u, 576u, 575u, 574u, 574u, 573u, 572u, 572u, 571u, 571u, 570u, 569u, 569u, 568u, 568u, 567u, 566u, 566u, 565u, 564u, 564u, 563u, 563u, 562u, 561u, 561u, 560u, 560u, 559u, 558u, 558u, 557u, 557u, 556u, 555u, 555u, 554u, 554u, 553u, 553u, 552u, 551u, 551u, 550u, 550u, 549u, 548u, 548u, 547u, 547u, 546u, 546u, 545u, 544u, 544u, 543u, 543u, 542u, 542u, 541u, 541u, 540u, 539u, 539u, 538u, 538u, 537u, 537u, 536u, 536u, 535u, 534u, 534u, 533u, 533u, 532u, 532u, 531u, 531u, 530u, 530u, 529u, 529u, 528u, 527u, 527u, 526u, 526u, 525u, 525u, 524u, 524u, 523u, 523u, 522u, 522u, 521u, 521u, 520u, 520u, 519u, 519u, 518u, 518u, 517u, 517u, 516u, 516u, 515u, 515u, 514u, 514u }; double num = 0.03125763125763126; double num2 = 1.0 / 255.0; int num3 = 0; while (num3 < 256) { GammaToLinearTab[num3] = (ushort)(Math.Pow(num2 * (double)num3, 0.8) * 4095.0 + 0.5); int num4 = num3 + 1; num3 = num4; } int num5 = 0; while (num5 <= 32) { LinearToGammaTab[num5] = (int)(255.0 * Math.Pow(num * (double)num5, 1.25) + 0.5); int num4 = num5 + 1; num5 = num4; } InitializeModesProbabilities(); InitializeFixedCostsI4(); } private static void InitializeModesProbabilities() { ModesProba[0, 0] = new byte[9] { 231, 120, 48, 89, 115, 113, 120, 152, 112 }; ModesProba[0, 1] = new byte[9] { 152, 179, 64, 126, 170, 118, 46, 70, 95 }; ModesProba[0, 2] = new byte[9] { 175, 69, 143, 80, 85, 82, 72, 155, 103 }; ModesProba[0, 3] = new byte[9] { 56, 58, 10, 171, 218, 189, 17, 13, 152 }; ModesProba[0, 4] = new byte[9] { 114, 26, 17, 163, 44, 195, 21, 10, 173 }; ModesProba[0, 5] = new byte[9] { 121, 24, 80, 195, 26, 62, 44, 64, 85 }; ModesProba[0, 6] = new byte[9] { 144, 71, 10, 38, 171, 213, 144, 34, 26 }; ModesProba[0, 7] = new byte[9] { 170, 46, 55, 19, 136, 160, 33, 206, 71 }; ModesProba[0, 8] = new byte[9] { 63, 20, 8, 114, 114, 208, 12, 9, 226 }; ModesProba[0, 9] = new byte[9] { 81, 40, 11, 96, 182, 84, 29, 16, 36 }; ModesProba[1, 0] = new byte[9] { 134, 183, 89, 137, 98, 101, 106, 165, 148 }; ModesProba[1, 1] = new byte[9] { 72, 187, 100, 130, 157, 111, 32, 75, 80 }; ModesProba[1, 2] = new byte[9] { 66, 102, 167, 99, 74, 62, 40, 234, 128 }; ModesProba[1, 3] = new byte[9] { 41, 53, 9, 178, 241, 141, 26, 8, 107 }; ModesProba[1, 4] = new byte[9] { 74, 43, 26, 146, 73, 166, 49, 23, 157 }; ModesProba[1, 5] = new byte[9] { 65, 38, 105, 160, 51, 52, 31, 115, 128 }; ModesProba[1, 6] = new byte[9] { 104, 79, 12, 27, 217, 255, 87, 17, 7 }; ModesProba[1, 7] = new byte[9] { 87, 68, 71, 44, 114, 51, 15, 186, 23 }; ModesProba[1, 8] = new byte[9] { 47, 41, 14, 110, 182, 183, 21, 17, 194 }; ModesProba[1, 9] = new byte[9] { 66, 45, 25, 102, 197, 189, 23, 18, 22 }; ModesProba[2, 0] = new byte[9] { 88, 88, 147, 150, 42, 46, 45, 196, 205 }; ModesProba[2, 1] = new byte[9] { 43, 97, 183, 117, 85, 38, 35, 179, 61 }; ModesProba[2, 2] = new byte[9] { 39, 53, 200, 87, 26, 21, 43, 232, 171 }; ModesProba[2, 3] = new byte[9] { 56, 34, 51, 104, 114, 102, 29, 93, 77 }; ModesProba[2, 4] = new byte[9] { 39, 28, 85, 171, 58, 165, 90, 98, 64 }; ModesProba[2, 5] = new byte[9] { 34, 22, 116, 206, 23, 34, 43, 166, 73 }; ModesProba[2, 6] = new byte[9] { 107, 54, 32, 26, 51, 1, 81, 43, 31 }; ModesProba[2, 7] = new byte[9] { 68, 25, 106, 22, 64, 171, 36, 225, 114 }; ModesProba[2, 8] = new byte[9] { 34, 19, 21, 102, 132, 188, 16, 76, 124 }; ModesProba[2, 9] = new byte[9] { 62, 18, 78, 95, 85, 57, 50, 48, 51 }; ModesProba[3, 0] = new byte[9] { 193, 101, 35, 159, 215, 111, 89, 46, 111 }; ModesProba[3, 1] = new byte[9] { 60, 148, 31, 172, 219, 228, 21, 18, 111 }; ModesProba[3, 2] = new byte[9] { 112, 113, 77, 85, 179, 255, 38, 120, 114 }; ModesProba[3, 3] = new byte[9] { 40, 42, 1, 196, 245, 209, 10, 25, 109 }; ModesProba[3, 4] = new byte[9] { 88, 43, 29, 140, 166, 213, 37, 43, 154 }; ModesProba[3, 5] = new byte[9] { 61, 63, 30, 155, 67, 45, 68, 1, 209 }; ModesProba[3, 6] = new byte[9] { 100, 80, 8, 43, 154, 1, 51, 26, 71 }; ModesProba[3, 7] = new byte[9] { 142, 78, 78, 16, 255, 128, 34, 197, 171 }; ModesProba[3, 8] = new byte[9] { 41, 40, 5, 102, 211, 183, 4, 1, 221 }; ModesProba[3, 9] = new byte[9] { 51, 50, 17, 168, 209, 192, 23, 25, 82 }; ModesProba[4, 0] = new byte[9] { 138, 31, 36, 171, 27, 166, 38, 44, 229 }; ModesProba[4, 1] = new byte[9] { 67, 87, 58, 169, 82, 115, 26, 59, 179 }; ModesProba[4, 2] = new byte[9] { 63, 59, 90, 180, 59, 166, 93, 73, 154 }; ModesProba[4, 3] = new byte[9] { 40, 40, 21, 116, 143, 209, 34, 39, 175 }; ModesProba[4, 4] = new byte[9] { 47, 15, 16, 183, 34, 223, 49, 45, 183 }; ModesProba[4, 5] = new byte[9] { 46, 17, 33, 183, 6, 98, 15, 32, 183 }; ModesProba[4, 6] = new byte[9] { 57, 46, 22, 24, 128, 1, 54, 17, 37 }; ModesProba[4, 7] = new byte[9] { 65, 32, 73, 115, 28, 128, 23, 128, 205 }; ModesProba[4, 8] = new byte[9] { 40, 3, 9, 115, 51, 192, 18, 6, 223 }; ModesProba[4, 9] = new byte[9] { 87, 37, 9, 115, 59, 77, 64, 21, 47 }; ModesProba[5, 0] = new byte[9] { 104, 55, 44, 218, 9, 54, 53, 130, 226 }; ModesProba[5, 1] = new byte[9] { 64, 90, 70, 205, 40, 41, 23, 26, 57 }; ModesProba[5, 2] = new byte[9] { 54, 57, 112, 184, 5, 41, 38, 166, 213 }; ModesProba[5, 3] = new byte[9] { 30, 34, 26, 133, 152, 116, 10, 32, 134 }; ModesProba[5, 4] = new byte[9] { 39, 19, 53, 221, 26, 114, 32, 73, 255 }; ModesProba[5, 5] = new byte[9] { 31, 9, 65, 234, 2, 15, 1, 118, 73 }; ModesProba[5, 6] = new byte[9] { 75, 32, 12, 51, 192, 255, 160, 43, 51 }; ModesProba[5, 7] = new byte[9] { 88, 31, 35, 67, 102, 85, 55, 186, 85 }; ModesProba[5, 8] = new byte[9] { 56, 21, 23, 111, 59, 205, 45, 37, 192 }; ModesProba[5, 9] = new byte[9] { 55, 38, 70, 124, 73, 102, 1, 34, 98 }; ModesProba[6, 0] = new byte[9] { 125, 98, 42, 88, 104, 85, 117, 175, 82 }; ModesProba[6, 1] = new byte[9] { 95, 84, 53, 89, 128, 100, 113, 101, 45 }; ModesProba[6, 2] = new byte[9] { 75, 79, 123, 47, 51, 128, 81, 171, 1 }; ModesProba[6, 3] = new byte[9] { 57, 17, 5, 71, 102, 57, 53, 41, 49 }; ModesProba[6, 4] = new byte[9] { 38, 33, 13, 121, 57, 73, 26, 1, 85 }; ModesProba[6, 5] = new byte[9] { 41, 10, 67, 138, 77, 110, 90, 47, 114 }; ModesProba[6, 6] = new byte[9] { 115, 21, 2, 10, 102, 255, 166, 23, 6 }; ModesProba[6, 7] = new byte[9] { 101, 29, 16, 10, 85, 128, 101, 196, 26 }; ModesProba[6, 8] = new byte[9] { 57, 18, 10, 102, 102, 213, 34, 20, 43 }; ModesProba[6, 9] = new byte[9] { 117, 20, 15, 36, 163, 128, 68, 1, 26 }; ModesProba[7, 0] = new byte[9] { 102, 61, 71, 37, 34, 53, 31, 243, 192 }; ModesProba[7, 1] = new byte[9] { 69, 60, 71, 38, 73, 119, 28, 222, 37 }; ModesProba[7, 2] = new byte[9] { 68, 45, 128, 34, 1, 47, 11, 245, 171 }; ModesProba[7, 3] = new byte[9] { 62, 17, 19, 70, 146, 85, 55, 62, 70 }; ModesProba[7, 4] = new byte[9] { 37, 43, 37, 154, 100, 163, 85, 160, 1 }; ModesProba[7, 5] = new byte[9] { 63, 9, 92, 136, 28, 64, 32, 201, 85 }; ModesProba[7, 6] = new byte[9] { 75, 15, 9, 9, 64, 255, 184, 119, 16 }; ModesProba[7, 7] = new byte[9] { 86, 6, 28, 5, 64, 255, 25, 248, 1 }; ModesProba[7, 8] = new byte[9] { 56, 8, 17, 132, 137, 255, 55, 116, 128 }; ModesProba[7, 9] = new byte[9] { 58, 15, 20, 82, 135, 57, 26, 121, 40 }; ModesProba[8, 0] = new byte[9] { 164, 50, 31, 137, 154, 133, 25, 35, 218 }; ModesProba[8, 1] = new byte[9] { 51, 103, 44, 131, 131, 123, 31, 6, 158 }; ModesProba[8, 2] = new byte[9] { 86, 40, 64, 135, 148, 224, 45, 183, 128 }; ModesProba[8, 3] = new byte[9] { 22, 26, 17, 131, 240, 154, 14, 1, 209 }; ModesProba[8, 4] = new byte[9] { 45, 16, 21, 91, 64, 222, 7, 1, 197 }; ModesProba[8, 5] = new byte[9] { 56, 21, 39, 155, 60, 138, 23, 102, 213 }; ModesProba[8, 6] = new byte[9] { 83, 12, 13, 54, 192, 255, 68, 47, 28 }; ModesProba[8, 7] = new byte[9] { 85, 26, 85, 85, 128, 128, 32, 146, 171 }; ModesProba[8, 8] = new byte[9] { 18, 11, 7, 63, 144, 171, 4, 4, 246 }; ModesProba[8, 9] = new byte[9] { 35, 27, 10, 146, 174, 171, 12, 26, 128 }; ModesProba[9, 0] = new byte[9] { 190, 80, 35, 99, 180, 80, 126, 54, 45 }; ModesProba[9, 1] = new byte[9] { 85, 126, 47, 87, 176, 51, 41, 20, 32 }; ModesProba[9, 2] = new byte[9] { 101, 75, 128, 139, 118, 146, 116, 128, 85 }; ModesProba[9, 3] = new byte[9] { 56, 41, 15, 176, 236, 85, 37, 9, 62 }; ModesProba[9, 4] = new byte[9] { 71, 30, 17, 119, 118, 255, 17, 18, 138 }; ModesProba[9, 5] = new byte[9] { 101, 38, 60, 138, 55, 70, 43, 26, 142 }; ModesProba[9, 6] = new byte[9] { 146, 36, 19, 30, 171, 255, 97, 27, 20 }; ModesProba[9, 7] = new byte[9] { 138, 45, 61, 62, 219, 1, 81, 188, 64 }; ModesProba[9, 8] = new byte[9] { 32, 41, 20, 117, 151, 142, 20, 21, 163 }; ModesProba[9, 9] = new byte[9] { 112, 19, 12, 61, 195, 128, 48, 4, 24 }; } private static void InitializeFixedCostsI4() { Vp8FixedCostsI4[0, 0] = new short[10] { 40, 1151, 1723, 1874, 2103, 2019, 1628, 1777, 2226, 2137 }; Vp8FixedCostsI4[0, 1] = new short[10] { 192, 469, 1296, 1308, 1849, 1794, 1781, 1703, 1713, 1522 }; Vp8FixedCostsI4[0, 2] = new short[10] { 142, 910, 762, 1684, 1849, 1576, 1460, 1305, 1801, 1657 }; Vp8FixedCostsI4[0, 3] = new short[10] { 559, 641, 1370, 421, 1182, 1569, 1612, 1725, 863, 1007 }; Vp8FixedCostsI4[0, 4] = new short[10] { 299, 1059, 1256, 1108, 636, 1068, 1581, 1883, 869, 1142 }; Vp8FixedCostsI4[0, 5] = new short[10] { 277, 1111, 707, 1362, 1089, 672, 1603, 1541, 1545, 1291 }; Vp8FixedCostsI4[0, 6] = new short[10] { 214, 781, 1609, 1303, 1632, 2229, 726, 1560, 1713, 918 }; Vp8FixedCostsI4[0, 7] = new short[10] { 152, 1037, 1046, 1759, 1983, 2174, 1358, 742, 1740, 1390 }; Vp8FixedCostsI4[0, 8] = new short[10] { 512, 1046, 1420, 753, 752, 1297, 1486, 1613, 460, 1207 }; Vp8FixedCostsI4[0, 9] = new short[10] { 424, 827, 1362, 719, 1462, 1202, 1199, 1476, 1199, 538 }; Vp8FixedCostsI4[1, 0] = new short[10] { 240, 402, 1134, 1491, 1659, 1505, 1517, 1555, 1979, 2099 }; Vp8FixedCostsI4[1, 1] = new short[10] { 467, 242, 960, 1232, 1714, 1620, 1834, 1570, 1676, 1391 }; Vp8FixedCostsI4[1, 2] = new short[10] { 500, 455, 463, 1507, 1699, 1282, 1564, 982, 2114, 2114 }; Vp8FixedCostsI4[1, 3] = new short[10] { 672, 643, 1372, 331, 1589, 1667, 1453, 1938, 996, 876 }; Vp8FixedCostsI4[1, 4] = new short[10] { 458, 783, 1037, 911, 738, 968, 1165, 1518, 859, 1033 }; Vp8FixedCostsI4[1, 5] = new short[10] { 504, 815, 504, 1139, 1219, 719, 1506, 1085, 1268, 1268 }; Vp8FixedCostsI4[1, 6] = new short[10] { 333, 630, 1445, 1239, 1883, 3672, 799, 1548, 1865, 598 }; Vp8FixedCostsI4[1, 7] = new short[10] { 399, 644, 746, 1342, 1856, 1350, 1493, 613, 1855, 1015 }; Vp8FixedCostsI4[1, 8] = new short[10] { 622, 749, 1205, 608, 1066, 1408, 1290, 1406, 546, 971 }; Vp8FixedCostsI4[1, 9] = new short[10] { 500, 753, 1041, 668, 1230, 1617, 1297, 1425, 1383, 523 }; Vp8FixedCostsI4[2, 0] = new short[10] { 394, 553, 523, 1502, 1536, 981, 1608, 1142, 1666, 2181 }; Vp8FixedCostsI4[2, 1] = new short[10] { 655, 430, 375, 1411, 1861, 1220, 1677, 1135, 1978, 1553 }; Vp8FixedCostsI4[2, 2] = new short[10] { 690, 640, 245, 1954, 2070, 1194, 1528, 982, 1972, 2232 }; Vp8FixedCostsI4[2, 3] = new short[10] { 559, 834, 741, 867, 1131, 980, 1225, 852, 1092, 784 }; Vp8FixedCostsI4[2, 4] = new short[10] { 690, 875, 516, 959, 673, 894, 1056, 1190, 1528, 1126 }; Vp8FixedCostsI4[2, 5] = new short[10] { 740, 951, 384, 1277, 1177, 492, 1579, 1155, 1846, 1513 }; Vp8FixedCostsI4[2, 6] = new short[10] { 323, 775, 1062, 1776, 3062, 1274, 813, 1188, 1372, 655 }; Vp8FixedCostsI4[2, 7] = new short[10] { 488, 971, 484, 1767, 1515, 1775, 1115, 503, 1539, 1461 }; Vp8FixedCostsI4[2, 8] = new short[10] { 740, 1006, 998, 709, 851, 1230, 1337, 788, 741, 721 }; Vp8FixedCostsI4[2, 9] = new short[10] { 522, 1073, 573, 1045, 1346, 887, 1046, 1146, 1203, 697 }; Vp8FixedCostsI4[3, 0] = new short[10] { 105, 864, 1442, 1009, 1934, 1840, 1519, 1920, 1673, 1579 }; Vp8FixedCostsI4[3, 1] = new short[10] { 534, 305, 1193, 683, 1388, 2164, 1802, 1894, 1264, 1170 }; Vp8FixedCostsI4[3, 2] = new short[10] { 305, 518, 877, 1108, 1426, 3215, 1425, 1064, 1320, 1242 }; Vp8FixedCostsI4[3, 3] = new short[10] { 683, 732, 1927, 257, 1493, 2048, 1858, 1552, 1055, 947 }; Vp8FixedCostsI4[3, 4] = new short[10] { 394, 814, 1024, 660, 959, 1556, 1282, 1289, 893, 1047 }; Vp8FixedCostsI4[3, 5] = new short[10] { 528, 615, 996, 940, 1201, 635, 1094, 2515, 803, 1358 }; Vp8FixedCostsI4[3, 6] = new short[10] { 347, 614, 1609, 1187, 3133, 1345, 1007, 1339, 1017, 667 }; Vp8FixedCostsI4[3, 7] = new short[10] { 218, 740, 878, 1605, 3650, 3650, 1345, 758, 1357, 1617 }; Vp8FixedCostsI4[3, 8] = new short[10] { 672, 750, 1541, 558, 1257, 1599, 1870, 2135, 402, 1087 }; Vp8FixedCostsI4[3, 9] = new short[10] { 592, 684, 1161, 430, 1092, 1497, 1475, 1489, 1095, 822 }; Vp8FixedCostsI4[4, 0] = new short[10] { 228, 1056, 1059, 1368, 752, 982, 1512, 1518, 987, 1782 }; Vp8FixedCostsI4[4, 1] = new short[10] { 494, 514, 818, 942, 965, 892, 1610, 1356, 1048, 1363 }; Vp8FixedCostsI4[4, 2] = new short[10] { 512, 648, 591, 1042, 761, 991, 1196, 1454, 1309, 1463 }; Vp8FixedCostsI4[4, 3] = new short[10] { 683, 749, 1043, 676, 841, 1396, 1133, 1138, 654, 939 }; Vp8FixedCostsI4[4, 4] = new short[10] { 622, 1101, 1126, 994, 361, 1077, 1203, 1318, 877, 1219 }; Vp8FixedCostsI4[4, 5] = new short[10] { 631, 1068, 857, 1650, 651, 477, 1650, 1419, 828, 1170 }; Vp8FixedCostsI4[4, 6] = new short[10] { 555, 727, 1068, 1335, 3127, 1339, 820, 1331, 1077, 429 }; Vp8FixedCostsI4[4, 7] = new short[10] { 504, 879, 624, 1398, 889, 889, 1392, 808, 891, 1406 }; Vp8FixedCostsI4[4, 8] = new short[10] { 683, 1602, 1289, 977, 578, 983, 1280, 1708, 406, 1122 }; Vp8FixedCostsI4[4, 9] = new short[10] { 399, 865, 1433, 1070, 1072, 764, 968, 1477, 1223, 678 }; Vp8FixedCostsI4[5, 0] = new short[10] { 333, 760, 935, 1638, 1010, 529, 1646, 1410, 1472, 2219 }; Vp8FixedCostsI4[5, 1] = new short[10] { 512, 494, 750, 1160, 1215, 610, 1870, 1868, 1628, 1169 }; Vp8FixedCostsI4[5, 2] = new short[10] { 572, 646, 492, 1934, 1208, 603, 1580, 1099, 1398, 1995 }; Vp8FixedCostsI4[5, 3] = new short[10] { 786, 789, 942, 581, 1018, 951, 1599, 1207, 731, 768 }; Vp8FixedCostsI4[5, 4] = new short[10] { 690, 1015, 672, 1078, 582, 504, 1693, 1438, 1108, 2897 }; Vp8FixedCostsI4[5, 5] = new short[10] { 768, 1267, 571, 2005, 1243, 244, 2881, 1380, 1786, 1453 }; Vp8FixedCostsI4[5, 6] = new short[10] { 452, 899, 1293, 903, 1311, 3100, 465, 1311, 1319, 813 }; Vp8FixedCostsI4[5, 7] = new short[10] { 394, 927, 942, 1103, 1358, 1104, 946, 593, 1363, 1109 }; Vp8FixedCostsI4[5, 8] = new short[10] { 559, 1005, 1007, 1016, 658, 1173, 1021, 1164, 623, 1028 }; Vp8FixedCostsI4[5, 9] = new short[10] { 564, 796, 632, 1005, 1014, 863, 2316, 1268, 938, 764 }; Vp8FixedCostsI4[6, 0] = new short[10] { 266, 606, 1098, 1228, 1497, 1243, 948, 1030, 1734, 1461 }; Vp8FixedCostsI4[6, 1] = new short[10] { 366, 585, 901, 1060, 1407, 1247, 876, 1134, 1620, 1054 }; Vp8FixedCostsI4[6, 2] = new short[10] { 452, 565, 542, 1729, 1479, 1479, 1016, 886, 2938, 1150 }; Vp8FixedCostsI4[6, 3] = new short[10] { 555, 1088, 1533, 950, 1354, 895, 834, 1019, 1021, 496 }; Vp8FixedCostsI4[6, 4] = new short[10] { 704, 815, 1193, 971, 973, 640, 1217, 2214, 832, 578 }; Vp8FixedCostsI4[6, 5] = new short[10] { 672, 1245, 579, 871, 875, 774, 872, 1273, 1027, 949 }; Vp8FixedCostsI4[6, 6] = new short[10] { 296, 1134, 2050, 1784, 1636, 3425, 442, 1550, 2076, 722 }; Vp8FixedCostsI4[6, 7] = new short[10] { 342, 982, 1259, 1846, 1848, 1848, 622, 568, 1847, 1052 }; Vp8FixedCostsI4[6, 8] = new short[10] { 555, 1064, 1304, 828, 746, 1343, 1075, 1329, 1078, 494 }; Vp8FixedCostsI4[6, 9] = new short[10] { 288, 1167, 1285, 1174, 1639, 1639, 833, 2254, 1304, 509 }; Vp8FixedCostsI4[7, 0] = new short[10] { 342, 719, 767, 1866, 1757, 1270, 1246, 550, 1746, 2151 }; Vp8FixedCostsI4[7, 1] = new short[10] { 483, 653, 694, 1509, 1459, 1410, 1218, 507, 1914, 1266 }; Vp8FixedCostsI4[7, 2] = new short[10] { 488, 757, 447, 2979, 1813, 1268, 1654, 539, 1849, 2109 }; Vp8FixedCostsI4[7, 3] = new short[10] { 522, 1097, 1085, 851, 1365, 1111, 851, 901, 961, 605 }; Vp8FixedCostsI4[7, 4] = new short[10] { 709, 716, 841, 728, 736, 945, 941, 862, 2845, 1057 }; Vp8FixedCostsI4[7, 5] = new short[10] { 512, 1323, 500, 1336, 1083, 681, 1342, 717, 1604, 1350 }; Vp8FixedCostsI4[7, 6] = new short[10] { 452, 1155, 1372, 1900, 1501, 3290, 311, 944, 1919, 922 }; Vp8FixedCostsI4[7, 7] = new short[10] { 403, 1520, 977, 2132, 1733, 3522, 1076, 276, 3335, 1547 }; Vp8FixedCostsI4[7, 8] = new short[10] { 559, 1374, 1101, 615, 673, 2462, 974, 795, 984, 984 }; Vp8FixedCostsI4[7, 9] = new short[10] { 547, 1122, 1062, 812, 1410, 951, 1140, 622, 1268, 651 }; Vp8FixedCostsI4[8, 0] = new short[10] { 165, 982, 1235, 938, 1334, 1366, 1659, 1578, 964, 1612 }; Vp8FixedCostsI4[8, 1] = new short[10] { 592, 422, 925, 847, 1139, 1112, 1387, 2036, 861, 1041 }; Vp8FixedCostsI4[8, 2] = new short[10] { 403, 837, 732, 770, 941, 1658, 1250, 809, 1407, 1407 }; Vp8FixedCostsI4[8, 3] = new short[10] { 896, 874, 1071, 381, 1568, 1722, 1437, 2192, 480, 1035 }; Vp8FixedCostsI4[8, 4] = new short[10] { 640, 1098, 1012, 1032, 684, 1382, 1581, 2106, 416, 865 }; Vp8FixedCostsI4[8, 5] = new short[10] { 559, 1005, 819, 914, 710, 770, 1418, 920, 838, 1435 }; Vp8FixedCostsI4[8, 6] = new short[10] { 415, 1258, 1245, 870, 1278, 3067, 770, 1021, 1287, 522 }; Vp8FixedCostsI4[8, 7] = new short[10] { 406, 990, 601, 1009, 1265, 1265, 1267, 759, 1017, 1277 }; Vp8FixedCostsI4[8, 8] = new short[10] { 968, 1182, 1329, 788, 1032, 1292, 1705, 1714, 203, 1403 }; Vp8FixedCostsI4[8, 9] = new short[10] { 732, 877, 1279, 471, 901, 1161, 1545, 1294, 755, 755 }; Vp8FixedCostsI4[9, 0] = new short[10] { 111, 931, 1378, 1185, 1933, 1648, 1148, 1714, 1873, 1307 }; Vp8FixedCostsI4[9, 1] = new short[10] { 406, 414, 1030, 1023, 1910, 1404, 1313, 1647, 1509, 793 }; Vp8FixedCostsI4[9, 2] = new short[10] { 342, 640, 575, 1088, 1241, 1349, 1161, 1350, 1756, 1502 }; Vp8FixedCostsI4[9, 3] = new short[10] { 559, 766, 1185, 357, 1682, 1428, 1329, 1897, 1219, 802 }; Vp8FixedCostsI4[9, 4] = new short[10] { 473, 909, 1164, 771, 719, 2508, 1427, 1432, 722, 782 }; Vp8FixedCostsI4[9, 5] = new short[10] { 342, 892, 785, 1145, 1150, 794, 1296, 1550, 973, 1057 }; Vp8FixedCostsI4[9, 6] = new short[10] { 208, 1036, 1326, 1343, 1606, 3395, 815, 1455, 1618, 712 }; Vp8FixedCostsI4[9, 7] = new short[10] { 228, 928, 890, 1046, 3499, 1711, 994, 829, 1720, 1318 }; Vp8FixedCostsI4[9, 8] = new short[10] { 768, 724, 1058, 636, 991, 1075, 1319, 1324, 616, 825 }; Vp8FixedCostsI4[9, 9] = new short[10] { 305, 1167, 1358, 899, 1587, 1587, 987, 1988, 1332, 501 }; } } public class WebpMetadata : IDeepCloneable { public WebpFileFormatType? FileFormat { get; set; } public WebpMetadata() { } private WebpMetadata(WebpMetadata other) { FileFormat = other.FileFormat; } public IDeepCloneable DeepClone() { return new WebpMetadata(this); } } internal static class WebpThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidImageContentException(string errorMessage) { throw new InvalidImageContentException(errorMessage); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowImageFormatException(string errorMessage) { throw new ImageFormatException(errorMessage); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotSupportedException(string errorMessage) { throw new NotSupportedException(errorMessage); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidImageDimensions(string errorMessage) { throw new InvalidImageContentException(errorMessage); } } public enum WebpTransparentColorMode { Clear, Preserve } } namespace SixLabors.ImageSharp.Formats.Webp.Lossy { internal enum IntraPredictionMode { DcPrediction, TrueMotion, VPrediction, HPrediction } internal enum LoopFilter { None, Simple, Complex } internal static class LossyUtils { private static readonly Vector128 Mean16x4Mask = Vector128.Create((short)255).AsByte(); private static readonly Vector128 SignBit = Vector128.Create((byte)128); private static readonly Vector128 Three = Vector128.Create((byte)3).AsSByte(); private static readonly Vector128 FourShort = Vector128.Create((short)4); private static readonly Vector128 FourSByte = Vector128.Create((byte)4).AsSByte(); private static readonly Vector128 Nine = Vector128.Create((short)2304).AsSByte(); private static readonly Vector128 SixtyThree = Vector128.Create((short)63).AsSByte(); private static readonly Vector128 SixtyFour = Vector128.Create((byte)64).AsSByte(); private static readonly Vector128 K1 = Vector128.Create((short)20091); private static readonly Vector128 K2 = Vector128.Create((short)(-30068)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Vp8_Sse16X16(Span a, Span b) { if (Avx2.IsSupported) { return Vp8_Sse16xN_Avx2(a, b, 4); } if (Sse2.IsSupported) { return Vp8_Sse16xN_Sse2(a, b, 8); } return Vp8_SseNxN(a, b, 16, 16); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Vp8_Sse16X8(Span a, Span b) { if (Avx2.IsSupported) { return Vp8_Sse16xN_Avx2(a, b, 2); } if (Sse2.IsSupported) { return Vp8_Sse16xN_Sse2(a, b, 4); } return Vp8_SseNxN(a, b, 16, 8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Vp8_Sse4X4(Span a, Span b) { if (Avx2.IsSupported) { ref byte reference = ref MemoryMarshal.GetReference(a); ref byte reference2 = ref MemoryMarshal.GetReference(b); Vector256 vector = Vector256.Create(Unsafe.As>(ref reference), Unsafe.As>(ref Unsafe.Add(ref reference, 32))); Vector256 vector2 = Vector256.Create(Unsafe.As>(ref Unsafe.Add(ref reference, 64)), Unsafe.As>(ref Unsafe.Add(ref reference, 96))); Vector256 vector3 = Vector256.Create(Unsafe.As>(ref reference2), Unsafe.As>(ref Unsafe.Add(ref reference2, 32))); Vector256 vector4 = Vector256.Create(Unsafe.As>(ref Unsafe.Add(ref reference2, 64)), Unsafe.As>(ref Unsafe.Add(ref reference2, 96))); Vector256 vector5 = Avx2.UnpackLow(vector.AsInt32(), vector2.AsInt32()); Vector256 vector6 = Avx2.UnpackLow(vector3.AsInt32(), vector4.AsInt32()); Vector256 left = Avx2.UnpackLow(vector5.AsByte(), Vector256.Zero); Vector256 right = Avx2.UnpackLow(vector6.AsByte(), Vector256.Zero); Vector256 vector7 = Avx2.SubtractSaturate(left, right); return Numerics.ReduceSum(Avx2.MultiplyAddAdjacent(vector7.AsInt16(), vector7.AsInt16())); } if (Sse2.IsSupported) { ref byte reference3 = ref MemoryMarshal.GetReference(a); ref byte reference4 = ref MemoryMarshal.GetReference(b); Vector128 vector8 = Unsafe.As>(ref reference3); Vector128 vector9 = Unsafe.As>(ref Unsafe.Add(ref reference3, 32)); Vector128 vector10 = Unsafe.As>(ref Unsafe.Add(ref reference3, 64)); Vector128 vector11 = Unsafe.As>(ref Unsafe.Add(ref reference3, 96)); Vector128 vector12 = Unsafe.As>(ref reference4); Vector128 vector13 = Unsafe.As>(ref Unsafe.Add(ref reference4, 32)); Vector128 vector14 = Unsafe.As>(ref Unsafe.Add(ref reference4, 64)); Vector128 vector15 = Unsafe.As>(ref Unsafe.Add(ref reference4, 96)); Vector128 vector16 = Sse2.UnpackLow(vector8.AsInt32(), vector9.AsInt32()); Vector128 vector17 = Sse2.UnpackLow(vector10.AsInt32(), vector11.AsInt32()); Vector128 vector18 = Sse2.UnpackLow(vector12.AsInt32(), vector13.AsInt32()); Vector128 vector19 = Sse2.UnpackLow(vector14.AsInt32(), vector15.AsInt32()); Vector128 left2 = Sse2.UnpackLow(vector16.AsByte(), Vector128.Zero); Vector128 left3 = Sse2.UnpackLow(vector17.AsByte(), Vector128.Zero); Vector128 right2 = Sse2.UnpackLow(vector18.AsByte(), Vector128.Zero); Vector128 right3 = Sse2.UnpackLow(vector19.AsByte(), Vector128.Zero); Vector128 vector20 = Sse2.SubtractSaturate(left2, right2); Vector128 vector21 = Sse2.SubtractSaturate(left3, right3); Vector128 left4 = Sse2.MultiplyAddAdjacent(vector20.AsInt16(), vector20.AsInt16()); Vector128 right4 = Sse2.MultiplyAddAdjacent(vector21.AsInt16(), vector21.AsInt16()); return Numerics.ReduceSum(Sse2.Add(left4, right4)); } return Vp8_SseNxN(a, b, 4, 4); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Vp8_SseNxN(Span a, Span b, int w, int h) { int num = 0; int num2 = 0; int num3 = 0; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { int num4 = a[num2 + j] - b[num3 + j]; num += num4 * num4; } num2 += 32; num3 += 32; } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Vp8_Sse16xN_Sse2(Span a, Span b, int numPairs) { Vector128 vector = Vector128.Zero; nint num = 0; ref byte reference = ref MemoryMarshal.GetReference(a); ref byte reference2 = ref MemoryMarshal.GetReference(b); for (int i = 0; i < numPairs; i++) { Vector128 a2 = Unsafe.As>(ref Unsafe.Add(ref reference, num)); Vector128 b2 = Unsafe.As>(ref Unsafe.Add(ref reference2, num)); Vector128 a3 = Unsafe.As>(ref Unsafe.Add(ref reference, num + 32)); Vector128 b3 = Unsafe.As>(ref Unsafe.Add(ref reference2, num + 32)); Vector128 left = SubtractAndAccumulate(a2, b2); Vector128 right = SubtractAndAccumulate(a3, b3); vector = Sse2.Add(vector, Sse2.Add(left, right)); num += 64; } return Numerics.ReduceSum(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Vp8_Sse16xN_Avx2(Span a, Span b, int numPairs) { Vector256 vector = Vector256.Zero; nint num = 0; ref byte reference = ref MemoryMarshal.GetReference(a); ref byte reference2 = ref MemoryMarshal.GetReference(b); for (int i = 0; i < numPairs; i++) { Vector256 a2 = Vector256.Create(Unsafe.As>(ref Unsafe.Add(ref reference, num)), Unsafe.As>(ref Unsafe.Add(ref reference, num + 32))); Vector256 b2 = Vector256.Create(Unsafe.As>(ref Unsafe.Add(ref reference2, num)), Unsafe.As>(ref Unsafe.Add(ref reference2, num + 32))); Vector256 a3 = Vector256.Create(Unsafe.As>(ref Unsafe.Add(ref reference, num + 64)), Unsafe.As>(ref Unsafe.Add(ref reference, num + 96))); Vector256 b3 = Vector256.Create(Unsafe.As>(ref Unsafe.Add(ref reference2, num + 64)), Unsafe.As>(ref Unsafe.Add(ref reference2, num + 96))); Vector256 left = SubtractAndAccumulate(a2, b2); Vector256 right = SubtractAndAccumulate(a3, b3); vector = Avx2.Add(vector, Avx2.Add(left, right)); num += 128; } return Numerics.ReduceSum(vector); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 SubtractAndAccumulate(Vector128 a, Vector128 b) { Vector128 left = Sse2.SubtractSaturate(a, b); Vector128 right = Sse2.SubtractSaturate(b, a); Vector128 left2 = Sse2.Or(left, right); Vector128 vector = Sse2.UnpackLow(left2, Vector128.Zero); Vector128 vector2 = Sse2.UnpackHigh(left2, Vector128.Zero); Vector128 left3 = Sse2.MultiplyAddAdjacent(vector.AsInt16(), vector.AsInt16()); Vector128 right2 = Sse2.MultiplyAddAdjacent(vector2.AsInt16(), vector2.AsInt16()); return Sse2.Add(left3, right2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector256 SubtractAndAccumulate(Vector256 a, Vector256 b) { Vector256 left = Avx2.SubtractSaturate(a, b); Vector256 right = Avx2.SubtractSaturate(b, a); Vector256 left2 = Avx2.Or(left, right); Vector256 vector = Avx2.UnpackLow(left2, Vector256.Zero); Vector256 vector2 = Avx2.UnpackHigh(left2, Vector256.Zero); Vector256 left3 = Avx2.MultiplyAddAdjacent(vector.AsInt16(), vector.AsInt16()); Vector256 right2 = Avx2.MultiplyAddAdjacent(vector2.AsInt16(), vector2.AsInt16()); return Avx2.Add(left3, right2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Vp8Copy4X4(Span src, Span dst) { Copy(src, dst, 4, 4); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Vp8Copy16X8(Span src, Span dst) { Copy(src, dst, 16, 8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Copy(Span src, Span dst, int w, int h) { int num = 0; for (int i = 0; i < h; i++) { src.Slice(num, w).CopyTo(dst.Slice(num, w)); num += 32; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Vp8Disto16X16(Span a, Span b, Span w, Span scratch) { int num = 0; int length = 112; for (int i = 0; i < 512; i += 128) { for (int j = 0; j < 16; j += 4) { num += Vp8Disto4X4(a.Slice(j + i, length), b.Slice(j + i, length), w, scratch); } } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Vp8Disto4X4(Span a, Span b, Span w, Span scratch) { if (Sse41.IsSupported) { return Math.Abs(TTransformSse41(a, b, w)) >> 5; } int num = TTransform(a, w, scratch); return Math.Abs(TTransform(b, w, scratch) - num) >> 5; } public static void DC16(Span dst, Span yuv, int offset) { int num = offset - 1; int num2 = offset - 32; int num3 = 16; for (int i = 0; i < 16; i++) { num3 += yuv[num + i * 32] + yuv[num2 + i]; } Put16(num3 >> 5, dst); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void TM16(Span dst, Span yuv, int offset) { TrueMotion(dst, yuv, offset, 16); } public static void VE16(Span dst, Span yuv, int offset) { Span span = yuv.Slice(offset - 32, 16); for (int i = 0; i < 16; i++) { span.CopyTo(dst.Slice(i * 32)); } } public static void HE16(Span dst, Span yuv, int offset) { offset--; for (int num = 16; num > 0; num--) { byte value = yuv[offset]; Memset(dst, value, 0, 16); offset += 32; dst = dst.Slice(32); } } public static void DC16NoTop(Span dst, Span yuv, int offset) { int num = 8; for (int i = 0; i < 16; i++) { num += yuv[-1 + i * 32 + offset]; } Put16(num >> 4, dst); } public static void DC16NoLeft(Span dst, Span yuv, int offset) { int num = 8; for (int i = 0; i < 16; i++) { num += yuv[i - 32 + offset]; } Put16(num >> 4, dst); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void DC16NoTopLeft(Span dst) { Put16(128, dst); } public static void DC8uv(Span dst, Span yuv, int offset) { int num = 8; int num2 = offset - 1; int num3 = offset - 32; for (int i = 0; i < 8; i++) { num += yuv[num3 + i] + yuv[num2 + i * 32]; } Put8x8uv((byte)(num >> 4), dst); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void TM8uv(Span dst, Span yuv, int offset) { TrueMotion(dst, yuv, offset, 8); } public static void VE8uv(Span dst, Span yuv, int offset) { Span span = yuv.Slice(offset - 32, 8); int num = 256; for (int i = 0; i < num; i += 32) { span.CopyTo(dst.Slice(i)); } } public static void HE8uv(Span dst, Span yuv, int offset) { offset--; for (int i = 0; i < 8; i++) { byte value = yuv[offset]; Memset(dst, value, 0, 8); dst = dst.Slice(32); offset += 32; } } public static void DC8uvNoTop(Span dst, Span yuv, int offset) { int num = 4; int num2 = offset - 1; int num3 = 256; for (int i = 0; i < num3; i += 32) { num += yuv[num2 + i]; } Put8x8uv((byte)(num >> 3), dst); } public static void DC8uvNoLeft(Span dst, Span yuv, int offset) { int num = offset - 32; int num2 = 4; for (int i = 0; i < 8; i++) { num2 += yuv[num + i]; } Put8x8uv((byte)(num2 >> 3), dst); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void DC8uvNoTopLeft(Span dst) { Put8x8uv(128, dst); } public static void DC4(Span dst, Span yuv, int offset) { int num = 4; int num2 = offset - 32; int num3 = offset - 1; for (int i = 0; i < 4; i++) { num += yuv[num2 + i] + yuv[num3 + i * 32]; } num >>= 3; int num4 = 128; for (int j = 0; j < num4; j += 32) { Memset(dst, (byte)num, j, 4); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void TM4(Span dst, Span yuv, int offset) { TrueMotion(dst, yuv, offset, 4); } public static void VE4(Span dst, Span yuv, int offset, Span vals) { int num = offset - 32; vals[0] = Avg3(yuv[num - 1], yuv[num], yuv[num + 1]); vals[1] = Avg3(yuv[num], yuv[num + 1], yuv[num + 2]); vals[2] = Avg3(yuv[num + 1], yuv[num + 2], yuv[num + 3]); vals[3] = Avg3(yuv[num + 2], yuv[num + 3], yuv[num + 4]); int num2 = 128; for (int i = 0; i < num2; i += 32) { vals.CopyTo(dst.Slice(i)); } } public static void HE4(Span dst, Span yuv, int offset) { int num = offset - 1; byte a = yuv[num - 32]; byte b = yuv[num]; byte b2 = yuv[num + 32]; byte b3 = yuv[num + 64]; byte b4 = yuv[num + 96]; uint value = (uint)(16843009 * Avg3(a, b, b2)); BinaryPrimitives.WriteUInt32BigEndian(dst, value); value = (uint)(16843009 * Avg3(b, b2, b3)); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(32), value); value = (uint)(16843009 * Avg3(b2, b3, b4)); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(64), value); value = (uint)(16843009 * Avg3(b3, b4, b4)); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(96), value); } public static void RD4(Span dst, Span yuv, int offset) { int num = offset - 1; byte b = yuv[num]; byte b2 = yuv[num + 32]; byte b3 = yuv[num + 64]; byte c = yuv[num + 96]; byte b4 = yuv[num - 32]; byte b5 = yuv[offset - 32]; byte b6 = yuv[offset + 1 - 32]; byte b7 = yuv[offset + 2 - 32]; byte a = yuv[offset + 3 - 32]; Dst(dst, 0, 3, Avg3(b2, b3, c)); byte v = Avg3(b, b2, b3); Dst(dst, 1, 3, v); Dst(dst, 0, 2, v); byte v2 = Avg3(b4, b, b2); Dst(dst, 2, 3, v2); Dst(dst, 1, 2, v2); Dst(dst, 0, 1, v2); byte v3 = Avg3(b5, b4, b); Dst(dst, 3, 3, v3); Dst(dst, 2, 2, v3); Dst(dst, 1, 1, v3); Dst(dst, 0, 0, v3); byte v4 = Avg3(b6, b5, b4); Dst(dst, 3, 2, v4); Dst(dst, 2, 1, v4); Dst(dst, 1, 0, v4); byte v5 = Avg3(b7, b6, b5); Dst(dst, 3, 1, v5); Dst(dst, 2, 0, v5); Dst(dst, 3, 0, Avg3(a, b7, b6)); } public static void VR4(Span dst, Span yuv, int offset) { int num = offset - 1; byte b = yuv[num]; byte b2 = yuv[num + 32]; byte a = yuv[num + 64]; byte b3 = yuv[num - 32]; byte b4 = yuv[offset - 32]; byte b5 = yuv[offset + 1 - 32]; byte b6 = yuv[offset + 2 - 32]; byte b7 = yuv[offset + 3 - 32]; byte v = Avg2(b3, b4); Dst(dst, 0, 0, v); Dst(dst, 1, 2, v); byte v2 = Avg2(b4, b5); Dst(dst, 1, 0, v2); Dst(dst, 2, 2, v2); byte v3 = Avg2(b5, b6); Dst(dst, 2, 0, v3); Dst(dst, 3, 2, v3); Dst(dst, 3, 0, Avg2(b6, b7)); Dst(dst, 0, 3, Avg3(a, b2, b)); Dst(dst, 0, 2, Avg3(b2, b, b3)); byte v4 = Avg3(b, b3, b4); Dst(dst, 0, 1, v4); Dst(dst, 1, 3, v4); byte v5 = Avg3(b3, b4, b5); Dst(dst, 1, 1, v5); Dst(dst, 2, 3, v5); byte v6 = Avg3(b4, b5, b6); Dst(dst, 2, 1, v6); Dst(dst, 3, 3, v6); Dst(dst, 3, 1, Avg3(b5, b6, b7)); } public static void LD4(Span dst, Span yuv, int offset) { byte a = yuv[offset - 32]; byte b = yuv[offset + 1 - 32]; byte b2 = yuv[offset + 2 - 32]; byte b3 = yuv[offset + 3 - 32]; byte b4 = yuv[offset + 4 - 32]; byte b5 = yuv[offset + 5 - 32]; byte b6 = yuv[offset + 6 - 32]; byte b7 = yuv[offset + 7 - 32]; Dst(dst, 0, 0, Avg3(a, b, b2)); byte v = Avg3(b, b2, b3); Dst(dst, 1, 0, v); Dst(dst, 0, 1, v); byte v2 = Avg3(b2, b3, b4); Dst(dst, 2, 0, v2); Dst(dst, 1, 1, v2); Dst(dst, 0, 2, v2); byte v3 = Avg3(b3, b4, b5); Dst(dst, 3, 0, v3); Dst(dst, 2, 1, v3); Dst(dst, 1, 2, v3); Dst(dst, 0, 3, v3); byte v4 = Avg3(b4, b5, b6); Dst(dst, 3, 1, v4); Dst(dst, 2, 2, v4); Dst(dst, 1, 3, v4); byte v5 = Avg3(b5, b6, b7); Dst(dst, 3, 2, v5); Dst(dst, 2, 3, v5); Dst(dst, 3, 3, Avg3(b6, b7, b7)); } public static void VL4(Span dst, Span yuv, int offset) { byte a = yuv[offset - 32]; byte b = yuv[offset + 1 - 32]; byte b2 = yuv[offset + 2 - 32]; byte b3 = yuv[offset + 3 - 32]; byte b4 = yuv[offset + 4 - 32]; byte b5 = yuv[offset + 5 - 32]; byte b6 = yuv[offset + 6 - 32]; byte c = yuv[offset + 7 - 32]; Dst(dst, 0, 0, Avg2(a, b)); byte v = Avg2(b, b2); Dst(dst, 1, 0, v); Dst(dst, 0, 2, v); byte v2 = Avg2(b2, b3); Dst(dst, 2, 0, v2); Dst(dst, 1, 2, v2); byte v3 = Avg2(b3, b4); Dst(dst, 3, 0, v3); Dst(dst, 2, 2, v3); Dst(dst, 0, 1, Avg3(a, b, b2)); byte v4 = Avg3(b, b2, b3); Dst(dst, 1, 1, v4); Dst(dst, 0, 3, v4); byte v5 = Avg3(b2, b3, b4); Dst(dst, 2, 1, v5); Dst(dst, 1, 3, v5); byte v6 = Avg3(b3, b4, b5); Dst(dst, 3, 1, v6); Dst(dst, 2, 3, v6); Dst(dst, 3, 2, Avg3(b4, b5, b6)); Dst(dst, 3, 3, Avg3(b5, b6, c)); } public static void HD4(Span dst, Span yuv, int offset) { byte b = yuv[offset - 1]; byte b2 = yuv[offset - 1 + 32]; byte b3 = yuv[offset - 1 + 64]; byte a = yuv[offset - 1 + 96]; byte b4 = yuv[offset - 1 - 32]; byte b5 = yuv[offset - 32]; byte b6 = yuv[offset + 1 - 32]; byte c = yuv[offset + 2 - 32]; byte v = Avg2(b, b4); Dst(dst, 0, 0, v); Dst(dst, 2, 1, v); byte v2 = Avg2(b2, b); Dst(dst, 0, 1, v2); Dst(dst, 2, 2, v2); byte v3 = Avg2(b3, b2); Dst(dst, 0, 2, v3); Dst(dst, 2, 3, v3); Dst(dst, 0, 3, Avg2(a, b3)); Dst(dst, 3, 0, Avg3(b5, b6, c)); Dst(dst, 2, 0, Avg3(b4, b5, b6)); byte v4 = Avg3(b, b4, b5); Dst(dst, 1, 0, v4); Dst(dst, 3, 1, v4); byte v5 = Avg3(b2, b, b4); Dst(dst, 1, 1, v5); Dst(dst, 3, 2, v5); byte v6 = Avg3(b3, b2, b); Dst(dst, 1, 2, v6); Dst(dst, 3, 3, v6); Dst(dst, 1, 3, Avg3(a, b3, b2)); } public static void HU4(Span dst, Span yuv, int offset) { byte a = yuv[offset - 1]; byte b = yuv[offset - 1 + 32]; byte b2 = yuv[offset - 1 + 64]; byte b3 = yuv[offset - 1 + 96]; Dst(dst, 0, 0, Avg2(a, b)); byte v = Avg2(b, b2); Dst(dst, 2, 0, v); Dst(dst, 0, 1, v); byte v2 = Avg2(b2, b3); Dst(dst, 2, 1, v2); Dst(dst, 0, 2, v2); Dst(dst, 1, 0, Avg3(a, b, b2)); byte v3 = Avg3(b, b2, b3); Dst(dst, 3, 0, v3); Dst(dst, 1, 1, v3); byte v4 = Avg3(b2, b3, b3); Dst(dst, 3, 1, v4); Dst(dst, 1, 2, v4); Dst(dst, 3, 2, b3); Dst(dst, 2, 2, b3); Dst(dst, 0, 3, b3); Dst(dst, 1, 3, b3); Dst(dst, 2, 3, b3); Dst(dst, 3, 3, b3); } public static void TransformWht(Span input, Span output, Span scratch) { Span span = scratch.Slice(0, 16); span.Clear(); for (int i = 0; i < 4; i++) { int index = 4 + i; int index2 = 8 + i; int index3 = 12 + i; int num = input[i] + input[index3]; int num2 = input[index] + input[index2]; int num3 = input[index] - input[index2]; int num4 = input[i] - input[index3]; span[i] = num + num2; span[index2] = num - num2; span[index] = num4 + num3; span[index3] = num4 - num3; } int num5 = 0; for (int j = 0; j < 4; j++) { int num6 = j * 4; int num7 = span[num6] + 3; int num8 = num7 + span[3 + num6]; int num9 = span[1 + num6] + span[2 + num6]; int num10 = span[1 + num6] - span[2 + num6]; int num11 = num7 - span[3 + num6]; output[num5] = (short)(num8 + num9 >> 3); output[num5 + 16] = (short)(num11 + num10 >> 3); output[num5 + 32] = (short)(num8 - num9 >> 3); output[num5 + 48] = (short)(num11 - num10 >> 3); num5 += 64; } } public static int TTransform(Span input, Span w, Span scratch) { int num = 0; Span span = scratch.Slice(0, 16); span.Clear(); int num2 = 0; for (int i = 0; i < 4; i++) { int index = num2 + 1; int index2 = num2 + 2; int index3 = num2 + 3; int num3 = input[num2] + input[index2]; int num4 = input[index] + input[index3]; int num5 = input[index] - input[index3]; int num6 = input[num2] - input[index2]; span[i * 4] = num3 + num4; span[1 + i * 4] = num6 + num5; span[2 + i * 4] = num6 - num5; span[3 + i * 4] = num3 - num4; num2 += 32; } for (int j = 0; j < 4; j++) { int num7 = span[j] + span[8 + j]; int num8 = span[4 + j] + span[12 + j]; int num9 = span[4 + j] - span[12 + j]; int num10 = span[j] - span[8 + j]; int value = num7 + num8; int value2 = num10 + num9; int value3 = num10 - num9; int value4 = num7 - num8; num += w[0] * Math.Abs(value); num += w[4] * Math.Abs(value2); num += w[8] * Math.Abs(value3); num += w[12] * Math.Abs(value4); w = w.Slice(1); } return num; } public static int TTransformSse41(Span inputA, Span inputB, Span w) { Vector128 vector = Unsafe.As>(ref MemoryMarshal.GetReference(inputA)); Vector128 vector2 = Unsafe.As>(ref MemoryMarshal.GetReference(inputA.Slice(32, 16))); Vector128 vector3 = Unsafe.As>(ref MemoryMarshal.GetReference(inputA.Slice(64, 16))); Vector128 vector4 = Unsafe.As>(ref MemoryMarshal.GetReference(inputA.Slice(96, 16))).AsInt64(); Vector128 vector5 = Unsafe.As>(ref MemoryMarshal.GetReference(inputB)); Vector128 vector6 = Unsafe.As>(ref MemoryMarshal.GetReference(inputB.Slice(32, 16))); Vector128 vector7 = Unsafe.As>(ref MemoryMarshal.GetReference(inputB.Slice(64, 16))); Vector128 vector8 = Unsafe.As>(ref MemoryMarshal.GetReference(inputB.Slice(96, 16))).AsInt64(); Vector128 vector9 = Sse2.UnpackLow(vector.AsInt32(), vector5.AsInt32()); Vector128 vector10 = Sse2.UnpackLow(vector2.AsInt32(), vector6.AsInt32()); Vector128 vector11 = Sse2.UnpackLow(vector3.AsInt32(), vector7.AsInt32()); Vector128 vector12 = Sse2.UnpackLow(vector4.AsInt32(), vector8.AsInt32()); Vector128 left = Sse41.ConvertToVector128Int16(vector9.AsByte()); Vector128 left2 = Sse41.ConvertToVector128Int16(vector10.AsByte()); Vector128 right = Sse41.ConvertToVector128Int16(vector11.AsByte()); Vector128 right2 = Sse41.ConvertToVector128Int16(vector12.AsByte()); Vector128 left3 = Sse2.Add(left, right); Vector128 right3 = Sse2.Add(left2, right2); Vector128 right4 = Sse2.Subtract(left2, right2); Vector128 left4 = Sse2.Subtract(left, right); Vector128 b = Sse2.Add(left3, right3); Vector128 b2 = Sse2.Add(left4, right4); Vector128 b3 = Sse2.Subtract(left4, right4); Vector128 b4 = Sse2.Subtract(left3, right3); Vp8Transpose_2_4x4_16b(b, b2, b3, b4, out var output, out var output2, out var output3, out var output4); Vector128 vector13 = Unsafe.As>(ref MemoryMarshal.GetReference(w)); Vector128 vector14 = Unsafe.As>(ref MemoryMarshal.GetReference(w.Slice(8, 8))); left3 = Sse2.Add(output.AsInt16(), output3.AsInt16()); right3 = Sse2.Add(output2.AsInt16(), output4.AsInt16()); right4 = Sse2.Subtract(output2.AsInt16(), output4.AsInt16()); Vector128 left5 = Sse2.Subtract(output.AsInt16(), output3.AsInt16()); b = Sse2.Add(left3, right3); b2 = Sse2.Add(left5, right4); b3 = Sse2.Subtract(left5, right4); b4 = Sse2.Subtract(left3, right3); Vector128 vector15 = Sse2.UnpackLow(b.AsInt64(), b2.AsInt64()); Vector128 vector16 = Sse2.UnpackLow(b3.AsInt64(), b4.AsInt64()); Vector128 vector17 = Sse2.UnpackHigh(b.AsInt64(), b2.AsInt64()); Vector128 vector18 = Sse2.UnpackHigh(b3.AsInt64(), b4.AsInt64()); Vector128 vector19 = Ssse3.Abs(vector15.AsInt16()); Vector128 vector20 = Ssse3.Abs(vector16.AsInt16()); Vector128 vector21 = Ssse3.Abs(vector17.AsInt16()); Vector128 vector22 = Ssse3.Abs(vector18.AsInt16()); Vector128 left6 = Sse2.MultiplyAddAdjacent(vector19.AsInt16(), vector13.AsInt16()); Vector128 right5 = Sse2.MultiplyAddAdjacent(vector20.AsInt16(), vector14.AsInt16()); Vector128 left7 = Sse2.MultiplyAddAdjacent(vector21.AsInt16(), vector13.AsInt16()); Vector128 right6 = Sse2.MultiplyAddAdjacent(vector22.AsInt16(), vector14.AsInt16()); Vector128 vector23 = Sse2.Add(left6, right5); return Numerics.ReduceSum(Sse2.Subtract(right: Sse2.Add(left7, right6).AsInt32(), left: vector23.AsInt32())); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Vp8Transpose_2_4x4_16b(Vector128 b0, Vector128 b1, Vector128 b2, Vector128 b3, out Vector128 output0, out Vector128 output1, out Vector128 output2, out Vector128 output3) { Vector128 vector = Sse2.UnpackLow(b0, b1); Vector128 vector2 = Sse2.UnpackLow(b2, b3); Vector128 vector3 = Sse2.UnpackHigh(b0, b1); Vector128 vector4 = Sse2.UnpackHigh(b2, b3); Vector128 vector5 = Sse2.UnpackLow(vector.AsInt32(), vector2.AsInt32()); Vector128 vector6 = Sse2.UnpackLow(vector3.AsInt32(), vector4.AsInt32()); Vector128 vector7 = Sse2.UnpackHigh(vector.AsInt32(), vector2.AsInt32()); Vector128 vector8 = Sse2.UnpackHigh(vector3.AsInt32(), vector4.AsInt32()); output0 = Sse2.UnpackLow(vector5.AsInt64(), vector6.AsInt64()); output1 = Sse2.UnpackHigh(vector5.AsInt64(), vector6.AsInt64()); output2 = Sse2.UnpackLow(vector7.AsInt64(), vector8.AsInt64()); output3 = Sse2.UnpackHigh(vector7.AsInt64(), vector8.AsInt64()); } public static void TransformTwo(Span src, Span dst, Span scratch) { if (Sse2.IsSupported) { ref short reference = ref MemoryMarshal.GetReference(src); Vector128 left = Vector128.Create(Unsafe.As(ref reference), 0L); Vector128 left2 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, 4)), 0L); Vector128 left3 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, 8)), 0L); Vector128 left4 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, 12)), 0L); Vector128 right = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, 16)), 0L); Vector128 right2 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, 20)), 0L); Vector128 right3 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, 24)), 0L); Vector128 right4 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, 28)), 0L); left = Sse2.UnpackLow(left, right); left2 = Sse2.UnpackLow(left2, right2); left3 = Sse2.UnpackLow(left3, right3); left4 = Sse2.UnpackLow(left4, right4); Vector128 vector = Sse2.Add(left.AsInt16(), left3.AsInt16()); Vector128 vector2 = Sse2.Subtract(left.AsInt16(), left3.AsInt16()); Vector128 left5 = Sse2.MultiplyHigh(left2.AsInt16(), K2); Vector128 right5 = Sse2.MultiplyHigh(left4.AsInt16(), K1); Vector128 vector3 = Sse2.Subtract(left2.AsInt16(), left4.AsInt16()); Vector128 right6 = Sse2.Add(right: Sse2.Subtract(left5, right5), left: vector3.AsInt16()); Vector128 left6 = Sse2.MultiplyHigh(left2.AsInt16(), K1); Vector128 right7 = Sse2.MultiplyHigh(left4.AsInt16(), K2); Vector128 left7 = Sse2.Add(left2.AsInt16(), left4.AsInt16()); Vector128 right8 = Sse2.Add(left6, right7); Vector128 right9 = Sse2.Add(left7, right8); Vector128 b = Sse2.Add(vector.AsInt16(), right9); Vector128 b2 = Sse2.Add(vector2.AsInt16(), right6); Vector128 b3 = Sse2.Subtract(vector2.AsInt16(), right6); Vector128 b4 = Sse2.Subtract(vector.AsInt16(), right9); Vp8Transpose_2_4x4_16b(b, b2, b3, b4, out var output, out var output2, out var output3, out var output4); Vector128 left8 = Sse2.Add(output.AsInt16(), FourShort); vector = Sse2.Add(left8, output3.AsInt16()); vector2 = Sse2.Subtract(left8, output3.AsInt16()); left5 = Sse2.MultiplyHigh(output2.AsInt16(), K2); right5 = Sse2.MultiplyHigh(output4.AsInt16(), K1); Vector128 left9 = Sse2.Subtract(output2.AsInt16(), output4.AsInt16()); Vector128 right10 = Sse2.Subtract(left5, right5); right6 = Sse2.Add(left9, right10); left6 = Sse2.MultiplyHigh(output2.AsInt16(), K1); right7 = Sse2.MultiplyHigh(output4.AsInt16(), K2); Vector128 left10 = Sse2.Add(output2.AsInt16(), output4.AsInt16()); right8 = Sse2.Add(left6, right7); right9 = Sse2.Add(left10, right8); b = Sse2.Add(vector, right9); b2 = Sse2.Add(vector2, right6); b3 = Sse2.Subtract(vector2, right6); b4 = Sse2.Subtract(vector, right9); Vector128 b5 = Sse2.ShiftRightArithmetic(b, 3); Vector128 b6 = Sse2.ShiftRightArithmetic(b2, 3); Vector128 b7 = Sse2.ShiftRightArithmetic(b3, 3); Vector128 b8 = Sse2.ShiftRightArithmetic(b4, 3); Vp8Transpose_2_4x4_16b(b5, b6, b7, b8, out output, out output2, out output3, out output4); ref byte reference2 = ref MemoryMarshal.GetReference(dst); Vector128 left11 = Vector128.Create(Unsafe.As(ref reference2), 0L).AsByte(); Vector128 left12 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 32)), 0L).AsByte(); Vector128 left13 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 64)), 0L).AsByte(); Vector128 left14 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 96)), 0L).AsByte(); left11 = Sse2.UnpackLow(left11, Vector128.Zero); left12 = Sse2.UnpackLow(left12, Vector128.Zero); left13 = Sse2.UnpackLow(left13, Vector128.Zero); left14 = Sse2.UnpackLow(left14, Vector128.Zero); left11 = Sse2.Add(left11.AsInt16(), output.AsInt16()).AsByte(); left12 = Sse2.Add(left12.AsInt16(), output2.AsInt16()).AsByte(); left13 = Sse2.Add(left13.AsInt16(), output3.AsInt16()).AsByte(); left14 = Sse2.Add(left14.AsInt16(), output4.AsInt16()).AsByte(); left11 = Sse2.PackUnsignedSaturate(left11.AsInt16(), left11.AsInt16()); left12 = Sse2.PackUnsignedSaturate(left12.AsInt16(), left12.AsInt16()); left13 = Sse2.PackUnsignedSaturate(left13.AsInt16(), left13.AsInt16()); left14 = Sse2.PackUnsignedSaturate(left14.AsInt16(), left14.AsInt16()); ref byte reference3 = ref MemoryMarshal.GetReference(dst); Unsafe.As>(ref reference3) = left11.GetLower(); Unsafe.As>(ref Unsafe.Add(ref reference3, 32)) = left12.GetLower(); Unsafe.As>(ref Unsafe.Add(ref reference3, 64)) = left13.GetLower(); Unsafe.As>(ref Unsafe.Add(ref reference3, 96)) = left14.GetLower(); } else { TransformOne(src, dst, scratch); TransformOne(src.Slice(16), dst.Slice(4), scratch); } } public static void TransformOne(Span src, Span dst, Span scratch) { if (Sse2.IsSupported) { ref short reference = ref MemoryMarshal.GetReference(src); Vector128 vector = Vector128.Create(Unsafe.As(ref reference), 0L); Vector128 vector2 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, 4)), 0L); Vector128 vector3 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, 8)), 0L); Vector128 vector4 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference, 12)), 0L); Vector128 vector5 = Sse2.Add(vector.AsInt16(), vector3.AsInt16()); Vector128 vector6 = Sse2.Subtract(vector.AsInt16(), vector3.AsInt16()); Vector128 left = Sse2.MultiplyHigh(vector2.AsInt16(), K2); Vector128 right = Sse2.MultiplyHigh(vector4.AsInt16(), K1); Vector128 vector7 = Sse2.Subtract(vector2.AsInt16(), vector4.AsInt16()); Vector128 right2 = Sse2.Add(right: Sse2.Subtract(left, right), left: vector7.AsInt16()); Vector128 left2 = Sse2.MultiplyHigh(vector2.AsInt16(), K1); Vector128 right3 = Sse2.MultiplyHigh(vector4.AsInt16(), K2); Vector128 left3 = Sse2.Add(vector2.AsInt16(), vector4.AsInt16()); Vector128 right4 = Sse2.Add(left2, right3); Vector128 right5 = Sse2.Add(left3, right4); Vector128 b = Sse2.Add(vector5.AsInt16(), right5); Vector128 b2 = Sse2.Add(vector6.AsInt16(), right2); Vector128 b3 = Sse2.Subtract(vector6.AsInt16(), right2); Vector128 b4 = Sse2.Subtract(vector5.AsInt16(), right5); Vp8Transpose_2_4x4_16b(b, b2, b3, b4, out var output, out var output2, out var output3, out var output4); Vector128 left4 = Sse2.Add(output.AsInt16(), FourShort); vector5 = Sse2.Add(left4, output3.AsInt16()); vector6 = Sse2.Subtract(left4, output3.AsInt16()); left = Sse2.MultiplyHigh(output2.AsInt16(), K2); right = Sse2.MultiplyHigh(output4.AsInt16(), K1); Vector128 left5 = Sse2.Subtract(output2.AsInt16(), output4.AsInt16()); Vector128 right6 = Sse2.Subtract(left, right); right2 = Sse2.Add(left5, right6); left2 = Sse2.MultiplyHigh(output2.AsInt16(), K1); right3 = Sse2.MultiplyHigh(output4.AsInt16(), K2); Vector128 left6 = Sse2.Add(output2.AsInt16(), output4.AsInt16()); right4 = Sse2.Add(left2, right3); right5 = Sse2.Add(left6, right4); b = Sse2.Add(vector5, right5); b2 = Sse2.Add(vector6, right2); b3 = Sse2.Subtract(vector6, right2); b4 = Sse2.Subtract(vector5, right5); Vector128 b5 = Sse2.ShiftRightArithmetic(b, 3); Vector128 b6 = Sse2.ShiftRightArithmetic(b2, 3); Vector128 b7 = Sse2.ShiftRightArithmetic(b3, 3); Vector128 b8 = Sse2.ShiftRightArithmetic(b4, 3); Vp8Transpose_2_4x4_16b(b5, b6, b7, b8, out output, out output2, out output3, out output4); ref byte reference2 = ref MemoryMarshal.GetReference(dst); Vector128 left7 = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref reference2)).AsByte(); Vector128 left8 = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref Unsafe.Add(ref reference2, 32))).AsByte(); Vector128 left9 = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref Unsafe.Add(ref reference2, 64))).AsByte(); Vector128 left10 = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref Unsafe.Add(ref reference2, 96))).AsByte(); left7 = Sse2.UnpackLow(left7, Vector128.Zero); left8 = Sse2.UnpackLow(left8, Vector128.Zero); left9 = Sse2.UnpackLow(left9, Vector128.Zero); left10 = Sse2.UnpackLow(left10, Vector128.Zero); left7 = Sse2.Add(left7.AsInt16(), output.AsInt16()).AsByte(); left8 = Sse2.Add(left8.AsInt16(), output2.AsInt16()).AsByte(); left9 = Sse2.Add(left9.AsInt16(), output3.AsInt16()).AsByte(); left10 = Sse2.Add(left10.AsInt16(), output4.AsInt16()).AsByte(); left7 = Sse2.PackUnsignedSaturate(left7.AsInt16(), left7.AsInt16()); left8 = Sse2.PackUnsignedSaturate(left8.AsInt16(), left8.AsInt16()); left9 = Sse2.PackUnsignedSaturate(left9.AsInt16(), left9.AsInt16()); left10 = Sse2.PackUnsignedSaturate(left10.AsInt16(), left10.AsInt16()); ref byte reference3 = ref MemoryMarshal.GetReference(dst); int num = Sse2.ConvertToInt32(left7.AsInt32()); int num2 = Sse2.ConvertToInt32(left8.AsInt32()); int num3 = Sse2.ConvertToInt32(left9.AsInt32()); int num4 = Sse2.ConvertToInt32(left10.AsInt32()); Unsafe.As(ref reference3) = num; Unsafe.As(ref Unsafe.Add(ref reference3, 32)) = num2; Unsafe.As(ref Unsafe.Add(ref reference3, 64)) = num3; Unsafe.As(ref Unsafe.Add(ref reference3, 96)) = num4; } else { Span span = scratch.Slice(0, 16); int num5 = 0; for (int i = 0; i < 4; i++) { int index = i + 4; int index2 = i + 8; int index3 = i + 12; int num6 = src[i] + src[index2]; int num7 = src[i] - src[index2]; int num8 = Mul2(src[index]) - Mul1(src[index3]); int num9 = Mul1(src[index]) + Mul2(src[index3]); span[num5++] = num6 + num9; span[num5++] = num7 + num8; span[num5++] = num7 - num8; span[num5++] = num6 - num9; } num5 = 0; int num10 = 0; for (int j = 0; j < 4; j++) { int index4 = num5 + 4; int index5 = num5 + 8; int index6 = num5 + 12; int num11 = span[num5] + 4; int num12 = num11 + span[index5]; int num13 = num11 - span[index5]; int num14 = Mul2(span[index4]) - Mul1(span[index6]); int num15 = Mul1(span[index4]) + Mul2(span[index6]); Store(dst.Slice(num10), 0, 0, num12 + num15); Store(dst.Slice(num10), 1, 0, num13 + num14); Store(dst.Slice(num10), 2, 0, num13 - num14); Store(dst.Slice(num10), 3, 0, num12 - num15); num5++; num10 += 32; } } } public static void TransformDc(Span src, Span dst) { int v = src[0] + 4; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { Store(dst, j, i, v); } } } public static void TransformAc3(Span src, Span dst) { int num = src[0] + 4; int num2 = Mul2(src[4]); int num3 = Mul1(src[4]); int c = Mul2(src[1]); int d = Mul1(src[1]); Store2(dst, 0, num + num3, d, c); Store2(dst, 1, num + num2, d, c); Store2(dst, 2, num - num2, d, c); Store2(dst, 3, num - num3, d, c); } public static void TransformUv(Span src, Span dst, Span scratch) { TransformTwo(src.Slice(0), dst, scratch); TransformTwo(src.Slice(32), dst.Slice(128), scratch); } public static void TransformDcuv(Span src, Span dst) { if (src[0] != 0) { TransformDc(src.Slice(0), dst); } if (src[16] != 0) { TransformDc(src.Slice(16), dst.Slice(4)); } if (src[32] != 0) { TransformDc(src.Slice(32), dst.Slice(128)); } if (src[48] != 0) { TransformDc(src.Slice(48), dst.Slice(132)); } } public static void SimpleVFilter16(Span p, int offset, int stride, int thresh) { if (Sse2.IsSupported) { ref byte source = ref Unsafe.Add(ref MemoryMarshal.GetReference(p), offset); Vector128 p2 = Unsafe.As>(ref Unsafe.Subtract(ref source, 2 * stride)); Vector128 p3 = Unsafe.As>(ref Unsafe.Subtract(ref source, stride)); Vector128 q = Unsafe.As>(ref source); Vector128 q2 = Unsafe.As>(ref Unsafe.Add(ref source, stride)); DoFilter2Sse2(ref p2, ref p3, ref q, ref q2, thresh); ref byte source2 = ref Unsafe.Add(ref MemoryMarshal.GetReference(p), offset); Unsafe.As>(ref Unsafe.Subtract(ref source2, stride)) = p3.AsSByte(); Unsafe.As>(ref source2) = q.AsSByte(); return; } int t = 2 * thresh + 1; int num = 16 + offset; for (int i = offset; i < num; i++) { if (NeedsFilter(p, i, stride, t)) { DoFilter2(p, i, stride); } } } public static void SimpleHFilter16(Span p, int offset, int stride, int thresh) { if (Sse2.IsSupported) { ref byte reference = ref Unsafe.Add(ref MemoryMarshal.GetReference(p), offset - 2); Load16x4(ref reference, ref Unsafe.Add(ref reference, 8 * stride), stride, out var p2, out var p3, out var q, out var q2); DoFilter2Sse2(ref p2, ref p3, ref q, ref q2, thresh); Store16x4(p2, p3, q, q2, ref reference, ref Unsafe.Add(ref reference, 8 * stride), stride); return; } int t = 2 * thresh + 1; int num = offset + 16 * stride; for (int i = offset; i < num; i += stride) { if (NeedsFilter(p, i, 1, t)) { DoFilter2(p, i, 1); } } } public static void SimpleVFilter16i(Span p, int offset, int stride, int thresh) { if (Sse2.IsSupported) { for (int num = 3; num > 0; num--) { offset += 4 * stride; SimpleVFilter16(p, offset, stride, thresh); } } else { for (int num2 = 3; num2 > 0; num2--) { offset += 4 * stride; SimpleVFilter16(p, offset, stride, thresh); } } } public static void SimpleHFilter16i(Span p, int offset, int stride, int thresh) { if (Sse2.IsSupported) { for (int num = 3; num > 0; num--) { offset += 4; SimpleHFilter16(p, offset, stride, thresh); } } else { for (int num2 = 3; num2 > 0; num2--) { offset += 4; SimpleHFilter16(p, offset, stride, thresh); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void VFilter16(Span p, int offset, int stride, int thresh, int ithresh, int hevThresh) { if (Sse2.IsSupported) { ref byte reference = ref MemoryMarshal.GetReference(p); Vector128 p2 = Unsafe.As>(ref Unsafe.Add(ref reference, offset - 4 * stride)); Vector128 p3 = Unsafe.As>(ref Unsafe.Add(ref reference, offset - 3 * stride)); Vector128 p4 = Unsafe.As>(ref Unsafe.Add(ref reference, offset - 2 * stride)); Vector128 p5 = Unsafe.As>(ref Unsafe.Add(ref reference, offset - stride)); Vector128 left = Abs(p4, p5); left = Sse2.Max(left, Abs(p2, p3)); left = Sse2.Max(left, Abs(p3, p4)); Vector128 q = Unsafe.As>(ref Unsafe.Add(ref reference, offset)); Vector128 q2 = Unsafe.As>(ref Unsafe.Add(ref reference, offset + stride)); Vector128 q3 = Unsafe.As>(ref Unsafe.Add(ref reference, offset + 2 * stride)); p2 = Unsafe.As>(ref Unsafe.Add(ref reference, offset + 3 * stride)); left = Sse2.Max(left, Abs(q2, q)); left = Sse2.Max(left, Abs(p2, q3)); left = Sse2.Max(left, Abs(q3, q2)); ComplexMask(p4, p5, q, q2, thresh, ithresh, ref left); DoFilter6Sse2(ref p3, ref p4, ref p5, ref q, ref q2, ref q3, left, hevThresh); ref byte reference2 = ref MemoryMarshal.GetReference(p); Unsafe.As>(ref Unsafe.Add(ref reference2, offset - 3 * stride)) = p3.AsInt32(); Unsafe.As>(ref Unsafe.Add(ref reference2, offset - 2 * stride)) = p4.AsInt32(); Unsafe.As>(ref Unsafe.Add(ref reference2, offset - stride)) = p5.AsInt32(); Unsafe.As>(ref Unsafe.Add(ref reference2, offset)) = q.AsInt32(); Unsafe.As>(ref Unsafe.Add(ref reference2, offset + stride)) = q2.AsInt32(); Unsafe.As>(ref Unsafe.Add(ref reference2, offset + 2 * stride)) = q3.AsInt32(); } else { FilterLoop26(p, offset, stride, 1, 16, thresh, ithresh, hevThresh); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void HFilter16(Span p, int offset, int stride, int thresh, int ithresh, int hevThresh) { if (Sse2.IsSupported) { ref byte reference = ref MemoryMarshal.GetReference(p); ref byte reference2 = ref Unsafe.Add(ref reference, offset - 4); Load16x4(ref reference2, ref Unsafe.Add(ref reference2, 8 * stride), stride, out var p2, out var p3, out var q, out var q2); Vector128 left = Abs(q, q2); left = Sse2.Max(left, Abs(p2, p3)); left = Sse2.Max(left, Abs(p3, q)); Load16x4(ref Unsafe.Add(ref reference, offset), ref Unsafe.Add(ref reference, offset + 8 * stride), stride, out var p4, out var p5, out var q3, out var q4); left = Sse2.Max(left, Abs(p5, p4)); left = Sse2.Max(left, Abs(q4, q3)); left = Sse2.Max(left, Abs(q3, p5)); ComplexMask(q, q2, p4, p5, thresh, ithresh, ref left); DoFilter6Sse2(ref p3, ref q, ref q2, ref p4, ref p5, ref q3, left, hevThresh); Store16x4(p2, p3, q, q2, ref reference2, ref Unsafe.Add(ref reference2, 8 * stride), stride); Store16x4(p4, p5, q3, q4, ref Unsafe.Add(ref reference, offset), ref Unsafe.Add(ref reference, offset + 8 * stride), stride); } else { FilterLoop26(p, offset, 1, stride, 16, thresh, ithresh, hevThresh); } } public static void VFilter16i(Span p, int offset, int stride, int thresh, int ithresh, int hevThresh) { if (Sse2.IsSupported) { ref byte reference = ref MemoryMarshal.GetReference(p); Vector128 q = Unsafe.As>(ref Unsafe.Add(ref reference, offset)); Vector128 q2 = Unsafe.As>(ref Unsafe.Add(ref reference, offset + stride)); Vector128 p2 = Unsafe.As>(ref Unsafe.Add(ref reference, offset + 2 * stride)); Vector128 p3 = Unsafe.As>(ref Unsafe.Add(ref reference, offset + 3 * stride)); for (int num = 3; num > 0; num--) { Span span = p.Slice(offset + 2 * stride); offset += 4 * stride; Vector128 left = Abs(p3, p2); left = Sse2.Max(left, Abs(q, q2)); left = Sse2.Max(left, Abs(q2, p2)); q = Unsafe.As>(ref Unsafe.Add(ref reference, offset)); q2 = Unsafe.As>(ref Unsafe.Add(ref reference, offset + stride)); Vector128 vector = Unsafe.As>(ref Unsafe.Add(ref reference, offset + 2 * stride)); Vector128 vector2 = Unsafe.As>(ref Unsafe.Add(ref reference, offset + 3 * stride)); left = Sse2.Max(left, Abs(vector, vector2)); left = Sse2.Max(left, Abs(q, q2)); left = Sse2.Max(left, Abs(q2, vector)); ComplexMask(p2, p3, q, q2, thresh, ithresh, ref left); DoFilter4Sse2(ref p2, ref p3, ref q, ref q2, left, hevThresh); ref byte reference2 = ref MemoryMarshal.GetReference(span); Unsafe.As>(ref reference2) = p2.AsInt32(); Unsafe.As>(ref Unsafe.Add(ref reference2, stride)) = p3.AsInt32(); Unsafe.As>(ref Unsafe.Add(ref reference2, stride * 2)) = q.AsInt32(); Unsafe.As>(ref Unsafe.Add(ref reference2, stride * 3)) = q2.AsInt32(); p2 = vector; p3 = vector2; } } else { for (int num2 = 3; num2 > 0; num2--) { offset += 4 * stride; FilterLoop24(p, offset, stride, 1, 16, thresh, ithresh, hevThresh); } } } public static void HFilter16i(Span p, int offset, int stride, int thresh, int ithresh, int hevThresh) { if (Sse2.IsSupported) { ref byte reference = ref MemoryMarshal.GetReference(p); Load16x4(ref Unsafe.Add(ref reference, offset), ref Unsafe.Add(ref reference, offset + 8 * stride), stride, out var p2, out var p3, out var q, out var q2); for (int num = 3; num > 0; num--) { ref byte reference2 = ref Unsafe.Add(ref reference, offset + 2); offset += 4; Vector128 left = Abs(q, q2); left = Sse2.Max(left, Abs(p2, p3)); left = Sse2.Max(left, Abs(p3, q)); Load16x4(ref Unsafe.Add(ref reference, offset), ref Unsafe.Add(ref reference, offset + 8 * stride), stride, out p2, out p3, out var q3, out var q4); left = Sse2.Max(left, Abs(q3, q4)); left = Sse2.Max(left, Abs(p2, p3)); left = Sse2.Max(left, Abs(p3, q3)); ComplexMask(q, q2, p2, p3, thresh, ithresh, ref left); DoFilter4Sse2(ref q, ref q2, ref p2, ref p3, left, hevThresh); Store16x4(q, q2, p2, p3, ref reference2, ref Unsafe.Add(ref reference2, 8 * stride), stride); q = q3; q2 = q4; } } else { for (int num2 = 3; num2 > 0; num2--) { offset += 4; FilterLoop24(p, offset, 1, stride, 16, thresh, ithresh, hevThresh); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void VFilter8(Span u, Span v, int offset, int stride, int thresh, int ithresh, int hevThresh) { if (Sse2.IsSupported) { ref byte reference = ref MemoryMarshal.GetReference(u); ref byte reference2 = ref MemoryMarshal.GetReference(v); Vector128 p = LoadUvEdge(ref reference, ref reference2, offset - 4 * stride); Vector128 p2 = LoadUvEdge(ref reference, ref reference2, offset - 3 * stride); Vector128 p3 = LoadUvEdge(ref reference, ref reference2, offset - 2 * stride); Vector128 p4 = LoadUvEdge(ref reference, ref reference2, offset - stride); Vector128 left = Abs(p3, p4); left = Sse2.Max(left, Abs(p, p2)); left = Sse2.Max(left, Abs(p2, p3)); Vector128 q = LoadUvEdge(ref reference, ref reference2, offset); Vector128 q2 = LoadUvEdge(ref reference, ref reference2, offset + stride); Vector128 q3 = LoadUvEdge(ref reference, ref reference2, offset + 2 * stride); p = LoadUvEdge(ref reference, ref reference2, offset + 3 * stride); left = Sse2.Max(left, Abs(q2, q)); left = Sse2.Max(left, Abs(p, q3)); left = Sse2.Max(left, Abs(q3, q2)); ComplexMask(p3, p4, q, q2, thresh, ithresh, ref left); DoFilter6Sse2(ref p2, ref p3, ref p4, ref q, ref q2, ref q3, left, hevThresh); StoreUv(p2, ref reference, ref reference2, offset - 3 * stride); StoreUv(p3, ref reference, ref reference2, offset - 2 * stride); StoreUv(p4, ref reference, ref reference2, offset - stride); StoreUv(q, ref reference, ref reference2, offset); StoreUv(q2, ref reference, ref reference2, offset + stride); StoreUv(q3, ref reference, ref reference2, offset + 2 * stride); } else { FilterLoop26(u, offset, stride, 1, 8, thresh, ithresh, hevThresh); FilterLoop26(v, offset, stride, 1, 8, thresh, ithresh, hevThresh); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void HFilter8(Span u, Span v, int offset, int stride, int thresh, int ithresh, int hevThresh) { if (Sse2.IsSupported) { ref byte reference = ref MemoryMarshal.GetReference(u); ref byte reference2 = ref MemoryMarshal.GetReference(v); Load16x4(ref Unsafe.Add(ref reference, offset - 4), ref Unsafe.Add(ref reference2, offset - 4), stride, out var p, out var p2, out var q, out var q2); Vector128 left = Abs(q, q2); left = Sse2.Max(left, Abs(p, p2)); left = Sse2.Max(left, Abs(p2, q)); Load16x4(ref Unsafe.Add(ref reference, offset), ref Unsafe.Add(ref reference2, offset), stride, out var p3, out var p4, out var q3, out var q4); left = Sse2.Max(left, Abs(p4, p3)); left = Sse2.Max(left, Abs(q4, q3)); left = Sse2.Max(left, Abs(q3, p4)); ComplexMask(q, q2, p3, p4, thresh, ithresh, ref left); DoFilter6Sse2(ref p2, ref q, ref q2, ref p3, ref p4, ref q3, left, hevThresh); Store16x4(p, p2, q, q2, ref Unsafe.Add(ref reference, offset - 4), ref Unsafe.Add(ref reference2, offset - 4), stride); Store16x4(p3, p4, q3, q4, ref Unsafe.Add(ref reference, offset), ref Unsafe.Add(ref reference2, offset), stride); } else { FilterLoop26(u, offset, 1, stride, 8, thresh, ithresh, hevThresh); FilterLoop26(v, offset, 1, stride, 8, thresh, ithresh, hevThresh); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void VFilter8i(Span u, Span v, int offset, int stride, int thresh, int ithresh, int hevThresh) { if (Sse2.IsSupported) { ref byte reference = ref MemoryMarshal.GetReference(u); ref byte reference2 = ref MemoryMarshal.GetReference(v); Vector128 p = LoadUvEdge(ref reference, ref reference2, offset); Vector128 vector = LoadUvEdge(ref reference, ref reference2, offset + stride); Vector128 p2 = LoadUvEdge(ref reference, ref reference2, offset + stride * 2); Vector128 p3 = LoadUvEdge(ref reference, ref reference2, offset + stride * 3); Vector128 left = Abs(p2, p3); left = Sse2.Max(left, Abs(p, vector)); left = Sse2.Max(left, Abs(vector, p2)); offset += 4 * stride; Vector128 q = LoadUvEdge(ref reference, ref reference2, offset); Vector128 q2 = LoadUvEdge(ref reference, ref reference2, offset + stride); vector = LoadUvEdge(ref reference, ref reference2, offset + stride * 2); p = LoadUvEdge(ref reference, ref reference2, offset + stride * 3); left = Sse2.Max(left, Abs(q2, q)); left = Sse2.Max(left, Abs(p, vector)); left = Sse2.Max(left, Abs(vector, q2)); ComplexMask(p2, p3, q, q2, thresh, ithresh, ref left); DoFilter4Sse2(ref p2, ref p3, ref q, ref q2, left, hevThresh); StoreUv(p2, ref reference, ref reference2, offset + -2 * stride); StoreUv(p3, ref reference, ref reference2, offset + -1 * stride); StoreUv(q, ref reference, ref reference2, offset); StoreUv(q2, ref reference, ref reference2, offset + stride); } else { int offset2 = offset + 4 * stride; FilterLoop24(u, offset2, stride, 1, 8, thresh, ithresh, hevThresh); FilterLoop24(v, offset2, stride, 1, 8, thresh, ithresh, hevThresh); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void HFilter8i(Span u, Span v, int offset, int stride, int thresh, int ithresh, int hevThresh) { if (Sse2.IsSupported) { ref byte reference = ref MemoryMarshal.GetReference(u); ref byte reference2 = ref MemoryMarshal.GetReference(v); Load16x4(ref Unsafe.Add(ref reference, offset), ref Unsafe.Add(ref reference2, offset), stride, out var p, out var p2, out var q, out var q2); Vector128 left = Abs(q, q2); left = Sse2.Max(left, Abs(p, p2)); left = Sse2.Max(left, Abs(p2, q)); offset += 4; Load16x4(ref Unsafe.Add(ref reference, offset), ref Unsafe.Add(ref reference2, offset), stride, out var p3, out var p4, out p2, out p); left = Sse2.Max(left, Abs(p4, p3)); left = Sse2.Max(left, Abs(p, p2)); left = Sse2.Max(left, Abs(p2, p4)); ComplexMask(q, q2, p3, p4, thresh, ithresh, ref left); DoFilter4Sse2(ref q, ref q2, ref p3, ref p4, left, hevThresh); offset -= 2; Store16x4(q, q2, p3, p4, ref Unsafe.Add(ref reference, offset), ref Unsafe.Add(ref reference2, offset), stride); } else { int offset2 = offset + 4; FilterLoop24(u, offset2, 1, stride, 8, thresh, ithresh, hevThresh); FilterLoop24(v, offset2, 1, stride, 8, thresh, ithresh, hevThresh); } } public static void Mean16x4(Span input, Span dc) { if (Ssse3.IsSupported) { Vector128 vector = Unsafe.As>(ref MemoryMarshal.GetReference(input)); Vector128 vector2 = Unsafe.As>(ref MemoryMarshal.GetReference(input.Slice(32, 16))); Vector128 vector3 = Unsafe.As>(ref MemoryMarshal.GetReference(input.Slice(64, 16))); Vector128 vector4 = Unsafe.As>(ref MemoryMarshal.GetReference(input.Slice(96, 16))); Vector128 vector5 = Sse2.ShiftRightLogical(vector.AsInt16(), 8); Vector128 vector6 = Sse2.ShiftRightLogical(vector2.AsInt16(), 8); Vector128 vector7 = Sse2.ShiftRightLogical(vector3.AsInt16(), 8); Vector128 vector8 = Sse2.ShiftRightLogical(vector4.AsInt16(), 8); Vector128 vector9 = Sse2.And(vector, Mean16x4Mask); Vector128 vector10 = Sse2.And(vector2, Mean16x4Mask); Vector128 vector11 = Sse2.And(vector3, Mean16x4Mask); Vector128 vector12 = Sse2.And(vector4, Mean16x4Mask); Vector128 left = Sse2.Add(vector5.AsInt32(), vector9.AsInt32()); Vector128 right = Sse2.Add(vector6.AsInt32(), vector10.AsInt32()); Vector128 left2 = Sse2.Add(vector7.AsInt32(), vector11.AsInt32()); Vector128 right2 = Sse2.Add(vector8.AsInt32(), vector12.AsInt32()); Vector128 left3 = Sse2.Add(left, right); Vector128 right3 = Sse2.Add(left2, right2); Vector128 vector13 = Sse2.Add(left3, right3); Vector128 vector14 = Sse2.UnpackLow(Ssse3.HorizontalAdd(vector13.AsInt16(), vector13.AsInt16()), Vector128.Zero).AsUInt32(); Unsafe.As>(ref MemoryMarshal.GetReference(dc)) = vector14; return; } for (int i = 0; i < 4; i++) { uint num = 0u; for (int j = 0; j < 4; j++) { for (int k = 0; k < 4; k++) { num += input[k + j * 32]; } } dc[i] = num; input = input.Slice(4); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Avg2(byte a, byte b) { return (byte)(a + b + 1 >> 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Avg3(byte a, byte b, byte c) { return (byte)(a + 2 * b + c + 2 >> 2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Dst(Span dst, int x, int y, byte v) { dst[x + y * 32] = v; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Clip8B(int v) { return (byte)(((v & -256) == 0) ? ((uint)v) : ((v >= 0) ? 255u : 0u)); } public static int Vp8BitCost(int bit, byte proba) { if (bit != 0) { return WebpLookupTables.Vp8EntropyCost[255 - proba]; } return WebpLookupTables.Vp8EntropyCost[proba]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Put16(int v, Span dst) { for (int i = 0; i < 16; i++) { Memset(dst.Slice(i * 32), (byte)v, 0, 16); } } private static void TrueMotion(Span dst, Span yuv, int offset, int size) { int num = offset - 32; Span span = yuv.Slice(num); byte b = yuv[num - 1]; int num2 = offset - 1; byte b2 = yuv[num2]; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { dst[j] = (byte)Clamp255(b2 + span[j] - b); } num2 += 32; b2 = yuv[num2]; dst = dst.Slice(32); } } private static void FilterLoop24(Span p, int offset, int hStride, int vStride, int size, int thresh, int ithresh, int hevThresh) { int t = 2 * thresh + 1; while (size-- > 0) { if (NeedsFilter2(p, offset, hStride, t, ithresh)) { if (Hev(p, offset, hStride, hevThresh)) { DoFilter2(p, offset, hStride); } else { DoFilter4(p, offset, hStride); } } offset += vStride; } } private static void FilterLoop26(Span p, int offset, int hStride, int vStride, int size, int thresh, int ithresh, int hevThresh) { int t = 2 * thresh + 1; while (size-- > 0) { if (NeedsFilter2(p, offset, hStride, t, ithresh)) { if (Hev(p, offset, hStride, hevThresh)) { DoFilter2(p, offset, hStride); } else { DoFilter6(p, offset, hStride); } } offset += vStride; } } private static void DoFilter2(Span p, int offset, int step) { int num = p[offset - 2 * step]; int num2 = p[offset - step]; int num3 = p[offset]; int num4 = p[offset + step]; int num5 = 3 * (num3 - num2) + WebpLookupTables.Sclip1(num - num4); int num6 = WebpLookupTables.Sclip2(num5 + 4 >> 3); int num7 = WebpLookupTables.Sclip2(num5 + 3 >> 3); p[offset - step] = WebpLookupTables.Clip1(num2 + num7); p[offset] = WebpLookupTables.Clip1(num3 - num6); } private static void DoFilter2Sse2(ref Vector128 p1, ref Vector128 p0, ref Vector128 q0, ref Vector128 q1, int thresh) { Vector128 vector = Sse2.Xor(p1, SignBit); Vector128 vector2 = Sse2.Xor(q1, SignBit); Vector128 right = NeedsFilter(p1, p0, q0, q1, thresh); p0 = Sse2.Xor(p0, SignBit); q0 = Sse2.Xor(q0, SignBit); Vector128 left = GetBaseDelta(vector.AsSByte(), p0.AsSByte(), q0.AsSByte(), vector2.AsSByte()).AsByte(); left = Sse2.And(left, right); DoSimpleFilterSse2(ref p0, ref q0, left); p0 = Sse2.Xor(p0, SignBit); q0 = Sse2.Xor(q0, SignBit); } private static void DoFilter4Sse2(ref Vector128 p1, ref Vector128 p0, ref Vector128 q0, ref Vector128 q1, Vector128 mask, int tresh) { Vector128 notHev = GetNotHev(ref p1, ref p0, ref q0, ref q1, tresh); p1 = Sse2.Xor(p1, SignBit); p0 = Sse2.Xor(p0, SignBit); q0 = Sse2.Xor(q0, SignBit); q1 = Sse2.Xor(q1, SignBit); Vector128 vector = Sse2.SubtractSaturate(p1.AsSByte(), q1.AsSByte()); vector = Sse2.AndNot(notHev, vector.AsByte()).AsSByte(); Vector128 right = Sse2.SubtractSaturate(q0.AsSByte(), p0.AsSByte()); vector = Sse2.AddSaturate(vector, right); vector = Sse2.AddSaturate(vector, right); vector = Sse2.AddSaturate(vector, right); vector = Sse2.And(vector.AsByte(), mask).AsSByte(); right = Sse2.AddSaturate(vector, Three); Vector128 vector2 = Sse2.AddSaturate(vector, FourSByte); right = SignedShift8b(right.AsByte()); vector2 = SignedShift8b(vector2.AsByte()); p0 = Sse2.AddSaturate(p0.AsSByte(), right).AsByte(); q0 = Sse2.SubtractSaturate(q0.AsSByte(), vector2).AsByte(); p0 = Sse2.Xor(p0, SignBit); q0 = Sse2.Xor(q0, SignBit); right = Sse2.Add(vector2, SignBit.AsSByte()); vector2 = Sse2.Average(right.AsByte(), Vector128.Zero).AsSByte(); vector2 = Sse2.Subtract(vector2, SixtyFour); vector2 = Sse2.And(notHev, vector2.AsByte()).AsSByte(); q1 = Sse2.SubtractSaturate(q1.AsSByte(), vector2).AsByte(); p1 = Sse2.AddSaturate(p1.AsSByte(), vector2).AsByte(); p1 = Sse2.Xor(p1.AsByte(), SignBit); q1 = Sse2.Xor(q1.AsByte(), SignBit); } private static void DoFilter6Sse2(ref Vector128 p2, ref Vector128 p1, ref Vector128 p0, ref Vector128 q0, ref Vector128 q1, ref Vector128 q2, Vector128 mask, int tresh) { Vector128 notHev = GetNotHev(ref p1, ref p0, ref q0, ref q1, tresh); p1 = Sse2.Xor(p1, SignBit); p0 = Sse2.Xor(p0, SignBit); q0 = Sse2.Xor(q0, SignBit); q1 = Sse2.Xor(q1, SignBit); p2 = Sse2.Xor(p2, SignBit); q2 = Sse2.Xor(q2, SignBit); Vector128 baseDelta = GetBaseDelta(p1.AsSByte(), p0.AsSByte(), q0.AsSByte(), q1.AsSByte()); Vector128 fl = Sse2.And(right: Sse2.AndNot(notHev, mask), left: baseDelta.AsByte()); DoSimpleFilterSse2(ref p0, ref q0, fl); fl = Sse2.And(right: Sse2.And(notHev, mask), left: baseDelta.AsByte()); Vector128 vector = Sse2.UnpackLow(Vector128.Zero, fl); Vector128 vector2 = Sse2.UnpackHigh(Vector128.Zero, fl); Vector128 vector3 = Sse2.MultiplyHigh(vector.AsInt16(), Nine.AsInt16()); Vector128 vector4 = Sse2.MultiplyHigh(vector2.AsInt16(), Nine.AsInt16()); Vector128 vector5 = Sse2.Add(vector3, SixtyThree.AsInt16()); Vector128 vector6 = Sse2.Add(vector4, SixtyThree.AsInt16()); Vector128 vector7 = Sse2.Add(vector5, vector3); Vector128 vector8 = Sse2.Add(vector6, vector4); Vector128 a0Low = Sse2.Add(vector7, vector3); Vector128 a0High = Sse2.Add(vector8, vector4); Update2Pixels(ref p2, ref q2, vector5, vector6); Update2Pixels(ref p1, ref q1, vector7, vector8); Update2Pixels(ref p0, ref q0, a0Low, a0High); } private static void DoSimpleFilterSse2(ref Vector128 p0, ref Vector128 q0, Vector128 fl) { Vector128 vector = Sse2.AddSaturate(fl.AsSByte(), Three); Vector128 vector2 = Sse2.AddSaturate(fl.AsSByte(), FourSByte); vector2 = SignedShift8b(vector2.AsByte()).AsSByte(); vector = SignedShift8b(vector.AsByte()).AsSByte(); q0 = Sse2.SubtractSaturate(q0.AsSByte(), vector2).AsByte(); p0 = Sse2.AddSaturate(p0.AsSByte(), vector).AsByte(); } private static Vector128 GetNotHev(ref Vector128 p1, ref Vector128 p0, ref Vector128 q0, ref Vector128 q1, int hevThresh) { Vector128 left = Abs(p1, p0); Vector128 right = Abs(q1, q0); return Sse2.CompareEqual(Sse2.SubtractSaturate(right: Vector128.Create((byte)hevThresh), left: Sse2.Max(left, right)), Vector128.Zero); } private static void DoFilter4(Span p, int offset, int step) { int index = offset - 2 * step; int num = p[index]; int num2 = p[offset - step]; int num3 = p[offset]; int num4 = p[offset + step]; int num5 = 3 * (num3 - num2); int num6 = WebpLookupTables.Sclip2(num5 + 4 >> 3); int num7 = WebpLookupTables.Sclip2(num5 + 3 >> 3); int num8 = num6 + 1 >> 1; p[index] = WebpLookupTables.Clip1(num + num8); p[offset - step] = WebpLookupTables.Clip1(num2 + num7); p[offset] = WebpLookupTables.Clip1(num3 - num6); p[offset + step] = WebpLookupTables.Clip1(num4 - num8); } private static void DoFilter6(Span p, int offset, int step) { int num = 2 * step; int num2 = 3 * step; int index = offset - step; int num3 = p[offset - num2]; int num4 = p[offset - num]; int num5 = p[index]; int num6 = p[offset]; int num7 = p[offset + step]; int num8 = p[offset + num]; int num9 = WebpLookupTables.Sclip1(3 * (num6 - num5) + WebpLookupTables.Sclip1(num4 - num7)); int num10 = 27 * num9 + 63 >> 7; int num11 = 18 * num9 + 63 >> 7; int num12 = 9 * num9 + 63 >> 7; p[offset - num2] = WebpLookupTables.Clip1(num3 + num12); p[offset - num] = WebpLookupTables.Clip1(num4 + num11); p[index] = WebpLookupTables.Clip1(num5 + num10); p[offset] = WebpLookupTables.Clip1(num6 - num10); p[offset + step] = WebpLookupTables.Clip1(num7 - num11); p[offset + num] = WebpLookupTables.Clip1(num8 - num12); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool NeedsFilter(Span p, int offset, int step, int t) { int num = p[offset + -2 * step]; int num2 = p[offset - step]; int num3 = p[offset]; int num4 = p[offset + step]; return 4 * WebpLookupTables.Abs0(num2 - num3) + WebpLookupTables.Abs0(num - num4) <= t; } private static bool NeedsFilter2(Span p, int offset, int step, int t, int it) { int num = 2 * step; int num2 = 3 * step; int num3 = p[offset - 4 * step]; int num4 = p[offset - num2]; int num5 = p[offset - num]; int num6 = p[offset - step]; int num7 = p[offset]; int num8 = p[offset + step]; int num9 = p[offset + num]; int num10 = p[offset + num2]; if (4 * WebpLookupTables.Abs0(num6 - num7) + WebpLookupTables.Abs0(num5 - num8) > t) { return false; } if (WebpLookupTables.Abs0(num3 - num4) <= it && WebpLookupTables.Abs0(num4 - num5) <= it && WebpLookupTables.Abs0(num5 - num6) <= it && WebpLookupTables.Abs0(num10 - num9) <= it && WebpLookupTables.Abs0(num9 - num8) <= it) { return WebpLookupTables.Abs0(num8 - num7) <= it; } return false; } private static Vector128 NeedsFilter(Vector128 p1, Vector128 p0, Vector128 q0, Vector128 q1, int thresh) { Vector128 vector = Vector128.Create((byte)thresh); Vector128 left = Abs(p1, q1); Vector128 right = Vector128.Create((byte)254); Vector128 vector2 = Sse2.ShiftRightLogical(Sse2.And(left, right).AsInt16(), 1); Vector128 vector3 = Abs(p0, q0); return Sse2.CompareEqual(Sse2.SubtractSaturate(Sse2.AddSaturate(Sse2.AddSaturate(vector3, vector3).AsByte(), vector2.AsByte()), vector.AsByte()), Vector128.Zero); } private static void Load16x4(ref byte r0, ref byte r8, int stride, out Vector128 p1, out Vector128 p0, out Vector128 q0, out Vector128 q1) { Load8x4(ref r0, stride, out var p2, out var q2); Load8x4(ref r8, stride, out p0, out q1); p1 = Sse2.UnpackLow(p2.AsInt64(), p0.AsInt64()).AsByte(); p0 = Sse2.UnpackHigh(p2.AsInt64(), p0.AsInt64()).AsByte(); q0 = Sse2.UnpackLow(q2.AsInt64(), q1.AsInt64()).AsByte(); q1 = Sse2.UnpackHigh(q2.AsInt64(), q1.AsInt64()).AsByte(); } private static void Load8x4(ref byte bRef, int stride, out Vector128 p, out Vector128 q) { uint e = Unsafe.As(ref Unsafe.Add(ref bRef, 6 * stride)); uint e2 = Unsafe.As(ref Unsafe.Add(ref bRef, 2 * stride)); uint e3 = Unsafe.As(ref Unsafe.Add(ref bRef, 4 * stride)); Vector128 vector = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref bRef, 0)), e3, e2, e).AsByte(); Vector128 vector2 = Vector128.Create(e3: Unsafe.As(ref Unsafe.Add(ref bRef, 7 * stride)), e2: Unsafe.As(ref Unsafe.Add(ref bRef, 3 * stride)), e1: Unsafe.As(ref Unsafe.Add(ref bRef, 5 * stride)), e0: Unsafe.As(ref Unsafe.Add(ref bRef, stride))).AsByte(); Vector128 vector3 = Sse2.UnpackLow(vector.AsSByte(), vector2.AsSByte()); Vector128 vector4 = Sse2.UnpackHigh(vector.AsSByte(), vector2.AsSByte()); Vector128 vector5 = Sse2.UnpackLow(vector3.AsInt16(), vector4.AsInt16()); Vector128 vector6 = Sse2.UnpackHigh(vector3.AsInt16(), vector4.AsInt16()); p = Sse2.UnpackLow(vector5.AsInt32(), vector6.AsInt32()).AsByte(); q = Sse2.UnpackHigh(vector5.AsInt32(), vector6.AsInt32()).AsByte(); } private static void Store16x4(Vector128 p1, Vector128 p0, Vector128 q0, Vector128 q1, ref byte r0Ref, ref byte r8Ref, int stride) { Vector128 vector = Sse2.UnpackLow(p1, p0); Vector128 vector2 = Sse2.UnpackHigh(p1, p0); Vector128 vector3 = Sse2.UnpackLow(q0, q1); Vector128 vector4 = Sse2.UnpackHigh(q0, q1); Vector128 vector5 = vector; vector = Sse2.UnpackLow(vector5.AsInt16(), vector3.AsInt16()).AsByte(); vector3 = Sse2.UnpackHigh(vector5.AsInt16(), vector3.AsInt16()).AsByte(); Vector128 vector6 = vector2; vector2 = Sse2.UnpackLow(vector6.AsInt16(), vector4.AsInt16()).AsByte(); vector4 = Sse2.UnpackHigh(vector6.AsInt16(), vector4.AsInt16()).AsByte(); Store4x4(vector, ref r0Ref, stride); Store4x4(vector3, ref Unsafe.Add(ref r0Ref, 4 * stride), stride); Store4x4(vector2, ref r8Ref, stride); Store4x4(vector4, ref Unsafe.Add(ref r8Ref, 4 * stride), stride); } private static void Store4x4(Vector128 x, ref byte dstRef, int stride) { int num = 0; for (int i = 0; i < 4; i++) { Unsafe.As(ref Unsafe.Add(ref dstRef, num)) = Sse2.ConvertToInt32(x.AsInt32()); x = Sse2.ShiftRightLogical128BitLane(x, 4); num += stride; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 GetBaseDelta(Vector128 p1, Vector128 p0, Vector128 q0, Vector128 q1) { Vector128 left = Sse2.SubtractSaturate(p1, q1); Vector128 vector = Sse2.SubtractSaturate(q0, p0); Vector128 right = Sse2.AddSaturate(left, vector); Vector128 right2 = Sse2.AddSaturate(vector, right); return Sse2.AddSaturate(vector, right2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 SignedShift8b(Vector128 x) { Vector128 vector = Sse2.UnpackLow(Vector128.Zero, x); Vector128 vector2 = Sse2.UnpackHigh(Vector128.Zero, x); Vector128 left = Sse2.ShiftRightArithmetic(vector.AsInt16(), 11); Vector128 right = Sse2.ShiftRightArithmetic(vector2.AsInt16(), 11); return Sse2.PackSignedSaturate(left, right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ComplexMask(Vector128 p1, Vector128 p0, Vector128 q0, Vector128 q1, int thresh, int ithresh, ref Vector128 mask) { Vector128 right = Vector128.Create((byte)ithresh); Vector128 left = Sse2.CompareEqual(Sse2.SubtractSaturate(mask, right), Vector128.Zero); Vector128 right2 = NeedsFilter(p1, p0, q0, q1, thresh); mask = Sse2.And(left, right2); } private static void Update2Pixels(ref Vector128 pi, ref Vector128 qi, Vector128 a0Low, Vector128 a0High) { Vector128 left = Sse2.ShiftRightArithmetic(a0Low, 7); Vector128 right = Sse2.ShiftRightArithmetic(a0High, 7); Vector128 right2 = Sse2.PackSignedSaturate(left, right); pi = Sse2.AddSaturate(pi.AsSByte(), right2).AsByte(); qi = Sse2.SubtractSaturate(qi.AsSByte(), right2).AsByte(); pi = Sse2.Xor(pi, SignBit.AsByte()); qi = Sse2.Xor(qi, SignBit.AsByte()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 LoadUvEdge(ref byte uRef, ref byte vRef, int offset) { Vector128 left = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref uRef, offset)), 0L); Vector128 right = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref vRef, offset)), 0L); return Sse2.UnpackLow(left, right).AsByte(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void StoreUv(Vector128 x, ref byte uRef, ref byte vRef, int offset) { Unsafe.As>(ref Unsafe.Add(ref uRef, offset)) = x.GetLower(); Unsafe.As>(ref Unsafe.Add(ref vRef, offset)) = x.GetUpper(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 Abs(Vector128 p, Vector128 q) { return Sse2.Or(Sse2.SubtractSaturate(q, p), Sse2.SubtractSaturate(p, q)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool Hev(Span p, int offset, int step, int thresh) { byte num = p[offset - 2 * step]; int num2 = p[offset - step]; int num3 = p[offset]; int num4 = p[offset + step]; if (WebpLookupTables.Abs0(num - num2) <= thresh) { return WebpLookupTables.Abs0(num4 - num3) > thresh; } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Store(Span dst, int x, int y, int v) { int index = x + y * 32; dst[index] = Clip8B(dst[index] + (v >> 3)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Store2(Span dst, int y, int dc, int d, int c) { Store(dst, 0, y, dc + d); Store(dst, 1, y, dc + c); Store(dst, 2, y, dc - c); Store(dst, 3, y, dc - d); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Mul1(int a) { return (a * 20091 >> 16) + a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Mul2(int a) { return a * 35468 >> 16; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Put8x8uv(byte value, Span dst) { int num = 256; for (int i = 0; i < num; i += 32) { Memset(dst, value, i, 8); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Memset(Span dst, byte value, int startIdx, int count) { dst.Slice(startIdx, count).Fill(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Clamp255(int x) { if (x >= 0) { if (x <= 255) { return x; } return 255; } return 0; } } internal class PassStats { public bool IsFirst { get; set; } public float Dq { get; set; } public float Q { get; set; } public float LastQ { get; set; } public float Qmin { get; } public float Qmax { get; } public double Value { get; set; } public double LastValue { get; set; } public double Target { get; } public bool DoSizeSearch { get; } public PassStats(long targetSize, float targetPsnr, int qMin, int qMax, int quality) { bool flag = targetSize != 0; IsFirst = true; Dq = 10f; Qmin = qMin; Qmax = qMax; Q = Numerics.Clamp(quality, qMin, qMax); LastQ = Q; Target = (flag ? ((float)targetSize) : ((targetPsnr > 0f) ? targetPsnr : 40f)); Value = 0.0; LastValue = 0.0; DoSizeSearch = flag; } public float ComputeNextQ() { float value; if (!IsFirst) { value = ((Value == LastValue) ? 0f : ((float)((Target - Value) / (LastValue - Value) * (double)(LastQ - Q)))); } else { value = ((Value > Target) ? (0f - Dq) : Dq); IsFirst = false; } Dq = Numerics.Clamp(value, -30f, 30f); LastQ = Q; LastValue = Value; Q = Numerics.Clamp(Q + Dq, Qmin, Qmax); return Q; } } internal static class QuantEnc { private static readonly byte[] Zigzag = new byte[16] { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 }; private static readonly ushort[] WeightY = new ushort[16] { 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2 }; private const int MaxLevel = 2047; private static readonly Vector128 MaxCoeff2047 = Vector128.Create((short)2047); private static readonly Vector256 MaxCoeff2047Vec256 = Vector256.Create((short)2047); private static readonly Vector256 Cst256 = Vector256.Create(0, 1, 2, 3, 8, 9, 254, byte.MaxValue, 10, 11, 4, 5, 6, 7, 12, 13, 2, 3, 8, 9, 10, 11, 4, 5, 254, byte.MaxValue, 6, 7, 12, 13, 14, 15); private static readonly Vector256 Cst78 = Vector256.Create(254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 14, 15, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 0, 1, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue); private static readonly Vector128 CstLo = Vector128.Create(0, 1, 2, 3, 8, 9, 254, byte.MaxValue, 10, 11, 4, 5, 6, 7, 12, 13); private static readonly Vector128 Cst7 = Vector128.Create(254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 14, 15, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue); private static readonly Vector128 CstHi = Vector128.Create(2, 3, 8, 9, 10, 11, 4, 5, 254, byte.MaxValue, 6, 7, 12, 13, 14, 15); private static readonly Vector128 Cst8 = Vector128.Create(254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 0, 1, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue, 254, byte.MaxValue); private const int C1 = 7; private const int C2 = 8; private const int DSHIFT = 4; private const int DSCALE = 1; public static void PickBestIntra16(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8SegmentInfo[] segmentInfos, Vp8EncProba proba) { Vp8SegmentInfo vp8SegmentInfo = segmentInfos[it.CurrentMacroBlockInfo.Segment]; int lambdaI = vp8SegmentInfo.LambdaI16; int tLambda = vp8SegmentInfo.TLambda; Span span = it.YuvIn.AsSpan(0); Span scratch = it.Scratch3; Vp8ModeScore vp8ModeScore = new Vp8ModeScore(); Vp8Residual res = new Vp8Residual(); Vp8ModeScore vp8ModeScore2 = vp8ModeScore; Vp8ModeScore vp8ModeScore3 = rd; bool flag = IsFlatSource16(span); rd.ModeI16 = -1; for (int i = 0; i < 4; i++) { Span span2 = it.YuvOut2.AsSpan(0); vp8ModeScore2.ModeI16 = i; vp8ModeScore2.Nz = (uint)ReconstructIntra16(it, vp8SegmentInfo, vp8ModeScore2, span2, i); vp8ModeScore2.D = LossyUtils.Vp8_Sse16X16(span, span2); vp8ModeScore2.SD = ((tLambda != 0) ? Mult8B(tLambda, LossyUtils.Vp8Disto16X16(span, span2, WeightY, scratch)) : 0); vp8ModeScore2.H = WebpConstants.Vp8FixedCostsI16[i]; vp8ModeScore2.R = it.GetCostLuma16(vp8ModeScore2, proba, res); if (flag) { flag = IsFlat(vp8ModeScore2.YAcLevels, 16, 0); if (flag) { vp8ModeScore2.D *= 2L; vp8ModeScore2.SD *= 2L; } } vp8ModeScore2.SetRdScore(lambdaI); if (i == 0 || vp8ModeScore2.Score < vp8ModeScore3.Score) { Vp8ModeScore vp8ModeScore4 = vp8ModeScore2; vp8ModeScore2 = vp8ModeScore3; vp8ModeScore3 = vp8ModeScore4; it.SwapOut(); } } if (vp8ModeScore3 != rd) { rd = vp8ModeScore3; } rd.SetRdScore(vp8SegmentInfo.LambdaMode); it.SetIntra16Mode(rd.ModeI16); if ((rd.Nz & 0x100FFFF) == 16777216 && rd.D > vp8SegmentInfo.MinDisto) { vp8SegmentInfo.StoreMaxDelta(rd.YDcLevels); } } public static bool PickBestIntra4(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8SegmentInfo[] segmentInfos, Vp8EncProba proba, int maxI4HeaderBits) { Vp8SegmentInfo vp8SegmentInfo = segmentInfos[it.CurrentMacroBlockInfo.Segment]; int lambdaI = vp8SegmentInfo.LambdaI4; int tLambda = vp8SegmentInfo.TLambda; Span span = it.YuvIn.AsSpan(0); Span yuvOut = it.YuvOut2.AsSpan(0); Span scratch = it.Scratch3; int num = 0; Vp8ModeScore vp8ModeScore = new Vp8ModeScore(); if (maxI4HeaderBits == 0) { return false; } vp8ModeScore.InitScore(); vp8ModeScore.H = 211L; vp8ModeScore.SetRdScore(vp8SegmentInfo.LambdaMode); it.StartI4(); Vp8ModeScore vp8ModeScore2 = new Vp8ModeScore(); Vp8ModeScore vp8ModeScore3 = new Vp8ModeScore(); Vp8Residual res = new Vp8Residual(); Span levels = new short[16]; do { int num2 = 1; vp8ModeScore2.Clear(); int num3 = -1; Span span2 = span.Slice(WebpLookupTables.Vp8Scan[it.I4]); short[] costModeI = it.GetCostModeI4(rd.ModesI4); Span span3 = yuvOut.Slice(WebpLookupTables.Vp8Scan[it.I4]); Span span4 = it.Scratch.AsSpan(); span4.Clear(); vp8ModeScore2.InitScore(); it.MakeIntra4Preds(); for (int i = 0; i < 10; i++) { vp8ModeScore3.Clear(); levels.Clear(); vp8ModeScore3.Nz = (uint)ReconstructIntra4(it, vp8SegmentInfo, levels, span2, span4, i); vp8ModeScore3.D = LossyUtils.Vp8_Sse4X4(span2, span4); vp8ModeScore3.SD = ((tLambda != 0) ? Mult8B(tLambda, LossyUtils.Vp8Disto4X4(span2, span4, WeightY, scratch)) : 0); vp8ModeScore3.H = costModeI[i]; if (i > 0 && IsFlat(levels, num2, 3)) { vp8ModeScore3.R = 140 * num2; } else { vp8ModeScore3.R = 0L; } vp8ModeScore3.SetRdScore(lambdaI); if (num3 < 0 || vp8ModeScore3.Score < vp8ModeScore2.Score) { vp8ModeScore3.R += it.GetCostLuma4(levels, proba, res); vp8ModeScore3.SetRdScore(lambdaI); if (num3 < 0 || vp8ModeScore3.Score < vp8ModeScore2.Score) { vp8ModeScore2.CopyScore(vp8ModeScore3); num3 = i; Span span5 = span4; span4 = span3; span3 = span5; levels.CopyTo(vp8ModeScore.YAcLevels.AsSpan(it.I4 * 16, 16)); } } } vp8ModeScore2.SetRdScore(vp8SegmentInfo.LambdaMode); vp8ModeScore.AddScore(vp8ModeScore2); if (vp8ModeScore.Score >= rd.Score) { return false; } num += (int)vp8ModeScore2.H; if (num > maxI4HeaderBits) { return false; } LossyUtils.Vp8Copy4X4(span3, yuvOut.Slice(WebpLookupTables.Vp8Scan[it.I4])); rd.ModesI4[it.I4] = (byte)num3; it.TopNz[it.I4 & 3] = (it.LeftNz[it.I4 >> 2] = ((vp8ModeScore2.Nz != 0) ? 1 : 0)); } while (it.RotateI4(yuvOut)); rd.CopyScore(vp8ModeScore); it.SetIntra4Mode(rd.ModesI4); it.SwapOut(); vp8ModeScore.YAcLevels.AsSpan().CopyTo(rd.YAcLevels); return true; } public static void PickBestUv(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8SegmentInfo[] segmentInfos, Vp8EncProba proba) { Vp8SegmentInfo vp8SegmentInfo = segmentInfos[it.CurrentMacroBlockInfo.Segment]; int lambdaUv = vp8SegmentInfo.LambdaUv; Span a = it.YuvIn.AsSpan(16); Span span = it.YuvOut2.AsSpan(16); Span span2 = it.YuvOut.AsSpan(16); Span span3 = span2; Vp8ModeScore vp8ModeScore = new Vp8ModeScore(); Vp8ModeScore vp8ModeScore2 = new Vp8ModeScore(); Vp8Residual res = new Vp8Residual(); rd.ModeUv = -1; vp8ModeScore.InitScore(); for (int i = 0; i < 4; i++) { vp8ModeScore2.Clear(); vp8ModeScore2.Nz = (uint)ReconstructUv(it, vp8SegmentInfo, vp8ModeScore2, span, i); vp8ModeScore2.D = LossyUtils.Vp8_Sse16X8(a, span); vp8ModeScore2.SD = 0L; vp8ModeScore2.H = WebpConstants.Vp8FixedCostsUv[i]; vp8ModeScore2.R = it.GetCostUv(vp8ModeScore2, proba, res); if (i > 0 && IsFlat(vp8ModeScore2.UvLevels, 8, 2)) { vp8ModeScore2.R += 1120L; } vp8ModeScore2.SetRdScore(lambdaUv); if (i == 0 || vp8ModeScore2.Score < vp8ModeScore.Score) { vp8ModeScore.CopyScore(vp8ModeScore2); rd.ModeUv = i; vp8ModeScore2.UvLevels.CopyTo(rd.UvLevels.AsSpan()); for (int j = 0; j < 2; j++) { rd.Derr[j, 0] = vp8ModeScore2.Derr[j, 0]; rd.Derr[j, 1] = vp8ModeScore2.Derr[j, 1]; rd.Derr[j, 2] = vp8ModeScore2.Derr[j, 2]; } Span span4 = span3; span3 = span; span = span4; } } it.SetIntraUvMode(rd.ModeUv); rd.AddScore(vp8ModeScore); if (span3 != span2) { LossyUtils.Vp8Copy16X8(span3, span2); } it.StoreDiffusionErrors(rd); } public static int ReconstructIntra16(Vp8EncIterator it, Vp8SegmentInfo dqm, Vp8ModeScore rd, Span yuvOut, int mode) { Span span = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]); Span span2 = it.YuvIn.AsSpan(0); int num = 0; Span span3 = it.Scratch2.AsSpan(); Span scratch = it.Scratch3.AsSpan(0, 16); span3.Clear(); scratch.Clear(); Span span4 = span3.Slice(0, 16); Span span5 = span3.Slice(16, 256); for (int i = 0; i < 16; i += 2) { Vp8Encoding.FTransform2(span2.Slice(WebpLookupTables.Vp8Scan[i]), span.Slice(WebpLookupTables.Vp8Scan[i]), span5.Slice(i * 16, 16), span5.Slice((i + 1) * 16, 16), scratch); } Vp8Encoding.FTransformWht(span5, span4, scratch); num |= QuantizeBlock(span4, rd.YDcLevels, ref dqm.Y2) << 24; for (int i = 0; i < 16; i += 2) { span5[i * 16] = (span5[(i + 1) * 16] = 0); num |= Quantize2Blocks(span5.Slice(i * 16, 32), rd.YAcLevels.AsSpan(i * 16, 32), ref dqm.Y1) << i; } LossyUtils.TransformWht(span4, span5, scratch); for (int i = 0; i < 16; i += 2) { Vp8Encoding.ITransformTwo(span.Slice(WebpLookupTables.Vp8Scan[i]), span5.Slice(i * 16, 32), yuvOut.Slice(WebpLookupTables.Vp8Scan[i]), scratch); } return num; } public static int ReconstructIntra4(Vp8EncIterator it, Vp8SegmentInfo dqm, Span levels, Span src, Span yuvOut, int mode) { Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]); Span span = it.Scratch2.AsSpan(0, 16); Span scratch = it.Scratch3.AsSpan(0, 16); Vp8Encoding.FTransform(src, reference, span, scratch); int result = QuantizeBlock(span, levels, ref dqm.Y1); Vp8Encoding.ITransformOne(reference, span, yuvOut, scratch); return result; } public static int ReconstructUv(Vp8EncIterator it, Vp8SegmentInfo dqm, Vp8ModeScore rd, Span yuvOut, int mode) { Span span = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]); Span span2 = it.YuvIn.AsSpan(16); int num = 0; Span tmp = it.Scratch2.AsSpan(0, 128); Span scratch = it.Scratch3.AsSpan(0, 16); for (int i = 0; i < 8; i += 2) { Vp8Encoding.FTransform2(span2.Slice(WebpLookupTables.Vp8ScanUv[i]), span.Slice(WebpLookupTables.Vp8ScanUv[i]), tmp.Slice(i * 16, 16), tmp.Slice((i + 1) * 16, 16), scratch); } CorrectDcValues(it, ref dqm.Uv, tmp, rd); for (int i = 0; i < 8; i += 2) { num |= Quantize2Blocks(tmp.Slice(i * 16, 32), rd.UvLevels.AsSpan(i * 16, 32), ref dqm.Uv) << i; } for (int i = 0; i < 8; i += 2) { Vp8Encoding.ITransformTwo(span.Slice(WebpLookupTables.Vp8ScanUv[i]), tmp.Slice(i * 16, 32), yuvOut.Slice(WebpLookupTables.Vp8ScanUv[i]), scratch); } return num << 16; } public static void RefineUsingDistortion(Vp8EncIterator it, Vp8SegmentInfo[] segmentInfos, Vp8ModeScore rd, bool tryBothModes, bool refineUvMode, int mbHeaderLimit) { long num = 36028797018963967L; int num2 = 0; bool flag = tryBothModes || it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16; Vp8SegmentInfo vp8SegmentInfo = segmentInfos[it.CurrentMacroBlockInfo.Segment]; long num3 = vp8SegmentInfo.I4Penalty; long num4 = 0L; long num5 = (tryBothModes ? mbHeaderLimit : 36028797018963967L); if (flag) { int intra16Mode = -1; Span span = it.YuvIn.AsSpan(0); for (int i = 0; i < 4; i++) { Span b = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[i]); long num6 = LossyUtils.Vp8_Sse16X16(span, b) * 256 + WebpConstants.Vp8FixedCostsI16[i] * 106; if ((i <= 0 || WebpConstants.Vp8FixedCostsI16[i] <= num5) && num6 < num) { intra16Mode = i; num = num6; } } if ((it.X == 0 || it.Y == 0) && IsFlatSource16(span)) { intra16Mode = ((it.X != 0) ? 2 : 0); tryBothModes = false; } it.SetIntra16Mode(intra16Mode); } if (tryBothModes || !flag) { flag = false; it.StartI4(); do { int num7 = -1; long num8 = 36028797018963967L; Span span2 = it.YuvIn.AsSpan(WebpLookupTables.Vp8Scan[it.I4]); short[] costModeI = it.GetCostModeI4(rd.ModesI4); it.MakeIntra4Preds(); for (int i = 0; i < 10; i++) { Span b2 = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[i]); long num9 = LossyUtils.Vp8_Sse4X4(span2, b2) * 256 + costModeI[i] * 11; if (num9 < num8) { num7 = i; num8 = num9; } } num4 += costModeI[num7]; rd.ModesI4[it.I4] = (byte)num7; num3 += num8; if (num3 >= num || num4 > num5) { flag = true; break; } Span yuvOut = it.YuvOut2.AsSpan(WebpLookupTables.Vp8Scan[it.I4]); num2 |= ReconstructIntra4(it, vp8SegmentInfo, rd.YAcLevels.AsSpan(it.I4 * 16, 16), span2, yuvOut, num7) << it.I4; } while (it.RotateI4(it.YuvOut2.AsSpan(0))); } if (!flag) { it.SetIntra4Mode(rd.ModesI4); it.SwapOut(); num = num3; } else { int mode = it.Preds[it.PredIdx]; num2 = ReconstructIntra16(it, vp8SegmentInfo, rd, it.YuvOut.AsSpan(0), mode); } if (refineUvMode) { int intraUvMode = -1; long num10 = 36028797018963967L; Span a = it.YuvIn.AsSpan(16); for (int i = 0; i < 4; i++) { Span b3 = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[i]); long num11 = LossyUtils.Vp8_Sse16X8(a, b3) * 256 + WebpConstants.Vp8FixedCostsUv[i] * 120; if (num11 < num10) { intraUvMode = i; num10 = num11; } } it.SetIntraUvMode(intraUvMode); } num2 |= ReconstructUv(it, vp8SegmentInfo, rd, it.YuvOut.AsSpan(16), it.CurrentMacroBlockInfo.UvMode); rd.Nz = (uint)num2; rd.Score = num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Quantize2Blocks(Span input, Span output, ref Vp8Matrix mtx) { return QuantizeBlock(input.Slice(0, 16), output.Slice(0, 16), ref mtx) | (QuantizeBlock(input.Slice(16, 16), output.Slice(16, 16), ref mtx) << 1); } public unsafe static int QuantizeBlock(Span input, Span output, ref Vp8Matrix mtx) { if (Avx2.IsSupported) { Vector256 vector = Unsafe.As>(ref MemoryMarshal.GetReference(input)); Vector256 right = Unsafe.As>(ref mtx.IQ[0]); Vector256 vector2 = Unsafe.As>(ref mtx.Q[0]); Vector256 vector3 = Avx2.Abs(vector); Avx2.Add(right: Unsafe.As>(ref mtx.Sharpen[0]), left: vector3.AsInt16()); Vector256 right2 = Avx2.MultiplyHigh(vector3, right); Vector256 left = Avx2.MultiplyLow(vector3, right); Vector256 vector4 = Avx2.UnpackLow(left, right2); Vector256 vector5 = Avx2.UnpackHigh(left, right2); Vector256 vector6 = Unsafe.As>(ref mtx.Bias[0]); Vector256 vector7 = Unsafe.As>(ref mtx.Bias[8]); vector4 = Avx2.Add(vector4.AsInt32(), vector6.AsInt32()).AsUInt16(); vector5 = Avx2.Add(vector5.AsInt32(), vector7.AsInt32()).AsUInt16(); vector4 = Avx2.ShiftRightArithmetic(vector4.AsInt32(), 17).AsUInt16(); vector5 = Avx2.ShiftRightArithmetic(vector5.AsInt32(), 17).AsUInt16(); Vector256 vector8 = Avx2.Sign(Avx2.Min(Avx2.PackSignedSaturate(vector4.AsInt32(), vector5.AsInt32()), MaxCoeff2047Vec256), vector); vector = Avx2.MultiplyLow(vector8, vector2.AsInt16()); Unsafe.As>(ref MemoryMarshal.GetReference(input)) = vector; Vector256 left2 = Avx2.Shuffle(vector8.AsByte(), Cst256); Vector256 vector9 = Avx2.Shuffle(vector8.AsByte(), Cst78); Vector256 right3 = Avx2.Permute2x128(vector9, vector9, 1); Vector256 vector10 = Avx2.Or(left2, right3).AsInt16(); Unsafe.As>(ref MemoryMarshal.GetReference(output)) = vector10; return (Avx2.MoveMask(Avx2.CompareEqual(Avx2.PackSignedSaturate(vector10, vector10), Vector256.Zero)) != -1) ? 1 : 0; } if (Sse41.IsSupported) { Vector128 vector11 = Unsafe.As>(ref MemoryMarshal.GetReference(input)); Vector128 vector12 = Unsafe.As>(ref MemoryMarshal.GetReference(input.Slice(8, 8))); Vector128 right4 = Unsafe.As>(ref mtx.IQ[0]); Vector128 right5 = Unsafe.As>(ref mtx.IQ[8]); Vector128 vector13 = Unsafe.As>(ref mtx.Q[0]); Vector128 vector14 = Unsafe.As>(ref mtx.Q[8]); Vector128 vector15 = Ssse3.Abs(vector11); Vector128 vector16 = Ssse3.Abs(vector12); Vector128 right6 = Unsafe.As>(ref mtx.Sharpen[0]); Vector128 right7 = Unsafe.As>(ref mtx.Sharpen[8]); Sse2.Add(vector15.AsInt16(), right6); Sse2.Add(vector16.AsInt16(), right7); Vector128 right8 = Sse2.MultiplyHigh(vector15, right4); Vector128 left3 = Sse2.MultiplyLow(vector15, right4); Vector128 right9 = Sse2.MultiplyHigh(vector16, right5); Vector128 left4 = Sse2.MultiplyLow(vector16, right5); Vector128 vector17 = Sse2.UnpackLow(left3, right8); Vector128 vector18 = Sse2.UnpackHigh(left3, right8); Vector128 vector19 = Sse2.UnpackLow(left4, right9); Vector128 vector20 = Sse2.UnpackHigh(left4, right9); Vector128 vector21 = Unsafe.As>(ref mtx.Bias[0]); Vector128 vector22 = Unsafe.As>(ref mtx.Bias[4]); Vector128 vector23 = Unsafe.As>(ref mtx.Bias[8]); Vector128 vector24 = Unsafe.As>(ref mtx.Bias[12]); vector17 = Sse2.Add(vector17.AsInt32(), vector21.AsInt32()).AsUInt16(); vector18 = Sse2.Add(vector18.AsInt32(), vector22.AsInt32()).AsUInt16(); vector19 = Sse2.Add(vector19.AsInt32(), vector23.AsInt32()).AsUInt16(); vector20 = Sse2.Add(vector20.AsInt32(), vector24.AsInt32()).AsUInt16(); vector17 = Sse2.ShiftRightArithmetic(vector17.AsInt32(), 17).AsUInt16(); vector18 = Sse2.ShiftRightArithmetic(vector18.AsInt32(), 17).AsUInt16(); vector19 = Sse2.ShiftRightArithmetic(vector19.AsInt32(), 17).AsUInt16(); vector20 = Sse2.ShiftRightArithmetic(vector20.AsInt32(), 17).AsUInt16(); Vector128 left5 = Sse2.PackSignedSaturate(vector17.AsInt32(), vector18.AsInt32()); Vector128 left6 = Sse2.PackSignedSaturate(vector19.AsInt32(), vector20.AsInt32()); left5 = Sse2.Min(left5, MaxCoeff2047); Vector128 left7 = Sse2.Min(left6, MaxCoeff2047); left5 = Ssse3.Sign(left5, vector11); Vector128 vector25 = Ssse3.Sign(left7, vector12); vector11 = Sse2.MultiplyLow(left5, vector13.AsInt16()); vector12 = Sse2.MultiplyLow(vector25, vector14.AsInt16()); ref short reference = ref MemoryMarshal.GetReference(input); Unsafe.As>(ref reference) = vector11; Unsafe.As>(ref Unsafe.Add(ref reference, 8)) = vector12; Vector128 left8 = Ssse3.Shuffle(left5.AsByte(), CstLo); Vector128 right10 = Ssse3.Shuffle(left5.AsByte(), Cst7); Vector128 left9 = Ssse3.Shuffle(vector25.AsByte(), CstHi); Vector128 right11 = Ssse3.Shuffle(vector25.AsByte(), Cst8); Vector128 vector26 = Sse2.Or(left8, right11); Vector128 vector27 = Sse2.Or(left9, right10); ref short reference2 = ref MemoryMarshal.GetReference(output); Unsafe.As>(ref reference2) = vector26.AsInt16(); Unsafe.As>(ref Unsafe.Add(ref reference2, 8)) = vector27.AsInt16(); return (Sse2.MoveMask(Sse2.CompareEqual(Sse2.PackSignedSaturate(vector26.AsInt16(), vector27.AsInt16()), Vector128.Zero)) != 65535) ? 1 : 0; } int num = -1; for (int i = 0; i < 16; i++) { int num2 = Zigzag[i]; bool flag = input[num2] < 0; uint num3 = (uint)((flag ? (-input[num2]) : input[num2]) + mtx.Sharpen[num2]); if (num3 > mtx.ZThresh[num2]) { uint num4 = mtx.Q[num2]; uint iQ = mtx.IQ[num2]; uint b = mtx.Bias[num2]; int num5 = QuantDiv(num3, iQ, b); if (num5 > 2047) { num5 = 2047; } if (flag) { num5 = -num5; } input[num2] = (short)(num5 * (int)num4); output[i] = (short)num5; if (num5 != 0) { num = i; } } else { output[i] = 0; input[num2] = 0; } } return (num >= 0) ? 1 : 0; } public unsafe static int QuantizeSingle(Span v, ref Vp8Matrix mtx) { int num = v[0]; bool flag = num < 0; if (flag) { num = -num; } if (num > (int)mtx.ZThresh[0]) { int num2 = QuantDiv((uint)num, mtx.IQ[0], mtx.Bias[0]) * mtx.Q[0]; int num3 = num - num2; v[0] = (short)(flag ? (-num2) : num2); return (flag ? (-num3) : num3) >> 1; } v[0] = 0; return (flag ? (-num) : num) >> 1; } public static void CorrectDcValues(Vp8EncIterator it, ref Vp8Matrix mtx, Span tmp, Vp8ModeScore rd) { for (int i = 0; i <= 1; i++) { Span span = it.TopDerr.AsSpan(it.X * 4 + i, 2); Span span2 = it.LeftDerr.AsSpan(i, 2); Span v = tmp.Slice(i * 4 * 16, 64); v[0] += (short)(7 * span[0] + 8 * span2[0] >> 3); int num = QuantizeSingle(v, ref mtx); v[16] += (short)(7 * span[1] + 8 * num >> 3); int num2 = QuantizeSingle(v.Slice(16), ref mtx); v[32] += (short)(7 * num + 8 * span2[1] >> 3); int num3 = QuantizeSingle(v.Slice(32), ref mtx); v[48] += (short)(7 * num2 + 8 * num3 >> 3); int num4 = QuantizeSingle(v.Slice(48), ref mtx); rd.Derr[i, 0] = num2; rd.Derr[i, 1] = num3; rd.Derr[i, 2] = num4; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsFlatSource16(Span src) { Span span = BitConverter.GetBytes((uint)(src[0] * 16843009)).AsSpan(); for (nint num = 0; num < 16; num++) { if (!src.Slice(0, 4).SequenceEqual(span) || !src.Slice(4, 4).SequenceEqual(span) || !src.Slice(8, 4).SequenceEqual(span) || !src.Slice(12, 4).SequenceEqual(span)) { return false; } src = src.Slice(32); } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsFlat(Span levels, int numBlocks, int thresh) { int num = 0; ref short reference = ref MemoryMarshal.GetReference(levels); int num2 = 0; while (numBlocks-- > 0) { for (nint num3 = 1; num3 < 16; num3++) { num += ((Unsafe.Add(ref reference, num2) != 0) ? 1 : 0); if (num > thresh) { return false; } } num2 += 16; } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Mult8B(int a, int b) { return a * b + 128 >> 8; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int QuantDiv(uint n, uint iQ, uint b) { return (int)(n * iQ + b >> 17); } } internal class Vp8BandProbas { public Vp8ProbaArray[] Probabilities { get; } public Vp8BandProbas() { Probabilities = new Vp8ProbaArray[3]; for (int i = 0; i < 3; i++) { Probabilities[i] = new Vp8ProbaArray(); } } } internal class Vp8CostArray { public ushort[] Costs { get; } public Vp8CostArray() { Costs = new ushort[68]; } } internal class Vp8Costs { public Vp8CostArray[] Costs { get; } public Vp8Costs() { Costs = new Vp8CostArray[3]; for (int i = 0; i < 3; i++) { Costs[i] = new Vp8CostArray(); } } } internal class Vp8Decoder : IDisposable { private Vp8MacroBlock leftMacroBlock; public Vp8FrameHeader FrameHeader { get; } public Vp8PictureHeader PictureHeader { get; } public Vp8FilterHeader FilterHeader { get; } public Vp8SegmentHeader SegmentHeader { get; } public int NumPartsMinusOne { get; set; } public Vp8BitReader[] Vp8BitReaders { get; } public Vp8QuantMatrix[] DeQuantMatrices { get; } public bool UseSkipProbability { get; set; } public byte SkipProbability { get; set; } public Vp8Proba Probabilities { get; set; } public byte[] IntraT { get; set; } public byte[] IntraL { get; } public int MbWidth { get; } public int MbHeight { get; } public int TopLeftMbX { get; set; } public int TopLeftMbY { get; set; } public int BottomRightMbX { get; set; } public int BottomRightMbY { get; set; } public int MbX { get; set; } public int MbY { get; set; } public Vp8MacroBlockData[] MacroBlockData { get; } public Vp8MacroBlock[] MacroBlockInfo { get; } public LoopFilter Filter { get; set; } public Vp8FilterInfo[,] FilterStrength { get; } public IMemoryOwner YuvBuffer { get; } public Vp8TopSamples[] YuvTopSamples { get; } public IMemoryOwner CacheY { get; } public IMemoryOwner CacheU { get; } public IMemoryOwner CacheV { get; } public int CacheYOffset { get; set; } public int CacheUvOffset { get; set; } public int CacheYStride { get; } public int CacheUvStride { get; } public IMemoryOwner TmpYBuffer { get; } public IMemoryOwner TmpUBuffer { get; } public IMemoryOwner TmpVBuffer { get; } public IMemoryOwner Pixels { get; } public Vp8FilterInfo[] FilterInfo { get; set; } public Vp8MacroBlock CurrentMacroBlock => MacroBlockInfo[MbX]; public Vp8MacroBlock LeftMacroBlock => leftMacroBlock ?? (leftMacroBlock = new Vp8MacroBlock()); public Vp8MacroBlockData CurrentBlockData => MacroBlockData[MbX]; public Vp8Decoder(Vp8FrameHeader frameHeader, Vp8PictureHeader pictureHeader, Vp8SegmentHeader segmentHeader, Vp8Proba probabilities, MemoryAllocator memoryAllocator) { FilterHeader = new Vp8FilterHeader(); FrameHeader = frameHeader; PictureHeader = pictureHeader; SegmentHeader = segmentHeader; Probabilities = probabilities; IntraL = new byte[4]; MbWidth = (int)(PictureHeader.Width + 15 >> 4); MbHeight = (int)(PictureHeader.Height + 15 >> 4); CacheYStride = 16 * MbWidth; CacheUvStride = 8 * MbWidth; MacroBlockInfo = new Vp8MacroBlock[MbWidth + 1]; MacroBlockData = new Vp8MacroBlockData[MbWidth]; YuvTopSamples = new Vp8TopSamples[MbWidth]; FilterInfo = new Vp8FilterInfo[MbWidth]; for (int i = 0; i < MbWidth; i++) { MacroBlockInfo[i] = new Vp8MacroBlock(); MacroBlockData[i] = new Vp8MacroBlockData(); YuvTopSamples[i] = new Vp8TopSamples(); FilterInfo[i] = new Vp8FilterInfo(); } MacroBlockInfo[MbWidth] = new Vp8MacroBlock(); DeQuantMatrices = new Vp8QuantMatrix[4]; FilterStrength = new Vp8FilterInfo[4, 2]; for (int j = 0; j < 4; j++) { DeQuantMatrices[j] = new Vp8QuantMatrix(); for (int k = 0; k < 2; k++) { FilterStrength[j, k] = new Vp8FilterInfo(); } } uint width = pictureHeader.Width; uint height = pictureHeader.Height; byte num = WebpConstants.FilterExtraRows[2]; int num2 = num * CacheYStride; int num3 = num / 2 * CacheUvStride; YuvBuffer = memoryAllocator.Allocate(832 + num2); CacheY = memoryAllocator.Allocate(16 * CacheYStride + num2); int length = 16 * CacheUvStride + num3; CacheU = memoryAllocator.Allocate(length); CacheV = memoryAllocator.Allocate(length); TmpYBuffer = memoryAllocator.Allocate((int)width); TmpUBuffer = memoryAllocator.Allocate((int)width); TmpVBuffer = memoryAllocator.Allocate((int)width); Pixels = memoryAllocator.Allocate((int)(width * height * 4)); Vp8BitReaders = new Vp8BitReader[8]; } public void PrecomputeFilterStrengths() { if (Filter == LoopFilter.None) { return; } Vp8FilterHeader filterHeader = FilterHeader; for (int i = 0; i < 4; i++) { int num; if (SegmentHeader.UseSegment) { num = SegmentHeader.FilterStrength[i]; if (!SegmentHeader.Delta) { num += filterHeader.FilterLevel; } } else { num = filterHeader.FilterLevel; } for (int j = 0; j <= 1; j++) { Vp8FilterInfo vp8FilterInfo = FilterStrength[i, j]; int num2 = num; if (filterHeader.UseLfDelta) { num2 += filterHeader.RefLfDelta[0]; if (j > 0) { num2 += filterHeader.ModeLfDelta[0]; } } num2 = ((num2 >= 0) ? ((num2 > 63) ? 63 : num2) : 0); if (num2 > 0) { int num3 = num2; if (filterHeader.Sharpness > 0) { num3 = ((filterHeader.Sharpness <= 4) ? (num3 >> 1) : (num3 >> 2)); int num4 = 9 - filterHeader.Sharpness; if (num3 > num4) { num3 = num4; } } if (num3 < 1) { num3 = 1; } vp8FilterInfo.InnerLevel = (byte)num3; vp8FilterInfo.Limit = (byte)(2 * num2 + num3); vp8FilterInfo.HighEdgeVarianceThreshold = (byte)((num2 >= 40) ? 2u : ((num2 >= 15) ? 1u : 0u)); } else { vp8FilterInfo.Limit = 0; } vp8FilterInfo.UseInnerFiltering = j == 1; } } } public void Dispose() { YuvBuffer.Dispose(); CacheY.Dispose(); CacheU.Dispose(); CacheV.Dispose(); TmpYBuffer.Dispose(); TmpUBuffer.Dispose(); TmpVBuffer.Dispose(); Pixels.Dispose(); } } internal class Vp8EncIterator { public const int YOffEnc = 0; public const int UOffEnc = 16; public const int VOffEnc = 24; private const int MaxIntra16Mode = 2; private const int MaxIntra4Mode = 2; private const int MaxUvMode = 2; private const int DefaultAlpha = -1; private readonly int mbw; private readonly int mbh; private readonly int predsWidth; private readonly byte[] vp8TopLeftI4 = new byte[16] { 17, 21, 25, 29, 13, 17, 21, 25, 9, 13, 17, 21, 5, 9, 13, 17 }; private int currentMbIdx; private int nzIdx; private int predIdx; private int yTopIdx; private int uvTopIdx; public int X { get; set; } public int Y { get; set; } public byte[] YuvIn { get; } public byte[] YuvOut { get; set; } public byte[] YuvOut2 { get; set; } public byte[] YuvP { get; } public byte[] YLeft { get; } public byte[] UvLeft { get; } public sbyte[] LeftDerr { get; } public byte[] YTop { get; } public byte[] UvTop { get; } public byte[] Preds { get; } public int PredIdx => predIdx; public uint[] Nz { get; } public sbyte[] TopDerr { get; } public byte[] I4Boundary { get; } public int I4BoundaryIdx { get; set; } public int I4 { get; set; } public int[] TopNz { get; } public int[] LeftNz { get; } public long LumaBits { get; set; } public long[,] BitCount { get; } public long UvBits { get; set; } public int CountDown { get; set; } public byte[] Scratch { get; } public short[] Scratch2 { get; } public int[] Scratch3 { get; } public Vp8MacroBlockInfo CurrentMacroBlockInfo => Mb[currentMbIdx]; private Vp8MacroBlockInfo[] Mb { get; } public Vp8EncIterator(byte[] yTop, byte[] uvTop, uint[] nz, Vp8MacroBlockInfo[] mb, byte[] preds, sbyte[] topDerr, int mbw, int mbh) { YTop = yTop; UvTop = uvTop; Nz = nz; Mb = mb; Preds = preds; TopDerr = topDerr; LeftDerr = new sbyte[4]; this.mbw = mbw; this.mbh = mbh; currentMbIdx = 0; nzIdx = 1; yTopIdx = 0; uvTopIdx = 0; predsWidth = 4 * mbw + 1; predIdx = predsWidth; YuvIn = new byte[512]; YuvOut = new byte[512]; YuvOut2 = new byte[512]; YuvP = new byte[1792]; YLeft = new byte[32]; UvLeft = new byte[32]; TopNz = new int[9]; LeftNz = new int[9]; I4Boundary = new byte[37]; BitCount = new long[4, 3]; Scratch = new byte[512]; Scratch2 = new short[272]; Scratch3 = new int[16]; byte value = 204; YuvIn.AsSpan().Fill(value); YuvOut.AsSpan().Fill(value); YuvOut2.AsSpan().Fill(value); YuvP.AsSpan().Fill(value); YLeft.AsSpan().Fill(value); UvLeft.AsSpan().Fill(value); Scratch.AsSpan().Fill(value); Reset(); } public void Init() { Reset(); } public void InitFilter() { } public void StartI4() { I4 = 0; I4BoundaryIdx = vp8TopLeftI4[0]; for (int i = 0; i < 17; i++) { I4Boundary[i] = YLeft[15 - i + 1]; } Span span = YTop.AsSpan(yTopIdx); for (int i = 0; i < 16; i++) { I4Boundary[17 + i] = span[i]; } if (X < mbw - 1) { for (int i = 16; i < 20; i++) { I4Boundary[17 + i] = span[i]; } } else { for (int i = 16; i < 20; i++) { I4Boundary[17 + i] = I4Boundary[32]; } } NzToBytes(); } public void Import(Span y, Span u, Span v, int yStride, int uvStride, int width, int height, bool importBoundarySamples) { int num = (Y * yStride + X) * 16; int num2 = (Y * uvStride + X) * 8; Span src = y.Slice(num); Span src2 = u.Slice(num2); Span src3 = v.Slice(num2); int num3 = Math.Min(width - X * 16, 16); int num4 = Math.Min(height - Y * 16, 16); int num5 = num3 + 1 >> 1; int num6 = num4 + 1 >> 1; Span dst = YuvIn.AsSpan(0); Span dst2 = YuvIn.AsSpan(16); Span dst3 = YuvIn.AsSpan(24); ImportBlock(src, yStride, dst, num3, num4, 16); ImportBlock(src2, uvStride, dst2, num5, num6, 8); ImportBlock(src3, uvStride, dst3, num5, num6, 8); if (!importBoundarySamples) { return; } if (X == 0) { InitLeft(); } else { Span span = YLeft.AsSpan(); Span span2 = UvLeft.AsSpan(0, 16); Span span3 = UvLeft.AsSpan(16, 16); if (Y == 0) { span[0] = 127; span2[0] = 127; span3[0] = 127; } else { span[0] = y[num - 1 - yStride]; span2[0] = u[num2 - 1 - uvStride]; span3[0] = v[num2 - 1 - uvStride]; } ImportLine(y.Slice(num - 1), yStride, span.Slice(1), num4, 16); ImportLine(u.Slice(num2 - 1), uvStride, span2.Slice(1), num6, 8); ImportLine(v.Slice(num2 - 1), uvStride, span3.Slice(1), num6, 8); } Span dst4 = YTop.AsSpan(yTopIdx, 16); if (Y == 0) { dst4.Fill(127); UvTop.AsSpan(uvTopIdx, 16).Fill(127); } else { ImportLine(y.Slice(num - yStride), 1, dst4, num3, 16); ImportLine(u.Slice(num2 - uvStride), 1, UvTop.AsSpan(uvTopIdx, 8), num5, 8); ImportLine(v.Slice(num2 - uvStride), 1, UvTop.AsSpan(uvTopIdx + 8, 8), num5, 8); } } public int FastMbAnalyze(int quality) { int num = 8 + 9 * quality / 100; Span span = stackalloc uint[16]; for (int i = 0; i < 16; i += 4) { LossyUtils.Mean16x4(YuvIn.AsSpan(i * 32), span.Slice(i, 4)); } uint num2 = 0u; uint num3 = 0u; for (int i = 0; i < 16; i++) { num2 += span[i]; num3 += span[i] * span[i]; } if (num * num3 < num2 * num2) { SetIntra16Mode(0); } else { byte[] intra4Mode = new byte[16]; SetIntra4Mode(intra4Mode); } return 0; } public int MbAnalyzeBestIntra16Mode() { int num = 2; int num2 = -1; int intra16Mode = 0; MakeLuma16Preds(); for (int i = 0; i < num; i++) { Vp8Histogram vp8Histogram = new Vp8Histogram(); vp8Histogram.CollectHistogram(YuvIn.AsSpan(0), YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[i]), 0, 16); int alpha = vp8Histogram.GetAlpha(); if (alpha > num2) { num2 = alpha; intra16Mode = i; } } SetIntra16Mode(intra16Mode); return num2; } public int MbAnalyzeBestIntra4Mode(int bestAlpha) { byte[] array = new byte[16]; int num = 2; Vp8Histogram vp8Histogram = new Vp8Histogram(); int num2 = 0; StartI4(); do { int num3 = -1; Vp8Histogram[] array2 = new Vp8Histogram[2]; Span reference = YuvIn.AsSpan(WebpLookupTables.Vp8Scan[I4]); MakeIntra4Preds(); for (int i = 0; i < num; i++) { array2[num2] = new Vp8Histogram(); array2[num2].CollectHistogram(reference, YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[i]), 0, 1); int alpha = array2[num2].GetAlpha(); if (alpha > num3) { num3 = alpha; array[I4] = (byte)i; num2 ^= 1; } } array2[num2 ^ 1].Merge(vp8Histogram); } while (RotateI4(YuvIn.AsSpan(0))); int alpha2 = vp8Histogram.GetAlpha(); if (alpha2 > bestAlpha) { SetIntra4Mode(array); bestAlpha = alpha2; } return bestAlpha; } public int MbAnalyzeBestUvMode() { int num = -1; int num2 = 0; int intraUvMode = 0; int num3 = 2; MakeChroma8Preds(); for (int i = 0; i < num3; i++) { Vp8Histogram vp8Histogram = new Vp8Histogram(); vp8Histogram.CollectHistogram(YuvIn.AsSpan(16), YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[i]), 16, 24); int alpha = vp8Histogram.GetAlpha(); if (alpha > num) { num = alpha; } if (i == 0 || alpha < num2) { num2 = alpha; intraUvMode = i; } } SetIntraUvMode(intraUvMode); return num; } public void SetIntra16Mode(int mode) { Span span = Preds.AsSpan(predIdx); for (int i = 0; i < 4; i++) { span.Slice(0, 4).Fill((byte)mode); span = span.Slice(predsWidth); } CurrentMacroBlockInfo.MacroBlockType = Vp8MacroBlockType.I16X16; } public void SetIntra4Mode(byte[] modes) { int num = 0; int num2 = predIdx; for (int num3 = 4; num3 > 0; num3--) { modes.AsSpan(num, 4).CopyTo(Preds.AsSpan(num2)); num2 += predsWidth; num += 4; } CurrentMacroBlockInfo.MacroBlockType = Vp8MacroBlockType.I4X4; } public int GetCostLuma16(Vp8ModeScore rd, Vp8EncProba proba, Vp8Residual res) { int num = 0; NzToBytes(); res.Init(0, 1, proba); res.SetCoeffs(rd.YDcLevels); num += res.GetResidualCost(TopNz[8] + LeftNz[8]); res.Init(1, 0, proba); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { int ctx = TopNz[j] + LeftNz[i]; res.SetCoeffs(rd.YAcLevels.AsSpan((j + i * 4) * 16, 16)); num += res.GetResidualCost(ctx); TopNz[j] = (LeftNz[i] = ((res.Last >= 0) ? 1 : 0)); } } return num; } public short[] GetCostModeI4(byte[] modes) { int num = predsWidth; int num2 = predIdx; int num3 = I4 & 3; int num4 = I4 >> 2; int num5 = ((num3 == 0) ? Preds[num2 + num4 * num - 1] : modes[I4 - 1]); int num6 = ((num4 == 0) ? Preds[num2 - num + num3] : modes[I4 - 4]); return WebpLookupTables.Vp8FixedCostsI4[num6, num5]; } public int GetCostLuma4(Span levels, Vp8EncProba proba, Vp8Residual res) { int num = I4 & 3; int num2 = I4 >> 2; res.Init(0, 3, proba); int ctx = TopNz[num] + LeftNz[num2]; res.SetCoeffs(levels); return 0 + res.GetResidualCost(ctx); } public int GetCostUv(Vp8ModeScore rd, Vp8EncProba proba, Vp8Residual res) { int num = 0; NzToBytes(); res.Init(0, 2, proba); for (int i = 0; i <= 2; i += 2) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { int ctx = TopNz[4 + i + k] + LeftNz[4 + i + j]; res.SetCoeffs(rd.UvLevels.AsSpan((i * 2 + k + j * 2) * 16, 16)); num += res.GetResidualCost(ctx); TopNz[4 + i + k] = (LeftNz[4 + i + j] = ((res.Last >= 0) ? 1 : 0)); } } } return num; } public void SetIntraUvMode(int mode) { CurrentMacroBlockInfo.UvMode = mode; } public void SetSkip(bool skip) { CurrentMacroBlockInfo.Skip = skip; } public void SetSegment(int segment) { CurrentMacroBlockInfo.Segment = segment; } public void StoreDiffusionErrors(Vp8ModeScore rd) { for (int i = 0; i <= 1; i++) { Span span = TopDerr.AsSpan(X * 4 + i, 2); Span span2 = LeftDerr.AsSpan(i, 2); span2[0] = (sbyte)rd.Derr[i, 0]; span2[1] = (sbyte)(3 * rd.Derr[i, 2] >> 2); span[0] = (sbyte)rd.Derr[i, 1]; span[1] = (sbyte)(rd.Derr[i, 2] - span2[1]); } } public bool IsDone() { return CountDown <= 0; } public bool Next() { if (++X == mbw) { SetRow(++Y); } else { currentMbIdx++; nzIdx++; predIdx += 4; yTopIdx += 16; uvTopIdx += 16; } return --CountDown > 0; } public void SaveBoundary() { int x = X; int y = Y; Span span = YuvOut.AsSpan(0); Span span2 = YuvOut.AsSpan(16); if (x < mbw - 1) { for (int i = 0; i < 16; i++) { YLeft[i + 1] = span[15 + i * 32]; } for (int j = 0; j < 8; j++) { UvLeft[j + 1] = span2[7 + j * 32]; UvLeft[j + 16 + 1] = span2[15 + j * 32]; } YLeft[0] = YTop[yTopIdx + 15]; UvLeft[0] = UvTop[uvTopIdx + 7]; UvLeft[16] = UvTop[uvTopIdx + 8 + 7]; } if (y < mbh - 1) { span.Slice(480, 16).CopyTo(YTop.AsSpan(yTopIdx)); span2.Slice(224, 16).CopyTo(UvTop.AsSpan(uvTopIdx)); } } public bool RotateI4(Span yuvOut) { Span span = yuvOut.Slice(WebpLookupTables.Vp8Scan[I4]); Span span2 = I4Boundary.AsSpan(); int i4BoundaryIdx = I4BoundaryIdx; for (int i = 0; i <= 3; i++) { span2[i4BoundaryIdx - 4 + i] = span[i + 96]; } if ((I4 & 3) != 3) { for (int i = 0; i <= 2; i++) { span2[i4BoundaryIdx + i] = span[3 + (2 - i) * 32]; } } else { for (int i = 0; i <= 3; i++) { span2[i4BoundaryIdx + i] = span2[i4BoundaryIdx + i + 4]; } } int i2 = I4 + 1; I4 = i2; if (I4 == 16) { return false; } I4BoundaryIdx = vp8TopLeftI4[I4]; return true; } public void ResetAfterSkip() { if (CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16) { Nz[nzIdx] = 0u; LeftNz[8] = 0; } else { Nz[nzIdx] &= 16777216u; } } public void MakeLuma16Preds() { Span left = ((X != 0) ? YLeft.AsSpan() : ((Span)null)); Span top = ((Y != 0) ? YTop.AsSpan(yTopIdx) : ((Span)null)); Vp8Encoding.EncPredLuma16(YuvP, left, top); } public void MakeChroma8Preds() { Span left = ((X != 0) ? UvLeft.AsSpan() : ((Span)null)); Span top = ((Y != 0) ? UvTop.AsSpan(uvTopIdx) : ((Span)null)); Vp8Encoding.EncPredChroma8(YuvP, left, top); } public void MakeIntra4Preds() { Vp8Encoding.EncPredLuma4(YuvP, I4Boundary, I4BoundaryIdx, Scratch.AsSpan(0, 4)); } public void SwapOut() { byte[] yuvOut = YuvOut; YuvOut = YuvOut2; YuvOut2 = yuvOut; } public void NzToBytes() { Span span = Nz.AsSpan(); uint nz = span[nzIdx - 1]; uint nz2 = span[nzIdx]; Span span2 = TopNz; Span span3 = LeftNz; span2[0] = Bit(nz2, 12); span2[1] = Bit(nz2, 13); span2[2] = Bit(nz2, 14); span2[3] = Bit(nz2, 15); span2[4] = Bit(nz2, 18); span2[5] = Bit(nz2, 19); span2[6] = Bit(nz2, 22); span2[7] = Bit(nz2, 23); span2[8] = Bit(nz2, 24); span3[0] = Bit(nz, 3); span3[1] = Bit(nz, 7); span3[2] = Bit(nz, 11); span3[3] = Bit(nz, 15); span3[4] = Bit(nz, 17); span3[5] = Bit(nz, 19); span3[6] = Bit(nz, 21); span3[7] = Bit(nz, 23); } public void BytesToNz() { uint num = 0u; int[] topNz = TopNz; int[] leftNz = LeftNz; num |= (uint)((topNz[0] << 12) | (topNz[1] << 13)); num |= (uint)((topNz[2] << 14) | (topNz[3] << 15)); num |= (uint)((topNz[4] << 18) | (topNz[5] << 19)); num |= (uint)((topNz[6] << 22) | (topNz[7] << 23)); num |= (uint)(topNz[8] << 24); num |= (uint)((leftNz[0] << 3) | (leftNz[1] << 7)); num |= (uint)(leftNz[2] << 11); num |= (uint)((leftNz[4] << 17) | (leftNz[6] << 21)); Nz[nzIdx] = num; } private void ImportBlock(Span src, int srcStride, Span dst, int w, int h, int size) { int num = 0; int num2 = 0; for (int i = 0; i < h; i++) { src.Slice(num2, w).CopyTo(dst.Slice(num)); if (w < size) { dst.Slice(num + w, size - w).Fill(dst[num + w - 1]); } num += 32; num2 += srcStride; } for (int j = h; j < size; j++) { dst.Slice(num - 32, size).CopyTo(dst.Slice(num)); num += 32; } } private void ImportLine(Span src, int srcStride, Span dst, int len, int totalLen) { int num = 0; int i; for (i = 0; i < len; i++) { dst[i] = src[num]; num += srcStride; } for (; i < totalLen; i++) { dst[i] = dst[len - 1]; } } private void Reset() { SetRow(0); SetCountDown(mbw * mbh); InitTop(); Array.Clear(BitCount, 0, BitCount.Length); } private void SetRow(int y) { X = 0; Y = y; currentMbIdx = y * mbw; nzIdx = 1; yTopIdx = 0; uvTopIdx = 0; predIdx = predsWidth + y * 4 * predsWidth; InitLeft(); } private void InitLeft() { Span span = YLeft.AsSpan(); Span span2 = UvLeft.AsSpan(0, 16); Span span3 = UvLeft.AsSpan(16, 16); byte b = (byte)((Y > 0) ? 129u : 127u); span[0] = b; span2[0] = b; span3[0] = b; span.Slice(1, 16).Fill(129); span2.Slice(1, 8).Fill(129); span3.Slice(1, 8).Fill(129); LeftNz[8] = 0; LeftDerr.AsSpan().Clear(); } private void InitTop() { int length = mbw * 16; YTop.AsSpan(0, length).Fill(127); UvTop.AsSpan().Fill(127); Nz.AsSpan().Clear(); int num = 4 * mbw + 1; int num2 = 4 * mbh + 1; int num3 = num * num2; Preds.AsSpan(num3 + predsWidth, mbw).Clear(); TopDerr.AsSpan().Clear(); } private int Bit(uint nz, int n) { return ((nz & (1 << n)) != 0) ? 1 : 0; } private void SetCountDown(int countDown) { CountDown = countDown; } } internal class Vp8Encoder : IDisposable { private readonly MemoryAllocator memoryAllocator; private readonly Configuration configuration; private readonly int quality; private readonly WebpEncodingMethod method; private readonly int entropyPasses; private readonly int filterStrength; private readonly int spatialNoiseShaping; private Vp8BitWriter bitWriter; private readonly Vp8RdLevel rdOptLevel; private int maxI4HeaderBits; private int alpha; private int uvAlpha; private readonly bool alphaCompression; private const int NumMbSegments = 4; private const int MaxItersKMeans = 6; private const float DqLimit = 0.4f; private const ulong Partition0SizeLimit = 1069547520uL; private const long HeaderSizeEstimate = 30L; private const int QMin = 0; private const int QMax = 100; private static ReadOnlySpan AverageBytesPerMb => new byte[8] { 50, 24, 16, 9, 7, 5, 3, 2 }; public int BaseQuant { get; set; } public Vp8EncProba Proba { get; } public Vp8EncSegmentHeader SegmentHeader { get; private set; } public Vp8SegmentInfo[] SegmentInfos { get; } public Vp8MacroBlockInfo[] MbInfo { get; } public Vp8FilterHeader FilterHeader { get; } public int Alpha { get; set; } public int Width { get; } public int Height { get; } public int PredsWidth { get; } public int Mbw { get; } public int Mbh { get; } public int DqY1Dc { get; private set; } public int DqY2Ac { get; private set; } public int DqY2Dc { get; private set; } public int DqUvAc { get; private set; } public int DqUvDc { get; private set; } private IMemoryOwner Y { get; } private IMemoryOwner U { get; } private IMemoryOwner V { get; } public byte[] YTop { get; } public byte[] UvTop { get; } public uint[] Nz { get; } public byte[] Preds { get; } public sbyte[] TopDerr { get; } private int MbHeaderLimit { get; } public Vp8Encoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, WebpEncodingMethod method, int entropyPasses, int filterStrength, int spatialNoiseShaping, bool alphaCompression) { this.memoryAllocator = memoryAllocator; this.configuration = configuration; Width = width; Height = height; this.quality = Numerics.Clamp(quality, 0, 100); this.method = method; this.entropyPasses = Numerics.Clamp(entropyPasses, 1, 10); this.filterStrength = Numerics.Clamp(filterStrength, 0, 100); this.spatialNoiseShaping = Numerics.Clamp(spatialNoiseShaping, 0, 100); this.alphaCompression = alphaCompression; rdOptLevel = ((method == WebpEncodingMethod.Level6) ? Vp8RdLevel.RdOptTrellisAll : ((method >= WebpEncodingMethod.Level5) ? Vp8RdLevel.RdOptTrellis : ((method >= WebpEncodingMethod.Level3) ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone))); int length = width * height; Mbw = width + 15 >> 4; Mbh = height + 15 >> 4; int length2 = (width + 1 >> 1) * (height + 1 >> 1); Y = this.memoryAllocator.Allocate(length); U = this.memoryAllocator.Allocate(length2); V = this.memoryAllocator.Allocate(length2); YTop = new byte[Mbw * 16]; UvTop = new byte[Mbw * 16 * 2]; Nz = new uint[Mbw + 1]; MbHeaderLimit = 1069547520 / (Mbw * Mbh); TopDerr = new sbyte[Mbw * 4]; int num = 100; maxI4HeaderBits = 65536 * num * num / 10000; MbInfo = new Vp8MacroBlockInfo[Mbw * Mbh]; for (int i = 0; i < MbInfo.Length; i++) { MbInfo[i] = new Vp8MacroBlockInfo(); } SegmentInfos = new Vp8SegmentInfo[4]; for (int j = 0; j < 4; j++) { SegmentInfos[j] = new Vp8SegmentInfo(); } FilterHeader = new Vp8FilterHeader(); int num2 = (4 * Mbw + 1) * (4 * Mbh + 1) + PredsWidth + 1; PredsWidth = 4 * Mbw + 1; Proba = new Vp8EncProba(); Preds = new byte[num2 + PredsWidth + Mbw]; Preds.AsSpan().Fill(205); Nz.AsSpan().Fill(3452816845u); ResetBoundaryPredictions(); } public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; int num = width * height; Span span = Y.GetSpan(); Span span2 = U.GetSpan(); Span span3 = V.GetSpan(); bool flag = YuvConversion.ConvertRgbToYuv(image, configuration, memoryAllocator, span, span2, span3); int num2 = width; int uvStride = num2 + 1 >> 1; Vp8EncIterator vp8EncIterator = new Vp8EncIterator(YTop, UvTop, Nz, MbInfo, Preds, TopDerr, Mbw, Mbh); int[] alphas = new int[256]; alpha = MacroBlockAnalysis(width, height, vp8EncIterator, span, span2, span3, num2, uvStride, alphas, out uvAlpha); int num3 = Mbw * Mbw; alpha /= num3; uvAlpha /= num3; SegmentHeader = new Vp8EncSegmentHeader(4); AssignSegments(alphas); SetLoopParams(quality); int num4 = AverageBytesPerMb[BaseQuant >> 4]; int expectedSize = Mbw * Mbh * num4; bitWriter = new Vp8BitWriter(expectedSize, this); int size = 0; bool flag2 = false; using AlphaEncoder alphaEncoder = new AlphaEncoder(); Span span4 = Span.Empty; IMemoryOwner memoryOwner = null; try { if (flag) { memoryOwner = alphaEncoder.EncodeAlpha(image, configuration, memoryAllocator, alphaCompression, out size); span4 = memoryOwner.GetSpan(); if (size < num) { flag2 = true; } } StatLoop(width, height, num2, uvStride); vp8EncIterator.Init(); vp8EncIterator.InitFilter(); Vp8ModeScore rd = new Vp8ModeScore(); Vp8Residual residual = new Vp8Residual(); do { bool flag3 = !Proba.UseSkipProba; rd.Clear(); vp8EncIterator.Import(span, span2, span3, num2, uvStride, width, height, importBoundarySamples: false); if (!Decimate(vp8EncIterator, ref rd, rdOptLevel) || flag3) { CodeResiduals(vp8EncIterator, rd, residual); } else { vp8EncIterator.ResetAfterSkip(); } vp8EncIterator.SaveBoundary(); } while (vp8EncIterator.Next()); AdjustFilterStrength(); ImageMetadata metadata = image.Metadata; metadata.SyncProfiles(); bitWriter.WriteEncodedImageToStream(stream, metadata.ExifProfile, metadata.XmpProfile, (uint)width, (uint)height, flag, span4.Slice(0, size), alphaCompression && flag2); } finally { memoryOwner?.Dispose(); } } public void Dispose() { Y.Dispose(); U.Dispose(); V.Dispose(); } private void StatLoop(int width, int height, int yStride, int uvStride) { int num = 0; float num2 = 0f; bool flag = num > 0 || num2 > 0f; bool num3 = (method == WebpEncodingMethod.Level0 || method == WebpEncodingMethod.Level3) && !flag; int num4 = entropyPasses; Vp8RdLevel rdOpt = ((method >= WebpEncodingMethod.Level3 || flag) ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone); int num5 = Mbw * Mbh; PassStats passStats = new PassStats(num, num2, 0, 100, quality); Proba.ResetTokenStats(); if (num3) { num5 = ((method != WebpEncodingMethod.Level3) ? ((num5 > 200) ? (num5 >> 2) : 50) : ((num5 > 200) ? (num5 >> 1) : 100)); } while (num4-- > 0) { bool flag2 = MathF.Abs(passStats.Dq) <= 0.4f || num4 == 0 || maxI4HeaderBits == 0; long num6 = OneStatPass(width, height, yStride, uvStride, rdOpt, num5, passStats); if (num6 == 0L) { return; } if (maxI4HeaderBits > 0 && num6 > 1069547520) { num4++; maxI4HeaderBits >>= 1; continue; } if (flag2) { break; } if (flag) { passStats.ComputeNextQ(); if (MathF.Abs(passStats.Dq) <= 0.4f) { break; } } } if (!flag || !passStats.DoSizeSearch) { Proba.FinalizeSkipProba(Mbw, Mbh); Proba.FinalizeTokenProbas(); } Proba.CalculateLevelCosts(); } private long OneStatPass(int width, int height, int yStride, int uvStride, Vp8RdLevel rdOpt, int nbMbs, PassStats stats) { Span span = Y.GetSpan(); Span span2 = U.GetSpan(); Span span3 = V.GetSpan(); Vp8EncIterator vp8EncIterator = new Vp8EncIterator(YTop, UvTop, Nz, MbInfo, Preds, TopDerr, Mbw, Mbh); long num = 0L; long num2 = 0L; long num3 = 0L; long size = nbMbs * 384; vp8EncIterator.Init(); SetLoopParams(stats.Q); Vp8ModeScore rd = new Vp8ModeScore(); do { rd.Clear(); vp8EncIterator.Import(span, span2, span3, yStride, uvStride, width, height, importBoundarySamples: false); if (Decimate(vp8EncIterator, ref rd, rdOpt)) { Vp8EncProba proba = Proba; int nbSkip = proba.NbSkip + 1; proba.NbSkip = nbSkip; } RecordResiduals(vp8EncIterator, rd); num += rd.R + rd.H; num2 += rd.H; num3 += rd.D; vp8EncIterator.SaveBoundary(); } while (vp8EncIterator.Next() && --nbMbs > 0); num2 += SegmentHeader.Size; if (stats.DoSizeSearch) { num += Proba.FinalizeSkipProba(Mbw, Mbh); num += Proba.FinalizeTokenProbas(); num = (num + num2 + 1024 >> 11) + 30; stats.Value = num; } else { stats.Value = GetPsnr(num3, size); } return num2; } private void SetLoopParams(float q) { SetSegmentParams(q); SetSegmentProbas(); ResetStats(); } private unsafe void AdjustFilterStrength() { if (filterStrength <= 0) { return; } int num = 0; for (int i = 0; i < 4; i++) { Vp8SegmentInfo vp8SegmentInfo = SegmentInfos[i]; int delta = vp8SegmentInfo.MaxEdge * vp8SegmentInfo.Y2.Q[1] >> 3; int num2 = FilterStrengthFromDelta(FilterHeader.Sharpness, delta); if (num2 > vp8SegmentInfo.FStrength) { vp8SegmentInfo.FStrength = num2; } if (num < vp8SegmentInfo.FStrength) { num = vp8SegmentInfo.FStrength; } } FilterHeader.FilterLevel = num; } private void ResetBoundaryPredictions() { Span span = Preds.AsSpan(); Span span2 = Preds.AsSpan(PredsWidth - 1); for (int i = 0; i < 4 * Mbw; i++) { span[i] = 0; } for (int j = 0; j < 4 * Mbh; j++) { span2[j * PredsWidth] = 0; } int num = 4 * Mbw + 1; int num2 = 4 * Mbh + 1; int num3 = num * num2; Preds.AsSpan(num3 + PredsWidth - 4, 4).Clear(); Nz[0] = 0u; } private void AssignSegments(int[] alphas) { int num = ((SegmentHeader.NumSegments < 4) ? SegmentHeader.NumSegments : 4); int[] array = new int[4]; int mid = 0; int[] array2 = new int[256]; int[] array3 = new int[4]; int[] array4 = new int[4]; int i; for (i = 0; i <= 255 && alphas[i] == 0; i++) { } int num2 = i; i = 255; while (i > num2 && alphas[i] == 0) { i--; } int num3 = i; int num4 = num3 - num2; int num5 = 0; i = 1; while (num5 < num) { array[num5] = num2 + i * num4 / (2 * num); num5++; i += 2; } for (num5 = 0; num5 < 6; num5++) { for (i = 0; i < num; i++) { array3[i] = 0; array4[i] = 0; } i = 0; for (int j = num2; j <= num3; j++) { if (alphas[j] != 0) { for (; i + 1 < num && Math.Abs(j - array[i + 1]) < Math.Abs(j - array[i]); i++) { } array2[j] = i; array4[i] += j * alphas[j]; array3[i] += alphas[j]; } } int num6 = 0; mid = 0; int num7 = 0; for (i = 0; i < num; i++) { if (array3[i] != 0) { int num8 = (array4[i] + array3[i] / 2) / array3[i]; num6 += Math.Abs(array[i] - num8); array[i] = num8; mid += num8 * array3[i]; num7 += array3[i]; } } mid = (mid + num7 / 2) / num7; if (num6 < 5) { break; } } for (i = 0; i < Mbw * Mbh; i++) { Vp8MacroBlockInfo obj = MbInfo[i]; int num9 = obj.Alpha; obj.Segment = array2[num9]; obj.Alpha = array[array2[num9]]; } SetSegmentAlphas(array, mid); } private void SetSegmentAlphas(int[] centers, int mid) { int numSegments = SegmentHeader.NumSegments; Vp8SegmentInfo[] segmentInfos = SegmentInfos; int num = centers[0]; int num2 = centers[0]; if (numSegments > 1) { for (int i = 0; i < numSegments; i++) { if (num > centers[i]) { num = centers[i]; } if (num2 < centers[i]) { num2 = centers[i]; } } } if (num2 == num) { num2 = num + 1; } for (int i = 0; i < numSegments; i++) { int value = 255 * (centers[i] - mid) / (num2 - num); int value2 = 255 * (centers[i] - num) / (num2 - num); segmentInfos[i].Alpha = Numerics.Clamp(value, -127, 127); segmentInfos[i].Beta = Numerics.Clamp(value2, 0, 255); } } private void SetSegmentParams(float quality) { int numSegments = SegmentHeader.NumSegments; Vp8SegmentInfo[] segmentInfos = SegmentInfos; double num = 0.9 * (double)spatialNoiseShaping / 100.0 / 128.0; double x = QualityToCompression((double)quality / 100.0); for (int i = 0; i < numSegments; i++) { double y = 1.0 - num * (double)segmentInfos[i].Alpha; double num2 = Math.Pow(x, y); int value = (int)(127.0 * (1.0 - num2)); segmentInfos[i].Quant = Numerics.Clamp(value, 0, 127); } BaseQuant = segmentInfos[0].Quant; DqUvAc = (uvAlpha - 64) * 10 / 70; DqUvAc = DqUvAc * spatialNoiseShaping / 100; DqUvAc = Numerics.Clamp(DqUvAc, -4, 6); DqUvDc = -4 * spatialNoiseShaping / 100; DqUvDc = Numerics.Clamp(DqUvDc, -15, 15); DqY1Dc = 0; DqY2Dc = 0; DqY2Ac = 0; SetupFilterStrength(); SetupMatrices(segmentInfos); } private void SetupFilterStrength() { int sharpness = 0; int num = 1; int num2 = 5 * filterStrength; for (int i = 0; i < 4; i++) { Vp8SegmentInfo vp8SegmentInfo = SegmentInfos[i]; int delta = WebpLookupTables.AcTable[Numerics.Clamp(vp8SegmentInfo.Quant, 0, 127)] >> 2; int num3 = FilterStrengthFromDelta(FilterHeader.Sharpness, delta) * num2 / (256 + vp8SegmentInfo.Beta); vp8SegmentInfo.FStrength = ((num3 >= 2) ? ((num3 > 63) ? 63 : num3) : 0); } FilterHeader.FilterLevel = SegmentInfos[0].FStrength; FilterHeader.Simple = num == 0; FilterHeader.Sharpness = sharpness; } private void SetSegmentProbas() { int[] array = new int[4]; for (int i = 0; i < Mbw * Mbh; i++) { Vp8MacroBlockInfo vp8MacroBlockInfo = MbInfo[i]; array[vp8MacroBlockInfo.Segment]++; } if (SegmentHeader.NumSegments > 1) { byte[] segments = Proba.Segments; segments[0] = (byte)GetProba(array[0] + array[1], array[2] + array[3]); segments[1] = (byte)GetProba(array[0], array[1]); segments[2] = (byte)GetProba(array[2], array[3]); SegmentHeader.UpdateMap = segments[0] != byte.MaxValue || segments[1] != byte.MaxValue || segments[2] != byte.MaxValue; if (!SegmentHeader.UpdateMap) { ResetSegments(); } SegmentHeader.Size = array[0] * (LossyUtils.Vp8BitCost(0, segments[0]) + LossyUtils.Vp8BitCost(0, segments[1])) + array[1] * (LossyUtils.Vp8BitCost(0, segments[0]) + LossyUtils.Vp8BitCost(1, segments[1])) + array[2] * (LossyUtils.Vp8BitCost(1, segments[0]) + LossyUtils.Vp8BitCost(0, segments[2])) + array[3] * (LossyUtils.Vp8BitCost(1, segments[0]) + LossyUtils.Vp8BitCost(1, segments[2])); } else { SegmentHeader.UpdateMap = false; SegmentHeader.Size = 0; } } private void ResetSegments() { for (int i = 0; i < Mbw * Mbh; i++) { MbInfo[i].Segment = 0; } } private void ResetStats() { Vp8EncProba proba = Proba; proba.CalculateLevelCosts(); proba.NbSkip = 0; } private unsafe void SetupMatrices(Vp8SegmentInfo[] dqm) { int num = ((method >= WebpEncodingMethod.Level4) ? spatialNoiseShaping : 0); foreach (Vp8SegmentInfo vp8SegmentInfo in dqm) { int quant = vp8SegmentInfo.Quant; ref ushort q = ref vp8SegmentInfo.Y1.Q[0]; q = WebpLookupTables.DcTable[Numerics.Clamp(quant + DqY1Dc, 0, 127)]; vp8SegmentInfo.Y1.Q[1] = WebpLookupTables.AcTable[Numerics.Clamp(quant, 0, 127)]; ref ushort q2 = ref vp8SegmentInfo.Y2.Q[0]; q2 = (ushort)(WebpLookupTables.DcTable[Numerics.Clamp(quant + DqY2Dc, 0, 127)] * 2); vp8SegmentInfo.Y2.Q[1] = WebpLookupTables.AcTable2[Numerics.Clamp(quant + DqY2Ac, 0, 127)]; ref ushort q3 = ref vp8SegmentInfo.Uv.Q[0]; q3 = WebpLookupTables.DcTable[Numerics.Clamp(quant + DqUvDc, 0, 117)]; vp8SegmentInfo.Uv.Q[1] = WebpLookupTables.AcTable[Numerics.Clamp(quant + DqUvAc, 0, 127)]; int num2 = vp8SegmentInfo.Y1.Expand(0); int num3 = vp8SegmentInfo.Y2.Expand(1); int num4 = vp8SegmentInfo.Uv.Expand(2); vp8SegmentInfo.LambdaI16 = 3 * num3 * num3; vp8SegmentInfo.LambdaI4 = 3 * num2 * num2 >> 7; vp8SegmentInfo.LambdaUv = 3 * num4 * num4 >> 6; vp8SegmentInfo.LambdaMode = num2 * num2 >> 7; vp8SegmentInfo.TLambda = num * num2 >> 5; vp8SegmentInfo.LambdaI16 = ((vp8SegmentInfo.LambdaI16 < 1) ? 1 : vp8SegmentInfo.LambdaI16); vp8SegmentInfo.LambdaI4 = ((vp8SegmentInfo.LambdaI4 < 1) ? 1 : vp8SegmentInfo.LambdaI4); vp8SegmentInfo.LambdaUv = ((vp8SegmentInfo.LambdaUv < 1) ? 1 : vp8SegmentInfo.LambdaUv); vp8SegmentInfo.LambdaMode = ((vp8SegmentInfo.LambdaMode < 1) ? 1 : vp8SegmentInfo.LambdaMode); vp8SegmentInfo.TLambda = ((vp8SegmentInfo.TLambda < 1) ? 1 : vp8SegmentInfo.TLambda); vp8SegmentInfo.MinDisto = 20 * vp8SegmentInfo.Y1.Q[0]; vp8SegmentInfo.MaxEdge = 0; vp8SegmentInfo.I4Penalty = 1000 * num2 * num2; } } private int MacroBlockAnalysis(int width, int height, Vp8EncIterator it, Span y, Span u, Span v, int yStride, int uvStride, int[] alphas, out int uvAlpha) { int num = 0; uvAlpha = 0; if (!it.IsDone()) { do { it.Import(y, u, v, yStride, uvStride, width, height, importBoundarySamples: true); int bestUvAlpha; int num2 = MbAnalyze(it, alphas, out bestUvAlpha); num += num2; uvAlpha += bestUvAlpha; } while (it.Next()); } return num; } private int MbAnalyze(Vp8EncIterator it, int[] alphas, out int bestUvAlpha) { it.SetIntra16Mode(0); it.SetSkip(skip: false); it.SetSegment(0); int num; if (method <= WebpEncodingMethod.Level1) { num = it.FastMbAnalyze(quality); } else { num = it.MbAnalyzeBestIntra16Mode(); if (method >= WebpEncodingMethod.Level5) { num = it.MbAnalyzeBestIntra4Mode(num); } } bestUvAlpha = it.MbAnalyzeBestUvMode(); num = 3 * num + bestUvAlpha + 2 >> 2; num = FinalAlphaValue(num); alphas[num]++; it.CurrentMacroBlockInfo.Alpha = num; return num; } private bool Decimate(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8RdLevel rdOpt) { rd.InitScore(); it.MakeLuma16Preds(); it.MakeChroma8Preds(); if (rdOpt > Vp8RdLevel.RdOptNone) { QuantEnc.PickBestIntra16(it, ref rd, SegmentInfos, Proba); if (method >= WebpEncodingMethod.Level2) { QuantEnc.PickBestIntra4(it, ref rd, SegmentInfos, Proba, maxI4HeaderBits); } QuantEnc.PickBestUv(it, ref rd, SegmentInfos, Proba); } else { QuantEnc.RefineUsingDistortion(it, SegmentInfos, rd, method >= WebpEncodingMethod.Level2, method >= WebpEncodingMethod.Level1, MbHeaderLimit); } bool flag = rd.Nz == 0; it.SetSkip(flag); return flag; } private void CodeResiduals(Vp8EncIterator it, Vp8ModeScore rd, Vp8Residual residual) { bool flag = it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16; int segment = it.CurrentMacroBlockInfo.Segment; it.NzToBytes(); int num = bitWriter.NumBytes(); if (flag) { residual.Init(0, 1, Proba); residual.SetCoeffs(rd.YDcLevels); int num2 = bitWriter.PutCoeffs(it.TopNz[8] + it.LeftNz[8], residual); it.TopNz[8] = (it.LeftNz[8] = num2); residual.Init(1, 0, Proba); } else { residual.Init(0, 3, Proba); } for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { int ctx = it.TopNz[j] + it.LeftNz[i]; Span coeffs = rd.YAcLevels.AsSpan(16 * (j + i * 4), 16); residual.SetCoeffs(coeffs); int num3 = bitWriter.PutCoeffs(ctx, residual); it.TopNz[j] = (it.LeftNz[i] = num3); } } int num4 = bitWriter.NumBytes(); residual.Init(0, 2, Proba); for (int k = 0; k <= 2; k += 2) { for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { int ctx2 = it.TopNz[4 + k + j] + it.LeftNz[4 + k + i]; residual.SetCoeffs(rd.UvLevels.AsSpan(16 * (k * 2 + j + i * 2), 16)); int num5 = bitWriter.PutCoeffs(ctx2, residual); it.TopNz[4 + k + j] = (it.LeftNz[4 + k + i] = num5); } } } int num6 = bitWriter.NumBytes(); it.LumaBits = num4 - num; it.UvBits = num6 - num4; it.BitCount[segment, flag ? 1u : 0u] += it.LumaBits; it.BitCount[segment, 2] += it.UvBits; it.BytesToNz(); } private void RecordResiduals(Vp8EncIterator it, Vp8ModeScore rd) { Vp8Residual vp8Residual = new Vp8Residual(); bool num = it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16; it.NzToBytes(); if (num) { vp8Residual.Init(0, 1, Proba); vp8Residual.SetCoeffs(rd.YDcLevels); int num2 = vp8Residual.RecordCoeffs(it.TopNz[8] + it.LeftNz[8]); it.TopNz[8] = num2; it.LeftNz[8] = num2; vp8Residual.Init(1, 0, Proba); } else { vp8Residual.Init(0, 3, Proba); } for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { int ctx = it.TopNz[j] + it.LeftNz[i]; Span coeffs = rd.YAcLevels.AsSpan(16 * (j + i * 4), 16); vp8Residual.SetCoeffs(coeffs); int num3 = vp8Residual.RecordCoeffs(ctx); it.TopNz[j] = num3; it.LeftNz[i] = num3; } } vp8Residual.Init(0, 2, Proba); for (int k = 0; k <= 2; k += 2) { for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { int ctx2 = it.TopNz[4 + k + j] + it.LeftNz[4 + k + i]; vp8Residual.SetCoeffs(rd.UvLevels.AsSpan(16 * (k * 2 + j + i * 2), 16)); int num4 = vp8Residual.RecordCoeffs(ctx2); it.TopNz[4 + k + j] = num4; it.LeftNz[4 + k + i] = num4; } } } it.BytesToNz(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int FinalAlphaValue(int alpha) { alpha = 255 - alpha; return Numerics.Clamp(alpha, 0, 255); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double QualityToCompression(double c) { return Math.Pow((c < 0.75) ? (c * (2.0 / 3.0)) : (2.0 * c - 1.0), 1.0 / 3.0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int FilterStrengthFromDelta(int sharpness, int delta) { int num = ((delta < 64) ? delta : 63); return WebpLookupTables.LevelsFromDelta[sharpness, num]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double GetPsnr(long mse, long size) { if (mse <= 0 || size <= 0) { return 99.0; } return 10.0 * Math.Log10(65025f * (float)size / (float)mse); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetProba(int a, int b) { int num = a + b; if (num != 0) { return (255 * a + num / 2) / num; } return 255; } } internal static class Vp8Encoding { private const int KC1 = 85627; private const int KC2 = 35468; private static readonly byte[] Clip1; private const int I16DC16 = 0; private const int I16TM16 = 16; private const int I16VE16 = 512; private const int I16HE16 = 528; private const int C8DC8 = 1024; private const int C8TM8 = 1040; private const int C8VE8 = 1280; private const int C8HE8 = 1296; public static readonly int[] Vp8I16ModeOffsets; public static readonly int[] Vp8UvModeOffsets; private const int I4DC4 = 1536; private const int I4TM4 = 1540; private const int I4VE4 = 1544; private const int I4HE4 = 1548; private const int I4RD4 = 1552; private const int I4VR4 = 1556; private const int I4LD4 = 1560; private const int I4VL4 = 1564; private const int I4HD4 = 1664; private const int I4HU4 = 1668; public static readonly int[] Vp8I4ModeOffsets; private static readonly Vector128 K1; private static readonly Vector128 K2; private static readonly Vector128 Four; private static readonly Vector128 Seven; private static readonly Vector128 K88p; private static readonly Vector128 K88m; private static readonly Vector128 K5352_2217p; private static readonly Vector128 K5352_2217m; private static readonly Vector128 K937; private static readonly Vector128 K1812; private static readonly Vector128 K5352_2217; private static readonly Vector128 K2217_5352; private static readonly Vector128 K12000PlusOne; private static readonly Vector128 K51000; private static readonly byte MmShuffle2301; private static readonly byte MmShuffle1032; static Vp8Encoding() { Clip1 = new byte[766]; Vp8I16ModeOffsets = new int[4] { 0, 16, 512, 528 }; Vp8UvModeOffsets = new int[4] { 1024, 1040, 1280, 1296 }; Vp8I4ModeOffsets = new int[10] { 1536, 1540, 1544, 1548, 1552, 1556, 1560, 1564, 1664, 1668 }; K1 = Vector128.Create((short)20091).AsInt16(); K2 = Vector128.Create((short)(-30068)).AsInt16(); Four = Vector128.Create((short)4); Seven = Vector128.Create((short)7); K88p = Vector128.Create(8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0).AsInt16(); K88m = Vector128.Create(8, 0, 248, byte.MaxValue, 8, 0, 248, byte.MaxValue, 8, 0, 248, byte.MaxValue, 8, 0, 248, byte.MaxValue).AsInt16(); K5352_2217p = Vector128.Create(232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8).AsInt16(); K5352_2217m = Vector128.Create(169, 8, 24, 235, 169, 8, 24, 235, 169, 8, 24, 235, 169, 8, 24, 235).AsInt16(); K937 = Vector128.Create(937); K1812 = Vector128.Create(1812); K5352_2217 = Vector128.Create(169, 8, 232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8, 232, 20).AsInt16(); K2217_5352 = Vector128.Create(24, 235, 169, 8, 24, 235, 169, 8, 24, 235, 169, 8, 24, 235, 169, 8).AsInt16(); K12000PlusOne = Vector128.Create(77536); K51000 = Vector128.Create(51000); MmShuffle2301 = SimdUtils.Shuffle.MmShuffle(2, 3, 0, 1); MmShuffle1032 = SimdUtils.Shuffle.MmShuffle(1, 0, 3, 2); for (int i = -255; i <= 510; i++) { Clip1[255 + i] = Clip8b(i); } } public static void ITransformTwo(Span reference, Span input, Span dst, Span scratch) { if (Sse2.IsSupported) { ref short reference2 = ref MemoryMarshal.GetReference(input); Vector128 left = Vector128.Create(Unsafe.As(ref reference2), 0L); Vector128 left2 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 4)), 0L); Vector128 left3 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 8)), 0L); Vector128 left4 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 12)), 0L); Vector128 right = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 16)), 0L); Vector128 right2 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 20)), 0L); Vector128 right3 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 24)), 0L); Vector128 right4 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 28)), 0L); left = Sse2.UnpackLow(left, right); left2 = Sse2.UnpackLow(left2, right2); left3 = Sse2.UnpackLow(left3, right3); left4 = Sse2.UnpackLow(left4, right4); InverseTransformVerticalPass(left, left3, left2, left4, out var tmp, out var tmp2, out var tmp3, out var tmp4); LossyUtils.Vp8Transpose_2_4x4_16b(tmp, tmp2, tmp3, tmp4, out var output, out var output2, out var output3, out var output4); InverseTransformHorizontalPass(output, output3, output2, output4, out var shifted, out var shifted2, out var shifted3, out var shifted4); LossyUtils.Vp8Transpose_2_4x4_16b(shifted, shifted2, shifted3, shifted4, out output, out output2, out output3, out output4); Vector128 zero = Vector128.Zero; Vector128 zero2 = Vector128.Zero; Vector128 zero3 = Vector128.Zero; Vector128 zero4 = Vector128.Zero; ref byte reference3 = ref MemoryMarshal.GetReference(reference); zero = Vector128.Create(Unsafe.As(ref reference3), 0L).AsByte(); zero2 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference3, 32)), 0L).AsByte(); zero3 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference3, 64)), 0L).AsByte(); zero4 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference3, 96)), 0L).AsByte(); zero = Sse2.UnpackLow(zero, Vector128.Zero); zero2 = Sse2.UnpackLow(zero2, Vector128.Zero); zero3 = Sse2.UnpackLow(zero3, Vector128.Zero); zero4 = Sse2.UnpackLow(zero4, Vector128.Zero); Vector128 vector = Sse2.Add(zero.AsInt16(), output.AsInt16()); Vector128 vector2 = Sse2.Add(zero2.AsInt16(), output2.AsInt16()); Vector128 vector3 = Sse2.Add(zero3.AsInt16(), output3.AsInt16()); Vector128 vector4 = Sse2.Add(zero4.AsInt16(), output4.AsInt16()); zero = Sse2.PackUnsignedSaturate(vector, vector); zero2 = Sse2.PackUnsignedSaturate(vector2, vector2); zero3 = Sse2.PackUnsignedSaturate(vector3, vector3); zero4 = Sse2.PackUnsignedSaturate(vector4, vector4); ref byte reference4 = ref MemoryMarshal.GetReference(dst); Unsafe.As>(ref reference4) = zero.GetLower(); Unsafe.As>(ref Unsafe.Add(ref reference4, 32)) = zero2.GetLower(); Unsafe.As>(ref Unsafe.Add(ref reference4, 64)) = zero3.GetLower(); Unsafe.As>(ref Unsafe.Add(ref reference4, 96)) = zero4.GetLower(); } else { ITransformOne(reference, input, dst, scratch); ITransformOne(reference.Slice(4), input.Slice(16), dst.Slice(4), scratch); } } public static void ITransformOne(Span reference, Span input, Span dst, Span scratch) { if (Sse2.IsSupported) { ref short reference2 = ref MemoryMarshal.GetReference(input); Vector128 @in = Vector128.Create(Unsafe.As(ref reference2), 0L); Vector128 in2 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 4)), 0L); Vector128 in3 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 8)), 0L); Vector128 in4 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 12)), 0L); InverseTransformVerticalPass(@in, in3, in2, in4, out var tmp, out var tmp2, out var tmp3, out var tmp4); LossyUtils.Vp8Transpose_2_4x4_16b(tmp, tmp2, tmp3, tmp4, out var output, out var output2, out var output3, out var output4); InverseTransformHorizontalPass(output, output3, output2, output4, out var shifted, out var shifted2, out var shifted3, out var shifted4); LossyUtils.Vp8Transpose_2_4x4_16b(shifted, shifted2, shifted3, shifted4, out output, out output2, out output3, out output4); Vector128 zero = Vector128.Zero; Vector128 zero2 = Vector128.Zero; Vector128 zero3 = Vector128.Zero; _ = Vector128.Zero; ref byte reference3 = ref MemoryMarshal.GetReference(reference); zero = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref reference3)).AsByte(); zero2 = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref Unsafe.Add(ref reference3, 32))).AsByte(); zero3 = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref Unsafe.Add(ref reference3, 64))).AsByte(); Vector128 left = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref Unsafe.Add(ref reference3, 96))).AsByte(); zero = Sse2.UnpackLow(zero, Vector128.Zero); zero2 = Sse2.UnpackLow(zero2, Vector128.Zero); zero3 = Sse2.UnpackLow(zero3, Vector128.Zero); Vector128 vector = Sse2.UnpackLow(left, Vector128.Zero); Vector128 vector2 = Sse2.Add(zero.AsInt16(), output.AsInt16()); Vector128 vector3 = Sse2.Add(zero2.AsInt16(), output2.AsInt16()); Vector128 vector4 = Sse2.Add(zero3.AsInt16(), output3.AsInt16()); Vector128 vector5 = Sse2.Add(vector.AsInt16(), output4.AsInt16()); zero = Sse2.PackUnsignedSaturate(vector2, vector2); zero2 = Sse2.PackUnsignedSaturate(vector3, vector3); zero3 = Sse2.PackUnsignedSaturate(vector4, vector4); Vector128 vector6 = Sse2.PackUnsignedSaturate(vector5, vector5); ref byte reference4 = ref MemoryMarshal.GetReference(dst); int num = Sse2.ConvertToInt32(zero.AsInt32()); int num2 = Sse2.ConvertToInt32(zero2.AsInt32()); int num3 = Sse2.ConvertToInt32(zero3.AsInt32()); int num4 = Sse2.ConvertToInt32(vector6.AsInt32()); Unsafe.As(ref reference4) = num; Unsafe.As(ref Unsafe.Add(ref reference4, 32)) = num2; Unsafe.As(ref Unsafe.Add(ref reference4, 64)) = num3; Unsafe.As(ref Unsafe.Add(ref reference4, 96)) = num4; } else { Span span = scratch.Slice(0, 16); for (int i = 0; i < 4; i++) { int num5 = input[0] + input[8]; int num6 = input[0] - input[8]; int num7 = Mul(input[4], 35468) - Mul(input[12], 85627); int num8 = Mul(input[4], 85627) + Mul(input[12], 35468); span[0] = num5 + num8; span[1] = num6 + num7; span[2] = num6 - num7; span[3] = num5 - num8; span = span.Slice(4); input = input.Slice(1); } span = scratch; for (int i = 0; i < 4; i++) { int num9 = span[0] + 4; int num10 = num9 + span[8]; int num11 = num9 - span[8]; int num12 = Mul(span[4], 35468) - Mul(span[12], 85627); int num13 = Mul(span[4], 85627) + Mul(span[12], 35468); Store(dst, reference, 0, i, num10 + num13); Store(dst, reference, 1, i, num11 + num12); Store(dst, reference, 2, i, num11 - num12); Store(dst, reference, 3, i, num10 - num13); span = span.Slice(1); } } } private static void InverseTransformVerticalPass(Vector128 in0, Vector128 in2, Vector128 in1, Vector128 in3, out Vector128 tmp0, out Vector128 tmp1, out Vector128 tmp2, out Vector128 tmp3) { Vector128 left = Sse2.Add(in0.AsInt16(), in2.AsInt16()); Vector128 left2 = Sse2.Subtract(in0.AsInt16(), in2.AsInt16()); Vector128 left3 = Sse2.MultiplyHigh(in1.AsInt16(), K2); Vector128 right = Sse2.MultiplyHigh(in3.AsInt16(), K1); Vector128 left4 = Sse2.Subtract(in1.AsInt16(), in3.AsInt16()); Vector128 right2 = Sse2.Subtract(left3, right); Vector128 right3 = Sse2.Add(left4, right2); Vector128 left5 = Sse2.MultiplyHigh(in1.AsInt16(), K1); Vector128 right4 = Sse2.MultiplyHigh(in3.AsInt16(), K2); Vector128 left6 = Sse2.Add(in1.AsInt16(), in3.AsInt16()); Vector128 right5 = Sse2.Add(left5, right4); Vector128 right6 = Sse2.Add(left6, right5); tmp0 = Sse2.Add(left, right6); tmp1 = Sse2.Add(left2, right3); tmp2 = Sse2.Subtract(left2, right3); tmp3 = Sse2.Subtract(left, right6); } private static void InverseTransformHorizontalPass(Vector128 t0, Vector128 t2, Vector128 t1, Vector128 t3, out Vector128 shifted0, out Vector128 shifted1, out Vector128 shifted2, out Vector128 shifted3) { Vector128 left = Sse2.Add(t0.AsInt16(), Four); Vector128 left2 = Sse2.Add(left, t2.AsInt16()); Vector128 left3 = Sse2.Subtract(left, t2.AsInt16()); Vector128 left4 = Sse2.MultiplyHigh(t1.AsInt16(), K2); Vector128 right = Sse2.MultiplyHigh(t3.AsInt16(), K1); Vector128 left5 = Sse2.Subtract(t1.AsInt16(), t3.AsInt16()); Vector128 right2 = Sse2.Subtract(left4, right); Vector128 right3 = Sse2.Add(left5, right2); Vector128 left6 = Sse2.MultiplyHigh(t1.AsInt16(), K1); Vector128 right4 = Sse2.MultiplyHigh(t3.AsInt16(), K2); Vector128 left7 = Sse2.Add(t1.AsInt16(), t3.AsInt16()); Vector128 right5 = Sse2.Add(left6, right4); Vector128 right6 = Sse2.Add(left7, right5); Vector128 value = Sse2.Add(left2, right6); Vector128 value2 = Sse2.Add(left3, right3); Vector128 value3 = Sse2.Subtract(left3, right3); Vector128 value4 = Sse2.Subtract(left2, right6); shifted0 = Sse2.ShiftRightArithmetic(value, 3); shifted1 = Sse2.ShiftRightArithmetic(value2, 3); shifted2 = Sse2.ShiftRightArithmetic(value3, 3); shifted3 = Sse2.ShiftRightArithmetic(value4, 3); } public static void FTransform2(Span src, Span reference, Span output, Span output2, Span scratch) { if (Sse2.IsSupported) { ref byte reference2 = ref MemoryMarshal.GetReference(src); ref byte reference3 = ref MemoryMarshal.GetReference(reference); Vector128 vector = Vector128.Create(Unsafe.As(ref reference2), 0L); Vector128 vector2 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 32)), 0L); Vector128 vector3 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 64)), 0L); Vector128 vector4 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 96)), 0L); Vector128 vector5 = Vector128.Create(Unsafe.As(ref reference3), 0L); Vector128 vector6 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference3, 32)), 0L); Vector128 vector7 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference3, 64)), 0L); Vector128 vector8 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference3, 96)), 0L); Vector128 vector9 = Sse2.UnpackLow(vector.AsByte(), Vector128.Zero); Vector128 vector10 = Sse2.UnpackLow(vector2.AsByte(), Vector128.Zero); Vector128 vector11 = Sse2.UnpackLow(vector3.AsByte(), Vector128.Zero); Vector128 vector12 = Sse2.UnpackLow(vector4.AsByte(), Vector128.Zero); Vector128 vector13 = Sse2.UnpackLow(vector5.AsByte(), Vector128.Zero); Vector128 vector14 = Sse2.UnpackLow(vector6.AsByte(), Vector128.Zero); Vector128 vector15 = Sse2.UnpackLow(vector7.AsByte(), Vector128.Zero); Vector128 vector16 = Sse2.UnpackLow(vector8.AsByte(), Vector128.Zero); Vector128 vector17 = Sse2.Subtract(vector9.AsInt16(), vector13.AsInt16()); Vector128 vector18 = Sse2.Subtract(vector10.AsInt16(), vector14.AsInt16()); Vector128 vector19 = Sse2.Subtract(vector11.AsInt16(), vector15.AsInt16()); Vector128 vector20 = Sse2.Subtract(vector12.AsInt16(), vector16.AsInt16()); Vector128 vector21 = Sse2.UnpackLow(vector17.AsInt32(), vector18.AsInt32()); Vector128 vector22 = Sse2.UnpackLow(vector19.AsInt32(), vector20.AsInt32()); Vector128 vector23 = Sse2.UnpackHigh(vector17.AsInt32(), vector18.AsInt32()); Vector128 vector24 = Sse2.UnpackHigh(vector19.AsInt32(), vector20.AsInt32()); FTransformPass1SSE2(vector21.AsInt16(), vector22.AsInt16(), out var @out, out var out2); FTransformPass1SSE2(vector23.AsInt16(), vector24.AsInt16(), out var out3, out var out4); FTransformPass2SSE2(@out, out2, output); FTransformPass2SSE2(out3, out4, output2); } else { FTransform(src, reference, output, scratch); FTransform(src.Slice(4), reference.Slice(4), output2, scratch); } } public static void FTransform(Span src, Span reference, Span output, Span scratch) { if (Sse2.IsSupported) { ref byte reference2 = ref MemoryMarshal.GetReference(src); ref byte reference3 = ref MemoryMarshal.GetReference(reference); Vector128 vector = Vector128.Create(Unsafe.As(ref reference2), 0L); Vector128 vector2 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 32)), 0L); Vector128 vector3 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 64)), 0L); Vector128 vector4 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference2, 96)), 0L); Vector128 vector5 = Vector128.Create(Unsafe.As(ref reference3), 0L); Vector128 vector6 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference3, 32)), 0L); Vector128 vector7 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference3, 64)), 0L); Vector128 vector8 = Vector128.Create(Unsafe.As(ref Unsafe.Add(ref reference3, 96)), 0L); Vector128 vector9 = Sse2.UnpackLow(vector.AsInt16(), vector2.AsInt16()); Vector128 vector10 = Sse2.UnpackLow(vector3.AsInt16(), vector4.AsInt16()); Vector128 vector11 = Sse2.UnpackLow(vector5.AsInt16(), vector6.AsInt16()); Vector128 vector12 = Sse2.UnpackLow(vector7.AsInt16(), vector8.AsInt16()); Vector128 vector13 = Sse2.UnpackLow(vector9.AsByte(), Vector128.Zero); Vector128 vector14 = Sse2.UnpackLow(vector10.AsByte(), Vector128.Zero); Vector128 vector15 = Sse2.UnpackLow(vector11.AsByte(), Vector128.Zero); Vector128 vector16 = Sse2.UnpackLow(vector12.AsByte(), Vector128.Zero); Vector128 row = Sse2.Subtract(vector13.AsInt16(), vector15.AsInt16()); Vector128 row2 = Sse2.Subtract(vector14.AsInt16(), vector16.AsInt16()); FTransformPass1SSE2(row, row2, out var @out, out var out2); FTransformPass2SSE2(@out, out2, output); return; } Span span = scratch.Slice(0, 16); int num = 0; int num2 = 0; for (int i = 0; i < 4; i++) { int num3 = src[num + 3] - reference[num2 + 3]; int num4 = src[num + 2] - reference[num2 + 2]; int num5 = src[num + 1] - reference[num2 + 1]; int num6 = src[num] - reference[num2]; int num7 = num6 + num3; int num8 = num5 + num4; int num9 = num5 - num4; int num10 = num6 - num3; span[3 + i * 4] = num10 * 2217 - num9 * 5352 + 937 >> 9; span[2 + i * 4] = (num7 - num8) * 8; span[1 + i * 4] = num9 * 2217 + num10 * 5352 + 1812 >> 9; span[i * 4] = (num7 + num8) * 8; num += 32; num2 += 32; } for (int i = 0; i < 4; i++) { int num11 = span[12 + i]; int num12 = span[8 + i]; int num13 = span[4 + i] + num12; int num14 = span[4 + i] - num12; int num15 = span[i] + num11; int num16 = span[i] - num11; output[12 + i] = (short)(num16 * 2217 - num14 * 5352 + 51000 >> 16); output[8 + i] = (short)(num15 - num13 + 7 >> 4); output[4 + i] = (short)((num14 * 2217 + num16 * 5352 + 12000 >> 16) + ((num16 != 0) ? 1 : 0)); output[i] = (short)(num15 + num13 + 7 >> 4); } } public static void FTransformPass1SSE2(Vector128 row01, Vector128 row23, out Vector128 out01, out Vector128 out32) { Vector128 vector = Sse2.ShuffleHigh(row01, MmShuffle2301); Vector128 vector2 = Sse2.ShuffleHigh(row23, MmShuffle2301); Vector128 vector3 = Sse2.UnpackLow(vector.AsInt64(), vector2.AsInt64()); Vector128 vector4 = Sse2.UnpackHigh(vector.AsInt64(), vector2.AsInt64()); Vector128 left = Sse2.Add(vector3.AsInt16(), vector4.AsInt16()); Vector128 left2 = Sse2.Subtract(vector3.AsInt16(), vector4.AsInt16()); Vector128 left3 = Sse2.MultiplyAddAdjacent(left, K88p); Vector128 right = Sse2.MultiplyAddAdjacent(left, K88m); Vector128 left4 = Sse2.MultiplyAddAdjacent(left2, K5352_2217p); Vector128 left5 = Sse2.MultiplyAddAdjacent(left2, K5352_2217m); Vector128 value = Sse2.Add(left4, K1812); Vector128 value2 = Sse2.Add(left5, K937); Vector128 left6 = Sse2.ShiftRightArithmetic(value, 9); Vector128 right2 = Sse2.ShiftRightArithmetic(value2, 9); Vector128 left7 = Sse2.PackSignedSaturate(left3, right); Vector128 right3 = Sse2.PackSignedSaturate(left6, right2); Vector128 vector5 = Sse2.UnpackLow(left7, right3); Vector128 vector6 = Sse2.UnpackHigh(left7, right3); Vector128 value3 = Sse2.UnpackHigh(vector5.AsInt32(), vector6.AsInt32()); out01 = Sse2.UnpackLow(vector5.AsInt32(), vector6.AsInt32()); out32 = Sse2.Shuffle(value3, MmShuffle1032); } public static void FTransformPass2SSE2(Vector128 v01, Vector128 v32, Span output) { Vector128 vector = Sse2.Subtract(v01.AsInt16(), v32.AsInt16()); Vector128 left = Sse2.UnpackLow(Sse2.UnpackHigh(vector.AsInt64(), vector.AsInt64()).AsInt16(), vector.AsInt16()); Vector128 left2 = Sse2.MultiplyAddAdjacent(left, K5352_2217); Vector128 left3 = Sse2.MultiplyAddAdjacent(left, K2217_5352); Vector128 value = Sse2.Add(left2, K12000PlusOne); Vector128 value2 = Sse2.Add(left3, K51000); Vector128 vector2 = Sse2.ShiftRightArithmetic(value, 16); Vector128 vector3 = Sse2.ShiftRightArithmetic(value2, 16); Vector128 left4 = Sse2.PackSignedSaturate(vector2, vector2); Vector128 vector4 = Sse2.PackSignedSaturate(vector3, vector3); Vector128 vector5 = Sse2.Add(left4, Sse2.CompareEqual(vector, Vector128.Zero)); Vector128 vector6 = Sse2.Add(v01.AsInt16(), v32.AsInt16()); Vector128 left5 = Sse2.Add(vector6.AsInt16(), Seven); Vector128 right = Sse2.UnpackHigh(vector6.AsInt64(), vector6.AsInt64()).AsInt16(); Vector128 value3 = Sse2.Add(left5, right); Vector128 value4 = Sse2.Subtract(left5, right); Vector128 vector7 = Sse2.ShiftRightArithmetic(value3, 4); Vector128 vector8 = Sse2.ShiftRightArithmetic(value4, 4); Vector128 vector9 = Sse2.UnpackLow(vector7.AsInt64(), vector5.AsInt64()); Vector128 vector10 = Sse2.UnpackLow(vector8.AsInt64(), vector4.AsInt64()); ref short reference = ref MemoryMarshal.GetReference(output); Unsafe.As>(ref reference) = vector9.AsInt16(); Unsafe.As>(ref Unsafe.Add(ref reference, 8)) = vector10.AsInt16(); } public static void FTransformWht(Span input, Span output, Span scratch) { Span span = scratch.Slice(0, 16); int num = 0; for (int i = 0; i < 4; i++) { int num2 = input[num + 16] + input[num + 48]; int num3 = input[num + 16] - input[num + 48]; int num4 = input[num] + input[num + 32]; int num5 = input[num] - input[num + 32]; span[3 + i * 4] = num4 - num2; span[2 + i * 4] = num5 - num3; span[1 + i * 4] = num5 + num3; span[i * 4] = num4 + num2; num += 64; } for (int i = 0; i < 4; i++) { int num6 = span[12 + i]; int num7 = span[8 + i]; int num8 = span[4 + i] + num6; int num9 = span[4 + i] - num6; int num10 = span[i] + num7; int num11 = span[i] - num7; int num12 = num10 + num8; int num13 = num11 + num9; int num14 = num11 - num9; int num15 = num10 - num8; output[12 + i] = (short)(num15 >> 1); output[8 + i] = (short)(num14 >> 1); output[4 + i] = (short)(num13 >> 1); output[i] = (short)(num12 >> 1); } } public static void EncPredLuma16(Span dst, Span left, Span top) { DcMode(dst.Slice(0), left, top, 16, 16, 5); VerticalPred(dst.Slice(512), top, 16); HorizontalPred(dst.Slice(528), left, 16); TrueMotion(dst.Slice(16), left, top, 16); } public static void EncPredChroma8(Span dst, Span left, Span top) { DcMode(dst.Slice(1024), left, top, 8, 8, 4); VerticalPred(dst.Slice(1280), top, 8); HorizontalPred(dst.Slice(1296), left, 8); TrueMotion(dst.Slice(1040), left, top, 8); dst = dst.Slice(8); if (top != null) { top = top.Slice(8); } if (left != null) { left = left.Slice(16); } DcMode(dst.Slice(1024), left, top, 8, 8, 4); VerticalPred(dst.Slice(1280), top, 8); HorizontalPred(dst.Slice(1296), left, 8); TrueMotion(dst.Slice(1040), left, top, 8); } public static void EncPredLuma4(Span dst, Span top, int topOffset, Span vals) { Dc4(dst.Slice(1536), top, topOffset); Tm4(dst.Slice(1540), top, topOffset); Ve4(dst.Slice(1544), top, topOffset, vals); He4(dst.Slice(1548), top, topOffset); Rd4(dst.Slice(1552), top, topOffset); Vr4(dst.Slice(1556), top, topOffset); Ld4(dst.Slice(1560), top, topOffset); Vl4(dst.Slice(1564), top, topOffset); Hd4(dst.Slice(1664), top, topOffset); Hu4(dst.Slice(1668), top, topOffset); } private static void VerticalPred(Span dst, Span top, int size) { if (top != null) { for (int i = 0; i < size; i++) { top.Slice(0, size).CopyTo(dst.Slice(i * 32)); } } else { Fill(dst, 127, size); } } public static void HorizontalPred(Span dst, Span left, int size) { if (left != null) { left = left.Slice(1); for (int i = 0; i < size; i++) { dst.Slice(i * 32, size).Fill(left[i]); } } else { Fill(dst, 129, size); } } public static void TrueMotion(Span dst, Span left, Span top, int size) { if (left != null) { if (top != null) { Span span = Clip1.AsSpan(255 - left[0]); for (int i = 0; i < size; i++) { Span span2 = span.Slice(left[i + 1]); for (int j = 0; j < size; j++) { dst[j] = span2[top[j]]; } dst = dst.Slice(32); } } else { HorizontalPred(dst, left, size); } } else if (top != null) { VerticalPred(dst, top, size); } else { Fill(dst, 129, size); } } private static void DcMode(Span dst, Span left, Span top, int size, int round, int shift) { int num = 0; if (top != null) { for (int i = 0; i < size; i++) { num += top[i]; } if (left != null) { left = left.Slice(1); for (int i = 0; i < size; i++) { num += left[i]; } } else { num += num; } num = num + round >> shift; } else if (left != null) { left = left.Slice(1); for (int i = 0; i < size; i++) { num += left[i]; } num += num; num = num + round >> shift; } else { num = 128; } Fill(dst, num, size); } private static void Dc4(Span dst, Span top, int topOffset) { uint num = 4u; for (int i = 0; i < 4; i++) { num += (uint)(top[topOffset + i] + top[topOffset - 5 + i]); } Fill(dst, (int)(num >> 3), 4); } private static void Tm4(Span dst, Span top, int topOffset) { Span span = Clip1.AsSpan(255 - top[topOffset - 1]); for (int i = 0; i < 4; i++) { Span span2 = span.Slice(top[topOffset - 2 - i]); for (int j = 0; j < 4; j++) { dst[j] = span2[top[topOffset + j]]; } dst = dst.Slice(32); } } private static void Ve4(Span dst, Span top, int topOffset, Span vals) { vals[0] = LossyUtils.Avg3(top[topOffset - 1], top[topOffset], top[topOffset + 1]); vals[1] = LossyUtils.Avg3(top[topOffset], top[topOffset + 1], top[topOffset + 2]); vals[2] = LossyUtils.Avg3(top[topOffset + 1], top[topOffset + 2], top[topOffset + 3]); vals[3] = LossyUtils.Avg3(top[topOffset + 2], top[topOffset + 3], top[topOffset + 4]); for (int i = 0; i < 4; i++) { vals.CopyTo(dst.Slice(i * 32)); } } private static void He4(Span dst, Span top, int topOffset) { byte a = top[topOffset - 1]; byte b = top[topOffset - 2]; byte b2 = top[topOffset - 3]; byte b3 = top[topOffset - 4]; byte b4 = top[topOffset - 5]; uint value = (uint)(16843009 * LossyUtils.Avg3(a, b, b2)); BinaryPrimitives.WriteUInt32BigEndian(dst, value); value = (uint)(16843009 * LossyUtils.Avg3(b, b2, b3)); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(32), value); value = (uint)(16843009 * LossyUtils.Avg3(b2, b3, b4)); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(64), value); value = (uint)(16843009 * LossyUtils.Avg3(b3, b4, b4)); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(96), value); } private static void Rd4(Span dst, Span top, int topOffset) { byte b = top[topOffset - 1]; byte b2 = top[topOffset - 2]; byte b3 = top[topOffset - 3]; byte b4 = top[topOffset - 4]; byte c = top[topOffset - 5]; byte b5 = top[topOffset]; byte b6 = top[topOffset + 1]; byte b7 = top[topOffset + 2]; byte a = top[topOffset + 3]; LossyUtils.Dst(dst, 0, 3, LossyUtils.Avg3(b3, b4, c)); byte v = LossyUtils.Avg3(b2, b3, b4); LossyUtils.Dst(dst, 0, 2, v); LossyUtils.Dst(dst, 1, 3, v); byte v2 = LossyUtils.Avg3(b, b2, b3); LossyUtils.Dst(dst, 0, 1, v2); LossyUtils.Dst(dst, 1, 2, v2); LossyUtils.Dst(dst, 2, 3, v2); byte v3 = LossyUtils.Avg3(b5, b, b2); LossyUtils.Dst(dst, 0, 0, v3); LossyUtils.Dst(dst, 1, 1, v3); LossyUtils.Dst(dst, 2, 2, v3); LossyUtils.Dst(dst, 3, 3, v3); byte v4 = LossyUtils.Avg3(b6, b5, b); LossyUtils.Dst(dst, 1, 0, v4); LossyUtils.Dst(dst, 2, 1, v4); LossyUtils.Dst(dst, 3, 2, v4); byte v5 = LossyUtils.Avg3(b7, b6, b5); LossyUtils.Dst(dst, 2, 0, v5); LossyUtils.Dst(dst, 3, 1, v5); LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg3(a, b7, b6)); } private static void Vr4(Span dst, Span top, int topOffset) { byte b = top[topOffset - 1]; byte b2 = top[topOffset - 2]; byte b3 = top[topOffset - 3]; byte a = top[topOffset - 4]; byte b4 = top[topOffset]; byte b5 = top[topOffset + 1]; byte b6 = top[topOffset + 2]; byte b7 = top[topOffset + 3]; byte v = LossyUtils.Avg2(b, b4); LossyUtils.Dst(dst, 0, 0, v); LossyUtils.Dst(dst, 1, 2, v); byte v2 = LossyUtils.Avg2(b4, b5); LossyUtils.Dst(dst, 1, 0, v2); LossyUtils.Dst(dst, 2, 2, v2); byte v3 = LossyUtils.Avg2(b5, b6); LossyUtils.Dst(dst, 2, 0, v3); LossyUtils.Dst(dst, 3, 2, v3); LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg2(b6, b7)); LossyUtils.Dst(dst, 0, 3, LossyUtils.Avg3(a, b3, b2)); LossyUtils.Dst(dst, 0, 2, LossyUtils.Avg3(b3, b2, b)); byte v4 = LossyUtils.Avg3(b2, b, b4); LossyUtils.Dst(dst, 0, 1, v4); LossyUtils.Dst(dst, 1, 3, v4); byte v5 = LossyUtils.Avg3(b, b4, b5); LossyUtils.Dst(dst, 1, 1, v5); LossyUtils.Dst(dst, 2, 3, v5); byte v6 = LossyUtils.Avg3(b4, b5, b6); LossyUtils.Dst(dst, 2, 1, v6); LossyUtils.Dst(dst, 3, 3, v6); LossyUtils.Dst(dst, 3, 1, LossyUtils.Avg3(b5, b6, b7)); } private static void Ld4(Span dst, Span top, int topOffset) { byte a = top[topOffset]; byte b = top[topOffset + 1]; byte b2 = top[topOffset + 2]; byte b3 = top[topOffset + 3]; byte b4 = top[topOffset + 4]; byte b5 = top[topOffset + 5]; byte b6 = top[topOffset + 6]; byte b7 = top[topOffset + 7]; LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg3(a, b, b2)); byte v = LossyUtils.Avg3(b, b2, b3); LossyUtils.Dst(dst, 1, 0, v); LossyUtils.Dst(dst, 0, 1, v); byte v2 = LossyUtils.Avg3(b2, b3, b4); LossyUtils.Dst(dst, 2, 0, v2); LossyUtils.Dst(dst, 1, 1, v2); LossyUtils.Dst(dst, 0, 2, v2); byte v3 = LossyUtils.Avg3(b3, b4, b5); LossyUtils.Dst(dst, 3, 0, v3); LossyUtils.Dst(dst, 2, 1, v3); LossyUtils.Dst(dst, 1, 2, v3); LossyUtils.Dst(dst, 0, 3, v3); byte v4 = LossyUtils.Avg3(b4, b5, b6); LossyUtils.Dst(dst, 3, 1, v4); LossyUtils.Dst(dst, 2, 2, v4); LossyUtils.Dst(dst, 1, 3, v4); byte v5 = LossyUtils.Avg3(b5, b6, b7); LossyUtils.Dst(dst, 3, 2, v5); LossyUtils.Dst(dst, 2, 3, v5); LossyUtils.Dst(dst, 3, 3, LossyUtils.Avg3(b6, b7, b7)); } private static void Vl4(Span dst, Span top, int topOffset) { byte a = top[topOffset]; byte b = top[topOffset + 1]; byte b2 = top[topOffset + 2]; byte b3 = top[topOffset + 3]; byte b4 = top[topOffset + 4]; byte b5 = top[topOffset + 5]; byte b6 = top[topOffset + 6]; byte c = top[topOffset + 7]; LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg2(a, b)); byte v = LossyUtils.Avg2(b, b2); LossyUtils.Dst(dst, 1, 0, v); LossyUtils.Dst(dst, 0, 2, v); byte v2 = LossyUtils.Avg2(b2, b3); LossyUtils.Dst(dst, 2, 0, v2); LossyUtils.Dst(dst, 1, 2, v2); byte v3 = LossyUtils.Avg2(b3, b4); LossyUtils.Dst(dst, 3, 0, v3); LossyUtils.Dst(dst, 2, 2, v3); LossyUtils.Dst(dst, 0, 1, LossyUtils.Avg3(a, b, b2)); byte v4 = LossyUtils.Avg3(b, b2, b3); LossyUtils.Dst(dst, 1, 1, v4); LossyUtils.Dst(dst, 0, 3, v4); byte v5 = LossyUtils.Avg3(b2, b3, b4); LossyUtils.Dst(dst, 2, 1, v5); LossyUtils.Dst(dst, 1, 3, v5); byte v6 = LossyUtils.Avg3(b3, b4, b5); LossyUtils.Dst(dst, 3, 1, v6); LossyUtils.Dst(dst, 2, 3, v6); LossyUtils.Dst(dst, 3, 2, LossyUtils.Avg3(b4, b5, b6)); LossyUtils.Dst(dst, 3, 3, LossyUtils.Avg3(b5, b6, c)); } private static void Hd4(Span dst, Span top, int topOffset) { byte b = top[topOffset - 1]; byte b2 = top[topOffset - 2]; byte b3 = top[topOffset - 3]; byte b4 = top[topOffset - 4]; byte a = top[topOffset - 5]; byte b5 = top[topOffset]; byte b6 = top[topOffset + 1]; byte c = top[topOffset + 2]; byte v = LossyUtils.Avg2(b2, b); LossyUtils.Dst(dst, 0, 0, v); LossyUtils.Dst(dst, 2, 1, v); byte v2 = LossyUtils.Avg2(b3, b2); LossyUtils.Dst(dst, 0, 1, v2); LossyUtils.Dst(dst, 2, 2, v2); byte v3 = LossyUtils.Avg2(b4, b3); LossyUtils.Dst(dst, 0, 2, v3); LossyUtils.Dst(dst, 2, 3, v3); LossyUtils.Dst(dst, 0, 3, LossyUtils.Avg2(a, b4)); LossyUtils.Dst(dst, 3, 0, LossyUtils.Avg3(b5, b6, c)); LossyUtils.Dst(dst, 2, 0, LossyUtils.Avg3(b, b5, b6)); byte v4 = LossyUtils.Avg3(b2, b, b5); LossyUtils.Dst(dst, 1, 0, v4); LossyUtils.Dst(dst, 3, 1, v4); byte v5 = LossyUtils.Avg3(b3, b2, b); LossyUtils.Dst(dst, 1, 1, v5); LossyUtils.Dst(dst, 3, 2, v5); byte v6 = LossyUtils.Avg3(b4, b3, b2); LossyUtils.Dst(dst, 1, 2, v6); LossyUtils.Dst(dst, 3, 3, v6); LossyUtils.Dst(dst, 1, 3, LossyUtils.Avg3(a, b4, b3)); } private static void Hu4(Span dst, Span top, int topOffset) { byte a = top[topOffset - 2]; byte b = top[topOffset - 3]; byte b2 = top[topOffset - 4]; byte b3 = top[topOffset - 5]; LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg2(a, b)); byte v = LossyUtils.Avg2(b, b2); LossyUtils.Dst(dst, 2, 0, v); LossyUtils.Dst(dst, 0, 1, v); byte v2 = LossyUtils.Avg2(b2, b3); LossyUtils.Dst(dst, 2, 1, v2); LossyUtils.Dst(dst, 0, 2, v2); LossyUtils.Dst(dst, 1, 0, LossyUtils.Avg3(a, b, b2)); byte v3 = LossyUtils.Avg3(b, b2, b3); LossyUtils.Dst(dst, 3, 0, v3); LossyUtils.Dst(dst, 1, 1, v3); byte v4 = LossyUtils.Avg3(b2, b3, b3); LossyUtils.Dst(dst, 3, 1, v4); LossyUtils.Dst(dst, 1, 2, v4); LossyUtils.Dst(dst, 3, 2, b3); LossyUtils.Dst(dst, 2, 2, b3); LossyUtils.Dst(dst, 0, 3, b3); LossyUtils.Dst(dst, 1, 3, b3); LossyUtils.Dst(dst, 2, 3, b3); LossyUtils.Dst(dst, 3, 3, b3); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Fill(Span dst, int value, int size) { for (int i = 0; i < size; i++) { dst.Slice(i * 32, size).Fill((byte)value); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte Clip8b(int v) { if ((v & -256) != 0) { if (v >= 0) { return byte.MaxValue; } return 0; } return (byte)v; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Store(Span dst, Span reference, int x, int y, int v) { dst[x + y * 32] = LossyUtils.Clip8B(reference[x + y * 32] + (v >> 3)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Mul(int a, int b) { return a * b >> 16; } } internal class Vp8EncProba { private const int MaxVariableLevel = 67; private const int SkipProbaThreshold = 250; public byte[] Segments { get; } public byte SkipProba { get; set; } public bool UseSkipProba { get; set; } public Vp8BandProbas[][] Coeffs { get; } public Vp8Stats[][] Stats { get; } public Vp8Costs[][] LevelCost { get; } public Vp8Costs[][] RemappedCosts { get; } public int NbSkip { get; set; } public bool Dirty { get; set; } public Vp8EncProba() { Dirty = true; UseSkipProba = false; Segments = new byte[3]; Coeffs = new Vp8BandProbas[4][]; for (int i = 0; i < Coeffs.Length; i++) { Coeffs[i] = new Vp8BandProbas[8]; for (int j = 0; j < Coeffs[i].Length; j++) { Coeffs[i][j] = new Vp8BandProbas(); } } Stats = new Vp8Stats[4][]; for (int k = 0; k < Coeffs.Length; k++) { Stats[k] = new Vp8Stats[8]; for (int l = 0; l < Stats[k].Length; l++) { Stats[k][l] = new Vp8Stats(); } } LevelCost = new Vp8Costs[4][]; for (int m = 0; m < LevelCost.Length; m++) { LevelCost[m] = new Vp8Costs[8]; for (int n = 0; n < LevelCost[m].Length; n++) { LevelCost[m][n] = new Vp8Costs(); } } RemappedCosts = new Vp8Costs[4][]; for (int num = 0; num < RemappedCosts.Length; num++) { RemappedCosts[num] = new Vp8Costs[16]; for (int num2 = 0; num2 < RemappedCosts[num].Length; num2++) { RemappedCosts[num][num2] = new Vp8Costs(); } } Segments.AsSpan().Fill(byte.MaxValue); for (int num3 = 0; num3 < 4; num3++) { for (int num4 = 0; num4 < 8; num4++) { for (int num5 = 0; num5 < 3; num5++) { Vp8ProbaArray vp8ProbaArray = Coeffs[num3][num4].Probabilities[num5]; for (int num6 = 0; num6 < 11; num6++) { vp8ProbaArray.Probabilities[num6] = WebpLookupTables.DefaultCoeffsProba[num3, num4, num5, num6]; } } } } } public void CalculateLevelCosts() { if (!Dirty) { return; } for (int i = 0; i < 4; i++) { for (int j = 0; j < 8; j++) { for (int k = 0; k < 3; k++) { Vp8ProbaArray vp8ProbaArray = Coeffs[i][j].Probabilities[k]; Vp8CostArray vp8CostArray = LevelCost[i][j].Costs[k]; int num = ((k > 0) ? LossyUtils.Vp8BitCost(1, vp8ProbaArray.Probabilities[0]) : 0); int num2 = LossyUtils.Vp8BitCost(1, vp8ProbaArray.Probabilities[1]) + num; vp8CostArray.Costs[0] = (ushort)(LossyUtils.Vp8BitCost(0, vp8ProbaArray.Probabilities[1]) + num); for (int l = 1; l <= 67; l++) { vp8CostArray.Costs[l] = (ushort)(num2 + VariableLevelCost(l, vp8ProbaArray.Probabilities)); } } } for (int m = 0; m < 16; m++) { for (int n = 0; n < 3; n++) { Vp8CostArray vp8CostArray2 = RemappedCosts[i][m].Costs[n]; LevelCost[i][WebpConstants.Vp8EncBands[m]].Costs[n].Costs.CopyTo(vp8CostArray2.Costs.AsSpan()); } } } Dirty = false; } public int FinalizeTokenProbas() { bool flag = false; int num = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 8; j++) { for (int k = 0; k < 3; k++) { for (int l = 0; l < 11; l++) { uint num2 = Stats[i][j].Stats[k].Stats[l]; int nb = (int)(num2 & 0xFFFF); int total = (int)((num2 >> 16) & 0xFFFF); int num3 = WebpLookupTables.CoeffsUpdateProba[i, j, k, l]; int num4 = WebpLookupTables.DefaultCoeffsProba[i, j, k, l]; int num5 = CalcTokenProba(nb, total); int num6 = BranchCost(nb, total, num4) + LossyUtils.Vp8BitCost(0, (byte)num3); int num7 = BranchCost(nb, total, num5) + LossyUtils.Vp8BitCost(1, (byte)num3) + 2048; bool flag2 = num6 > num7; num += LossyUtils.Vp8BitCost(flag2 ? 1 : 0, (byte)num3); if (flag2) { Coeffs[i][j].Probabilities[k].Probabilities[l] = (byte)num5; flag = flag || num5 != num4; num += 2048; } else { Coeffs[i][j].Probabilities[k].Probabilities[l] = (byte)num4; } } } } } Dirty = flag; return num; } public int FinalizeSkipProba(int mbw, int mbh) { int num = mbw * mbh; int nbSkip = NbSkip; SkipProba = (byte)CalcSkipProba(nbSkip, num); UseSkipProba = SkipProba < 250; int num2 = 256; if (UseSkipProba) { num2 += nbSkip * LossyUtils.Vp8BitCost(1, SkipProba) + (num - nbSkip) * LossyUtils.Vp8BitCost(0, SkipProba); num2 += 2048; } return num2; } public void ResetTokenStats() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 8; j++) { for (int k = 0; k < 3; k++) { for (int l = 0; l < 11; l++) { Stats[i][j].Stats[k].Stats[l] = 0u; } } } } } private static int CalcSkipProba(long nb, long total) { return (int)((total != 0L) ? ((total - nb) * 255 / total) : 255); } private static int VariableLevelCost(int level, Span probas) { int num = WebpLookupTables.Vp8LevelCodes[level - 1][0]; int num2 = WebpLookupTables.Vp8LevelCodes[level - 1][1]; int num3 = 0; int num4 = 2; while (num != 0) { if ((num & 1) != 0) { num3 += LossyUtils.Vp8BitCost(num2 & 1, probas[num4]); } num2 >>= 1; num >>= 1; num4++; } return num3; } private static int CalcTokenProba(int nb, int total) { if (nb == 0) { return 255; } return 255 - nb * 255 / total; } private static int BranchCost(int nb, int total, int proba) { return nb * LossyUtils.Vp8BitCost(1, (byte)proba) + (total - nb) * LossyUtils.Vp8BitCost(0, (byte)proba); } } internal class Vp8EncSegmentHeader { public int NumSegments { get; } public bool UpdateMap { get; set; } public int Size { get; set; } public Vp8EncSegmentHeader(int numSegments) { NumSegments = numSegments; UpdateMap = NumSegments > 1; Size = 0; } } internal class Vp8FilterHeader { private const int NumRefLfDeltas = 4; private const int NumModeLfDeltas = 4; private int filterLevel; private int sharpness; public LoopFilter LoopFilter { get; set; } public int FilterLevel { get { return filterLevel; } set { Guard.MustBeBetweenOrEqualTo(value, 0, 63, "FilterLevel"); filterLevel = value; } } public int Sharpness { get { return sharpness; } set { Guard.MustBeBetweenOrEqualTo(value, 0, 7, "Sharpness"); sharpness = value; } } public bool Simple { get; set; } public int I4x4LfDelta { get; set; } public bool UseLfDelta { get; set; } public int[] RefLfDelta { get; } public int[] ModeLfDelta { get; } public Vp8FilterHeader() { RefLfDelta = new int[4]; ModeLfDelta = new int[4]; } } internal class Vp8FilterInfo : IDeepCloneable { private byte limit; private byte innerLevel; private byte highEdgeVarianceThreshold; public byte Limit { get { return limit; } set { Guard.MustBeBetweenOrEqualTo(value, (byte)0, (byte)189, "Limit"); limit = value; } } public byte InnerLevel { get { return innerLevel; } set { Guard.MustBeBetweenOrEqualTo(value, (byte)0, (byte)63, "InnerLevel"); innerLevel = value; } } public bool UseInnerFiltering { get; set; } public byte HighEdgeVarianceThreshold { get { return highEdgeVarianceThreshold; } set { Guard.MustBeBetweenOrEqualTo(value, (byte)0, (byte)2, "HighEdgeVarianceThreshold"); highEdgeVarianceThreshold = value; } } public Vp8FilterInfo() { } public Vp8FilterInfo(Vp8FilterInfo other) { Limit = other.Limit; HighEdgeVarianceThreshold = other.HighEdgeVarianceThreshold; InnerLevel = other.InnerLevel; UseInnerFiltering = other.UseInnerFiltering; } public IDeepCloneable DeepClone() { return new Vp8FilterInfo(this); } } internal class Vp8FrameHeader { public bool KeyFrame { get; set; } public sbyte Profile { get; set; } public uint PartitionLength { get; set; } } internal sealed class Vp8Histogram { private readonly int[] scratch = new int[16]; private readonly short[] output = new short[16]; private readonly int[] distribution = new int[32]; private const int MaxCoeffThresh = 31; private static readonly Vector256 MaxCoeffThreshVec = Vector256.Create((short)31); private int maxValue; private int lastNonZero; public Vp8Histogram() { maxValue = 0; lastNonZero = 1; } public int GetAlpha() { int num = maxValue; int num2 = lastNonZero; if (num <= 1) { return 0; } return 510 * num2 / num; } public void CollectHistogram(Span reference, Span pred, int startBlock, int endBlock) { distribution.AsSpan().Clear(); for (int i = startBlock; i < endBlock; i++) { Vp8Encoding.FTransform(reference.Slice(WebpLookupTables.Vp8DspScan[i]), pred.Slice(WebpLookupTables.Vp8DspScan[i]), output, scratch); if (Avx2.IsSupported) { ref short reference2 = ref MemoryMarshal.GetReference(output); Vector256 vector = Avx2.Min(Avx2.ShiftRightArithmetic(Avx2.Abs(Unsafe.As>(ref reference2).AsInt16()).AsInt16(), 3), MaxCoeffThreshVec); Unsafe.As>(ref reference2) = vector; for (int j = 0; j < 16; j++) { distribution[output[j]]++; } } else { for (int k = 0; k < 16; k++) { int num = ClipMax(Math.Abs(output[k]) >> 3, 31); distribution[num]++; } } } SetHistogramData(distribution); } public void Merge(Vp8Histogram other) { if (maxValue > other.maxValue) { other.maxValue = maxValue; } if (lastNonZero > other.lastNonZero) { other.lastNonZero = lastNonZero; } } private void SetHistogramData(int[] distribution) { int num = 0; int num2 = 1; for (int i = 0; i <= 31; i++) { int num3 = distribution[i]; if (num3 > 0) { if (num3 > num) { num = num3; } num2 = i; } } maxValue = num; lastNonZero = num2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int ClipMax(int v, int max) { if (v <= max) { return v; } return max; } } internal ref struct Vp8Io { public int Width { get; set; } public int Height { get; set; } public int MbY { get; set; } public int MbW { get; set; } public int MbH { get; set; } public Span Y { get; set; } public Span U { get; set; } public Span V { get; set; } public int YStride { get; set; } public int UvStride { get; set; } public bool UseScaling { get; set; } public int ScaledWidth { get; set; } public int ScaledHeight { get; set; } } internal class Vp8MacroBlock { public uint NoneZeroAcDcCoeffs { get; set; } public uint NoneZeroDcCoeffs { get; set; } } internal class Vp8MacroBlockData { public short[] Coeffs { get; set; } public bool IsI4x4 { get; set; } public byte[] Modes { get; } public byte UvMode { get; set; } public uint NonZeroY { get; set; } public uint NonZeroUv { get; set; } public bool Skip { get; set; } public byte Segment { get; set; } public Vp8MacroBlockData() { Modes = new byte[16]; Coeffs = new short[384]; } } [DebuggerDisplay("Type: {MacroBlockType}, Alpha: {Alpha}, UvMode: {UvMode}")] internal class Vp8MacroBlockInfo { public Vp8MacroBlockType MacroBlockType { get; set; } public int UvMode { get; set; } public bool Skip { get; set; } public int Segment { get; set; } public int Alpha { get; set; } } internal enum Vp8MacroBlockType { I4X4, I16X16 } internal struct Vp8Matrix { private static readonly int[][] BiasMatrices = new int[3][] { new int[2] { 96, 110 }, new int[2] { 96, 108 }, new int[2] { 110, 115 } }; private static readonly byte[] FreqSharpening = new byte[16] { 0, 30, 60, 90, 30, 60, 90, 90, 60, 90, 90, 90, 90, 90, 90, 90 }; private const int SharpenBits = 11; public unsafe fixed ushort Q[16]; public unsafe fixed ushort IQ[16]; public unsafe fixed uint Bias[16]; public unsafe fixed uint ZThresh[16]; public unsafe fixed short Sharpen[16]; public unsafe int Expand(int type) { for (int i = 0; i < 2; i++) { int num = ((i > 0) ? 1 : 0); int b = BiasMatrices[type][num]; IQ[i] = (ushort)(131072 / Q[i]); Bias[i] = (uint)BIAS(b); ZThresh[i] = (131071 - Bias[i]) / IQ[i]; } for (int i = 2; i < 16; i++) { Q[i] = Q[1]; IQ[i] = IQ[1]; Bias[i] = Bias[1]; ZThresh[i] = ZThresh[1]; } int num2 = 0; for (int i = 0; i < 16; i++) { if (type == 0) { Sharpen[i] = (short)(FreqSharpening[i] * Q[i] >> 11); } else { Sharpen[i] = 0; } num2 += Q[i]; } return num2 + 8 >> 4; } private static int BIAS(int b) { return b << 9; } } internal class Vp8ModeScore { public const long MaxCost = 36028797018963967L; private const int RdDistoMult = 256; public long D { get; set; } public long SD { get; set; } public long H { get; set; } public long R { get; set; } public long Score { get; set; } public short[] YDcLevels { get; } public short[] YAcLevels { get; } public short[] UvLevels { get; } public int ModeI16 { get; set; } public byte[] ModesI4 { get; } public int ModeUv { get; set; } public uint Nz { get; set; } public int[,] Derr { get; } public Vp8ModeScore() { YDcLevels = new short[16]; YAcLevels = new short[256]; UvLevels = new short[128]; ModesI4 = new byte[16]; Derr = new int[2, 3]; } public void Clear() { Array.Clear(YDcLevels, 0, YDcLevels.Length); Array.Clear(YAcLevels, 0, YAcLevels.Length); Array.Clear(UvLevels, 0, UvLevels.Length); Array.Clear(ModesI4, 0, ModesI4.Length); Array.Clear(Derr, 0, Derr.Length); } public void InitScore() { D = 0L; SD = 0L; R = 0L; H = 0L; Nz = 0u; Score = 36028797018963967L; } public void CopyScore(Vp8ModeScore other) { D = other.D; SD = other.SD; R = other.R; H = other.H; Nz = other.Nz; Score = other.Score; } public void AddScore(Vp8ModeScore other) { D += other.D; SD += other.SD; R += other.R; H += other.H; Nz |= other.Nz; Score += other.Score; } public void SetRdScore(int lambda) { Score = (R + H) * lambda + 256 * (D + SD); } } internal class Vp8PictureHeader { public uint Width { get; set; } public uint Height { get; set; } public sbyte XScale { get; set; } public sbyte YScale { get; set; } public sbyte ColorSpace { get; set; } public sbyte ClampType { get; set; } } internal class Vp8Proba { private const int MbFeatureTreeProbs = 3; public uint[] Segments { get; } public Vp8BandProbas[,] Bands { get; } public Vp8BandProbas[][] BandsPtr { get; } public Vp8Proba() { Segments = new uint[3]; Bands = new Vp8BandProbas[4, 8]; BandsPtr = new Vp8BandProbas[4][]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 8; j++) { Bands[i, j] = new Vp8BandProbas(); } } for (int k = 0; k < 4; k++) { BandsPtr[k] = new Vp8BandProbas[17]; } } } internal class Vp8ProbaArray { public byte[] Probabilities { get; } public Vp8ProbaArray() { Probabilities = new byte[11]; } } internal class Vp8QuantMatrix { private int dither; public int[] Y1Mat { get; } = new int[2]; public int[] Y2Mat { get; } = new int[2]; public int[] UvMat { get; } = new int[2]; public int UvQuant { get; set; } public int Dither { get { return dither; } set { Guard.MustBeBetweenOrEqualTo(value, 0, 255, "Dither"); dither = value; } } } internal enum Vp8RdLevel { RdOptNone, RdOptBasic, RdOptTrellis, RdOptTrellisAll } internal class Vp8Residual { private static readonly Vector256 Cst2 = Vector256.Create((byte)2); private static readonly Vector256 Cst67 = Vector256.Create((byte)67); private readonly byte[] scratch = new byte[32]; private readonly ushort[] scratchUShort = new ushort[16]; public int First { get; set; } public int Last { get; set; } public int CoeffType { get; set; } public short[] Coeffs { get; } = new short[16]; public Vp8BandProbas[] Prob { get; set; } public Vp8Stats[] Stats { get; set; } public Vp8Costs[] Costs { get; set; } public void Init(int first, int coeffType, Vp8EncProba prob) { First = first; CoeffType = coeffType; Prob = prob.Coeffs[CoeffType]; Stats = prob.Stats[CoeffType]; Costs = prob.RemappedCosts[CoeffType]; Coeffs.AsSpan().Clear(); } public void SetCoeffs(Span coeffs) { if (Sse2.IsSupported) { ref short reference = ref MemoryMarshal.GetReference(coeffs); Vector128 vector = Unsafe.As>(ref reference); Vector128 vector2 = Unsafe.As>(ref Unsafe.Add(ref reference, 8)); Vector128 value = Sse2.CompareEqual(Sse2.PackSignedSaturate(vector.AsInt16(), vector2.AsInt16()), Vector128.Zero); uint num = (uint)(0xFFFF ^ Sse2.MoveMask(value)); Last = ((num != 0) ? Numerics.Log2(num) : (-1)); } else { Last = -1; for (int num2 = 15; num2 >= 0; num2--) { if (coeffs[num2] != 0) { Last = num2; break; } } } coeffs.Slice(0, 16).CopyTo(Coeffs); } public int RecordCoeffs(int ctx) { int first = First; Vp8StatsArray statsArr = Stats[first].Stats[ctx]; if (Last < 0) { RecordStats(0, statsArr, 0); return 0; } while (first <= Last) { RecordStats(1, statsArr, 0); int num; while ((num = Coeffs[first++]) == 0) { RecordStats(0, statsArr, 1); statsArr = Stats[WebpConstants.Vp8EncBands[first]].Stats[0]; } RecordStats(1, statsArr, 1); bool flag = (uint)(num + 1) > 2u; if (RecordStats(flag ? 1 : 0, statsArr, 2) == 0) { statsArr = Stats[WebpConstants.Vp8EncBands[first]].Stats[1]; continue; } num = Math.Abs(num); if (num > 67) { num = 67; } int num2 = WebpLookupTables.Vp8LevelCodes[num - 1][1]; int num3 = WebpLookupTables.Vp8LevelCodes[num - 1][0]; int num4 = 0; while ((num3 >>= 1) != 0) { int num5 = 2 << num4; if ((num3 & 1) != 0) { RecordStats(((num2 & num5) != 0) ? 1 : 0, statsArr, 3 + num4); } num4++; } statsArr = Stats[WebpConstants.Vp8EncBands[first]].Stats[2]; } if (first < 16) { RecordStats(0, statsArr, 0); } return 1; } public int GetResidualCost(int ctx0) { int i = First; int num = Prob[i].Probabilities[ctx0].Probabilities[0]; Vp8Costs[] costs = Costs; Vp8CostArray vp8CostArray = costs[i].Costs[ctx0]; int num2 = ((ctx0 == 0) ? LossyUtils.Vp8BitCost(1, (byte)num) : 0); if (Last < 0) { return LossyUtils.Vp8BitCost(0, (byte)num); } if (Avx2.IsSupported) { Span span = scratch.AsSpan(0, 16); Span span2 = scratch.AsSpan(16, 16); Span span3 = scratchUShort.AsSpan(); Vector256 vector = Unsafe.As>(ref MemoryMarshal.GetReference(Coeffs)).AsInt16(); Vector256 right = Avx2.Subtract(Vector256.Zero, vector); Vector256 vector2 = Avx2.Max(vector, right); Vector256 vector3 = Avx2.PackSignedSaturate(vector2, vector2); Vector256 vector4 = Avx2.Min(vector3.AsByte(), Cst2); Vector256 vector5 = Avx2.Min(vector3.AsByte(), Cst67); ref byte reference = ref MemoryMarshal.GetReference(span); ref byte reference2 = ref MemoryMarshal.GetReference(span2); ref ushort reference3 = ref MemoryMarshal.GetReference(span3); Unsafe.As>(ref reference) = vector4.GetLower(); Unsafe.As>(ref reference2) = vector5.GetLower(); Unsafe.As>(ref reference3) = vector2.AsUInt16(); int num4; int num5; for (; i < Last; i++) { int num3 = span[i]; num4 = span2[i]; num5 = span3[i]; num2 += WebpLookupTables.Vp8LevelFixedCosts[num5] + vp8CostArray.Costs[num4]; vp8CostArray = costs[i + 1].Costs[num3]; } num4 = span2[i]; num5 = span3[i]; num2 += WebpLookupTables.Vp8LevelFixedCosts[num5] + vp8CostArray.Costs[num4]; if (i < 15) { int num6 = WebpConstants.Vp8EncBands[i + 1]; int num7 = span[i]; int num8 = Prob[num6].Probabilities[num7].Probabilities[0]; num2 += LossyUtils.Vp8BitCost(0, (byte)num8); } return num2; } int num9; for (; i < Last; i++) { num9 = Math.Abs(Coeffs[i]); int num10 = ((num9 >= 2) ? 2 : num9); num2 += LevelCost(vp8CostArray.Costs, num9); vp8CostArray = costs[i + 1].Costs[num10]; } num9 = Math.Abs(Coeffs[i]); num2 += LevelCost(vp8CostArray.Costs, num9); if (i < 15) { int num11 = WebpConstants.Vp8EncBands[i + 1]; int num12 = ((num9 == 1) ? 1 : 2); int num13 = Prob[num11].Probabilities[num12].Probabilities[0]; num2 += LossyUtils.Vp8BitCost(0, (byte)num13); } return num2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LevelCost(Span table, int level) { return WebpLookupTables.Vp8LevelFixedCosts[level] + table[(level > 67) ? 67 : level]; } private int RecordStats(int bit, Vp8StatsArray statsArr, int idx) { if (statsArr.Stats[idx] >= 4294836224u) { statsArr.Stats[idx] = (statsArr.Stats[idx] + 1 >> 1) & 0x7FFF7FFF; } statsArr.Stats[idx] += (uint)(65536 + bit); return bit; } } internal class Vp8SegmentHeader { private const int NumMbSegments = 4; public bool UseSegment { get; set; } public bool UpdateMap { get; set; } public bool Delta { get; set; } public byte[] Quantizer { get; } public byte[] FilterStrength { get; } public Vp8SegmentHeader() { Quantizer = new byte[4]; FilterStrength = new byte[4]; } } internal class Vp8SegmentInfo { public Vp8Matrix Y1; public Vp8Matrix Y2; public Vp8Matrix Uv; public int Alpha { get; set; } public int Beta { get; set; } public int Quant { get; set; } public int FStrength { get; set; } public int MaxEdge { get; set; } public long I4Penalty { get; set; } public int MinDisto { get; set; } public int LambdaI16 { get; set; } public int LambdaI4 { get; set; } public int TLambda { get; set; } public int LambdaUv { get; set; } public int LambdaMode { get; set; } public void StoreMaxDelta(Span dcs) { int num = Math.Abs(dcs[1]); int num2 = Math.Abs(dcs[2]); int num3 = Math.Abs(dcs[4]); int num4 = ((num2 > num) ? num2 : num); num4 = ((num3 > num4) ? num3 : num4); if (num4 > MaxEdge) { MaxEdge = num4; } } } internal class Vp8Stats { public Vp8StatsArray[] Stats { get; } public Vp8Stats() { Stats = new Vp8StatsArray[3]; for (int i = 0; i < 3; i++) { Stats[i] = new Vp8StatsArray(); } } } internal class Vp8StatsArray { public uint[] Stats { get; } public Vp8StatsArray() { Stats = new uint[11]; } } internal class Vp8TopSamples { public byte[] Y { get; } = new byte[16]; public byte[] U { get; } = new byte[8]; public byte[] V { get; } = new byte[8]; } internal sealed class WebpLossyDecoder { private readonly Vp8BitReader bitReader; private readonly MemoryAllocator memoryAllocator; private readonly Configuration configuration; private readonly int[] scratch = new int[16]; private readonly byte[] scratchBytes = new byte[4]; public WebpLossyDecoder(Vp8BitReader bitReader, MemoryAllocator memoryAllocator, Configuration configuration) { this.bitReader = bitReader; this.memoryAllocator = memoryAllocator; this.configuration = configuration; } public void Decode(Buffer2D pixels, int width, int height, WebpImageInfo info) where TPixel : unmanaged, IPixel { sbyte colorSpace = (sbyte)bitReader.ReadValue(1); sbyte clampType = (sbyte)bitReader.ReadValue(1); Vp8PictureHeader pictureHeader = new Vp8PictureHeader { Width = (uint)width, Height = (uint)height, XScale = info.XScale, YScale = info.YScale, ColorSpace = colorSpace, ClampType = clampType }; Vp8Proba vp8Proba = new Vp8Proba(); Vp8SegmentHeader segmentHeader = ParseSegmentHeader(vp8Proba); using Vp8Decoder vp8Decoder = new Vp8Decoder(info.Vp8FrameHeader, pictureHeader, segmentHeader, vp8Proba, memoryAllocator); Vp8Io io = InitializeVp8Io(vp8Decoder, pictureHeader); ParseFilterHeader(vp8Decoder); vp8Decoder.PrecomputeFilterStrengths(); ParsePartitions(vp8Decoder); ParseDequantizationIndices(vp8Decoder); bitReader.ReadBool(); ParseProbabilities(vp8Decoder); ParseFrame(vp8Decoder, io); WebpFeatures features = info.Features; if (features != null && features.Alpha) { using (AlphaDecoder alphaDecoder = new AlphaDecoder(width, height, info.Features.AlphaData, info.Features.AlphaChunkHeader, memoryAllocator, configuration)) { alphaDecoder.Decode(); DecodePixelValues(width, height, vp8Decoder.Pixels.Memory.Span, pixels, alphaDecoder.Alpha); return; } } DecodePixelValues(width, height, vp8Decoder.Pixels.Memory.Span, pixels); } private void DecodePixelValues(int width, int height, Span pixelData, Buffer2D decodedPixels) where TPixel : unmanaged, IPixel { int num = width * 3; for (int i = 0; i < height; i++) { Span span = pixelData.Slice(i * num, num); Span destinationPixels = decodedPixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromBgr24Bytes(configuration, span, destinationPixels, width); } } private void DecodePixelValues(int width, int height, Span pixelData, Buffer2D decodedPixels, IMemoryOwner alpha) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); Span span = alpha.Memory.Span; Span span2 = MemoryMarshal.Cast(pixelData); for (int i = 0; i < height; i++) { int num = i * width; Span span3 = decodedPixels.DangerousGetRowSpan(i); for (int j = 0; j < width; j++) { int index = num + j; Bgr24 bgr = span2[index]; val.FromBgra32(new Bgra32(bgr.R, bgr.G, bgr.B, span[index])); span3[j] = val; } } } private void ParseFrame(Vp8Decoder dec, Vp8Io io) { dec.MbY = 0; while (dec.MbY < dec.BottomRightMbY) { long num = dec.MbY & dec.NumPartsMinusOne; Vp8BitReader bitreader = dec.Vp8BitReaders[num]; for (int i = 0; i < dec.MbWidth; i++) { ParseIntraMode(dec, i); } int mbX; while (dec.MbX < dec.MbWidth) { DecodeMacroBlock(dec, bitreader); mbX = dec.MbX + 1; dec.MbX = mbX; } InitScanline(dec); ProcessRow(dec, io); mbX = dec.MbY + 1; dec.MbY = mbX; } } private void ParseIntraMode(Vp8Decoder dec, int mbX) { Vp8MacroBlockData vp8MacroBlockData = dec.MacroBlockData[mbX]; Span span = dec.IntraT.AsSpan(4 * mbX, 4); byte[] intraL = dec.IntraL; if (dec.SegmentHeader.UpdateMap) { vp8MacroBlockData.Segment = ((bitReader.GetBit((int)dec.Probabilities.Segments[0]) == 0) ? ((byte)bitReader.GetBit((int)dec.Probabilities.Segments[1])) : ((byte)(bitReader.GetBit((int)dec.Probabilities.Segments[2]) + 2))); } else { vp8MacroBlockData.Segment = 0; } if (dec.UseSkipProbability) { vp8MacroBlockData.Skip = bitReader.GetBit(dec.SkipProbability) == 1; } vp8MacroBlockData.IsI4x4 = bitReader.GetBit(145) == 0; if (!vp8MacroBlockData.IsI4x4) { int num = ((bitReader.GetBit(156) == 0) ? ((bitReader.GetBit(163) != 0) ? 2 : 0) : ((bitReader.GetBit(128) != 0) ? 1 : 3)); vp8MacroBlockData.Modes[0] = (byte)num; for (int i = 0; i < intraL.Length; i++) { intraL[i] = (byte)num; span[i] = (byte)num; } } else { Span destination = vp8MacroBlockData.Modes.AsSpan(); for (int j = 0; j < 4; j++) { int num2 = intraL[j]; for (int k = 0; k < 4; k++) { byte[] array = WebpLookupTables.ModesProba[span[k], num2]; int num3; for (num3 = WebpConstants.YModesIntra4[bitReader.GetBit(array[0])]; num3 > 0; num3 = WebpConstants.YModesIntra4[2 * num3 + bitReader.GetBit(array[num3])]) { } num2 = -num3; span[k] = (byte)num2; } span.CopyTo(destination); destination = destination.Slice(4); intraL[j] = (byte)num2; } } vp8MacroBlockData.UvMode = (byte)((bitReader.GetBit(142) != 0) ? ((bitReader.GetBit(114) == 0) ? 2u : ((bitReader.GetBit(183) != 0) ? 1u : 3u)) : 0u); } private void InitScanline(Vp8Decoder dec) { Vp8MacroBlock leftMacroBlock = dec.LeftMacroBlock; leftMacroBlock.NoneZeroAcDcCoeffs = 0u; leftMacroBlock.NoneZeroDcCoeffs = 0u; for (int i = 0; i < dec.IntraL.Length; i++) { dec.IntraL[i] = 0; } dec.MbX = 0; } private void ProcessRow(Vp8Decoder dec, Vp8Io io) { ReconstructRow(dec); FinishRow(dec, io); } private void ReconstructRow(Vp8Decoder dec) { int mbY = dec.MbY; Memory memory = dec.YuvBuffer.Memory; Span span = memory.Span; Span dst = span.Slice(40); Span dst2 = span.Slice(584); Span dst3 = span.Slice(600); int num = 512; for (int i = 0; i < num; i += 32) { span[i - 1 + 40] = 129; } num = 256; for (int j = 0; j < num; j += 32) { span[j - 1 + 584] = 129; span[j - 1 + 600] = 129; } if (mbY > 0) { span[7] = (span[551] = (span[567] = 129)); } else { Span span2 = span.Slice(7, 21); for (int k = 0; k < span2.Length; k++) { span2[k] = 127; } span2 = span.Slice(551, 9); for (int l = 0; l < span2.Length; l++) { span2[l] = 127; } span2 = span.Slice(567, 9); for (int m = 0; m < span2.Length; m++) { span2[m] = 127; } } for (int n = 0; n < dec.MbWidth; n++) { Vp8MacroBlockData vp8MacroBlockData = dec.MacroBlockData[n]; Span span3; if (n > 0) { for (int num2 = -1; num2 < 16; num2++) { int start = num2 * 32 + 12 + 40; int start2 = num2 * 32 - 4 + 40; span3 = span.Slice(start, 4); span3.CopyTo(span.Slice(start2)); } for (int num3 = -1; num3 < 8; num3++) { int start3 = num3 * 32 + 4 + 584; int start4 = num3 * 32 - 4 + 584; span3 = span.Slice(start3, 4); span3.CopyTo(span.Slice(start4)); start3 = num3 * 32 + 4 + 600; start4 = num3 * 32 - 4 + 600; span3 = span.Slice(start3, 4); span3.CopyTo(span.Slice(start4)); } } Vp8TopSamples vp8TopSamples = dec.YuvTopSamples[n]; short[] coeffs = vp8MacroBlockData.Coeffs; uint num4 = vp8MacroBlockData.NonZeroY; if (mbY > 0) { vp8TopSamples.Y.CopyTo(span.Slice(8)); vp8TopSamples.U.CopyTo(span.Slice(552)); vp8TopSamples.V.CopyTo(span.Slice(568)); } if (vp8MacroBlockData.IsI4x4) { Span destination = span.Slice(24); if (mbY > 0) { if (n >= dec.MbWidth - 1) { byte b = vp8TopSamples.Y[15]; destination[0] = b; destination[1] = b; destination[2] = b; destination[3] = b; } else { span3 = dec.YuvTopSamples[n + 1].Y.AsSpan(0, 4); span3.CopyTo(destination); } } Span span4 = MemoryMarshal.Cast(span.Slice(24)); span4[32] = (span4[64] = (span4[96] = span4[0])); int num5 = 0; while (num5 < 16) { int num6 = 40 + WebpConstants.Scan[num5]; Span dst4 = span.Slice(num6); switch (vp8MacroBlockData.Modes[num5]) { case 0: LossyUtils.DC4(dst4, span, num6); break; case 1: LossyUtils.TM4(dst4, span, num6); break; case 2: LossyUtils.VE4(dst4, span, num6, scratchBytes); break; case 3: LossyUtils.HE4(dst4, span, num6); break; case 4: LossyUtils.RD4(dst4, span, num6); break; case 5: LossyUtils.VR4(dst4, span, num6); break; case 6: LossyUtils.LD4(dst4, span, num6); break; case 7: LossyUtils.VL4(dst4, span, num6); break; case 8: LossyUtils.HD4(dst4, span, num6); break; case 9: LossyUtils.HU4(dst4, span, num6); break; } DoTransform(num4, coeffs.AsSpan(num5 * 16), dst4, scratch); num5++; num4 <<= 2; } } else { switch (CheckMode(n, mbY, vp8MacroBlockData.Modes[0])) { case 0: LossyUtils.DC16(dst, span, 40); break; case 1: LossyUtils.TM16(dst, span, 40); break; case 2: LossyUtils.VE16(dst, span, 40); break; case 3: LossyUtils.HE16(dst, span, 40); break; case 4: LossyUtils.DC16NoTop(dst, span, 40); break; case 5: LossyUtils.DC16NoLeft(dst, span, 40); break; case 6: LossyUtils.DC16NoTopLeft(dst); break; } if (num4 != 0) { int num7 = 0; while (num7 < 16) { DoTransform(num4, coeffs.AsSpan(num7 * 16), dst.Slice(WebpConstants.Scan[num7]), scratch); num7++; num4 <<= 2; } } } uint nonZeroUv = vp8MacroBlockData.NonZeroUv; switch (CheckMode(n, mbY, vp8MacroBlockData.UvMode)) { case 0: LossyUtils.DC8uv(dst2, span, 584); LossyUtils.DC8uv(dst3, span, 600); break; case 1: LossyUtils.TM8uv(dst2, span, 584); LossyUtils.TM8uv(dst3, span, 600); break; case 2: LossyUtils.VE8uv(dst2, span, 584); LossyUtils.VE8uv(dst3, span, 600); break; case 3: LossyUtils.HE8uv(dst2, span, 584); LossyUtils.HE8uv(dst3, span, 600); break; case 4: LossyUtils.DC8uvNoTop(dst2, span, 584); LossyUtils.DC8uvNoTop(dst3, span, 600); break; case 5: LossyUtils.DC8uvNoLeft(dst2, span, 584); LossyUtils.DC8uvNoLeft(dst3, span, 600); break; case 6: LossyUtils.DC8uvNoTopLeft(dst2); LossyUtils.DC8uvNoTopLeft(dst3); break; } DoUVTransform(nonZeroUv, coeffs.AsSpan(256), dst2, scratch); DoUVTransform(nonZeroUv >> 8, coeffs.AsSpan(320), dst3, scratch); if (mbY < dec.MbHeight - 1) { span3 = dst.Slice(480, 16); span3.CopyTo(vp8TopSamples.Y); span3 = dst2.Slice(224, 8); span3.CopyTo(vp8TopSamples.U); span3 = dst3.Slice(224, 8); span3.CopyTo(vp8TopSamples.V); } memory = dec.CacheY.Memory; span3 = memory.Span; Span span5 = span3.Slice(dec.CacheYOffset + n * 16); memory = dec.CacheU.Memory; span3 = memory.Span; Span span6 = span3.Slice(dec.CacheUvOffset + n * 8); memory = dec.CacheV.Memory; span3 = memory.Span; Span span7 = span3.Slice(dec.CacheUvOffset + n * 8); for (int num8 = 0; num8 < 16; num8++) { span3 = dst.Slice(num8 * 32, Math.Min(16, span5.Length)); span3.CopyTo(span5.Slice(num8 * dec.CacheYStride)); } for (int num9 = 0; num9 < 8; num9++) { int start5 = num9 * dec.CacheUvStride; span3 = dst2.Slice(num9 * 32, Math.Min(8, span6.Length)); span3.CopyTo(span6.Slice(start5)); span3 = dst3.Slice(num9 * 32, Math.Min(8, span7.Length)); span3.CopyTo(span7.Slice(start5)); } } } private void FilterRow(Vp8Decoder dec) { int mbY = dec.MbY; for (int i = dec.TopLeftMbX; i < dec.BottomRightMbX; i++) { DoFilter(dec, i, mbY); } } private void DoFilter(Vp8Decoder dec, int mbx, int mby) { int cacheYStride = dec.CacheYStride; Vp8FilterInfo vp8FilterInfo = dec.FilterInfo[mbx]; int innerLevel = vp8FilterInfo.InnerLevel; int limit = vp8FilterInfo.Limit; if (limit == 0) { return; } Memory memory; if (dec.Filter == LoopFilter.Simple) { int offset = dec.CacheYOffset + mbx * 16; if (mbx > 0) { memory = dec.CacheY.Memory; LossyUtils.SimpleHFilter16(memory.Span, offset, cacheYStride, limit + 4); } if (vp8FilterInfo.UseInnerFiltering) { memory = dec.CacheY.Memory; LossyUtils.SimpleHFilter16i(memory.Span, offset, cacheYStride, limit); } if (mby > 0) { memory = dec.CacheY.Memory; LossyUtils.SimpleVFilter16(memory.Span, offset, cacheYStride, limit + 4); } if (vp8FilterInfo.UseInnerFiltering) { memory = dec.CacheY.Memory; LossyUtils.SimpleVFilter16i(memory.Span, offset, cacheYStride, limit); } } else if (dec.Filter == LoopFilter.Complex) { int cacheUvStride = dec.CacheUvStride; int offset2 = dec.CacheYOffset + mbx * 16; int offset3 = dec.CacheUvOffset + mbx * 8; int highEdgeVarianceThreshold = vp8FilterInfo.HighEdgeVarianceThreshold; if (mbx > 0) { memory = dec.CacheY.Memory; LossyUtils.HFilter16(memory.Span, offset2, cacheYStride, limit + 4, innerLevel, highEdgeVarianceThreshold); memory = dec.CacheU.Memory; Span span = memory.Span; memory = dec.CacheV.Memory; LossyUtils.HFilter8(span, memory.Span, offset3, cacheUvStride, limit + 4, innerLevel, highEdgeVarianceThreshold); } if (vp8FilterInfo.UseInnerFiltering) { memory = dec.CacheY.Memory; LossyUtils.HFilter16i(memory.Span, offset2, cacheYStride, limit, innerLevel, highEdgeVarianceThreshold); memory = dec.CacheU.Memory; Span span2 = memory.Span; memory = dec.CacheV.Memory; LossyUtils.HFilter8i(span2, memory.Span, offset3, cacheUvStride, limit, innerLevel, highEdgeVarianceThreshold); } if (mby > 0) { memory = dec.CacheY.Memory; LossyUtils.VFilter16(memory.Span, offset2, cacheYStride, limit + 4, innerLevel, highEdgeVarianceThreshold); memory = dec.CacheU.Memory; Span span3 = memory.Span; memory = dec.CacheV.Memory; LossyUtils.VFilter8(span3, memory.Span, offset3, cacheUvStride, limit + 4, innerLevel, highEdgeVarianceThreshold); } if (vp8FilterInfo.UseInnerFiltering) { memory = dec.CacheY.Memory; LossyUtils.VFilter16i(memory.Span, offset2, cacheYStride, limit, innerLevel, highEdgeVarianceThreshold); memory = dec.CacheU.Memory; Span span4 = memory.Span; memory = dec.CacheV.Memory; LossyUtils.VFilter8i(span4, memory.Span, offset3, cacheUvStride, limit, innerLevel, highEdgeVarianceThreshold); } } } private void FinishRow(Vp8Decoder dec, Vp8Io io) { int num = WebpConstants.FilterExtraRows[(int)dec.Filter]; int length = num * dec.CacheYStride; int length2 = num / 2 * dec.CacheUvStride; Memory memory = dec.CacheY.Memory; Span span = memory.Span; memory = dec.CacheU.Memory; Span span2 = memory.Span; memory = dec.CacheV.Memory; Span span3 = memory.Span; int mbY = dec.MbY; bool flag = mbY == 0; bool flag2 = mbY >= dec.BottomRightMbY - 1; if (dec.Filter != LoopFilter.None && dec.MbY >= dec.TopLeftMbY && dec.MbY <= dec.BottomRightMbY) { FilterRow(dec); } int num2 = mbY * 16; int num3 = (mbY + 1) * 16; if (!flag) { num2 -= num; io.Y = span; io.U = span2; io.V = span3; } else { memory = dec.CacheY.Memory; io.Y = memory.Span.Slice(dec.CacheYOffset); memory = dec.CacheU.Memory; io.U = memory.Span.Slice(dec.CacheUvOffset); memory = dec.CacheV.Memory; io.V = memory.Span.Slice(dec.CacheUvOffset); } if (!flag2) { num3 -= num; } if (num3 > io.Height) { num3 = io.Height; } if (num2 < num3) { io.MbY = num2; io.MbW = io.Width; io.MbH = num3 - num2; EmitRgb(dec, io); } if (!flag2) { Span span4 = span.Slice(16 * dec.CacheYStride, length); memory = dec.CacheY.Memory; span4.CopyTo(memory.Span); span4 = span2.Slice(8 * dec.CacheUvStride, length2); memory = dec.CacheU.Memory; span4.CopyTo(memory.Span); span4 = span3.Slice(8 * dec.CacheUvStride, length2); memory = dec.CacheV.Memory; span4.CopyTo(memory.Span); } } private int EmitRgb(Vp8Decoder dec, Vp8Io io) { Memory memory = dec.Pixels.Memory; Span span = memory.Span; int num = io.MbH; Span span2 = io.Y; Span span3 = io.U; Span span4 = io.V; memory = dec.TmpYBuffer.Memory; Span span5 = memory.Span; memory = dec.TmpUBuffer.Memory; Span span6 = memory.Span; memory = dec.TmpVBuffer.Memory; Span span7 = memory.Span; Span topU = span6; Span topV = span7; int num2 = 3 * io.Width; int num3 = io.MbY * num2; Span span8 = span.Slice(num3); int num4 = io.MbY + io.MbH; int mbW = io.MbW; int length = (mbW + 1) / 2; int i = io.MbY; byte[] uvBuffer = new byte[463]; if (i == 0) { YuvConversion.UpSample(span2, default(Span), span3, span4, span3, span4, span8, default(Span), mbW, uvBuffer); } else { YuvConversion.UpSample(span5, span2, topU, topV, span3, span4, span.Slice(num3 - num2), span8, mbW, uvBuffer); num++; } int start = 2 * num2; int start2 = 2 * io.YStride; for (; i + 2 < num4; i += 2) { topU = span3; topV = span4; span3 = span3.Slice(io.UvStride); span4 = span4.Slice(io.UvStride); YuvConversion.UpSample(span2.Slice(io.YStride), span2.Slice(start2), topU, topV, span3, span4, span8.Slice(num2), span8.Slice(start), mbW, uvBuffer); span2 = span2.Slice(start2); span8 = span8.Slice(start); } span2 = span2.Slice(io.YStride); if (num4 < io.Height) { span2.Slice(0, mbW).CopyTo(span5); span3.Slice(0, length).CopyTo(span6); span4.Slice(0, length).CopyTo(span7); num--; } else if ((num4 & 1) == 0) { YuvConversion.UpSample(span2, default(Span), span3, span4, span3, span4, span8.Slice(num2), default(Span), mbW, uvBuffer); } return num; } private void DoTransform(uint bits, Span src, Span dst, Span scratch) { switch (bits >> 30) { case 3u: LossyUtils.TransformOne(src, dst, scratch); break; case 2u: LossyUtils.TransformAc3(src, dst); break; case 1u: LossyUtils.TransformDc(src, dst); break; } } private void DoUVTransform(uint bits, Span src, Span dst, Span scratch) { if ((bits & 0xFF) != 0) { if ((bits & 0xAA) != 0) { LossyUtils.TransformUv(src, dst, scratch); } else { LossyUtils.TransformDcuv(src, dst); } } } private void DecodeMacroBlock(Vp8Decoder dec, Vp8BitReader bitreader) { Vp8MacroBlock leftMacroBlock = dec.LeftMacroBlock; Vp8MacroBlock currentMacroBlock = dec.CurrentMacroBlock; Vp8MacroBlockData currentBlockData = dec.CurrentBlockData; bool flag = dec.UseSkipProbability && currentBlockData.Skip; if (!flag) { flag = ParseResiduals(dec, bitreader, currentMacroBlock); } else { uint noneZeroAcDcCoeffs = (currentMacroBlock.NoneZeroAcDcCoeffs = 0u); leftMacroBlock.NoneZeroAcDcCoeffs = noneZeroAcDcCoeffs; if (!currentBlockData.IsI4x4) { noneZeroAcDcCoeffs = (currentMacroBlock.NoneZeroDcCoeffs = 0u); leftMacroBlock.NoneZeroDcCoeffs = noneZeroAcDcCoeffs; } currentBlockData.NonZeroY = 0u; currentBlockData.NonZeroUv = 0u; } if (dec.Filter != LoopFilter.None) { Vp8FilterInfo vp8FilterInfo = dec.FilterStrength[currentBlockData.Segment, currentBlockData.IsI4x4 ? 1u : 0u]; dec.FilterInfo[dec.MbX] = (Vp8FilterInfo)vp8FilterInfo.DeepClone(); dec.FilterInfo[dec.MbX].UseInnerFiltering |= !flag; } } private bool ParseResiduals(Vp8Decoder dec, Vp8BitReader br, Vp8MacroBlock mb) { uint num = 0u; uint num2 = 0u; int num3 = 0; Vp8MacroBlockData currentBlockData = dec.CurrentBlockData; Vp8QuantMatrix vp8QuantMatrix = dec.DeQuantMatrices[currentBlockData.Segment]; Vp8BandProbas[][] bandsPtr = dec.Probabilities.BandsPtr; Vp8MacroBlock leftMacroBlock = dec.LeftMacroBlock; short[] coeffs = currentBlockData.Coeffs; for (int i = 0; i < coeffs.Length; i++) { coeffs[i] = 0; } int num4; Vp8BandProbas[] prob; if (currentBlockData.IsI4x4) { num4 = 0; prob = bandsPtr[3]; } else { short[] array = new short[16]; int ctx = (int)(mb.NoneZeroDcCoeffs + leftMacroBlock.NoneZeroDcCoeffs); int coeffs2 = GetCoeffs(br, bandsPtr[1], ctx, vp8QuantMatrix.Y2Mat, 0, array); uint noneZeroDcCoeffs = (leftMacroBlock.NoneZeroDcCoeffs = ((coeffs2 > 0) ? 1u : 0u)); mb.NoneZeroDcCoeffs = noneZeroDcCoeffs; if (coeffs2 > 1) { LossyUtils.TransformWht(array, coeffs, scratch); } else { int num6 = array[0] + 3 >> 3; for (int j = 0; j < 256; j += 16) { coeffs[j] = (short)num6; } } num4 = 1; prob = bandsPtr[0]; } byte b = (byte)(mb.NoneZeroAcDcCoeffs & 0xF); byte b2 = (byte)(leftMacroBlock.NoneZeroAcDcCoeffs & 0xF); for (int k = 0; k < 4; k++) { int num7 = b2 & 1; uint num8 = 0u; for (int l = 0; l < 4; l++) { int ctx2 = num7 + (b & 1); int coeffs3 = GetCoeffs(br, prob, ctx2, vp8QuantMatrix.Y1Mat, num4, coeffs.AsSpan(num3)); num7 = ((coeffs3 > num4) ? 1 : 0); b = (byte)((b >> 1) | (num7 << 7)); num8 = NzCodeBits(num8, coeffs3, (coeffs[num3] != 0) ? 1 : 0); num3 += 16; } b >>= 4; b2 = (byte)((b2 >> 1) | (num7 << 7)); num = (num << 8) | num8; } uint num9 = b; uint num10 = (uint)(b2 >> 4); for (int m = 0; m < 4; m += 2) { uint num11 = 0u; int num12 = 4 + m; b = (byte)(mb.NoneZeroAcDcCoeffs >> num12); b2 = (byte)(leftMacroBlock.NoneZeroAcDcCoeffs >> num12); for (int n = 0; n < 2; n++) { int num13 = b2 & 1; for (int num14 = 0; num14 < 2; num14++) { int ctx3 = num13 + (b & 1); int coeffs4 = GetCoeffs(br, bandsPtr[2], ctx3, vp8QuantMatrix.UvMat, 0, coeffs.AsSpan(num3)); num13 = ((coeffs4 > 0) ? 1 : 0); b = (byte)((b >> 1) | (num13 << 3)); num11 = NzCodeBits(num11, coeffs4, (coeffs[num3] != 0) ? 1 : 0); num3 += 16; } b >>= 2; b2 = (byte)((b2 >> 1) | (num13 << 5)); } num2 |= num11 << 4 * m; num9 |= (uint)(b << 4 << m); num10 |= (uint)((b2 & 0xF0) << m); } mb.NoneZeroAcDcCoeffs = num9; leftMacroBlock.NoneZeroAcDcCoeffs = num10; currentBlockData.NonZeroY = num; currentBlockData.NonZeroUv = num2; return (num | num2) == 0; } private int GetCoeffs(Vp8BitReader br, Vp8BandProbas[] prob, int ctx, int[] dq, int n, Span coeffs) { Vp8ProbaArray vp8ProbaArray = prob[n].Probabilities[ctx]; while (n < 16) { if (br.GetBit(vp8ProbaArray.Probabilities[0]) == 0) { return n; } while (br.GetBit(vp8ProbaArray.Probabilities[1]) == 0) { vp8ProbaArray = prob[++n].Probabilities[0]; if (n == 16) { return 16; } } int v; if (br.GetBit(vp8ProbaArray.Probabilities[2]) == 0) { v = 1; vp8ProbaArray = prob[n + 1].Probabilities[1]; } else { v = GetLargeValue(br, vp8ProbaArray.Probabilities); vp8ProbaArray = prob[n + 1].Probabilities[2]; } int num = ((n > 0) ? 1 : 0); coeffs[WebpConstants.Zigzag[n]] = (short)(br.GetSigned(v) * dq[num]); n++; } return 16; } private int GetLargeValue(Vp8BitReader br, byte[] p) { if (br.GetBit(p[3]) == 0) { if (br.GetBit(p[4]) == 0) { return 2; } return 3 + br.GetBit(p[5]); } int num; if (br.GetBit(p[6]) == 0) { if (br.GetBit(p[7]) == 0) { return 5 + br.GetBit(159); } num = 7 + 2 * br.GetBit(165); return num + br.GetBit(145); } int bit = br.GetBit(p[8]); int bit2 = br.GetBit(p[9 + bit]); int num2 = 2 * bit + bit2; num = 0; byte[] array = null; switch (num2) { case 0: array = WebpConstants.Cat3; break; case 1: array = WebpConstants.Cat4; break; case 2: array = WebpConstants.Cat5; break; case 3: array = WebpConstants.Cat6; break; default: WebpThrowHelper.ThrowImageFormatException("VP8 parsing error"); break; } for (int i = 0; i < array.Length; i++) { num += num + br.GetBit(array[i]); } return num + (3 + (8 << num2)); } private Vp8SegmentHeader ParseSegmentHeader(Vp8Proba proba) { Vp8SegmentHeader vp8SegmentHeader = new Vp8SegmentHeader { UseSegment = bitReader.ReadBool() }; if (vp8SegmentHeader.UseSegment) { vp8SegmentHeader.UpdateMap = bitReader.ReadBool(); if (bitReader.ReadBool()) { vp8SegmentHeader.Delta = bitReader.ReadBool(); for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++) { byte b = (byte)(bitReader.ReadBool() ? ((uint)bitReader.ReadSignedValue(7)) : 0u); vp8SegmentHeader.Quantizer[i] = b; } for (int j = 0; j < vp8SegmentHeader.FilterStrength.Length; j++) { byte b2 = (byte)(bitReader.ReadBool() ? ((uint)bitReader.ReadSignedValue(6)) : 0u); vp8SegmentHeader.FilterStrength[j] = b2; } if (vp8SegmentHeader.UpdateMap) { for (int k = 0; k < proba.Segments.Length; k++) { bool flag = bitReader.ReadBool(); proba.Segments[k] = (flag ? bitReader.ReadValue(8) : 255u); } } } } else { vp8SegmentHeader.UpdateMap = false; } return vp8SegmentHeader; } private void ParseFilterHeader(Vp8Decoder dec) { Vp8FilterHeader filterHeader = dec.FilterHeader; filterHeader.LoopFilter = (bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Complex); filterHeader.FilterLevel = (int)bitReader.ReadValue(6); filterHeader.Sharpness = (int)bitReader.ReadValue(3); filterHeader.UseLfDelta = bitReader.ReadBool(); dec.Filter = ((filterHeader.FilterLevel != 0) ? filterHeader.LoopFilter : LoopFilter.None); if (filterHeader.UseLfDelta && bitReader.ReadBool()) { for (int i = 0; i < filterHeader.RefLfDelta.Length; i++) { if (bitReader.ReadBool()) { filterHeader.RefLfDelta[i] = bitReader.ReadSignedValue(6); } } for (int j = 0; j < filterHeader.ModeLfDelta.Length; j++) { if (bitReader.ReadBool()) { filterHeader.ModeLfDelta[j] = bitReader.ReadSignedValue(6); } } } byte num = WebpConstants.FilterExtraRows[(int)dec.Filter]; int cacheYOffset = num * dec.CacheYStride; int cacheUvOffset = num / 2 * dec.CacheUvStride; dec.CacheYOffset = cacheYOffset; dec.CacheUvOffset = cacheUvOffset; } private void ParsePartitions(Vp8Decoder dec) { uint num = bitReader.Remaining - bitReader.PartitionLength; int partitionLength = (int)bitReader.PartitionLength; Span span = bitReader.Data.Slice(partitionLength); int num2 = (int)num; dec.NumPartsMinusOne = (1 << (int)bitReader.ReadValue(2)) - 1; int numPartsMinusOne = dec.NumPartsMinusOne; int num3 = numPartsMinusOne * 3; int num4 = partitionLength + num3; num2 -= num3; for (int i = 0; i < numPartsMinusOne; i++) { int num5 = span[0] | (span[1] << 8) | (span[2] << 16); if (num5 > num2) { num5 = num2; } dec.Vp8BitReaders[i] = new Vp8BitReader(bitReader.Data, (uint)num5, num4); num4 += num5; num2 -= num5; span = span.Slice(3); } dec.Vp8BitReaders[numPartsMinusOne] = new Vp8BitReader(bitReader.Data, (uint)num2, num4); } private void ParseDequantizationIndices(Vp8Decoder decoder) { Vp8SegmentHeader segmentHeader = decoder.SegmentHeader; int num = (int)bitReader.ReadValue(7); int num2 = (bitReader.ReadBool() ? bitReader.ReadSignedValue(4) : 0); int num3 = (bitReader.ReadBool() ? bitReader.ReadSignedValue(4) : 0); int num4 = (bitReader.ReadBool() ? bitReader.ReadSignedValue(4) : 0); int num5 = (bitReader.ReadBool() ? bitReader.ReadSignedValue(4) : 0); int num6 = (bitReader.ReadBool() ? bitReader.ReadSignedValue(4) : 0); for (int i = 0; i < 4; i++) { int num7; if (segmentHeader.UseSegment) { num7 = segmentHeader.Quantizer[i]; if (!segmentHeader.Delta) { num7 += num; } } else { if (i > 0) { decoder.DeQuantMatrices[i] = decoder.DeQuantMatrices[0]; continue; } num7 = num; } Vp8QuantMatrix vp8QuantMatrix = decoder.DeQuantMatrices[i]; vp8QuantMatrix.Y1Mat[0] = WebpLookupTables.DcTable[Clip(num7 + num2, 127)]; vp8QuantMatrix.Y1Mat[1] = WebpLookupTables.AcTable[Clip(num7, 127)]; vp8QuantMatrix.Y2Mat[0] = WebpLookupTables.DcTable[Clip(num7 + num3, 127)] * 2; vp8QuantMatrix.Y2Mat[1] = WebpLookupTables.AcTable[Clip(num7 + num4, 127)] * 101581 >> 16; if (vp8QuantMatrix.Y2Mat[1] < 8) { vp8QuantMatrix.Y2Mat[1] = 8; } vp8QuantMatrix.UvMat[0] = WebpLookupTables.DcTable[Clip(num7 + num5, 117)]; vp8QuantMatrix.UvMat[1] = WebpLookupTables.AcTable[Clip(num7 + num6, 127)]; vp8QuantMatrix.UvQuant = num7 + num6; } } private void ParseProbabilities(Vp8Decoder dec) { Vp8Proba probabilities = dec.Probabilities; for (int i = 0; i < 4; i++) { for (int j = 0; j < 8; j++) { for (int k = 0; k < 3; k++) { for (int l = 0; l < 11; l++) { byte prob = WebpLookupTables.CoeffsUpdateProba[i, j, k, l]; byte b = (byte)((bitReader.GetBit(prob) != 0) ? bitReader.ReadValue(8) : WebpLookupTables.DefaultCoeffsProba[i, j, k, l]); probabilities.Bands[i, j].Probabilities[k].Probabilities[l] = b; } } } for (int m = 0; m < 17; m++) { probabilities.BandsPtr[i][m] = probabilities.Bands[i, WebpConstants.Vp8EncBands[m]]; } } dec.UseSkipProbability = bitReader.ReadBool(); if (dec.UseSkipProbability) { dec.SkipProbability = (byte)bitReader.ReadValue(8); } } private static Vp8Io InitializeVp8Io(Vp8Decoder dec, Vp8PictureHeader pictureHeader) { Vp8Io result = default(Vp8Io); result.Width = (int)pictureHeader.Width; result.Height = (int)pictureHeader.Height; result.UseScaling = false; result.ScaledWidth = result.Width; result.ScaledHeight = result.ScaledHeight; result.MbW = result.Width; result.MbH = result.Height; uint num = pictureHeader.Width + 15 >> 4; result.YStride = (int)(16 * num); result.UvStride = (int)(8 * num); int num2 = 4 * dec.MbWidth; dec.IntraT = new byte[num2]; int num3 = WebpConstants.FilterExtraRows[(int)dec.Filter]; if (dec.Filter == LoopFilter.Complex) { dec.TopLeftMbX = 0; dec.TopLeftMbY = 0; } else { int topLeftMbY = (dec.TopLeftMbX = -num3 >> 4); dec.TopLeftMbY = topLeftMbY; if (dec.TopLeftMbX < 0) { dec.TopLeftMbX = 0; } if (dec.TopLeftMbY < 0) { dec.TopLeftMbY = 0; } } dec.BottomRightMbY = result.Height + 15 + num3 >> 4; dec.BottomRightMbX = result.Width + 15 + num3 >> 4; if (dec.BottomRightMbX > dec.MbWidth) { dec.BottomRightMbX = dec.MbWidth; } if (dec.BottomRightMbY > dec.MbHeight) { dec.BottomRightMbY = dec.MbHeight; } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint NzCodeBits(uint nzCoeffs, int nz, int dcNz) { nzCoeffs <<= 2; nzCoeffs |= (uint)((nz > 3) ? 3 : ((nz > 1) ? 2 : dcNz)); return nzCoeffs; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int CheckMode(int mbx, int mby, int mode) { if (mode == 0) { if (mbx == 0) { if (mby != 0) { return 5; } return 6; } if (mby != 0) { return 0; } return 4; } return mode; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Clip(int value, int max) { if (value >= 0) { if (value <= max) { return value; } return max; } return 0; } } internal static class YuvConversion { private const int YuvFix = 16; private const int YuvHalf = 32768; private static readonly Vector128 One = Vector128.Create((byte)1); private static readonly Vector128 K19077 = Vector128.Create((short)19077).AsByte(); private static readonly Vector128 K26149 = Vector128.Create((short)26149).AsByte(); private static readonly Vector128 K14234 = Vector128.Create((short)14234).AsByte(); private static readonly Vector128 K33050 = Vector128.Create(26, 129, 26, 129, 26, 129, 26, 129, 26, 129, 26, 129, 26, 129, 26, 129); private static readonly Vector128 K17685 = Vector128.Create((short)17685).AsByte(); private static readonly Vector128 K6419 = Vector128.Create((short)6419).AsByte(); private static readonly Vector128 K13320 = Vector128.Create((short)13320).AsByte(); private static readonly Vector128 K8708 = Vector128.Create((short)8708).AsByte(); private static readonly Vector128 PlanarTo24Shuffle0 = Vector128.Create(0, byte.MaxValue, byte.MaxValue, 1, byte.MaxValue, byte.MaxValue, 2, byte.MaxValue, byte.MaxValue, 3, byte.MaxValue, byte.MaxValue, 4, byte.MaxValue, byte.MaxValue, 5); private static readonly Vector128 PlanarTo24Shuffle1 = Vector128.Create(byte.MaxValue, byte.MaxValue, 6, byte.MaxValue, byte.MaxValue, 7, byte.MaxValue, byte.MaxValue, 8, byte.MaxValue, byte.MaxValue, 9, byte.MaxValue, byte.MaxValue, 10, byte.MaxValue); private static readonly Vector128 PlanarTo24Shuffle2 = Vector128.Create(byte.MaxValue, 11, byte.MaxValue, byte.MaxValue, 12, byte.MaxValue, byte.MaxValue, 13, byte.MaxValue, byte.MaxValue, 14, byte.MaxValue, byte.MaxValue, 15, byte.MaxValue, byte.MaxValue); private static readonly Vector128 PlanarTo24Shuffle3 = Vector128.Create(byte.MaxValue, 0, byte.MaxValue, byte.MaxValue, 1, byte.MaxValue, byte.MaxValue, 2, byte.MaxValue, byte.MaxValue, 3, byte.MaxValue, byte.MaxValue, 4, byte.MaxValue, byte.MaxValue); private static readonly Vector128 PlanarTo24Shuffle4 = Vector128.Create(5, byte.MaxValue, byte.MaxValue, 6, byte.MaxValue, byte.MaxValue, 7, byte.MaxValue, byte.MaxValue, 8, byte.MaxValue, byte.MaxValue, 9, byte.MaxValue, byte.MaxValue, 10); private static readonly Vector128 PlanarTo24Shuffle5 = Vector128.Create(byte.MaxValue, byte.MaxValue, 11, byte.MaxValue, byte.MaxValue, 12, byte.MaxValue, byte.MaxValue, 13, byte.MaxValue, byte.MaxValue, 14, byte.MaxValue, byte.MaxValue, 15, byte.MaxValue); private static readonly Vector128 PlanarTo24Shuffle6 = Vector128.Create(byte.MaxValue, byte.MaxValue, 0, byte.MaxValue, byte.MaxValue, 1, byte.MaxValue, byte.MaxValue, 2, byte.MaxValue, byte.MaxValue, 3, byte.MaxValue, byte.MaxValue, 4, byte.MaxValue); private static readonly Vector128 PlanarTo24Shuffle7 = Vector128.Create(byte.MaxValue, 5, byte.MaxValue, byte.MaxValue, 6, byte.MaxValue, byte.MaxValue, 7, byte.MaxValue, byte.MaxValue, 8, byte.MaxValue, byte.MaxValue, 9, byte.MaxValue, byte.MaxValue); private static readonly Vector128 PlanarTo24Shuffle8 = Vector128.Create(10, byte.MaxValue, byte.MaxValue, 11, byte.MaxValue, byte.MaxValue, 12, byte.MaxValue, byte.MaxValue, 13, byte.MaxValue, byte.MaxValue, 14, byte.MaxValue, byte.MaxValue, 15); public static void UpSample(Span topY, Span bottomY, Span topU, Span topV, Span curU, Span curV, Span topDst, Span bottomDst, int len, byte[] uvBuffer) { if (Sse41.IsSupported) { UpSampleSse41(topY, bottomY, topU, topV, curU, curV, topDst, bottomDst, len, uvBuffer); } else { UpSampleScalar(topY, bottomY, topU, topV, curU, curV, topDst, bottomDst, len); } } private static void UpSampleScalar(Span topY, Span bottomY, Span topU, Span topV, Span curU, Span curV, Span topDst, Span bottomDst, int len) { int num = 3; int num2 = len - 1 >> 1; uint num3 = LoadUv(topU[0], topV[0]); uint num4 = LoadUv(curU[0], curV[0]); uint num5 = 3 * num3 + num4 + 131074 >> 2; YuvToBgr(topY[0], (int)(num5 & 0xFF), (int)(num5 >> 16), topDst); if (bottomY != default(Span)) { num5 = 3 * num4 + num3 + 131074 >> 2; YuvToBgr(bottomY[0], (int)(num5 & 0xFF), (int)(num5 >> 16), bottomDst); } for (int i = 1; i <= num2; i++) { uint num6 = LoadUv(topU[i], topV[i]); uint num7 = LoadUv(curU[i], curV[i]); uint num8 = num3 + num6 + num4 + num7 + 524296; uint num9 = num8 + 2 * (num6 + num4) >> 3; uint num10 = num8 + 2 * (num3 + num7) >> 3; num5 = num9 + num3 >> 1; uint num11 = num10 + num6 >> 1; int num12 = i * 2; YuvToBgr(topY[num12 - 1], (int)(num5 & 0xFF), (int)(num5 >> 16), topDst.Slice((num12 - 1) * num)); YuvToBgr(topY[num12], (int)(num11 & 0xFF), (int)(num11 >> 16), topDst.Slice(num12 * num)); if (bottomY != default(Span)) { num5 = num10 + num4 >> 1; num11 = num9 + num7 >> 1; YuvToBgr(bottomY[num12 - 1], (int)(num5 & 0xFF), (int)(num5 >> 16), bottomDst.Slice((num12 - 1) * num)); YuvToBgr(bottomY[num12], (int)(num11 & 0xFF), (int)(num11 >> 16), bottomDst.Slice(num12 * num)); } num3 = num6; num4 = num7; } if ((len & 1) == 0) { num5 = 3 * num3 + num4 + 131074 >> 2; YuvToBgr(topY[len - 1], (int)(num5 & 0xFF), (int)(num5 >> 16), topDst.Slice((len - 1) * num)); if (bottomY != default(Span)) { num5 = 3 * num4 + num3 + 131074 >> 2; YuvToBgr(bottomY[len - 1], (int)(num5 & 0xFF), (int)(num5 >> 16), bottomDst.Slice((len - 1) * num)); } } } private static void UpSampleSse41(Span topY, Span bottomY, Span topU, Span topV, Span curU, Span curV, Span topDst, Span bottomDst, int len, byte[] uvBuffer) { Array.Clear(uvBuffer, 0, uvBuffer.Length); Span span = uvBuffer.AsSpan(15); Span span2 = span.Slice(32); int num = (topU[0] + curU[0] >> 1) + 1; int num2 = (topV[0] + curV[0] >> 1) + 1; int u = topU[0] + num >> 1; int v = topV[0] + num2 >> 1; YuvToBgr(topY[0], u, v, topDst); if (bottomY != default(Span)) { int u2 = curU[0] + num >> 1; int v2 = curV[0] + num2 >> 1; YuvToBgr(bottomY[0], u2, v2, bottomDst); } ref byte reference = ref MemoryMarshal.GetReference(topU); ref byte reference2 = ref MemoryMarshal.GetReference(topV); ref byte reference3 = ref MemoryMarshal.GetReference(curU); ref byte reference4 = ref MemoryMarshal.GetReference(curV); int num3; int num4; if (bottomY != null) { num3 = 1; num4 = 0; while (num3 + 32 + 1 <= len) { UpSample32Pixels(ref Unsafe.Add(ref reference, num4), ref Unsafe.Add(ref reference3, num4), span); UpSample32Pixels(ref Unsafe.Add(ref reference2, num4), ref Unsafe.Add(ref reference4, num4), span2); ConvertYuvToBgrWithBottomYSse41(topY, bottomY, topDst, bottomDst, span, span2, num3, 3); num3 += 32; num4 += 16; } } else { num3 = 1; num4 = 0; while (num3 + 32 + 1 <= len) { UpSample32Pixels(ref Unsafe.Add(ref reference, num4), ref Unsafe.Add(ref reference3, num4), span); UpSample32Pixels(ref Unsafe.Add(ref reference2, num4), ref Unsafe.Add(ref reference4, num4), span2); ConvertYuvToBgrSse41(topY, topDst, span, span2, num3, 3); num3 += 32; num4 += 16; } } if (len > 1) { int numPixels = (len + 1 >> 1) - (num3 >> 1); Span topDst2 = span.Slice(128); Span bottomDst2 = topDst2.Slice(128); Span span3 = bottomDst2.Slice(128); Span span4 = ((bottomY == null) ? ((Span)null) : span3.Slice(32)); UpSampleLastBlock(topU.Slice(num4), curU.Slice(num4), numPixels, span); UpSampleLastBlock(topV.Slice(num4), curV.Slice(num4), numPixels, span2); topY.Slice(num3, len - num3).CopyTo(span3); if (bottomY != default(Span)) { bottomY.Slice(num3, len - num3).CopyTo(span4); ConvertYuvToBgrWithBottomYSse41(span3, span4, topDst2, bottomDst2, span, span2, 0, 3); } else { ConvertYuvToBgrSse41(span3, topDst2, span, span2, 0, 3); } topDst2.Slice(0, (len - num3) * 3).CopyTo(topDst.Slice(num3 * 3)); if (bottomY != default(Span)) { bottomDst2.Slice(0, (len - num3) * 3).CopyTo(bottomDst.Slice(num3 * 3)); } } } private static void UpSample32Pixels(ref byte r1, ref byte r2, Span output) { Vector128 vector = Unsafe.As>(ref r1); Vector128 vector2 = Unsafe.As>(ref Unsafe.Add(ref r1, 1)); Vector128 vector3 = Unsafe.As>(ref r2); Vector128 vector4 = Unsafe.As>(ref Unsafe.Add(ref r2, 1)); Vector128 vector5 = Sse2.Average(vector, vector4); Vector128 vector6 = Sse2.Average(vector2, vector3); Vector128 vector7 = Sse2.Xor(vector5, vector6); Vector128 vector8 = Sse2.Xor(vector, vector4); Vector128 vector9 = Sse2.Xor(vector2, vector3); Vector128 k = Sse2.Subtract(right: Sse2.And(Sse2.Or(Sse2.Or(vector8, vector9), vector7), One), left: Sse2.Average(vector5, vector6)); Vector128 m = GetM(k, vector7, vector9, vector6); Vector128 m2 = GetM(k, vector7, vector8, vector5); PackAndStore(vector, vector2, m, m2, output); PackAndStore(vector3, vector4, m2, m, output.Slice(64)); } private static void UpSampleLastBlock(Span tb, Span bb, int numPixels, Span output) { Span span = stackalloc byte[17]; Span span2 = stackalloc byte[17]; tb.Slice(0, numPixels).CopyTo(span); bb.Slice(0, numPixels).CopyTo(span2); int num = 17 - numPixels; if (num > 0) { span.Slice(numPixels, num).Fill(span[numPixels - 1]); span2.Slice(numPixels, num).Fill(span2[numPixels - 1]); } UpSample32Pixels(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(span2), output); } private static Vector128 GetM(Vector128 k, Vector128 st, Vector128 ij, Vector128 input) { Vector128 left = Sse2.Average(k, input); Vector128 left2 = Sse2.And(ij, st); Vector128 right = Sse2.Xor(k, input); Vector128 right2 = Sse2.And(Sse2.Or(left2, right), One); return Sse2.Subtract(left, right2); } private static void PackAndStore(Vector128 a, Vector128 b, Vector128 da, Vector128 db, Span output) { Vector128 left = Sse2.Average(a, da); Vector128 right = Sse2.Average(b, db); Vector128 vector = Sse2.UnpackLow(left, right); Vector128 vector2 = Sse2.UnpackHigh(left, right); ref byte reference = ref MemoryMarshal.GetReference(output); ref byte source = ref Unsafe.Add(ref reference, 16); Unsafe.As>(ref reference) = vector; Unsafe.As>(ref source) = vector2; } public static bool ConvertRgbToYuv(Image image, Configuration configuration, MemoryAllocator memoryAllocator, Span y, Span u, Span v) where TPixel : unmanaged, IPixel { Buffer2D pixelBuffer = image.Frames.RootFrame.PixelBuffer; int width = pixelBuffer.Width; int height = pixelBuffer.Height; int num = width + 1 >> 1; using IMemoryOwner buffer = memoryAllocator.Allocate(4 * num); using IMemoryOwner buffer2 = memoryAllocator.Allocate(width); using IMemoryOwner buffer3 = memoryAllocator.Allocate(width); Span span = buffer.GetSpan(); Span span2 = buffer2.GetSpan(); Span span3 = buffer3.GetSpan(); int num2 = 0; bool result = false; int i; for (i = 0; i < height - 1; i += 2) { Span span4 = pixelBuffer.DangerousGetRowSpan(i); Span span5 = pixelBuffer.DangerousGetRowSpan(i + 1); PixelOperations.Instance.ToBgra32(configuration, span4, span2); PixelOperations.Instance.ToBgra32(configuration, span5, span3); int num3; if (WebpCommonUtils.CheckNonOpaque(span2)) { num3 = (WebpCommonUtils.CheckNonOpaque(span3) ? 1 : 0); if (num3 != 0) { result = true; } } else { num3 = 0; } if (num3 == 0) { AccumulateRgb(span2, span3, span, width); } else { AccumulateRgba(span2, span3, span, width); } ConvertRgbaToUv(span, u.Slice(num2 * num), v.Slice(num2 * num), num); num2++; ConvertRgbaToY(span2, y.Slice(i * width), width); ConvertRgbaToY(span3, y.Slice((i + 1) * width), width); } if ((height & 1) != 0) { Span span6 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToBgra32(configuration, span6, span2); ConvertRgbaToY(span2, y.Slice(i * width), width); if (!WebpCommonUtils.CheckNonOpaque(span2)) { AccumulateRgb(span2, span2, span, width); } else { AccumulateRgba(span2, span2, span, width); result = true; } ConvertRgbaToUv(span, u.Slice(num2 * num), v.Slice(num2 * num), num); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ConvertRgbaToY(Span rowSpan, Span y, int width) { for (int i = 0; i < width; i++) { y[i] = (byte)RgbToY(rowSpan[i].R, rowSpan[i].G, rowSpan[i].B, 32768); } } public static void ConvertRgbaToUv(Span rgb, Span u, Span v, int width) { int num = 0; int num2 = 0; while (num2 < width) { int r = rgb[num]; int g = rgb[num + 1]; int b = rgb[num + 2]; u[num2] = (byte)RgbToU(r, g, b, 131072); v[num2] = (byte)RgbToV(r, g, b, 131072); num2++; num += 4; } } public static void AccumulateRgb(Span rowSpan, Span nextRowSpan, Span dst, int width) { int num = 0; int num2 = 0; int num3 = 0; while (num2 < width >> 1) { Bgra32 bgra = rowSpan[num3]; Bgra32 bgra2 = rowSpan[num3 + 1]; Bgra32 bgra3 = nextRowSpan[num3]; Bgra32 bgra4 = nextRowSpan[num3 + 1]; dst[num] = (ushort)LinearToGamma(GammaToLinear(bgra.R) + GammaToLinear(bgra2.R) + GammaToLinear(bgra3.R) + GammaToLinear(bgra4.R), 0); dst[num + 1] = (ushort)LinearToGamma(GammaToLinear(bgra.G) + GammaToLinear(bgra2.G) + GammaToLinear(bgra3.G) + GammaToLinear(bgra4.G), 0); dst[num + 2] = (ushort)LinearToGamma(GammaToLinear(bgra.B) + GammaToLinear(bgra2.B) + GammaToLinear(bgra3.B) + GammaToLinear(bgra4.B), 0); num2++; num3 += 2; num += 4; } if ((width & 1) != 0) { Bgra32 bgra = rowSpan[num3]; Bgra32 bgra2 = nextRowSpan[num3]; dst[num] = (ushort)LinearToGamma(GammaToLinear(bgra.R) + GammaToLinear(bgra2.R), 1); dst[num + 1] = (ushort)LinearToGamma(GammaToLinear(bgra.G) + GammaToLinear(bgra2.G), 1); dst[num + 2] = (ushort)LinearToGamma(GammaToLinear(bgra.B) + GammaToLinear(bgra2.B), 1); } } public static void AccumulateRgba(Span rowSpan, Span nextRowSpan, Span dst, int width) { int num = 0; int num2 = 0; int num3 = 0; while (num2 < width >> 1) { Bgra32 bgra = rowSpan[num3]; Bgra32 bgra2 = rowSpan[num3 + 1]; Bgra32 bgra3 = nextRowSpan[num3]; Bgra32 bgra4 = nextRowSpan[num3 + 1]; uint num4 = (uint)(bgra.A + bgra2.A + bgra3.A + bgra4.A); int num5; int num6; int num7; if ((num4 == 0 || num4 == 1020) ? true : false) { num5 = (ushort)LinearToGamma(GammaToLinear(bgra.R) + GammaToLinear(bgra2.R) + GammaToLinear(bgra3.R) + GammaToLinear(bgra4.R), 0); num6 = (ushort)LinearToGamma(GammaToLinear(bgra.G) + GammaToLinear(bgra2.G) + GammaToLinear(bgra3.G) + GammaToLinear(bgra4.G), 0); num7 = (ushort)LinearToGamma(GammaToLinear(bgra.B) + GammaToLinear(bgra2.B) + GammaToLinear(bgra3.B) + GammaToLinear(bgra4.B), 0); } else { num5 = LinearToGammaWeighted(bgra.R, bgra2.R, bgra3.R, bgra4.R, bgra.A, bgra2.A, bgra3.A, bgra4.A, num4); num6 = LinearToGammaWeighted(bgra.G, bgra2.G, bgra3.G, bgra4.G, bgra.A, bgra2.A, bgra3.A, bgra4.A, num4); num7 = LinearToGammaWeighted(bgra.B, bgra2.B, bgra3.B, bgra4.B, bgra.A, bgra2.A, bgra3.A, bgra4.A, num4); } dst[num] = (ushort)num5; dst[num + 1] = (ushort)num6; dst[num + 2] = (ushort)num7; dst[num + 3] = (ushort)num4; num2++; num3 += 2; num += 4; } if ((width & 1) != 0) { Bgra32 bgra = rowSpan[num3]; Bgra32 bgra2 = nextRowSpan[num3]; uint num8 = (uint)(2uL * (ulong)(bgra.A + bgra2.A)); int num9; int num10; int num11; if ((num8 == 0 || num8 == 1020) ? true : false) { num9 = (ushort)LinearToGamma(GammaToLinear(bgra.R) + GammaToLinear(bgra2.R), 1); num10 = (ushort)LinearToGamma(GammaToLinear(bgra.G) + GammaToLinear(bgra2.G), 1); num11 = (ushort)LinearToGamma(GammaToLinear(bgra.B) + GammaToLinear(bgra2.B), 1); } else { num9 = LinearToGammaWeighted(bgra.R, bgra2.R, bgra.R, bgra2.R, bgra.A, bgra2.A, bgra.A, bgra2.A, num8); num10 = LinearToGammaWeighted(bgra.G, bgra2.G, bgra.G, bgra2.G, bgra.A, bgra2.A, bgra.A, bgra2.A, num8); num11 = LinearToGammaWeighted(bgra.B, bgra2.B, bgra.B, bgra2.B, bgra.A, bgra2.A, bgra.A, bgra2.A, num8); } dst[num] = (ushort)num9; dst[num + 1] = (ushort)num10; dst[num + 2] = (ushort)num11; dst[num + 3] = (ushort)num8; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LinearToGammaWeighted(byte rgb0, byte rgb1, byte rgb2, byte rgb3, byte a0, byte a1, byte a2, byte a3, uint totalA) { return LinearToGamma((a0 * GammaToLinear(rgb0) + a1 * GammaToLinear(rgb1) + a2 * GammaToLinear(rgb2) + a3 * GammaToLinear(rgb3)) * WebpLookupTables.InvAlpha[totalA] >> 17, 0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LinearToGamma(uint baseValue, int shift) { return Interpolate((int)(baseValue << shift)) + 64 >> 7; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint GammaToLinear(byte v) { return WebpLookupTables.GammaToLinearTab[v]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Interpolate(int v) { int num = v >> 9; int num2 = v & 0x1FF; int num3 = WebpLookupTables.LinearToGammaTab[num]; return WebpLookupTables.LinearToGammaTab[num + 1] * num2 + num3 * (512 - num2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int RgbToY(byte r, byte g, byte b, int rounding) { return 16839 * r + 33059 * g + 6420 * b + rounding + 1048576 >> 16; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int RgbToU(int r, int g, int b, int rounding) { return ClipUv(-9719 * r - 19081 * g + 28800 * b, rounding); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int RgbToV(int r, int g, int b, int rounding) { return ClipUv(28800 * r - 24116 * g - 4684 * b, rounding); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int ClipUv(int uv, int rounding) { uv = uv + rounding + 33554432 >> 18; if ((uv & -256) != 0) { if (uv >= 0) { return 255; } return 0; } return uv; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint LoadUv(byte u, byte v) { return (uint)(u | (v << 16)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void YuvToBgr(int y, int u, int v, Span bgr) { bgr[2] = (byte)YuvToR(y, v); bgr[1] = (byte)YuvToG(y, u, v); bgr[0] = (byte)YuvToB(y, u); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ConvertYuvToBgrSse41(Span topY, Span topDst, Span ru, Span rv, int curX, int step) { YuvToBgrSse41(topY.Slice(curX), ru, rv, topDst.Slice(curX * step)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ConvertYuvToBgrWithBottomYSse41(Span topY, Span bottomY, Span topDst, Span bottomDst, Span ru, Span rv, int curX, int step) { YuvToBgrSse41(topY.Slice(curX), ru, rv, topDst.Slice(curX * step)); YuvToBgrSse41(bottomY.Slice(curX), ru.Slice(64), rv.Slice(64), bottomDst.Slice(curX * step)); } private static void YuvToBgrSse41(Span y, Span u, Span v, Span dst) { ref byte reference = ref MemoryMarshal.GetReference(y); ref byte reference2 = ref MemoryMarshal.GetReference(u); ref byte reference3 = ref MemoryMarshal.GetReference(v); ConvertYuv444ToBgrSse41(ref reference, ref reference2, ref reference3, out var r, out var g, out var b); ConvertYuv444ToBgrSse41(ref Unsafe.Add(ref reference, 8), ref Unsafe.Add(ref reference2, 8), ref Unsafe.Add(ref reference3, 8), out var r2, out var g2, out var b2); ConvertYuv444ToBgrSse41(ref Unsafe.Add(ref reference, 16), ref Unsafe.Add(ref reference2, 16), ref Unsafe.Add(ref reference3, 16), out var r3, out var g3, out var b3); ConvertYuv444ToBgrSse41(ref Unsafe.Add(ref reference, 24), ref Unsafe.Add(ref reference2, 24), ref Unsafe.Add(ref reference3, 24), out var r4, out var g4, out var b4); Vector128 input = Sse2.PackUnsignedSaturate(b, b2); Vector128 input2 = Sse2.PackUnsignedSaturate(b3, b4); Vector128 input3 = Sse2.PackUnsignedSaturate(g, g2); Vector128 input4 = Sse2.PackUnsignedSaturate(g3, g4); Vector128 input5 = Sse2.PackUnsignedSaturate(r, r2); Vector128 input6 = Sse2.PackUnsignedSaturate(r3, r4); PlanarTo24bSse41(input, input2, input3, input4, input5, input6, dst); } private static void PlanarTo24bSse41(Vector128 input0, Vector128 input1, Vector128 input2, Vector128 input3, Vector128 input4, Vector128 input5, Span rgb) { ChannelMixing(input0, input1, PlanarTo24Shuffle0, PlanarTo24Shuffle1, PlanarTo24Shuffle2, out var output, out var output2, out var output3, out var output4, out var output5, out var output6); ChannelMixing(input2, input3, PlanarTo24Shuffle3, PlanarTo24Shuffle4, PlanarTo24Shuffle5, out var output7, out var output8, out var output9, out var output10, out var output11, out var output12); ChannelMixing(input4, input5, PlanarTo24Shuffle6, PlanarTo24Shuffle7, PlanarTo24Shuffle8, out var output13, out var output14, out var output15, out var output16, out var output17, out var output18); Vector128 left = Sse2.Or(output, output7); Vector128 left2 = Sse2.Or(output2, output8); Vector128 left3 = Sse2.Or(output3, output9); Vector128 left4 = Sse2.Or(output4, output10); Vector128 left5 = Sse2.Or(output5, output11); Vector128 left6 = Sse2.Or(output6, output12); ref byte reference = ref MemoryMarshal.GetReference(rgb); Unsafe.As>(ref reference) = Sse2.Or(left, output13); Unsafe.As>(ref Unsafe.Add(ref reference, 16)) = Sse2.Or(left2, output14); Unsafe.As>(ref Unsafe.Add(ref reference, 32)) = Sse2.Or(left3, output15); Unsafe.As>(ref Unsafe.Add(ref reference, 48)) = Sse2.Or(left4, output16); Unsafe.As>(ref Unsafe.Add(ref reference, 64)) = Sse2.Or(left5, output17); Unsafe.As>(ref Unsafe.Add(ref reference, 80)) = Sse2.Or(left6, output18); } private static void ChannelMixing(Vector128 input0, Vector128 input1, Vector128 shuffle0, Vector128 shuffle1, Vector128 shuffle2, out Vector128 output0, out Vector128 output1, out Vector128 output2, out Vector128 output3, out Vector128 output4, out Vector128 output5) { output0 = Ssse3.Shuffle(input0, shuffle0); output1 = Ssse3.Shuffle(input0, shuffle1); output2 = Ssse3.Shuffle(input0, shuffle2); output3 = Ssse3.Shuffle(input1, shuffle0); output4 = Ssse3.Shuffle(input1, shuffle1); output5 = Ssse3.Shuffle(input1, shuffle2); } private static void ConvertYuv444ToBgrSse41(ref byte y, ref byte u, ref byte v, out Vector128 r, out Vector128 g, out Vector128 b) { Vector128 right = Unsafe.As>(ref y); Vector128 right2 = Unsafe.As>(ref u); Vector128 right3 = Unsafe.As>(ref v); right = Sse2.UnpackLow(Vector128.Zero, right); right2 = Sse2.UnpackLow(Vector128.Zero, right2); right3 = Sse2.UnpackLow(Vector128.Zero, right3); Vector128 vector = Sse2.MultiplyHigh(right.AsUInt16(), K19077.AsUInt16()); Vector128 right4 = Sse2.MultiplyHigh(right3.AsUInt16(), K26149.AsUInt16()); Vector128 left = Sse2.MultiplyHigh(right2.AsUInt16(), K6419.AsUInt16()); Vector128 right5 = Sse2.MultiplyHigh(right3.AsUInt16(), K13320.AsUInt16()); Vector128 vector2 = Sse2.Add(Sse2.Subtract(vector.AsUInt16(), K14234.AsUInt16()), right4); Vector128 left2 = Sse2.Add(vector.AsUInt16(), K8708.AsUInt16()); Vector128 right6 = Sse2.Add(left, right5); Vector128 vector3 = Sse2.Subtract(left2, right6); Vector128 vector4 = Sse2.SubtractSaturate(Sse2.AddSaturate(Sse2.MultiplyHigh(right2.AsUInt16(), K33050.AsUInt16()), vector), K17685.AsUInt16()); r = Sse2.ShiftRightArithmetic(vector2.AsInt16(), 6); g = Sse2.ShiftRightArithmetic(vector3.AsInt16(), 6); b = Sse2.ShiftRightLogical(vector4.AsInt16(), 6); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int YuvToB(int y, int u) { return Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int YuvToG(int y, int u, int v) { return Clip8(MultHi(y, 19077) - MultHi(u, 6419) - MultHi(v, 13320) + 8708); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int YuvToR(int y, int v) { return Clip8(MultHi(y, 19077) + MultHi(v, 26149) - 14234); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int MultHi(int v, int coeff) { return v * coeff >> 8; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte Clip8(int v) { int num = 16383; return (byte)(((v & ~num) == 0) ? ((uint)(v >> 6)) : ((v >= 0) ? 255u : 0u)); } } } namespace SixLabors.ImageSharp.Formats.Webp.Lossless { internal static class BackwardReferenceEncoder { public const int MaxLengthBits = 12; private const float MaxEntropy = 1E+30f; private const int WindowOffsetsSizeMax = 32; public const int MaxLength = 4095; private const int MinLength = 4; public static Vp8LBackwardRefs GetBackwardReferences(int width, int height, ReadOnlySpan bgra, int quality, int lz77TypesToTry, ref int cacheBits, MemoryAllocator memoryAllocator, Vp8LHashChain hashChain, Vp8LBackwardRefs best, Vp8LBackwardRefs worst) { int num = 0; double num2 = -1.0; int num3 = cacheBits; Vp8LHashChain vp8LHashChain = null; Vp8LStreaks stats = new Vp8LStreaks(); Vp8LBitEntropy bitsEntropy = new Vp8LBitEntropy(); int num4 = 1; while (lz77TypesToTry > 0) { int bestCacheBits = num3; if ((lz77TypesToTry & num4) != 0) { switch ((Vp8LLz77Type)num4) { case Vp8LLz77Type.Lz77Rle: BackwardReferencesRle(width, height, bgra, 0, worst); break; case Vp8LLz77Type.Lz77Standard: BackwardReferencesLz77(width, height, bgra, 0, hashChain, worst); break; case Vp8LLz77Type.Lz77Box: vp8LHashChain = new Vp8LHashChain(memoryAllocator, width * height); BackwardReferencesLz77Box(width, height, bgra, 0, hashChain, vp8LHashChain, worst); break; } bestCacheBits = CalculateBestCacheSize(bgra, quality, worst, bestCacheBits); if (bestCacheBits > 0) { BackwardRefsWithLocalCache(bgra, bestCacheBits, worst); } double num5 = new Vp8LHistogram(worst, bestCacheBits).EstimateBits(stats, bitsEntropy); if (num == 0 || num5 < num2) { Vp8LBackwardRefs vp8LBackwardRefs = worst; worst = best; best = vp8LBackwardRefs; num2 = num5; cacheBits = bestCacheBits; num = num4; } } lz77TypesToTry &= ~num4; num4 <<= 1; } if ((num == 1 || num == 4) && quality >= 25) { Vp8LHashChain hashChain2 = ((num == 1) ? hashChain : vp8LHashChain); BackwardReferencesTraceBackwards(width, height, memoryAllocator, bgra, cacheBits, hashChain2, best, worst); if (new Vp8LHistogram(worst, cacheBits).EstimateBits(stats, bitsEntropy) < num2) { best = worst; } } BackwardReferences2DLocality(width, best); vp8LHashChain?.Dispose(); return best; } private static int CalculateBestCacheSize(ReadOnlySpan bgra, int quality, Vp8LBackwardRefs refs, int bestCacheBits) { int num = ((quality > 25) ? bestCacheBits : 0); if (num == 0) { return 0; } double num2 = 1.0000000150474662E+30; int num3 = 0; ColorCache[] array = new ColorCache[11]; Vp8LHistogram[] array2 = new Vp8LHistogram[11]; for (int i = 0; i <= 10; i++) { array2[i] = new Vp8LHistogram(i); array[i] = new ColorCache(); array[i].Init(i); } for (int j = 0; j < refs.Refs.Count; j++) { PixOrCopy pixOrCopy = refs.Refs[j]; if (pixOrCopy.IsLiteral()) { uint num4 = bgra[num3++]; uint num5 = (num4 >> 24) & 0xFF; uint num6 = (num4 >> 16) & 0xFF; uint num7 = (num4 >> 8) & 0xFF; uint num8 = num4 & 0xFF; int num9 = ColorCache.HashPix(num4, 32 - num); array2[0].Blue[num8]++; array2[0].Literal[num7]++; array2[0].Red[num6]++; array2[0].Alpha[num5]++; int num10 = num; while (num10 >= 1) { if (array[num10].Lookup(num9) == num4) { array2[num10].Literal[280 + num9]++; } else { array[num10].Set((uint)num9, num4); array2[num10].Blue[num8]++; array2[num10].Literal[num7]++; array2[num10].Red[num6]++; array2[num10].Alpha[num5]++; } num10--; num9 >>= 1; } continue; } int num11 = pixOrCopy.Len; uint num12 = bgra[num3] ^ 0xFFFFFFFFu; int extraBits = 0; int extraBitsValue = 0; int num13 = LosslessUtils.PrefixEncode(num11, ref extraBits, ref extraBitsValue); for (int k = 0; k <= num; k++) { array2[k].Literal[256 + num13]++; } do { if (bgra[num3] != num12) { int num14 = ColorCache.HashPix(bgra[num3], 32 - num); int num15 = num; while (num15 >= 1) { array[num15].Colors[num14] = bgra[num3]; num15--; num14 >>= 1; } num12 = bgra[num3]; } num3++; } while (--num11 != 0); } Vp8LStreaks stats = new Vp8LStreaks(); Vp8LBitEntropy bitsEntropy = new Vp8LBitEntropy(); for (int l = 0; l <= num; l++) { double num16 = array2[l].EstimateBits(stats, bitsEntropy); if (l == 0 || num16 < num2) { num2 = num16; bestCacheBits = l; } } return bestCacheBits; } private static void BackwardReferencesTraceBackwards(int xSize, int ySize, MemoryAllocator memoryAllocator, ReadOnlySpan bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refsSrc, Vp8LBackwardRefs refsDst) { int num = xSize * ySize; using IMemoryOwner memoryOwner = memoryAllocator.Allocate(num); Span span = memoryOwner.GetSpan(); BackwardReferencesHashChainDistanceOnly(xSize, ySize, memoryAllocator, bgra, cacheBits, hashChain, refsSrc, memoryOwner); int num2 = TraceBackwards(span, num); Span chosenPath = span.Slice(num - num2); BackwardReferencesHashChainFollowChosenPath(bgra, cacheBits, chosenPath, num2, hashChain, refsDst); } private static void BackwardReferencesHashChainDistanceOnly(int xSize, int ySize, MemoryAllocator memoryAllocator, ReadOnlySpan bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refs, IMemoryOwner distArrayBuffer) { int num = xSize * ySize; bool flag = cacheBits > 0; CostModel costModel = new CostModel(280 + ((cacheBits > 0) ? (1 << cacheBits) : 0)); int num2 = -1; int num3 = -1; double num4 = -1.0; int num5 = -1; int num6 = 0; ColorCache colorCache = new ColorCache(); if (flag) { colorCache.Init(cacheBits); } costModel.Build(xSize, cacheBits, refs); using CostManager costManager = new CostManager(memoryAllocator, distArrayBuffer, num, costModel); Span span = costManager.Costs.GetSpan(); Span span2 = distArrayBuffer.GetSpan(); span2[0] = 0; AddSingleLiteralWithCostModel(bgra, colorCache, costModel, 0, flag, 0f, span, span2); for (int i = 1; i < num; i++) { float num7 = span[i - 1]; int num8 = hashChain.FindOffset(i); int num9 = hashChain.FindLength(i); AddSingleLiteralWithCostModel(bgra, colorCache, costModel, i, flag, num7, span, span2); if (num9 >= 2) { if (num8 != num2) { int distance = DistanceToPlaneCode(xSize, num8); num4 = costModel.GetDistanceCost(distance); num5 = 1; costManager.PushInterval((double)num7 + num4, i, num9); } else { if (num5 != 0) { num6 = i - 1 + num3 - 1; num5 = 0; } if (i + num9 - 1 > num6) { int num10 = 0; int j; for (j = i; j <= num6; j++) { int num11 = hashChain.FindOffset(j + 1); num10 = hashChain.FindLength(j + 1); if (num11 != num8) { num10 = hashChain.FindLength(j); break; } } costManager.UpdateCostAtIndex(j - 1, doCleanIntervals: false); costManager.UpdateCostAtIndex(j, doCleanIntervals: false); costManager.PushInterval((double)span[j - 1] + num4, j, num10); num6 = j + num10 - 1; } } } costManager.UpdateCostAtIndex(i, doCleanIntervals: true); num2 = num8; num3 = num9; } } private static int TraceBackwards(Span distArray, int distArraySize) { int num = 0; int num2 = distArraySize; int num3 = distArraySize - 1; while (num3 >= 0) { ushort num4 = distArray[num3]; num2--; num++; distArray[num2] = num4; num3 -= num4; } return num; } private static void BackwardReferencesHashChainFollowChosenPath(ReadOnlySpan bgra, int cacheBits, Span chosenPath, int chosenPathSize, Vp8LHashChain hashChain, Vp8LBackwardRefs backwardRefs) { bool flag = cacheBits > 0; ColorCache colorCache = new ColorCache(); int num = 0; if (flag) { colorCache.Init(cacheBits); } backwardRefs.Refs.Clear(); for (int i = 0; i < chosenPathSize; i++) { int num2 = chosenPath[i]; if (num2 != 1) { int distance = hashChain.FindOffset(num); backwardRefs.Add(PixOrCopy.CreateCopy((uint)distance, (ushort)num2)); if (flag) { for (int j = 0; j < num2; j++) { colorCache.Insert(bgra[num + j]); } } num += num2; continue; } int num3 = (flag ? colorCache.Contains(bgra[num]) : (-1)); PixOrCopy pixOrCopy; if (num3 >= 0) { pixOrCopy = PixOrCopy.CreateCacheIdx(num3); } else { if (flag) { colorCache.Insert(bgra[num]); } pixOrCopy = PixOrCopy.CreateLiteral(bgra[num]); } backwardRefs.Add(pixOrCopy); num++; } } private static void AddSingleLiteralWithCostModel(ReadOnlySpan bgra, ColorCache colorCache, CostModel costModel, int idx, bool useColorCache, float prevCost, Span cost, Span distArray) { double num = prevCost; uint num2 = bgra[idx]; int num3 = (useColorCache ? colorCache.Contains(num2) : (-1)); if (num3 >= 0) { double num4 = 0.68; num += costModel.GetCacheCost((uint)num3) * num4; } else { double num5 = 0.82; if (useColorCache) { colorCache.Insert(num2); } num += costModel.GetLiteralCost(num2) * num5; } if ((double)cost[idx] > num) { cost[idx] = (float)num; distArray[idx] = 1; } } private static void BackwardReferencesLz77(int xSize, int ySize, ReadOnlySpan bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refs) { int num = -1; bool flag = cacheBits > 0; int num2 = xSize * ySize; ColorCache colorCache = new ColorCache(); if (flag) { colorCache.Init(cacheBits); } refs.Refs.Clear(); int num3; for (int i = 0; i < num2; i += num3) { int distance = hashChain.FindOffset(i); num3 = hashChain.FindLength(i); if (num3 >= 4) { int num4 = num3; int num5 = 0; int num6 = ((i + num4 >= num2) ? (num2 - 1) : (i + num4)); num = ((i > num) ? i : num); for (int j = num + 1; j <= num6; j++) { int num7 = hashChain.FindLength(j); int num8 = j + ((num7 < 4) ? 1 : num7); if (num8 > num5) { num3 = j - i; num5 = num8; if (num5 >= num2) { break; } } } } else { num3 = 1; } if (num3 == 1) { AddSingleLiteral(bgra[i], flag, colorCache, refs); continue; } refs.Add(PixOrCopy.CreateCopy((uint)distance, (ushort)num3)); if (flag) { for (int j = i; j < i + num3; j++) { colorCache.Insert(bgra[j]); } } } } private static void BackwardReferencesLz77Box(int xSize, int ySize, ReadOnlySpan bgra, int cacheBits, Vp8LHashChain hashChainBest, Vp8LHashChain hashChain, Vp8LBackwardRefs refs) { int num = xSize * ySize; int[] array = new int[32]; int[] array2 = new int[32]; int num2 = 0; int num3 = 0; short[] array3 = new short[xSize * ySize]; int num4 = -1; int num5 = -1; int num6 = num - 2; int num7 = num6; array3[num7 + 1] = 1; while (num6 >= 0) { if (bgra[num6] == bgra[num6 + 1]) { array3[num7] = array3[num7 + 1]; if (array3[num7 + 1] != 4095) { array3[num7]++; } } else { array3[num7] = 1; } num6--; num7--; } for (int i = 0; i <= 6; i++) { for (int j = -6; j <= 6; j++) { int num8 = i * xSize + j; if (num8 > 0) { int num9 = DistanceToPlaneCode(xSize, num8) - 1; if (num9 < 32) { array[num9] = num8; } } } } for (num6 = 0; num6 < 32; num6++) { if (array[num6] != 0) { array[num2++] = array[num6]; } } for (num6 = 0; num6 < num2; num6++) { bool flag = false; for (int k = 0; k < num2; k++) { if (flag) { break; } flag |= array[num6] == array[k] + 1; } if (!flag) { array2[num3] = array[num6]; num3++; } } Span span = hashChain.OffsetLength.GetSpan(); span[0] = 0u; for (num6 = 1; num6 < num; num6++) { int num10 = hashChainBest.FindLength(num6); int num11 = 0; bool flag2 = true; if (num10 >= 4095) { num11 = hashChainBest.FindOffset(num6); for (int l = 0; l < num2; l++) { if (num11 == array[l]) { flag2 = false; break; } } } if (flag2) { bool flag3 = num5 > 1 && num5 < 4095; int num12 = (flag3 ? num3 : num2); num10 = (flag3 ? (num5 - 1) : 0); num11 = (flag3 ? num4 : 0); for (int l = 0; l < num12; l++) { int num13 = 0; int num14 = num6; int num15 = (flag3 ? (num6 - array2[l]) : (num6 - array[l])); if (num15 < 0 || bgra[num15] != bgra[num6]) { continue; } do { int num16 = array3[num15]; int num17 = array3[num14]; if (num16 != num17) { num13 += ((num16 < num17) ? num16 : num17); break; } num13 += num16; num15 += num16; num14 += num16; } while (num13 <= 4095 && num14 < num && bgra[num15] == bgra[num14]); if (num10 < num13) { num11 = (flag3 ? array2[l] : array[l]); if (num13 >= 4095) { num10 = 4095; break; } num10 = num13; } } } if (num10 <= 4) { span[num6] = 0u; num4 = 0; num5 = 0; } else { span[num6] = (uint)((num11 << 12) | num10); num4 = num11; num5 = num10; } } span[0] = 0u; BackwardReferencesLz77(xSize, ySize, bgra, cacheBits, hashChain, refs); } private static void BackwardReferencesRle(int xSize, int ySize, ReadOnlySpan bgra, int cacheBits, Vp8LBackwardRefs refs) { int num = xSize * ySize; bool flag = cacheBits > 0; ColorCache colorCache = new ColorCache(); if (flag) { colorCache.Init(cacheBits); } refs.Refs.Clear(); AddSingleLiteral(bgra[0], flag, colorCache, refs); int num2 = 1; while (num2 < num) { int maxLimit = LosslessUtils.MaxFindCopyLength(num - num2); int num3 = LosslessUtils.FindMatchLength(bgra.Slice(num2), bgra.Slice(num2 - 1), 0, maxLimit); int num4 = ((num2 >= xSize) ? LosslessUtils.FindMatchLength(bgra.Slice(num2), bgra.Slice(num2 - xSize), 0, maxLimit) : 0); if (num3 >= num4 && num3 >= 4) { refs.Add(PixOrCopy.CreateCopy(1u, (ushort)num3)); num2 += num3; } else if (num4 >= 4) { refs.Add(PixOrCopy.CreateCopy((uint)xSize, (ushort)num4)); if (flag) { for (int i = 0; i < num4; i++) { colorCache.Insert(bgra[num2 + i]); } } num2 += num4; } else { AddSingleLiteral(bgra[num2], flag, colorCache, refs); num2++; } } } private static void BackwardRefsWithLocalCache(ReadOnlySpan bgra, int cacheBits, Vp8LBackwardRefs refs) { int num = 0; ColorCache colorCache = new ColorCache(); colorCache.Init(cacheBits); for (int i = 0; i < refs.Refs.Count; i++) { PixOrCopy pixOrCopy = refs.Refs[i]; if (pixOrCopy.IsLiteral()) { uint bgraOrDistance = pixOrCopy.BgraOrDistance; int num2 = colorCache.Contains(bgraOrDistance); if (num2 >= 0) { pixOrCopy.Mode = PixOrCopyMode.CacheIdx; pixOrCopy.BgraOrDistance = (uint)num2; pixOrCopy.Len = 1; } else { colorCache.Insert(bgraOrDistance); } num++; } else { for (int j = 0; j < pixOrCopy.Len; j++) { colorCache.Insert(bgra[num++]); } } } } private static void BackwardReferences2DLocality(int xSize, Vp8LBackwardRefs refs) { using List.Enumerator enumerator = refs.Refs.GetEnumerator(); while (enumerator.MoveNext()) { if (enumerator.Current.IsCopy()) { int bgraOrDistance = (int)enumerator.Current.BgraOrDistance; int bgraOrDistance2 = DistanceToPlaneCode(xSize, bgraOrDistance); enumerator.Current.BgraOrDistance = (uint)bgraOrDistance2; } } } private static void AddSingleLiteral(uint pixel, bool useColorCache, ColorCache colorCache, Vp8LBackwardRefs refs) { PixOrCopy pixOrCopy; if (useColorCache) { int index = colorCache.GetIndex(pixel); if (colorCache.Lookup(index) == pixel) { pixOrCopy = PixOrCopy.CreateCacheIdx(index); } else { pixOrCopy = PixOrCopy.CreateLiteral(pixel); colorCache.Set((uint)index, pixel); } } else { pixOrCopy = PixOrCopy.CreateLiteral(pixel); } refs.Add(pixOrCopy); } public static int DistanceToPlaneCode(int xSize, int dist) { int num = dist / xSize; int num2 = dist - num * xSize; if (num2 <= 8 && num < 8) { return (int)(WebpLookupTables.PlaneToCodeLut[num * 16 + 8 - num2] + 1); } if (num2 > xSize - 8 && num < 7) { return (int)(WebpLookupTables.PlaneToCodeLut[(num + 1) * 16 + 8 + (xSize - num2)] + 1); } return dist + 120; } } internal class ColorCache { private const uint HashMul = 506832829u; public uint[] Colors { get; private set; } public int HashShift { get; private set; } public int HashBits { get; private set; } public void Init(int hashBits) { int num = 1 << hashBits; Colors = new uint[num]; HashBits = hashBits; HashShift = 32 - hashBits; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Insert(uint bgra) { int num = HashPix(bgra, HashShift); Colors[num] = bgra; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint Lookup(int key) { return Colors[key]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Contains(uint bgra) { int num = HashPix(bgra, HashShift); if (Colors[num] != bgra) { return -1; } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetIndex(uint bgra) { return HashPix(bgra, HashShift); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Set(uint key, uint bgra) { Colors[key] = bgra; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int HashPix(uint argb, int shift) { return (int)(argb * 506832829 >> shift); } } internal static class ColorSpaceTransformUtils { private static readonly Vector128 CollectColorRedTransformsGreenMask = Vector128.Create(65280).AsByte(); private static readonly Vector128 CollectColorRedTransformsAndMask = Vector128.Create((short)255).AsByte(); private static readonly Vector256 CollectColorRedTransformsGreenMask256 = Vector256.Create(65280).AsByte(); private static readonly Vector256 CollectColorRedTransformsAndMask256 = Vector256.Create((short)255).AsByte(); private static readonly Vector128 CollectColorBlueTransformsGreenMask = Vector128.Create(0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue); private static readonly Vector128 CollectColorBlueTransformsGreenBlueMask = Vector128.Create(byte.MaxValue, byte.MaxValue, 0, 0, byte.MaxValue, byte.MaxValue, 0, 0, byte.MaxValue, byte.MaxValue, 0, 0, byte.MaxValue, byte.MaxValue, 0, 0); private static readonly Vector128 CollectColorBlueTransformsBlueMask = Vector128.Create(byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0); private static readonly Vector128 CollectColorBlueTransformsShuffleLowMask = Vector128.Create(byte.MaxValue, 2, byte.MaxValue, 6, byte.MaxValue, 10, byte.MaxValue, 14, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); private static readonly Vector128 CollectColorBlueTransformsShuffleHighMask = Vector128.Create(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, 2, byte.MaxValue, 6, byte.MaxValue, 10, byte.MaxValue, 14); private static readonly Vector256 CollectColorBlueTransformsShuffleLowMask256 = Vector256.Create(byte.MaxValue, 2, byte.MaxValue, 6, byte.MaxValue, 10, byte.MaxValue, 14, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, 18, byte.MaxValue, 22, byte.MaxValue, 26, byte.MaxValue, 30, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); private static readonly Vector256 CollectColorBlueTransformsShuffleHighMask256 = Vector256.Create(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, 2, byte.MaxValue, 6, byte.MaxValue, 10, byte.MaxValue, 14, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, 18, byte.MaxValue, 22, byte.MaxValue, 26, byte.MaxValue, 30); private static readonly Vector256 CollectColorBlueTransformsGreenBlueMask256 = Vector256.Create(byte.MaxValue, byte.MaxValue, 0, 0, byte.MaxValue, byte.MaxValue, 0, 0, byte.MaxValue, byte.MaxValue, 0, 0, byte.MaxValue, byte.MaxValue, 0, 0, byte.MaxValue, byte.MaxValue, 0, 0, byte.MaxValue, byte.MaxValue, 0, 0, byte.MaxValue, byte.MaxValue, 0, 0, byte.MaxValue, byte.MaxValue, 0, 0); private static readonly Vector256 CollectColorBlueTransformsBlueMask256 = Vector256.Create(byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0); private static readonly Vector256 CollectColorBlueTransformsGreenMask256 = Vector256.Create(0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue); public static void CollectColorBlueTransforms(Span bgra, int stride, int tileWidth, int tileHeight, int greenToBlue, int redToBlue, Span histo) { if (Avx2.IsSupported && tileWidth >= 16) { Span span = stackalloc ushort[16]; Vector256 right = Vector256.Create(LosslessUtils.Cst5b(redToBlue)); Vector256 right2 = Vector256.Create(LosslessUtils.Cst5b(greenToBlue)); for (int i = 0; i < tileHeight; i++) { ref uint reference = ref MemoryMarshal.GetReference(bgra.Slice(i * stride)); for (nint num = 0; num <= tileWidth - 16; num += 16) { IntPtr elementOffset = num; nint elementOffset2 = num + 8; Vector256 vector = Unsafe.As>(ref Unsafe.Add(ref reference, elementOffset)).AsByte(); Vector256 vector2 = Unsafe.As>(ref Unsafe.Add(ref reference, elementOffset2)).AsByte(); Vector256 left = Avx2.Shuffle(vector, CollectColorBlueTransformsShuffleLowMask256); Vector256 right3 = Avx2.Shuffle(vector2, CollectColorBlueTransformsShuffleHighMask256); Vector256 vector3 = Avx2.Or(left, right3); Vector256 vector4 = Avx2.And(vector, CollectColorBlueTransformsGreenBlueMask256); Vector256 vector5 = Avx2.PackUnsignedSaturate(right: Avx2.And(vector2, CollectColorBlueTransformsGreenBlueMask256).AsInt32(), left: vector4.AsInt32()); Vector256 vector6 = Avx2.And(vector5.AsByte(), CollectColorBlueTransformsGreenMask256); Vector256 vector7 = Avx2.And(Avx2.Subtract(right: Avx2.MultiplyHigh(vector3.AsInt16(), right).AsByte(), left: Avx2.Subtract(right: Avx2.MultiplyHigh(vector6.AsInt16(), right2).AsByte(), left: vector5.AsByte())), CollectColorBlueTransformsBlueMask256); Unsafe.As>(ref MemoryMarshal.GetReference(span)) = vector7.AsUInt16(); for (int j = 0; j < 16; j++) { histo[span[j]]++; } } } int num2 = tileWidth & 0xF; if (num2 > 0) { CollectColorBlueTransformsNoneVectorized(bgra.Slice(tileWidth - num2), stride, num2, tileHeight, greenToBlue, redToBlue, histo); } } else if (Sse41.IsSupported) { Span span2 = stackalloc ushort[8]; Vector128 right4 = Vector128.Create(LosslessUtils.Cst5b(redToBlue)); Vector128 right5 = Vector128.Create(LosslessUtils.Cst5b(greenToBlue)); for (int k = 0; k < tileHeight; k++) { ref uint reference2 = ref MemoryMarshal.GetReference(bgra.Slice(k * stride)); for (nint num3 = 0; num3 <= tileWidth - 8; num3 += 8) { IntPtr elementOffset3 = num3; nint elementOffset4 = num3 + 4; Vector128 vector8 = Unsafe.As>(ref Unsafe.Add(ref reference2, elementOffset3)).AsByte(); Vector128 vector9 = Unsafe.As>(ref Unsafe.Add(ref reference2, elementOffset4)).AsByte(); Vector128 left2 = Ssse3.Shuffle(vector8, CollectColorBlueTransformsShuffleLowMask); Vector128 right6 = Ssse3.Shuffle(vector9, CollectColorBlueTransformsShuffleHighMask); Vector128 vector10 = Sse2.Or(left2, right6); Vector128 vector11 = Sse2.And(vector8, CollectColorBlueTransformsGreenBlueMask); Vector128 vector12 = Sse41.PackUnsignedSaturate(right: Sse2.And(vector9, CollectColorBlueTransformsGreenBlueMask).AsInt32(), left: vector11.AsInt32()); Vector128 vector13 = Sse2.And(vector12.AsByte(), CollectColorBlueTransformsGreenMask); Vector128 vector14 = Sse2.And(Sse2.Subtract(right: Sse2.MultiplyHigh(vector10.AsInt16(), right4).AsByte(), left: Sse2.Subtract(right: Sse2.MultiplyHigh(vector13.AsInt16(), right5).AsByte(), left: vector12.AsByte())), CollectColorBlueTransformsBlueMask); Unsafe.As>(ref MemoryMarshal.GetReference(span2)) = vector14.AsUInt16(); for (int l = 0; l < 8; l++) { histo[span2[l]]++; } } } int num4 = tileWidth & 7; if (num4 > 0) { CollectColorBlueTransformsNoneVectorized(bgra.Slice(tileWidth - num4), stride, num4, tileHeight, greenToBlue, redToBlue, histo); } } else { CollectColorBlueTransformsNoneVectorized(bgra, stride, tileWidth, tileHeight, greenToBlue, redToBlue, histo); } } private static void CollectColorBlueTransformsNoneVectorized(Span bgra, int stride, int tileWidth, int tileHeight, int greenToBlue, int redToBlue, Span histo) { int num = 0; while (tileHeight-- > 0) { for (int i = 0; i < tileWidth; i++) { histo[LosslessUtils.TransformColorBlue((sbyte)greenToBlue, (sbyte)redToBlue, bgra[num + i])]++; } num += stride; } } public static void CollectColorRedTransforms(Span bgra, int stride, int tileWidth, int tileHeight, int greenToRed, Span histo) { if (Avx2.IsSupported && tileWidth >= 16) { Vector256 right = Vector256.Create(LosslessUtils.Cst5b(greenToRed)); Span span = stackalloc ushort[16]; for (int i = 0; i < tileHeight; i++) { ref uint reference = ref MemoryMarshal.GetReference(bgra.Slice(i * stride)); for (nint num = 0; num <= tileWidth - 16; num += 16) { IntPtr elementOffset = num; nint elementOffset2 = num + 8; Vector256 vector = Unsafe.As>(ref Unsafe.Add(ref reference, elementOffset)).AsByte(); Vector256 vector2 = Unsafe.As>(ref Unsafe.Add(ref reference, elementOffset2)).AsByte(); Vector256 vector3 = Avx2.And(vector, CollectColorRedTransformsGreenMask256); Vector256 vector4 = Avx2.And(vector2, CollectColorRedTransformsGreenMask256); Vector256 vector5 = Avx2.PackUnsignedSaturate(vector3.AsInt32(), vector4.AsInt32()); Vector256 left = Avx2.ShiftRightLogical(vector.AsInt32(), 16); Vector256 right2 = Avx2.ShiftRightLogical(vector2.AsInt32(), 16); Vector256 vector6 = Avx2.PackUnsignedSaturate(left, right2); Vector256 vector7 = Avx2.And(Avx2.Subtract(right: Avx2.MultiplyHigh(vector5.AsInt16(), right).AsByte(), left: vector6.AsByte()), CollectColorRedTransformsAndMask256); Unsafe.As>(ref MemoryMarshal.GetReference(span)) = vector7.AsUInt16(); for (int j = 0; j < 16; j++) { histo[span[j]]++; } } } int num2 = tileWidth & 0xF; if (num2 > 0) { CollectColorRedTransformsNoneVectorized(bgra.Slice(tileWidth - num2), stride, num2, tileHeight, greenToRed, histo); } } else if (Sse41.IsSupported) { Vector128 right3 = Vector128.Create(LosslessUtils.Cst5b(greenToRed)); Span span2 = stackalloc ushort[8]; for (int k = 0; k < tileHeight; k++) { ref uint reference2 = ref MemoryMarshal.GetReference(bgra.Slice(k * stride)); for (nint num3 = 0; num3 <= tileWidth - 8; num3 += 8) { IntPtr elementOffset3 = num3; nint elementOffset4 = num3 + 4; Vector128 vector8 = Unsafe.As>(ref Unsafe.Add(ref reference2, elementOffset3)).AsByte(); Vector128 vector9 = Unsafe.As>(ref Unsafe.Add(ref reference2, elementOffset4)).AsByte(); Vector128 vector10 = Sse2.And(vector8, CollectColorRedTransformsGreenMask); Vector128 vector11 = Sse2.And(vector9, CollectColorRedTransformsGreenMask); Vector128 vector12 = Sse41.PackUnsignedSaturate(vector10.AsInt32(), vector11.AsInt32()); Vector128 left2 = Sse2.ShiftRightLogical(vector8.AsInt32(), 16); Vector128 right4 = Sse2.ShiftRightLogical(vector9.AsInt32(), 16); Vector128 vector13 = Sse41.PackUnsignedSaturate(left2, right4); Vector128 vector14 = Sse2.And(Sse2.Subtract(right: Sse2.MultiplyHigh(vector12.AsInt16(), right3).AsByte(), left: vector13.AsByte()), CollectColorRedTransformsAndMask); Unsafe.As>(ref MemoryMarshal.GetReference(span2)) = vector14.AsUInt16(); for (int l = 0; l < 8; l++) { histo[span2[l]]++; } } } int num4 = tileWidth & 7; if (num4 > 0) { CollectColorRedTransformsNoneVectorized(bgra.Slice(tileWidth - num4), stride, num4, tileHeight, greenToRed, histo); } } else { CollectColorRedTransformsNoneVectorized(bgra, stride, tileWidth, tileHeight, greenToRed, histo); } } private static void CollectColorRedTransformsNoneVectorized(Span bgra, int stride, int tileWidth, int tileHeight, int greenToRed, Span histo) { int num = 0; while (tileHeight-- > 0) { for (int i = 0; i < tileWidth; i++) { histo[LosslessUtils.TransformColorRed((sbyte)greenToRed, bgra[num + i])]++; } num += stride; } } } [DebuggerDisplay("Start: {Start}, End: {End}, Cost: {Cost}")] internal class CostCacheInterval { public double Cost { get; set; } public int Start { get; set; } public int End { get; set; } } [DebuggerDisplay("Start: {Start}, End: {End}, Cost: {Cost}")] internal class CostInterval { public float Cost { get; set; } public int Start { get; set; } public int End { get; set; } public int Index { get; set; } public CostInterval Previous { get; set; } public CostInterval Next { get; set; } } internal sealed class CostManager : IDisposable { private CostInterval head; private const int FreeIntervalsStartCount = 25; private readonly Stack freeIntervals = new Stack(25); public int Count { get; set; } public List CostCache { get; } public int CacheIntervalsSize { get; } public IMemoryOwner Costs { get; } public IMemoryOwner DistArray { get; } public List CacheIntervals { get; } public CostManager(MemoryAllocator memoryAllocator, IMemoryOwner distArray, int pixCount, CostModel costModel) { int num = ((pixCount > 4095) ? 4095 : pixCount); CacheIntervals = new List(); CostCache = new List(); Costs = memoryAllocator.Allocate(pixCount); DistArray = distArray; Count = 0; for (int i = 0; i < 25; i++) { freeIntervals.Push(new CostInterval()); } int cacheIntervalsSize = CacheIntervalsSize; CacheIntervalsSize = cacheIntervalsSize + 1; CostCache.Add(costModel.GetLengthCost(0)); for (int j = 1; j < num; j++) { CostCache.Add(costModel.GetLengthCost(j)); if (CostCache[j] != CostCache[j - 1]) { cacheIntervalsSize = CacheIntervalsSize; CacheIntervalsSize = cacheIntervalsSize + 1; } } CostCacheInterval costCacheInterval = new CostCacheInterval { Start = 0, End = 1, Cost = CostCache[0] }; CacheIntervals.Add(costCacheInterval); for (int k = 1; k < num; k++) { double num2 = CostCache[k]; if (num2 != costCacheInterval.Cost) { costCacheInterval = new CostCacheInterval { Start = k, Cost = num2 }; CacheIntervals.Add(costCacheInterval); } costCacheInterval.End = k + 1; } Costs.GetSpan().Fill(1E+38f); } public void UpdateCostAtIndex(int i, bool doCleanIntervals) { CostInterval costInterval = head; while (costInterval != null && costInterval.Start <= i) { CostInterval next = costInterval.Next; if (costInterval.End <= i) { if (doCleanIntervals) { PopInterval(costInterval); } } else { UpdateCost(i, costInterval.Index, costInterval.Cost); } costInterval = next; } } public void PushInterval(double distanceCost, int position, int len) { int num = 10; Span span = Costs.GetSpan(); Span span2 = DistArray.GetSpan(); if (len < num) { for (int i = position; i < position + len; i++) { int num2 = i - position; float num3 = (float)(distanceCost + CostCache[num2]); if (span[i] > num3) { span[i] = num3; span2[i] = (ushort)(num2 + 1); } } return; } CostInterval costInterval = head; for (int j = 0; j < CacheIntervalsSize && CacheIntervals[j].Start < len; j++) { int num4 = position + CacheIntervals[j].Start; int num5 = position + ((CacheIntervals[j].End > len) ? len : CacheIntervals[j].End); float num6 = (float)(distanceCost + CacheIntervals[j].Cost); while (costInterval != null && costInterval.Start < num5) { CostInterval next = costInterval.Next; if (num4 < costInterval.End) { if (num6 >= costInterval.Cost) { int end = costInterval.End; InsertInterval(costInterval, num6, position, num4, costInterval.Start); num4 = end; if (num4 >= num5) { break; } } else if (num4 <= costInterval.Start) { if (costInterval.End > num5) { costInterval.Start = num5; break; } PopInterval(costInterval); } else { if (num5 < costInterval.End) { int end2 = costInterval.End; costInterval.End = num4; InsertInterval(costInterval, costInterval.Cost, costInterval.Index, num5, end2); break; } costInterval.End = num4; } } costInterval = next; } InsertInterval(costInterval, num6, position, num4, num5); } } private void PopInterval(CostInterval interval) { if (interval != null) { ConnectIntervals(interval.Previous, interval.Next); Count--; interval.Next = null; interval.Previous = null; freeIntervals.Push(interval); } } private void InsertInterval(CostInterval intervalIn, float cost, int position, int start, int end) { if (start < end) { CostInterval costInterval; if (freeIntervals.Count > 0) { costInterval = freeIntervals.Pop(); costInterval.Cost = cost; costInterval.Start = start; costInterval.End = end; costInterval.Index = position; } else { costInterval = new CostInterval { Cost = cost, Start = start, End = end, Index = position }; } PositionOrphanInterval(costInterval, intervalIn); Count++; } } private void PositionOrphanInterval(CostInterval current, CostInterval previous) { if (previous == null) { previous = head; } while (previous != null && current.Start < previous.Start) { previous = previous.Previous; } while (previous?.Next != null && previous.Next.Start < current.Start) { previous = previous.Next; } ConnectIntervals(current, (previous != null) ? previous.Next : head); ConnectIntervals(previous, current); } private void ConnectIntervals(CostInterval prev, CostInterval next) { if (prev != null) { prev.Next = next; } else { head = next; } if (next != null) { next.Previous = prev; } } private void UpdateCost(int i, int position, float cost) { Span span = Costs.GetSpan(); Span span2 = DistArray.GetSpan(); int num = i - position; if (span[i] > cost) { span[i] = cost; span2[i] = (ushort)(num + 1); } } public void Dispose() { Costs.Dispose(); } } internal class CostModel { private const int ValuesInBytes = 256; public double[] Alpha { get; } public double[] Red { get; } public double[] Blue { get; } public double[] Distance { get; } public double[] Literal { get; } public CostModel(int literalArraySize) { Alpha = new double[256]; Red = new double[256]; Blue = new double[256]; Distance = new double[40]; Literal = new double[literalArraySize]; } public void Build(int xSize, int cacheBits, Vp8LBackwardRefs backwardRefs) { Vp8LHistogram vp8LHistogram = new Vp8LHistogram(cacheBits); using List.Enumerator enumerator = backwardRefs.Refs.GetEnumerator(); while (enumerator.MoveNext()) { vp8LHistogram.AddSinglePixOrCopy(enumerator.Current, useDistanceModifier: true, xSize); } ConvertPopulationCountTableToBitEstimates(vp8LHistogram.NumCodes(), vp8LHistogram.Literal, Literal); ConvertPopulationCountTableToBitEstimates(256, vp8LHistogram.Red, Red); ConvertPopulationCountTableToBitEstimates(256, vp8LHistogram.Blue, Blue); ConvertPopulationCountTableToBitEstimates(256, vp8LHistogram.Alpha, Alpha); ConvertPopulationCountTableToBitEstimates(40, vp8LHistogram.Distance, Distance); } public double GetLengthCost(int length) { int extraBits = 0; int num = LosslessUtils.PrefixEncodeBits(length, ref extraBits); return Literal[256 + num] + (double)extraBits; } public double GetDistanceCost(int distance) { int extraBits = 0; int num = LosslessUtils.PrefixEncodeBits(distance, ref extraBits); return Distance[num] + (double)extraBits; } public double GetCacheCost(uint idx) { int num = (int)(280 + idx); return Literal[num]; } public double GetLiteralCost(uint v) { return Alpha[v >> 24] + Red[(v >> 16) & 0xFF] + Literal[(v >> 8) & 0xFF] + Blue[v & 0xFF]; } private static void ConvertPopulationCountTableToBitEstimates(int numSymbols, uint[] populationCounts, double[] output) { uint num = 0u; int num2 = 0; for (int i = 0; i < numSymbols; i++) { num += populationCounts[i]; if (populationCounts[i] != 0) { num2++; } } if (num2 <= 1) { output.AsSpan(0, numSymbols).Clear(); return; } double num3 = LosslessUtils.FastLog2(num); for (int j = 0; j < numSymbols; j++) { output[j] = num3 - (double)LosslessUtils.FastLog2(populationCounts[j]); } } } internal class CrunchConfig { public EntropyIx EntropyIdx { get; set; } public List SubConfigs { get; } = new List(); } internal class CrunchSubConfig { public int Lz77 { get; set; } public bool DoNotCache { get; set; } } internal class DominantCostRange { public double LiteralMax { get; set; } public double LiteralMin { get; set; } public double RedMax { get; set; } public double RedMin { get; set; } public double BlueMax { get; set; } public double BlueMin { get; set; } public DominantCostRange() { LiteralMax = 0.0; LiteralMin = double.MaxValue; RedMax = 0.0; RedMin = double.MaxValue; BlueMax = 0.0; BlueMin = double.MaxValue; } public void UpdateDominantCostRange(Vp8LHistogram h) { if (LiteralMax < h.LiteralCost) { LiteralMax = h.LiteralCost; } if (LiteralMin > h.LiteralCost) { LiteralMin = h.LiteralCost; } if (RedMax < h.RedCost) { RedMax = h.RedCost; } if (RedMin > h.RedCost) { RedMin = h.RedCost; } if (BlueMax < h.BlueCost) { BlueMax = h.BlueCost; } if (BlueMin > h.BlueCost) { BlueMin = h.BlueCost; } } public int GetHistoBinIndex(Vp8LHistogram h, int numPartitions) { return (GetBinIdForEntropy(LiteralMin, LiteralMax, h.LiteralCost, numPartitions) * numPartitions + GetBinIdForEntropy(RedMin, RedMax, h.RedCost, numPartitions)) * numPartitions + GetBinIdForEntropy(BlueMin, BlueMax, h.BlueCost, numPartitions); } private static int GetBinIdForEntropy(double min, double max, double val, int numPartitions) { double num = max - min; if (num > 0.0) { double num2 = val - min; return (int)(((double)numPartitions - 1E-06) * num2 / num); } return 0; } } internal struct HistogramBinInfo { public short First; public ushort NumCombineFailures; } internal class HistogramEncoder { private const int NumPartitions = 4; private const int BinSize = 64; private const int MaxHistoGreedy = 100; private const uint NonTrivialSym = uint.MaxValue; private const ushort InvalidHistogramSymbol = ushort.MaxValue; public static void GetHistoImageSymbols(int xSize, int ySize, Vp8LBackwardRefs refs, int quality, int histoBits, int cacheBits, List imageHisto, Vp8LHistogram tmpHisto, ushort[] histogramSymbols) { int num = ((histoBits <= 0) ? 1 : LosslessUtils.SubSampleSize(xSize, histoBits)); int num2 = ((histoBits <= 0) ? 1 : LosslessUtils.SubSampleSize(ySize, histoBits)); int num3 = num * num2; int num4 = 64; ushort[] array = new ushort[num3]; ushort[] clusterMappings = new ushort[num3]; List list = new List(num3); for (int i = 0; i < num3; i++) { list.Add(new Vp8LHistogram(cacheBits)); } HistogramBuild(xSize, histoBits, refs, list); int num5 = HistogramCopyAndAnalyze(list, imageHisto, histogramSymbols); if (num5 > num4 * 2 && quality < 100) { ushort[] binMap = array; int numClusters = num5; double combineCostFactor = GetCombineCostFactor(num3, quality); HistogramAnalyzeEntropyBin(imageHisto, binMap); HistogramCombineEntropyBin(imageHisto, histogramSymbols, clusterMappings, tmpHisto, binMap, num4, combineCostFactor); OptimizeHistogramSymbols(clusterMappings, numClusters, array, histogramSymbols); } float num6 = (float)quality / 100f; int minClusterSize = (int)(1f + num6 * num6 * num6 * 99f); if (HistogramCombineStochastic(imageHisto, minClusterSize)) { RemoveEmptyHistograms(imageHisto); HistogramCombineGreedy(imageHisto); } RemoveEmptyHistograms(imageHisto); HistogramRemap(list, imageHisto, histogramSymbols); } private static void RemoveEmptyHistograms(List histograms) { int num = 0; for (int i = 0; i < histograms.Count; i++) { if (histograms[i] != null) { histograms[num++] = histograms[i]; } } histograms.RemoveRange(num, histograms.Count - num); } private static void HistogramBuild(int xSize, int histoBits, Vp8LBackwardRefs backwardRefs, List histograms) { int num = 0; int num2 = 0; int num3 = LosslessUtils.SubSampleSize(xSize, histoBits); foreach (PixOrCopy @ref in backwardRefs.Refs) { int index = (num2 >> histoBits) * num3 + (num >> histoBits); histograms[index].AddSinglePixOrCopy(@ref, useDistanceModifier: false); num += @ref.Len; while (num >= xSize) { num -= xSize; num2++; } } } private static void HistogramAnalyzeEntropyBin(List histograms, ushort[] binMap) { int count = histograms.Count; DominantCostRange dominantCostRange = new DominantCostRange(); for (int i = 0; i < count; i++) { if (histograms[i] != null) { dominantCostRange.UpdateDominantCostRange(histograms[i]); } } for (int j = 0; j < count; j++) { if (histograms[j] != null) { binMap[j] = (ushort)dominantCostRange.GetHistoBinIndex(histograms[j], 4); } } } private static int HistogramCopyAndAnalyze(List origHistograms, List histograms, ushort[] histogramSymbols) { Vp8LStreaks stats = new Vp8LStreaks(); Vp8LBitEntropy bitsEntropy = new Vp8LBitEntropy(); int num = 0; for (int i = 0; i < origHistograms.Count; i++) { Vp8LHistogram vp8LHistogram = origHistograms[i]; vp8LHistogram.UpdateHistogramCost(stats, bitsEntropy); if (!vp8LHistogram.IsUsed[0] && !vp8LHistogram.IsUsed[1] && !vp8LHistogram.IsUsed[2] && !vp8LHistogram.IsUsed[3] && !vp8LHistogram.IsUsed[4]) { origHistograms[i] = null; histograms[i] = null; histogramSymbols[i] = ushort.MaxValue; } else { histograms[i] = (Vp8LHistogram)vp8LHistogram.DeepClone(); histogramSymbols[i] = (ushort)num++; } } return histogramSymbols.Count((ushort h) => h != ushort.MaxValue); } private static void HistogramCombineEntropyBin(List histograms, ushort[] clusters, ushort[] clusterMappings, Vp8LHistogram curCombo, ushort[] binMap, int numBins, double combineCostFactor) { HistogramBinInfo[] array = new HistogramBinInfo[64]; for (int i = 0; i < numBins; i++) { array[i].First = -1; array[i].NumCombineFailures = 0; } for (int j = 0; j < histograms.Count; j++) { clusterMappings[j] = (ushort)j; } List list = new List(); Vp8LStreaks stats = new Vp8LStreaks(); Vp8LBitEntropy bitsEntropy = new Vp8LBitEntropy(); for (int k = 0; k < histograms.Count; k++) { if (histograms[k] == null) { continue; } int num = binMap[k]; int first = array[num].First; if (first == -1) { array[num].First = (short)k; continue; } double num2 = (0.0 - histograms[k].BitCost) * combineCostFactor; if (histograms[first].AddEval(histograms[k], stats, bitsEntropy, num2, curCombo) < num2) { bool num3 = curCombo.TrivialSymbol != uint.MaxValue || (histograms[k].TrivialSymbol == uint.MaxValue && histograms[first].TrivialSymbol == uint.MaxValue); int num4 = 32; if (num3 || array[num].NumCombineFailures >= num4) { Vp8LHistogram value = curCombo; curCombo = histograms[first]; histograms[first] = value; histograms[k] = null; list.Add(k); clusterMappings[clusters[k]] = clusters[first]; } else { array[num].NumCombineFailures++; } } } foreach (int item in list.OrderByDescending((int result) => result)) { histograms.RemoveAt(item); } } private static void OptimizeHistogramSymbols(ushort[] clusterMappings, int numClusters, ushort[] clusterMappingsTmp, ushort[] symbols) { bool flag = true; while (flag) { flag = false; for (int i = 0; i < numClusters; i++) { int num; for (num = clusterMappings[i]; num != clusterMappings[num]; num = clusterMappings[num]) { clusterMappings[num] = clusterMappings[clusterMappings[num]]; } if (num != clusterMappings[i]) { flag = true; clusterMappings[i] = (ushort)num; } } } int num2 = 0; clusterMappingsTmp.AsSpan().Clear(); for (int j = 0; j < symbols.Length; j++) { if (symbols[j] != ushort.MaxValue) { int num3 = clusterMappings[symbols[j]]; if (num3 > 0 && clusterMappingsTmp[num3] == 0) { num2++; clusterMappingsTmp[num3] = (ushort)num2; } symbols[j] = clusterMappingsTmp[num3]; } } } private static bool HistogramCombineStochastic(List histograms, int minClusterSize) { uint seed = 1u; int num = 0; int num2 = histograms.Count((Vp8LHistogram h) => h != null); int num3 = num2; int num4 = num3 / 2; Vp8LStreaks stats = new Vp8LStreaks(); Vp8LBitEntropy bitsEntropy = new Vp8LBitEntropy(); if (num2 < minClusterSize) { return true; } List list = new List(); int num5 = 9; int[] array = new int[histograms.Count]; int num6 = 0; for (int num7 = 0; num7 < histograms.Count; num7++) { if (histograms[num7] != null) { array[num6++] = num7; } } for (int num8 = 0; num8 < num3; num8++) { if (num2 < minClusterSize) { break; } if (++num >= num4) { break; } double threshold = ((list.Count == 0) ? 0.0 : list[0].CostDiff); int num9 = num2 / 2; uint num10 = (uint)((num2 - 1) * num2); int num11 = 0; while (num2 >= 2 && num11 < num9) { uint num12 = MyRand(ref seed) % num10; int num13 = (int)(num12 / (num2 - 1)); int num14 = (int)(num12 % (num2 - 1)); if (num14 >= num13) { num14++; } num13 = array[num13]; num14 = array[num14]; double num15 = HistoPriorityListPush(list, num5, histograms, num13, num14, threshold, stats, bitsEntropy); if (num15 < 0.0) { threshold = num15; if (list.Count == num5) { break; } } num11++; } if (list.Count == 0) { continue; } int idx = list[0].Idx1; int idx2 = list[0].Idx2; int num16 = Array.IndexOf(array, idx2); Span span = array.AsSpan(num16 + 1, num2 - num16 - 1); Span destination = array.AsSpan(num16); span.CopyTo(destination); HistogramAdd(histograms[idx2], histograms[idx], histograms[idx]); histograms.ElementAt(idx).BitCost = list[0].CostCombo; histograms[idx2] = null; num2--; int num17 = 0; while (num17 < list.Count) { HistogramPair histogramPair = list[num17]; bool flag = histogramPair.Idx1 == idx || histogramPair.Idx1 == idx2; bool flag2 = histogramPair.Idx2 == idx || histogramPair.Idx2 == idx2; bool flag3 = false; if (flag && flag2) { list[num17] = list[list.Count - 1]; list.RemoveAt(list.Count - 1); continue; } if (flag) { histogramPair.Idx1 = idx; flag3 = true; } else if (flag2) { histogramPair.Idx2 = idx; flag3 = true; } if (histogramPair.Idx1 > histogramPair.Idx2) { int idx3 = histogramPair.Idx2; histogramPair.Idx2 = histogramPair.Idx1; histogramPair.Idx1 = idx3; } if (flag3) { HistoListUpdatePair(histograms[histogramPair.Idx1], histograms[histogramPair.Idx2], stats, bitsEntropy, 0.0, histogramPair); if (histogramPair.CostDiff >= 0.0) { list[num17] = list[list.Count - 1]; list.RemoveAt(list.Count - 1); continue; } } HistoListUpdateHead(list, histogramPair); num17++; } num = 0; } return num2 <= minClusterSize; } private static void HistogramCombineGreedy(List histograms) { int num = histograms.Count((Vp8LHistogram h) => h != null); List list = new List(); int maxSize = num * num; Vp8LStreaks stats = new Vp8LStreaks(); Vp8LBitEntropy bitsEntropy = new Vp8LBitEntropy(); for (int num2 = 0; num2 < num; num2++) { if (histograms[num2] == null) { continue; } for (int num3 = num2 + 1; num3 < num; num3++) { if (histograms[num3] != null) { HistoPriorityListPush(list, maxSize, histograms, num2, num3, 0.0, stats, bitsEntropy); } } } while (list.Count > 0) { int idx = list[0].Idx1; int idx2 = list[0].Idx2; HistogramAdd(histograms[idx2], histograms[idx], histograms[idx]); histograms[idx].BitCost = list[0].CostCombo; histograms[idx2] = null; int num4 = 0; while (num4 < list.Count) { HistogramPair histogramPair = list.ElementAt(num4); if (histogramPair.Idx1 == idx || histogramPair.Idx2 == idx || histogramPair.Idx1 == idx2 || histogramPair.Idx2 == idx2) { list[num4] = list[list.Count - 1]; list.RemoveAt(list.Count - 1); } else { HistoListUpdateHead(list, histogramPair); num4++; } } for (int num5 = 0; num5 < num; num5++) { if (num5 != idx && histograms[num5] != null) { HistoPriorityListPush(list, maxSize, histograms, idx, num5, 0.0, stats, bitsEntropy); } } } } private static void HistogramRemap(List input, List output, ushort[] symbols) { int count = input.Count; int count2 = output.Count; Vp8LStreaks stats = new Vp8LStreaks(); Vp8LBitEntropy bitsEntropy = new Vp8LBitEntropy(); if (count2 > 1) { for (int i = 0; i < count; i++) { if (input[i] == null) { symbols[i] = symbols[i - 1]; continue; } int num = 0; double num2 = double.MaxValue; for (int j = 0; j < count2; j++) { double num3 = output[j].AddThresh(input[i], stats, bitsEntropy, num2); if (j == 0 || num3 < num2) { num2 = num3; num = j; } } symbols[i] = (ushort)num; } } else { for (int k = 0; k < count; k++) { symbols[k] = 0; } } int paletteCodeBits = output.First().PaletteCodeBits; output.Clear(); for (int l = 0; l < count2; l++) { output.Add(new Vp8LHistogram(paletteCodeBits)); } for (int m = 0; m < count; m++) { if (input[m] != null) { int index = symbols[m]; input[m].Add(output[index], output[index]); } } } private static double HistoPriorityListPush(List histoList, int maxSize, List histograms, int idx1, int idx2, double threshold, Vp8LStreaks stats, Vp8LBitEntropy bitsEntropy) { HistogramPair histogramPair = new HistogramPair(); if (histoList.Count == maxSize) { return 0.0; } if (idx1 > idx2) { int num = idx2; idx2 = idx1; idx1 = num; } histogramPair.Idx1 = idx1; histogramPair.Idx2 = idx2; Vp8LHistogram h = histograms[idx1]; Vp8LHistogram h2 = histograms[idx2]; HistoListUpdatePair(h, h2, stats, bitsEntropy, threshold, histogramPair); if (histogramPair.CostDiff >= threshold) { return 0.0; } histoList.Add(histogramPair); HistoListUpdateHead(histoList, histogramPair); return histogramPair.CostDiff; } private static void HistoListUpdatePair(Vp8LHistogram h1, Vp8LHistogram h2, Vp8LStreaks stats, Vp8LBitEntropy bitsEntropy, double threshold, HistogramPair pair) { double num = h1.BitCost + h2.BitCost; pair.CostCombo = 0.0; h1.GetCombinedHistogramEntropy(h2, stats, bitsEntropy, num + threshold, pair.CostCombo, out var cost); pair.CostCombo = cost; pair.CostDiff = pair.CostCombo - num; } private static void HistoListUpdateHead(List histoList, HistogramPair pair) { if (pair.CostDiff < histoList[0].CostDiff) { int index = histoList.IndexOf(pair); histoList[index] = histoList[0]; histoList[0] = pair; } } private static void HistogramAdd(Vp8LHistogram a, Vp8LHistogram b, Vp8LHistogram output) { a.Add(b, output); output.TrivialSymbol = ((a.TrivialSymbol == b.TrivialSymbol) ? a.TrivialSymbol : uint.MaxValue); } private static double GetCombineCostFactor(int histoSize, int quality) { double num = 0.16; if (quality < 90) { if (histoSize > 256) { num /= 2.0; } if (histoSize > 512) { num /= 2.0; } if (histoSize > 1024) { num /= 2.0; } if (quality <= 50) { num /= 2.0; } } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint MyRand(ref uint seed) { seed = (uint)((ulong)((long)seed * 48271L) % 2147483647uL); return seed; } } [DebuggerDisplay("Idx1: {Idx1}, Idx2: {Idx2}, CostDiff: {CostDiff}, CostCombo: {CostCombo}")] internal class HistogramPair { public int Idx1 { get; set; } public int Idx2 { get; set; } public double CostDiff { get; set; } public double CostCombo { get; set; } } internal struct HTreeGroup { public List HTrees { get; } public bool IsTrivialLiteral { get; set; } public uint LiteralArb { get; set; } public bool IsTrivialCode { get; set; } public bool UsePackedTable { get; set; } public HuffmanCode[] PackedTable { get; set; } public HTreeGroup(uint packedTableSize) { HTrees = new List(5); PackedTable = new HuffmanCode[packedTableSize]; IsTrivialCode = false; IsTrivialLiteral = false; LiteralArb = 0u; UsePackedTable = false; } } internal static class HuffIndex { public const int Green = 0; public const int Red = 1; public const int Blue = 2; public const int Alpha = 3; public const int Dist = 4; } [DebuggerDisplay("BitsUsed: {BitsUsed}, Value: {Value}")] internal struct HuffmanCode { public int BitsUsed { get; set; } public uint Value { get; set; } } [DebuggerDisplay("TotalCount = {TotalCount}, Value = {Value}, Left = {PoolIndexLeft}, Right = {PoolIndexRight}")] internal struct HuffmanTree { public int TotalCount { get; set; } public int Value { get; set; } public int PoolIndexLeft { get; set; } public int PoolIndexRight { get; set; } private HuffmanTree(HuffmanTree other) { TotalCount = other.TotalCount; Value = other.Value; PoolIndexLeft = other.PoolIndexLeft; PoolIndexRight = other.PoolIndexRight; } public static int Compare(HuffmanTree t1, HuffmanTree t2) { if (t1.TotalCount > t2.TotalCount) { return -1; } if (t1.TotalCount < t2.TotalCount) { return 1; } if (t1.Value >= t2.Value) { return 1; } return -1; } } internal struct HuffmanTreeCode { public int NumSymbols { get; set; } public byte[] CodeLengths { get; set; } public short[] Codes { get; set; } } [DebuggerDisplay("Code = {Code}, ExtraBits = {ExtraBits}")] internal class HuffmanTreeToken { public byte Code { get; set; } public byte ExtraBits { get; set; } } internal static class HuffmanUtils { public const int HuffmanTableBits = 8; public const int HuffmanPackedBits = 6; public const int HuffmanTableMask = 255; public const uint HuffmanPackedTableSize = 64u; private static readonly byte[] ReversedBits = new byte[16] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; public static void CreateHuffmanTree(uint[] histogram, int treeDepthLimit, bool[] bufRle, HuffmanTree[] huffTree, HuffmanTreeCode huffCode) { int numSymbols = huffCode.NumSymbols; bufRle.AsSpan().Clear(); OptimizeHuffmanForRle(numSymbols, bufRle, histogram); GenerateOptimalTree(huffTree, histogram, numSymbols, treeDepthLimit, huffCode.CodeLengths); ConvertBitDepthsToSymbols(huffCode); } public static void OptimizeHuffmanForRle(int length, bool[] goodForRle, uint[] counts) { while (length >= 0) { if (length == 0) { return; } if (counts[length - 1] != 0) { break; } length--; } uint num = counts[0]; int num2 = 0; for (int i = 0; i < length + 1; i++) { if (i == length || counts[i] != num) { if ((num == 0 && num2 >= 5) || (num != 0 && num2 >= 7)) { for (int j = 0; j < num2; j++) { goodForRle[i - j - 1] = true; } } num2 = 1; if (i != length) { num = counts[i]; } } else { num2++; } } num2 = 0; uint b = counts[0]; uint num3 = 0u; for (int k = 0; k < length + 1; k++) { if (k == length || goodForRle[k] || (k != 0 && goodForRle[k - 1]) || !ValuesShouldBeCollapsedToStrideAverage((int)counts[k], (int)b)) { if (num2 >= 4 || (num2 >= 3 && num3 == 0)) { uint num4 = (uint)((num3 + num2 / 2) / num2); if (num4 < 1) { num4 = 1u; } if (num3 == 0) { num4 = 0u; } for (uint num5 = 0u; num5 < num2; num5++) { counts[k - num5 - 1] = num4; } } num2 = 0; num3 = 0u; b = ((k < length - 3) ? ((counts[k] + counts[k + 1] + counts[k + 2] + counts[k + 3] + 2) / 4) : ((k < length) ? counts[k] : 0u)); } num2++; if (k != length) { num3 += counts[k]; if (num2 >= 4) { b = (uint)((num3 + num2 / 2) / num2); } } } } public static void GenerateOptimalTree(HuffmanTree[] tree, uint[] histogram, int histogramSize, int treeDepthLimit, byte[] bitDepths) { int num = 0; for (int i = 0; i < histogramSize; i++) { if (histogram[i] != 0) { num++; } } if (num == 0) { return; } Span pool = tree.AsSpan(num); uint num2 = 1u; while (true) { int num3 = num; int num4 = 0; for (int j = 0; j < histogramSize; j++) { if (histogram[j] != 0) { uint totalCount = ((histogram[j] < num2) ? num2 : histogram[j]); tree[num4].TotalCount = (int)totalCount; tree[num4].Value = j; tree[num4].PoolIndexLeft = -1; tree[num4].PoolIndexRight = -1; num4++; } } HuffmanTree[] array = tree.AsSpan(0, num3).ToArray(); Array.Sort(array, HuffmanTree.Compare); array.AsSpan().CopyTo(tree); if (num3 > 1) { int num5 = 0; while (num3 > 1) { pool[num5++] = tree[num3 - 1]; pool[num5++] = tree[num3 - 2]; int num6 = pool[num5 - 1].TotalCount + pool[num5 - 2].TotalCount; num3 -= 2; int k; for (k = 0; k < num3 && tree[k].TotalCount > num6; k++) { } int num7 = k + 1; int num8 = num3 - k; for (int num9 = num7 + num8 - 1; num9 >= num7; num9--) { tree[num9] = tree[num9 - 1]; } tree[k].TotalCount = num6; tree[k].Value = -1; tree[k].PoolIndexLeft = num5 - 1; tree[k].PoolIndexRight = num5 - 2; num3++; } SetBitDepths(tree, pool, bitDepths, 0); } else if (num3 == 1) { bitDepths[tree[0].Value] = 1; } int num10 = bitDepths[0]; for (int l = 1; l < histogramSize; l++) { if (num10 < bitDepths[l]) { num10 = bitDepths[l]; } } if (num10 > treeDepthLimit) { num2 *= 2; continue; } break; } } public static int CreateCompressedHuffmanTree(HuffmanTreeCode tree, HuffmanTreeToken[] tokensArray) { int numSymbols = tree.NumSymbols; int prevValue = 8; int i = 0; int num = 0; int num3; for (; i < numSymbols; i += num3) { int num2 = tree.CodeLengths[i]; int j; for (j = i + 1; j < numSymbols && tree.CodeLengths[j] == num2; j++) { } num3 = j - i; if (num2 == 0) { num += CodeRepeatedZeros(num3, tokensArray.AsSpan(num)); continue; } num += CodeRepeatedValues(num3, tokensArray.AsSpan(num), num2, prevValue); prevValue = num2; } return num; } public static int BuildHuffmanTable(Span table, int rootBits, int[] codeLengths, int codeLengthsSize) { int[] array = new int[codeLengthsSize]; int num = 1 << rootBits; int[] array2 = new int[16]; int[] array3 = new int[16]; int i; for (i = 0; i < codeLengthsSize; i++) { int num2 = codeLengths[i]; if (num2 > 15) { return 0; } array2[num2]++; } if (array2[0] == codeLengthsSize) { return 0; } array3[1] = 0; int j; for (j = 1; j < 15; j++) { int num3 = array2[j]; if (num3 > 1 << j) { return 0; } array3[j + 1] = array3[j] + num3; } for (i = 0; i < codeLengthsSize; i++) { int num4 = codeLengths[i]; if (num4 > 0) { array[array3[num4]++] = i; } } if (array3[15] == 1) { HuffmanCode code = new HuffmanCode { BitsUsed = 0, Value = (uint)array[0] }; ReplicateValue(table, 1, num, code); return num; } int num5 = -1; int num6 = num - 1; int num7 = 0; int num8 = 1; int num9 = 1; int num10 = rootBits; int num11 = 1 << num10; i = 0; j = 1; int num12 = 2; while (j <= rootBits) { int num13 = array2[j]; num9 <<= 1; num8 += num9; num9 -= array2[j]; if (num9 < 0) { return 0; } while (num13 > 0) { HuffmanCode code2 = new HuffmanCode { BitsUsed = j, Value = (uint)array[i++] }; ReplicateValue(table.Slice(num7), num12, num11, code2); num7 = GetNextKey(num7, j); num13--; } array2[j] = num13; j++; num12 <<= 1; } Span span = table; int num14 = 0; j = rootBits + 1; num12 = 2; while (j <= 15) { num9 <<= 1; num8 += num9; num9 -= array2[j]; if (num9 < 0) { return 0; } while (array2[j] > 0) { if ((num7 & num6) != num5) { span = span.Slice(num11); num14 += num11; num10 = NextTableBitSize(array2, j, rootBits); num11 = 1 << num10; num += num11; num5 = num7 & num6; table[num5] = new HuffmanCode { BitsUsed = num10 + rootBits, Value = (uint)(num14 - num5) }; } HuffmanCode code3 = new HuffmanCode { BitsUsed = j - rootBits, Value = (uint)array[i++] }; ReplicateValue(span.Slice(num7 >> rootBits), num12, num11, code3); num7 = GetNextKey(num7, j); array2[j]--; } j++; num12 <<= 1; } return num; } private static int CodeRepeatedZeros(int repetitions, Span tokens) { int num = 0; while (repetitions >= 1) { if (repetitions < 3) { for (int i = 0; i < repetitions; i++) { tokens[num].Code = 0; tokens[num].ExtraBits = 0; num++; } break; } if (repetitions < 11) { tokens[num].Code = 17; tokens[num].ExtraBits = (byte)(repetitions - 3); num++; break; } if (repetitions < 139) { tokens[num].Code = 18; tokens[num].ExtraBits = (byte)(repetitions - 11); num++; break; } tokens[num].Code = 18; tokens[num].ExtraBits = 127; num++; repetitions -= 138; } return num; } private static int CodeRepeatedValues(int repetitions, Span tokens, int value, int prevValue) { int num = 0; if (value != prevValue) { tokens[num].Code = (byte)value; tokens[num].ExtraBits = 0; num++; repetitions--; } while (repetitions >= 1) { if (repetitions < 3) { for (int i = 0; i < repetitions; i++) { tokens[num].Code = (byte)value; tokens[num].ExtraBits = 0; num++; } break; } if (repetitions < 7) { tokens[num].Code = 16; tokens[num].ExtraBits = (byte)(repetitions - 3); num++; break; } tokens[num].Code = 16; tokens[num].ExtraBits = 3; num++; repetitions -= 6; } return num; } private static void ConvertBitDepthsToSymbols(HuffmanTreeCode tree) { uint[] array = new uint[16]; int[] array2 = new int[16]; int numSymbols = tree.NumSymbols; for (int i = 0; i < numSymbols; i++) { int num = tree.CodeLengths[i]; array2[num]++; } array2[0] = 0; array[0] = 0u; uint num2 = 0u; for (int j = 1; j <= 15; j++) { num2 = (array[j] = (uint)(num2 + array2[j - 1] << 1)); } for (int k = 0; k < numSymbols; k++) { int num3 = tree.CodeLengths[k]; tree.Codes[k] = (short)ReverseBits(num3, array[num3]++); } } private static void SetBitDepths(Span tree, Span pool, byte[] bitDepths, int level) { if (tree[0].PoolIndexLeft >= 0) { SetBitDepths(pool.Slice(tree[0].PoolIndexLeft), pool, bitDepths, level + 1); SetBitDepths(pool.Slice(tree[0].PoolIndexRight), pool, bitDepths, level + 1); } else { bitDepths[tree[0].Value] = (byte)level; } } private static uint ReverseBits(int numBits, uint bits) { uint num = 0u; int num2 = 0; while (num2 < numBits) { num2 += 4; num |= (uint)(ReversedBits[bits & 0xF] << 16 - num2); bits >>= 4; } return num >> 16 - numBits; } private static int NextTableBitSize(int[] count, int len, int rootBits) { int num = 1 << len - rootBits; while (len < 15) { num -= count[len]; if (num <= 0) { break; } len++; num <<= 1; } return len - rootBits; } private static void ReplicateValue(Span table, int step, int end, HuffmanCode code) { do { end -= step; table[end] = code; } while (end > 0); } private static int GetNextKey(int key, int len) { int num = 1 << len - 1; while ((key & num) != 0) { num >>= 1; } if (num == 0) { return key; } return (key & (num - 1)) + num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool ValuesShouldBeCollapsedToStrideAverage(int a, int b) { return Math.Abs(a - b) < 4; } } internal static class LosslessUtils { private const int PrefixLookupIdxMax = 512; private const int LogLookupIdxMax = 256; private const int ApproxLogMax = 4096; private const int ApproxLogWithCorrectionMax = 65536; private const double Log2Reciprocal = 1.4426950408889634; private static readonly Vector256 AddGreenToBlueAndRedMaskAvx2 = Vector256.Create(1, byte.MaxValue, 1, byte.MaxValue, 5, byte.MaxValue, 5, byte.MaxValue, 9, byte.MaxValue, 9, byte.MaxValue, 13, byte.MaxValue, 13, byte.MaxValue, 17, byte.MaxValue, 17, byte.MaxValue, 21, byte.MaxValue, 21, byte.MaxValue, 25, byte.MaxValue, 25, byte.MaxValue, 29, byte.MaxValue, 29, byte.MaxValue); private static readonly Vector128 AddGreenToBlueAndRedMaskSsse3 = Vector128.Create(1, byte.MaxValue, 1, byte.MaxValue, 5, byte.MaxValue, 5, byte.MaxValue, 9, byte.MaxValue, 9, byte.MaxValue, 13, byte.MaxValue, 13, byte.MaxValue); private static readonly byte AddGreenToBlueAndRedShuffleMask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); private static readonly Vector256 SubtractGreenFromBlueAndRedMaskAvx2 = Vector256.Create(1, byte.MaxValue, 1, byte.MaxValue, 5, byte.MaxValue, 5, byte.MaxValue, 9, byte.MaxValue, 9, byte.MaxValue, 13, byte.MaxValue, 13, byte.MaxValue, 17, byte.MaxValue, 17, byte.MaxValue, 21, byte.MaxValue, 21, byte.MaxValue, 25, byte.MaxValue, 25, byte.MaxValue, 29, byte.MaxValue, 29, byte.MaxValue); private static readonly Vector128 SubtractGreenFromBlueAndRedMaskSsse3 = Vector128.Create(1, byte.MaxValue, 1, byte.MaxValue, 5, byte.MaxValue, 5, byte.MaxValue, 9, byte.MaxValue, 9, byte.MaxValue, 13, byte.MaxValue, 13, byte.MaxValue); private static readonly byte SubtractGreenFromBlueAndRedShuffleMask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); private static readonly Vector128 TransformColorAlphaGreenMask = Vector128.Create(0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue); private static readonly Vector256 TransformColorAlphaGreenMask256 = Vector256.Create(0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue); private static readonly Vector128 TransformColorRedBlueMask = Vector128.Create(byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0); private static readonly Vector256 TransformColorRedBlueMask256 = Vector256.Create(byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0); private static readonly byte TransformColorShuffleMask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); private static readonly Vector128 TransformColorInverseAlphaGreenMask = Vector128.Create(0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue); private static readonly Vector256 TransformColorInverseAlphaGreenMask256 = Vector256.Create(0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue, 0, byte.MaxValue); private static readonly byte TransformColorInverseShuffleMask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); public static int FindMatchLength(ReadOnlySpan array1, ReadOnlySpan array2, int bestLenMatch, int maxLimit) { if (array1[bestLenMatch] != array2[bestLenMatch]) { return 0; } return VectorMismatch(array1, array2, maxLimit); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int VectorMismatch(ReadOnlySpan array1, ReadOnlySpan array2, int length) { int i = 0; ref uint reference = ref MemoryMarshal.GetReference(array1); for (ref uint reference2 = ref MemoryMarshal.GetReference(array2); i < length && Unsafe.Add(ref reference, i) == Unsafe.Add(ref reference2, i); i++) { } return i; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int MaxFindCopyLength(int len) { if (len >= 4095) { return 4095; } return len; } public static int PrefixEncodeBits(int distance, ref int extraBits) { if (distance < 512) { (int, int) tuple = WebpLookupTables.PrefixEncodeCode[distance]; extraBits = tuple.Item2; return tuple.Item1; } return PrefixEncodeBitsNoLut(distance, ref extraBits); } public static int PrefixEncode(int distance, ref int extraBits, ref int extraBitsValue) { if (distance < 512) { (int, int) tuple = WebpLookupTables.PrefixEncodeCode[distance]; extraBits = tuple.Item2; extraBitsValue = WebpLookupTables.PrefixEncodeExtraBitsValue[distance]; return tuple.Item1; } return PrefixEncodeNoLut(distance, ref extraBits, ref extraBitsValue); } public static void AddGreenToBlueAndRed(Span pixelData) { if (Avx2.IsSupported) { int length = pixelData.Length; nint num; for (num = 0; num <= length - 8; num += 8) { ref uint source = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), num); Vector256 vector = Unsafe.As>(ref source).AsByte(); Vector256 right = Avx2.Shuffle(vector, AddGreenToBlueAndRedMaskAvx2); Vector256 vector2 = Avx2.Add(vector, right); Unsafe.As>(ref source) = vector2.AsUInt32(); } if (num != length) { AddGreenToBlueAndRedScalar(pixelData.Slice((int)num)); } } else if (Ssse3.IsSupported) { int length2 = pixelData.Length; nint num2; for (num2 = 0; num2 <= length2 - 4; num2 += 4) { ref uint source2 = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), num2); Vector128 vector3 = Unsafe.As>(ref source2).AsByte(); Vector128 right2 = Ssse3.Shuffle(vector3, AddGreenToBlueAndRedMaskSsse3); Vector128 vector4 = Sse2.Add(vector3, right2); Unsafe.As>(ref source2) = vector4.AsUInt32(); } if (num2 != length2) { AddGreenToBlueAndRedScalar(pixelData.Slice((int)num2)); } } else if (Sse2.IsSupported) { int length3 = pixelData.Length; nint num3; for (num3 = 0; num3 <= length3 - 4; num3 += 4) { ref uint source3 = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), num3); Vector128 vector5 = Unsafe.As>(ref source3).AsByte(); Vector128 vector6 = Sse2.Add(right: Sse2.ShuffleHigh(Sse2.ShuffleLow(Sse2.ShiftRightLogical(vector5.AsUInt16(), 8), AddGreenToBlueAndRedShuffleMask), AddGreenToBlueAndRedShuffleMask).AsByte(), left: vector5.AsByte()); Unsafe.As>(ref source3) = vector6.AsUInt32(); } if (num3 != length3) { AddGreenToBlueAndRedScalar(pixelData.Slice((int)num3)); } } else { AddGreenToBlueAndRedScalar(pixelData); } } private static void AddGreenToBlueAndRedScalar(Span pixelData) { int length = pixelData.Length; for (int i = 0; i < length; i++) { uint num = pixelData[i]; uint num2 = (num >> 8) & 0xFF; uint num3 = num & 0xFF00FF; num3 += (num2 << 16) | num2; num3 &= 0xFF00FF; pixelData[i] = (num & 0xFF00FF00u) | num3; } } public static void SubtractGreenFromBlueAndRed(Span pixelData) { if (Avx2.IsSupported) { int length = pixelData.Length; nint num; for (num = 0; num <= length - 8; num += 8) { ref uint source = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), num); Vector256 vector = Unsafe.As>(ref source).AsByte(); Vector256 right = Avx2.Shuffle(vector, SubtractGreenFromBlueAndRedMaskAvx2); Vector256 vector2 = Avx2.Subtract(vector, right); Unsafe.As>(ref source) = vector2.AsUInt32(); } if (num != length) { SubtractGreenFromBlueAndRedScalar(pixelData.Slice((int)num)); } } else if (Ssse3.IsSupported) { int length2 = pixelData.Length; nint num2; for (num2 = 0; num2 <= length2 - 4; num2 += 4) { ref uint source2 = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), num2); Vector128 vector3 = Unsafe.As>(ref source2).AsByte(); Vector128 right2 = Ssse3.Shuffle(vector3, SubtractGreenFromBlueAndRedMaskSsse3); Vector128 vector4 = Sse2.Subtract(vector3, right2); Unsafe.As>(ref source2) = vector4.AsUInt32(); } if (num2 != length2) { SubtractGreenFromBlueAndRedScalar(pixelData.Slice((int)num2)); } } else if (Sse2.IsSupported) { int length3 = pixelData.Length; nint num3; for (num3 = 0; num3 <= length3 - 4; num3 += 4) { ref uint source3 = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), num3); Vector128 vector5 = Unsafe.As>(ref source3).AsByte(); Vector128 vector6 = Sse2.Subtract(right: Sse2.ShuffleHigh(Sse2.ShuffleLow(Sse2.ShiftRightLogical(vector5.AsUInt16(), 8), SubtractGreenFromBlueAndRedShuffleMask), SubtractGreenFromBlueAndRedShuffleMask).AsByte(), left: vector5.AsByte()); Unsafe.As>(ref source3) = vector6.AsUInt32(); } if (num3 != length3) { SubtractGreenFromBlueAndRedScalar(pixelData.Slice((int)num3)); } } else { SubtractGreenFromBlueAndRedScalar(pixelData); } } private static void SubtractGreenFromBlueAndRedScalar(Span pixelData) { int length = pixelData.Length; for (int i = 0; i < length; i++) { uint num = pixelData[i]; uint num2 = (num >> 8) & 0xFF; uint num3 = (((num >> 16) & 0xFF) - num2) & 0xFF; uint num4 = ((num & 0xFF) - num2) & 0xFF; pixelData[i] = (num & 0xFF00FF00u) | (num3 << 16) | num4; } } public static void ColorIndexInverseTransform(Vp8LTransform transform, Span pixelData) { int num = 8 >> transform.Bits; int xSize = transform.XSize; int ySize = transform.YSize; Span span = transform.Data.GetSpan(); int num2 = 0; if (num < 8) { int num3 = (1 << transform.Bits) - 1; int num4 = (1 << num) - 1; uint[] array = new uint[xSize * ySize]; int num5 = 0; for (int i = 0; i < ySize; i++) { uint num6 = 0u; for (int j = 0; j < xSize; j++) { if ((j & num3) == 0) { num6 = GetArgbIndex(pixelData[num5++]); } array[num2++] = span[(int)(num6 & num4)]; num6 >>= num; } } array.AsSpan().CopyTo(pixelData); return; } for (int k = 0; k < ySize; k++) { for (int l = 0; l < xSize; l++) { uint argbIndex = GetArgbIndex(pixelData[num2]); pixelData[num2] = span[(int)argbIndex]; num2++; } } } public static void ColorSpaceInverseTransform(Vp8LTransform transform, Span pixelData) { int xSize = transform.XSize; int ySize = transform.YSize; int num = 1 << transform.Bits; int num2 = num - 1; int num3 = xSize & ~num2; int num4 = xSize - num3; int num5 = SubSampleSize(xSize, transform.Bits); int num6 = 0; int num7 = (num6 >> transform.Bits) * num5; Span span = transform.Data.GetSpan(); int i = 0; while (num6 < ySize) { int index = num7; Vp8LMultipliers m = default(Vp8LMultipliers); int num8 = i + num3; int num9 = i + xSize; for (; i < num8; i += num) { ColorCodeToMultipliers(span[index++], ref m); TransformColorInverse(m, pixelData.Slice(i, num)); } if (i < num9) { ColorCodeToMultipliers(span[index], ref m); TransformColorInverse(m, pixelData.Slice(i, num4)); i += num4; } num6++; if ((num6 & num2) == 0) { num7 += num5; } } } public static void TransformColor(Vp8LMultipliers m, Span pixelData, int numPixels) { if (Avx2.IsSupported && numPixels >= 8) { Vector256 vector = MkCst32(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector256 vector2 = MkCst32(Cst5b(m.RedToBlue), 0); nint num; for (num = 0; num <= numPixels - 8; num += 8) { ref uint source = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), num); Vector256 vector3 = Unsafe.As>(ref source); Vector256 vector4 = Avx2.Subtract(right: Avx2.And(Avx2.Add(right: Avx2.MultiplyHigh(Avx2.ShuffleHigh(Avx2.ShuffleLow(Avx2.And(vector3.AsByte(), TransformColorAlphaGreenMask256).AsInt16(), TransformColorShuffleMask).AsInt16(), TransformColorShuffleMask).AsInt16(), vector.AsInt16()).AsByte(), left: Avx2.ShiftRightLogical(Avx2.MultiplyHigh(Avx2.ShiftLeftLogical(vector3.AsInt16(), 8).AsInt16(), vector2.AsInt16()).AsInt32(), 16).AsByte()), TransformColorRedBlueMask256), left: vector3.AsByte()); Unsafe.As>(ref source) = vector4.AsUInt32(); } if (num != numPixels) { TransformColorScalar(m, pixelData.Slice((int)num), numPixels - (int)num); } } else if (Sse2.IsSupported) { Vector128 vector5 = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector128 vector6 = MkCst16(Cst5b(m.RedToBlue), 0); nint num2; for (num2 = 0; num2 <= numPixels - 4; num2 += 4) { ref uint source2 = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), num2); Vector128 vector7 = Unsafe.As>(ref source2); Vector128 vector8 = Sse2.Subtract(right: Sse2.And(Sse2.Add(right: Sse2.MultiplyHigh(Sse2.ShuffleHigh(Sse2.ShuffleLow(Sse2.And(vector7.AsByte(), TransformColorAlphaGreenMask).AsInt16(), TransformColorShuffleMask).AsInt16(), TransformColorShuffleMask).AsInt16(), vector5.AsInt16()).AsByte(), left: Sse2.ShiftRightLogical(Sse2.MultiplyHigh(Sse2.ShiftLeftLogical(vector7.AsInt16(), 8).AsInt16(), vector6.AsInt16()).AsInt32(), 16).AsByte()), TransformColorRedBlueMask), left: vector7.AsByte()); Unsafe.As>(ref source2) = vector8.AsUInt32(); } if (num2 != numPixels) { TransformColorScalar(m, pixelData.Slice((int)num2), numPixels - (int)num2); } } else { TransformColorScalar(m, pixelData, numPixels); } } private static void TransformColorScalar(Vp8LMultipliers m, Span data, int numPixels) { for (int i = 0; i < numPixels; i++) { uint num = data[i]; sbyte color = U32ToS8(num >> 8); sbyte b = U32ToS8(num >> 16); int num2 = b & 0xFF; int num3 = (int)(num & 0xFF); num2 -= ColorTransformDelta((sbyte)m.GreenToRed, color); num2 &= 0xFF; num3 -= ColorTransformDelta((sbyte)m.GreenToBlue, color); num3 -= ColorTransformDelta((sbyte)m.RedToBlue, b); num3 &= 0xFF; data[i] = (num & 0xFF00FF00u) | (uint)(num2 << 16) | (uint)num3; } } public static void TransformColorInverse(Vp8LMultipliers m, Span pixelData) { if (Avx2.IsSupported && pixelData.Length >= 8) { Vector256 vector = MkCst32(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector256 vector2 = MkCst32(Cst5b(m.RedToBlue), 0); nint num; for (num = 0; num <= pixelData.Length - 8; num += 8) { ref uint source = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), num); Vector256 vector3 = Unsafe.As>(ref source); Vector256 vector4 = Avx2.And(vector3.AsByte(), TransformColorInverseAlphaGreenMask256); Vector256 vector5 = Avx2.ShiftLeftLogical(Avx2.Add(right: Avx2.MultiplyHigh(Avx2.ShuffleHigh(Avx2.ShuffleLow(vector4.AsInt16(), TransformColorInverseShuffleMask).AsInt16(), TransformColorInverseShuffleMask).AsInt16(), vector.AsInt16()).AsByte(), left: vector3.AsByte()).AsInt16(), 8); Vector256 vector6 = Avx2.Or(Avx2.ShiftRightLogical(Avx2.Add(Avx2.ShiftRightLogical(Avx2.MultiplyHigh(vector5, vector2.AsInt16()).AsInt32(), 8).AsByte(), vector5.AsByte()).AsInt16(), 8).AsByte(), vector4); Unsafe.As>(ref source) = vector6.AsUInt32(); } if (num != pixelData.Length) { TransformColorInverseScalar(m, pixelData.Slice((int)num)); } } else if (Sse2.IsSupported) { Vector128 vector7 = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector128 vector8 = MkCst16(Cst5b(m.RedToBlue), 0); nint num2; for (num2 = 0; num2 <= pixelData.Length - 4; num2 += 4) { ref uint source2 = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), num2); Vector128 vector9 = Unsafe.As>(ref source2); Vector128 vector10 = Sse2.And(vector9.AsByte(), TransformColorInverseAlphaGreenMask); Vector128 vector11 = Sse2.ShiftLeftLogical(Sse2.Add(right: Sse2.MultiplyHigh(Sse2.ShuffleHigh(Sse2.ShuffleLow(vector10.AsInt16(), TransformColorInverseShuffleMask).AsInt16(), TransformColorInverseShuffleMask).AsInt16(), vector7.AsInt16()).AsByte(), left: vector9.AsByte()).AsInt16(), 8); Vector128 vector12 = Sse2.Or(Sse2.ShiftRightLogical(Sse2.Add(Sse2.ShiftRightLogical(Sse2.MultiplyHigh(vector11, vector8.AsInt16()).AsInt32(), 8).AsByte(), vector11.AsByte()).AsInt16(), 8).AsByte(), vector10); Unsafe.As>(ref source2) = vector12.AsUInt32(); } if (num2 != pixelData.Length) { TransformColorInverseScalar(m, pixelData.Slice((int)num2)); } } else { TransformColorInverseScalar(m, pixelData); } } private static void TransformColorInverseScalar(Vp8LMultipliers m, Span pixelData) { for (int i = 0; i < pixelData.Length; i++) { uint num = pixelData[i]; sbyte color = (sbyte)(num >> 8); int num2 = (int)((num >> 16) & 0xFF); int num3 = (int)(num & 0xFF); num2 += ColorTransformDelta((sbyte)m.GreenToRed, color); num2 &= 0xFF; num3 += ColorTransformDelta((sbyte)m.GreenToBlue, color); num3 += ColorTransformDelta((sbyte)m.RedToBlue, (sbyte)num2); num3 &= 0xFF; pixelData[i] = (num & 0xFF00FF00u) | (uint)(num2 << 16) | (uint)num3; } } public unsafe static void PredictorInverseTransform(Vp8LTransform transform, Span pixelData, Span outputSpan) { fixed (uint* ptr = pixelData) { fixed (uint* ptr2 = outputSpan) { uint* ptr3 = ptr; uint* ptr4 = ptr2; int xSize = transform.XSize; Span span = transform.Data.GetSpan(); PredictorAdd0(ptr3, 1, ptr4); PredictorAdd1(ptr3 + 1, xSize - 1, ptr4 + 1); ptr3 += xSize; ptr4 += xSize; int num = 1; int ySize = transform.YSize; int num2 = 1 << transform.Bits; int num3 = num2 - 1; int num4 = SubSampleSize(xSize, transform.Bits); int num5 = (num >> transform.Bits) * num4; Span scratch = stackalloc short[8]; while (num < ySize) { int num6 = num5; int num7 = 1; PredictorAdd2(ptr3, ptr4 - xSize, 1, ptr4); while (num7 < xSize) { uint num8 = (span[num6++] >> 8) & 0xF; int num9 = (num7 & ~num3) + num2; if (num9 > xSize) { num9 = xSize; } switch (num8) { case 0u: PredictorAdd0(ptr3 + num7, num9 - num7, ptr4 + num7); break; case 1u: PredictorAdd1(ptr3 + num7, num9 - num7, ptr4 + num7); break; case 2u: PredictorAdd2(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; case 3u: PredictorAdd3(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; case 4u: PredictorAdd4(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; case 5u: PredictorAdd5(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; case 6u: PredictorAdd6(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; case 7u: PredictorAdd7(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; case 8u: PredictorAdd8(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; case 9u: PredictorAdd9(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; case 10u: PredictorAdd10(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; case 11u: PredictorAdd11(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7, scratch); break; case 12u: PredictorAdd12(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; case 13u: PredictorAdd13(ptr3 + num7, ptr4 + num7 - xSize, num9 - num7, ptr4 + num7); break; } num7 = num9; } ptr3 += xSize; ptr4 += xSize; num++; if ((num & num3) == 0) { num5 += num4; } } } } outputSpan.CopyTo(pixelData); } public static void ExpandColorMap(int numColors, Span transformData, Span newColorMap) { newColorMap[0] = transformData[0]; Span span = MemoryMarshal.Cast(transformData); Span span2 = MemoryMarshal.Cast(newColorMap); int num = 4 * numColors; int i; for (i = 4; i < num; i++) { span2[i] = (byte)((span[i] + span2[i - 4]) & 0xFF); } for (int num2 = 4 * newColorMap.Length; i < num2; i++) { span2[i] = 0; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint SubPixels(uint a, uint b) { uint num = 16711935 + (a & 0xFF00FF00u) - (b & 0xFF00FF00u); uint num2 = (uint)(-16711936 + (int)(a & 0xFF00FF)) - (b & 0xFF00FF); return (num & 0xFF00FF00u) | (num2 & 0xFF00FF); } public static void BundleColorMap(Span row, int width, int xBits, Span dst) { if (xBits > 0) { int num = 1 << 3 - xBits; int num2 = (1 << xBits) - 1; uint num3 = 4278190080u; for (int i = 0; i < width; i++) { int num4 = i & num2; if (num4 == 0) { num3 = 4278190080u; } num3 |= (uint)(row[i] << 8 + num * num4); dst[i >> xBits] = num3; } } else { for (int i = 0; i < width; i++) { dst[i] = (uint)(0xFF000000u | (row[i] << 8)); } } } public static float CombinedShannonEntropy(Span x, Span y) { if (Avx2.IsSupported) { double num = 0.0; Vector256 source = Vector256.Zero; ref int reference = ref MemoryMarshal.GetReference(x); ref int reference2 = ref MemoryMarshal.GetReference(y); Vector256 vector = Vector256.Zero; Vector256 vector2 = Vector256.Zero; ref int reference3 = ref Unsafe.As, int>(ref source); for (nint num2 = 0; num2 < 256; num2 += 8) { Vector256 vector3 = Unsafe.As>(ref Unsafe.Add(ref reference, num2)); Vector256 right = Unsafe.As>(ref Unsafe.Add(ref reference2, num2)); if (Avx2.MoveMask(Avx2.CompareEqual(vector3, Vector256.Zero).AsByte()) != -1) { Vector256 vector4 = Avx2.Add(vector3, right); vector = Avx2.Add(vector, vector4); vector2 = Avx2.Add(vector2, vector3); Unsafe.As>(ref reference3) = vector4; if (reference3 != 0) { num -= (double)FastSLog2((uint)reference3); if (Unsafe.Add(ref reference, num2) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference, num2)); } } if (Unsafe.Add(ref reference3, 1) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference3, 1)); if (Unsafe.Add(ref reference, num2 + 1) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference, num2 + 1)); } } if (Unsafe.Add(ref reference3, 2) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference3, 2)); if (Unsafe.Add(ref reference, num2 + 2) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference, num2 + 2)); } } if (Unsafe.Add(ref reference3, 3) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference3, 3)); if (Unsafe.Add(ref reference, num2 + 3) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference, num2 + 3)); } } if (Unsafe.Add(ref reference3, 4) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference3, 4)); if (Unsafe.Add(ref reference, num2 + 4) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference, num2 + 4)); } } if (Unsafe.Add(ref reference3, 5) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference3, 5)); if (Unsafe.Add(ref reference, num2 + 5) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference, num2 + 5)); } } if (Unsafe.Add(ref reference3, 6) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference3, 6)); if (Unsafe.Add(ref reference, num2 + 6) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference, num2 + 6)); } } if (Unsafe.Add(ref reference3, 7) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference3, 7)); if (Unsafe.Add(ref reference, num2 + 7) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference, num2 + 7)); } } } else { vector = Avx2.Add(vector, right); if (Unsafe.Add(ref reference2, num2) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference2, num2)); } if (Unsafe.Add(ref reference2, num2 + 1) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference2, num2 + 1)); } if (Unsafe.Add(ref reference2, num2 + 2) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference2, num2 + 2)); } if (Unsafe.Add(ref reference2, num2 + 3) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference2, num2 + 3)); } if (Unsafe.Add(ref reference2, num2 + 4) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference2, num2 + 4)); } if (Unsafe.Add(ref reference2, num2 + 5) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference2, num2 + 5)); } if (Unsafe.Add(ref reference2, num2 + 6) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference2, num2 + 6)); } if (Unsafe.Add(ref reference2, num2 + 7) != 0) { num -= (double)FastSLog2((uint)Unsafe.Add(ref reference2, num2 + 7)); } } } int v = Numerics.ReduceSum(vector2); int v2 = Numerics.ReduceSum(vector); num += (double)(FastSLog2((uint)v) + FastSLog2((uint)v2)); return (float)num; } double num3 = 0.0; uint num4 = 0u; uint num5 = 0u; for (int i = 0; i < 256; i++) { uint num6 = (uint)x[i]; if (num6 != 0) { uint num7 = num6 + (uint)y[i]; num4 += num6; num3 -= (double)FastSLog2(num6); num5 += num7; num3 -= (double)FastSLog2(num7); } else if (y[i] != 0) { num5 += (uint)y[i]; num3 -= (double)FastSLog2((uint)y[i]); } } num3 += (double)(FastSLog2(num4) + FastSLog2(num5)); return (float)num3; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte TransformColorRed(sbyte greenToRed, uint argb) { sbyte color = U32ToS8(argb >> 8); return (byte)(((int)(argb >> 16) - ColorTransformDelta(greenToRed, color)) & 0xFF); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte TransformColorBlue(sbyte greenToBlue, sbyte redToBlue, uint argb) { sbyte color = U32ToS8(argb >> 8); sbyte color2 = U32ToS8(argb >> 16); return (byte)(((int)(argb & 0xFF) - ColorTransformDelta(greenToBlue, color) - ColorTransformDelta(redToBlue, color2)) & 0xFF); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float FastLog2(uint v) { if (v >= 256) { return FastLog2Slow(v); } return WebpLookupTables.Log2Table[v]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float FastSLog2(uint v) { if (v >= 256) { return FastSLog2Slow(v); } return WebpLookupTables.SLog2Table[v]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ColorCodeToMultipliers(uint colorCode, ref Vp8LMultipliers m) { m.GreenToRed = (byte)(colorCode & 0xFF); m.GreenToBlue = (byte)((colorCode >> 8) & 0xFF); m.RedToBlue = (byte)((colorCode >> 16) & 0xFF); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int NearLosslessBits(int nearLosslessQuality) { return 5 - nearLosslessQuality / 20; } private static float FastSLog2Slow(uint v) { if (v < 65536) { int num = 0; uint num2 = 1u; float num3 = v; uint num4 = v; do { num++; v >>= 1; num2 <<= 1; } while (v >= 256); int num5 = (int)(23 * (num4 & (num2 - 1)) >> 4); return num3 * (WebpLookupTables.Log2Table[v] + (float)num) + (float)num5; } return (float)(1.4426950408889634 * (double)v * Math.Log(v)); } private static float FastLog2Slow(uint v) { if (v < 65536) { int num = 0; uint num2 = 1u; uint num3 = v; do { num++; v >>= 1; num2 <<= 1; } while (v >= 256); double num4 = WebpLookupTables.Log2Table[v] + (float)num; if (num3 >= 4096) { int num5 = (int)(23 * (num3 & (num2 - 1))) >> 4; num4 += (double)num5 / (double)num3; } return (float)num4; } return (float)(1.4426950408889634 * Math.Log(v)); } private static int PrefixEncodeBitsNoLut(int distance, ref int extraBits) { int num = Numerics.Log2((uint)(--distance)); int num2 = (distance >> num - 1) & 1; extraBits = num - 1; return 2 * num + num2; } private static int PrefixEncodeNoLut(int distance, ref int extraBits, ref int extraBitsValue) { int num = Numerics.Log2((uint)(--distance)); int num2 = (distance >> num - 1) & 1; extraBits = num - 1; extraBitsValue = distance & ((1 << extraBits) - 1); return 2 * num + num2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd0(uint* input, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { output[i] = AddPixels(input[i], 4278190080u); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd1(uint* input, int numberOfPixels, uint* output) { uint b = output[-1]; for (int i = 0; i < numberOfPixels; i++) { b = (output[i] = AddPixels(input[i], b)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd2(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor2(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd3(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor3(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd4(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor4(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd5(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor5(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd6(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor6(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd7(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor7(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd8(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor8(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd9(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor9(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd10(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor10(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd11(uint* input, uint* upper, int numberOfPixels, uint* output, Span scratch) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor11(output[i - 1], upper + i, scratch); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd12(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor12(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void PredictorAdd13(uint* input, uint* upper, int numberOfPixels, uint* output) { for (int i = 0; i < numberOfPixels; i++) { uint b = Predictor13(output[i - 1], upper + i); output[i] = AddPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor2(uint left, uint* top) { return *top; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor3(uint left, uint* top) { return top[1]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor4(uint left, uint* top) { return top[-1]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor5(uint left, uint* top) { return Average3(left, *top, top[1]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor6(uint left, uint* top) { return Average2(left, top[-1]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor7(uint left, uint* top) { return Average2(left, *top); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor8(uint left, uint* top) { return Average2(top[-1], *top); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor9(uint left, uint* top) { return Average2(*top, top[1]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor10(uint left, uint* top) { return Average4(left, top[-1], *top, top[1]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor11(uint left, uint* top, Span scratch) { return Select(*top, left, top[-1], scratch); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor12(uint left, uint* top) { return ClampedAddSubtractFull(left, *top, top[-1]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static uint Predictor13(uint left, uint* top) { return ClampedAddSubtractHalf(left, *top, top[-1]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub0(uint* input, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { output[i] = SubPixels(input[i], 4278190080u); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub1(uint* input, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { output[i] = SubPixels(input[i], input[i - 1]); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub2(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor2(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub3(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor3(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub4(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor4(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub5(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor5(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub6(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor6(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub7(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor7(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub8(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor8(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub9(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor9(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub10(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor10(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub11(uint* input, uint* upper, int numPixels, uint* output, Span scratch) { for (int i = 0; i < numPixels; i++) { uint b = Predictor11(input[i - 1], upper + i, scratch); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub12(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor12(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static void PredictorSub13(uint* input, uint* upper, int numPixels, uint* output) { for (int i = 0; i < numPixels; i++) { uint b = Predictor13(input[i - 1], upper + i); output[i] = SubPixels(input[i], b); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SubSampleSize(int size, int samplingBits) { return size + (1 << samplingBits) - 1 >> samplingBits; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint AddPixels(uint a, uint b) { uint num = (a & 0xFF00FF00u) + (b & 0xFF00FF00u); uint num2 = (a & 0xFF00FF) + (b & 0xFF00FF); return (num & 0xFF00FF00u) | (num2 & 0xFF00FF); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short Cst5b(int x) { return (short)((short)(x << 8) >> 5); } private static uint ClampedAddSubtractFull(uint c0, uint c1, uint c2) { if (Sse2.IsSupported) { Vector128 vector = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c0).AsByte(), Vector128.Zero); Vector128 vector2 = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c1).AsByte(), Vector128.Zero); Vector128 obj = Sse2.Subtract(right: Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c2).AsByte(), Vector128.Zero).AsInt16(), left: Sse2.Add(vector.AsInt16(), vector2.AsInt16())); return Sse2.ConvertToUInt32(Sse2.PackUnsignedSaturate(obj, obj).AsUInt32()); } int num = AddSubtractComponentFull((int)(c0 >> 24), (int)(c1 >> 24), (int)(c2 >> 24)); int num2 = AddSubtractComponentFull((int)((c0 >> 16) & 0xFF), (int)((c1 >> 16) & 0xFF), (int)((c2 >> 16) & 0xFF)); int num3 = AddSubtractComponentFull((int)((c0 >> 8) & 0xFF), (int)((c1 >> 8) & 0xFF), (int)((c2 >> 8) & 0xFF)); int num4 = AddSubtractComponentFull((int)(c0 & 0xFF), (int)(c1 & 0xFF), (int)(c2 & 0xFF)); return (uint)((num << 24) | (num2 << 16) | (num3 << 8) | num4); } private static uint ClampedAddSubtractHalf(uint c0, uint c1, uint c2) { if (Sse2.IsSupported) { Vector128 vector = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c0).AsByte(), Vector128.Zero); Vector128 vector2 = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c1).AsByte(), Vector128.Zero); Vector128 vector3 = Sse2.UnpackLow(Sse2.ConvertScalarToVector128UInt32(c2).AsByte(), Vector128.Zero); Vector128 vector4 = Sse2.ShiftRightLogical(Sse2.Add(vector2.AsInt16(), vector.AsInt16()), 1); Vector128 left = Sse2.Subtract(vector4, vector3.AsInt16()); Vector128 right = Sse2.CompareGreaterThan(vector3.AsInt16(), vector4.AsInt16()); Vector128 right2 = Sse2.ShiftRightArithmetic(Sse2.Subtract(left, right), 1); Vector128 vector5 = Sse2.Add(vector4, right2).AsInt16(); return Sse2.ConvertToUInt32(Sse2.PackUnsignedSaturate(vector5, vector5).AsUInt32()); } uint num = Average2(c0, c1); int num2 = AddSubtractComponentHalf((int)(num >> 24), (int)(c2 >> 24)); int num3 = AddSubtractComponentHalf((int)((num >> 16) & 0xFF), (int)((c2 >> 16) & 0xFF)); int num4 = AddSubtractComponentHalf((int)((num >> 8) & 0xFF), (int)((c2 >> 8) & 0xFF)); int num5 = AddSubtractComponentHalf((int)(num & 0xFF), (int)(c2 & 0xFF)); return (uint)((num2 << 24) | (num3 << 16) | (num4 << 8) | num5); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int AddSubtractComponentHalf(int a, int b) { return (int)Clip255((uint)(a + (a - b) / 2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int AddSubtractComponentFull(int a, int b, int c) { return (int)Clip255((uint)(a + b - c)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Clip255(uint a) { if (a >= 256) { return ~a >> 24; } return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 MkCst16(int hi, int lo) { return Vector128.Create((hi << 16) | (lo & 0xFFFF)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector256 MkCst32(int hi, int lo) { return Vector256.Create((hi << 16) | (lo & 0xFFFF)); } private unsafe static uint Select(uint a, uint b, uint c, Span scratch) { if (Sse2.IsSupported) { Span span = scratch; fixed (short* address = span) { Vector128 vector = Sse2.ConvertScalarToVector128UInt32(a).AsByte(); Vector128 vector2 = Sse2.ConvertScalarToVector128UInt32(b).AsByte(); Vector128 vector3 = Sse2.ConvertScalarToVector128UInt32(c).AsByte(); Vector128 left = Sse2.SubtractSaturate(vector, vector3); Vector128 right = Sse2.SubtractSaturate(vector3, vector); Vector128 left2 = Sse2.SubtractSaturate(vector2, vector3); Vector128 right2 = Sse2.SubtractSaturate(vector3, vector2); Vector128 left3 = Sse2.Or(left, right); Vector128 left4 = Sse2.Or(left2, right2); Vector128 source = Sse2.Subtract(right: Sse2.UnpackLow(left3, Vector128.Zero).AsUInt16(), left: Sse2.UnpackLow(left4, Vector128.Zero).AsUInt16()); Sse2.Store((ushort*)address, source); if (span[3] + span[2] + span[1] + span[0] > 0) { return b; } return a; } } if (Sub3((int)(a >> 24), (int)(b >> 24), (int)(c >> 24)) + Sub3((int)((a >> 16) & 0xFF), (int)((b >> 16) & 0xFF), (int)((c >> 16) & 0xFF)) + Sub3((int)((a >> 8) & 0xFF), (int)((b >> 8) & 0xFF), (int)((c >> 8) & 0xFF)) + Sub3((int)(a & 0xFF), (int)(b & 0xFF), (int)(c & 0xFF)) > 0) { return b; } return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Sub3(int a, int b, int c) { int value = b - c; int value2 = a - c; return Math.Abs(value) - Math.Abs(value2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Average2(uint a0, uint a1) { return (((a0 ^ a1) & 0xFEFEFEFEu) >> 1) + (a0 & a1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Average3(uint a0, uint a1, uint a2) { return Average2(Average2(a0, a2), a1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Average4(uint a0, uint a1, uint a2, uint a3) { return Average2(Average2(a0, a1), Average2(a2, a3)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint GetArgbIndex(uint idx) { return (idx >> 8) & 0xFF; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int ColorTransformDelta(sbyte colorPred, sbyte color) { return colorPred * color >> 5; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static sbyte U32ToS8(uint v) { return (sbyte)(v & 0xFF); } } internal static class NearLosslessEnc { private const int MinDimForNearLossless = 64; public static void ApplyNearLossless(int xSize, int ySize, int quality, Span argbSrc, Span argbDst, int stride) { uint[] array = new uint[xSize * 3]; int num = LosslessUtils.NearLosslessBits(quality); if ((xSize < 64 && ySize < 64) || ySize < 3) { for (int i = 0; i < ySize; i++) { argbSrc.Slice(i * stride, xSize).CopyTo(argbDst.Slice(i * xSize, xSize)); } return; } NearLossless(xSize, ySize, argbSrc, stride, num, array, argbDst); for (int num2 = num - 1; num2 != 0; num2--) { NearLossless(xSize, ySize, argbDst, xSize, num2, array, argbDst); } } private static void NearLossless(int xSize, int ySize, Span argbSrc, int stride, int limitBits, Span copyBuffer, Span argbDst) { int limit = 1 << limitBits; Span span = copyBuffer; Span span2 = copyBuffer.Slice(xSize, xSize); Span span3 = copyBuffer.Slice(xSize * 2, xSize); argbSrc.Slice(0, xSize).CopyTo(span2); argbSrc.Slice(xSize, xSize).CopyTo(span3); int num = 0; int num2 = 0; for (int i = 0; i < ySize; i++) { if (i == 0 || i == ySize - 1) { argbSrc.Slice(num, xSize).CopyTo(argbDst.Slice(num2, xSize)); } else { argbSrc.Slice(num + stride, xSize).CopyTo(span3); argbDst[num2] = argbSrc[num]; argbDst[num2 + xSize - 1] = argbSrc[num + xSize - 1]; for (int j = 1; j < xSize - 1; j++) { if (IsSmooth(span, span2, span3, j, limit)) { argbDst[num2 + j] = span2[j]; } else { argbDst[num2 + j] = ClosestDiscretizedArgb(span2[j], limitBits); } } } Span span4 = span; span = span2; span2 = span3; span3 = span4; num += stride; num2 += xSize; } } private static uint ClosestDiscretizedArgb(uint a, int bits) { return (FindClosestDiscretized(a >> 24, bits) << 24) | (FindClosestDiscretized((a >> 16) & 0xFF, bits) << 16) | (FindClosestDiscretized((a >> 8) & 0xFF, bits) << 8) | FindClosestDiscretized(a & 0xFF, bits); } private static uint FindClosestDiscretized(uint a, int bits) { uint num = (uint)((1 << bits) - 1); uint num2 = a + (num >> 1) + ((a >> bits) & 1); if (num2 > 255) { return 255u; } return num2 & ~num; } private static bool IsSmooth(Span prevRow, Span currRow, Span nextRow, int ix, int limit) { if (IsNear(currRow[ix], currRow[ix - 1], limit) && IsNear(currRow[ix], currRow[ix + 1], limit) && IsNear(currRow[ix], prevRow[ix], limit)) { return IsNear(currRow[ix], nextRow[ix], limit); } return false; } private static bool IsNear(uint a, uint b, int limit) { for (int i = 0; i < 4; i++) { int num = (int)(((a >> i * 8) & 0xFF) - ((b >> i * 8) & 0xFF)); if (num >= limit || num <= -limit) { return false; } } return true; } } [DebuggerDisplay("Mode: {Mode}, Len: {Len}, BgraOrDistance: {BgraOrDistance}")] internal sealed class PixOrCopy { public PixOrCopyMode Mode { get; set; } public ushort Len { get; set; } public uint BgraOrDistance { get; set; } public static PixOrCopy CreateCacheIdx(int idx) { return new PixOrCopy { Mode = PixOrCopyMode.CacheIdx, BgraOrDistance = (uint)idx, Len = 1 }; } public static PixOrCopy CreateLiteral(uint bgra) { return new PixOrCopy { Mode = PixOrCopyMode.Literal, BgraOrDistance = bgra, Len = 1 }; } public static PixOrCopy CreateCopy(uint distance, ushort len) { return new PixOrCopy { Mode = PixOrCopyMode.Copy, BgraOrDistance = distance, Len = len }; } public uint Literal(int component) { return (BgraOrDistance >> component * 8) & 0xFF; } public uint CacheIdx() { return BgraOrDistance; } public ushort Length() { return Len; } public uint Distance() { return BgraOrDistance; } public bool IsLiteral() { return Mode == PixOrCopyMode.Literal; } public bool IsCacheIdx() { return Mode == PixOrCopyMode.CacheIdx; } public bool IsCopy() { return Mode == PixOrCopyMode.Copy; } } internal enum PixOrCopyMode : byte { Literal, CacheIdx, Copy, None } internal static class PredictorEncoder { private static readonly sbyte[][] Offset = new sbyte[8][] { new sbyte[2] { 0, -1 }, new sbyte[2] { 0, 1 }, new sbyte[2] { -1, 0 }, new sbyte[2] { 1, 0 }, new sbyte[2] { -1, -1 }, new sbyte[2] { -1, 1 }, new sbyte[2] { 1, -1 }, new sbyte[2] { 1, 1 } }; private const int GreenRedToBlueNumAxis = 8; private const int GreenRedToBlueMaxIters = 7; private const float MaxDiffCost = 1E+30f; private const uint MaskAlpha = 4278190080u; private const float SpatialPredictorBias = 15f; private const int PredLowEffort = 11; private static ReadOnlySpan DeltaLut => new sbyte[7] { 16, 16, 8, 4, 2, 2, 2 }; public static void ResidualImage(int width, int height, int bits, Span bgra, Span bgraScratch, Span image, int[][] histoArgb, int[][] bestHisto, bool nearLossless, int nearLosslessQuality, WebpTransparentColorMode transparentColorMode, bool usedSubtractGreen, bool lowEffort) { int num = LosslessUtils.SubSampleSize(width, bits); int num2 = LosslessUtils.SubSampleSize(height, bits); int maxQuantization = 1 << LosslessUtils.NearLosslessBits(nearLosslessQuality); Span scratch = stackalloc short[8]; int[][] array = new int[4][]; for (int i = 0; i < 4; i++) { array[i] = new int[256]; } if (lowEffort) { for (int j = 0; j < num * num2; j++) { image[j] = 4278192896u; } } else { for (int k = 0; k < num2; k++) { for (int l = 0; l < num; l++) { int bestPredictorForTile = GetBestPredictorForTile(width, height, l, k, bits, array, bgraScratch, bgra, histoArgb, bestHisto, maxQuantization, transparentColorMode, usedSubtractGreen, nearLossless, image, scratch); image[k * num + l] = (uint)(0xFF000000u | (bestPredictorForTile << 8)); } } } CopyImageWithPrediction(width, height, bits, image, bgraScratch, bgra, maxQuantization, transparentColorMode, usedSubtractGreen, nearLossless, lowEffort); } public static void ColorSpaceTransform(int width, int height, int bits, int quality, Span bgra, Span image, Span scratch) { int num = 1 << bits; int num2 = LosslessUtils.SubSampleSize(width, bits); int num3 = LosslessUtils.SubSampleSize(height, bits); int[] array = new int[256]; int[] array2 = new int[256]; Vp8LMultipliers vp8LMultipliers = default(Vp8LMultipliers); Vp8LMultipliers m = default(Vp8LMultipliers); for (int i = 0; i < num3; i++) { for (int j = 0; j < num2; j++) { int num4 = j * num; int num5 = i * num; int min = GetMin(num4 + num, width); int min2 = GetMin(num5 + num, height); int num6 = i * num2 + j; if (i != 0) { LosslessUtils.ColorCodeToMultipliers(image[num6 - num2], ref m); } vp8LMultipliers = GetBestColorTransformForTile(j, i, bits, vp8LMultipliers, m, quality, width, height, array, array2, bgra, scratch); image[num6] = MultipliersToColorCode(vp8LMultipliers); CopyTileWithColorTransform(width, height, num4, num5, num, vp8LMultipliers, bgra); for (int k = num5; k < min2; k++) { int l = k * width + num4; for (int num7 = l + min - num4; l < num7; l++) { uint num8 = bgra[l]; if ((l < 2 || num8 != bgra[l - 2] || num8 != bgra[l - 1]) && (l < width + 2 || bgra[l - 2] != bgra[l - width - 2] || bgra[l - 1] != bgra[l - width - 1] || num8 != bgra[l - width])) { array[(num8 >> 16) & 0xFF]++; array2[num8 & 0xFF]++; } } } } } } private static int GetBestPredictorForTile(int width, int height, int tileX, int tileY, int bits, int[][] accumulated, Span argbScratch, Span argb, int[][] histoArgb, int[][] bestHisto, int maxQuantization, WebpTransparentColorMode transparentColorMode, bool usedSubtractGreen, bool nearLossless, Span modes, Span scratch) { int num = tileX << bits; int num2 = tileY << bits; int a = 1 << bits; int min = GetMin(a, height - num2); int min2 = GetMin(a, width - num); int num3 = ((num > 0) ? 1 : 0); int num4 = num - num3; int width2 = min2 + num3 + ((min2 < width) ? 1 : 0) - num; int num5 = LosslessUtils.SubSampleSize(width, bits); int num6 = (int)((tileX > 0) ? ((modes[tileY * num5 + tileX - 1] >> 8) & 0xFF) : 255); int num7 = (int)((tileY > 0) ? ((modes[(tileY - 1) * num5 + tileX] >> 8) & 0xFF) : 255); Span span = argbScratch; Span span2 = span.Slice(width + 1); Span maxDiffs = MemoryMarshal.Cast(span2.Slice(width + 1)); float num8 = 1E+30f; int result = 0; uint[] array = new uint[64]; for (int i = 0; i < 4; i++) { histoArgb[i].AsSpan().Clear(); bestHisto[i].AsSpan().Clear(); } for (int j = 0; j < 14; j++) { if (num2 > 0) { Span span3 = argb.Slice((num2 - 1) * width + num4, min2 + num3 + 1); Span destination = span2.Slice(num4); span3.CopyTo(destination); } for (int k = 0; k < min; k++) { int num9 = num2 + k; Span span4 = span; span = span2; span2 = span4; int num10 = num9 * width + num4; Span span5 = argb.Slice(num10, min2 + num3 + ((num9 + 1 < height) ? 1 : 0)); Span destination2 = span2.Slice(num4); span5.CopyTo(destination2); if (nearLossless && maxQuantization > 1 && num9 >= 1 && num9 + 1 < height) { MaxDiffsForRow(width2, width, argb, num10, maxDiffs.Slice(num4), usedSubtractGreen); } GetResidual(width, height, span, span2, maxDiffs, j, num, num + min2, num9, maxQuantization, transparentColorMode, usedSubtractGreen, nearLossless, array, scratch); for (int l = 0; l < min2; l++) { UpdateHisto(histoArgb, array[l]); } } float num11 = PredictionCostSpatialHistogram(accumulated, histoArgb); if (j == num6) { num11 -= 15f; } if (j == num7) { num11 -= 15f; } if (num11 < num8) { int[][] array2 = histoArgb; histoArgb = bestHisto; bestHisto = array2; num8 = num11; result = j; } for (int m = 0; m < 4; m++) { histoArgb[m].AsSpan().Clear(); } } for (int n = 0; n < 4; n++) { for (int num12 = 0; num12 < 256; num12++) { accumulated[n][num12] += bestHisto[n][num12]; } } return result; } private unsafe static void GetResidual(int width, int height, Span upperRowSpan, Span currentRowSpan, Span maxDiffs, int mode, int xStart, int xEnd, int y, int maxQuantization, WebpTransparentColorMode transparentColorMode, bool usedSubtractGreen, bool nearLossless, Span output, Span scratch) { if (transparentColorMode == WebpTransparentColorMode.Preserve) { PredictBatch(mode, xStart, y, xEnd - xStart, currentRowSpan, upperRowSpan, output, scratch); return; } fixed (uint* ptr = currentRowSpan) { fixed (uint* ptr2 = upperRowSpan) { for (int i = xStart; i < xEnd; i++) { uint num = 0u; if (y == 0) { num = ((i == 0) ? 4278190080u : ptr[i - 1]); } else if (i == 0) { num = ptr2[i]; } else { switch (mode) { case 0: num = 4278190080u; break; case 1: num = ptr[i - 1]; break; case 2: num = LosslessUtils.Predictor2(ptr[i - 1], ptr2 + i); break; case 3: num = LosslessUtils.Predictor3(ptr[i - 1], ptr2 + i); break; case 4: num = LosslessUtils.Predictor4(ptr[i - 1], ptr2 + i); break; case 5: num = LosslessUtils.Predictor5(ptr[i - 1], ptr2 + i); break; case 6: num = LosslessUtils.Predictor6(ptr[i - 1], ptr2 + i); break; case 7: num = LosslessUtils.Predictor7(ptr[i - 1], ptr2 + i); break; case 8: num = LosslessUtils.Predictor8(ptr[i - 1], ptr2 + i); break; case 9: num = LosslessUtils.Predictor9(ptr[i - 1], ptr2 + i); break; case 10: num = LosslessUtils.Predictor10(ptr[i - 1], ptr2 + i); break; case 11: num = LosslessUtils.Predictor11(ptr[i - 1], ptr2 + i, scratch); break; case 12: num = LosslessUtils.Predictor12(ptr[i - 1], ptr2 + i); break; case 13: num = LosslessUtils.Predictor13(ptr[i - 1], ptr2 + i); break; } } uint num2; if (nearLossless) { if (maxQuantization == 1 || mode == 0 || y == 0 || y == height - 1 || i == 0 || i == width - 1) { num2 = LosslessUtils.SubPixels(ptr[i], num); } else { num2 = NearLossless(ptr[i], num, maxQuantization, maxDiffs[i], usedSubtractGreen); ptr[i] = LosslessUtils.AddPixels(num, num2); } } else { num2 = LosslessUtils.SubPixels(ptr[i], num); } if ((ptr[i] & 0xFF000000u) == 0) { num2 &= 0xFF000000u; ptr[i] = num & 0xFFFFFF; if (i == 0 && y != 0) { ptr2[width] = *ptr; } } output[i - xStart] = num2; } } } } private static uint NearLossless(uint value, uint predict, int maxQuantization, int maxDiff, bool usedSubtractGreen) { byte b = 0; byte b2 = 0; if (maxDiff <= 2) { return LosslessUtils.SubPixels(value, predict); } int num; for (num = maxQuantization; num >= maxDiff; num >>= 1) { } uint num2 = value >> 24; bool flag = ((num2 == 0 || num2 == 255) ? true : false); byte b3 = ((!flag) ? NearLosslessComponent((byte)(value >> 24), (byte)(predict >> 24), byte.MaxValue, num) : NearLosslessDiff((byte)((value >> 24) & 0xFF), (byte)((predict >> 24) & 0xFF))); byte b4 = NearLosslessComponent((byte)((value >> 8) & 0xFF), (byte)((predict >> 8) & 0xFF), byte.MaxValue, num); if (usedSubtractGreen) { b = (byte)(((predict >> 8) + b4) & 0xFF); b2 = NearLosslessDiff(b, (byte)((value >> 8) & 0xFF)); } byte b5 = NearLosslessComponent(NearLosslessDiff((byte)((value >> 16) & 0xFF), b2), (byte)((predict >> 16) & 0xFF), (byte)(255 - b), num); byte b6 = NearLosslessComponent(NearLosslessDiff((byte)(value & 0xFF), b2), (byte)(predict & 0xFF), (byte)(255 - b), num); return (uint)((b3 << 24) | (b5 << 16) | (b4 << 8) | b6); } private static byte NearLosslessComponent(byte value, byte predict, byte boundary, int quantization) { int num = (value - predict) & 0xFF; int num2 = (boundary - predict) & 0xFF; int num3 = num & ~(quantization - 1); int num4 = num3 + quantization; int num5 = ((((boundary - value) & 0xFF) < num2) ? 1 : 0); if (num - num3 < num4 - num + num5) { if (num > num2 && num3 <= num2) { return (byte)(num3 + (quantization >> 1)); } return (byte)num3; } if (num <= num2 && num4 > num2) { return (byte)(num3 + (quantization >> 1)); } return (byte)num4; } private static void CopyImageWithPrediction(int width, int height, int bits, Span modes, Span argbScratch, Span argb, int maxQuantization, WebpTransparentColorMode transparentColorMode, bool usedSubtractGreen, bool nearLossless, bool lowEffort) { int num = LosslessUtils.SubSampleSize(width, bits); Span span = argbScratch; Span span2 = span.Slice(width + 1); Span span3 = MemoryMarshal.Cast(span2.Slice(width + 1)); Span span4 = span3.Slice(width); Span scratch = stackalloc short[8]; for (int i = 0; i < height; i++) { Span span5 = span; span = span2; span2 = span5; argb.Slice(i * width, width + ((i + 1 < height) ? 1 : 0)).CopyTo(span2); if (lowEffort) { PredictBatch(11, 0, i, width, span2, span, argb.Slice(i * width), scratch); continue; } if (nearLossless && maxQuantization > 1) { Span span6 = span3; span3 = span4; span4 = span6; if (i + 2 < height) { MaxDiffsForRow(width, width, argb, (i + 1) * width, span4, usedSubtractGreen); } } int num2 = 0; while (num2 < width) { int mode = (int)((modes[(i >> bits) * num + (num2 >> bits)] >> 8) & 0xFF); int num3 = num2 + (1 << bits); if (num3 > width) { num3 = width; } GetResidual(width, height, span, span2, span3, mode, num2, num3, i, maxQuantization, transparentColorMode, usedSubtractGreen, nearLossless, argb.Slice(i * width + num2), scratch); num2 = num3; } } } private unsafe static void PredictBatch(int mode, int xStart, int y, int numPixels, Span currentSpan, Span upperSpan, Span outputSpan, Span scratch) { fixed (uint* ptr = currentSpan) { fixed (uint* ptr2 = upperSpan) { fixed (uint* ptr3 = outputSpan) { uint* ptr4 = ptr3; if (xStart == 0) { if (y == 0) { LosslessUtils.PredictorSub0(ptr, 1, ptr4); } else { LosslessUtils.PredictorSub2(ptr, ptr2, 1, ptr4); } xStart++; ptr4++; numPixels--; } if (y == 0) { LosslessUtils.PredictorSub1(ptr + xStart, numPixels, ptr4); return; } switch (mode) { case 0: LosslessUtils.PredictorSub0(ptr + xStart, numPixels, ptr4); break; case 1: LosslessUtils.PredictorSub1(ptr + xStart, numPixels, ptr4); break; case 2: LosslessUtils.PredictorSub2(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; case 3: LosslessUtils.PredictorSub3(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; case 4: LosslessUtils.PredictorSub4(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; case 5: LosslessUtils.PredictorSub5(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; case 6: LosslessUtils.PredictorSub6(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; case 7: LosslessUtils.PredictorSub7(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; case 8: LosslessUtils.PredictorSub8(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; case 9: LosslessUtils.PredictorSub9(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; case 10: LosslessUtils.PredictorSub10(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; case 11: LosslessUtils.PredictorSub11(ptr + xStart, ptr2 + xStart, numPixels, ptr4, scratch); break; case 12: LosslessUtils.PredictorSub12(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; case 13: LosslessUtils.PredictorSub13(ptr + xStart, ptr2 + xStart, numPixels, ptr4); break; } } } } } private static void MaxDiffsForRow(int width, int stride, Span argb, int offset, Span maxDiffs, bool usedSubtractGreen) { if (width <= 2) { return; } uint num = argb[offset]; uint num2 = argb[offset + 1]; if (usedSubtractGreen) { num = AddGreenToBlueAndRed(num); num2 = AddGreenToBlueAndRed(num2); } for (int i = 1; i < width - 1; i++) { uint num3 = argb[offset - stride + i]; uint num4 = argb[offset + stride + i]; uint left = num; num = num2; num2 = argb[offset + i + 1]; if (usedSubtractGreen) { num3 = AddGreenToBlueAndRed(num3); num4 = AddGreenToBlueAndRed(num4); num2 = AddGreenToBlueAndRed(num2); } maxDiffs[i] = (byte)MaxDiffAroundPixel(num, num3, num4, left, num2); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int MaxDiffBetweenPixels(uint p1, uint p2) { int a = Math.Abs((int)((p1 >> 24) - (p2 >> 24))); int b = Math.Abs((int)(((p1 >> 16) & 0xFF) - ((p2 >> 16) & 0xFF))); int a2 = Math.Abs((int)(((p1 >> 8) & 0xFF) - ((p2 >> 8) & 0xFF))); return GetMax(b: GetMax(a2, Math.Abs((int)((p1 & 0xFF) - (p2 & 0xFF)))), a: GetMax(a, b)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int MaxDiffAroundPixel(uint current, uint up, uint down, uint left, uint right) { int a = MaxDiffBetweenPixels(current, up); int b = MaxDiffBetweenPixels(current, down); int a2 = MaxDiffBetweenPixels(current, left); return GetMax(b: GetMax(a2, MaxDiffBetweenPixels(current, right)), a: GetMax(a, b)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void UpdateHisto(int[][] histoArgb, uint argb) { histoArgb[0][argb >> 24]++; histoArgb[1][(argb >> 16) & 0xFF]++; histoArgb[2][(argb >> 8) & 0xFF]++; histoArgb[3][argb & 0xFF]++; } private static uint AddGreenToBlueAndRed(uint argb) { uint num = (argb >> 8) & 0xFF; uint num2 = argb & 0xFF00FF; num2 += (num << 16) | num; num2 &= 0xFF00FF; return (argb & 0xFF00FF00u) | num2; } private static void CopyTileWithColorTransform(int xSize, int ySize, int tileX, int tileY, int maxTileSize, Vp8LMultipliers colorTransform, Span argb) { int min = GetMin(maxTileSize, xSize - tileX); int min2 = GetMin(maxTileSize, ySize - tileY); argb = argb.Slice(tileY * xSize + tileX); while (min2-- > 0) { LosslessUtils.TransformColor(colorTransform, argb, min); if (argb.Length > xSize) { argb = argb.Slice(xSize); } } } private static Vp8LMultipliers GetBestColorTransformForTile(int tileX, int tileY, int bits, Vp8LMultipliers prevX, Vp8LMultipliers prevY, int quality, int xSize, int ySize, int[] accumulatedRedHisto, int[] accumulatedBlueHisto, Span argb, Span scratch) { int num = 1 << bits; int num2 = tileY * num; int num3 = tileX * num; int min = GetMin(num3 + num, xSize); int min2 = GetMin(num2 + num, ySize); int tileWidth = min - num3; int tileHeight = min2 - num2; Span argb2 = argb.Slice(num2 * xSize + num3); Vp8LMultipliers bestTx = default(Vp8LMultipliers); GetBestGreenToRed(argb2, xSize, scratch, tileWidth, tileHeight, prevX, prevY, quality, accumulatedRedHisto, ref bestTx); GetBestGreenRedToBlue(argb2, xSize, scratch, tileWidth, tileHeight, prevX, prevY, quality, accumulatedBlueHisto, ref bestTx); return bestTx; } private static void GetBestGreenToRed(Span argb, int stride, Span scratch, int tileWidth, int tileHeight, Vp8LMultipliers prevX, Vp8LMultipliers prevY, int quality, int[] accumulatedRedHisto, ref Vp8LMultipliers bestTx) { int num = 4 + (7 * quality >> 8); int num2 = 0; double num3 = GetPredictionCostCrossColorRed(argb, stride, scratch, tileWidth, tileHeight, prevX, prevY, num2, accumulatedRedHisto); for (int i = 0; i < num; i++) { int num4 = 32 >> i; for (int j = -num4; j <= num4; j += 2 * num4) { int num5 = j + num2; double predictionCostCrossColorRed = GetPredictionCostCrossColorRed(argb, stride, scratch, tileWidth, tileHeight, prevX, prevY, num5, accumulatedRedHisto); if (predictionCostCrossColorRed < num3) { num3 = predictionCostCrossColorRed; num2 = num5; } } } bestTx.GreenToRed = (byte)(num2 & 0xFF); } private static void GetBestGreenRedToBlue(Span argb, int stride, Span scratch, int tileWidth, int tileHeight, Vp8LMultipliers prevX, Vp8LMultipliers prevY, int quality, int[] accumulatedBlueHisto, ref Vp8LMultipliers bestTx) { int num = ((quality < 25) ? 1 : ((quality > 50) ? 7 : 4)); int num2 = 0; int num3 = 0; double num4 = GetPredictionCostCrossColorBlue(argb, stride, scratch, tileWidth, tileHeight, prevX, prevY, num2, num3, accumulatedBlueHisto); for (int i = 0; i < num; i++) { int num5 = DeltaLut[i]; for (int j = 0; j < 8; j++) { int num6 = Offset[j][0] * num5 + num2; int num7 = Offset[j][1] * num5 + num3; double predictionCostCrossColorBlue = GetPredictionCostCrossColorBlue(argb, stride, scratch, tileWidth, tileHeight, prevX, prevY, num6, num7, accumulatedBlueHisto); if (predictionCostCrossColorBlue < num4) { num4 = predictionCostCrossColorBlue; num2 = num6; num3 = num7; } if (quality < 25 && i == 4) { break; } } if (num5 == 2 && num2 == 0 && num3 == 0) { break; } } bestTx.GreenToBlue = (byte)(num2 & 0xFF); bestTx.RedToBlue = (byte)(num3 & 0xFF); } private static double GetPredictionCostCrossColorRed(Span argb, int stride, Span scratch, int tileWidth, int tileHeight, Vp8LMultipliers prevX, Vp8LMultipliers prevY, int greenToRed, int[] accumulatedRedHisto) { Span span = scratch.Slice(0, 256); span.Clear(); ColorSpaceTransformUtils.CollectColorRedTransforms(argb, stride, tileWidth, tileHeight, greenToRed, span); double num = PredictionCostCrossColor(accumulatedRedHisto, span); if ((byte)greenToRed == prevX.GreenToRed) { num -= 3.0; } if ((byte)greenToRed == prevY.GreenToRed) { num -= 3.0; } if (greenToRed == 0) { num -= 3.0; } return num; } private static double GetPredictionCostCrossColorBlue(Span argb, int stride, Span scratch, int tileWidth, int tileHeight, Vp8LMultipliers prevX, Vp8LMultipliers prevY, int greenToBlue, int redToBlue, int[] accumulatedBlueHisto) { Span span = scratch.Slice(0, 256); span.Clear(); ColorSpaceTransformUtils.CollectColorBlueTransforms(argb, stride, tileWidth, tileHeight, greenToBlue, redToBlue, span); double num = PredictionCostCrossColor(accumulatedBlueHisto, span); if ((byte)greenToBlue == prevX.GreenToBlue) { num -= 3.0; } if ((byte)greenToBlue == prevY.GreenToBlue) { num -= 3.0; } if ((byte)redToBlue == prevX.RedToBlue) { num -= 3.0; } if ((byte)redToBlue == prevY.RedToBlue) { num -= 3.0; } if (greenToBlue == 0) { num -= 3.0; } if (redToBlue == 0) { num -= 3.0; } return num; } private static float PredictionCostSpatialHistogram(int[][] accumulated, int[][] tile) { double num = 0.0; for (int i = 0; i < 4; i++) { double expVal = 0.94; num += (double)PredictionCostSpatial(tile[i], 1, expVal); num += (double)LosslessUtils.CombinedShannonEntropy(tile[i], accumulated[i]); } return (float)num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double PredictionCostCrossColor(int[] accumulated, Span counts) { return LosslessUtils.CombinedShannonEntropy(counts, accumulated) + PredictionCostSpatial(counts, 3, 2.4); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float PredictionCostSpatial(Span counts, int weight0, double expVal) { int num = 16; double num2 = 0.6; double num3 = weight0 * counts[0]; for (int i = 1; i < num; i++) { num3 += expVal * (double)(counts[i] + counts[256 - i]); expVal *= num2; } return (float)(-0.1 * num3); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte NearLosslessDiff(byte a, byte b) { return (byte)((a - b) & 0xFF); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint MultipliersToColorCode(Vp8LMultipliers m) { return (uint)(-16777216 | (m.RedToBlue << 16) | (m.GreenToBlue << 8) | m.GreenToRed); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetMin(int a, int b) { if (a <= b) { return a; } return b; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetMax(int a, int b) { if (a >= b) { return a; } return b; } } internal class Vp8LBackwardRefs { public int BlockSize { get; set; } public List Refs { get; } public Vp8LBackwardRefs(int pixels) { Refs = new List(pixels); } public void Add(PixOrCopy pixOrCopy) { Refs.Add(pixOrCopy); } } internal class Vp8LBitEntropy { private const uint NonTrivialSym = uint.MaxValue; public double Entropy { get; set; } public uint Sum { get; set; } public int NoneZeros { get; set; } public uint MaxVal { get; set; } public uint NoneZeroCode { get; set; } public Vp8LBitEntropy() { Entropy = 0.0; Sum = 0u; NoneZeros = 0; MaxVal = 0u; NoneZeroCode = uint.MaxValue; } public void Init() { Entropy = 0.0; Sum = 0u; NoneZeros = 0; MaxVal = 0u; NoneZeroCode = uint.MaxValue; } public double BitsEntropyRefine() { double num; if (NoneZeros < 5) { if (NoneZeros <= 1) { return 0.0; } if (NoneZeros == 2) { return 0.99 * (double)Sum + 0.01 * Entropy; } num = ((NoneZeros != 3) ? 0.7 : 0.95); } else { num = 0.627; } double num2 = 2 * Sum - MaxVal; num2 = num * num2 + (1.0 - num) * Entropy; if (!(Entropy < num2)) { return Entropy; } return num2; } public void BitsEntropyUnrefined(Span array, int n) { Init(); for (int i = 0; i < n; i++) { if (array[i] != 0) { Sum += array[i]; NoneZeroCode = (uint)i; NoneZeros++; Entropy -= LosslessUtils.FastSLog2(array[i]); if (MaxVal < array[i]) { MaxVal = array[i]; } } } Entropy += LosslessUtils.FastSLog2(Sum); } public void BitsEntropyUnrefined(uint[] x, int length, Vp8LStreaks stats) { int iPrev = 0; uint valPrev = x[0]; Init(); int i; for (i = 1; i < length; i++) { uint num = x[i]; if (num != valPrev) { GetEntropyUnrefined(num, i, ref valPrev, ref iPrev, stats); } } GetEntropyUnrefined(0u, i, ref valPrev, ref iPrev, stats); Entropy += LosslessUtils.FastSLog2(Sum); } public void GetCombinedEntropyUnrefined(uint[] x, uint[] y, int length, Vp8LStreaks stats) { int iPrev = 0; uint valPrev = x[0] + y[0]; Init(); int i; for (i = 1; i < length; i++) { uint num = x[i] + y[i]; if (num != valPrev) { GetEntropyUnrefined(num, i, ref valPrev, ref iPrev, stats); } } GetEntropyUnrefined(0u, i, ref valPrev, ref iPrev, stats); Entropy += LosslessUtils.FastSLog2(Sum); } public void GetEntropyUnrefined(uint[] x, int length, Vp8LStreaks stats) { int iPrev = 0; uint valPrev = x[0]; Init(); int i; for (i = 1; i < length; i++) { uint num = x[i]; if (num != valPrev) { GetEntropyUnrefined(num, i, ref valPrev, ref iPrev, stats); } } GetEntropyUnrefined(0u, i, ref valPrev, ref iPrev, stats); Entropy += LosslessUtils.FastSLog2(Sum); } private void GetEntropyUnrefined(uint val, int i, ref uint valPrev, ref int iPrev, Vp8LStreaks stats) { int num = i - iPrev; if (valPrev != 0) { Sum += (uint)(int)(valPrev * num); NoneZeros += num; NoneZeroCode = (uint)iPrev; Entropy -= LosslessUtils.FastSLog2(valPrev) * (float)num; if (MaxVal < valPrev) { MaxVal = valPrev; } } stats.Counts[(valPrev != 0) ? 1u : 0u] += ((num > 3) ? 1 : 0); stats.Streaks[(valPrev != 0) ? 1u : 0u][(num > 3) ? 1u : 0u] += num; valPrev = val; iPrev = i; } } internal class Vp8LDecoder : IDisposable { public int Width { get; set; } public int Height { get; set; } public Vp8LMetadata Metadata { get; set; } public List Transforms { get; set; } public IMemoryOwner Pixels { get; } public Vp8LDecoder(int width, int height, MemoryAllocator memoryAllocator) { Width = width; Height = height; Metadata = new Vp8LMetadata(); Pixels = memoryAllocator.Allocate(width * height, AllocationOptions.Clean); } public void Dispose() { Pixels.Dispose(); Metadata?.HuffmanImage?.Dispose(); if (Transforms == null) { return; } foreach (Vp8LTransform transform in Transforms) { transform.Data?.Dispose(); } } } internal class Vp8LEncoder : IDisposable { private readonly int[] scratch = new int[256]; private readonly int[][] histoArgb = new int[4][] { new int[256], new int[256], new int[256], new int[256] }; private readonly int[][] bestHisto = new int[4][] { new int[256], new int[256], new int[256], new int[256] }; private readonly MemoryAllocator memoryAllocator; private readonly Configuration configuration; private const int MaxRefsBlockPerImage = 16; private const int MinBlockSize = 256; private Vp8LBitWriter bitWriter; private readonly int quality; private readonly WebpEncodingMethod method; private readonly WebpTransparentColorMode transparentColorMode; private readonly bool nearLossless; private readonly int nearLosslessQuality; private const int ApplyPaletteGreedyMax = 4; private const int PaletteInvSizeBits = 11; private const int PaletteInvSize = 2048; private static ReadOnlySpan StorageOrder => new byte[19] { 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; private static ReadOnlySpan Order => new byte[4] { 1, 2, 0, 3 }; public IMemoryOwner Bgra { get; } public IMemoryOwner EncodedData { get; } public IMemoryOwner BgraScratch { get; set; } public int CurrentWidth { get; set; } public int HistoBits { get; set; } public int TransformBits { get; set; } public IMemoryOwner TransformData { get; set; } public int CacheBits { get; set; } public bool UseCrossColorTransform { get; set; } public bool UseSubtractGreenTransform { get; set; } public bool UsePredictorTransform { get; set; } public bool UsePalette { get; set; } public int PaletteSize { get; set; } public IMemoryOwner Palette { get; } public Vp8LBackwardRefs[] Refs { get; } public Vp8LHashChain HashChain { get; } public Vp8LEncoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, WebpEncodingMethod method, WebpTransparentColorMode transparentColorMode, bool nearLossless, int nearLosslessQuality) { int num = width * height; int expectedSize = num * 2; this.memoryAllocator = memoryAllocator; this.configuration = configuration; this.quality = Numerics.Clamp(quality, 0, 100); this.method = method; this.transparentColorMode = transparentColorMode; this.nearLossless = nearLossless; this.nearLosslessQuality = Numerics.Clamp(nearLosslessQuality, 0, 100); bitWriter = new Vp8LBitWriter(expectedSize); Bgra = memoryAllocator.Allocate(num); EncodedData = memoryAllocator.Allocate(num); Palette = memoryAllocator.Allocate(256); Refs = new Vp8LBackwardRefs[3]; HashChain = new Vp8LHashChain(memoryAllocator, num); int num2 = (num - 1) / 16 + 1; for (int i = 0; i < Refs.Length; i++) { Refs[i] = new Vp8LBackwardRefs(num) { BlockSize = ((num2 < 256) ? 256 : num2) }; } } public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; ImageMetadata metadata = image.Metadata; metadata.SyncProfiles(); bool hasAlpha = ConvertPixelsToBgra(image, width, height); WriteImageSize(width, height); WriteAlphaAndVersion(hasAlpha); EncodeStream(image); bitWriter.WriteEncodedImageToStream(stream, metadata.ExifProfile, metadata.XmpProfile, (uint)width, (uint)height, hasAlpha); } public int EncodeAlphaImageData(Image image, IMemoryOwner alphaData) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; int num = width * height; ConvertPixelsToBgra(image, width, height); EncodeStream(image); bitWriter.Finish(); int num2 = bitWriter.NumBytes(); if (num2 >= num) { return num; } bitWriter.WriteToBuffer(alphaData.GetSpan()); return num2; } private void WriteImageSize(int inputImgWidth, int inputImgHeight) { Guard.MustBeLessThan(inputImgWidth, 16383, "inputImgWidth"); Guard.MustBeLessThan(inputImgHeight, 16383, "inputImgHeight"); uint bits = (uint)(inputImgWidth - 1); uint bits2 = (uint)(inputImgHeight - 1); bitWriter.PutBits(bits, 14); bitWriter.PutBits(bits2, 14); } private void WriteAlphaAndVersion(bool hasAlpha) { bitWriter.PutBits(hasAlpha ? 1u : 0u, 1); bitWriter.PutBits(0u, 3); } private void EncodeStream(Image image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Span span = Bgra.GetSpan(); Span span2 = EncodedData.GetSpan(); bool flag = method == WebpEncodingMethod.Level0; bool redAndBlueAlwaysZero; CrunchConfig[] array = EncoderAnalyze(span, width, height, out redAndBlueAlwaysZero); int num = 0; Vp8LBitWriter bwInit = bitWriter; Vp8LBitWriter dst = bitWriter.Clone(); bool flag2 = true; CrunchConfig[] array2 = array; foreach (CrunchConfig crunchConfig in array2) { span.CopyTo(span2); bool flag3 = true; EntropyIx entropyIdx = crunchConfig.EntropyIdx; bool usePalette = entropyIdx - 4 <= EntropyIx.Spatial; UsePalette = usePalette; entropyIdx = crunchConfig.EntropyIdx; usePalette = entropyIdx - 2 <= EntropyIx.Spatial; UseSubtractGreenTransform = usePalette; entropyIdx = crunchConfig.EntropyIdx; usePalette = ((entropyIdx == EntropyIx.Spatial || entropyIdx == EntropyIx.SpatialSubGreen) ? true : false); UsePredictorTransform = usePalette; if (flag) { UseCrossColorTransform = false; } else { UseCrossColorTransform = !redAndBlueAlwaysZero && UsePredictorTransform; } AllocateTransformBuffer(width, height); CacheBits = 0; ClearRefs(); if (nearLossless && nearLosslessQuality < 100 && !UsePalette && !UsePredictorTransform) { AllocateTransformBuffer(width, height); NearLosslessEnc.ApplyNearLossless(width, height, nearLosslessQuality, span, span, width); } if (UsePalette) { EncodePalette(flag); MapImageFromPalette(width, height); if (flag3 && PaletteSize < 1024) { CacheBits = Numerics.Log2((uint)PaletteSize) + 1; } } if (UseSubtractGreenTransform) { ApplySubtractGreen(); } if (UsePredictorTransform) { ApplyPredictFilter(CurrentWidth, height, flag); } if (UseCrossColorTransform) { ApplyCrossColorFilter(CurrentWidth, height, flag); } bitWriter.PutBits(0u, 1); EncodeImage(CurrentWidth, height, flag3, crunchConfig, CacheBits, flag); if (flag2 || bitWriter.NumBytes() < num) { num = bitWriter.NumBytes(); BitWriterSwap(ref bitWriter, ref dst); } if (array.Length > 1) { bitWriter.Reset(bwInit); } flag2 = false; } BitWriterSwap(ref dst, ref bitWriter); } private bool ConvertPixelsToBgra(Image image, int width, int height) where TPixel : unmanaged, IPixel { Buffer2D pixelBuffer = image.Frames.RootFrame.PixelBuffer; bool flag = false; Span span = MemoryMarshal.Cast(Bgra.GetSpan()); int num = width * 4; for (int i = 0; i < height; i++) { Span span2 = pixelBuffer.DangerousGetRowSpan(i); Span span3 = span.Slice(i * num, num); PixelOperations.Instance.ToBgra32Bytes(configuration, span2, span3, width); if (!flag) { flag = WebpCommonUtils.CheckNonOpaque(MemoryMarshal.Cast(span3)); } } return flag; } private CrunchConfig[] EncoderAnalyze(ReadOnlySpan bgra, int width, int height, out bool redAndBlueAlwaysZero) { bool flag = AnalyzeAndCreatePalette(bgra, width, height); HistoBits = GetHistoBits(method, flag, width, height); TransformBits = GetTransformBits(method, HistoBits); int paletteSize = PaletteSize; int num = ((paletteSize <= 0 || paletteSize > 16) ? 1 : 2); EntropyIx entropyIx = AnalyzeEntropy(bgra, width, height, flag, PaletteSize, TransformBits, out redAndBlueAlwaysZero); bool doNotCache = false; List list = new List(); if (method == WebpEncodingMethod.Level6 && quality == 100) { doNotCache = true; foreach (EntropyIx item in Enum.GetValues(typeof(EntropyIx)).Cast()) { if ((item != EntropyIx.Palette && item != EntropyIx.PaletteAndSpatial) || flag) { list.Add(new CrunchConfig { EntropyIdx = item }); } } } else { list.Add(new CrunchConfig { EntropyIdx = entropyIx }); if (quality >= 75 && method == WebpEncodingMethod.Level5) { doNotCache = true; if (entropyIx == EntropyIx.Palette) { list.Add(new CrunchConfig { EntropyIdx = EntropyIx.PaletteAndSpatial }); } } } foreach (CrunchConfig item2 in list) { for (int i = 0; i < num; i++) { item2.SubConfigs.Add(new CrunchSubConfig { Lz77 = ((i == 0) ? 3 : 4), DoNotCache = doNotCache }); } } return list.ToArray(); } private void EncodeImage(int width, int height, bool useCache, CrunchConfig config, int cacheBits, bool lowEffort) { Span span = EncodedData.GetSpan(); int num = LosslessUtils.SubSampleSize(width, HistoBits) * LosslessUtils.SubSampleSize(height, HistoBits); ushort[] array = new ushort[num]; HuffmanTree[] array2 = new HuffmanTree[57]; for (int i = 0; i < array2.Length; i++) { array2[i] = default(HuffmanTree); } if (useCache) { if (cacheBits == 0) { cacheBits = 10; } } else { cacheBits = 0; } HashChain.Fill(span, quality, width, height, lowEffort); Vp8LBitWriter vp8LBitWriter = ((config.SubConfigs.Count > 1) ? bitWriter.Clone() : bitWriter); Vp8LBitWriter bwInit = bitWriter; bool flag = true; foreach (CrunchSubConfig subConfig in config.SubConfigs) { Vp8LBackwardRefs backwardReferences = BackwardReferenceEncoder.GetBackwardReferences(width, height, span, quality, subConfig.Lz77, ref cacheBits, memoryAllocator, HashChain, Refs[0], Refs[1]); Vp8LBackwardRefs refsTmp = Refs[backwardReferences.Equals(Refs[0]) ? 1u : 0u]; bitWriter.Reset(bwInit); Vp8LHistogram tmpHisto = new Vp8LHistogram(cacheBits); List list = new List(num); for (int j = 0; j < num; j++) { list.Add(new Vp8LHistogram(cacheBits)); } HistogramEncoder.GetHistoImageSymbols(width, height, backwardReferences, quality, HistoBits, cacheBits, list, tmpHisto, array); int count = list.Count; HuffmanTreeCode[] array3 = new HuffmanTreeCode[5 * count]; for (int k = 0; k < array3.Length; k++) { array3[k] = default(HuffmanTreeCode); } GetHuffBitLengthsAndCodes(list, array3); if (cacheBits > 0) { bitWriter.PutBits(1u, 1); bitWriter.PutBits((uint)cacheBits, 4); } else { bitWriter.PutBits(0u, 1); } bool flag2 = count > 1; bitWriter.PutBits(flag2 ? 1u : 0u, 1); if (flag2) { using IMemoryOwner buffer = memoryAllocator.Allocate(num); Span span2 = buffer.GetSpan(); int num2 = 0; for (int l = 0; l < num; l++) { int num3 = array[l] & 0xFFFF; span2[l] = (uint)(num3 << 8); if (num3 >= num2) { num2 = num3 + 1; } } bitWriter.PutBits((uint)(HistoBits - 2), 3); EncodeImageNoHuffman(span2, HashChain, refsTmp, Refs[2], LosslessUtils.SubSampleSize(width, HistoBits), LosslessUtils.SubSampleSize(height, HistoBits), quality, lowEffort); } int num4 = 0; for (int m = 0; m < 5 * list.Count; m++) { HuffmanTreeCode huffmanTreeCode = array3[m]; if (num4 < huffmanTreeCode.NumSymbols) { num4 = huffmanTreeCode.NumSymbols; } } HuffmanTreeToken[] array4 = new HuffmanTreeToken[num4]; for (int n = 0; n < array4.Length; n++) { array4[n] = new HuffmanTreeToken(); } for (int num5 = 0; num5 < 5 * list.Count; num5++) { HuffmanTreeCode huffmanCode = array3[num5]; StoreHuffmanCode(array2, array4, huffmanCode); ClearHuffmanTreeIfOnlyOneSymbol(huffmanCode); } StoreImageToBitMask(width, HistoBits, backwardReferences, array, array3); if (flag || (vp8LBitWriter != null && bitWriter.NumBytes() < vp8LBitWriter.NumBytes())) { Vp8LBitWriter vp8LBitWriter2 = bitWriter; bitWriter = vp8LBitWriter; vp8LBitWriter = vp8LBitWriter2; } flag = false; } bitWriter = vp8LBitWriter; } private void EncodePalette(bool lowEffort) { Span bgra = new uint[256]; int paletteSize = PaletteSize; Span span = Palette.Memory.Span; bitWriter.PutBits(1u, 1); bitWriter.PutBits(3u, 2); bitWriter.PutBits((uint)(paletteSize - 1), 8); for (int num = paletteSize - 1; num >= 1; num--) { bgra[num] = LosslessUtils.SubPixels(span[num], span[num - 1]); } bgra[0] = span[0]; EncodeImageNoHuffman(bgra, HashChain, Refs[0], Refs[1], paletteSize, 1, 20, lowEffort); } private void ApplySubtractGreen() { bitWriter.PutBits(1u, 1); bitWriter.PutBits(2u, 2); LosslessUtils.SubtractGreenFromBlueAndRed(EncodedData.GetSpan()); } private void ApplyPredictFilter(int width, int height, bool lowEffort) { int num = (UsePalette ? 100 : nearLosslessQuality); int transformBits = TransformBits; int width2 = LosslessUtils.SubSampleSize(width, transformBits); int height2 = LosslessUtils.SubSampleSize(height, transformBits); PredictorEncoder.ResidualImage(width, height, transformBits, EncodedData.GetSpan(), BgraScratch.GetSpan(), TransformData.GetSpan(), histoArgb, bestHisto, nearLossless, num, transparentColorMode, UseSubtractGreenTransform, lowEffort); bitWriter.PutBits(1u, 1); bitWriter.PutBits(0u, 2); bitWriter.PutBits((uint)(transformBits - 2), 3); EncodeImageNoHuffman(TransformData.GetSpan(), HashChain, Refs[0], Refs[1], width2, height2, quality, lowEffort); } private void ApplyCrossColorFilter(int width, int height, bool lowEffort) { int transformBits = TransformBits; int width2 = LosslessUtils.SubSampleSize(width, transformBits); int height2 = LosslessUtils.SubSampleSize(height, transformBits); PredictorEncoder.ColorSpaceTransform(width, height, transformBits, quality, EncodedData.GetSpan(), TransformData.GetSpan(), scratch); bitWriter.PutBits(1u, 1); bitWriter.PutBits(1u, 2); bitWriter.PutBits((uint)(transformBits - 2), 3); EncodeImageNoHuffman(TransformData.GetSpan(), HashChain, Refs[0], Refs[1], width2, height2, quality, lowEffort); } private void EncodeImageNoHuffman(Span bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, int quality, bool lowEffort) { int cacheBits = 0; ushort[] histogramSymbols = new ushort[1]; HuffmanTreeCode[] array = new HuffmanTreeCode[5]; for (int i = 0; i < array.Length; i++) { array[i] = default(HuffmanTreeCode); } HuffmanTree[] array2 = new HuffmanTree[57]; for (int j = 0; j < array2.Length; j++) { array2[j] = default(HuffmanTree); } hashChain.Fill(bgra, quality, width, height, lowEffort); Vp8LBackwardRefs backwardReferences = BackwardReferenceEncoder.GetBackwardReferences(width, height, bgra, quality, 3, ref cacheBits, memoryAllocator, hashChain, refsTmp1, refsTmp2); List list = new List(); list.Add(new Vp8LHistogram(cacheBits)); list[0].StoreRefs(backwardReferences); GetHuffBitLengthsAndCodes(list, array); bitWriter.PutBits(0u, 1); int num = 0; for (int k = 0; k < 5; k++) { HuffmanTreeCode huffmanTreeCode = array[k]; if (num < huffmanTreeCode.NumSymbols) { num = huffmanTreeCode.NumSymbols; } } HuffmanTreeToken[] array3 = new HuffmanTreeToken[num]; for (int l = 0; l < array3.Length; l++) { array3[l] = new HuffmanTreeToken(); } for (int m = 0; m < 5; m++) { HuffmanTreeCode huffmanCode = array[m]; StoreHuffmanCode(array2, array3, huffmanCode); ClearHuffmanTreeIfOnlyOneSymbol(huffmanCode); } StoreImageToBitMask(width, 0, backwardReferences, histogramSymbols, array); } private void StoreHuffmanCode(HuffmanTree[] huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode huffmanCode) { int num = 0; Span span = scratch.AsSpan(0, 2); span.Clear(); int num2 = 8; int num3 = 1 << num2; for (int i = 0; i < huffmanCode.NumSymbols; i++) { if (num >= 3) { break; } if (huffmanCode.CodeLengths[i] != 0) { if (num < 2) { span[num] = i; } num++; } } if (num == 0) { bitWriter.PutBits(1u, 4); } else if (num <= 2 && span[0] < num3 && span[1] < num3) { bitWriter.PutBits(1u, 1); bitWriter.PutBits((uint)(num - 1), 1); if (span[0] <= 1) { bitWriter.PutBits(0u, 1); bitWriter.PutBits((uint)span[0], 1); } else { bitWriter.PutBits(1u, 1); bitWriter.PutBits((uint)span[0], 8); } if (num == 2) { bitWriter.PutBits((uint)span[1], 8); } } else { StoreFullHuffmanCode(huffTree, tokens, huffmanCode); } } private void StoreFullHuffmanCode(HuffmanTree[] huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode tree) { byte[] array = new byte[19]; short[] codes = new short[19]; HuffmanTreeCode huffmanTreeCode = new HuffmanTreeCode { NumSymbols = 19, CodeLengths = array, Codes = codes }; bitWriter.PutBits(0u, 1); int num = HuffmanUtils.CreateCompressedHuffmanTree(tree, tokens); uint[] array2 = new uint[20]; bool[] bufRle = new bool[20]; int i; for (i = 0; i < num; i++) { array2[tokens[i].Code]++; } HuffmanUtils.CreateHuffmanTree(array2, 7, bufRle, huffTree, huffmanTreeCode); StoreHuffmanTreeOfHuffmanTreeToBitMask(array); ClearHuffmanTreeIfOnlyOneSymbol(huffmanTreeCode); int num2 = 0; int num3 = num; i = num; while (i-- > 0) { int code = tokens[i].Code; if ((code != 0 && (uint)(code - 17) > 1u) || 1 == 0) { break; } num3--; num2 += array[code]; switch (code) { case 17: num2 += 3; break; case 18: num2 += 7; break; } } bool flag = num3 > 1 && num2 > 12; int numTokens = (flag ? num3 : num); bitWriter.PutBits(flag ? 1u : 0u, 1); if (flag) { if (num3 == 2) { bitWriter.PutBits(0u, 5); } else { int num4 = Numerics.Log2((uint)(num3 - 2)) / 2 + 1; bitWriter.PutBits((uint)(num4 - 1), 3); bitWriter.PutBits((uint)(num3 - 2), num4 * 2); } } StoreHuffmanTreeToBitMask(tokens, numTokens, huffmanTreeCode); } private void StoreHuffmanTreeToBitMask(HuffmanTreeToken[] tokens, int numTokens, HuffmanTreeCode huffmanCode) { for (int i = 0; i < numTokens; i++) { int code = tokens[i].Code; int extraBits = tokens[i].ExtraBits; bitWriter.PutBits((uint)huffmanCode.Codes[code], huffmanCode.CodeLengths[code]); switch (code) { case 16: bitWriter.PutBits((uint)extraBits, 2); break; case 17: bitWriter.PutBits((uint)extraBits, 3); break; case 18: bitWriter.PutBits((uint)extraBits, 7); break; } } } private void StoreHuffmanTreeOfHuffmanTreeToBitMask(byte[] codeLengthBitDepth) { int num = 19; while (num > 4 && codeLengthBitDepth[StorageOrder[num - 1]] == 0) { num--; } bitWriter.PutBits((uint)(num - 4), 4); for (int i = 0; i < num; i++) { bitWriter.PutBits(codeLengthBitDepth[StorageOrder[i]], 3); } } private void StoreImageToBitMask(int width, int histoBits, Vp8LBackwardRefs backwardRefs, ushort[] histogramSymbols, HuffmanTreeCode[] huffmanCodes) { int num = ((histoBits <= 0) ? 1 : LosslessUtils.SubSampleSize(width, histoBits)); int num2 = ((histoBits != 0) ? (-(1 << histoBits)) : 0); int num3 = 0; int num4 = 0; int num5 = num3 & num2; int num6 = num4 & num2; int num7 = histogramSymbols[0]; Span span = huffmanCodes.AsSpan(5 * num7); foreach (PixOrCopy @ref in backwardRefs.Refs) { if (num5 != (num3 & num2) || num6 != (num4 & num2)) { num5 = num3 & num2; num6 = num4 & num2; num7 = histogramSymbols[(num4 >> histoBits) * num + (num3 >> histoBits)]; span = huffmanCodes.AsSpan(5 * num7); } if (@ref.IsLiteral()) { for (int i = 0; i < 4; i++) { int codeIndex = (int)@ref.Literal(Order[i]); bitWriter.WriteHuffmanCode(span[i], codeIndex); } } else if (@ref.IsCacheIdx()) { int num8 = (int)@ref.CacheIdx(); int codeIndex2 = 280 + num8; bitWriter.WriteHuffmanCode(span[0], codeIndex2); } else { int extraBitsValue = 0; int extraBits = 0; uint distance = @ref.Distance(); int num9 = LosslessUtils.PrefixEncode(@ref.Len, ref extraBits, ref extraBitsValue); bitWriter.WriteHuffmanCodeWithExtraBits(span[0], 256 + num9, extraBitsValue, extraBits); num9 = LosslessUtils.PrefixEncode((int)distance, ref extraBits, ref extraBitsValue); bitWriter.WriteHuffmanCode(span[4], num9); bitWriter.PutBits((uint)extraBitsValue, extraBits); } num3 += @ref.Length(); while (num3 >= width) { num3 -= width; num4++; } } } private EntropyIx AnalyzeEntropy(ReadOnlySpan bgra, int width, int height, bool usePalette, int paletteSize, int transformBits, out bool redAndBlueAlwaysZero) { if (usePalette && paletteSize <= 16) { redAndBlueAlwaysZero = true; return EntropyIx.Palette; } using IMemoryOwner memoryOwner = memoryAllocator.Allocate(3328, AllocationOptions.Clean); Span span = memoryOwner.Memory.Span; uint b = bgra[0]; ReadOnlySpan readOnlySpan = null; for (int i = 0; i < height; i++) { ReadOnlySpan readOnlySpan2 = bgra.Slice(i * width, width); for (int j = 0; j < width; j++) { uint num = readOnlySpan2[j]; uint num2 = LosslessUtils.SubPixels(num, b); b = num; if (num2 != 0 && (!(readOnlySpan != null) || num != readOnlySpan[j])) { AddSingle(num, span.Slice(0), span.Slice(1024), span.Slice(512), span.Slice(1536)); AddSingle(num2, span.Slice(256), span.Slice(1280), span.Slice(768), span.Slice(1792)); AddSingleSubGreen(num, span.Slice(2048), span.Slice(2560)); AddSingleSubGreen(num2, span.Slice(2304), span.Slice(2816)); uint num3 = HashPix(num); span[(int)(3072 + num3)]++; } } readOnlySpan = readOnlySpan2; } double[] array = new double[13]; double[] array2 = new double[6]; int num4 = (usePalette ? 4 : 3); span[2304]++; span[2816]++; span[1280]++; span[768]++; span[1792]++; span[256]++; Vp8LBitEntropy vp8LBitEntropy = new Vp8LBitEntropy(); for (int k = 0; k < 13; k++) { vp8LBitEntropy.Init(); Span array3 = span.Slice(k * 256, 256); vp8LBitEntropy.BitsEntropyUnrefined(array3, 256); array[k] = vp8LBitEntropy.BitsEntropyRefine(); } array2[0] = array[0] + array[4] + array[2] + array[6]; array2[1] = array[1] + array[5] + array[3] + array[7]; array2[2] = array[0] + array[8] + array[2] + array[10]; array2[3] = array[1] + array[9] + array[3] + array[11]; array2[4] = array[12]; array2[1] += (float)(LosslessUtils.SubSampleSize(width, transformBits) * LosslessUtils.SubSampleSize(height, transformBits)) * LosslessUtils.FastLog2(14u); array2[3] += (float)(LosslessUtils.SubSampleSize(width, transformBits) * LosslessUtils.SubSampleSize(height, transformBits)) * LosslessUtils.FastLog2(24u); array2[4] += paletteSize * 8; EntropyIx entropyIx = EntropyIx.Direct; for (int l = 1; l <= num4; l++) { if (array2[(uint)entropyIx] > array2[l]) { entropyIx = (EntropyIx)l; } } redAndBlueAlwaysZero = true; byte[][] array4 = new byte[5][] { new byte[2] { 4, 6 }, new byte[2] { 5, 7 }, new byte[2] { 8, 10 }, new byte[2] { 9, 11 }, new byte[2] { 4, 6 } }; Span span2 = span.Slice(256 * array4[(uint)entropyIx][0]); Span span3 = span.Slice(256 * array4[(uint)entropyIx][1]); for (int m = 1; m < 256; m++) { if ((span2[m] | span3[m]) != 0) { redAndBlueAlwaysZero = false; break; } } return entropyIx; } private bool AnalyzeAndCreatePalette(ReadOnlySpan bgra, int width, int height) { Span span = Palette.Memory.Span; PaletteSize = GetColorPalette(bgra, width, height, span); if (PaletteSize > 256) { PaletteSize = 0; return false; } uint[] array = span.Slice(0, PaletteSize).ToArray(); Array.Sort(array); array.CopyTo(span); if (PaletteHasNonMonotonousDeltas(span, PaletteSize)) { GreedyMinimizeDeltas(span, PaletteSize); } return true; } private int GetColorPalette(ReadOnlySpan bgra, int width, int height, Span palette) { HashSet hashSet = new HashSet(); for (int i = 0; i < height; i++) { ReadOnlySpan readOnlySpan = bgra.Slice(i * width, width); for (int j = 0; j < width; j++) { hashSet.Add(readOnlySpan[j]); if (hashSet.Count > 256) { return 257; } } } using HashSet.Enumerator enumerator = hashSet.GetEnumerator(); int num = 0; while (enumerator.MoveNext()) { palette[num++] = enumerator.Current; } return hashSet.Count; } private void MapImageFromPalette(int width, int height) { Span span = EncodedData.GetSpan(); int currentWidth = CurrentWidth; Span span2 = EncodedData.GetSpan(); Span span3 = Palette.GetSpan(); int paletteSize = PaletteSize; int num = ((paletteSize > 4) ? ((paletteSize <= 16) ? 1 : 0) : ((paletteSize <= 2) ? 3 : 2)); CurrentWidth = LosslessUtils.SubSampleSize(width, num); ApplyPalette(span, currentWidth, span2, CurrentWidth, span3, paletteSize, width, height, num); } private void ApplyPalette(Span src, int srcStride, Span dst, int dstStride, Span palette, int paletteSize, int width, int height, int xBits) { using IMemoryOwner buffer = memoryAllocator.Allocate(width); Span span = buffer.GetSpan(); if (paletteSize < 4) { uint num = palette[0]; uint num2 = 0u; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { uint num3 = src[j]; if (num3 != num) { num2 = SearchColorGreedy(palette, num3); num = num3; } span[j] = (byte)num2; } BundleColorMap(span, width, xBits, dst); src = src.Slice(srcStride); dst = dst.Slice(dstStride); } return; } uint[] array = new uint[2048]; int k; for (k = 0; k < 3; k++) { bool flag = true; array.AsSpan().Fill(uint.MaxValue); for (int l = 0; l < paletteSize; l++) { uint num4 = 0u; switch (k) { case 0: num4 = ApplyPaletteHash0(palette[l]); break; case 1: num4 = ApplyPaletteHash1(palette[l]); break; case 2: num4 = ApplyPaletteHash2(palette[l]); break; } if (array[num4] != uint.MaxValue) { flag = false; break; } array[num4] = (uint)l; } if (flag) { break; } } if ((uint)k <= 2u) { ApplyPaletteFor(width, height, palette, k, src, srcStride, dst, dstStride, span, array, xBits); return; } uint[] idxMap = new uint[paletteSize]; uint[] array2 = new uint[paletteSize]; PrepareMapToPalette(palette, paletteSize, array2, idxMap); ApplyPaletteForWithIdxMap(width, height, palette, src, srcStride, dst, dstStride, span, idxMap, xBits, array2, paletteSize); } private static void ApplyPaletteFor(int width, int height, Span palette, int hashIdx, Span src, int srcStride, Span dst, int dstStride, Span tmpRow, uint[] buffer, int xBits) { uint num = palette[0]; uint num2 = 0u; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { uint num3 = src[j]; if (num3 != num) { switch (hashIdx) { case 0: num2 = buffer[ApplyPaletteHash0(num3)]; break; case 1: num2 = buffer[ApplyPaletteHash1(num3)]; break; case 2: num2 = buffer[ApplyPaletteHash2(num3)]; break; } num = num3; } tmpRow[j] = (byte)num2; } LosslessUtils.BundleColorMap(tmpRow, width, xBits, dst); src = src.Slice(srcStride); dst = dst.Slice(dstStride); } } private static void ApplyPaletteForWithIdxMap(int width, int height, Span palette, Span src, int srcStride, Span dst, int dstStride, Span tmpRow, uint[] idxMap, int xBits, uint[] paletteSorted, int paletteSize) { uint num = palette[0]; uint num2 = 0u; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { uint num3 = src[j]; if (num3 != num) { num2 = idxMap[SearchColorNoIdx(paletteSorted, num3, paletteSize)]; num = num3; } tmpRow[j] = (byte)num2; } LosslessUtils.BundleColorMap(tmpRow, width, xBits, dst); src = src.Slice(srcStride); dst = dst.Slice(dstStride); } } private static void PrepareMapToPalette(Span palette, int numColors, uint[] sorted, uint[] idxMap) { palette.Slice(0, numColors).CopyTo(sorted); Array.Sort(sorted, PaletteCompareColorsForSort); for (int i = 0; i < numColors; i++) { idxMap[SearchColorNoIdx(sorted, palette[i], numColors)] = (uint)i; } } private static int SearchColorNoIdx(uint[] sorted, uint color, int hi) { int num = 0; if (sorted[num] == color) { return num; } int num2; while (true) { num2 = num + hi >> 1; if (sorted[num2] == color) { break; } if (sorted[num2] < color) { num = num2; } else { hi = num2; } } return num2; } private static void ClearHuffmanTreeIfOnlyOneSymbol(HuffmanTreeCode huffmanCode) { int num = 0; for (int i = 0; i < huffmanCode.NumSymbols; i++) { if (huffmanCode.CodeLengths[i] != 0) { num++; if (num > 1) { return; } } } for (int j = 0; j < huffmanCode.NumSymbols; j++) { huffmanCode.CodeLengths[j] = 0; huffmanCode.Codes[j] = 0; } } private static bool PaletteHasNonMonotonousDeltas(Span palette, int numColors) { uint b = 0u; byte b2 = 0; for (int i = 0; i < numColors; i++) { uint num = LosslessUtils.SubPixels(palette[i], b); byte b3 = (byte)((num >> 16) & 0xFF); byte b4 = (byte)((num >> 8) & 0xFF); byte b5 = (byte)(num & 0xFF); if (b3 != 0) { b2 |= (byte)((b3 < 128) ? 1 : 2); } if (b4 != 0) { b2 |= (byte)((b4 < 128) ? 8 : 16); } if (b5 != 0) { b2 |= (byte)((b5 < 128) ? 64 : 128); } } return (b2 & (b2 << 1)) != 0; } private static void GreedyMinimizeDeltas(Span palette, int numColors) { uint col = 0u; for (int i = 0; i < numColors; i++) { int index = i; uint num = uint.MaxValue; for (int j = i; j < numColors; j++) { uint num2 = PaletteColorDistance(palette[j], col); if (num > num2) { num = num2; index = j; } } uint num3 = palette[index]; palette[index] = palette[i]; palette[i] = num3; col = palette[i]; } } private static void GetHuffBitLengthsAndCodes(List histogramImage, HuffmanTreeCode[] huffmanCodes) { int num = 0; for (int i = 0; i < histogramImage.Count; i++) { Vp8LHistogram vp8LHistogram = histogramImage[i]; int num2 = 5 * i; for (int j = 0; j < 5; j++) { int numSymbols = j switch { 4 => 40, 0 => vp8LHistogram.NumCodes(), _ => 256, }; huffmanCodes[num2 + j].NumSymbols = numSymbols; } } int num3 = 5 * histogramImage.Count; for (int k = 0; k < num3; k++) { int numSymbols2 = huffmanCodes[k].NumSymbols; huffmanCodes[k].Codes = new short[numSymbols2]; huffmanCodes[k].CodeLengths = new byte[numSymbols2]; if (num < numSymbols2) { num = numSymbols2; } } bool[] bufRle = new bool[num]; HuffmanTree[] array = new HuffmanTree[3 * num]; for (int l = 0; l < array.Length; l++) { array[l] = default(HuffmanTree); } for (int m = 0; m < histogramImage.Count; m++) { int num4 = 5 * m; Vp8LHistogram vp8LHistogram2 = histogramImage[m]; HuffmanUtils.CreateHuffmanTree(vp8LHistogram2.Literal, 15, bufRle, array, huffmanCodes[num4]); HuffmanUtils.CreateHuffmanTree(vp8LHistogram2.Red, 15, bufRle, array, huffmanCodes[num4 + 1]); HuffmanUtils.CreateHuffmanTree(vp8LHistogram2.Blue, 15, bufRle, array, huffmanCodes[num4 + 2]); HuffmanUtils.CreateHuffmanTree(vp8LHistogram2.Alpha, 15, bufRle, array, huffmanCodes[num4 + 3]); HuffmanUtils.CreateHuffmanTree(vp8LHistogram2.Distance, 15, bufRle, array, huffmanCodes[num4 + 4]); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint PaletteColorDistance(uint col1, uint col2) { uint num = LosslessUtils.SubPixels(col1, col2); uint num2 = 9u; return (PaletteComponentDistance(num & 0xFF) + PaletteComponentDistance((num >> 8) & 0xFF) + PaletteComponentDistance((num >> 16) & 0xFF)) * num2 + PaletteComponentDistance((num >> 24) & 0xFF); } private static int GetHistoBits(WebpEncodingMethod method, bool usePalette, int width, int height) { int i; for (i = (int)((usePalette ? 9 : 7) - method); LosslessUtils.SubSampleSize(width, i) * LosslessUtils.SubSampleSize(height, i) > 2600; i++) { } if (i >= 2) { if (i <= 9) { return i; } return 9; } return 2; } private static void BundleColorMap(Span row, int width, int xBits, Span dst) { if (xBits > 0) { int num = 1 << 3 - xBits; int num2 = (1 << xBits) - 1; uint num3 = 4278190080u; for (int i = 0; i < width; i++) { int num4 = i & num2; if (num4 == 0) { num3 = 4278190080u; } num3 |= (uint)(row[i] << 8 + num * num4); dst[i >> xBits] = num3; } } else { for (int i = 0; i < width; i++) { dst[i] = (uint)(0xFF000000u | (row[i] << 8)); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void BitWriterSwap(ref Vp8LBitWriter src, ref Vp8LBitWriter dst) { Vp8LBitWriter vp8LBitWriter = src; src = dst; dst = vp8LBitWriter; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetTransformBits(WebpEncodingMethod method, int histoBits) { int num = ((method < WebpEncodingMethod.Level4) ? 6 : ((method > WebpEncodingMethod.Level4) ? 4 : 5)); if (histoBits <= num) { return histoBits; } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void AddSingle(uint p, Span a, Span r, Span g, Span b) { a[(int)((p >> 24) & 0xFF)]++; r[(int)((p >> 16) & 0xFF)]++; g[(int)((p >> 8) & 0xFF)]++; b[(int)(p & 0xFF)]++; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void AddSingleSubGreen(uint p, Span r, Span b) { int num = (int)p >> 8; r[(int)((p >> 16) - num) & 0xFF]++; b[(int)(p - num) & 0xFF]++; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint SearchColorGreedy(Span palette, uint color) { if (color == palette[0]) { return 0u; } if (color == palette[1]) { return 1u; } if (color == palette[2]) { return 2u; } return 3u; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint ApplyPaletteHash0(uint color) { return (color >> 8) & 0xFF; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint ApplyPaletteHash1(uint color) { return (uint)((long)(color & 0xFFFFFF) * 4222244071L) >> 21; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint ApplyPaletteHash2(uint color) { return (uint)((long)(color & 0xFFFFFF) * 2147483647L) >> 21; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint HashPix(uint pix) { return (uint)((((long)pix + (long)(pix >> 19)) * 969276327) & 0xFFFFFFFFu) >> 24; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int PaletteCompareColorsForSort(uint p1, uint p2) { if (p1 >= p2) { return 1; } return -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint PaletteComponentDistance(uint v) { if (v > 128) { return 256 - v; } return v; } public void AllocateTransformBuffer(int width, int height) { int length = (UsePredictorTransform ? ((width + 1) * 2 + (width * 2 + 4 - 1) / 4) : 0); int length2 = ((UsePredictorTransform || UseCrossColorTransform) ? (LosslessUtils.SubSampleSize(width, TransformBits) * LosslessUtils.SubSampleSize(height, TransformBits)) : 0); BgraScratch = memoryAllocator.Allocate(length); TransformData = memoryAllocator.Allocate(length2); CurrentWidth = width; } public void ClearRefs() { for (int i = 0; i < Refs.Length; i++) { Refs[i].Refs.Clear(); } } public void Dispose() { Bgra.Dispose(); EncodedData.Dispose(); BgraScratch.Dispose(); Palette.Dispose(); TransformData.Dispose(); HashChain.Dispose(); } } internal sealed class Vp8LHashChain : IDisposable { private const uint HashMultiplierHi = 3332679571u; private const uint HashMultiplierLo = 1540483478u; private const int HashBits = 18; private const int HashSize = 262144; private const int WindowSizeBits = 20; private const int WindowSize = 1048456; private readonly MemoryAllocator memoryAllocator; public IMemoryOwner OffsetLength { get; } public int Size { get; } public Vp8LHashChain(MemoryAllocator memoryAllocator, int size) { this.memoryAllocator = memoryAllocator; OffsetLength = this.memoryAllocator.Allocate(size, AllocationOptions.Clean); Size = size; } public void Fill(ReadOnlySpan bgra, int quality, int xSize, int ySize, bool lowEffort) { int num = xSize * ySize; int maxItersForQuality = GetMaxItersForQuality(quality); int windowSizeForHashChain = GetWindowSizeForHashChain(quality, xSize); if (num <= 2) { OffsetLength.GetSpan()[0] = 0u; return; } using IMemoryOwner buffer = memoryAllocator.Allocate(262144); using IMemoryOwner buffer2 = memoryAllocator.Allocate(num, AllocationOptions.Clean); Span span = buffer.GetSpan(); Span span2 = buffer2.GetSpan(); span.Fill(-1); bool flag = bgra.Length > 1 && bgra[0] == bgra[1]; Span span3 = stackalloc uint[2]; int num2 = 0; while (num2 < num - 2) { bool flag2 = bgra[num2 + 1] == bgra[num2 + 2]; if (flag && flag2) { span3.Clear(); uint num3 = 1u; span3[0] = bgra[num2]; for (; num2 + (int)num3 + 2 < num && bgra[(int)(num2 + num3 + 2)] == bgra[num2]; num3++) { } if (num3 > 4095) { num2 += (int)(num3 - 4095); num3 = 4095u; } while (num3 != 0) { span3[1] = num3--; uint pixPairHash = GetPixPairHash64(span3); span2[num2] = span[(int)pixPairHash]; span[(int)pixPairHash] = num2++; } flag = false; } else { uint pixPairHash = GetPixPairHash64(bgra.Slice(num2)); span2[num2] = span[(int)pixPairHash]; span[(int)pixPairHash] = num2++; flag = flag2; } } span2[num2] = span[(int)GetPixPairHash64(bgra.Slice(num2))]; Span span4 = OffsetLength.GetSpan(); span4[0] = (span4[num - 1] = 0u); int num4 = num - 2; while (num4 > 0) { int num5 = LosslessUtils.MaxFindCopyLength(num - 1 - num4); int num6 = num4; int num7 = maxItersForQuality; int num8 = 0; uint num9 = 0u; int num10 = ((num4 > windowSizeForHashChain) ? (num4 - windowSizeForHashChain) : 0); int num11 = ((num5 < 256) ? num5 : 256); num2 = span2[num4]; if (!lowEffort) { int num12; if (num4 >= (uint)xSize) { num12 = LosslessUtils.FindMatchLength(bgra.Slice(num6 - xSize), bgra.Slice(num6), num8, num5); if (num12 > num8) { num8 = num12; num9 = (uint)xSize; } num7--; } num12 = LosslessUtils.FindMatchLength(bgra.Slice(num6 - 1), bgra.Slice(num6), num8, num5); if (num12 > num8) { num8 = num12; num9 = 1u; } num7--; if (num8 == 4095) { num2 = num10 - 1; } } uint num13 = bgra.Slice(num6)[num8]; while (num2 >= num10 && --num7 > 0) { if (bgra[num2 + num8] == num13) { int num12 = LosslessUtils.VectorMismatch(bgra.Slice(num2), bgra.Slice(num6), num5); if (num8 < num12) { num8 = num12; num9 = (uint)(num4 - num2); num13 = bgra.Slice(num6)[num8]; if (num8 >= num11) { break; } } } num2 = span2[num2]; } uint num14 = (uint)num4; while (true) { span4[num4] = (num9 << 12) | (uint)num8; num4--; if (num9 == 0 || num4 == 0 || num4 < num9 || bgra[(int)(num4 - num9)] != bgra[num4] || (num8 == 4095 && num9 != 1 && num4 + 4095 < num14)) { break; } if (num8 < 4095) { num8++; num14 = (uint)num4; } } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int FindLength(int basePosition) { return (int)(OffsetLength.GetSpan()[basePosition] & 0xFFF); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int FindOffset(int basePosition) { return (int)(OffsetLength.GetSpan()[basePosition] >> 12); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint GetPixPairHash64(ReadOnlySpan bgra) { return (uint)((int)bgra[1] * -962287725 + (int)(bgra[0] * 1540483478)) >> 14; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetMaxItersForQuality(int quality) { return 8 + quality * quality / 128; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetWindowSizeForHashChain(int quality, int xSize) { int num = ((quality > 75) ? 1048456 : ((quality > 50) ? (xSize << 8) : ((quality > 25) ? (xSize << 6) : (xSize << 4)))); if (num <= 1048456) { return num; } return 1048456; } public void Dispose() { OffsetLength.Dispose(); } } internal sealed class Vp8LHistogram : IDeepCloneable { private const uint NonTrivialSym = uint.MaxValue; public int PaletteCodeBits { get; set; } public double BitCost { get; set; } public double LiteralCost { get; set; } public double RedCost { get; set; } public double BlueCost { get; set; } public uint[] Red { get; } public uint[] Blue { get; } public uint[] Alpha { get; } public uint[] Literal { get; } public uint[] Distance { get; } public uint TrivialSymbol { get; set; } public bool[] IsUsed { get; } private Vp8LHistogram(Vp8LHistogram other) : this(other.PaletteCodeBits) { other.Red.AsSpan().CopyTo(Red); other.Blue.AsSpan().CopyTo(Blue); other.Alpha.AsSpan().CopyTo(Alpha); other.Literal.AsSpan().CopyTo(Literal); other.Distance.AsSpan().CopyTo(Distance); other.IsUsed.AsSpan().CopyTo(IsUsed); LiteralCost = other.LiteralCost; RedCost = other.RedCost; BlueCost = other.BlueCost; BitCost = other.BitCost; TrivialSymbol = other.TrivialSymbol; PaletteCodeBits = other.PaletteCodeBits; } public Vp8LHistogram(Vp8LBackwardRefs refs, int paletteCodeBits) : this(paletteCodeBits) { StoreRefs(refs); } public Vp8LHistogram(int paletteCodeBits) { PaletteCodeBits = paletteCodeBits; Red = new uint[257]; Blue = new uint[257]; Alpha = new uint[257]; Distance = new uint[40]; int num = 1304; Literal = new uint[num + 1]; IsUsed = new bool[5]; } public IDeepCloneable DeepClone() { return new Vp8LHistogram(this); } public void StoreRefs(Vp8LBackwardRefs refs) { foreach (PixOrCopy @ref in refs.Refs) { AddSinglePixOrCopy(@ref, useDistanceModifier: false); } } public void AddSinglePixOrCopy(PixOrCopy v, bool useDistanceModifier, int xSize = 0) { if (v.IsLiteral()) { Alpha[v.Literal(3)]++; Red[v.Literal(2)]++; Literal[v.Literal(1)]++; Blue[v.Literal(0)]++; } else if (v.IsCacheIdx()) { int num = (int)(280 + v.CacheIdx()); Literal[num]++; } else { int extraBits = 0; int num2 = LosslessUtils.PrefixEncodeBits(v.Length(), ref extraBits); Literal[256 + num2]++; num2 = (useDistanceModifier ? LosslessUtils.PrefixEncodeBits(BackwardReferenceEncoder.DistanceToPlaneCode(xSize, (int)v.Distance()), ref extraBits) : LosslessUtils.PrefixEncodeBits((int)v.Distance(), ref extraBits)); Distance[num2]++; } } public int NumCodes() { return 280 + ((PaletteCodeBits > 0) ? (1 << PaletteCodeBits) : 0); } public double EstimateBits(Vp8LStreaks stats, Vp8LBitEntropy bitsEntropy) { uint trivialSym = 0u; return PopulationCost(Literal, NumCodes(), ref trivialSym, ref IsUsed[0], stats, bitsEntropy) + PopulationCost(Red, 256, ref trivialSym, ref IsUsed[1], stats, bitsEntropy) + PopulationCost(Blue, 256, ref trivialSym, ref IsUsed[2], stats, bitsEntropy) + PopulationCost(Alpha, 256, ref trivialSym, ref IsUsed[3], stats, bitsEntropy) + PopulationCost(Distance, 40, ref trivialSym, ref IsUsed[4], stats, bitsEntropy) + ExtraCost(Literal.AsSpan(256), 24) + ExtraCost(Distance, 40); } public void UpdateHistogramCost(Vp8LStreaks stats, Vp8LBitEntropy bitsEntropy) { uint trivialSym = 0u; uint trivialSym2 = 0u; uint trivialSym3 = 0u; uint trivialSym4 = 0u; double num = PopulationCost(Alpha, 256, ref trivialSym, ref IsUsed[3], stats, bitsEntropy); double num2 = PopulationCost(Distance, 40, ref trivialSym4, ref IsUsed[4], stats, bitsEntropy) + ExtraCost(Distance, 40); int length = NumCodes(); LiteralCost = PopulationCost(Literal, length, ref trivialSym4, ref IsUsed[0], stats, bitsEntropy) + ExtraCost(Literal.AsSpan(256), 24); RedCost = PopulationCost(Red, 256, ref trivialSym2, ref IsUsed[1], stats, bitsEntropy); BlueCost = PopulationCost(Blue, 256, ref trivialSym3, ref IsUsed[2], stats, bitsEntropy); BitCost = LiteralCost + RedCost + BlueCost + num + num2; if ((trivialSym | trivialSym2 | trivialSym3) == uint.MaxValue) { TrivialSymbol = uint.MaxValue; } else { TrivialSymbol = (trivialSym << 24) | (trivialSym2 << 16) | trivialSym3; } } public double AddEval(Vp8LHistogram b, Vp8LStreaks stats, Vp8LBitEntropy bitsEntropy, double costThreshold, Vp8LHistogram output) { double num = BitCost + b.BitCost; costThreshold += num; if (GetCombinedHistogramEntropy(b, stats, bitsEntropy, costThreshold, 0.0, out var cost)) { Add(b, output); output.BitCost = cost; output.PaletteCodeBits = PaletteCodeBits; } return cost - num; } public double AddThresh(Vp8LHistogram b, Vp8LStreaks stats, Vp8LBitEntropy bitsEntropy, double costThreshold) { double costInitial = 0.0 - BitCost; GetCombinedHistogramEntropy(b, stats, bitsEntropy, costThreshold, costInitial, out var cost); return cost; } public void Add(Vp8LHistogram b, Vp8LHistogram output) { int literalSize = NumCodes(); AddLiteral(b, output, literalSize); AddRed(b, output, 256); AddBlue(b, output, 256); AddAlpha(b, output, 256); AddDistance(b, output, 40); for (int i = 0; i < 5; i++) { output.IsUsed[i] = IsUsed[i] | b.IsUsed[i]; } output.TrivialSymbol = ((TrivialSymbol == b.TrivialSymbol) ? TrivialSymbol : uint.MaxValue); } public bool GetCombinedHistogramEntropy(Vp8LHistogram b, Vp8LStreaks stats, Vp8LBitEntropy bitEntropy, double costThreshold, double costInitial, out double cost) { bool trivialAtEnd = false; cost = costInitial; cost += GetCombinedEntropy(Literal, b.Literal, NumCodes(), IsUsed[0], b.IsUsed[0], trivialAtEnd: false, stats, bitEntropy); cost += ExtraCostCombined(Literal.AsSpan(256), b.Literal.AsSpan(256), 24); if (cost > costThreshold) { return false; } if (TrivialSymbol != uint.MaxValue && TrivialSymbol == b.TrivialSymbol) { uint num = (TrivialSymbol >> 24) & 0xFF; uint num2 = (TrivialSymbol >> 16) & 0xFF; uint num3 = TrivialSymbol & 0xFF; if ((num == 0 || num == 255) && (num2 == 0 || num2 == 255) && (num3 == 0 || num3 == 255)) { trivialAtEnd = true; } } cost += GetCombinedEntropy(Red, b.Red, 256, IsUsed[1], b.IsUsed[1], trivialAtEnd, stats, bitEntropy); if (cost > costThreshold) { return false; } cost += GetCombinedEntropy(Blue, b.Blue, 256, IsUsed[2], b.IsUsed[2], trivialAtEnd, stats, bitEntropy); if (cost > costThreshold) { return false; } cost += GetCombinedEntropy(Alpha, b.Alpha, 256, IsUsed[3], b.IsUsed[3], trivialAtEnd, stats, bitEntropy); if (cost > costThreshold) { return false; } cost += GetCombinedEntropy(Distance, b.Distance, 40, IsUsed[4], b.IsUsed[4], trivialAtEnd: false, stats, bitEntropy); if (cost > costThreshold) { return false; } cost += ExtraCostCombined(Distance, b.Distance, 40); if (cost > costThreshold) { return false; } return true; } private void AddLiteral(Vp8LHistogram b, Vp8LHistogram output, int literalSize) { if (IsUsed[0]) { if (b.IsUsed[0]) { AddVector(Literal, b.Literal, output.Literal, literalSize); } else { Literal.AsSpan(0, literalSize).CopyTo(output.Literal); } } else if (b.IsUsed[0]) { b.Literal.AsSpan(0, literalSize).CopyTo(output.Literal); } else { output.Literal.AsSpan(0, literalSize).Clear(); } } private void AddRed(Vp8LHistogram b, Vp8LHistogram output, int size) { if (IsUsed[1]) { if (b.IsUsed[1]) { AddVector(Red, b.Red, output.Red, size); } else { Red.AsSpan(0, size).CopyTo(output.Red); } } else if (b.IsUsed[1]) { b.Red.AsSpan(0, size).CopyTo(output.Red); } else { output.Red.AsSpan(0, size).Clear(); } } private void AddBlue(Vp8LHistogram b, Vp8LHistogram output, int size) { if (IsUsed[2]) { if (b.IsUsed[2]) { AddVector(Blue, b.Blue, output.Blue, size); } else { Blue.AsSpan(0, size).CopyTo(output.Blue); } } else if (b.IsUsed[2]) { b.Blue.AsSpan(0, size).CopyTo(output.Blue); } else { output.Blue.AsSpan(0, size).Clear(); } } private void AddAlpha(Vp8LHistogram b, Vp8LHistogram output, int size) { if (IsUsed[3]) { if (b.IsUsed[3]) { AddVector(Alpha, b.Alpha, output.Alpha, size); } else { Alpha.AsSpan(0, size).CopyTo(output.Alpha); } } else if (b.IsUsed[3]) { b.Alpha.AsSpan(0, size).CopyTo(output.Alpha); } else { output.Alpha.AsSpan(0, size).Clear(); } } private void AddDistance(Vp8LHistogram b, Vp8LHistogram output, int size) { if (IsUsed[4]) { if (b.IsUsed[4]) { AddVector(Distance, b.Distance, output.Distance, size); } else { Distance.AsSpan(0, size).CopyTo(output.Distance); } } else if (b.IsUsed[4]) { b.Distance.AsSpan(0, size).CopyTo(output.Distance); } else { output.Distance.AsSpan(0, size).Clear(); } } private static double GetCombinedEntropy(uint[] x, uint[] y, int length, bool isXUsed, bool isYUsed, bool trivialAtEnd, Vp8LStreaks stats, Vp8LBitEntropy bitEntropy) { stats.Clear(); bitEntropy.Init(); if (trivialAtEnd) { stats.Streaks[1][0] = 1; stats.Counts[0] = 1; stats.Streaks[0][1] = length - 1; return stats.FinalHuffmanCost(); } if (isXUsed) { if (isYUsed) { bitEntropy.GetCombinedEntropyUnrefined(x, y, length, stats); } else { bitEntropy.GetEntropyUnrefined(x, length, stats); } } else if (isYUsed) { bitEntropy.GetEntropyUnrefined(y, length, stats); } else { stats.Counts[0] = 1; stats.Streaks[0][(length > 3) ? 1u : 0u] = length; bitEntropy.Init(); } return bitEntropy.BitsEntropyRefine() + stats.FinalHuffmanCost(); } private static double ExtraCostCombined(Span x, Span y, int length) { double num = 0.0; for (int i = 2; i < length - 2; i++) { int num2 = (int)(x[i + 2] + y[i + 2]); num += (double)((i >> 1) * num2); } return num; } private static double PopulationCost(uint[] population, int length, ref uint trivialSym, ref bool isUsed, Vp8LStreaks stats, Vp8LBitEntropy bitEntropy) { bitEntropy.Init(); stats.Clear(); bitEntropy.BitsEntropyUnrefined(population, length, stats); trivialSym = ((bitEntropy.NoneZeros == 1) ? bitEntropy.NoneZeroCode : uint.MaxValue); isUsed = stats.Streaks[1][0] != 0 || stats.Streaks[1][1] != 0; return bitEntropy.BitsEntropyRefine() + stats.FinalHuffmanCost(); } private static double ExtraCost(Span population, int length) { double num = 0.0; for (int i = 2; i < length - 2; i++) { num += (double)((i >> 1) * population[i + 2]); } return num; } private static void AddVector(Span a, Span b, Span output, int count) { if (Avx2.IsSupported) { ref uint reference = ref MemoryMarshal.GetReference(a); ref uint reference2 = ref MemoryMarshal.GetReference(b); ref uint reference3 = ref MemoryMarshal.GetReference(output); int i; for (i = 0; i + 32 <= count; i += 32) { Vector256 left = Unsafe.As>(ref Unsafe.Add(ref reference, i)); Vector256 left2 = Unsafe.As>(ref Unsafe.Add(ref reference, i + 8)); Vector256 left3 = Unsafe.As>(ref Unsafe.Add(ref reference, i + 16)); Vector256 left4 = Unsafe.As>(ref Unsafe.Add(ref reference, i + 24)); Vector256 right = Unsafe.As>(ref Unsafe.Add(ref reference2, i)); Vector256 right2 = Unsafe.As>(ref Unsafe.Add(ref reference2, i + 8)); Vector256 right3 = Unsafe.As>(ref Unsafe.Add(ref reference2, i + 16)); Vector256 right4 = Unsafe.As>(ref Unsafe.Add(ref reference2, i + 24)); Unsafe.As>(ref Unsafe.Add(ref reference3, i)) = Avx2.Add(left, right); Unsafe.As>(ref Unsafe.Add(ref reference3, i + 8)) = Avx2.Add(left2, right2); Unsafe.As>(ref Unsafe.Add(ref reference3, i + 16)) = Avx2.Add(left3, right3); Unsafe.As>(ref Unsafe.Add(ref reference3, i + 24)) = Avx2.Add(left4, right4); } for (; i < count; i++) { output[i] = a[i] + b[i]; } } else { for (int j = 0; j < count; j++) { output[j] = a[j] + b[j]; } } } } internal enum Vp8LLz77Type { Lz77Standard = 1, Lz77Rle = 2, Lz77Box = 4 } internal class Vp8LMetadata { public int ColorCacheSize { get; set; } public ColorCache ColorCache { get; set; } public int HuffmanMask { get; set; } public int HuffmanSubSampleBits { get; set; } public int HuffmanXSize { get; set; } public IMemoryOwner HuffmanImage { get; set; } public int NumHTreeGroups { get; set; } public HTreeGroup[] HTreeGroups { get; set; } public HuffmanCode[] HuffmanTables { get; set; } } internal struct Vp8LMultipliers { public byte GreenToRed; public byte GreenToBlue; public byte RedToBlue; } internal class Vp8LStreaks { public int[] Counts { get; } public int[][] Streaks { get; } public Vp8LStreaks() { Counts = new int[2]; Streaks = new int[2][]; Streaks[0] = new int[2]; Streaks[1] = new int[2]; } public void Clear() { Counts.AsSpan().Clear(); Streaks[0].AsSpan().Clear(); Streaks[1].AsSpan().Clear(); } public double FinalHuffmanCost() { return InitialHuffmanCost() + ((double)Counts[0] * 1.5625 + 15.0 / 64.0 * (double)Streaks[0][1]) + ((double)Counts[1] * 2.578125 + 45.0 / 64.0 * (double)Streaks[1][1]) + 1.796875 * (double)Streaks[0][0] + 3.28125 * (double)Streaks[1][0]; } private static double InitialHuffmanCost() { double num = 9.1; return 57.0 - num; } } [DebuggerDisplay("Transformtype: {TransformType}")] internal class Vp8LTransform { public Vp8LTransformType TransformType { get; } public int Bits { get; set; } public int XSize { get; set; } public int YSize { get; } public IMemoryOwner Data { get; set; } public Vp8LTransform(Vp8LTransformType transformType, int xSize, int ySize) { TransformType = transformType; XSize = xSize; YSize = ySize; } } internal enum Vp8LTransformType : uint { PredictorTransform, CrossColorTransform, SubtractGreen, ColorIndexingTransform } internal sealed class WebpLosslessDecoder { private readonly Vp8LBitReader bitReader; private readonly Configuration configuration; private readonly MemoryAllocator memoryAllocator; private const int BitsSpecialMarker = 256; private const uint PackedNonLiteralCode = 0u; private static readonly int CodeToPlaneCodes = WebpLookupTables.CodeToPlane.Length; private const int FixedTableSize = 2300; private static readonly int[] TableSize = new int[12] { 2954, 2956, 2958, 2962, 2970, 2986, 3018, 3082, 3212, 3468, 3980, 5004 }; private static readonly int NumCodeLengthCodes = CodeLengthCodeOrder.Length; private static ReadOnlySpan CodeLengthCodeOrder => new byte[19] { 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; private static ReadOnlySpan LiteralMap => new byte[5] { 0, 1, 1, 1, 0 }; public WebpLosslessDecoder(Vp8LBitReader bitReader, MemoryAllocator memoryAllocator, Configuration configuration) { this.bitReader = bitReader; this.memoryAllocator = memoryAllocator; this.configuration = configuration; } public void Decode(Buffer2D pixels, int width, int height) where TPixel : unmanaged, IPixel { using Vp8LDecoder vp8LDecoder = new Vp8LDecoder(width, height, memoryAllocator); DecodeImageStream(vp8LDecoder, width, height, isLevel0: true); DecodeImageData(vp8LDecoder, vp8LDecoder.Pixels.Memory.Span); DecodePixelValues(vp8LDecoder, pixels, width, height); } public IMemoryOwner DecodeImageStream(Vp8LDecoder decoder, int xSize, int ySize, bool isLevel0) { int num = xSize; int num2 = 0; if (isLevel0) { decoder.Transforms = new List(4); while (bitReader.ReadBit()) { if (num2 > 4) { WebpThrowHelper.ThrowImageFormatException($"The maximum number of transforms of {4} was exceeded"); } ReadTransformation(num, ySize, decoder); if (decoder.Transforms[num2].TransformType == Vp8LTransformType.ColorIndexingTransform) { num = LosslessUtils.SubSampleSize(num, decoder.Transforms[num2].Bits); } num2++; } } else { decoder.Metadata = new Vp8LMetadata(); } bool num3 = bitReader.ReadBit(); int num4 = 0; int colorCacheSize = 0; if (num3) { num4 = (int)bitReader.ReadValue(4); if (num4 < 1 || num4 > 11) { WebpThrowHelper.ThrowImageFormatException("Invalid color cache bits found"); } } ReadHuffmanCodes(decoder, num, ySize, num4, isLevel0); decoder.Metadata.ColorCacheSize = colorCacheSize; if (num3) { decoder.Metadata.ColorCache = new ColorCache(); colorCacheSize = 1 << num4; decoder.Metadata.ColorCacheSize = colorCacheSize; decoder.Metadata.ColorCache.Init(num4); } else { decoder.Metadata.ColorCacheSize = 0; } UpdateDecoder(decoder, num, ySize); if (isLevel0) { return null; } IMemoryOwner memoryOwner = memoryAllocator.Allocate(decoder.Width * decoder.Height, AllocationOptions.Clean); DecodeImageData(decoder, memoryOwner.GetSpan()); return memoryOwner; } private void DecodePixelValues(Vp8LDecoder decoder, Buffer2D pixels, int width, int height) where TPixel : unmanaged, IPixel { Span span = decoder.Pixels.GetSpan(); ApplyInverseTransforms(decoder, span, memoryAllocator); Span span2 = MemoryMarshal.Cast(span); int num = width * 4; for (int i = 0; i < height; i++) { Span span3 = span2.Slice(i * num, num); Span span4 = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromBgra32Bytes(configuration, span3.Slice(0, num), span4.Slice(0, width), width); } } public void DecodeImageData(Vp8LDecoder decoder, Span pixelData) { int width = decoder.Width; int height = decoder.Height; int row = 0 / width; int col = 0 % width; int colorCacheSize = decoder.Metadata.ColorCacheSize; ColorCache colorCache = decoder.Metadata.ColorCache; int num = 280 + colorCacheSize; int huffmanMask = decoder.Metadata.HuffmanMask; Span hTreeGroupForPos = GetHTreeGroupForPos(decoder.Metadata, col, row); int num2 = width * height; int decodedPixels = 0; int i = decodedPixels; while (decodedPixels < num2) { if ((col & huffmanMask) == 0) { hTreeGroupForPos = GetHTreeGroupForPos(decoder.Metadata, col, row); } if (hTreeGroupForPos[0].IsTrivialCode) { pixelData[decodedPixels] = hTreeGroupForPos[0].LiteralArb; AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref i); continue; } bitReader.FillBitWindow(); int num3; if (hTreeGroupForPos[0].UsePackedTable) { num3 = (int)ReadPackedSymbols(hTreeGroupForPos, pixelData, decodedPixels); if (bitReader.IsEndOfStream()) { break; } if (num3 == 0L) { AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref i); continue; } } else { num3 = (int)ReadSymbol(hTreeGroupForPos[0].HTrees[0]); } if (bitReader.IsEndOfStream()) { break; } if (num3 < 256) { if (hTreeGroupForPos[0].IsTrivialLiteral) { pixelData[decodedPixels] = hTreeGroupForPos[0].LiteralArb | (uint)(num3 << 8); } else { uint num4 = ReadSymbol(hTreeGroupForPos[0].HTrees[1]); bitReader.FillBitWindow(); uint num5 = ReadSymbol(hTreeGroupForPos[0].HTrees[2]); uint num6 = ReadSymbol(hTreeGroupForPos[0].HTrees[3]); if (bitReader.IsEndOfStream()) { break; } pixelData[decodedPixels] = (uint)(((byte)num6 << 24) | ((byte)num4 << 16) | ((byte)num3 << 8) | (byte)num5); } AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref i); } else if (num3 < 280) { int lengthSymbol = num3 - 256; int copyLength = GetCopyLength(lengthSymbol); uint distanceSymbol = ReadSymbol(hTreeGroupForPos[0].HTrees[4]); bitReader.FillBitWindow(); int copyDistance = GetCopyDistance((int)distanceSymbol); int dist = PlaneCodeToDistance(width, copyDistance); if (bitReader.IsEndOfStream()) { break; } CopyBlock(pixelData, decodedPixels, dist, copyLength); decodedPixels += copyLength; col += copyLength; while (col >= width) { col -= width; row++; } if ((col & huffmanMask) != 0) { hTreeGroupForPos = GetHTreeGroupForPos(decoder.Metadata, col, row); } if (colorCache != null) { for (; i < decodedPixels; i++) { colorCache.Insert(pixelData[i]); } } } else if (num3 < num) { int key = num3 - 280; for (; i < decodedPixels; i++) { colorCache.Insert(pixelData[i]); } pixelData[decodedPixels] = colorCache.Lookup(key); AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref i); } else { WebpThrowHelper.ThrowImageFormatException("Webp parsing error"); } } } private void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, Span pixelData, ref int lastCached) { col++; decodedPixels++; if (col < width) { return; } col = 0; row++; if (colorCache != null) { while (lastCached < decodedPixels) { colorCache.Insert(pixelData[lastCached]); lastCached++; } } } private void ReadHuffmanCodes(Vp8LDecoder decoder, int xSize, int ySize, int colorCacheBits, bool allowRecursion) { int num = 0; int num2 = 1; int num3 = 1; if (allowRecursion && bitReader.ReadBit()) { int num4 = (int)(bitReader.ReadValue(3) + 2); int num5 = LosslessUtils.SubSampleSize(xSize, num4); int num6 = LosslessUtils.SubSampleSize(ySize, num4); int num7 = num5 * num6; IMemoryOwner memoryOwner = DecodeImageStream(decoder, num5, num6, isLevel0: false); Span span = memoryOwner.GetSpan(); decoder.Metadata.HuffmanSubSampleBits = num4; for (int i = 0; i < num7; i++) { uint num8 = (span[i] >> 8) & 0xFFFF; span[i] = num8; if (num8 >= num3) { num3 = (int)(num8 + 1); } } num2 = num3; decoder.Metadata.HuffmanImage = memoryOwner; } for (int j = 0; j < 5; j++) { int num9 = WebpConstants.AlphabetSize[j]; if (j == 0 && colorCacheBits > 0) { num9 += 1 << colorCacheBits; } if (num < num9) { num = num9; } } int num10 = TableSize[colorCacheBits]; HuffmanCode[] array = new HuffmanCode[num2 * num10]; HTreeGroup[] array2 = new HTreeGroup[num2]; Span table = array.AsSpan(); int[] array3 = new int[num]; for (int k = 0; k < num3; k++) { array2[k] = new HTreeGroup(64u); HTreeGroup hTreeGroup = array2[k]; int num11 = 0; bool flag = true; int num12 = 0; array3.AsSpan().Clear(); for (int l = 0; l < 5; l++) { int num13 = WebpConstants.AlphabetSize[l]; if (l == 0 && colorCacheBits > 0) { num13 += 1 << colorCacheBits; } int num14 = ReadHuffmanCode(num13, array3, table); if (num14 == 0) { WebpThrowHelper.ThrowImageFormatException("Huffman table size is zero"); } hTreeGroup.HTrees.Add(table.Slice(0, num14).ToArray()); HuffmanCode huffmanCode = table[0]; if (flag && LiteralMap[l] == 1) { flag = huffmanCode.BitsUsed == 0; } num11 += huffmanCode.BitsUsed; table = table.Slice(num14); if (l > 3) { continue; } int num15 = array3[0]; for (int m = 1; m < num13; m++) { int num16 = array3[m]; if (num16 > num15) { num15 = num16; } } num12 += num15; } hTreeGroup.IsTrivialLiteral = flag; hTreeGroup.IsTrivialCode = false; if (flag) { uint value = hTreeGroup.HTrees[1][0].Value; uint value2 = hTreeGroup.HTrees[2][0].Value; uint value3 = hTreeGroup.HTrees[0][0].Value; uint value4 = hTreeGroup.HTrees[3][0].Value; hTreeGroup.LiteralArb = (value4 << 24) | (value << 16) | value2; if (num11 == 0 && value3 < 256) { hTreeGroup.IsTrivialCode = true; hTreeGroup.LiteralArb |= value3 << 8; } } hTreeGroup.UsePackedTable = !hTreeGroup.IsTrivialCode && num12 < 6; if (hTreeGroup.UsePackedTable) { BuildPackedTable(hTreeGroup); } } decoder.Metadata.NumHTreeGroups = num2; decoder.Metadata.HTreeGroups = array2; decoder.Metadata.HuffmanTables = array; } private int ReadHuffmanCode(int alphabetSize, int[] codeLengths, Span table) { bool flag = bitReader.ReadBit(); for (int i = 0; i < alphabetSize; i++) { codeLengths[i] = 0; } if (flag) { uint num = bitReader.ReadValue(1) + 1; uint num2 = bitReader.ReadValue(1); uint num3 = bitReader.ReadValue((num2 == 0) ? 1 : 8); codeLengths[num3] = 1; if (num == 2) { num3 = bitReader.ReadValue(8); codeLengths[num3] = 1; } } else { int[] array = new int[NumCodeLengthCodes]; uint num4 = bitReader.ReadValue(4) + 4; if (num4 > NumCodeLengthCodes) { WebpThrowHelper.ThrowImageFormatException("Bitstream error, numCodes has an invalid value"); } for (int j = 0; j < num4; j++) { array[CodeLengthCodeOrder[j]] = (int)bitReader.ReadValue(3); } ReadHuffmanCodeLengths(table, array, alphabetSize, codeLengths); } return HuffmanUtils.BuildHuffmanTable(table, 8, codeLengths, alphabetSize); } private void ReadHuffmanCodeLengths(Span table, int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths) { int num = 0; int num2 = 8; if (HuffmanUtils.BuildHuffmanTable(table, 7, codeLengthCodeLengths, NumCodeLengthCodes) == 0) { WebpThrowHelper.ThrowImageFormatException("Error building huffman table"); } int num3; if (bitReader.ReadBit()) { int nBits = (int)(2 + 2 * bitReader.ReadValue(3)); num3 = (int)(2 + bitReader.ReadValue(nBits)); } else { num3 = numSymbols; } while (num < numSymbols && num3-- != 0) { bitReader.FillBitWindow(); int index = (int)(bitReader.PrefetchBits() & 0x7F); HuffmanCode huffmanCode = table[index]; bitReader.AdvanceBitPosition(huffmanCode.BitsUsed); uint value = huffmanCode.Value; if (value < 16) { codeLengths[num++] = (int)value; if (value != 0) { num2 = (int)value; } continue; } bool flag = value == 16; uint num4 = value - 16; int nBits2 = WebpConstants.CodeLengthExtraBits[num4]; int num5 = WebpConstants.CodeLengthRepeatOffsets[num4]; int num6 = (int)(bitReader.ReadValue(nBits2) + num5); if (num + num6 > numSymbols) { break; } int num7 = (flag ? num2 : 0); while (num6-- > 0) { codeLengths[num++] = num7; } } } private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder) { Vp8LTransformType vp8LTransformType = (Vp8LTransformType)bitReader.ReadValue(2); Vp8LTransform vp8LTransform = new Vp8LTransform(vp8LTransformType, xSize, ySize); foreach (Vp8LTransform transform in decoder.Transforms) { if (transform.TransformType == vp8LTransform.TransformType) { WebpThrowHelper.ThrowImageFormatException("Each transform can only be present once"); } } switch (vp8LTransformType) { case Vp8LTransformType.ColorIndexingTransform: { uint num = bitReader.ReadValue(8) + 1; int num2; switch (num) { case 0u: case 1u: case 2u: num2 = 3; break; case 3u: case 4u: num2 = 2; break; case 5u: case 6u: case 7u: case 8u: case 9u: case 10u: case 11u: case 12u: case 13u: case 14u: case 15u: case 16u: num2 = 1; break; default: num2 = 0; break; } int bits = num2; vp8LTransform.Bits = bits; using (IMemoryOwner buffer = DecodeImageStream(decoder, (int)num, 1, isLevel0: false)) { int length = 1 << (8 >> vp8LTransform.Bits); IMemoryOwner memoryOwner = memoryAllocator.Allocate(length, AllocationOptions.Clean); LosslessUtils.ExpandColorMap((int)num, buffer.GetSpan(), memoryOwner.GetSpan()); vp8LTransform.Data = memoryOwner; } break; } case Vp8LTransformType.PredictorTransform: case Vp8LTransformType.CrossColorTransform: { vp8LTransform.Bits = (int)(bitReader.ReadValue(3) + 2); int xSize2 = LosslessUtils.SubSampleSize(vp8LTransform.XSize, vp8LTransform.Bits); int ySize2 = LosslessUtils.SubSampleSize(vp8LTransform.YSize, vp8LTransform.Bits); IMemoryOwner data = DecodeImageStream(decoder, xSize2, ySize2, isLevel0: false); vp8LTransform.Data = data; break; } } decoder.Transforms.Add(vp8LTransform); } public static void ApplyInverseTransforms(Vp8LDecoder decoder, Span pixelData, MemoryAllocator memoryAllocator) { List transforms = decoder.Transforms; for (int num = transforms.Count - 1; num >= 0; num--) { Vp8LTransform vp8LTransform = transforms[num]; switch (vp8LTransform.TransformType) { case Vp8LTransformType.PredictorTransform: { using (IMemoryOwner buffer = memoryAllocator.Allocate(pixelData.Length, AllocationOptions.Clean)) { LosslessUtils.PredictorInverseTransform(vp8LTransform, pixelData, buffer.GetSpan()); } break; } case Vp8LTransformType.SubtractGreen: LosslessUtils.AddGreenToBlueAndRed(pixelData); break; case Vp8LTransformType.CrossColorTransform: LosslessUtils.ColorSpaceInverseTransform(vp8LTransform, pixelData); break; case Vp8LTransformType.ColorIndexingTransform: LosslessUtils.ColorIndexInverseTransform(vp8LTransform, pixelData); break; } } } public void DecodeAlphaData(AlphaDecoder dec) { Span data = MemoryMarshal.Cast(dec.Vp8LDec.Pixels.Memory.Span); int num = 0; int num2 = 0; Vp8LDecoder vp8LDec = dec.Vp8LDec; int width = vp8LDec.Width; int height = vp8LDec.Height; Vp8LMetadata metadata = vp8LDec.Metadata; int num3 = 0; int num4 = width * height; int num5 = num4; int num6 = height; int huffmanMask = metadata.HuffmanMask; Span span = ((num3 < num5) ? GetHTreeGroupForPos(metadata, num2, num) : ((Span)null)); while (!bitReader.Eos && num3 < num5) { if ((num2 & huffmanMask) == 0) { span = GetHTreeGroupForPos(metadata, num2, num); } bitReader.FillBitWindow(); int num7 = (int)ReadSymbol(span[0].HTrees[0]); if (num7 < 256) { data[num3] = (byte)num7; num3++; num2++; if (num2 >= width) { num2 = 0; num++; if (num <= num6 && num % 16 == 0) { dec.ExtractPalettedAlphaRows(num); } } } else if (num7 < 280) { int lengthSymbol = num7 - 256; int copyLength = GetCopyLength(lengthSymbol); int distanceSymbol = (int)ReadSymbol(span[0].HTrees[4]); bitReader.FillBitWindow(); int copyDistance = GetCopyDistance(distanceSymbol); int num8 = PlaneCodeToDistance(width, copyDistance); if (num3 >= num8 && num4 - num3 >= copyLength) { CopyBlock8B(data, num3, num8, copyLength); } else { WebpThrowHelper.ThrowImageFormatException("error while decoding alpha data"); } num3 += copyLength; num2 += copyLength; while (num2 >= width) { num2 -= width; num++; if (num <= num6 && num % 16 == 0) { dec.ExtractPalettedAlphaRows(num); } } if (num3 < num5 && (num2 & huffmanMask) > 0) { span = GetHTreeGroupForPos(metadata, num2, num); } } else { WebpThrowHelper.ThrowImageFormatException("bitstream error while parsing alpha data"); } bitReader.Eos = bitReader.IsEndOfStream(); } dec.ExtractPalettedAlphaRows((num > num6) ? num6 : num); } private void UpdateDecoder(Vp8LDecoder decoder, int width, int height) { int huffmanSubSampleBits = decoder.Metadata.HuffmanSubSampleBits; decoder.Width = width; decoder.Height = height; decoder.Metadata.HuffmanXSize = LosslessUtils.SubSampleSize(width, huffmanSubSampleBits); decoder.Metadata.HuffmanMask = ((huffmanSubSampleBits == 0) ? (-1) : ((1 << huffmanSubSampleBits) - 1)); } private uint ReadPackedSymbols(Span group, Span pixelData, int decodedPixels) { uint num = (uint)(bitReader.PrefetchBits() & 0x3F); HuffmanCode huffmanCode = group[0].PackedTable[num]; if (huffmanCode.BitsUsed < 256) { bitReader.AdvanceBitPosition(huffmanCode.BitsUsed); pixelData[decodedPixels] = huffmanCode.Value; return 0u; } bitReader.AdvanceBitPosition(huffmanCode.BitsUsed - 256); return huffmanCode.Value; } private void BuildPackedTable(HTreeGroup hTreeGroup) { for (uint num = 0u; num < 64; num++) { uint num2 = num; ref HuffmanCode reference = ref hTreeGroup.PackedTable[num2]; HuffmanCode hCode = hTreeGroup.HTrees[0][num2]; if (hCode.Value >= 256) { reference.BitsUsed = hCode.BitsUsed + 256; reference.Value = hCode.Value; continue; } reference.BitsUsed = 0; reference.Value = 0u; num2 >>= AccumulateHCode(hCode, 8, ref reference); num2 >>= AccumulateHCode(hTreeGroup.HTrees[1][num2], 16, ref reference); num2 >>= AccumulateHCode(hTreeGroup.HTrees[2][num2], 0, ref reference); num2 >>= AccumulateHCode(hTreeGroup.HTrees[3][num2], 24, ref reference); } } private uint ReadSymbol(Span table) { uint num = (uint)bitReader.PrefetchBits(); Span span = table.Slice((int)(num & 0xFF)); int num2 = span[0].BitsUsed - 8; if (num2 > 0) { bitReader.AdvanceBitPosition(8); num = (uint)bitReader.PrefetchBits(); span = span.Slice((int)span[0].Value); span = span.Slice((int)num & ((1 << num2) - 1)); } bitReader.AdvanceBitPosition(span[0].BitsUsed); return span[0].Value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetCopyLength(int lengthSymbol) { return GetCopyDistance(lengthSymbol); } private int GetCopyDistance(int distanceSymbol) { if (distanceSymbol < 4) { return distanceSymbol + 1; } int num = distanceSymbol - 2 >> 1; return (int)((2 + (distanceSymbol & 1) << num) + bitReader.ReadValue(num) + 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Span GetHTreeGroupForPos(Vp8LMetadata metadata, int x, int y) { uint metaIndex = GetMetaIndex(metadata.HuffmanImage, metadata.HuffmanXSize, metadata.HuffmanSubSampleBits, x, y); return metadata.HTreeGroups.AsSpan((int)metaIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint GetMetaIndex(IMemoryOwner huffmanImage, int xSize, int bits, int x, int y) { if (bits == 0) { return 0u; } return huffmanImage.GetSpan()[xSize * (y >> bits) + (x >> bits)]; } private static int PlaneCodeToDistance(int xSize, int planeCode) { if (planeCode > CodeToPlaneCodes) { return planeCode - CodeToPlaneCodes; } int num = WebpLookupTables.CodeToPlane[planeCode - 1]; int num2 = num >> 4; int num3 = 8 - (num & 0xF); int num4 = num2 * xSize + num3; if (num4 < 1) { return 1; } return num4; } private static void CopyBlock(Span pixelData, int decodedPixels, int dist, int length) { int num = decodedPixels - dist; if (num < 0) { WebpThrowHelper.ThrowImageFormatException("webp image data seems to be invalid"); } if (dist >= length) { Span span = pixelData.Slice(num, length); Span destination = pixelData.Slice(decodedPixels); span.CopyTo(destination); return; } Span span2 = pixelData.Slice(num); Span span3 = pixelData.Slice(decodedPixels); for (int i = 0; i < length; i++) { span3[i] = span2[i]; } } private static void CopyBlock8B(Span data, int pos, int dist, int length) { if (dist >= length) { data.Slice(pos - dist, length).CopyTo(data.Slice(pos)); return; } Span span = data.Slice(pos); Span span2 = data.Slice(pos - dist); for (int i = 0; i < length; i++) { span[i] = span2[i]; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int AccumulateHCode(HuffmanCode hCode, int shift, ref HuffmanCode huff) { huff.BitsUsed += hCode.BitsUsed; huff.Value |= hCode.Value << shift; return hCode.BitsUsed; } } } namespace SixLabors.ImageSharp.Formats.Webp.BitWriter { internal abstract class BitWriterBase { private const uint MaxDimension = 16777215u; private const ulong MaxCanvasPixels = 4294967295uL; protected const uint ExtendedFileChunkSize = 18u; private byte[] buffer; private readonly byte[] scratchBuffer = new byte[4]; public byte[] Buffer => buffer; protected BitWriterBase(int expectedSize) { buffer = new byte[expectedSize]; } private protected BitWriterBase(byte[] buffer) { this.buffer = buffer; } public void WriteToStream(Stream stream) { stream.Write(Buffer.AsSpan(0, NumBytes())); } public void WriteToBuffer(Span dest) { Buffer.AsSpan(0, NumBytes()).CopyTo(dest); } public abstract void BitWriterResize(int extraSize); public abstract int NumBytes(); public abstract void Finish(); protected void ResizeBuffer(int maxBytes, int sizeRequired) { int num = 3 * maxBytes >> 1; if (num < sizeRequired) { num = sizeRequired; } num = (num >> 10) + 1 << 10; Array.Resize(ref buffer, num); } protected void WriteRiffHeader(Stream stream, uint riffSize) { stream.Write(WebpConstants.RiffFourCc); BinaryPrimitives.WriteUInt32LittleEndian(scratchBuffer, riffSize); stream.Write(scratchBuffer.AsSpan(0, 4)); stream.Write(WebpConstants.WebpHeader); } protected uint MetadataChunkSize(byte[] metadataBytes) { uint num = (uint)metadataBytes.Length; return 8 + num + (num & 1); } protected uint AlphaChunkSize(Span alphaBytes) { uint num = (uint)(alphaBytes.Length + 1); return 8 + num + (num & 1); } protected void WriteMetadataProfile(Stream stream, byte[] metadataBytes, WebpChunkType chunkType) { uint num = (uint)metadataBytes.Length; Span span = scratchBuffer.AsSpan(0, 4); BinaryPrimitives.WriteUInt32BigEndian(span, (uint)chunkType); stream.Write(span); BinaryPrimitives.WriteUInt32LittleEndian(span, num); stream.Write(span); stream.Write(metadataBytes); if ((num & 1) == 1) { stream.WriteByte(0); } } protected void WriteAlphaChunk(Stream stream, Span dataBytes, bool alphaDataIsCompressed) { uint num = (uint)(dataBytes.Length + 1); Span span = scratchBuffer.AsSpan(0, 4); BinaryPrimitives.WriteUInt32BigEndian(span, 1095520328u); stream.Write(span); BinaryPrimitives.WriteUInt32LittleEndian(span, num); stream.Write(span); byte b = 0; if (alphaDataIsCompressed) { b |= 1; } stream.WriteByte(b); stream.Write(dataBytes); if ((num & 1) == 1) { stream.WriteByte(0); } } protected void WriteVp8XHeader(Stream stream, ExifProfile exifProfile, XmpProfile xmpProfile, uint width, uint height, bool hasAlpha) { if (width > 16777215 || height > 16777215) { WebpThrowHelper.ThrowInvalidImageDimensions($"Image width or height exceeds maximum allowed dimension of {16777215u}"); } if ((ulong)(width * height) > 4294967295uL) { WebpThrowHelper.ThrowInvalidImageDimensions("The product of image width and height MUST be at most 2^32 - 1"); } uint num = 0u; if (exifProfile != null) { num |= 8; } if (xmpProfile != null) { num |= 4; } if (hasAlpha) { num |= 0x10; } Span span = scratchBuffer.AsSpan(0, 4); stream.Write(WebpConstants.Vp8XMagicBytes); BinaryPrimitives.WriteUInt32LittleEndian(span, 10u); stream.Write(span); BinaryPrimitives.WriteUInt32LittleEndian(span, num); stream.Write(span); BinaryPrimitives.WriteUInt32LittleEndian(span, width - 1); stream.Write(span.Slice(0, 3)); BinaryPrimitives.WriteUInt32LittleEndian(span, height - 1); stream.Write(span.Slice(0, 3)); } } internal class Vp8BitWriter : BitWriterBase { private const int DC_PRED = 0; private const int TM_PRED = 1; private const int V_PRED = 2; private const int H_PRED = 3; private const int B_DC_PRED = 0; private const int B_TM_PRED = 1; private const int B_VE_PRED = 2; private const int B_HE_PRED = 3; private const int B_RD_PRED = 4; private const int B_VR_PRED = 5; private const int B_LD_PRED = 6; private const int B_VL_PRED = 7; private const int B_HD_PRED = 8; private const int B_HU_PRED = 9; private readonly Vp8Encoder enc; private int range; private int value; private int run; private int nbBits; private uint pos; private readonly int maxPos; public Vp8BitWriter(int expectedSize) : base(expectedSize) { range = 254; value = 0; run = 0; nbBits = -8; pos = 0u; maxPos = 0; } public Vp8BitWriter(int expectedSize, Vp8Encoder enc) : this(expectedSize) { this.enc = enc; } public override int NumBytes() { return (int)pos; } public int PutCoeffs(int ctx, Vp8Residual residual) { int first = residual.First; Vp8ProbaArray vp8ProbaArray = residual.Prob[first].Probabilities[ctx]; if (!PutBit(residual.Last >= 0, vp8ProbaArray.Probabilities[0])) { return 0; } while (first < 16) { int num = residual.Coeffs[first++]; bool flag = num < 0; int num2 = (flag ? (-num) : num); if (!PutBit(num2 != 0, vp8ProbaArray.Probabilities[1])) { vp8ProbaArray = residual.Prob[WebpConstants.Vp8EncBands[first]].Probabilities[0]; continue; } if (!PutBit(num2 > 1, vp8ProbaArray.Probabilities[2])) { vp8ProbaArray = residual.Prob[WebpConstants.Vp8EncBands[first]].Probabilities[1]; } else { if (!PutBit(num2 > 4, vp8ProbaArray.Probabilities[3])) { if (PutBit(num2 != 2, vp8ProbaArray.Probabilities[4])) { PutBit(num2 == 4, vp8ProbaArray.Probabilities[5]); } } else if (!PutBit(num2 > 10, vp8ProbaArray.Probabilities[6])) { if (!PutBit(num2 > 6, vp8ProbaArray.Probabilities[7])) { PutBit(num2 == 6, 159); } else { PutBit(num2 >= 9, 165); PutBit((num2 & 1) == 0, 145); } } else { int num3; byte[] array; if (num2 < 19) { PutBit(0, vp8ProbaArray.Probabilities[8]); PutBit(0, vp8ProbaArray.Probabilities[9]); num2 -= 11; num3 = 4; array = WebpConstants.Cat3; } else if (num2 < 35) { PutBit(0, vp8ProbaArray.Probabilities[8]); PutBit(1, vp8ProbaArray.Probabilities[9]); num2 -= 19; num3 = 8; array = WebpConstants.Cat4; } else if (num2 < 67) { PutBit(1, vp8ProbaArray.Probabilities[8]); PutBit(0, vp8ProbaArray.Probabilities[10]); num2 -= 35; num3 = 16; array = WebpConstants.Cat5; } else { PutBit(1, vp8ProbaArray.Probabilities[8]); PutBit(1, vp8ProbaArray.Probabilities[10]); num2 -= 67; num3 = 1024; array = WebpConstants.Cat6; } int num4 = 0; while (num3 != 0) { PutBit(num2 & num3, array[num4++]); num3 >>= 1; } } vp8ProbaArray = residual.Prob[WebpConstants.Vp8EncBands[first]].Probabilities[2]; } PutBitUniform(flag ? 1 : 0); if (first != 16 && PutBit(first <= residual.Last, vp8ProbaArray.Probabilities[0])) { continue; } return 1; } return 1; } public override void BitWriterResize(int extraSize) { long num = pos + extraSize; if (num > maxPos) { ResizeBuffer(maxPos, (int)num); } } public override void Finish() { PutBits(0u, 9 - nbBits); nbBits = 0; Flush(); } public void PutSegment(int s, Span p) { if (PutBit(s >= 2, p[0])) { p = p.Slice(1); } PutBit(s & 1, p[1]); } public void PutI16Mode(int mode) { bool bit = ((mode == 1 || mode == 3) ? true : false); if (PutBit(bit, 156)) { PutBit(mode == 1, 128); } else { PutBit(mode == 2, 163); } } public int PutI4Mode(int mode, Span prob) { if (PutBit(mode != 0, prob[0]) && PutBit(mode != 1, prob[1]) && PutBit(mode != 2, prob[2])) { if (!PutBit(mode >= 6, prob[3])) { if (PutBit(mode != 3, prob[4])) { PutBit(mode != 4, prob[5]); } } else if (PutBit(mode != 6, prob[6]) && PutBit(mode != 7, prob[7])) { PutBit(mode != 8, prob[8]); } } return mode; } public void PutUvMode(int uvMode) { if (PutBit(uvMode != 0, 142) && PutBit(uvMode != 2, 114)) { PutBit(uvMode != 3, 183); } } private void PutBits(uint value, int nbBits) { for (uint num = (uint)(1 << nbBits - 1); num != 0; num >>= 1) { PutBitUniform((int)(value & num)); } } private bool PutBit(bool bit, int prob) { return PutBit(bit ? 1 : 0, prob); } private bool PutBit(int bit, int prob) { int num = range * prob >> 8; if (bit != 0) { value += num + 1; range -= num + 1; } else { range = num; } if (range < 127) { int num2 = WebpLookupTables.Norm[range]; range = WebpLookupTables.NewRange[range]; value <<= num2; nbBits += num2; if (nbBits > 0) { Flush(); } } return bit != 0; } private int PutBitUniform(int bit) { int num = range >> 1; if (bit != 0) { value += num + 1; range -= num + 1; } else { range = num; } if (range < 127) { range = WebpLookupTables.NewRange[range]; value <<= 1; nbBits++; if (nbBits > 0) { Flush(); } } return bit; } private void PutSignedBits(int value, int nbBits) { if (PutBitUniform((value != 0) ? 1 : 0) != 0) { if (value < 0) { int num = (-value << 1) | 1; PutBits((uint)num, nbBits + 1); } else { PutBits((uint)(value << 1), nbBits + 1); } } } private void Flush() { int num = 8 + nbBits; int num2 = value >> num; value -= num2 << num; nbBits -= 8; if ((num2 & 0xFF) != 255) { uint num3 = pos; BitWriterResize(run + 1); if ((num2 & 0x100) != 0 && num3 != 0) { base.Buffer[num3 - 1]++; } if (run > 0) { int num4 = (((num2 & 0x100) == 0) ? 255 : 0); while (run > 0) { base.Buffer[num3++] = (byte)num4; run--; } } base.Buffer[num3++] = (byte)(num2 & 0xFF); pos = num3; } else { run++; } } public void WriteEncodedImageToStream(Stream stream, ExifProfile exifProfile, XmpProfile xmpProfile, uint width, uint height, bool hasAlpha, Span alphaData, bool alphaDataIsCompressed) { bool flag = false; byte[] metadataBytes = null; byte[] metadataBytes2 = null; uint num = 0u; if (exifProfile != null) { flag = true; metadataBytes = exifProfile.ToByteArray(); num += MetadataChunkSize(metadataBytes); } if (xmpProfile != null) { flag = true; metadataBytes2 = xmpProfile.Data; num += MetadataChunkSize(metadataBytes2); } if (hasAlpha) { flag = true; num += AlphaChunkSize(alphaData); } if (flag) { num += 18; } Finish(); uint num2 = (uint)NumBytes(); Vp8BitWriter vp8BitWriter = new Vp8BitWriter(enc.Mbw * enc.Mbh * 7 / 8); uint num3 = GeneratePartition0(vp8BitWriter); uint num4 = 10 + num3; num4 += num2; uint num5 = num4 & 1; num4 += num5; num += 12 + num4; WriteWebpHeaders(stream, num3, num4, num, flag, width, height, exifProfile, xmpProfile, hasAlpha, alphaData, alphaDataIsCompressed); vp8BitWriter.WriteToStream(stream); WriteToStream(stream); if (num5 == 1) { stream.WriteByte(0); } if (exifProfile != null) { WriteMetadataProfile(stream, metadataBytes, WebpChunkType.Exif); } if (xmpProfile != null) { WriteMetadataProfile(stream, metadataBytes2, WebpChunkType.Xmp); } } private uint GeneratePartition0(Vp8BitWriter bitWriter) { bitWriter.PutBitUniform(0); bitWriter.PutBitUniform(0); WriteSegmentHeader(bitWriter); WriteFilterHeader(bitWriter); bitWriter.PutBits(0u, 2); WriteQuant(bitWriter); bitWriter.PutBitUniform(0); WriteProbas(bitWriter); CodeIntraModes(bitWriter); bitWriter.Finish(); return (uint)bitWriter.NumBytes(); } private void WriteSegmentHeader(Vp8BitWriter bitWriter) { Vp8EncSegmentHeader segmentHeader = enc.SegmentHeader; Vp8EncProba proba = enc.Proba; if (bitWriter.PutBitUniform((segmentHeader.NumSegments > 1) ? 1 : 0) == 0) { return; } int bit = 1; bitWriter.PutBitUniform(segmentHeader.UpdateMap ? 1 : 0); if (bitWriter.PutBitUniform(bit) != 0) { bitWriter.PutBitUniform(1); for (int i = 0; i < 4; i++) { bitWriter.PutSignedBits(enc.SegmentInfos[i].Quant, 7); } for (int j = 0; j < 4; j++) { bitWriter.PutSignedBits(enc.SegmentInfos[j].FStrength, 6); } } if (!segmentHeader.UpdateMap) { return; } for (int k = 0; k < 3; k++) { if (bitWriter.PutBitUniform((proba.Segments[k] != byte.MaxValue) ? 1 : 0) != 0) { bitWriter.PutBits(proba.Segments[k], 8); } } } private void WriteFilterHeader(Vp8BitWriter bitWriter) { Vp8FilterHeader filterHeader = enc.FilterHeader; bool flag = filterHeader.I4x4LfDelta != 0; bitWriter.PutBitUniform(filterHeader.Simple ? 1 : 0); bitWriter.PutBits((uint)filterHeader.FilterLevel, 6); bitWriter.PutBits((uint)filterHeader.Sharpness, 3); if (bitWriter.PutBitUniform(flag ? 1 : 0) != 0) { bool flag2 = filterHeader.I4x4LfDelta != 0; if (bitWriter.PutBitUniform(flag2 ? 1 : 0) != 0) { bitWriter.PutBits(0u, 4); bitWriter.PutSignedBits(filterHeader.I4x4LfDelta, 6); bitWriter.PutBits(0u, 3); } } } private void WriteQuant(Vp8BitWriter bitWriter) { bitWriter.PutBits((uint)enc.BaseQuant, 7); bitWriter.PutSignedBits(enc.DqY1Dc, 4); bitWriter.PutSignedBits(enc.DqY2Dc, 4); bitWriter.PutSignedBits(enc.DqY2Ac, 4); bitWriter.PutSignedBits(enc.DqUvDc, 4); bitWriter.PutSignedBits(enc.DqUvAc, 4); } private void WriteProbas(Vp8BitWriter bitWriter) { Vp8EncProba proba = enc.Proba; for (int i = 0; i < 4; i++) { for (int j = 0; j < 8; j++) { for (int k = 0; k < 3; k++) { for (int l = 0; l < 11; l++) { byte b = proba.Coeffs[i][j].Probabilities[k].Probabilities[l]; bool bit = b != WebpLookupTables.DefaultCoeffsProba[i, j, k, l]; if (bitWriter.PutBit(bit, WebpLookupTables.CoeffsUpdateProba[i, j, k, l])) { bitWriter.PutBits(b, 8); } } } } } if (bitWriter.PutBitUniform(proba.UseSkipProba ? 1 : 0) != 0) { bitWriter.PutBits(proba.SkipProba, 8); } } private void CodeIntraModes(Vp8BitWriter bitWriter) { Vp8EncIterator vp8EncIterator = new Vp8EncIterator(enc.YTop, enc.UvTop, enc.Nz, enc.MbInfo, enc.Preds, enc.TopDerr, enc.Mbw, enc.Mbh); int predsWidth = enc.PredsWidth; do { Vp8MacroBlockInfo currentMacroBlockInfo = vp8EncIterator.CurrentMacroBlockInfo; int num = vp8EncIterator.PredIdx; Span span = vp8EncIterator.Preds.AsSpan(num); if (enc.SegmentHeader.UpdateMap) { bitWriter.PutSegment(currentMacroBlockInfo.Segment, enc.Proba.Segments); } if (enc.Proba.UseSkipProba) { bitWriter.PutBit(currentMacroBlockInfo.Skip, enc.Proba.SkipProba); } if (bitWriter.PutBit(currentMacroBlockInfo.MacroBlockType != Vp8MacroBlockType.I4X4, 145)) { bitWriter.PutI16Mode(span[0]); } else { Span span2 = vp8EncIterator.Preds.AsSpan(num - predsWidth); for (int i = 0; i < 4; i++) { int num2 = vp8EncIterator.Preds[num - 1]; for (int j = 0; j < 4; j++) { byte[] array = WebpLookupTables.ModesProba[span2[j], num2]; num2 = bitWriter.PutI4Mode(vp8EncIterator.Preds[num + j], array); } span2 = vp8EncIterator.Preds.AsSpan(num); num += predsWidth; } } bitWriter.PutUvMode(currentMacroBlockInfo.UvMode); } while (vp8EncIterator.Next()); } private void WriteWebpHeaders(Stream stream, uint size0, uint vp8Size, uint riffSize, bool isVp8X, uint width, uint height, ExifProfile exifProfile, XmpProfile xmpProfile, bool hasAlpha, Span alphaData, bool alphaDataIsCompressed) { WriteRiffHeader(stream, riffSize); if (isVp8X) { WriteVp8XHeader(stream, exifProfile, xmpProfile, width, height, hasAlpha); if (hasAlpha) { WriteAlphaChunk(stream, alphaData, alphaDataIsCompressed); } } WriteVp8Header(stream, vp8Size); WriteFrameHeader(stream, size0); } private void WriteVp8Header(Stream stream, uint size) { Span span = stackalloc byte[8]; WebpConstants.Vp8MagicBytes.AsSpan().CopyTo(span); BinaryPrimitives.WriteUInt32LittleEndian(span.Slice(4), size); stream.Write(span); } private void WriteFrameHeader(Stream stream, uint size0) { uint num = 0u; int width = enc.Width; int height = enc.Height; byte[] array = new byte[10]; uint num2 = 0 | (num << 1) | 0x10 | (size0 << 5); array[0] = (byte)(num2 & 0xFF); array[1] = (byte)((num2 >> 8) & 0xFF); array[2] = (byte)((num2 >> 16) & 0xFF); array[3] = WebpConstants.Vp8HeaderMagicBytes[0]; array[4] = WebpConstants.Vp8HeaderMagicBytes[1]; array[5] = WebpConstants.Vp8HeaderMagicBytes[2]; array[6] = (byte)(width & 0xFF); array[7] = (byte)(width >> 8); array[8] = (byte)(height & 0xFF); array[9] = (byte)(height >> 8); stream.Write(array); } } internal class Vp8LBitWriter : BitWriterBase { private readonly byte[] scratchBuffer = new byte[8]; private const int MinExtraSize = 32768; private const int WriterBytes = 4; private const int WriterBits = 32; private ulong bits; private int used; private int cur; public Vp8LBitWriter(int expectedSize) : base(expectedSize) { } private Vp8LBitWriter(byte[] buffer, ulong bits, int used, int cur) : base(buffer) { this.bits = bits; this.used = used; this.cur = cur; } public void PutBits(uint bits, int nBits) { if (nBits > 0) { if (used >= 32) { PutBitsFlushBits(); } this.bits |= (ulong)bits << used; used += nBits; } } public void Reset(Vp8LBitWriter bwInit) { bits = bwInit.bits; used = bwInit.used; cur = bwInit.cur; } public void WriteHuffmanCode(HuffmanTreeCode code, int codeIndex) { int nBits = code.CodeLengths[codeIndex]; int num = code.Codes[codeIndex]; PutBits((uint)num, nBits); } public void WriteHuffmanCodeWithExtraBits(HuffmanTreeCode code, int codeIndex, int bits, int nBits) { int num = code.CodeLengths[codeIndex]; int num2 = code.Codes[codeIndex]; PutBits((uint)((bits << num) | num2), num + nBits); } public override int NumBytes() { return cur + (used + 7 >> 3); } public Vp8LBitWriter Clone() { byte[] dst = new byte[base.Buffer.Length]; System.Buffer.BlockCopy(base.Buffer, 0, dst, 0, cur); return new Vp8LBitWriter(dst, bits, used, cur); } public override void Finish() { BitWriterResize(used + 7 >> 3); while (used > 0) { base.Buffer[cur++] = (byte)bits; bits >>= 8; used -= 8; } used = 0; } public void WriteEncodedImageToStream(Stream stream, ExifProfile exifProfile, XmpProfile xmpProfile, uint width, uint height, bool hasAlpha) { bool flag = false; byte[] metadataBytes = null; byte[] metadataBytes2 = null; uint num = 0u; if (exifProfile != null) { flag = true; num += 18; metadataBytes = exifProfile.ToByteArray(); num += MetadataChunkSize(metadataBytes); } if (xmpProfile != null) { flag = true; num += 18; metadataBytes2 = xmpProfile.Data; num += MetadataChunkSize(metadataBytes2); } Finish(); uint num2 = (uint)NumBytes(); num2++; uint num3 = num2 & 1; num += 12 + num2 + num3; WriteRiffHeader(stream, num); if (flag) { WriteVp8XHeader(stream, exifProfile, xmpProfile, width, height, hasAlpha); } stream.Write(WebpConstants.Vp8LMagicBytes); BinaryPrimitives.WriteUInt32LittleEndian(scratchBuffer, num2); stream.Write(scratchBuffer.AsSpan(0, 4)); stream.WriteByte(47); WriteToStream(stream); if (num3 == 1) { stream.WriteByte(0); } if (exifProfile != null) { WriteMetadataProfile(stream, metadataBytes, WebpChunkType.Exif); } if (xmpProfile != null) { WriteMetadataProfile(stream, metadataBytes2, WebpChunkType.Xmp); } } private void PutBitsFlushBits() { if (cur + 4 > base.Buffer.Length) { int extraSize = base.Buffer.Length - cur + 32768; BitWriterResize(extraSize); } BinaryPrimitives.WriteUInt64LittleEndian(scratchBuffer, bits); scratchBuffer.AsSpan(0, 4).CopyTo(base.Buffer.AsSpan(cur)); cur += 4; bits >>= 32; used -= 32; } public override void BitWriterResize(int extraSize) { int maxBytes = base.Buffer.Length + base.Buffer.Length; int sizeRequired = cur + extraSize; ResizeBuffer(maxBytes, sizeRequired); } } } namespace SixLabors.ImageSharp.Formats.Webp.BitReader { internal abstract class BitReaderBase : IDisposable { private bool isDisposed; public IMemoryOwner Data { get; set; } protected void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator) { Data = memoryAllocator.Allocate(bytesToRead); input.Read(Data.Memory.Span.Slice(0, bytesToRead), 0, bytesToRead); } protected virtual void Dispose(bool disposing) { if (!isDisposed) { if (disposing) { Data?.Dispose(); } isDisposed = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } internal class Vp8BitReader : BitReaderBase { private const int BitsCount = 56; private ulong value; private uint range; private int bits; private uint bufferMax; private uint bufferEnd; private bool eof; private long pos; public int Pos => (int)pos; public uint ImageDataSize { get; } public uint PartitionLength { get; } public uint Remaining { get; set; } public Vp8BitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator, uint partitionLength, int startPos = 0) { Guard.MustBeLessThan(imageDataSize, 2147483647u, "imageDataSize"); ImageDataSize = imageDataSize; PartitionLength = partitionLength; ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator); InitBitreader(partitionLength, startPos); } public Vp8BitReader(IMemoryOwner imageData, uint partitionLength, int startPos = 0) { base.Data = imageData; ImageDataSize = (uint)imageData.Memory.Length; PartitionLength = partitionLength; InitBitreader(partitionLength, startPos); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetBit(int prob) { uint num = range; if (bits < 0) { LoadNewBytes(); } int num2 = bits; uint num3 = (uint)(num * prob >> 8); bool num4 = value >> num2 > num3; if (num4) { num -= num3; value -= (ulong)(num3 + 1) << num2; } else { num = num3 + 1; } int num5 = 7 ^ Numerics.Log2(num); num <<= num5; bits -= num5; range = num - 1; return num4 ? 1 : 0; } public int GetSigned(int v) { if (bits < 0) { LoadNewBytes(); } int num = bits; uint num2 = range >> 1; ulong num3 = value >> num; ulong num4 = num2 - num3 >> 31; bits--; range = (uint)(((int)range + (int)num4) | 1); value -= ((num2 + 1) & num4) << num; return (v ^ (int)num4) - (int)num4; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ReadBool() { return ReadValue(1) == 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint ReadValue(int nBits) { uint num = 0u; while (nBits-- > 0) { num |= (uint)(GetBit(128) << nBits); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int ReadSignedValue(int nBits) { int num = (int)ReadValue(nBits); if (ReadValue(1) == 0) { return num; } return -num; } private void InitBitreader(uint size, int pos = 0) { long num = pos + size; range = 254u; value = 0uL; bits = -8; eof = false; this.pos = pos; bufferEnd = (uint)num; bufferMax = (uint)((size > 8) ? (num - 8 + 1) : pos); LoadNewBytes(); } [MethodImpl(MethodImplOptions.NoInlining)] private void LoadNewBytes() { if (pos < bufferMax) { ulong x = BinaryPrimitives.ReadUInt64LittleEndian(base.Data.Memory.Span.Slice((int)pos, 8)); pos += 7L; ulong num = ByteSwap64(x); num >>= 8; value = num | (value << 56); bits += 56; } else { LoadFinalBytes(); } } private void LoadFinalBytes() { if (pos < bufferEnd) { bits += 8; value = base.Data.Memory.Span[(int)pos++] | (value << 8); } else if (!eof) { value <<= 8; bits += 8; eof = true; } else { bits = 0; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private ulong ByteSwap64(ulong x) { x = ((x & 0xFFFFFFFF00000000uL) >> 32) | ((x & 0xFFFFFFFFu) << 32); x = ((x & 0xFFFF0000FFFF0000uL) >> 16) | ((x & 0xFFFF0000FFFFL) << 16); x = ((x & 0xFF00FF00FF00FF00uL) >> 8) | ((x & 0xFF00FF00FF00FFL) << 8); return x; } } internal class Vp8LBitReader : BitReaderBase { private const int Vp8LMaxNumBitRead = 24; private const int Lbits = 64; private const int Wbits = 32; private static readonly uint[] BitMask = new uint[25] { 0u, 1u, 3u, 7u, 15u, 31u, 63u, 127u, 255u, 511u, 1023u, 2047u, 4095u, 8191u, 16383u, 32767u, 65535u, 131071u, 262143u, 524287u, 1048575u, 2097151u, 4194303u, 8388607u, 16777215u }; private ulong value; private readonly long len; private long pos; private int bitPos; public bool Eos { get; set; } public Vp8LBitReader(IMemoryOwner data) { base.Data = data; Memory memory = data.Memory; len = memory.Length; value = 0uL; bitPos = 0; Eos = false; ulong num = 0uL; memory = base.Data.Memory; Span span = memory.Span; for (int i = 0; i < 8; i++) { num |= (ulong)span[i] << 8 * i; } value = num; pos = 8L; } public Vp8LBitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator) { long num = imageDataSize; ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator); len = num; value = 0uL; bitPos = 0; Eos = false; if (num > 8) { num = 8L; } ulong num2 = 0uL; Span span = base.Data.Memory.Span; for (int i = 0; i < num; i++) { num2 |= (ulong)span[i] << 8 * i; } value = num2; pos = num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint ReadValue(int nBits) { if (!Eos && nBits <= 24) { ulong num = PrefetchBits() & BitMask[nBits]; bitPos += nBits; ShiftBytes(); return (uint)num; } return 0u; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ReadBit() { return ReadValue(1) != 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AdvanceBitPosition(int numberOfBits) { bitPos += numberOfBits; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ulong PrefetchBits() { return value >> (bitPos & 0x3F); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FillBitWindow() { if (bitPos >= 32) { DoFillBitWindow(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsEndOfStream() { if (!Eos) { if (pos == len) { return bitPos > 64; } return false; } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DoFillBitWindow() { ShiftBytes(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ShiftBytes() { Span span = base.Data.Memory.Span; while (bitPos >= 8 && pos < len) { value >>= 8; value |= (ulong)span[(int)pos] << 56; pos++; bitPos -= 8; } } } } namespace SixLabors.ImageSharp.Formats.Tiff { internal class DirectoryReader { private const int DirectoryMax = 65534; private readonly Stream stream; private readonly MemoryAllocator allocator; private ulong nextIfdOffset; public ByteOrder ByteOrder { get; private set; } public bool IsBigTiff { get; private set; } public DirectoryReader(Stream stream, MemoryAllocator allocator) { this.stream = stream; this.allocator = allocator; } public IEnumerable Read() { ByteOrder = ReadByteOrder(stream); HeaderReader headerReader = new HeaderReader(stream, ByteOrder); headerReader.ReadFileHeader(); nextIfdOffset = headerReader.FirstIfdOffset; IsBigTiff = headerReader.IsBigTiff; return ReadIfds(headerReader.IsBigTiff); } private static ByteOrder ReadByteOrder(Stream stream) { Span buffer = stackalloc byte[2]; if (stream.Read(buffer) != 2) { throw TiffThrowHelper.ThrowInvalidHeader(); } if (buffer[0] == 73 && buffer[1] == 73) { return ByteOrder.LittleEndian; } if (buffer[0] == 77 && buffer[1] == 77) { return ByteOrder.BigEndian; } throw TiffThrowHelper.ThrowInvalidHeader(); } private IEnumerable ReadIfds(bool isBigTiff) { List list = new List(); while (nextIfdOffset != 0L && nextIfdOffset < (ulong)stream.Length) { EntryReader entryReader = new EntryReader(stream, ByteOrder, allocator); entryReader.ReadTags(isBigTiff, nextIfdOffset); if (entryReader.BigValues.Count > 0) { entryReader.BigValues.Sort(((ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif) t1, (ulong Offset, ExifDataType DataType, ulong NumberOfComponents, ExifValue Exif) t2) => t1.Offset.CompareTo(t2.Offset)); if (entryReader.BigValues[0].Offset < entryReader.NextIfdOffset) { entryReader.ReadBigValues(); } } if (nextIfdOffset >= entryReader.NextIfdOffset && entryReader.NextIfdOffset != 0L) { TiffThrowHelper.ThrowImageFormatException("TIFF image contains circular directory offsets"); } nextIfdOffset = entryReader.NextIfdOffset; list.Add(entryReader); if (list.Count >= 65534) { TiffThrowHelper.ThrowImageFormatException("TIFF image contains too many directories"); } } List list2 = new List(list.Count); foreach (EntryReader item2 in list) { item2.ReadBigValues(); ExifProfile item = new ExifProfile(item2.Values, item2.InvalidTags); list2.Add(item); } return list2; } } internal class EntryReader : BaseExifReader { public List Values { get; } = new List(); public ulong NextIfdOffset { get; private set; } public EntryReader(Stream stream, ByteOrder byteOrder, MemoryAllocator allocator) : base(stream, allocator) { base.IsBigEndian = byteOrder == ByteOrder.BigEndian; } public void ReadTags(bool isBigTiff, ulong ifdOffset) { if (!isBigTiff) { ReadValues(Values, (uint)ifdOffset); NextIfdOffset = ReadUInt32(); ReadSubIfd(Values); } else { ReadValues64(Values, ifdOffset); NextIfdOffset = ReadUInt64(); } } public void ReadBigValues() { ReadBigValues(Values); } } internal class HeaderReader : BaseExifReader { public bool IsBigTiff { get; private set; } public ulong FirstIfdOffset { get; private set; } public HeaderReader(Stream stream, ByteOrder byteOrder) : base(stream, null) { base.IsBigEndian = byteOrder == ByteOrder.BigEndian; } public void ReadFileHeader() { switch (ReadUInt16()) { case 42: IsBigTiff = false; FirstIfdOffset = ReadUInt32(); return; case 43: { IsBigTiff = true; ushort num = ReadUInt16(); ushort num2 = ReadUInt16(); if (num == 8 && num2 == 0) { FirstIfdOffset = ReadUInt64(); return; } break; } } TiffThrowHelper.ThrowInvalidHeader(); } } internal interface ITiffDecoderOptions { bool IgnoreMetadata { get; } FrameDecodingMode DecodingMode { get; } } internal interface ITiffEncoderOptions { TiffBitsPerPixel? BitsPerPixel { get; } TiffCompression? Compression { get; } DeflateCompressionLevel? CompressionLevel { get; } TiffPhotometricInterpretation? PhotometricInterpretation { get; } TiffPredictor? HorizontalPredictor { get; } IQuantizer Quantizer { get; } } public enum TiffBitsPerPixel { Bit1 = 1, Bit4 = 4, Bit6 = 6, Bit8 = 8, Bit10 = 10, Bit12 = 12, Bit14 = 14, Bit16 = 16, Bit24 = 24, Bit30 = 30, Bit36 = 36, Bit42 = 42, Bit48 = 48 } public readonly struct TiffBitsPerSample : IEquatable { public readonly ushort Channel0; public readonly ushort Channel1; public readonly ushort Channel2; public readonly ushort Channel3; public readonly byte Channels; public TiffBitsPerSample(ushort channel0, ushort channel1, ushort channel2, ushort channel3 = 0) { Channel0 = (ushort)Numerics.Clamp(channel0, 0, 32); Channel1 = (ushort)Numerics.Clamp(channel1, 0, 32); Channel2 = (ushort)Numerics.Clamp(channel2, 0, 32); Channel3 = (ushort)Numerics.Clamp(channel3, 0, 32); Channels = 0; Channels += ((Channel0 != 0) ? ((byte)1) : ((byte)0)); Channels += ((Channel1 != 0) ? ((byte)1) : ((byte)0)); Channels += ((Channel2 != 0) ? ((byte)1) : ((byte)0)); Channels += ((Channel3 != 0) ? ((byte)1) : ((byte)0)); } public static bool TryParse(ushort[] value, out TiffBitsPerSample sample) { if (value == null || value.Length == 0) { sample = default(TiffBitsPerSample); return false; } ushort channel = 0; ushort channel2; ushort channel3; ushort channel4; switch (value.Length) { case 4: channel = value[3]; channel2 = value[2]; channel3 = value[1]; channel4 = value[0]; break; case 3: channel2 = value[2]; channel3 = value[1]; channel4 = value[0]; break; case 2: channel2 = 0; channel3 = value[1]; channel4 = value[0]; break; default: channel2 = 0; channel3 = 0; channel4 = value[0]; break; } sample = new TiffBitsPerSample(channel4, channel3, channel2, channel); return true; } public override bool Equals(object obj) { if (obj is TiffBitsPerSample other) { return Equals(other); } return false; } public bool Equals(TiffBitsPerSample other) { if (Channel0 == other.Channel0 && Channel1 == other.Channel1 && Channel2 == other.Channel2) { return Channel3 == other.Channel3; } return false; } public override int GetHashCode() { return HashCode.Combine(Channel0, Channel1, Channel2, Channel3); } public ushort[] ToArray() { if (Channel1 != 0) { if (Channel2 != 0) { if (Channel3 != 0) { return new ushort[4] { Channel0, Channel1, Channel2, Channel3 }; } return new ushort[3] { Channel0, Channel1, Channel2 }; } return new ushort[2] { Channel0, Channel1 }; } return new ushort[1] { Channel0 }; } public TiffBitsPerPixel BitsPerPixel() { return (TiffBitsPerPixel)(Channel0 + Channel1 + Channel2 + Channel3); } public override string ToString() { if (Channel3 != 0) { return $"TiffBitsPerSample({Channel0}, {Channel1}, {Channel2}, {Channel3})"; } return $"TiffBitsPerSample({Channel0}, {Channel1}, {Channel2})"; } } public sealed class TiffConfigurationModule : IConfigurationModule { public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(TiffFormat.Instance, new TiffEncoder()); configuration.ImageFormatsManager.SetDecoder(TiffFormat.Instance, new TiffDecoder()); configuration.ImageFormatsManager.AddImageFormatDetector(new TiffImageFormatDetector()); } } public class TiffDecoder : IImageDecoder, ITiffDecoderOptions, IImageInfoDetector { public bool IgnoreMetadata { get; set; } public FrameDecodingMode DecodingMode { get; set; } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(stream, "stream"); return new TiffDecoderCore(configuration, this).Decode(configuration, stream, cancellationToken); } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) { return Decode(configuration, stream, cancellationToken); } public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, "stream"); return new TiffDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); } } internal class TiffDecoderCore : IImageDecoderInternals { private readonly MemoryAllocator memoryAllocator; private readonly bool ignoreMetadata; private readonly FrameDecodingMode decodingMode; private BufferedReadStream inputStream; private ByteOrder byteOrder; private bool isBigTiff; public TiffBitsPerSample BitsPerSample { get; set; } public int BitsPerPixel { get; set; } public ushort[] ColorMap { get; set; } public TiffColorType ColorType { get; set; } public Rational[] ReferenceBlackAndWhite { get; set; } public Rational[] YcbcrCoefficients { get; set; } public ushort[] YcbcrSubSampling { get; set; } public TiffDecoderCompressionType CompressionType { get; set; } public FaxCompressionOptions FaxCompressionOptions { get; set; } public TiffFillOrder FillOrder { get; set; } public TiffExtraSampleType? ExtraSamplesType { get; set; } public byte[] JpegTables { get; set; } public TiffPlanarConfiguration PlanarConfiguration { get; set; } public TiffPhotometricInterpretation PhotometricInterpretation { get; set; } public TiffSampleFormat SampleFormat { get; set; } public TiffPredictor Predictor { get; set; } public Configuration Configuration { get; } public Size Dimensions { get; private set; } public TiffDecoderCore(Configuration configuration, ITiffDecoderOptions options) { if (options == null) { options = new TiffDecoder(); } Configuration = configuration ?? Configuration.Default; ignoreMetadata = options.IgnoreMetadata; decodingMode = options.DecodingMode; memoryAllocator = Configuration.MemoryAllocator; } public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { List> list = new List>(); try { inputStream = stream; DirectoryReader directoryReader = new DirectoryReader(stream, Configuration.MemoryAllocator); IEnumerable enumerable = directoryReader.Read(); byteOrder = directoryReader.ByteOrder; isBigTiff = directoryReader.IsBigTiff; foreach (ExifProfile item2 in enumerable) { cancellationToken.ThrowIfCancellationRequested(); ImageFrame item = DecodeFrame(item2, cancellationToken); list.Add(item); if (decodingMode == FrameDecodingMode.First) { break; } } ImageMetadata metadata = TiffDecoderMetadataCreator.Create(list, ignoreMetadata, directoryReader.ByteOrder, directoryReader.IsBigTiff); ImageFrame imageFrame = list[0]; Dimensions = imageFrame.Size(); foreach (ImageFrame item3 in list) { if (item3.Size() != imageFrame.Size()) { TiffThrowHelper.ThrowNotSupported("Images with different sizes are not supported"); } } return new Image(Configuration, metadata, list); } catch { foreach (ImageFrame item4 in list) { item4.Dispose(); } throw; } } public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { inputStream = stream; DirectoryReader directoryReader = new DirectoryReader(stream, Configuration.MemoryAllocator); ExifProfile exifProfile = directoryReader.Read().First(); TiffFrameMetadata tiffFrameMetadata = TiffFrameMetadata.Parse(exifProfile); return new ImageInfo(metadata: TiffDecoderMetadataCreator.Create(directoryReader.ByteOrder, directoryReader.IsBigTiff, exifProfile), width: GetImageWidth(exifProfile), height: GetImageHeight(exifProfile), pixelType: new PixelTypeInfo((int)tiffFrameMetadata.BitsPerPixel.Value)); } private ImageFrame DecodeFrame(ExifProfile tags, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { ImageFrameMetadata imageFrameMetadata = new ImageFrameMetadata(); if (!ignoreMetadata) { imageFrameMetadata.ExifProfile = tags; } TiffFrameMetadata tiffMetadata = imageFrameMetadata.GetTiffMetadata(); TiffFrameMetadata.Parse(tiffMetadata, tags); this.VerifyAndParse(tags, tiffMetadata); int imageWidth = GetImageWidth(tags); int imageHeight = GetImageHeight(tags); ImageFrame imageFrame = new ImageFrame(Configuration, imageWidth, imageHeight, imageFrameMetadata); int rowsPerStrip = ((tags.GetValue(ExifTag.RowsPerStrip) != null) ? ((int)tags.GetValue(ExifTag.RowsPerStrip).Value) : int.MaxValue); Array array = (Array)tags.GetValueInternal(ExifTag.StripOffsets).GetValue(); Array array2 = (Array)tags.GetValueInternal(ExifTag.StripByteCounts).GetValue(); Span span; using (ConvertNumbers(array, out span)) { Span span2; using (ConvertNumbers(array2, out span2)) { if (PlanarConfiguration == TiffPlanarConfiguration.Planar) { DecodeStripsPlanar(imageFrame, rowsPerStrip, span, span2, cancellationToken); } else { DecodeStripsChunky(imageFrame, rowsPerStrip, span, span2, cancellationToken); } return imageFrame; } } } private IMemoryOwner ConvertNumbers(Array array, out Span span) { if (array is Number[] array2) { IMemoryOwner memoryOwner = memoryAllocator.Allocate(array2.Length); span = memoryOwner.GetSpan(); for (int i = 0; i < array2.Length; i++) { span[i] = (uint)array2[i]; } return memoryOwner; } span = (ulong[])array; return null; } private int CalculateStripBufferSize(int width, int height, int plane = -1) { int num = 0; if (PlanarConfiguration == TiffPlanarConfiguration.Chunky) { num = BitsPerPixel; } else { switch (plane) { case 0: num = BitsPerSample.Channel0; break; case 1: num = BitsPerSample.Channel1; break; case 2: num = BitsPerSample.Channel2; break; case 3: num = BitsPerSample.Channel2; break; default: TiffThrowHelper.ThrowNotSupported("More then 4 color channels are not supported"); break; } } return (width * num + 7) / 8 * height; } private void DecodeStripsPlanar(ImageFrame frame, int rowsPerStrip, Span stripOffsets, Span stripByteCounts, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { int channels = BitsPerSample.Channels; int num = stripOffsets.Length / channels; int bitsPerPixel = BitsPerPixel; Buffer2D pixelBuffer = frame.PixelBuffer; IMemoryOwner[] array = new IMemoryOwner[channels]; try { for (int i = 0; i < array.Length; i++) { int length = CalculateStripBufferSize(frame.Width, rowsPerStrip, i); array[i] = memoryAllocator.Allocate(length); } using TiffBaseDecompressor tiffBaseDecompressor = TiffDecompressorsFactory.Create(Configuration, CompressionType, memoryAllocator, PhotometricInterpretation, frame.Width, bitsPerPixel, ColorType, Predictor, FaxCompressionOptions, JpegTables, FillOrder, byteOrder); TiffBasePlanarColorDecoder tiffBasePlanarColorDecoder = TiffColorDecoderFactory.CreatePlanar(ColorType, BitsPerSample, ExtraSamplesType, ColorMap, ReferenceBlackAndWhite, YcbcrCoefficients, YcbcrSubSampling, byteOrder); for (int j = 0; j < num; j++) { cancellationToken.ThrowIfCancellationRequested(); int num2 = ((j < num - 1 || frame.Height % rowsPerStrip == 0) ? rowsPerStrip : (frame.Height % rowsPerStrip)); int num3 = j; for (int k = 0; k < channels; k++) { tiffBaseDecompressor.Decompress(inputStream, stripOffsets[num3], stripByteCounts[num3], num2, array[k].GetSpan()); num3 += num; } tiffBasePlanarColorDecoder.Decode(array, pixelBuffer, 0, rowsPerStrip * j, frame.Width, num2); } } finally { IMemoryOwner[] array2 = array; for (int l = 0; l < array2.Length; l++) { array2[l]?.Dispose(); } } } private void DecodeStripsChunky(ImageFrame frame, int rowsPerStrip, Span stripOffsets, Span stripByteCounts, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { if (rowsPerStrip == int.MaxValue) { rowsPerStrip = frame.Height; } int length = CalculateStripBufferSize(frame.Width, rowsPerStrip); int bitsPerPixel = BitsPerPixel; using IMemoryOwner buffer = memoryAllocator.Allocate(length, AllocationOptions.Clean); Span span = buffer.GetSpan(); Buffer2D pixelBuffer = frame.PixelBuffer; using TiffBaseDecompressor tiffBaseDecompressor = TiffDecompressorsFactory.Create(Configuration, CompressionType, memoryAllocator, PhotometricInterpretation, frame.Width, bitsPerPixel, ColorType, Predictor, FaxCompressionOptions, JpegTables, FillOrder, byteOrder); TiffBaseColorDecoder tiffBaseColorDecoder = TiffColorDecoderFactory.Create(Configuration, memoryAllocator, ColorType, BitsPerSample, ExtraSamplesType, ColorMap, ReferenceBlackAndWhite, YcbcrCoefficients, YcbcrSubSampling, byteOrder); for (int i = 0; i < stripOffsets.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); int num = ((i < stripOffsets.Length - 1 || frame.Height % rowsPerStrip == 0) ? rowsPerStrip : (frame.Height % rowsPerStrip)); int num2 = rowsPerStrip * i; if (num2 + num > frame.Height) { break; } tiffBaseDecompressor.Decompress(inputStream, stripOffsets[i], stripByteCounts[i], num, span); tiffBaseColorDecoder.Decode(span, pixelBuffer, 0, num2, frame.Width, num); } } private static int GetImageWidth(ExifProfile exifProfile) { IExifValue value = exifProfile.GetValue(ExifTag.ImageWidth); if (value == null) { TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageWidth"); } return (int)value.Value; } private static int GetImageHeight(ExifProfile exifProfile) { IExifValue value = exifProfile.GetValue(ExifTag.ImageLength); if (value == null) { TiffThrowHelper.ThrowImageFormatException("The TIFF image frame is missing the ImageLength"); } return (int)value.Value; } } internal static class TiffDecoderMetadataCreator { public static ImageMetadata Create(List> frames, bool ignoreMetadata, ByteOrder byteOrder, bool isBigTiff) where TPixel : unmanaged, IPixel { if (frames.Count < 1) { TiffThrowHelper.ThrowImageFormatException("Expected at least one frame."); } ImageMetadata result = Create(byteOrder, isBigTiff, frames[0].Metadata.ExifProfile); if (!ignoreMetadata) { for (int i = 0; i < frames.Count; i++) { ImageFrameMetadata metadata = frames[i].Metadata; if (TryGetIptc(metadata.ExifProfile.Values, out var iptcBytes)) { metadata.IptcProfile = new IptcProfile(iptcBytes); } IExifValue value = metadata.ExifProfile.GetValue(ExifTag.XMP); if (value != null) { metadata.XmpProfile = new XmpProfile(value.Value); } IExifValue value2 = metadata.ExifProfile.GetValue(ExifTag.IccProfile); if (value2 != null) { metadata.IccProfile = new IccProfile(value2.Value); } } } return result; } public static ImageMetadata Create(ByteOrder byteOrder, bool isBigTiff, ExifProfile exifProfile) { ImageMetadata imageMetadata = new ImageMetadata(); SetResolution(imageMetadata, exifProfile); TiffMetadata tiffMetadata = imageMetadata.GetTiffMetadata(); tiffMetadata.ByteOrder = byteOrder; tiffMetadata.FormatType = (isBigTiff ? TiffFormatType.BigTIFF : TiffFormatType.Default); return imageMetadata; } private static void SetResolution(ImageMetadata imageMetaData, ExifProfile exifProfile) { imageMetaData.ResolutionUnits = ((exifProfile == null) ? PixelResolutionUnit.PixelsPerInch : UnitConverter.ExifProfileToResolutionUnit(exifProfile)); double? num = exifProfile?.GetValue(ExifTag.XResolution)?.Value.ToDouble(); if (num.HasValue) { imageMetaData.HorizontalResolution = num.Value; } double? num2 = exifProfile?.GetValue(ExifTag.YResolution)?.Value.ToDouble(); if (num2.HasValue) { imageMetaData.VerticalResolution = num2.Value; } } private static bool TryGetIptc(IReadOnlyList exifValues, out byte[] iptcBytes) { iptcBytes = null; IExifValue exifValue = exifValues.FirstOrDefault((IExifValue f) => f.Tag == ExifTag.IPTC); if (exifValue != null) { if (exifValue.DataType == ExifDataType.Byte || exifValue.DataType == ExifDataType.Undefined) { iptcBytes = (byte[])exifValue.GetValue(); return true; } if (exifValue.DataType == ExifDataType.Long) { uint[] array = (uint[])exifValue.GetValue(); iptcBytes = new byte[array.Length * 4]; Buffer.BlockCopy(array, 0, iptcBytes, 0, array.Length * 4); if (iptcBytes[0] == 28) { return true; } if (iptcBytes[3] != 28) { return false; } Span span = iptcBytes.AsSpan(); Span destination = stackalloc byte[4]; for (int num = 0; num < iptcBytes.Length; num += 4) { span.Slice(num, 4).CopyTo(destination); iptcBytes[num] = destination[3]; iptcBytes[num + 1] = destination[2]; iptcBytes[num + 2] = destination[1]; iptcBytes[num + 3] = destination[0]; } return true; } } return false; } } internal static class TiffDecoderOptionsParser { private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky; public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata frameMetadata) { if (exifProfile.GetValueInternal(ExifTag.TileOffsets) != null || exifProfile.GetValueInternal(ExifTag.TileByteCounts) != null) { TiffThrowHelper.ThrowNotSupported("Tiled images are not supported."); } IExifValue valueInternal = exifProfile.GetValueInternal(ExifTag.ExtraSamples); if (valueInternal != null) { short[] obj = (short[])valueInternal.GetValue(); if (obj.Length != 1) { TiffThrowHelper.ThrowNotSupported("ExtraSamples is only supported with one extra sample for alpha data."); } TiffExtraSampleType tiffExtraSampleType = (TiffExtraSampleType)obj[0]; options.ExtraSamplesType = tiffExtraSampleType; if ((uint)(tiffExtraSampleType - 1) > 1u) { TiffThrowHelper.ThrowNotSupported("Decoding Tiff images with ExtraSamples is not supported with UnspecifiedData."); } } TiffFillOrder tiffFillOrder = (TiffFillOrder)(exifProfile.GetValue(ExifTag.FillOrder)?.Value ?? 1); if (tiffFillOrder == TiffFillOrder.LeastSignificantBitFirst && frameMetadata.BitsPerPixel != TiffBitsPerPixel.Bit1) { TiffThrowHelper.ThrowNotSupported("The lower-order bits of the byte FillOrder is only supported in combination with 1bit per pixel bicolor tiff's."); } if (frameMetadata.Predictor == TiffPredictor.FloatingPoint) { TiffThrowHelper.ThrowNotSupported("TIFF images with FloatingPoint horizontal predictor are not supported."); } TiffSampleFormat[] array = exifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select((ushort a) => (TiffSampleFormat)a).ToArray(); TiffSampleFormat? tiffSampleFormat = null; if (array != null) { tiffSampleFormat = array[0]; TiffSampleFormat[] array2 = array; foreach (TiffSampleFormat tiffSampleFormat2 in array2) { if (tiffSampleFormat2 != TiffSampleFormat.UnsignedInteger && tiffSampleFormat2 != TiffSampleFormat.Float) { TiffThrowHelper.ThrowNotSupported("ImageSharp only supports the UnsignedInteger and Float SampleFormat."); } } } ushort[] array3 = exifProfile.GetValue(ExifTag.YCbCrSubsampling)?.Value; if (array3 != null && array3.Length != 2) { TiffThrowHelper.ThrowImageFormatException("Invalid YCbCrSubsampling, expected 2 values."); } if (array3 != null && array3[1] > array3[0]) { TiffThrowHelper.ThrowImageFormatException("ChromaSubsampleVert shall always be less than or equal to ChromaSubsampleHoriz."); } if (exifProfile.GetValue(ExifTag.StripRowCounts)?.Value != null) { TiffThrowHelper.ThrowNotSupported("Variable-sized strips are not supported."); } VerifyRequiredFieldsArePresent(exifProfile, frameMetadata); options.PlanarConfiguration = (TiffPlanarConfiguration)(exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? 1); options.Predictor = frameMetadata.Predictor ?? TiffPredictor.None; options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffPhotometricInterpretation.Rgb; options.SampleFormat = tiffSampleFormat ?? TiffSampleFormat.UnsignedInteger; options.BitsPerPixel = (int)(frameMetadata.BitsPerPixel.HasValue ? frameMetadata.BitsPerPixel.Value : TiffBitsPerPixel.Bit24); options.BitsPerSample = frameMetadata.BitsPerSample ?? new TiffBitsPerSample(0, 0, 0, 0); options.ReferenceBlackAndWhite = exifProfile.GetValue(ExifTag.ReferenceBlackWhite)?.Value; options.YcbcrCoefficients = exifProfile.GetValue(ExifTag.YCbCrCoefficients)?.Value; options.YcbcrSubSampling = exifProfile.GetValue(ExifTag.YCbCrSubsampling)?.Value; options.FillOrder = tiffFillOrder; options.JpegTables = exifProfile.GetValue(ExifTag.JPEGTables)?.Value; options.ParseColorType(exifProfile); options.ParseCompression(frameMetadata.Compression, exifProfile); } private static void VerifyRequiredFieldsArePresent(ExifProfile exifProfile, TiffFrameMetadata frameMetadata) { if (exifProfile.GetValueInternal(ExifTag.StripOffsets) == null) { TiffThrowHelper.ThrowImageFormatException("StripOffsets are missing and are required for decoding the TIFF image!"); } if (exifProfile.GetValueInternal(ExifTag.StripByteCounts) == null) { TiffThrowHelper.ThrowImageFormatException("StripByteCounts are missing and are required for decoding the TIFF image!"); } if (!frameMetadata.BitsPerPixel.HasValue) { TiffThrowHelper.ThrowNotSupported("The TIFF BitsPerSample entry is missing which is required to decode the image!"); } } private static void ParseColorType(this TiffDecoderCore options, ExifProfile exifProfile) { switch (options.PhotometricInterpretation) { case TiffPhotometricInterpretation.WhiteIsZero: { if (options.BitsPerSample.Channels != 1) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } ushort channel = options.BitsPerSample.Channel0; if (channel > 32) { TiffThrowHelper.ThrowNotSupported("Bits per sample is not supported."); } switch (channel) { case 32: if (options.SampleFormat == TiffSampleFormat.Float) { options.ColorType = TiffColorType.WhiteIsZero32Float; } else { options.ColorType = TiffColorType.WhiteIsZero32; } break; case 24: options.ColorType = TiffColorType.WhiteIsZero24; break; case 16: options.ColorType = TiffColorType.WhiteIsZero16; break; case 8: options.ColorType = TiffColorType.WhiteIsZero8; break; case 4: options.ColorType = TiffColorType.WhiteIsZero4; break; case 1: options.ColorType = TiffColorType.WhiteIsZero1; break; default: options.ColorType = TiffColorType.WhiteIsZero; break; } break; } case TiffPhotometricInterpretation.BlackIsZero: { if (options.BitsPerSample.Channels != 1) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } ushort channel2 = options.BitsPerSample.Channel0; if (channel2 > 32) { TiffThrowHelper.ThrowNotSupported("Bits per sample is not supported."); } switch (channel2) { case 32: if (options.SampleFormat == TiffSampleFormat.Float) { options.ColorType = TiffColorType.BlackIsZero32Float; } else { options.ColorType = TiffColorType.BlackIsZero32; } break; case 24: options.ColorType = TiffColorType.BlackIsZero24; break; case 16: options.ColorType = TiffColorType.BlackIsZero16; break; case 8: options.ColorType = TiffColorType.BlackIsZero8; break; case 4: options.ColorType = TiffColorType.BlackIsZero4; break; case 1: options.ColorType = TiffColorType.BlackIsZero1; break; default: options.ColorType = TiffColorType.BlackIsZero; break; } break; } case TiffPhotometricInterpretation.Rgb: { TiffBitsPerSample bitsPerSample = options.BitsPerSample; byte channels = bitsPerSample.Channels; if ((uint)(channels - 3) > 1u) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } if ((bitsPerSample.Channels == 3 && (bitsPerSample.Channel0 != bitsPerSample.Channel1 || bitsPerSample.Channel1 != bitsPerSample.Channel2)) || (bitsPerSample.Channels == 4 && (bitsPerSample.Channel0 != bitsPerSample.Channel1 || bitsPerSample.Channel1 != bitsPerSample.Channel2 || bitsPerSample.Channel2 != bitsPerSample.Channel3))) { TiffThrowHelper.ThrowNotSupported("Only BitsPerSample with equal bits per channel are supported."); } if (options.PlanarConfiguration == TiffPlanarConfiguration.Chunky) { switch (options.BitsPerSample.Channel0) { case 32: if (options.SampleFormat == TiffSampleFormat.Float) { options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.RgbFloat323232 : TiffColorType.RgbaFloat32323232); } else { options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb323232 : TiffColorType.Rgba32323232); } break; case 24: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb242424 : TiffColorType.Rgba24242424); break; case 16: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb161616 : TiffColorType.Rgba16161616); break; case 14: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb141414 : TiffColorType.Rgba14141414); break; case 12: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb121212 : TiffColorType.Rgba12121212); break; case 10: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb101010 : TiffColorType.Rgba10101010); break; case 8: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb888 : TiffColorType.Rgba8888); break; case 6: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb666 : TiffColorType.Rgba6666); break; case 5: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb555 : TiffColorType.Rgba5555); break; case 4: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb444 : TiffColorType.Rgba4444); break; case 3: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb333 : TiffColorType.Rgba3333); break; case 2: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb222 : TiffColorType.Rgba2222); break; default: TiffThrowHelper.ThrowNotSupported("Bits per sample is not supported."); break; } } else { switch (options.BitsPerSample.Channel0) { case 32: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb323232Planar : TiffColorType.Rgba32323232Planar); break; case 24: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb242424Planar : TiffColorType.Rgba24242424Planar); break; case 16: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb161616Planar : TiffColorType.Rgba16161616Planar); break; default: options.ColorType = ((options.BitsPerSample.Channels == 3) ? TiffColorType.Rgb888Planar : TiffColorType.Rgba8888Planar); break; } } break; } case TiffPhotometricInterpretation.PaletteColor: options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; if (options.ColorMap != null) { if (options.BitsPerSample.Channels != 1) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } options.ColorType = TiffColorType.PaletteColor; } else { TiffThrowHelper.ThrowNotSupported("The TIFF ColorMap entry is missing for a palette color image."); } break; case TiffPhotometricInterpretation.YCbCr: options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value; if (options.BitsPerSample.Channels != 3) { TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported."); } if (options.BitsPerSample.Channel0 != 8) { TiffThrowHelper.ThrowNotSupported("Only 8 bits per channel is supported for YCbCr images."); } options.ColorType = ((options.PlanarConfiguration == TiffPlanarConfiguration.Chunky) ? TiffColorType.YCbCr : TiffColorType.YCbCrPlanar); break; default: TiffThrowHelper.ThrowNotSupported($"The specified TIFF photometric interpretation is not supported: {options.PhotometricInterpretation}"); break; } } private static void ParseCompression(this TiffDecoderCore options, TiffCompression? compression, ExifProfile exifProfile) { switch (compression ?? TiffCompression.None) { case TiffCompression.None: options.CompressionType = TiffDecoderCompressionType.None; break; case TiffCompression.PackBits: options.CompressionType = TiffDecoderCompressionType.PackBits; break; case TiffCompression.Deflate: case TiffCompression.OldDeflate: options.CompressionType = TiffDecoderCompressionType.Deflate; break; case TiffCompression.Lzw: options.CompressionType = TiffDecoderCompressionType.Lzw; break; case TiffCompression.CcittGroup3Fax: options.CompressionType = TiffDecoderCompressionType.T4; options.FaxCompressionOptions = ((exifProfile.GetValue(ExifTag.T4Options) != null) ? ((FaxCompressionOptions)exifProfile.GetValue(ExifTag.T4Options).Value) : FaxCompressionOptions.None); break; case TiffCompression.CcittGroup4Fax: options.CompressionType = TiffDecoderCompressionType.T6; options.FaxCompressionOptions = ((exifProfile.GetValue(ExifTag.T4Options) != null) ? ((FaxCompressionOptions)exifProfile.GetValue(ExifTag.T4Options).Value) : FaxCompressionOptions.None); break; case TiffCompression.Ccitt1D: options.CompressionType = TiffDecoderCompressionType.HuffmanRle; break; case TiffCompression.Jpeg: options.CompressionType = TiffDecoderCompressionType.Jpeg; if (options.PhotometricInterpretation == TiffPhotometricInterpretation.YCbCr && options.JpegTables == null) { options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; options.ColorType = TiffColorType.Rgb; } break; default: TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format '{compression}' is not supported"); break; } } } public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { public TiffBitsPerPixel? BitsPerPixel { get; set; } public TiffCompression? Compression { get; set; } public DeflateCompressionLevel? CompressionLevel { get; set; } public TiffPhotometricInterpretation? PhotometricInterpretation { get; set; } public TiffPredictor? HorizontalPredictor { get; set; } public IQuantizer Quantizer { get; set; } public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { new TiffEncoderCore(this, image.GetMemoryAllocator()).Encode(image, stream); } public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return new TiffEncoderCore(this, image.GetMemoryAllocator()).EncodeAsync(image, stream, cancellationToken); } } internal sealed class TiffEncoderCore : IImageEncoderInternals { private static readonly ushort ByteOrderMarker = (ushort)(BitConverter.IsLittleEndian ? 18761 : 19789); private readonly MemoryAllocator memoryAllocator; private readonly byte[] buffer = new byte[4]; private Configuration configuration; private readonly IQuantizer quantizer; private readonly DeflateCompressionLevel compressionLevel; private const TiffPredictor DefaultPredictor = TiffPredictor.None; private const TiffBitsPerPixel DefaultBitsPerPixel = TiffBitsPerPixel.Bit24; private const TiffCompression DefaultCompression = TiffCompression.None; private const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.Rgb; private readonly List<(long, uint)> frameMarkers = new List<(long, uint)>(); internal TiffPhotometricInterpretation? PhotometricInterpretation { get; private set; } internal TiffCompression? CompressionType { get; set; } internal TiffPredictor? HorizontalPredictor { get; set; } internal TiffBitsPerPixel? BitsPerPixel { get; private set; } public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; PhotometricInterpretation = options.PhotometricInterpretation; quantizer = options.Quantizer ?? KnownQuantizers.Octree; BitsPerPixel = options.BitsPerPixel; HorizontalPredictor = options.HorizontalPredictor; CompressionType = options.Compression; compressionLevel = options.CompressionLevel ?? DeflateCompressionLevel.Level6; } public void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); configuration = image.GetConfiguration(); TiffFrameMetadata tiffMetadata = image.Frames.RootFrame.Metadata.GetTiffMetadata(); TiffBitsPerPixel? bitsPerPixel = BitsPerPixel ?? tiffMetadata.BitsPerPixel; TiffPhotometricInterpretation? photometricInterpretation = PhotometricInterpretation ?? tiffMetadata.PhotometricInterpretation; TiffPredictor predictor = HorizontalPredictor ?? tiffMetadata.Predictor ?? TiffPredictor.None; TiffCompression compression = CompressionType ?? tiffMetadata.Compression ?? TiffCompression.None; SanitizeAndSetEncoderOptions(bitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation, compression, predictor); using TiffStreamWriter tiffStreamWriter = new TiffStreamWriter(stream); long ifdOffset = WriteHeader(tiffStreamWriter); Image image2 = image; foreach (ImageFrame frame in image.Frames) { cancellationToken.ThrowIfCancellationRequested(); (frame.Metadata.ExifProfile?.GetValue(ExifTag.SubfileType)?.Value).GetValueOrDefault(); ifdOffset = WriteFrame(tiffStreamWriter, frame, image.Metadata, image2, ifdOffset); image2 = null; } long position = tiffStreamWriter.BaseStream.Position; foreach (var frameMarker in frameMarkers) { tiffStreamWriter.WriteMarkerFast(frameMarker.Item1, frameMarker.Item2); } tiffStreamWriter.BaseStream.Seek(position, SeekOrigin.Begin); } public long WriteHeader(TiffStreamWriter writer) { writer.Write(ByteOrderMarker); writer.Write((ushort)42); return writer.PlaceMarker(); } private long WriteFrame(TiffStreamWriter writer, ImageFrame frame, ImageMetadata imageMetadata, Image image, long ifdOffset) where TPixel : unmanaged, IPixel { using TiffBaseCompressor compressor = TiffCompressorFactory.Create(CompressionType ?? TiffCompression.None, writer.BaseStream, memoryAllocator, frame.Width, (int)BitsPerPixel.Value, compressionLevel, (HorizontalPredictor != TiffPredictor.Horizontal) ? TiffPredictor.None : HorizontalPredictor.Value); TiffEncoderEntriesCollector tiffEncoderEntriesCollector = new TiffEncoderEntriesCollector(); using TiffBaseColorWriter tiffBaseColorWriter = TiffColorWriterFactory.Create(PhotometricInterpretation, frame, quantizer, memoryAllocator, configuration, tiffEncoderEntriesCollector, (int)BitsPerPixel.Value); int rowsPerStrip = CalcRowsPerStrip(frame.Height, tiffBaseColorWriter.BytesPerRow, CompressionType); tiffBaseColorWriter.Write(compressor, rowsPerStrip); if (image != null) { tiffEncoderEntriesCollector.ProcessMetadata(image); } tiffEncoderEntriesCollector.ProcessFrameInfo(frame, imageMetadata); tiffEncoderEntriesCollector.ProcessImageFormat(this); frameMarkers.Add((ifdOffset, (uint)writer.Position)); return WriteIfd(writer, tiffEncoderEntriesCollector.Entries); } private int CalcRowsPerStrip(int height, int bytesPerRow, TiffCompression? compression) { if (compression.HasValue && compression == TiffCompression.Jpeg) { return height; } int num = (((compression.HasValue && compression == TiffCompression.Deflate) || (compression.HasValue && compression == TiffCompression.Lzw)) ? 16384 : 8192) / bytesPerRow; if (num > 0) { if (num < height) { return num; } return height; } return 1; } private long WriteIfd(TiffStreamWriter writer, List entries) { if (entries.Count == 0) { TiffThrowHelper.ThrowArgumentException("There must be at least one entry per IFD."); } uint num = (uint)((int)writer.Position + (6 + entries.Count * 12)); List list = new List(); entries.Sort((IExifValue a, IExifValue b) => (ushort)a.Tag - (ushort)b.Tag); writer.Write((ushort)entries.Count); foreach (IExifValue entry in entries) { writer.Write((ushort)entry.Tag); writer.Write((ushort)entry.DataType); writer.Write(ExifWriter.GetNumberOfComponents(entry)); uint length = ExifWriter.GetLength(entry); if (length <= 4) { int length2 = ExifWriter.WriteValue(entry, buffer, 0); writer.WritePadded(buffer.AsSpan(0, length2)); continue; } byte[] array = new byte[length]; ExifWriter.WriteValue(entry, array, 0); list.Add(array); writer.Write(num); num += (uint)(array.Length + array.Length % 2); } long result = writer.PlaceMarker(); foreach (byte[] item in list) { writer.Write(item); if (item.Length % 2 == 1) { writer.Write(0); } } return result; } private void SanitizeAndSetEncoderOptions(TiffBitsPerPixel? bitsPerPixel, int inputBitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression, TiffPredictor predictor) { if (bitsPerPixel.HasValue) { switch (bitsPerPixel) { case TiffBitsPerPixel.Bit1: if (compression == TiffCompression.Ccitt1D || compression == TiffCompression.CcittGroup3Fax || compression == TiffCompression.CcittGroup4Fax) { SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None); } else { SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.BlackIsZero, compression, TiffPredictor.None); } break; case TiffBitsPerPixel.Bit4: SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.PaletteColor, compression, TiffPredictor.None); break; case TiffBitsPerPixel.Bit8: SetEncoderOptions(bitsPerPixel, photometricInterpretation ?? TiffPhotometricInterpretation.BlackIsZero, compression, predictor); break; case TiffBitsPerPixel.Bit6: case TiffBitsPerPixel.Bit10: case TiffBitsPerPixel.Bit12: case TiffBitsPerPixel.Bit14: case TiffBitsPerPixel.Bit16: case TiffBitsPerPixel.Bit30: case TiffBitsPerPixel.Bit36: case TiffBitsPerPixel.Bit42: case TiffBitsPerPixel.Bit48: SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, TiffPredictor.None); break; default: SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.Rgb, compression, predictor); break; } return; } if (!photometricInterpretation.HasValue) { if (inputBitsPerPixel == 8) { SetEncoderOptions(TiffBitsPerPixel.Bit8, TiffPhotometricInterpretation.BlackIsZero, compression, predictor); } else { SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor); } return; } switch (photometricInterpretation) { case TiffPhotometricInterpretation.WhiteIsZero: case TiffPhotometricInterpretation.BlackIsZero: if (CompressionType == TiffCompression.Ccitt1D || CompressionType == TiffCompression.CcittGroup3Fax || CompressionType == TiffCompression.CcittGroup4Fax) { SetEncoderOptions(TiffBitsPerPixel.Bit1, photometricInterpretation, compression, TiffPredictor.None); } else { SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); } break; case TiffPhotometricInterpretation.PaletteColor: SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); break; case TiffPhotometricInterpretation.Rgb: SetEncoderOptions(TiffBitsPerPixel.Bit24, photometricInterpretation, compression, predictor); break; default: SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor); break; } } private void SetEncoderOptions(TiffBitsPerPixel? bitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression, TiffPredictor predictor) { BitsPerPixel = bitsPerPixel; PhotometricInterpretation = photometricInterpretation; CompressionType = compression; HorizontalPredictor = predictor; } } internal class TiffEncoderEntriesCollector { private abstract class BaseProcessor { protected TiffEncoderEntriesCollector Collector { get; } public BaseProcessor(TiffEncoderEntriesCollector collector) { Collector = collector; } } private class MetadataProcessor : BaseProcessor { public MetadataProcessor(TiffEncoderEntriesCollector collector) : base(collector) { } public void Process(Image image) { ImageFrame rootFrame = image.Frames.RootFrame; ExifProfile exifProfile = rootFrame.Metadata.ExifProfile ?? new ExifProfile(); XmpProfile xmpProfile = rootFrame.Metadata.XmpProfile; ProcessProfiles(image.Metadata, exifProfile, xmpProfile); ProcessMetadata(exifProfile); if (!base.Collector.Entries.Exists((IExifValue t) => t.Tag == ExifTag.Software)) { base.Collector.Add(new ExifString(ExifTagValue.Software) { Value = "ImageSharp" }); } } private static bool IsPureMetadata(ExifTag tag) { switch ((ExifTagValue)(ushort)tag) { case ExifTagValue.DocumentName: case ExifTagValue.ImageDescription: case ExifTagValue.Make: case ExifTagValue.Model: case ExifTagValue.Software: case ExifTagValue.DateTime: case ExifTagValue.Artist: case ExifTagValue.HostComputer: case ExifTagValue.TargetPrinter: case ExifTagValue.XMP: case ExifTagValue.Rating: case ExifTagValue.RatingPercent: case ExifTagValue.ImageID: case ExifTagValue.Copyright: case ExifTagValue.MDLabName: case ExifTagValue.MDSampleInfo: case ExifTagValue.MDPrepDate: case ExifTagValue.MDPrepTime: case ExifTagValue.MDFileUnits: case ExifTagValue.SEMInfo: case ExifTagValue.XPTitle: case ExifTagValue.XPComment: case ExifTagValue.XPAuthor: case ExifTagValue.XPKeywords: case ExifTagValue.XPSubject: return true; default: return false; } } private void ProcessMetadata(ExifProfile exifProfile) { foreach (IExifValue entry in exifProfile.Values) { if (entry.DataType == ExifDataType.Ifd) { continue; } switch ((ExifTagValue)(ushort)entry.Tag) { case ExifTagValue.SubIFDs: case ExifTagValue.XMP: case ExifTagValue.IPTC: case ExifTagValue.SubIFDOffset: case ExifTagValue.IccProfile: case ExifTagValue.GPSIFDOffset: continue; } switch (ExifTags.GetPart(entry.Tag)) { case ExifParts.IfdTags: if (!IsPureMetadata(entry.Tag)) { continue; } break; } if (!base.Collector.Entries.Exists((IExifValue t) => t.Tag == entry.Tag)) { base.Collector.AddOrReplace(entry.DeepClone()); } } } private void ProcessProfiles(ImageMetadata imageMetadata, ExifProfile exifProfile, XmpProfile xmpProfile) { if (exifProfile != null && exifProfile.Parts != ExifParts.None) { foreach (IExifValue entry in exifProfile.Values) { if (!base.Collector.Entries.Exists((IExifValue t) => t.Tag == entry.Tag) && entry.GetValue() != null) { ExifParts part = ExifTags.GetPart(entry.Tag); if (part != ExifParts.None && exifProfile.Parts.HasFlag(part)) { base.Collector.AddOrReplace(entry.DeepClone()); } } } } else { exifProfile.RemoveValue(ExifTag.SubIFDOffset); } if (imageMetadata.IptcProfile != null) { imageMetadata.IptcProfile.UpdateData(); ExifByteArray entry2 = new ExifByteArray(ExifTagValue.IPTC, ExifDataType.Byte) { Value = imageMetadata.IptcProfile.Data }; base.Collector.Add(entry2); } else { exifProfile.RemoveValue(ExifTag.IPTC); } if (imageMetadata.IccProfile != null) { ExifByteArray entry3 = new ExifByteArray(ExifTagValue.IccProfile, ExifDataType.Undefined) { Value = imageMetadata.IccProfile.ToByteArray() }; base.Collector.Add(entry3); } else { exifProfile.RemoveValue(ExifTag.IccProfile); } if (xmpProfile != null) { ExifByteArray entry4 = new ExifByteArray(ExifTagValue.XMP, ExifDataType.Byte) { Value = xmpProfile.Data }; base.Collector.Add(entry4); } else { exifProfile.RemoveValue(ExifTag.XMP); } } } private class FrameInfoProcessor : BaseProcessor { public FrameInfoProcessor(TiffEncoderEntriesCollector collector) : base(collector) { } public void Process(ImageFrame frame, ImageMetadata imageMetadata) { base.Collector.AddOrReplace(new ExifLong(ExifTagValue.ImageWidth) { Value = (uint)frame.Width }); base.Collector.AddOrReplace(new ExifLong(ExifTagValue.ImageLength) { Value = (uint)frame.Height }); ProcessResolution(imageMetadata); } private void ProcessResolution(ImageMetadata imageMetadata) { ExifResolutionValues exifResolutionValues = UnitConverter.GetExifResolutionValues(imageMetadata.ResolutionUnits, imageMetadata.HorizontalResolution, imageMetadata.VerticalResolution); base.Collector.AddOrReplace(new ExifShort(ExifTagValue.ResolutionUnit) { Value = exifResolutionValues.ResolutionUnit }); if (exifResolutionValues.VerticalResolution.HasValue && exifResolutionValues.HorizontalResolution.HasValue) { base.Collector.AddOrReplace(new ExifRational(ExifTagValue.XResolution) { Value = new Rational(exifResolutionValues.HorizontalResolution.Value) }); base.Collector.AddOrReplace(new ExifRational(ExifTagValue.YResolution) { Value = new Rational(exifResolutionValues.VerticalResolution.Value) }); } } } private class ImageFormatProcessor : BaseProcessor { public ImageFormatProcessor(TiffEncoderEntriesCollector collector) : base(collector) { } public void Process(TiffEncoderCore encoder) { ExifShort entry = new ExifShort(ExifTagValue.PlanarConfiguration) { Value = 1 }; ExifLong entry2 = new ExifLong(ExifTagValue.SamplesPerPixel) { Value = GetSamplesPerPixel(encoder) }; ushort[] bitsPerSampleValue = GetBitsPerSampleValue(encoder); ExifShortArray entry3 = new ExifShortArray(ExifTagValue.BitsPerSample) { Value = bitsPerSampleValue }; ushort compressionType = GetCompressionType(encoder); ExifShort entry4 = new ExifShort(ExifTagValue.Compression) { Value = compressionType }; ExifShort entry5 = new ExifShort(ExifTagValue.PhotometricInterpretation) { Value = (ushort)encoder.PhotometricInterpretation.Value }; base.Collector.AddOrReplace(entry); base.Collector.AddOrReplace(entry2); base.Collector.AddOrReplace(entry3); base.Collector.AddOrReplace(entry4); base.Collector.AddOrReplace(entry5); if (encoder.HorizontalPredictor == TiffPredictor.Horizontal && (encoder.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb || encoder.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor || encoder.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero)) { ExifShort entry6 = new ExifShort(ExifTagValue.Predictor) { Value = 2 }; base.Collector.AddOrReplace(entry6); } } private static uint GetSamplesPerPixel(TiffEncoderCore encoder) { switch (encoder.PhotometricInterpretation) { case TiffPhotometricInterpretation.WhiteIsZero: case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.PaletteColor: return 1u; default: return 3u; } } private static ushort[] GetBitsPerSampleValue(TiffEncoderCore encoder) { switch (encoder.PhotometricInterpretation) { case TiffPhotometricInterpretation.PaletteColor: if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit4) { return TiffConstants.BitsPerSample4Bit.ToArray(); } return TiffConstants.BitsPerSample8Bit.ToArray(); case TiffPhotometricInterpretation.Rgb: return TiffConstants.BitsPerSampleRgb8Bit.ToArray(); case TiffPhotometricInterpretation.WhiteIsZero: if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) { return TiffConstants.BitsPerSample1Bit.ToArray(); } return TiffConstants.BitsPerSample8Bit.ToArray(); case TiffPhotometricInterpretation.BlackIsZero: if (encoder.BitsPerPixel == TiffBitsPerPixel.Bit1) { return TiffConstants.BitsPerSample1Bit.ToArray(); } return TiffConstants.BitsPerSample8Bit.ToArray(); default: return TiffConstants.BitsPerSampleRgb8Bit.ToArray(); } } private static ushort GetCompressionType(TiffEncoderCore encoder) { switch (encoder.CompressionType) { case TiffCompression.Deflate: return 8; case TiffCompression.PackBits: return 32773; case TiffCompression.Lzw: if (encoder.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb || encoder.PhotometricInterpretation == TiffPhotometricInterpretation.PaletteColor || encoder.PhotometricInterpretation == TiffPhotometricInterpretation.BlackIsZero) { return 5; } break; case TiffCompression.CcittGroup3Fax: return 3; case TiffCompression.CcittGroup4Fax: return 4; case TiffCompression.Ccitt1D: return 2; case TiffCompression.Jpeg: return 7; } return 1; } } private const string SoftwareValue = "ImageSharp"; public List Entries { get; } = new List(); public void ProcessMetadata(Image image) { new MetadataProcessor(this).Process(image); } public void ProcessFrameInfo(ImageFrame frame, ImageMetadata imageMetadata) { new FrameInfoProcessor(this).Process(frame, imageMetadata); } public void ProcessImageFormat(TiffEncoderCore encoder) { new ImageFormatProcessor(this).Process(encoder); } public void AddOrReplace(IExifValue entry) { int num = Entries.FindIndex((IExifValue t) => t.Tag == entry.Tag); if (num >= 0) { Entries[num] = entry; } else { Entries.Add(entry); } } private void Add(IExifValue entry) { Entries.Add(entry); } } internal enum TiffExtraSampleType { UnspecifiedData, AssociatedAlphaData, UnassociatedAlphaData } public sealed class TiffFormat : IImageFormat, IImageFormat, IImageFormat { public static TiffFormat Instance { get; } = new TiffFormat(); public string Name => "TIFF"; public string DefaultMimeType => "image/tiff"; public IEnumerable MimeTypes => TiffConstants.MimeTypes; public IEnumerable FileExtensions => TiffConstants.FileExtensions; private TiffFormat() { } public TiffMetadata CreateDefaultFormatMetadata() { return new TiffMetadata(); } public TiffFrameMetadata CreateDefaultFormatFrameMetadata() { return new TiffFrameMetadata(); } } public enum TiffFormatType { Default, BigTIFF } public class TiffFrameMetadata : IDeepCloneable { public TiffBitsPerPixel? BitsPerPixel { get; set; } public TiffBitsPerSample? BitsPerSample { get; set; } public TiffCompression? Compression { get; set; } public TiffPhotometricInterpretation? PhotometricInterpretation { get; set; } public TiffPredictor? Predictor { get; set; } public TiffFrameMetadata() { } private TiffFrameMetadata(TiffFrameMetadata other) { BitsPerPixel = other.BitsPerPixel; Compression = other.Compression; PhotometricInterpretation = other.PhotometricInterpretation; Predictor = other.Predictor; } internal static TiffFrameMetadata Parse(ExifProfile profile) { TiffFrameMetadata tiffFrameMetadata = new TiffFrameMetadata(); Parse(tiffFrameMetadata, profile); return tiffFrameMetadata; } internal static void Parse(TiffFrameMetadata meta, ExifProfile profile) { if (profile != null) { if (TiffBitsPerSample.TryParse(profile.GetValue(ExifTag.BitsPerSample)?.Value, out var sample)) { meta.BitsPerSample = sample; } meta.BitsPerPixel = meta.BitsPerSample?.BitsPerPixel(); meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value; meta.PhotometricInterpretation = (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value; meta.Predictor = (TiffPredictor?)profile.GetValue(ExifTag.Predictor)?.Value; profile.RemoveValue(ExifTag.BitsPerSample); profile.RemoveValue(ExifTag.Compression); profile.RemoveValue(ExifTag.PhotometricInterpretation); profile.RemoveValue(ExifTag.Predictor); } } public IDeepCloneable DeepClone() { return new TiffFrameMetadata(this); } } public sealed class TiffImageFormatDetector : IImageFormatDetector { public int HeaderSize => 8; public IImageFormat DetectFormat(ReadOnlySpan header) { if (IsSupportedFileFormat(header)) { return TiffFormat.Instance; } return null; } private bool IsSupportedFileFormat(ReadOnlySpan header) { if (header.Length >= HeaderSize) { if (header[0] == 73 && header[1] == 73) { if (header[2] == 42 && header[3] == 0) { return true; } if (header[2] == 43 && header[3] == 0 && header[4] == 8 && header[5] == 0 && header[6] == 0 && header[7] == 0) { return true; } } else if (header[0] == 77 && header[1] == 77) { if (header[2] == 0 && header[3] == 42) { return true; } if (header[2] == 0 && header[3] == 43 && header[4] == 0 && header[5] == 8 && header[6] == 0 && header[7] == 0) { return true; } } } return false; } } public class TiffMetadata : IDeepCloneable { public ByteOrder ByteOrder { get; set; } public TiffFormatType FormatType { get; set; } public TiffMetadata() { } private TiffMetadata(TiffMetadata other) { ByteOrder = other.ByteOrder; } public IDeepCloneable DeepClone() { return new TiffMetadata(this); } } internal static class TiffThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static Exception ThrowImageFormatException(string errorMessage) { throw new ImageFormatException(errorMessage); } [MethodImpl(MethodImplOptions.NoInlining)] public static Exception NotSupportedDecompressor(string compressionType) { throw new NotSupportedException("Not supported decoder compression method: " + compressionType); } [MethodImpl(MethodImplOptions.NoInlining)] public static Exception NotSupportedCompressor(string compressionType) { throw new NotSupportedException("Not supported encoder compression method: " + compressionType); } [MethodImpl(MethodImplOptions.NoInlining)] public static Exception InvalidColorType(string colorType) { throw new NotSupportedException("Invalid color type: " + colorType); } [MethodImpl(MethodImplOptions.NoInlining)] public static Exception ThrowInvalidHeader() { throw new ImageFormatException("Invalid TIFF file header."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotSupported(string message) { throw new NotSupportedException(message); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentException(string message) { throw new ArgumentException(message); } } } namespace SixLabors.ImageSharp.Formats.Tiff.Writers { internal abstract class TiffBaseColorWriter : IDisposable where TPixel : unmanaged, IPixel { private bool isDisposed; public abstract int BitsPerPixel { get; } public int BytesPerRow => (Image.Width * BitsPerPixel + 7) / 8; protected ImageFrame Image { get; } protected MemoryAllocator MemoryAllocator { get; } protected Configuration Configuration { get; } protected TiffEncoderEntriesCollector EntriesCollector { get; } protected TiffBaseColorWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) { Image = image; MemoryAllocator = memoryAllocator; Configuration = configuration; EntriesCollector = entriesCollector; } public virtual void Write(TiffBaseCompressor compressor, int rowsPerStrip) { int num = (Image.Height + rowsPerStrip - 1) / rowsPerStrip; uint[] array = new uint[num]; uint[] array2 = new uint[num]; int num2 = 0; compressor.Initialize(rowsPerStrip); for (int i = 0; i < Image.Height; i += rowsPerStrip) { long position = compressor.Output.Position; int height = Math.Min(rowsPerStrip, Image.Height - i); EncodeStrip(i, height, compressor); long position2 = compressor.Output.Position; array[num2] = (uint)position; array2[num2] = (uint)(position2 - position); num2++; } AddStripTags(rowsPerStrip, array, array2); } public void Dispose() { if (!isDisposed) { isDisposed = true; Dispose(disposing: true); } } protected abstract void EncodeStrip(int y, int height, TiffBaseCompressor compressor); private void AddStripTags(int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) { EntriesCollector.AddOrReplace(new ExifLong(ExifTagValue.RowsPerStrip) { Value = (uint)rowsPerStrip }); EntriesCollector.AddOrReplace(new ExifLongArray(ExifTagValue.StripOffsets) { Value = stripOffsets }); EntriesCollector.AddOrReplace(new ExifLongArray(ExifTagValue.StripByteCounts) { Value = stripByteCounts }); } protected abstract void Dispose(bool disposing); } internal sealed class TiffBiColorWriter : TiffBaseColorWriter where TPixel : unmanaged, IPixel { private readonly Image imageBlackWhite; private IMemoryOwner pixelsAsGray; private IMemoryOwner bitStrip; public override int BitsPerPixel => 1; public TiffBiColorWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) : base(image, memoryAllocator, configuration, entriesCollector) { imageBlackWhite = new Image(configuration, new ImageMetadata(), new ImageFrame[1] { image.Clone() }); imageBlackWhite.Mutate(delegate(IImageProcessingContext img) { img.BinaryDither(KnownDitherings.FloydSteinberg); }); } protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { int width = base.Image.Width; if (compressor.Method == TiffCompression.CcittGroup3Fax || compressor.Method == TiffCompression.Ccitt1D || compressor.Method == TiffCompression.CcittGroup4Fax) { int stripPixels = width * height; if (pixelsAsGray == null) { pixelsAsGray = base.MemoryAllocator.Allocate(stripPixels); } imageBlackWhite.ProcessPixelRows(delegate(PixelAccessor accessor) { Span span4 = pixelsAsGray.GetSpan(); int num8 = y + height; int num9 = 0; for (int i = y; i < num8; i++) { Span rowSpan = accessor.GetRowSpan(i); Span destBytes = span4.Slice(num9 * width, width); PixelOperations.Instance.ToL8Bytes(base.Configuration, rowSpan, destBytes, width); num9++; } compressor.CompressStrip(span4.Slice(0, stripPixels), height); }); return; } int length = base.BytesPerRow * height; if (bitStrip == null) { bitStrip = base.MemoryAllocator.Allocate(length); } if (pixelsAsGray == null) { pixelsAsGray = base.MemoryAllocator.Allocate(width); } Span span = pixelsAsGray.GetSpan(); Span rows = bitStrip.Slice(0, length); rows.Clear(); Buffer2D pixelBuffer = imageBlackWhite.Frames.RootFrame.PixelBuffer; int num = 0; int num2 = y + height; for (int num3 = y; num3 < num2; num3++) { int num4 = 0; int num5 = 0; Span span2 = rows.Slice(num * base.BytesPerRow); Span span3 = pixelBuffer.DangerousGetRowSpan(num3); PixelOperations.Instance.ToL8Bytes(base.Configuration, span3, span, width); for (int num6 = 0; num6 < base.Image.Width; num6++) { int num7 = 7 - num4; if (span[num6] == byte.MaxValue) { span2[num5] |= (byte)(1 << num7); } num4++; if (num4 == 8) { num5++; num4 = 0; } } num++; } compressor.CompressStrip(rows, height); } protected override void Dispose(bool disposing) { imageBlackWhite?.Dispose(); pixelsAsGray?.Dispose(); bitStrip?.Dispose(); } } internal static class TiffColorWriterFactory { public static TiffBaseColorWriter Create(TiffPhotometricInterpretation? photometricInterpretation, ImageFrame image, IQuantizer quantizer, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector, int bitsPerPixel) where TPixel : unmanaged, IPixel { switch (photometricInterpretation) { case TiffPhotometricInterpretation.PaletteColor: return new TiffPaletteWriter(image, quantizer, memoryAllocator, configuration, entriesCollector, bitsPerPixel); case TiffPhotometricInterpretation.WhiteIsZero: case TiffPhotometricInterpretation.BlackIsZero: if (bitsPerPixel == 1) { return new TiffBiColorWriter(image, memoryAllocator, configuration, entriesCollector); } return new TiffGrayWriter(image, memoryAllocator, configuration, entriesCollector); default: return new TiffRgbWriter(image, memoryAllocator, configuration, entriesCollector); } } } internal abstract class TiffCompositeColorWriter : TiffBaseColorWriter where TPixel : unmanaged, IPixel { private IMemoryOwner rowBuffer; protected TiffCompositeColorWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) : base(image, memoryAllocator, configuration, entriesCollector) { } protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { if (rowBuffer == null) { rowBuffer = base.MemoryAllocator.Allocate(base.BytesPerRow * height); } rowBuffer.Clear(); Span span = rowBuffer.GetSpan().Slice(0, base.BytesPerRow * height); int width = base.Image.Width; using IMemoryOwner buffer = base.MemoryAllocator.Allocate(height * width); Span span2 = buffer.GetSpan(); int num = y + height; int num2 = 0; for (int i = y; i < num; i++) { base.Image.PixelBuffer.DangerousGetRowSpan(i).CopyTo(span2.Slice(num2 * width, width)); num2++; } EncodePixels(span2, span); compressor.CompressStrip(span, height); } protected abstract void EncodePixels(Span pixels, Span buffer); protected override void Dispose(bool disposing) { rowBuffer?.Dispose(); } } internal sealed class TiffGrayWriter : TiffCompositeColorWriter where TPixel : unmanaged, IPixel { public override int BitsPerPixel => 8; public TiffGrayWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) : base(image, memoryAllocator, configuration, entriesCollector) { } protected override void EncodePixels(Span pixels, Span buffer) { PixelOperations.Instance.ToL8Bytes(base.Configuration, pixels, buffer, pixels.Length); } } internal sealed class TiffPaletteWriter : TiffBaseColorWriter where TPixel : unmanaged, IPixel { private readonly int maxColors; private readonly int colorPaletteSize; private readonly int colorPaletteBytes; private readonly IndexedImageFrame quantizedImage; private IMemoryOwner indexedPixelsBuffer; public override int BitsPerPixel { get; } public TiffPaletteWriter(ImageFrame image, IQuantizer quantizer, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector, int bitsPerPixel) : base(image, memoryAllocator, configuration, entriesCollector) { BitsPerPixel = bitsPerPixel; maxColors = ((BitsPerPixel == 4) ? 16 : 256); colorPaletteSize = maxColors * 3; colorPaletteBytes = colorPaletteSize * 2; using IQuantizer quantizer2 = quantizer.CreatePixelSpecificQuantizer(base.Configuration, new QuantizerOptions { MaxColors = maxColors }); quantizedImage = quantizer2.BuildPaletteAndQuantizeFrame(image, image.Bounds()); AddColorMapTag(); } protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { int width = base.Image.Width; if (BitsPerPixel == 4) { int num = width >> 1; int num2 = (width & 1) * height; int length = num * height + num2; if (indexedPixelsBuffer == null) { indexedPixelsBuffer = base.MemoryAllocator.Allocate(length); } Span span = indexedPixelsBuffer.GetSpan(); int num3 = 0; int num4 = y + height; for (int i = y; i < num4; i++) { ReadOnlySpan readOnlySpan = quantizedImage.DangerousGetRowSpan(i); int num5 = 0; for (int j = 0; j < num; j++) { span[num3] = (byte)((readOnlySpan[num5] << 4) | (readOnlySpan[num5 + 1] & 0xF)); num5 += 2; num3++; } if (width % 2 != 0) { span[num3++] = (byte)(readOnlySpan[num5] << 4); } } compressor.CompressStrip(span.Slice(0, num3), height); } else { int length2 = width * height; if (indexedPixelsBuffer == null) { indexedPixelsBuffer = base.MemoryAllocator.Allocate(length2); } Span span2 = indexedPixelsBuffer.GetSpan(); int num6 = y + height; int num7 = 0; for (int k = y; k < num6; k++) { quantizedImage.DangerousGetRowSpan(k).CopyTo(span2.Slice(num7 * width, width)); num7++; } compressor.CompressStrip(span2.Slice(0, length2), height); } } protected override void Dispose(bool disposing) { quantizedImage?.Dispose(); indexedPixelsBuffer?.Dispose(); } private void AddColorMapTag() { using IMemoryOwner buffer = base.MemoryAllocator.Allocate(colorPaletteBytes); Span span = buffer.GetSpan(); ReadOnlySpan span2 = quantizedImage.Palette.Span; Span destinationPixels = MemoryMarshal.Cast(span[..(span2.Length * 3 * 2)]); PixelOperations.Instance.ToRgb48(base.Configuration, span2, destinationPixels); int num = maxColors - span2.Length; ushort[] array = new ushort[colorPaletteSize]; int num2 = 0; for (int i = 0; i < span2.Length; i++) { array[num2++] = destinationPixels[i].R; } num2 += num; for (int j = 0; j < span2.Length; j++) { array[num2++] = destinationPixels[j].G; } num2 += num; for (int k = 0; k < span2.Length; k++) { array[num2++] = destinationPixels[k].B; } ExifShortArray entry = new ExifShortArray(ExifTagValue.ColorMap) { Value = array }; base.EntriesCollector.AddOrReplace(entry); } } internal sealed class TiffRgbWriter : TiffCompositeColorWriter where TPixel : unmanaged, IPixel { public override int BitsPerPixel => 24; public TiffRgbWriter(ImageFrame image, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) : base(image, memoryAllocator, configuration, entriesCollector) { } protected override void EncodePixels(Span pixels, Span buffer) { PixelOperations.Instance.ToRgb24Bytes(base.Configuration, pixels, buffer, pixels.Length); } } internal sealed class TiffStreamWriter : IDisposable { private static readonly byte[] PaddingBytes = new byte[4]; private readonly byte[] buffer = new byte[4]; public bool IsLittleEndian => BitConverter.IsLittleEndian; public long Position => BaseStream.Position; public Stream BaseStream { get; } public TiffStreamWriter(Stream output) { BaseStream = output; } public long PlaceMarker() { long position = BaseStream.Position; Write(0u); return position; } public void Write(byte[] value) { BaseStream.Write(value, 0, value.Length); } public void Write(ReadOnlySpan value) { BaseStream.Write(value); } public void Write(byte value) { BaseStream.WriteByte(value); } public void Write(ushort value) { if (IsLittleEndian) { BinaryPrimitives.WriteUInt16LittleEndian(buffer, value); } else { BinaryPrimitives.WriteUInt16BigEndian(buffer, value); } BaseStream.Write(buffer.AsSpan(0, 2)); } public void Write(uint value) { if (IsLittleEndian) { BinaryPrimitives.WriteUInt32LittleEndian(buffer, value); } else { BinaryPrimitives.WriteUInt32BigEndian(buffer, value); } BaseStream.Write(buffer.AsSpan(0, 4)); } public void WritePadded(Span value) { BaseStream.Write(value); if (value.Length % 4 != 0) { BaseStream.Write(PaddingBytes, 0, 4 - value.Length % 4); } } public void WriteMarker(long offset, uint value) { long position = BaseStream.Position; BaseStream.Seek(offset, SeekOrigin.Begin); Write(value); BaseStream.Seek(position, SeekOrigin.Begin); } public void WriteMarkerFast(long offset, uint value) { BaseStream.Seek(offset, SeekOrigin.Begin); Write(value); } public void Dispose() { BaseStream.Flush(); } } } namespace SixLabors.ImageSharp.Formats.Tiff.Utils { internal ref struct BitReader { private readonly ReadOnlySpan array; private int offset; private int bitOffset; public BitReader(ReadOnlySpan array) { this.array = array; offset = 0; bitOffset = 0; } public int ReadBits(uint bits) { int num = 0; for (uint num2 = 0u; num2 < bits; num2++) { int num3 = (array[offset] >> 7 - bitOffset) & 1; num = (num << 1) | num3; bitOffset++; if (bitOffset == 8) { bitOffset = 0; offset++; } } return num; } public void NextRow() { if (bitOffset > 0) { bitOffset = 0; offset++; } } } internal static class TiffUtils { private const float Scale24Bit = 5.960465E-08f; private const float Scale32Bit = 2.3283064E-10f; public static Vector4 Vector4Default { get; } = new Vector4(0f, 0f, 0f, 0f); public static Rgba64 Rgba64Default { get; } = new Rgba64(0, 0, 0, 0); public static L16 L16Default { get; } = new L16(0); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ConvertToUShortBigEndian(ReadOnlySpan buffer) { return BinaryPrimitives.ReadUInt16BigEndian(buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ConvertToUShortLittleEndian(ReadOnlySpan buffer) { return BinaryPrimitives.ReadUInt16LittleEndian(buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ConvertToUIntBigEndian(ReadOnlySpan buffer) { return BinaryPrimitives.ReadUInt32BigEndian(buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ConvertToUIntLittleEndian(ReadOnlySpan buffer) { return BinaryPrimitives.ReadUInt32LittleEndian(buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorFromL8(L8 l8, byte intensity, TPixel color) where TPixel : unmanaged, IPixel { l8.PackedValue = intensity; color.FromL8(l8); return color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorFromRgb64(Rgba64 rgba, ulong r, ulong g, ulong b, TPixel color) where TPixel : unmanaged, IPixel { rgba.PackedValue = r | (g << 16) | (b << 32) | 0xFFFF000000000000uL; color.FromRgba64(rgba); return color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorFromRgba64(Rgba64 rgba, ulong r, ulong g, ulong b, ulong a, TPixel color) where TPixel : unmanaged, IPixel { rgba.PackedValue = r | (g << 16) | (b << 32) | (a << 48); color.FromRgba64(rgba); return color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorFromRgba64Premultiplied(Rgba64 rgba, ulong r, ulong g, ulong b, ulong a, TPixel color) where TPixel : unmanaged, IPixel { rgba.PackedValue = r | (g << 16) | (b << 32) | (a << 48); Vector4 vector = rgba.ToVector4(); return UnPremultiply(ref vector, color); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorScaleTo24Bit(ulong r, ulong g, ulong b, TPixel color) where TPixel : unmanaged, IPixel { Vector4 vector = new Vector4((float)r * 5.960465E-08f, (float)g * 5.960465E-08f, (float)b * 5.960465E-08f, 1f); color.FromScaledVector4(vector); return color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorScaleTo24Bit(ulong r, ulong g, ulong b, ulong a, TPixel color) where TPixel : unmanaged, IPixel { Vector4 vector = new Vector4(r, g, b, a) * 5.960465E-08f; color.FromScaledVector4(vector); return color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorScaleTo24BitPremultiplied(ulong r, ulong g, ulong b, ulong a, TPixel color) where TPixel : unmanaged, IPixel { Vector4 vector = new Vector4(r, g, b, a) * 5.960465E-08f; return UnPremultiply(ref vector, color); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorScaleTo32Bit(ulong r, ulong g, ulong b, TPixel color) where TPixel : unmanaged, IPixel { Vector4 vector = new Vector4((float)r * 2.3283064E-10f, (float)g * 2.3283064E-10f, (float)b * 2.3283064E-10f, 1f); color.FromScaledVector4(vector); return color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorScaleTo32Bit(ulong r, ulong g, ulong b, ulong a, TPixel color) where TPixel : unmanaged, IPixel { Vector4 vector = new Vector4(r, g, b, a) * 2.3283064E-10f; color.FromScaledVector4(vector); return color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorScaleTo32BitPremultiplied(ulong r, ulong g, ulong b, ulong a, TPixel color) where TPixel : unmanaged, IPixel { Vector4 vector = new Vector4(r, g, b, a) * 2.3283064E-10f; return UnPremultiply(ref vector, color); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorFromL16(L16 l16, ushort intensity, TPixel color) where TPixel : unmanaged, IPixel { l16.PackedValue = intensity; color.FromL16(l16); return color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorScaleTo24Bit(ulong intensity, TPixel color) where TPixel : unmanaged, IPixel { Vector4 vector = new Vector4((float)intensity * 5.960465E-08f, (float)intensity * 5.960465E-08f, (float)intensity * 5.960465E-08f, 1f); color.FromScaledVector4(vector); return color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ColorScaleTo32Bit(ulong intensity, TPixel color) where TPixel : unmanaged, IPixel { Vector4 vector = new Vector4((float)intensity * 2.3283064E-10f, (float)intensity * 2.3283064E-10f, (float)intensity * 2.3283064E-10f, 1f); color.FromScaledVector4(vector); return color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel UnPremultiply(ref Vector4 vector, TPixel color) where TPixel : unmanaged, IPixel { Numerics.UnPremultiply(ref vector); color.FromScaledVector4(vector); return color; } public static int PaddingToNextInteger(int valueToRoundUp, int subSampling) { if (valueToRoundUp % subSampling == 0) { return 0; } return subSampling - valueToRoundUp % subSampling; } } } namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { internal class BlackIsZero16TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; private readonly Configuration configuration; public BlackIsZero16TiffColor(Configuration configuration, bool isBigEndian) { this.configuration = configuration; this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { L16 l16Default = TiffUtils.L16Default; TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); int num = 0; for (int i = top; i < top + height; i++) { Span destinationPixels = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < destinationPixels.Length; j++) { ushort intensity = TiffUtils.ConvertToUShortBigEndian(data.Slice(num, 2)); num += 2; destinationPixels[j] = TiffUtils.ColorFromL16(l16Default, intensity, color); } } else { int num2 = destinationPixels.Length * 2; PixelOperations.Instance.FromL16Bytes(configuration, data.Slice(num, num2), destinationPixels, destinationPixels.Length); num += num2; } } } } internal class BlackIsZero1TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); int num = 0; Color black = Color.Black; Color white = Color.White; for (int i = top; i < top + height; i++) { for (int j = left; j < left + width; j += 8) { byte b = data[num++]; int num2 = Math.Min(left + width - j, 8); for (int k = 0; k < num2; k++) { int num3 = (b >> 7 - k) & 1; val.FromRgba32((num3 == 0) ? black : white); pixels[j + k, i] = val; } } } } } internal class BlackIsZero24TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public BlackIsZero24TiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); byte[] array = new byte[4]; int start = (isBigEndian ? 1 : 0); Span destination = array.AsSpan(start); int num = 0; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { data.Slice(num, 3).CopyTo(destination); ulong intensity = TiffUtils.ConvertToUIntBigEndian(array); num += 3; span[j] = TiffUtils.ColorScaleTo24Bit(intensity, color); } } else { for (int k = 0; k < span.Length; k++) { data.Slice(num, 3).CopyTo(destination); ulong intensity2 = TiffUtils.ConvertToUIntLittleEndian(array); num += 3; span[k] = TiffUtils.ColorScaleTo24Bit(intensity2, color); } } } } } internal class BlackIsZero32FloatTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public BlackIsZero32FloatTiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); val.FromScaledVector4(TiffUtils.Vector4Default); byte[] array = new byte[4]; int num = 0; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { data.Slice(num, 4).CopyTo(array); Array.Reverse(array); float num2 = BitConverter.ToSingle(array, 0); num += 4; Vector4 vector = new Vector4(num2, num2, num2, 1f); val.FromScaledVector4(vector); span[j] = val; } } else { for (int k = 0; k < span.Length; k++) { data.Slice(num, 4).CopyTo(array); float num3 = BitConverter.ToSingle(array, 0); num += 4; Vector4 vector2 = new Vector4(num3, num3, num3, 1f); val.FromScaledVector4(vector2); span[k] = val; } } } } } internal class BlackIsZero32TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public BlackIsZero32TiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); int num = 0; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { ulong intensity = TiffUtils.ConvertToUIntBigEndian(data.Slice(num, 4)); num += 4; span[j] = TiffUtils.ColorScaleTo32Bit(intensity, color); } } else { for (int k = 0; k < span.Length; k++) { ulong intensity2 = TiffUtils.ConvertToUIntLittleEndian(data.Slice(num, 4)); num += 4; span[k] = TiffUtils.ColorScaleTo32Bit(intensity2, color); } } } } } internal class BlackIsZero4TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); int num = 0; bool flag = (width & 1) == 1; L8 source = default(L8); for (int i = top; i < top + height; i++) { int num2 = left; while (num2 < left + width - 1) { byte num3 = data[num++]; byte packedValue = (byte)(((num3 & 0xF0) >> 4) * 17); source.PackedValue = packedValue; val.FromL8(source); pixels[num2++, i] = val; byte packedValue2 = (byte)((num3 & 0xF) * 17); source.PackedValue = packedValue2; val.FromL8(source); pixels[num2++, i] = val; } if (flag) { byte packedValue3 = (byte)(((data[num++] & 0xF0) >> 4) * 17); source.PackedValue = packedValue3; val.FromL8(source); pixels[left + width - 1, i] = val; } } } } internal class BlackIsZero8TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly Configuration configuration; public BlackIsZero8TiffColor(Configuration configuration) { this.configuration = configuration; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { int num = 0; for (int i = top; i < top + height; i++) { Span destinationPixels = pixels.DangerousGetRowSpan(i).Slice(left, width); int length = destinationPixels.Length; PixelOperations.Instance.FromL8Bytes(configuration, data.Slice(num, length), destinationPixels, destinationPixels.Length); num += length; } } } internal class BlackIsZeroTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; private readonly float factor; public BlackIsZeroTiffColor(TiffBitsPerSample bitsPerSample) { bitsPerSample0 = bitsPerSample.Channel0; factor = (float)(1 << (int)bitsPerSample0) - 1f; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); BitReader bitReader = new BitReader(data); for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); for (int j = 0; j < span.Length; j++) { float num = (float)bitReader.ReadBits(bitsPerSample0) / factor; val.FromScaledVector4(new Vector4(num, num, num, 1f)); span[j] = val; } bitReader.NextRow(); } } } internal class PaletteTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; private readonly TPixel[] palette; public PaletteTiffColor(TiffBitsPerSample bitsPerSample, ushort[] colorMap) { bitsPerSample0 = bitsPerSample.Channel0; int colorCount = 1 << (int)bitsPerSample0; palette = GeneratePalette(colorMap, colorCount); } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { BitReader bitReader = new BitReader(data); for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); for (int j = 0; j < span.Length; j++) { int num = bitReader.ReadBits(bitsPerSample0); span[j] = palette[num]; } bitReader.NextRow(); } } private static TPixel[] GeneratePalette(ushort[] colorMap, int colorCount) { TPixel[] array = new TPixel[colorCount]; int num = colorCount * 2; for (int i = 0; i < array.Length; i++) { float x = (float)(int)colorMap[i] / 65535f; float y = (float)(int)colorMap[colorCount + i] / 65535f; float z = (float)(int)colorMap[num + i] / 65535f; array[i].FromScaledVector4(new Vector4(x, y, z, 1f)); } return array; } } internal class Rgb161616TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; private readonly Configuration configuration; public Rgb161616TiffColor(Configuration configuration, bool isBigEndian) { this.configuration = configuration; this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { Rgba64 rgba64Default = TiffUtils.Rgba64Default; TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); int num = 0; for (int i = top; i < top + height; i++) { Span destinationPixels = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < destinationPixels.Length; j++) { ulong r = TiffUtils.ConvertToUShortBigEndian(data.Slice(num, 2)); num += 2; ulong g = TiffUtils.ConvertToUShortBigEndian(data.Slice(num, 2)); num += 2; ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(num, 2)); num += 2; destinationPixels[j] = TiffUtils.ColorFromRgb64(rgba64Default, r, g, b, color); } } else { int num2 = destinationPixels.Length * 6; PixelOperations.Instance.FromRgb48Bytes(configuration, data.Slice(num, num2), destinationPixels, destinationPixels.Length); num += num2; } } } } internal class Rgb16PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public Rgb16PlanarTiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { Rgba64 rgba64Default = TiffUtils.Rgba64Default; TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); Span span = data[0].GetSpan(); Span span2 = data[1].GetSpan(); Span span3 = data[2].GetSpan(); int num = 0; for (int i = top; i < top + height; i++) { Span span4 = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span4.Length; j++) { ulong r = TiffUtils.ConvertToUShortBigEndian(span.Slice(num, 2)); ulong g = TiffUtils.ConvertToUShortBigEndian(span2.Slice(num, 2)); ulong b = TiffUtils.ConvertToUShortBigEndian(span3.Slice(num, 2)); num += 2; span4[j] = TiffUtils.ColorFromRgb64(rgba64Default, r, g, b, color); } } else { for (int k = 0; k < span4.Length; k++) { ulong r2 = TiffUtils.ConvertToUShortLittleEndian(span.Slice(num, 2)); ulong g2 = TiffUtils.ConvertToUShortLittleEndian(span2.Slice(num, 2)); ulong b2 = TiffUtils.ConvertToUShortLittleEndian(span3.Slice(num, 2)); num += 2; span4[k] = TiffUtils.ColorFromRgb64(rgba64Default, r2, g2, b2, color); } } } } } internal class Rgb242424TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public Rgb242424TiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); int num = 0; Span span = stackalloc byte[4]; int start = (isBigEndian ? 1 : 0); Span destination = span.Slice(start); for (int i = top; i < top + height; i++) { Span span2 = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span2.Length; j++) { data.Slice(num, 3).CopyTo(destination); ulong r = TiffUtils.ConvertToUIntBigEndian(span); num += 3; data.Slice(num, 3).CopyTo(destination); ulong g = TiffUtils.ConvertToUIntBigEndian(span); num += 3; data.Slice(num, 3).CopyTo(destination); ulong b = TiffUtils.ConvertToUIntBigEndian(span); num += 3; span2[j] = TiffUtils.ColorScaleTo24Bit(r, g, b, color); } } else { for (int k = 0; k < span2.Length; k++) { data.Slice(num, 3).CopyTo(destination); ulong r2 = TiffUtils.ConvertToUIntLittleEndian(span); num += 3; data.Slice(num, 3).CopyTo(destination); ulong g2 = TiffUtils.ConvertToUIntLittleEndian(span); num += 3; data.Slice(num, 3).CopyTo(destination); ulong b2 = TiffUtils.ConvertToUIntLittleEndian(span); num += 3; span2[k] = TiffUtils.ColorScaleTo24Bit(r2, g2, b2, color); } } } } } internal class Rgb24PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public Rgb24PlanarTiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); Span span = stackalloc byte[4]; int start = (isBigEndian ? 1 : 0); Span span2 = data[0].GetSpan(); Span span3 = data[1].GetSpan(); Span span4 = data[2].GetSpan(); Span destination = span.Slice(start); int num = 0; for (int i = top; i < top + height; i++) { Span span5 = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span5.Length; j++) { span2.Slice(num, 3).CopyTo(destination); ulong r = TiffUtils.ConvertToUIntBigEndian(span); span3.Slice(num, 3).CopyTo(destination); ulong g = TiffUtils.ConvertToUIntBigEndian(span); span4.Slice(num, 3).CopyTo(destination); ulong b = TiffUtils.ConvertToUIntBigEndian(span); num += 3; span5[j] = TiffUtils.ColorScaleTo24Bit(r, g, b, color); } } else { for (int k = 0; k < span5.Length; k++) { span2.Slice(num, 3).CopyTo(destination); ulong r2 = TiffUtils.ConvertToUIntLittleEndian(span); span3.Slice(num, 3).CopyTo(destination); ulong g2 = TiffUtils.ConvertToUIntLittleEndian(span); span4.Slice(num, 3).CopyTo(destination); ulong b2 = TiffUtils.ConvertToUIntLittleEndian(span); num += 3; span5[k] = TiffUtils.ColorScaleTo24Bit(r2, g2, b2, color); } } } } } internal class Rgb323232TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public Rgb323232TiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); int num = 0; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { ulong r = TiffUtils.ConvertToUIntBigEndian(data.Slice(num, 4)); num += 4; ulong g = TiffUtils.ConvertToUIntBigEndian(data.Slice(num, 4)); num += 4; ulong b = TiffUtils.ConvertToUIntBigEndian(data.Slice(num, 4)); num += 4; span[j] = TiffUtils.ColorScaleTo32Bit(r, g, b, color); } } else { for (int k = 0; k < span.Length; k++) { ulong r2 = TiffUtils.ConvertToUIntLittleEndian(data.Slice(num, 4)); num += 4; ulong g2 = TiffUtils.ConvertToUIntLittleEndian(data.Slice(num, 4)); num += 4; ulong b2 = TiffUtils.ConvertToUIntLittleEndian(data.Slice(num, 4)); num += 4; span[k] = TiffUtils.ColorScaleTo32Bit(r2, g2, b2, color); } } } } } internal class Rgb32PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public Rgb32PlanarTiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); Span span = data[0].GetSpan(); Span span2 = data[1].GetSpan(); Span span3 = data[2].GetSpan(); int num = 0; for (int i = top; i < top + height; i++) { Span span4 = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span4.Length; j++) { ulong r = TiffUtils.ConvertToUIntBigEndian(span.Slice(num, 4)); ulong g = TiffUtils.ConvertToUIntBigEndian(span2.Slice(num, 4)); ulong b = TiffUtils.ConvertToUIntBigEndian(span3.Slice(num, 4)); num += 4; span4[j] = TiffUtils.ColorScaleTo32Bit(r, g, b, color); } } else { for (int k = 0; k < span4.Length; k++) { ulong r2 = TiffUtils.ConvertToUIntLittleEndian(span.Slice(num, 4)); ulong g2 = TiffUtils.ConvertToUIntLittleEndian(span2.Slice(num, 4)); ulong b2 = TiffUtils.ConvertToUIntLittleEndian(span3.Slice(num, 4)); num += 4; span4[k] = TiffUtils.ColorScaleTo32Bit(r2, g2, b2, color); } } } } } internal class Rgb444TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); int num = 0; Bgra4444 bgra = default(Bgra4444); for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i); for (int j = left; j < left + width; j += 2) { byte r = (byte)((data[num] & 0xF0) >> 4); byte g = (byte)(data[num] & 0xF); num++; byte b = (byte)((data[num] & 0xF0) >> 4); bgra.PackedValue = ToBgraPackedValue(b, g, r); val.FromScaledVector4(bgra.ToScaledVector4()); span[j] = val; if (j + 1 >= span.Length) { num++; break; } r = (byte)(data[num] & 0xF); num++; g = (byte)((data[num] & 0xF0) >> 4); b = (byte)(data[num] & 0xF); num++; bgra.PackedValue = ToBgraPackedValue(b, g, r); val.FromScaledVector4(bgra.ToScaledVector4()); span[j + 1] = val; } } } private static ushort ToBgraPackedValue(byte b, byte g, byte r) { return (ushort)(b | (g << 4) | (r << 8) | 0xF000); } } internal class Rgb888TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly Configuration configuration; public Rgb888TiffColor(Configuration configuration) { this.configuration = configuration; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { int num = 0; for (int i = top; i < top + height; i++) { Span destinationPixels = pixels.DangerousGetRowSpan(i).Slice(left, width); int num2 = destinationPixels.Length * 3; PixelOperations.Instance.FromRgb24Bytes(configuration, data.Slice(num, num2), destinationPixels, destinationPixels.Length); num += num2; } } } internal class Rgba16161616TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; private readonly Configuration configuration; private readonly MemoryAllocator memoryAllocator; private readonly TiffExtraSampleType? extraSamplesType; public Rgba16161616TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType, bool isBigEndian) { this.configuration = configuration; this.isBigEndian = isBigEndian; this.memoryAllocator = memoryAllocator; this.extraSamplesType = extraSamplesType; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { Rgba64 rgba64Default = TiffUtils.Rgba64Default; TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); bool flag = extraSamplesType.HasValue && extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int num = 0; using IMemoryOwner buffer = (flag ? memoryAllocator.Allocate(width) : null); Span span = (flag ? buffer.GetSpan() : Span.Empty); for (int i = top; i < top + height; i++) { Span span2 = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span2.Length; j++) { ulong r = TiffUtils.ConvertToUShortBigEndian(data.Slice(num, 2)); num += 2; ulong g = TiffUtils.ConvertToUShortBigEndian(data.Slice(num, 2)); num += 2; ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(num, 2)); num += 2; ulong a = TiffUtils.ConvertToUShortBigEndian(data.Slice(num, 2)); num += 2; span2[j] = (flag ? TiffUtils.ColorFromRgba64Premultiplied(rgba64Default, r, g, b, a, color) : TiffUtils.ColorFromRgba64(rgba64Default, r, g, b, a, color)); } } else { int num2 = span2.Length * 8; PixelOperations.Instance.FromRgba64Bytes(configuration, data.Slice(num, num2), span2, span2.Length); if (flag) { PixelOperations.Instance.ToVector4(configuration, span2, span); PixelOperations.Instance.FromVector4Destructive(configuration, span, span2, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); } num += num2; } } } } internal class Rgba16PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; private readonly TiffExtraSampleType? extraSamplesType; public Rgba16PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian) { this.extraSamplesType = extraSamplesType; this.isBigEndian = isBigEndian; } public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { Rgba64 rgba64Default = TiffUtils.Rgba64Default; TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); Span span = data[0].GetSpan(); Span span2 = data[1].GetSpan(); Span span3 = data[2].GetSpan(); Span span4 = data[3].GetSpan(); bool flag = extraSamplesType.HasValue && extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int num = 0; for (int i = top; i < top + height; i++) { Span span5 = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span5.Length; j++) { ulong r = TiffUtils.ConvertToUShortBigEndian(span.Slice(num, 2)); ulong g = TiffUtils.ConvertToUShortBigEndian(span2.Slice(num, 2)); ulong b = TiffUtils.ConvertToUShortBigEndian(span3.Slice(num, 2)); ulong a = TiffUtils.ConvertToUShortBigEndian(span4.Slice(num, 2)); num += 2; span5[j] = (flag ? TiffUtils.ColorFromRgba64Premultiplied(rgba64Default, r, g, b, a, color) : TiffUtils.ColorFromRgba64(rgba64Default, r, g, b, a, color)); } } else { for (int k = 0; k < span5.Length; k++) { ulong r2 = TiffUtils.ConvertToUShortLittleEndian(span.Slice(num, 2)); ulong g2 = TiffUtils.ConvertToUShortLittleEndian(span2.Slice(num, 2)); ulong b2 = TiffUtils.ConvertToUShortLittleEndian(span3.Slice(num, 2)); ulong a2 = TiffUtils.ConvertToUShortLittleEndian(span4.Slice(num, 2)); num += 2; span5[k] = (flag ? TiffUtils.ColorFromRgba64Premultiplied(rgba64Default, r2, g2, b2, a2, color) : TiffUtils.ColorFromRgba64(rgba64Default, r2, g2, b2, a2, color)); } } } } } internal class Rgba24242424TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; private readonly TiffExtraSampleType? extraSamplesType; public Rgba24242424TiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian) { this.extraSamplesType = extraSamplesType; this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); bool flag = extraSamplesType.HasValue && extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int num = 0; Span span = stackalloc byte[4]; int start = (isBigEndian ? 1 : 0); Span destination = span.Slice(start); for (int i = top; i < top + height; i++) { Span span2 = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span2.Length; j++) { data.Slice(num, 3).CopyTo(destination); ulong r = TiffUtils.ConvertToUIntBigEndian(span); num += 3; data.Slice(num, 3).CopyTo(destination); ulong g = TiffUtils.ConvertToUIntBigEndian(span); num += 3; data.Slice(num, 3).CopyTo(destination); ulong b = TiffUtils.ConvertToUIntBigEndian(span); num += 3; data.Slice(num, 3).CopyTo(destination); ulong a = TiffUtils.ConvertToUIntBigEndian(span); num += 3; span2[j] = (flag ? TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : TiffUtils.ColorScaleTo24Bit(r, g, b, a, color)); } } else { for (int k = 0; k < span2.Length; k++) { data.Slice(num, 3).CopyTo(destination); ulong r2 = TiffUtils.ConvertToUIntLittleEndian(span); num += 3; data.Slice(num, 3).CopyTo(destination); ulong g2 = TiffUtils.ConvertToUIntLittleEndian(span); num += 3; data.Slice(num, 3).CopyTo(destination); ulong b2 = TiffUtils.ConvertToUIntLittleEndian(span); num += 3; data.Slice(num, 3).CopyTo(destination); ulong a2 = TiffUtils.ConvertToUIntLittleEndian(span); num += 3; span2[k] = (flag ? TiffUtils.ColorScaleTo24BitPremultiplied(r2, g2, b2, a2, color) : TiffUtils.ColorScaleTo24Bit(r2, g2, b2, a2, color)); } } } } } internal class Rgba24PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; private readonly TiffExtraSampleType? extraSamplesType; public Rgba24PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian) { this.extraSamplesType = extraSamplesType; this.isBigEndian = isBigEndian; } public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); Span span = stackalloc byte[4]; int start = (isBigEndian ? 1 : 0); Span span2 = data[0].GetSpan(); Span span3 = data[1].GetSpan(); Span span4 = data[2].GetSpan(); Span span5 = data[3].GetSpan(); Span destination = span.Slice(start); bool flag = extraSamplesType.HasValue && extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int num = 0; for (int i = top; i < top + height; i++) { Span span6 = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span6.Length; j++) { span2.Slice(num, 3).CopyTo(destination); ulong r = TiffUtils.ConvertToUIntBigEndian(span); span3.Slice(num, 3).CopyTo(destination); ulong g = TiffUtils.ConvertToUIntBigEndian(span); span4.Slice(num, 3).CopyTo(destination); ulong b = TiffUtils.ConvertToUIntBigEndian(span); span5.Slice(num, 3).CopyTo(destination); ulong a = TiffUtils.ConvertToUIntBigEndian(span); num += 3; span6[j] = (flag ? TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) : TiffUtils.ColorScaleTo24Bit(r, g, b, a, color)); } } else { for (int k = 0; k < span6.Length; k++) { span2.Slice(num, 3).CopyTo(destination); ulong r2 = TiffUtils.ConvertToUIntLittleEndian(span); span3.Slice(num, 3).CopyTo(destination); ulong g2 = TiffUtils.ConvertToUIntLittleEndian(span); span4.Slice(num, 3).CopyTo(destination); ulong b2 = TiffUtils.ConvertToUIntLittleEndian(span); span5.Slice(num, 3).CopyTo(destination); ulong a2 = TiffUtils.ConvertToUIntLittleEndian(span); num += 3; span6[k] = (flag ? TiffUtils.ColorScaleTo24BitPremultiplied(r2, g2, b2, a2, color) : TiffUtils.ColorScaleTo24Bit(r2, g2, b2, a2, color)); } } } } } internal class Rgba32323232TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; private readonly TiffExtraSampleType? extraSamplesType; public Rgba32323232TiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian) { this.extraSamplesType = extraSamplesType; this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); bool flag = extraSamplesType.HasValue && extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int num = 0; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { ulong r = TiffUtils.ConvertToUIntBigEndian(data.Slice(num, 4)); num += 4; ulong g = TiffUtils.ConvertToUIntBigEndian(data.Slice(num, 4)); num += 4; ulong b = TiffUtils.ConvertToUIntBigEndian(data.Slice(num, 4)); num += 4; ulong a = TiffUtils.ConvertToUIntBigEndian(data.Slice(num, 4)); num += 4; span[j] = (flag ? TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : TiffUtils.ColorScaleTo32Bit(r, g, b, a, color)); } } else { for (int k = 0; k < span.Length; k++) { ulong r2 = TiffUtils.ConvertToUIntLittleEndian(data.Slice(num, 4)); num += 4; ulong g2 = TiffUtils.ConvertToUIntLittleEndian(data.Slice(num, 4)); num += 4; ulong b2 = TiffUtils.ConvertToUIntLittleEndian(data.Slice(num, 4)); num += 4; ulong a2 = TiffUtils.ConvertToUIntLittleEndian(data.Slice(num, 4)); num += 4; span[k] = (flag ? TiffUtils.ColorScaleTo32BitPremultiplied(r2, g2, b2, a2, color) : TiffUtils.ColorScaleTo32Bit(r2, g2, b2, a2, color)); } } } } } internal class Rgba32PlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; private readonly TiffExtraSampleType? extraSamplesType; public Rgba32PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian) { this.extraSamplesType = extraSamplesType; this.isBigEndian = isBigEndian; } public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); Span span = data[0].GetSpan(); Span span2 = data[1].GetSpan(); Span span3 = data[2].GetSpan(); Span span4 = data[3].GetSpan(); bool flag = extraSamplesType.HasValue && extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int num = 0; for (int i = top; i < top + height; i++) { Span span5 = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span5.Length; j++) { ulong r = TiffUtils.ConvertToUIntBigEndian(span.Slice(num, 4)); ulong g = TiffUtils.ConvertToUIntBigEndian(span2.Slice(num, 4)); ulong b = TiffUtils.ConvertToUIntBigEndian(span3.Slice(num, 4)); ulong a = TiffUtils.ConvertToUIntBigEndian(span4.Slice(num, 4)); num += 4; span5[j] = (flag ? TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) : TiffUtils.ColorScaleTo32Bit(r, g, b, a, color)); } } else { for (int k = 0; k < span5.Length; k++) { ulong r2 = TiffUtils.ConvertToUIntLittleEndian(span.Slice(num, 4)); ulong g2 = TiffUtils.ConvertToUIntLittleEndian(span2.Slice(num, 4)); ulong b2 = TiffUtils.ConvertToUIntLittleEndian(span3.Slice(num, 4)); ulong a2 = TiffUtils.ConvertToUIntLittleEndian(span4.Slice(num, 4)); num += 4; span5[k] = (flag ? TiffUtils.ColorScaleTo32BitPremultiplied(r2, g2, b2, a2, color) : TiffUtils.ColorScaleTo32Bit(r2, g2, b2, a2, color)); } } } } } internal class Rgba8888TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly Configuration configuration; private readonly MemoryAllocator memoryAllocator; private readonly TiffExtraSampleType? extraSamplesType; public Rgba8888TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType) { this.configuration = configuration; this.memoryAllocator = memoryAllocator; this.extraSamplesType = extraSamplesType; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { int num = 0; bool flag = extraSamplesType.HasValue && extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; default(TPixel).FromScaledVector4(TiffUtils.Vector4Default); using IMemoryOwner buffer = (flag ? memoryAllocator.Allocate(width) : null); Span span = (flag ? buffer.GetSpan() : Span.Empty); for (int i = top; i < top + height; i++) { Span span2 = pixels.DangerousGetRowSpan(i).Slice(left, width); int num2 = span2.Length * 4; PixelOperations.Instance.FromRgba32Bytes(configuration, data.Slice(num, num2), span2, span2.Length); if (flag) { PixelOperations.Instance.ToVector4(configuration, span2, span); PixelOperations.Instance.FromVector4Destructive(configuration, span, span2, PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply); } num += num2; } } } internal class RgbaFloat32323232TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public RgbaFloat32323232TiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); val.FromScaledVector4(TiffUtils.Vector4Default); int num = 0; byte[] array = new byte[4]; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { data.Slice(num, 4).CopyTo(array); Array.Reverse(array); float x = BitConverter.ToSingle(array, 0); num += 4; data.Slice(num, 4).CopyTo(array); Array.Reverse(array); float y = BitConverter.ToSingle(array, 0); num += 4; data.Slice(num, 4).CopyTo(array); Array.Reverse(array); float z = BitConverter.ToSingle(array, 0); num += 4; data.Slice(num, 4).CopyTo(array); Array.Reverse(array); float w = BitConverter.ToSingle(array, 0); num += 4; Vector4 vector = new Vector4(x, y, z, w); val.FromScaledVector4(vector); span[j] = val; } } else { for (int k = 0; k < span.Length; k++) { data.Slice(num, 4).CopyTo(array); float x2 = BitConverter.ToSingle(array, 0); num += 4; data.Slice(num, 4).CopyTo(array); float y2 = BitConverter.ToSingle(array, 0); num += 4; data.Slice(num, 4).CopyTo(array); float z2 = BitConverter.ToSingle(array, 0); num += 4; data.Slice(num, 4).CopyTo(array); float w2 = BitConverter.ToSingle(array, 0); num += 4; Vector4 vector2 = new Vector4(x2, y2, z2, w2); val.FromScaledVector4(vector2); span[k] = val; } } } } } internal class RgbaPlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly float rFactor; private readonly float gFactor; private readonly float bFactor; private readonly float aFactor; private readonly ushort bitsPerSampleR; private readonly ushort bitsPerSampleG; private readonly ushort bitsPerSampleB; private readonly ushort bitsPerSampleA; private readonly TiffExtraSampleType? extraSampleType; public RgbaPlanarTiffColor(TiffExtraSampleType? extraSampleType, TiffBitsPerSample bitsPerSample) { bitsPerSampleR = bitsPerSample.Channel0; bitsPerSampleG = bitsPerSample.Channel1; bitsPerSampleB = bitsPerSample.Channel2; bitsPerSampleA = bitsPerSample.Channel3; rFactor = (float)(1 << (int)bitsPerSampleR) - 1f; gFactor = (float)(1 << (int)bitsPerSampleG) - 1f; bFactor = (float)(1 << (int)bitsPerSampleB) - 1f; aFactor = (float)(1 << (int)bitsPerSampleA) - 1f; this.extraSampleType = extraSampleType; } public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); bool flag = extraSampleType.HasValue && extraSampleType == TiffExtraSampleType.AssociatedAlphaData; BitReader bitReader = new BitReader(data[0].GetSpan()); BitReader bitReader2 = new BitReader(data[1].GetSpan()); BitReader bitReader3 = new BitReader(data[2].GetSpan()); BitReader bitReader4 = new BitReader(data[3].GetSpan()); for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); for (int j = 0; j < span.Length; j++) { float x = (float)bitReader.ReadBits(bitsPerSampleR) / rFactor; float y = (float)bitReader2.ReadBits(bitsPerSampleG) / gFactor; float z = (float)bitReader3.ReadBits(bitsPerSampleB) / bFactor; float w = (float)bitReader4.ReadBits(bitsPerSampleA) / aFactor; Vector4 vector = new Vector4(x, y, z, w); if (flag) { val = TiffUtils.UnPremultiply(ref vector, val); } else { val.FromScaledVector4(vector); } span[j] = val; } bitReader.NextRow(); bitReader2.NextRow(); bitReader3.NextRow(); bitReader4.NextRow(); } } } internal class RgbaTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly float rFactor; private readonly float gFactor; private readonly float bFactor; private readonly float aFactor; private readonly ushort bitsPerSampleR; private readonly ushort bitsPerSampleG; private readonly ushort bitsPerSampleB; private readonly ushort bitsPerSampleA; private readonly TiffExtraSampleType? extraSamplesType; public RgbaTiffColor(TiffExtraSampleType? extraSampleType, TiffBitsPerSample bitsPerSample) { bitsPerSampleR = bitsPerSample.Channel0; bitsPerSampleG = bitsPerSample.Channel1; bitsPerSampleB = bitsPerSample.Channel2; bitsPerSampleA = bitsPerSample.Channel3; rFactor = (float)(1 << (int)bitsPerSampleR) - 1f; gFactor = (float)(1 << (int)bitsPerSampleG) - 1f; bFactor = (float)(1 << (int)bitsPerSampleB) - 1f; aFactor = (float)(1 << (int)bitsPerSampleA) - 1f; extraSamplesType = extraSampleType; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); BitReader bitReader = new BitReader(data); bool flag = extraSamplesType.HasValue && extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); for (int j = 0; j < span.Length; j++) { float x = (float)bitReader.ReadBits(bitsPerSampleR) / rFactor; float y = (float)bitReader.ReadBits(bitsPerSampleG) / gFactor; float z = (float)bitReader.ReadBits(bitsPerSampleB) / bFactor; float w = (float)bitReader.ReadBits(bitsPerSampleB) / aFactor; Vector4 vector = new Vector4(x, y, z, w); if (flag) { span[j] = TiffUtils.UnPremultiply(ref vector, val); continue; } val.FromScaledVector4(vector); span[j] = val; } bitReader.NextRow(); } } } internal class RgbFloat323232TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public RgbFloat323232TiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); val.FromScaledVector4(TiffUtils.Vector4Default); int num = 0; byte[] array = new byte[4]; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { data.Slice(num, 4).CopyTo(array); Array.Reverse(array); float x = BitConverter.ToSingle(array, 0); num += 4; data.Slice(num, 4).CopyTo(array); Array.Reverse(array); float y = BitConverter.ToSingle(array, 0); num += 4; data.Slice(num, 4).CopyTo(array); Array.Reverse(array); float z = BitConverter.ToSingle(array, 0); num += 4; Vector4 vector = new Vector4(x, y, z, 1f); val.FromScaledVector4(vector); span[j] = val; } } else { for (int k = 0; k < span.Length; k++) { data.Slice(num, 4).CopyTo(array); float x2 = BitConverter.ToSingle(array, 0); num += 4; data.Slice(num, 4).CopyTo(array); float y2 = BitConverter.ToSingle(array, 0); num += 4; data.Slice(num, 4).CopyTo(array); float z2 = BitConverter.ToSingle(array, 0); num += 4; Vector4 vector2 = new Vector4(x2, y2, z2, 1f); val.FromScaledVector4(vector2); span[k] = val; } } } } } internal class RgbPlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly float rFactor; private readonly float gFactor; private readonly float bFactor; private readonly ushort bitsPerSampleR; private readonly ushort bitsPerSampleG; private readonly ushort bitsPerSampleB; public RgbPlanarTiffColor(TiffBitsPerSample bitsPerSample) { bitsPerSampleR = bitsPerSample.Channel0; bitsPerSampleG = bitsPerSample.Channel1; bitsPerSampleB = bitsPerSample.Channel2; rFactor = (float)(1 << (int)bitsPerSampleR) - 1f; gFactor = (float)(1 << (int)bitsPerSampleG) - 1f; bFactor = (float)(1 << (int)bitsPerSampleB) - 1f; } public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); BitReader bitReader = new BitReader(data[0].GetSpan()); BitReader bitReader2 = new BitReader(data[1].GetSpan()); BitReader bitReader3 = new BitReader(data[2].GetSpan()); for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); for (int j = 0; j < span.Length; j++) { float x = (float)bitReader.ReadBits(bitsPerSampleR) / rFactor; float y = (float)bitReader2.ReadBits(bitsPerSampleG) / gFactor; float z = (float)bitReader3.ReadBits(bitsPerSampleB) / bFactor; val.FromScaledVector4(new Vector4(x, y, z, 1f)); span[j] = val; } bitReader.NextRow(); bitReader2.NextRow(); bitReader3.NextRow(); } } } internal class RgbTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly float rFactor; private readonly float gFactor; private readonly float bFactor; private readonly ushort bitsPerSampleR; private readonly ushort bitsPerSampleG; private readonly ushort bitsPerSampleB; public RgbTiffColor(TiffBitsPerSample bitsPerSample) { bitsPerSampleR = bitsPerSample.Channel0; bitsPerSampleG = bitsPerSample.Channel1; bitsPerSampleB = bitsPerSample.Channel2; rFactor = (float)(1 << (int)bitsPerSampleR) - 1f; gFactor = (float)(1 << (int)bitsPerSampleG) - 1f; bFactor = (float)(1 << (int)bitsPerSampleB) - 1f; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); BitReader bitReader = new BitReader(data); for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); for (int j = 0; j < span.Length; j++) { float x = (float)bitReader.ReadBits(bitsPerSampleR) / rFactor; float y = (float)bitReader.ReadBits(bitsPerSampleG) / gFactor; float z = (float)bitReader.ReadBits(bitsPerSampleB) / bFactor; val.FromScaledVector4(new Vector4(x, y, z, 1f)); span[j] = val; } bitReader.NextRow(); } } } internal abstract class TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public abstract void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height); } internal abstract class TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { public abstract void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height); } internal static class TiffColorDecoderFactory where TPixel : unmanaged, IPixel { public static TiffBaseColorDecoder Create(Configuration configuration, MemoryAllocator memoryAllocator, TiffColorType colorType, TiffBitsPerSample bitsPerSample, TiffExtraSampleType? extraSampleType, ushort[] colorMap, Rational[] referenceBlackAndWhite, Rational[] ycbcrCoefficients, ushort[] ycbcrSubSampling, ByteOrder byteOrder) { return colorType switch { TiffColorType.WhiteIsZero => new WhiteIsZeroTiffColor(bitsPerSample), TiffColorType.WhiteIsZero1 => new WhiteIsZero1TiffColor(), TiffColorType.WhiteIsZero4 => new WhiteIsZero4TiffColor(), TiffColorType.WhiteIsZero8 => new WhiteIsZero8TiffColor(), TiffColorType.WhiteIsZero16 => new WhiteIsZero16TiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.WhiteIsZero24 => new WhiteIsZero24TiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.WhiteIsZero32 => new WhiteIsZero32TiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.WhiteIsZero32Float => new WhiteIsZero32FloatTiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.BlackIsZero => new BlackIsZeroTiffColor(bitsPerSample), TiffColorType.BlackIsZero1 => new BlackIsZero1TiffColor(), TiffColorType.BlackIsZero4 => new BlackIsZero4TiffColor(), TiffColorType.BlackIsZero8 => new BlackIsZero8TiffColor(configuration), TiffColorType.BlackIsZero16 => new BlackIsZero16TiffColor(configuration, byteOrder == ByteOrder.BigEndian), TiffColorType.BlackIsZero24 => new BlackIsZero24TiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.BlackIsZero32 => new BlackIsZero32TiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.BlackIsZero32Float => new BlackIsZero32FloatTiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.Rgb => new RgbTiffColor(bitsPerSample), TiffColorType.Rgb222 => new RgbTiffColor(bitsPerSample), TiffColorType.Rgba2222 => new RgbaTiffColor(extraSampleType, bitsPerSample), TiffColorType.Rgb333 => new RgbTiffColor(bitsPerSample), TiffColorType.Rgba3333 => new RgbaTiffColor(extraSampleType, bitsPerSample), TiffColorType.Rgb444 => new Rgb444TiffColor(), TiffColorType.Rgba4444 => new RgbaTiffColor(extraSampleType, bitsPerSample), TiffColorType.Rgb555 => new RgbTiffColor(bitsPerSample), TiffColorType.Rgba5555 => new RgbaTiffColor(extraSampleType, bitsPerSample), TiffColorType.Rgb666 => new RgbTiffColor(bitsPerSample), TiffColorType.Rgba6666 => new RgbaTiffColor(extraSampleType, bitsPerSample), TiffColorType.Rgb888 => new Rgb888TiffColor(configuration), TiffColorType.Rgba8888 => new Rgba8888TiffColor(configuration, memoryAllocator, extraSampleType), TiffColorType.Rgb101010 => new RgbTiffColor(bitsPerSample), TiffColorType.Rgba10101010 => new RgbaTiffColor(extraSampleType, bitsPerSample), TiffColorType.Rgb121212 => new RgbTiffColor(bitsPerSample), TiffColorType.Rgba12121212 => new RgbaTiffColor(extraSampleType, bitsPerSample), TiffColorType.Rgb141414 => new RgbTiffColor(bitsPerSample), TiffColorType.Rgba14141414 => new RgbaTiffColor(extraSampleType, bitsPerSample), TiffColorType.Rgb161616 => new Rgb161616TiffColor(configuration, byteOrder == ByteOrder.BigEndian), TiffColorType.Rgba16161616 => new Rgba16161616TiffColor(configuration, memoryAllocator, extraSampleType, byteOrder == ByteOrder.BigEndian), TiffColorType.Rgb242424 => new Rgb242424TiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.Rgba24242424 => new Rgba24242424TiffColor(extraSampleType, byteOrder == ByteOrder.BigEndian), TiffColorType.Rgb323232 => new Rgb323232TiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.Rgba32323232 => new Rgba32323232TiffColor(extraSampleType, byteOrder == ByteOrder.BigEndian), TiffColorType.RgbFloat323232 => new RgbFloat323232TiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.RgbaFloat32323232 => new RgbaFloat32323232TiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.PaletteColor => new PaletteTiffColor(bitsPerSample, colorMap), TiffColorType.YCbCr => new YCbCrTiffColor(memoryAllocator, referenceBlackAndWhite, ycbcrCoefficients, ycbcrSubSampling), _ => throw TiffThrowHelper.InvalidColorType(colorType.ToString()), }; } public static TiffBasePlanarColorDecoder CreatePlanar(TiffColorType colorType, TiffBitsPerSample bitsPerSample, TiffExtraSampleType? extraSampleType, ushort[] colorMap, Rational[] referenceBlackAndWhite, Rational[] ycbcrCoefficients, ushort[] ycbcrSubSampling, ByteOrder byteOrder) { return colorType switch { TiffColorType.Rgb888Planar => new RgbPlanarTiffColor(bitsPerSample), TiffColorType.Rgba8888Planar => new RgbaPlanarTiffColor(extraSampleType, bitsPerSample), TiffColorType.YCbCrPlanar => new YCbCrPlanarTiffColor(referenceBlackAndWhite, ycbcrCoefficients, ycbcrSubSampling), TiffColorType.Rgb161616Planar => new Rgb16PlanarTiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.Rgba16161616Planar => new Rgba16PlanarTiffColor(extraSampleType, byteOrder == ByteOrder.BigEndian), TiffColorType.Rgb242424Planar => new Rgb24PlanarTiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.Rgba24242424Planar => new Rgba24PlanarTiffColor(extraSampleType, byteOrder == ByteOrder.BigEndian), TiffColorType.Rgb323232Planar => new Rgb32PlanarTiffColor(byteOrder == ByteOrder.BigEndian), TiffColorType.Rgba32323232Planar => new Rgba32PlanarTiffColor(extraSampleType, byteOrder == ByteOrder.BigEndian), _ => throw TiffThrowHelper.InvalidColorType(colorType.ToString()), }; } } internal enum TiffColorType { BlackIsZero, BlackIsZero1, BlackIsZero4, BlackIsZero8, BlackIsZero16, BlackIsZero24, BlackIsZero32, BlackIsZero32Float, WhiteIsZero, WhiteIsZero1, WhiteIsZero4, WhiteIsZero8, WhiteIsZero16, WhiteIsZero24, WhiteIsZero32, WhiteIsZero32Float, PaletteColor, Rgb, Rgb222, Rgba2222, Rgb333, Rgba3333, Rgb444, Rgba4444, Rgb555, Rgba5555, Rgb666, Rgba6666, Rgb888, Rgba8888, Rgb101010, Rgba10101010, Rgb121212, Rgba12121212, Rgb141414, Rgba14141414, Rgb161616, Rgba16161616, Rgb242424, Rgba24242424, Rgb323232, Rgba32323232, RgbFloat323232, RgbaFloat32323232, Rgb888Planar, Rgba8888Planar, Rgb161616Planar, Rgba16161616Planar, Rgb242424Planar, Rgba24242424Planar, Rgb323232Planar, Rgba32323232Planar, YCbCr, YCbCrPlanar } internal class WhiteIsZero16TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public WhiteIsZero16TiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { L16 l16Default = TiffUtils.L16Default; TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); int num = 0; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { ushort intensity = (ushort)(65535 - TiffUtils.ConvertToUShortBigEndian(data.Slice(num, 2))); num += 2; span[j] = TiffUtils.ColorFromL16(l16Default, intensity, color); } } else { for (int k = 0; k < span.Length; k++) { ushort intensity2 = (ushort)(65535 - TiffUtils.ConvertToUShortLittleEndian(data.Slice(num, 2))); num += 2; span[k] = TiffUtils.ColorFromL16(l16Default, intensity2, color); } } } } } internal class WhiteIsZero1TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); int num = 0; Color black = Color.Black; Color white = Color.White; for (int i = top; i < top + height; i++) { for (int j = left; j < left + width; j += 8) { byte b = data[num++]; int num2 = Math.Min(left + width - j, 8); for (int k = 0; k < num2; k++) { int num3 = (b >> 7 - k) & 1; val.FromRgba32((num3 == 0) ? white : black); pixels[j + k, i] = val; } } } } } internal class WhiteIsZero24TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public WhiteIsZero24TiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); byte[] array = new byte[4]; int start = (isBigEndian ? 1 : 0); Span destination = array.AsSpan(start); int num = 0; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { data.Slice(num, 3).CopyTo(destination); ulong intensity = 16777215 - TiffUtils.ConvertToUIntBigEndian(array); num += 3; span[j] = TiffUtils.ColorScaleTo24Bit(intensity, color); } } else { for (int k = 0; k < span.Length; k++) { data.Slice(num, 3).CopyTo(destination); ulong intensity2 = 16777215 - TiffUtils.ConvertToUIntLittleEndian(array); num += 3; span[k] = TiffUtils.ColorScaleTo24Bit(intensity2, color); } } } } } internal class WhiteIsZero32FloatTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public WhiteIsZero32FloatTiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); val.FromScaledVector4(TiffUtils.Vector4Default); byte[] array = new byte[4]; int num = 0; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { data.Slice(num, 4).CopyTo(array); Array.Reverse(array); float num2 = 1f - BitConverter.ToSingle(array, 0); num += 4; Vector4 vector = new Vector4(num2, num2, num2, 1f); val.FromScaledVector4(vector); span[j] = val; } } else { for (int k = 0; k < span.Length; k++) { data.Slice(num, 4).CopyTo(array); float num3 = 1f - BitConverter.ToSingle(array, 0); num += 4; Vector4 vector2 = new Vector4(num3, num3, num3, 1f); val.FromScaledVector4(vector2); span[k] = val; } } } } } internal class WhiteIsZero32TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly bool isBigEndian; public WhiteIsZero32TiffColor(bool isBigEndian) { this.isBigEndian = isBigEndian; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); color.FromScaledVector4(TiffUtils.Vector4Default); int num = 0; for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); if (isBigEndian) { for (int j = 0; j < span.Length; j++) { ulong intensity = uint.MaxValue - TiffUtils.ConvertToUIntBigEndian(data.Slice(num, 4)); num += 4; span[j] = TiffUtils.ColorScaleTo32Bit(intensity, color); } } else { for (int k = 0; k < span.Length; k++) { ulong intensity2 = uint.MaxValue - TiffUtils.ConvertToUIntLittleEndian(data.Slice(num, 4)); num += 4; span[k] = TiffUtils.ColorScaleTo32Bit(intensity2, color); } } } } } internal class WhiteIsZero4TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); int num = 0; bool flag = (width & 1) == 1; L8 source = default(L8); for (int i = top; i < top + height; i++) { int num2 = left; while (num2 < left + width - 1) { byte b = data[num++]; byte packedValue = (byte)((15 - ((b & 0xF0) >> 4)) * 17); source.PackedValue = packedValue; val.FromL8(source); pixels[num2++, i] = val; byte packedValue2 = (byte)((15 - (b & 0xF)) * 17); source.PackedValue = packedValue2; val.FromL8(source); pixels[num2++, i] = val; } if (flag) { byte b2 = data[num++]; byte packedValue3 = (byte)((15 - ((b2 & 0xF0) >> 4)) * 17); source.PackedValue = packedValue3; val.FromL8(source); pixels[left + width - 1, i] = val; } } } } internal class WhiteIsZero8TiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel color = default(TPixel); int num = 0; L8 l = default(L8); for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); for (int j = 0; j < span.Length; j++) { byte intensity = (byte)(255 - data[num++]); span[j] = TiffUtils.ColorFromL8(l, intensity, color); } } } } internal class WhiteIsZeroTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly ushort bitsPerSample0; private readonly float factor; public WhiteIsZeroTiffColor(TiffBitsPerSample bitsPerSample) { bitsPerSample0 = bitsPerSample.Channel0; factor = (float)Math.Pow(2.0, (int)bitsPerSample0) - 1f; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { TPixel val = default(TPixel); BitReader bitReader = new BitReader(data); for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); for (int j = 0; j < span.Length; j++) { int num = bitReader.ReadBits(bitsPerSample0); float num2 = 1f - (float)num / factor; val.FromScaledVector4(new Vector4(num2, num2, num2, 1f)); span[j] = val; } bitReader.NextRow(); } } } internal class YCbCrConverter { private readonly struct CodingRangeExpander { private readonly float f1; private readonly float f2; public CodingRangeExpander(Rational referenceBlack, Rational referenceWhite, int codingRange) { float num = referenceBlack.ToSingle(); float num2 = referenceWhite.ToSingle(); f1 = (float)codingRange / (num2 - num); f2 = f1 * num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public float Expand(float code) { return code * f1 - f2; } } private readonly struct YCbCrToRgbConverter { private readonly float cr2R; private readonly float cb2B; private readonly float y2G; private readonly float cr2G; private readonly float cb2G; public YCbCrToRgbConverter(Rational lumaRed, Rational lumaGreen, Rational lumaBlue) { cr2R = 2f - 2f * lumaRed.ToSingle(); cb2B = 2f - 2f * lumaBlue.ToSingle(); y2G = (1f - lumaBlue.ToSingle() - lumaRed.ToSingle()) / lumaGreen.ToSingle(); cr2G = 2f * lumaRed.ToSingle() * (lumaRed.ToSingle() - 1f) / lumaGreen.ToSingle(); cb2G = 2f * lumaBlue.ToSingle() * (lumaBlue.ToSingle() - 1f) / lumaGreen.ToSingle(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32 Convert(float y, float cb, float cr) { return new Rgba32 { R = RoundAndClampTo8Bit(cr * cr2R + y), G = RoundAndClampTo8Bit(y2G * y + cr2G * cr + cb2G * cb), B = RoundAndClampTo8Bit(cb * cb2B + y), A = byte.MaxValue }; } } private readonly CodingRangeExpander yExpander; private readonly CodingRangeExpander cbExpander; private readonly CodingRangeExpander crExpander; private readonly YCbCrToRgbConverter converter; private static readonly Rational[] DefaultLuma = new Rational[3] { new Rational(299u, 1000u), new Rational(587u, 1000u), new Rational(114u, 1000u) }; private static readonly Rational[] DefaultReferenceBlackWhite = new Rational[6] { new Rational(0u, 1u), new Rational(255u, 1u), new Rational(128u, 1u), new Rational(255u, 1u), new Rational(128u, 1u), new Rational(255u, 1u) }; public YCbCrConverter(Rational[] referenceBlackAndWhite, Rational[] coefficients) { if (referenceBlackAndWhite == null) { referenceBlackAndWhite = DefaultReferenceBlackWhite; } if (coefficients == null) { coefficients = DefaultLuma; } if (referenceBlackAndWhite.Length != 6) { TiffThrowHelper.ThrowImageFormatException("reference black and white array should have 6 entry's"); } if (coefficients.Length != 3) { TiffThrowHelper.ThrowImageFormatException("luma coefficients array should have 6 entry's"); } yExpander = new CodingRangeExpander(referenceBlackAndWhite[0], referenceBlackAndWhite[1], 255); cbExpander = new CodingRangeExpander(referenceBlackAndWhite[2], referenceBlackAndWhite[3], 127); crExpander = new CodingRangeExpander(referenceBlackAndWhite[4], referenceBlackAndWhite[5], 127); converter = new YCbCrToRgbConverter(coefficients[0], coefficients[1], coefficients[2]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgba32 ConvertToRgba32(byte y, byte cb, byte cr) { float y2 = yExpander.Expand((int)y); float cb2 = cbExpander.Expand((int)cb); float cr2 = crExpander.Expand((int)cr); return converter.Convert(y2, cb2, cr2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte RoundAndClampTo8Bit(float value) { return (byte)Numerics.Clamp((int)MathF.Round(value), 0, 255); } } internal class YCbCrPlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { private readonly YCbCrConverter converter; private readonly ushort[] ycbcrSubSampling; public YCbCrPlanarTiffColor(Rational[] referenceBlackAndWhite, Rational[] coefficients, ushort[] ycbcrSubSampling) { converter = new YCbCrConverter(referenceBlackAndWhite, coefficients); this.ycbcrSubSampling = ycbcrSubSampling; } public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { Span span = data[0].GetSpan(); Span span2 = data[1].GetSpan(); Span span3 = data[2].GetSpan(); if (ycbcrSubSampling != null && (ycbcrSubSampling[0] != 1 || ycbcrSubSampling[1] != 1)) { ReverseChromaSubSampling(width, height, ycbcrSubSampling[0], ycbcrSubSampling[1], span2, span3); } TPixel val = default(TPixel); int num = 0; int num2 = 0; if (ycbcrSubSampling != null) { num2 = TiffUtils.PaddingToNextInteger(width, ycbcrSubSampling[0]); } for (int i = top; i < top + height; i++) { Span span4 = pixels.DangerousGetRowSpan(i).Slice(left, width); for (int j = 0; j < span4.Length; j++) { Rgba32 source = converter.ConvertToRgba32(span[num], span2[num], span3[num]); val.FromRgba32(source); span4[j] = val; num++; } num += num2; } } private static void ReverseChromaSubSampling(int width, int height, int horizontalSubSampling, int verticalSubSampling, Span planarCb, Span planarCr) { width += TiffUtils.PaddingToNextInteger(width, horizontalSubSampling); height += TiffUtils.PaddingToNextInteger(height, verticalSubSampling); for (int num = height - 1; num >= 0; num--) { for (int num2 = width - 1; num2 >= 0; num2--) { int index = num * width + num2; int index2 = num / verticalSubSampling * (width / horizontalSubSampling) + num2 / horizontalSubSampling; planarCb[index] = planarCb[index2]; planarCr[index] = planarCr[index2]; } } } } internal class YCbCrTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { private readonly MemoryAllocator memoryAllocator; private readonly YCbCrConverter converter; private readonly ushort[] ycbcrSubSampling; public YCbCrTiffColor(MemoryAllocator memoryAllocator, Rational[] referenceBlackAndWhite, Rational[] coefficients, ushort[] ycbcrSubSampling) { this.memoryAllocator = memoryAllocator; converter = new YCbCrConverter(referenceBlackAndWhite, coefficients); this.ycbcrSubSampling = ycbcrSubSampling; } public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { ReadOnlySpan ycbcrData = data; if (ycbcrSubSampling != null && (ycbcrSubSampling[0] != 1 || ycbcrSubSampling[1] != 1)) { int num = width + 4; int num2 = height + 4; int length = num * num2 * 3; using IMemoryOwner buffer = memoryAllocator.Allocate(length); Span span = buffer.GetSpan(); ReverseChromaSubSampling(width, height, ycbcrSubSampling[0], ycbcrSubSampling[1], data, span); ycbcrData = span; DecodeYCbCrData(pixels, left, top, width, height, ycbcrData); return; } DecodeYCbCrData(pixels, left, top, width, height, ycbcrData); } private void DecodeYCbCrData(Buffer2D pixels, int left, int top, int width, int height, ReadOnlySpan ycbcrData) { TPixel val = default(TPixel); int num = 0; int num2 = 0; if (ycbcrSubSampling != null) { num2 = TiffUtils.PaddingToNextInteger(width, ycbcrSubSampling[0]); } for (int i = top; i < top + height; i++) { Span span = pixels.DangerousGetRowSpan(i).Slice(left, width); for (int j = 0; j < span.Length; j++) { Rgba32 source = converter.ConvertToRgba32(ycbcrData[num], ycbcrData[num + 1], ycbcrData[num + 2]); val.FromRgba32(source); span[j] = val; num += 3; } num += num2 * 3; } } private static void ReverseChromaSubSampling(int width, int height, int horizontalSubSampling, int verticalSubSampling, ReadOnlySpan source, Span destination) { width += TiffUtils.PaddingToNextInteger(width, horizontalSubSampling); height += TiffUtils.PaddingToNextInteger(height, verticalSubSampling); int num = width / horizontalSubSampling; int num2 = height / verticalSubSampling; int num3 = horizontalSubSampling * verticalSubSampling; int num4 = num3 + 2; for (int num5 = num2 - 1; num5 >= 0; num5--) { for (int num6 = num - 1; num6 >= 0; num6--) { int num7 = num5 * num + num6; ReadOnlySpan readOnlySpan = source.Slice(num7 * num4, num4); byte b = readOnlySpan[num3 + 1]; byte b2 = readOnlySpan[num3]; for (int num8 = verticalSubSampling - 1; num8 >= 0; num8--) { for (int num9 = horizontalSubSampling - 1; num9 >= 0; num9--) { int num10 = 3 * ((num5 * verticalSubSampling + num8) * width + num6 * horizontalSubSampling + num9); destination[num10 + 2] = b; destination[num10 + 1] = b2; destination[num10] = readOnlySpan[num8 * horizontalSubSampling + num9]; } } } } } } } namespace SixLabors.ImageSharp.Formats.Tiff.Constants { public enum TiffCompression : ushort { Invalid = 0, None = 1, Ccitt1D = 2, CcittGroup3Fax = 3, CcittGroup4Fax = 4, Lzw = 5, OldJpeg = 6, Jpeg = 7, Deflate = 8, ItuTRecT82 = 9, ItuTRecT43 = 10, NeXT = 32766, PackBits = 32773, ThunderScan = 32809, OldDeflate = 32946 } internal static class TiffConstants { public const byte ByteOrderLittleEndian = 73; public const byte ByteOrderBigEndian = 77; public const ushort ByteOrderLittleEndianShort = 18761; public const ushort ByteOrderBigEndianShort = 19789; public const ushort HeaderMagicNumber = 42; public const ushort BigTiffHeaderMagicNumber = 43; public const ushort BigTiffBytesize = 8; public const int RowsPerStripInfinity = int.MaxValue; public const int SizeOfRational = 8; public const int DefaultStripSize = 8192; public static readonly TiffBitsPerSample BitsPerSample1Bit = new TiffBitsPerSample(1, 0, 0, 0); public static readonly TiffBitsPerSample BitsPerSample4Bit = new TiffBitsPerSample(4, 0, 0, 0); public static readonly TiffBitsPerSample BitsPerSample8Bit = new TiffBitsPerSample(8, 0, 0, 0); public static readonly TiffBitsPerSample BitsPerSampleRgb8Bit = new TiffBitsPerSample(8, 8, 8, 0); public static readonly IEnumerable MimeTypes = new string[2] { "image/tiff", "image/tiff-fx" }; public static readonly IEnumerable FileExtensions = new string[2] { "tiff", "tif" }; } internal enum TiffExtraSamples { Unspecified, AssociatedAlpha, UnassociatedAlpha } internal enum TiffFillOrder : ushort { MostSignificantBitFirst = 1, LeastSignificantBitFirst } [Flags] public enum TiffNewSubfileType : uint { FullImage = 0u, Preview = 1u, SinglePage = 2u, TransparencyMask = 4u, AlternativePreview = 0x10000u, MixedRasterContent = 8u } internal enum TiffOrientation { TopLeft = 1, TopRight, BottomRight, BottomLeft, LeftTop, RightTop, RightBottom, LeftBottom } public enum TiffPhotometricInterpretation : ushort { WhiteIsZero = 0, BlackIsZero = 1, Rgb = 2, PaletteColor = 3, TransparencyMask = 4, Separated = 5, YCbCr = 6, CieLab = 8, IccLab = 9, ItuLab = 10, ColorFilterArray = 32803, LinearRaw = 34892 } public enum TiffPlanarConfiguration : ushort { Chunky = 1, Planar } public enum TiffPredictor : ushort { None = 1, Horizontal, FloatingPoint } public enum TiffSampleFormat : ushort { UnsignedInteger = 1, SignedInteger, Float, Undefined, ComplexInt, ComplexFloat } public enum TiffSubfileType : ushort { FullImage = 1, Preview, SinglePage } internal enum TiffThresholding { None = 1, Ordered, Random } } namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal static class BitWriterUtils { public static void WriteBits(Span buffer, int pos, uint count, byte value) { int num = pos % 8; int num2 = pos / 8; int num3 = num2 + num; int num4 = (int)(num3 + count); if (value == 1) { for (int i = num3; i < num4; i++) { WriteBit(buffer, num2, num); num++; if (num >= 8) { num = 0; num2++; } } return; } for (int j = num3; j < num4; j++) { WriteZeroBit(buffer, num2, num); num++; if (num >= 8) { num = 0; num2++; } } } public static void WriteBit(Span buffer, int bufferPos, int bitPos) { buffer[bufferPos] |= (byte)(1 << 7 - bitPos); } public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) { buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << 7 - bitPos)); } } [Flags] public enum FaxCompressionOptions : uint { None = 0u, TwoDimensionalCoding = 1u, UncompressedMode = 2u, EolPadding = 4u } internal static class HorizontalPredictor { public static void Undo(Span pixelBytes, int width, TiffColorType colorType, bool isBigEndian) { switch (colorType) { case TiffColorType.BlackIsZero8: case TiffColorType.WhiteIsZero8: case TiffColorType.PaletteColor: UndoGray8Bit(pixelBytes, width); break; case TiffColorType.BlackIsZero16: case TiffColorType.WhiteIsZero16: UndoGray16Bit(pixelBytes, width, isBigEndian); break; case TiffColorType.BlackIsZero32: case TiffColorType.WhiteIsZero32: UndoGray32Bit(pixelBytes, width, isBigEndian); break; case TiffColorType.Rgb888: UndoRgb24Bit(pixelBytes, width); break; case TiffColorType.Rgba8888: UndoRgba32Bit(pixelBytes, width); break; case TiffColorType.Rgb161616: UndoRgb48Bit(pixelBytes, width, isBigEndian); break; case TiffColorType.Rgba16161616: UndoRgba64Bit(pixelBytes, width, isBigEndian); break; case TiffColorType.Rgb323232: UndoRgb96Bit(pixelBytes, width, isBigEndian); break; case TiffColorType.Rgba32323232: UndoRgba128Bit(pixelBytes, width, isBigEndian); break; } } public static void ApplyHorizontalPrediction(Span rows, int width, int bitsPerPixel) { switch (bitsPerPixel) { case 8: ApplyHorizontalPrediction8Bit(rows, width); break; case 24: ApplyHorizontalPrediction24Bit(rows, width); break; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ApplyHorizontalPrediction24Bit(Span rows, int width) { int num = rows.Length / width; for (int i = 0; i < num; i++) { Span span = MemoryMarshal.Cast(rows.Slice(i * width, width)); for (int num2 = span.Length - 1; num2 >= 1; num2--) { byte r = (byte)(span[num2].R - span[num2 - 1].R); byte g = (byte)(span[num2].G - span[num2 - 1].G); byte b = (byte)(span[num2].B - span[num2 - 1].B); Rgb24 source = new Rgb24(r, g, b); span[num2].FromRgb24(source); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ApplyHorizontalPrediction8Bit(Span rows, int width) { int num = rows.Length / width; for (int i = 0; i < num; i++) { Span span = rows.Slice(i * width, width); for (int num2 = span.Length - 1; num2 >= 1; num2--) { span[num2] -= span[num2 - 1]; } } } private static void UndoGray8Bit(Span pixelBytes, int width) { int num = pixelBytes.Length / width; for (int i = 0; i < num; i++) { Span span = pixelBytes.Slice(i * width, width); byte b = span[0]; for (int j = 1; j < width; j++) { b += span[j]; span[j] = b; } } } private static void UndoGray16Bit(Span pixelBytes, int width, bool isBigEndian) { int num = width * 2; int num2 = pixelBytes.Length / num; if (isBigEndian) { for (int i = 0; i < num2; i++) { int num3 = 0; Span span = pixelBytes.Slice(i * num, num); ushort num4 = TiffUtils.ConvertToUShortBigEndian(span.Slice(num3, 2)); num3 += 2; for (int j = 1; j < width; j++) { Span span2 = span.Slice(num3, 2); ushort num5 = TiffUtils.ConvertToUShortBigEndian(span2); num4 += num5; BinaryPrimitives.WriteUInt16BigEndian(span2, num4); num3 += 2; } } return; } for (int k = 0; k < num2; k++) { int num6 = 0; Span span3 = pixelBytes.Slice(k * num, num); ushort num7 = TiffUtils.ConvertToUShortLittleEndian(span3.Slice(num6, 2)); num6 += 2; for (int l = 1; l < width; l++) { Span span4 = span3.Slice(num6, 2); ushort num8 = TiffUtils.ConvertToUShortLittleEndian(span4); num7 += num8; BinaryPrimitives.WriteUInt16LittleEndian(span4, num7); num6 += 2; } } } private static void UndoGray32Bit(Span pixelBytes, int width, bool isBigEndian) { int num = width * 4; int num2 = pixelBytes.Length / num; if (isBigEndian) { for (int i = 0; i < num2; i++) { int num3 = 0; Span span = pixelBytes.Slice(i * num, num); uint num4 = TiffUtils.ConvertToUIntBigEndian(span.Slice(num3, 4)); num3 += 4; for (int j = 1; j < width; j++) { Span span2 = span.Slice(num3, 4); uint num5 = TiffUtils.ConvertToUIntBigEndian(span2); num4 += num5; BinaryPrimitives.WriteUInt32BigEndian(span2, num4); num3 += 4; } } return; } for (int k = 0; k < num2; k++) { int num6 = 0; Span span3 = pixelBytes.Slice(k * num, num); uint num7 = TiffUtils.ConvertToUIntLittleEndian(span3.Slice(num6, 4)); num6 += 4; for (int l = 1; l < width; l++) { Span span4 = span3.Slice(num6, 4); uint num8 = TiffUtils.ConvertToUIntLittleEndian(span4); num7 += num8; BinaryPrimitives.WriteUInt32LittleEndian(span4, num7); num6 += 4; } } } private static void UndoRgb24Bit(Span pixelBytes, int width) { int num = width * 3; int num2 = pixelBytes.Length / num; for (int i = 0; i < num2; i++) { Span span = MemoryMarshal.Cast(pixelBytes.Slice(i * num, num)).Slice(0, width); ref Rgb24 reference = ref MemoryMarshal.GetReference(span); byte b = reference.R; byte b2 = reference.G; byte b3 = reference.B; for (int j = 1; j < span.Length; j++) { ref Rgb24 reference2 = ref span[j]; b += reference2.R; b2 += reference2.G; b3 += reference2.B; Rgb24 source = new Rgb24(b, b2, b3); reference2.FromRgb24(source); } } } private static void UndoRgba32Bit(Span pixelBytes, int width) { int num = width * 4; int num2 = pixelBytes.Length / num; for (int i = 0; i < num2; i++) { Span span = MemoryMarshal.Cast(pixelBytes.Slice(i * num, num)).Slice(0, width); ref Rgba32 reference = ref MemoryMarshal.GetReference(span); byte b = reference.R; byte b2 = reference.G; byte b3 = reference.B; byte b4 = reference.A; for (int j = 1; j < span.Length; j++) { ref Rgba32 reference2 = ref span[j]; b += reference2.R; b2 += reference2.G; b3 += reference2.B; b4 += reference2.A; Rgba32 source = new Rgba32(b, b2, b3, b4); reference2.FromRgba32(source); } } } private static void UndoRgb48Bit(Span pixelBytes, int width, bool isBigEndian) { int num = width * 6; int num2 = pixelBytes.Length / num; if (isBigEndian) { for (int i = 0; i < num2; i++) { int num3 = 0; Span span = pixelBytes.Slice(i * num, num); ushort num4 = TiffUtils.ConvertToUShortBigEndian(span.Slice(num3, 2)); num3 += 2; ushort num5 = TiffUtils.ConvertToUShortBigEndian(span.Slice(num3, 2)); num3 += 2; ushort num6 = TiffUtils.ConvertToUShortBigEndian(span.Slice(num3, 2)); num3 += 2; for (int j = 1; j < width; j++) { Span span2 = span.Slice(num3, 2); ushort num7 = TiffUtils.ConvertToUShortBigEndian(span2); num4 += num7; BinaryPrimitives.WriteUInt16BigEndian(span2, num4); num3 += 2; Span span3 = span.Slice(num3, 2); ushort num8 = TiffUtils.ConvertToUShortBigEndian(span3); num5 += num8; BinaryPrimitives.WriteUInt16BigEndian(span3, num5); num3 += 2; Span span4 = span.Slice(num3, 2); ushort num9 = TiffUtils.ConvertToUShortBigEndian(span4); num6 += num9; BinaryPrimitives.WriteUInt16BigEndian(span4, num6); num3 += 2; } } return; } for (int k = 0; k < num2; k++) { int num10 = 0; Span span5 = pixelBytes.Slice(k * num, num); ushort num11 = TiffUtils.ConvertToUShortLittleEndian(span5.Slice(num10, 2)); num10 += 2; ushort num12 = TiffUtils.ConvertToUShortLittleEndian(span5.Slice(num10, 2)); num10 += 2; ushort num13 = TiffUtils.ConvertToUShortLittleEndian(span5.Slice(num10, 2)); num10 += 2; for (int l = 1; l < width; l++) { Span span6 = span5.Slice(num10, 2); ushort num14 = TiffUtils.ConvertToUShortLittleEndian(span6); num11 += num14; BinaryPrimitives.WriteUInt16LittleEndian(span6, num11); num10 += 2; Span span7 = span5.Slice(num10, 2); ushort num15 = TiffUtils.ConvertToUShortLittleEndian(span7); num12 += num15; BinaryPrimitives.WriteUInt16LittleEndian(span7, num12); num10 += 2; Span span8 = span5.Slice(num10, 2); ushort num16 = TiffUtils.ConvertToUShortLittleEndian(span8); num13 += num16; BinaryPrimitives.WriteUInt16LittleEndian(span8, num13); num10 += 2; } } } private static void UndoRgba64Bit(Span pixelBytes, int width, bool isBigEndian) { int num = width * 8; int num2 = pixelBytes.Length / num; if (isBigEndian) { for (int i = 0; i < num2; i++) { int num3 = 0; Span span = pixelBytes.Slice(i * num, num); ushort num4 = TiffUtils.ConvertToUShortBigEndian(span.Slice(num3, 2)); num3 += 2; ushort num5 = TiffUtils.ConvertToUShortBigEndian(span.Slice(num3, 2)); num3 += 2; ushort num6 = TiffUtils.ConvertToUShortBigEndian(span.Slice(num3, 2)); num3 += 2; ushort num7 = TiffUtils.ConvertToUShortBigEndian(span.Slice(num3, 2)); num3 += 2; for (int j = 1; j < width; j++) { Span span2 = span.Slice(num3, 2); ushort num8 = TiffUtils.ConvertToUShortBigEndian(span2); num4 += num8; BinaryPrimitives.WriteUInt16BigEndian(span2, num4); num3 += 2; Span span3 = span.Slice(num3, 2); ushort num9 = TiffUtils.ConvertToUShortBigEndian(span3); num5 += num9; BinaryPrimitives.WriteUInt16BigEndian(span3, num5); num3 += 2; Span span4 = span.Slice(num3, 2); ushort num10 = TiffUtils.ConvertToUShortBigEndian(span4); num6 += num10; BinaryPrimitives.WriteUInt16BigEndian(span4, num6); num3 += 2; Span span5 = span.Slice(num3, 2); ushort num11 = TiffUtils.ConvertToUShortBigEndian(span5); num7 += num11; BinaryPrimitives.WriteUInt16BigEndian(span5, num7); num3 += 2; } } return; } for (int k = 0; k < num2; k++) { int num12 = 0; Span span6 = pixelBytes.Slice(k * num, num); ushort num13 = TiffUtils.ConvertToUShortLittleEndian(span6.Slice(num12, 2)); num12 += 2; ushort num14 = TiffUtils.ConvertToUShortLittleEndian(span6.Slice(num12, 2)); num12 += 2; ushort num15 = TiffUtils.ConvertToUShortLittleEndian(span6.Slice(num12, 2)); num12 += 2; ushort num16 = TiffUtils.ConvertToUShortLittleEndian(span6.Slice(num12, 2)); num12 += 2; for (int l = 1; l < width; l++) { Span span7 = span6.Slice(num12, 2); ushort num17 = TiffUtils.ConvertToUShortLittleEndian(span7); num13 += num17; BinaryPrimitives.WriteUInt16LittleEndian(span7, num13); num12 += 2; Span span8 = span6.Slice(num12, 2); ushort num18 = TiffUtils.ConvertToUShortLittleEndian(span8); num14 += num18; BinaryPrimitives.WriteUInt16LittleEndian(span8, num14); num12 += 2; Span span9 = span6.Slice(num12, 2); ushort num19 = TiffUtils.ConvertToUShortLittleEndian(span9); num15 += num19; BinaryPrimitives.WriteUInt16LittleEndian(span9, num15); num12 += 2; Span span10 = span6.Slice(num12, 2); ushort num20 = TiffUtils.ConvertToUShortLittleEndian(span10); num16 += num20; BinaryPrimitives.WriteUInt16LittleEndian(span10, num16); num12 += 2; } } } private static void UndoRgb96Bit(Span pixelBytes, int width, bool isBigEndian) { int num = width * 12; int num2 = pixelBytes.Length / num; if (isBigEndian) { for (int i = 0; i < num2; i++) { int num3 = 0; Span span = pixelBytes.Slice(i * num, num); uint num4 = TiffUtils.ConvertToUIntBigEndian(span.Slice(num3, 4)); num3 += 4; uint num5 = TiffUtils.ConvertToUIntBigEndian(span.Slice(num3, 4)); num3 += 4; uint num6 = TiffUtils.ConvertToUIntBigEndian(span.Slice(num3, 4)); num3 += 4; for (int j = 1; j < width; j++) { Span span2 = span.Slice(num3, 4); uint num7 = TiffUtils.ConvertToUIntBigEndian(span2); num4 += num7; BinaryPrimitives.WriteUInt32BigEndian(span2, num4); num3 += 4; Span span3 = span.Slice(num3, 4); uint num8 = TiffUtils.ConvertToUIntBigEndian(span3); num5 += num8; BinaryPrimitives.WriteUInt32BigEndian(span3, num5); num3 += 4; Span span4 = span.Slice(num3, 4); uint num9 = TiffUtils.ConvertToUIntBigEndian(span4); num6 += num9; BinaryPrimitives.WriteUInt32BigEndian(span4, num6); num3 += 4; } } return; } for (int k = 0; k < num2; k++) { int num10 = 0; Span span5 = pixelBytes.Slice(k * num, num); uint num11 = TiffUtils.ConvertToUIntLittleEndian(span5.Slice(num10, 4)); num10 += 4; uint num12 = TiffUtils.ConvertToUIntLittleEndian(span5.Slice(num10, 4)); num10 += 4; uint num13 = TiffUtils.ConvertToUIntLittleEndian(span5.Slice(num10, 4)); num10 += 4; for (int l = 1; l < width; l++) { Span span6 = span5.Slice(num10, 4); uint num14 = TiffUtils.ConvertToUIntLittleEndian(span6); num11 += num14; BinaryPrimitives.WriteUInt32LittleEndian(span6, num11); num10 += 4; Span span7 = span5.Slice(num10, 4); uint num15 = TiffUtils.ConvertToUIntLittleEndian(span7); num12 += num15; BinaryPrimitives.WriteUInt32LittleEndian(span7, num12); num10 += 4; Span span8 = span5.Slice(num10, 4); uint num16 = TiffUtils.ConvertToUIntLittleEndian(span8); num13 += num16; BinaryPrimitives.WriteUInt32LittleEndian(span8, num13); num10 += 4; } } } private static void UndoRgba128Bit(Span pixelBytes, int width, bool isBigEndian) { int num = width * 16; int num2 = pixelBytes.Length / num; if (isBigEndian) { for (int i = 0; i < num2; i++) { int num3 = 0; Span span = pixelBytes.Slice(i * num, num); uint num4 = TiffUtils.ConvertToUIntBigEndian(span.Slice(num3, 4)); num3 += 4; uint num5 = TiffUtils.ConvertToUIntBigEndian(span.Slice(num3, 4)); num3 += 4; uint num6 = TiffUtils.ConvertToUIntBigEndian(span.Slice(num3, 4)); num3 += 4; uint num7 = TiffUtils.ConvertToUIntBigEndian(span.Slice(num3, 4)); num3 += 4; for (int j = 1; j < width; j++) { Span span2 = span.Slice(num3, 4); uint num8 = TiffUtils.ConvertToUIntBigEndian(span2); num4 += num8; BinaryPrimitives.WriteUInt32BigEndian(span2, num4); num3 += 4; Span span3 = span.Slice(num3, 4); uint num9 = TiffUtils.ConvertToUIntBigEndian(span3); num5 += num9; BinaryPrimitives.WriteUInt32BigEndian(span3, num5); num3 += 4; Span span4 = span.Slice(num3, 4); uint num10 = TiffUtils.ConvertToUIntBigEndian(span4); num6 += num10; BinaryPrimitives.WriteUInt32BigEndian(span4, num6); num3 += 4; Span span5 = span.Slice(num3, 4); uint num11 = TiffUtils.ConvertToUIntBigEndian(span5); num7 += num11; BinaryPrimitives.WriteUInt32BigEndian(span5, num7); num3 += 4; } } return; } for (int k = 0; k < num2; k++) { int num12 = 0; Span span6 = pixelBytes.Slice(k * num, num); uint num13 = TiffUtils.ConvertToUIntLittleEndian(span6.Slice(num12, 4)); num12 += 4; uint num14 = TiffUtils.ConvertToUIntLittleEndian(span6.Slice(num12, 4)); num12 += 4; uint num15 = TiffUtils.ConvertToUIntLittleEndian(span6.Slice(num12, 4)); num12 += 4; uint num16 = TiffUtils.ConvertToUIntLittleEndian(span6.Slice(num12, 4)); num12 += 4; for (int l = 1; l < width; l++) { Span span7 = span6.Slice(num12, 4); uint num17 = TiffUtils.ConvertToUIntLittleEndian(span7); num13 += num17; BinaryPrimitives.WriteUInt32LittleEndian(span7, num13); num12 += 4; Span span8 = span6.Slice(num12, 4); uint num18 = TiffUtils.ConvertToUIntLittleEndian(span8); num14 += num18; BinaryPrimitives.WriteUInt32LittleEndian(span8, num14); num12 += 4; Span span9 = span6.Slice(num12, 4); uint num19 = TiffUtils.ConvertToUIntLittleEndian(span9); num15 += num19; BinaryPrimitives.WriteUInt32LittleEndian(span9, num15); num12 += 4; Span span10 = span6.Slice(num12, 4); uint num20 = TiffUtils.ConvertToUIntLittleEndian(span10); num16 += num20; BinaryPrimitives.WriteUInt32LittleEndian(span10, num16); num12 += 4; } } } } internal abstract class TiffBaseCompression : IDisposable { private bool isDisposed; public int Width { get; } public int BitsPerPixel { get; } public int BytesPerRow { get; } public TiffPredictor Predictor { get; } protected MemoryAllocator Allocator { get; } protected TiffBaseCompression(MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) { Allocator = allocator; Width = width; BitsPerPixel = bitsPerPixel; Predictor = predictor; BytesPerRow = (width * bitsPerPixel + 7) / 8; } public void Dispose() { if (!isDisposed) { isDisposed = true; Dispose(disposing: true); } } protected abstract void Dispose(bool disposing); } internal abstract class TiffBaseCompressor : TiffBaseCompression { public abstract TiffCompression Method { get; } public Stream Output { get; } protected TiffBaseCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) : base(allocator, width, bitsPerPixel, predictor) { Output = output; } public abstract void Initialize(int rowsPerStrip); public abstract void CompressStrip(Span rows, int height); } internal abstract class TiffBaseDecompressor : TiffBaseCompression { protected TiffBaseDecompressor(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) : base(memoryAllocator, width, bitsPerPixel, predictor) { } public void Decompress(BufferedReadStream stream, ulong stripOffset, ulong stripByteCount, int stripHeight, Span buffer) { stream.Seek((long)stripOffset, SeekOrigin.Begin); Decompress(stream, (int)stripByteCount, stripHeight, buffer); if ((long)(stripOffset + stripByteCount) < stream.Position) { TiffThrowHelper.ThrowImageFormatException("Out of range when reading a strip."); } } protected abstract void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer); } internal static class TiffCompressorFactory { public static TiffBaseCompressor Create(TiffCompression method, Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, DeflateCompressionLevel compressionLevel, TiffPredictor predictor) { switch (method) { case TiffCompression.None: case TiffCompression.OldJpeg: case TiffCompression.ItuTRecT82: case TiffCompression.ItuTRecT43: case TiffCompression.OldDeflate: return new NoCompressor(output, allocator, width, bitsPerPixel); case TiffCompression.Jpeg: return new TiffJpegCompressor(output, allocator, width, bitsPerPixel); case TiffCompression.PackBits: return new PackBitsCompressor(output, allocator, width, bitsPerPixel); case TiffCompression.Deflate: return new DeflateCompressor(output, allocator, width, bitsPerPixel, predictor, compressionLevel); case TiffCompression.Lzw: return new LzwCompressor(output, allocator, width, bitsPerPixel, predictor); case TiffCompression.CcittGroup3Fax: return new T4BitCompressor(output, allocator, width, bitsPerPixel); case TiffCompression.CcittGroup4Fax: return new T6BitCompressor(output, allocator, width, bitsPerPixel); case TiffCompression.Ccitt1D: return new T4BitCompressor(output, allocator, width, bitsPerPixel, useModifiedHuffman: true); default: throw TiffThrowHelper.NotSupportedCompressor(method.ToString()); } } } internal enum TiffDecoderCompressionType { None, PackBits, Deflate, Lzw, T4, T6, HuffmanRle, Jpeg } internal static class TiffDecompressorsFactory { public static TiffBaseDecompressor Create(Configuration configuration, TiffDecoderCompressionType method, MemoryAllocator allocator, TiffPhotometricInterpretation photometricInterpretation, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, FaxCompressionOptions faxOptions, byte[] jpegTables, TiffFillOrder fillOrder, ByteOrder byteOrder) { return method switch { TiffDecoderCompressionType.None => new NoneTiffCompression(allocator, width, bitsPerPixel), TiffDecoderCompressionType.PackBits => new PackBitsTiffCompression(allocator, width, bitsPerPixel), TiffDecoderCompressionType.Deflate => new DeflateTiffCompression(allocator, width, bitsPerPixel, colorType, predictor, byteOrder == ByteOrder.BigEndian), TiffDecoderCompressionType.Lzw => new LzwTiffCompression(allocator, width, bitsPerPixel, colorType, predictor, byteOrder == ByteOrder.BigEndian), TiffDecoderCompressionType.T4 => new T4TiffCompression(allocator, fillOrder, width, bitsPerPixel, faxOptions, photometricInterpretation), TiffDecoderCompressionType.T6 => new T6TiffCompression(allocator, fillOrder, width, bitsPerPixel, photometricInterpretation), TiffDecoderCompressionType.HuffmanRle => new ModifiedHuffmanTiffCompression(allocator, fillOrder, width, bitsPerPixel, photometricInterpretation), TiffDecoderCompressionType.Jpeg => new JpegTiffCompression(configuration, allocator, width, bitsPerPixel, jpegTables, photometricInterpretation), _ => throw TiffThrowHelper.NotSupportedDecompressor("method"), }; } } } namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { internal readonly ref struct CcittReferenceScanline { private readonly ReadOnlySpan scanLine; private readonly int width; private readonly byte whiteByte; public bool IsEmpty => scanLine.IsEmpty; public CcittReferenceScanline(bool whiteIsZero, ReadOnlySpan scanLine) { this.scanLine = scanLine; width = scanLine.Length; whiteByte = (byte)((!whiteIsZero) ? byte.MaxValue : 0); } public CcittReferenceScanline(bool whiteIsZero, int width) { scanLine = default(ReadOnlySpan); this.width = width; whiteByte = (byte)((!whiteIsZero) ? byte.MaxValue : 0); } public int FindB1(int a0, byte a0Byte) { if (IsEmpty) { return FindB1ForImaginaryWhiteLine(a0, a0Byte); } return FindB1ForNormalLine(a0, a0Byte); } public int FindB2(int b1) { if (IsEmpty) { return FindB2ForImaginaryWhiteLine(); } return FindB2ForNormalLine(b1); } private int FindB1ForImaginaryWhiteLine(int a0, byte a0Byte) { if (a0 < 0 && a0Byte != whiteByte) { return 0; } return width; } private int FindB1ForNormalLine(int a0, byte a0Byte) { int num = 0; if (a0 < 0) { if (a0Byte != scanLine[0]) { return 0; } } else { num = a0; } ReadOnlySpan span = scanLine.Slice(num); byte value = (byte)(~a0Byte); int num2 = span.IndexOf(value); if (num2 < 0) { return scanLine.Length; } if (num2 != 0) { return num + num2; } value = (byte)(~span[0]); num2 = span.IndexOf(value); if (num2 < 0) { return scanLine.Length; } span = span.Slice(num2); num += num2; num2 = span.IndexOf((byte)(~value)); if (num2 < 0) { return scanLine.Length; } return num2 + num; } private int FindB2ForImaginaryWhiteLine() { return width; } private int FindB2ForNormalLine(int b1) { if (b1 >= scanLine.Length) { return scanLine.Length; } byte value = (byte)(~scanLine[b1]); int num = b1 + 1; int num2 = scanLine.Slice(num).IndexOf(value); if (num2 == -1) { return scanLine.Length; } return num + num2; } } [DebuggerDisplay("Type = {Type}")] internal readonly struct CcittTwoDimensionalCode { private readonly ushort value; public CcittTwoDimensionalCodeType Type => (CcittTwoDimensionalCodeType)(value & 0xFF); public CcittTwoDimensionalCode(CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0) { value = (ushort)((byte)type | ((bitsRequired & 0xF) << 8) | ((extensionBits & 7) << 11)); } } internal enum CcittTwoDimensionalCodeType { None, Pass, Horizontal, Vertical0, VerticalR1, VerticalR2, VerticalR3, VerticalL1, VerticalL2, VerticalL3, Extensions1D, Extensions2D } internal sealed class DeflateTiffCompression : TiffBaseDecompressor { private readonly bool isBigEndian; private readonly TiffColorType colorType; public DeflateTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian) : base(memoryAllocator, width, bitsPerPixel, predictor) { this.colorType = colorType; this.isBigEndian = isBigEndian; } protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { long pos = stream.Position; using (ZlibInflateStream zlibInflateStream = new ZlibInflateStream(stream, delegate { int num3 = (int)(byteCount - (stream.Position - pos)); return (num3 > 0) ? num3 : 0; })) { zlibInflateStream.AllocateNewBytes(byteCount, isCriticalChunk: true); DeflateStream compressedStream = zlibInflateStream.CompressedStream; int num2; for (int num = 0; num < buffer.Length; num += num2) { num2 = compressedStream.Read(buffer, num, buffer.Length - num); if (num2 <= 0) { break; } } } if (base.Predictor == TiffPredictor.Horizontal) { HorizontalPredictor.Undo(buffer, base.Width, colorType, isBigEndian); } } protected override void Dispose(bool disposing) { } } internal sealed class GrayJpegSpectralConverter : SpectralConverter where TPixel : unmanaged, IPixel { public GrayJpegSpectralConverter(Configuration configuration) : base(configuration) { } protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) { return JpegColorConverterBase.GetConverter(JpegColorSpace.Grayscale, frame.Precision); } } internal sealed class JpegTiffCompression : TiffBaseDecompressor { private readonly Configuration configuration; private readonly byte[] jpegTables; private readonly TiffPhotometricInterpretation photometricInterpretation; public JpegTiffCompression(Configuration configuration, MemoryAllocator memoryAllocator, int width, int bitsPerPixel, byte[] jpegTables, TiffPhotometricInterpretation photometricInterpretation) : base(memoryAllocator, width, bitsPerPixel) { this.configuration = configuration; this.jpegTables = jpegTables; this.photometricInterpretation = photometricInterpretation; } protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { if (jpegTables != null) { using (JpegDecoderCore jpegDecoderCore = new JpegDecoderCore(configuration, new JpegDecoder())) { switch (photometricInterpretation) { case TiffPhotometricInterpretation.WhiteIsZero: case TiffPhotometricInterpretation.BlackIsZero: { using SpectralConverter spectralConverter2 = new GrayJpegSpectralConverter(configuration); HuffmanScanDecoder huffmanScanDecoder2 = new HuffmanScanDecoder(stream, spectralConverter2, CancellationToken.None); jpegDecoderCore.LoadTables(jpegTables, huffmanScanDecoder2); jpegDecoderCore.ParseStream(stream, huffmanScanDecoder2, CancellationToken.None); using Buffer2D pixelBuffer2 = spectralConverter2.GetPixelBuffer(CancellationToken.None); CopyImageBytesToBuffer(buffer, pixelBuffer2); break; } case TiffPhotometricInterpretation.Rgb: case TiffPhotometricInterpretation.YCbCr: { using SpectralConverter spectralConverter = ((photometricInterpretation == TiffPhotometricInterpretation.YCbCr) ? new RgbJpegSpectralConverter(configuration) : new SpectralConverter(configuration)); HuffmanScanDecoder huffmanScanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None); jpegDecoderCore.LoadTables(jpegTables, huffmanScanDecoder); jpegDecoderCore.ParseStream(stream, huffmanScanDecoder, CancellationToken.None); using Buffer2D pixelBuffer = spectralConverter.GetPixelBuffer(CancellationToken.None); CopyImageBytesToBuffer(buffer, pixelBuffer); break; } default: TiffThrowHelper.ThrowNotSupported($"Jpeg compressed tiff with photometric interpretation {photometricInterpretation} is not supported"); break; } return; } } using Image image = Image.Load(stream); CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer); } private static void CopyImageBytesToBuffer(Span buffer, Buffer2D pixelBuffer) { int num = 0; for (int i = 0; i < pixelBuffer.Height; i++) { Span span = MemoryMarshal.AsBytes(pixelBuffer.DangerousGetRowSpan(i)); span.CopyTo(buffer.Slice(num)); num += span.Length; } } private static void CopyImageBytesToBuffer(Span buffer, Buffer2D pixelBuffer) { int num = 0; for (int i = 0; i < pixelBuffer.Height; i++) { Span span = MemoryMarshal.AsBytes(pixelBuffer.DangerousGetRowSpan(i)); span.CopyTo(buffer.Slice(num)); num += span.Length; } } protected override void Dispose(bool disposing) { } } public class LzwString { private static readonly LzwString Empty = new LzwString(0, 0, 0, null); private readonly LzwString previous; private readonly byte value; public int Length { get; } public byte FirstChar { get; } public LzwString(byte code) : this(code, code, 1, null) { } private LzwString(byte value, byte firstChar, int length, LzwString previous) { this.value = value; FirstChar = firstChar; Length = length; this.previous = previous; } public LzwString Concatenate(byte other) { if (this == Empty) { return new LzwString(other); } return new LzwString(other, FirstChar, Length + 1, this); } public int WriteTo(Span buffer, int offset) { if (Length == 0) { return 0; } if (Length == 1) { buffer[offset] = value; return 1; } LzwString lzwString = this; int num = Length - 1; if (num >= buffer.Length) { TiffThrowHelper.ThrowImageFormatException("Error reading lzw compressed stream. Either pixel buffer to write to is to small or code length is invalid!"); } for (int num2 = num; num2 >= 0; num2--) { buffer[offset + num2] = lzwString.value; lzwString = lzwString.previous; } return Length; } } internal sealed class LzwTiffCompression : TiffBaseDecompressor { private readonly bool isBigEndian; private readonly TiffColorType colorType; public LzwTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffColorType colorType, TiffPredictor predictor, bool isBigEndian) : base(memoryAllocator, width, bitsPerPixel, predictor) { this.colorType = colorType; this.isBigEndian = isBigEndian; } protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { new TiffLzwDecoder(stream).DecodePixels(buffer); if (base.Predictor == TiffPredictor.Horizontal) { HorizontalPredictor.Undo(buffer, base.Width, colorType, isBigEndian); } } protected override void Dispose(bool disposing) { } } internal sealed class ModifiedHuffmanBitReader : T4BitReader { public override bool HasMoreData { get { if (base.Position >= (ulong)((long)base.DataLength - 1L)) { return (uint)(base.BitsRead - 1) < 6u; } return true; } } public override bool IsEndOfScanLine { get { if (base.IsWhiteRun && base.CurValueBitsRead == 12 && base.Value == 1) { return true; } if (base.CurValueBitsRead == 11 && base.Value == 0) { return true; } return false; } } public ModifiedHuffmanBitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) : base(input, fillOrder, bytesToRead, allocator) { } public override void StartNewRow() { base.StartNewRow(); if ((base.BitsRead & 7) != 0) { base.Position++; ResetBitsRead(); } } protected override void ReadEolBeforeFirstData() { } } internal sealed class ModifiedHuffmanTiffCompression : TiffBaseDecompressor { private readonly byte whiteValue; private readonly byte blackValue; private TiffFillOrder FillOrder { get; } public ModifiedHuffmanTiffCompression(MemoryAllocator allocator, TiffFillOrder fillOrder, int width, int bitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) : base(allocator, width, bitsPerPixel) { FillOrder = fillOrder; bool flag = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; whiteValue = ((!flag) ? ((byte)1) : ((byte)0)); blackValue = (flag ? ((byte)1) : ((byte)0)); } protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { using ModifiedHuffmanBitReader modifiedHuffmanBitReader = new ModifiedHuffmanBitReader(stream, FillOrder, byteCount, base.Allocator); buffer.Clear(); uint num = 0u; uint num2 = 0u; while (modifiedHuffmanBitReader.HasMoreData) { modifiedHuffmanBitReader.ReadNextRun(); if (modifiedHuffmanBitReader.RunLength != 0) { if (modifiedHuffmanBitReader.IsWhiteRun) { BitWriterUtils.WriteBits(buffer, (int)num, modifiedHuffmanBitReader.RunLength, whiteValue); } else { BitWriterUtils.WriteBits(buffer, (int)num, modifiedHuffmanBitReader.RunLength, blackValue); } num += modifiedHuffmanBitReader.RunLength; num2 += modifiedHuffmanBitReader.RunLength; } if (num2 == base.Width) { modifiedHuffmanBitReader.StartNewRow(); num2 = 0u; uint num3 = 8 - num % 8; if (num3 != 8) { BitWriterUtils.WriteBits(buffer, (int)num, num3, 0); num += num3; } } if (num2 > base.Width) { TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, decoded more pixels then image width"); } } } protected override void Dispose(bool disposing) { } } internal sealed class NoneTiffCompression : TiffBaseDecompressor { public NoneTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel) : base(memoryAllocator, width, bitsPerPixel) { } protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { stream.Read(buffer, 0, Math.Min(buffer.Length, byteCount)); } protected override void Dispose(bool disposing) { } } internal sealed class PackBitsTiffCompression : TiffBaseDecompressor { private IMemoryOwner compressedDataMemory; public PackBitsTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel) : base(memoryAllocator, width, bitsPerPixel) { } protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { if (compressedDataMemory == null) { compressedDataMemory = base.Allocator.Allocate(byteCount); } else if (compressedDataMemory.Length() < byteCount) { compressedDataMemory.Dispose(); compressedDataMemory = base.Allocator.Allocate(byteCount); } Span span = compressedDataMemory.GetSpan(); stream.Read(span, 0, byteCount); int num = 0; int num2 = 0; while (num < byteCount) { byte b = span[num]; if (b <= 127) { int num3 = num + 1; int num4 = span[num] + 1; if (num3 + num4 > span.Length) { TiffThrowHelper.ThrowImageFormatException("Tiff packbits compression error: not enough data."); } span.Slice(num3, num4).CopyTo(buffer.Slice(num2)); num += num4 + 1; num2 += num4; } else if (b == 128) { num++; } else { byte value = span[num + 1]; int num5 = 257 - b; ArrayCopyRepeat(value, buffer, num2, num5); num += 2; num2 += num5; } } } private static void ArrayCopyRepeat(byte value, Span destinationArray, int destinationIndex, int length) { for (int i = 0; i < length; i++) { destinationArray[i + destinationIndex] = value; } } protected override void Dispose(bool disposing) { compressedDataMemory?.Dispose(); } } internal sealed class RgbJpegSpectralConverter : SpectralConverter where TPixel : unmanaged, IPixel { public RgbJpegSpectralConverter(Configuration configuration) : base(configuration) { } protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) { return JpegColorConverterBase.GetConverter(JpegColorSpace.RGB, frame.Precision); } } internal class T4BitReader : IDisposable { private readonly TiffFillOrder fillOrder; private bool isFirstScanLine; private bool terminationCodeFound; private bool isStartOfRow; private readonly bool eolPadding; private const int MinCodeLength = 2; private readonly int maxCodeLength = 13; private static readonly Dictionary WhiteLen4TermCodes = new Dictionary { { 7u, 2u }, { 8u, 3u }, { 11u, 4u }, { 12u, 5u }, { 14u, 6u }, { 15u, 7u } }; private static readonly Dictionary WhiteLen5TermCodes = new Dictionary { { 19u, 8u }, { 20u, 9u }, { 7u, 10u }, { 8u, 11u } }; private static readonly Dictionary WhiteLen6TermCodes = new Dictionary { { 7u, 1u }, { 8u, 12u }, { 3u, 13u }, { 52u, 14u }, { 53u, 15u }, { 42u, 16u }, { 43u, 17u } }; private static readonly Dictionary WhiteLen7TermCodes = new Dictionary { { 39u, 18u }, { 12u, 19u }, { 8u, 20u }, { 23u, 21u }, { 3u, 22u }, { 4u, 23u }, { 40u, 24u }, { 43u, 25u }, { 19u, 26u }, { 36u, 27u }, { 24u, 28u } }; private static readonly Dictionary WhiteLen8TermCodes = new Dictionary { { 53u, 0u }, { 2u, 29u }, { 3u, 30u }, { 26u, 31u }, { 27u, 32u }, { 18u, 33u }, { 19u, 34u }, { 20u, 35u }, { 21u, 36u }, { 22u, 37u }, { 23u, 38u }, { 40u, 39u }, { 41u, 40u }, { 42u, 41u }, { 43u, 42u }, { 44u, 43u }, { 45u, 44u }, { 4u, 45u }, { 5u, 46u }, { 10u, 47u }, { 11u, 48u }, { 82u, 49u }, { 83u, 50u }, { 84u, 51u }, { 85u, 52u }, { 36u, 53u }, { 37u, 54u }, { 88u, 55u }, { 89u, 56u }, { 90u, 57u }, { 91u, 58u }, { 74u, 59u }, { 75u, 60u }, { 50u, 61u }, { 51u, 62u }, { 52u, 63u } }; private static readonly Dictionary BlackLen2TermCodes = new Dictionary { { 3u, 2u }, { 2u, 3u } }; private static readonly Dictionary BlackLen3TermCodes = new Dictionary { { 2u, 1u }, { 3u, 4u } }; private static readonly Dictionary BlackLen4TermCodes = new Dictionary { { 3u, 5u }, { 2u, 6u } }; private static readonly Dictionary BlackLen5TermCodes = new Dictionary { { 3u, 7u } }; private static readonly Dictionary BlackLen6TermCodes = new Dictionary { { 5u, 8u }, { 4u, 9u } }; private static readonly Dictionary BlackLen7TermCodes = new Dictionary { { 4u, 10u }, { 5u, 11u }, { 7u, 12u } }; private static readonly Dictionary BlackLen8TermCodes = new Dictionary { { 4u, 13u }, { 7u, 14u } }; private static readonly Dictionary BlackLen9TermCodes = new Dictionary { { 24u, 15u } }; private static readonly Dictionary BlackLen10TermCodes = new Dictionary { { 55u, 0u }, { 23u, 16u }, { 24u, 17u }, { 8u, 18u } }; private static readonly Dictionary BlackLen11TermCodes = new Dictionary { { 103u, 19u }, { 104u, 20u }, { 108u, 21u }, { 55u, 22u }, { 40u, 23u }, { 23u, 24u }, { 24u, 25u } }; private static readonly Dictionary BlackLen12TermCodes = new Dictionary { { 202u, 26u }, { 203u, 27u }, { 204u, 28u }, { 205u, 29u }, { 104u, 30u }, { 105u, 31u }, { 106u, 32u }, { 107u, 33u }, { 210u, 34u }, { 211u, 35u }, { 212u, 36u }, { 213u, 37u }, { 214u, 38u }, { 215u, 39u }, { 108u, 40u }, { 109u, 41u }, { 218u, 42u }, { 219u, 43u }, { 84u, 44u }, { 85u, 45u }, { 86u, 46u }, { 87u, 47u }, { 100u, 48u }, { 101u, 49u }, { 82u, 50u }, { 83u, 51u }, { 36u, 52u }, { 55u, 53u }, { 56u, 54u }, { 39u, 55u }, { 40u, 56u }, { 88u, 57u }, { 89u, 58u }, { 43u, 59u }, { 44u, 60u }, { 90u, 61u }, { 102u, 62u }, { 103u, 63u } }; private static readonly Dictionary WhiteLen5MakeupCodes = new Dictionary { { 27u, 64u }, { 18u, 128u } }; private static readonly Dictionary WhiteLen6MakeupCodes = new Dictionary { { 23u, 192u }, { 24u, 1664u } }; private static readonly Dictionary WhiteLen8MakeupCodes = new Dictionary { { 54u, 320u }, { 55u, 384u }, { 100u, 448u }, { 101u, 512u }, { 104u, 576u }, { 103u, 640u } }; private static readonly Dictionary WhiteLen7MakeupCodes = new Dictionary { { 55u, 256u } }; private static readonly Dictionary WhiteLen9MakeupCodes = new Dictionary { { 204u, 704u }, { 205u, 768u }, { 210u, 832u }, { 211u, 896u }, { 212u, 960u }, { 213u, 1024u }, { 214u, 1088u }, { 215u, 1152u }, { 216u, 1216u }, { 217u, 1280u }, { 218u, 1344u }, { 219u, 1408u }, { 152u, 1472u }, { 153u, 1536u }, { 154u, 1600u }, { 155u, 1728u } }; private static readonly Dictionary WhiteLen11MakeupCodes = new Dictionary { { 8u, 1792u }, { 12u, 1856u }, { 13u, 1920u } }; private static readonly Dictionary WhiteLen12MakeupCodes = new Dictionary { { 18u, 1984u }, { 19u, 2048u }, { 20u, 2112u }, { 21u, 2176u }, { 22u, 2240u }, { 23u, 2304u }, { 28u, 2368u }, { 29u, 2432u }, { 30u, 2496u }, { 31u, 2560u } }; private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary { { 15u, 64u } }; private static readonly Dictionary BlackLen11MakeupCodes = new Dictionary { { 8u, 1792u }, { 12u, 1856u }, { 13u, 1920u } }; private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary { { 200u, 128u }, { 201u, 192u }, { 91u, 256u }, { 51u, 320u }, { 52u, 384u }, { 53u, 448u }, { 18u, 1984u }, { 19u, 2048u }, { 20u, 2112u }, { 21u, 2176u }, { 22u, 2240u }, { 23u, 2304u }, { 28u, 2368u }, { 29u, 2432u }, { 30u, 2496u }, { 31u, 2560u } }; private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary { { 108u, 512u }, { 109u, 576u }, { 74u, 640u }, { 75u, 704u }, { 76u, 768u }, { 77u, 832u }, { 114u, 896u }, { 115u, 960u }, { 116u, 1024u }, { 117u, 1088u }, { 118u, 1152u }, { 119u, 1216u }, { 82u, 1280u }, { 83u, 1344u }, { 84u, 1408u }, { 85u, 1472u }, { 90u, 1536u }, { 91u, 1600u }, { 100u, 1664u }, { 101u, 1728u } }; protected uint Value { get; private set; } protected int CurValueBitsRead { get; private set; } protected int BitsRead { get; private set; } protected int DataLength { get; } protected ulong Position { get; set; } public IMemoryOwner Data { get; } public virtual bool HasMoreData => Position < (ulong)((long)DataLength - 1L); public bool IsWhiteRun { get; protected set; } public uint RunLength { get; private set; } public virtual bool IsEndOfScanLine { get { if (eolPadding) { if (CurValueBitsRead >= 12) { return Value == 1; } return false; } if (CurValueBitsRead == 12) { return Value == 1; } return false; } } public T4BitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator, bool eolPadding = false) { this.fillOrder = fillOrder; Data = allocator.Allocate(bytesToRead); ReadImageDataFromStream(input, bytesToRead); DataLength = bytesToRead; BitsRead = 0; Value = 0u; CurValueBitsRead = 0; Position = 0uL; IsWhiteRun = true; isFirstScanLine = true; isStartOfRow = true; terminationCodeFound = false; RunLength = 0u; this.eolPadding = eolPadding; if (this.eolPadding) { maxCodeLength = 24; } } public void ReadNextRun() { if (terminationCodeFound) { IsWhiteRun = !IsWhiteRun; terminationCodeFound = false; } Reset(); ReadEolBeforeFirstData(); Value = ReadValue(2); do { if (CurValueBitsRead > maxCodeLength) { TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error: invalid code length read"); } if (IsMakeupCode()) { if (IsWhiteRun) { RunLength += WhiteMakeupCodeRunLength(); } else { RunLength += BlackMakeupCodeRunLength(); } isStartOfRow = false; Reset(resetRunLength: false); continue; } if (IsTerminatingCode()) { if (isStartOfRow && IsWhiteRun && WhiteTerminatingCodeRunLength() == 0) { Reset(); isStartOfRow = false; terminationCodeFound = true; RunLength = 0u; break; } if (IsWhiteRun) { RunLength += WhiteTerminatingCodeRunLength(); } else { RunLength += BlackTerminatingCodeRunLength(); } terminationCodeFound = true; isStartOfRow = false; break; } uint num = ReadValue(1); Value = (Value << 1) | num; if (IsEndOfScanLine) { StartNewRow(); } } while (!IsEndOfScanLine); isFirstScanLine = false; } public virtual void StartNewRow() { IsWhiteRun = true; isStartOfRow = true; terminationCodeFound = false; } public void Dispose() { Data.Dispose(); } protected virtual void ReadEolBeforeFirstData() { if (isFirstScanLine) { Value = ReadValue(eolPadding ? 16 : 12); if (!IsEndOfScanLine) { TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error: expected start of data marker not found"); } Reset(); } } protected void Reset(bool resetRunLength = true) { Value = 0u; CurValueBitsRead = 0; if (resetRunLength) { RunLength = 0u; } } protected void ResetBitsRead() { BitsRead = 0; } protected uint ReadValue(int nBits) { uint num = 0u; int num2 = nBits; while (num2-- > 0) { uint bit = GetBit(); num |= bit << num2; CurValueBitsRead++; } return num; } private uint WhiteTerminatingCodeRunLength() { return CurValueBitsRead switch { 4 => WhiteLen4TermCodes[Value], 5 => WhiteLen5TermCodes[Value], 6 => WhiteLen6TermCodes[Value], 7 => WhiteLen7TermCodes[Value], 8 => WhiteLen8TermCodes[Value], _ => 0u, }; } private uint BlackTerminatingCodeRunLength() { return CurValueBitsRead switch { 2 => BlackLen2TermCodes[Value], 3 => BlackLen3TermCodes[Value], 4 => BlackLen4TermCodes[Value], 5 => BlackLen5TermCodes[Value], 6 => BlackLen6TermCodes[Value], 7 => BlackLen7TermCodes[Value], 8 => BlackLen8TermCodes[Value], 9 => BlackLen9TermCodes[Value], 10 => BlackLen10TermCodes[Value], 11 => BlackLen11TermCodes[Value], 12 => BlackLen12TermCodes[Value], _ => 0u, }; } private uint WhiteMakeupCodeRunLength() { return CurValueBitsRead switch { 5 => WhiteLen5MakeupCodes[Value], 6 => WhiteLen6MakeupCodes[Value], 7 => WhiteLen7MakeupCodes[Value], 8 => WhiteLen8MakeupCodes[Value], 9 => WhiteLen9MakeupCodes[Value], 11 => WhiteLen11MakeupCodes[Value], 12 => WhiteLen12MakeupCodes[Value], _ => 0u, }; } private uint BlackMakeupCodeRunLength() { return CurValueBitsRead switch { 10 => BlackLen10MakeupCodes[Value], 11 => BlackLen11MakeupCodes[Value], 12 => BlackLen12MakeupCodes[Value], 13 => BlackLen13MakeupCodes[Value], _ => 0u, }; } private bool IsMakeupCode() { if (IsWhiteRun) { return IsWhiteMakeupCode(); } return IsBlackMakeupCode(); } private bool IsWhiteMakeupCode() { return CurValueBitsRead switch { 5 => WhiteLen5MakeupCodes.ContainsKey(Value), 6 => WhiteLen6MakeupCodes.ContainsKey(Value), 7 => WhiteLen7MakeupCodes.ContainsKey(Value), 8 => WhiteLen8MakeupCodes.ContainsKey(Value), 9 => WhiteLen9MakeupCodes.ContainsKey(Value), 11 => WhiteLen11MakeupCodes.ContainsKey(Value), 12 => WhiteLen12MakeupCodes.ContainsKey(Value), _ => false, }; } private bool IsBlackMakeupCode() { return CurValueBitsRead switch { 10 => BlackLen10MakeupCodes.ContainsKey(Value), 11 => BlackLen11MakeupCodes.ContainsKey(Value), 12 => BlackLen12MakeupCodes.ContainsKey(Value), 13 => BlackLen13MakeupCodes.ContainsKey(Value), _ => false, }; } private bool IsTerminatingCode() { if (IsWhiteRun) { return IsWhiteTerminatingCode(); } return IsBlackTerminatingCode(); } private bool IsWhiteTerminatingCode() { return CurValueBitsRead switch { 4 => WhiteLen4TermCodes.ContainsKey(Value), 5 => WhiteLen5TermCodes.ContainsKey(Value), 6 => WhiteLen6TermCodes.ContainsKey(Value), 7 => WhiteLen7TermCodes.ContainsKey(Value), 8 => WhiteLen8TermCodes.ContainsKey(Value), _ => false, }; } private bool IsBlackTerminatingCode() { return CurValueBitsRead switch { 2 => BlackLen2TermCodes.ContainsKey(Value), 3 => BlackLen3TermCodes.ContainsKey(Value), 4 => BlackLen4TermCodes.ContainsKey(Value), 5 => BlackLen5TermCodes.ContainsKey(Value), 6 => BlackLen6TermCodes.ContainsKey(Value), 7 => BlackLen7TermCodes.ContainsKey(Value), 8 => BlackLen8TermCodes.ContainsKey(Value), 9 => BlackLen9TermCodes.ContainsKey(Value), 10 => BlackLen10TermCodes.ContainsKey(Value), 11 => BlackLen11TermCodes.ContainsKey(Value), 12 => BlackLen12TermCodes.ContainsKey(Value), _ => false, }; } private uint GetBit() { if (BitsRead >= 8) { LoadNewByte(); } Span span = Data.GetSpan(); int num = 8 - BitsRead - 1; bool result = (span[(int)Position] & (1 << num)) != 0; BitsRead++; return result ? 1u : 0u; } private void LoadNewByte() { Position++; ResetBitsRead(); if (Position >= (ulong)DataLength) { TiffThrowHelper.ThrowImageFormatException("tiff image has invalid ccitt compressed data"); } } private void ReadImageDataFromStream(Stream input, int bytesToRead) { Span span = Data.GetSpan(); input.Read(span, 0, bytesToRead); if (fillOrder == TiffFillOrder.LeastSignificantBitFirst) { for (int i = 0; i < span.Length; i++) { span[i] = ReverseBits(span[i]); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte ReverseBits(byte b) { return (byte)((ulong)((((long)b * 2149582850L) & 0x884422110L) * 4311810305L) >> 32); } } internal sealed class T4TiffCompression : TiffBaseDecompressor { private readonly FaxCompressionOptions faxCompressionOptions; private readonly byte whiteValue; private readonly byte blackValue; private readonly int width; private TiffFillOrder FillOrder { get; } public T4TiffCompression(MemoryAllocator allocator, TiffFillOrder fillOrder, int width, int bitsPerPixel, FaxCompressionOptions faxOptions, TiffPhotometricInterpretation photometricInterpretation) : base(allocator, width, bitsPerPixel) { faxCompressionOptions = faxOptions; FillOrder = fillOrder; this.width = width; bool flag = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; whiteValue = ((!flag) ? ((byte)1) : ((byte)0)); blackValue = (flag ? ((byte)1) : ((byte)0)); } protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { if (faxCompressionOptions.HasFlag(FaxCompressionOptions.TwoDimensionalCoding)) { TiffThrowHelper.ThrowNotSupported("TIFF CCITT 2D compression is not yet supported"); } bool eolPadding = faxCompressionOptions.HasFlag(FaxCompressionOptions.EolPadding); using T4BitReader t4BitReader = new T4BitReader(stream, FillOrder, byteCount, base.Allocator, eolPadding); buffer.Clear(); uint num = 0u; uint num2 = 0u; while (t4BitReader.HasMoreData) { t4BitReader.ReadNextRun(); if (t4BitReader.RunLength != 0) { WritePixelRun(buffer, t4BitReader, num); num += t4BitReader.RunLength; num2 += t4BitReader.RunLength; } if (t4BitReader.IsEndOfScanLine) { uint num3 = 8 - num % 8; if (num3 != 8) { BitWriterUtils.WriteBits(buffer, (int)num, num3, 0); num += num3; } num2 = 0u; } } if (num2 != 0 && num2 < width) { t4BitReader.ReadNextRun(); WritePixelRun(buffer, t4BitReader, num); } } private void WritePixelRun(Span buffer, T4BitReader bitReader, uint bitsWritten) { if (bitReader.IsWhiteRun) { BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); } else { BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); } } protected override void Dispose(bool disposing) { } } internal sealed class T6BitReader : T4BitReader { private readonly int maxCodeLength = 12; private static readonly CcittTwoDimensionalCode None = new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.None, 0); private static readonly Dictionary Len1Codes = new Dictionary { { 1u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Vertical0, 1) } }; private static readonly Dictionary Len3Codes = new Dictionary { { 1u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Horizontal, 3) }, { 2u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL1, 3) }, { 3u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR1, 3) } }; private static readonly Dictionary Len4Codes = new Dictionary { { 1u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Pass, 4) } }; private static readonly Dictionary Len6Codes = new Dictionary { { 3u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR2, 6) }, { 2u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL2, 6) } }; private static readonly Dictionary Len7Codes = new Dictionary { { 3u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR3, 7) }, { 2u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL3, 7) }, { 1u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Extensions2D, 7) }, { 0u, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Extensions1D, 7) } }; public override bool HasMoreData { get { if (base.Position >= (ulong)((long)base.DataLength - 1L)) { return (uint)(base.BitsRead - 1) < 6u; } return true; } } public CcittTwoDimensionalCode Code { get; internal set; } public T6BitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) : base(input, fillOrder, bytesToRead, allocator) { } public bool ReadNextCodeWord() { Code = None; Reset(); uint num = ReadValue(1); do { if (base.CurValueBitsRead > maxCodeLength) { TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error: invalid code length read"); } switch (base.CurValueBitsRead) { case 1: if (Len1Codes.ContainsKey(num)) { Code = Len1Codes[num]; return false; } break; case 3: if (Len3Codes.ContainsKey(num)) { Code = Len3Codes[num]; return false; } break; case 4: if (Len4Codes.ContainsKey(num)) { Code = Len4Codes[num]; return false; } break; case 6: if (Len6Codes.ContainsKey(num)) { Code = Len6Codes[num]; return false; } break; case 7: if (Len7Codes.ContainsKey(num)) { Code = Len7Codes[num]; return false; } break; } uint num2 = ReadValue(1); num = (num << 1) | num2; } while (!IsEndOfScanLine); if (IsEndOfScanLine) { return true; } return false; } protected override void ReadEolBeforeFirstData() { } public void SwapColor() { base.IsWhiteRun = !base.IsWhiteRun; } } internal sealed class T6TiffCompression : TiffBaseDecompressor { private readonly bool isWhiteZero; private readonly byte whiteValue; private readonly byte blackValue; private readonly int width; private TiffFillOrder FillOrder { get; } public T6TiffCompression(MemoryAllocator allocator, TiffFillOrder fillOrder, int width, int bitsPerPixel, TiffPhotometricInterpretation photometricInterpretation) : base(allocator, width, bitsPerPixel) { FillOrder = fillOrder; this.width = width; isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; whiteValue = ((!isWhiteZero) ? ((byte)1) : ((byte)0)); blackValue = (isWhiteZero ? ((byte)1) : ((byte)0)); } protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { using IMemoryOwner buffer2 = base.Allocator.Allocate(width * 2); Span span = buffer2.GetSpan(); Span span2 = span.Slice(0, width); span = buffer2.GetSpan(); Span span3 = span.Slice(width, width); using T6BitReader bitReader = new T6BitReader(stream, FillOrder, byteCount, base.Allocator); CcittReferenceScanline referenceScanline = new CcittReferenceScanline(isWhiteZero, width); uint bitsWritten = 0u; for (int i = 0; i < stripHeight; i++) { span2.Clear(); Decode2DScanline(bitReader, isWhiteZero, referenceScanline, span2); bitsWritten = WriteScanLine(buffer, span2, bitsWritten); span2.CopyTo(span3); referenceScanline = new CcittReferenceScanline(isWhiteZero, span3); } } private uint WriteScanLine(Span buffer, Span scanLine, uint bitsWritten) { byte b = (byte)((!isWhiteZero) ? 255u : 0u); for (int i = 0; i < scanLine.Length; i++) { BitWriterUtils.WriteBits(buffer, (int)bitsWritten, 1u, (scanLine[i] == b) ? whiteValue : blackValue); bitsWritten++; } uint num = bitsWritten % 8; if (num != 0) { uint num2 = 8 - num; BitWriterUtils.WriteBits(buffer, (int)bitsWritten, num2, 0); bitsWritten += num2; } return bitsWritten; } private static void Decode2DScanline(T6BitReader bitReader, bool whiteIsZero, CcittReferenceScanline referenceScanline, Span scanline) { int length = scanline.Length; bitReader.StartNewRow(); int a = -1; byte b = (byte)((!whiteIsZero) ? byte.MaxValue : 0); int num = 0; while (true) { if (bitReader.ReadNextCodeWord()) { if (whiteIsZero) { scanline.Clear(); } else { scanline.Fill(byte.MaxValue); } break; } int num2 = referenceScanline.FindB1(a, b); switch (bitReader.Code.Type) { case CcittTwoDimensionalCodeType.None: TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, could not read a valid code word."); break; case CcittTwoDimensionalCodeType.Pass: { int num4 = referenceScanline.FindB2(num2); scanline.Slice(num, num4 - num).Fill(b); num = num4; a = num4; break; } case CcittTwoDimensionalCodeType.Horizontal: { bitReader.ReadNextRun(); int runLength = (int)bitReader.RunLength; if (runLength > (uint)(scanline.Length - num)) { TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error"); } scanline.Slice(num, runLength).Fill(b); num += runLength; b = (byte)(~b); bitReader.ReadNextRun(); runLength = (int)bitReader.RunLength; if (runLength > (uint)(scanline.Length - num)) { TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error"); } scanline.Slice(num, runLength).Fill(b); num += runLength; b = (byte)(~b); a = num; break; } case CcittTwoDimensionalCodeType.Vertical0: { int num3 = num2; scanline.Slice(num, num3 - num).Fill(b); num = num3; a = num3; b = (byte)(~b); bitReader.SwapColor(); break; } case CcittTwoDimensionalCodeType.VerticalR1: { int num3 = num2 + 1; scanline.Slice(num, num3 - num).Fill(b); num = num3; a = num3; b = (byte)(~b); bitReader.SwapColor(); break; } case CcittTwoDimensionalCodeType.VerticalR2: { int num3 = num2 + 2; scanline.Slice(num, num3 - num).Fill(b); num = num3; a = num3; b = (byte)(~b); bitReader.SwapColor(); break; } case CcittTwoDimensionalCodeType.VerticalR3: { int num3 = num2 + 3; scanline.Slice(num, num3 - num).Fill(b); num = num3; a = num3; b = (byte)(~b); bitReader.SwapColor(); break; } case CcittTwoDimensionalCodeType.VerticalL1: { int num3 = num2 - 1; scanline.Slice(num, num3 - num).Fill(b); num = num3; a = num3; b = (byte)(~b); bitReader.SwapColor(); break; } case CcittTwoDimensionalCodeType.VerticalL2: { int num3 = num2 - 2; scanline.Slice(num, num3 - num).Fill(b); num = num3; a = num3; b = (byte)(~b); bitReader.SwapColor(); break; } case CcittTwoDimensionalCodeType.VerticalL3: { int num3 = num2 - 3; scanline.Slice(num, num3 - num).Fill(b); num = num3; a = num3; b = (byte)(~b); bitReader.SwapColor(); break; } default: throw new NotSupportedException("ccitt extensions are not supported."); } if (num != length) { if (num > length) { TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, unpacked data > width"); } continue; } break; } } protected override void Dispose(bool disposing) { } } internal sealed class TiffLzwDecoder { private readonly Stream stream; private const int ClearCode = 256; private const int EoiCode = 257; private const int MinBits = 9; private const int MaxBits = 12; private const int TableSize = 4096; private readonly LzwString[] table; private int tableLength; private int bitsPerCode; private int oldCode = 256; private int maxCode; private int bitMask; private int maxString; private bool eofReached; private int nextData; private int nextBits; public TiffLzwDecoder(Stream stream) { Guard.NotNull(stream, "stream"); this.stream = stream; table = new LzwString[4096]; for (int i = 0; i < 256; i++) { table[i] = new LzwString((byte)i); } Init(); } private void Init() { tableLength = 258; bitsPerCode = 9; bitMask = BitmaskFor(bitsPerCode); maxCode = MaxCode(); maxString = 1; } public void DecodePixels(Span pixels) { int num = 0; int nextCode; while ((nextCode = GetNextCode()) != 257) { if (nextCode == 256) { Init(); nextCode = GetNextCode(); if (nextCode == 257) { break; } if (table[nextCode] == null) { TiffThrowHelper.ThrowImageFormatException($"Corrupted TIFF LZW: code {nextCode} (table size: {tableLength})"); } num += table[nextCode].WriteTo(pixels, num); } else { if (table[oldCode] == null) { TiffThrowHelper.ThrowImageFormatException($"Corrupted TIFF LZW: code {oldCode} (table size: {tableLength})"); } if (IsInTable(nextCode)) { num += table[nextCode].WriteTo(pixels, num); AddStringToTable(table[oldCode].Concatenate(table[nextCode].FirstChar)); } else { LzwString lzwString = table[oldCode].Concatenate(table[oldCode].FirstChar); num += lzwString.WriteTo(pixels, num); AddStringToTable(lzwString); } } oldCode = nextCode; if (num >= pixels.Length) { break; } } } private void AddStringToTable(LzwString lzwString) { if (tableLength > table.Length) { TiffThrowHelper.ThrowImageFormatException($"TIFF LZW with more than {12} bits per code encountered (table overflow)"); } table[tableLength++] = lzwString; if (tableLength > maxCode) { bitsPerCode++; if (bitsPerCode > 12) { bitsPerCode = 12; } bitMask = BitmaskFor(bitsPerCode); maxCode = MaxCode(); } if (lzwString.Length > maxString) { maxString = lzwString.Length; } } private int GetNextCode() { if (eofReached) { return 257; } int num = stream.ReadByte(); if (num < 0) { eofReached = true; return 257; } nextData = (nextData << 8) | num; nextBits += 8; if (nextBits < bitsPerCode) { num = stream.ReadByte(); if (num < 0) { eofReached = true; return 257; } nextData = (nextData << 8) | num; nextBits += 8; } int result = (nextData >> nextBits - bitsPerCode) & bitMask; nextBits -= bitsPerCode; return result; } private bool IsInTable(int code) { return code < tableLength; } private int MaxCode() { return bitMask - 1; } private static int BitmaskFor(int bits) { return (1 << bits) - 1; } } } namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { internal sealed class DeflateCompressor : TiffBaseCompressor { private readonly DeflateCompressionLevel compressionLevel; private readonly MemoryStream memoryStream = new MemoryStream(); public override TiffCompression Method => TiffCompression.Deflate; public DeflateCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor, DeflateCompressionLevel compressionLevel) : base(output, allocator, width, bitsPerPixel, predictor) { this.compressionLevel = compressionLevel; } public override void Initialize(int rowsPerStrip) { } public override void CompressStrip(Span rows, int height) { memoryStream.Seek(0L, SeekOrigin.Begin); using (ZlibDeflateStream zlibDeflateStream = new ZlibDeflateStream(base.Allocator, memoryStream, compressionLevel)) { if (base.Predictor == TiffPredictor.Horizontal) { HorizontalPredictor.ApplyHorizontalPrediction(rows, base.BytesPerRow, base.BitsPerPixel); } zlibDeflateStream.Write(rows); zlibDeflateStream.Flush(); } int count = (int)memoryStream.Position; byte[] buffer = memoryStream.GetBuffer(); base.Output.Write(buffer, 0, count); } protected override void Dispose(bool disposing) { } } internal sealed class LzwCompressor : TiffBaseCompressor { private TiffLzwEncoder lzwEncoder; public override TiffCompression Method => TiffCompression.Lzw; public LzwCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor) : base(output, allocator, width, bitsPerPixel, predictor) { } public override void Initialize(int rowsPerStrip) { lzwEncoder = new TiffLzwEncoder(base.Allocator); } public override void CompressStrip(Span rows, int height) { if (base.Predictor == TiffPredictor.Horizontal) { HorizontalPredictor.ApplyHorizontalPrediction(rows, base.BytesPerRow, base.BitsPerPixel); } lzwEncoder.Encode(rows, base.Output); } protected override void Dispose(bool disposing) { lzwEncoder?.Dispose(); } } internal sealed class NoCompressor : TiffBaseCompressor { public override TiffCompression Method => TiffCompression.None; public NoCompressor(Stream output, MemoryAllocator memoryAllocator, int width, int bitsPerPixel) : base(output, memoryAllocator, width, bitsPerPixel) { } public override void Initialize(int rowsPerStrip) { } public override void CompressStrip(Span rows, int height) { base.Output.Write(rows); } protected override void Dispose(bool disposing) { } } internal sealed class PackBitsCompressor : TiffBaseCompressor { private IMemoryOwner pixelData; public override TiffCompression Method => TiffCompression.PackBits; public PackBitsCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel) : base(output, allocator, width, bitsPerPixel) { } public override void Initialize(int rowsPerStrip) { int num = (base.BytesPerRow + 126) / 127 + 1; pixelData = base.Allocator.Allocate(base.BytesPerRow + num); } public override void CompressStrip(Span rows, int height) { Span span = pixelData.GetSpan(); for (int i = 0; i < height; i++) { int length = PackBitsWriter.PackBits(rows.Slice(i * base.BytesPerRow, base.BytesPerRow), span); base.Output.Write(span.Slice(0, length)); } } protected override void Dispose(bool disposing) { pixelData?.Dispose(); } } internal static class PackBitsWriter { public static int PackBits(ReadOnlySpan rowSpan, Span compressedRowSpan) { int num = 127; int num2 = 0; int num3 = 0; int num4 = 0; while (num2 < rowSpan.Length) { if (IsReplicateRun(rowSpan, num2)) { if (num4 > 0) { WriteLiteralRun(rowSpan, num2, num4, compressedRowSpan, num3); num3 += num4 + 1; } int num5 = FindRunLength(rowSpan, num2, num); WriteRun(rowSpan, num2, num5, compressedRowSpan, num3); num3 += 2; num4 = 0; num2 += num5; } else { num4++; num2++; if (num4 >= num) { WriteLiteralRun(rowSpan, num2, num4, compressedRowSpan, num3); num3 += num4 + 1; num4 = 0; } } } if (num4 > 0) { WriteLiteralRun(rowSpan, num2, num4, compressedRowSpan, num3); num3 += num4 + 1; } return num3; } private static void WriteLiteralRun(ReadOnlySpan rowSpan, int end, int literalRunLength, Span compressedRowSpan, int compressedRowPos) { int start = end - literalRunLength; sbyte b = (sbyte)(literalRunLength - 1); compressedRowSpan[compressedRowPos] = (byte)b; rowSpan.Slice(start, literalRunLength).CopyTo(compressedRowSpan.Slice(compressedRowPos + 1)); } private static void WriteRun(ReadOnlySpan rowSpan, int start, int runLength, Span compressedRowSpan, int compressedRowPos) { sbyte b = (sbyte)(-runLength + 1); compressedRowSpan[compressedRowPos] = (byte)b; compressedRowSpan[compressedRowPos + 1] = rowSpan[start]; } private static bool IsReplicateRun(ReadOnlySpan rowSpan, int startPos) { byte b = rowSpan[startPos]; int num = 0; for (int i = startPos + 1; i < rowSpan.Length && rowSpan[i] == b; i++) { num++; if (num >= 2) { return true; } } return false; } private static int FindRunLength(ReadOnlySpan rowSpan, int startPos, int maxRunLength) { byte b = rowSpan[startPos]; int num = 1; for (int i = startPos + 1; i < rowSpan.Length && rowSpan[i] == b; i++) { num++; if (num == maxRunLength) { break; } } return num; } } internal sealed class T4BitCompressor : TiffCcittCompressor { private readonly bool useModifiedHuffman; public override TiffCompression Method { get { if (!useModifiedHuffman) { return TiffCompression.CcittGroup3Fax; } return TiffCompression.Ccitt1D; } } public T4BitCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, bool useModifiedHuffman = false) : base(output, allocator, width, bitsPerPixel) { this.useModifiedHuffman = useModifiedHuffman; } protected override void CompressStrip(Span pixelsAsGray, int height, Span compressedData) { if (!useModifiedHuffman) { WriteCode(12u, 1u, compressedData); } for (int i = 0; i < height; i++) { bool flag = true; bool flag2 = true; int num = 0; Span span = pixelsAsGray.Slice(i * base.Width, base.Width); while (num < base.Width) { uint num2 = 0u; for (int j = num; j < base.Width && (!flag || span[j] == byte.MaxValue); j++) { if (flag && span[j] == byte.MaxValue) { num2++; continue; } if (!flag && span[j] != 0) { break; } if (!flag && span[j] == 0) { num2++; } } if (flag2 && num2 == 0) { WriteCode(8u, 53u, compressedData); flag = false; flag2 = false; continue; } uint codeLength; uint termCode; if (num2 <= 63) { termCode = GetTermCode(num2, out codeLength, flag); WriteCode(codeLength, termCode, compressedData); num += (int)num2; flag2 = false; flag = !flag; continue; } num2 = GetBestFittingMakeupRunLength(num2); termCode = GetMakeupCode(num2, out codeLength, flag); WriteCode(codeLength, termCode, compressedData); num += (int)num2; if (num == base.Width) { if (flag) { WriteCode(8u, 53u, compressedData); } else { WriteCode(10u, 55u, compressedData); } } } WriteEndOfLine(compressedData); } } private void WriteEndOfLine(Span compressedData) { if (useModifiedHuffman) { PadByte(); } else { WriteCode(12u, 1u, compressedData); } } } internal sealed class T6BitCompressor : TiffCcittCompressor { private static readonly (uint Length, uint Code)[] VerticalCodes = new(uint, uint)[7] { (7u, 3u), (6u, 3u), (3u, 3u), (1u, 1u), (3u, 2u), (6u, 2u), (7u, 2u) }; private IMemoryOwner referenceLineBuffer; public override TiffCompression Method => TiffCompression.CcittGroup4Fax; public T6BitCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel) : base(output, allocator, width, bitsPerPixel) { } protected override void CompressStrip(Span pixelsAsGray, int height, Span compressedData) { Span span = referenceLineBuffer.GetSpan(); span.Fill(byte.MaxValue); for (int i = 0; i < height; i++) { Span row = pixelsAsGray.Slice(i * base.Width, base.Width); uint num = 0u; uint num2 = ((row[0] != 0) ? FindRunEnd(row, 0u) : 0u); uint num3 = ((span[0] != 0) ? FindRunEnd(span, 0u) : 0u); while (true) { uint num4 = FindRunEnd(span, num3); if (num4 < num2) { WriteCode(4u, 1u, compressedData); num = num4; } else { int num5 = int.MaxValue; if (num3 >= num2 && num3 - num2 <= 3) { num5 = (int)(num3 - num2); } else if (num3 < num2 && num2 - num3 <= 3) { num5 = (int)(0 - (num2 - num3)); } if (num5 >= -3 && num5 <= 3) { var (codeLength, code) = VerticalCodes[num5 + 3]; WriteCode(codeLength, code, compressedData); num = num2; } else { WriteCode(3u, 1u, compressedData); uint num6 = FindRunEnd(row, num2); if (num + num2 == 0 || row[(int)num] != 0) { WriteRun(num2 - num, isWhiteRun: true, compressedData); WriteRun(num6 - num2, isWhiteRun: false, compressedData); } else { WriteRun(num2 - num, isWhiteRun: false, compressedData); WriteRun(num6 - num2, isWhiteRun: true, compressedData); } num = num6; } } if (num >= row.Length) { break; } byte b = row[(int)num]; num2 = FindRunEnd(row, num, b); num3 = FindRunEnd(span, num, (byte)(~b)); num3 = FindRunEnd(span, num3, b); } row.CopyTo(span); } WriteCode(12u, 1u, compressedData); WriteCode(12u, 1u, compressedData); } protected override void Dispose(bool disposing) { referenceLineBuffer?.Dispose(); base.Dispose(disposing); } private uint FindRunEnd(Span row, uint startIndex, byte? color = null) { if (startIndex >= row.Length) { return (uint)row.Length; } byte valueOrDefault = color.GetValueOrDefault(row[(int)startIndex]); for (int i = (int)startIndex; i < row.Length; i++) { if (row[i] != valueOrDefault) { return (uint)i; } } return (uint)row.Length; } public override void Initialize(int rowsPerStrip) { base.Initialize(rowsPerStrip); referenceLineBuffer = base.Allocator.Allocate(base.Width); } private void WriteRun(uint runLength, bool isWhiteRun, Span compressedData) { uint codeLength; uint makeupCode; while (runLength > 63) { uint bestFittingMakeupRunLength = GetBestFittingMakeupRunLength(runLength); makeupCode = GetMakeupCode(bestFittingMakeupRunLength, out codeLength, isWhiteRun); WriteCode(codeLength, makeupCode, compressedData); runLength -= bestFittingMakeupRunLength; } makeupCode = GetTermCode(runLength, out codeLength, isWhiteRun); WriteCode(codeLength, makeupCode, compressedData); } } internal abstract class TiffCcittCompressor : TiffBaseCompressor { protected const uint WhiteZeroRunTermCode = 53u; protected const uint BlackZeroRunTermCode = 55u; private static readonly uint[] MakeupRunLength = new uint[40] { 64u, 128u, 192u, 256u, 320u, 384u, 448u, 512u, 576u, 640u, 704u, 768u, 832u, 896u, 960u, 1024u, 1088u, 1152u, 1216u, 1280u, 1344u, 1408u, 1472u, 1536u, 1600u, 1664u, 1728u, 1792u, 1856u, 1920u, 1984u, 2048u, 2112u, 2176u, 2240u, 2304u, 2368u, 2432u, 2496u, 2560u }; private static readonly Dictionary WhiteLen4TermCodes = new Dictionary { { 2u, 7u }, { 3u, 8u }, { 4u, 11u }, { 5u, 12u }, { 6u, 14u }, { 7u, 15u } }; private static readonly Dictionary WhiteLen5TermCodes = new Dictionary { { 8u, 19u }, { 9u, 20u }, { 10u, 7u }, { 11u, 8u } }; private static readonly Dictionary WhiteLen6TermCodes = new Dictionary { { 1u, 7u }, { 12u, 8u }, { 13u, 3u }, { 14u, 52u }, { 15u, 53u }, { 16u, 42u }, { 17u, 43u } }; private static readonly Dictionary WhiteLen7TermCodes = new Dictionary { { 18u, 39u }, { 19u, 12u }, { 20u, 8u }, { 21u, 23u }, { 22u, 3u }, { 23u, 4u }, { 24u, 40u }, { 25u, 43u }, { 26u, 19u }, { 27u, 36u }, { 28u, 24u } }; private static readonly Dictionary WhiteLen8TermCodes = new Dictionary { { 0u, 53u }, { 29u, 2u }, { 30u, 3u }, { 31u, 26u }, { 32u, 27u }, { 33u, 18u }, { 34u, 19u }, { 35u, 20u }, { 36u, 21u }, { 37u, 22u }, { 38u, 23u }, { 39u, 40u }, { 40u, 41u }, { 41u, 42u }, { 42u, 43u }, { 43u, 44u }, { 44u, 45u }, { 45u, 4u }, { 46u, 5u }, { 47u, 10u }, { 48u, 11u }, { 49u, 82u }, { 50u, 83u }, { 51u, 84u }, { 52u, 85u }, { 53u, 36u }, { 54u, 37u }, { 55u, 88u }, { 56u, 89u }, { 57u, 90u }, { 58u, 91u }, { 59u, 74u }, { 60u, 75u }, { 61u, 50u }, { 62u, 51u }, { 63u, 52u } }; private static readonly Dictionary BlackLen2TermCodes = new Dictionary { { 2u, 3u }, { 3u, 2u } }; private static readonly Dictionary BlackLen3TermCodes = new Dictionary { { 1u, 2u }, { 4u, 3u } }; private static readonly Dictionary BlackLen4TermCodes = new Dictionary { { 5u, 3u }, { 6u, 2u } }; private static readonly Dictionary BlackLen5TermCodes = new Dictionary { { 7u, 3u } }; private static readonly Dictionary BlackLen6TermCodes = new Dictionary { { 8u, 5u }, { 9u, 4u } }; private static readonly Dictionary BlackLen7TermCodes = new Dictionary { { 10u, 4u }, { 11u, 5u }, { 12u, 7u } }; private static readonly Dictionary BlackLen8TermCodes = new Dictionary { { 13u, 4u }, { 14u, 7u } }; private static readonly Dictionary BlackLen9TermCodes = new Dictionary { { 15u, 24u } }; private static readonly Dictionary BlackLen10TermCodes = new Dictionary { { 0u, 55u }, { 16u, 23u }, { 17u, 24u }, { 18u, 8u } }; private static readonly Dictionary BlackLen11TermCodes = new Dictionary { { 19u, 103u }, { 20u, 104u }, { 21u, 108u }, { 22u, 55u }, { 23u, 40u }, { 24u, 23u }, { 25u, 24u } }; private static readonly Dictionary BlackLen12TermCodes = new Dictionary { { 26u, 202u }, { 27u, 203u }, { 28u, 204u }, { 29u, 205u }, { 30u, 104u }, { 31u, 105u }, { 32u, 106u }, { 33u, 107u }, { 34u, 210u }, { 35u, 211u }, { 36u, 212u }, { 37u, 213u }, { 38u, 214u }, { 39u, 215u }, { 40u, 108u }, { 41u, 109u }, { 42u, 218u }, { 43u, 219u }, { 44u, 84u }, { 45u, 85u }, { 46u, 86u }, { 47u, 87u }, { 48u, 100u }, { 49u, 101u }, { 50u, 82u }, { 51u, 83u }, { 52u, 36u }, { 53u, 55u }, { 54u, 56u }, { 55u, 39u }, { 56u, 40u }, { 57u, 88u }, { 58u, 89u }, { 59u, 43u }, { 60u, 44u }, { 61u, 90u }, { 62u, 102u }, { 63u, 103u } }; private static readonly Dictionary WhiteLen5MakeupCodes = new Dictionary { { 64u, 27u }, { 128u, 18u } }; private static readonly Dictionary WhiteLen6MakeupCodes = new Dictionary { { 192u, 23u }, { 1664u, 24u } }; private static readonly Dictionary WhiteLen8MakeupCodes = new Dictionary { { 320u, 54u }, { 384u, 55u }, { 448u, 100u }, { 512u, 101u }, { 576u, 104u }, { 640u, 103u } }; private static readonly Dictionary WhiteLen7MakeupCodes = new Dictionary { { 256u, 55u } }; private static readonly Dictionary WhiteLen9MakeupCodes = new Dictionary { { 704u, 204u }, { 768u, 205u }, { 832u, 210u }, { 896u, 211u }, { 960u, 212u }, { 1024u, 213u }, { 1088u, 214u }, { 1152u, 215u }, { 1216u, 216u }, { 1280u, 217u }, { 1344u, 218u }, { 1408u, 219u }, { 1472u, 152u }, { 1536u, 153u }, { 1600u, 154u }, { 1728u, 155u } }; private static readonly Dictionary WhiteLen11MakeupCodes = new Dictionary { { 1792u, 8u }, { 1856u, 12u }, { 1920u, 13u } }; private static readonly Dictionary WhiteLen12MakeupCodes = new Dictionary { { 1984u, 18u }, { 2048u, 19u }, { 2112u, 20u }, { 2176u, 21u }, { 2240u, 22u }, { 2304u, 23u }, { 2368u, 28u }, { 2432u, 29u }, { 2496u, 30u }, { 2560u, 31u } }; private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary { { 64u, 15u } }; private static readonly Dictionary BlackLen11MakeupCodes = new Dictionary { { 1792u, 8u }, { 1856u, 12u }, { 1920u, 13u } }; private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary { { 128u, 200u }, { 192u, 201u }, { 256u, 91u }, { 320u, 51u }, { 384u, 52u }, { 448u, 53u }, { 1984u, 18u }, { 2048u, 19u }, { 2112u, 20u }, { 2176u, 21u }, { 2240u, 22u }, { 2304u, 23u }, { 2368u, 28u }, { 2432u, 29u }, { 2496u, 30u }, { 2560u, 31u } }; private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary { { 512u, 108u }, { 576u, 109u }, { 640u, 74u }, { 704u, 75u }, { 768u, 76u }, { 832u, 77u }, { 896u, 114u }, { 960u, 115u }, { 1024u, 116u }, { 1088u, 117u }, { 1152u, 118u }, { 1216u, 119u }, { 1280u, 82u }, { 1344u, 83u }, { 1408u, 84u }, { 1472u, 85u }, { 1536u, 90u }, { 1600u, 91u }, { 1664u, 100u }, { 1728u, 101u } }; private int bytePosition; private byte bitPosition; private IMemoryOwner compressedDataBuffer; protected TiffCcittCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel) : base(output, allocator, width, bitsPerPixel) { bytePosition = 0; bitPosition = 0; } private uint GetWhiteMakeupCode(uint runLength, out uint codeLength) { codeLength = 0u; if (WhiteLen5MakeupCodes.ContainsKey(runLength)) { codeLength = 5u; return WhiteLen5MakeupCodes[runLength]; } if (WhiteLen6MakeupCodes.ContainsKey(runLength)) { codeLength = 6u; return WhiteLen6MakeupCodes[runLength]; } if (WhiteLen7MakeupCodes.ContainsKey(runLength)) { codeLength = 7u; return WhiteLen7MakeupCodes[runLength]; } if (WhiteLen8MakeupCodes.ContainsKey(runLength)) { codeLength = 8u; return WhiteLen8MakeupCodes[runLength]; } if (WhiteLen9MakeupCodes.ContainsKey(runLength)) { codeLength = 9u; return WhiteLen9MakeupCodes[runLength]; } if (WhiteLen11MakeupCodes.ContainsKey(runLength)) { codeLength = 11u; return WhiteLen11MakeupCodes[runLength]; } if (WhiteLen12MakeupCodes.ContainsKey(runLength)) { codeLength = 12u; return WhiteLen12MakeupCodes[runLength]; } return 0u; } private uint GetBlackMakeupCode(uint runLength, out uint codeLength) { codeLength = 0u; if (BlackLen10MakeupCodes.ContainsKey(runLength)) { codeLength = 10u; return BlackLen10MakeupCodes[runLength]; } if (BlackLen11MakeupCodes.ContainsKey(runLength)) { codeLength = 11u; return BlackLen11MakeupCodes[runLength]; } if (BlackLen12MakeupCodes.ContainsKey(runLength)) { codeLength = 12u; return BlackLen12MakeupCodes[runLength]; } if (BlackLen13MakeupCodes.ContainsKey(runLength)) { codeLength = 13u; return BlackLen13MakeupCodes[runLength]; } return 0u; } private uint GetWhiteTermCode(uint runLength, out uint codeLength) { codeLength = 0u; if (WhiteLen4TermCodes.ContainsKey(runLength)) { codeLength = 4u; return WhiteLen4TermCodes[runLength]; } if (WhiteLen5TermCodes.ContainsKey(runLength)) { codeLength = 5u; return WhiteLen5TermCodes[runLength]; } if (WhiteLen6TermCodes.ContainsKey(runLength)) { codeLength = 6u; return WhiteLen6TermCodes[runLength]; } if (WhiteLen7TermCodes.ContainsKey(runLength)) { codeLength = 7u; return WhiteLen7TermCodes[runLength]; } if (WhiteLen8TermCodes.ContainsKey(runLength)) { codeLength = 8u; return WhiteLen8TermCodes[runLength]; } return 0u; } private uint GetBlackTermCode(uint runLength, out uint codeLength) { codeLength = 0u; if (BlackLen2TermCodes.ContainsKey(runLength)) { codeLength = 2u; return BlackLen2TermCodes[runLength]; } if (BlackLen3TermCodes.ContainsKey(runLength)) { codeLength = 3u; return BlackLen3TermCodes[runLength]; } if (BlackLen4TermCodes.ContainsKey(runLength)) { codeLength = 4u; return BlackLen4TermCodes[runLength]; } if (BlackLen5TermCodes.ContainsKey(runLength)) { codeLength = 5u; return BlackLen5TermCodes[runLength]; } if (BlackLen6TermCodes.ContainsKey(runLength)) { codeLength = 6u; return BlackLen6TermCodes[runLength]; } if (BlackLen7TermCodes.ContainsKey(runLength)) { codeLength = 7u; return BlackLen7TermCodes[runLength]; } if (BlackLen8TermCodes.ContainsKey(runLength)) { codeLength = 8u; return BlackLen8TermCodes[runLength]; } if (BlackLen9TermCodes.ContainsKey(runLength)) { codeLength = 9u; return BlackLen9TermCodes[runLength]; } if (BlackLen10TermCodes.ContainsKey(runLength)) { codeLength = 10u; return BlackLen10TermCodes[runLength]; } if (BlackLen11TermCodes.ContainsKey(runLength)) { codeLength = 11u; return BlackLen11TermCodes[runLength]; } if (BlackLen12TermCodes.ContainsKey(runLength)) { codeLength = 12u; return BlackLen12TermCodes[runLength]; } return 0u; } protected uint GetBestFittingMakeupRunLength(uint runLength) { for (int i = 0; i < MakeupRunLength.Length - 1; i++) { if (MakeupRunLength[i] <= runLength && MakeupRunLength[i + 1] > runLength) { return MakeupRunLength[i]; } } return MakeupRunLength[MakeupRunLength.Length - 1]; } protected uint GetTermCode(uint runLength, out uint codeLength, bool isWhiteRun) { if (isWhiteRun) { return GetWhiteTermCode(runLength, out codeLength); } return GetBlackTermCode(runLength, out codeLength); } protected uint GetMakeupCode(uint runLength, out uint codeLength, bool isWhiteRun) { if (isWhiteRun) { return GetWhiteMakeupCode(runLength, out codeLength); } return GetBlackMakeupCode(runLength, out codeLength); } protected void PadByte() { if (bitPosition % 8 != 0) { bytePosition++; bitPosition = 0; } } protected void WriteCode(uint codeLength, uint code, Span compressedData) { while (codeLength != 0) { int num = (int)codeLength; if ((code & (1 << num - 1)) != 0) { BitWriterUtils.WriteBit(compressedData, bytePosition, bitPosition); } else { BitWriterUtils.WriteZeroBit(compressedData, bytePosition, bitPosition); } bitPosition++; if (bitPosition == 8) { bytePosition++; bitPosition = 0; } codeLength--; } } public override void CompressStrip(Span pixelsAsGray, int height) { compressedDataBuffer.Clear(); Span span = compressedDataBuffer.GetSpan(); bytePosition = 0; bitPosition = 0; CompressStrip(pixelsAsGray, height, span); int num = ((bitPosition != 0) ? (bytePosition + 1) : bytePosition); base.Output.Write(span[..num]); } protected abstract void CompressStrip(Span pixelsAsGray, int height, Span compressedData); protected override void Dispose(bool disposing) { compressedDataBuffer?.Dispose(); } public override void Initialize(int rowsPerStrip) { int length = base.Width * rowsPerStrip; compressedDataBuffer = base.Allocator.Allocate(length); } } internal class TiffJpegCompressor : TiffBaseCompressor { public override TiffCompression Method => TiffCompression.Jpeg; public TiffJpegCompressor(Stream output, MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) : base(output, memoryAllocator, width, bitsPerPixel, predictor) { } public override void Initialize(int rowsPerStrip) { } public override void CompressStrip(Span rows, int height) { int width = rows.Length / 3 / height; using MemoryStream memoryStream = new MemoryStream(); Image.LoadPixelData(rows, width, height).Save(memoryStream, new JpegEncoder { ColorType = JpegColorType.Rgb }); memoryStream.Position = 0L; memoryStream.WriteTo(base.Output); } protected override void Dispose(bool disposing) { } } internal sealed class TiffLzwEncoder : IDisposable { private static readonly int ClearCode = 256; private static readonly int EoiCode = 257; private static readonly int MinBits = 9; private static readonly int MaxBits = 12; private static readonly int TableSize = 1 << MaxBits; private readonly IMemoryOwner children; private readonly IMemoryOwner siblings; private readonly IMemoryOwner suffixes; private int parent; private int bitsPerCode; private int nextValidCode; private int maxCode; private int bits; private int bitPos; private int bufferPosition; public TiffLzwEncoder(MemoryAllocator memoryAllocator) { children = memoryAllocator.Allocate(TableSize); siblings = memoryAllocator.Allocate(TableSize); suffixes = memoryAllocator.Allocate(TableSize); } public void Encode(Span data, Stream stream) { Reset(); Span span = children.GetSpan(); Span span2 = suffixes.GetSpan(); Span span3 = siblings.GetSpan(); if (data.Length == 0) { return; } if (parent == -1) { WriteCode(stream, ClearCode); parent = ReadNextByte(data); } while (bufferPosition < data.Length) { int num = ReadNextByte(data); int num2 = span[parent]; if (num2 > 0) { if (span2[num2] == num) { parent = num2; continue; } int index = num2; while (true) { if (span3[index] > 0) { index = span3[index]; if (span2[index] == num) { parent = index; break; } continue; } span3[index] = (short)nextValidCode; span2[nextValidCode] = (short)num; WriteCode(stream, parent); parent = num; nextValidCode++; IncreaseCodeSizeOrResetIfNeeded(stream); break; } } else { span[parent] = (short)nextValidCode; span2[nextValidCode] = (short)num; WriteCode(stream, parent); parent = num; nextValidCode++; IncreaseCodeSizeOrResetIfNeeded(stream); } } WriteCode(stream, parent); WriteCode(stream, EoiCode); if (bitPos > 0) { WriteCode(stream, 0); } } public void Dispose() { children.Dispose(); siblings.Dispose(); suffixes.Dispose(); } private void Reset() { children.Clear(); siblings.Clear(); suffixes.Clear(); parent = -1; bitsPerCode = MinBits; nextValidCode = EoiCode + 1; maxCode = (1 << bitsPerCode) - 1; bits = 0; bitPos = 0; bufferPosition = 0; } private byte ReadNextByte(Span data) { return data[bufferPosition++]; } private void IncreaseCodeSizeOrResetIfNeeded(Stream stream) { if (nextValidCode > maxCode) { if (bitsPerCode == MaxBits) { WriteCode(stream, ClearCode); ResetTables(); } else { bitsPerCode++; maxCode = MaxValue(bitsPerCode); } } } private void WriteCode(Stream stream, int code) { bits = (bits << bitsPerCode) | (code & maxCode); bitPos += bitsPerCode; while (bitPos >= 8) { int num = (bits >> bitPos - 8) & 0xFF; stream.WriteByte((byte)num); bitPos -= 8; } bits &= BitmaskFor(bitPos); } private void ResetTables() { children.GetSpan().Clear(); siblings.GetSpan().Clear(); bitsPerCode = MinBits; maxCode = MaxValue(bitsPerCode); nextValidCode = EoiCode + 1; } private static int MaxValue(int codeLen) { return (1 << codeLen) - 1; } private static int BitmaskFor(int bits) { return MaxValue(bits); } } } namespace SixLabors.ImageSharp.Formats.Tga { internal interface ITgaDecoderOptions { } internal interface ITgaEncoderOptions { TgaBitsPerPixel? BitsPerPixel { get; } TgaCompression Compression { get; } } public enum TgaBitsPerPixel : byte { Pixel8 = 8, Pixel16 = 16, Pixel24 = 24, Pixel32 = 32 } public enum TgaCompression { None, RunLength } public sealed class TgaConfigurationModule : IConfigurationModule { public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(TgaFormat.Instance, new TgaEncoder()); configuration.ImageFormatsManager.SetDecoder(TgaFormat.Instance, new TgaDecoder()); configuration.ImageFormatsManager.AddImageFormatDetector(new TgaImageFormatDetector()); } } internal static class TgaConstants { public static readonly IEnumerable MimeTypes = new string[2] { "image/x-tga", "image/x-targa" }; public static readonly IEnumerable FileExtensions = new string[4] { "tga", "vda", "icb", "vst" }; public const int FileHeaderLength = 18; } public sealed class TgaDecoder : IImageDecoder, ITgaDecoderOptions, IImageInfoDetector { public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(stream, "stream"); return new TgaDecoderCore(configuration, this).Decode(configuration, stream, cancellationToken); } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) { return Decode(configuration, stream, cancellationToken); } public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, "stream"); return new TgaDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); } } internal sealed class TgaDecoderCore : IImageDecoderInternals { private readonly byte[] scratchBuffer = new byte[4]; private ImageMetadata metadata; private TgaMetadata tgaMetadata; private TgaFileHeader fileHeader; private readonly MemoryAllocator memoryAllocator; private BufferedReadStream currentStream; private readonly ITgaDecoderOptions options; private bool hasAlpha; public Configuration Configuration { get; } public Size Dimensions => new Size(fileHeader.Width, fileHeader.Height); public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options) { Configuration = configuration; memoryAllocator = configuration.MemoryAllocator; this.options = options; } public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { try { TgaImageOrigin origin = ReadFileHeader(stream); currentStream.Skip(fileHeader.IdLength); if (fileHeader.ColorMapType != 0 && fileHeader.ColorMapType != 1) { TgaThrowHelper.ThrowNotSupportedException($"Unknown tga colormap type {fileHeader.ColorMapType} found"); } if (fileHeader.Width == 0 || fileHeader.Height == 0) { throw new UnknownImageFormatException("Width or height cannot be 0"); } Image image = new Image(Configuration, fileHeader.Width, fileHeader.Height, metadata); Buffer2D rootFramePixelBuffer = image.GetRootFramePixelBuffer(); if (fileHeader.ColorMapType == 1) { if (fileHeader.CMapLength <= 0) { TgaThrowHelper.ThrowInvalidImageContentException("Missing tga color map length"); } if (fileHeader.CMapDepth <= 0) { TgaThrowHelper.ThrowInvalidImageContentException("Missing tga color map depth"); } int num = fileHeader.CMapDepth / 8; int num2 = fileHeader.CMapLength * num; using (IMemoryOwner buffer = memoryAllocator.Allocate(num2, AllocationOptions.Clean)) { Span span = buffer.GetSpan(); currentStream.Read(span, fileHeader.CMapStart, num2); if (fileHeader.ImageType == TgaImageType.RleColorMapped) { ReadPalettedRle(fileHeader.Width, fileHeader.Height, rootFramePixelBuffer, span, num, origin); } else { ReadPaletted(fileHeader.Width, fileHeader.Height, rootFramePixelBuffer, span, num, origin); } } return image; } if (fileHeader.CMapLength > 0) { int num3 = fileHeader.CMapDepth / 8; currentStream.Skip(fileHeader.CMapLength * num3); } switch (fileHeader.PixelDepth) { case 8: if (fileHeader.ImageType.IsRunLengthEncoded()) { ReadRle(fileHeader.Width, fileHeader.Height, rootFramePixelBuffer, 1, origin); } else { ReadMonoChrome(fileHeader.Width, fileHeader.Height, rootFramePixelBuffer, origin); } break; case 15: case 16: if (fileHeader.ImageType.IsRunLengthEncoded()) { ReadRle(fileHeader.Width, fileHeader.Height, rootFramePixelBuffer, 2, origin); } else { ReadBgra16(fileHeader.Width, fileHeader.Height, rootFramePixelBuffer, origin); } break; case 24: if (fileHeader.ImageType.IsRunLengthEncoded()) { ReadRle(fileHeader.Width, fileHeader.Height, rootFramePixelBuffer, 3, origin); } else { ReadBgr24(fileHeader.Width, fileHeader.Height, rootFramePixelBuffer, origin); } break; case 32: if (fileHeader.ImageType.IsRunLengthEncoded()) { ReadRle(fileHeader.Width, fileHeader.Height, rootFramePixelBuffer, 4, origin); } else { ReadBgra32(fileHeader.Width, fileHeader.Height, rootFramePixelBuffer, origin); } break; default: TgaThrowHelper.ThrowNotSupportedException("ImageSharp does not support this kind of tga files."); break; } return image; } catch (IndexOutOfRangeException innerException) { throw new ImageFormatException("TGA image does not have a valid format.", innerException); } } private void ReadPaletted(int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); bool flag = InvertX(origin); for (int i = 0; i < height; i++) { int y = InvertY(i, height, origin); Span pixelRow = pixels.DangerousGetRowSpan(y); switch (colorMapPixelSizeInBytes) { case 2: if (flag) { for (int num2 = width - 1; num2 >= 0; num2--) { ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, num2, color, pixelRow); } } else { for (int k = 0; k < width; k++) { ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, k, color, pixelRow); } } break; case 3: if (flag) { for (int num3 = width - 1; num3 >= 0; num3--) { ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, num3, color, pixelRow); } } else { for (int l = 0; l < width; l++) { ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, l, color, pixelRow); } } break; case 4: if (flag) { for (int num = width - 1; num >= 0; num--) { ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, num, color, pixelRow); } } else { for (int j = 0; j < width; j++) { ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, j, color, pixelRow); } } break; } } } private void ReadPalettedRle(int width, int height, Buffer2D pixels, Span palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { int num = 1; using IMemoryOwner buffer = memoryAllocator.Allocate(width * height * num, AllocationOptions.Clean); TPixel color = default(TPixel); Span span = buffer.GetSpan(); UncompressRle(width, height, span, 1); for (int i = 0; i < height; i++) { int y = InvertY(i, height, origin); Span span2 = pixels.DangerousGetRowSpan(y); int num2 = i * width * num; for (int j = 0; j < width; j++) { int index = num2 + j; switch (colorMapPixelSizeInBytes) { case 1: color.FromL8(Unsafe.As(ref palette[span[index] * colorMapPixelSizeInBytes])); break; case 2: ReadPalettedBgra16Pixel(palette, span[index], colorMapPixelSizeInBytes, ref color); break; case 3: color.FromBgr24(Unsafe.As(ref palette[span[index] * colorMapPixelSizeInBytes])); break; case 4: color.FromBgra32(Unsafe.As(ref palette[span[index] * colorMapPixelSizeInBytes])); break; } int index2 = InvertX(j, width, origin); span2[index2] = color; } } } private void ReadMonoChrome(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { if (InvertX(origin)) { TPixel color = default(TPixel); for (int i = 0; i < height; i++) { int y = InvertY(i, height, origin); Span pixelSpan = pixels.DangerousGetRowSpan(y); for (int num = width - 1; num >= 0; num--) { ReadL8Pixel(color, num, pixelSpan); } } return; } using IMemoryOwner buffer = memoryAllocator.AllocatePaddedPixelRowBuffer(width, 1, 0); Span span = buffer.GetSpan(); if (InvertY(origin)) { for (int num2 = height - 1; num2 >= 0; num2--) { ReadL8Row(width, pixels, span, num2); } } else { for (int j = 0; j < height; j++) { ReadL8Row(width, pixels, span, j); } } } private void ReadBgra16(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); bool flag = InvertX(origin); using IMemoryOwner buffer = memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { int y = InvertY(i, height, origin); Span destinationPixels = pixels.DangerousGetRowSpan(y); if (flag) { for (int num = width - 1; num >= 0; num--) { currentStream.Read(scratchBuffer, 0, 2); if (!hasAlpha) { scratchBuffer[1] |= 128; } if (fileHeader.ImageType == TgaImageType.BlackAndWhite) { val.FromLa16(Unsafe.As(ref scratchBuffer[0])); } else { val.FromBgra5551(Unsafe.As(ref scratchBuffer[0])); } destinationPixels[num] = val; } continue; } currentStream.Read(span); if (!hasAlpha) { for (int j = 1; j < span.Length; j += 2) { span[j] |= 128; } } if (fileHeader.ImageType == TgaImageType.BlackAndWhite) { PixelOperations.Instance.FromLa16Bytes(Configuration, span, destinationPixels, width); } else { PixelOperations.Instance.FromBgra5551Bytes(Configuration, span, destinationPixels, width); } } } private void ReadBgr24(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { if (InvertX(origin)) { TPixel color = default(TPixel); for (int i = 0; i < height; i++) { int y = InvertY(i, height, origin); Span pixelSpan = pixels.DangerousGetRowSpan(y); for (int num = width - 1; num >= 0; num--) { ReadBgr24Pixel(color, num, pixelSpan); } } return; } using IMemoryOwner buffer = memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, 0); Span span = buffer.GetSpan(); if (InvertY(origin)) { for (int num2 = height - 1; num2 >= 0; num2--) { ReadBgr24Row(width, pixels, span, num2); } } else { for (int j = 0; j < height; j++) { ReadBgr24Row(width, pixels, span, j); } } } private void ReadBgra32(int width, int height, Buffer2D pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { TPixel color = default(TPixel); bool flag = InvertX(origin); if (tgaMetadata.AlphaChannelBits == 8 && !flag) { using (IMemoryOwner buffer = memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0)) { Span span = buffer.GetSpan(); if (InvertY(origin)) { for (int num = height - 1; num >= 0; num--) { ReadBgra32Row(width, pixels, span, num); } } else { for (int i = 0; i < height; i++) { ReadBgra32Row(width, pixels, span, i); } } return; } } for (int j = 0; j < height; j++) { int y = InvertY(j, height, origin); Span pixelRow = pixels.DangerousGetRowSpan(y); if (flag) { for (int num2 = width - 1; num2 >= 0; num2--) { ReadBgra32Pixel(num2, color, pixelRow); } } else { for (int k = 0; k < width; k++) { ReadBgra32Pixel(k, color, pixelRow); } } } } private void ReadRle(int width, int height, Buffer2D pixels, int bytesPerPixel, TgaImageOrigin origin) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); byte alphaChannelBits = tgaMetadata.AlphaChannelBits; using IMemoryOwner buffer = memoryAllocator.Allocate(width * height * bytesPerPixel, AllocationOptions.Clean); Span span = buffer.GetSpan(); UncompressRle(width, height, span, bytesPerPixel); for (int i = 0; i < height; i++) { int y = InvertY(i, height, origin); Span span2 = pixels.DangerousGetRowSpan(y); int num = i * width * bytesPerPixel; for (int j = 0; j < width; j++) { int num2 = num + j * bytesPerPixel; switch (bytesPerPixel) { case 1: val.FromL8(Unsafe.As(ref span[num2])); break; case 2: if (!hasAlpha) { span[num2 + 1] = (byte)(span[num2 + 1] | 0x80); } if (fileHeader.ImageType == TgaImageType.RleBlackAndWhite) { val.FromLa16(Unsafe.As(ref span[num2])); } else { val.FromBgra5551(Unsafe.As(ref span[num2])); } break; case 3: val.FromBgr24(Unsafe.As(ref span[num2])); break; case 4: { if (hasAlpha) { val.FromBgra32(Unsafe.As(ref span[num2])); break; } byte a = ((alphaChannelBits == 0) ? byte.MaxValue : span[num2 + 3]); val.FromBgra32(new Bgra32(span[num2 + 2], span[num2 + 1], span[num2], a)); break; } } int index = InvertX(j, width, origin); span2[index] = val; } } } public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { ReadFileHeader(stream); return new ImageInfo(new PixelTypeInfo(fileHeader.PixelDepth), fileHeader.Width, fileHeader.Height, metadata); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadL8Row(int width, Buffer2D pixels, Span row, int y) where TPixel : unmanaged, IPixel { currentStream.Read(row); Span destinationPixels = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.FromL8Bytes(Configuration, row, destinationPixels, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadL8Pixel(TPixel color, int x, Span pixelSpan) where TPixel : unmanaged, IPixel { byte source = (byte)currentStream.ReadByte(); color.FromL8(Unsafe.As(ref source)); pixelSpan[x] = color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadBgr24Pixel(TPixel color, int x, Span pixelSpan) where TPixel : unmanaged, IPixel { currentStream.Read(scratchBuffer, 0, 3); color.FromBgr24(Unsafe.As(ref scratchBuffer[0])); pixelSpan[x] = color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadBgr24Row(int width, Buffer2D pixels, Span row, int y) where TPixel : unmanaged, IPixel { currentStream.Read(row); Span destinationPixels = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.FromBgr24Bytes(Configuration, row, destinationPixels, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadBgra32Pixel(int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { currentStream.Read(scratchBuffer, 0, 4); byte a = ((tgaMetadata.AlphaChannelBits == 0) ? byte.MaxValue : scratchBuffer[3]); color.FromBgra32(new Bgra32(scratchBuffer[2], scratchBuffer[1], scratchBuffer[0], a)); pixelRow[x] = color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadBgra32Row(int width, Buffer2D pixels, Span row, int y) where TPixel : unmanaged, IPixel { currentStream.Read(row); Span destinationPixels = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.FromBgra32Bytes(Configuration, row, destinationPixels, width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadPalettedBgra16Pixel(Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { int index = currentStream.ReadByte(); ReadPalettedBgra16Pixel(palette, index, colorMapPixelSizeInBytes, ref color); pixelRow[x] = color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadPalettedBgra16Pixel(Span palette, int index, int colorMapPixelSizeInBytes, ref TPixel color) where TPixel : unmanaged, IPixel { Bgra5551 source = default(Bgra5551); source.FromBgra5551(Unsafe.As(ref palette[index * colorMapPixelSizeInBytes])); if (!hasAlpha) { source.PackedValue |= 32768; } color.FromBgra5551(source); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadPalettedBgr24Pixel(Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { int num = currentStream.ReadByte(); color.FromBgr24(Unsafe.As(ref palette[num * colorMapPixelSizeInBytes])); pixelRow[x] = color; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadPalettedBgra32Pixel(Span palette, int colorMapPixelSizeInBytes, int x, TPixel color, Span pixelRow) where TPixel : unmanaged, IPixel { int num = currentStream.ReadByte(); color.FromBgra32(Unsafe.As(ref palette[num * colorMapPixelSizeInBytes])); pixelRow[x] = color; } private void UncompressRle(int width, int height, Span buffer, int bytesPerPixel) { int num = 0; byte[] array = new byte[bytesPerPixel]; int num2 = width * height; while (num < num2) { byte b = (byte)currentStream.ReadByte(); if (b >> 7 == 1) { int num3 = b & 0x7F; currentStream.Read(array, 0, bytesPerPixel); int num4 = num * bytesPerPixel; int num5 = 0; while (num5 < num3 + 1) { array.AsSpan().CopyTo(buffer.Slice(num4)); num4 += bytesPerPixel; num5++; num++; } } else { int num6 = b; int num7 = num * bytesPerPixel; int num8 = 0; while (num8 < num6 + 1) { currentStream.Read(array, 0, bytesPerPixel); array.AsSpan().CopyTo(buffer.Slice(num7)); num7 += bytesPerPixel; num8++; num++; } } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int InvertY(int y, int height, TgaImageOrigin origin) { if (InvertY(origin)) { return height - y - 1; } return y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool InvertY(TgaImageOrigin origin) { if ((uint)origin <= 1u) { return true; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int InvertX(int x, int width, TgaImageOrigin origin) { if (InvertX(origin)) { return width - x - 1; } return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool InvertX(TgaImageOrigin origin) { if (origin == TgaImageOrigin.BottomRight || origin == TgaImageOrigin.TopRight) { return true; } return false; } private TgaImageOrigin ReadFileHeader(BufferedReadStream stream) { currentStream = stream; Span span = stackalloc byte[18]; currentStream.Read(span, 0, 18); fileHeader = TgaFileHeader.Parse(span); metadata = new ImageMetadata(); tgaMetadata = metadata.GetTgaMetadata(); tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)fileHeader.PixelDepth; int num = fileHeader.ImageDescriptor & 0xF; if (num != 0 && num != 1 && num != 8) { TgaThrowHelper.ThrowInvalidImageContentException("Invalid alpha channel bits"); } tgaMetadata.AlphaChannelBits = (byte)num; hasAlpha = num > 0; return (TgaImageOrigin)((fileHeader.ImageDescriptor & 0x30) >> 4); } } public sealed class TgaEncoder : IImageEncoder, ITgaEncoderOptions { public TgaBitsPerPixel? BitsPerPixel { get; set; } public TgaCompression Compression { get; set; } = TgaCompression.RunLength; public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { new TgaEncoderCore(this, image.GetMemoryAllocator()).Encode(image, stream); } public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return new TgaEncoderCore(this, image.GetMemoryAllocator()).EncodeAsync(image, stream, cancellationToken); } } internal sealed class TgaEncoderCore : IImageEncoderInternals { private readonly MemoryAllocator memoryAllocator; private Configuration configuration; private readonly byte[] buffer = new byte[2]; private TgaBitsPerPixel? bitsPerPixel; private readonly TgaCompression compression; public TgaEncoderCore(ITgaEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; bitsPerPixel = options.BitsPerPixel; compression = options.Compression; } public void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); configuration = image.GetConfiguration(); TgaMetadata tgaMetadata = image.Metadata.GetTgaMetadata(); bitsPerPixel = bitsPerPixel ?? tgaMetadata.BitsPerPixel; TgaImageType imageType = ((compression == TgaCompression.RunLength) ? TgaImageType.RleTrueColor : TgaImageType.TrueColor); if (bitsPerPixel == TgaBitsPerPixel.Pixel8) { imageType = ((compression == TgaCompression.RunLength) ? TgaImageType.RleBlackAndWhite : TgaImageType.BlackAndWhite); } byte b = 0; if (compression == TgaCompression.RunLength) { b |= 0x20; } TgaBitsPerPixel? tgaBitsPerPixel = bitsPerPixel; if (tgaBitsPerPixel.HasValue && tgaBitsPerPixel == TgaBitsPerPixel.Pixel32) { b |= 8; } tgaBitsPerPixel = bitsPerPixel; if (tgaBitsPerPixel.HasValue && tgaBitsPerPixel == TgaBitsPerPixel.Pixel16) { b |= 1; } TgaFileHeader tgaFileHeader = new TgaFileHeader(0, 0, imageType, 0, 0, 0, 0, (short)((compression == TgaCompression.RunLength) ? ((short)image.Height) : 0), (short)image.Width, (short)image.Height, (byte)bitsPerPixel.Value, b); Span span = stackalloc byte[18]; tgaFileHeader.WriteTo(span); stream.Write(span, 0, 18); if (compression == TgaCompression.RunLength) { WriteRunLengthEncodedImage(stream, image.Frames.RootFrame); } else { WriteImage(stream, image.Frames.RootFrame); } stream.Flush(); } private void WriteImage(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { Buffer2D pixelBuffer = image.PixelBuffer; TgaBitsPerPixel? tgaBitsPerPixel = bitsPerPixel; if (tgaBitsPerPixel.HasValue) { switch (tgaBitsPerPixel.GetValueOrDefault()) { case TgaBitsPerPixel.Pixel8: Write8Bit(stream, pixelBuffer); break; case TgaBitsPerPixel.Pixel16: Write16Bit(stream, pixelBuffer); break; case TgaBitsPerPixel.Pixel24: Write24Bit(stream, pixelBuffer); break; case TgaBitsPerPixel.Pixel32: Write32Bit(stream, pixelBuffer); break; } } } private void WriteRunLengthEncodedImage(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { Rgba32 dest = default(Rgba32); Buffer2D pixelBuffer = image.PixelBuffer; int num = image.Width * image.Height; byte b; for (int i = 0; i < num; i += b + 1) { int num2 = i % pixelBuffer.Width; int num3 = i / pixelBuffer.Width; TPixel sourcePixel = pixelBuffer[num2, num3]; sourcePixel.ToRgba32(ref dest); b = FindEqualPixels(pixelBuffer, num2, num3); stream.WriteByte((byte)(b | 0x80)); switch (bitsPerPixel) { case TgaBitsPerPixel.Pixel8: { int luminance = GetLuminance(sourcePixel); stream.WriteByte((byte)luminance); break; } case TgaBitsPerPixel.Pixel16: { Bgra5551 bgra = new Bgra5551(dest.ToVector4()); BinaryPrimitives.TryWriteInt16LittleEndian(buffer, (short)bgra.PackedValue); stream.WriteByte(buffer[0]); stream.WriteByte(buffer[1]); break; } case TgaBitsPerPixel.Pixel24: stream.WriteByte(dest.B); stream.WriteByte(dest.G); stream.WriteByte(dest.R); break; case TgaBitsPerPixel.Pixel32: stream.WriteByte(dest.B); stream.WriteByte(dest.G); stream.WriteByte(dest.R); stream.WriteByte(dest.A); break; } } } private byte FindEqualPixels(Buffer2D pixels, int xStart, int yStart) where TPixel : unmanaged, IPixel { byte b = 0; bool flag = true; TPixel val = pixels[xStart, yStart]; for (int i = yStart; i < pixels.Height; i++) { for (int j = (flag ? (xStart + 1) : 0); j < pixels.Width; j++) { TPixel other = pixels[j, i]; if (val.Equals(other)) { b++; if (b >= 127) { return b; } continue; } return b; } flag = false; } return b; } private IMemoryOwner AllocateRow(int width, int bytesPerPixel) { return memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, 0); } private void Write8Bit(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner memoryOwner = AllocateRow(pixels.Width, 1); Span span = memoryOwner.GetSpan(); for (int num = pixels.Height - 1; num >= 0; num--) { Span span2 = pixels.DangerousGetRowSpan(num); PixelOperations.Instance.ToL8Bytes(configuration, span2, span, span2.Length); stream.Write(span); } } private void Write16Bit(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner memoryOwner = AllocateRow(pixels.Width, 2); Span span = memoryOwner.GetSpan(); for (int num = pixels.Height - 1; num >= 0; num--) { Span span2 = pixels.DangerousGetRowSpan(num); PixelOperations.Instance.ToBgra5551Bytes(configuration, span2, span, span2.Length); stream.Write(span); } } private void Write24Bit(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner memoryOwner = AllocateRow(pixels.Width, 3); Span span = memoryOwner.GetSpan(); for (int num = pixels.Height - 1; num >= 0; num--) { Span span2 = pixels.DangerousGetRowSpan(num); PixelOperations.Instance.ToBgr24Bytes(configuration, span2, span, span2.Length); stream.Write(span); } } private void Write32Bit(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner memoryOwner = AllocateRow(pixels.Width, 4); Span span = memoryOwner.GetSpan(); for (int num = pixels.Height - 1; num >= 0; num--) { Span span2 = pixels.DangerousGetRowSpan(num); PixelOperations.Instance.ToBgra32Bytes(configuration, span2, span, span2.Length); stream.Write(span); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetLuminance(TPixel sourcePixel) where TPixel : unmanaged, IPixel { Vector4 vector = sourcePixel.ToVector4(); return ColorNumerics.GetBT709Luminance(ref vector, 256); } } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct TgaFileHeader { public const int Size = 18; public byte IdLength { get; } public byte ColorMapType { get; } public TgaImageType ImageType { get; } public short CMapStart { get; } public short CMapLength { get; } public byte CMapDepth { get; } public short XOffset { get; } public short YOffset { get; } public short Width { get; } public short Height { get; } public byte PixelDepth { get; } public byte ImageDescriptor { get; } public TgaFileHeader(byte idLength, byte colorMapType, TgaImageType imageType, short cMapStart, short cMapLength, byte cMapDepth, short xOffset, short yOffset, short width, short height, byte pixelDepth, byte imageDescriptor) { IdLength = idLength; ColorMapType = colorMapType; ImageType = imageType; CMapStart = cMapStart; CMapLength = cMapLength; CMapDepth = cMapDepth; XOffset = xOffset; YOffset = yOffset; Width = width; Height = height; PixelDepth = pixelDepth; ImageDescriptor = imageDescriptor; } public static TgaFileHeader Parse(Span data) { return MemoryMarshal.Cast(data)[0]; } public void WriteTo(Span buffer) { Unsafe.As(ref MemoryMarshal.GetReference(buffer)) = this; } } public sealed class TgaFormat : IImageFormat, IImageFormat { public static TgaFormat Instance { get; } = new TgaFormat(); public string Name => "TGA"; public string DefaultMimeType => "image/tga"; public IEnumerable MimeTypes => TgaConstants.MimeTypes; public IEnumerable FileExtensions => TgaConstants.FileExtensions; public TgaMetadata CreateDefaultFormatMetadata() { return new TgaMetadata(); } } public sealed class TgaImageFormatDetector : IImageFormatDetector { public int HeaderSize => 16; public IImageFormat DetectFormat(ReadOnlySpan header) { if (!IsSupportedFileFormat(header)) { return null; } return TgaFormat.Instance; } private bool IsSupportedFileFormat(ReadOnlySpan header) { if (header.Length >= HeaderSize) { if (header[1] != 0 && header[1] != 1) { return false; } if (!((TgaImageType)header[2]).IsValid()) { return false; } if (header[1] == 0 && (header[3] != 0 || header[4] != 0 || header[5] != 0 || header[6] != 0 || header[7] != 0)) { return false; } if ((header[12] == 0 && header[13] == 0) || (header[14] == 0 && header[15] == 0)) { return false; } return true; } return false; } } internal enum TgaImageOrigin { BottomLeft, BottomRight, TopLeft, TopRight } public enum TgaImageType : byte { NoImageData = 0, ColorMapped = 1, TrueColor = 2, BlackAndWhite = 3, RleColorMapped = 9, RleTrueColor = 10, RleBlackAndWhite = 11 } public static class TgaImageTypeExtensions { public static bool IsRunLengthEncoded(this TgaImageType imageType) { if (imageType == TgaImageType.RleColorMapped || imageType == TgaImageType.RleBlackAndWhite || imageType == TgaImageType.RleTrueColor) { return true; } return false; } public static bool IsValid(this TgaImageType imageType) { if (imageType <= TgaImageType.BlackAndWhite || imageType - 9 <= TgaImageType.TrueColor) { return true; } return false; } } public class TgaMetadata : IDeepCloneable { public TgaBitsPerPixel BitsPerPixel { get; set; } = TgaBitsPerPixel.Pixel24; public byte AlphaChannelBits { get; set; } public TgaMetadata() { } private TgaMetadata(TgaMetadata other) { BitsPerPixel = other.BitsPerPixel; } public IDeepCloneable DeepClone() { return new TgaMetadata(this); } } internal static class TgaThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidImageContentException(string errorMessage) { throw new InvalidImageContentException(errorMessage); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException) { throw new InvalidImageContentException(errorMessage, innerException); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotSupportedException(string errorMessage) { throw new NotSupportedException(errorMessage); } } } namespace SixLabors.ImageSharp.Formats.Png { internal static class Adam7 { public static readonly int[] ColumnIncrement = new int[7] { 8, 8, 4, 4, 2, 2, 1 }; public static readonly int[] FirstColumn = new int[7] { 0, 4, 0, 2, 0, 1, 0 }; public static readonly int[] FirstRow = new int[7] { 0, 0, 4, 0, 2, 0, 1 }; public static readonly int[] RowIncrement = new int[7] { 8, 8, 8, 4, 4, 2, 2 }; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ComputeBlockWidth(int width, int pass) { return (width + ColumnIncrement[pass] - 1 - FirstColumn[pass]) / ColumnIncrement[pass]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ComputeBlockHeight(int height, int pass) { return (height + RowIncrement[pass] - 1 - FirstRow[pass]) / RowIncrement[pass]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ComputeColumns(int width, int passIndex) { return passIndex switch { 0 => (width + 7) / 8, 1 => (width + 3) / 8, 2 => (width + 3) / 4, 3 => (width + 1) / 4, 4 => (width + 1) / 2, 5 => width / 2, 6 => width, _ => throw new ArgumentException($"Not a valid pass index: {passIndex}"), }; } } internal interface IPngDecoderOptions { bool IgnoreMetadata { get; } } internal interface IPngEncoderOptions { PngBitDepth? BitDepth { get; } PngColorType? ColorType { get; } PngFilterMethod? FilterMethod { get; } PngCompressionLevel CompressionLevel { get; } int TextCompressionThreshold { get; } float? Gamma { get; } IQuantizer Quantizer { get; } byte Threshold { get; } PngInterlaceMode? InterlaceMethod { get; } bool IgnoreMetadata { get; } PngChunkFilter? ChunkFilter { get; } PngTransparentColorMode TransparentColorMode { get; } } public enum PngBitDepth : byte { Bit1 = 1, Bit2 = 2, Bit4 = 4, Bit8 = 8, Bit16 = 0x10 } internal readonly struct PngChunk { public int Length { get; } public PngChunkType Type { get; } public IMemoryOwner Data { get; } public bool IsCritical { get { if (Type != PngChunkType.Header && Type != PngChunkType.Palette) { return Type == PngChunkType.Data; } return true; } } public PngChunk(int length, PngChunkType type, IMemoryOwner data = null) { Length = length; Type = type; Data = data; } } [Flags] public enum PngChunkFilter { None = 0, ExcludePhysicalChunk = 1, ExcludeGammaChunk = 2, ExcludeExifChunk = 4, ExcludeTextChunks = 8, ExcludeAll = -1 } internal enum PngChunkType : uint { Data = 1229209940u, End = 1229278788u, Header = 1229472850u, Palette = 1347179589u, Exif = 1700284774u, Gamma = 1732332865u, Physical = 1883789683u, Text = 1950701684u, CompressedText = 2052348020u, InternationalText = 1767135348u, Transparency = 1951551059u, Time = 1950960965u, Background = 1649100612u, EmbeddedColorProfile = 1766015824u, SignificantBits = 1933723988u, StandardRgbColourSpace = 1934772034u, Histogram = 1749635924u, SuggestedPalette = 1934642260u, Chroma = 1665684045u, ProprietaryApple = 1130840649u } public enum PngColorType : byte { Grayscale = 0, Rgb = 2, Palette = 3, GrayscaleWithAlpha = 4, RgbWithAlpha = 6 } [EditorBrowsable(EditorBrowsableState.Never)] public enum PngCompressionLevel { Level0 = 0, NoCompression = 0, Level1 = 1, BestSpeed = 1, Level2 = 2, Level3 = 3, Level4 = 4, Level5 = 5, Level6 = 6, DefaultCompression = 6, Level7 = 7, Level8 = 8, Level9 = 9, BestCompression = 9 } public sealed class PngConfigurationModule : IConfigurationModule { public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(PngFormat.Instance, new PngEncoder()); configuration.ImageFormatsManager.SetDecoder(PngFormat.Instance, new PngDecoder()); configuration.ImageFormatsManager.AddImageFormatDetector(new PngImageFormatDetector()); } } internal static class PngConstants { public static readonly Encoding Encoding = Encoding.GetEncoding("ISO-8859-1"); public static readonly Encoding LanguageEncoding = Encoding.ASCII; public static readonly Encoding TranslatedEncoding = Encoding.UTF8; public static readonly IEnumerable MimeTypes = new string[1] { "image/png" }; public static readonly IEnumerable FileExtensions = new string[1] { "png" }; public const ulong HeaderValue = 9894494448401390090uL; public static readonly Dictionary ColorTypes = new Dictionary { [PngColorType.Grayscale] = new byte[5] { 1, 2, 4, 8, 16 }, [PngColorType.Rgb] = new byte[2] { 8, 16 }, [PngColorType.Palette] = new byte[4] { 1, 2, 4, 8 }, [PngColorType.GrayscaleWithAlpha] = new byte[2] { 8, 16 }, [PngColorType.RgbWithAlpha] = new byte[2] { 8, 16 } }; public const int MaxTextKeywordLength = 79; public const int MinTextKeywordLength = 1; public static ReadOnlySpan HeaderBytes => new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 }; public static ReadOnlySpan XmpKeyword => "XML:com.adobe.xmp"u8; } public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector { public bool IgnoreMetadata { get; set; } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return new PngDecoderCore(configuration, this).Decode(configuration, stream, cancellationToken); } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) { IImageInfo imageInfo = new PngDecoderCore(configuration, colorMetadataOnly: true).Identify(configuration, stream, cancellationToken); stream.Position = 0L; PngMetadata pngMetadata = imageInfo.Metadata.GetPngMetadata(); PngColorType valueOrDefault = pngMetadata.ColorType.GetValueOrDefault(); PngBitDepth valueOrDefault2 = pngMetadata.BitDepth.GetValueOrDefault(); switch (valueOrDefault) { case PngColorType.Grayscale: if (valueOrDefault2 == PngBitDepth.Bit16) { if (pngMetadata.HasTransparency) { return Decode(configuration, stream, cancellationToken); } return Decode(configuration, stream, cancellationToken); } if (pngMetadata.HasTransparency) { return Decode(configuration, stream, cancellationToken); } return Decode(configuration, stream, cancellationToken); case PngColorType.Rgb: if (valueOrDefault2 == PngBitDepth.Bit16) { if (pngMetadata.HasTransparency) { return Decode(configuration, stream, cancellationToken); } return Decode(configuration, stream, cancellationToken); } if (pngMetadata.HasTransparency) { return Decode(configuration, stream, cancellationToken); } return Decode(configuration, stream, cancellationToken); case PngColorType.Palette: return Decode(configuration, stream, cancellationToken); case PngColorType.GrayscaleWithAlpha: if (valueOrDefault2 != PngBitDepth.Bit16) { return Decode(configuration, stream, cancellationToken); } return Decode(configuration, stream, cancellationToken); case PngColorType.RgbWithAlpha: if (valueOrDefault2 != PngBitDepth.Bit16) { return Decode(configuration, stream, cancellationToken); } return Decode(configuration, stream, cancellationToken); default: return Decode(configuration, stream, cancellationToken); } } public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) { return new PngDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); } } internal sealed class PngDecoderCore : IImageDecoderInternals { private readonly byte[] buffer = new byte[4]; private readonly bool ignoreMetadata; private readonly bool colorMetadataOnly; private readonly MemoryAllocator memoryAllocator; private BufferedReadStream currentStream; private PngHeader header; private int bytesPerPixel; private int bytesPerSample; private int bytesPerScanline; private byte[] palette; private byte[] paletteAlpha; private IMemoryOwner previousScanline; private IMemoryOwner scanline; private int currentRow = Adam7.FirstRow[0]; private int currentRowBytesRead; private PngColorType pngColorType; private PngChunk? nextChunk; public Configuration Configuration { get; } public Size Dimensions => new Size(header.Width, header.Height); public PngDecoderCore(Configuration configuration, IPngDecoderOptions options) { Configuration = configuration ?? Configuration.Default; memoryAllocator = Configuration.MemoryAllocator; ignoreMetadata = options.IgnoreMetadata; } internal PngDecoderCore(Configuration configuration, bool colorMetadataOnly) { Configuration = configuration ?? Configuration.Default; memoryAllocator = Configuration.MemoryAllocator; this.colorMetadataOnly = colorMetadataOnly; ignoreMetadata = true; } public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { ImageMetadata imageMetadata = new ImageMetadata(); PngMetadata pngMetadata = imageMetadata.GetPngMetadata(); currentStream = stream; currentStream.Skip(8); Image image = null; try { PngChunk chunk; while (TryReadChunk(out chunk)) { try { switch (chunk.Type) { case PngChunkType.Header: ReadHeaderChunk(pngMetadata, chunk.Data.GetSpan()); break; case PngChunkType.Physical: ReadPhysicalChunk(imageMetadata, chunk.Data.GetSpan()); break; case PngChunkType.Gamma: ReadGammaChunk(pngMetadata, chunk.Data.GetSpan()); break; case PngChunkType.Data: if (image == null) { InitializeImage(imageMetadata, out image); } ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata); break; case PngChunkType.Palette: { byte[] array3 = new byte[chunk.Length]; chunk.Data.GetSpan().CopyTo(array3); palette = array3; break; } case PngChunkType.Transparency: { byte[] array2 = new byte[chunk.Length]; chunk.Data.GetSpan().CopyTo(array2); paletteAlpha = array2; AssignTransparentMarkers(array2, pngMetadata); break; } case PngChunkType.Text: ReadTextChunk(imageMetadata, pngMetadata, chunk.Data.GetSpan()); break; case PngChunkType.CompressedText: ReadCompressedTextChunk(imageMetadata, pngMetadata, chunk.Data.GetSpan()); break; case PngChunkType.InternationalText: ReadInternationalTextChunk(imageMetadata, chunk.Data.GetSpan()); break; case PngChunkType.Exif: if (!ignoreMetadata) { byte[] array = new byte[chunk.Length]; chunk.Data.GetSpan().CopyTo(array); MergeOrSetExifProfile(imageMetadata, new ExifProfile(array), replaceExistingKeys: true); } break; case PngChunkType.ProprietaryApple: PngThrowHelper.ThrowInvalidChunkType("Proprietary Apple PNG detected! This PNG file is not conform to the specification and cannot be decoded."); break; case PngChunkType.End: goto end_IL_0022; } } finally { chunk.Data?.Dispose(); } continue; end_IL_0022: break; } if (image == null) { PngThrowHelper.ThrowNoData(); } return image; } catch { image?.Dispose(); throw; } finally { scanline?.Dispose(); previousScanline?.Dispose(); nextChunk?.Data?.Dispose(); } } public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { ImageMetadata imageMetadata = new ImageMetadata(); PngMetadata pngMetadata = imageMetadata.GetPngMetadata(); currentStream = stream; currentStream.Skip(8); try { PngChunk chunk; while (TryReadChunk(out chunk)) { try { switch (chunk.Type) { case PngChunkType.Header: ReadHeaderChunk(pngMetadata, chunk.Data.GetSpan()); goto end_IL_002d; case PngChunkType.Physical: if (colorMetadataOnly) { SkipChunkDataAndCrc(in chunk); } else { ReadPhysicalChunk(imageMetadata, chunk.Data.GetSpan()); } goto end_IL_002d; case PngChunkType.Gamma: if (colorMetadataOnly) { SkipChunkDataAndCrc(in chunk); } else { ReadGammaChunk(pngMetadata, chunk.Data.GetSpan()); } goto end_IL_002d; case PngChunkType.Data: if (colorMetadataOnly) { break; } SkipChunkDataAndCrc(in chunk); goto end_IL_002d; case PngChunkType.Transparency: { byte[] array2 = new byte[chunk.Length]; chunk.Data.GetSpan().CopyTo(array2); paletteAlpha = array2; AssignTransparentMarkers(array2, pngMetadata); if (colorMetadataOnly) { break; } goto end_IL_002d; } case PngChunkType.Text: if (colorMetadataOnly) { SkipChunkDataAndCrc(in chunk); } else { ReadTextChunk(imageMetadata, pngMetadata, chunk.Data.GetSpan()); } goto end_IL_002d; case PngChunkType.CompressedText: if (colorMetadataOnly) { SkipChunkDataAndCrc(in chunk); } else { ReadCompressedTextChunk(imageMetadata, pngMetadata, chunk.Data.GetSpan()); } goto end_IL_002d; case PngChunkType.InternationalText: if (colorMetadataOnly) { SkipChunkDataAndCrc(in chunk); } else { ReadInternationalTextChunk(imageMetadata, chunk.Data.GetSpan()); } goto end_IL_002d; case PngChunkType.Exif: if (colorMetadataOnly) { SkipChunkDataAndCrc(in chunk); } else if (!ignoreMetadata) { byte[] array = new byte[chunk.Length]; chunk.Data.GetSpan().CopyTo(array); MergeOrSetExifProfile(imageMetadata, new ExifProfile(array), replaceExistingKeys: true); } goto end_IL_002d; default: if (colorMetadataOnly) { SkipChunkDataAndCrc(in chunk); } goto end_IL_002d; case PngChunkType.End: break; } break; end_IL_002d:; } finally { chunk.Data?.Dispose(); } } if (header.Width == 0 && header.Height == 0) { PngThrowHelper.ThrowNoHeader(); } return new ImageInfo(new PixelTypeInfo(CalculateBitsPerPixel()), header.Width, header.Height, imageMetadata); } finally { scanline?.Dispose(); previousScanline?.Dispose(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte ReadByteLittleEndian(ReadOnlySpan buffer, int offset) { return (byte)(((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF)); } private bool TryScaleUpTo8BitArray(ReadOnlySpan source, int bytesPerScanline, int bits, out IMemoryOwner buffer) { if (bits >= 8) { buffer = null; return false; } buffer = memoryAllocator.Allocate(bytesPerScanline * 8 / bits, AllocationOptions.Clean); ref byte reference = ref MemoryMarshal.GetReference(source); ref byte reference2 = ref buffer.GetReference(); int num = 255 >> 8 - bits; int num2 = 0; for (int i = 0; i < bytesPerScanline; i++) { byte b = Unsafe.Add(ref reference, i); for (int j = 0; j < 8; j += bits) { int num3 = (b >> 8 - bits - j) & num; Unsafe.Add(ref reference2, num2) = (byte)num3; num2++; } } return true; } private void ReadPhysicalChunk(ImageMetadata metadata, ReadOnlySpan data) { PhysicalChunkData physicalChunkData = PhysicalChunkData.Parse(data); metadata.ResolutionUnits = ((physicalChunkData.UnitSpecifier != 0) ? PixelResolutionUnit.PixelsPerMeter : PixelResolutionUnit.AspectRatio); metadata.HorizontalResolution = physicalChunkData.XAxisPixelsPerUnit; metadata.VerticalResolution = physicalChunkData.YAxisPixelsPerUnit; } private void ReadGammaChunk(PngMetadata pngMetadata, ReadOnlySpan data) { if (data.Length >= 4) { pngMetadata.Gamma = (float)BinaryPrimitives.ReadUInt32BigEndian(data) * 1E-05f; } } private void InitializeImage(ImageMetadata metadata, out Image image) where TPixel : unmanaged, IPixel { image = new Image(Configuration, header.Width, header.Height, metadata); bytesPerPixel = CalculateBytesPerPixel(); bytesPerScanline = CalculateScanlineLength(header.Width) + 1; bytesPerSample = 1; if (header.BitDepth >= 8) { bytesPerSample = header.BitDepth / 8; } previousScanline?.Dispose(); scanline?.Dispose(); previousScanline = memoryAllocator.Allocate(bytesPerScanline, AllocationOptions.Clean); scanline = Configuration.MemoryAllocator.Allocate(bytesPerScanline, AllocationOptions.Clean); } private int CalculateBitsPerPixel() { switch (pngColorType) { case PngColorType.Grayscale: case PngColorType.Palette: return header.BitDepth; case PngColorType.GrayscaleWithAlpha: return header.BitDepth * 2; case PngColorType.Rgb: return header.BitDepth * 3; case PngColorType.RgbWithAlpha: return header.BitDepth * 4; default: PngThrowHelper.ThrowNotSupportedColor(); return -1; } } private int CalculateBytesPerPixel() { switch (pngColorType) { case PngColorType.Grayscale: if (header.BitDepth != 16) { return 1; } return 2; case PngColorType.GrayscaleWithAlpha: if (header.BitDepth != 16) { return 2; } return 4; case PngColorType.Palette: return 1; case PngColorType.Rgb: if (header.BitDepth != 16) { return 3; } return 6; default: if (header.BitDepth != 16) { return 4; } return 8; } } private int CalculateScanlineLength(int width) { int num = ((header.BitDepth == 16) ? 16 : 8); int num2 = width * header.BitDepth * bytesPerPixel; int num3 = num2 % num; if (num3 != 0) { num2 += num - num3; } return num2 / num; } private void ReadScanlines(PngChunk chunk, ImageFrame image, PngMetadata pngMetadata) where TPixel : unmanaged, IPixel { using ZlibInflateStream zlibInflateStream = new ZlibInflateStream(currentStream, ReadNextDataChunk); zlibInflateStream.AllocateNewBytes(chunk.Length, isCriticalChunk: true); DeflateStream compressedStream = zlibInflateStream.CompressedStream; if (header.InterlaceMethod == PngInterlaceMode.Adam7) { DecodeInterlacedPixelData(compressedStream, image, pngMetadata); } else { DecodePixelData(compressedStream, image, pngMetadata); } } private void DecodePixelData(DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata) where TPixel : unmanaged, IPixel { while (currentRow < header.Height) { Span span = scanline.GetSpan(); while (currentRowBytesRead < bytesPerScanline) { int num = compressedStream.Read(span, currentRowBytesRead, bytesPerScanline - currentRowBytesRead); if (num <= 0) { return; } currentRowBytesRead += num; } currentRowBytesRead = 0; switch ((FilterType)span[0]) { case FilterType.Sub: SubFilter.Decode(span, bytesPerPixel); break; case FilterType.Up: UpFilter.Decode(span, previousScanline.GetSpan()); break; case FilterType.Average: AverageFilter.Decode(span, previousScanline.GetSpan(), bytesPerPixel); break; case FilterType.Paeth: PaethFilter.Decode(span, previousScanline.GetSpan(), bytesPerPixel); break; default: PngThrowHelper.ThrowUnknownFilter(); break; case FilterType.None: break; } ProcessDefilteredScanline(span, image, pngMetadata); SwapScanlineBuffers(); currentRow++; } } private void DecodeInterlacedPixelData(DeflateStream compressedStream, ImageFrame image, PngMetadata pngMetadata) where TPixel : unmanaged, IPixel { int num = 0; int width = header.Width; Buffer2D pixelBuffer = image.PixelBuffer; while (true) { int num2 = Adam7.ComputeColumns(width, num); if (num2 == 0) { num++; continue; } int num3 = CalculateScanlineLength(num2) + 1; while (currentRow < header.Height) { while (currentRowBytesRead < num3) { int num4 = compressedStream.Read(scanline.GetSpan(), currentRowBytesRead, num3 - currentRowBytesRead); if (num4 <= 0) { return; } currentRowBytesRead += num4; } currentRowBytesRead = 0; Span span = scanline.Slice(0, num3); Span span2 = previousScanline.Slice(0, num3); switch ((FilterType)span[0]) { case FilterType.Sub: SubFilter.Decode(span, bytesPerPixel); break; case FilterType.Up: UpFilter.Decode(span, span2); break; case FilterType.Average: AverageFilter.Decode(span, span2, bytesPerPixel); break; case FilterType.Paeth: PaethFilter.Decode(span, span2, bytesPerPixel); break; default: PngThrowHelper.ThrowUnknownFilter(); break; case FilterType.None: break; } Span rowSpan = pixelBuffer.DangerousGetRowSpan(currentRow); ProcessInterlacedDefilteredScanline(scanline.GetSpan(), rowSpan, pngMetadata, Adam7.FirstColumn[num], Adam7.ColumnIncrement[num]); SwapScanlineBuffers(); currentRow += Adam7.RowIncrement[num]; } num++; previousScanline.Clear(); if (num >= 7) { break; } currentRow = Adam7.FirstRow[num]; } num = 0; } private void ProcessDefilteredScanline(ReadOnlySpan defilteredScanline, ImageFrame pixels, PngMetadata pngMetadata) where TPixel : unmanaged, IPixel { Span rowSpan = pixels.PixelBuffer.DangerousGetRowSpan(currentRow); ReadOnlySpan readOnlySpan = defilteredScanline.Slice(1, defilteredScanline.Length - 1); IMemoryOwner memoryOwner = null; try { ReadOnlySpan scanlineSpan = (TryScaleUpTo8BitArray(readOnlySpan, bytesPerScanline - 1, header.BitDepth, out memoryOwner) ? ((ReadOnlySpan)memoryOwner.GetSpan()) : readOnlySpan); switch (pngColorType) { case PngColorType.Grayscale: PngScanlineProcessor.ProcessGrayscaleScanline(in header, scanlineSpan, rowSpan, pngMetadata.HasTransparency, pngMetadata.TransparentL16.GetValueOrDefault(), pngMetadata.TransparentL8.GetValueOrDefault()); break; case PngColorType.GrayscaleWithAlpha: PngScanlineProcessor.ProcessGrayscaleWithAlphaScanline(in header, scanlineSpan, rowSpan, bytesPerPixel, bytesPerSample); break; case PngColorType.Palette: PngScanlineProcessor.ProcessPaletteScanline(in header, scanlineSpan, rowSpan, palette, paletteAlpha); break; case PngColorType.Rgb: PngScanlineProcessor.ProcessRgbScanline(Configuration, in header, scanlineSpan, rowSpan, bytesPerPixel, bytesPerSample, pngMetadata.HasTransparency, pngMetadata.TransparentRgb48.GetValueOrDefault(), pngMetadata.TransparentRgb24.GetValueOrDefault()); break; case PngColorType.RgbWithAlpha: PngScanlineProcessor.ProcessRgbaScanline(Configuration, in header, scanlineSpan, rowSpan, bytesPerPixel, bytesPerSample); break; case (PngColorType)1: case (PngColorType)5: break; } } finally { memoryOwner?.Dispose(); } } private void ProcessInterlacedDefilteredScanline(ReadOnlySpan defilteredScanline, Span rowSpan, PngMetadata pngMetadata, int pixelOffset = 0, int increment = 1) where TPixel : unmanaged, IPixel { ReadOnlySpan readOnlySpan = defilteredScanline.Slice(1, defilteredScanline.Length - 1); IMemoryOwner memoryOwner = null; try { ReadOnlySpan scanlineSpan = (TryScaleUpTo8BitArray(readOnlySpan, bytesPerScanline, header.BitDepth, out memoryOwner) ? ((ReadOnlySpan)memoryOwner.GetSpan()) : readOnlySpan); switch (pngColorType) { case PngColorType.Grayscale: PngScanlineProcessor.ProcessInterlacedGrayscaleScanline(in header, scanlineSpan, rowSpan, pixelOffset, increment, pngMetadata.HasTransparency, pngMetadata.TransparentL16.GetValueOrDefault(), pngMetadata.TransparentL8.GetValueOrDefault()); break; case PngColorType.GrayscaleWithAlpha: PngScanlineProcessor.ProcessInterlacedGrayscaleWithAlphaScanline(in header, scanlineSpan, rowSpan, pixelOffset, increment, bytesPerPixel, bytesPerSample); break; case PngColorType.Palette: PngScanlineProcessor.ProcessInterlacedPaletteScanline(in header, scanlineSpan, rowSpan, pixelOffset, increment, palette, paletteAlpha); break; case PngColorType.Rgb: PngScanlineProcessor.ProcessInterlacedRgbScanline(in header, scanlineSpan, rowSpan, pixelOffset, increment, bytesPerPixel, bytesPerSample, pngMetadata.HasTransparency, pngMetadata.TransparentRgb48.GetValueOrDefault(), pngMetadata.TransparentRgb24.GetValueOrDefault()); break; case PngColorType.RgbWithAlpha: PngScanlineProcessor.ProcessInterlacedRgbaScanline(in header, scanlineSpan, rowSpan, pixelOffset, increment, bytesPerPixel, bytesPerSample); break; case (PngColorType)1: case (PngColorType)5: break; } } finally { memoryOwner?.Dispose(); } } private void AssignTransparentMarkers(ReadOnlySpan alpha, PngMetadata pngMetadata) { if (pngColorType == PngColorType.Rgb) { if (alpha.Length >= 6) { if (header.BitDepth == 16) { ushort r = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)); ushort g = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2)); ushort b = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2)); pngMetadata.TransparentRgb48 = new Rgb48(r, g, b); pngMetadata.HasTransparency = true; } else { byte r2 = ReadByteLittleEndian(alpha, 0); byte g2 = ReadByteLittleEndian(alpha, 2); byte b2 = ReadByteLittleEndian(alpha, 4); pngMetadata.TransparentRgb24 = new Rgb24(r2, g2, b2); pngMetadata.HasTransparency = true; } } } else if (pngColorType == PngColorType.Grayscale && alpha.Length >= 2) { if (header.BitDepth == 16) { pngMetadata.TransparentL16 = new L16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2))); } else { pngMetadata.TransparentL8 = new L8(ReadByteLittleEndian(alpha, 0)); } pngMetadata.HasTransparency = true; } } private void ReadHeaderChunk(PngMetadata pngMetadata, ReadOnlySpan data) { header = PngHeader.Parse(data); header.Validate(); pngMetadata.BitDepth = (PngBitDepth)header.BitDepth; pngMetadata.ColorType = header.ColorType; pngMetadata.InterlaceMethod = header.InterlaceMethod; pngColorType = header.ColorType; } private void ReadTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, ReadOnlySpan data) { if (ignoreMetadata) { return; } int num = data.IndexOf(0); if ((num < 1 || num > 79) ? true : false) { return; } ReadOnlySpan keywordBytes = data.Slice(0, num); if (TryReadTextKeyword(keywordBytes, out var name)) { string text = PngConstants.Encoding.GetString(data.Slice(num + 1)); if (!TryReadTextChunkMetadata(baseMetadata, name, text)) { metadata.TextData.Add(new PngTextData(name, text, string.Empty, string.Empty)); } } } private void ReadCompressedTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, ReadOnlySpan data) { if (ignoreMetadata) { return; } int num = data.IndexOf(0); if (num < 1 || num > 79 || data[num + 1] != 0) { return; } ReadOnlySpan keywordBytes = data.Slice(0, num); if (TryReadTextKeyword(keywordBytes, out var name)) { ReadOnlySpan compressedData = data.Slice(num + 2); if (TryUncompressTextData(compressedData, PngConstants.Encoding, out var value) && !TryReadTextChunkMetadata(baseMetadata, name, value)) { metadata.TextData.Add(new PngTextData(name, value, string.Empty, string.Empty)); } } } private bool TryReadTextChunkMetadata(ImageMetadata baseMetadata, string chunkName, string chunkText) { if (chunkName.Equals("Raw profile type exif", StringComparison.OrdinalIgnoreCase) && TryReadLegacyExifTextChunk(baseMetadata, chunkText)) { return true; } return false; } private bool TryReadLegacyExifTextChunk(ImageMetadata metadata, string data) { ReadOnlySpan readOnlySpan = data.AsSpan(); readOnlySpan = readOnlySpan.TrimStart(); if (!StringEqualsInsensitive(readOnlySpan.Slice(0, 4), "exif".AsSpan())) { return false; } readOnlySpan = readOnlySpan.Slice(4).TrimStart(); int start = readOnlySpan.IndexOf('\n'); int num = ParseInt32(readOnlySpan.Slice(0, readOnlySpan.IndexOf('\n'))); readOnlySpan = readOnlySpan.Slice(start).Trim(); ReadOnlySpan other = new byte[6] { 69, 120, 105, 102, 0, 0 }; if (num < other.Length) { return false; } byte[] array = new byte[num - other.Length]; try { byte[] array2 = array; if (array.Length < other.Length) { array2 = new byte[other.Length]; } HexConverter.HexStringToBytes(readOnlySpan.Slice(0, other.Length * 2), array2); if (!array2.AsSpan().Slice(0, other.Length).SequenceEqual(other)) { return false; } readOnlySpan = readOnlySpan.Slice(other.Length * 2); num -= other.Length; int num2 = 0; while (num2 < num) { ReadOnlySpan chars = readOnlySpan; int num3 = readOnlySpan.IndexOf('\n'); if (num3 != -1) { chars = readOnlySpan.Slice(0, num3); } num2 += HexConverter.HexStringToBytes(chars, array.AsSpan().Slice(num2)); readOnlySpan = readOnlySpan.Slice(num3 + 1); } } catch { return false; } MergeOrSetExifProfile(metadata, new ExifProfile(array), replaceExistingKeys: false); return true; } private static bool StringEqualsInsensitive(ReadOnlySpan span1, ReadOnlySpan span2) { return MemoryExtensions.Equals(span1, span2, StringComparison.OrdinalIgnoreCase); } private static int ParseInt32(ReadOnlySpan span) { return int.Parse(span); } private void MergeOrSetExifProfile(ImageMetadata metadata, ExifProfile newProfile, bool replaceExistingKeys) { if (metadata.ExifProfile == null) { metadata.ExifProfile = newProfile; return; } foreach (IExifValue value in newProfile.Values) { if (replaceExistingKeys || metadata.ExifProfile.GetValueInternal(value.Tag) == null) { metadata.ExifProfile.SetValueInternal(value.Tag, value.GetValue()); } } } private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpan data) { if (ignoreMetadata) { return; } PngMetadata pngMetadata = metadata.GetPngMetadata(); int num = data.IndexOf(0); if (num < 1 || num > 79) { return; } byte b = data[num + 1]; if ((b != 0 && b != 1) || data[num + 2] != 0) { return; } int num2 = num + 3; int num3 = data.Slice(num2).IndexOf(0); if (num3 < 0) { return; } string languageTag = PngConstants.LanguageEncoding.GetString(data.Slice(num2, num3)); int num4 = num2 + num3 + 1; int num5 = data.Slice(num4).IndexOf(0); string translatedKeyword = PngConstants.TranslatedEncoding.GetString(data.Slice(num4, num5)); ReadOnlySpan keywordBytes = data.Slice(0, num); if (!TryReadTextKeyword(keywordBytes, out var name)) { return; } int start = num4 + num5 + 1; if (b == 1) { ReadOnlySpan compressedData = data.Slice(start); if (TryUncompressTextData(compressedData, PngConstants.TranslatedEncoding, out var value)) { pngMetadata.TextData.Add(new PngTextData(name, value, languageTag, translatedKeyword)); } } else if (IsXmpTextData(keywordBytes)) { XmpProfile xmpProfile = new XmpProfile(data.Slice(start).ToArray()); metadata.XmpProfile = xmpProfile; } else { string value2 = PngConstants.TranslatedEncoding.GetString(data.Slice(start)); pngMetadata.TextData.Add(new PngTextData(name, value2, languageTag, translatedKeyword)); } } private bool TryUncompressTextData(ReadOnlySpan compressedData, Encoding encoding, out string value) { using MemoryStream stream = new MemoryStream(compressedData.ToArray()); using BufferedReadStream innerStream = new BufferedReadStream(Configuration, stream); using ZlibInflateStream zlibInflateStream = new ZlibInflateStream(innerStream); if (!zlibInflateStream.AllocateNewBytes(compressedData.Length, isCriticalChunk: false)) { value = null; return false; } List list = new List(); for (int num = zlibInflateStream.CompressedStream.Read(buffer, 0, buffer.Length); num != 0; num = zlibInflateStream.CompressedStream.Read(buffer, 0, buffer.Length)) { list.AddRange(buffer.AsSpan(0, num).ToArray()); } value = encoding.GetString(list.ToArray()); return true; } private int ReadNextDataChunk() { if (nextChunk.HasValue) { return 0; } currentStream.Read(buffer, 0, 4); if (TryReadChunk(out var chunk)) { if (chunk.Type == PngChunkType.Data) { chunk.Data?.Dispose(); return chunk.Length; } nextChunk = chunk; } return 0; } private bool TryReadChunk(out PngChunk chunk) { if (nextChunk.HasValue) { chunk = nextChunk.Value; nextChunk = null; return true; } if (!TryReadChunkLength(out var result)) { chunk = default(PngChunk); return false; } while (result < 0 || result > currentStream.Length - currentStream.Position) { if (!TryReadChunkLength(out result)) { chunk = default(PngChunk); return false; } } PngChunkType pngChunkType = ReadChunkType(); if (colorMetadataOnly && pngChunkType != PngChunkType.Header && pngChunkType != PngChunkType.Transparency) { chunk = new PngChunk(result, pngChunkType); return true; } long position = currentStream.Position; chunk = new PngChunk(result, pngChunkType, ReadChunkData(result)); ValidateChunk(in chunk); if (pngChunkType == PngChunkType.Data) { currentStream.Position = position; } return true; } private void ValidateChunk(in PngChunk chunk) { uint num = ReadChunkCrc(); if (chunk.IsCritical) { Span span = stackalloc byte[4]; BinaryPrimitives.WriteUInt32BigEndian(span, (uint)chunk.Type); if (Crc32.Calculate(Crc32.Calculate(span), chunk.Data.GetSpan()) != num) { string chunkTypeName = Encoding.ASCII.GetString(span); chunk.Data?.Dispose(); PngThrowHelper.ThrowInvalidChunkCrc(chunkTypeName); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private uint ReadChunkCrc() { uint result = 0u; if (currentStream.Read(buffer, 0, 4) == 4) { result = BinaryPrimitives.ReadUInt32BigEndian(buffer); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SkipChunkDataAndCrc(in PngChunk chunk) { currentStream.Skip(chunk.Length); currentStream.Skip(4); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private IMemoryOwner ReadChunkData(int length) { length = (int)Math.Min(length, currentStream.Length - currentStream.Position); IMemoryOwner result = Configuration.MemoryAllocator.Allocate(length, AllocationOptions.Clean); currentStream.Read(result.GetSpan(), 0, length); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private PngChunkType ReadChunkType() { if (currentStream.Read(buffer, 0, 4) == 4) { return (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer); } PngThrowHelper.ThrowInvalidChunkType(); return (PngChunkType)0u; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool TryReadChunkLength(out int result) { if (currentStream.Read(buffer, 0, 4) == 4) { result = BinaryPrimitives.ReadInt32BigEndian(buffer); return true; } result = 0; return false; } private bool TryReadTextKeyword(ReadOnlySpan keywordBytes, out string name) { name = string.Empty; ReadOnlySpan readOnlySpan = keywordBytes; for (int i = 0; i < readOnlySpan.Length; i++) { byte b = readOnlySpan[i]; if ((b < 32 || b > 126) && (b < 161 || b > byte.MaxValue)) { return false; } } name = PngConstants.Encoding.GetString(keywordBytes); if (string.IsNullOrWhiteSpace(name) || name.StartsWith(" ") || name.EndsWith(" ")) { return false; } return true; } private bool IsXmpTextData(ReadOnlySpan keywordBytes) { return keywordBytes.SequenceEqual(PngConstants.XmpKeyword); } private void SwapScanlineBuffers() { IMemoryOwner memoryOwner = previousScanline; previousScanline = scanline; scanline = memoryOwner; } } public sealed class PngEncoder : IImageEncoder, IPngEncoderOptions { public PngBitDepth? BitDepth { get; set; } public PngColorType? ColorType { get; set; } public PngFilterMethod? FilterMethod { get; set; } public PngCompressionLevel CompressionLevel { get; set; } = PngCompressionLevel.Level6; public int TextCompressionThreshold { get; set; } = 1024; public float? Gamma { get; set; } public IQuantizer Quantizer { get; set; } public byte Threshold { get; set; } = byte.MaxValue; public PngInterlaceMode? InterlaceMethod { get; set; } public PngChunkFilter? ChunkFilter { get; set; } public bool IgnoreMetadata { get; set; } public PngTransparentColorMode TransparentColorMode { get; set; } public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { using PngEncoderCore encoder = new PngEncoderCore(image.GetMemoryAllocator(), image.GetConfiguration(), new PngEncoderOptions(this)); encoder.Encode(image, stream); } public async Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { using PngEncoderCore encoder = new PngEncoderCore(image.GetMemoryAllocator(), image.GetConfiguration(), new PngEncoderOptions(this)); await encoder.EncodeAsync(image, stream, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); } } internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable { private const int MaxBlockSize = 65535; private readonly MemoryAllocator memoryAllocator; private readonly Configuration configuration; private readonly byte[] buffer = new byte[8]; private readonly byte[] chunkDataBuffer = new byte[16]; private readonly PngEncoderOptions options; private byte bitDepth; private bool use16Bit; private int bytesPerPixel; private int width; private int height; private IMemoryOwner previousScanline; private IMemoryOwner currentScanline; public PngEncoderCore(MemoryAllocator memoryAllocator, Configuration configuration, PngEncoderOptions options) { this.memoryAllocator = memoryAllocator; this.configuration = configuration; this.options = options; } public void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); width = image.Width; height = image.Height; ImageMetadata metadata = image.Metadata; PngMetadata formatMetadata = metadata.GetFormatMetadata(PngFormat.Instance); PngEncoderOptionsHelpers.AdjustOptions(options, formatMetadata, out use16Bit, out bytesPerPixel); Image image2 = null; bool flag = options.TransparentColorMode == PngTransparentColorMode.Clear; if (flag) { image2 = image.Clone(); ClearTransparentPixels(image2); } IndexedImageFrame indexedImageFrame = CreateQuantizedImage(image, image2); stream.Write(PngConstants.HeaderBytes); WriteHeaderChunk(stream); WriteGammaChunk(stream); WritePaletteChunk(stream, indexedImageFrame); WriteTransparencyChunk(stream, formatMetadata); WritePhysicalChunk(stream, metadata); WriteExifChunk(stream, metadata); WriteXmpChunk(stream, metadata); WriteTextChunks(stream, formatMetadata); WriteDataChunks(flag ? image2 : image, indexedImageFrame, stream); WriteEndChunk(stream); stream.Flush(); indexedImageFrame?.Dispose(); image2?.Dispose(); } public void Dispose() { previousScanline?.Dispose(); currentScanline?.Dispose(); previousScanline = null; currentScanline = null; } private static void ClearTransparentPixels(Image image) where TPixel : unmanaged, IPixel { image.ProcessPixelRows(delegate(PixelAccessor accessor) { Rgba32 dest = default(Rgba32); Rgba32 source = Color.Transparent; for (int i = 0; i < accessor.Height; i++) { Span rowSpan = accessor.GetRowSpan(i); for (int j = 0; j < accessor.Width; j++) { rowSpan[j].ToRgba32(ref dest); if (dest.A == 0) { rowSpan[j].FromRgba32(source); } } } }); } private IndexedImageFrame CreateQuantizedImage(Image image, Image clonedImage) where TPixel : unmanaged, IPixel { IndexedImageFrame indexedImageFrame; if (options.TransparentColorMode == PngTransparentColorMode.Clear) { indexedImageFrame = PngEncoderOptionsHelpers.CreateQuantizedFrame(options, clonedImage); bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(options, indexedImageFrame); } else { indexedImageFrame = PngEncoderOptionsHelpers.CreateQuantizedFrame(options, image); bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(options, indexedImageFrame); } return indexedImageFrame; } private void CollectGrayscaleBytes(ReadOnlySpan rowSpan) where TPixel : unmanaged, IPixel { MemoryMarshal.GetReference(rowSpan); Span span = currentScanline.GetSpan(); MemoryMarshal.GetReference(span); if (options.ColorType == PngColorType.Grayscale) { if (use16Bit) { using (IMemoryOwner memoryOwner = memoryAllocator.Allocate(rowSpan.Length)) { Span span2 = memoryOwner.GetSpan(); ref L16 reference = ref MemoryMarshal.GetReference(span2); PixelOperations.Instance.ToL16(configuration, rowSpan, span2); int num = 0; int num2 = 0; while (num < span2.Length) { L16 l = Unsafe.Add(ref reference, num); BinaryPrimitives.WriteUInt16BigEndian(span.Slice(num2, 2), l.PackedValue); num++; num2 += 2; } return; } } if (bitDepth == 8) { PixelOperations.Instance.ToL8Bytes(configuration, rowSpan, span, rowSpan.Length); return; } using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(rowSpan.Length, AllocationOptions.Clean); int num3 = 255 / (ColorNumerics.GetColorCountForBitDepth(bitDepth) - 1); Span span3 = memoryOwner2.GetSpan(); PixelOperations.Instance.ToL8Bytes(configuration, rowSpan, span3, rowSpan.Length); PngEncoderHelpers.ScaleDownFrom8BitArray(span3, span, bitDepth, num3); return; } if (use16Bit) { using (IMemoryOwner memoryOwner3 = memoryAllocator.Allocate(rowSpan.Length)) { Span span4 = memoryOwner3.GetSpan(); ref La32 reference2 = ref MemoryMarshal.GetReference(span4); PixelOperations.Instance.ToLa32(configuration, rowSpan, span4); int num4 = 0; int num5 = 0; while (num4 < span4.Length) { La32 la = Unsafe.Add(ref reference2, num4); BinaryPrimitives.WriteUInt16BigEndian(span.Slice(num5, 2), la.L); BinaryPrimitives.WriteUInt16BigEndian(span.Slice(num5 + 2, 2), la.A); num4++; num5 += 4; } return; } } PixelOperations.Instance.ToLa16Bytes(configuration, rowSpan, span, rowSpan.Length); } private void CollectTPixelBytes(ReadOnlySpan rowSpan) where TPixel : unmanaged, IPixel { Span span = currentScanline.GetSpan(); switch (bytesPerPixel) { case 4: PixelOperations.Instance.ToRgba32Bytes(configuration, rowSpan, span, rowSpan.Length); return; case 3: PixelOperations.Instance.ToRgb24Bytes(configuration, rowSpan, span, rowSpan.Length); return; case 8: { using IMemoryOwner memoryOwner = memoryAllocator.Allocate(rowSpan.Length); Span span2 = memoryOwner.GetSpan(); ref Rgba64 reference = ref MemoryMarshal.GetReference(span2); PixelOperations.Instance.ToRgba64(configuration, rowSpan, span2); int num = 0; int num2 = 0; while (num < rowSpan.Length) { Rgba64 rgba = Unsafe.Add(ref reference, num); BinaryPrimitives.WriteUInt16BigEndian(span.Slice(num2, 2), rgba.R); BinaryPrimitives.WriteUInt16BigEndian(span.Slice(num2 + 2, 2), rgba.G); BinaryPrimitives.WriteUInt16BigEndian(span.Slice(num2 + 4, 2), rgba.B); BinaryPrimitives.WriteUInt16BigEndian(span.Slice(num2 + 6, 2), rgba.A); num++; num2 += 8; } return; } } using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(rowSpan.Length); Span span3 = memoryOwner2.GetSpan(); ref Rgb48 reference2 = ref MemoryMarshal.GetReference(span3); PixelOperations.Instance.ToRgb48(configuration, rowSpan, span3); int num3 = 0; int num4 = 0; while (num3 < rowSpan.Length) { Rgb48 rgb = Unsafe.Add(ref reference2, num3); BinaryPrimitives.WriteUInt16BigEndian(span.Slice(num4, 2), rgb.R); BinaryPrimitives.WriteUInt16BigEndian(span.Slice(num4 + 2, 2), rgb.G); BinaryPrimitives.WriteUInt16BigEndian(span.Slice(num4 + 4, 2), rgb.B); num3++; num4 += 6; } } private void CollectPixelBytes(ReadOnlySpan rowSpan, IndexedImageFrame quantized, int row) where TPixel : unmanaged, IPixel { switch (options.ColorType) { case PngColorType.Palette: if (bitDepth < 8) { PngEncoderHelpers.ScaleDownFrom8BitArray(quantized.DangerousGetRowSpan(row), currentScanline.GetSpan(), bitDepth); } else { quantized.DangerousGetRowSpan(row).CopyTo(currentScanline.GetSpan()); } break; case PngColorType.Grayscale: case PngColorType.GrayscaleWithAlpha: CollectGrayscaleBytes(rowSpan); break; default: CollectTPixelBytes(rowSpan); break; } } private void FilterPixelBytes(ref Span filter, ref Span attempt) { int sum; switch (options.FilterMethod) { case PngFilterMethod.None: NoneFilter.Encode(currentScanline.GetSpan(), filter); break; case PngFilterMethod.Sub: SubFilter.Encode(currentScanline.GetSpan(), filter, bytesPerPixel, out sum); break; case PngFilterMethod.Up: UpFilter.Encode(currentScanline.GetSpan(), previousScanline.GetSpan(), filter, out sum); break; case PngFilterMethod.Average: AverageFilter.Encode(currentScanline.GetSpan(), previousScanline.GetSpan(), filter, bytesPerPixel, out sum); break; case PngFilterMethod.Paeth: PaethFilter.Encode(currentScanline.GetSpan(), previousScanline.GetSpan(), filter, bytesPerPixel, out sum); break; default: ApplyOptimalFilteredScanline(ref filter, ref attempt); break; } } private void CollectAndFilterPixelRow(ReadOnlySpan rowSpan, ref Span filter, ref Span attempt, IndexedImageFrame quantized, int row) where TPixel : unmanaged, IPixel { CollectPixelBytes(rowSpan, quantized, row); FilterPixelBytes(ref filter, ref attempt); } private void EncodeAdam7IndexedPixelRow(ReadOnlySpan row, ref Span filter, ref Span attempt) { if (bitDepth < 8) { PngEncoderHelpers.ScaleDownFrom8BitArray(row, currentScanline.GetSpan(), bitDepth); } else { row.CopyTo(currentScanline.GetSpan()); } FilterPixelBytes(ref filter, ref attempt); } private void ApplyOptimalFilteredScanline(ref Span filter, ref Span attempt) { if (options.ColorType == PngColorType.Palette || height == 1 || bitDepth < 8) { NoneFilter.Encode(currentScanline.GetSpan(), filter); return; } Span span = currentScanline.GetSpan(); Span span2 = previousScanline.GetSpan(); int num = int.MaxValue; SubFilter.Encode(span, attempt, bytesPerPixel, out var sum); if (sum < num) { num = sum; SwapSpans(ref filter, ref attempt); } UpFilter.Encode(span, span2, attempt, out sum); if (sum < num) { num = sum; SwapSpans(ref filter, ref attempt); } AverageFilter.Encode(span, span2, attempt, bytesPerPixel, out sum); if (sum < num) { num = sum; SwapSpans(ref filter, ref attempt); } PaethFilter.Encode(span, span2, attempt, bytesPerPixel, out sum); if (sum < num) { SwapSpans(ref filter, ref attempt); } } private void WriteHeaderChunk(Stream stream) { PngHeader pngHeader = new PngHeader(width, height, bitDepth, options.ColorType.Value, 0, 0, options.InterlaceMethod.Value); pngHeader.WriteTo(chunkDataBuffer); WriteChunk(stream, PngChunkType.Header, chunkDataBuffer, 0, 13); } private void WritePaletteChunk(Stream stream, IndexedImageFrame quantized) where TPixel : unmanaged, IPixel { if (quantized == null) { return; } ReadOnlyMemory palette = quantized.Palette; int length = palette.Span.Length; int length2 = length * Unsafe.SizeOf(); bool flag = false; using IMemoryOwner memoryOwner = memoryAllocator.Allocate(length2); using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(length); ref Rgb24 reference = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(memoryOwner.GetSpan())); ref byte reference2 = ref MemoryMarshal.GetReference(memoryOwner2.GetSpan()); using IMemoryOwner memoryOwner3 = quantized.Configuration.MemoryAllocator.Allocate(length); Span span = memoryOwner3.GetSpan(); PixelOperations instance = PixelOperations.Instance; Configuration obj = quantized.Configuration; palette = quantized.Palette; instance.ToRgba32(obj, palette.Span, span); ref Rgba32 reference3 = ref MemoryMarshal.GetReference(span); for (int i = 0; i < length; i++) { Rgba32 rgba = Unsafe.Add(ref reference3, i); byte b = rgba.A; Unsafe.Add(ref reference, i) = rgba.Rgb; if (b > options.Threshold) { b = byte.MaxValue; } flag = flag || b < byte.MaxValue; Unsafe.Add(ref reference2, i) = b; } WriteChunk(stream, PngChunkType.Palette, memoryOwner.GetSpan(), 0, length2); if (flag) { WriteChunk(stream, PngChunkType.Transparency, memoryOwner2.GetSpan(), 0, length); } } private void WritePhysicalChunk(Stream stream, ImageMetadata meta) { if ((options.ChunkFilter.GetValueOrDefault() & PngChunkFilter.ExcludePhysicalChunk) != PngChunkFilter.ExcludePhysicalChunk) { PhysicalChunkData.FromMetadata(meta).WriteTo(chunkDataBuffer); WriteChunk(stream, PngChunkType.Physical, chunkDataBuffer, 0, 9); } } private void WriteExifChunk(Stream stream, ImageMetadata meta) { if ((options.ChunkFilter.GetValueOrDefault() & PngChunkFilter.ExcludeExifChunk) != PngChunkFilter.ExcludeExifChunk && meta.ExifProfile != null && meta.ExifProfile.Values.Count != 0) { meta.SyncProfiles(); WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray()); } } private void WriteXmpChunk(Stream stream, ImageMetadata meta) { if ((options.ChunkFilter.GetValueOrDefault() & PngChunkFilter.ExcludeTextChunks) == PngChunkFilter.ExcludeTextChunks || meta.XmpProfile == null) { return; } byte[] data = meta.XmpProfile.Data; if (data.Length == 0) { return; } int length = data.Length + PngConstants.XmpKeyword.Length + 5; using IMemoryOwner memoryOwner = memoryAllocator.Allocate(length); Span span = memoryOwner.GetSpan(); PngConstants.XmpKeyword.CopyTo(span); int length2 = PngConstants.XmpKeyword.Length; span[length2++] = 0; span[length2++] = 0; span[length2++] = 0; span[length2++] = 0; span[length2++] = 0; data.CopyTo(span.Slice(length2)); WriteChunk(stream, PngChunkType.InternationalText, span); } private void WriteTextChunks(Stream stream, PngMetadata meta) { if ((options.ChunkFilter.GetValueOrDefault() & PngChunkFilter.ExcludeTextChunks) == PngChunkFilter.ExcludeTextChunks) { return; } for (int i = 0; i < meta.TextData.Count; i++) { PngTextData pngTextData = meta.TextData[i]; bool flag = false; string value = pngTextData.Value; for (int j = 0; j < value.Length; j++) { if (value[j] > 'ÿ') { flag = true; break; } } if (flag || !string.IsNullOrWhiteSpace(pngTextData.LanguageTag) || !string.IsNullOrWhiteSpace(pngTextData.TranslatedKeyword)) { byte[] bytes = PngConstants.Encoding.GetBytes(pngTextData.Keyword); byte[] array = ((pngTextData.Value.Length > options.TextCompressionThreshold) ? GetCompressedTextBytes(PngConstants.TranslatedEncoding.GetBytes(pngTextData.Value)) : PngConstants.TranslatedEncoding.GetBytes(pngTextData.Value)); byte[] bytes2 = PngConstants.TranslatedEncoding.GetBytes(pngTextData.TranslatedKeyword); byte[] bytes3 = PngConstants.LanguageEncoding.GetBytes(pngTextData.LanguageTag); int length = bytes.Length + array.Length + bytes2.Length + bytes3.Length + 5; using IMemoryOwner memoryOwner = memoryAllocator.Allocate(length); Span span = memoryOwner.GetSpan(); bytes.CopyTo(span); int num = bytes.Length; span[num++] = 0; if (pngTextData.Value.Length > options.TextCompressionThreshold) { span[num++] = 1; } else { span[num++] = 0; } span[num++] = 0; bytes3.CopyTo(span.Slice(num)); num += bytes3.Length; span[num++] = 0; bytes2.CopyTo(span.Slice(num)); num += bytes2.Length; span[num++] = 0; array.CopyTo(span.Slice(num)); WriteChunk(stream, PngChunkType.InternationalText, span); } else if (pngTextData.Value.Length > options.TextCompressionThreshold) { byte[] compressedTextBytes = GetCompressedTextBytes(PngConstants.Encoding.GetBytes(pngTextData.Value)); int length2 = pngTextData.Keyword.Length + compressedTextBytes.Length + 2; using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(length2); Span span2 = memoryOwner2.GetSpan(); PngConstants.Encoding.GetBytes(pngTextData.Keyword).CopyTo(span2); int length3 = pngTextData.Keyword.Length; span2[length3++] = 0; span2[length3++] = 0; compressedTextBytes.CopyTo(span2.Slice(length3)); WriteChunk(stream, PngChunkType.CompressedText, span2.ToArray()); } else { int length4 = pngTextData.Keyword.Length + pngTextData.Value.Length + 1; using IMemoryOwner memoryOwner3 = memoryAllocator.Allocate(length4); Span span3 = memoryOwner3.GetSpan(); PngConstants.Encoding.GetBytes(pngTextData.Keyword).CopyTo(span3); int length5 = pngTextData.Keyword.Length; span3[length5++] = 0; PngConstants.Encoding.GetBytes(pngTextData.Value).CopyTo(span3.Slice(length5)); WriteChunk(stream, PngChunkType.Text, span3.ToArray()); } } } private byte[] GetCompressedTextBytes(byte[] textBytes) { using MemoryStream memoryStream = new MemoryStream(); using (ZlibDeflateStream zlibDeflateStream = new ZlibDeflateStream(memoryAllocator, memoryStream, options.CompressionLevel)) { zlibDeflateStream.Write(textBytes); } return memoryStream.ToArray(); } private void WriteGammaChunk(Stream stream) { if ((options.ChunkFilter.GetValueOrDefault() & PngChunkFilter.ExcludeGammaChunk) != PngChunkFilter.ExcludeGammaChunk && options.Gamma > 0f) { uint value = (uint)(options.Gamma * 100000f).Value; BinaryPrimitives.WriteUInt32BigEndian(chunkDataBuffer.AsSpan(0, 4), value); WriteChunk(stream, PngChunkType.Gamma, chunkDataBuffer, 0, 4); } } private void WriteTransparencyChunk(Stream stream, PngMetadata pngMetadata) { if (!pngMetadata.HasTransparency) { return; } Span destination = chunkDataBuffer.AsSpan(); if (pngMetadata.ColorType == PngColorType.Rgb) { if (pngMetadata.TransparentRgb48.HasValue && use16Bit) { Rgb48 value = pngMetadata.TransparentRgb48.Value; BinaryPrimitives.WriteUInt16LittleEndian(destination, value.R); BinaryPrimitives.WriteUInt16LittleEndian(destination.Slice(2, 2), value.G); BinaryPrimitives.WriteUInt16LittleEndian(destination.Slice(4, 2), value.B); WriteChunk(stream, PngChunkType.Transparency, chunkDataBuffer, 0, 6); } else if (pngMetadata.TransparentRgb24.HasValue) { destination.Clear(); Rgb24 value2 = pngMetadata.TransparentRgb24.Value; destination[1] = value2.R; destination[3] = value2.G; destination[5] = value2.B; WriteChunk(stream, PngChunkType.Transparency, chunkDataBuffer, 0, 6); } } else if (pngMetadata.ColorType == PngColorType.Grayscale) { if (pngMetadata.TransparentL16.HasValue && use16Bit) { BinaryPrimitives.WriteUInt16LittleEndian(destination, pngMetadata.TransparentL16.Value.PackedValue); WriteChunk(stream, PngChunkType.Transparency, chunkDataBuffer, 0, 2); } else if (pngMetadata.TransparentL8.HasValue) { destination.Clear(); destination[1] = pngMetadata.TransparentL8.Value.PackedValue; WriteChunk(stream, PngChunkType.Transparency, chunkDataBuffer, 0, 2); } } } private void WriteDataChunks(Image pixels, IndexedImageFrame quantized, Stream stream) where TPixel : unmanaged, IPixel { byte[] array; int num; using (MemoryStream memoryStream = new MemoryStream()) { using (ZlibDeflateStream deflateStream = new ZlibDeflateStream(memoryAllocator, memoryStream, options.CompressionLevel)) { if (options.InterlaceMethod == PngInterlaceMode.Adam7) { if (quantized != null) { EncodeAdam7IndexedPixels(quantized, deflateStream); } else { EncodeAdam7Pixels(pixels, deflateStream); } } else { EncodePixels(pixels, quantized, deflateStream); } } array = memoryStream.ToArray(); num = array.Length; } int num2 = num / 65535; if (num % 65535 != 0) { num2++; } for (int i = 0; i < num2; i++) { int num3 = num - i * 65535; if (num3 > 65535) { num3 = 65535; } WriteChunk(stream, PngChunkType.Data, array, i * 65535, num3); } } private void AllocateScanlineBuffers(int bytesPerScanline) { previousScanline?.Dispose(); currentScanline?.Dispose(); previousScanline = memoryAllocator.Allocate(bytesPerScanline, AllocationOptions.Clean); currentScanline = memoryAllocator.Allocate(bytesPerScanline, AllocationOptions.Clean); } private void EncodePixels(Image pixels, IndexedImageFrame quantized, ZlibDeflateStream deflateStream) where TPixel : unmanaged, IPixel { int num = CalculateScanlineLength(width); int length = num + 1; AllocateScanlineBuffers(num); IMemoryOwner filterBuffer = memoryAllocator.Allocate(length, AllocationOptions.Clean); try { IMemoryOwner attemptBuffer = memoryAllocator.Allocate(length, AllocationOptions.Clean); try { pixels.ProcessPixelRows(delegate(PixelAccessor accessor) { Span filter = filterBuffer.GetSpan(); Span attempt = attemptBuffer.GetSpan(); for (int i = 0; i < height; i++) { CollectAndFilterPixelRow(accessor.GetRowSpan(i), ref filter, ref attempt, quantized, i); deflateStream.Write(filter); SwapScanlineBuffers(); } }); } finally { if (attemptBuffer != null) { attemptBuffer.Dispose(); } } } finally { if (filterBuffer != null) { filterBuffer.Dispose(); } } } private void EncodeAdam7Pixels(Image image, ZlibDeflateStream deflateStream) where TPixel : unmanaged, IPixel { int num = image.Width; int num2 = image.Height; Buffer2D pixelBuffer = image.Frames.RootFrame.PixelBuffer; for (int i = 0; i < 7; i++) { int num3 = Adam7.FirstRow[i]; int num4 = Adam7.FirstColumn[i]; int num5 = Adam7.ComputeBlockWidth(num, i); int num6 = ((bytesPerPixel <= 1) ? ((num5 * bitDepth + 7) / 8) : (num5 * bytesPerPixel)); int length = num6 + 1; AllocateScanlineBuffers(num6); using IMemoryOwner memoryOwner = memoryAllocator.Allocate(num5); using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(length, AllocationOptions.Clean); using IMemoryOwner memoryOwner3 = memoryAllocator.Allocate(length, AllocationOptions.Clean); Span span = memoryOwner.GetSpan(); Span filter = memoryOwner2.GetSpan(); Span attempt = memoryOwner3.GetSpan(); for (int j = num3; j < num2; j += Adam7.RowIncrement[i]) { Span span2 = pixelBuffer.DangerousGetRowSpan(j); int k = num4; int num7 = 0; for (; k < num; k += Adam7.ColumnIncrement[i]) { span[num7++] = span2[k]; } CollectAndFilterPixelRow(span, ref filter, ref attempt, null, -1); deflateStream.Write(filter); SwapScanlineBuffers(); } } } private void EncodeAdam7IndexedPixels(IndexedImageFrame quantized, ZlibDeflateStream deflateStream) where TPixel : unmanaged, IPixel { int num = quantized.Width; int num2 = quantized.Height; for (int i = 0; i < 7; i++) { int num3 = Adam7.FirstRow[i]; int num4 = Adam7.FirstColumn[i]; int num5 = Adam7.ComputeBlockWidth(num, i); int num6 = ((bytesPerPixel <= 1) ? ((num5 * bitDepth + 7) / 8) : (num5 * bytesPerPixel)); int length = num6 + 1; AllocateScanlineBuffers(num6); using IMemoryOwner memoryOwner = memoryAllocator.Allocate(num5); using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(length, AllocationOptions.Clean); using IMemoryOwner memoryOwner3 = memoryAllocator.Allocate(length, AllocationOptions.Clean); Span span = memoryOwner.GetSpan(); Span filter = memoryOwner2.GetSpan(); Span attempt = memoryOwner3.GetSpan(); for (int j = num3; j < num2; j += Adam7.RowIncrement[i]) { ReadOnlySpan readOnlySpan = quantized.DangerousGetRowSpan(j); int k = num4; int num7 = 0; for (; k < num; k += Adam7.ColumnIncrement[i]) { span[num7++] = readOnlySpan[k]; } EncodeAdam7IndexedPixelRow(span, ref filter, ref attempt); deflateStream.Write(filter); SwapScanlineBuffers(); } } } private void WriteEndChunk(Stream stream) { WriteChunk(stream, PngChunkType.End, null); } private void WriteChunk(Stream stream, PngChunkType type, Span data) { WriteChunk(stream, type, data, 0, data.Length); } private void WriteChunk(Stream stream, PngChunkType type, Span data, int offset, int length) { BinaryPrimitives.WriteInt32BigEndian(buffer, length); BinaryPrimitives.WriteUInt32BigEndian(buffer.AsSpan(4, 4), (uint)type); stream.Write(buffer, 0, 8); uint num = Crc32.Calculate(buffer.AsSpan(4, 4)); if (data != null && length > 0) { stream.Write(data, offset, length); num = Crc32.Calculate(num, data.Slice(offset, length)); } BinaryPrimitives.WriteUInt32BigEndian(buffer, num); stream.Write(buffer, 0, 4); } private int CalculateScanlineLength(int width) { int num = ((bitDepth == 16) ? 16 : 8); int num2 = width * bitDepth * bytesPerPixel; int num3 = num2 % num; if (num3 != 0) { num2 += num - num3; } return num2 / num; } private void SwapScanlineBuffers() { IMemoryOwner memoryOwner = previousScanline; previousScanline = currentScanline; currentScanline = memoryOwner; } private static void SwapSpans(ref Span a, ref Span b) { Span span = b; b = a; a = span; } } internal static class PngEncoderHelpers { public static void ScaleDownFrom8BitArray(ReadOnlySpan source, Span result, int bits, float scale = 1f) { ref byte reference = ref MemoryMarshal.GetReference(source); ref byte reference2 = ref MemoryMarshal.GetReference(result); int num = 8 - bits; byte b = (byte)(255 >> num); byte b2 = (byte)num; int num2 = 0; int num3 = 0; for (int i = 0; i < source.Length; i++) { int num4 = (int)MathF.Round((float)(int)Unsafe.Add(ref reference, i) / scale) & b; num2 |= num4 << num; if (num == 0) { num = b2; Unsafe.Add(ref reference2, num3) = (byte)num2; num3++; num2 = 0; } else { num -= bits; } } if (num != b2) { Unsafe.Add(ref reference2, num3) = (byte)num2; } } } internal class PngEncoderOptions : IPngEncoderOptions { public PngBitDepth? BitDepth { get; set; } public PngColorType? ColorType { get; set; } public PngFilterMethod? FilterMethod { get; set; } public PngCompressionLevel CompressionLevel { get; } = PngCompressionLevel.Level6; public int TextCompressionThreshold { get; } public float? Gamma { get; set; } public IQuantizer Quantizer { get; set; } public byte Threshold { get; } public PngInterlaceMode? InterlaceMethod { get; set; } public PngChunkFilter? ChunkFilter { get; set; } public bool IgnoreMetadata { get; set; } public PngTransparentColorMode TransparentColorMode { get; set; } public PngEncoderOptions(IPngEncoderOptions source) { BitDepth = source.BitDepth; ColorType = source.ColorType; FilterMethod = source.FilterMethod; CompressionLevel = source.CompressionLevel; TextCompressionThreshold = source.TextCompressionThreshold; Gamma = source.Gamma; Quantizer = source.Quantizer; Threshold = source.Threshold; InterlaceMethod = source.InterlaceMethod; ChunkFilter = source.ChunkFilter; IgnoreMetadata = source.IgnoreMetadata; TransparentColorMode = source.TransparentColorMode; } } internal static class PngEncoderOptionsHelpers { public static void AdjustOptions(PngEncoderOptions options, PngMetadata pngMetadata, out bool use16Bit, out int bytesPerPixel) where TPixel : unmanaged, IPixel { PngEncoderOptions pngEncoderOptions = options; float? gamma = pngEncoderOptions.Gamma; float valueOrDefault = gamma.GetValueOrDefault(); if (!gamma.HasValue) { valueOrDefault = pngMetadata.Gamma; PngEncoderOptions pngEncoderOptions2 = pngEncoderOptions; float? gamma2 = valueOrDefault; pngEncoderOptions2.Gamma = gamma2; } pngEncoderOptions = options; PngColorType? colorType = pngEncoderOptions.ColorType; PngColorType valueOrDefault2 = colorType.GetValueOrDefault(); if (!colorType.HasValue) { valueOrDefault2 = pngMetadata.ColorType ?? SuggestColorType(); PngEncoderOptions pngEncoderOptions3 = pngEncoderOptions; PngColorType? colorType2 = valueOrDefault2; pngEncoderOptions3.ColorType = colorType2; } pngEncoderOptions = options; PngBitDepth? bitDepth = pngEncoderOptions.BitDepth; PngBitDepth valueOrDefault3 = bitDepth.GetValueOrDefault(); if (!bitDepth.HasValue) { valueOrDefault3 = pngMetadata.BitDepth ?? SuggestBitDepth(); PngEncoderOptions pngEncoderOptions4 = pngEncoderOptions; PngBitDepth? bitDepth2 = valueOrDefault3; pngEncoderOptions4.BitDepth = bitDepth2; } if (!options.FilterMethod.HasValue) { if (options.ColorType == PngColorType.Palette) { options.FilterMethod = PngFilterMethod.None; } else { options.FilterMethod = PngFilterMethod.Paeth; } } byte value = (byte)options.BitDepth.Value; if (Array.IndexOf(PngConstants.ColorTypes[options.ColorType.Value], value) == -1) { options.BitDepth = PngBitDepth.Bit8; } pngEncoderOptions = options; if (!pngEncoderOptions.InterlaceMethod.HasValue) { PngInterlaceMode? pngInterlaceMode = (pngEncoderOptions.InterlaceMethod = pngMetadata.InterlaceMethod); } use16Bit = options.BitDepth == PngBitDepth.Bit16; bytesPerPixel = CalculateBytesPerPixel(options.ColorType, use16Bit); if (options.IgnoreMetadata) { options.ChunkFilter = PngChunkFilter.ExcludeAll; } } public static IndexedImageFrame CreateQuantizedFrame(PngEncoderOptions options, Image image) where TPixel : unmanaged, IPixel { if (options.ColorType != PngColorType.Palette) { return null; } if (options.Quantizer == null) { int colorCountForBitDepth = ColorNumerics.GetColorCountForBitDepth((int)options.BitDepth.Value); options.Quantizer = new WuQuantizer(new QuantizerOptions { MaxColors = colorCountForBitDepth }); } using IQuantizer quantizer = options.Quantizer.CreatePixelSpecificQuantizer(image.GetConfiguration()); ImageFrame rootFrame = image.Frames.RootFrame; return quantizer.BuildPaletteAndQuantizeFrame(rootFrame, rootFrame.Bounds()); } public static byte CalculateBitDepth(PngEncoderOptions options, IndexedImageFrame quantizedFrame) where TPixel : unmanaged, IPixel { byte b2; if (options.ColorType == PngColorType.Palette) { byte val = (byte)Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(quantizedFrame.Palette.Length), 1, 8); byte b = Math.Max((byte)options.BitDepth.Value, val); switch (b) { case 3: b = 4; break; case 5: case 6: case 7: b = 8; break; } b2 = b; } else { b2 = (byte)options.BitDepth.Value; } if (Array.IndexOf(PngConstants.ColorTypes[options.ColorType.Value], b2) == -1) { throw new NotSupportedException("Bit depth is not supported or not valid."); } return b2; } private static int CalculateBytesPerPixel(PngColorType? pngColorType, bool use16Bit) { return pngColorType switch { PngColorType.Grayscale => (!use16Bit) ? 1 : 2, PngColorType.GrayscaleWithAlpha => use16Bit ? 4 : 2, PngColorType.Palette => 1, PngColorType.Rgb => use16Bit ? 6 : 3, _ => use16Bit ? 8 : 4, }; } private static PngColorType SuggestColorType() where TPixel : unmanaged, IPixel { Type typeFromHandle = typeof(TPixel); if ((object)typeFromHandle != null) { if (typeFromHandle == typeof(A8)) { return PngColorType.GrayscaleWithAlpha; } if (typeFromHandle == typeof(Argb32)) { return PngColorType.RgbWithAlpha; } if (typeFromHandle == typeof(Bgr24)) { return PngColorType.Rgb; } if (typeFromHandle == typeof(Bgra32)) { return PngColorType.RgbWithAlpha; } if (typeFromHandle == typeof(L8)) { return PngColorType.Grayscale; } if (typeFromHandle == typeof(L16)) { return PngColorType.Grayscale; } if (typeFromHandle == typeof(La16)) { return PngColorType.GrayscaleWithAlpha; } if (typeFromHandle == typeof(La32)) { return PngColorType.GrayscaleWithAlpha; } if (typeFromHandle == typeof(Rgb24)) { return PngColorType.Rgb; } if (typeFromHandle == typeof(Rgba32)) { return PngColorType.RgbWithAlpha; } if (typeFromHandle == typeof(Rgb48)) { return PngColorType.Rgb; } if (typeFromHandle == typeof(Rgba64)) { return PngColorType.RgbWithAlpha; } if (typeFromHandle == typeof(RgbaVector)) { return PngColorType.RgbWithAlpha; } } return PngColorType.RgbWithAlpha; } private static PngBitDepth SuggestBitDepth() where TPixel : unmanaged, IPixel { Type typeFromHandle = typeof(TPixel); if ((object)typeFromHandle != null) { if (typeFromHandle == typeof(A8)) { return PngBitDepth.Bit8; } if (typeFromHandle == typeof(Argb32)) { return PngBitDepth.Bit8; } if (typeFromHandle == typeof(Bgr24)) { return PngBitDepth.Bit8; } if (typeFromHandle == typeof(Bgra32)) { return PngBitDepth.Bit8; } if (typeFromHandle == typeof(L8)) { return PngBitDepth.Bit8; } if (typeFromHandle == typeof(L16)) { return PngBitDepth.Bit16; } if (typeFromHandle == typeof(La16)) { return PngBitDepth.Bit8; } if (typeFromHandle == typeof(La32)) { return PngBitDepth.Bit16; } if (typeFromHandle == typeof(Rgb24)) { return PngBitDepth.Bit8; } if (typeFromHandle == typeof(Rgba32)) { return PngBitDepth.Bit8; } if (typeFromHandle == typeof(Rgb48)) { return PngBitDepth.Bit16; } if (typeFromHandle == typeof(Rgba64)) { return PngBitDepth.Bit16; } if (typeFromHandle == typeof(RgbaVector)) { return PngBitDepth.Bit16; } } return PngBitDepth.Bit8; } } public enum PngFilterMethod { None, Sub, Up, Average, Paeth, Adaptive } public sealed class PngFormat : IImageFormat, IImageFormat { public static PngFormat Instance { get; } = new PngFormat(); public string Name => "PNG"; public string DefaultMimeType => "image/png"; public IEnumerable MimeTypes => PngConstants.MimeTypes; public IEnumerable FileExtensions => PngConstants.FileExtensions; private PngFormat() { } public PngMetadata CreateDefaultFormatMetadata() { return new PngMetadata(); } } internal readonly struct PngHeader { public const int Size = 13; public int Width { get; } public int Height { get; } public byte BitDepth { get; } public PngColorType ColorType { get; } public byte CompressionMethod { get; } public byte FilterMethod { get; } public PngInterlaceMode InterlaceMethod { get; } public PngHeader(int width, int height, byte bitDepth, PngColorType colorType, byte compressionMethod, byte filterMethod, PngInterlaceMode interlaceMethod) { Width = width; Height = height; BitDepth = bitDepth; ColorType = colorType; CompressionMethod = compressionMethod; FilterMethod = filterMethod; InterlaceMethod = interlaceMethod; } public void Validate() { if (!PngConstants.ColorTypes.TryGetValue(ColorType, out var value)) { throw new NotSupportedException($"Invalid or unsupported color type. Was '{ColorType}'."); } if (value.AsSpan().IndexOf(BitDepth) == -1) { throw new NotSupportedException($"Invalid or unsupported bit depth. Was '{BitDepth}'."); } if (FilterMethod != 0) { throw new NotSupportedException($"Invalid filter method. Expected 0. Was '{FilterMethod}'."); } if (InterlaceMethod != PngInterlaceMode.None && InterlaceMethod != PngInterlaceMode.Adam7) { throw new NotSupportedException($"Invalid interlace method. Expected 'None' or 'Adam7'. Was '{InterlaceMethod}'."); } } public void WriteTo(Span buffer) { BinaryPrimitives.WriteInt32BigEndian(buffer.Slice(0, 4), Width); BinaryPrimitives.WriteInt32BigEndian(buffer.Slice(4, 4), Height); buffer[8] = BitDepth; buffer[9] = (byte)ColorType; buffer[10] = CompressionMethod; buffer[11] = FilterMethod; buffer[12] = (byte)InterlaceMethod; } public static PngHeader Parse(ReadOnlySpan data) { return new PngHeader(BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)), BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)), data[8], (PngColorType)data[9], data[10], data[11], (PngInterlaceMode)data[12]); } } public sealed class PngImageFormatDetector : IImageFormatDetector { public int HeaderSize => 8; public IImageFormat DetectFormat(ReadOnlySpan header) { if (!IsSupportedFileFormat(header)) { return null; } return PngFormat.Instance; } private bool IsSupportedFileFormat(ReadOnlySpan header) { if (header.Length >= HeaderSize) { return BinaryPrimitives.ReadUInt64BigEndian(header) == 9894494448401390090uL; } return false; } } public enum PngInterlaceMode : byte { None, Adam7 } public class PngMetadata : IDeepCloneable { public PngBitDepth? BitDepth { get; set; } public PngColorType? ColorType { get; set; } public PngInterlaceMode? InterlaceMethod { get; set; } = PngInterlaceMode.None; public float Gamma { get; set; } public Rgb24? TransparentRgb24 { get; set; } public Rgb48? TransparentRgb48 { get; set; } public L8? TransparentL8 { get; set; } public L16? TransparentL16 { get; set; } public bool HasTransparency { get; set; } public IList TextData { get; set; } = new List(); public PngMetadata() { } private PngMetadata(PngMetadata other) { BitDepth = other.BitDepth; ColorType = other.ColorType; Gamma = other.Gamma; InterlaceMethod = other.InterlaceMethod; HasTransparency = other.HasTransparency; TransparentL8 = other.TransparentL8; TransparentL16 = other.TransparentL16; TransparentRgb24 = other.TransparentRgb24; TransparentRgb48 = other.TransparentRgb48; for (int i = 0; i < other.TextData.Count; i++) { TextData.Add(other.TextData[i]); } } public IDeepCloneable DeepClone() { return new PngMetadata(this); } } internal static class PngScanlineProcessor { public static void ProcessGrayscaleScanline(in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, bool hasTrans, L16 luminance16Trans, L8 luminanceTrans) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); ref byte reference = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel reference2 = ref MemoryMarshal.GetReference(rowSpan); int num = 255 / (ColorNumerics.GetColorCountForBitDepth(header.BitDepth) - 1); if (!hasTrans) { if (header.BitDepth == 16) { int num2 = 0; int num3 = 0; while (num2 < header.Width) { ushort source = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num3, 2)); val.FromL16(Unsafe.As(ref source)); Unsafe.Add(ref reference2, num2) = val; num2++; num3 += 2; } } else { for (int i = 0; i < header.Width; i++) { byte source2 = (byte)(Unsafe.Add(ref reference, i) * num); val.FromL8(Unsafe.As(ref source2)); Unsafe.Add(ref reference2, i) = val; } } } else if (header.BitDepth == 16) { La32 source3 = default(La32); int num4 = 0; int num5 = 0; while (num4 < header.Width) { ushort num6 = (source3.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num5, 2))); source3.A = (ushort)((!num6.Equals(luminance16Trans.PackedValue)) ? ushort.MaxValue : 0); val.FromLa32(source3); Unsafe.Add(ref reference2, num4) = val; num4++; num5 += 2; } } else { La16 source4 = default(La16); byte obj = (byte)(luminanceTrans.PackedValue * num); for (int j = 0; j < header.Width; j++) { byte b = (source4.L = (byte)(Unsafe.Add(ref reference, j) * num)); source4.A = (byte)((!b.Equals(obj)) ? byte.MaxValue : 0); val.FromLa16(source4); Unsafe.Add(ref reference2, j) = val; } } } public static void ProcessInterlacedGrayscaleScanline(in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, int pixelOffset, int increment, bool hasTrans, L16 luminance16Trans, L8 luminanceTrans) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); ref byte reference = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel reference2 = ref MemoryMarshal.GetReference(rowSpan); int num = 255 / (ColorNumerics.GetColorCountForBitDepth(header.BitDepth) - 1); if (!hasTrans) { if (header.BitDepth == 16) { int num2 = pixelOffset; int num3 = 0; while (num2 < header.Width) { ushort source = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num3, 2)); val.FromL16(Unsafe.As(ref source)); Unsafe.Add(ref reference2, num2) = val; num2 += increment; num3 += 2; } } else { int num4 = pixelOffset; int num5 = 0; while (num4 < header.Width) { byte source2 = (byte)(Unsafe.Add(ref reference, num5) * num); val.FromL8(Unsafe.As(ref source2)); Unsafe.Add(ref reference2, num4) = val; num4 += increment; num5++; } } } else if (header.BitDepth == 16) { La32 source3 = default(La32); int num6 = pixelOffset; int num7 = 0; while (num6 < header.Width) { ushort num8 = (source3.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num7, 2))); source3.A = (ushort)((!num8.Equals(luminance16Trans.PackedValue)) ? ushort.MaxValue : 0); val.FromLa32(source3); Unsafe.Add(ref reference2, num6) = val; num6 += increment; num7 += 2; } } else { La16 source4 = default(La16); byte obj = (byte)(luminanceTrans.PackedValue * num); int num9 = pixelOffset; int num10 = 0; while (num9 < header.Width) { byte b = (source4.L = (byte)(Unsafe.Add(ref reference, num10) * num)); source4.A = (byte)((!b.Equals(obj)) ? byte.MaxValue : 0); val.FromLa16(source4); Unsafe.Add(ref reference2, num9) = val; num9 += increment; num10++; } } } public static void ProcessGrayscaleWithAlphaScanline(in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, int bytesPerPixel, int bytesPerSample) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); ref byte reference = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel reference2 = ref MemoryMarshal.GetReference(rowSpan); if (header.BitDepth == 16) { La32 source = default(La32); int num = 0; int num2 = 0; while (num < header.Width) { source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2, 2)); source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + 2, 2)); val.FromLa32(source); Unsafe.Add(ref reference2, num) = val; num++; num2 += 4; } } else { La16 source2 = default(La16); for (int i = 0; i < header.Width; i++) { int num3 = i * bytesPerPixel; source2.L = Unsafe.Add(ref reference, num3); source2.A = Unsafe.Add(ref reference, num3 + bytesPerSample); val.FromLa16(source2); Unsafe.Add(ref reference2, i) = val; } } } public static void ProcessInterlacedGrayscaleWithAlphaScanline(in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, int pixelOffset, int increment, int bytesPerPixel, int bytesPerSample) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); ref byte reference = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel reference2 = ref MemoryMarshal.GetReference(rowSpan); if (header.BitDepth == 16) { La32 source = default(La32); int num = pixelOffset; int num2 = 0; while (num < header.Width) { source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2, 2)); source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + 2, 2)); val.FromLa32(source); Unsafe.Add(ref reference2, num) = val; num += increment; num2 += 4; } } else { int num3 = 0; La16 source2 = default(La16); for (int i = pixelOffset; i < header.Width; i += increment) { source2.L = Unsafe.Add(ref reference, num3); source2.A = Unsafe.Add(ref reference, num3 + bytesPerSample); val.FromLa16(source2); Unsafe.Add(ref reference2, i) = val; num3 += bytesPerPixel; } } } public static void ProcessPaletteScanline(in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, ReadOnlySpan palette, byte[] paletteAlpha) where TPixel : unmanaged, IPixel { if (palette.IsEmpty) { PngThrowHelper.ThrowMissingPalette(); } TPixel val = default(TPixel); ref byte reference = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel reference2 = ref MemoryMarshal.GetReference(rowSpan); ReadOnlySpan span = MemoryMarshal.Cast(palette); ref Rgb24 reference3 = ref MemoryMarshal.GetReference(span); int num = span.Length - 1; if (paletteAlpha != null && paletteAlpha.Length != 0) { Rgba32 source = default(Rgba32); ref byte source2 = ref paletteAlpha[0]; for (int i = 0; i < header.Width; i++) { int num2 = Numerics.Clamp(Unsafe.Add(ref reference, i), 0, num); source.Rgb = Unsafe.Add(ref reference3, num2); source.A = ((paletteAlpha.Length > num2) ? Unsafe.Add(ref source2, num2) : byte.MaxValue); val.FromRgba32(source); Unsafe.Add(ref reference2, i) = val; } } else { for (int j = 0; j < header.Width; j++) { uint num3 = Unsafe.Add(ref reference, j); Rgb24 source3 = Unsafe.Add(ref reference3, (int)Math.Min(num3, num)); val.FromRgb24(source3); Unsafe.Add(ref reference2, j) = val; } } } public static void ProcessInterlacedPaletteScanline(in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, int pixelOffset, int increment, ReadOnlySpan palette, byte[] paletteAlpha) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); ref byte reference = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel reference2 = ref MemoryMarshal.GetReference(rowSpan); ref Rgb24 reference3 = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(palette)); if (paletteAlpha != null && paletteAlpha.Length != 0) { Rgba32 source = default(Rgba32); ref byte source2 = ref paletteAlpha[0]; int num = pixelOffset; int num2 = 0; while (num < header.Width) { int num3 = Unsafe.Add(ref reference, num2); source.A = ((paletteAlpha.Length > num3) ? Unsafe.Add(ref source2, num3) : byte.MaxValue); source.Rgb = Unsafe.Add(ref reference3, num3); val.FromRgba32(source); Unsafe.Add(ref reference2, num) = val; num += increment; num2++; } } else { int num4 = pixelOffset; int num5 = 0; while (num4 < header.Width) { int elementOffset = Unsafe.Add(ref reference, num5); Rgb24 source3 = Unsafe.Add(ref reference3, elementOffset); val.FromRgb24(source3); Unsafe.Add(ref reference2, num4) = val; num4 += increment; num5++; } } } public static void ProcessRgbScanline(Configuration configuration, in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, int bytesPerPixel, int bytesPerSample, bool hasTrans, Rgb48 rgb48Trans, Rgb24 rgb24Trans) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); ref TPixel reference = ref MemoryMarshal.GetReference(rowSpan); if (!hasTrans) { if (header.BitDepth == 16) { Rgb48 source = default(Rgb48); int num = 0; int num2 = 0; while (num < header.Width) { source.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2, bytesPerSample)); source.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + bytesPerSample, bytesPerSample)); source.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + 2 * bytesPerSample, bytesPerSample)); val.FromRgb48(source); Unsafe.Add(ref reference, num) = val; num++; num2 += bytesPerPixel; } } else { PixelOperations.Instance.FromRgb24Bytes(configuration, scanlineSpan, rowSpan, header.Width); } } else if (header.BitDepth == 16) { Rgb48 rgb = default(Rgb48); Rgba64 source2 = default(Rgba64); int num3 = 0; int num4 = 0; while (num3 < header.Width) { rgb.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num4, bytesPerSample)); rgb.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num4 + bytesPerSample, bytesPerSample)); rgb.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num4 + 2 * bytesPerSample, bytesPerSample)); source2.Rgb = rgb; source2.A = (ushort)((!rgb.Equals(rgb48Trans)) ? ushort.MaxValue : 0); val.FromRgba64(source2); Unsafe.Add(ref reference, num3) = val; num3++; num4 += bytesPerPixel; } } else { Rgba32 source3 = default(Rgba32); ref Rgb24 reference2 = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(scanlineSpan)); for (int i = 0; i < header.Width; i++) { ref Rgb24 reference3 = ref Unsafe.Add(ref reference2, i); source3.Rgb = reference3; source3.A = (byte)((!reference3.Equals(rgb24Trans)) ? byte.MaxValue : 0); val.FromRgba32(source3); Unsafe.Add(ref reference, i) = val; } } } public static void ProcessInterlacedRgbScanline(in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, int pixelOffset, int increment, int bytesPerPixel, int bytesPerSample, bool hasTrans, Rgb48 rgb48Trans, Rgb24 rgb24Trans) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); ref byte reference = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel reference2 = ref MemoryMarshal.GetReference(rowSpan); if (header.BitDepth == 16) { if (hasTrans) { Rgb48 rgb = default(Rgb48); Rgba64 source = default(Rgba64); int num = pixelOffset; int num2 = 0; while (num < header.Width) { rgb.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2, bytesPerSample)); rgb.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + bytesPerSample, bytesPerSample)); rgb.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + 2 * bytesPerSample, bytesPerSample)); source.Rgb = rgb; source.A = (ushort)((!rgb.Equals(rgb48Trans)) ? ushort.MaxValue : 0); val.FromRgba64(source); Unsafe.Add(ref reference2, num) = val; num += increment; num2 += bytesPerPixel; } } else { Rgb48 source2 = default(Rgb48); int num3 = pixelOffset; int num4 = 0; while (num3 < header.Width) { source2.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num4, bytesPerSample)); source2.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num4 + bytesPerSample, bytesPerSample)); source2.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num4 + 2 * bytesPerSample, bytesPerSample)); val.FromRgb48(source2); Unsafe.Add(ref reference2, num3) = val; num3 += increment; num4 += bytesPerPixel; } } } else if (hasTrans) { Rgba32 source3 = default(Rgba32); int num5 = pixelOffset; int num6 = 0; while (num5 < header.Width) { source3.R = Unsafe.Add(ref reference, num6); source3.G = Unsafe.Add(ref reference, num6 + bytesPerSample); source3.B = Unsafe.Add(ref reference, num6 + 2 * bytesPerSample); source3.A = (byte)((!rgb24Trans.Equals(source3.Rgb)) ? byte.MaxValue : 0); val.FromRgba32(source3); Unsafe.Add(ref reference2, num5) = val; num5 += increment; num6 += bytesPerPixel; } } else { Rgb24 source4 = default(Rgb24); int num7 = pixelOffset; int num8 = 0; while (num7 < header.Width) { source4.R = Unsafe.Add(ref reference, num8); source4.G = Unsafe.Add(ref reference, num8 + bytesPerSample); source4.B = Unsafe.Add(ref reference, num8 + 2 * bytesPerSample); val.FromRgb24(source4); Unsafe.Add(ref reference2, num7) = val; num7 += increment; num8 += bytesPerPixel; } } } public static void ProcessRgbaScanline(Configuration configuration, in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, int bytesPerPixel, int bytesPerSample) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); ref TPixel reference = ref MemoryMarshal.GetReference(rowSpan); if (header.BitDepth == 16) { Rgba64 source = default(Rgba64); int num = 0; int num2 = 0; while (num < header.Width) { source.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2, bytesPerSample)); source.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + bytesPerSample, bytesPerSample)); source.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + 2 * bytesPerSample, bytesPerSample)); source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + 3 * bytesPerSample, bytesPerSample)); val.FromRgba64(source); Unsafe.Add(ref reference, num) = val; num++; num2 += bytesPerPixel; } } else { PixelOperations.Instance.FromRgba32Bytes(configuration, scanlineSpan, rowSpan, header.Width); } } public static void ProcessInterlacedRgbaScanline(in PngHeader header, ReadOnlySpan scanlineSpan, Span rowSpan, int pixelOffset, int increment, int bytesPerPixel, int bytesPerSample) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); ref byte reference = ref MemoryMarshal.GetReference(scanlineSpan); ref TPixel reference2 = ref MemoryMarshal.GetReference(rowSpan); if (header.BitDepth == 16) { Rgba64 source = default(Rgba64); int num = pixelOffset; int num2 = 0; while (num < header.Width) { source.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2, bytesPerSample)); source.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + bytesPerSample, bytesPerSample)); source.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + 2 * bytesPerSample, bytesPerSample)); source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(num2 + 3 * bytesPerSample, bytesPerSample)); val.FromRgba64(source); Unsafe.Add(ref reference2, num) = val; num += increment; num2 += bytesPerPixel; } } else { Rgba32 source2 = default(Rgba32); int num3 = pixelOffset; int num4 = 0; while (num3 < header.Width) { source2.R = Unsafe.Add(ref reference, num4); source2.G = Unsafe.Add(ref reference, num4 + bytesPerSample); source2.B = Unsafe.Add(ref reference, num4 + 2 * bytesPerSample); source2.A = Unsafe.Add(ref reference, num4 + 3 * bytesPerSample); val.FromRgba32(source2); Unsafe.Add(ref reference2, num3) = val; num3 += increment; num4 += bytesPerPixel; } } } } public readonly struct PngTextData : IEquatable { public string Keyword { get; } public string Value { get; } public string LanguageTag { get; } public string TranslatedKeyword { get; } public PngTextData(string keyword, string value, string languageTag, string translatedKeyword) { Guard.NotNullOrWhiteSpace(keyword, "keyword"); Keyword = keyword.Trim(); Value = value; LanguageTag = languageTag; TranslatedKeyword = translatedKeyword; } public static bool operator ==(PngTextData left, PngTextData right) { return left.Equals(right); } public static bool operator !=(PngTextData left, PngTextData right) { return !(left == right); } public override bool Equals(object obj) { if (obj is PngTextData other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(Keyword, Value, LanguageTag, TranslatedKeyword); } public override string ToString() { return "PngTextData [ Name=" + Keyword + ", Value=" + Value + " ]"; } public bool Equals(PngTextData other) { if (Keyword.Equals(other.Keyword) && Value.Equals(other.Value) && LanguageTag.Equals(other.LanguageTag)) { return TranslatedKeyword.Equals(other.TranslatedKeyword); } return false; } } internal static class PngThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException) { throw new InvalidImageContentException(errorMessage, innerException); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNoHeader() { throw new InvalidImageContentException("PNG Image does not contain a header chunk"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNoData() { throw new InvalidImageContentException("PNG Image does not contain a data chunk"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowMissingPalette() { throw new InvalidImageContentException("PNG Image does not contain a palette chunk"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidChunkType() { throw new InvalidImageContentException("Invalid PNG data."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidChunkType(string message) { throw new InvalidImageContentException(message); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidChunkCrc(string chunkTypeName) { throw new InvalidImageContentException("CRC Error. PNG " + chunkTypeName + " chunk is corrupt!"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotSupportedColor() { throw new NotSupportedException("Unsupported PNG color type"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowUnknownFilter() { throw new InvalidImageContentException("Unknown filter type."); } } public enum PngTransparentColorMode { Preserve, Clear } } namespace SixLabors.ImageSharp.Formats.Png.Filters { internal static class AverageFilter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(Span scanline, Span previousScanline, int bytesPerPixel) { if (Sse2.IsSupported && bytesPerPixel == 4) { DecodeSse2(scanline, previousScanline); } else { DecodeScalar(scanline, previousScanline, bytesPerPixel); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void DecodeSse2(Span scanline, Span previousScanline) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(previousScanline); Vector128 vector = Vector128.Zero; Vector128 right = Vector128.Create((byte)1); int num = scanline.Length; nint num2 = 1; while (num >= 4) { ref byte source = ref Unsafe.Add(ref reference, num2); Vector128 left = vector; Vector128 right2 = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref Unsafe.Add(ref reference2, num2))).AsByte(); vector = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref source)).AsByte(); Vector128 left2 = Sse2.Average(left, right2); Vector128 right3 = Sse2.And(Sse2.Xor(left, right2), right); left2 = Sse2.Subtract(left2, right3); vector = Sse2.Add(vector, left2); Unsafe.As(ref source) = Sse2.ConvertToInt32(vector.AsInt32()); num -= 4; num2 += 4; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void DecodeScalar(Span scanline, Span previousScanline, int bytesPerPixel) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(previousScanline); nint num; for (num = 1; num <= bytesPerPixel; num++) { ref byte reference3 = ref Unsafe.Add(ref reference, num); byte b = Unsafe.Add(ref reference2, num); reference3 = (byte)(reference3 + (b >> 1)); } for (; num < scanline.Length; num++) { ref byte reference4 = ref Unsafe.Add(ref reference, num); byte left = Unsafe.Add(ref reference, num - bytesPerPixel); byte above = Unsafe.Add(ref reference2, num); reference4 = (byte)(reference4 + Average(left, above)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, int bytesPerPixel, out int sum) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(previousScanline); ref byte reference3 = ref MemoryMarshal.GetReference(result); sum = 0; reference3 = 3; int num = 0; while (num < bytesPerPixel) { byte b = Unsafe.Add(ref reference, num); byte b2 = Unsafe.Add(ref reference2, num); num++; ref byte reference4 = ref Unsafe.Add(ref reference3, num); reference4 = (byte)(b - (b2 >> 1)); sum += Numerics.Abs((sbyte)reference4); } if (Avx2.IsSupported) { Vector256 zero = Vector256.Zero; Vector256 vector = Vector256.Zero; Vector256 right = Avx2.CompareEqual(vector, vector).AsByte(); int num2 = num - bytesPerPixel; while (num + Vector256.Count <= scanline.Length) { Vector256 left = Unsafe.As>(ref Unsafe.Add(ref reference, num)); Vector256 left2 = Unsafe.As>(ref Unsafe.Add(ref reference, num2)); Vector256 right2 = Avx2.Xor(Avx2.Average(right: Avx2.Xor(Unsafe.As>(ref Unsafe.Add(ref reference2, num)), right), left: Avx2.Xor(left2, right)), right); Vector256 vector2 = Avx2.Subtract(left, right2); Unsafe.As>(ref Unsafe.Add(ref reference3, num + 1)) = vector2; num += Vector256.Count; vector = Avx2.Add(vector, Avx2.SumAbsoluteDifferences(Avx2.Abs(vector2.AsSByte()), zero).AsInt32()); num2 += Vector256.Count; } sum += Numerics.EvenReduceSum(vector); } else if (Sse2.IsSupported) { Vector128 zero2 = Vector128.Zero; Vector128 zero3 = Vector128.Zero; Vector128 vector3 = Vector128.Zero; Vector128 vector4 = Sse2.CompareEqual(vector3, vector3).AsByte(); int num3 = num - bytesPerPixel; while (num + Vector128.Count <= scanline.Length) { Vector128 left3 = Unsafe.As>(ref Unsafe.Add(ref reference, num)); Vector128 left4 = Unsafe.As>(ref Unsafe.Add(ref reference, num3)); Vector128 right3 = Sse2.Xor(Sse2.Average(right: Sse2.Xor(Unsafe.As>(ref Unsafe.Add(ref reference2, num)), vector4), left: Sse2.Xor(left4, vector4)), vector4); Vector128 vector5 = Sse2.Subtract(left3, right3); Unsafe.As>(ref Unsafe.Add(ref reference3, num + 1)) = vector5; num += Vector128.Count; Vector128 left5; if (Ssse3.IsSupported) { left5 = Ssse3.Abs(vector5.AsSByte()).AsSByte(); } else { Vector128 left6 = Sse2.CompareGreaterThan(vector5.AsSByte(), zero2); left6 = Sse2.Xor(left6, vector4.AsSByte()); left5 = Sse2.Xor(Sse2.Add(vector5.AsSByte(), left6), left6); } Vector128 left7 = Sse2.UnpackLow(left5, zero2).AsInt16(); Vector128 left8 = Sse2.UnpackHigh(left5, zero2).AsInt16(); Vector128 right4 = Sse2.UnpackLow(left7, zero3).AsInt32(); Vector128 right5 = Sse2.UnpackHigh(left7, zero3).AsInt32(); vector3 = Sse2.Add(vector3, right4); vector3 = Sse2.Add(vector3, right5); right4 = Sse2.UnpackLow(left8, zero3).AsInt32(); right5 = Sse2.UnpackHigh(left8, zero3).AsInt32(); vector3 = Sse2.Add(vector3, right4); vector3 = Sse2.Add(vector3, right5); num3 += Vector128.Count; } sum += Numerics.ReduceSum(vector3); } int num4 = num - bytesPerPixel; while (num < scanline.Length) { byte b3 = Unsafe.Add(ref reference, num); byte left9 = Unsafe.Add(ref reference, num4); byte above = Unsafe.Add(ref reference2, num); num++; ref byte reference5 = ref Unsafe.Add(ref reference3, num); reference5 = (byte)(b3 - Average(left9, above)); sum += Numerics.Abs((sbyte)reference5); num4++; } sum -= 3; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Average(byte left, byte above) { return left + above >> 1; } } internal enum FilterType { None, Sub, Up, Average, Paeth } internal static class NoneFilter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(ReadOnlySpan scanline, Span result) { result[0] = 0; result = result.Slice(1); scanline.Slice(0, Math.Min(scanline.Length, result.Length)).CopyTo(result); } } internal static class PaethFilter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(Span scanline, Span previousScanline, int bytesPerPixel) { if (Sse41.IsSupported && bytesPerPixel == 4) { DecodeSse41(scanline, previousScanline); } else { DecodeScalar(scanline, previousScanline, bytesPerPixel); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void DecodeSse41(Span scanline, Span previousScanline) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(previousScanline); Vector128 vector = Vector128.Zero; Vector128 vector2 = Vector128.Zero; int num = scanline.Length; nint num2 = 1; while (num >= 4) { ref byte source = ref Unsafe.Add(ref reference, num2); Vector128 vector3 = vector; Vector128 vector4 = vector2; vector = Sse2.UnpackLow(Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref Unsafe.Add(ref reference2, num2))).AsByte(), Vector128.Zero); vector2 = Sse2.UnpackLow(Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref source)).AsByte(), Vector128.Zero); Vector128 vector5 = Sse2.Subtract(vector.AsInt16(), vector3.AsInt16()); Vector128 vector6 = Sse2.Subtract(vector4.AsInt16(), vector3.AsInt16()); Vector128 vector7 = Sse2.Add(vector5.AsInt16(), vector6.AsInt16()); vector5 = Ssse3.Abs(vector5.AsInt16()).AsInt16(); vector6 = Ssse3.Abs(vector6.AsInt16()).AsInt16(); Vector128 left = Sse2.Min(Ssse3.Abs(vector7.AsInt16()).AsInt16(), Sse2.Min(vector5, vector6)); Vector128 right = Sse41.BlendVariable(Sse41.BlendVariable(vector3, vector, Sse2.CompareEqual(left, vector6).AsByte()), vector4, Sse2.CompareEqual(left, vector5).AsByte()); vector2 = Sse2.Add(vector2, right); Unsafe.As(ref source) = Sse2.ConvertToInt32(Sse2.PackUnsignedSaturate(vector2.AsInt16(), vector2.AsInt16()).AsInt32()); num -= 4; num2 += 4; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void DecodeScalar(Span scanline, Span previousScanline, int bytesPerPixel) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(previousScanline); int num = bytesPerPixel + 1; int i; for (i = 1; i < num; i++) { ref byte reference3 = ref Unsafe.Add(ref reference, i); byte b = Unsafe.Add(ref reference2, i); reference3 += b; } for (; i < scanline.Length; i++) { ref byte reference4 = ref Unsafe.Add(ref reference, i); byte left = Unsafe.Add(ref reference, i - bytesPerPixel); byte above = Unsafe.Add(ref reference2, i); byte upperLeft = Unsafe.Add(ref reference2, i - bytesPerPixel); reference4 += PaethPredictor(left, above, upperLeft); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, int bytesPerPixel, out int sum) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(previousScanline); ref byte reference3 = ref MemoryMarshal.GetReference(result); sum = 0; reference3 = 4; int num = 0; while (num < bytesPerPixel) { byte b = Unsafe.Add(ref reference, num); byte above = Unsafe.Add(ref reference2, num); num++; ref byte reference4 = ref Unsafe.Add(ref reference3, num); reference4 = (byte)(b - PaethPredictor(0, above, 0)); sum += Numerics.Abs((sbyte)reference4); } if (Avx2.IsSupported) { Vector256 zero = Vector256.Zero; Vector256 vector = Vector256.Zero; int num2 = num - bytesPerPixel; while (num + Vector256.Count <= scanline.Length) { Vector256 left = Unsafe.As>(ref Unsafe.Add(ref reference, num)); Vector256 left2 = Unsafe.As>(ref Unsafe.Add(ref reference, num2)); Vector256 above2 = Unsafe.As>(ref Unsafe.Add(ref reference2, num)); Vector256 upleft = Unsafe.As>(ref Unsafe.Add(ref reference2, num2)); Vector256 vector2 = Avx2.Subtract(left, PaethPredictor(left2, above2, upleft)); Unsafe.As>(ref Unsafe.Add(ref reference3, num + 1)) = vector2; num += Vector256.Count; vector = Avx2.Add(vector, Avx2.SumAbsoluteDifferences(Avx2.Abs(vector2.AsSByte()), zero).AsInt32()); num2 += Vector256.Count; } sum += Numerics.EvenReduceSum(vector); } else if (Vector.IsHardwareAccelerated) { Vector accumulator = Vector.Zero; int num3 = num - bytesPerPixel; while (num + Vector.Count <= scanline.Length) { Vector vector3 = Unsafe.As>(ref Unsafe.Add(ref reference, num)); Vector left3 = Unsafe.As>(ref Unsafe.Add(ref reference, num3)); Vector above3 = Unsafe.As>(ref Unsafe.Add(ref reference2, num)); Vector upperLeft = Unsafe.As>(ref Unsafe.Add(ref reference2, num3)); Vector vector4 = vector3 - PaethPredictor(left3, above3, upperLeft); Unsafe.As>(ref Unsafe.Add(ref reference3, num + 1)) = vector4; num += Vector.Count; Numerics.Accumulate(ref accumulator, Vector.AsVectorByte(Vector.Abs(Vector.AsVectorSByte(vector4)))); num3 += Vector.Count; } for (int i = 0; i < Vector.Count; i++) { sum += (int)accumulator[i]; } } int num4 = num - bytesPerPixel; while (num < scanline.Length) { byte b2 = Unsafe.Add(ref reference, num); byte left4 = Unsafe.Add(ref reference, num4); byte above4 = Unsafe.Add(ref reference2, num); byte upperLeft2 = Unsafe.Add(ref reference2, num4); num++; ref byte reference5 = ref Unsafe.Add(ref reference3, num); reference5 = (byte)(b2 - PaethPredictor(left4, above4, upperLeft2)); sum += Numerics.Abs((sbyte)reference5); num4++; } sum -= 4; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte PaethPredictor(byte left, byte above, byte upperLeft) { int num = left + above - upperLeft; int num2 = Numerics.Abs(num - left); int num3 = Numerics.Abs(num - above); int num4 = Numerics.Abs(num - upperLeft); if (num2 <= num3 && num2 <= num4) { return left; } if (num3 <= num4) { return above; } return upperLeft; } private static Vector256 PaethPredictor(Vector256 left, Vector256 above, Vector256 upleft) { Vector256 zero = Vector256.Zero; Vector256 vector = Avx2.SubtractSaturate(above, upleft); Vector256 vector2 = Avx2.SubtractSaturate(left, upleft); Vector256 vector3 = Avx2.Or(Avx2.SubtractSaturate(upleft, above), vector); Vector256 vector4 = Avx2.Or(Avx2.SubtractSaturate(upleft, left), vector2); Vector256 left2 = Avx2.Min(Avx2.Or(Avx2.CompareEqual(Avx2.CompareEqual(vector, zero), Avx2.CompareEqual(vector2, zero)), Avx2.Or(Avx2.SubtractSaturate(vector4, vector3), Avx2.SubtractSaturate(vector3, vector4))), vector4); return Avx2.BlendVariable(Avx2.BlendVariable(upleft, above, Avx2.CompareEqual(left2, vector4)), left, Avx2.CompareEqual(Avx2.Min(left2, vector3), vector3)); } private static Vector PaethPredictor(Vector left, Vector above, Vector upperLeft) { Vector.Widen(left, out var low, out var high); Vector.Widen(above, out var low2, out var high2); Vector.Widen(upperLeft, out var low3, out var high3); Vector low4 = PaethPredictor(Vector.AsVectorInt16(low), Vector.AsVectorInt16(low2), Vector.AsVectorInt16(low3)); Vector high4 = PaethPredictor(Vector.AsVectorInt16(high), Vector.AsVectorInt16(high2), Vector.AsVectorInt16(high3)); return Vector.AsVectorByte(Vector.Narrow(low4, high4)); } private static Vector PaethPredictor(Vector left, Vector above, Vector upperLeft) { Vector vector = left + above - upperLeft; Vector left2 = Vector.Abs(vector - left); Vector vector2 = Vector.Abs(vector - above); Vector right = Vector.Abs(vector - upperLeft); Vector left3 = Vector.LessThanOrEqual(left2, vector2); Vector right2 = Vector.LessThanOrEqual(left2, right); Vector condition = Vector.LessThanOrEqual(vector2, right); return Vector.ConditionalSelect(Vector.BitwiseAnd(left3, right2), left, Vector.ConditionalSelect(condition, above, upperLeft)); } } internal static class SubFilter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(Span scanline, int bytesPerPixel) { if (Sse2.IsSupported && bytesPerPixel == 4) { DecodeSse2(scanline); } else { DecodeScalar(scanline, bytesPerPixel); } } private static void DecodeSse2(Span scanline) { ref byte reference = ref MemoryMarshal.GetReference(scanline); Vector128 vector = Vector128.Zero; int num = scanline.Length; nint num2 = 1; while (num >= 4) { ref byte source = ref Unsafe.Add(ref reference, num2); Vector128 right = vector; vector = Sse2.ConvertScalarToVector128Int32(Unsafe.As(ref source)).AsByte(); vector = Sse2.Add(vector, right); Unsafe.As(ref source) = Sse2.ConvertToInt32(vector.AsInt32()); num -= 4; num2 += 4; } } private static void DecodeScalar(Span scanline, int bytesPerPixel) { ref byte reference = ref MemoryMarshal.GetReference(scanline); nint num = bytesPerPixel + 1; Unsafe.Add(ref reference, num); for (; num < scanline.Length; num++) { ref byte reference2 = ref Unsafe.Add(ref reference, num); byte b = Unsafe.Add(ref reference, num - bytesPerPixel); reference2 += b; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(ReadOnlySpan scanline, ReadOnlySpan result, int bytesPerPixel, out int sum) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(result); sum = 0; reference2 = 1; int num = 0; while (num < bytesPerPixel) { byte b = Unsafe.Add(ref reference, num); num++; ref byte reference3 = ref Unsafe.Add(ref reference2, num); reference3 = b; sum += Numerics.Abs((sbyte)reference3); } if (Avx2.IsSupported) { Vector256 zero = Vector256.Zero; Vector256 vector = Vector256.Zero; int num2 = num - bytesPerPixel; while (num + Vector256.Count <= scanline.Length) { Vector256 left = Unsafe.As>(ref Unsafe.Add(ref reference, num)); Vector256 right = Unsafe.As>(ref Unsafe.Add(ref reference, num2)); Vector256 vector2 = Avx2.Subtract(left, right); Unsafe.As>(ref Unsafe.Add(ref reference2, num + 1)) = vector2; num += Vector256.Count; vector = Avx2.Add(vector, Avx2.SumAbsoluteDifferences(Avx2.Abs(vector2.AsSByte()), zero).AsInt32()); num2 += Vector256.Count; } sum += Numerics.EvenReduceSum(vector); } else if (Vector.IsHardwareAccelerated) { Vector accumulator = Vector.Zero; int num3 = num - bytesPerPixel; while (num + Vector.Count <= scanline.Length) { Vector vector3 = Unsafe.As>(ref Unsafe.Add(ref reference, num)); Vector vector4 = Unsafe.As>(ref Unsafe.Add(ref reference, num3)); Vector vector5 = vector3 - vector4; Unsafe.As>(ref Unsafe.Add(ref reference2, num + 1)) = vector5; num += Vector.Count; Numerics.Accumulate(ref accumulator, Vector.AsVectorByte(Vector.Abs(Vector.AsVectorSByte(vector5)))); num3 += Vector.Count; } for (int i = 0; i < Vector.Count; i++) { sum += (int)accumulator[i]; } } int num4 = num - bytesPerPixel; while (num < scanline.Length) { byte b2 = Unsafe.Add(ref reference, num); byte b3 = Unsafe.Add(ref reference, num4); num++; ref byte reference4 = ref Unsafe.Add(ref reference2, num); reference4 = (byte)(b2 - b3); sum += Numerics.Abs((sbyte)reference4); num4++; } sum--; } } internal static class UpFilter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(Span scanline, Span previousScanline) { if (Avx2.IsSupported) { DecodeAvx2(scanline, previousScanline); } else if (Sse2.IsSupported) { DecodeSse2(scanline, previousScanline); } else { DecodeScalar(scanline, previousScanline); } } private static void DecodeAvx2(Span scanline, Span previousScanline) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(previousScanline); int num = scanline.Length; nint num2 = 1; while (num >= 32) { ref byte source = ref Unsafe.Add(ref reference, num2); Vector256 vector = Avx2.Add(right: Unsafe.As>(ref source), left: Unsafe.As>(ref Unsafe.Add(ref reference2, num2))); Unsafe.As>(ref source) = vector; num2 += 32; num -= 32; } for (nint num3 = num2; num3 < scanline.Length; num3++) { ref byte reference3 = ref Unsafe.Add(ref reference, num2); byte b = Unsafe.Add(ref reference2, num2); reference3 += b; num2++; } } private static void DecodeSse2(Span scanline, Span previousScanline) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(previousScanline); int num = scanline.Length; nint num2 = 1; while (num >= 16) { ref byte source = ref Unsafe.Add(ref reference, num2); Vector128 vector = Sse2.Add(right: Unsafe.As>(ref source), left: Unsafe.As>(ref Unsafe.Add(ref reference2, num2))); Unsafe.As>(ref source) = vector; num2 += 16; num -= 16; } for (nint num3 = num2; num3 < scanline.Length; num3++) { ref byte reference3 = ref Unsafe.Add(ref reference, num2); byte b = Unsafe.Add(ref reference2, num2); reference3 += b; num2++; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void DecodeScalar(Span scanline, Span previousScanline) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(previousScanline); for (int i = 1; i < scanline.Length; i++) { ref byte reference3 = ref Unsafe.Add(ref reference, i); byte b = Unsafe.Add(ref reference2, i); reference3 += b; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(ReadOnlySpan scanline, ReadOnlySpan previousScanline, Span result, out int sum) { ref byte reference = ref MemoryMarshal.GetReference(scanline); ref byte reference2 = ref MemoryMarshal.GetReference(previousScanline); ref byte reference3 = ref MemoryMarshal.GetReference(result); sum = 0; reference3 = 2; int num = 0; if (Avx2.IsSupported) { Vector256 zero = Vector256.Zero; Vector256 vector = Vector256.Zero; while (num + Vector256.Count <= scanline.Length) { Vector256 left = Unsafe.As>(ref Unsafe.Add(ref reference, num)); Vector256 right = Unsafe.As>(ref Unsafe.Add(ref reference2, num)); Vector256 vector2 = Avx2.Subtract(left, right); Unsafe.As>(ref Unsafe.Add(ref reference3, num + 1)) = vector2; num += Vector256.Count; vector = Avx2.Add(vector, Avx2.SumAbsoluteDifferences(Avx2.Abs(vector2.AsSByte()), zero).AsInt32()); } sum += Numerics.EvenReduceSum(vector); } else if (Vector.IsHardwareAccelerated) { Vector accumulator = Vector.Zero; while (num + Vector.Count <= scanline.Length) { Vector vector3 = Unsafe.As>(ref Unsafe.Add(ref reference, num)); Vector vector4 = Unsafe.As>(ref Unsafe.Add(ref reference2, num)); Vector vector5 = vector3 - vector4; Unsafe.As>(ref Unsafe.Add(ref reference3, num + 1)) = vector5; num += Vector.Count; Numerics.Accumulate(ref accumulator, Vector.AsVectorByte(Vector.Abs(Vector.AsVectorSByte(vector5)))); } for (int i = 0; i < Vector.Count; i++) { sum += (int)accumulator[i]; } } while (num < scanline.Length) { byte b = Unsafe.Add(ref reference, num); byte b2 = Unsafe.Add(ref reference2, num); num++; ref byte reference4 = ref Unsafe.Add(ref reference3, num); reference4 = (byte)(b - b2); sum += Numerics.Abs((sbyte)reference4); } sum -= 2; } } } namespace SixLabors.ImageSharp.Formats.Png.Chunks { internal readonly struct PhysicalChunkData { public const int Size = 9; public uint XAxisPixelsPerUnit { get; } public uint YAxisPixelsPerUnit { get; } public byte UnitSpecifier { get; } public PhysicalChunkData(uint x, uint y, byte unitSpecifier) { XAxisPixelsPerUnit = x; YAxisPixelsPerUnit = y; UnitSpecifier = unitSpecifier; } public static PhysicalChunkData Parse(ReadOnlySpan data) { uint x = BinaryPrimitives.ReadUInt32BigEndian(data.Slice(0, 4)); uint y = BinaryPrimitives.ReadUInt32BigEndian(data.Slice(4, 4)); byte unitSpecifier = data[8]; return new PhysicalChunkData(x, y, unitSpecifier); } public static PhysicalChunkData FromMetadata(ImageMetadata meta) { byte b = 0; uint x; uint y; switch (meta.ResolutionUnits) { case PixelResolutionUnit.AspectRatio: b = 0; x = (uint)Math.Round(meta.HorizontalResolution); y = (uint)Math.Round(meta.VerticalResolution); break; case PixelResolutionUnit.PixelsPerInch: b = 1; x = (uint)Math.Round(UnitConverter.InchToMeter(meta.HorizontalResolution)); y = (uint)Math.Round(UnitConverter.InchToMeter(meta.VerticalResolution)); break; case PixelResolutionUnit.PixelsPerCentimeter: b = 1; x = (uint)Math.Round(UnitConverter.CmToMeter(meta.HorizontalResolution)); y = (uint)Math.Round(UnitConverter.CmToMeter(meta.VerticalResolution)); break; default: b = 1; x = (uint)Math.Round(meta.HorizontalResolution); y = (uint)Math.Round(meta.VerticalResolution); break; } return new PhysicalChunkData(x, y, b); } public void WriteTo(Span buffer) { BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(0, 4), XAxisPixelsPerUnit); BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(4, 4), YAxisPixelsPerUnit); buffer[8] = UnitSpecifier; } } } namespace SixLabors.ImageSharp.Formats.Pbm { internal class BinaryDecoder { private static L8 white = new L8(byte.MaxValue); private static L8 black = new L8(0); public static void Process(Configuration configuration, Buffer2D pixels, BufferedReadStream stream, PbmColorType colorType, PbmComponentType componentType) where TPixel : unmanaged, IPixel { switch (colorType) { case PbmColorType.Grayscale: if (componentType == PbmComponentType.Byte) { ProcessGrayscale(configuration, pixels, stream); } else { ProcessWideGrayscale(configuration, pixels, stream); } break; case PbmColorType.Rgb: if (componentType == PbmComponentType.Byte) { ProcessRgb(configuration, pixels, stream); } else { ProcessWideRgb(configuration, pixels, stream); } break; default: ProcessBlackAndWhite(configuration, pixels, stream); break; } } private static void ProcessGrayscale(Configuration configuration, Buffer2D pixels, BufferedReadStream stream) where TPixel : unmanaged, IPixel { int width = pixels.Width; int height = pixels.Height; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { if (stream.Read(span) < span.Length) { break; } Span destinationPixels = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromL8Bytes(configuration, span, destinationPixels, width); } } private static void ProcessWideGrayscale(Configuration configuration, Buffer2D pixels, BufferedReadStream stream) where TPixel : unmanaged, IPixel { int width = pixels.Width; int height = pixels.Height; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width * 2); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { if (stream.Read(span) < span.Length) { break; } Span destinationPixels = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromL16Bytes(configuration, span, destinationPixels, width); } } private static void ProcessRgb(Configuration configuration, Buffer2D pixels, BufferedReadStream stream) where TPixel : unmanaged, IPixel { int width = pixels.Width; int height = pixels.Height; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width * 3); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { if (stream.Read(span) < span.Length) { break; } Span destinationPixels = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromRgb24Bytes(configuration, span, destinationPixels, width); } } private static void ProcessWideRgb(Configuration configuration, Buffer2D pixels, BufferedReadStream stream) where TPixel : unmanaged, IPixel { int width = pixels.Width; int height = pixels.Height; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width * 6); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { if (stream.Read(span) < span.Length) { break; } Span destinationPixels = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromRgb48Bytes(configuration, span, destinationPixels, width); } } private static void ProcessBlackAndWhite(Configuration configuration, Buffer2D pixels, BufferedReadStream stream) where TPixel : unmanaged, IPixel { int width = pixels.Width; int height = pixels.Height; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { int num = 0; while (num < width) { int num2 = stream.ReadByte(); if (num2 < 0) { return; } int num3 = Math.Min(8, width - num); for (int j = 0; j < num3; j++) { bool flag = (num2 & (128 >> j)) != 0; span[num] = (flag ? black : white); num++; } } Span destinationPixels = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromL8(configuration, span, destinationPixels); } } } internal class BinaryEncoder { public static void WritePixels(Configuration configuration, Stream stream, ImageFrame image, PbmColorType colorType, PbmComponentType componentType) where TPixel : unmanaged, IPixel { switch (colorType) { case PbmColorType.Grayscale: if (componentType == PbmComponentType.Byte) { WriteGrayscale(configuration, stream, image); } else { WriteWideGrayscale(configuration, stream, image); } break; case PbmColorType.Rgb: if (componentType == PbmComponentType.Byte) { WriteRgb(configuration, stream, image); } else { WriteWideRgb(configuration, stream, image); } break; default: WriteBlackAndWhite(configuration, stream, image); break; } } private static void WriteGrayscale(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Buffer2D pixelBuffer = image.PixelBuffer; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { Span span2 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToL8Bytes(configuration, span2, span, width); stream.Write(span); } } private static void WriteWideGrayscale(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Buffer2D pixelBuffer = image.PixelBuffer; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width * 2); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { Span span2 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToL16Bytes(configuration, span2, span, width); stream.Write(span); } } private static void WriteRgb(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Buffer2D pixelBuffer = image.PixelBuffer; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width * 3); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { Span span2 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToRgb24Bytes(configuration, span2, span, width); stream.Write(span); } } private static void WriteWideRgb(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Buffer2D pixelBuffer = image.PixelBuffer; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width * 6); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { Span span2 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToRgb48Bytes(configuration, span2, span, width); stream.Write(span); } } private static void WriteBlackAndWhite(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Buffer2D pixelBuffer = image.PixelBuffer; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width); Span span = buffer.GetSpan(); int num = 0; int num2 = 0; for (int i = 0; i < height; i++) { Span span2 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToL8(configuration, span2, span); int num3 = 0; while (num3 < width) { int num4 = num; for (int j = num2; j < 8; j++) { if (span[num3].PackedValue < 128) { num4 |= 128 >> j; } num3++; if (num3 == width) { num = num4; num2 = (j + 1) & 7; break; } } if (num2 == 0) { stream.WriteByte((byte)num4); num = 0; } } } } } internal static class BufferedReadStreamExtensions { public static bool SkipWhitespaceAndComments(this BufferedReadStream stream) { int num; do { num = stream.ReadByte(); if (num < 0) { return false; } if (num != 35) { continue; } int num2; do { num2 = stream.ReadByte(); if (num2 < 0) { return false; } } while (num2 != 10); num = num2; } while (((uint)(num - 9) <= 1u || num == 13 || num == 32) ? true : false); stream.Seek(-1L, SeekOrigin.Current); return true; } public static bool ReadDecimal(this BufferedReadStream stream, out int value) { value = 0; while (true) { int num = stream.ReadByte(); if (num < 0) { return false; } num -= 48; if ((uint)num > 9u) { break; } value = value * 10 + num; } return true; } } internal interface IPbmEncoderOptions { PbmEncoding? Encoding { get; } PbmColorType? ColorType { get; } PbmComponentType? ComponentType { get; } } public enum PbmColorType : byte { BlackAndWhite, Grayscale, Rgb } public enum PbmComponentType : byte { Bit, Byte, Short } public sealed class PbmConfigurationModule : IConfigurationModule { public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(PbmFormat.Instance, new PbmEncoder()); configuration.ImageFormatsManager.SetDecoder(PbmFormat.Instance, new PbmDecoder()); configuration.ImageFormatsManager.AddImageFormatDetector(new PbmImageFormatDetector()); } } internal static class PbmConstants { public const ushort MaxLength = ushort.MaxValue; public static readonly IEnumerable MimeTypes = new string[2] { "image/x-portable-pixmap", "image/x-portable-anymap" }; public static readonly IEnumerable FileExtensions = new string[3] { "ppm", "pbm", "pgm" }; } public sealed class PbmDecoder : IImageDecoder, IImageInfoDetector { public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(stream, "stream"); return new PbmDecoderCore(configuration).Decode(configuration, stream, cancellationToken); } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) { return Decode(configuration, stream, cancellationToken); } public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, "stream"); return new PbmDecoderCore(configuration).Identify(configuration, stream, cancellationToken); } } internal sealed class PbmDecoderCore : IImageDecoderInternals { private int maxPixelValue; public Configuration Configuration { get; } public PbmColorType ColorType { get; private set; } public Size PixelSize { get; private set; } public PbmComponentType ComponentType { get; private set; } public PbmEncoding Encoding { get; private set; } public ImageMetadata Metadata { get; private set; } Size IImageDecoderInternals.Dimensions => PixelSize; private bool NeedsUpscaling { get { if (ColorType != PbmColorType.BlackAndWhite) { int num = maxPixelValue; if (num != 255) { return num != 65535; } return false; } return false; } } public PbmDecoderCore(Configuration configuration) { Configuration = configuration ?? Configuration.Default; } public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { ProcessHeader(stream); Image image = new Image(Configuration, PixelSize.Width, PixelSize.Height, Metadata); Buffer2D rootFramePixelBuffer = image.GetRootFramePixelBuffer(); ProcessPixels(stream, rootFramePixelBuffer); if (NeedsUpscaling) { ProcessUpscaling(image); } return image; } public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { ProcessHeader(stream); return new ImageInfo(new PixelTypeInfo((ComponentType == PbmComponentType.Short) ? 16 : 8), PixelSize.Width, PixelSize.Height, Metadata); } private void ProcessHeader(BufferedReadStream stream) { Span buffer = stackalloc byte[2]; if (stream.Read(buffer) != 2 || buffer[0] != 80) { throw new InvalidImageContentException("Empty or not an PPM image."); } switch ((char)buffer[1]) { case '1': ColorType = PbmColorType.BlackAndWhite; Encoding = PbmEncoding.Plain; break; case '2': ColorType = PbmColorType.Grayscale; Encoding = PbmEncoding.Plain; break; case '3': ColorType = PbmColorType.Rgb; Encoding = PbmEncoding.Plain; break; case '4': ColorType = PbmColorType.BlackAndWhite; Encoding = PbmEncoding.Binary; break; case '5': ColorType = PbmColorType.Grayscale; Encoding = PbmEncoding.Binary; break; case '6': ColorType = PbmColorType.Rgb; Encoding = PbmEncoding.Binary; break; default: throw new InvalidImageContentException("Unknown of not implemented image type encountered."); } int value = default(int); int value2 = default(int); if (!stream.SkipWhitespaceAndComments() || !stream.ReadDecimal(out value) || !stream.SkipWhitespaceAndComments() || !stream.ReadDecimal(out value2) || !stream.SkipWhitespaceAndComments()) { ThrowPrematureEof(); } if (ColorType != PbmColorType.BlackAndWhite) { if (!stream.ReadDecimal(out maxPixelValue)) { ThrowPrematureEof(); } if (maxPixelValue > 255) { ComponentType = PbmComponentType.Short; } else { ComponentType = PbmComponentType.Byte; } stream.SkipWhitespaceAndComments(); } else { ComponentType = PbmComponentType.Bit; } PixelSize = new Size(value, value2); Metadata = new ImageMetadata(); PbmMetadata pbmMetadata = Metadata.GetPbmMetadata(); pbmMetadata.Encoding = Encoding; pbmMetadata.ColorType = ColorType; pbmMetadata.ComponentType = ComponentType; static void ThrowPrematureEof() { throw new InvalidImageContentException("Reached EOF while reading the header."); } } private void ProcessPixels(BufferedReadStream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { if (Encoding == PbmEncoding.Binary) { BinaryDecoder.Process(Configuration, pixels, stream, ColorType, ComponentType); } else { PlainDecoder.Process(Configuration, pixels, stream, ColorType, ComponentType); } } private void ProcessUpscaling(Image image) where TPixel : unmanaged, IPixel { int num = ((ComponentType == PbmComponentType.Short) ? 65535 : 255); float factor = num / maxPixelValue; image.Mutate(delegate(IImageProcessingContext x) { x.Brightness(factor); }); } } public sealed class PbmEncoder : IImageEncoder, IPbmEncoderOptions { public PbmEncoding? Encoding { get; set; } public PbmColorType? ColorType { get; set; } public PbmComponentType? ComponentType { get; set; } public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { new PbmEncoderCore(image.GetConfiguration(), this).Encode(image, stream); } public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return new PbmEncoderCore(image.GetConfiguration(), this).EncodeAsync(image, stream, cancellationToken); } } internal sealed class PbmEncoderCore : IImageEncoderInternals { private const byte NewLine = 10; private const byte Space = 32; private const byte P = 80; private Configuration configuration; private readonly IPbmEncoderOptions options; private PbmEncoding encoding; private PbmColorType colorType; private PbmComponentType componentType; public PbmEncoderCore(Configuration configuration, IPbmEncoderOptions options) { this.configuration = configuration; this.options = options; } public void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); DeduceOptions(image); byte signature = DeduceSignature(); WriteHeader(stream, signature, image.Size()); WritePixels(stream, image.Frames.RootFrame); stream.Flush(); } private void DeduceOptions(Image image) where TPixel : unmanaged, IPixel { configuration = image.GetConfiguration(); PbmMetadata pbmMetadata = image.Metadata.GetPbmMetadata(); encoding = options.Encoding ?? pbmMetadata.Encoding; colorType = options.ColorType ?? pbmMetadata.ColorType; if (colorType != PbmColorType.BlackAndWhite) { componentType = options.ComponentType ?? pbmMetadata.ComponentType; } else { componentType = PbmComponentType.Bit; } } private byte DeduceSignature() { if (colorType == PbmColorType.BlackAndWhite) { if (encoding == PbmEncoding.Plain) { return 49; } return 52; } if (colorType == PbmColorType.Grayscale) { if (encoding == PbmEncoding.Plain) { return 50; } return 53; } if (encoding == PbmEncoding.Plain) { return 51; } return 54; } private void WriteHeader(Stream stream, byte signature, Size pixelSize) { Span buffer = stackalloc byte[128]; int num = 3; buffer[0] = 80; buffer[1] = signature; buffer[2] = 10; Utf8Formatter.TryFormat(pixelSize.Width, buffer.Slice(num), out var bytesWritten); num += bytesWritten; buffer[num++] = 32; Utf8Formatter.TryFormat(pixelSize.Height, buffer.Slice(num), out bytesWritten); num += bytesWritten; buffer[num++] = 10; if (colorType != PbmColorType.BlackAndWhite) { Utf8Formatter.TryFormat((componentType == PbmComponentType.Short) ? 65535 : 255, buffer.Slice(num), out bytesWritten); num += bytesWritten; buffer[num++] = 10; } stream.Write(buffer, 0, num); } private void WritePixels(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { if (encoding == PbmEncoding.Plain) { PlainEncoder.WritePixels(configuration, stream, image, colorType, componentType); } else { BinaryEncoder.WritePixels(configuration, stream, image, colorType, componentType); } } } public enum PbmEncoding : byte { Plain, Binary } public sealed class PbmFormat : IImageFormat, IImageFormat { public static PbmFormat Instance { get; } = new PbmFormat(); public string Name => "PBM"; public string DefaultMimeType => "image/x-portable-pixmap"; public IEnumerable MimeTypes => PbmConstants.MimeTypes; public IEnumerable FileExtensions => PbmConstants.FileExtensions; private PbmFormat() { } public PbmMetadata CreateDefaultFormatMetadata() { return new PbmMetadata(); } } public sealed class PbmImageFormatDetector : IImageFormatDetector { private const byte P = 80; private const byte Zero = 48; private const byte Seven = 55; public int HeaderSize => 2; public IImageFormat DetectFormat(ReadOnlySpan header) { if (!IsSupportedFileFormat(header)) { return null; } return PbmFormat.Instance; } private bool IsSupportedFileFormat(ReadOnlySpan header) { if (1u < (uint)header.Length) { if (header[0] == 80) { return (uint)(header[1] - 48 - 1) < 6u; } return false; } return false; } } public class PbmMetadata : IDeepCloneable { public PbmEncoding Encoding { get; set; } public PbmColorType ColorType { get; set; } = PbmColorType.Grayscale; public PbmComponentType ComponentType { get; set; } public PbmMetadata() { ComponentType = ((ColorType != PbmColorType.BlackAndWhite) ? PbmComponentType.Byte : PbmComponentType.Bit); } private PbmMetadata(PbmMetadata other) { Encoding = other.Encoding; ColorType = other.ColorType; ComponentType = other.ComponentType; } public IDeepCloneable DeepClone() { return new PbmMetadata(this); } } internal class PlainDecoder { private static readonly L8 White = new L8(byte.MaxValue); private static readonly L8 Black = new L8(0); public static void Process(Configuration configuration, Buffer2D pixels, BufferedReadStream stream, PbmColorType colorType, PbmComponentType componentType) where TPixel : unmanaged, IPixel { switch (colorType) { case PbmColorType.Grayscale: if (componentType == PbmComponentType.Byte) { ProcessGrayscale(configuration, pixels, stream); } else { ProcessWideGrayscale(configuration, pixels, stream); } break; case PbmColorType.Rgb: if (componentType == PbmComponentType.Byte) { ProcessRgb(configuration, pixels, stream); } else { ProcessWideRgb(configuration, pixels, stream); } break; default: ProcessBlackAndWhite(configuration, pixels, stream); break; } } private static void ProcessGrayscale(Configuration configuration, Buffer2D pixels, BufferedReadStream stream) where TPixel : unmanaged, IPixel { int width = pixels.Width; int height = pixels.Height; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width); Span span = buffer.GetSpan(); bool flag = false; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { stream.ReadDecimal(out var value); span[j] = new L8((byte)value); flag = !stream.SkipWhitespaceAndComments(); if (flag) { break; } } Span destinationPixels = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromL8(configuration, span, destinationPixels); if (flag) { break; } } } private static void ProcessWideGrayscale(Configuration configuration, Buffer2D pixels, BufferedReadStream stream) where TPixel : unmanaged, IPixel { int width = pixels.Width; int height = pixels.Height; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width); Span span = buffer.GetSpan(); bool flag = false; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { stream.ReadDecimal(out var value); span[j] = new L16((ushort)value); flag = !stream.SkipWhitespaceAndComments(); if (flag) { break; } } Span destinationPixels = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromL16(configuration, span, destinationPixels); if (flag) { break; } } } private static void ProcessRgb(Configuration configuration, Buffer2D pixels, BufferedReadStream stream) where TPixel : unmanaged, IPixel { int width = pixels.Width; int height = pixels.Height; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width); Span span = buffer.GetSpan(); bool flag = false; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (!stream.ReadDecimal(out var value) || !stream.SkipWhitespaceAndComments() || !stream.ReadDecimal(out var value2) || !stream.SkipWhitespaceAndComments()) { flag = true; break; } stream.ReadDecimal(out var value3); span[j] = new Rgb24((byte)value, (byte)value2, (byte)value3); flag = !stream.SkipWhitespaceAndComments(); if (flag) { break; } } Span destinationPixels = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromRgb24(configuration, span, destinationPixels); if (flag) { break; } } } private static void ProcessWideRgb(Configuration configuration, Buffer2D pixels, BufferedReadStream stream) where TPixel : unmanaged, IPixel { int width = pixels.Width; int height = pixels.Height; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width); Span span = buffer.GetSpan(); bool flag = false; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (!stream.ReadDecimal(out var value) || !stream.SkipWhitespaceAndComments() || !stream.ReadDecimal(out var value2) || !stream.SkipWhitespaceAndComments()) { flag = true; break; } stream.ReadDecimal(out var value3); span[j] = new Rgb48((ushort)value, (ushort)value2, (ushort)value3); flag = !stream.SkipWhitespaceAndComments(); if (flag) { break; } } Span destinationPixels = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromRgb48(configuration, span, destinationPixels); if (flag) { break; } } } private static void ProcessBlackAndWhite(Configuration configuration, Buffer2D pixels, BufferedReadStream stream) where TPixel : unmanaged, IPixel { int width = pixels.Width; int height = pixels.Height; using IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(width); Span span = buffer.GetSpan(); bool flag = false; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { stream.ReadDecimal(out var value); span[j] = ((value == 0) ? White : Black); flag = !stream.SkipWhitespaceAndComments(); if (flag) { break; } } Span destinationPixels = pixels.DangerousGetRowSpan(i); PixelOperations.Instance.FromL8(configuration, span, destinationPixels); if (flag) { break; } } } } internal class PlainEncoder { private const byte NewLine = 10; private const byte Space = 32; private const byte Zero = 48; private const byte One = 49; private const int MaxCharsPerPixelBlackAndWhite = 2; private const int MaxCharsPerPixelGrayscale = 4; private const int MaxCharsPerPixelGrayscaleWide = 6; private const int MaxCharsPerPixelRgb = 12; private const int MaxCharsPerPixelRgbWide = 18; private static readonly StandardFormat DecimalFormat = StandardFormat.Parse("D"); public static void WritePixels(Configuration configuration, Stream stream, ImageFrame image, PbmColorType colorType, PbmComponentType componentType) where TPixel : unmanaged, IPixel { switch (colorType) { case PbmColorType.Grayscale: if (componentType == PbmComponentType.Byte) { WriteGrayscale(configuration, stream, image); } else { WriteWideGrayscale(configuration, stream, image); } break; case PbmColorType.Rgb: if (componentType == PbmComponentType.Byte) { WriteRgb(configuration, stream, image); } else { WriteWideRgb(configuration, stream, image); } break; default: WriteBlackAndWhite(configuration, stream, image); break; } stream.WriteByte(32); } private static void WriteGrayscale(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Buffer2D pixelBuffer = image.PixelBuffer; MemoryAllocator memoryAllocator = configuration.MemoryAllocator; using IMemoryOwner buffer = memoryAllocator.Allocate(width); Span span = buffer.GetSpan(); using IMemoryOwner buffer2 = memoryAllocator.Allocate(width * 4); Span span2 = buffer2.GetSpan(); for (int i = 0; i < height; i++) { Span span3 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToL8(configuration, span3, span); int num = 0; for (int j = 0; j < width; j++) { Utf8Formatter.TryFormat(span[j].PackedValue, span2.Slice(num), out var bytesWritten, DecimalFormat); num += bytesWritten; span2[num++] = 32; } span2[num - 1] = 10; stream.Write(span2, 0, num); } } private static void WriteWideGrayscale(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Buffer2D pixelBuffer = image.PixelBuffer; MemoryAllocator memoryAllocator = configuration.MemoryAllocator; using IMemoryOwner buffer = memoryAllocator.Allocate(width); Span span = buffer.GetSpan(); using IMemoryOwner buffer2 = memoryAllocator.Allocate(width * 6); Span span2 = buffer2.GetSpan(); for (int i = 0; i < height; i++) { Span span3 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToL16(configuration, span3, span); int num = 0; for (int j = 0; j < width; j++) { Utf8Formatter.TryFormat(span[j].PackedValue, span2.Slice(num), out var bytesWritten, DecimalFormat); num += bytesWritten; span2[num++] = 32; } span2[num - 1] = 10; stream.Write(span2, 0, num); } } private static void WriteRgb(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Buffer2D pixelBuffer = image.PixelBuffer; MemoryAllocator memoryAllocator = configuration.MemoryAllocator; using IMemoryOwner buffer = memoryAllocator.Allocate(width); Span span = buffer.GetSpan(); using IMemoryOwner buffer2 = memoryAllocator.Allocate(width * 12); Span span2 = buffer2.GetSpan(); for (int i = 0; i < height; i++) { Span span3 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToRgb24(configuration, span3, span); int num = 0; for (int j = 0; j < width; j++) { Utf8Formatter.TryFormat(span[j].R, span2.Slice(num), out var bytesWritten, DecimalFormat); num += bytesWritten; span2[num++] = 32; Utf8Formatter.TryFormat(span[j].G, span2.Slice(num), out bytesWritten, DecimalFormat); num += bytesWritten; span2[num++] = 32; Utf8Formatter.TryFormat(span[j].B, span2.Slice(num), out bytesWritten, DecimalFormat); num += bytesWritten; span2[num++] = 32; } span2[num - 1] = 10; stream.Write(span2, 0, num); } } private static void WriteWideRgb(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Buffer2D pixelBuffer = image.PixelBuffer; MemoryAllocator memoryAllocator = configuration.MemoryAllocator; using IMemoryOwner buffer = memoryAllocator.Allocate(width); Span span = buffer.GetSpan(); using IMemoryOwner buffer2 = memoryAllocator.Allocate(width * 18); Span span2 = buffer2.GetSpan(); for (int i = 0; i < height; i++) { Span span3 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToRgb48(configuration, span3, span); int num = 0; for (int j = 0; j < width; j++) { Utf8Formatter.TryFormat(span[j].R, span2.Slice(num), out var bytesWritten, DecimalFormat); num += bytesWritten; span2[num++] = 32; Utf8Formatter.TryFormat(span[j].G, span2.Slice(num), out bytesWritten, DecimalFormat); num += bytesWritten; span2[num++] = 32; Utf8Formatter.TryFormat(span[j].B, span2.Slice(num), out bytesWritten, DecimalFormat); num += bytesWritten; span2[num++] = 32; } span2[num - 1] = 10; stream.Write(span2, 0, num); } } private static void WriteBlackAndWhite(Configuration configuration, Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { int width = image.Width; int height = image.Height; Buffer2D pixelBuffer = image.PixelBuffer; MemoryAllocator memoryAllocator = configuration.MemoryAllocator; using IMemoryOwner buffer = memoryAllocator.Allocate(width); Span span = buffer.GetSpan(); using IMemoryOwner buffer2 = memoryAllocator.Allocate(width * 2); Span span2 = buffer2.GetSpan(); for (int i = 0; i < height; i++) { Span span3 = pixelBuffer.DangerousGetRowSpan(i); PixelOperations.Instance.ToL8(configuration, span3, span); int num = 0; for (int j = 0; j < width; j++) { byte b = (byte)((span[j].PackedValue < 128) ? 49 : 48); span2[num++] = b; span2[num++] = 32; } span2[num - 1] = 10; stream.Write(span2, 0, num); } } } } namespace SixLabors.ImageSharp.Formats.Jpeg { internal interface IJpegDecoderOptions { bool IgnoreMetadata { get; } } internal interface IJpegEncoderOptions { int? Quality { get; set; } JpegColorType? ColorType { get; } } public enum JpegColorType : byte { YCbCrRatio420, YCbCrRatio444, YCbCrRatio422, YCbCrRatio411, YCbCrRatio410, Luminance, Rgb, Cmyk } public sealed class JpegConfigurationModule : IConfigurationModule { public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder()); configuration.ImageFormatsManager.SetDecoder(JpegFormat.Instance, new JpegDecoder()); configuration.ImageFormatsManager.AddImageFormatDetector(new JpegImageFormatDetector()); } } internal static class JpegConstants { internal static class Markers { public const byte XFF = byte.MaxValue; public const int XFFInt = 255; public const byte SOI = 216; public const byte EOI = 217; public const byte APP0 = 224; public const byte APP1 = 225; public const byte APP2 = 226; public const byte APP3 = 227; public const byte APP4 = 228; public const byte APP5 = 229; public const byte APP6 = 230; public const byte APP7 = 231; public const byte APP8 = 232; public const byte APP9 = 233; public const byte APP10 = 234; public const byte APP11 = 235; public const byte APP12 = 236; public const byte APP13 = 237; public const byte APP14 = 238; public const byte APP15 = 239; public const byte DAC = 204; public const byte COM = 254; public const byte DQT = 219; public const byte SOF0 = 192; public const byte SOF1 = 193; public const byte SOF2 = 194; public const byte SOF3 = 195; public const byte SOF5 = 197; public const byte SOF6 = 198; public const byte SOF7 = 199; public const byte SOF9 = 201; public const byte SOF10 = 202; public const byte SOF11 = 203; public const byte SOF13 = 205; public const byte SOF14 = 206; public const byte SOF15 = 207; public const byte DHT = 196; public const byte DRI = 221; public const byte SOS = 218; public const byte RST0 = 208; public const byte RST7 = 215; } internal static class Adobe { public const byte ColorTransformUnknown = 0; public const byte ColorTransformYCbCr = 1; public const byte ColorTransformYcck = 2; } internal static class Huffman { public const int RegisterSize = 64; public const int FetchBits = 48; public const int FetchLoop = 6; public const int MinBits = 16; public const int LookupBits = 8; public const int SlowBits = 9; public const int LookupSize = 256; } public const ushort MaxLength = ushort.MaxValue; public static readonly IEnumerable MimeTypes = new string[2] { "image/jpeg", "image/pjpeg" }; public static readonly IEnumerable FileExtensions = new string[3] { "jpg", "jpeg", "jfif" }; } public sealed class JpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector { public bool IgnoreMetadata { get; set; } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(stream, "stream"); using JpegDecoderCore decoder = new JpegDecoderCore(configuration, this); return decoder.Decode(configuration, stream, cancellationToken); } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) { return Decode(configuration, stream, cancellationToken); } public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, "stream"); using JpegDecoderCore decoder = new JpegDecoderCore(configuration, this); return decoder.Identify(configuration, stream, cancellationToken); } } internal sealed class JpegDecoderCore : IRawJpegData, IDisposable, IImageDecoderInternals { private readonly byte[] supportedPrecisions = new byte[2] { 8, 12 }; private readonly byte[] temp = new byte[128]; private readonly byte[] markerBuffer = new byte[2]; private bool hasExif; private byte[] exifData; private bool hasIcc; private byte[] iccData; private bool hasIptc; private byte[] iptcData; private bool hasXmp; private byte[] xmpData; private JFifMarker jFif; private bool hasJFif; private AdobeMarker adobe; private HuffmanScanDecoder scanDecoder; public Configuration Configuration { get; } public JpegFrame Frame { get; private set; } Size IImageDecoderInternals.Dimensions => Frame.PixelSize; public bool IgnoreMetadata { get; } public ImageMetadata Metadata { get; private set; } public JpegColorSpace ColorSpace { get; private set; } public JpegComponent[] Components => Frame.Components; IJpegComponent[] IRawJpegData.Components => Components; public Block8x8F[] QuantizationTables { get; private set; } public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) { Configuration = configuration ?? Configuration.Default; IgnoreMetadata = options.IgnoreMetadata; } public static JpegFileMarker FindNextFileMarker(byte[] marker, BufferedReadStream stream) { if (stream.Read(marker, 0, 2) == 0) { return new JpegFileMarker(217, stream.Length - 2); } if (marker[0] == byte.MaxValue) { int num = marker[1]; while (num == 255) { int num2 = stream.ReadByte(); if (num2 == -1) { return new JpegFileMarker(217, stream.Length - 2); } num = num2; } return new JpegFileMarker((byte)num, stream.Position - 2); } return new JpegFileMarker(marker[1], stream.Position - 2, invalid: true); } public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { using SpectralConverter spectralConverter = new SpectralConverter(Configuration); HuffmanScanDecoder huffmanScanDecoder = new HuffmanScanDecoder(stream, spectralConverter, cancellationToken); ParseStream(stream, huffmanScanDecoder, cancellationToken); InitExifProfile(); InitIccProfile(); InitIptcProfile(); InitXmpProfile(); InitDerivedMetadataProperties(); return new Image(Configuration, spectralConverter.GetPixelBuffer(cancellationToken), Metadata); } public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { ParseStream(stream, null, cancellationToken); InitExifProfile(); InitIccProfile(); InitIptcProfile(); InitXmpProfile(); InitDerivedMetadataProperties(); Size pixelSize = Frame.PixelSize; return new ImageInfo(new PixelTypeInfo(Frame.BitsPerPixel), pixelSize.Width, pixelSize.Height, Metadata); } public void LoadTables(byte[] tableBytes, HuffmanScanDecoder huffmanScanDecoder) { Metadata = new ImageMetadata(); QuantizationTables = new Block8x8F[4]; scanDecoder = huffmanScanDecoder; if (tableBytes.Length < 4) { JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker"); } using MemoryStream stream = new MemoryStream(tableBytes); using BufferedReadStream bufferedReadStream = new BufferedReadStream(Configuration, stream); bufferedReadStream.Read(markerBuffer, 0, 2); if (new JpegFileMarker(markerBuffer[1], 0L).Marker != 216) { JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker."); } bufferedReadStream.Read(markerBuffer, 0, 2); JpegFileMarker jpegFileMarker = new JpegFileMarker(markerBuffer[1], (int)bufferedReadStream.Position - 2); while (jpegFileMarker.Marker != 217 || (jpegFileMarker.Marker == 217 && jpegFileMarker.Invalid)) { if (!jpegFileMarker.Invalid) { int num = ReadUint16(bufferedReadStream) - 2; if (bufferedReadStream.RemainingBytes < (uint)num) { JpegThrowHelper.ThrowNotEnoughBytesForMarker(jpegFileMarker.Marker); } switch (jpegFileMarker.Marker) { case 196: ProcessDefineHuffmanTablesMarker(bufferedReadStream, num); break; case 219: ProcessDefineQuantizationTablesMarker(bufferedReadStream, num); break; case 221: ProcessDefineRestartIntervalMarker(bufferedReadStream, num); break; case 217: return; } } if (bufferedReadStream.Read(markerBuffer, 0, 2) != 2) { JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker"); } jpegFileMarker = new JpegFileMarker(markerBuffer[1], 0L); } } internal void ParseStream(BufferedReadStream stream, HuffmanScanDecoder scanDecoder, CancellationToken cancellationToken) { bool flag = scanDecoder == null; this.scanDecoder = scanDecoder; Metadata = new ImageMetadata(); stream.Read(markerBuffer, 0, 2); if (new JpegFileMarker(markerBuffer[1], 0L).Marker != 216) { JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker."); } stream.Read(markerBuffer, 0, 2); byte marker = markerBuffer[1]; JpegFileMarker frameMarker = new JpegFileMarker(marker, (int)stream.Position - 2); if (QuantizationTables == null) { Block8x8F[] array = (QuantizationTables = new Block8x8F[4]); } while (frameMarker.Marker != 217 || (frameMarker.Marker == 217 && frameMarker.Invalid)) { cancellationToken.ThrowIfCancellationRequested(); if (!frameMarker.Invalid) { int num = ReadUint16(stream) - 2; if (stream.RemainingBytes < (uint)num) { JpegThrowHelper.ThrowNotEnoughBytesForMarker(frameMarker.Marker); } switch (frameMarker.Marker) { case 192: case 193: case 194: ProcessStartOfFrameMarker(stream, num, in frameMarker, flag); break; case 197: JpegThrowHelper.ThrowNotSupportedException("Decoding jpeg files with differential sequential DCT is not supported."); break; case 198: JpegThrowHelper.ThrowNotSupportedException("Decoding jpeg files with differential progressive DCT is not supported."); break; case 195: case 199: JpegThrowHelper.ThrowNotSupportedException("Decoding lossless jpeg files is not supported."); break; case 201: case 202: case 203: case 205: case 206: case 207: JpegThrowHelper.ThrowNotSupportedException("Decoding jpeg files with arithmetic coding is not supported."); break; case 218: if (!flag) { ProcessStartOfScanMarker(stream, num); break; } return; case 196: if (flag) { stream.Skip(num); } else { ProcessDefineHuffmanTablesMarker(stream, num); } break; case 219: ProcessDefineQuantizationTablesMarker(stream, num); break; case 221: if (flag) { stream.Skip(num); } else { ProcessDefineRestartIntervalMarker(stream, num); } break; case 224: ProcessApplicationHeaderMarker(stream, num); break; case 225: ProcessApp1Marker(stream, num); break; case 226: ProcessApp2Marker(stream, num); break; case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: stream.Skip(num); break; case 237: ProcessApp13Marker(stream, num); break; case 238: ProcessApp14Marker(stream, num); break; case 239: case 254: stream.Skip(num); break; case 204: JpegThrowHelper.ThrowNotSupportedException("Decoding jpeg files with arithmetic coding is not supported."); break; } } frameMarker = FindNextFileMarker(markerBuffer, stream); } } public void Dispose() { Frame?.Dispose(); Frame = null; scanDecoder = null; } private JpegColorSpace DeduceJpegColorSpace(byte componentCount) { switch (componentCount) { case 1: return JpegColorSpace.Grayscale; case 3: if (!adobe.Equals(default(AdobeMarker))) { if (adobe.ColorTransform == 1) { return JpegColorSpace.YCbCr; } if (adobe.ColorTransform == 0) { return JpegColorSpace.RGB; } if (Components[2].Id == 3 && Components[1].Id == 2 && Components[0].Id == 1) { return JpegColorSpace.YCbCr; } JpegThrowHelper.ThrowNotSupportedColorSpace(); } if (hasJFif) { return JpegColorSpace.YCbCr; } if (Components[2].Id == 66 && Components[1].Id == 71 && Components[0].Id == 82) { return JpegColorSpace.RGB; } if (Components[2].Id == 3 && Components[1].Id == 2 && Components[0].Id == 1) { return JpegColorSpace.YCbCr; } if (Components[2].VerticalSamplingFactor == 1 && Components[1].VerticalSamplingFactor == 1 && Components[0].VerticalSamplingFactor == 1 && Components[2].HorizontalSamplingFactor == 1 && Components[1].HorizontalSamplingFactor == 1 && Components[0].HorizontalSamplingFactor == 1) { return JpegColorSpace.RGB; } return JpegColorSpace.YCbCr; case 4: if (!adobe.Equals(default(AdobeMarker))) { if (adobe.ColorTransform == 2) { return JpegColorSpace.Ycck; } if (adobe.ColorTransform == 0) { return JpegColorSpace.Cmyk; } JpegThrowHelper.ThrowNotSupportedColorSpace(); } return JpegColorSpace.Cmyk; default: JpegThrowHelper.ThrowNotSupportedComponentCount(componentCount); return JpegColorSpace.Undefined; } } private JpegColorType DeduceJpegColorType() { switch (ColorSpace) { case JpegColorSpace.Grayscale: return JpegColorType.Luminance; case JpegColorSpace.RGB: return JpegColorType.Rgb; case JpegColorSpace.YCbCr: if (Frame.Components[0].HorizontalSamplingFactor == 1 && Frame.Components[0].VerticalSamplingFactor == 1 && Frame.Components[1].HorizontalSamplingFactor == 1 && Frame.Components[1].VerticalSamplingFactor == 1 && Frame.Components[2].HorizontalSamplingFactor == 1 && Frame.Components[2].VerticalSamplingFactor == 1) { return JpegColorType.YCbCrRatio444; } if (Frame.Components[0].HorizontalSamplingFactor == 2 && Frame.Components[0].VerticalSamplingFactor == 2 && Frame.Components[1].HorizontalSamplingFactor == 1 && Frame.Components[1].VerticalSamplingFactor == 1 && Frame.Components[2].HorizontalSamplingFactor == 1 && Frame.Components[2].VerticalSamplingFactor == 1) { return JpegColorType.YCbCrRatio420; } if (Frame.Components[0].HorizontalSamplingFactor == 1 && Frame.Components[0].VerticalSamplingFactor == 1 && Frame.Components[1].HorizontalSamplingFactor == 1 && Frame.Components[1].VerticalSamplingFactor == 2 && Frame.Components[2].HorizontalSamplingFactor == 1 && Frame.Components[2].VerticalSamplingFactor == 2) { return JpegColorType.YCbCrRatio422; } if (Frame.Components[0].HorizontalSamplingFactor == 4 && Frame.Components[0].VerticalSamplingFactor == 1 && Frame.Components[1].HorizontalSamplingFactor == 1 && Frame.Components[1].VerticalSamplingFactor == 1 && Frame.Components[2].HorizontalSamplingFactor == 1 && Frame.Components[2].VerticalSamplingFactor == 1) { return JpegColorType.YCbCrRatio411; } if (Frame.Components[0].HorizontalSamplingFactor == 4 && Frame.Components[0].VerticalSamplingFactor == 2 && Frame.Components[1].HorizontalSamplingFactor == 1 && Frame.Components[1].VerticalSamplingFactor == 1 && Frame.Components[2].HorizontalSamplingFactor == 1 && Frame.Components[2].VerticalSamplingFactor == 1) { return JpegColorType.YCbCrRatio410; } return JpegColorType.YCbCrRatio420; case JpegColorSpace.Cmyk: return JpegColorType.Cmyk; default: return JpegColorType.YCbCrRatio420; } } private void InitExifProfile() { if (hasExif) { Metadata.ExifProfile = new ExifProfile(exifData); } } private void InitIccProfile() { if (hasIcc) { IccProfile iccProfile = new IccProfile(iccData); if (iccProfile.CheckIsValid()) { Metadata.IccProfile = iccProfile; } } } private void InitIptcProfile() { if (hasIptc) { IptcProfile iptcProfile = new IptcProfile(iptcData); Metadata.IptcProfile = iptcProfile; } } private void InitXmpProfile() { if (hasXmp) { XmpProfile xmpProfile = new XmpProfile(xmpData); Metadata.XmpProfile = xmpProfile; } } private void InitDerivedMetadataProperties() { if (jFif.XDensity > 0 && jFif.YDensity > 0) { Metadata.HorizontalResolution = jFif.XDensity; Metadata.VerticalResolution = jFif.YDensity; Metadata.ResolutionUnits = jFif.DensityUnits; } else if (hasExif) { double exifResolutionValue = GetExifResolutionValue(ExifTag.XResolution); double exifResolutionValue2 = GetExifResolutionValue(ExifTag.YResolution); if (exifResolutionValue > 0.0 && exifResolutionValue2 > 0.0) { Metadata.HorizontalResolution = exifResolutionValue; Metadata.VerticalResolution = exifResolutionValue2; Metadata.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(Metadata.ExifProfile); } } } private double GetExifResolutionValue(ExifTag tag) { return Metadata.ExifProfile.GetValue(tag)?.Value.ToDouble() ?? 0.0; } private void ExtendProfile(ref byte[] profile, byte[] extension) { int num = profile.Length; Array.Resize(ref profile, num + extension.Length); Buffer.BlockCopy(extension, 0, profile, num, extension.Length); } private void ProcessApplicationHeaderMarker(BufferedReadStream stream, int remaining) { hasJFif = true; if (remaining < 13 || !jFif.Equals(default(JFifMarker))) { stream.Skip(remaining); return; } stream.Read(temp, 0, 13); remaining -= 13; JFifMarker.TryParse(temp, out jFif); if (remaining > 0) { if (stream.Position + remaining >= stream.Length) { JpegThrowHelper.ThrowInvalidImageContentException("Bad App0 Marker length."); } stream.Skip(remaining); } } private void ProcessApp1Marker(BufferedReadStream stream, int remaining) { if (remaining < 6 || IgnoreMetadata) { stream.Skip(remaining); return; } if (stream.Position + remaining >= stream.Length) { JpegThrowHelper.ThrowInvalidImageContentException("Bad App1 Marker length."); } stream.Read(temp, 0, 6); remaining -= 6; if (ProfileResolver.IsProfile(temp, ProfileResolver.ExifMarker)) { hasExif = true; byte[] array = new byte[remaining]; stream.Read(array, 0, remaining); if (exifData == null) { exifData = array; } else { ExtendProfile(ref exifData, array); } remaining = 0; } if (ProfileResolver.IsProfile(temp, ProfileResolver.XmpMarker.Slice(0, 6))) { if (remaining < 23 || IgnoreMetadata) { stream.Skip(remaining); return; } stream.Read(temp, 6, 23); remaining -= 23; if (ProfileResolver.IsProfile(temp, ProfileResolver.XmpMarker)) { hasXmp = true; byte[] array2 = new byte[remaining]; stream.Read(array2, 0, remaining); if (xmpData == null) { xmpData = array2; } else { ExtendProfile(ref xmpData, array2); } remaining = 0; } } stream.Skip(remaining); } private void ProcessApp2Marker(BufferedReadStream stream, int remaining) { if (remaining < 14 || IgnoreMetadata) { stream.Skip(remaining); return; } byte[] array = new byte[14]; stream.Read(array, 0, 14); remaining -= 14; if (ProfileResolver.IsProfile(array, ProfileResolver.IccMarker)) { hasIcc = true; byte[] array2 = new byte[remaining]; stream.Read(array2, 0, remaining); if (iccData == null) { iccData = array2; } else { ExtendProfile(ref iccData, array2); } } else { stream.Skip(remaining); } } private void ProcessApp13Marker(BufferedReadStream stream, int remaining) { if (remaining < ProfileResolver.AdobePhotoshopApp13Marker.Length || IgnoreMetadata) { stream.Skip(remaining); return; } stream.Read(temp, 0, ProfileResolver.AdobePhotoshopApp13Marker.Length); remaining -= ProfileResolver.AdobePhotoshopApp13Marker.Length; if (ProfileResolver.IsProfile(temp, ProfileResolver.AdobePhotoshopApp13Marker)) { byte[] array = new byte[remaining]; stream.Read(array, 0, remaining); Span blockDataSpan = array.AsSpan(); while (blockDataSpan.Length > 12 && ProfileResolver.IsProfile(blockDataSpan.Slice(0, 4), ProfileResolver.AdobeImageResourceBlockMarker)) { blockDataSpan = blockDataSpan.Slice(4); if (ProfileResolver.IsProfile(blockDataSpan.Slice(0, 2), ProfileResolver.AdobeIptcMarker)) { int num = ReadImageResourceNameLength(blockDataSpan); int num2 = ReadResourceDataLength(blockDataSpan, num); int num3 = 2 + num + 4; if (num2 > 0 && blockDataSpan.Length >= num3 + num2) { hasIptc = true; iptcData = blockDataSpan.Slice(num3, num2).ToArray(); break; } } else { int num4 = ReadImageResourceNameLength(blockDataSpan); int num5 = ReadResourceDataLength(blockDataSpan, num4); int num6 = 2 + num4 + 4; if (blockDataSpan.Length < num6 + num5) { break; } blockDataSpan = blockDataSpan.Slice(num6 + num5); } } } else { stream.Skip(remaining); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int ReadImageResourceNameLength(Span blockDataSpan) { byte b = blockDataSpan[2]; int num = ((b == 0) ? 2 : b); if (num % 2 != 0) { num++; } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int ReadResourceDataLength(Span blockDataSpan, int resourceBlockNameLength) { return BinaryPrimitives.ReadInt32BigEndian(blockDataSpan.Slice(2 + resourceBlockNameLength, 4)); } private void ProcessApp14Marker(BufferedReadStream stream, int remaining) { if (remaining < 12) { stream.Skip(remaining); return; } stream.Read(temp, 0, 12); remaining -= 12; AdobeMarker.TryParse(temp, out adobe); if (remaining > 0) { stream.Skip(remaining); } } private void ProcessDefineQuantizationTablesMarker(BufferedReadStream stream, int remaining) { JpegMetadata formatMetadata = Metadata.GetFormatMetadata(JpegFormat.Instance); while (remaining > 0) { int num = stream.ReadByte(); int num2 = num & 0xF; int num3 = num >> 4; if (num2 > 3) { JpegThrowHelper.ThrowBadQuantizationTableIndex(num2); } remaining--; ref Block8x8F reference = ref QuantizationTables[num2]; switch (num3) { case 0: { if (remaining < 64) { JpegThrowHelper.ThrowBadMarker("DQT", remaining); } stream.Read(temp, 0, 64); remaining -= 64; for (int j = 0; j < 64; j++) { reference[ZigZag.ZigZagOrder[j]] = (int)temp[j]; } break; } case 1: { if (remaining < 128) { JpegThrowHelper.ThrowBadMarker("DQT", remaining); } stream.Read(temp, 0, 128); remaining -= 128; for (int i = 0; i < 64; i++) { reference[ZigZag.ZigZagOrder[i]] = (temp[2 * i] << 8) | temp[2 * i + 1]; } break; } default: JpegThrowHelper.ThrowBadQuantizationTablePrecision(num3); break; } switch (num2) { case 0: formatMetadata.LuminanceQuality = Quantization.EstimateLuminanceQuality(ref reference); break; case 1: formatMetadata.ChrominanceQuality = Quantization.EstimateChrominanceQuality(ref reference); break; } FastFloatingPointDCT.AdjustToIDCT(ref reference); } } private void ProcessStartOfFrameMarker(BufferedReadStream stream, int remaining, in JpegFileMarker frameMarker, bool metadataOnly) { if (Frame != null) { if (metadataOnly) { return; } JpegThrowHelper.ThrowInvalidImageContentException("Multiple SOF markers. Only single frame jpegs supported."); } stream.Read(temp, 0, 6); byte b = temp[0]; if (Array.IndexOf(supportedPrecisions, b) == -1) { JpegThrowHelper.ThrowInvalidImageContentException("Only 8-Bit and 12-Bit precision supported."); } int num = (temp[1] << 8) | temp[2]; int num2 = (temp[3] << 8) | temp[4]; if (num == 0 || num2 == 0) { JpegThrowHelper.ThrowInvalidImageDimensions(num2, num); } byte b2 = temp[5]; if (b2 > 4) { JpegThrowHelper.ThrowNotSupportedComponentCount(b2); } Frame = new JpegFrame(frameMarker, b, num2, num, b2); remaining -= 6; if (remaining != b2 * 3) { JpegThrowHelper.ThrowBadMarker("SOFn", remaining); } stream.Read(temp, 0, remaining); Frame.ComponentIds = new byte[b2]; Frame.ComponentOrder = new byte[b2]; Frame.Components = new JpegComponent[b2]; int num3 = 0; int num4 = 0; int num5 = 0; for (int i = 0; i < b2; i++) { byte b3 = temp[num5]; byte num6 = temp[num5 + 1]; int num7 = (num6 >> 4) & 0xF; int num8 = num6 & 0xF; if (Numerics.IsOutOfRange(num7, 1, 4)) { JpegThrowHelper.ThrowBadSampling(num7); } if (Numerics.IsOutOfRange(num8, 1, 4)) { JpegThrowHelper.ThrowBadSampling(num8); } if (num3 < num7) { num3 = num7; } if (num4 < num8) { num4 = num8; } byte b4 = temp[num5 + 2]; if (b4 > 3) { JpegThrowHelper.ThrowBadQuantizationTableIndex(b4); } JpegComponent jpegComponent = new JpegComponent(Configuration.MemoryAllocator, Frame, b3, num7, num8, b4, i); Frame.Components[i] = jpegComponent; Frame.ComponentIds[i] = b3; num5 += 3; } ColorSpace = DeduceJpegColorSpace(b2); Metadata.GetJpegMetadata().ColorType = DeduceJpegColorType(); if (!metadataOnly) { Frame.Init(num3, num4); scanDecoder.InjectFrameData(Frame, this); } } private void ProcessDefineHuffmanTablesMarker(BufferedReadStream stream, int remaining) { int num = remaining; using IMemoryOwner buffer = Configuration.MemoryAllocator.Allocate(1297); Span span = buffer.GetSpan(); Span span2 = span.Slice(0, 17); Span buffer2 = span.Slice(17, 256); Span workspace = MemoryMarshal.Cast(span.Slice(273)); int num2 = 2; while (num2 < remaining) { byte num3 = (byte)stream.ReadByte(); int num4 = num3 >> 4; int num5 = num3 & 0xF; if (num4 > 1) { JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table type: {num4}."); } if (num5 > 3) { JpegThrowHelper.ThrowInvalidImageContentException($"Bad huffman table index: {num5}."); } stream.Read(span2, 1, 16); int num6 = 0; for (int i = 1; i < 17; i++) { num6 += span2[i]; } num -= 17; if (num6 > 256 || num6 > num) { JpegThrowHelper.ThrowInvalidImageContentException("Huffman table has excessive length."); } stream.Read(buffer2, 0, num6); num2 += 17 + num6; scanDecoder.BuildHuffmanTable(num4, num5, span2, buffer2.Slice(0, num6), workspace); } } private void ProcessDefineRestartIntervalMarker(BufferedReadStream stream, int remaining) { if (remaining != 2) { JpegThrowHelper.ThrowBadMarker("DRI", remaining); } scanDecoder.ResetInterval = ReadUint16(stream); } private void ProcessStartOfScanMarker(BufferedReadStream stream, int remaining) { if (Frame == null) { JpegThrowHelper.ThrowInvalidImageContentException("No readable SOFn (Start Of Frame) marker found."); } int num = stream.ReadByte(); if (num == 0 || num > Frame.ComponentCount) { JpegThrowHelper.ThrowInvalidImageContentException($"Invalid number of components in scan: {num}."); } int num2 = num * 2; if (remaining != 4 + num2) { JpegThrowHelper.ThrowBadMarker("SOS", remaining); } stream.Read(temp, 0, num2); Frame.MultiScan = Frame.ComponentCount != num; for (int i = 0; i < num2; i += 2) { int num3 = temp[i]; int num4 = -1; for (int j = 0; j < Frame.ComponentIds.Length; j++) { byte b = Frame.ComponentIds[j]; if (num3 == b) { num4 = j; break; } } if (num4 == -1) { JpegThrowHelper.ThrowInvalidImageContentException($"Unknown component id in scan: {num3}."); } Frame.ComponentOrder[i / 2] = (byte)num4; JpegComponent obj = Frame.Components[num4]; byte num5 = temp[i + 1]; int num6 = num5 >> 4; int num7 = num5 & 0xF; if (num6 >= 4 || num7 >= 4) { JpegThrowHelper.ThrowInvalidImageContentException($"Invalid huffman table for component:{num3}: dc={num6}, ac={num7}"); } obj.DCHuffmanTableId = num6; obj.ACHuffmanTableId = num7; } if (stream.Read(temp, 0, 3) != 3) { JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read progressive scan decoding data"); } int spectralStart = temp[0]; scanDecoder.SpectralStart = spectralStart; int spectralEnd = temp[1]; scanDecoder.SpectralEnd = spectralEnd; int num8 = temp[2]; scanDecoder.SuccessiveHigh = num8 >> 4; scanDecoder.SuccessiveLow = num8 & 0xF; scanDecoder.ParseEntropyCodedData(num); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private ushort ReadUint16(BufferedReadStream stream) { if (stream.Read(markerBuffer, 0, 2) != 2) { JpegThrowHelper.ThrowInvalidImageContentException("jpeg stream does not contain enough data, could not read ushort."); } return BinaryPrimitives.ReadUInt16BigEndian(markerBuffer); } } public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions { public int? Quality { get; set; } public JpegColorType? ColorType { get; set; } public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { new JpegEncoderCore(this).Encode(image, stream); } public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return new JpegEncoderCore(this).EncodeAsync(image, stream, cancellationToken); } } internal sealed class JpegEncoderCore : IImageEncoderInternals { private const int QuantizationTableCount = 2; private readonly byte[] buffer = new byte[20]; private readonly int? quality; private JpegColorType? colorType; private Stream outputStream; public JpegEncoderCore(IJpegEncoderOptions options) { quality = options.Quality; if (IsSupportedColorType(options.ColorType)) { colorType = options.ColorType; } } public void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); if (image.Width >= 65535 || image.Height >= 65535) { JpegThrowHelper.ThrowDimensionsTooLarge(image.Width, image.Height); } cancellationToken.ThrowIfCancellationRequested(); outputStream = stream; ImageMetadata metadata = image.Metadata; JpegMetadata jpegMetadata = metadata.GetJpegMetadata(); if (!colorType.HasValue) { colorType = GetFallbackColorType(image); } int componentCount = ((colorType == JpegColorType.Luminance) ? 1 : 3); ReadOnlySpan componentIds = GetComponentIds(); InitQuantizationTables(componentCount, jpegMetadata, out var luminanceQuantTable, out var chrominanceQuantTable); WriteStartOfImage(); if (colorType != JpegColorType.Rgb) { WriteJfifApplicationHeader(metadata); } WriteProfiles(metadata); if (colorType == JpegColorType.Rgb) { WriteApp14Marker(); } WriteDefineQuantizationTables(ref luminanceQuantTable, ref chrominanceQuantTable); WriteStartOfFrame(image.Width, image.Height, componentCount, componentIds); WriteDefineHuffmanTables(componentCount); WriteStartOfScan(componentCount, componentIds); switch (colorType) { case JpegColorType.YCbCrRatio444: new HuffmanScanEncoder(3, stream).Encode444(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken); break; case JpegColorType.YCbCrRatio420: new HuffmanScanEncoder(6, stream).Encode420(image, ref luminanceQuantTable, ref chrominanceQuantTable, cancellationToken); break; case JpegColorType.Luminance: new HuffmanScanEncoder(1, stream).EncodeGrayscale(image, ref luminanceQuantTable, cancellationToken); break; case JpegColorType.Rgb: new HuffmanScanEncoder(3, stream).EncodeRgb(image, ref luminanceQuantTable, cancellationToken); break; } WriteEndOfImageMarker(); stream.Flush(); } private static JpegColorType? GetFallbackColorType(Image image) where TPixel : unmanaged, IPixel { JpegColorType? result = null; JpegMetadata jpegMetadata = image.Metadata.GetJpegMetadata(); if (IsSupportedColorType(jpegMetadata.ColorType)) { return jpegMetadata.ColorType; } if (typeof(TPixel) == typeof(L8) || typeof(TPixel) == typeof(L16) || typeof(TPixel) == typeof(La16) || typeof(TPixel) == typeof(La32)) { result = JpegColorType.Luminance; } return result; } private static bool IsSupportedColorType(JpegColorType? colorType) { if (colorType != JpegColorType.YCbCrRatio444 && colorType != JpegColorType.YCbCrRatio420 && colorType != JpegColorType.Luminance) { return colorType == JpegColorType.Rgb; } return true; } private ReadOnlySpan GetComponentIds() { if (colorType == JpegColorType.Rgb) { return "RGB"u8; } return new byte[3] { 1, 2, 3 }; } private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F quant) { dqt[offset++] = (byte)i; for (int j = 0; j < 64; j++) { dqt[offset++] = (byte)quant[ZigZag.ZigZagOrder[j]]; } } private void WriteStartOfImage() { buffer[0] = byte.MaxValue; buffer[1] = 216; outputStream.Write(buffer, 0, 2); } private void WriteJfifApplicationHeader(ImageMetadata meta) { buffer[0] = byte.MaxValue; buffer[1] = 224; buffer[2] = 0; buffer[3] = 16; buffer[4] = 74; buffer[5] = 70; buffer[6] = 73; buffer[7] = 70; buffer[8] = 0; buffer[9] = 1; buffer[10] = 1; Span destination = buffer.AsSpan(12, 2); Span destination2 = buffer.AsSpan(14, 2); if (meta.ResolutionUnits == PixelResolutionUnit.PixelsPerMeter) { buffer[11] = 1; BinaryPrimitives.WriteInt16BigEndian(destination, (short)Math.Round(UnitConverter.MeterToInch(meta.HorizontalResolution))); BinaryPrimitives.WriteInt16BigEndian(destination2, (short)Math.Round(UnitConverter.MeterToInch(meta.VerticalResolution))); } else { buffer[11] = (byte)meta.ResolutionUnits; BinaryPrimitives.WriteInt16BigEndian(destination, (short)Math.Round(meta.HorizontalResolution)); BinaryPrimitives.WriteInt16BigEndian(destination2, (short)Math.Round(meta.VerticalResolution)); } buffer[16] = 0; buffer[17] = 0; outputStream.Write(buffer, 0, 18); } private void WriteDefineHuffmanTables(int componentCount) { ReadOnlySpan readOnlySpan = new byte[4] { 0, 16, 1, 17 }; int num = 2; HuffmanSpec[] array = HuffmanSpec.TheHuffmanSpecs; if (componentCount == 1) { array = new HuffmanSpec[2] { HuffmanSpec.TheHuffmanSpecs[0], HuffmanSpec.TheHuffmanSpecs[1] }; } for (int i = 0; i < array.Length; i++) { num += 17 + array[i].Values.Length; } WriteMarkerHeader(196, num); for (int j = 0; j < array.Length; j++) { outputStream.WriteByte(readOnlySpan[j]); outputStream.Write(array[j].Count); outputStream.Write(array[j].Values); } } private void WriteDefineQuantizationTables(ref Block8x8F luminanceQuantTable, ref Block8x8F chrominanceQuantTable) { int length = 132; WriteMarkerHeader(219, length); int num = 130; byte[] dqt = new byte[num]; int offset = 0; WriteDataToDqt(dqt, ref offset, QuantIndex.Luminance, ref luminanceQuantTable); WriteDataToDqt(dqt, ref offset, QuantIndex.Chrominance, ref chrominanceQuantTable); outputStream.Write(dqt, 0, num); } private void WriteApp14Marker() { WriteMarkerHeader(238, 14); buffer[0] = 65; buffer[1] = 100; buffer[2] = 111; buffer[3] = 98; buffer[4] = 101; BinaryPrimitives.WriteInt16BigEndian(buffer.AsSpan(5, 2), 100); BinaryPrimitives.WriteInt16BigEndian(buffer.AsSpan(7, 2), 0); BinaryPrimitives.WriteInt16BigEndian(buffer.AsSpan(9, 2), 0); buffer[11] = 0; outputStream.Write(buffer.AsSpan(0, 12)); } private void WriteExifProfile(ExifProfile exifProfile) { if (exifProfile == null || exifProfile.Values.Count == 0) { return; } byte[] array = exifProfile.ToByteArray(); if (array.Length != 0) { int length = ProfileResolver.ExifMarker.Length; int num = length + array.Length; int num2 = ((num > 65533) ? 65533 : num); int app1Length = num2 + 2; WriteApp1Header(app1Length); outputStream.Write(ProfileResolver.ExifMarker); outputStream.Write(array, 0, num2 - length); num -= num2; for (int i = 65527; i < array.Length; i += 65527) { num2 = ((num > 65527) ? 65527 : num); app1Length = num2 + 2 + length; WriteApp1Header(app1Length); outputStream.Write(ProfileResolver.ExifMarker); outputStream.Write(array, i, num2); num -= num2; } } } private void WriteIptcProfile(IptcProfile iptcProfile) { if (iptcProfile == null || !iptcProfile.Values.Any()) { return; } iptcProfile.UpdateData(); byte[] data = iptcProfile.Data; if (data.Length != 0) { if (data.Length > 65533) { throw new ImageFormatException($"Iptc profile size exceeds limit of {65533} bytes"); } int length = 2 + ProfileResolver.AdobePhotoshopApp13Marker.Length + ProfileResolver.AdobeImageResourceBlockMarker.Length + ProfileResolver.AdobeIptcMarker.Length + 2 + 4 + data.Length; WriteAppHeader(length, 237); outputStream.Write(ProfileResolver.AdobePhotoshopApp13Marker); outputStream.Write(ProfileResolver.AdobeImageResourceBlockMarker); outputStream.Write(ProfileResolver.AdobeIptcMarker); outputStream.WriteByte(0); outputStream.WriteByte(0); BinaryPrimitives.WriteInt32BigEndian(buffer, data.Length); outputStream.Write(buffer, 0, 4); outputStream.Write(data, 0, data.Length); } } private void WriteXmpProfile(XmpProfile xmpProfile) { if (xmpProfile == null) { return; } byte[] data = xmpProfile.Data; if (data == null || data.Length == 0) { return; } int num = data.Length; int num2 = 0; while (num > 0) { int num3 = num; if (num3 > 65504) { num3 = 65504; } num -= num3; int app1Length = 2 + ProfileResolver.XmpMarker.Length + num3; WriteApp1Header(app1Length); outputStream.Write(ProfileResolver.XmpMarker); outputStream.Write(data, num2, num3); num2 += num3; } } private void WriteApp1Header(int app1Length) { WriteAppHeader(app1Length, 225); } private void WriteAppHeader(int length, byte appMarker) { buffer[0] = byte.MaxValue; buffer[1] = appMarker; buffer[2] = (byte)((length >> 8) & 0xFF); buffer[3] = (byte)(length & 0xFF); outputStream.Write(buffer, 0, 4); } private void WriteIccProfile(IccProfile iccProfile) { if (iccProfile == null) { return; } byte[] array = iccProfile.ToByteArray(); if (array == null || array.Length == 0) { return; } int num = array.Length; int num2 = num / 65519; if (num2 * 65519 != num) { num2++; } int num3 = 1; int num4 = 0; while (num > 0) { int num5 = num; if (num5 > 65519) { num5 = 65519; } num -= num5; buffer[0] = byte.MaxValue; buffer[1] = 226; int num6 = num5 + 16; buffer[2] = (byte)((num6 >> 8) & 0xFF); buffer[3] = (byte)(num6 & 0xFF); outputStream.Write(buffer, 0, 4); buffer[0] = 73; buffer[1] = 67; buffer[2] = 67; buffer[3] = 95; buffer[4] = 80; buffer[5] = 82; buffer[6] = 79; buffer[7] = 70; buffer[8] = 73; buffer[9] = 76; buffer[10] = 69; buffer[11] = 0; buffer[12] = (byte)num3; buffer[13] = (byte)num2; outputStream.Write(buffer, 0, 14); outputStream.Write(array, num4, num5); num3++; num4 += num5; } } private void WriteProfiles(ImageMetadata metadata) { if (metadata != null) { metadata.SyncProfiles(); WriteExifProfile(metadata.ExifProfile); WriteXmpProfile(metadata.XmpProfile); WriteIccProfile(metadata.IccProfile); WriteIptcProfile(metadata.IptcProfile); } } private void WriteStartOfFrame(int width, int height, int componentCount, ReadOnlySpan componentIds) { ReadOnlySpan readOnlySpan = new byte[3] { 34, 17, 17 }; ReadOnlySpan readOnlySpan2 = new byte[3] { 0, 1, 1 }; if (colorType == JpegColorType.Luminance) { readOnlySpan = new byte[3] { 17, 0, 0 }; } else { switch (colorType) { case JpegColorType.YCbCrRatio444: case JpegColorType.Rgb: readOnlySpan = new byte[3] { 17, 17, 17 }; if (colorType == JpegColorType.Rgb) { readOnlySpan2 = new byte[3] { 0, 0, 0 }; } break; case JpegColorType.YCbCrRatio420: readOnlySpan = new byte[3] { 34, 17, 17 }; break; } } int length = 8 + 3 * componentCount; WriteMarkerHeader(192, length); buffer[0] = 8; buffer[1] = (byte)(height >> 8); buffer[2] = (byte)(height & 0xFF); buffer[3] = (byte)(width >> 8); buffer[4] = (byte)(width & 0xFF); buffer[5] = (byte)componentCount; for (int i = 0; i < componentCount; i++) { int num = 3 * i; Span span = buffer.AsSpan(num + 6, 3); span[2] = readOnlySpan2[i]; span[1] = readOnlySpan[i]; span[0] = componentIds[i]; } outputStream.Write(buffer, 0, 3 * (componentCount - 1) + 9); } private void WriteStartOfScan(int componentCount, ReadOnlySpan componentIds) { ReadOnlySpan readOnlySpan = new byte[3] { 0, 17, 17 }; if (colorType == JpegColorType.Rgb) { readOnlySpan = new byte[3] { 0, 0, 0 }; } buffer[0] = byte.MaxValue; buffer[1] = 218; int num = 6 + 2 * componentCount; buffer[2] = 0; buffer[3] = (byte)num; buffer[4] = (byte)componentCount; for (int i = 0; i < componentCount; i++) { int num2 = 2 * i; buffer[num2 + 5] = componentIds[i]; buffer[num2 + 6] = readOnlySpan[i]; } buffer[num - 1] = 0; buffer[num] = 63; buffer[num + 1] = 0; outputStream.Write(buffer, 0, num + 2); } private void WriteEndOfImageMarker() { buffer[0] = byte.MaxValue; buffer[1] = 217; outputStream.Write(buffer, 0, 2); } private void WriteMarkerHeader(byte marker, int length) { buffer[0] = byte.MaxValue; buffer[1] = marker; buffer[2] = (byte)(length >> 8); buffer[3] = (byte)(length & 0xFF); outputStream.Write(buffer, 0, 4); } private void InitQuantizationTables(int componentCount, JpegMetadata metadata, out Block8x8F luminanceQuantTable, out Block8x8F chrominanceQuantTable) { int value; int value2; if (quality.HasValue) { value = quality.Value; value2 = quality.Value; } else { value = metadata.LuminanceQuality; value2 = metadata.ChrominanceQuality; } value = Numerics.Clamp(value, 1, 100); luminanceQuantTable = Quantization.ScaleLuminanceTable(value); chrominanceQuantTable = default(Block8x8F); if (componentCount > 1) { value2 = Numerics.Clamp(value2, 1, 100); chrominanceQuantTable = Quantization.ScaleChrominanceTable(value2); if (!colorType.HasValue) { colorType = ((value2 >= 91) ? JpegColorType.YCbCrRatio444 : JpegColorType.YCbCrRatio420); } } } } public sealed class JpegFormat : IImageFormat, IImageFormat { public static JpegFormat Instance { get; } = new JpegFormat(); public string Name => "JPEG"; public string DefaultMimeType => "image/jpeg"; public IEnumerable MimeTypes => JpegConstants.MimeTypes; public IEnumerable FileExtensions => JpegConstants.FileExtensions; private JpegFormat() { } public JpegMetadata CreateDefaultFormatMetadata() { return new JpegMetadata(); } } public sealed class JpegImageFormatDetector : IImageFormatDetector { public int HeaderSize => 11; public IImageFormat DetectFormat(ReadOnlySpan header) { if (!IsSupportedFileFormat(header)) { return null; } return JpegFormat.Instance; } private bool IsSupportedFileFormat(ReadOnlySpan header) { if (header.Length >= HeaderSize) { if (!IsJfif(header) && !IsExif(header)) { return IsJpeg(header); } return true; } return false; } private bool IsJfif(ReadOnlySpan header) { if (header[6] == 74 && header[7] == 70 && header[8] == 73 && header[9] == 70) { return header[10] == 0; } return false; } private bool IsExif(ReadOnlySpan header) { if (header[6] == 69 && header[7] == 120 && header[8] == 105 && header[9] == 102) { return header[10] == 0; } return false; } private bool IsJpeg(ReadOnlySpan header) { if (header[0] == byte.MaxValue) { return header[1] == 216; } return false; } } public class JpegMetadata : IDeepCloneable { private int? luminanceQuality; private int? chrominanceQuality; internal int LuminanceQuality { get { return luminanceQuality ?? 75; } set { luminanceQuality = value; } } internal int ChrominanceQuality { get { return chrominanceQuality ?? 75; } set { chrominanceQuality = value; } } public int Quality { get { if (!luminanceQuality.HasValue) { return 75; } int value = luminanceQuality.Value; if (!chrominanceQuality.HasValue) { return value; } int value2 = chrominanceQuality.Value; return Math.Max(value, value2); } set { LuminanceQuality = value; ChrominanceQuality = value; } } public JpegColorType? ColorType { get; set; } public JpegMetadata() { } private JpegMetadata(JpegMetadata other) { ColorType = other.ColorType; luminanceQuality = other.luminanceQuality; chrominanceQuality = other.chrominanceQuality; } public IDeepCloneable DeepClone() { return new JpegMetadata(this); } } internal static class JpegThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotSupportedException(string errorMessage) { throw new NotSupportedException(errorMessage); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidImageContentException(string errorMessage) { throw new InvalidImageContentException(errorMessage); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowBadMarker(string marker, int length) { throw new InvalidImageContentException($"Marker {marker} has bad length {length}."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotEnoughBytesForMarker(byte marker) { throw new InvalidImageContentException($"Input stream does not have enough bytes to parse declared contents of the {marker:X2} marker."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowBadQuantizationTableIndex(int index) { throw new InvalidImageContentException($"Bad Quantization Table index {index}."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowBadQuantizationTablePrecision(int precision) { throw new InvalidImageContentException($"Unknown Quantization Table precision {precision}."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowBadSampling() { throw new InvalidImageContentException("Bad sampling factor."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowBadSampling(int factor) { throw new InvalidImageContentException($"Bad sampling factor: {factor}"); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowBadProgressiveScan(int ss, int se, int ah, int al) { throw new InvalidImageContentException($"Invalid progressive parameters Ss={ss} Se={se} Ah={ah} Al={al}."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidImageDimensions(int width, int height) { throw new InvalidImageContentException($"Invalid image dimensions: {width}x{height}."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowDimensionsTooLarge(int width, int height) { throw new ImageFormatException($"Image is too large to encode at {width}x{height} for JPEG format."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotSupportedComponentCount(int componentCount) { throw new NotSupportedException($"Images with {componentCount} components are not supported."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotSupportedColorSpace() { throw new NotSupportedException("Image color space could not be deduced."); } } } namespace SixLabors.ImageSharp.Formats.Jpeg.Components { [StructLayout(LayoutKind.Explicit)] internal struct Block8x8 { public const int Size = 64; [FieldOffset(0)] private unsafe fixed short data[64]; [FieldOffset(0)] public Vector128 V0; [FieldOffset(16)] public Vector128 V1; [FieldOffset(32)] public Vector128 V2; [FieldOffset(48)] public Vector128 V3; [FieldOffset(64)] public Vector128 V4; [FieldOffset(80)] public Vector128 V5; [FieldOffset(96)] public Vector128 V6; [FieldOffset(112)] public Vector128 V7; [FieldOffset(0)] public Vector256 V01; [FieldOffset(32)] public Vector256 V23; [FieldOffset(64)] public Vector256 V45; [FieldOffset(96)] public Vector256 V67; public short this[int idx] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Unsafe.Add(ref Unsafe.As(ref this), idx); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Unsafe.Add(ref Unsafe.As(ref this), idx) = value; } } public short this[int x, int y] { get { return this[y * 8 + x]; } set { this[y * 8 + x] = value; } } public static Block8x8 Load(Span data) { Unsafe.SkipInit(out var value); value.LoadFrom(data); return value; } public Block8x8F AsFloatBlock() { Block8x8F result = default(Block8x8F); result.LoadFrom(ref this); return result; } public short[] ToArray() { short[] array = new short[64]; CopyTo(array); return array; } public void CopyTo(Span destination) { ref byte source = ref Unsafe.As(ref this); Unsafe.CopyBlockUnaligned(ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destination)), in source, 128u); } public void CopyTo(Span destination) { for (int i = 0; i < 64; i++) { destination[i] = this[i]; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void LoadFrom(Span source) { ref byte source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref this), in source2, 128u); } public void LoadFrom(Span source) { for (int i = 0; i < 64; i++) { this[i] = (short)source[i]; } } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('['); for (int i = 0; i < 64; i++) { stringBuilder.Append(this[i]); if (i < 63) { stringBuilder.Append(','); } } stringBuilder.Append(']'); return stringBuilder.ToString(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public nint GetLastNonZeroIndex() { if (Avx2.IsSupported) { Vector256 zero = Vector256.Zero; ref Vector256 source = ref Unsafe.As>(ref this); for (nint num = 3; num >= 0; num--) { int num2 = Avx2.MoveMask(Avx2.CompareEqual(Unsafe.Add(ref source, num), zero).AsByte()); if (num2 != -1) { int num3 = BitOperations.LeadingZeroCount((uint)(~num2)); int num4 = 15 - num3 / 2; return num * 16 + num4; } } return -1; } nint num5 = 63; ref short source2 = ref Unsafe.As(ref this); while (num5 >= 0 && Unsafe.Add(ref source2, num5) == 0) { num5--; } return num5; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TransposeInplace() { ref short source = ref Unsafe.As(ref this); Swap(ref Unsafe.Add(ref source, 1), ref Unsafe.Add(ref source, 8)); Swap(ref Unsafe.Add(ref source, 2), ref Unsafe.Add(ref source, 16)); Swap(ref Unsafe.Add(ref source, 3), ref Unsafe.Add(ref source, 24)); Swap(ref Unsafe.Add(ref source, 4), ref Unsafe.Add(ref source, 32)); Swap(ref Unsafe.Add(ref source, 5), ref Unsafe.Add(ref source, 40)); Swap(ref Unsafe.Add(ref source, 6), ref Unsafe.Add(ref source, 48)); Swap(ref Unsafe.Add(ref source, 7), ref Unsafe.Add(ref source, 56)); Swap(ref Unsafe.Add(ref source, 10), ref Unsafe.Add(ref source, 17)); Swap(ref Unsafe.Add(ref source, 11), ref Unsafe.Add(ref source, 25)); Swap(ref Unsafe.Add(ref source, 12), ref Unsafe.Add(ref source, 33)); Swap(ref Unsafe.Add(ref source, 13), ref Unsafe.Add(ref source, 41)); Swap(ref Unsafe.Add(ref source, 14), ref Unsafe.Add(ref source, 49)); Swap(ref Unsafe.Add(ref source, 15), ref Unsafe.Add(ref source, 57)); Swap(ref Unsafe.Add(ref source, 19), ref Unsafe.Add(ref source, 26)); Swap(ref Unsafe.Add(ref source, 20), ref Unsafe.Add(ref source, 34)); Swap(ref Unsafe.Add(ref source, 21), ref Unsafe.Add(ref source, 42)); Swap(ref Unsafe.Add(ref source, 22), ref Unsafe.Add(ref source, 50)); Swap(ref Unsafe.Add(ref source, 23), ref Unsafe.Add(ref source, 58)); Swap(ref Unsafe.Add(ref source, 28), ref Unsafe.Add(ref source, 35)); Swap(ref Unsafe.Add(ref source, 29), ref Unsafe.Add(ref source, 43)); Swap(ref Unsafe.Add(ref source, 30), ref Unsafe.Add(ref source, 51)); Swap(ref Unsafe.Add(ref source, 31), ref Unsafe.Add(ref source, 59)); Swap(ref Unsafe.Add(ref source, 37), ref Unsafe.Add(ref source, 44)); Swap(ref Unsafe.Add(ref source, 38), ref Unsafe.Add(ref source, 52)); Swap(ref Unsafe.Add(ref source, 39), ref Unsafe.Add(ref source, 60)); Swap(ref Unsafe.Add(ref source, 46), ref Unsafe.Add(ref source, 53)); Swap(ref Unsafe.Add(ref source, 47), ref Unsafe.Add(ref source, 61)); Swap(ref Unsafe.Add(ref source, 55), ref Unsafe.Add(ref source, 62)); static void Swap(ref short a, ref short b) { short num = a; a = b; b = num; } } public static long TotalDifference(ref Block8x8 a, ref Block8x8 b) { long num = 0L; for (int i = 0; i < 64; i++) { int value = a[i] - b[i]; num += Math.Abs(value); } return num; } } [StructLayout(LayoutKind.Explicit)] internal struct Block8x8F : IEquatable { public const int Size = 64; [FieldOffset(0)] public Vector4 V0L; [FieldOffset(16)] public Vector4 V0R; [FieldOffset(32)] public Vector4 V1L; [FieldOffset(48)] public Vector4 V1R; [FieldOffset(64)] public Vector4 V2L; [FieldOffset(80)] public Vector4 V2R; [FieldOffset(96)] public Vector4 V3L; [FieldOffset(112)] public Vector4 V3R; [FieldOffset(128)] public Vector4 V4L; [FieldOffset(144)] public Vector4 V4R; [FieldOffset(160)] public Vector4 V5L; [FieldOffset(176)] public Vector4 V5R; [FieldOffset(192)] public Vector4 V6L; [FieldOffset(208)] public Vector4 V6R; [FieldOffset(224)] public Vector4 V7L; [FieldOffset(240)] public Vector4 V7R; public const int RowCount = 8; [FieldOffset(0)] public Vector256 V0; [FieldOffset(32)] public Vector256 V1; [FieldOffset(64)] public Vector256 V2; [FieldOffset(96)] public Vector256 V3; [FieldOffset(128)] public Vector256 V4; [FieldOffset(160)] public Vector256 V5; [FieldOffset(192)] public Vector256 V6; [FieldOffset(224)] public Vector256 V7; private static readonly Vector256 MultiplyIntoInt16ShuffleMask = Vector256.Create(0, 1, 4, 5, 2, 3, 6, 7); public float this[int idx] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Unsafe.Add(ref Unsafe.As(ref this), (nint)(uint)idx); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Unsafe.Add(ref Unsafe.As(ref this), (nint)(uint)idx) = value; } } public float this[int x, int y] { get { return this[y * 8 + x]; } set { this[y * 8 + x] = value; } } public static Block8x8F Load(Span data) { Block8x8F result = default(Block8x8F); result.LoadFrom(data); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void LoadFrom(Span source) { ref byte source2 = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); Unsafe.CopyBlock(ref Unsafe.As(ref this), in source2, 256u); } public unsafe void LoadFrom(Span source) { fixed (Vector4* v0L = &V0L) { float* ptr = (float*)v0L; for (int i = 0; i < 64; i++) { ptr[i] = source[i]; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ScaledCopyTo(float[] dest) { fixed (Vector4* v0L = &V0L) { void* ptr = v0L; Marshal.Copy((IntPtr)ptr, dest, 0, 64); } } public float[] ToArray() { float[] array = new float[64]; ScaledCopyTo(array); return array; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void MultiplyInPlace(float value) { if (Avx.IsSupported) { Vector256 right = Vector256.Create(value); V0 = Avx.Multiply(V0, right); V1 = Avx.Multiply(V1, right); V2 = Avx.Multiply(V2, right); V3 = Avx.Multiply(V3, right); V4 = Avx.Multiply(V4, right); V5 = Avx.Multiply(V5, right); V6 = Avx.Multiply(V6, right); V7 = Avx.Multiply(V7, right); } else { Vector4 vector = new Vector4(value); V0L *= vector; V0R *= vector; V1L *= vector; V1R *= vector; V2L *= vector; V2R *= vector; V3L *= vector; V3R *= vector; V4L *= vector; V4R *= vector; V5L *= vector; V5R *= vector; V6L *= vector; V6R *= vector; V7L *= vector; V7R *= vector; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void MultiplyInPlace(ref Block8x8F other) { if (Avx.IsSupported) { V0 = Avx.Multiply(V0, other.V0); V1 = Avx.Multiply(V1, other.V1); V2 = Avx.Multiply(V2, other.V2); V3 = Avx.Multiply(V3, other.V3); V4 = Avx.Multiply(V4, other.V4); V5 = Avx.Multiply(V5, other.V5); V6 = Avx.Multiply(V6, other.V6); V7 = Avx.Multiply(V7, other.V7); return; } V0L *= other.V0L; V0R *= other.V0R; V1L *= other.V1L; V1R *= other.V1R; V2L *= other.V2L; V2R *= other.V2R; V3L *= other.V3L; V3R *= other.V3R; V4L *= other.V4L; V4R *= other.V4R; V5L *= other.V5L; V5R *= other.V5R; V6L *= other.V6L; V6R *= other.V6R; V7L *= other.V7L; V7R *= other.V7R; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddInPlace(float value) { if (Avx.IsSupported) { Vector256 right = Vector256.Create(value); V0 = Avx.Add(V0, right); V1 = Avx.Add(V1, right); V2 = Avx.Add(V2, right); V3 = Avx.Add(V3, right); V4 = Avx.Add(V4, right); V5 = Avx.Add(V5, right); V6 = Avx.Add(V6, right); V7 = Avx.Add(V7, right); } else { Vector4 vector = new Vector4(value); V0L += vector; V0R += vector; V1L += vector; V1R += vector; V2L += vector; V2R += vector; V3L += vector; V3R += vector; V4L += vector; V4R += vector; V5L += vector; V5R += vector; V6L += vector; V6R += vector; V7L += vector; V7R += vector; } } public static void Quantize(ref Block8x8F block, ref Block8x8 dest, ref Block8x8F qt) { if (Avx2.IsSupported) { MultiplyIntoInt16_Avx2(ref block, ref qt, ref dest); ZigZag.ApplyTransposingZigZagOrderingAvx2(ref dest); return; } if (Ssse3.IsSupported) { MultiplyIntoInt16_Sse2(ref block, ref qt, ref dest); ZigZag.ApplyTransposingZigZagOrderingSsse3(ref dest); return; } for (int i = 0; i < 64; i++) { int idx = ZigZag.TransposingOrder[i]; float num = block[idx] * qt[idx]; num += ((num < 0f) ? (-0.5f) : 0.5f); dest[i] = (short)num; } } public void RoundInto(ref Block8x8 dest) { for (int i = 0; i < 64; i++) { float num = this[i]; num = ((!(num < 0f)) ? (num + 0.5f) : (num - 0.5f)); dest[i] = (short)num; } } public Block8x8 RoundAsInt16Block() { Block8x8 dest = default(Block8x8); RoundInto(ref dest); return dest; } public void NormalizeColorsAndRoundInPlace(float maximum) { if (SimdUtils.HasVector8) { NormalizeColorsAndRoundInPlaceVector8(maximum); return; } NormalizeColorsInPlace(maximum); RoundInPlace(); } public void RoundInPlace() { for (int i = 0; i < 64; i++) { this[i] = MathF.Round(this[i]); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void LoadFrom(ref Block8x8 source) { if (SimdUtils.HasVector8) { LoadFromInt16ExtendedAvx2(ref source); } else { LoadFromInt16Scalar(ref source); } } public void LoadFromInt16ExtendedAvx2(ref Block8x8 source) { ref Vector reference = ref Unsafe.As>(ref source); ref Vector reference2 = ref Unsafe.As>(ref this); SimdUtils.ExtendedIntrinsics.ConvertToSingle(reference, out var dest, out var dest2); reference2 = dest; Unsafe.Add(ref reference2, 1) = dest2; SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref reference, 1), out dest, out dest2); Unsafe.Add(ref reference2, 2) = dest; Unsafe.Add(ref reference2, 3) = dest2; SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref reference, 2), out dest, out dest2); Unsafe.Add(ref reference2, 4) = dest; Unsafe.Add(ref reference2, 5) = dest2; SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref reference, 3), out dest, out dest2); Unsafe.Add(ref reference2, 6) = dest; Unsafe.Add(ref reference2, 7) = dest2; } public bool EqualsToScalar(int value) { if (Avx2.IsSupported) { Vector256 right = Vector256.Create(value); for (int i = 0; i < 8; i++) { if (Avx2.MoveMask(Avx2.CompareEqual(Avx.ConvertToVector256Int32WithTruncation(Unsafe.Add(ref V0, i)), right).AsByte()) != -1) { return false; } } return true; } ref float source = ref Unsafe.As(ref this); for (int j = 0; j < 64; j++) { if ((int)Unsafe.Add(ref source, j) != value) { return false; } } return true; } public bool Equals(Block8x8F other) { if (V0L == other.V0L && V0R == other.V0R && V1L == other.V1L && V1R == other.V1R && V2L == other.V2L && V2R == other.V2R && V3L == other.V3L && V3R == other.V3R && V4L == other.V4L && V4R == other.V4R && V5L == other.V5L && V5R == other.V5R && V6L == other.V6L && V6R == other.V6R && V7L == other.V7L) { return V7R == other.V7R; } return false; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('['); for (int i = 0; i < 63; i++) { stringBuilder.Append(this[i]); stringBuilder.Append(','); } stringBuilder.Append(this[63]); stringBuilder.Append(']'); return stringBuilder.ToString(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TransposeInplace() { if (Avx.IsSupported) { TransposeInplace_Avx(); } else { TransposeInplace_Scalar(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void TransposeInplace_Scalar() { ref float source = ref Unsafe.As(ref this); Swap(ref Unsafe.Add(ref source, 1), ref Unsafe.Add(ref source, 8)); Swap(ref Unsafe.Add(ref source, 2), ref Unsafe.Add(ref source, 16)); Swap(ref Unsafe.Add(ref source, 3), ref Unsafe.Add(ref source, 24)); Swap(ref Unsafe.Add(ref source, 4), ref Unsafe.Add(ref source, 32)); Swap(ref Unsafe.Add(ref source, 5), ref Unsafe.Add(ref source, 40)); Swap(ref Unsafe.Add(ref source, 6), ref Unsafe.Add(ref source, 48)); Swap(ref Unsafe.Add(ref source, 7), ref Unsafe.Add(ref source, 56)); Swap(ref Unsafe.Add(ref source, 10), ref Unsafe.Add(ref source, 17)); Swap(ref Unsafe.Add(ref source, 11), ref Unsafe.Add(ref source, 25)); Swap(ref Unsafe.Add(ref source, 12), ref Unsafe.Add(ref source, 33)); Swap(ref Unsafe.Add(ref source, 13), ref Unsafe.Add(ref source, 41)); Swap(ref Unsafe.Add(ref source, 14), ref Unsafe.Add(ref source, 49)); Swap(ref Unsafe.Add(ref source, 15), ref Unsafe.Add(ref source, 57)); Swap(ref Unsafe.Add(ref source, 19), ref Unsafe.Add(ref source, 26)); Swap(ref Unsafe.Add(ref source, 20), ref Unsafe.Add(ref source, 34)); Swap(ref Unsafe.Add(ref source, 21), ref Unsafe.Add(ref source, 42)); Swap(ref Unsafe.Add(ref source, 22), ref Unsafe.Add(ref source, 50)); Swap(ref Unsafe.Add(ref source, 23), ref Unsafe.Add(ref source, 58)); Swap(ref Unsafe.Add(ref source, 28), ref Unsafe.Add(ref source, 35)); Swap(ref Unsafe.Add(ref source, 29), ref Unsafe.Add(ref source, 43)); Swap(ref Unsafe.Add(ref source, 30), ref Unsafe.Add(ref source, 51)); Swap(ref Unsafe.Add(ref source, 31), ref Unsafe.Add(ref source, 59)); Swap(ref Unsafe.Add(ref source, 37), ref Unsafe.Add(ref source, 44)); Swap(ref Unsafe.Add(ref source, 38), ref Unsafe.Add(ref source, 52)); Swap(ref Unsafe.Add(ref source, 39), ref Unsafe.Add(ref source, 60)); Swap(ref Unsafe.Add(ref source, 46), ref Unsafe.Add(ref source, 53)); Swap(ref Unsafe.Add(ref source, 47), ref Unsafe.Add(ref source, 61)); Swap(ref Unsafe.Add(ref source, 55), ref Unsafe.Add(ref source, 62)); static void Swap(ref float a, ref float b) { float num = a; a = b; b = num; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector NormalizeAndRound(Vector row, Vector off, Vector max) { row += off; row = Vector.Max(row, Vector.Zero); row = Vector.Min(row, max); return row.FastRound(); } public void NormalizeColorsInPlace(float maximum) { Vector4 min = new Vector4(0f); Vector4 max = new Vector4(maximum); Vector4 vector = new Vector4(MathF.Ceiling(maximum / 2f)); V0L = Numerics.Clamp(V0L + vector, min, max); V0R = Numerics.Clamp(V0R + vector, min, max); V1L = Numerics.Clamp(V1L + vector, min, max); V1R = Numerics.Clamp(V1R + vector, min, max); V2L = Numerics.Clamp(V2L + vector, min, max); V2R = Numerics.Clamp(V2R + vector, min, max); V3L = Numerics.Clamp(V3L + vector, min, max); V3R = Numerics.Clamp(V3R + vector, min, max); V4L = Numerics.Clamp(V4L + vector, min, max); V4R = Numerics.Clamp(V4R + vector, min, max); V5L = Numerics.Clamp(V5L + vector, min, max); V5R = Numerics.Clamp(V5R + vector, min, max); V6L = Numerics.Clamp(V6L + vector, min, max); V6R = Numerics.Clamp(V6R + vector, min, max); V7L = Numerics.Clamp(V7L + vector, min, max); V7R = Numerics.Clamp(V7R + vector, min, max); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void NormalizeColorsAndRoundInPlaceVector8(float maximum) { Vector off = new Vector(MathF.Ceiling(maximum / 2f)); Vector max = new Vector(maximum); ref Vector reference = ref Unsafe.As>(ref V0L); reference = NormalizeAndRound(reference, off, max); ref Vector reference2 = ref Unsafe.As>(ref V1L); reference2 = NormalizeAndRound(reference2, off, max); ref Vector reference3 = ref Unsafe.As>(ref V2L); reference3 = NormalizeAndRound(reference3, off, max); ref Vector reference4 = ref Unsafe.As>(ref V3L); reference4 = NormalizeAndRound(reference4, off, max); ref Vector reference5 = ref Unsafe.As>(ref V4L); reference5 = NormalizeAndRound(reference5, off, max); ref Vector reference6 = ref Unsafe.As>(ref V5L); reference6 = NormalizeAndRound(reference6, off, max); ref Vector reference7 = ref Unsafe.As>(ref V6L); reference7 = NormalizeAndRound(reference7, off, max); ref Vector reference8 = ref Unsafe.As>(ref V7L); reference8 = NormalizeAndRound(reference8, off, max); } public void LoadFromInt16Scalar(ref Block8x8 source) { ref short source2 = ref Unsafe.As(ref source); V0L.X = Unsafe.Add(ref source2, 0); V0L.Y = Unsafe.Add(ref source2, 1); V0L.Z = Unsafe.Add(ref source2, 2); V0L.W = Unsafe.Add(ref source2, 3); V0R.X = Unsafe.Add(ref source2, 4); V0R.Y = Unsafe.Add(ref source2, 5); V0R.Z = Unsafe.Add(ref source2, 6); V0R.W = Unsafe.Add(ref source2, 7); V1L.X = Unsafe.Add(ref source2, 8); V1L.Y = Unsafe.Add(ref source2, 9); V1L.Z = Unsafe.Add(ref source2, 10); V1L.W = Unsafe.Add(ref source2, 11); V1R.X = Unsafe.Add(ref source2, 12); V1R.Y = Unsafe.Add(ref source2, 13); V1R.Z = Unsafe.Add(ref source2, 14); V1R.W = Unsafe.Add(ref source2, 15); V2L.X = Unsafe.Add(ref source2, 16); V2L.Y = Unsafe.Add(ref source2, 17); V2L.Z = Unsafe.Add(ref source2, 18); V2L.W = Unsafe.Add(ref source2, 19); V2R.X = Unsafe.Add(ref source2, 20); V2R.Y = Unsafe.Add(ref source2, 21); V2R.Z = Unsafe.Add(ref source2, 22); V2R.W = Unsafe.Add(ref source2, 23); V3L.X = Unsafe.Add(ref source2, 24); V3L.Y = Unsafe.Add(ref source2, 25); V3L.Z = Unsafe.Add(ref source2, 26); V3L.W = Unsafe.Add(ref source2, 27); V3R.X = Unsafe.Add(ref source2, 28); V3R.Y = Unsafe.Add(ref source2, 29); V3R.Z = Unsafe.Add(ref source2, 30); V3R.W = Unsafe.Add(ref source2, 31); V4L.X = Unsafe.Add(ref source2, 32); V4L.Y = Unsafe.Add(ref source2, 33); V4L.Z = Unsafe.Add(ref source2, 34); V4L.W = Unsafe.Add(ref source2, 35); V4R.X = Unsafe.Add(ref source2, 36); V4R.Y = Unsafe.Add(ref source2, 37); V4R.Z = Unsafe.Add(ref source2, 38); V4R.W = Unsafe.Add(ref source2, 39); V5L.X = Unsafe.Add(ref source2, 40); V5L.Y = Unsafe.Add(ref source2, 41); V5L.Z = Unsafe.Add(ref source2, 42); V5L.W = Unsafe.Add(ref source2, 43); V5R.X = Unsafe.Add(ref source2, 44); V5R.Y = Unsafe.Add(ref source2, 45); V5R.Z = Unsafe.Add(ref source2, 46); V5R.W = Unsafe.Add(ref source2, 47); V6L.X = Unsafe.Add(ref source2, 48); V6L.Y = Unsafe.Add(ref source2, 49); V6L.Z = Unsafe.Add(ref source2, 50); V6L.W = Unsafe.Add(ref source2, 51); V6R.X = Unsafe.Add(ref source2, 52); V6R.Y = Unsafe.Add(ref source2, 53); V6R.Z = Unsafe.Add(ref source2, 54); V6R.W = Unsafe.Add(ref source2, 55); V7L.X = Unsafe.Add(ref source2, 56); V7L.Y = Unsafe.Add(ref source2, 57); V7L.Z = Unsafe.Add(ref source2, 58); V7L.W = Unsafe.Add(ref source2, 59); V7R.X = Unsafe.Add(ref source2, 60); V7R.Y = Unsafe.Add(ref source2, 61); V7R.Z = Unsafe.Add(ref source2, 62); V7R.W = Unsafe.Add(ref source2, 63); } private static void MultiplyIntoInt16_Avx2(ref Block8x8F a, ref Block8x8F b, ref Block8x8 dest) { ref Vector256 v = ref a.V0; ref Vector256 v2 = ref b.V0; ref Vector256 v3 = ref dest.V01; for (nint num = 0; num < 8; num += 2) { Vector256 left = Avx.ConvertToVector256Int32(Avx.Multiply(Unsafe.Add(ref v, num + 0), Unsafe.Add(ref v2, num + 0))); Vector256 right = Avx.ConvertToVector256Int32(Avx.Multiply(Unsafe.Add(ref v, num + 1), Unsafe.Add(ref v2, num + 1))); Vector256 vector = Avx2.PackSignedSaturate(left, right); vector = Avx2.PermuteVar8x32(vector.AsInt32(), MultiplyIntoInt16ShuffleMask).AsInt16(); Unsafe.Add(ref v3, (IntPtr)((uint)num / 2)) = vector; } } private static void MultiplyIntoInt16_Sse2(ref Block8x8F a, ref Block8x8F b, ref Block8x8 dest) { ref Vector128 source = ref Unsafe.As>(ref a); ref Vector128 source2 = ref Unsafe.As>(ref b); ref Vector128 source3 = ref Unsafe.As>(ref dest); for (int i = 0; i < 16; i += 2) { Vector128 left = Sse2.ConvertToVector128Int32(Sse.Multiply(Unsafe.Add(ref source, i), Unsafe.Add(ref source2, i))); Vector128 right = Sse2.ConvertToVector128Int32(Sse.Multiply(Unsafe.Add(ref source, i + 1), Unsafe.Add(ref source2, i + 1))); Vector128 vector = Sse2.PackSignedSaturate(left, right); Unsafe.Add(ref source3, (IntPtr)((uint)i / 2u)) = vector; } } private void TransposeInplace_Avx() { Vector256 left = Avx.InsertVector128(V0, Unsafe.As>(ref V4L), 1); Vector256 right = Avx.InsertVector128(V1, Unsafe.As>(ref V5L), 1); Vector256 left2 = Avx.InsertVector128(V2, Unsafe.As>(ref V6L), 1); Vector256 right2 = Avx.InsertVector128(V3, Unsafe.As>(ref V7L), 1); Vector256 left3 = Avx.InsertVector128(Unsafe.As>(ref V0R).ToVector256(), Unsafe.As>(ref V4R), 1); Vector256 right3 = Avx.InsertVector128(Unsafe.As>(ref V1R).ToVector256(), Unsafe.As>(ref V5R), 1); Vector256 left4 = Avx.InsertVector128(Unsafe.As>(ref V2R).ToVector256(), Unsafe.As>(ref V6R), 1); Vector256 right4 = Avx.InsertVector128(Unsafe.As>(ref V3R).ToVector256(), Unsafe.As>(ref V7R), 1); Vector256 vector = Avx.UnpackLow(left, right); Vector256 vector2 = Avx.UnpackLow(left2, right2); Vector256 right5 = Avx.Shuffle(vector, vector2, 78); V0 = Avx.Blend(vector, right5, 204); V1 = Avx.Blend(vector2, right5, 51); Vector256 vector3 = Avx.UnpackLow(left3, right3); Vector256 vector4 = Avx.UnpackLow(left4, right4); right5 = Avx.Shuffle(vector3, vector4, 78); V4 = Avx.Blend(vector3, right5, 204); V5 = Avx.Blend(vector4, right5, 51); Vector256 vector5 = Avx.UnpackHigh(left, right); Vector256 vector6 = Avx.UnpackHigh(left2, right2); right5 = Avx.Shuffle(vector5, vector6, 78); V2 = Avx.Blend(vector5, right5, 204); V3 = Avx.Blend(vector6, right5, 51); Vector256 vector7 = Avx.UnpackHigh(left3, right3); Vector256 vector8 = Avx.UnpackHigh(left4, right4); right5 = Avx.Shuffle(vector7, vector8, 78); V6 = Avx.Blend(vector7, right5, 204); V7 = Avx.Blend(vector8, right5, 51); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ScaledCopyTo(in Buffer2DRegion region, int horizontalScale, int verticalScale) { ScaledCopyTo(ref region.GetReferenceToOrigin(), region.Stride, horizontalScale, verticalScale); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) { if (horizontalScale == 1 && verticalScale == 1) { Copy1x1Scale(ref areaOrigin, areaStride); } else if (horizontalScale == 2 && verticalScale == 2) { Copy2x2Scale(ref areaOrigin, areaStride); } else { CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale); } } public void Copy1x1Scale(ref float areaOrigin, int areaStride) { ref byte selfBase = ref Unsafe.As(ref this); ref byte destBase = ref Unsafe.As(ref areaOrigin); int destStride = areaStride * 4; CopyRowImpl(ref selfBase, ref destBase, destStride, 0); CopyRowImpl(ref selfBase, ref destBase, destStride, 1); CopyRowImpl(ref selfBase, ref destBase, destStride, 2); CopyRowImpl(ref selfBase, ref destBase, destStride, 3); CopyRowImpl(ref selfBase, ref destBase, destStride, 4); CopyRowImpl(ref selfBase, ref destBase, destStride, 5); CopyRowImpl(ref selfBase, ref destBase, destStride, 6); CopyRowImpl(ref selfBase, ref destBase, destStride, 7); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) { ref byte source = ref Unsafe.Add(ref selfBase, row * 8 * 4); Unsafe.CopyBlock(ref Unsafe.Add(ref destBase, row * destStride), in source, 32u); } private void Copy2x2Scale(ref float areaOrigin, int areaStride) { ref Vector2 destBase = ref Unsafe.As(ref areaOrigin); int destStride = areaStride / 2; WidenCopyRowImpl2x2(ref destBase, 0, destStride); WidenCopyRowImpl2x2(ref destBase, 1, destStride); WidenCopyRowImpl2x2(ref destBase, 2, destStride); WidenCopyRowImpl2x2(ref destBase, 3, destStride); WidenCopyRowImpl2x2(ref destBase, 4, destStride); WidenCopyRowImpl2x2(ref destBase, 5, destStride); WidenCopyRowImpl2x2(ref destBase, 6, destStride); WidenCopyRowImpl2x2(ref destBase, 7, destStride); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride) { ref Vector4 reference = ref Unsafe.Add(ref V0L, 2 * row); ref Vector4 reference2 = ref Unsafe.Add(ref reference, 1); int num = 2 * row * destStride; ref Vector4 reference3 = ref Unsafe.As(ref Unsafe.Add(ref destBase, num)); ref Vector4 reference4 = ref Unsafe.As(ref Unsafe.Add(ref destBase, num + destStride)); Vector4 vector = new Vector4(reference.X) { Z = reference.Y, W = reference.Y }; Vector4 vector2 = new Vector4(reference.Z) { Z = reference.W, W = reference.W }; Vector4 vector3 = new Vector4(reference2.X) { Z = reference2.Y, W = reference2.Y }; Vector4 vector4 = new Vector4(reference2.Z) { Z = reference2.W, W = reference2.W }; reference3 = vector; Unsafe.Add(ref reference3, 1) = vector2; Unsafe.Add(ref reference3, 2) = vector3; Unsafe.Add(ref reference3, 3) = vector4; reference4 = vector; Unsafe.Add(ref reference4, 1) = vector2; Unsafe.Add(ref reference4, 2) = vector3; Unsafe.Add(ref reference4, 3) = vector4; } [MethodImpl(MethodImplOptions.NoInlining)] private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) { for (int i = 0; i < 8; i++) { int num = i * verticalScale; int num2 = i * 8; for (int j = 0; j < 8; j++) { int num3 = j * horizontalScale; float num4 = this[num2 + j]; for (int k = 0; k < verticalScale; k++) { int num5 = (num + k) * areaStride + num3; for (int l = 0; l < horizontalScale; l++) { Unsafe.Add(ref areaOrigin, num5 + l) = num4; } } } } } } internal static class FastFloatingPointDCT { private static readonly Vector4 mm128_F_0_7071 = new Vector4(0.70710677f); private static readonly Vector4 mm128_F_0_3826 = new Vector4(0.38268343f); private static readonly Vector4 mm128_F_0_5411 = new Vector4(0.5411961f); private static readonly Vector4 mm128_F_1_3065 = new Vector4(1.306563f); private static readonly Vector4 mm128_F_1_4142 = new Vector4(1.4142135f); private static readonly Vector4 mm128_F_1_8477 = new Vector4(1.847759f); private static readonly Vector4 mm128_F_n1_0823 = new Vector4(-1.0823922f); private static readonly Vector4 mm128_F_n2_6131 = new Vector4(-2.613126f); private static readonly float[] AdjustmentCoefficients = new float[64] { 1f, 1.3870399f, 1.306563f, 1.1758755f, 1f, 0.78569496f, 0.5411961f, 0.27589938f, 1.3870399f, 1.9238797f, 1.812255f, 1.6309863f, 1.3870399f, 1.0897902f, 0.7506606f, 0.38268346f, 1.306563f, 1.812255f, 1.707107f, 1.5363555f, 1.306563f, 1.02656f, 0.7071068f, 0.36047992f, 1.1758755f, 1.6309863f, 1.5363555f, 1.3826833f, 1.1758755f, 0.9238795f, 0.63637924f, 0.32442334f, 1f, 1.3870399f, 1.306563f, 1.1758755f, 1f, 0.78569496f, 0.5411961f, 0.27589938f, 0.78569496f, 1.0897902f, 1.02656f, 0.9238795f, 0.78569496f, 0.61731654f, 0.42521507f, 0.21677275f, 0.5411961f, 0.7506606f, 0.7071068f, 0.63637924f, 0.5411961f, 0.42521507f, 0.29289323f, 0.14931567f, 0.27589938f, 0.38268346f, 0.36047992f, 0.32442334f, 0.27589938f, 0.21677275f, 0.14931567f, 0.076120466f }; private static readonly Vector256 mm256_F_0_7071 = Vector256.Create(0.70710677f); private static readonly Vector256 mm256_F_0_3826 = Vector256.Create(0.38268343f); private static readonly Vector256 mm256_F_0_5411 = Vector256.Create(0.5411961f); private static readonly Vector256 mm256_F_1_3065 = Vector256.Create(1.306563f); private static readonly Vector256 mm256_F_1_4142 = Vector256.Create(1.4142135f); private static readonly Vector256 mm256_F_1_8477 = Vector256.Create(1.847759f); private static readonly Vector256 mm256_F_n1_0823 = Vector256.Create(-1.0823922f); private static readonly Vector256 mm256_F_n2_6131 = Vector256.Create(-2.613126f); public static void AdjustToIDCT(ref Block8x8F quantTable) { ref float reference = ref Unsafe.As(ref quantTable); ref float reference2 = ref MemoryMarshal.GetReference(AdjustmentCoefficients); for (nint num = 0; num < 64; num++) { reference = 0.125f * reference * Unsafe.Add(ref reference2, num); reference = ref Unsafe.Add(ref reference, 1); } quantTable.TransposeInplace(); } public static void AdjustToFDCT(ref Block8x8F quantTable) { ref float reference = ref Unsafe.As(ref quantTable); ref float reference2 = ref MemoryMarshal.GetReference(AdjustmentCoefficients); for (nint num = 0; num < 64; num++) { reference = 0.125f / (reference * Unsafe.Add(ref reference2, num)); reference = ref Unsafe.Add(ref reference, 1); } quantTable.TransposeInplace(); } public static void TransformIDCT(ref Block8x8F block) { if (Avx.IsSupported) { IDCT8x8_Avx(ref block); } else { IDCT_Vector4(ref block); } } public static void TransformFDCT(ref Block8x8F block) { if (Avx.IsSupported) { FDCT8x8_Avx(ref block); } else { FDCT_Vector4(ref block); } } private static void IDCT_Vector4(ref Block8x8F transposedBlock) { IDCT8x4_Vector(ref transposedBlock.V0L); IDCT8x4_Vector(ref transposedBlock.V0R); transposedBlock.TransposeInplace(); IDCT8x4_Vector(ref transposedBlock.V0L); IDCT8x4_Vector(ref transposedBlock.V0R); static void IDCT8x4_Vector(ref Vector4 vecRef) { Vector4 vector = Unsafe.Add(ref vecRef, 0); Vector4 vector2 = Unsafe.Add(ref vecRef, 4); Vector4 vector3 = Unsafe.Add(ref vecRef, 8); Vector4 vector4 = Unsafe.Add(ref vecRef, 12); Vector4 vector5 = vector; Vector4 vector6 = vector5 + vector3; Vector4 vector7 = vector5 - vector3; Vector4 vector8 = vector2 + vector4; Vector4 vector9 = (vector2 - vector4) * mm128_F_1_4142 - vector8; vector = vector6 + vector8; vector4 = vector6 - vector8; vector2 = vector7 + vector9; vector3 = vector7 - vector9; Vector4 vector10 = Unsafe.Add(ref vecRef, 2); Vector4 vector11 = Unsafe.Add(ref vecRef, 6); Vector4 vector12 = Unsafe.Add(ref vecRef, 10); Vector4 vector13 = Unsafe.Add(ref vecRef, 14); Vector4 vector14 = vector12 + vector11; Vector4 vector15 = vector12 - vector11; Vector4 vector16 = vector10 + vector13; Vector4 vector17 = vector10 - vector13; vector13 = vector16 + vector14; vector7 = (vector16 - vector14) * mm128_F_1_4142; vector5 = (vector15 + vector17) * mm128_F_1_8477; vector6 = vector17 * mm128_F_n1_0823 + vector5; vector9 = vector15 * mm128_F_n2_6131 + vector5; vector12 = vector9 - vector13; vector11 = vector7 - vector12; vector10 = vector6 - vector11; Unsafe.Add(ref vecRef, 0) = vector + vector13; Unsafe.Add(ref vecRef, 14) = vector - vector13; Unsafe.Add(ref vecRef, 2) = vector2 + vector12; Unsafe.Add(ref vecRef, 12) = vector2 - vector12; Unsafe.Add(ref vecRef, 4) = vector3 + vector11; Unsafe.Add(ref vecRef, 10) = vector3 - vector11; Unsafe.Add(ref vecRef, 6) = vector4 + vector10; Unsafe.Add(ref vecRef, 8) = vector4 - vector10; } } public static void FDCT_Vector4(ref Block8x8F block) { FDCT8x4_Vector(ref block.V0L); FDCT8x4_Vector(ref block.V0R); block.TransposeInplace(); FDCT8x4_Vector(ref block.V0L); FDCT8x4_Vector(ref block.V0R); static void FDCT8x4_Vector(ref Vector4 vecRef) { Vector4 vector = Unsafe.Add(ref vecRef, 0) + Unsafe.Add(ref vecRef, 14); Vector4 vector2 = Unsafe.Add(ref vecRef, 0) - Unsafe.Add(ref vecRef, 14); Vector4 vector3 = Unsafe.Add(ref vecRef, 2) + Unsafe.Add(ref vecRef, 12); Vector4 vector4 = Unsafe.Add(ref vecRef, 2) - Unsafe.Add(ref vecRef, 12); Vector4 vector5 = Unsafe.Add(ref vecRef, 4) + Unsafe.Add(ref vecRef, 10); Vector4 vector6 = Unsafe.Add(ref vecRef, 4) - Unsafe.Add(ref vecRef, 10); Vector4 vector7 = Unsafe.Add(ref vecRef, 6) + Unsafe.Add(ref vecRef, 8); Vector4 vector8 = Unsafe.Add(ref vecRef, 6) - Unsafe.Add(ref vecRef, 8); Vector4 vector9 = vector + vector7; Vector4 vector10 = vector - vector7; Vector4 vector11 = vector3 + vector5; Vector4 vector12 = vector3 - vector5; Unsafe.Add(ref vecRef, 0) = vector9 + vector11; Unsafe.Add(ref vecRef, 8) = vector9 - vector11; Vector4 vector13 = (vector12 + vector10) * mm128_F_0_7071; Unsafe.Add(ref vecRef, 4) = vector10 + vector13; Unsafe.Add(ref vecRef, 12) = vector10 - vector13; vector9 = vector8 + vector6; vector11 = vector6 + vector4; vector12 = vector4 + vector2; Vector4 vector14 = (vector9 - vector12) * mm128_F_0_3826; Vector4 vector15 = mm128_F_0_5411 * vector9 + vector14; Vector4 vector16 = mm128_F_1_3065 * vector12 + vector14; Vector4 vector17 = vector11 * mm128_F_0_7071; Vector4 vector18 = vector2 + vector17; Vector4 vector19 = vector2 - vector17; Unsafe.Add(ref vecRef, 10) = vector19 + vector15; Unsafe.Add(ref vecRef, 6) = vector19 - vector15; Unsafe.Add(ref vecRef, 2) = vector18 + vector16; Unsafe.Add(ref vecRef, 14) = vector18 - vector16; } } private static void FDCT8x8_Avx(ref Block8x8F block) { FDCT8x8_1D_Avx(ref block); block.TransposeInplace(); FDCT8x8_1D_Avx(ref block); static void FDCT8x8_1D_Avx(ref Block8x8F reference) { Vector256 left = Avx.Add(reference.V0, reference.V7); Vector256 vector = Avx.Subtract(reference.V0, reference.V7); Vector256 left2 = Avx.Add(reference.V1, reference.V6); Vector256 vector2 = Avx.Subtract(reference.V1, reference.V6); Vector256 right = Avx.Add(reference.V2, reference.V5); Vector256 vector3 = Avx.Subtract(reference.V2, reference.V5); Vector256 right2 = Avx.Add(reference.V3, reference.V4); Vector256 left3 = Avx.Subtract(reference.V3, reference.V4); Vector256 left4 = Avx.Add(left, right2); Vector256 vector4 = Avx.Subtract(left, right2); Vector256 right3 = Avx.Add(left2, right); Vector256 left5 = Avx.Subtract(left2, right); reference.V0 = Avx.Add(left4, right3); reference.V4 = Avx.Subtract(left4, right3); Vector256 right4 = Avx.Multiply(Avx.Add(left5, vector4), mm256_F_0_7071); reference.V2 = Avx.Add(vector4, right4); reference.V6 = Avx.Subtract(vector4, right4); left4 = Avx.Add(left3, vector3); right3 = Avx.Add(vector3, vector2); left5 = Avx.Add(vector2, vector); Vector256 va = Avx.Multiply(Avx.Subtract(left4, left5), mm256_F_0_3826); Vector256 right5 = SimdUtils.HwIntrinsics.MultiplyAdd(in va, in mm256_F_0_5411, in left4); Vector256 right6 = SimdUtils.HwIntrinsics.MultiplyAdd(in va, in mm256_F_1_3065, in left5); Vector256 right7 = Avx.Multiply(right3, mm256_F_0_7071); Vector256 left6 = Avx.Add(vector, right7); Vector256 left7 = Avx.Subtract(vector, right7); reference.V5 = Avx.Add(left7, right5); reference.V3 = Avx.Subtract(left7, right5); reference.V1 = Avx.Add(left6, right6); reference.V7 = Avx.Subtract(left6, right6); } } private static void IDCT8x8_Avx(ref Block8x8F transposedBlock) { IDCT8x8_1D_Avx(ref transposedBlock); transposedBlock.TransposeInplace(); IDCT8x8_1D_Avx(ref transposedBlock); static void IDCT8x8_1D_Avx(ref Block8x8F block) { Vector256 v = block.V0; Vector256 v2 = block.V2; Vector256 v3 = block.V4; Vector256 v4 = block.V6; Vector256 left = v; Vector256 left2 = Avx.Add(left, v3); Vector256 left3 = Avx.Subtract(left, v3); Vector256 vs = Avx.Add(v2, v4); Vector256 right = SimdUtils.HwIntrinsics.MultiplySubstract(in vs, Avx.Subtract(v2, v4), in mm256_F_1_4142); v = Avx.Add(left2, vs); v4 = Avx.Subtract(left2, vs); v2 = Avx.Add(left3, right); v3 = Avx.Subtract(left3, right); Vector256 v5 = block.V1; Vector256 v6 = block.V3; Vector256 v7 = block.V5; Vector256 v8 = block.V7; Vector256 right2 = Avx.Add(v7, v6); Vector256 vm = Avx.Subtract(v7, v6); Vector256 left4 = Avx.Add(v5, v8); Vector256 vm2 = Avx.Subtract(v5, v8); v8 = Avx.Add(left4, right2); left3 = Avx.Multiply(Avx.Subtract(left4, right2), mm256_F_1_4142); left = Avx.Multiply(Avx.Add(vm, vm2), mm256_F_1_8477); Vector256 left5 = SimdUtils.HwIntrinsics.MultiplyAdd(in left, in vm2, in mm256_F_n1_0823); right = SimdUtils.HwIntrinsics.MultiplyAdd(in left, in vm, in mm256_F_n2_6131); v7 = Avx.Subtract(right, v8); v6 = Avx.Subtract(left3, v7); v5 = Avx.Subtract(left5, v6); block.V0 = Avx.Add(v, v8); block.V7 = Avx.Subtract(v, v8); block.V1 = Avx.Add(v2, v7); block.V6 = Avx.Subtract(v2, v7); block.V2 = Avx.Add(v3, v6); block.V5 = Avx.Subtract(v3, v6); block.V3 = Avx.Add(v4, v5); block.V4 = Avx.Subtract(v4, v5); } } } internal static class Quantization { public const int MaxQualityFactor = 100; public const int MinQualityFactor = 1; public const int DefaultQualityFactor = 75; public const int QualityEstimationConfidenceLowerThreshold = 25; public const int QualityEstimationConfidenceUpperThreshold = 98; public static ReadOnlySpan LuminanceTable => new byte[64] { 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99 }; public static ReadOnlySpan ChrominanceTable => new byte[64] { 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 }; public static int EstimateQuality(ref Block8x8F table, ReadOnlySpan target) { double num = 0.0; if (table.EqualsToScalar(1)) { return 100; } for (int i = 0; i < 64; i++) { double num2 = (((int)table[i] == 0) ? 999.99 : (100.0 * (double)(table[i] / (float)(int)target[i]))); num += num2; } num /= 64.0; int value = ((!(num <= 100.0)) ? ((int)Math.Round(5000.0 / num)) : ((int)Math.Round((200.0 - num) / 2.0))); return Numerics.Clamp(value, 1, 100); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int EstimateLuminanceQuality(ref Block8x8F luminanceTable) { return EstimateQuality(ref luminanceTable, LuminanceTable); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int EstimateChrominanceQuality(ref Block8x8F chrominanceTable) { return EstimateQuality(ref chrominanceTable, ChrominanceTable); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int QualityToScale(int quality) { if (quality >= 50) { return 200 - quality * 2; } return 5000 / quality; } private static Block8x8F ScaleQuantizationTable(int scale, ReadOnlySpan unscaledTable) { Block8x8F result = default(Block8x8F); for (int i = 0; i < 64; i++) { int value = (unscaledTable[i] * scale + 50) / 100; result[i] = Numerics.Clamp(value, 1, 255); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Block8x8F ScaleLuminanceTable(int quality) { return ScaleQuantizationTable(QualityToScale(quality), LuminanceTable); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Block8x8F ScaleChrominanceTable(int quality) { return ScaleQuantizationTable(QualityToScale(quality), ChrominanceTable); } } internal ref struct RowOctet where T : struct { private Span row0; private Span row1; private Span row2; private Span row3; private Span row4; private Span row5; private Span row6; private Span row7; public Span this[int y] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return y switch { 0 => row0, 1 => row1, 2 => row2, 3 => row3, 4 => row4, 5 => row5, 6 => row6, 7 => row7, _ => ThrowIndexOutOfRangeException(), }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private set { switch (y) { case 0: row0 = value; break; case 1: row1 = value; break; case 2: row2 = value; break; case 3: row3 = value; break; case 4: row4 = value; break; case 5: row5 = value; break; case 6: row6 = value; break; default: row7 = value; break; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(Buffer2D buffer, int startY) { int num = startY; int num2 = Math.Min(num + 8, buffer.Height); int num3 = 0; while (num < num2) { this[num3++] = buffer.DangerousGetRowSpan(num++); } } [MethodImpl(MethodImplOptions.NoInlining)] private static Span ThrowIndexOutOfRangeException() { throw new IndexOutOfRangeException(); } } internal static class SizeExtensions { public static Size MultiplyBy(this Size a, Size b) { return new Size(a.Width * b.Width, a.Height * b.Height); } public static Size DivideBy(this Size a, Size b) { return new Size(a.Width / b.Width, a.Height / b.Height); } public static Size DivideRoundUp(this Size originalSize, int divX, int divY) { Vector2 vector = (SizeF)originalSize; vector /= new Vector2(divX, divY); vector.X = MathF.Ceiling(vector.X); vector.Y = MathF.Ceiling(vector.Y); return new Size((int)vector.X, (int)vector.Y); } public static Size DivideRoundUp(this Size originalSize, int divisor) { return originalSize.DivideRoundUp(divisor, divisor); } public static Size DivideRoundUp(this Size originalSize, Size divisor) { return originalSize.DivideRoundUp(divisor.Width, divisor.Height); } } internal static class ZigZag { private const byte _ = byte.MaxValue; public static ReadOnlySpan ZigZagOrder => new byte[80] { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }; public static ReadOnlySpan TransposingOrder => new byte[80] { 0, 8, 1, 2, 9, 16, 24, 17, 10, 3, 4, 11, 18, 25, 32, 40, 33, 26, 19, 12, 5, 6, 13, 20, 27, 34, 41, 48, 56, 49, 42, 35, 28, 21, 14, 7, 15, 22, 29, 36, 43, 50, 57, 58, 51, 44, 37, 30, 23, 31, 38, 45, 52, 59, 60, 53, 46, 39, 47, 54, 61, 62, 55, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }; private static ReadOnlySpan SseShuffleMasks => new byte[352] { 0, 1, 255, 255, 2, 3, 4, 5, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 255, 255, 255, 255, 2, 3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 255, 255, 2, 3, 255, 255, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 4, 5, 255, 255, 255, 255, 6, 7, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 9, 255, 255, 255, 255, 10, 11, 255, 255, 255, 255, 255, 255, 6, 7, 255, 255, 255, 255, 255, 255, 255, 255, 8, 9, 255, 255, 4, 5, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 6, 7, 255, 255, 255, 255, 2, 3, 255, 255, 255, 255, 255, 255, 4, 5, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 255, 255, 2, 3, 255, 255, 255, 255, 255, 255, 255, 255, 12, 13, 255, 255, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 255, 255, 255, 255, 255, 255, 12, 13, 255, 255, 255, 255, 8, 9, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 255, 255, 6, 7, 255, 255, 255, 255, 255, 255, 255, 255, 8, 9, 255, 255, 255, 255, 255, 255, 4, 5, 255, 255, 255, 255, 6, 7, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 3, 4, 5, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 8, 9, 255, 255, 255, 255, 10, 11, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 6, 7, 8, 9, 255, 255, 12, 13, 255, 255, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 12, 13, 255, 255, 255, 255, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 255, 255, 14, 15 }; private static ReadOnlySpan AvxShuffleMasks => new byte[608] { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 255, 255, 255, 255, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 0, 1, 8, 9, 2, 3, 4, 5, 10, 11, 255, 255, 255, 255, 255, 255, 12, 13, 2, 3, 4, 5, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 8, 9, 2, 3, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 10, 11, 255, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 8, 9, 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 255, 255, 255, 255, 255, 255, 8, 9, 2, 3, 4, 5, 10, 11, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 12, 13, 6, 7, 255, 255, 255, 255, 255, 255, 255, 255, 8, 9, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 4, 5, 10, 11, 255, 255, 255, 255, 255, 255, 12, 13, 6, 7, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 8, 9, 2, 3, 255, 255, 255, 255, 255, 255, 255, 255, 12, 13, 6, 7, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 9, 2, 3, 255, 255, 255, 255, 255, 255, 4, 5, 10, 11, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 12, 13, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 6, 7, 255, 255, 255, 255, 255, 255, 255, 255, 8, 9, 2, 3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 4, 5, 10, 11, 12, 13, 6, 7, 255, 255, 255, 255, 255, 255, 6, 7, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 4, 5, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 8, 9, 2, 3, 10, 11, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 10, 11, 12, 13, 2, 3, 255, 255, 255, 255, 255, 255, 0, 1, 6, 7, 8, 9, 2, 3, 10, 11 }; public unsafe static void ApplyTransposingZigZagOrderingSsse3(ref Block8x8 block) { fixed (byte* reference = &MemoryMarshal.GetReference(SseShuffleMasks)) { Vector128 vector = block.V0.AsByte(); Vector128 value = block.V1.AsByte(); Vector128 vector2 = block.V2.AsByte(); Vector128 vector3 = block.V3.AsByte(); Vector128 vector4 = block.V4.AsByte(); Vector128 vector5 = block.V5.AsByte(); Vector128 value2 = block.V6.AsByte(); Vector128 vector6 = block.V7.AsByte(); Vector128 left = Ssse3.Shuffle(vector, Sse2.LoadVector128(reference)).AsInt16(); Vector128 right = Ssse3.Shuffle(value, Sse2.LoadVector128(reference + 16)).AsInt16(); Vector128 vector7 = Sse2.Or(right: Ssse3.Shuffle(vector2, Sse2.LoadVector128(reference + 32)).AsInt16(), left: Sse2.Or(left, right)); vector7 = Sse2.Insert(vector7.AsUInt16(), Sse2.Extract(vector3.AsUInt16(), 0), 6).AsInt16(); Vector128 left2 = Ssse3.Shuffle(vector, Sse2.LoadVector128(reference + 48)).AsInt16(); Vector128 right2 = Ssse3.Shuffle(value, Sse2.LoadVector128(reference + 64)).AsInt16(); Vector128 vector8 = Sse2.Or(left2, right2); vector8 = Sse2.Insert(vector8.AsUInt16(), Sse2.Extract(vector2.AsUInt16(), 2), 4).AsInt16(); vector8 = Sse2.Insert(vector8.AsUInt16(), Sse2.Extract(vector3.AsUInt16(), 1), 5).AsInt16(); vector8 = Sse2.Insert(vector8.AsUInt16(), Sse2.Extract(vector4.AsUInt16(), 0), 6).AsInt16(); vector8 = Sse2.Insert(vector8.AsUInt16(), Sse2.Extract(vector5.AsUInt16(), 0), 7).AsInt16(); Vector128 left3 = Ssse3.Shuffle(vector, Sse2.LoadVector128(reference + 80)).AsInt16(); Vector128 right3 = Ssse3.Shuffle(value, Sse2.LoadVector128(reference + 96)).AsInt16(); Vector128 vector9 = Sse2.Or(right: Ssse3.Shuffle(vector2, Sse2.LoadVector128(reference + 112)).AsInt16(), left: Sse2.Or(left3, right3)); vector9 = Sse2.Insert(vector9.AsUInt16(), Sse2.Extract(vector3.AsUInt16(), 2), 1).AsInt16(); vector9 = Sse2.Insert(vector9.AsUInt16(), Sse2.Extract(vector4.AsUInt16(), 1), 0).AsInt16(); Vector128 left4 = Ssse3.Shuffle(vector4, Sse2.LoadVector128(reference + 128)).AsInt16(); Vector128 right4 = Ssse3.Shuffle(vector5, Sse2.LoadVector128(reference + 144)).AsInt16(); Vector128 vector10 = Sse2.Or(right: Ssse3.Shuffle(value2, Sse2.LoadVector128(reference + 160)).AsInt16(), left: Sse2.Or(left4, right4)); vector10 = Sse2.Insert(vector10.AsUInt16(), Sse2.Extract(vector3.AsUInt16(), 3), 0).AsInt16(); vector10 = Sse2.Insert(vector10.AsUInt16(), Sse2.Extract(vector6.AsUInt16(), 0), 4).AsInt16(); Vector128 left5 = Ssse3.Shuffle(value, Sse2.LoadVector128(reference + 176)).AsInt16(); Vector128 right5 = Ssse3.Shuffle(vector2, Sse2.LoadVector128(reference + 192)).AsInt16(); Vector128 vector11 = Sse2.Or(right: Ssse3.Shuffle(vector3, Sse2.LoadVector128(reference + 208)).AsInt16(), left: Sse2.Or(left5, right5)); vector11 = Sse2.Insert(vector11.AsUInt16(), Sse2.Extract(vector.AsUInt16(), 7), 3).AsInt16(); vector11 = Sse2.Insert(vector11.AsUInt16(), Sse2.Extract(vector4.AsUInt16(), 4), 7).AsInt16(); Vector128 left6 = Ssse3.Shuffle(vector5, Sse2.LoadVector128(reference + 224)).AsInt16(); Vector128 right6 = Ssse3.Shuffle(value2, Sse2.LoadVector128(reference + 240)).AsInt16(); Vector128 vector12 = Sse2.Or(right: Ssse3.Shuffle(vector6, Sse2.LoadVector128(reference + 256)).AsInt16(), left: Sse2.Or(left6, right6)); vector12 = Sse2.Insert(vector12.AsUInt16(), Sse2.Extract(vector3.AsUInt16(), 6), 7).AsInt16(); vector12 = Sse2.Insert(vector12.AsUInt16(), Sse2.Extract(vector4.AsUInt16(), 5), 6).AsInt16(); Vector128 left7 = Ssse3.Shuffle(value2, Sse2.LoadVector128(reference + 272)).AsInt16(); Vector128 right7 = Ssse3.Shuffle(vector6, Sse2.LoadVector128(reference + 288)).AsInt16(); Vector128 vector13 = Sse2.Or(left7, right7); vector13 = Sse2.Insert(vector13.AsUInt16(), Sse2.Extract(vector2.AsUInt16(), 7), 0).AsInt16(); vector13 = Sse2.Insert(vector13.AsUInt16(), Sse2.Extract(vector3.AsUInt16(), 7), 1).AsInt16(); vector13 = Sse2.Insert(vector13.AsUInt16(), Sse2.Extract(vector4.AsUInt16(), 6), 2).AsInt16(); vector13 = Sse2.Insert(vector13.AsUInt16(), Sse2.Extract(vector5.AsUInt16(), 5), 3).AsInt16(); Vector128 left8 = Ssse3.Shuffle(vector5, Sse2.LoadVector128(reference + 304)).AsInt16(); Vector128 right8 = Ssse3.Shuffle(value2, Sse2.LoadVector128(reference + 320)).AsInt16(); Vector128 vector14 = Sse2.Or(right: Ssse3.Shuffle(vector6, Sse2.LoadVector128(reference + 336)).AsInt16(), left: Sse2.Or(left8, right8)); vector14 = Sse2.Insert(vector14.AsUInt16(), Sse2.Extract(vector4.AsUInt16(), 7), 1).AsInt16(); block.V0 = vector7; block.V1 = vector8; block.V2 = vector9; block.V3 = vector10; block.V4 = vector11; block.V5 = vector12; block.V6 = vector13; block.V7 = vector14; } } public unsafe static void ApplyTransposingZigZagOrderingAvx2(ref Block8x8 block) { fixed (byte* reference = &MemoryMarshal.GetReference(AvxShuffleMasks)) { Vector256 vector = block.V01.AsByte(); Vector256 vector2 = block.V23.AsByte(); Vector256 vector3 = block.V45.AsByte(); Vector256 vector4 = block.V67.AsByte(); Vector256 control = Avx.LoadVector256(reference).AsInt32(); Vector256 left = Avx2.Shuffle(Avx2.PermuteVar8x32(vector.AsInt32(), control).AsByte(), Avx.LoadVector256(reference + 32)).AsByte(); Vector256 value = Avx2.PermuteVar8x32(vector2.AsInt32(), control).AsByte(); value = Avx2.Shuffle(value, Avx.LoadVector256(reference + 64)).AsByte(); Vector256 control2 = Avx.LoadVector256(reference + 96).AsInt32(); Vector256 value2 = Avx2.PermuteVar8x32(vector3.AsInt32(), control2).AsByte(); Vector256 right = Avx2.Shuffle(value2, Avx.LoadVector256(reference + 128)).AsByte(); Vector256 vector5 = Avx2.Or(left, Avx2.Or(value, right)); Vector256 control3 = Avx.LoadVector256(reference + 160).AsInt32(); Vector256 value3 = Avx2.PermuteVar8x32(vector.AsInt32(), control3).AsByte(); Vector256 left2 = Avx2.Shuffle(value3, Avx.LoadVector256(reference + 192)).AsByte(); Vector256 value4 = Avx2.PermuteVar8x32(vector2.AsInt32(), control2).AsByte(); value4 = Avx2.Shuffle(value4, Avx.LoadVector256(reference + 224)).AsByte(); Vector256 left3 = Avx2.Shuffle(value2, Avx.LoadVector256(reference + 256)).AsByte(); Vector256 value5 = Avx2.PermuteVar8x32(vector4.AsInt32(), control3).AsByte(); Vector256 vector6 = Avx2.Or(right: Avx2.Or(left3, Avx2.Shuffle(value5, Avx.LoadVector256(reference + 288)).AsByte()), left: Avx2.Or(left2, value4)); Vector256 left4 = Avx2.Shuffle(value3, Avx.LoadVector256(reference + 320)).AsByte(); Vector256 control4 = Avx.LoadVector256(reference + 352).AsInt32(); Vector256 value6 = Avx2.PermuteVar8x32(vector2.AsInt32(), control4).AsByte(); Vector256 right2 = Avx2.Shuffle(value6, Avx.LoadVector256(reference + 384)).AsByte(); Vector256 value7 = Avx2.PermuteVar8x32(vector3.AsInt32(), control4).AsByte(); value7 = Avx2.Shuffle(value7, Avx.LoadVector256(reference + 416)).AsByte(); Vector256 vector7 = Avx2.Or(right: Avx2.Or(value7, Avx2.Shuffle(value5, Avx.LoadVector256(reference + 448)).AsByte()), left: Avx2.Or(left4, right2)); Vector256 left5 = Avx2.Shuffle(value6, Avx.LoadVector256(reference + 480)).AsByte(); Vector256 control5 = Avx.LoadVector256(reference + 512).AsInt32(); Vector256 value8 = Avx2.PermuteVar8x32(vector3.AsInt32(), control5).AsByte(); value8 = Avx2.Shuffle(value8, Avx.LoadVector256(reference + 544)).AsByte(); Vector256 value9 = Avx2.PermuteVar8x32(vector4.AsInt32(), control5).AsByte(); value9 = Avx2.Shuffle(value9, Avx.LoadVector256(reference + 576)).AsByte(); Vector256 vector8 = Avx2.Or(left5, Avx2.Or(value8, value9)); block.V01 = vector5.AsInt16(); block.V23 = vector6.AsInt16(); block.V45 = vector7.AsInt16(); block.V67 = vector8.AsInt16(); } } } } namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder { internal enum HuffIndex { LuminanceDC, LuminanceAC, ChrominanceDC, ChrominanceAC } internal readonly struct HuffmanLut { public static readonly HuffmanLut[] TheHuffmanLut; public int[] Values { get; } static HuffmanLut() { TheHuffmanLut = new HuffmanLut[4]; for (int i = 0; i < HuffmanSpec.TheHuffmanSpecs.Length; i++) { TheHuffmanLut[i] = new HuffmanLut(HuffmanSpec.TheHuffmanSpecs[i]); } } public HuffmanLut(HuffmanSpec spec) { int num = 0; byte[] values = spec.Values; foreach (byte b in values) { if (b > num) { num = b; } } Values = new int[num + 1]; int num2 = 0; int num3 = 0; for (int j = 0; j < spec.Count.Length; j++) { int num4 = j + 1; for (int k = 0; k < spec.Count[j]; k++) { Values[spec.Values[num3]] = num4 | (num2 << 32 - num4); num2++; num3++; } num2 <<= 1; } } } internal class HuffmanScanEncoder { private const int MaxBytesPerBlock = 416; private const int MaxBytesPerBlockMultiplier = 2; private const int OutputBufferLengthMultiplier = 2; private HuffmanLut[] huffmanTables; private uint accumulatedBits; private readonly uint[] emitBuffer; private readonly byte[] streamWriteBuffer; private int bitCount; private int emitWriteIndex; private Block8x8 tempBlock; private readonly Stream target; private bool IsStreamFlushNeeded { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return emitWriteIndex < (uint)emitBuffer.Length / 2u; } } public HuffmanScanEncoder(int blocksPerCodingUnit, Stream outputStream) { int num = 416 * blocksPerCodingUnit; emitBuffer = new uint[num / 4]; emitWriteIndex = emitBuffer.Length; streamWriteBuffer = new byte[num * 2]; target = outputStream; } public void Encode444(Image pixels, ref Block8x8F luminanceQuantTable, ref Block8x8F chrominanceQuantTable, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { FastFloatingPointDCT.AdjustToFDCT(ref luminanceQuantTable); FastFloatingPointDCT.AdjustToFDCT(ref chrominanceQuantTable); huffmanTables = HuffmanLut.TheHuffmanLut; int prevDC = 0; int prevDC2 = 0; int prevDC3 = 0; ImageFrame rootFrame = pixels.Frames.RootFrame; Buffer2D pixelBuffer = rootFrame.PixelBuffer; RowOctet currentRows = default(RowOctet); YCbCrForwardConverter444 yCbCrForwardConverter = new YCbCrForwardConverter444(rootFrame); for (int i = 0; i < pixels.Height; i += 8) { cancellationToken.ThrowIfCancellationRequested(); currentRows.Update(pixelBuffer, i); for (int j = 0; j < pixels.Width; j += 8) { yCbCrForwardConverter.Convert(j, i, ref currentRows); prevDC = WriteBlock(QuantIndex.Luminance, prevDC, ref yCbCrForwardConverter.Y, ref luminanceQuantTable); prevDC2 = WriteBlock(QuantIndex.Chrominance, prevDC2, ref yCbCrForwardConverter.Cb, ref chrominanceQuantTable); prevDC3 = WriteBlock(QuantIndex.Chrominance, prevDC3, ref yCbCrForwardConverter.Cr, ref chrominanceQuantTable); if (IsStreamFlushNeeded) { FlushToStream(); } } } FlushRemainingBytes(); } public void Encode420(Image pixels, ref Block8x8F luminanceQuantTable, ref Block8x8F chrominanceQuantTable, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { FastFloatingPointDCT.AdjustToFDCT(ref luminanceQuantTable); FastFloatingPointDCT.AdjustToFDCT(ref chrominanceQuantTable); huffmanTables = HuffmanLut.TheHuffmanLut; int prevDC = 0; int prevDC2 = 0; int prevDC3 = 0; ImageFrame rootFrame = pixels.Frames.RootFrame; Buffer2D pixelBuffer = rootFrame.PixelBuffer; RowOctet currentRows = default(RowOctet); YCbCrForwardConverter420 yCbCrForwardConverter = new YCbCrForwardConverter420(rootFrame); for (int i = 0; i < pixels.Height; i += 16) { cancellationToken.ThrowIfCancellationRequested(); for (int j = 0; j < pixels.Width; j += 16) { for (int k = 0; k < 2; k++) { int num = k * 8; currentRows.Update(pixelBuffer, i + num); yCbCrForwardConverter.Convert(j, i, ref currentRows, k); prevDC = WriteBlock(QuantIndex.Luminance, prevDC, ref yCbCrForwardConverter.YLeft, ref luminanceQuantTable); prevDC = WriteBlock(QuantIndex.Luminance, prevDC, ref yCbCrForwardConverter.YRight, ref luminanceQuantTable); } prevDC2 = WriteBlock(QuantIndex.Chrominance, prevDC2, ref yCbCrForwardConverter.Cb, ref chrominanceQuantTable); prevDC3 = WriteBlock(QuantIndex.Chrominance, prevDC3, ref yCbCrForwardConverter.Cr, ref chrominanceQuantTable); if (IsStreamFlushNeeded) { FlushToStream(); } } } FlushRemainingBytes(); } public void EncodeGrayscale(Image pixels, ref Block8x8F luminanceQuantTable, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { FastFloatingPointDCT.AdjustToFDCT(ref luminanceQuantTable); huffmanTables = HuffmanLut.TheHuffmanLut; int prevDC = 0; ImageFrame rootFrame = pixels.Frames.RootFrame; Buffer2D pixelBuffer = rootFrame.PixelBuffer; RowOctet currentRows = default(RowOctet); LuminanceForwardConverter luminanceForwardConverter = new LuminanceForwardConverter(rootFrame); for (int i = 0; i < pixels.Height; i += 8) { cancellationToken.ThrowIfCancellationRequested(); currentRows.Update(pixelBuffer, i); for (int j = 0; j < pixels.Width; j += 8) { luminanceForwardConverter.Convert(j, i, ref currentRows); prevDC = WriteBlock(QuantIndex.Luminance, prevDC, ref luminanceForwardConverter.Y, ref luminanceQuantTable); if (IsStreamFlushNeeded) { FlushToStream(); } } } FlushRemainingBytes(); } public void EncodeRgb(Image pixels, ref Block8x8F quantTable, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { FastFloatingPointDCT.AdjustToFDCT(ref quantTable); huffmanTables = HuffmanLut.TheHuffmanLut; int prevDC = 0; int prevDC2 = 0; int prevDC3 = 0; ImageFrame rootFrame = pixels.Frames.RootFrame; Buffer2D pixelBuffer = rootFrame.PixelBuffer; RowOctet currentRows = default(RowOctet); RgbForwardConverter rgbForwardConverter = new RgbForwardConverter(rootFrame); for (int i = 0; i < pixels.Height; i += 8) { cancellationToken.ThrowIfCancellationRequested(); currentRows.Update(pixelBuffer, i); for (int j = 0; j < pixels.Width; j += 8) { rgbForwardConverter.Convert(j, i, ref currentRows); prevDC = WriteBlock(QuantIndex.Luminance, prevDC, ref rgbForwardConverter.R, ref quantTable); prevDC2 = WriteBlock(QuantIndex.Luminance, prevDC2, ref rgbForwardConverter.G, ref quantTable); prevDC3 = WriteBlock(QuantIndex.Luminance, prevDC3, ref rgbForwardConverter.B, ref quantTable); if (IsStreamFlushNeeded) { FlushToStream(); } } } FlushRemainingBytes(); } private int WriteBlock(QuantIndex index, int prevDC, ref Block8x8F block, ref Block8x8F quant) { ref Block8x8 reference = ref tempBlock; block.AddInPlace(-128f); FastFloatingPointDCT.TransformFDCT(ref block); Block8x8F.Quantize(ref block, ref reference, ref quant); int num = reference[0]; EmitHuffRLE(huffmanTables[2 * (int)index].Values, 0, num - prevDC); int[] values = huffmanTables[2 * (int)index + 1].Values; nint lastNonZeroIndex = reference.GetLastNonZeroIndex(); int num2 = 0; ref short source = ref Unsafe.As(ref reference); for (nint num3 = 1; num3 <= lastNonZeroIndex; num3++) { int num4 = Unsafe.Add(ref source, num3); if (num4 == 0) { num2 += 16; continue; } while (num2 >= 256) { EmitHuff(values, 240); num2 -= 256; } EmitHuffRLE(values, num2, num4); num2 = 0; } if (lastNonZeroIndex != 63) { EmitHuff(values, 0); } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Emit(uint bits, int count) { accumulatedBits |= bits >> bitCount; count += bitCount; if (count >= 32) { emitBuffer[--emitWriteIndex] = accumulatedBits; accumulatedBits = bits << 32 - bitCount; count -= 32; } bitCount = count; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EmitHuff(int[] table, int value) { int num = table[value]; Emit((uint)(num & -256), num & 0xFF); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EmitHuffRLE(int[] table, int runLength, int value) { int num = value; int num2 = value; if (num < 0) { num = -value; num2 = value - 1; } int huffmanEncodingLength = GetHuffmanEncodingLength((uint)num); int num3 = table[runLength | huffmanEncodingLength]; int num4 = num3 & 0xFF; uint num5 = (uint)(num3 & -65536); uint num6 = (uint)(num2 << 32 - huffmanEncodingLength); Emit(num5 | (num6 >> num4), num4 + huffmanEncodingLength); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetHuffmanEncodingLength(uint value) { return 32 - BitOperations.LeadingZeroCount(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void FlushToStream(int endIndex) { Span span = MemoryMarshal.AsBytes(emitBuffer.AsSpan()); int count = 0; int num = span.Length - 1; if (BitConverter.IsLittleEndian) { for (int num2 = num; num2 >= endIndex; num2--) { byte b = span[num2]; streamWriteBuffer[count++] = b; if (b == byte.MaxValue) { streamWriteBuffer[count++] = 0; } } } else { for (int num3 = num; num3 >= endIndex; num3 -= 4) { for (int i = num3 - 3; i <= num3; i++) { byte b2 = span[i]; streamWriteBuffer[count++] = b2; if (b2 == byte.MaxValue) { streamWriteBuffer[count++] = 0; } } } } target.Write(streamWriteBuffer, 0, count); emitWriteIndex = emitBuffer.Length; } private void FlushToStream() { FlushToStream(emitWriteIndex * 4); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void FlushRemainingBytes() { int num = (int)Numerics.DivideCeil((uint)bitCount, 8u); uint num2 = accumulatedBits | (uint)(-1 >>> bitCount); emitBuffer[emitWriteIndex - 1] = num2; int endIndex = emitWriteIndex * 4 - num; FlushToStream(endIndex); } } internal readonly struct HuffmanSpec { public static readonly HuffmanSpec[] TheHuffmanSpecs = new HuffmanSpec[4] { new HuffmanSpec(new byte[16] { 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, new byte[12] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }), new HuffmanSpec(new byte[16] { 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125 }, new byte[162] { 1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20, 50, 129, 145, 161, 8, 35, 66, 177, 193, 21, 82, 209, 240, 36, 51, 98, 114, 130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250 }), new HuffmanSpec(new byte[16] { 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, new byte[12] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }), new HuffmanSpec(new byte[16] { 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119 }, new byte[162] { 0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34, 50, 129, 8, 20, 66, 145, 161, 177, 193, 9, 35, 51, 82, 240, 21, 98, 114, 209, 10, 22, 36, 52, 225, 37, 241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 130, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 226, 227, 228, 229, 230, 231, 232, 233, 234, 242, 243, 244, 245, 246, 247, 248, 249, 250 }) }; public readonly byte[] Count; public readonly byte[] Values; public HuffmanSpec(byte[] count, byte[] values) { Count = count; Values = values; } } internal ref struct LuminanceForwardConverter where TPixel : unmanaged, IPixel { private const int PixelsPerSample = 64; public Block8x8F Y; private readonly Span pixelSpan; private readonly Span l8Span; private readonly Size samplingAreaSize; private readonly Configuration config; private static Size SampleSize => new Size(8, 8); public LuminanceForwardConverter(ImageFrame frame) { Y = default(Block8x8F); pixelSpan = new TPixel[64].AsSpan(); l8Span = new L8[64].AsSpan(); samplingAreaSize = new Size(frame.Width, frame.Height); config = frame.GetConfiguration(); } public void Convert(int x, int y, ref RowOctet currentRows) { YCbCrForwardConverter.LoadAndStretchEdges(currentRows, pixelSpan, new Point(x, y), SampleSize, samplingAreaSize); PixelOperations.Instance.ToL8(config, pixelSpan, l8Span); ref Block8x8F y2 = ref Y; ref L8 reference = ref MemoryMarshal.GetReference(l8Span); if (RgbToYCbCrConverterVectorized.IsSupported) { ConvertAvx(ref reference, ref y2); } else { ConvertScalar(ref reference, ref y2); } } private static void ConvertAvx(ref L8 l8Start, ref Block8x8F yBlock) { ref Vector128 source = ref Unsafe.As>(ref l8Start); ref Vector256 v = ref yBlock.V0; for (nint num = 0; num < 8; num++) { Unsafe.Add(ref v, num) = Avx.ConvertToVector256Single(Avx2.ConvertToVector256Int32(Unsafe.AddByteOffset(ref source, 8 * num))); } } private static void ConvertScalar(ref L8 l8Start, ref Block8x8F yBlock) { for (int i = 0; i < 64; i++) { yBlock[i] = (int)Unsafe.Add(ref l8Start, i).PackedValue; } } } internal enum QuantIndex { Luminance, Chrominance } internal ref struct RgbForwardConverter where TPixel : unmanaged, IPixel { private const int PixelsPerSample = 64; private const int RgbSpanByteSize = 192; public Block8x8F R; public Block8x8F G; public Block8x8F B; private readonly Span pixelSpan; private readonly Span rgbSpan; private readonly Size samplingAreaSize; private readonly Configuration config; private static Size SampleSize => new Size(8, 8); public RgbForwardConverter(ImageFrame frame) { R = default(Block8x8F); G = default(Block8x8F); B = default(Block8x8F); pixelSpan = new TPixel[64].AsSpan(); rgbSpan = MemoryMarshal.Cast(new byte[192 + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan()); samplingAreaSize = new Size(frame.Width, frame.Height); config = frame.GetConfiguration(); } public void Convert(int x, int y, ref RowOctet currentRows) { YCbCrForwardConverter.LoadAndStretchEdges(currentRows, pixelSpan, new Point(x, y), SampleSize, samplingAreaSize); PixelOperations.Instance.ToRgb24(config, pixelSpan, rgbSpan); ref Block8x8F r = ref R; ref Block8x8F g = ref G; ref Block8x8F b = ref B; if (RgbToYCbCrConverterVectorized.IsSupported) { ConvertAvx(rgbSpan, ref r, ref g, ref b); } else { ConvertScalar(rgbSpan, ref r, ref g, ref b); } } private static void ConvertAvx(Span rgbSpan, ref Block8x8F rBlock, ref Block8x8F gBlock, ref Block8x8F bBlock) { ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(rgbSpan)); ref Vector256 v = ref rBlock.V0; ref Vector256 v2 = ref gBlock.V0; ref Vector256 v3 = ref bBlock.V0; Vector256 right = Vector256.Create(0).AsByte(); Vector256 control = Unsafe.As>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.MoveFirst24BytesToSeparateLanes)); Vector256 mask = Unsafe.As>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.ExtractRgb)); for (nint num = 0; num < 8; num++) { Vector256 left = Avx2.Shuffle(Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref source, 24 * num).AsUInt32(), control).AsByte(), mask); Vector256 left2 = Avx2.UnpackLow(left, right); Vector256 left3 = Avx2.UnpackHigh(left, right); Unsafe.Add(ref v, num) = Avx.ConvertToVector256Single(Avx2.UnpackLow(left2, right).AsInt32()); Unsafe.Add(ref v2, num) = Avx.ConvertToVector256Single(Avx2.UnpackHigh(left2, right).AsInt32()); Unsafe.Add(ref v3, num) = Avx.ConvertToVector256Single(Avx2.UnpackLow(left3, right).AsInt32()); } } private static void ConvertScalar(Span rgbSpan, ref Block8x8F redBlock, ref Block8x8F greenBlock, ref Block8x8F blueBlock) { ref Rgb24 reference = ref MemoryMarshal.GetReference(rgbSpan); for (int i = 0; i < 64; i++) { Rgb24 rgb = Unsafe.Add(ref reference, (nint)(uint)i); redBlock[i] = (int)rgb.R; greenBlock[i] = (int)rgb.G; blueBlock[i] = (int)rgb.B; } } } internal struct RgbToYCbCrConverterLut { public unsafe fixed int YRTable[256]; public unsafe fixed int YGTable[256]; public unsafe fixed int YBTable[256]; public unsafe fixed int CbRTable[256]; public unsafe fixed int CbGTable[256]; public unsafe fixed int CbBTable[256]; public unsafe fixed int CrGTable[256]; public unsafe fixed int CrBTable[256]; private const int ScaleBits = 16; private const int CBCrOffset = 8388608; private const int Half = 32768; public unsafe static RgbToYCbCrConverterLut Create() { RgbToYCbCrConverterLut result = default(RgbToYCbCrConverterLut); for (int i = 0; i <= 255; i++) { result.YRTable[i] = Fix(0.299f) * i; result.YGTable[i] = Fix(0.587f) * i; result.YBTable[i] = Fix(0.114f) * i + 32768; result.CbRTable[i] = -Fix(0.16873589f) * i; result.CbGTable[i] = -Fix(0.3312641f) * i; result.CbBTable[i] = Fix(0.5f) * i + 8388608 + 32768 - 1; result.CrGTable[i] = -Fix(0.41868758f) * i; result.CrBTable[i] = -Fix(0.08131241f) * i; } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe float CalculateY(byte r, byte g, byte b) { return YRTable[(int)r] + YGTable[(int)g] + YBTable[(int)b] >> 16; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe float CalculateCb(byte r, byte g, byte b) { return CbRTable[(int)r] + CbGTable[(int)g] + CbBTable[(int)b] >> 16; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe float CalculateCr(byte r, byte g, byte b) { return CbBTable[(int)r] + CrGTable[(int)g] + CrBTable[(int)b] >> 16; } public void Convert444(Span rgbSpan, ref Block8x8F yBlock, ref Block8x8F cbBlock, ref Block8x8F crBlock) { ref Rgb24 source = ref rgbSpan[0]; for (int i = 0; i < 64; i++) { Rgb24 rgb = Unsafe.Add(ref source, i); yBlock[i] = CalculateY(rgb.R, rgb.G, rgb.B); cbBlock[i] = CalculateCb(rgb.R, rgb.G, rgb.B); crBlock[i] = CalculateCr(rgb.R, rgb.G, rgb.B); } } public void Convert420(Span rgbSpan, ref Block8x8F yBlockLeft, ref Block8x8F yBlockRight, ref Block8x8F cbBlock, ref Block8x8F crBlock, int row) { ref float source = ref Unsafe.As(ref yBlockLeft); ref float source2 = ref Unsafe.As(ref yBlockRight); int elementOffset = row * 32; ref float source3 = ref Unsafe.Add(ref Unsafe.As(ref cbBlock), elementOffset); ref float source4 = ref Unsafe.Add(ref Unsafe.As(ref crBlock), elementOffset); ref Rgb24 source5 = ref rgbSpan[0]; for (int i = 0; i < 8; i += 2) { int elementOffset2 = i * 8; ref Rgb24 reference = ref Unsafe.Add(ref source5, i * 16); int num = 8 * (i / 2); ConvertChunk420(ref reference, ref Unsafe.Add(ref source, elementOffset2), ref Unsafe.Add(ref source3, num), ref Unsafe.Add(ref source4, num)); ConvertChunk420(ref Unsafe.Add(ref reference, 8), ref Unsafe.Add(ref source2, elementOffset2), ref Unsafe.Add(ref source3, num + 4), ref Unsafe.Add(ref source4, num + 4)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ConvertChunk420(ref Rgb24 stride, ref float yBlock, ref float cbBlock, ref float crBlock) { for (int i = 0; i < 8; i += 2) { ref float reference = ref Unsafe.Add(ref yBlock, i); Rgb24 px = Unsafe.Add(ref stride, i); Rgb24 px2 = Unsafe.Add(ref stride, i + 1); reference = CalculateY(px.R, px.G, px.B); Unsafe.Add(ref reference, 1) = CalculateY(px2.R, px2.G, px2.B); Rgb24 px3 = Unsafe.Add(ref stride, i + 16); Rgb24 px4 = Unsafe.Add(ref stride, i + 17); Unsafe.Add(ref reference, 8) = CalculateY(px3.R, px3.G, px3.B); Unsafe.Add(ref reference, 9) = CalculateY(px4.R, px4.G, px4.B); Unsafe.Add(ref cbBlock, i / 2) = CalculateAverageCb(px, px2, px3, px4); Unsafe.Add(ref crBlock, i / 2) = CalculateAverageCr(px, px2, px3, px4); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private float CalculateAverageCb(Rgb24 px0, Rgb24 px1, Rgb24 px2, Rgb24 px3) { return 0.25f * (CalculateCb(px0.R, px0.G, px0.B) + CalculateCb(px1.R, px1.G, px1.B) + CalculateCb(px2.R, px2.G, px2.B) + CalculateCb(px3.R, px3.G, px3.B)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private float CalculateAverageCr(Rgb24 px0, Rgb24 px1, Rgb24 px2, Rgb24 px3) { return 0.25f * (CalculateCr(px0.R, px0.G, px0.B) + CalculateCr(px1.R, px1.G, px1.B) + CalculateCr(px2.R, px2.G, px2.B) + CalculateCr(px3.R, px3.G, px3.B)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Fix(float x) { return (int)(x * 65536f + 0.5f); } } internal static class RgbToYCbCrConverterVectorized { public static bool IsSupported => Avx2.IsSupported; public static int AvxCompatibilityPadding { get { if (IsSupported) { return 8; } return 0; } } internal static ReadOnlySpan MoveFirst24BytesToSeparateLanes => new byte[32] { 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0 }; internal static ReadOnlySpan ExtractRgb => new byte[32] { 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 255, 255, 255, 255, 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 255, 255, 255, 255 }; public static void Convert444(ReadOnlySpan rgbSpan, ref Block8x8F yBlock, ref Block8x8F cbBlock, ref Block8x8F crBlock) { Vector256 vm = Vector256.Create(0.299f); Vector256 vm2 = Vector256.Create(0.587f); Vector256 left = Vector256.Create(0.114f); Vector256 vm3 = Vector256.Create(-0.168736f); Vector256 vm4 = Vector256.Create(-0.331264f); Vector256 left2 = Vector256.Create(128f); Vector256 vm5 = Vector256.Create(-0.418688f); Vector256 left3 = Vector256.Create(-0.081312f); Vector256 vm6 = Vector256.Create(0.5f); Vector256 right = Vector256.Create(0).AsByte(); ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(rgbSpan)); ref Vector256 v = ref yBlock.V0; ref Vector256 v2 = ref cbBlock.V0; ref Vector256 v3 = ref crBlock.V0; Vector256 control = Unsafe.As>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes)); Vector256 mask = Unsafe.As>(ref MemoryMarshal.GetReference(ExtractRgb)); for (int i = 0; i < 8; i++) { Vector256 left4 = Avx2.Shuffle(Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref source, (IntPtr)(24 * i)).AsUInt32(), control).AsByte(), mask); Vector256 left5 = Avx2.UnpackLow(left4, right); Vector256 left6 = Avx2.UnpackHigh(left4, right); Vector256 vm7 = Avx.ConvertToVector256Single(Avx2.UnpackLow(left5, right).AsInt32()); Vector256 vm8 = Avx.ConvertToVector256Single(Avx2.UnpackHigh(left5, right).AsInt32()); Vector256 right2 = Avx.ConvertToVector256Single(Avx2.UnpackLow(left6, right).AsInt32()); Unsafe.Add(ref v, i) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(left, right2), in vm2, in vm8), in vm, in vm7); Unsafe.Add(ref v2, i) = Avx.Add(left2, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(vm6, right2), in vm4, in vm8), in vm3, in vm7)); Unsafe.Add(ref v3, i) = Avx.Add(left2, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(left3, right2), in vm5, in vm8), in vm6, in vm7)); } } public static void Convert420(ReadOnlySpan rgbSpan, ref Block8x8F yBlockLeft, ref Block8x8F yBlockRight, ref Block8x8F cbBlock, ref Block8x8F crBlock, int row) { Vector256 vm = Vector256.Create(0.299f); Vector256 vm2 = Vector256.Create(0.587f); Vector256 left = Vector256.Create(0.114f); Vector256 vm3 = Vector256.Create(-0.168736f); Vector256 vm4 = Vector256.Create(-0.331264f); Vector256 left2 = Vector256.Create(128f); Vector256 vm5 = Vector256.Create(-0.418688f); Vector256 left3 = Vector256.Create(-0.081312f); Vector256 vm6 = Vector256.Create(0.5f); Vector256 right = Vector256.Create(0).AsByte(); ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(rgbSpan)); int elementOffset = row * 4; ref Vector256 source2 = ref Unsafe.Add(ref Unsafe.As>(ref cbBlock), elementOffset); ref Vector256 source3 = ref Unsafe.Add(ref Unsafe.As>(ref crBlock), elementOffset); Vector256 control = Unsafe.As>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes)); Vector256 mask = Unsafe.As>(ref MemoryMarshal.GetReference(ExtractRgb)); Span> span = stackalloc Vector256[4]; Span> span2 = stackalloc Vector256[4]; Span> span3 = stackalloc Vector256[4]; for (int i = 0; i < 4; i++) { Vector256 vm7; Vector256 vm8; Vector256 vector; for (int j = 0; j < 4; j += 2) { Vector256 left4 = Avx2.Shuffle(Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref source, (IntPtr)(24 * (i * 4 + j))).AsUInt32(), control).AsByte(), mask); Vector256 left5 = Avx2.UnpackLow(left4, right); Vector256 left6 = Avx2.UnpackHigh(left4, right); vm7 = Avx.ConvertToVector256Single(Avx2.UnpackLow(left5, right).AsInt32()); vm8 = Avx.ConvertToVector256Single(Avx2.UnpackHigh(left5, right).AsInt32()); vector = Avx.ConvertToVector256Single(Avx2.UnpackLow(left6, right).AsInt32()); int elementOffset2 = i * 2 + ((j & 2) >> 1); Unsafe.Add(ref yBlockLeft.V0, elementOffset2) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(left, vector), in vm2, in vm8), in vm, in vm7); span[j] = vm7; span2[j] = vm8; span3[j] = vector; } for (int k = 1; k < 4; k += 2) { Vector256 left7 = Avx2.Shuffle(Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref source, (IntPtr)(24 * (i * 4 + k))).AsUInt32(), control).AsByte(), mask); Vector256 left5 = Avx2.UnpackLow(left7, right); Vector256 left8 = Avx2.UnpackHigh(left7, right); vm7 = Avx.ConvertToVector256Single(Avx2.UnpackLow(left5, right).AsInt32()); vm8 = Avx.ConvertToVector256Single(Avx2.UnpackHigh(left5, right).AsInt32()); vector = Avx.ConvertToVector256Single(Avx2.UnpackLow(left8, right).AsInt32()); int elementOffset3 = i * 2 + ((k & 2) >> 1); Unsafe.Add(ref yBlockRight.V0, elementOffset3) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(left, vector), in vm2, in vm8), in vm, in vm7); span[k] = vm7; span2[k] = vm8; span3[k] = vector; } vm7 = Scale16x2_8x1(span); vm8 = Scale16x2_8x1(span2); vector = Scale16x2_8x1(span3); Unsafe.Add(ref source2, i) = Avx.Add(left2, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(vm6, vector), in vm4, in vm8), in vm3, in vm7)); Unsafe.Add(ref source3, i) = Avx.Add(left2, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(left3, vector), in vm5, in vm8), in vm6, in vm7)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Vector256 Scale16x2_8x1(ReadOnlySpan> v) { Vector256 right = Vector256.Create(0.25f); Vector256 left = Avx.Add(v[0], v[2]); Vector256 right2 = Avx.Add(v[1], v[3]); return Avx2.Permute4x64(Avx.Multiply(Avx.HorizontalAdd(left, right2), right).AsDouble(), 216).AsSingle(); } } internal ref struct YCbCrForwardConverter420 where TPixel : unmanaged, IPixel { private const int PixelsPerSample = 128; private const int RgbSpanByteSize = 384; public Block8x8F YLeft; public Block8x8F YRight; public Block8x8F Cb; public Block8x8F Cr; private RgbToYCbCrConverterLut colorTables; private readonly Span pixelSpan; private readonly Span rgbSpan; private readonly Size samplingAreaSize; private readonly Configuration config; private static Size SampleSize => new Size(16, 8); public YCbCrForwardConverter420(ImageFrame frame) { YLeft = default(Block8x8F); YRight = default(Block8x8F); Cb = default(Block8x8F); Cr = default(Block8x8F); pixelSpan = new TPixel[128].AsSpan(); rgbSpan = MemoryMarshal.Cast(new byte[384 + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan()); samplingAreaSize = new Size(frame.Width, frame.Height); config = frame.GetConfiguration(); if (!RgbToYCbCrConverterVectorized.IsSupported) { colorTables = RgbToYCbCrConverterLut.Create(); } else { colorTables = default(RgbToYCbCrConverterLut); } } public void Convert(int x, int y, ref RowOctet currentRows, int idx) { YCbCrForwardConverter.LoadAndStretchEdges(currentRows, pixelSpan, new Point(x, y), SampleSize, samplingAreaSize); PixelOperations.Instance.ToRgb24(config, pixelSpan, rgbSpan); if (RgbToYCbCrConverterVectorized.IsSupported) { RgbToYCbCrConverterVectorized.Convert420(rgbSpan, ref YLeft, ref YRight, ref Cb, ref Cr, idx); } else { colorTables.Convert420(rgbSpan, ref YLeft, ref YRight, ref Cb, ref Cr, idx); } } } internal ref struct YCbCrForwardConverter444 where TPixel : unmanaged, IPixel { private const int PixelsPerSample = 64; private const int RgbSpanByteSize = 192; public Block8x8F Y; public Block8x8F Cb; public Block8x8F Cr; private RgbToYCbCrConverterLut colorTables; private readonly Span pixelSpan; private readonly Span rgbSpan; private readonly Size samplingAreaSize; private readonly Configuration config; private static Size SampleSize => new Size(8, 8); public YCbCrForwardConverter444(ImageFrame frame) { Y = default(Block8x8F); Cb = default(Block8x8F); Cr = default(Block8x8F); pixelSpan = new TPixel[64].AsSpan(); rgbSpan = MemoryMarshal.Cast(new byte[192 + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan()); samplingAreaSize = new Size(frame.Width, frame.Height); config = frame.GetConfiguration(); if (!RgbToYCbCrConverterVectorized.IsSupported) { colorTables = RgbToYCbCrConverterLut.Create(); } else { colorTables = default(RgbToYCbCrConverterLut); } } public void Convert(int x, int y, ref RowOctet currentRows) { YCbCrForwardConverter.LoadAndStretchEdges(currentRows, pixelSpan, new Point(x, y), SampleSize, samplingAreaSize); PixelOperations.Instance.ToRgb24(config, pixelSpan, rgbSpan); ref Block8x8F y2 = ref Y; ref Block8x8F cb = ref Cb; ref Block8x8F cr = ref Cr; if (RgbToYCbCrConverterVectorized.IsSupported) { RgbToYCbCrConverterVectorized.Convert444(rgbSpan, ref y2, ref cb, ref cr); } else { colorTables.Convert444(rgbSpan, ref y2, ref cb, ref cr); } } } internal static class YCbCrForwardConverter where TPixel : unmanaged, IPixel { public static void LoadAndStretchEdges(RowOctet source, Span dest, Point start, Size sampleSize, Size totalSize) { int num = Math.Min(sampleSize.Width, totalSize.Width - start.X); int num2 = Math.Min(sampleSize.Height, totalSize.Height - start.Y); uint byteCount = (uint)(num * Unsafe.SizeOf()); int num3 = sampleSize.Width - num; ref byte reference = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(dest)); int num4 = sampleSize.Width * Unsafe.SizeOf(); for (int i = 0; i < num2; i++) { ref byte source2 = ref Unsafe.As(ref source[i][start.X]); ref byte reference2 = ref Unsafe.Add(ref reference, i * num4); Unsafe.CopyBlock(ref reference2, in source2, byteCount); ref TPixel reference3 = ref Unsafe.Add(ref Unsafe.As(ref reference2), num - 1); for (int j = 1; j <= num3; j++) { Unsafe.Add(ref reference3, j) = reference3; } } int num5 = sampleSize.Height - num2; if (num5 != 0) { ref byte source3 = ref Unsafe.Add(ref reference, (num2 - 1) * num4); for (int k = 1; k <= num5; k++) { Unsafe.CopyBlock(ref Unsafe.Add(ref source3, num4 * k), in source3, (uint)num4); } } } } } namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { internal readonly struct AdobeMarker : IEquatable { public const int Length = 12; public short DCTEncodeVersion { get; } public short APP14Flags0 { get; } public short APP14Flags1 { get; } public byte ColorTransform { get; } private AdobeMarker(short dctEncodeVersion, short app14Flags0, short app14Flags1, byte colorTransform) { DCTEncodeVersion = dctEncodeVersion; APP14Flags0 = app14Flags0; APP14Flags1 = app14Flags1; ColorTransform = colorTransform; } public static bool TryParse(byte[] bytes, out AdobeMarker marker) { if (ProfileResolver.IsProfile(bytes, ProfileResolver.AdobeMarker)) { short dctEncodeVersion = (short)((bytes[5] << 8) | bytes[6]); short app14Flags = (short)((bytes[7] << 8) | bytes[8]); short app14Flags2 = (short)((bytes[9] << 8) | bytes[10]); byte colorTransform = bytes[11]; marker = new AdobeMarker(dctEncodeVersion, app14Flags, app14Flags2, colorTransform); return true; } marker = default(AdobeMarker); return false; } public bool Equals(AdobeMarker other) { if (DCTEncodeVersion == other.DCTEncodeVersion && APP14Flags0 == other.APP14Flags0 && APP14Flags1 == other.APP14Flags1) { return ColorTransform == other.ColorTransform; } return false; } public override bool Equals(object obj) { if (obj is AdobeMarker other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(DCTEncodeVersion, APP14Flags0, APP14Flags1, ColorTransform); } } internal struct HuffmanScanBuffer { private readonly BufferedReadStream stream; private ulong data; private int remainingBits; private bool badData; public byte Marker { get; private set; } public long MarkerPosition { get; private set; } public bool NoData { get; private set; } public HuffmanScanBuffer(BufferedReadStream stream) { this.stream = stream; data = 0uL; remainingBits = 0; Marker = byte.MaxValue; MarkerPosition = 0L; badData = false; NoData = false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CheckBits() { if (remainingBits < 16) { FillBuffer(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { data = 0uL; remainingBits = 0; Marker = byte.MaxValue; MarkerPosition = 0L; badData = false; NoData = false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasRestartMarker() { return HasRestart(Marker); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasBadMarker() { if (Marker != byte.MaxValue) { return !HasRestartMarker(); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FillBuffer() { remainingBits += 48; data = (data << 48) | GetBytes(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe int DecodeHuffman(ref HuffmanTable h) { CheckBits(); int num = PeekBits(8); int i = h.LookaheadSize[num]; if (i < 9) { remainingBits -= i; return h.LookaheadValue[num]; } ulong num2; for (num2 = data << 64 - remainingBits; num2 > h.MaxCode[i]; i++) { } remainingBits -= i; return h.Values[(h.ValOffset[i] + (int)(num2 >> 64 - i)) & 0xFF]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Receive(int nbits) { CheckBits(); return Extend(GetBits(nbits), nbits); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool HasRestart(byte marker) { if (marker >= 208) { return marker <= 215; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetBits(int nbits) { return (int)ExtractBits(data, remainingBits -= nbits, nbits); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int PeekBits(int nbits) { return (int)ExtractBits(data, remainingBits - nbits, nbits); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong ExtractBits(ulong value, int offset, int size) { return (value >> offset) & (ulong)((1 << size) - 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Extend(int v, int nbits) { return v - (((v + v >> nbits) - 1) & ((1 << nbits) - 1)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private ulong GetBytes() { ulong num = 0uL; for (int i = 0; i < 6; i++) { int num2 = ReadStream(); if (num2 == 255) { int num3 = ReadStream(); while (true) { switch (num3) { case 255: goto IL_001f; default: Marker = (byte)num3; badData = true; MarkerPosition = stream.Position - 2; break; case 0: break; } break; IL_001f: num3 = ReadStream(); } } num = (num << 8) | (ulong)num2; } return num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool FindNextMarker() { while (true) { int num = stream.ReadByte(); switch (num) { default: continue; case -1: return false; case 255: break; } while (true) { switch (num) { case 0: break; case 255: goto IL_001c; default: Marker = (byte)num; badData = true; MarkerPosition = stream.Position - 2; return true; } break; IL_001c: num = stream.ReadByte(); if (num == -1) { return false; } } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int ReadStream() { int num = ((!badData) ? stream.ReadByte() : 0); if (num == -1 || (badData && data == 0L && stream.Position >= stream.Length)) { badData = true; NoData = true; num = 0; } return num; } } internal class HuffmanScanDecoder { private readonly BufferedReadStream stream; private JpegFrame frame; private JpegComponent[] components; private int scanComponentCount; private int restartInterval; private int todo; private int eobrun; private readonly HuffmanTable[] dcHuffmanTables; private readonly HuffmanTable[] acHuffmanTables; private HuffmanScanBuffer scanBuffer; private readonly SpectralConverter spectralConverter; private readonly CancellationToken cancellationToken; public int ResetInterval { set { restartInterval = value; todo = value; } } public int SpectralStart { get; set; } public int SpectralEnd { get; set; } public int SuccessiveHigh { get; set; } public int SuccessiveLow { get; set; } public HuffmanScanDecoder(BufferedReadStream stream, SpectralConverter converter, CancellationToken cancellationToken) { this.stream = stream; spectralConverter = converter; this.cancellationToken = cancellationToken; dcHuffmanTables = new HuffmanTable[4]; acHuffmanTables = new HuffmanTable[4]; } public void ParseEntropyCodedData(int scanComponentCount) { cancellationToken.ThrowIfCancellationRequested(); this.scanComponentCount = scanComponentCount; scanBuffer = new HuffmanScanBuffer(stream); bool fullScan = frame.Progressive || frame.MultiScan; frame.AllocateComponents(fullScan); if (!frame.Progressive) { ParseBaselineData(); } else { ParseProgressiveData(); } if (scanBuffer.HasBadMarker()) { stream.Position = scanBuffer.MarkerPosition; } } public void InjectFrameData(JpegFrame frame, IRawJpegData jpegData) { this.frame = frame; components = frame.Components; spectralConverter.InjectFrameData(frame, jpegData); } private void ParseBaselineData() { if (scanComponentCount != 1) { ParseBaselineDataInterleaved(); spectralConverter.CommitConversion(); } else if (frame.ComponentCount == 1) { ParseBaselineDataSingleComponent(); spectralConverter.CommitConversion(); } else { ParseBaselineDataNonInterleaved(); } } private void ParseBaselineDataInterleaved() { int num = 0; int mcusPerColumn = frame.McusPerColumn; int mcusPerLine = frame.McusPerLine; ref HuffmanScanBuffer reference = ref scanBuffer; for (int i = 0; i < mcusPerColumn; i++) { cancellationToken.ThrowIfCancellationRequested(); for (int j = 0; j < mcusPerLine; j++) { int num2 = num % mcusPerLine; for (int k = 0; k < scanComponentCount; k++) { int num3 = frame.ComponentOrder[k]; JpegComponent jpegComponent = components[num3]; ref HuffmanTable dcTable = ref dcHuffmanTables[jpegComponent.DCHuffmanTableId]; ref HuffmanTable acTable = ref acHuffmanTables[jpegComponent.ACHuffmanTableId]; int horizontalSamplingFactor = jpegComponent.HorizontalSamplingFactor; int verticalSamplingFactor = jpegComponent.VerticalSamplingFactor; for (int l = 0; l < verticalSamplingFactor; l++) { ref Block8x8 reference2 = ref MemoryMarshal.GetReference(jpegComponent.SpectralBlocks.DangerousGetRowSpan(l)); for (int m = 0; m < horizontalSamplingFactor; m++) { if (reference.NoData) { spectralConverter.ConvertStrideBaseline(); return; } int elementOffset = num2 * horizontalSamplingFactor + m; DecodeBlockBaseline(jpegComponent, ref Unsafe.Add(ref reference2, elementOffset), ref dcTable, ref acTable); } } } num++; HandleRestart(); } spectralConverter.ConvertStrideBaseline(); } } private void ParseBaselineDataNonInterleaved() { JpegComponent jpegComponent = components[frame.ComponentOrder[0]]; ref HuffmanScanBuffer reference = ref scanBuffer; int widthInBlocks = jpegComponent.WidthInBlocks; int heightInBlocks = jpegComponent.HeightInBlocks; ref HuffmanTable dcTable = ref dcHuffmanTables[jpegComponent.DCHuffmanTableId]; ref HuffmanTable acTable = ref acHuffmanTables[jpegComponent.ACHuffmanTableId]; for (int i = 0; i < heightInBlocks; i++) { cancellationToken.ThrowIfCancellationRequested(); ref Block8x8 reference2 = ref MemoryMarshal.GetReference(jpegComponent.SpectralBlocks.DangerousGetRowSpan(i)); for (int j = 0; j < widthInBlocks; j++) { if (reference.NoData) { return; } DecodeBlockBaseline(jpegComponent, ref Unsafe.Add(ref reference2, j), ref dcTable, ref acTable); HandleRestart(); } } } private void ParseBaselineDataSingleComponent() { JpegComponent jpegComponent = frame.Components[0]; int mcusPerColumn = frame.McusPerColumn; int widthInBlocks = jpegComponent.WidthInBlocks; int height = jpegComponent.SamplingFactors.Height; ref HuffmanTable dcTable = ref dcHuffmanTables[jpegComponent.DCHuffmanTableId]; ref HuffmanTable acTable = ref acHuffmanTables[jpegComponent.ACHuffmanTableId]; ref HuffmanScanBuffer reference = ref scanBuffer; for (int i = 0; i < mcusPerColumn; i++) { cancellationToken.ThrowIfCancellationRequested(); for (int j = 0; j < height; j++) { ref Block8x8 reference2 = ref MemoryMarshal.GetReference(jpegComponent.SpectralBlocks.DangerousGetRowSpan(j)); for (int k = 0; k < widthInBlocks; k++) { if (reference.NoData) { spectralConverter.ConvertStrideBaseline(); return; } DecodeBlockBaseline(jpegComponent, ref Unsafe.Add(ref reference2, k), ref dcTable, ref acTable); HandleRestart(); } } spectralConverter.ConvertStrideBaseline(); } } private void CheckProgressiveData() { bool flag = false; if (SpectralStart == 0) { if (SpectralEnd != 0) { flag = true; } } else { if (SpectralEnd < SpectralStart || SpectralEnd > 63) { flag = true; } if (scanComponentCount != 1) { flag = true; } } if (SuccessiveHigh != 0 && SuccessiveHigh - 1 != SuccessiveLow) { flag = true; } if (SuccessiveLow > 13) { flag = true; } if (flag) { JpegThrowHelper.ThrowBadProgressiveScan(SpectralStart, SpectralEnd, SuccessiveHigh, SuccessiveLow); } } private void ParseProgressiveData() { CheckProgressiveData(); if (scanComponentCount == 1) { ParseProgressiveDataNonInterleaved(); } else { ParseProgressiveDataInterleaved(); } } private void ParseProgressiveDataInterleaved() { int num = 0; int mcusPerColumn = frame.McusPerColumn; int mcusPerLine = frame.McusPerLine; ref HuffmanScanBuffer reference = ref scanBuffer; for (int i = 0; i < mcusPerColumn; i++) { for (int j = 0; j < mcusPerLine; j++) { int num2 = num / mcusPerLine; int num3 = num % mcusPerLine; for (int k = 0; k < scanComponentCount; k++) { int num4 = frame.ComponentOrder[k]; JpegComponent jpegComponent = components[num4]; ref HuffmanTable dcTable = ref dcHuffmanTables[jpegComponent.DCHuffmanTableId]; int horizontalSamplingFactor = jpegComponent.HorizontalSamplingFactor; int verticalSamplingFactor = jpegComponent.VerticalSamplingFactor; for (int l = 0; l < verticalSamplingFactor; l++) { int y = num2 * verticalSamplingFactor + l; ref Block8x8 reference2 = ref MemoryMarshal.GetReference(jpegComponent.SpectralBlocks.DangerousGetRowSpan(y)); for (int m = 0; m < horizontalSamplingFactor; m++) { if (reference.NoData) { return; } int elementOffset = num3 * horizontalSamplingFactor + m; DecodeBlockProgressiveDC(jpegComponent, ref Unsafe.Add(ref reference2, elementOffset), ref dcTable); } } } num++; HandleRestart(); } } } private void ParseProgressiveDataNonInterleaved() { JpegComponent jpegComponent = components[frame.ComponentOrder[0]]; ref HuffmanScanBuffer reference = ref scanBuffer; int widthInBlocks = jpegComponent.WidthInBlocks; int heightInBlocks = jpegComponent.HeightInBlocks; if (SpectralStart == 0) { ref HuffmanTable dcTable = ref dcHuffmanTables[jpegComponent.DCHuffmanTableId]; for (int i = 0; i < heightInBlocks; i++) { cancellationToken.ThrowIfCancellationRequested(); ref Block8x8 reference2 = ref MemoryMarshal.GetReference(jpegComponent.SpectralBlocks.DangerousGetRowSpan(i)); for (int j = 0; j < widthInBlocks; j++) { if (reference.NoData) { return; } DecodeBlockProgressiveDC(jpegComponent, ref Unsafe.Add(ref reference2, j), ref dcTable); HandleRestart(); } } return; } ref HuffmanTable acTable = ref acHuffmanTables[jpegComponent.ACHuffmanTableId]; for (int k = 0; k < heightInBlocks; k++) { cancellationToken.ThrowIfCancellationRequested(); ref Block8x8 reference3 = ref MemoryMarshal.GetReference(jpegComponent.SpectralBlocks.DangerousGetRowSpan(k)); for (int l = 0; l < widthInBlocks; l++) { if (reference.NoData) { return; } DecodeBlockProgressiveAC(ref Unsafe.Add(ref reference3, l), ref acTable); HandleRestart(); } } } private void DecodeBlockBaseline(JpegComponent component, ref Block8x8 block, ref HuffmanTable dcTable, ref HuffmanTable acTable) { ref short reference = ref Unsafe.As(ref block); ref HuffmanScanBuffer reference2 = ref scanBuffer; int num = reference2.DecodeHuffman(ref dcTable); if (num != 0) { num = reference2.Receive(num); } num = (component.DcPredictor = num + component.DcPredictor); reference = (short)num; int num3 = 1; while (num3 < 64) { int num4 = reference2.DecodeHuffman(ref acTable); int num5 = num4 >> 4; num4 &= 0xF; if (num4 != 0) { num3 += num5; num4 = reference2.Receive(num4); Unsafe.Add(ref reference, ZigZag.TransposingOrder[num3++]) = (short)num4; continue; } if (num5 != 0) { num3 += 16; continue; } break; } } private void DecodeBlockProgressiveDC(JpegComponent component, ref Block8x8 block, ref HuffmanTable dcTable) { ref short reference = ref Unsafe.As(ref block); ref HuffmanScanBuffer reference2 = ref scanBuffer; if (SuccessiveHigh == 0) { int num = reference2.DecodeHuffman(ref dcTable); if (num != 0) { num = reference2.Receive(num); } num = (component.DcPredictor = num + component.DcPredictor); reference = (short)(num << SuccessiveLow); } else { reference2.CheckBits(); reference |= (short)(reference2.GetBits(1) << SuccessiveLow); } } private void DecodeBlockProgressiveAC(ref Block8x8 block, ref HuffmanTable acTable) { ref short reference = ref Unsafe.As(ref block); if (SuccessiveHigh == 0) { if (eobrun != 0) { eobrun--; return; } ref HuffmanScanBuffer reference2 = ref scanBuffer; int spectralStart = SpectralStart; int spectralEnd = SpectralEnd; int successiveLow = SuccessiveLow; int num; for (num = spectralStart; num <= spectralEnd; num++) { int num2 = reference2.DecodeHuffman(ref acTable); int num3 = num2 >> 4; num2 &= 0xF; num += num3; if (num2 != 0) { num2 = reference2.Receive(num2); Unsafe.Add(ref reference, ZigZag.TransposingOrder[num]) = (short)(num2 << successiveLow); } else if (num3 != 15) { eobrun = 1 << num3; if (num3 != 0) { reference2.CheckBits(); eobrun += reference2.GetBits(num3); } eobrun--; break; } } } else { DecodeBlockProgressiveACRefined(ref reference, ref acTable); } } private void DecodeBlockProgressiveACRefined(ref short blockDataRef, ref HuffmanTable acTable) { ref HuffmanScanBuffer reference = ref scanBuffer; int spectralStart = SpectralStart; int spectralEnd = SpectralEnd; int num = 1 << SuccessiveLow; int num2 = -1 << SuccessiveLow; int i = spectralStart; if (eobrun == 0) { for (; i <= spectralEnd; i++) { int num3 = reference.DecodeHuffman(ref acTable); int num4 = num3 >> 4; num3 &= 0xF; if (num3 != 0) { reference.CheckBits(); num3 = ((reference.GetBits(1) == 0) ? num2 : num); } else if (num4 != 15) { eobrun = 1 << num4; if (num4 != 0) { reference.CheckBits(); eobrun += reference.GetBits(num4); } break; } do { ref short reference2 = ref Unsafe.Add(ref blockDataRef, ZigZag.TransposingOrder[i]); if (reference2 != 0) { reference.CheckBits(); if (reference.GetBits(1) != 0 && (reference2 & num) == 0) { reference2 += (short)((reference2 >= 0) ? num : num2); } } else if (--num4 < 0) { break; } i++; } while (i <= spectralEnd); if (num3 != 0 && i < 64) { Unsafe.Add(ref blockDataRef, ZigZag.TransposingOrder[i]) = (short)num3; } } } if (eobrun <= 0) { return; } for (; i <= spectralEnd; i++) { ref short reference3 = ref Unsafe.Add(ref blockDataRef, ZigZag.TransposingOrder[i]); if (reference3 != 0) { reference.CheckBits(); if (reference.GetBits(1) != 0 && (reference3 & num) == 0) { reference3 += (short)((reference3 >= 0) ? num : num2); } } } eobrun--; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Reset() { for (int i = 0; i < components.Length; i++) { components[i].DcPredictor = 0; } eobrun = 0; scanBuffer.Reset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool HandleRestart() { if (restartInterval > 0 && --todo == 0) { if (scanBuffer.Marker == byte.MaxValue && !scanBuffer.FindNextMarker()) { return false; } todo = restartInterval; if (scanBuffer.HasRestartMarker()) { Reset(); return true; } if (scanBuffer.HasBadMarker()) { stream.Position = scanBuffer.MarkerPosition; Reset(); return true; } } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void BuildHuffmanTable(int type, int index, ReadOnlySpan codeLengths, ReadOnlySpan values, Span workspace) { ((type == 0) ? dcHuffmanTables : acHuffmanTables)[index] = new HuffmanTable(codeLengths, values, workspace); } } internal struct HuffmanTable { public const int WorkspaceByteSize = 1024; public unsafe fixed byte Values[256]; public unsafe fixed ulong MaxCode[18]; public unsafe fixed int ValOffset[19]; public unsafe fixed byte LookaheadSize[256]; public unsafe fixed byte LookaheadValue[256]; public unsafe HuffmanTable(ReadOnlySpan codeLengths, ReadOnlySpan values, Span workspace) { Unsafe.CopyBlockUnaligned(ref Values[0], in MemoryMarshal.GetReference(values), (uint)values.Length); uint num = 0u; int num2 = 1; int num3 = 0; for (int i = 1; i <= 16; i++) { int num4 = codeLengths[i]; for (int j = 0; j < num4; j++) { workspace[num3++] = num; num++; } if (num >= 1 << num2) { JpegThrowHelper.ThrowInvalidImageContentException("Bad huffman table."); } num <<= 1; num2++; } num3 = 0; for (int k = 1; k <= 16; k++) { if (codeLengths[k] != 0) { ValOffset[k] = num3 - (int)workspace[num3]; num3 += codeLengths[k]; MaxCode[k] = workspace[num3 - 1]; ref ulong reference = ref MaxCode[k]; reference <<= 64 - k; ref ulong reference2 = ref MaxCode[k]; reference2 |= (ulong)((1L << 64 - k) - 1); } else { MaxCode[k] = 0uL; } } ValOffset[18] = 0; MaxCode[17] = ulong.MaxValue; Unsafe.InitBlockUnaligned(ref LookaheadSize[0], 9, 256u); num3 = 0; for (int l = 1; l <= 8; l++) { int num5 = 8 - l; int num6 = 1; while (num6 <= codeLengths[l]) { int num7 = (int)(workspace[num3] << num5); for (int num8 = 1 << 8 - l; num8 > 0; num8--) { LookaheadSize[num7] = (byte)l; LookaheadValue[num7] = Values[num3]; num7++; } num6++; num3++; } } } } internal interface IJpegComponent { int Index { get; } Size SizeInBlocks { get; } Size SamplingFactors { get; } Size SubSamplingDivisors { get; } int QuantizationTableIndex { get; } Buffer2D SpectralBlocks { get; } } internal interface IRawJpegData : IDisposable { JpegColorSpace ColorSpace { get; } IJpegComponent[] Components { get; } Block8x8F[] QuantizationTables { get; } } internal readonly struct JFifMarker : IEquatable { public const int Length = 13; public byte MajorVersion { get; } public byte MinorVersion { get; } public PixelResolutionUnit DensityUnits { get; } public short XDensity { get; } public short YDensity { get; } private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity) { if (xDensity <= 0) { JpegThrowHelper.ThrowInvalidImageContentException($"X-Density {xDensity} must be greater than 0."); } if (yDensity <= 0) { JpegThrowHelper.ThrowInvalidImageContentException($"Y-Density {yDensity} must be greater than 0."); } MajorVersion = majorVersion; MinorVersion = minorVersion; DensityUnits = (PixelResolutionUnit)densityUnits; XDensity = xDensity; YDensity = yDensity; } public static bool TryParse(byte[] bytes, out JFifMarker marker) { if (ProfileResolver.IsProfile(bytes, ProfileResolver.JFifMarker)) { byte majorVersion = bytes[5]; byte minorVersion = bytes[6]; byte densityUnits = bytes[7]; short num = (short)((bytes[8] << 8) | bytes[9]); short num2 = (short)((bytes[10] << 8) | bytes[11]); if (num > 0 && num2 > 0) { marker = new JFifMarker(majorVersion, minorVersion, densityUnits, num, num2); return true; } } marker = default(JFifMarker); return false; } public bool Equals(JFifMarker other) { if (MajorVersion == other.MajorVersion && MinorVersion == other.MinorVersion && DensityUnits == other.DensityUnits && XDensity == other.XDensity) { return YDensity == other.YDensity; } return false; } public override bool Equals(object obj) { if (obj is JFifMarker other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(MajorVersion, MinorVersion, DensityUnits, XDensity, YDensity); } } internal enum JpegColorSpace { Undefined, Grayscale, Ycck, Cmyk, RGB, YCbCr } internal sealed class JpegComponent : IDisposable, IJpegComponent { private readonly MemoryAllocator memoryAllocator; public byte Id { get; } public int DcPredictor { get; set; } public int HorizontalSamplingFactor { get; } public int VerticalSamplingFactor { get; } public Buffer2D SpectralBlocks { get; private set; } public Size SubSamplingDivisors { get; private set; } public int QuantizationTableIndex { get; } public int Index { get; } public Size SizeInBlocks { get; private set; } public Size SamplingFactors { get; set; } public int WidthInBlocks { get; private set; } public int HeightInBlocks { get; private set; } public int DCHuffmanTableId { get; set; } public int ACHuffmanTableId { get; set; } public JpegFrame Frame { get; } public JpegComponent(MemoryAllocator memoryAllocator, JpegFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) { this.memoryAllocator = memoryAllocator; Frame = frame; Id = id; HorizontalSamplingFactor = horizontalFactor; VerticalSamplingFactor = verticalFactor; SamplingFactors = new Size(HorizontalSamplingFactor, VerticalSamplingFactor); QuantizationTableIndex = quantizationTableIndex; Index = index; } public void Dispose() { SpectralBlocks?.Dispose(); SpectralBlocks = null; } public void Init(int maxSubFactorH, int maxSubFactorV) { WidthInBlocks = (int)MathF.Ceiling(MathF.Ceiling((float)Frame.PixelWidth / 8f) * (float)HorizontalSamplingFactor / (float)maxSubFactorH); HeightInBlocks = (int)MathF.Ceiling(MathF.Ceiling((float)Frame.PixelHeight / 8f) * (float)VerticalSamplingFactor / (float)maxSubFactorV); int width = Frame.McusPerLine * HorizontalSamplingFactor; int height = Frame.McusPerColumn * VerticalSamplingFactor; SizeInBlocks = new Size(width, height); SubSamplingDivisors = new Size(maxSubFactorH, maxSubFactorV).DivideBy(SamplingFactors); if (SubSamplingDivisors.Width == 0 || SubSamplingDivisors.Height == 0) { JpegThrowHelper.ThrowBadSampling(); } } public void AllocateSpectral(bool fullScan) { if (SpectralBlocks == null) { int width = SizeInBlocks.Width; int height = (fullScan ? SizeInBlocks.Height : VerticalSamplingFactor); SpectralBlocks = memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean); } } } internal class JpegComponentPostProcessor : IDisposable { private readonly Size blockAreaSize; private readonly JpegFrame frame; private readonly int blockRowsPerStep; private readonly IJpegComponent component; private readonly IRawJpegData rawJpeg; public Buffer2D ColorBuffer { get; } public JpegComponentPostProcessor(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component) { this.frame = frame; this.component = component; this.rawJpeg = rawJpeg; blockAreaSize = this.component.SubSamplingDivisors * 8; ColorBuffer = memoryAllocator.Allocate2DOveraligned(postProcessorBufferSize.Width, postProcessorBufferSize.Height, blockAreaSize.Height); blockRowsPerStep = postProcessorBufferSize.Height / 8 / this.component.SubSamplingDivisors.Height; } public void Dispose() { ColorBuffer.Dispose(); } public void CopyBlocksToColorBuffer(int spectralStep) { Buffer2D spectralBlocks = component.SpectralBlocks; float maxColorChannelValue = frame.MaxColorChannelValue; int width = ColorBuffer.Width; int num = spectralStep * blockRowsPerStep; Size subSamplingDivisors = component.SubSamplingDivisors; Block8x8F other = rawJpeg.QuantizationTables[component.QuantizationTableIndex]; Block8x8F block = default(Block8x8F); for (int i = 0; i < blockRowsPerStep; i++) { int y = i * blockAreaSize.Height; Span span = ColorBuffer.DangerousGetRowSpan(y); Span span2 = spectralBlocks.DangerousGetRowSpan(num + i); for (int j = 0; j < spectralBlocks.Width; j++) { block.LoadFrom(ref span2[j]); block.MultiplyInPlace(ref other); FastFloatingPointDCT.TransformIDCT(ref block); block.NormalizeColorsAndRoundInPlace(maxColorChannelValue); int index = j * blockAreaSize.Width; block.ScaledCopyTo(ref span[index], width, subSamplingDivisors.Width, subSamplingDivisors.Height); } } } public void ClearSpectralBuffers() { Buffer2D spectralBlocks = component.SpectralBlocks; for (int i = 0; i < spectralBlocks.Height; i++) { spectralBlocks.DangerousGetRowSpan(i).Clear(); } } public Span GetColorBufferRowSpan(int row) { return ColorBuffer.DangerousGetRowSpan(row); } } internal readonly struct JpegFileMarker { public bool Invalid { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public byte Marker { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public long Position { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } public JpegFileMarker(byte marker, long position) : this(marker, position, invalid: false) { } public JpegFileMarker(byte marker, long position, bool invalid) { Marker = marker; Position = position; Invalid = invalid; } public override string ToString() { return Marker.ToString("X"); } } internal sealed class JpegFrame : IDisposable { public bool Extended { get; private set; } public bool Progressive { get; private set; } public bool MultiScan { get; set; } public byte Precision { get; private set; } public float MaxColorChannelValue { get; private set; } public int PixelHeight { get; private set; } public int PixelWidth { get; private set; } public Size PixelSize => new Size(PixelWidth, PixelHeight); public byte ComponentCount { get; private set; } public byte[] ComponentIds { get; set; } public byte[] ComponentOrder { get; set; } public JpegComponent[] Components { get; set; } public int McusPerLine { get; set; } public int McusPerColumn { get; set; } public Size McuSize => new Size(McusPerLine, McusPerColumn); public int BitsPerPixel => ComponentCount * Precision; public JpegFrame(JpegFileMarker sofMarker, byte precision, int width, int height, byte componentCount) { Extended = sofMarker.Marker == 193; Progressive = sofMarker.Marker == 194; Precision = precision; MaxColorChannelValue = MathF.Pow(2f, (int)precision) - 1f; PixelWidth = width; PixelHeight = height; ComponentCount = componentCount; } public void Dispose() { if (Components != null) { for (int i = 0; i < Components.Length; i++) { Components[i]?.Dispose(); } Components = null; } } public void Init(int maxSubFactorH, int maxSubFactorV) { McusPerLine = (int)Numerics.DivideCeil((uint)PixelWidth, (uint)(maxSubFactorH * 8)); McusPerColumn = (int)Numerics.DivideCeil((uint)PixelHeight, (uint)(maxSubFactorV * 8)); for (int i = 0; i < ComponentCount; i++) { Components[i].Init(maxSubFactorH, maxSubFactorV); } } public void AllocateComponents(bool fullScan) { for (int i = 0; i < ComponentCount; i++) { Components[i].AllocateSpectral(fullScan); } } } internal static class ProfileResolver { public static ReadOnlySpan JFifMarker => "JFIF\0"u8; public static ReadOnlySpan IccMarker => "ICC_PROFILE\0"u8; public static ReadOnlySpan AdobePhotoshopApp13Marker => "Photoshop 3.0\0"u8; public static ReadOnlySpan AdobeImageResourceBlockMarker => "8BIM"u8; public static ReadOnlySpan AdobeIptcMarker => new byte[2] { 4, 4 }; public static ReadOnlySpan ExifMarker => new byte[6] { 69, 120, 105, 102, 0, 0 }; public static ReadOnlySpan XmpMarker => "http://ns.adobe.com/xap/1.0/\0"u8; public static ReadOnlySpan AdobeMarker => "Adobe"u8; public static bool IsProfile(ReadOnlySpan bytesToCheck, ReadOnlySpan profileIdentifier) { if (bytesToCheck.Length >= profileIdentifier.Length) { return bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier); } return false; } } internal abstract class SpectralConverter { protected bool Converted { get; private set; } public abstract void InjectFrameData(JpegFrame frame, IRawJpegData jpegData); public abstract void ConvertStrideBaseline(); public void CommitConversion() { Converted = true; } protected virtual JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) { return JpegColorConverterBase.GetConverter(jpegData.ColorSpace, frame.Precision); } } internal class SpectralConverter : SpectralConverter, IDisposable where TPixel : unmanaged, IPixel { private readonly Configuration configuration; private JpegComponentPostProcessor[] componentProcessors; private JpegColorConverterBase colorConverter; private IMemoryOwner rgbBuffer; private IMemoryOwner paddedProxyPixelRow; private Buffer2D pixelBuffer; private int pixelRowsPerStep; private int pixelRowCounter; public SpectralConverter(Configuration configuration) { this.configuration = configuration; } public Buffer2D GetPixelBuffer(CancellationToken cancellationToken) { if (!base.Converted) { int num = (int)Math.Ceiling((float)pixelBuffer.Height / (float)pixelRowsPerStep); for (int i = 0; i < num; i++) { cancellationToken.ThrowIfCancellationRequested(); ConvertStride(i); } } Buffer2D result = pixelBuffer; pixelBuffer = null; return result; } public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData) { MemoryAllocator memoryAllocator = configuration.MemoryAllocator; int num = frame.Components.Max((JpegComponent component) => component.SizeInBlocks.Width); int num2 = frame.Components.Max((JpegComponent component) => component.SamplingFactors.Height); pixelRowsPerStep = num2 * 8; pixelBuffer = memoryAllocator.Allocate2D(frame.PixelWidth, frame.PixelHeight, configuration.PreferContiguousImageBuffers, AllocationOptions.Clean); paddedProxyPixelRow = memoryAllocator.Allocate(frame.PixelWidth + 3); Size postProcessorBufferSize = new Size(num * 8, pixelRowsPerStep); componentProcessors = new JpegComponentPostProcessor[frame.Components.Length]; for (int num3 = 0; num3 < componentProcessors.Length; num3++) { componentProcessors[num3] = new JpegComponentPostProcessor(memoryAllocator, frame, jpegData, postProcessorBufferSize, frame.Components[num3]); } rgbBuffer = memoryAllocator.Allocate(frame.PixelWidth * 3); colorConverter = GetColorConverter(frame, jpegData); } public override void ConvertStrideBaseline() { ConvertStride(0); JpegComponentPostProcessor[] array = componentProcessors; for (int i = 0; i < array.Length; i++) { array[i].ClearSpectralBuffers(); } } private void ConvertStride(int spectralStep) { int num = Math.Min(pixelBuffer.Height, pixelRowCounter + pixelRowsPerStep); for (int i = 0; i < componentProcessors.Length; i++) { componentProcessors[i].CopyBlocksToColorBuffer(spectralStep); } int width = pixelBuffer.Width; for (int j = pixelRowCounter; j < num; j++) { int row = j - pixelRowCounter; JpegColorConverterBase.ComponentValues values = new JpegColorConverterBase.ComponentValues(componentProcessors, row); colorConverter.ConvertToRgbInplace(in values); values = values.Slice(0, width); Span span = rgbBuffer.Slice(0, width); Span span2 = rgbBuffer.Slice(width, width); Span span3 = rgbBuffer.Slice(width * 2, width); SimdUtils.NormalizedFloatToByteSaturate(values.Component0, span); SimdUtils.NormalizedFloatToByteSaturate(values.Component1, span2); SimdUtils.NormalizedFloatToByteSaturate(values.Component2, span3); if (pixelBuffer.DangerousTryGetPaddedRowSpan(j, 3, out var paddedSpan)) { PixelOperations.Instance.PackFromRgbPlanes(configuration, span, span2, span3, paddedSpan); continue; } Span span4 = paddedProxyPixelRow.GetSpan(); PixelOperations.Instance.PackFromRgbPlanes(configuration, span, span2, span3, span4); span4.Slice(0, width).CopyTo(pixelBuffer.DangerousGetRowSpan(j)); } pixelRowCounter += pixelRowsPerStep; } public void Dispose() { if (componentProcessors != null) { JpegComponentPostProcessor[] array = componentProcessors; for (int i = 0; i < array.Length; i++) { array[i].Dispose(); } } rgbBuffer?.Dispose(); paddedProxyPixelRow?.Dispose(); pixelBuffer?.Dispose(); } } } namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { internal abstract class JpegColorConverterBase { internal sealed class FromCmykAvx : JpegColorConverterAvx { public FromCmykAvx(int precision) : base(JpegColorSpace.Cmyk, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); ref Vector256 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); ref Vector256 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); ref Vector256 source4 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); Vector256 right = Vector256.Create(1f / (base.MaximumValue * base.MaximumValue)); nint num = values.Component0.Length / Vector256.Count; for (nint num2 = 0; num2 < num; num2++) { ref Vector256 reference = ref Unsafe.Add(ref source, num2); ref Vector256 reference2 = ref Unsafe.Add(ref source2, num2); ref Vector256 reference3 = ref Unsafe.Add(ref source3, num2); Vector256 left = Unsafe.Add(ref source4, num2); left = Avx.Multiply(left, right); reference = Avx.Multiply(reference, left); reference2 = Avx.Multiply(reference2, left); reference3 = Avx.Multiply(reference3, left); } } } internal sealed class FromCmykScalar : JpegColorConverterScalar { public FromCmykScalar(int precision) : base(JpegColorSpace.Cmyk, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { ConvertCoreInplace(in values, base.MaximumValue); } internal static void ConvertCoreInplace(in ComponentValues values, float maxValue) { Span component = values.Component0; Span component2 = values.Component1; Span component3 = values.Component2; Span component4 = values.Component3; float num = 1f / (maxValue * maxValue); for (int i = 0; i < component.Length; i++) { float num2 = component[i]; float num3 = component2[i]; float num4 = component3[i]; float num5 = component4[i]; num5 *= num; component[i] = num2 * num5; component2[i] = num3 * num5; component3[i] = num4 * num5; } } } internal sealed class FromCmykVector : JpegColorConverterVector { public FromCmykVector(int precision) : base(JpegColorSpace.Cmyk, precision) { } protected override void ConvertCoreVectorizedInplace(in ComponentValues values) { ref Vector source = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); ref Vector source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); ref Vector source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); ref Vector source4 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); Vector vector = new Vector(1f / (base.MaximumValue * base.MaximumValue)); nint num = values.Component0.Length / Vector.Count; for (nint num2 = 0; num2 < num; num2++) { ref Vector reference = ref Unsafe.Add(ref source, num2); ref Vector reference2 = ref Unsafe.Add(ref source2, num2); ref Vector reference3 = ref Unsafe.Add(ref source3, num2); Vector vector2 = Unsafe.Add(ref source4, num2); vector2 *= vector; reference *= vector2; reference2 *= vector2; reference3 *= vector2; } } protected override void ConvertCoreInplace(in ComponentValues values) { FromCmykScalar.ConvertCoreInplace(in values, base.MaximumValue); } } internal sealed class FromGrayscaleAvx : JpegColorConverterAvx { public FromGrayscaleAvx(int precision) : base(JpegColorSpace.Grayscale, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); Vector256 right = Vector256.Create(1f / base.MaximumValue); nint num = values.Component0.Length / Vector256.Count; for (nint num2 = 0; num2 < num; num2++) { ref Vector256 reference = ref Unsafe.Add(ref source, num2); reference = Avx.Multiply(reference, right); } } } internal sealed class FromGrayscaleScalar : JpegColorConverterScalar { public FromGrayscaleScalar(int precision) : base(JpegColorSpace.Grayscale, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { ConvertCoreInplace(values.Component0, base.MaximumValue); } internal static void ConvertCoreInplace(Span values, float maxValue) { ref float reference = ref MemoryMarshal.GetReference(values); float num = 1f / maxValue; for (nint num2 = 0; num2 < values.Length; num2++) { Unsafe.Add(ref reference, num2) *= num; } } } internal sealed class FromGrayScaleVector : JpegColorConverterVector { public FromGrayScaleVector(int precision) : base(JpegColorSpace.Grayscale, precision) { } protected override void ConvertCoreVectorizedInplace(in ComponentValues values) { ref Vector source = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); Vector vector = new Vector(1f / base.MaximumValue); nint num = values.Component0.Length / Vector.Count; for (nint num2 = 0; num2 < num; num2++) { Unsafe.Add(ref source, num2) *= vector; } } protected override void ConvertCoreInplace(in ComponentValues values) { FromGrayscaleScalar.ConvertCoreInplace(values.Component0, base.MaximumValue); } } internal sealed class FromRgbAvx : JpegColorConverterAvx { public FromRgbAvx(int precision) : base(JpegColorSpace.RGB, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); ref Vector256 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); ref Vector256 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); Vector256 right = Vector256.Create(1f / base.MaximumValue); nint num = values.Component0.Length / Vector256.Count; for (nint num2 = 0; num2 < num; num2++) { ref Vector256 reference = ref Unsafe.Add(ref source, num2); ref Vector256 reference2 = ref Unsafe.Add(ref source2, num2); ref Vector256 reference3 = ref Unsafe.Add(ref source3, num2); reference = Avx.Multiply(reference, right); reference2 = Avx.Multiply(reference2, right); reference3 = Avx.Multiply(reference3, right); } } } internal sealed class FromRgbScalar : JpegColorConverterScalar { public FromRgbScalar(int precision) : base(JpegColorSpace.RGB, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { ConvertCoreInplace(values, base.MaximumValue); } internal static void ConvertCoreInplace(ComponentValues values, float maxValue) { FromGrayscaleScalar.ConvertCoreInplace(values.Component0, maxValue); FromGrayscaleScalar.ConvertCoreInplace(values.Component1, maxValue); FromGrayscaleScalar.ConvertCoreInplace(values.Component2, maxValue); } } internal sealed class FromRgbVector : JpegColorConverterVector { public FromRgbVector(int precision) : base(JpegColorSpace.RGB, precision) { } protected override void ConvertCoreVectorizedInplace(in ComponentValues values) { ref Vector source = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); ref Vector source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); ref Vector source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); Vector vector = new Vector(1f / base.MaximumValue); nint num = values.Component0.Length / Vector.Count; for (nint num2 = 0; num2 < num; num2++) { ref Vector reference = ref Unsafe.Add(ref source, num2); ref Vector reference2 = ref Unsafe.Add(ref source2, num2); ref Vector reference3 = ref Unsafe.Add(ref source3, num2); reference *= vector; reference2 *= vector; reference3 *= vector; } } protected override void ConvertCoreInplace(in ComponentValues values) { FromRgbScalar.ConvertCoreInplace(values, base.MaximumValue); } } internal sealed class FromYCbCrAvx : JpegColorConverterAvx { public FromYCbCrAvx(int precision) : base(JpegColorSpace.YCbCr, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); ref Vector256 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); ref Vector256 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); Vector256 right = Vector256.Create(0f - base.HalfValue); Vector256 right2 = Vector256.Create(1f / base.MaximumValue); Vector256 vm = Vector256.Create(1.402f); Vector256 vm2 = Vector256.Create(-0.3441363f); Vector256 vm3 = Vector256.Create(-0.7141363f); Vector256 vm4 = Vector256.Create(1.772f); nint num = values.Component0.Length / Vector256.Count; for (nint num2 = 0; num2 < num; num2++) { ref Vector256 reference = ref Unsafe.Add(ref source, num2); ref Vector256 reference2 = ref Unsafe.Add(ref source2, num2); ref Vector256 reference3 = ref Unsafe.Add(ref source3, num2); Vector256 va = reference; Vector256 vm5 = Avx.Add(reference2, right); Vector256 vm6 = Avx.Add(reference3, right); Vector256 value = SimdUtils.HwIntrinsics.MultiplyAdd(in va, in vm6, in vm); Vector256 value2 = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(in va, in vm5, in vm2), in vm6, in vm3); Vector256 value3 = SimdUtils.HwIntrinsics.MultiplyAdd(in va, in vm5, in vm4); value = Avx.Multiply(Avx.RoundToNearestInteger(value), right2); value2 = Avx.Multiply(Avx.RoundToNearestInteger(value2), right2); value3 = Avx.Multiply(Avx.RoundToNearestInteger(value3), right2); reference = value; reference2 = value2; reference3 = value3; } } } internal sealed class FromYCbCrScalar : JpegColorConverterScalar { internal const float RCrMult = 1.402f; internal const float GCbMult = 0.3441363f; internal const float GCrMult = 0.7141363f; internal const float BCbMult = 1.772f; public FromYCbCrScalar(int precision) : base(JpegColorSpace.YCbCr, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { ConvertCoreInplace(in values, base.MaximumValue, base.HalfValue); } internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue) { Span component = values.Component0; Span component2 = values.Component1; Span component3 = values.Component2; float num = 1f / maxValue; for (int i = 0; i < component.Length; i++) { float num2 = component[i]; float num3 = component2[i] - halfValue; float num4 = component3[i] - halfValue; component[i] = MathF.Round(num2 + 1.402f * num4, MidpointRounding.AwayFromZero) * num; component2[i] = MathF.Round(num2 - 0.3441363f * num3 - 0.7141363f * num4, MidpointRounding.AwayFromZero) * num; component3[i] = MathF.Round(num2 + 1.772f * num3, MidpointRounding.AwayFromZero) * num; } } } internal sealed class FromYCbCrVector : JpegColorConverterVector { public FromYCbCrVector(int precision) : base(JpegColorSpace.YCbCr, precision) { } protected override void ConvertCoreVectorizedInplace(in ComponentValues values) { ref Vector source = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); ref Vector source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); ref Vector source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); Vector vector = new Vector(0f - base.HalfValue); Vector vector2 = new Vector(1f / base.MaximumValue); Vector vector3 = new Vector(1.402f); Vector vector4 = new Vector(-0.3441363f); Vector vector5 = new Vector(-0.7141363f); Vector vector6 = new Vector(1.772f); nint num = values.Component0.Length / Vector.Count; for (nint num2 = 0; num2 < num; num2++) { ref Vector reference = ref Unsafe.Add(ref source, num2); ref Vector reference2 = ref Unsafe.Add(ref source2, num2); ref Vector reference3 = ref Unsafe.Add(ref source3, num2); Vector vector7 = Unsafe.Add(ref source, num2); Vector vector8 = Unsafe.Add(ref source2, num2) + vector; Vector vector9 = Unsafe.Add(ref source3, num2) + vector; Vector v = vector7 + vector9 * vector3; Vector v2 = vector7 + vector8 * vector4 + vector9 * vector5; Vector v3 = vector7 + vector8 * vector6; v = v.FastRound(); v2 = v2.FastRound(); v3 = v3.FastRound(); v *= vector2; v2 *= vector2; v3 *= vector2; reference = v; reference2 = v2; reference3 = v3; } } protected override void ConvertCoreInplace(in ComponentValues values) { FromYCbCrScalar.ConvertCoreInplace(in values, base.MaximumValue, base.HalfValue); } } internal sealed class FromYccKAvx : JpegColorConverterAvx { public FromYccKAvx(int precision) : base(JpegColorSpace.Ycck, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { ref Vector256 source = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); ref Vector256 source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); ref Vector256 source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); ref Vector256 source4 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); Vector256 right = Vector256.Create(0f - base.HalfValue); Vector256 right2 = Vector256.Create(1f / (base.MaximumValue * base.MaximumValue)); Vector256 left = Vector256.Create(base.MaximumValue); Vector256 vm = Vector256.Create(1.402f); Vector256 vm2 = Vector256.Create(-0.3441363f); Vector256 vm3 = Vector256.Create(-0.7141363f); Vector256 vm4 = Vector256.Create(1.772f); nint num = values.Component0.Length / Vector256.Count; for (nint num2 = 0; num2 < num; num2++) { ref Vector256 reference = ref Unsafe.Add(ref source, num2); ref Vector256 reference2 = ref Unsafe.Add(ref source2, num2); ref Vector256 reference3 = ref Unsafe.Add(ref source3, num2); Vector256 va = reference; Vector256 vm5 = Avx.Add(reference2, right); Vector256 vm6 = Avx.Add(reference3, right); Vector256 right3 = Avx.Multiply(Unsafe.Add(ref source4, num2), right2); Vector256 value = SimdUtils.HwIntrinsics.MultiplyAdd(in va, in vm6, in vm); Vector256 value2 = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(in va, in vm5, in vm2), in vm6, in vm3); Vector256 value3 = SimdUtils.HwIntrinsics.MultiplyAdd(in va, in vm5, in vm4); value = Avx.Subtract(left, Avx.RoundToNearestInteger(value)); value2 = Avx.Subtract(left, Avx.RoundToNearestInteger(value2)); value3 = Avx.Subtract(left, Avx.RoundToNearestInteger(value3)); value = Avx.Multiply(value, right3); value2 = Avx.Multiply(value2, right3); value3 = Avx.Multiply(value3, right3); reference = value; reference2 = value2; reference3 = value3; } } } internal sealed class FromYccKScalar : JpegColorConverterScalar { public FromYccKScalar(int precision) : base(JpegColorSpace.Ycck, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { ConvertCoreInplace(in values, base.MaximumValue, base.HalfValue); } internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue) { Span component = values.Component0; Span component2 = values.Component1; Span component3 = values.Component2; Span component4 = values.Component3; float num = 1f / (maxValue * maxValue); for (int i = 0; i < values.Component0.Length; i++) { float num2 = component[i]; float num3 = component2[i] - halfValue; float num4 = component3[i] - halfValue; float num5 = component4[i] * num; component[i] = (maxValue - MathF.Round(num2 + 1.402f * num4, MidpointRounding.AwayFromZero)) * num5; component2[i] = (maxValue - MathF.Round(num2 - 0.344136f * num3 - 0.714136f * num4, MidpointRounding.AwayFromZero)) * num5; component3[i] = (maxValue - MathF.Round(num2 + 1.772f * num3, MidpointRounding.AwayFromZero)) * num5; } } } internal sealed class FromYccKVector : JpegColorConverterVector { public FromYccKVector(int precision) : base(JpegColorSpace.Ycck, precision) { } protected override void ConvertCoreVectorizedInplace(in ComponentValues values) { ref Vector source = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); ref Vector source2 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); ref Vector source3 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); ref Vector source4 = ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3)); Vector vector = new Vector(0f - base.HalfValue); Vector vector2 = new Vector(1f / (base.MaximumValue * base.MaximumValue)); Vector vector3 = new Vector(base.MaximumValue); Vector vector4 = new Vector(1.402f); Vector vector5 = new Vector(-0.3441363f); Vector vector6 = new Vector(-0.7141363f); Vector vector7 = new Vector(1.772f); nint num = values.Component0.Length / Vector.Count; for (nint num2 = 0; num2 < num; num2++) { ref Vector reference = ref Unsafe.Add(ref source, num2); ref Vector reference2 = ref Unsafe.Add(ref source2, num2); ref Vector reference3 = ref Unsafe.Add(ref source3, num2); Vector vector8 = reference; Vector vector9 = reference2 + vector; Vector vector10 = reference3 + vector; Vector vector11 = Unsafe.Add(ref source4, num2) * vector2; Vector v = vector8 + vector10 * vector4; Vector v2 = vector8 + vector9 * vector5 + vector10 * vector6; Vector v3 = vector8 + vector9 * vector7; v = (vector3 - v.FastRound()) * vector11; v2 = (vector3 - v2.FastRound()) * vector11; v3 = (vector3 - v3.FastRound()) * vector11; reference = v; reference2 = v2; reference3 = v3; } } protected override void ConvertCoreInplace(in ComponentValues values) { FromYccKScalar.ConvertCoreInplace(in values, base.MaximumValue, base.HalfValue); } } internal abstract class JpegColorConverterAvx : JpegColorConverterBase { public override bool IsAvailable => Avx.IsSupported; protected JpegColorConverterAvx(JpegColorSpace colorSpace, int precision) : base(colorSpace, precision) { } } public readonly ref struct ComponentValues { public readonly int ComponentCount; public readonly Span Component0; public readonly Span Component1; public readonly Span Component2; public readonly Span Component3; public ComponentValues(IReadOnlyList> componentBuffers, int row) { ComponentCount = componentBuffers.Count; Component0 = componentBuffers[0].DangerousGetRowSpan(row); Component1 = ((ComponentCount > 1) ? componentBuffers[1].DangerousGetRowSpan(row) : Component0); Component2 = ((ComponentCount > 2) ? componentBuffers[2].DangerousGetRowSpan(row) : Component0); Component3 = ((ComponentCount > 3) ? componentBuffers[3].DangerousGetRowSpan(row) : Span.Empty); } public ComponentValues(IReadOnlyList processors, int row) { ComponentCount = processors.Count; Component0 = processors[0].GetColorBufferRowSpan(row); Component1 = ((ComponentCount > 1) ? processors[1].GetColorBufferRowSpan(row) : Component0); Component2 = ((ComponentCount > 2) ? processors[2].GetColorBufferRowSpan(row) : Component0); Component3 = ((ComponentCount > 3) ? processors[3].GetColorBufferRowSpan(row) : Span.Empty); } internal ComponentValues(int componentCount, Span c0, Span c1, Span c2, Span c3) { ComponentCount = componentCount; Component0 = c0; Component1 = c1; Component2 = c2; Component3 = c3; } public ComponentValues Slice(int start, int length) { Span c = Component0.Slice(start, length); Span c2 = ((Component1.Length > 0) ? Component1.Slice(start, length) : Span.Empty); Span c3 = ((Component2.Length > 0) ? Component2.Slice(start, length) : Span.Empty); Span c4 = ((Component3.Length > 0) ? Component3.Slice(start, length) : Span.Empty); return new ComponentValues(ComponentCount, c, c2, c3, c4); } } internal abstract class JpegColorConverterScalar : JpegColorConverterBase { public override bool IsAvailable => true; protected JpegColorConverterScalar(JpegColorSpace colorSpace, int precision) : base(colorSpace, precision) { } } internal abstract class JpegColorConverterVector : JpegColorConverterBase { public sealed override bool IsAvailable { get { if (Vector.IsHardwareAccelerated) { return Vector.Count % 4 == 0; } return false; } } protected JpegColorConverterVector(JpegColorSpace colorSpace, int precision) : base(colorSpace, precision) { } public override void ConvertToRgbInplace(in ComponentValues values) { int length = values.Component0.Length; int num = (int)((uint)length % (uint)Vector.Count); int num2 = length - num; ComponentValues values2 = values.Slice(0, num2); ConvertCoreVectorizedInplace(in values2); if (num > 0) { values2 = values.Slice(num2, num); ConvertCoreInplace(in values2); } } protected virtual void ConvertCoreVectorizedInplace(in ComponentValues values) { throw new NotImplementedException(); } protected virtual void ConvertCoreInplace(in ComponentValues values) { throw new NotImplementedException(); } } private static readonly JpegColorConverterBase[] Converters = CreateConverters(); public abstract bool IsAvailable { get; } public JpegColorSpace ColorSpace { get; } public int Precision { get; } private float MaximumValue { get; } private float HalfValue { get; } protected JpegColorConverterBase(JpegColorSpace colorSpace, int precision) { ColorSpace = colorSpace; Precision = precision; MaximumValue = MathF.Pow(2f, precision) - 1f; HalfValue = MathF.Ceiling(MaximumValue / 2f); } public static JpegColorConverterBase GetConverter(JpegColorSpace colorSpace, int precision) { return Array.Find(Converters, (JpegColorConverterBase c) => c.ColorSpace == colorSpace && c.Precision == precision) ?? throw new Exception($"Could not find any converter for JpegColorSpace {colorSpace}!"); } public abstract void ConvertToRgbInplace(in ComponentValues values); private static JpegColorConverterBase[] CreateConverters() { List list = new List(); list.AddRange(GetYCbCrConverters(8)); list.AddRange(GetYccKConverters(8)); list.AddRange(GetCmykConverters(8)); list.AddRange(GetGrayScaleConverters(8)); list.AddRange(GetRgbConverters(8)); list.AddRange(GetYCbCrConverters(12)); list.AddRange(GetYccKConverters(12)); list.AddRange(GetCmykConverters(12)); list.AddRange(GetGrayScaleConverters(12)); list.AddRange(GetRgbConverters(12)); return list.Where((JpegColorConverterBase x) => x.IsAvailable).ToArray(); } private static IEnumerable GetYCbCrConverters(int precision) { yield return new FromYCbCrAvx(precision); yield return new FromYCbCrVector(precision); yield return new FromYCbCrScalar(precision); } private static IEnumerable GetYccKConverters(int precision) { yield return new FromYccKAvx(precision); yield return new FromYccKVector(precision); yield return new FromYccKScalar(precision); } private static IEnumerable GetCmykConverters(int precision) { yield return new FromCmykAvx(precision); yield return new FromCmykVector(precision); yield return new FromCmykScalar(precision); } private static IEnumerable GetGrayScaleConverters(int precision) { yield return new FromGrayscaleAvx(precision); yield return new FromGrayScaleVector(precision); yield return new FromGrayscaleScalar(precision); } private static IEnumerable GetRgbConverters(int precision) { yield return new FromRgbAvx(precision); yield return new FromRgbVector(precision); yield return new FromRgbScalar(precision); } } } namespace SixLabors.ImageSharp.Formats.Gif { public enum GifColorTableMode { Global, Local } public sealed class GifConfigurationModule : IConfigurationModule { public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(GifFormat.Instance, new GifEncoder()); configuration.ImageFormatsManager.SetDecoder(GifFormat.Instance, new GifDecoder()); configuration.ImageFormatsManager.AddImageFormatDetector(new GifImageFormatDetector()); } } internal static class GifConstants { public const string FileType = "GIF"; public const string FileVersion = "89a"; public const byte ExtensionIntroducer = 33; public const byte GraphicControlLabel = 249; public const byte ApplicationExtensionLabel = byte.MaxValue; public const byte ApplicationBlockSize = 11; public const string NetscapeApplicationIdentification = "NETSCAPE2.0"; public const byte NetscapeLoopingSubBlockSize = 3; public const byte CommentLabel = 254; public const int MaxCommentSubBlockLength = 255; public const byte ImageDescriptorLabel = 44; public const byte PlainTextLabel = 1; public const byte ImageLabel = 44; public const byte Terminator = 0; public const byte EndIntroducer = 59; public static readonly Encoding Encoding = Encoding.ASCII; public static readonly IEnumerable MimeTypes = new string[1] { "image/gif" }; public static readonly IEnumerable FileExtensions = new string[1] { "gif" }; internal static ReadOnlySpan MagicNumber => "GIF89a"u8; internal static ReadOnlySpan NetscapeApplicationIdentificationBytes => "NETSCAPE2.0"u8; internal static ReadOnlySpan XmpApplicationIdentificationBytes => "XMP DataXMP"u8; } public sealed class GifDecoder : IImageDecoder, IGifDecoderOptions, IImageInfoDetector { public bool IgnoreMetadata { get; set; } public FrameDecodingMode DecodingMode { get; set; } public uint MaxFrames { get; set; } = uint.MaxValue; public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return new GifDecoderCore(configuration, this).Decode(configuration, stream, cancellationToken); } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) { return Decode(configuration, stream, cancellationToken); } public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, "stream"); return new GifDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); } } internal sealed class GifDecoderCore : IImageDecoderInternals { private byte[] buffer = new byte[16]; private IMemoryOwner globalColorTable; private IMemoryOwner currentLocalColorTable; private int currentLocalColorTableSize; private Rectangle? restoreArea; private GifLogicalScreenDescriptor logicalScreenDescriptor; private GifGraphicControlExtension graphicsControlExtension; private GifImageDescriptor imageDescriptor; private readonly Configuration configuration; private readonly MemoryAllocator memoryAllocator; private readonly uint maxFrames; private readonly bool skipMetadata; private ImageMetadata metadata; private GifMetadata gifMetadata; public Configuration Configuration => configuration; public Size Dimensions => new Size(imageDescriptor.Width, imageDescriptor.Height); private MemoryAllocator MemoryAllocator => Configuration.MemoryAllocator; public GifDecoderCore(Configuration configuration, IGifDecoderOptions options) { skipMetadata = options.IgnoreMetadata; this.configuration = configuration ?? Configuration.Default; maxFrames = ((options.DecodingMode != FrameDecodingMode.All) ? 1u : options.MaxFrames); memoryAllocator = this.configuration.MemoryAllocator; } public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { uint num = 0u; Image image = null; ImageFrame previousFrame = null; try { ReadLogicalScreenDescriptorAndGlobalColorTable(stream); int num2 = stream.ReadByte(); do { switch (num2) { case 44: if (previousFrame != null && ++num == maxFrames) { goto end_IL_00ca; } ReadFrame(stream, ref image, ref previousFrame); imageDescriptor = default(GifImageDescriptor); graphicsControlExtension = default(GifGraphicControlExtension); break; case 33: switch (stream.ReadByte()) { case 249: ReadGraphicalControlExtension(stream); break; case 254: ReadComments(stream); break; case 255: ReadApplicationExtension(stream); break; case 1: SkipBlock(stream); break; } break; case 0: case 59: goto end_IL_00ca; } num2 = stream.ReadByte(); continue; end_IL_00ca: break; } while (num2 != -1); } finally { globalColorTable?.Dispose(); currentLocalColorTable?.Dispose(); } if (image == null) { GifThrowHelper.ThrowInvalidImageContentException("No data"); } return image; } public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { uint num = 0u; ImageFrameMetadata previousFrame = null; List frameMetadata = new List(); try { ReadLogicalScreenDescriptorAndGlobalColorTable(stream); int num2 = stream.ReadByte(); do { switch (num2) { case 44: if (previousFrame != null && ++num == maxFrames) { goto end_IL_00cd; } ReadFrameMetadata(stream, frameMetadata, ref previousFrame); imageDescriptor = default(GifImageDescriptor); graphicsControlExtension = default(GifGraphicControlExtension); break; case 33: switch (stream.ReadByte()) { case 249: ReadGraphicalControlExtension(stream); break; case 254: ReadComments(stream); break; case 255: ReadApplicationExtension(stream); break; case 1: SkipBlock(stream); break; } break; case 0: case 59: goto end_IL_00cd; } num2 = stream.ReadByte(); continue; end_IL_00cd: break; } while (num2 != -1); } finally { globalColorTable?.Dispose(); currentLocalColorTable?.Dispose(); } if (logicalScreenDescriptor.Width == 0 && logicalScreenDescriptor.Height == 0) { GifThrowHelper.ThrowNoHeader(); } return new ImageInfo(new PixelTypeInfo(logicalScreenDescriptor.BitsPerPixel), logicalScreenDescriptor.Width, logicalScreenDescriptor.Height, metadata); } private void ReadGraphicalControlExtension(BufferedReadStream stream) { if (stream.Read(buffer, 0, 6) != 6) { GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension"); } graphicsControlExtension = GifGraphicControlExtension.Parse(buffer); } private void ReadImageDescriptor(BufferedReadStream stream) { if (stream.Read(buffer, 0, 9) != 9) { GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor"); } imageDescriptor = GifImageDescriptor.Parse(buffer); if (imageDescriptor.Height == 0 || imageDescriptor.Width == 0) { GifThrowHelper.ThrowInvalidImageContentException("Width or height should not be 0"); } } private void ReadLogicalScreenDescriptor(BufferedReadStream stream) { if (stream.Read(buffer, 0, 7) != 7) { GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the logical screen descriptor"); } logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(buffer); } private void ReadApplicationExtension(BufferedReadStream stream) { int num = stream.ReadByte(); long position = stream.Position; if (num == 11) { stream.Read(buffer, 0, 11); if (buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes) && !skipMetadata) { GifXmpApplicationExtension gifXmpApplicationExtension = GifXmpApplicationExtension.Read(stream, memoryAllocator); if (gifXmpApplicationExtension.Data.Length != 0) { metadata.XmpProfile = new XmpProfile(gifXmpApplicationExtension.Data); return; } stream.Position = position; SkipBlock(stream, num); return; } int num2 = stream.ReadByte(); if (num2 == 3) { stream.Read(buffer, 0, 3); gifMetadata.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(buffer.AsSpan(1)).RepeatCount; stream.Skip(1); } else { SkipBlock(stream, num2); } } else { SkipBlock(stream, num); } } private static void SkipBlock(BufferedReadStream stream, int blockSize = 0) { if (blockSize > 0) { stream.Skip(blockSize); } int count; while ((count = stream.ReadByte()) > 0) { stream.Skip(count); } } private void ReadComments(BufferedReadStream stream) { StringBuilder stringBuilder = new StringBuilder(); int num; while ((num = stream.ReadByte()) != 0) { if (num > 255) { GifThrowHelper.ThrowInvalidImageContentException($"Gif comment length '{num}' exceeds max '{255}' of a comment data block"); } if (skipMetadata) { stream.Seek(num, SeekOrigin.Current); continue; } using IMemoryOwner memoryOwner = memoryAllocator.Allocate(num); Span span = memoryOwner.GetSpan(); stream.Read(span); string value = GifConstants.Encoding.GetString(span); stringBuilder.Append(value); } if (stringBuilder.Length > 0) { gifMetadata.Comments.Add(stringBuilder.ToString()); } } private void ReadFrame(BufferedReadStream stream, ref Image? image, ref ImageFrame? previousFrame) where TPixel : unmanaged, IPixel { ReadImageDescriptor(stream); bool localColorTableFlag = imageDescriptor.LocalColorTableFlag; Span span; if (localColorTableFlag) { int length = (currentLocalColorTableSize = imageDescriptor.LocalColorTableSize * 3); if (currentLocalColorTable == null) { currentLocalColorTable = configuration.MemoryAllocator.Allocate(768, AllocationOptions.Clean); } span = currentLocalColorTable.GetSpan(); stream.Read(span.Slice(0, length)); } Span span2 = default(Span); if (localColorTableFlag) { span = currentLocalColorTable.GetSpan(); span2 = span.Slice(0, currentLocalColorTableSize); } else if (globalColorTable != null) { span2 = globalColorTable.GetSpan(); } ReadOnlySpan colorTable = MemoryMarshal.Cast(span2); ReadFrameColors(stream, ref image, ref previousFrame, colorTable, in imageDescriptor); SkipBlock(stream); } private void ReadFrameColors(BufferedReadStream stream, ref Image? image, ref ImageFrame? previousFrame, ReadOnlySpan colorTable, in GifImageDescriptor descriptor) where TPixel : unmanaged, IPixel { int width = logicalScreenDescriptor.Width; int height = logicalScreenDescriptor.Height; bool transparencyFlag = graphicsControlExtension.TransparencyFlag; ImageFrame imageFrame = null; ImageFrame imageFrame2 = null; ImageFrame imageFrame3; if (previousFrame == null) { if (!transparencyFlag) { image = new Image(configuration, width, height, Color.Black.ToPixel(), metadata); } else { image = new Image(configuration, width, height, metadata); } SetFrameMetadata(image.Frames.RootFrame.Metadata); imageFrame3 = image.Frames.RootFrame; } else { if (graphicsControlExtension.DisposalMethod == GifDisposalMethod.RestoreToPrevious) { imageFrame = previousFrame; } imageFrame2 = image.Frames.AddFrame(previousFrame); SetFrameMetadata(imageFrame2.Metadata); imageFrame3 = imageFrame2; RestoreToBackground(imageFrame3); } if (colorTable.Length == 0) { return; } int num = 0; int num2 = 8; int num3 = 0; int top = descriptor.Top; int num4 = top + descriptor.Height; int left = descriptor.Left; int num5 = left + descriptor.Width; byte transparencyIndex = graphicsControlExtension.TransparencyIndex; int num6 = colorTable.Length - 1; using IMemoryOwner memoryOwner = memoryAllocator.Allocate(descriptor.Width); Span span = memoryOwner.Memory.Span; ref byte reference = ref MemoryMarshal.GetReference(span); int minCodeSize = stream.ReadByte(); if (LzwDecoder.IsValidMinCodeSize(minCodeSize)) { using LzwDecoder lzwDecoder = new LzwDecoder(configuration.MemoryAllocator, stream, minCodeSize); for (int i = top; i < num4 && i < height; i++) { int y; if (descriptor.InterlaceFlag) { if (num3 >= descriptor.Height) { num++; switch (num) { case 1: num3 = 4; break; case 2: num3 = 2; num2 = 4; break; case 3: num3 = 1; num2 = 2; break; } } y = Math.Min(num3 + descriptor.Top, image.Height); num3 += num2; } else { y = i; } lzwDecoder.DecodePixelRow(span); ref TPixel reference2 = ref MemoryMarshal.GetReference(imageFrame3.PixelBuffer.DangerousGetRowSpan(y)); if (!transparencyFlag) { for (int j = left; j < num5 && j < width; j++) { int index = Numerics.Clamp(Unsafe.Add(ref reference, j - left), 0, num6); ref TPixel reference3 = ref Unsafe.Add(ref reference2, j); Rgb24 source = colorTable[index]; reference3.FromRgb24(source); } continue; } for (int k = left; k < num5 && k < width; k++) { int num7 = Unsafe.Add(ref reference, k - left); if (num7 <= num6 && num7 != transparencyIndex) { ref TPixel reference4 = ref Unsafe.Add(ref reference2, k); Rgb24 source2 = colorTable[num7]; reference4.FromRgb24(source2); } } } } if (imageFrame != null) { previousFrame = imageFrame; return; } previousFrame = imageFrame2 ?? image.Frames.RootFrame; if (graphicsControlExtension.DisposalMethod == GifDisposalMethod.RestoreToBackground) { restoreArea = new Rectangle(descriptor.Left, descriptor.Top, descriptor.Width, descriptor.Height); } } private void ReadFrameMetadata(BufferedReadStream stream, List frameMetadata, ref ImageFrameMetadata? previousFrame) { ReadImageDescriptor(stream); if (imageDescriptor.LocalColorTableFlag) { int length = (currentLocalColorTableSize = imageDescriptor.LocalColorTableSize * 3); if (currentLocalColorTable == null) { currentLocalColorTable = configuration.MemoryAllocator.Allocate(768, AllocationOptions.Clean); } stream.Read(currentLocalColorTable.GetSpan().Slice(0, length)); } int minCodeSize = stream.ReadByte(); if (LzwDecoder.IsValidMinCodeSize(minCodeSize)) { using LzwDecoder lzwDecoder = new LzwDecoder(configuration.MemoryAllocator, stream, minCodeSize); lzwDecoder.SkipIndices(imageDescriptor.Width * imageDescriptor.Height); } ImageFrameMetadata imageFrameMetadata = new ImageFrameMetadata(); frameMetadata.Add(imageFrameMetadata); SetFrameMetadata(imageFrameMetadata); previousFrame = imageFrameMetadata; SkipBlock(stream); } private void RestoreToBackground(ImageFrame frame) where TPixel : unmanaged, IPixel { Rectangle? rectangle = restoreArea; if (rectangle.HasValue) { Rectangle rectangle2 = Rectangle.Intersect(frame.Bounds(), restoreArea.Value); frame.PixelBuffer.GetRegion(rectangle2).Clear(); restoreArea = null; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SetFrameMetadata(ImageFrameMetadata meta) { GifFrameMetadata gifFrameMetadata = meta.GetGifMetadata(); if (graphicsControlExtension.DelayTime > 0) { gifFrameMetadata.FrameDelay = graphicsControlExtension.DelayTime; } if (logicalScreenDescriptor.GlobalColorTableFlag && logicalScreenDescriptor.GlobalColorTableSize > 0) { gifFrameMetadata.ColorTableLength = logicalScreenDescriptor.GlobalColorTableSize; } else if (imageDescriptor.LocalColorTableFlag && imageDescriptor.LocalColorTableSize > 0) { gifFrameMetadata.ColorTableLength = imageDescriptor.LocalColorTableSize; } gifFrameMetadata.DisposalMethod = graphicsControlExtension.DisposalMethod; } private void ReadLogicalScreenDescriptorAndGlobalColorTable(BufferedReadStream stream) { stream.Skip(6); ReadLogicalScreenDescriptor(stream); ImageMetadata imageMetadata = new ImageMetadata(); if (logicalScreenDescriptor.PixelAspectRatio > 0) { imageMetadata.ResolutionUnits = PixelResolutionUnit.AspectRatio; float num = (float)(logicalScreenDescriptor.PixelAspectRatio + 15) / 64f; if (num > 1f) { imageMetadata.HorizontalResolution = num; imageMetadata.VerticalResolution = 1.0; } else { imageMetadata.VerticalResolution = 1f / num; imageMetadata.HorizontalResolution = 1.0; } } metadata = imageMetadata; gifMetadata = imageMetadata.GetGifMetadata(); gifMetadata.ColorTableMode = ((!logicalScreenDescriptor.GlobalColorTableFlag) ? GifColorTableMode.Local : GifColorTableMode.Global); if (logicalScreenDescriptor.GlobalColorTableFlag) { int num2 = logicalScreenDescriptor.GlobalColorTableSize * 3; if (num2 > 0) { globalColorTable = memoryAllocator.Allocate(num2, AllocationOptions.Clean); Span span = globalColorTable.GetSpan(); stream.Read(span); } } } } public enum GifDisposalMethod { Unspecified, NotDispose, RestoreToBackground, RestoreToPrevious } public sealed class GifEncoder : IImageEncoder, IGifEncoderOptions { public IQuantizer Quantizer { get; set; } = KnownQuantizers.Octree; public GifColorTableMode? ColorTableMode { get; set; } public IPixelSamplingStrategy GlobalPixelSamplingStrategy { get; set; } = new DefaultPixelSamplingStrategy(); public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { new GifEncoderCore(image.GetConfiguration(), this).Encode(image, stream); } public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return new GifEncoderCore(image.GetConfiguration(), this).EncodeAsync(image, stream, cancellationToken); } } internal sealed class GifEncoderCore : IImageEncoderInternals { private readonly MemoryAllocator memoryAllocator; private readonly Configuration configuration; private readonly byte[] buffer = new byte[20]; private readonly IQuantizer quantizer; private GifColorTableMode? colorTableMode; private int bitDepth; private readonly IPixelSamplingStrategy pixelSamplingStrategy; public GifEncoderCore(Configuration configuration, IGifEncoderOptions options) { this.configuration = configuration; memoryAllocator = configuration.MemoryAllocator; quantizer = options.Quantizer; colorTableMode = options.ColorTableMode; pixelSamplingStrategy = options.GlobalPixelSamplingStrategy; } public void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); ImageMetadata metadata = image.Metadata; GifMetadata gifMetadata = metadata.GetGifMetadata(); GifColorTableMode valueOrDefault = colorTableMode.GetValueOrDefault(); if (!colorTableMode.HasValue) { valueOrDefault = gifMetadata.ColorTableMode; colorTableMode = valueOrDefault; } bool flag = colorTableMode == GifColorTableMode.Global; IndexedImageFrame indexedImageFrame; using (IQuantizer quantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration)) { if (flag) { quantizer.BuildPalette(pixelSamplingStrategy, image); indexedImageFrame = quantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds()); } else { indexedImageFrame = quantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); } } bitDepth = ColorNumerics.GetBitsNeededForColorDepth(indexedImageFrame.Palette.Length); WriteHeader(stream); int transparentIndex = GetTransparentIndex(indexedImageFrame); WriteLogicalScreenDescriptor(metadata, image.Width, image.Height, transparentIndex, flag, stream); if (flag) { WriteColorTable(indexedImageFrame, stream); } WriteComments(gifMetadata, stream); XmpProfile xmpProfile = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile; WriteApplicationExtensions(stream, image.Frames.Count, gifMetadata.RepeatCount, xmpProfile); if (flag) { EncodeGlobal(image, indexedImageFrame, transparentIndex, stream); } else { EncodeLocal(image, indexedImageFrame, stream); } indexedImageFrame.Dispose(); stream.WriteByte(59); } private void EncodeGlobal(Image image, IndexedImageFrame quantized, int transparencyIndex, Stream stream) where TPixel : unmanaged, IPixel { PaletteQuantizer paletteQuantizer = default(PaletteQuantizer); bool flag = false; for (int i = 0; i < image.Frames.Count; i++) { ImageFrame imageFrame = image.Frames[i]; GifFrameMetadata gifMetadata = imageFrame.Metadata.GetGifMetadata(); WriteGraphicalControlExtension(gifMetadata, transparencyIndex, stream); WriteImageDescriptor(imageFrame, hasColorTable: false, stream); if (i == 0) { WriteImageData(quantized, stream); continue; } if (!flag) { flag = true; paletteQuantizer = new PaletteQuantizer(configuration, quantizer.Options, quantized.Palette); } using IndexedImageFrame image2 = paletteQuantizer.QuantizeFrame(imageFrame, imageFrame.Bounds()); WriteImageData(image2, stream); } paletteQuantizer.Dispose(); } private void EncodeLocal(Image image, IndexedImageFrame quantized, Stream stream) where TPixel : unmanaged, IPixel { ImageFrame imageFrame = null; GifFrameMetadata gifFrameMetadata = null; for (int i = 0; i < image.Frames.Count; i++) { ImageFrame imageFrame2 = image.Frames[i]; GifFrameMetadata gifMetadata = imageFrame2.Metadata.GetGifMetadata(); if (quantized == null) { if (imageFrame != null && gifFrameMetadata.ColorTableLength != gifMetadata.ColorTableLength && gifMetadata.ColorTableLength > 0) { QuantizerOptions options = new QuantizerOptions { Dither = this.quantizer.Options.Dither, DitherScale = this.quantizer.Options.DitherScale, MaxColors = gifMetadata.ColorTableLength }; using IQuantizer quantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration, options); quantized = quantizer.BuildPaletteAndQuantizeFrame(imageFrame2, imageFrame2.Bounds()); } else { using IQuantizer quantizer2 = this.quantizer.CreatePixelSpecificQuantizer(configuration); quantized = quantizer2.BuildPaletteAndQuantizeFrame(imageFrame2, imageFrame2.Bounds()); } } bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length); WriteGraphicalControlExtension(gifMetadata, GetTransparentIndex(quantized), stream); WriteImageDescriptor(imageFrame2, hasColorTable: true, stream); WriteColorTable(quantized, stream); WriteImageData(quantized, stream); quantized.Dispose(); quantized = null; imageFrame = imageFrame2; gifFrameMetadata = gifMetadata; } } private int GetTransparentIndex(IndexedImageFrame quantized) where TPixel : unmanaged, IPixel { int result = -1; ReadOnlySpan span = quantized.Palette.Span; using IMemoryOwner memoryOwner = quantized.Configuration.MemoryAllocator.Allocate(span.Length); Span span2 = memoryOwner.GetSpan(); PixelOperations.Instance.ToRgba32(quantized.Configuration, span, span2); ref Rgba32 reference = ref MemoryMarshal.GetReference(span2); for (int num = span2.Length - 1; num >= 0; num--) { if (Unsafe.Add(ref reference, num).Equals(default(Rgba32))) { result = num; } } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteHeader(Stream stream) { stream.Write(GifConstants.MagicNumber); } private void WriteLogicalScreenDescriptor(ImageMetadata metadata, int width, int height, int transparencyIndex, bool useGlobalTable, Stream stream) { byte packedValue = GifLogicalScreenDescriptor.GetPackedValue(useGlobalTable, bitDepth - 1, sortFlag: false, bitDepth - 1); byte pixelAspectRatio = 0; if (metadata.ResolutionUnits == PixelResolutionUnit.AspectRatio) { double horizontalResolution = metadata.HorizontalResolution; double verticalResolution = metadata.VerticalResolution; if (horizontalResolution != verticalResolution) { pixelAspectRatio = ((!(horizontalResolution > verticalResolution)) ? ((byte)(1.0 / verticalResolution * 64.0 - 15.0)) : ((byte)(horizontalResolution * 64.0 - 15.0))); } } new GifLogicalScreenDescriptor((ushort)width, (ushort)height, packedValue, (byte)transparencyIndex, pixelAspectRatio).WriteTo(buffer); stream.Write(buffer, 0, 7); } private void WriteApplicationExtensions(Stream stream, int frameCount, ushort repeatCount, XmpProfile xmpProfile) { if (frameCount > 1 && repeatCount != 1) { GifNetscapeLoopingApplicationExtension extension = new GifNetscapeLoopingApplicationExtension(repeatCount); WriteExtension(extension, stream); } if (xmpProfile != null) { GifXmpApplicationExtension extension2 = new GifXmpApplicationExtension(xmpProfile.Data); WriteExtension(extension2, stream); } } private void WriteComments(GifMetadata metadata, Stream stream) { if (metadata.Comments.Count == 0) { return; } for (int i = 0; i < metadata.Comments.Count; i++) { string text = metadata.Comments[i]; buffer[0] = 33; buffer[1] = 254; stream.Write(buffer, 0, 2); ReadOnlySpan commentSpan = text.AsSpan(); int j; for (j = 0; j <= text.Length - 255; j += 255) { WriteCommentSubBlock(stream, commentSpan, j, 255); } if (j < text.Length) { int length = text.Length - j; WriteCommentSubBlock(stream, commentSpan, j, length); } stream.WriteByte(0); } } private static void WriteCommentSubBlock(Stream stream, ReadOnlySpan commentSpan, int idx, int length) { string s = commentSpan.Slice(idx, length).ToString(); byte[] bytes = GifConstants.Encoding.GetBytes(s); stream.WriteByte((byte)length); stream.Write(bytes, 0, length); } private void WriteGraphicalControlExtension(GifFrameMetadata metadata, int transparencyIndex, Stream stream) { byte packedValue = GifGraphicControlExtension.GetPackedValue(metadata.DisposalMethod, userInputFlag: false, transparencyIndex > -1); GifGraphicControlExtension extension = new GifGraphicControlExtension(packedValue, (ushort)metadata.FrameDelay, (byte)transparencyIndex); WriteExtension(extension, stream); } private void WriteExtension(TGifExtension extension, Stream stream) where TGifExtension : struct, IGifExtension { IMemoryOwner memoryOwner = null; int contentLength = extension.ContentLength; if (contentLength != 0) { Span span; if (contentLength > buffer.Length - 3) { memoryOwner = memoryAllocator.Allocate(contentLength + 3); span = memoryOwner.GetSpan(); } else { span = buffer; } span[0] = 33; span[1] = extension.Label; extension.WriteTo(span.Slice(2)); span[contentLength + 2] = 0; stream.Write(span, 0, contentLength + 3); memoryOwner?.Dispose(); } } private void WriteImageDescriptor(ImageFrame image, bool hasColorTable, Stream stream) where TPixel : unmanaged, IPixel { byte packedValue = GifImageDescriptor.GetPackedValue(hasColorTable, interfaceFlag: false, sortFlag: false, bitDepth - 1); new GifImageDescriptor(0, 0, (ushort)image.Width, (ushort)image.Height, packedValue).WriteTo(buffer); stream.Write(buffer, 0, 10); } private void WriteColorTable(IndexedImageFrame image, Stream stream) where TPixel : unmanaged, IPixel { int length = ColorNumerics.GetColorCountForBitDepth(bitDepth) * Unsafe.SizeOf(); using IMemoryOwner memoryOwner = memoryAllocator.Allocate(length, AllocationOptions.Clean); Span span = memoryOwner.GetSpan(); PixelOperations instance = PixelOperations.Instance; Configuration obj = configuration; ReadOnlyMemory palette = image.Palette; ReadOnlySpan span2 = palette.Span; palette = image.Palette; instance.ToRgb24Bytes(obj, span2, span, palette.Length); stream.Write(span); } private void WriteImageData(IndexedImageFrame image, Stream stream) where TPixel : unmanaged, IPixel { using LzwEncoder lzwEncoder = new LzwEncoder(memoryAllocator, (byte)bitDepth); lzwEncoder.Encode(((IPixelSource)image).PixelBuffer, stream); } } public sealed class GifFormat : IImageFormat, IImageFormat, IImageFormat { public static GifFormat Instance { get; } = new GifFormat(); public string Name => "GIF"; public string DefaultMimeType => "image/gif"; public IEnumerable MimeTypes => GifConstants.MimeTypes; public IEnumerable FileExtensions => GifConstants.FileExtensions; private GifFormat() { } public GifMetadata CreateDefaultFormatMetadata() { return new GifMetadata(); } public GifFrameMetadata CreateDefaultFormatFrameMetadata() { return new GifFrameMetadata(); } } public class GifFrameMetadata : IDeepCloneable { public int ColorTableLength { get; set; } public int FrameDelay { get; set; } public GifDisposalMethod DisposalMethod { get; set; } public GifFrameMetadata() { } private GifFrameMetadata(GifFrameMetadata other) { ColorTableLength = other.ColorTableLength; FrameDelay = other.FrameDelay; DisposalMethod = other.DisposalMethod; } public IDeepCloneable DeepClone() { return new GifFrameMetadata(this); } } public sealed class GifImageFormatDetector : IImageFormatDetector { public int HeaderSize => 6; public IImageFormat DetectFormat(ReadOnlySpan header) { if (!IsSupportedFileFormat(header)) { return null; } return GifFormat.Instance; } private bool IsSupportedFileFormat(ReadOnlySpan header) { if (header.Length >= HeaderSize && header[0] == 71 && header[1] == 73 && header[2] == 70 && header[3] == 56 && (header[4] == 57 || header[4] == 55)) { return header[5] == 97; } return false; } } public class GifMetadata : IDeepCloneable { public ushort RepeatCount { get; set; } = 1; public GifColorTableMode ColorTableMode { get; set; } public int GlobalColorTableLength { get; set; } public IList Comments { get; set; } = new List(); public GifMetadata() { } private GifMetadata(GifMetadata other) { RepeatCount = other.RepeatCount; ColorTableMode = other.ColorTableMode; GlobalColorTableLength = other.GlobalColorTableLength; for (int i = 0; i < other.Comments.Count; i++) { Comments.Add(other.Comments[i]); } } public IDeepCloneable DeepClone() { return new GifMetadata(this); } } internal static class GifThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidImageContentException(string errorMessage) { throw new InvalidImageContentException(errorMessage); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidImageContentException(string errorMessage, Exception innerException) { throw new InvalidImageContentException(errorMessage, innerException); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNoHeader() { throw new InvalidImageContentException("Gif image does not contain a Logical Screen Descriptor."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNoData() { throw new InvalidImageContentException("Unable to read Gif image data"); } } internal interface IGifDecoderOptions { bool IgnoreMetadata { get; } FrameDecodingMode DecodingMode { get; } uint MaxFrames { get; set; } } internal interface IGifEncoderOptions { IQuantizer Quantizer { get; } GifColorTableMode? ColorTableMode { get; } IPixelSamplingStrategy GlobalPixelSamplingStrategy { get; } } internal sealed class LzwDecoder : IDisposable { private const int MaxStackSize = 4096; private const int MaximumLzwBits = 12; private const int NullCode = -1; private readonly BufferedReadStream stream; private readonly IMemoryOwner prefix; private readonly IMemoryOwner suffix; private readonly IMemoryOwner scratchBuffer; private readonly IMemoryOwner pixelStack; private readonly int minCodeSize; private readonly int clearCode; private readonly int endCode; private int code; private int codeSize; private int codeMask; private int availableCode; private int oldCode = -1; private int bits; private int top; private int count; private int bufferIndex; private int data; private int first; public LzwDecoder(MemoryAllocator memoryAllocator, BufferedReadStream stream, int minCodeSize) { this.stream = stream ?? throw new ArgumentNullException("stream"); prefix = memoryAllocator.Allocate(4096, AllocationOptions.Clean); suffix = memoryAllocator.Allocate(4096, AllocationOptions.Clean); pixelStack = memoryAllocator.Allocate(4097, AllocationOptions.Clean); scratchBuffer = memoryAllocator.Allocate(255); this.minCodeSize = minCodeSize; clearCode = 1 << minCodeSize; codeSize = minCodeSize + 1; codeMask = (1 << codeSize) - 1; endCode = clearCode + 1; availableCode = clearCode + 2; ref int reference = ref MemoryMarshal.GetReference(suffix.GetSpan()); for (code = 0; code < clearCode; code++) { Unsafe.Add(ref reference, code) = (byte)code; } } public static bool IsValidMinCodeSize(int minCodeSize) { int num = 1 << minCodeSize; if (minCodeSize < 2 || minCodeSize > 12 || num > 4096) { return false; } return true; } public void DecodePixelRow(Span indices) { indices.Clear(); ref byte reference = ref MemoryMarshal.GetReference(indices); ref int reference2 = ref MemoryMarshal.GetReference(prefix.GetSpan()); ref int reference3 = ref MemoryMarshal.GetReference(suffix.GetSpan()); ref int reference4 = ref MemoryMarshal.GetReference(pixelStack.GetSpan()); Span span = scratchBuffer.GetSpan(); int num = 0; int num2 = 0; while (num2 < indices.Length) { if (top == 0) { if (bits < codeSize) { if (count == 0) { count = ReadBlock(span); if (count == 0) { break; } bufferIndex = 0; } data += span[bufferIndex] << bits; bits += 8; bufferIndex++; count--; continue; } code = data & codeMask; data >>= codeSize; bits -= codeSize; if (code > availableCode || code == endCode) { break; } if (code == clearCode) { codeSize = minCodeSize + 1; codeMask = (1 << codeSize) - 1; availableCode = clearCode + 2; oldCode = -1; continue; } if (oldCode == -1) { Unsafe.Add(ref reference4, top++) = Unsafe.Add(ref reference3, code); oldCode = code; first = code; continue; } int num3 = code; if (code == availableCode) { Unsafe.Add(ref reference4, top++) = (byte)first; code = oldCode; } while (code > clearCode) { Unsafe.Add(ref reference4, top++) = Unsafe.Add(ref reference3, code); code = Unsafe.Add(ref reference2, code); } int num4 = (first = Unsafe.Add(ref reference3, code)); Unsafe.Add(ref reference4, top++) = num4; if (availableCode < 4096) { Unsafe.Add(ref reference2, availableCode) = oldCode; Unsafe.Add(ref reference3, availableCode) = first; availableCode++; if (availableCode == codeMask + 1 && availableCode < 4096) { codeSize++; codeMask = (1 << codeSize) - 1; } } oldCode = num3; } top--; num2++; Unsafe.Add(ref reference, num++) = (byte)Unsafe.Add(ref reference4, top); } } public void SkipIndices(int length) { ref int reference = ref MemoryMarshal.GetReference(prefix.GetSpan()); ref int reference2 = ref MemoryMarshal.GetReference(suffix.GetSpan()); ref int reference3 = ref MemoryMarshal.GetReference(pixelStack.GetSpan()); Span span = scratchBuffer.GetSpan(); int num = 0; while (num < length) { if (top == 0) { if (bits < codeSize) { if (count == 0) { count = ReadBlock(span); if (count == 0) { break; } bufferIndex = 0; } data += span[bufferIndex] << bits; bits += 8; bufferIndex++; count--; continue; } code = data & codeMask; data >>= codeSize; bits -= codeSize; if (code > availableCode || code == endCode) { break; } if (code == clearCode) { codeSize = minCodeSize + 1; codeMask = (1 << codeSize) - 1; availableCode = clearCode + 2; oldCode = -1; continue; } if (oldCode == -1) { Unsafe.Add(ref reference3, top++) = Unsafe.Add(ref reference2, code); oldCode = code; first = code; continue; } int num2 = code; if (code == availableCode) { Unsafe.Add(ref reference3, top++) = (byte)first; code = oldCode; } while (code > clearCode) { Unsafe.Add(ref reference3, top++) = Unsafe.Add(ref reference2, code); code = Unsafe.Add(ref reference, code); } int num3 = (first = Unsafe.Add(ref reference2, code)); Unsafe.Add(ref reference3, top++) = num3; if (availableCode < 4096) { Unsafe.Add(ref reference, availableCode) = oldCode; Unsafe.Add(ref reference2, availableCode) = first; availableCode++; if (availableCode == codeMask + 1 && availableCode < 4096) { codeSize++; codeMask = (1 << codeSize) - 1; } } oldCode = num2; } top--; num++; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int ReadBlock(Span buffer) { int num = stream.ReadByte(); if (num < 1) { return 0; } if (stream.Read(buffer, 0, num) == num) { return num; } return 0; } public void Dispose() { prefix.Dispose(); suffix.Dispose(); pixelStack.Dispose(); scratchBuffer.Dispose(); } } internal sealed class LzwEncoder : IDisposable { private const int HashSize = 5003; private const int HashShift = 4; private static readonly int[] Masks = new int[17] { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535 }; private const int MaxBits = 12; private const int MaxMaxCode = 4096; private readonly int initialCodeSize; private readonly IMemoryOwner hashTable; private readonly IMemoryOwner codeTable; private readonly byte[] accumulators = new byte[256]; private int bitCount; private int maxCode; private int freeEntry; private bool clearFlag; private int globalInitialBits; private int clearCode; private int eofCode; private int currentAccumulator; private int currentBits; private int accumulatorCount; public LzwEncoder(MemoryAllocator memoryAllocator, int colorDepth) { initialCodeSize = Math.Max(2, colorDepth); hashTable = memoryAllocator.Allocate(5003, AllocationOptions.Clean); codeTable = memoryAllocator.Allocate(5003, AllocationOptions.Clean); } public void Encode(Buffer2D indexedPixels, Stream stream) { stream.WriteByte((byte)initialCodeSize); Compress(indexedPixels, initialCodeSize + 1, stream); stream.WriteByte(0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetMaxcode(int bitCount) { return (1 << bitCount) - 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream) { Unsafe.Add(ref accumulatorsRef, accumulatorCount++) = c; if (accumulatorCount >= 254) { FlushPacket(stream); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ClearBlock(Stream stream) { ResetCodeTable(); freeEntry = clearCode + 2; clearFlag = true; Output(clearCode, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ResetCodeTable() { hashTable.GetSpan().Fill(-1); } private void Compress(Buffer2D indexedPixels, int initialBits, Stream stream) { globalInitialBits = initialBits; clearFlag = false; bitCount = globalInitialBits; maxCode = GetMaxcode(bitCount); clearCode = 1 << initialBits - 1; eofCode = clearCode + 1; freeEntry = clearCode + 2; accumulatorCount = 0; ResetCodeTable(); Output(clearCode, stream); ref int reference = ref MemoryMarshal.GetReference(hashTable.GetSpan()); ref int reference2 = ref MemoryMarshal.GetReference(codeTable.GetSpan()); int num = indexedPixels[0, 0]; for (int i = 0; i < indexedPixels.Height; i++) { ref byte reference3 = ref MemoryMarshal.GetReference(indexedPixels.DangerousGetRowSpan(i)); for (int j = ((i == 0) ? 1 : 0); j < indexedPixels.Width; j++) { int num2 = Unsafe.Add(ref reference3, j); int num3 = (num2 << 12) + num; int num4 = (num2 << 4) ^ num; if (Unsafe.Add(ref reference, num4) == num3) { num = Unsafe.Add(ref reference2, num4); continue; } if (Unsafe.Add(ref reference, num4) >= 0) { int num5 = 1; if (num4 != 0) { num5 = 5003 - num4; } do { if ((num4 -= num5) < 0) { num4 += 5003; } if (Unsafe.Add(ref reference, num4) == num3) { num = Unsafe.Add(ref reference2, num4); break; } } while (Unsafe.Add(ref reference, num4) >= 0); if (Unsafe.Add(ref reference, num4) == num3) { continue; } } Output(num, stream); num = num2; if (freeEntry < 4096) { Unsafe.Add(ref reference2, num4) = freeEntry++; Unsafe.Add(ref reference, num4) = num3; } else { ClearBlock(stream); } } } Output(num, stream); Output(eofCode, stream); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void FlushPacket(Stream outStream) { outStream.WriteByte((byte)accumulatorCount); outStream.Write(accumulators, 0, accumulatorCount); accumulatorCount = 0; } private void Output(int code, Stream outs) { ref byte reference = ref MemoryMarshal.GetReference(accumulators.AsSpan()); currentAccumulator &= Masks[currentBits]; if (currentBits > 0) { currentAccumulator |= code << currentBits; } else { currentAccumulator = code; } currentBits += bitCount; while (currentBits >= 8) { AddCharacter((byte)(currentAccumulator & 0xFF), ref reference, outs); currentAccumulator >>= 8; currentBits -= 8; } if (freeEntry > maxCode || clearFlag) { if (clearFlag) { maxCode = GetMaxcode(bitCount = globalInitialBits); clearFlag = false; } else { bitCount++; maxCode = ((bitCount == 12) ? 4096 : GetMaxcode(bitCount)); } } if (code == eofCode) { while (currentBits > 0) { AddCharacter((byte)(currentAccumulator & 0xFF), ref reference, outs); currentAccumulator >>= 8; currentBits -= 8; } if (accumulatorCount > 0) { FlushPacket(outs); } } } public void Dispose() { hashTable?.Dispose(); codeTable?.Dispose(); } } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct GifGraphicControlExtension : IGifExtension { public byte BlockSize { get; } public byte Packed { get; } public ushort DelayTime { get; } public byte TransparencyIndex { get; } public GifDisposalMethod DisposalMethod => (GifDisposalMethod)((Packed & 0x1C) >> 2); public bool TransparencyFlag => (Packed & 1) == 1; byte IGifExtension.Label => 249; int IGifExtension.ContentLength => 5; public GifGraphicControlExtension(byte packed, ushort delayTime, byte transparencyIndex) { BlockSize = 4; Packed = packed; DelayTime = delayTime; TransparencyIndex = transparencyIndex; } public int WriteTo(Span buffer) { Unsafe.As(ref MemoryMarshal.GetReference(buffer)) = this; return ((IGifExtension)this).ContentLength; } public static GifGraphicControlExtension Parse(ReadOnlySpan buffer) { return MemoryMarshal.Cast(buffer)[0]; } public static byte GetPackedValue(GifDisposalMethod disposalMethod, bool userInputFlag = false, bool transparencyFlag = false) { byte b = 0; b |= (byte)((int)disposalMethod << 2); if (userInputFlag) { b |= 2; } if (transparencyFlag) { b |= 1; } return b; } } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct GifImageDescriptor { public const int Size = 10; public ushort Left { get; } public ushort Top { get; } public ushort Width { get; } public ushort Height { get; } public byte Packed { get; } public bool LocalColorTableFlag => (Packed & 0x80) >> 7 == 1; public int LocalColorTableSize => 2 << (Packed & 7); public bool InterlaceFlag => (Packed & 0x40) >> 6 == 1; public GifImageDescriptor(ushort left, ushort top, ushort width, ushort height, byte packed) { Left = left; Top = top; Width = width; Height = height; Packed = packed; } public void WriteTo(Span buffer) { buffer[0] = 44; Unsafe.As(ref MemoryMarshal.GetReference(buffer.Slice(1))) = this; } public static GifImageDescriptor Parse(ReadOnlySpan buffer) { return MemoryMarshal.Cast(buffer)[0]; } public static byte GetPackedValue(bool localColorTableFlag, bool interfaceFlag, bool sortFlag, int localColorTableSize) { byte b = 0; if (localColorTableFlag) { b |= 0x80; } if (interfaceFlag) { b |= 0x40; } if (sortFlag) { b |= 0x20; } return (byte)(b | (byte)localColorTableSize); } } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct GifLogicalScreenDescriptor { public const int Size = 7; public ushort Width { get; } public ushort Height { get; } public byte Packed { get; } public byte BackgroundColorIndex { get; } public byte PixelAspectRatio { get; } public bool GlobalColorTableFlag => (Packed & 0x80) >> 7 == 1; public int GlobalColorTableSize => 2 << (Packed & 7); public int BitsPerPixel => (Packed & 7) + 1; public GifLogicalScreenDescriptor(ushort width, ushort height, byte packed, byte backgroundColorIndex, byte pixelAspectRatio = 0) { Width = width; Height = height; Packed = packed; BackgroundColorIndex = backgroundColorIndex; PixelAspectRatio = pixelAspectRatio; } public void WriteTo(Span buffer) { Unsafe.As(ref MemoryMarshal.GetReference(buffer)) = this; } public static GifLogicalScreenDescriptor Parse(ReadOnlySpan buffer) { GifLogicalScreenDescriptor result = MemoryMarshal.Cast(buffer)[0]; if (result.GlobalColorTableSize > 1020) { throw new ImageFormatException($"Invalid gif colormap size '{result.GlobalColorTableSize}'"); } return result; } public static byte GetPackedValue(bool globalColorTableFlag, int colorResolution, bool sortFlag, int globalColorTableSize) { byte b = 0; if (globalColorTableFlag) { b |= 0x80; } b |= (byte)(colorResolution << 4); if (sortFlag) { b |= 8; } return (byte)(b | (byte)globalColorTableSize); } } internal readonly struct GifNetscapeLoopingApplicationExtension : IGifExtension { public byte Label => byte.MaxValue; public int ContentLength => 16; public ushort RepeatCount { get; } public GifNetscapeLoopingApplicationExtension(ushort repeatCount) { RepeatCount = repeatCount; } public static GifNetscapeLoopingApplicationExtension Parse(ReadOnlySpan buffer) { return new GifNetscapeLoopingApplicationExtension(BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(0, 2))); } public int WriteTo(Span buffer) { buffer[0] = 11; GifConstants.NetscapeApplicationIdentificationBytes.CopyTo(buffer.Slice(1, 11)); buffer[12] = 3; buffer[13] = 1; BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(14, 2), RepeatCount); return ContentLength; } } internal readonly struct GifXmpApplicationExtension : IGifExtension { public byte Label => byte.MaxValue; public int ContentLength { get { if (Data.Length == 0) { return 0; } return Data.Length + 269; } } public byte[] Data { get; } public GifXmpApplicationExtension(byte[] data) { Data = data; } public static GifXmpApplicationExtension Read(Stream stream, MemoryAllocator allocator) { byte[] array = ReadXmpData(stream, allocator); int num = array.Length - 256; byte[] array2 = Array.Empty(); if (num > 0) { array2 = new byte[num]; array.AsSpan(0, num).CopyTo(array2); stream.Skip(1); } return new GifXmpApplicationExtension(array2); } public int WriteTo(Span buffer) { int num = 0; buffer[num++] = 11; ReadOnlySpan xmpApplicationIdentificationBytes = GifConstants.XmpApplicationIdentificationBytes; xmpApplicationIdentificationBytes.CopyTo(buffer.Slice(num)); num += xmpApplicationIdentificationBytes.Length; Data.CopyTo(buffer.Slice(num)); num += Data.Length; buffer[num++] = 1; for (byte b = byte.MaxValue; b > 0; b--) { buffer[num++] = b; } buffer[num++] = 0; return ContentLength; } private static byte[] ReadXmpData(Stream stream, MemoryAllocator allocator) { using ChunkedMemoryStream chunkedMemoryStream = new ChunkedMemoryStream(allocator); while (true) { int num = stream.ReadByte(); if (num <= 0) { break; } chunkedMemoryStream.WriteByte((byte)num); } return chunkedMemoryStream.ToArray(); } } public interface IGifExtension { byte Label { get; } int ContentLength { get; } int WriteTo(Span buffer); } } namespace SixLabors.ImageSharp.Formats.Bmp { [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct BmpArrayFileHeader { public short Type { get; } public int Size { get; } public int OffsetToNext { get; } public short ScreenWidth { get; } public short ScreenHeight { get; } public BmpArrayFileHeader(short type, int size, int offsetToNext, short width, short height) { Type = type; Size = size; OffsetToNext = offsetToNext; ScreenWidth = width; ScreenHeight = height; } public static BmpArrayFileHeader Parse(Span data) { return MemoryMarshal.Cast(data)[0]; } } public enum BmpBitsPerPixel : short { Pixel1 = 1, Pixel4 = 4, Pixel8 = 8, Pixel16 = 16, Pixel24 = 24, Pixel32 = 32 } internal enum BmpCompression { RGB = 0, RLE8 = 1, RLE4 = 2, BitFields = 3, JPEG = 4, PNG = 5, BI_ALPHABITFIELDS = 6, RLE24 = 100 } public sealed class BmpConfigurationModule : IConfigurationModule { public void Configure(Configuration configuration) { configuration.ImageFormatsManager.SetEncoder(BmpFormat.Instance, new BmpEncoder()); configuration.ImageFormatsManager.SetDecoder(BmpFormat.Instance, new BmpDecoder()); configuration.ImageFormatsManager.AddImageFormatDetector(new BmpImageFormatDetector()); } } internal static class BmpConstants { internal static class TypeMarkers { public const int Bitmap = 19778; public const int BitmapArray = 16706; public const int ColorIcon = 18755; public const int ColorPointer = 20547; public const int Icon = 17225; public const int Pointer = 21584; } public static readonly IEnumerable MimeTypes = new string[2] { "image/bmp", "image/x-windows-bmp" }; public static readonly IEnumerable FileExtensions = new string[3] { "bm", "bmp", "dip" }; } public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector { public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(stream, "stream"); return new BmpDecoderCore(configuration, this).Decode(configuration, stream, cancellationToken); } public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) { return Decode(configuration, stream, cancellationToken); } public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(stream, "stream"); return new BmpDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken); } } internal sealed class BmpDecoderCore : IImageDecoderInternals { private const int DefaultRgb16RMask = 31744; private const int DefaultRgb16GMask = 992; private const int DefaultRgb16BMask = 31; private const int RleCommand = 0; private const int RleEndOfLine = 0; private const int RleEndOfBitmap = 1; private const int RleDelta = 2; private BufferedReadStream stream; private ImageMetadata metadata; private BmpMetadata bmpMetadata; private BmpFileHeader fileHeader; private BmpFileMarkerType fileMarkerType; private BmpInfoHeader infoHeader; private readonly MemoryAllocator memoryAllocator; private readonly IBmpDecoderOptions options; public Configuration Configuration { get; } public Size Dimensions => new Size(infoHeader.Width, infoHeader.Height); public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options) { Configuration = configuration; memoryAllocator = configuration.MemoryAllocator; this.options = options; } public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Image image = null; try { bool inverted; byte[] palette; int bytesPerColorMapEntry = ReadImageHeaders(stream, out inverted, out palette); image = new Image(Configuration, infoHeader.Width, infoHeader.Height, metadata); Buffer2D rootFramePixelBuffer = image.GetRootFramePixelBuffer(); switch (infoHeader.Compression) { case BmpCompression.RGB: if (infoHeader.BitsPerPixel == 32) { if (bmpMetadata.InfoHeaderType == BmpInfoHeaderType.WinVersion3) { ReadRgb32Slow(rootFramePixelBuffer, infoHeader.Width, infoHeader.Height, inverted); } else { ReadRgb32Fast(rootFramePixelBuffer, infoHeader.Width, infoHeader.Height, inverted); } } else if (infoHeader.BitsPerPixel == 24) { ReadRgb24(rootFramePixelBuffer, infoHeader.Width, infoHeader.Height, inverted); } else if (infoHeader.BitsPerPixel == 16) { ReadRgb16(rootFramePixelBuffer, infoHeader.Width, infoHeader.Height, inverted); } else if (infoHeader.BitsPerPixel <= 8) { ReadRgbPalette(rootFramePixelBuffer, palette, infoHeader.Width, infoHeader.Height, infoHeader.BitsPerPixel, bytesPerColorMapEntry, inverted); } break; case BmpCompression.RLE24: ReadRle24(rootFramePixelBuffer, infoHeader.Width, infoHeader.Height, inverted); break; case BmpCompression.RLE8: case BmpCompression.RLE4: ReadRle(infoHeader.Compression, rootFramePixelBuffer, palette, infoHeader.Width, infoHeader.Height, inverted); break; case BmpCompression.BitFields: case BmpCompression.BI_ALPHABITFIELDS: ReadBitFields(rootFramePixelBuffer, inverted); break; default: BmpThrowHelper.ThrowNotSupportedException("Does not support this kind of bitmap files."); break; } return image; } catch (IndexOutOfRangeException innerException) { image?.Dispose(); throw new ImageFormatException("Bitmap does not have a valid format.", innerException); } catch { image?.Dispose(); throw; } } public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { ReadImageHeaders(stream, out var _, out var _); return new ImageInfo(new PixelTypeInfo(infoHeader.BitsPerPixel), infoHeader.Width, infoHeader.Height, metadata); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Invert(int y, int height, bool inverted) { if (inverted) { return y; } return height - y - 1; } private static int CalculatePadding(int width, int componentCount) { int num = width * componentCount % 4; if (num != 0) { num = 4 - num; } return num; } private void ReadBitFields(Buffer2D pixels, bool inverted) where TPixel : unmanaged, IPixel { if (infoHeader.BitsPerPixel == 16) { ReadRgb16(pixels, infoHeader.Width, infoHeader.Height, inverted, infoHeader.RedMask, infoHeader.GreenMask, infoHeader.BlueMask); } else { ReadRgb32BitFields(pixels, infoHeader.Width, infoHeader.Height, inverted, infoHeader.RedMask, infoHeader.GreenMask, infoHeader.BlueMask, infoHeader.AlphaMask); } } private void ReadRle(BmpCompression compression, Buffer2D pixels, byte[] colors, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); using IMemoryOwner memoryOwner = memoryAllocator.Allocate(width * height, AllocationOptions.Clean); using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(width * height, AllocationOptions.Clean); using IMemoryOwner memoryOwner3 = memoryAllocator.Allocate(height, AllocationOptions.Clean); Memory memory = memoryOwner3.Memory; Span span = memory.Span; memory = memoryOwner2.Memory; Span span2 = memory.Span; Span span3 = memoryOwner.Memory.Span; if (compression == BmpCompression.RLE8) { UncompressRle8(width, span3, span2, span); } else { UncompressRle4(width, span3, span2, span); } for (int i = 0; i < height; i++) { int y = Invert(i, height, inverted); int num = i * width; Span span4 = span3.Slice(num, width); Span span5 = pixels.DangerousGetRowSpan(y); if (span[i]) { for (int j = 0; j < width; j++) { byte b = span4[j]; if (span2[num + j]) { switch (options.RleSkippedPixelHandling) { case RleSkippedPixelHandling.FirstColorOfPalette: val.FromBgr24(Unsafe.As(ref colors[b * 4])); break; case RleSkippedPixelHandling.Transparent: val.FromScaledVector4(Vector4.Zero); break; default: val.FromScaledVector4(new Vector4(0f, 0f, 0f, 1f)); break; } } else { val.FromBgr24(Unsafe.As(ref colors[b * 4])); } span5[j] = val; } } else { for (int k = 0; k < width; k++) { val.FromBgr24(Unsafe.As(ref colors[span4[k] * 4])); span5[k] = val; } } } } private void ReadRle24(Buffer2D pixels, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); using IMemoryOwner buffer = memoryAllocator.Allocate(width * height * 3, AllocationOptions.Clean); using IMemoryOwner memoryOwner = memoryAllocator.Allocate(width * height, AllocationOptions.Clean); using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(height, AllocationOptions.Clean); Memory memory = memoryOwner2.Memory; Span span = memory.Span; memory = memoryOwner.Memory; Span span2 = memory.Span; Span span3 = buffer.GetSpan(); UncompressRle24(width, span3, span2, span); for (int i = 0; i < height; i++) { int y = Invert(i, height, inverted); Span span4 = pixels.DangerousGetRowSpan(y); if (span[i]) { int num = i * width; int num2 = num * 3; for (int j = 0; j < width; j++) { int index = num2 + j * 3; if (span2[num + j]) { switch (options.RleSkippedPixelHandling) { case RleSkippedPixelHandling.FirstColorOfPalette: val.FromBgr24(Unsafe.As(ref span3[index])); break; case RleSkippedPixelHandling.Transparent: val.FromScaledVector4(Vector4.Zero); break; default: val.FromScaledVector4(new Vector4(0f, 0f, 0f, 1f)); break; } } else { val.FromBgr24(Unsafe.As(ref span3[index])); } span4[j] = val; } } else { int num3 = i * width * 3; for (int k = 0; k < width; k++) { int index2 = num3 + k * 3; val.FromBgr24(Unsafe.As(ref span3[index2])); span4[k] = val; } } } } private void UncompressRle4(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { Span buffer2 = stackalloc byte[2]; int num = 0; while (num < buffer.Length) { if (stream.Read(buffer2, 0, buffer2.Length) != 2) { BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap."); } if (buffer2[0] == 0) { switch (buffer2[1]) { case 1: { int skipPixelCount = buffer.Length - num; RleSkipEndOfBitmap(num, w, skipPixelCount, undefinedPixels, rowsWithUndefinedPixels); return; } case 0: num += RleSkipEndOfLine(num, w, undefinedPixels, rowsWithUndefinedPixels); continue; case 2: { int dx = stream.ReadByte(); int dy = stream.ReadByte(); num += RleSkipDelta(num, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); continue; } } int num2 = buffer2[1]; int num3 = (num2 + 1) / 2; byte[] array = new byte[num3]; stream.Read(array, 0, array.Length); int num4 = 0; for (int i = 0; i < num2; i++) { byte b = array[num4]; if (i % 2 == 0) { byte b2 = (byte)((b >> 4) & 0xF); buffer[num++] = b2; } else { byte b3 = (byte)(b & 0xF); buffer[num++] = b3; num4++; } } int count = num3 & 1; stream.Skip(count); continue; } int num5 = buffer2[0]; byte num6 = buffer2[1]; byte b4 = (byte)(num6 & 0xF); byte b5 = (byte)((num6 >> 4) & 0xF); for (int j = 0; j < num5; j++) { if (j % 2 == 0) { buffer[num] = b5; } else { buffer[num] = b4; } num++; } } } private void UncompressRle8(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { Span buffer2 = stackalloc byte[2]; int i = 0; while (i < buffer.Length) { if (stream.Read(buffer2, 0, buffer2.Length) != 2) { BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap."); } if (buffer2[0] == 0) { switch (buffer2[1]) { case 1: { int skipPixelCount = buffer.Length - i; RleSkipEndOfBitmap(i, w, skipPixelCount, undefinedPixels, rowsWithUndefinedPixels); return; } case 0: i += RleSkipEndOfLine(i, w, undefinedPixels, rowsWithUndefinedPixels); continue; case 2: { int dx = stream.ReadByte(); int dy = stream.ReadByte(); i += RleSkipDelta(i, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); continue; } } byte num = buffer2[1]; byte[] array = new byte[num]; stream.Read(array, 0, array.Length); array.AsSpan().CopyTo(buffer.Slice(i)); i += array.Length; int count = num & 1; stream.Skip(count); } else { int num2 = i + buffer2[0]; byte b = buffer2[1]; for (; i < num2; i++) { buffer[i] = b; } } } } private void UncompressRle24(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { Span buffer2 = stackalloc byte[2]; int i = 0; while (i < buffer.Length) { if (stream.Read(buffer2, 0, buffer2.Length) != 2) { BmpThrowHelper.ThrowInvalidImageContentException("Failed to read 2 bytes from stream while uncompressing RLE24 bitmap."); } if (buffer2[0] == 0) { switch (buffer2[1]) { case 1: { int skipPixelCount = (buffer.Length - i * 3) / 3; RleSkipEndOfBitmap(i, w, skipPixelCount, undefinedPixels, rowsWithUndefinedPixels); return; } case 0: i += RleSkipEndOfLine(i, w, undefinedPixels, rowsWithUndefinedPixels); continue; case 2: { int dx = stream.ReadByte(); int dy = stream.ReadByte(); i += RleSkipDelta(i, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); continue; } } int num = buffer2[1]; byte[] array = new byte[num * 3]; stream.Read(array, 0, array.Length); array.AsSpan().CopyTo(buffer.Slice(i * 3)); i += num; int count = array.Length & 1; stream.Skip(count); } else { int num2 = i + buffer2[0]; byte b = buffer2[1]; byte b2 = (byte)stream.ReadByte(); byte b3 = (byte)stream.ReadByte(); int num3 = i * 3; for (; i < num2; i++) { buffer[num3++] = b; buffer[num3++] = b2; buffer[num3++] = b3; } } } } private static void RleSkipEndOfBitmap(int count, int w, int skipPixelCount, Span undefinedPixels, Span rowsWithUndefinedPixels) { for (int i = count; i < count + skipPixelCount; i++) { undefinedPixels[i] = true; } int num = count / w; int num2 = skipPixelCount / w - 1; int num3 = Math.Min(num + num2, rowsWithUndefinedPixels.Length - 1); for (int j = num; j <= num3; j++) { rowsWithUndefinedPixels[j] = true; } } private static int RleSkipEndOfLine(int count, int w, Span undefinedPixels, Span rowsWithUndefinedPixels) { rowsWithUndefinedPixels[count / w] = true; int num = count % w; if (num > 0) { int num2 = w - num; for (int i = count; i < count + num2; i++) { undefinedPixels[i] = true; } return num2; } return 0; } private static int RleSkipDelta(int count, int w, int dx, int dy, Span undefinedPixels, Span rowsWithUndefinedPixels) { int num = w * dy + dx; for (int i = count; i < count + num; i++) { undefinedPixels[i] = true; } int num2 = count / w; int num3 = Math.Min(num2 + dy, rowsWithUndefinedPixels.Length - 1); for (int j = num2; j <= num3; j++) { rowsWithUndefinedPixels[j] = true; } return num; } private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted) where TPixel : unmanaged, IPixel { int num = 8 / bitsPerPixel; int num2 = (width + num - 1) / num; int num3 = 255 >> 8 - bitsPerPixel; int num4 = num2 % 4; if (num4 != 0) { num4 = 4 - num4; } using IMemoryOwner buffer = memoryAllocator.Allocate(num2 + num4, AllocationOptions.Clean); TPixel val = default(TPixel); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { int y = Invert(i, height, inverted); stream.Read(span); int num5 = 0; Span span2 = pixels.DangerousGetRowSpan(y); for (int j = 0; j < num2; j++) { int num6 = j * num; int num7 = 0; int num8 = num6; while (num7 < num && num8 < width) { int num9 = ((span[num5] >> 8 - bitsPerPixel - num7 * bitsPerPixel) & num3) * bytesPerColorMapEntry; val.FromBgr24(Unsafe.As(ref colors[num9])); span2[num8] = val; num7++; num8++; } num5++; } } } private void ReadRgb16(Buffer2D pixels, int width, int height, bool inverted, int redMask = 31744, int greenMask = 992, int blueMask = 31) where TPixel : unmanaged, IPixel { int num = CalculatePadding(width, 2); int length = width * 2 + num; TPixel val = default(TPixel); int num2 = CalculateRightShift((uint)redMask); int num3 = CalculateRightShift((uint)greenMask); int num4 = CalculateRightShift((uint)blueMask); int num5 = CountBits((uint)redMask); int num6 = CountBits((uint)greenMask); int num7 = CountBits((uint)blueMask); using IMemoryOwner buffer = memoryAllocator.Allocate(length); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { stream.Read(span); int y = Invert(i, height, inverted); Span span2 = pixels.DangerousGetRowSpan(y); int num8 = 0; for (int j = 0; j < width; j++) { short num9 = BinaryPrimitives.ReadInt16LittleEndian(span.Slice(num8)); int num10 = ((num5 == 5) ? GetBytesFrom5BitValue((num9 & redMask) >> num2) : GetBytesFrom6BitValue((num9 & redMask) >> num2)); int num11 = ((num6 == 5) ? GetBytesFrom5BitValue((num9 & greenMask) >> num3) : GetBytesFrom6BitValue((num9 & greenMask) >> num3)); int num12 = ((num7 == 5) ? GetBytesFrom5BitValue((num9 & blueMask) >> num4) : GetBytesFrom6BitValue((num9 & blueMask) >> num4)); Rgb24 source = new Rgb24((byte)num10, (byte)num11, (byte)num12); val.FromRgb24(source); span2[j] = val; num8 += 2; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte GetBytesFrom5BitValue(int value) { return (byte)((value << 3) | (value >> 2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte GetBytesFrom6BitValue(int value) { return (byte)((value << 2) | (value >> 4)); } private void ReadRgb24(Buffer2D pixels, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { int paddingInBytes = CalculatePadding(width, 3); using IMemoryOwner buffer = memoryAllocator.AllocatePaddedPixelRowBuffer(width, 3, paddingInBytes); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { stream.Read(span); int y = Invert(i, height, inverted); Span destinationPixels = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.FromBgr24Bytes(Configuration, span, destinationPixels, width); } } private void ReadRgb32Fast(Buffer2D pixels, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { int paddingInBytes = CalculatePadding(width, 4); using IMemoryOwner buffer = memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, paddingInBytes); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { stream.Read(span); int y = Invert(i, height, inverted); Span destinationPixels = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.FromBgra32Bytes(Configuration, span, destinationPixels, width); } } private void ReadRgb32Slow(Buffer2D pixels, int width, int height, bool inverted) where TPixel : unmanaged, IPixel { int paddingInBytes = CalculatePadding(width, 4); using IMemoryOwner buffer = memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, paddingInBytes); using IMemoryOwner buffer2 = memoryAllocator.Allocate(width); Span span = buffer.GetSpan(); Span span2 = buffer2.GetSpan(); long position = stream.Position; bool flag = false; for (int i = 0; i < height; i++) { stream.Read(span); PixelOperations.Instance.FromBgra32Bytes(Configuration, span, span2, width); for (int j = 0; j < width; j++) { if (span2[j].A > 0) { flag = true; break; } } if (flag) { break; } } stream.Position = position; if (flag) { for (int k = 0; k < height; k++) { stream.Read(span); int y = Invert(k, height, inverted); Span destinationPixels = pixels.DangerousGetRowSpan(y); PixelOperations.Instance.FromBgra32Bytes(Configuration, span, destinationPixels, width); } return; } for (int l = 0; l < height; l++) { stream.Read(span); PixelOperations.Instance.FromBgra32Bytes(Configuration, span, span2, width); int y2 = Invert(l, height, inverted); Span span3 = pixels.DangerousGetRowSpan(y2); for (int m = 0; m < width; m++) { Bgra32 source = span2[m]; source.A = byte.MaxValue; span3[m].FromBgra32(source); } } } private void ReadRgb32BitFields(Buffer2D pixels, int width, int height, bool inverted, int redMask, int greenMask, int blueMask, int alphaMask) where TPixel : unmanaged, IPixel { TPixel val = default(TPixel); int num = CalculatePadding(width, 4); int length = width * 4 + num; int num2 = CalculateRightShift((uint)redMask); int num3 = CalculateRightShift((uint)greenMask); int num4 = CalculateRightShift((uint)blueMask); int num5 = CalculateRightShift((uint)alphaMask); int num6 = CountBits((uint)redMask); int num7 = CountBits((uint)greenMask); int num8 = CountBits((uint)blueMask); int num9 = CountBits((uint)alphaMask); float num10 = 1f / (float)(uint.MaxValue >> 32 - num6); float num11 = 1f / (float)(uint.MaxValue >> 32 - num7); float num12 = 1f / (float)(uint.MaxValue >> 32 - num8); uint num13 = uint.MaxValue >> 32 - num9; float num14 = 1f / (float)num13; bool flag = num6 > 8 || num7 > 8 || num8 > 8 || num14 > 8f; using IMemoryOwner buffer = memoryAllocator.Allocate(length); Span span = buffer.GetSpan(); for (int i = 0; i < height; i++) { stream.Read(span); int y = Invert(i, height, inverted); Span span2 = pixels.DangerousGetRowSpan(y); int num15 = 0; for (int j = 0; j < width; j++) { uint num16 = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(num15)); if (flag) { uint num17 = (uint)(num16 & redMask) >> num2; uint num18 = (uint)(num16 & greenMask) >> num3; uint num19 = (uint)(num16 & blueMask) >> num4; float w = ((alphaMask != 0) ? (num14 * (float)((uint)(num16 & alphaMask) >> num5)) : 1f); Vector4 vector = new Vector4((float)num17 * num10, (float)num18 * num11, (float)num19 * num12, w); val.FromScaledVector4(vector); } else { byte r = (byte)((num16 & redMask) >> num2); byte g = (byte)((num16 & greenMask) >> num3); byte b = (byte)((num16 & blueMask) >> num4); byte a = ((alphaMask != 0) ? ((byte)((num16 & alphaMask) >> num5)) : byte.MaxValue); val.FromRgba32(new Rgba32(r, g, b, a)); } span2[j] = val; num15 += 4; } } } private static int CalculateRightShift(uint n) { int num = 0; while (n != 0 && (1 & n) == 0) { num++; n >>= 1; } return num; } private static int CountBits(uint n) { int num = 0; while (n != 0) { num++; n &= n - 1; } return num; } private void ReadInfoHeader() { Span span = stackalloc byte[124]; stream.Read(span, 0, 4); int num = BinaryPrimitives.ReadInt32LittleEndian(span); if (num < 12 || num > 124) { BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize is '{num}'."); } stream.Read(span, 4, num - 4); BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2; if (num == 12) { infoHeaderType = BmpInfoHeaderType.WinVersion2; infoHeader = BmpInfoHeader.ParseCore(span); } else if (num == 16) { infoHeaderType = BmpInfoHeaderType.Os2Version2Short; infoHeader = BmpInfoHeader.ParseOs22Short(span); } else if (num == 40) { infoHeaderType = BmpInfoHeaderType.WinVersion3; infoHeader = BmpInfoHeader.ParseV3(span); if (infoHeader.Compression == BmpCompression.BitFields) { byte[] array = new byte[12]; stream.Read(array, 0, 12); Span span2 = array.AsSpan(); infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(span2.Slice(0, 4)); infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(span2.Slice(4, 4)); infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(span2.Slice(8, 4)); } else if (infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS) { byte[] array2 = new byte[16]; stream.Read(array2, 0, 16); Span span3 = array2.AsSpan(); infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(span3.Slice(0, 4)); infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(span3.Slice(4, 4)); infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(span3.Slice(8, 4)); infoHeader.AlphaMask = BinaryPrimitives.ReadInt32LittleEndian(span3.Slice(12, 4)); } } else if (num == 52) { infoHeaderType = BmpInfoHeaderType.AdobeVersion3; infoHeader = BmpInfoHeader.ParseAdobeV3(span, withAlpha: false); } else if (num == 56) { infoHeaderType = BmpInfoHeaderType.AdobeVersion3WithAlpha; infoHeader = BmpInfoHeader.ParseAdobeV3(span); } else if (num == 64) { infoHeaderType = BmpInfoHeaderType.Os2Version2; infoHeader = BmpInfoHeader.ParseOs2Version2(span); } else if (num >= 108) { infoHeaderType = ((num == 108) ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion5); infoHeader = BmpInfoHeader.ParseV4(span); } else { BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize '{num}'."); } ImageMetadata imageMetadata = new ImageMetadata { ResolutionUnits = PixelResolutionUnit.PixelsPerMeter }; if (infoHeader.XPelsPerMeter > 0 && infoHeader.YPelsPerMeter > 0) { imageMetadata.HorizontalResolution = infoHeader.XPelsPerMeter; imageMetadata.VerticalResolution = infoHeader.YPelsPerMeter; } else { imageMetadata.HorizontalResolution = Math.Round(UnitConverter.InchToMeter(96.0)); imageMetadata.VerticalResolution = Math.Round(UnitConverter.InchToMeter(96.0)); } metadata = imageMetadata; short bitsPerPixel = infoHeader.BitsPerPixel; bmpMetadata = metadata.GetBmpMetadata(); bmpMetadata.InfoHeaderType = infoHeaderType; bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; } private void ReadFileHeader() { Span span = stackalloc byte[14]; stream.Read(span, 0, 14); short num = BinaryPrimitives.ReadInt16LittleEndian(span); switch (num) { case 19778: fileMarkerType = BmpFileMarkerType.Bitmap; fileHeader = BmpFileHeader.Parse(span); break; case 16706: fileMarkerType = BmpFileMarkerType.BitmapArray; stream.Read(span, 0, 14); fileHeader = BmpFileHeader.Parse(span); if (fileHeader.Type != 19778) { BmpThrowHelper.ThrowNotSupportedException($"Unsupported bitmap file inside a BitmapArray file. File header bitmap type marker '{fileHeader.Type}'."); } break; default: BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{num}'."); break; } } private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out byte[] palette) { this.stream = stream; ReadFileHeader(); ReadInfoHeader(); inverted = false; if (infoHeader.Height < 0) { inverted = true; infoHeader.Height = -infoHeader.Height; } int num = 4; int num2 = -1; if (infoHeader.ClrUsed == 0) { if (infoHeader.BitsPerPixel == 1 || infoHeader.BitsPerPixel == 4 || infoHeader.BitsPerPixel == 8) { switch (fileMarkerType) { case BmpFileMarkerType.Bitmap: { num2 = fileHeader.Offset - 14 - infoHeader.HeaderSize; int colorCountForBitDepth = ColorNumerics.GetColorCountForBitDepth(infoHeader.BitsPerPixel); num = num2 / colorCountForBitDepth; num = Math.Max(num, 3); break; } case BmpFileMarkerType.BitmapArray: case BmpFileMarkerType.ColorIcon: case BmpFileMarkerType.ColorPointer: case BmpFileMarkerType.Icon: case BmpFileMarkerType.Pointer: num = 3; num2 = ColorNumerics.GetColorCountForBitDepth(infoHeader.BitsPerPixel) * num; break; } } } else { num2 = infoHeader.ClrUsed * num; } palette = null; if (num2 > 0) { if (this.stream.Position + num2 > fileHeader.Offset) { BmpThrowHelper.ThrowInvalidImageContentException($"Reading the color map would read beyond the bitmap offset. Either the color map size of '{num2}' is invalid or the bitmap offset."); } palette = new byte[num2]; this.stream.Read(palette, 0, num2); } infoHeader.VerifyDimensions(); int num3 = fileHeader.Offset - (int)this.stream.Position; if (num3 + (int)this.stream.Position > this.stream.Length) { BmpThrowHelper.ThrowInvalidImageContentException("Invalid fileheader offset found. Offset is greater than the stream length."); } if (num3 > 0) { this.stream.Skip(num3); } return num; } } public sealed class BmpEncoder : IImageEncoder, IBmpEncoderOptions { public BmpBitsPerPixel? BitsPerPixel { get; set; } public bool SupportTransparency { get; set; } public IQuantizer Quantizer { get; set; } public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { new BmpEncoderCore(this, image.GetMemoryAllocator()).Encode(image, stream); } public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { return new BmpEncoderCore(this, image.GetMemoryAllocator()).EncodeAsync(image, stream, cancellationToken); } } internal sealed class BmpEncoderCore : IImageEncoderInternals { private int padding; private const int Rgba32AlphaMask = -16777216; private const int Rgba32RedMask = 16711680; private const int Rgba32GreenMask = 65280; private const int Rgba32BlueMask = 255; private const int ColorPaletteSize8Bit = 1024; private const int ColorPaletteSize4Bit = 64; private const int ColorPaletteSize1Bit = 8; private readonly MemoryAllocator memoryAllocator; private Configuration configuration; private BmpBitsPerPixel? bitsPerPixel; private readonly bool writeV4Header; private readonly IQuantizer quantizer; public BmpEncoderCore(IBmpEncoderOptions options, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; bitsPerPixel = options.BitsPerPixel; writeV4Header = options.SupportTransparency; quantizer = options.Quantizer ?? KnownQuantizers.Octree; } public void Encode(Image image, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); configuration = image.GetConfiguration(); ImageMetadata metadata = image.Metadata; BmpMetadata bmpMetadata = metadata.GetBmpMetadata(); BmpBitsPerPixel valueOrDefault = bitsPerPixel.GetValueOrDefault(); if (!bitsPerPixel.HasValue) { valueOrDefault = bmpMetadata.BitsPerPixel; bitsPerPixel = valueOrDefault; } short value = (short)bitsPerPixel.Value; int num = 4 * ((image.Width * value + 31) / 32); padding = num - (int)((float)image.Width * ((float)value / 8f)); int xPelsPerMeter = 0; int yPelsPerMeter = 0; if (metadata.ResolutionUnits != PixelResolutionUnit.AspectRatio && metadata.HorizontalResolution > 0.0 && metadata.VerticalResolution > 0.0) { switch (metadata.ResolutionUnits) { case PixelResolutionUnit.PixelsPerInch: xPelsPerMeter = (int)Math.Round(UnitConverter.InchToMeter(metadata.HorizontalResolution)); yPelsPerMeter = (int)Math.Round(UnitConverter.InchToMeter(metadata.VerticalResolution)); break; case PixelResolutionUnit.PixelsPerCentimeter: xPelsPerMeter = (int)Math.Round(UnitConverter.CmToMeter(metadata.HorizontalResolution)); yPelsPerMeter = (int)Math.Round(UnitConverter.CmToMeter(metadata.VerticalResolution)); break; case PixelResolutionUnit.PixelsPerMeter: xPelsPerMeter = (int)Math.Round(metadata.HorizontalResolution); yPelsPerMeter = (int)Math.Round(metadata.VerticalResolution); break; } } int num2 = (writeV4Header ? 108 : 40); BmpInfoHeader bmpInfoHeader = new BmpInfoHeader(num2, height: image.Height, width: image.Width, planes: 1, bitsPerPixel: value, compression: BmpCompression.RGB, imageSize: image.Height * num, xPelsPerMeter: xPelsPerMeter, yPelsPerMeter: yPelsPerMeter); if (writeV4Header && bitsPerPixel == BmpBitsPerPixel.Pixel32) { bmpInfoHeader.AlphaMask = -16777216; bmpInfoHeader.RedMask = 16711680; bmpInfoHeader.GreenMask = 65280; bmpInfoHeader.BlueMask = 255; bmpInfoHeader.Compression = BmpCompression.BitFields; } int num3 = 0; if (bitsPerPixel == BmpBitsPerPixel.Pixel8) { num3 = 1024; } else if (bitsPerPixel == BmpBitsPerPixel.Pixel4) { num3 = 64; } else if (bitsPerPixel == BmpBitsPerPixel.Pixel1) { num3 = 8; } BmpFileHeader bmpFileHeader = new BmpFileHeader(19778, 14 + num2 + num3 + bmpInfoHeader.ImageSize, 0, 14 + num2 + num3); Span buffer = stackalloc byte[num2]; bmpFileHeader.WriteTo(buffer); stream.Write(buffer, 0, 14); if (writeV4Header) { bmpInfoHeader.WriteV4Header(buffer); } else { bmpInfoHeader.WriteV3Header(buffer); } stream.Write(buffer, 0, num2); WriteImage(stream, image.Frames.RootFrame); stream.Flush(); } private void WriteImage(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { Buffer2D pixelBuffer = image.PixelBuffer; BmpBitsPerPixel? bmpBitsPerPixel = bitsPerPixel; if (bmpBitsPerPixel.HasValue) { switch (bmpBitsPerPixel.GetValueOrDefault()) { case BmpBitsPerPixel.Pixel32: Write32Bit(stream, pixelBuffer); break; case BmpBitsPerPixel.Pixel24: Write24Bit(stream, pixelBuffer); break; case BmpBitsPerPixel.Pixel16: Write16Bit(stream, pixelBuffer); break; case BmpBitsPerPixel.Pixel8: Write8Bit(stream, image); break; case BmpBitsPerPixel.Pixel4: Write4BitColor(stream, image); break; case BmpBitsPerPixel.Pixel1: Write1BitColor(stream, image); break; } } } private IMemoryOwner AllocateRow(int width, int bytesPerPixel) { return memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding); } private void Write32Bit(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner buffer = AllocateRow(pixels.Width, 4); Span span = buffer.GetSpan(); for (int num = pixels.Height - 1; num >= 0; num--) { Span span2 = pixels.DangerousGetRowSpan(num); PixelOperations.Instance.ToBgra32Bytes(configuration, span2, span, span2.Length); stream.Write(span); } } private void Write24Bit(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { int width = pixels.Width; int length = width * 3; using IMemoryOwner buffer = AllocateRow(width, 3); Span span = buffer.GetSpan(); for (int num = pixels.Height - 1; num >= 0; num--) { Span span2 = pixels.DangerousGetRowSpan(num); PixelOperations.Instance.ToBgr24Bytes(configuration, span2, buffer.Slice(0, length), width); stream.Write(span); } } private void Write16Bit(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { int width = pixels.Width; int length = width * 2; using IMemoryOwner buffer = AllocateRow(width, 2); Span span = buffer.GetSpan(); for (int num = pixels.Height - 1; num >= 0; num--) { Span span2 = pixels.DangerousGetRowSpan(num); PixelOperations.Instance.ToBgra5551Bytes(configuration, span2, buffer.Slice(0, length), span2.Length); stream.Write(span); } } private void Write8Bit(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { bool flag = typeof(TPixel) == typeof(L8); using IMemoryOwner buffer = memoryAllocator.Allocate(1024, AllocationOptions.Clean); Span span = buffer.GetSpan(); if (flag) { Write8BitGray(stream, image, span); } else { Write8BitColor(stream, image, span); } } private void Write8BitColor(Stream stream, ImageFrame image, Span colorPalette) where TPixel : unmanaged, IPixel { using IQuantizer quantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration); using IndexedImageFrame indexedImageFrame = quantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds()); ReadOnlySpan span = indexedImageFrame.Palette.Span; WriteColorPalette(stream, span, colorPalette); for (int num = image.Height - 1; num >= 0; num--) { ReadOnlySpan buffer = indexedImageFrame.DangerousGetRowSpan(num); stream.Write(buffer); for (int i = 0; i < padding; i++) { stream.WriteByte(0); } } } private void Write8BitGray(Stream stream, ImageFrame image, Span colorPalette) where TPixel : unmanaged, IPixel { for (int i = 0; i <= 255; i++) { int num = i * 4; byte b = (byte)i; colorPalette[num] = b; colorPalette[num + 1] = b; colorPalette[num + 2] = b; colorPalette[num + 3] = 0; } stream.Write(colorPalette); Buffer2D pixelBuffer = image.PixelBuffer; for (int num2 = image.Height - 1; num2 >= 0; num2--) { ReadOnlySpan buffer = MemoryMarshal.AsBytes((ReadOnlySpan)pixelBuffer.DangerousGetRowSpan(num2)); stream.Write(buffer); for (int j = 0; j < padding; j++) { stream.WriteByte(0); } } } private void Write4BitColor(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { using IQuantizer quantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration, new QuantizerOptions { MaxColors = 16 }); using IndexedImageFrame indexedImageFrame = quantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds()); using IMemoryOwner buffer = memoryAllocator.Allocate(64, AllocationOptions.Clean); Span span = buffer.GetSpan(); ReadOnlySpan span2 = indexedImageFrame.Palette.Span; WriteColorPalette(stream, span2, span); int num = ((indexedImageFrame.DangerousGetRowSpan(0).Length % 2 != 0) ? (padding - 1) : padding); for (int num2 = image.Height - 1; num2 >= 0; num2--) { ReadOnlySpan readOnlySpan = indexedImageFrame.DangerousGetRowSpan(num2); int num3 = ((readOnlySpan.Length % 2 == 0) ? readOnlySpan.Length : (readOnlySpan.Length - 1)); for (int i = 0; i < num3; i += 2) { stream.WriteByte((byte)((readOnlySpan[i] << 4) | readOnlySpan[i + 1])); } if (readOnlySpan.Length % 2 != 0) { stream.WriteByte((byte)((readOnlySpan[readOnlySpan.Length - 1] << 4) | 0)); } for (int j = 0; j < num; j++) { stream.WriteByte(0); } } } private void Write1BitColor(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { using IQuantizer quantizer = this.quantizer.CreatePixelSpecificQuantizer(configuration, new QuantizerOptions { MaxColors = 2 }); using IndexedImageFrame indexedImageFrame = quantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds()); using IMemoryOwner buffer = memoryAllocator.Allocate(8, AllocationOptions.Clean); Span span = buffer.GetSpan(); ReadOnlySpan span2 = indexedImageFrame.Palette.Span; WriteColorPalette(stream, span2, span); int num = ((indexedImageFrame.DangerousGetRowSpan(0).Length % 8 != 0) ? (padding - 1) : padding); for (int num2 = image.Height - 1; num2 >= 0; num2--) { ReadOnlySpan quantizedPixelRow = indexedImageFrame.DangerousGetRowSpan(num2); int num3 = ((quantizedPixelRow.Length % 8 == 0) ? quantizedPixelRow.Length : (quantizedPixelRow.Length - 8)); for (int i = 0; i < num3; i += 8) { Write1BitPalette(stream, i, i + 8, quantizedPixelRow); } if (quantizedPixelRow.Length % 8 != 0) { int startIdx = quantizedPixelRow.Length - 7; num3 = quantizedPixelRow.Length; Write1BitPalette(stream, startIdx, num3, quantizedPixelRow); } for (int j = 0; j < num; j++) { stream.WriteByte(0); } } } private void WriteColorPalette(Stream stream, ReadOnlySpan quantizedColorPalette, Span colorPalette) where TPixel : unmanaged, IPixel { int length = quantizedColorPalette.Length * 4; PixelOperations.Instance.ToBgra32(configuration, quantizedColorPalette, MemoryMarshal.Cast(colorPalette.Slice(0, length))); Span span = MemoryMarshal.Cast(colorPalette); for (int i = 0; i < span.Length; i++) { span[i] &= 0xFFFFFF; } stream.Write(colorPalette); } private static void Write1BitPalette(Stream stream, int startIdx, int endIdx, ReadOnlySpan quantizedPixelRow) { int num = 7; byte b = 0; for (int i = startIdx; i < endIdx; i++) { b = (byte)(b | ((byte)(quantizedPixelRow[i] & 1) << num)); num--; } stream.WriteByte(b); } } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct BmpFileHeader { public const int Size = 14; public short Type { get; } public int FileSize { get; } public int Reserved { get; } public int Offset { get; } public BmpFileHeader(short type, int fileSize, int reserved, int offset) { Type = type; FileSize = fileSize; Reserved = reserved; Offset = offset; } public static BmpFileHeader Parse(Span data) { return MemoryMarshal.Cast(data)[0]; } public void WriteTo(Span buffer) { Unsafe.As(ref MemoryMarshal.GetReference(buffer)) = this; } } public enum BmpFileMarkerType { Bitmap, BitmapArray, ColorIcon, ColorPointer, Icon, Pointer } public sealed class BmpFormat : IImageFormat, IImageFormat { public static BmpFormat Instance { get; } = new BmpFormat(); public string Name => "BMP"; public string DefaultMimeType => "image/bmp"; public IEnumerable MimeTypes => BmpConstants.MimeTypes; public IEnumerable FileExtensions => BmpConstants.FileExtensions; private BmpFormat() { } public BmpMetadata CreateDefaultFormatMetadata() { return new BmpMetadata(); } } public sealed class BmpImageFormatDetector : IImageFormatDetector { public int HeaderSize => 2; public IImageFormat DetectFormat(ReadOnlySpan header) { if (!IsSupportedFileFormat(header)) { return null; } return BmpFormat.Instance; } private bool IsSupportedFileFormat(ReadOnlySpan header) { if (header.Length >= HeaderSize) { short num = BinaryPrimitives.ReadInt16LittleEndian(header); if (num != 19778) { return num == 16706; } return true; } return false; } } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct BmpInfoHeader { public const int CoreSize = 12; public const int Os22ShortSize = 16; public const int SizeV3 = 40; public const int AdobeV3Size = 52; public const int AdobeV3WithAlphaSize = 56; public const int Os2v2Size = 64; public const int SizeV4 = 108; public const int SizeV5 = 124; public const int MaxHeaderSize = 124; public const int HeaderSizeSize = 4; public int HeaderSize { get; set; } public int Width { get; set; } public int Height { get; set; } public short Planes { get; set; } public short BitsPerPixel { get; set; } public BmpCompression Compression { get; set; } public int ImageSize { get; set; } public int XPelsPerMeter { get; set; } public int YPelsPerMeter { get; set; } public int ClrUsed { get; set; } public int ClrImportant { get; set; } public int RedMask { get; set; } public int GreenMask { get; set; } public int BlueMask { get; set; } public int AlphaMask { get; set; } public int CsType { get; set; } public int RedX { get; set; } public int RedY { get; set; } public int RedZ { get; set; } public int GreenX { get; set; } public int GreenY { get; set; } public int GreenZ { get; set; } public int BlueX { get; set; } public int BlueY { get; set; } public int BlueZ { get; set; } public int GammaRed { get; set; } public int GammaGreen { get; set; } public int GammaBlue { get; set; } public BmpInfoHeader(int headerSize, int width, int height, short planes, short bitsPerPixel, BmpCompression compression = BmpCompression.RGB, int imageSize = 0, int xPelsPerMeter = 0, int yPelsPerMeter = 0, int clrUsed = 0, int clrImportant = 0, int redMask = 0, int greenMask = 0, int blueMask = 0, int alphaMask = 0, int csType = 0, int redX = 0, int redY = 0, int redZ = 0, int greenX = 0, int greenY = 0, int greenZ = 0, int blueX = 0, int blueY = 0, int blueZ = 0, int gammeRed = 0, int gammeGreen = 0, int gammeBlue = 0) { HeaderSize = headerSize; Width = width; Height = height; Planes = planes; BitsPerPixel = bitsPerPixel; Compression = compression; ImageSize = imageSize; XPelsPerMeter = xPelsPerMeter; YPelsPerMeter = yPelsPerMeter; ClrUsed = clrUsed; ClrImportant = clrImportant; RedMask = redMask; GreenMask = greenMask; BlueMask = blueMask; AlphaMask = alphaMask; CsType = csType; RedX = redX; RedY = redY; RedZ = redZ; GreenX = greenX; GreenY = greenY; GreenZ = greenZ; BlueX = blueX; BlueY = blueY; BlueZ = blueZ; GammaRed = gammeRed; GammaGreen = gammeGreen; GammaBlue = gammeBlue; } public static BmpInfoHeader ParseCore(ReadOnlySpan data) { return new BmpInfoHeader(BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)), BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(4, 2)), BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(6, 2)), BinaryPrimitives.ReadInt16LittleEndian(data.Slice(8, 2)), BinaryPrimitives.ReadInt16LittleEndian(data.Slice(10, 2))); } public static BmpInfoHeader ParseOs22Short(ReadOnlySpan data) { return new BmpInfoHeader(BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2))); } public static BmpInfoHeader ParseV3(ReadOnlySpan data) { return new BmpInfoHeader(BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)), (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4))); } public static BmpInfoHeader ParseAdobeV3(ReadOnlySpan data, bool withAlpha = true) { return new BmpInfoHeader(BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)), (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(40, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(44, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(48, 4)), withAlpha ? BinaryPrimitives.ReadInt32LittleEndian(data.Slice(52, 4)) : 0); } public static BmpInfoHeader ParseOs2Version2(ReadOnlySpan data) { BmpInfoHeader result = new BmpInfoHeader(BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2))); switch (BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4))) { case 0: result.Compression = BmpCompression.RGB; break; case 1: result.Compression = BmpCompression.RLE8; break; case 2: result.Compression = BmpCompression.RLE4; break; case 4: result.Compression = BmpCompression.RLE24; break; default: BmpThrowHelper.ThrowInvalidImageContentException("Compression type is not supported. ImageSharp only supports uncompressed, RLE4, RLE8 and RLE24."); break; } result.ImageSize = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)); result.XPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)); result.YPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)); result.ClrUsed = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)); result.ClrImportant = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)); return result; } public static BmpInfoHeader ParseV4(ReadOnlySpan data) { if (data.Length < 108) { throw new ArgumentException("data", $"Must be {108} bytes. Was {data.Length} bytes."); } return MemoryMarshal.Cast(data)[0]; } public void WriteV3Header(Span buffer) { buffer.Clear(); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(0, 4), 40); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(4, 4), Width); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(8, 4), Height); BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(12, 2), Planes); BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(14, 2), BitsPerPixel); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(16, 4), (int)Compression); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(20, 4), ImageSize); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(24, 4), XPelsPerMeter); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(28, 4), YPelsPerMeter); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(32, 4), ClrUsed); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(36, 4), ClrImportant); } public void WriteV4Header(Span buffer) { Unsafe.As(ref MemoryMarshal.GetReference(buffer)) = this; } internal void VerifyDimensions() { if (Width > 65535 || Height > 65535) { throw new InvalidOperationException($"The input bmp '{Width}x{Height}' is " + $"bigger then the max allowed size '{65535}x{65535}'"); } } } public enum BmpInfoHeaderType { WinVersion2 = 12, Os2Version2Short = 16, WinVersion3 = 40, AdobeVersion3 = 52, AdobeVersion3WithAlpha = 56, Os2Version2 = 64, WinVersion4 = 108, WinVersion5 = 124 } public class BmpMetadata : IDeepCloneable { public BmpInfoHeaderType InfoHeaderType { get; set; } public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; public BmpMetadata() { } private BmpMetadata(BmpMetadata other) { BitsPerPixel = other.BitsPerPixel; InfoHeaderType = other.InfoHeaderType; } public IDeepCloneable DeepClone() { return new BmpMetadata(this); } } internal static class BmpThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowInvalidImageContentException(string errorMessage) { throw new InvalidImageContentException(errorMessage); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotSupportedException(string errorMessage) { throw new NotSupportedException(errorMessage); } } internal interface IBmpDecoderOptions { RleSkippedPixelHandling RleSkippedPixelHandling { get; } } internal interface IBmpEncoderOptions { BmpBitsPerPixel? BitsPerPixel { get; } bool SupportTransparency { get; } IQuantizer Quantizer { get; } } public enum RleSkippedPixelHandling { Black, Transparent, FirstColorOfPalette } } namespace SixLabors.ImageSharp.Diagnostics { public delegate void UndisposedAllocationDelegate(string allocationStackTrace); public static class MemoryDiagnostics { private static int totalUndisposedAllocationCount; private static UndisposedAllocationDelegate undisposedAllocation; private static int undisposedAllocationSubscriptionCounter; private static readonly object SyncRoot = new object(); public static int TotalUndisposedAllocationCount => totalUndisposedAllocationCount; internal static bool UndisposedAllocationSubscribed => Volatile.Read(in undisposedAllocationSubscriptionCounter) > 0; public static event UndisposedAllocationDelegate UndisposedAllocation { add { lock (SyncRoot) { undisposedAllocationSubscriptionCounter++; undisposedAllocation = (UndisposedAllocationDelegate)Delegate.Combine(undisposedAllocation, value); } } remove { lock (SyncRoot) { undisposedAllocation = (UndisposedAllocationDelegate)Delegate.Remove(undisposedAllocation, value); undisposedAllocationSubscriptionCounter--; } } } internal static event Action MemoryAllocated; internal static event Action MemoryReleased; internal static void IncrementTotalUndisposedAllocationCount() { Interlocked.Increment(ref totalUndisposedAllocationCount); MemoryDiagnostics.MemoryAllocated?.Invoke(); } internal static void DecrementTotalUndisposedAllocationCount() { Interlocked.Decrement(ref totalUndisposedAllocationCount); MemoryDiagnostics.MemoryReleased?.Invoke(); } internal static void RaiseUndisposedMemoryResource(string allocationStackTrace) { if (undisposedAllocation != null) { ThreadPool.QueueUserWorkItem(delegate(string stackTrace) { undisposedAllocation?.Invoke(stackTrace); }, allocationStackTrace, preferLocal: false); } } } } namespace SixLabors.ImageSharp.Compression.Zlib { internal static class Adler32 { public const uint SeedValue = 1u; private const uint BASE = 65521u; private const uint NMAX = 5552u; private const int MinBufferSize = 64; private const int BlockSize = 32; private static ReadOnlySpan Tap1Tap2 => new byte[32] { 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Calculate(ReadOnlySpan buffer) { return Calculate(1u, buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public static uint Calculate(uint adler, ReadOnlySpan buffer) { if (buffer.IsEmpty) { return adler; } if (Avx2.IsSupported && buffer.Length >= 64) { return CalculateAvx2(adler, buffer); } if (Ssse3.IsSupported && buffer.Length >= 64) { return CalculateSse(adler, buffer); } return CalculateScalar(adler, buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe static uint CalculateSse(uint adler, ReadOnlySpan buffer) { uint s = adler & 0xFFFF; uint s2 = (adler >> 16) & 0xFFFF; uint length = (uint)buffer.Length; uint num = length / 32; length -= num * 32; fixed (byte* reference = &MemoryMarshal.GetReference(buffer)) { fixed (byte* reference2 = &MemoryMarshal.GetReference(Tap1Tap2)) { byte* ptr = reference; Vector128 right = Sse2.LoadVector128((sbyte*)reference2); Vector128 right2 = Sse2.LoadVector128((sbyte*)(reference2 + 16)); Vector128 zero = Vector128.Zero; Vector128 right3 = Vector128.Create((short)1); while (num != 0) { uint num2 = 173u; if (num2 > num) { num2 = num; } num -= num2; Vector128 vector = Vector128.CreateScalar(s * num2); Vector128 left = Vector128.CreateScalar(s2); Vector128 vector2 = Vector128.Zero; do { Vector128 left2 = Sse3.LoadDquVector128(ptr); Vector128 left3 = Sse3.LoadDquVector128(ptr + 16); vector = Sse2.Add(vector, vector2); vector2 = Sse2.Add(vector2, Sse2.SumAbsoluteDifferences(left2, zero).AsUInt32()); Vector128 left4 = Ssse3.MultiplyAddAdjacent(left2, right); left = Sse2.Add(left, Sse2.MultiplyAddAdjacent(left4, right3).AsUInt32()); vector2 = Sse2.Add(vector2, Sse2.SumAbsoluteDifferences(left3, zero).AsUInt32()); Vector128 left5 = Ssse3.MultiplyAddAdjacent(left3, right2); left = Sse2.Add(left, Sse2.MultiplyAddAdjacent(left5, right3).AsUInt32()); ptr += 32; } while (--num2 != 0); left = Sse2.Add(left, Sse2.ShiftLeftLogical(vector, 5)); vector2 = Sse2.Add(vector2, Sse2.Shuffle(vector2, 78)); s += vector2.ToScalar(); left = Sse2.Add(left, Sse2.Shuffle(left, 177)); left = Sse2.Add(left, Sse2.Shuffle(left, 78)); s2 = left.ToScalar(); s %= 65521; s2 %= 65521; } if (length != 0) { HandleLeftOver(ptr, length, ref s, ref s2); } return s | (s2 << 16); } } } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public unsafe static uint CalculateAvx2(uint adler, ReadOnlySpan buffer) { uint s = adler & 0xFFFF; uint s2 = (adler >> 16) & 0xFFFF; uint num = (uint)buffer.Length; fixed (byte* reference = &MemoryMarshal.GetReference(buffer)) { byte* ptr = reference; Vector256 zero = Vector256.Zero; Vector256 right = Vector256.Create((short)1); Vector256 right2 = Vector256.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1); Vector256 vector = Vector256.CreateScalar(s); Vector256 vector2 = Vector256.CreateScalar(s2); while (num >= 32) { int num2 = (int)((num < 5552) ? num : 5552); num2 -= num2 % 32; num -= (uint)num2; Vector256 right3 = vector; Vector256 vector3 = Vector256.Zero; while (num2 >= 32) { Vector256 left = Avx.LoadVector256(ptr); Vector256 vector4 = Avx2.SumAbsoluteDifferences(left, zero); vector = Avx2.Add(vector, vector4.AsUInt32()); vector3 = Avx2.Add(vector3, right3); vector2 = Avx2.Add(Avx2.MultiplyAddAdjacent(Avx2.MultiplyAddAdjacent(left, right2), right).AsUInt32(), vector2); right3 = vector; ptr += 32; num2 -= 32; } vector3 = Avx2.ShiftLeftLogical(vector3, 5); vector2 = Avx2.Add(vector2, vector3); s = (uint)Numerics.EvenReduceSum(vector.AsInt32()); s2 = (uint)Numerics.ReduceSum(vector2.AsInt32()); s %= 65521; s2 %= 65521; vector = Vector256.CreateScalar(s); vector2 = Vector256.CreateScalar(s2); } if (num != 0) { HandleLeftOver(ptr, num, ref s, ref s2); } return s | (s2 << 16); } } private unsafe static void HandleLeftOver(byte* localBufferPtr, uint length, ref uint s1, ref uint s2) { if (length >= 16) { s2 += (s1 += *localBufferPtr); s2 += (s1 += localBufferPtr[1]); s2 += (s1 += localBufferPtr[2]); s2 += (s1 += localBufferPtr[3]); s2 += (s1 += localBufferPtr[4]); s2 += (s1 += localBufferPtr[5]); s2 += (s1 += localBufferPtr[6]); s2 += (s1 += localBufferPtr[7]); s2 += (s1 += localBufferPtr[8]); s2 += (s1 += localBufferPtr[9]); s2 += (s1 += localBufferPtr[10]); s2 += (s1 += localBufferPtr[11]); s2 += (s1 += localBufferPtr[12]); s2 += (s1 += localBufferPtr[13]); s2 += (s1 += localBufferPtr[14]); s2 += (s1 += localBufferPtr[15]); localBufferPtr += 16; length -= 16; } while (length-- != 0) { s2 += (s1 += *(localBufferPtr++)); } if (s1 >= 65521) { s1 -= 65521u; } s2 %= 65521u; } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe static uint CalculateScalar(uint adler, ReadOnlySpan buffer) { uint num = adler & 0xFFFF; uint num2 = (adler >> 16) & 0xFFFF; fixed (byte* ptr = buffer) { byte* ptr2 = ptr; uint num3 = (uint)buffer.Length; while (num3 != 0) { uint num4 = ((num3 < 5552) ? num3 : 5552u); num3 -= num4; while (num4 >= 16) { num2 += (num += *ptr2); num2 += (num += ptr2[1]); num2 += (num += ptr2[2]); num2 += (num += ptr2[3]); num2 += (num += ptr2[4]); num2 += (num += ptr2[5]); num2 += (num += ptr2[6]); num2 += (num += ptr2[7]); num2 += (num += ptr2[8]); num2 += (num += ptr2[9]); num2 += (num += ptr2[10]); num2 += (num += ptr2[11]); num2 += (num += ptr2[12]); num2 += (num += ptr2[13]); num2 += (num += ptr2[14]); num2 += (num += ptr2[15]); ptr2 += 16; num4 -= 16; } while (num4-- != 0) { num2 += (num += *(ptr2++)); } num %= 65521; num2 %= 65521; } return (num2 << 16) | num; } } } internal static class Crc32 { public const uint SeedValue = 0u; private const int MinBufferSize = 64; private const int ChunksizeMask = 15; private static readonly ulong[] K05Poly = new ulong[8] { 5708721108uL, 7631803798uL, 6259578832uL, 3433693342uL, 5969371428uL, 0uL, 7976584769uL, 8439010881uL }; private static readonly uint[] CrcTable = new uint[256] { 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u }; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Calculate(ReadOnlySpan buffer) { return Calculate(0u, buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Calculate(uint crc, ReadOnlySpan buffer) { if (buffer.IsEmpty) { return crc; } if (Sse41.IsSupported && Pclmulqdq.IsSupported && buffer.Length >= 64) { return ~CalculateSse(~crc, buffer); } return ~CalculateScalar(~crc, buffer); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe static uint CalculateSse(uint crc, ReadOnlySpan buffer) { int num = buffer.Length & -16; int num2 = num; fixed (byte* ptr = buffer) { fixed (ulong* k05Poly = K05Poly) { byte* ptr2 = ptr; ulong* ptr3 = k05Poly; Vector128 left = Sse2.LoadVector128((ulong*)ptr2); Vector128 vector = Sse2.LoadVector128((ulong*)ptr2 + 2); Vector128 vector2 = Sse2.LoadVector128((ulong*)ptr2 + 4); Vector128 vector3 = Sse2.LoadVector128((ulong*)ptr2 + 6); left = Sse2.Xor(left, Sse2.ConvertScalarToVector128UInt32(crc).AsUInt64()); Vector128 right = Sse2.LoadVector128(ptr3); ptr2 += 64; Vector128 right2; for (num2 -= 64; num2 >= 64; num2 -= 64) { right2 = Pclmulqdq.CarrylessMultiply(left, right, 0); Vector128 right3 = Pclmulqdq.CarrylessMultiply(vector, right, 0); Vector128 right4 = Pclmulqdq.CarrylessMultiply(vector2, right, 0); Vector128 right5 = Pclmulqdq.CarrylessMultiply(vector3, right, 0); left = Pclmulqdq.CarrylessMultiply(left, right, 17); vector = Pclmulqdq.CarrylessMultiply(vector, right, 17); vector2 = Pclmulqdq.CarrylessMultiply(vector2, right, 17); vector3 = Pclmulqdq.CarrylessMultiply(vector3, right, 17); Vector128 right6 = Sse2.LoadVector128((ulong*)ptr2); Vector128 right7 = Sse2.LoadVector128((ulong*)ptr2 + 2); Vector128 right8 = Sse2.LoadVector128((ulong*)ptr2 + 4); Vector128 right9 = Sse2.LoadVector128((ulong*)ptr2 + 6); left = Sse2.Xor(left, right2); vector = Sse2.Xor(vector, right3); vector2 = Sse2.Xor(vector2, right4); vector3 = Sse2.Xor(vector3, right5); left = Sse2.Xor(left, right6); vector = Sse2.Xor(vector, right7); vector2 = Sse2.Xor(vector2, right8); vector3 = Sse2.Xor(vector3, right9); ptr2 += 64; } right = Sse2.LoadVector128(k05Poly + 2); right2 = Pclmulqdq.CarrylessMultiply(left, right, 0); left = Pclmulqdq.CarrylessMultiply(left, right, 17); left = Sse2.Xor(left, vector); left = Sse2.Xor(left, right2); right2 = Pclmulqdq.CarrylessMultiply(left, right, 0); left = Pclmulqdq.CarrylessMultiply(left, right, 17); left = Sse2.Xor(left, vector2); left = Sse2.Xor(left, right2); right2 = Pclmulqdq.CarrylessMultiply(left, right, 0); left = Pclmulqdq.CarrylessMultiply(left, right, 17); left = Sse2.Xor(left, vector3); left = Sse2.Xor(left, right2); while (num2 >= 16) { vector = Sse2.LoadVector128((ulong*)ptr2); right2 = Pclmulqdq.CarrylessMultiply(left, right, 0); left = Pclmulqdq.CarrylessMultiply(left, right, 17); left = Sse2.Xor(left, vector); left = Sse2.Xor(left, right2); ptr2 += 16; num2 -= 16; } vector = Pclmulqdq.CarrylessMultiply(left, right, 16); vector2 = Vector128.Create(-1, 0, -1, 0).AsUInt64(); left = Sse2.ShiftRightLogical128BitLane(left, 8); left = Sse2.Xor(left, vector); right = Sse2.LoadScalarVector128(ptr3 + 4); vector = Sse2.ShiftRightLogical128BitLane(left, 4); left = Sse2.And(left, vector2); left = Pclmulqdq.CarrylessMultiply(left, right, 0); left = Sse2.Xor(left, vector); right = Sse2.LoadVector128(ptr3 + 6); vector = Sse2.And(left, vector2); vector = Pclmulqdq.CarrylessMultiply(vector, right, 16); vector = Sse2.And(vector, vector2); vector = Pclmulqdq.CarrylessMultiply(vector, right, 0); left = Sse2.Xor(left, vector); crc = (uint)Sse41.Extract(left.AsInt32(), 1); if (buffer.Length - num != 0) { return CalculateScalar(crc, buffer.Slice(num)); } return crc; } } } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private static uint CalculateScalar(uint crc, ReadOnlySpan buffer) { ref uint reference = ref MemoryMarshal.GetReference(CrcTable.AsSpan()); ref byte reference2 = ref MemoryMarshal.GetReference(buffer); for (int i = 0; i < buffer.Length; i++) { crc = Unsafe.Add(ref reference, (int)((crc ^ Unsafe.Add(ref reference2, i)) & 0xFF)) ^ (crc >> 8); } return crc; } } public enum DeflateCompressionLevel { Level0 = 0, NoCompression = 0, Level1 = 1, BestSpeed = 1, Level2 = 2, Level3 = 3, Level4 = 4, Level5 = 5, Level6 = 6, DefaultCompression = 6, Level7 = 7, Level8 = 8, Level9 = 9, BestCompression = 9 } internal sealed class Deflater : IDisposable { public enum CompressionLevel { BestCompression = 9, BestSpeed = 1, DefaultCompression = -1, NoCompression = 0, Deflated = 8 } public const int BestCompression = 9; public const int BestSpeed = 1; public const int DefaultCompression = -1; public const int NoCompression = 0; public const int Deflated = 8; private int level; private int state; private DeflaterEngine engine; private bool isDisposed; private const int IsFlushing = 4; private const int IsFinishing = 8; private const int BusyState = 16; private const int FlushingState = 20; private const int FinishingState = 28; private const int FinishedState = 30; private const int ClosedState = 127; public bool IsFinished { get { if (state == 30) { return engine.Pending.IsFlushed; } return false; } } public bool IsNeedingInput => engine.NeedsInput(); public Deflater(MemoryAllocator memoryAllocator, int level) { switch (level) { case -1: level = 6; break; default: throw new ArgumentOutOfRangeException("level"); case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: break; } engine = new DeflaterEngine(memoryAllocator, DeflateStrategy.Default); SetLevel(level); Reset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { state = 16; engine.Pending.Reset(); engine.Reset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Flush() { state |= 4; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Finish() { state |= 12; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetInput(byte[] input, int offset, int count) { if ((state & 8) != 0) { DeflateThrowHelper.ThrowAlreadyFinished(); } engine.SetInput(input, offset, count); } public void SetLevel(int level) { switch (level) { case -1: level = 6; break; default: throw new ArgumentOutOfRangeException("level"); case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: break; } if (this.level != level) { this.level = level; engine.SetLevel(level); } } public int Deflate(Span output, int offset, int length) { int num = length; if (state == 127) { DeflateThrowHelper.ThrowAlreadyClosed(); } while (true) { int num2 = engine.Pending.Flush(output, offset, length); offset += num2; length -= num2; if (length == 0 || state == 30) { break; } if (engine.Deflate((state & 4) != 0, (state & 8) != 0)) { continue; } switch (state) { case 16: return num - length; case 20: if (level != 0) { for (int num3 = 8 + (-engine.Pending.BitCount & 7); num3 > 0; num3 -= 10) { engine.Pending.WriteBits(2, 10); } } state = 16; break; case 28: engine.Pending.AlignToByte(); state = 30; break; } } return num - length; } public void Dispose() { if (!isDisposed) { engine.Dispose(); engine = null; isDisposed = true; } } } internal static class DeflaterConstants { public const bool DEBUGGING = false; public const int STORED_BLOCK = 0; public const int STATIC_TREES = 1; public const int DYN_TREES = 2; public const int PRESET_DICT = 32; public const int DEFAULT_MEM_LEVEL = 8; public const int MAX_MATCH = 258; public const int MIN_MATCH = 3; public const int MAX_WBITS = 15; public const int WSIZE = 32768; public const int WMASK = 32767; public const int HASH_BITS = 15; public const int HASH_SIZE = 32768; public const int HASH_MASK = 32767; public const int HASH_SHIFT = 5; public const int MIN_LOOKAHEAD = 262; public const int MAX_DIST = 32506; public const int PENDING_BUF_SIZE = 65536; public static int MAX_BLOCK_SIZE = Math.Min(65535, 65531); public const int DEFLATE_STORED = 0; public const int DEFLATE_FAST = 1; public const int DEFLATE_SLOW = 2; public static int[] GOOD_LENGTH = new int[10] { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 }; public static int[] MAX_LAZY = new int[10] { 0, 4, 5, 6, 4, 16, 16, 32, 128, 258 }; public static int[] NICE_LENGTH = new int[10] { 0, 8, 16, 32, 16, 32, 128, 128, 258, 258 }; public static int[] MAX_CHAIN = new int[10] { 0, 4, 8, 32, 16, 32, 128, 256, 1024, 4096 }; public static int[] COMPR_FUNC = new int[10] { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; } internal enum DeflateStrategy { Default, Filtered, HuffmanOnly } internal sealed class DeflaterEngine : IDisposable { private const int TooFar = 4096; private int insertHashIndex; private int matchStart; private int matchLen; private bool prevAvailable; private int blockStart; private int strstart; private int lookahead; private int compressionFunction; private byte[] inputBuf; private int inputOff; private int inputEnd; private readonly DeflateStrategy strategy; private DeflaterHuffman huffman; private bool isDisposed; private IMemoryOwner headMemoryOwner; private MemoryHandle headMemoryHandle; private readonly Memory head; private unsafe readonly short* pinnedHeadPointer; private IMemoryOwner prevMemoryOwner; private MemoryHandle prevMemoryHandle; private readonly Memory prev; private unsafe readonly short* pinnedPrevPointer; private IMemoryOwner windowMemoryOwner; private MemoryHandle windowMemoryHandle; private readonly Memory window; private unsafe readonly byte* pinnedWindowPointer; private int maxChain; private int maxLazy; private int niceLength; private int goodLength; public DeflaterPendingBuffer Pending { get; } public unsafe DeflaterEngine(MemoryAllocator memoryAllocator, DeflateStrategy strategy) { huffman = new DeflaterHuffman(memoryAllocator); Pending = huffman.Pending; this.strategy = strategy; windowMemoryOwner = memoryAllocator.Allocate(65536); window = windowMemoryOwner.Memory; windowMemoryHandle = window.Pin(); pinnedWindowPointer = (byte*)windowMemoryHandle.Pointer; headMemoryOwner = memoryAllocator.Allocate(32768); head = headMemoryOwner.Memory; headMemoryHandle = head.Pin(); pinnedHeadPointer = (short*)headMemoryHandle.Pointer; prevMemoryOwner = memoryAllocator.Allocate(32768); prev = prevMemoryOwner.Memory; prevMemoryHandle = prev.Pin(); pinnedPrevPointer = (short*)prevMemoryHandle.Pointer; blockStart = (strstart = 1); } public bool Deflate(bool flush, bool finish) { bool flag = false; do { FillWindow(); bool flush2 = flush && inputOff == inputEnd; switch (compressionFunction) { case 0: flag = DeflateStored(flush2, finish); break; case 1: flag = DeflateFast(flush2, finish); break; case 2: flag = DeflateSlow(flush2, finish); break; default: DeflateThrowHelper.ThrowUnknownCompression(); break; } } while (Pending.IsFlushed && flag); return flag; } public void SetInput(byte[] buffer, int offset, int count) { if (buffer == null) { DeflateThrowHelper.ThrowNull("buffer"); } if (offset < 0) { DeflateThrowHelper.ThrowOutOfRange("offset"); } if (count < 0) { DeflateThrowHelper.ThrowOutOfRange("count"); } if (inputOff < inputEnd) { DeflateThrowHelper.ThrowNotProcessed(); } int num = offset + count; if (offset > num || num > buffer.Length) { DeflateThrowHelper.ThrowOutOfRange("count"); } inputBuf = buffer; inputOff = offset; inputEnd = num; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool NeedsInput() { return inputEnd == inputOff; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { huffman.Reset(); blockStart = (strstart = 1); lookahead = 0; prevAvailable = false; matchLen = 2; Span span = head.Span; span = span.Slice(0, 32768); span.Clear(); span = prev.Span; span = span.Slice(0, 32768); span.Clear(); } public unsafe void SetLevel(int level) { if (level < 0 || level > 9) { DeflateThrowHelper.ThrowOutOfRange("level"); } goodLength = DeflaterConstants.GOOD_LENGTH[level]; maxLazy = DeflaterConstants.MAX_LAZY[level]; niceLength = DeflaterConstants.NICE_LENGTH[level]; maxChain = DeflaterConstants.MAX_CHAIN[level]; if (DeflaterConstants.COMPR_FUNC[level] == compressionFunction) { return; } switch (compressionFunction) { case 0: if (strstart > blockStart) { huffman.FlushStoredBlock(window.Span, blockStart, strstart - blockStart, lastBlock: false); blockStart = strstart; } UpdateHash(); break; case 1: if (strstart > blockStart) { huffman.FlushBlock(window.Span, blockStart, strstart - blockStart, lastBlock: false); blockStart = strstart; } break; case 2: if (prevAvailable) { huffman.TallyLit(pinnedWindowPointer[strstart - 1] & 0xFF); } if (strstart > blockStart) { huffman.FlushBlock(window.Span, blockStart, strstart - blockStart, lastBlock: false); blockStart = strstart; } prevAvailable = false; matchLen = 2; break; } compressionFunction = DeflaterConstants.COMPR_FUNC[level]; } public void FillWindow() { if (strstart >= 65274) { SlideWindow(); } if (lookahead < 262 && inputOff < inputEnd) { int num = 65536 - lookahead - strstart; if (num > inputEnd - inputOff) { num = inputEnd - inputOff; } Unsafe.CopyBlockUnaligned(ref window.Span[strstart + lookahead], in inputBuf[inputOff], (uint)num); inputOff += num; lookahead += num; } if (lookahead >= 3) { UpdateHash(); } } public void Dispose() { if (!isDisposed) { huffman.Dispose(); windowMemoryHandle.Dispose(); windowMemoryOwner.Dispose(); headMemoryHandle.Dispose(); headMemoryOwner.Dispose(); prevMemoryHandle.Dispose(); prevMemoryOwner.Dispose(); windowMemoryOwner = null; headMemoryOwner = null; prevMemoryOwner = null; huffman = null; isDisposed = true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void UpdateHash() { byte* ptr = pinnedWindowPointer; insertHashIndex = (ptr[strstart] << 5) ^ ptr[strstart + 1]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe int InsertString() { int num = ((insertHashIndex << 5) ^ pinnedWindowPointer[strstart + 2]) & 0x7FFF; short* ptr = pinnedHeadPointer; short num2 = (pinnedPrevPointer[strstart & 0x7FFF] = ptr[num]); ptr[num] = (short)strstart; insertHashIndex = num; return num2 & 0xFFFF; } private unsafe void SlideWindow() { Span span = window.Span; ref byte destination = ref span[0]; span = window.Span; Unsafe.CopyBlockUnaligned(ref destination, in span[32768], 32768u); matchStart -= 32768; strstart -= 32768; blockStart -= 32768; short* ptr = pinnedHeadPointer; for (int i = 0; i < 32768; i++) { int num = ptr[i] & 0xFFFF; ptr[i] = (short)((num >= 32768) ? (num - 32768) : 0); } short* ptr2 = pinnedPrevPointer; for (int j = 0; j < 32768; j++) { int num2 = ptr2[j] & 0xFFFF; ptr2[j] = (short)((num2 >= 32768) ? (num2 - 32768) : 0); } } [MethodImpl(MethodImplOptions.AggressiveOptimization)] private unsafe bool FindLongestMatch(int curMatch) { int num = strstart; int num2 = num + Math.Min(258, lookahead) - 1; int num3 = Math.Max(num - 32506, 0); int num4 = maxChain; int num5 = Math.Min(niceLength, lookahead); int num6 = matchStart; int val = matchLen; val = (matchLen = Math.Max(val, 2)); if (num > num2 - val) { return false; } int num7 = num + val; byte* ptr = pinnedWindowPointer; int num8 = strstart; byte b = ptr[num7 - 1]; byte b2 = ptr[num7]; if (val >= goodLength) { num4 >>= 2; } short* ptr2 = pinnedPrevPointer; do { int num9 = curMatch; num = num8; int num10 = num9 + val; if (ptr[num10] != b2 || ptr[num10 - 1] != b || ptr[num9] != ptr[num] || ptr[++num9] != ptr[++num]) { continue; } switch ((num2 - num) & 7) { case 1: if (ptr[++num] != ptr[++num9]) { } break; case 2: if (ptr[++num] == ptr[++num9] && ptr[++num] != ptr[++num9]) { } break; case 3: if (ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] != ptr[++num9]) { } break; case 4: if (ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] != ptr[++num9]) { } break; case 5: if (ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] != ptr[++num9]) { } break; case 6: if (ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] != ptr[++num9]) { } break; case 7: if (ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9]) { _ = ptr[++num]; _ = ptr[++num9]; } break; } if (ptr[num] == ptr[num9]) { do { if (num == num2) { num++; num9++; break; } } while (ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9] && ptr[++num] == ptr[++num9]); } if (num - num8 > val) { num6 = curMatch; val = num - num8; if (val >= num5) { break; } b = ptr[num - 1]; b2 = ptr[num]; } } while ((curMatch = ptr2[curMatch & 0x7FFF] & 0xFFFF) > num3 && --num4 != 0); matchStart = num6; matchLen = val; return val >= 3; } private bool DeflateStored(bool flush, bool finish) { if (!flush && lookahead == 0) { return false; } strstart += lookahead; lookahead = 0; int num = strstart - blockStart; if (num >= DeflaterConstants.MAX_BLOCK_SIZE || (blockStart < 32768 && num >= 32506) || flush) { bool flag = finish; if (num > DeflaterConstants.MAX_BLOCK_SIZE) { num = DeflaterConstants.MAX_BLOCK_SIZE; flag = false; } huffman.FlushStoredBlock(window.Span, blockStart, num, flag); blockStart += num; if (!flag) { return num != 0; } return false; } return true; } private unsafe bool DeflateFast(bool flush, bool finish) { if (lookahead < 262 && !flush) { return false; } while (lookahead >= 262 || flush) { if (lookahead == 0) { huffman.FlushBlock(window.Span, blockStart, strstart - blockStart, finish); blockStart = strstart; return false; } if (strstart > 65274) { SlideWindow(); } int num; if (lookahead >= 3 && (num = InsertString()) != 0 && strategy != DeflateStrategy.HuffmanOnly && strstart - num <= 32506 && FindLongestMatch(num)) { bool flag = huffman.TallyDist(strstart - matchStart, matchLen); lookahead -= matchLen; if (matchLen <= maxLazy && lookahead >= 3) { while (--matchLen > 0) { strstart++; InsertString(); } strstart++; } else { strstart += matchLen; if (lookahead >= 2) { UpdateHash(); } } matchLen = 2; if (!flag) { continue; } } else { huffman.TallyLit(pinnedWindowPointer[strstart] & 0xFF); strstart++; lookahead--; } if (huffman.IsFull()) { bool flag2 = finish && lookahead == 0; huffman.FlushBlock(window.Span, blockStart, strstart - blockStart, flag2); blockStart = strstart; return !flag2; } } return true; } private unsafe bool DeflateSlow(bool flush, bool finish) { if (lookahead < 262 && !flush) { return false; } while (lookahead >= 262 || flush) { if (lookahead == 0) { if (prevAvailable) { huffman.TallyLit(pinnedWindowPointer[strstart - 1] & 0xFF); } prevAvailable = false; huffman.FlushBlock(window.Span, blockStart, strstart - blockStart, finish); blockStart = strstart; return false; } if (strstart >= 65274) { SlideWindow(); } int num = matchStart; int num2 = matchLen; if (lookahead >= 3) { int num3 = InsertString(); if (strategy != DeflateStrategy.HuffmanOnly && num3 != 0 && strstart - num3 <= 32506 && FindLongestMatch(num3) && matchLen <= 5 && (strategy == DeflateStrategy.Filtered || (matchLen == 3 && strstart - matchStart > 4096))) { matchLen = 2; } } if (num2 >= 3 && matchLen <= num2) { huffman.TallyDist(strstart - 1 - num, num2); num2 -= 2; do { strstart++; lookahead--; if (lookahead >= 3) { InsertString(); } } while (--num2 > 0); strstart++; lookahead--; prevAvailable = false; matchLen = 2; } else { if (prevAvailable) { huffman.TallyLit(pinnedWindowPointer[strstart - 1] & 0xFF); } prevAvailable = true; strstart++; lookahead--; } if (huffman.IsFull()) { int num4 = strstart - blockStart; if (prevAvailable) { num4--; } bool flag = finish && lookahead == 0 && !prevAvailable; huffman.FlushBlock(window.Span, blockStart, num4, flag); blockStart += num4; return !flag; } } return true; } } internal sealed class DeflaterHuffman : IDisposable { private sealed class Tree : IDisposable { private readonly int minNumCodes; private readonly int[] bitLengthCounts; private readonly int maxLength; private bool isDisposed; private readonly int elementCount; private readonly MemoryAllocator memoryAllocator; private IMemoryOwner codesMemoryOwner; private MemoryHandle codesMemoryHandle; private unsafe readonly short* codes; private IMemoryOwner frequenciesMemoryOwner; private MemoryHandle frequenciesMemoryHandle; private IMemoryOwner lengthsMemoryOwner; private MemoryHandle lengthsMemoryHandle; public int NumCodes { get; private set; } public unsafe short* Frequencies { get; } public unsafe byte* Length { get; } public unsafe Tree(MemoryAllocator memoryAllocator, int elements, int minCodes, int maxLength) { this.memoryAllocator = memoryAllocator; elementCount = elements; minNumCodes = minCodes; this.maxLength = maxLength; frequenciesMemoryOwner = memoryAllocator.Allocate(elements); frequenciesMemoryHandle = frequenciesMemoryOwner.Memory.Pin(); Frequencies = (short*)frequenciesMemoryHandle.Pointer; lengthsMemoryOwner = memoryAllocator.Allocate(elements); lengthsMemoryHandle = lengthsMemoryOwner.Memory.Pin(); Length = (byte*)lengthsMemoryHandle.Pointer; codesMemoryOwner = memoryAllocator.Allocate(elements); codesMemoryHandle = codesMemoryOwner.Memory.Pin(); codes = (short*)codesMemoryHandle.Pointer; bitLengthCounts = new int[maxLength]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { Memory memory = frequenciesMemoryOwner.Memory; memory.Span.Clear(); lengthsMemoryOwner.Memory.Span.Clear(); memory = codesMemoryOwner.Memory; memory.Span.Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteSymbol(DeflaterPendingBuffer pendingBuffer, int code) { pendingBuffer.WriteBits(codes[code] & 0xFFFF, Length[code]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetStaticCodes(ReadOnlySpan staticCodes, ReadOnlySpan staticLengths) { staticCodes.CopyTo(codesMemoryOwner.Memory.Span); staticLengths.CopyTo(lengthsMemoryOwner.Memory.Span); } public unsafe void BuildCodes() { ref int reference = ref MemoryMarshal.GetReference(stackalloc int[maxLength]); ref int reference2 = ref MemoryMarshal.GetReference(bitLengthCounts); int num = 0; for (int i = 0; i < maxLength; i++) { Unsafe.Add(ref reference, i) = num; num += Unsafe.Add(ref reference2, i) << 15 - i; } for (int j = 0; j < NumCodes; j++) { int num2 = Length[j]; if (num2 > 0) { codes[j] = BitReverse(Unsafe.Add(ref reference, num2 - 1)); Unsafe.Add(ref reference, num2 - 1) += 1 << 16 - num2; } } } [MethodImpl(MethodImplOptions.AggressiveOptimization)] public unsafe void BuildTree() { int num = elementCount; using IMemoryOwner memoryOwner = memoryAllocator.Allocate(num); Memory memory = memoryOwner.Memory; ref int reference = ref MemoryMarshal.GetReference(memory.Span); int num2 = 0; int num3 = 0; for (int i = 0; i < num; i++) { int num4 = Frequencies[i]; if (num4 != 0) { int num5 = num2++; int num6; while (num5 > 0 && Frequencies[Unsafe.Add(ref reference, num6 = num5 - 1 >> 1)] > num4) { Unsafe.Add(ref reference, num5) = Unsafe.Add(ref reference, num6); num5 = num6; } Unsafe.Add(ref reference, num5) = i; num3 = i; } } while (num2 < 2) { Unsafe.Add(ref reference, num2++) = ((num3 < 2) ? (++num3) : 0); } NumCodes = Math.Max(num3 + 1, minNumCodes); int num7 = num2; int num8 = 4 * num2 - 2; using IMemoryOwner memoryOwner2 = memoryAllocator.Allocate(num8); using IMemoryOwner memoryOwner3 = memoryAllocator.Allocate(2 * num2 - 1); memory = memoryOwner2.Memory; ref int reference2 = ref MemoryMarshal.GetReference(memory.Span); memory = memoryOwner3.Memory; ref int reference3 = ref MemoryMarshal.GetReference(memory.Span); int num9 = num7; for (int j = 0; j < num2; j++) { int num10 = Unsafe.Add(ref reference, j); int num11 = 2 * j; Unsafe.Add(ref reference2, num11) = num10; Unsafe.Add(ref reference2, num11 + 1) = -1; Unsafe.Add(ref reference3, j) = Frequencies[num10] << 8; Unsafe.Add(ref reference, j) = j; } do { int num12 = Unsafe.Add(ref reference, 0); int num13 = Unsafe.Add(ref reference, --num2); int num14 = 0; int num15; for (num15 = 1; num15 < num2; num15 = num15 * 2 + 1) { if (num15 + 1 < num2 && Unsafe.Add(ref reference3, Unsafe.Add(ref reference, num15)) > Unsafe.Add(ref reference3, Unsafe.Add(ref reference, num15 + 1))) { num15++; } Unsafe.Add(ref reference, num14) = Unsafe.Add(ref reference, num15); num14 = num15; } int num16 = Unsafe.Add(ref reference3, num13); while ((num15 = num14) > 0 && Unsafe.Add(ref reference3, Unsafe.Add(ref reference, num14 = num15 - 1 >> 1)) > num16) { Unsafe.Add(ref reference, num15) = Unsafe.Add(ref reference, num14); } Unsafe.Add(ref reference, num15) = num13; int num17 = Unsafe.Add(ref reference, 0); num13 = num9++; Unsafe.Add(ref reference2, 2 * num13) = num12; Unsafe.Add(ref reference2, 2 * num13 + 1) = num17; int num18 = Math.Min(Unsafe.Add(ref reference3, num12) & 0xFF, Unsafe.Add(ref reference3, num17) & 0xFF); num16 = (Unsafe.Add(ref reference3, num13) = Unsafe.Add(ref reference3, num12) + Unsafe.Add(ref reference3, num17) - num18 + 1); num14 = 0; for (num15 = 1; num15 < num2; num15 = num14 * 2 + 1) { if (num15 + 1 < num2 && Unsafe.Add(ref reference3, Unsafe.Add(ref reference, num15)) > Unsafe.Add(ref reference3, Unsafe.Add(ref reference, num15 + 1))) { num15++; } Unsafe.Add(ref reference, num14) = Unsafe.Add(ref reference, num15); num14 = num15; } while ((num15 = num14) > 0 && Unsafe.Add(ref reference3, Unsafe.Add(ref reference, num14 = num15 - 1 >> 1)) > num16) { Unsafe.Add(ref reference, num15) = Unsafe.Add(ref reference, num14); } Unsafe.Add(ref reference, num15) = num13; } while (num2 > 1); if (Unsafe.Add(ref reference, 0) != (num8 >> 1) - 1) { DeflateThrowHelper.ThrowHeapViolated(); } memory = memoryOwner2.Memory; BuildLength(memory.Span); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe int GetEncodedLength() { int num = 0; for (int i = 0; i < elementCount; i++) { num += Frequencies[i] * Length[i]; } return num; } public unsafe void CalcBLFreq(Tree blTree) { int num = -1; int num2 = 0; while (num2 < NumCodes) { int num3 = 1; int num4 = Length[num2]; int num5; int num6; if (num4 == 0) { num5 = 138; num6 = 3; } else { num5 = 6; num6 = 3; if (num != num4) { blTree.Frequencies[num4]++; num3 = 0; } } num = num4; num2++; while (num2 < NumCodes && num == Length[num2]) { num2++; if (++num3 >= num5) { break; } } if (num3 < num6) { blTree.Frequencies[num] += (short)num3; } else if (num != 0) { blTree.Frequencies[16]++; } else if (num3 <= 10) { blTree.Frequencies[17]++; } else { blTree.Frequencies[18]++; } } } public unsafe void WriteTree(DeflaterPendingBuffer pendingBuffer, Tree bitLengthTree) { int num = -1; int num2 = 0; while (num2 < NumCodes) { int num3 = 1; int num4 = Length[num2]; int num5; int num6; if (num4 == 0) { num5 = 138; num6 = 3; } else { num5 = 6; num6 = 3; if (num != num4) { bitLengthTree.WriteSymbol(pendingBuffer, num4); num3 = 0; } } num = num4; num2++; while (num2 < NumCodes && num == Length[num2]) { num2++; if (++num3 >= num5) { break; } } if (num3 < num6) { while (num3-- > 0) { bitLengthTree.WriteSymbol(pendingBuffer, num); } } else if (num != 0) { bitLengthTree.WriteSymbol(pendingBuffer, 16); pendingBuffer.WriteBits(num3 - 3, 2); } else if (num3 <= 10) { bitLengthTree.WriteSymbol(pendingBuffer, 17); pendingBuffer.WriteBits(num3 - 3, 3); } else { bitLengthTree.WriteSymbol(pendingBuffer, 18); pendingBuffer.WriteBits(num3 - 11, 7); } } } private unsafe void BuildLength(ReadOnlySpan children) { byte* length = Length; ref int reference = ref MemoryMarshal.GetReference(children); ref int reference2 = ref MemoryMarshal.GetReference(bitLengthCounts); int num = maxLength; int num2 = children.Length >> 1; int num3 = num2 + 1 >> 1; int num4 = 0; Array.Clear(bitLengthCounts, 0, num); using (IMemoryOwner memoryOwner = memoryAllocator.Allocate(num2, AllocationOptions.Clean)) { ref int reference3 = ref MemoryMarshal.GetReference(memoryOwner.Memory.Span); for (int num5 = num2 - 1; num5 >= 0; num5--) { if (children[2 * num5 + 1] != -1) { int num6 = Unsafe.Add(ref reference3, num5) + 1; if (num6 > num) { num6 = num; num4++; } Unsafe.Add(ref reference3, Unsafe.Add(ref reference, 2 * num5)) = (Unsafe.Add(ref reference3, Unsafe.Add(ref reference, 2 * num5 + 1)) = num6); } else { int num7 = Unsafe.Add(ref reference3, num5); Unsafe.Add(ref reference2, num7 - 1)++; length[Unsafe.Add(ref reference, 2 * num5)] = (byte)Unsafe.Add(ref reference3, num5); } } } if (num4 == 0) { return; } int num8 = num - 1; while (true) { if (Unsafe.Add(ref reference2, --num8) != 0) { do { Unsafe.Add(ref reference2, num8)--; Unsafe.Add(ref reference2, ++num8)++; num4 -= 1 << num - 1 - num8; } while (num4 > 0 && num8 < num - 1); if (num4 <= 0) { break; } } } Unsafe.Add(ref reference2, num - 1) += num4; Unsafe.Add(ref reference2, num - 2) -= num4; int num9 = 2 * num3; for (int num10 = num; num10 != 0; num10--) { int num11 = Unsafe.Add(ref reference2, num10 - 1); while (num11 > 0) { int num12 = 2 * Unsafe.Add(ref reference, num9++); if (Unsafe.Add(ref reference, num12 + 1) == -1) { length[Unsafe.Add(ref reference, num12)] = (byte)num10; num11--; } } } } public void Dispose() { if (!isDisposed) { frequenciesMemoryHandle.Dispose(); frequenciesMemoryOwner.Dispose(); lengthsMemoryHandle.Dispose(); lengthsMemoryOwner.Dispose(); codesMemoryHandle.Dispose(); codesMemoryOwner.Dispose(); frequenciesMemoryOwner = null; lengthsMemoryOwner = null; codesMemoryOwner = null; isDisposed = true; } } } private const int BufferSize = 16384; private const int LiteralNumber = 286; private const int DistanceNumber = 30; private const int BitLengthNumber = 19; private const int Repeat3To6 = 16; private const int Repeat3To10 = 17; private const int Repeat11To138 = 18; private const int EofSymbol = 256; private Tree literalTree; private Tree distTree; private Tree blTree; private readonly IMemoryOwner distanceMemoryOwner; private unsafe readonly short* pinnedDistanceBuffer; private MemoryHandle distanceBufferHandle; private readonly IMemoryOwner literalMemoryOwner; private unsafe readonly short* pinnedLiteralBuffer; private MemoryHandle literalBufferHandle; private int lastLiteral; private int extraBits; private bool isDisposed; private static readonly short[] StaticLCodes = new short[286] { 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 19, 275, 147, 403, 83, 339, 211, 467, 51, 307, 179, 435, 115, 371, 243, 499, 11, 267, 139, 395, 75, 331, 203, 459, 43, 299, 171, 427, 107, 363, 235, 491, 27, 283, 155, 411, 91, 347, 219, 475, 59, 315, 187, 443, 123, 379, 251, 507, 7, 263, 135, 391, 71, 327, 199, 455, 39, 295, 167, 423, 103, 359, 231, 487, 23, 279, 151, 407, 87, 343, 215, 471, 55, 311, 183, 439, 119, 375, 247, 503, 15, 271, 143, 399, 79, 335, 207, 463, 47, 303, 175, 431, 111, 367, 239, 495, 31, 287, 159, 415, 95, 351, 223, 479, 63, 319, 191, 447, 127, 383, 255, 511, 0, 64, 32, 96, 16, 80, 48, 112, 8, 72, 40, 104, 24, 88, 56, 120, 4, 68, 36, 100, 20, 84, 52, 116, 3, 131, 67, 195, 35, 163 }; private static readonly short[] StaticDCodes = new short[30] { 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23 }; private static ReadOnlySpan StaticLLength => new byte[286] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8 }; private static ReadOnlySpan StaticDLength => new byte[30] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; private static ReadOnlySpan BitLengthOrder => new byte[19] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; private static ReadOnlySpan Bit4Reverse => new byte[16] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; public DeflaterPendingBuffer Pending { get; private set; } public unsafe DeflaterHuffman(MemoryAllocator memoryAllocator) { Pending = new DeflaterPendingBuffer(memoryAllocator); literalTree = new Tree(memoryAllocator, 286, 257, 15); distTree = new Tree(memoryAllocator, 30, 1, 15); blTree = new Tree(memoryAllocator, 19, 4, 7); distanceMemoryOwner = memoryAllocator.Allocate(16384); distanceBufferHandle = distanceMemoryOwner.Memory.Pin(); pinnedDistanceBuffer = (short*)distanceBufferHandle.Pointer; literalMemoryOwner = memoryAllocator.Allocate(16384); literalBufferHandle = literalMemoryOwner.Memory.Pin(); pinnedLiteralBuffer = (short*)literalBufferHandle.Pointer; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { lastLiteral = 0; extraBits = 0; literalTree.Reset(); distTree.Reset(); blTree.Reset(); } public unsafe void SendAllTrees(int blTreeCodes) { blTree.BuildCodes(); literalTree.BuildCodes(); distTree.BuildCodes(); Pending.WriteBits(literalTree.NumCodes - 257, 5); Pending.WriteBits(distTree.NumCodes - 1, 5); Pending.WriteBits(blTreeCodes - 4, 4); for (int i = 0; i < blTreeCodes; i++) { Pending.WriteBits(blTree.Length[(int)BitLengthOrder[i]], 3); } literalTree.WriteTree(Pending, blTree); distTree.WriteTree(Pending, blTree); } public unsafe void CompressBlock() { DeflaterPendingBuffer pending = Pending; short* ptr = pinnedDistanceBuffer; short* ptr2 = pinnedLiteralBuffer; for (int i = 0; i < lastLiteral; i++) { int num = ptr2[i] & 0xFF; int num2 = ptr[i]; if (num2-- != 0) { int num3 = Lcode(num); literalTree.WriteSymbol(pending, num3); int num4 = (num3 - 261) / 4; if (num4 > 0 && num4 <= 5) { Pending.WriteBits(num & ((1 << num4) - 1), num4); } int num5 = Dcode(num2); distTree.WriteSymbol(pending, num5); num4 = (num5 >> 1) - 1; if (num4 > 0) { Pending.WriteBits(num2 & ((1 << num4) - 1), num4); } } else { literalTree.WriteSymbol(pending, num); } } literalTree.WriteSymbol(pending, 256); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FlushStoredBlock(ReadOnlySpan stored, int storedOffset, int storedLength, bool lastBlock) { Pending.WriteBits(lastBlock ? 1 : 0, 3); Pending.AlignToByte(); Pending.WriteShort(storedLength); Pending.WriteShort(~storedLength); Pending.WriteBlock(stored, storedOffset, storedLength); Reset(); } public unsafe void FlushBlock(ReadOnlySpan stored, int storedOffset, int storedLength, bool lastBlock) { literalTree.Frequencies[256]++; literalTree.BuildTree(); distTree.BuildTree(); literalTree.CalcBLFreq(blTree); distTree.CalcBLFreq(blTree); blTree.BuildTree(); int num = 4; for (int num2 = 18; num2 > num; num2--) { if (blTree.Length[(int)BitLengthOrder[num2]] > 0) { num = num2 + 1; } } int num3 = 14 + num * 3 + blTree.GetEncodedLength() + literalTree.GetEncodedLength() + distTree.GetEncodedLength() + extraBits; int num4 = extraBits; ref byte reference = ref MemoryMarshal.GetReference(StaticLLength); for (int i = 0; i < 286; i++) { num4 += literalTree.Frequencies[i] * Unsafe.Add(ref reference, i); } ref byte reference2 = ref MemoryMarshal.GetReference(StaticDLength); for (int j = 0; j < 30; j++) { num4 += distTree.Frequencies[j] * Unsafe.Add(ref reference2, j); } if (num3 >= num4) { num3 = num4; } if (storedOffset >= 0 && storedLength + 4 < num3 >> 3) { FlushStoredBlock(stored, storedOffset, storedLength, lastBlock); } else if (num3 == num4) { Pending.WriteBits(2 + (lastBlock ? 1 : 0), 3); literalTree.SetStaticCodes(StaticLCodes, StaticLLength); distTree.SetStaticCodes(StaticDCodes, StaticDLength); CompressBlock(); Reset(); } else { Pending.WriteBits(4 + (lastBlock ? 1 : 0), 3); SendAllTrees(num); CompressBlock(); Reset(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsFull() { return lastLiteral >= 16384; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe bool TallyLit(int literal) { pinnedDistanceBuffer[lastLiteral] = 0; pinnedLiteralBuffer[lastLiteral++] = (byte)literal; literalTree.Frequencies[literal]++; return IsFull(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe bool TallyDist(int distance, int length) { pinnedDistanceBuffer[lastLiteral] = (short)distance; pinnedLiteralBuffer[lastLiteral++] = (byte)(length - 3); int num = Lcode(length - 3); literalTree.Frequencies[num]++; if (num >= 265 && num < 285) { extraBits += (num - 261) / 4; } int num2 = Dcode(distance - 1); distTree.Frequencies[num2]++; if (num2 >= 4) { extraBits += (num2 >> 1) - 1; } return IsFull(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short BitReverse(int toReverse) { int num = toReverse >> 12; Guard.MustBeLessThanOrEqualTo((uint)num, 15u, "toReverse"); ref byte reference = ref MemoryMarshal.GetReference(Bit4Reverse); return (short)((Unsafe.Add(ref reference, toReverse & 0xF) << 12) | (Unsafe.Add(ref reference, (toReverse >> 4) & 0xF) << 8) | (Unsafe.Add(ref reference, (toReverse >> 8) & 0xF) << 4) | Unsafe.Add(ref reference, num)); } public void Dispose() { if (!isDisposed) { Pending.Dispose(); distanceBufferHandle.Dispose(); distanceMemoryOwner.Dispose(); literalBufferHandle.Dispose(); literalMemoryOwner.Dispose(); literalTree.Dispose(); blTree.Dispose(); distTree.Dispose(); Pending = null; literalTree = null; blTree = null; distTree = null; isDisposed = true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Lcode(int length) { if (length == 255) { return 285; } int num = 257; while (length >= 8) { num += 4; length >>= 1; } return num + length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Dcode(int distance) { int num = 0; while (distance >= 4) { num += 2; distance >>= 1; } return num + distance; } } internal sealed class DeflaterOutputStream : Stream { private const int BufferLength = 512; private IMemoryOwner memoryOwner; private readonly Memory buffer; private Deflater deflater; private readonly Stream rawStream; private bool isDisposed; public override bool CanRead => false; public override bool CanSeek => false; public override bool CanWrite => rawStream.CanWrite; public override long Length => rawStream.Length; public override long Position { get { return rawStream.Position; } set { throw new NotSupportedException(); } } public DeflaterOutputStream(MemoryAllocator memoryAllocator, Stream rawStream, int compressionLevel) { this.rawStream = rawStream; memoryOwner = memoryAllocator.Allocate(512); buffer = memoryOwner.Memory; deflater = new Deflater(memoryAllocator, compressionLevel); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override int ReadByte() { throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override void Flush() { deflater.Flush(); Deflate(flushing: true); rawStream.Flush(); } public override void Write(byte[] buffer, int offset, int count) { deflater.SetInput(buffer, offset, count); Deflate(); } private void Deflate() { Deflate(flushing: false); } private void Deflate(bool flushing) { while (flushing || !deflater.IsNeedingInput) { int num = deflater.Deflate(buffer.Span, 0, 512); if (num <= 0) { break; } rawStream.Write(buffer.Span.Slice(0, num)); } if (!deflater.IsNeedingInput) { DeflateThrowHelper.ThrowNoDeflate(); } } private void Finish() { deflater.Finish(); while (!deflater.IsFinished) { int num = deflater.Deflate(buffer.Span, 0, 512); if (num <= 0) { break; } rawStream.Write(buffer.Span.Slice(0, num)); } if (!deflater.IsFinished) { DeflateThrowHelper.ThrowNoDeflate(); } rawStream.Flush(); } protected override void Dispose(bool disposing) { if (!isDisposed) { if (disposing) { Finish(); deflater.Dispose(); memoryOwner.Dispose(); } deflater = null; memoryOwner = null; isDisposed = true; base.Dispose(disposing); } } } internal sealed class DeflaterPendingBuffer : IDisposable { private readonly Memory buffer; private unsafe readonly byte* pinnedBuffer; private IMemoryOwner bufferMemoryOwner; private MemoryHandle bufferMemoryHandle; private int start; private int end; private uint bits; private bool isDisposed; public int BitCount { get; private set; } public bool IsFlushed => end == 0; public unsafe DeflaterPendingBuffer(MemoryAllocator memoryAllocator) { bufferMemoryOwner = memoryAllocator.Allocate(65536); buffer = bufferMemoryOwner.Memory; bufferMemoryHandle = buffer.Pin(); pinnedBuffer = (byte*)bufferMemoryHandle.Pointer; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { int num = (BitCount = 0); start = (end = num); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteShort(int value) { byte* num = pinnedBuffer; num[end++] = (byte)value; num[end++] = (byte)(value >> 8); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteBlock(ReadOnlySpan block, int offset, int length) { Unsafe.CopyBlockUnaligned(ref buffer.Span[end], in MemoryMarshal.GetReference(block.Slice(offset)), (uint)length); end += length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void AlignToByte() { if (BitCount > 0) { byte* ptr = pinnedBuffer; ptr[end++] = (byte)bits; if (BitCount > 8) { ptr[end++] = (byte)(bits >> 8); } } bits = 0u; BitCount = 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBits(int b, int count) { bits |= (uint)(b << BitCount); BitCount += count; if (BitCount >= 16) { byte* num = pinnedBuffer; num[end++] = (byte)bits; num[end++] = (byte)(bits >> 8); bits >>= 16; BitCount -= 16; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteShortMSB(int value) { byte* num = pinnedBuffer; num[end++] = (byte)(value >> 8); num[end++] = (byte)value; } public unsafe int Flush(Span output, int offset, int length) { if (BitCount >= 8) { pinnedBuffer[end++] = (byte)bits; bits >>= 8; BitCount -= 8; } if (length > end - start) { length = end - start; Unsafe.CopyBlockUnaligned(ref output[offset], in buffer.Span[start], (uint)length); start = 0; end = 0; } else { Unsafe.CopyBlockUnaligned(ref output[offset], in buffer.Span[start], (uint)length); start += length; } return length; } public void Dispose() { if (!isDisposed) { bufferMemoryHandle.Dispose(); bufferMemoryOwner.Dispose(); bufferMemoryOwner = null; isDisposed = true; } } } internal static class DeflateThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowAlreadyFinished() { throw new InvalidOperationException("Finish() already called."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowAlreadyClosed() { throw new InvalidOperationException("Deflator already closed."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowUnknownCompression() { throw new InvalidOperationException("Unknown compression function."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNotProcessed() { throw new InvalidOperationException("Old input was not completely processed."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNull(string name) { throw new ArgumentNullException(name); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowOutOfRange(string name) { throw new ArgumentOutOfRangeException(name); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowHeapViolated() { throw new InvalidOperationException("Huffman heap invariant violated."); } [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowNoDeflate() { throw new ImageFormatException("Cannot deflate all input."); } } internal sealed class ZlibDeflateStream : Stream { private readonly Stream rawStream; private uint adler = 1u; private bool isDisposed; private DeflaterOutputStream deflateStream; public override bool CanRead => false; public override bool CanSeek => false; public override bool CanWrite => rawStream.CanWrite; public override long Length => rawStream.Length; public override long Position { get { return rawStream.Position; } set { throw new NotSupportedException(); } } public ZlibDeflateStream(MemoryAllocator memoryAllocator, Stream stream, DeflateCompressionLevel level) : this(memoryAllocator, stream, (PngCompressionLevel)level) { } public ZlibDeflateStream(MemoryAllocator memoryAllocator, Stream stream, PngCompressionLevel level) { rawStream = stream; int num = 218; if (level >= PngCompressionLevel.Level5 && level <= PngCompressionLevel.Level6) { num = 156; } else if (level >= PngCompressionLevel.Level3 && level <= PngCompressionLevel.Level4) { num = 94; } else if (level <= PngCompressionLevel.Level2) { num = 1; } num -= (30720 + num) % 31; if (num < 0) { num += 31; } rawStream.WriteByte(120); rawStream.WriteByte((byte)num); deflateStream = new DeflaterOutputStream(memoryAllocator, rawStream, (int)level); } public override void Flush() { deflateStream.Flush(); } public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void Write(byte[] buffer, int offset, int count) { deflateStream.Write(buffer, offset, count); adler = Adler32.Calculate(adler, buffer.AsSpan(offset, count)); } protected override void Dispose(bool disposing) { if (!isDisposed) { if (disposing) { deflateStream.Dispose(); uint num = adler; rawStream.WriteByte((byte)((num >> 24) & 0xFF)); rawStream.WriteByte((byte)((num >> 16) & 0xFF)); rawStream.WriteByte((byte)((num >> 8) & 0xFF)); rawStream.WriteByte((byte)(num & 0xFF)); } deflateStream = null; base.Dispose(disposing); isDisposed = true; } } } internal sealed class ZlibInflateStream : Stream { private static readonly byte[] ChecksumBuffer = new byte[4]; private static readonly Func GetDataNoOp = () => 0; private readonly BufferedReadStream innerStream; private bool isDisposed; private int currentDataRemaining; private readonly Func getData; public override bool CanRead => innerStream.CanRead; public override bool CanSeek => false; public override bool CanWrite { get { throw new NotSupportedException(); } } public override long Length { get { throw new NotSupportedException(); } } public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } public DeflateStream CompressedStream { get; private set; } public ZlibInflateStream(BufferedReadStream innerStream) : this(innerStream, GetDataNoOp) { } public ZlibInflateStream(BufferedReadStream innerStream, Func getData) { this.innerStream = innerStream; this.getData = getData; } public bool AllocateNewBytes(int bytes, bool isCriticalChunk) { currentDataRemaining = bytes; if (CompressedStream == null) { return InitializeInflateStream(isCriticalChunk); } return true; } public override void Flush() { throw new NotSupportedException(); } public override int ReadByte() { currentDataRemaining--; return innerStream.ReadByte(); } public override int Read(byte[] buffer, int offset, int count) { if (currentDataRemaining == 0) { currentDataRemaining = getData(); if (currentDataRemaining == 0) { return 0; } } int num = Math.Min(count, currentDataRemaining); currentDataRemaining -= num; int num2 = innerStream.Read(buffer, offset, num); long length = innerStream.Length; int num3 = 0; offset += num2; while (currentDataRemaining == 0 && num2 < count) { currentDataRemaining = getData(); if (currentDataRemaining == 0) { return num2; } offset += num3; if (offset >= length || offset >= count) { return num2; } num = Math.Min(count - num2, currentDataRemaining); currentDataRemaining -= num; num3 = innerStream.Read(buffer, offset, num); if (num3 == 0) { return num2; } num2 += num3; } return num2; } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } protected override void Dispose(bool disposing) { if (!isDisposed) { if (disposing && CompressedStream != null) { CompressedStream.Dispose(); CompressedStream = null; } base.Dispose(disposing); isDisposed = true; } } private bool InitializeInflateStream(bool isCriticalChunk) { int num = innerStream.ReadByte(); int num2 = innerStream.ReadByte(); currentDataRemaining -= 2; if (num == -1 || num2 == -1) { return false; } if ((num & 0xF) == 8) { int num3 = (num & 0xF0) >> 4; if (num3 > 7) { if (isCriticalChunk) { throw new ImageFormatException($"Invalid window size for ZLIB header: cinfo={num3}"); } return false; } if ((num2 & 0x20) != 0) { if (innerStream.Read(ChecksumBuffer, 0, 4) != 4) { return false; } currentDataRemaining -= 4; } CompressedStream = new DeflateStream(this, CompressionMode.Decompress, leaveOpen: true); return true; } if (isCriticalChunk) { throw new ImageFormatException($"Bad method for ZLIB header: cmf={num}"); } return false; } } } namespace SixLabors.ImageSharp.Tuples { internal struct Octet where T : unmanaged { public T V0; public T V1; public T V2; public T V3; public T V4; public T V5; public T V6; public T V7; public override readonly string ToString() { return $"Octet<{typeof(T)}>({V0},{V1},{V2},{V3},{V4},{V5},{V6},{V7})"; } } internal static class OctetExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void LoadFrom(this ref Octet destination, ref Octet source) { destination.V0 = source.V0; destination.V1 = source.V1; destination.V2 = source.V2; destination.V3 = source.V3; destination.V4 = source.V4; destination.V5 = source.V5; destination.V6 = source.V6; destination.V7 = source.V7; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void LoadFrom(this ref Octet destination, ref Octet source) { destination.V0 = (byte)source.V0; destination.V1 = (byte)source.V1; destination.V2 = (byte)source.V2; destination.V3 = (byte)source.V3; destination.V4 = (byte)source.V4; destination.V5 = (byte)source.V5; destination.V6 = (byte)source.V6; destination.V7 = (byte)source.V7; } } } namespace SixLabors.ImageSharp.Common.Helpers { internal readonly struct ExifResolutionValues { public ushort ResolutionUnit { get; } public double? HorizontalResolution { get; } public double? VerticalResolution { get; } public ExifResolutionValues(ushort resolutionUnit, double? horizontalResolution, double? verticalResolution) { ResolutionUnit = resolutionUnit; HorizontalResolution = horizontalResolution; VerticalResolution = verticalResolution; } } internal static class HexConverter { public static int HexStringToBytes(ReadOnlySpan chars, Span bytes) { if (chars.Length % 2 != 0) { throw new ArgumentException("Input string length must be a multiple of 2", "chars"); } if (bytes.Length * 2 < chars.Length) { throw new ArgumentException("Output span must be at least half the length of the input string"); } bytes = bytes.Slice(0, chars.Length / 2); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; while (num2 < bytes.Length) { num3 = FromChar(chars[num + 1]); num4 = FromChar(chars[num]); if ((num3 | num4) == 255) { break; } bytes[num2++] = (byte)((num4 << 4) | num3); num += 2; } if (num3 == 255) { num++; } if ((num3 | num4) == 255) { throw new ArgumentException("Input string contained non-hexadecimal characters", "chars"); } return num2; [MethodImpl(MethodImplOptions.AggressiveInlining)] static int FromChar(int c) { ReadOnlySpan readOnlySpan = new byte[256] { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; if (c < readOnlySpan.Length) { return readOnlySpan[c]; } return 255; } } } internal static class TestHelpers { internal const string ImageSharpBuiltAgainst = "netcoreapp3.1"; } internal static class UnitConverter { private const double CmsInMeter = 100.0; private const double CmsInInch = 2.54; private const double InchesInMeter = 39.37007874015748; private const PixelResolutionUnit DefaultResolutionUnit = PixelResolutionUnit.PixelsPerInch; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double CmToMeter(double x) { return x * 100.0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double MeterToCm(double x) { return x / 100.0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double MeterToInch(double x) { return x / 39.37007874015748; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double InchToMeter(double x) { return x * 39.37007874015748; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double CmToInch(double x) { return x / 2.54; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double InchToCm(double x) { return x * 2.54; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PixelResolutionUnit ExifProfileToResolutionUnit(ExifProfile profile) { IExifValue value = profile.GetValue(ExifTag.ResolutionUnit); if (value != null) { return (PixelResolutionUnit)(value.Value - 1); } return PixelResolutionUnit.PixelsPerInch; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ExifResolutionValues GetExifResolutionValues(PixelResolutionUnit unit, double horizontal, double vertical) { switch (unit) { case PixelResolutionUnit.PixelsPerMeter: unit = PixelResolutionUnit.PixelsPerCentimeter; horizontal = MeterToCm(horizontal); vertical = MeterToCm(vertical); break; default: unit = PixelResolutionUnit.PixelsPerInch; break; case PixelResolutionUnit.AspectRatio: case PixelResolutionUnit.PixelsPerInch: case PixelResolutionUnit.PixelsPerCentimeter: break; } ushort resolutionUnit = (ushort)(unit + 1); if (unit == PixelResolutionUnit.AspectRatio) { return new ExifResolutionValues(resolutionUnit, null, null); } return new ExifResolutionValues(resolutionUnit, horizontal, vertical); } } } namespace SixLabors.ImageSharp.ColorSpaces { public readonly struct CieLab : IEquatable { public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; public readonly float L; public readonly float A; public readonly float B; public readonly CieXyz WhitePoint; [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLab(float l, float a, float b) : this(l, a, b, DefaultWhitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLab(float l, float a, float b, CieXyz whitePoint) : this(new Vector3(l, a, b), whitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLab(Vector3 vector) : this(vector, DefaultWhitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLab(Vector3 vector, CieXyz whitePoint) { this = default(CieLab); L = vector.X; A = vector.Y; B = vector.Z; WhitePoint = whitePoint; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(CieLab left, CieLab right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieLab left, CieLab right) { return !left.Equals(right); } public override int GetHashCode() { return HashCode.Combine(L, A, B, WhitePoint); } public override string ToString() { return FormattableString.Invariant($"CieLab({L:#0.##}, {A:#0.##}, {B:#0.##})"); } public override bool Equals(object obj) { if (obj is CieLab other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLab other) { if (L.Equals(other.L) && A.Equals(other.A) && B.Equals(other.B)) { return WhitePoint.Equals(other.WhitePoint); } return false; } } public readonly struct CieLch : IEquatable { public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; private static readonly Vector3 Min = new Vector3(0f, -200f, 0f); private static readonly Vector3 Max = new Vector3(100f, 200f, 360f); public readonly float L; public readonly float C; public readonly float H; public readonly CieXyz WhitePoint; [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLch(float l, float c, float h) : this(l, c, h, DefaultWhitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLch(float l, float c, float h, CieXyz whitePoint) : this(new Vector3(l, c, h), whitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLch(Vector3 vector) : this(vector, DefaultWhitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLch(Vector3 vector, CieXyz whitePoint) { vector = Vector3.Clamp(vector, Min, Max); L = vector.X; C = vector.Y; H = vector.Z; WhitePoint = whitePoint; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(CieLch left, CieLch right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieLch left, CieLch right) { return !left.Equals(right); } public override int GetHashCode() { return HashCode.Combine(L, C, H, WhitePoint); } public override string ToString() { return FormattableString.Invariant($"CieLch({L:#0.##}, {C:#0.##}, {H:#0.##})"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { if (obj is CieLch other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLch other) { if (L.Equals(other.L) && C.Equals(other.C) && H.Equals(other.H)) { return WhitePoint.Equals(other.WhitePoint); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public float Saturation() { float num = 100f * (C / L); if (float.IsNaN(num)) { return 0f; } return num; } } public readonly struct CieLchuv : IEquatable { private static readonly Vector3 Min = new Vector3(0f, -200f, 0f); private static readonly Vector3 Max = new Vector3(100f, 200f, 360f); public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; public readonly float L; public readonly float C; public readonly float H; public readonly CieXyz WhitePoint; [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLchuv(float l, float c, float h) : this(l, c, h, DefaultWhitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLchuv(float l, float c, float h, CieXyz whitePoint) : this(new Vector3(l, c, h), whitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLchuv(Vector3 vector) : this(vector, DefaultWhitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLchuv(Vector3 vector, CieXyz whitePoint) { this = default(CieLchuv); vector = Vector3.Clamp(vector, Min, Max); L = vector.X; C = vector.Y; H = vector.Z; WhitePoint = whitePoint; } public static bool operator ==(CieLchuv left, CieLchuv right) { return left.Equals(right); } public static bool operator !=(CieLchuv left, CieLchuv right) { return !left.Equals(right); } public override int GetHashCode() { return HashCode.Combine(L, C, H, WhitePoint); } public override string ToString() { return FormattableString.Invariant($"CieLchuv({L:#0.##}, {C:#0.##}, {H:#0.##})"); } public override bool Equals(object obj) { if (obj is CieLchuv other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLchuv other) { if (L.Equals(other.L) && C.Equals(other.C) && H.Equals(other.H)) { return WhitePoint.Equals(other.WhitePoint); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public float Saturation() { float num = 100f * (C / L); if (float.IsNaN(num)) { return 0f; } return num; } } public readonly struct CieLuv : IEquatable { public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; public readonly float L; public readonly float U; public readonly float V; public readonly CieXyz WhitePoint; [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLuv(float l, float u, float v) : this(l, u, v, DefaultWhitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLuv(float l, float u, float v, CieXyz whitePoint) : this(new Vector3(l, u, v), whitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLuv(Vector3 vector) : this(vector, DefaultWhitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLuv(Vector3 vector, CieXyz whitePoint) { L = vector.X; U = vector.Y; V = vector.Z; WhitePoint = whitePoint; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(CieLuv left, CieLuv right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieLuv left, CieLuv right) { return !left.Equals(right); } public override int GetHashCode() { return HashCode.Combine(L, U, V, WhitePoint); } public override string ToString() { return FormattableString.Invariant($"CieLuv({L:#0.##}, {U:#0.##}, {V:#0.##})"); } public override bool Equals(object obj) { if (obj is CieLuv other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLuv other) { if (L.Equals(other.L) && U.Equals(other.U) && V.Equals(other.V)) { return WhitePoint.Equals(other.WhitePoint); } return false; } } public readonly struct CieXyy : IEquatable { public readonly float X; public readonly float Y; public readonly float Yl; [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyy(float x, float y, float yl) { X = x; Y = y; Yl = yl; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyy(Vector3 vector) { this = default(CieXyy); X = vector.X; Y = vector.Y; Yl = vector.Z; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(CieXyy left, CieXyy right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieXyy left, CieXyy right) { return !left.Equals(right); } public override int GetHashCode() { return HashCode.Combine(X, Y, Yl); } public override string ToString() { return FormattableString.Invariant($"CieXyy({X:#0.##}, {Y:#0.##}, {Yl:#0.##})"); } public override bool Equals(object obj) { if (obj is CieXyy other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyy other) { if (X.Equals(other.X) && Y.Equals(other.Y)) { return Yl.Equals(other.Yl); } return false; } } public readonly struct CieXyz : IEquatable { public readonly float X; public readonly float Y; public readonly float Z; [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyz(float x, float y, float z) : this(new Vector3(x, y, z)) { } public CieXyz(Vector3 vector) { this = default(CieXyz); X = vector.X; Y = vector.Y; Z = vector.Z; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(CieXyz left, CieXyz right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieXyz left, CieXyz right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 ToVector3() { return new Vector3(X, Y, Z); } public override int GetHashCode() { return HashCode.Combine(X, Y, Z); } public override string ToString() { return FormattableString.Invariant($"CieXyz({X:#0.##}, {Y:#0.##}, {Z:#0.##})"); } public override bool Equals(object obj) { if (obj is CieXyz other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyz other) { if (X.Equals(other.X) && Y.Equals(other.Y)) { return Z.Equals(other.Z); } return false; } } public readonly struct Cmyk : IEquatable { private static readonly Vector4 Min = Vector4.Zero; private static readonly Vector4 Max = Vector4.One; public readonly float C; public readonly float M; public readonly float Y; public readonly float K; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Cmyk(float c, float m, float y, float k) : this(new Vector4(c, m, y, k)) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Cmyk(Vector4 vector) { vector = Numerics.Clamp(vector, Min, Max); C = vector.X; M = vector.Y; Y = vector.Z; K = vector.W; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Cmyk left, Cmyk right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Cmyk left, Cmyk right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(C, M, Y, K); } public override string ToString() { return FormattableString.Invariant($"Cmyk({C:#0.##}, {M:#0.##}, {Y:#0.##}, {K:#0.##})"); } public override bool Equals(object obj) { if (obj is Cmyk other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Cmyk other) { if (C.Equals(other.C) && M.Equals(other.M) && Y.Equals(other.Y)) { return K.Equals(other.K); } return false; } } public readonly struct Hsl : IEquatable { private static readonly Vector3 Min = Vector3.Zero; private static readonly Vector3 Max = new Vector3(360f, 1f, 1f); public readonly float H; public readonly float S; public readonly float L; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Hsl(float h, float s, float l) : this(new Vector3(h, s, l)) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Hsl(Vector3 vector) { vector = Vector3.Clamp(vector, Min, Max); H = vector.X; S = vector.Y; L = vector.Z; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Hsl left, Hsl right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Hsl left, Hsl right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(H, S, L); } public override string ToString() { return FormattableString.Invariant($"Hsl({H:#0.##}, {S:#0.##}, {L:#0.##})"); } public override bool Equals(object obj) { if (obj is Hsl other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsl other) { if (H.Equals(other.H) && S.Equals(other.S)) { return L.Equals(other.L); } return false; } } public readonly struct Hsv : IEquatable { private static readonly Vector3 Min = Vector3.Zero; private static readonly Vector3 Max = new Vector3(360f, 1f, 1f); public readonly float H; public readonly float S; public readonly float V; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Hsv(float h, float s, float v) : this(new Vector3(h, s, v)) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Hsv(Vector3 vector) { vector = Vector3.Clamp(vector, Min, Max); H = vector.X; S = vector.Y; V = vector.Z; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Hsv left, Hsv right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Hsv left, Hsv right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(H, S, V); } public override string ToString() { return FormattableString.Invariant($"Hsv({H:#0.##}, {S:#0.##}, {V:#0.##})"); } public override bool Equals(object obj) { if (obj is Hsv other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsv other) { if (H.Equals(other.H) && S.Equals(other.S)) { return V.Equals(other.V); } return false; } } public readonly struct HunterLab : IEquatable { public static readonly CieXyz DefaultWhitePoint = Illuminants.C; public readonly float L; public readonly float A; public readonly float B; public readonly CieXyz WhitePoint; [MethodImpl(MethodImplOptions.AggressiveInlining)] public HunterLab(float l, float a, float b) : this(new Vector3(l, a, b), DefaultWhitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public HunterLab(float l, float a, float b, CieXyz whitePoint) : this(new Vector3(l, a, b), whitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public HunterLab(Vector3 vector) : this(vector, DefaultWhitePoint) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public HunterLab(Vector3 vector, CieXyz whitePoint) { L = vector.X; A = vector.Y; B = vector.Z; WhitePoint = whitePoint; } public static bool operator ==(HunterLab left, HunterLab right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HunterLab left, HunterLab right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(L, A, B, WhitePoint); } public override string ToString() { return FormattableString.Invariant($"HunterLab({L:#0.##}, {A:#0.##}, {B:#0.##})"); } public override bool Equals(object obj) { if (obj is HunterLab other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HunterLab other) { if (L.Equals(other.L) && A.Equals(other.A) && B.Equals(other.B)) { return WhitePoint.Equals(other.WhitePoint); } return false; } } public static class Illuminants { public static readonly CieXyz A = new CieXyz(1.0985f, 1f, 0.35585f); public static readonly CieXyz B = new CieXyz(0.99072f, 1f, 0.85223f); public static readonly CieXyz C = new CieXyz(0.98074f, 1f, 1.18232f); public static readonly CieXyz D50 = new CieXyz(0.96422f, 1f, 0.82521f); public static readonly CieXyz D55 = new CieXyz(0.95682f, 1f, 0.92149f); public static readonly CieXyz D65 = new CieXyz(0.95047f, 1f, 1.08883f); public static readonly CieXyz D75 = new CieXyz(0.94972f, 1f, 1.22638f); public static readonly CieXyz E = new CieXyz(1f, 1f, 1f); public static readonly CieXyz F2 = new CieXyz(0.99186f, 1f, 0.67393f); public static readonly CieXyz F7 = new CieXyz(0.95041f, 1f, 1.08747f); public static readonly CieXyz F11 = new CieXyz(1.00962f, 1f, 0.6435f); } public readonly struct LinearRgb : IEquatable { private static readonly Vector3 Min = Vector3.Zero; private static readonly Vector3 Max = Vector3.One; public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; public readonly float R; public readonly float G; public readonly float B; public readonly RgbWorkingSpace WorkingSpace; [MethodImpl(MethodImplOptions.AggressiveInlining)] public LinearRgb(float r, float g, float b) : this(r, g, b, DefaultWorkingSpace) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public LinearRgb(float r, float g, float b, RgbWorkingSpace workingSpace) : this(new Vector3(r, g, b), workingSpace) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public LinearRgb(Vector3 vector) : this(vector, DefaultWorkingSpace) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public LinearRgb(Vector3 vector, RgbWorkingSpace workingSpace) { vector = Vector3.Clamp(vector, Min, Max); R = vector.X; G = vector.Y; B = vector.Z; WorkingSpace = workingSpace; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(LinearRgb left, LinearRgb right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(LinearRgb left, LinearRgb right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 ToVector3() { return new Vector3(R, G, B); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(R, G, B); } public override string ToString() { return FormattableString.Invariant($"LinearRgb({R:#0.##}, {G:#0.##}, {B:#0.##})"); } public override bool Equals(object obj) { if (obj is LinearRgb other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(LinearRgb other) { if (R.Equals(other.R) && G.Equals(other.G)) { return B.Equals(other.B); } return false; } } public readonly struct Lms : IEquatable { public readonly float L; public readonly float M; public readonly float S; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lms(float l, float m, float s) : this(new Vector3(l, m, s)) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lms(Vector3 vector) { L = vector.X; M = vector.Y; S = vector.Z; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Lms left, Lms right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Lms left, Lms right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 ToVector3() { return new Vector3(L, M, S); } public override int GetHashCode() { return HashCode.Combine(L, M, S); } public override string ToString() { return FormattableString.Invariant($"Lms({L:#0.##}, {M:#0.##}, {S:#0.##})"); } public override bool Equals(object obj) { if (obj is Lms other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Lms other) { if (L.Equals(other.L) && M.Equals(other.M)) { return S.Equals(other.S); } return false; } } public readonly struct Rgb : IEquatable { public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; private static readonly Vector3 Min = Vector3.Zero; private static readonly Vector3 Max = Vector3.One; public readonly float R; public readonly float G; public readonly float B; public readonly RgbWorkingSpace WorkingSpace; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb(float r, float g, float b) : this(r, g, b, DefaultWorkingSpace) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb(float r, float g, float b, RgbWorkingSpace workingSpace) : this(new Vector3(r, g, b), workingSpace) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb(Vector3 vector) : this(vector, DefaultWorkingSpace) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb(Vector3 vector, RgbWorkingSpace workingSpace) { vector = Vector3.Clamp(vector, Min, Max); R = vector.X; G = vector.Y; B = vector.Z; WorkingSpace = workingSpace; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Rgb(Rgb24 color) { return new Rgb((float)(int)color.R / 255f, (float)(int)color.G / 255f, (float)(int)color.B / 255f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Rgb(Rgba32 color) { return new Rgb((float)(int)color.R / 255f, (float)(int)color.G / 255f, (float)(int)color.B / 255f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgb left, Rgb right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgb left, Rgb right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 ToVector3() { return new Vector3(R, G, B); } public override int GetHashCode() { return HashCode.Combine(R, G, B); } public override string ToString() { return FormattableString.Invariant($"Rgb({R:#0.##}, {G:#0.##}, {B:#0.##})"); } public override bool Equals(object obj) { if (obj is Rgb other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Rgb other) { if (R.Equals(other.R) && G.Equals(other.G)) { return B.Equals(other.B); } return false; } } public static class RgbWorkingSpaces { public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64f, 0.33f), new CieXyChromaticityCoordinates(0.3f, 0.6f), new CieXyChromaticityCoordinates(0.15f, 0.06f))); public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2f, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64f, 0.33f), new CieXyChromaticityCoordinates(0.3f, 0.6f), new CieXyChromaticityCoordinates(0.15f, 0.06f))); public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64f, 0.33f), new CieXyChromaticityCoordinates(0.3f, 0.6f), new CieXyChromaticityCoordinates(0.15f, 0.06f))); public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708f, 0.292f), new CieXyChromaticityCoordinates(0.17f, 0.797f), new CieXyChromaticityCoordinates(0.131f, 0.046f))); public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.67f, 0.33f), new CieXyChromaticityCoordinates(0.21f, 0.71f), new CieXyChromaticityCoordinates(0.14f, 0.08f))); public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2f, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64f, 0.33f), new CieXyChromaticityCoordinates(0.21f, 0.71f), new CieXyChromaticityCoordinates(0.15f, 0.06f))); public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8f, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.625f, 0.34f), new CieXyChromaticityCoordinates(0.28f, 0.595f), new CieXyChromaticityCoordinates(0.155f, 0.07f))); public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2f, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347f, 0.2653f), new CieXyChromaticityCoordinates(0.215f, 0.775f), new CieXyChromaticityCoordinates(0.13f, 0.035f))); public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2f, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888f, 0.3112f), new CieXyChromaticityCoordinates(0.1986f, 0.7551f), new CieXyChromaticityCoordinates(0.1265f, 0.0352f))); public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2f, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64f, 0.33f), new CieXyChromaticityCoordinates(0.28f, 0.65f), new CieXyChromaticityCoordinates(0.15f, 0.06f))); public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2f, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.735f, 0.265f), new CieXyChromaticityCoordinates(0.274f, 0.717f), new CieXyChromaticityCoordinates(0.167f, 0.009f))); public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8f, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.63f, 0.34f), new CieXyChromaticityCoordinates(0.295f, 0.605f), new CieXyChromaticityCoordinates(0.15f, 0.075f))); public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2f, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.696f, 0.3f), new CieXyChromaticityCoordinates(0.215f, 0.765f), new CieXyChromaticityCoordinates(0.13f, 0.035f))); public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2f, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.695f, 0.305f), new CieXyChromaticityCoordinates(0.26f, 0.7f), new CieXyChromaticityCoordinates(0.11f, 0.005f))); public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2f, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.67f, 0.33f), new CieXyChromaticityCoordinates(0.21f, 0.71f), new CieXyChromaticityCoordinates(0.14f, 0.08f))); public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2f, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64f, 0.33f), new CieXyChromaticityCoordinates(0.29f, 0.6f), new CieXyChromaticityCoordinates(0.15f, 0.06f))); public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8f, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347f, 0.2653f), new CieXyChromaticityCoordinates(0.1596f, 0.8404f), new CieXyChromaticityCoordinates(0.0366f, 0.0001f))); public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2f, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.63f, 0.34f), new CieXyChromaticityCoordinates(0.31f, 0.595f), new CieXyChromaticityCoordinates(0.155f, 0.07f))); public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2f, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.735f, 0.265f), new CieXyChromaticityCoordinates(0.115f, 0.826f), new CieXyChromaticityCoordinates(0.157f, 0.018f))); } public readonly struct YCbCr : IEquatable { private static readonly Vector3 Min = Vector3.Zero; private static readonly Vector3 Max = new Vector3(255f); public readonly float Y; public readonly float Cb; public readonly float Cr; [MethodImpl(MethodImplOptions.AggressiveInlining)] public YCbCr(float y, float cb, float cr) : this(new Vector3(y, cb, cr)) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public YCbCr(Vector3 vector) { vector = Vector3.Clamp(vector, Min, Max); Y = vector.X; Cb = vector.Y; Cr = vector.Z; } public static bool operator ==(YCbCr left, YCbCr right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(YCbCr left, YCbCr right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(Y, Cb, Cr); } public override string ToString() { return FormattableString.Invariant($"YCbCr({Y}, {Cb}, {Cr})"); } public override bool Equals(object obj) { if (obj is YCbCr other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(YCbCr other) { if (Y.Equals(other.Y) && Cb.Equals(other.Cb)) { return Cr.Equals(other.Cr); } return false; } } } namespace SixLabors.ImageSharp.ColorSpaces.Conversion { internal static class CieConstants { public const float Epsilon = 0.008856452f; public const float Kappa = 903.2963f; } public class ColorSpaceConverter { private static readonly CieLchToCieLabConverter CieLchToCieLabConverter = new CieLchToCieLabConverter(); private static readonly CieLabToCieLchConverter CieLabToCieLchConverter = new CieLabToCieLchConverter(); private static readonly CieLuvToCieLchuvConverter CieLuvToCieLchuvConverter = new CieLuvToCieLchuvConverter(); private static readonly CieLchuvToCieLuvConverter CieLchuvToCieLuvConverter = new CieLchuvToCieLuvConverter(); private static readonly CieXyzAndCieXyyConverter CieXyzAndCieXyyConverter = new CieXyzAndCieXyyConverter(); private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter(); private static readonly CieLuvToCieXyzConverter CieLuvToCieXyzConverter = new CieLuvToCieXyzConverter(); private static readonly HunterLabToCieXyzConverter HunterLabToCieXyzConverter = new HunterLabToCieXyzConverter(); private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter; private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter(); private static readonly ColorSpaceConverterOptions DefaultOptions = new ColorSpaceConverterOptions(); private readonly Matrix4x4 lmsAdaptationMatrix; private readonly CieXyz whitePoint; private readonly CieXyz targetLuvWhitePoint; private readonly CieXyz targetLabWhitePoint; private readonly CieXyz targetHunterLabWhitePoint; private readonly RgbWorkingSpace targetRgbWorkingSpace; private readonly IChromaticAdaptation chromaticAdaptation; private readonly bool performChromaticAdaptation; private readonly CieXyzAndLmsConverter cieXyzAndLmsConverter; private readonly CieXyzToCieLabConverter cieXyzToCieLabConverter; private readonly CieXyzToCieLuvConverter cieXyzToCieLuvConverter; private readonly CieXyzToHunterLabConverter cieXyzToHunterLabConverter; private readonly CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter; private static readonly HslAndRgbConverter HslAndRgbConverter = new HslAndRgbConverter(); private static readonly HsvAndRgbConverter HsvAndRgbConverter = new HsvAndRgbConverter(); private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter(); private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter(); private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter(); public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint) { return Adapt(in color, in sourceWhitePoint, in whitePoint); } public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint) { if (!performChromaticAdaptation || sourceWhitePoint.Equals(targetWhitePoint)) { return color; } return chromaticAdaptation.Transform(in color, in sourceWhitePoint, in targetWhitePoint); } public CieLab Adapt(in CieLab color) { if (!performChromaticAdaptation || color.WhitePoint.Equals(targetLabWhitePoint)) { return color; } return ToCieLab(ToCieXyz(in color)); } public CieLch Adapt(in CieLch color) { if (!performChromaticAdaptation || color.WhitePoint.Equals(targetLabWhitePoint)) { return color; } return ToCieLch(ToCieLab(in color)); } public CieLchuv Adapt(in CieLchuv color) { if (!performChromaticAdaptation || color.WhitePoint.Equals(targetLabWhitePoint)) { return color; } return ToCieLchuv(ToCieLuv(in color)); } public CieLuv Adapt(in CieLuv color) { if (!performChromaticAdaptation || color.WhitePoint.Equals(targetLuvWhitePoint)) { return color; } return ToCieLuv(ToCieXyz(in color)); } public HunterLab Adapt(in HunterLab color) { if (!performChromaticAdaptation || color.WhitePoint.Equals(targetHunterLabWhitePoint)) { return color; } return ToHunterLab(ToCieXyz(in color)); } public LinearRgb Adapt(in LinearRgb color) { if (!performChromaticAdaptation || color.WorkingSpace.Equals(targetRgbWorkingSpace)) { return color; } CieXyz source = GetLinearRgbToCieXyzConverter(color.WorkingSpace).Convert(in color); CieXyz input = chromaticAdaptation.Transform(in source, color.WorkingSpace.WhitePoint, targetRgbWorkingSpace.WhitePoint); return cieXyzToLinearRgbConverter.Convert(in input); } public Rgb Adapt(in Rgb color) { if (!performChromaticAdaptation) { return color; } return ToRgb(Adapt(ToLinearRgb(in color))); } public CieLab ToCieLab(in CieLch color) { return Adapt(CieLchToCieLabConverter.Convert(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in CieLchuv color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in CieLuv color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in CieXyy color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in CieXyz color) { CieXyz input = Adapt(in color, in whitePoint, in targetLabWhitePoint); return cieXyzToCieLabConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in Cmyk color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in Hsl color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in Hsv color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in HunterLab color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in Lms color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in LinearRgb color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in Rgb color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLab ToCieLab(in YCbCr color) { return ToCieLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref CieLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLab(in color); } } public CieLch ToCieLch(in CieLab color) { CieLab input = Adapt(in color); return CieLabToCieLchConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in CieLchuv color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in CieLuv color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in CieXyy color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in CieXyz color) { return ToCieLch(ToCieLab(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in Cmyk color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in Hsl color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in Hsv color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in HunterLab color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in LinearRgb color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in Lms color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in Rgb color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLch ToCieLch(in YCbCr color) { return ToCieLch(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref CieLch reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLch(in color); } } public CieLchuv ToCieLchuv(in CieLab color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in CieLch color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in CieLuv color) { CieLuv input = Adapt(in color); return CieLuvToCieLchuvConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in CieXyy color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in CieXyz color) { return ToCieLchuv(ToCieLuv(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in Cmyk color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in Hsl color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in Hsv color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in HunterLab color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in LinearRgb color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in Lms color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in Rgb color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLchuv ToCieLchuv(in YCbCr color) { return ToCieLchuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref CieLchuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLchuv(in color); } } public CieLuv ToCieLuv(in CieLab color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in CieLch color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in CieLchuv color) { return Adapt(CieLchuvToCieLuvConverter.Convert(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in CieXyy color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in CieXyz color) { CieXyz input = Adapt(in color, in whitePoint, in targetLuvWhitePoint); return cieXyzToCieLuvConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in Cmyk color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in Hsl color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in Hsv color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in HunterLab color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in Lms color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in LinearRgb color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in Rgb color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieLuv ToCieLuv(in YCbCr color) { return ToCieLuv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref CieLuv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieLuv(in color); } } public CieXyy ToCieXyy(in CieLab color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(in CieLch color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(in CieLchuv color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(in CieLuv color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(in CieXyz color) { return CieXyzAndCieXyyConverter.Convert(in color); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(in Cmyk color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(Hsl color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(reference3); } } public CieXyy ToCieXyy(in Hsv color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(in HunterLab color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(in LinearRgb color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(in Lms color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(in Rgb color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyy ToCieXyy(in YCbCr color) { return ToCieXyy(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref CieXyy reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyy(in color); } } public CieXyz ToCieXyz(in CieLab color) { return Adapt(CieLabToCieXyzConverter.Convert(in color), in color.WhitePoint); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in CieLch color) { return ToCieXyz(CieLchToCieLabConverter.Convert(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in CieLchuv color) { return ToCieXyz(CieLchuvToCieLuvConverter.Convert(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in CieLuv color) { return Adapt(CieLuvToCieXyzConverter.Convert(in color), in color.WhitePoint); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in CieXyy color) { return CieXyzAndCieXyyConverter.Convert(in color); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in Cmyk color) { return ToCieXyz(ToRgb(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in Hsl color) { return ToCieXyz(ToRgb(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in Hsv color) { return ToCieXyz(ToRgb(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in HunterLab color) { return Adapt(HunterLabToCieXyzConverter.Convert(in color), in color.WhitePoint); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in LinearRgb color) { return Adapt(GetLinearRgbToCieXyzConverter(color.WorkingSpace).Convert(in color), color.WorkingSpace.WhitePoint); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in Lms color) { return cieXyzAndLmsConverter.Convert(in color); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in Rgb color) { return ToCieXyz(RgbToLinearRgbConverter.Convert(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } public CieXyz ToCieXyz(in YCbCr color) { return ToCieXyz(ToRgb(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCieXyz(in color); } } private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) { LinearRgbToCieXyzConverter obj = linearRgbToCieXyzConverter; if (obj != null && obj.SourceWorkingSpace.Equals(workingSpace)) { return linearRgbToCieXyzConverter; } return linearRgbToCieXyzConverter = new LinearRgbToCieXyzConverter(workingSpace); } public Cmyk ToCmyk(in CieLab color) { return ToCmyk(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in CieLch color) { return ToCmyk(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in CieLchuv color) { return ToCmyk(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in CieLuv color) { return ToCmyk(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in CieXyy color) { return ToCmyk(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in CieXyz color) { Rgb input = ToRgb(in color); return CmykAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in Hsl color) { Rgb input = ToRgb(in color); return CmykAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in Hsv color) { Rgb input = ToRgb(in color); return CmykAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in HunterLab color) { return ToCmyk(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in LinearRgb color) { Rgb input = ToRgb(in color); return CmykAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in Lms color) { return ToCmyk(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in Rgb color) { return CmykAndRgbConverter.Convert(in color); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public Cmyk ToCmyk(in YCbCr color) { Rgb input = ToRgb(in color); return CmykAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref Cmyk reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToCmyk(in color); } } public ColorSpaceConverter() : this(DefaultOptions) { } public ColorSpaceConverter(ColorSpaceConverterOptions options) { Guard.NotNull(options, "options"); whitePoint = options.WhitePoint; targetLuvWhitePoint = options.TargetLuvWhitePoint; targetLabWhitePoint = options.TargetLabWhitePoint; targetHunterLabWhitePoint = options.TargetHunterLabWhitePoint; targetRgbWorkingSpace = options.TargetRgbWorkingSpace; chromaticAdaptation = options.ChromaticAdaptation; performChromaticAdaptation = chromaticAdaptation != null; lmsAdaptationMatrix = options.LmsAdaptationMatrix; cieXyzAndLmsConverter = new CieXyzAndLmsConverter(lmsAdaptationMatrix); cieXyzToCieLabConverter = new CieXyzToCieLabConverter(targetLabWhitePoint); cieXyzToCieLuvConverter = new CieXyzToCieLuvConverter(targetLuvWhitePoint); cieXyzToHunterLabConverter = new CieXyzToHunterLabConverter(targetHunterLabWhitePoint); cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(targetRgbWorkingSpace); } public Hsl ToHsl(in CieLab color) { return ToHsl(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(in CieLch color) { return ToHsl(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(in CieLchuv color) { return ToHsl(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(in CieLuv color) { return ToHsl(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(in CieXyy color) { return ToHsl(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(in CieXyz color) { Rgb input = ToRgb(in color); return HslAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(in Cmyk color) { Rgb input = ToRgb(in color); return HslAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(in Hsv color) { Rgb input = ToRgb(in color); return HslAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(in HunterLab color) { return ToHsl(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(in LinearRgb color) { Rgb input = ToRgb(in color); return HslAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(Lms color) { return ToHsl(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(reference3); } } public Hsl ToHsl(in Rgb color) { return HslAndRgbConverter.Convert(in color); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsl ToHsl(in YCbCr color) { Rgb input = ToRgb(in color); return HslAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref Hsl reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsl(in color); } } public Hsv ToHsv(in CieLab color) { return ToHsv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(in CieLch color) { return ToHsv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(in CieLchuv color) { return ToHsv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(in CieLuv color) { return ToHsv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(in CieXyy color) { return ToHsv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(in CieXyz color) { Rgb input = ToRgb(in color); return HsvAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(in Cmyk color) { Rgb input = ToRgb(in color); return HsvAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(in Hsl color) { Rgb input = ToRgb(in color); return HsvAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(in HunterLab color) { return ToHsv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(in LinearRgb color) { Rgb input = ToRgb(in color); return HsvAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(Lms color) { return ToHsv(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms reference3 = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(reference3); } } public Hsv ToHsv(in Rgb color) { return HsvAndRgbConverter.Convert(in color); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public Hsv ToHsv(in YCbCr color) { Rgb input = ToRgb(in color); return HsvAndRgbConverter.Convert(in input); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref Hsv reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHsv(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref HunterLab reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToHunterLab(in color); } } public HunterLab ToHunterLab(in CieLab color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in CieLch color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in CieLchuv color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in CieLuv color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in CieXyy color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in CieXyz color) { CieXyz input = Adapt(in color, in whitePoint, in targetHunterLabWhitePoint); return cieXyzToHunterLabConverter.Convert(in input); } public HunterLab ToHunterLab(in Cmyk color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in Hsl color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in Hsv color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in LinearRgb color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in Lms color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in Rgb color) { return ToHunterLab(ToCieXyz(in color)); } public HunterLab ToHunterLab(in YCbCr color) { return ToHunterLab(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref LinearRgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLinearRgb(in color); } } public LinearRgb ToLinearRgb(in CieLab color) { return ToLinearRgb(ToCieXyz(in color)); } public LinearRgb ToLinearRgb(in CieLch color) { return ToLinearRgb(ToCieXyz(in color)); } public LinearRgb ToLinearRgb(in CieLchuv color) { return ToLinearRgb(ToCieXyz(in color)); } public LinearRgb ToLinearRgb(in CieLuv color) { return ToLinearRgb(ToCieXyz(in color)); } public LinearRgb ToLinearRgb(in CieXyy color) { return ToLinearRgb(ToCieXyz(in color)); } public LinearRgb ToLinearRgb(in CieXyz color) { CieXyz input = Adapt(in color, in whitePoint, targetRgbWorkingSpace.WhitePoint); return cieXyzToLinearRgbConverter.Convert(in input); } public LinearRgb ToLinearRgb(in Cmyk color) { return ToLinearRgb(ToRgb(in color)); } public LinearRgb ToLinearRgb(in Hsl color) { return ToLinearRgb(ToRgb(in color)); } public LinearRgb ToLinearRgb(in Hsv color) { return ToLinearRgb(ToRgb(in color)); } public LinearRgb ToLinearRgb(in HunterLab color) { return ToLinearRgb(ToCieXyz(in color)); } public LinearRgb ToLinearRgb(in Lms color) { return ToLinearRgb(ToCieXyz(in color)); } public LinearRgb ToLinearRgb(in Rgb color) { return RgbToLinearRgbConverter.Convert(in color); } public LinearRgb ToLinearRgb(in YCbCr color) { return ToLinearRgb(ToRgb(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref Lms reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToLms(in color); } } public Lms ToLms(in CieLab color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in CieLch color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in CieLchuv color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in CieLuv color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in CieXyy color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in CieXyz color) { return cieXyzAndLmsConverter.Convert(in color); } public Lms ToLms(in Cmyk color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in Hsl color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in Hsv color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in HunterLab color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in LinearRgb color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in Rgb color) { return ToLms(ToCieXyz(in color)); } public Lms ToLms(in YCbCr color) { return ToLms(ToCieXyz(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLchuv reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLchuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref YCbCr reference = ref MemoryMarshal.GetReference(source); ref Rgb reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref YCbCr color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToRgb(in color); } } public Rgb ToRgb(in CieLab color) { return ToRgb(ToCieXyz(in color)); } public Rgb ToRgb(in CieLch color) { return ToRgb(ToCieXyz(in color)); } public Rgb ToRgb(in CieLchuv color) { return ToRgb(ToCieXyz(in color)); } public Rgb ToRgb(in CieLuv color) { return ToRgb(ToCieXyz(in color)); } public Rgb ToRgb(in CieXyy color) { return ToRgb(ToCieXyz(in color)); } public Rgb ToRgb(in CieXyz color) { return ToRgb(ToLinearRgb(in color)); } public Rgb ToRgb(in Cmyk color) { return CmykAndRgbConverter.Convert(in color); } public Rgb ToRgb(in Hsv color) { return HsvAndRgbConverter.Convert(in color); } public Rgb ToRgb(in Hsl color) { return HslAndRgbConverter.Convert(in color); } public Rgb ToRgb(in HunterLab color) { return ToRgb(ToCieXyz(in color)); } public Rgb ToRgb(in LinearRgb color) { return LinearRgbToRgbConverter.Convert(in color); } public Rgb ToRgb(in Lms color) { return ToRgb(ToCieXyz(in color)); } public Rgb ToRgb(in YCbCr color) { return Adapt(YCbCrAndRgbConverter.Convert(in color)); } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLab reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLch reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLch color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieLuv reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieLuv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyy reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyy color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Cmyk reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Cmyk color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsl reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsl color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Hsv reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Hsv color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref HunterLab reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref HunterLab color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref LinearRgb reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref LinearRgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Lms reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Lms color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public void Convert(ReadOnlySpan source, Span destination) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; ref Rgb reference = ref MemoryMarshal.GetReference(source); ref YCbCr reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref Rgb color = ref Unsafe.Add(ref reference, i); Unsafe.Add(ref reference2, i) = ToYCbCr(in color); } } public YCbCr ToYCbCr(in CieLab color) { return ToYCbCr(ToCieXyz(in color)); } public YCbCr ToYCbCr(in CieLch color) { return ToYCbCr(ToCieXyz(in color)); } public YCbCr ToYCbCr(in CieLuv color) { return ToYCbCr(ToCieXyz(in color)); } public YCbCr ToYCbCr(in CieXyy color) { return ToYCbCr(ToCieXyz(in color)); } public YCbCr ToYCbCr(in CieXyz color) { Rgb input = ToRgb(in color); return YCbCrAndRgbConverter.Convert(in input); } public YCbCr ToYCbCr(in Cmyk color) { Rgb input = ToRgb(in color); return YCbCrAndRgbConverter.Convert(in input); } public YCbCr ToYCbCr(in Hsl color) { Rgb input = ToRgb(in color); return YCbCrAndRgbConverter.Convert(in input); } public YCbCr ToYCbCr(in Hsv color) { Rgb input = ToRgb(in color); return YCbCrAndRgbConverter.Convert(in input); } public YCbCr ToYCbCr(in HunterLab color) { return ToYCbCr(ToCieXyz(in color)); } public YCbCr ToYCbCr(in LinearRgb color) { Rgb input = ToRgb(in color); return YCbCrAndRgbConverter.Convert(in input); } public YCbCr ToYCbCr(in Lms color) { return ToYCbCr(ToCieXyz(in color)); } public YCbCr ToYCbCr(in Rgb color) { return YCbCrAndRgbConverter.Convert(in color); } } public class ColorSpaceConverterOptions { public CieXyz WhitePoint { get; set; } = CieLuv.DefaultWhitePoint; public CieXyz TargetLuvWhitePoint { get; set; } = CieLuv.DefaultWhitePoint; public CieXyz TargetLabWhitePoint { get; set; } = CieLab.DefaultWhitePoint; public CieXyz TargetHunterLabWhitePoint { get; set; } = HunterLab.DefaultWhitePoint; public RgbWorkingSpace TargetRgbWorkingSpace { get; set; } = Rgb.DefaultWorkingSpace; public IChromaticAdaptation ChromaticAdaptation { get; set; } = new VonKriesChromaticAdaptation(); public Matrix4x4 LmsAdaptationMatrix { get; set; } = CieXyzAndLmsConverter.DefaultTransformationMatrix; } public readonly struct CieXyChromaticityCoordinates : IEquatable { public readonly float X; public readonly float Y; [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyChromaticityCoordinates(float x, float y) { X = x; Y = y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(X, Y); } public override string ToString() { return FormattableString.Invariant($"CieXyChromaticityCoordinates({X:#0.##}, {Y:#0.##})"); } public override bool Equals(object obj) { if (obj is CieXyChromaticityCoordinates other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyChromaticityCoordinates other) { if (X.Equals(other.X)) { return Y.Equals(other.Y); } return false; } } internal sealed class CieLabToCieLchConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLch Convert(in CieLab input) { float l = input.L; float a = input.A; float b = input.B; float c = MathF.Sqrt(a * a + b * b); float num = GeometryUtilities.RadianToDegree(MathF.Atan2(b, a)); for (num %= 360f; num < 0f; num += 360f) { } return new CieLch(l, c, num, input.WhitePoint); } } internal sealed class CieLabToCieXyzConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyz Convert(in CieLab input) { float l = input.L; float a = input.A; float b = input.B; float num = (l + 16f) / 116f; float num2 = a / 500f + num; float num3 = num - b / 200f; float num4 = Numerics.Pow3(num2); float num5 = Numerics.Pow3(num3); float x = ((num4 > 0.008856452f) ? num4 : ((116f * num2 - 16f) / 903.2963f)); float y = ((l > 8.000001f) ? Numerics.Pow3((l + 16f) / 116f) : (l / 903.2963f)); float z = ((num5 > 0.008856452f) ? num5 : ((116f * num3 - 16f) / 903.2963f)); Vector3 vector = new Vector3(input.WhitePoint.X, input.WhitePoint.Y, input.WhitePoint.Z); return new CieXyz(Vector3.Clamp(new Vector3(x, y, z), Vector3.Zero, Vector3.One) * vector); } } internal sealed class CieLchToCieLabConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLab Convert(in CieLch input) { float l = input.L; float c = input.C; float x = GeometryUtilities.DegreeToRadian(input.H); float a = c * MathF.Cos(x); float b = c * MathF.Sin(x); return new CieLab(l, a, b, input.WhitePoint); } } internal sealed class CieLchuvToCieLuvConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLuv Convert(in CieLchuv input) { float l = input.L; float c = input.C; float x = GeometryUtilities.DegreeToRadian(input.H); float u = c * MathF.Cos(x); float v = c * MathF.Sin(x); return new CieLuv(l, u, v, input.WhitePoint); } } internal sealed class CieLuvToCieLchuvConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLchuv Convert(in CieLuv input) { float l = input.L; float u = input.U; float v = input.V; float c = MathF.Sqrt(u * u + v * v); float num = GeometryUtilities.RadianToDegree(MathF.Atan2(v, u)); for (num %= 360f; num < 0f; num += 360f) { } return new CieLchuv(l, c, num, input.WhitePoint); } } internal sealed class CieLuvToCieXyzConverter { public CieXyz Convert(in CieLuv input) { float l = input.L; float u = input.U; float v = input.V; float num = ComputeU0(in input.WhitePoint); float num2 = ComputeV0(in input.WhitePoint); float num3 = ((l > 8.000001f) ? Numerics.Pow3((l + 16f) / 116f) : (l / 903.2963f)); float num4 = (52f * l / (u + 13f * l * num) - 1f) / 3f; float num5 = -5f * num3; float num6 = (num3 * (39f * l / (v + 13f * l * num2) - 5f) - num5) / (num4 - -0.3333333f); float num7 = num6 * num4 + num5; if (float.IsNaN(num6) || num6 < 0f) { num6 = 0f; } if (float.IsNaN(num3) || num3 < 0f) { num3 = 0f; } if (float.IsNaN(num7) || num7 < 0f) { num7 = 0f; } return new CieXyz(num6, num3, num7); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeU0(in CieXyz input) { return 4f * input.X / (input.X + 15f * input.Y + 3f * input.Z); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeV0(in CieXyz input) { return 9f * input.Y / (input.X + 15f * input.Y + 3f * input.Z); } } internal sealed class CieXyzAndCieXyyConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyy Convert(in CieXyz input) { float num = input.X / (input.X + input.Y + input.Z); float num2 = input.Y / (input.X + input.Y + input.Z); if (float.IsNaN(num) || float.IsNaN(num2)) { return new CieXyy(0f, 0f, input.Y); } return new CieXyy(num, num2, input.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyz Convert(in CieXyy input) { if (MathF.Abs(input.Y) < Constants.Epsilon) { return new CieXyz(0f, 0f, input.Yl); } float x = input.X * input.Yl / input.Y; float yl = input.Yl; float z = (1f - input.X - input.Y) * yl / input.Y; return new CieXyz(x, yl, z); } } internal abstract class CieXyzAndHunterLabConverterBase { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ComputeKa(CieXyz whitePoint) { if (whitePoint.Equals(Illuminants.C)) { return 175f; } return 88.36599f * (whitePoint.X + whitePoint.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ComputeKb(CieXyz whitePoint) { if (whitePoint == Illuminants.C) { return 70f; } return 32.0939f * (whitePoint.Y + whitePoint.Z); } } internal sealed class CieXyzAndLmsConverter { public static readonly Matrix4x4 DefaultTransformationMatrix = LmsAdaptationMatrix.Bradford; private Matrix4x4 inverseTransformationMatrix; private Matrix4x4 transformationMatrix; public CieXyzAndLmsConverter() : this(DefaultTransformationMatrix) { } public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix) { this.transformationMatrix = transformationMatrix; Matrix4x4.Invert(this.transformationMatrix, out inverseTransformationMatrix); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lms Convert(in CieXyz input) { return new Lms(Vector3.Transform(input.ToVector3(), transformationMatrix)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyz Convert(in Lms input) { return new CieXyz(Vector3.Transform(input.ToVector3(), inverseTransformationMatrix)); } } internal sealed class CieXyzToCieLabConverter { public CieXyz LabWhitePoint { get; } public CieXyzToCieLabConverter() : this(CieLab.DefaultWhitePoint) { } public CieXyzToCieLabConverter(CieXyz labWhitePoint) { LabWhitePoint = labWhitePoint; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLab Convert(in CieXyz input) { float x = LabWhitePoint.X; float y = LabWhitePoint.Y; float z = LabWhitePoint.Z; float num = input.X / x; float num2 = input.Y / y; float num3 = input.Z / z; float num4 = ((num > 0.008856452f) ? MathF.Pow(num, 0.3333333f) : ((903.2963f * num + 16f) / 116f)); float num5 = ((num2 > 0.008856452f) ? MathF.Pow(num2, 0.3333333f) : ((903.2963f * num2 + 16f) / 116f)); float num6 = ((num3 > 0.008856452f) ? MathF.Pow(num3, 0.3333333f) : ((903.2963f * num3 + 16f) / 116f)); float l = 116f * num5 - 16f; float a = 500f * (num4 - num5); float b = 200f * (num5 - num6); return new CieLab(l, a, b, LabWhitePoint); } } internal sealed class CieXyzToCieLuvConverter { public CieXyz LuvWhitePoint { get; } public CieXyzToCieLuvConverter() : this(CieLuv.DefaultWhitePoint) { } public CieXyzToCieLuvConverter(CieXyz luvWhitePoint) { LuvWhitePoint = luvWhitePoint; } public CieLuv Convert(in CieXyz input) { float num = input.Y / LuvWhitePoint.Y; float num2 = ComputeUp(in input); float num3 = ComputeVp(in input); float num4 = ComputeUp(LuvWhitePoint); float num5 = ComputeVp(LuvWhitePoint); float num6 = ((num > 0.008856452f) ? (116f * MathF.Pow(num, 0.3333333f) - 16f) : (903.2963f * num)); if (float.IsNaN(num6) || num6 < 0f) { num6 = 0f; } float num7 = 13f * num6 * (num2 - num4); float num8 = 13f * num6 * (num3 - num5); if (float.IsNaN(num7)) { num7 = 0f; } if (float.IsNaN(num8)) { num8 = 0f; } return new CieLuv(num6, num7, num8, LuvWhitePoint); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeUp(in CieXyz input) { return 4f * input.X / (input.X + 15f * input.Y + 3f * input.Z); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeVp(in CieXyz input) { return 9f * input.Y / (input.X + 15f * input.Y + 3f * input.Z); } } internal sealed class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase { public CieXyz HunterLabWhitePoint { get; } public CieXyzToHunterLabConverter() : this(HunterLab.DefaultWhitePoint) { } public CieXyzToHunterLabConverter(CieXyz labWhitePoint) { HunterLabWhitePoint = labWhitePoint; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public HunterLab Convert(in CieXyz input) { float x = input.X; float y = input.Y; float z = input.Z; float x2 = HunterLabWhitePoint.X; float y2 = HunterLabWhitePoint.Y; float z2 = HunterLabWhitePoint.Z; float num = CieXyzAndHunterLabConverterBase.ComputeKa(HunterLabWhitePoint); float num2 = CieXyzAndHunterLabConverterBase.ComputeKb(HunterLabWhitePoint); float num3 = y / y2; float num4 = MathF.Sqrt(num3); float l = 100f * num4; float num5 = num * ((x / x2 - num3) / num4); float num6 = num2 * ((num3 - z / z2) / num4); if (float.IsNaN(num5)) { num5 = 0f; } if (float.IsNaN(num6)) { num6 = 0f; } return new HunterLab(l, num5, num6, HunterLabWhitePoint); } } internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase { private readonly Matrix4x4 conversionMatrix; public RgbWorkingSpace TargetWorkingSpace { get; } public CieXyzToLinearRgbConverter() : this(Rgb.DefaultWorkingSpace) { } public CieXyzToLinearRgbConverter(RgbWorkingSpace workingSpace) { TargetWorkingSpace = workingSpace; Matrix4x4.Invert(LinearRgbAndCieXyzConverterBase.GetRgbToCieXyzMatrix(workingSpace), out var result); conversionMatrix = result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public LinearRgb Convert(in CieXyz input) { return new LinearRgb(Vector3.Transform(input.ToVector3(), conversionMatrix), TargetWorkingSpace); } } internal sealed class CmykAndRgbConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb Convert(in Cmyk input) { return new Rgb((Vector3.One - new Vector3(input.C, input.M, input.Y)) * (Vector3.One - new Vector3(input.K))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Cmyk Convert(in Rgb input) { Vector3 vector = Vector3.One - input.ToVector3(); Vector3 vector2 = new Vector3(MathF.Min(vector.X, MathF.Min(vector.Y, vector.Z))); if (MathF.Abs(vector2.X - 1f) < Constants.Epsilon) { return new Cmyk(0f, 0f, 0f, 1f); } vector = (vector - vector2) / (Vector3.One - vector2); return new Cmyk(vector.X, vector.Y, vector.Z, vector2.X); } } internal sealed class HslAndRgbConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb Convert(in Hsl input) { float num = input.H / 360f; float r = 0f; float g = 0f; float b = 0f; float s = input.S; float l = input.L; if (MathF.Abs(l) > Constants.Epsilon) { if (MathF.Abs(s) < Constants.Epsilon) { r = (g = (b = l)); } else { float num2 = ((l < 0.5f) ? (l * (1f + s)) : (l + s - l * s)); float first = 2f * l - num2; r = GetColorComponent(first, num2, num + 0.3333333f); g = GetColorComponent(first, num2, num); b = GetColorComponent(first, num2, num - 0.3333333f); } } return new Rgb(r, g, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Hsl Convert(in Rgb input) { float r = input.R; float g = input.G; float b = input.B; float num = MathF.Max(r, MathF.Max(g, b)); float num2 = MathF.Min(r, MathF.Min(g, b)); float num3 = num - num2; float num4 = 0f; float s = 0f; float num5 = (num + num2) / 2f; if (MathF.Abs(num3) < Constants.Epsilon) { return new Hsl(0f, s, num5); } if (MathF.Abs(r - num) < Constants.Epsilon) { num4 = (g - b) / num3; } else if (MathF.Abs(g - num) < Constants.Epsilon) { num4 = 2f + (b - r) / num3; } else if (MathF.Abs(b - num) < Constants.Epsilon) { num4 = 4f + (r - g) / num3; } num4 *= 60f; if (num4 < 0f) { num4 += 360f; } s = ((!(num5 <= 0.5f)) ? (num3 / (2f - num - num2)) : (num3 / (num + num2))); return new Hsl(num4, s, num5); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float GetColorComponent(float first, float second, float third) { third = MoveIntoRange(third); if (third < 0.1666667f) { return first + (second - first) * 6f * third; } if (third < 0.5f) { return second; } if (third < 2f / 3f) { return first + (second - first) * (2f / 3f - third) * 6f; } return first; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float MoveIntoRange(float value) { if (value < 0f) { value += 1f; } else if (value > 1f) { value -= 1f; } return value; } } internal sealed class HsvAndRgbConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb Convert(in Hsv input) { float s = input.S; float v = input.V; if (MathF.Abs(s) < Constants.Epsilon) { return new Rgb(v, v, v); } float num = ((MathF.Abs(input.H - 360f) < Constants.Epsilon) ? 0f : (input.H / 60f)); int num2 = (int)Math.Truncate(num); float num3 = num - (float)num2; float num4 = v * (1f - s); float num5 = v * (1f - s * num3); float num6 = v * (1f - s * (1f - num3)); float r; float g; float b; switch (num2) { case 0: r = v; g = num6; b = num4; break; case 1: r = num5; g = v; b = num4; break; case 2: r = num4; g = v; b = num6; break; case 3: r = num4; g = num5; b = v; break; case 4: r = num6; g = num4; b = v; break; default: r = v; g = num4; b = num5; break; } return new Rgb(r, g, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Hsv Convert(in Rgb input) { float r = input.R; float g = input.G; float b = input.B; float num = MathF.Max(r, MathF.Max(g, b)); float num2 = MathF.Min(r, MathF.Min(g, b)); float num3 = num - num2; float num4 = 0f; float s = 0f; float num5 = num; if (MathF.Abs(num3) < Constants.Epsilon) { return new Hsv(0f, s, num5); } if (MathF.Abs(r - num) < Constants.Epsilon) { num4 = (g - b) / num3; } else if (MathF.Abs(g - num) < Constants.Epsilon) { num4 = 2f + (b - r) / num3; } else if (MathF.Abs(b - num) < Constants.Epsilon) { num4 = 4f + (r - g) / num3; } num4 *= 60f; if ((double)num4 < 0.0) { num4 += 360f; } s = num3 / num5; return new Hsv(num4, s, num5); } } internal sealed class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase { [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyz Convert(in HunterLab input) { float l = input.L; float a = input.A; float b = input.B; float x = input.WhitePoint.X; float y = input.WhitePoint.Y; float z = input.WhitePoint.Z; float num = CieXyzAndHunterLabConverterBase.ComputeKa(input.WhitePoint); float num2 = CieXyzAndHunterLabConverterBase.ComputeKb(input.WhitePoint); float num3 = Numerics.Pow2(l / 100f); float num4 = MathF.Sqrt(num3); float y2 = num3 * y; float x2 = (a / num * num4 + num3) * x; float z2 = (b / num2 * num4 - num3) * (0f - z); return new CieXyz(x2, y2, z2); } } internal abstract class LinearRgbAndCieXyzConverterBase { public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpace workingSpace) { RgbPrimariesChromaticityCoordinates chromaticityCoordinates = workingSpace.ChromaticityCoordinates; float x = chromaticityCoordinates.R.X; float x2 = chromaticityCoordinates.G.X; float x3 = chromaticityCoordinates.B.X; float y = chromaticityCoordinates.R.Y; float y2 = chromaticityCoordinates.G.Y; float y3 = chromaticityCoordinates.B.Y; float num = x / y; float num2 = (1f - x - y) / y; float num3 = x2 / y2; float num4 = (1f - x2 - y2) / y2; float num5 = x3 / y3; float num6 = (1f - x3 - y3) / y3; Matrix4x4.Invert(new Matrix4x4 { M11 = num, M21 = num3, M31 = num5, M12 = 1f, M22 = 1f, M32 = 1f, M13 = num2, M23 = num4, M33 = num6, M44 = 1f }, out var result); Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), result); return new Matrix4x4 { M11 = vector.X * num, M21 = vector.Y * num3, M31 = vector.Z * num5, M12 = vector.X * 1f, M22 = vector.Y * 1f, M32 = vector.Z * 1f, M13 = vector.X * num2, M23 = vector.Y * num4, M33 = vector.Z * num6, M44 = 1f }; } } internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase { private readonly Matrix4x4 conversionMatrix; public RgbWorkingSpace SourceWorkingSpace { get; } public LinearRgbToCieXyzConverter() : this(Rgb.DefaultWorkingSpace) { } public LinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) { SourceWorkingSpace = workingSpace; conversionMatrix = LinearRgbAndCieXyzConverterBase.GetRgbToCieXyzMatrix(workingSpace); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyz Convert(in LinearRgb input) { return new CieXyz(Vector3.Transform(input.ToVector3(), conversionMatrix)); } } internal sealed class LinearRgbToRgbConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb Convert(in LinearRgb input) { return new Rgb(input.WorkingSpace.Compress(input.R), input.WorkingSpace.Compress(input.G), input.WorkingSpace.Compress(input.B), input.WorkingSpace); } } internal class RgbToLinearRgbConverter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public LinearRgb Convert(in Rgb input) { return new LinearRgb(input.WorkingSpace.Expand(input.R), input.WorkingSpace.Expand(input.G), input.WorkingSpace.Expand(input.B), input.WorkingSpace); } } internal sealed class YCbCrAndRgbConverter { private static readonly Vector3 MaxBytes = new Vector3(255f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb Convert(in YCbCr input) { float y = input.Y; float num = input.Cb - 128f; float num2 = input.Cr - 128f; float x = MathF.Round(y + 1.402f * num2, MidpointRounding.AwayFromZero); float y2 = MathF.Round(y - 0.344136f * num - 0.714136f * num2, MidpointRounding.AwayFromZero); float z = MathF.Round(y + 1.772f * num, MidpointRounding.AwayFromZero); return new Rgb(new Vector3(x, y2, z) / MaxBytes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public YCbCr Convert(in Rgb input) { Vector3 vector = input.ToVector3() * MaxBytes; float x = vector.X; float y = vector.Y; float z = vector.Z; float y2 = 0.299f * x + 0.587f * y + 0.114f * z; float cb = 128f + (-0.168736f * x - 0.331264f * y + 0.5f * z); float cr = 128f + (0.5f * x - 0.418688f * y - 0.081312f * z); return new YCbCr(y2, cb, cr); } } public interface IChromaticAdaptation { CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint); void Transform(ReadOnlySpan source, Span destination, CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint); } public static class LmsAdaptationMatrix { public static readonly Matrix4x4 VonKriesHPEAdjusted = Matrix4x4.Transpose(new Matrix4x4 { M11 = 0.40024f, M12 = 0.7076f, M13 = -0.08081f, M21 = -0.2263f, M22 = 1.16532f, M23 = 0.0457f, M31 = 0f, M32 = 0f, M33 = 0.91822f, M44 = 1f }); public static readonly Matrix4x4 VonKriesHPE = Matrix4x4.Transpose(new Matrix4x4 { M11 = 0.3897f, M12 = 0.689f, M13 = -0.0787f, M21 = -0.2298f, M22 = 1.1834f, M23 = 0.0464f, M31 = 0f, M32 = 0f, M33 = 1f, M44 = 1f }); public static readonly Matrix4x4 XyzScaling = Matrix4x4.Transpose(Matrix4x4.Identity); public static readonly Matrix4x4 Bradford = Matrix4x4.Transpose(new Matrix4x4 { M11 = 0.8951f, M12 = 0.2664f, M13 = -0.1614f, M21 = -0.7502f, M22 = 1.7135f, M23 = 0.0367f, M31 = 0.0389f, M32 = -0.0685f, M33 = 1.0296f, M44 = 1f }); public static readonly Matrix4x4 BradfordSharp = Matrix4x4.Transpose(new Matrix4x4 { M11 = 1.2694f, M12 = -0.0988f, M13 = -0.1706f, M21 = -0.8364f, M22 = 1.8006f, M23 = 0.0357f, M31 = 0.0297f, M32 = -0.0315f, M33 = 1.0018f, M44 = 1f }); public static readonly Matrix4x4 CMCCAT2000 = Matrix4x4.Transpose(new Matrix4x4 { M11 = 0.7982f, M12 = 0.3389f, M13 = -0.1371f, M21 = -0.5918f, M22 = 1.5512f, M23 = 0.0406f, M31 = 0.0008f, M32 = 0.239f, M33 = 0.9753f, M44 = 1f }); public static readonly Matrix4x4 CAT02 = Matrix4x4.Transpose(new Matrix4x4 { M11 = 0.7328f, M12 = 0.4296f, M13 = -0.1624f, M21 = -0.7036f, M22 = 1.6975f, M23 = 0.0061f, M31 = 0.003f, M32 = 0.0136f, M33 = 0.9834f, M44 = 1f }); } public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable { public CieXyChromaticityCoordinates R { get; } public CieXyChromaticityCoordinates G { get; } public CieXyChromaticityCoordinates B { get; } public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b) { R = r; G = g; B = b; } public static bool operator ==(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) { return left.Equals(right); } public static bool operator !=(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) { return !left.Equals(right); } public override bool Equals(object obj) { if (obj is RgbPrimariesChromaticityCoordinates other) { return Equals(other); } return false; } public bool Equals(RgbPrimariesChromaticityCoordinates other) { if (R.Equals(other.R) && G.Equals(other.G)) { return B.Equals(other.B); } return false; } public override int GetHashCode() { return HashCode.Combine(R, G, B); } } public sealed class VonKriesChromaticAdaptation : IChromaticAdaptation { private readonly CieXyzAndLmsConverter converter; public VonKriesChromaticAdaptation() : this(new CieXyzAndLmsConverter()) { } public VonKriesChromaticAdaptation(Matrix4x4 transformationMatrix) : this(new CieXyzAndLmsConverter(transformationMatrix)) { } internal VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) { this.converter = converter; } public CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint) { if (sourceWhitePoint.Equals(destinationWhitePoint)) { return source; } Lms lms = converter.Convert(in source); Lms lms2 = converter.Convert(in sourceWhitePoint); Vector3 left = converter.Convert(in destinationWhitePoint).ToVector3() / lms2.ToVector3(); Lms input = new Lms(Vector3.Multiply(left, lms.ToVector3())); return converter.Convert(in input); } public void Transform(ReadOnlySpan source, Span destination, CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint) { Guard.DestinationShouldNotBeTooShort(source, destination, "destination"); int length = source.Length; if (sourceWhitePoint.Equals(destinationWhitePoint)) { source.CopyTo(destination.Slice(0, length)); return; } ref CieXyz reference = ref MemoryMarshal.GetReference(source); ref CieXyz reference2 = ref MemoryMarshal.GetReference(destination); for (int i = 0; i < length; i++) { ref CieXyz input = ref Unsafe.Add(ref reference, i); ref CieXyz reference3 = ref Unsafe.Add(ref reference2, i); Lms lms = converter.Convert(in input); Lms lms2 = converter.Convert(in sourceWhitePoint); Vector3 left = converter.Convert(in destinationWhitePoint).ToVector3() / lms2.ToVector3(); Lms input2 = new Lms(Vector3.Multiply(left, lms.ToVector3())); reference3 = converter.Convert(in input2); } } CieXyz IChromaticAdaptation.Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint) { return Transform(in source, in sourceWhitePoint, in destinationWhitePoint); } void IChromaticAdaptation.Transform(ReadOnlySpan source, Span destination, CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint) { Transform(source, destination, sourceWhitePoint, in destinationWhitePoint); } } public sealed class GammaWorkingSpace : RgbWorkingSpace { public float Gamma { get; } public GammaWorkingSpace(float gamma, CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) : base(referenceWhite, chromaticityCoordinates) { Gamma = gamma; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override float Compress(float channel) { return GammaCompanding.Compress(channel, Gamma); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override float Expand(float channel) { return GammaCompanding.Expand(channel, Gamma); } public override bool Equals(object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (obj is GammaWorkingSpace gammaWorkingSpace) { if (Gamma.Equals(gammaWorkingSpace.Gamma) && base.WhitePoint.Equals(gammaWorkingSpace.WhitePoint)) { return base.ChromaticityCoordinates.Equals(gammaWorkingSpace.ChromaticityCoordinates); } return false; } return false; } public override int GetHashCode() { return HashCode.Combine(base.WhitePoint, base.ChromaticityCoordinates, Gamma); } } public sealed class LWorkingSpace : RgbWorkingSpace { public LWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) : base(referenceWhite, chromaticityCoordinates) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override float Compress(float channel) { return LCompanding.Compress(channel); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override float Expand(float channel) { return LCompanding.Expand(channel); } } public sealed class Rec2020WorkingSpace : RgbWorkingSpace { public Rec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) : base(referenceWhite, chromaticityCoordinates) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override float Compress(float channel) { return Rec2020Companding.Compress(channel); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override float Expand(float channel) { return Rec2020Companding.Expand(channel); } } public sealed class Rec709WorkingSpace : RgbWorkingSpace { public Rec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) : base(referenceWhite, chromaticityCoordinates) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override float Compress(float channel) { return Rec709Companding.Compress(channel); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override float Expand(float channel) { return Rec709Companding.Expand(channel); } } public abstract class RgbWorkingSpace { public CieXyz WhitePoint { get; } public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } protected RgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) { WhitePoint = referenceWhite; ChromaticityCoordinates = chromaticityCoordinates; } public abstract float Expand(float channel); public abstract float Compress(float channel); public override bool Equals(object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (obj is RgbWorkingSpace rgbWorkingSpace) { if (WhitePoint.Equals(rgbWorkingSpace.WhitePoint)) { return ChromaticityCoordinates.Equals(rgbWorkingSpace.ChromaticityCoordinates); } return false; } return false; } public override int GetHashCode() { return HashCode.Combine(WhitePoint, ChromaticityCoordinates); } } public sealed class SRgbWorkingSpace : RgbWorkingSpace { public SRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) : base(referenceWhite, chromaticityCoordinates) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override float Compress(float channel) { return SRgbCompanding.Compress(channel); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override float Expand(float channel) { return SRgbCompanding.Expand(channel); } } } namespace SixLabors.ImageSharp.ColorSpaces.Companding { public static class GammaCompanding { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Expand(float channel, float gamma) { return MathF.Pow(channel, gamma); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Compress(float channel, float gamma) { return MathF.Pow(channel, 1f / gamma); } } public static class LCompanding { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Expand(float channel) { if (!(channel <= 0.08f)) { return Numerics.Pow3((channel + 0.16f) / 1.16f); } return 100f * channel / 903.2963f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Compress(float channel) { if (!(channel <= 0.008856452f)) { return 1.16f * MathF.Pow(channel, 0.3333333f) - 0.16f; } return channel * 903.2963f / 100f; } } public static class Rec2020Companding { private const float Alpha = 1.0992968f; private const float AlphaMinusOne = 0.09929681f; private const float Beta = 0.01805397f; private const float InverseBeta = 0.08124286f; private const float Epsilon = 2.2222223f; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Expand(float channel) { if (!(channel < 0.08124286f)) { return MathF.Pow((channel + 0.09929681f) / 1.0992968f, 2.2222223f); } return channel / 4.5f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Compress(float channel) { if (!(channel < 0.01805397f)) { return 1.0992968f * MathF.Pow(channel, 0.45f) - 0.09929681f; } return 4.5f * channel; } } public static class Rec709Companding { private const float Epsilon = 2.2222223f; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Expand(float channel) { if (!(channel < 0.081f)) { return MathF.Pow((channel + 0.099f) / 1.099f, 2.2222223f); } return channel / 4.5f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Compress(float channel) { if (!(channel < 0.018f)) { return 1.099f * MathF.Pow(channel, 0.45f) - 0.099f; } return 4.5f * channel; } } public static class SRgbCompanding { private const int Length = 65537; private const int Scale = 65535; private static readonly Lazy LazyCompressTable = new Lazy(delegate { float[] array = new float[65537]; for (int i = 0; i < array.Length; i++) { double num = (double)i / 65535.0; num = ((!(num <= 0.0031308049535603713)) ? (1.055 * Math.Pow(num, 5.0 / 12.0) - 0.055) : (num * 12.92)); array[i] = (float)num; } return array; }, isThreadSafe: true); private static readonly Lazy LazyExpandTable = new Lazy(delegate { float[] array = new float[65537]; for (int i = 0; i < array.Length; i++) { double num = (double)i / 65535.0; num = ((!(num <= 0.04045)) ? Math.Pow((num + 0.055) / 1.055, 2.4) : (num / 12.92)); array[i] = (float)num; } return array; }, isThreadSafe: true); private static float[] ExpandTable => LazyExpandTable.Value; private static float[] CompressTable => LazyCompressTable.Value; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Expand(Span vectors) { if (Avx2.IsSupported && vectors.Length >= 2) { CompandAvx2(vectors, ExpandTable); if (Numerics.Modulo2(vectors.Length) != 0) { Expand(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1))); } } else { CompandScalar(vectors, ExpandTable); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Compress(Span vectors) { if (Avx2.IsSupported && vectors.Length >= 2) { CompandAvx2(vectors, CompressTable); if (Numerics.Modulo2(vectors.Length) != 0) { Compress(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1))); } } else { CompandScalar(vectors, CompressTable); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Expand(ref Vector4 vector) { vector.X = Expand(vector.X); vector.Y = Expand(vector.Y); vector.Z = Expand(vector.Z); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Compress(ref Vector4 vector) { vector.X = Compress(vector.X); vector.Y = Compress(vector.Y); vector.Z = Compress(vector.Z); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Expand(float channel) { if (!(channel <= 0.04045f)) { return MathF.Pow((channel + 0.055f) / 1.055f, 2.4f); } return channel / 12.92f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Compress(float channel) { if (!(channel <= 0.0031308f)) { return 1.055f * MathF.Pow(channel, 5f / 12f) - 0.055f; } return 12.92f * channel; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void CompandAvx2(Span vectors, float[] table) { fixed (float* baseAddress = &table[0]) { Vector256 vector = Vector256.Create(65535f); Vector256 zero = Vector256.Zero; Vector256 right = Vector256.Create(1); ref Vector256 reference = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); ref Vector256 right2 = ref Unsafe.Add(ref reference, (IntPtr)((uint)vectors.Length / 2u)); while (Unsafe.IsAddressLessThan(in reference, in right2)) { Vector256 right3 = Avx.Multiply(vector, reference); right3 = Avx.Min(Avx.Max(zero, right3), vector); Vector256 vector2 = Avx.ConvertToVector256Int32WithTruncation(right3); Vector256 right4 = Avx.ConvertToVector256Single(vector2); Vector256 left = Numerics.Lerp(Avx2.GatherVector256(baseAddress, vector2, 4), Avx2.GatherVector256(baseAddress, Avx2.Add(vector2, right), 4), Avx.Subtract(right3, right4)); reference = Avx.Blend(left, reference, 136); reference = ref Unsafe.Add(ref reference, 1); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void CompandScalar(Span vectors, float[] table) { fixed (float* ptr = &table[0]) { Vector4 zero = Vector4.Zero; Vector4 max = new Vector4(65535f); ref Vector4 reference = ref MemoryMarshal.GetReference(vectors); ref Vector4 right = ref Unsafe.Add(ref reference, vectors.Length); while (Unsafe.IsAddressLessThan(in reference, in right)) { Vector4 vector = Numerics.Clamp(reference * 65535f, zero, max); float x = vector.X; float y = vector.Y; float z = vector.Z; uint num = (uint)x; uint num2 = (uint)y; uint num3 = (uint)z; reference.X = Numerics.Lerp(ptr[num], ptr[num + 1], x - (float)(int)num); reference.Y = Numerics.Lerp(ptr[num2], ptr[num2 + 1], y - (float)(int)num2); reference.Z = Numerics.Lerp(ptr[num3], ptr[num3 + 1], z - (float)(int)num3); reference = ref Unsafe.Add(ref reference, 1); } } } } } namespace SixLabors.ImageSharp.Advanced { public static class AdvancedImageExtensions { public static IImageEncoder DetectEncoder(this Image source, string filePath) { Guard.NotNull(filePath, "filePath"); string extension = Path.GetExtension(filePath); IImageFormat imageFormat = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(extension); if (imageFormat == null) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("No encoder was found for extension '" + extension + "'. Registered encoders include:"); foreach (IImageFormat imageFormat2 in source.GetConfiguration().ImageFormats) { stringBuilder.AppendFormat(" - {0} : {1}{2}", imageFormat2.Name, string.Join(", ", imageFormat2.FileExtensions), Environment.NewLine); } throw new NotSupportedException(stringBuilder.ToString()); } IImageEncoder imageEncoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(imageFormat); if (imageEncoder == null) { StringBuilder stringBuilder2 = new StringBuilder(); stringBuilder2.AppendLine("No encoder was found for extension '" + extension + "' using image format '" + imageFormat.Name + "'. Registered encoders include:"); foreach (KeyValuePair imageEncoder2 in source.GetConfiguration().ImageFormatsManager.ImageEncoders) { stringBuilder2.AppendFormat(" - {0} : {1}{2}", imageEncoder2.Key, imageEncoder2.Value.GetType().Name, Environment.NewLine); } throw new NotSupportedException(stringBuilder2.ToString()); } return imageEncoder; } public static void AcceptVisitor(this Image source, IImageVisitor visitor) { source.Accept(visitor); } public static Task AcceptVisitorAsync(this Image source, IImageVisitorAsync visitor, CancellationToken cancellationToken = default(CancellationToken)) { return source.AcceptAsync(visitor, cancellationToken); } public static Configuration GetConfiguration(this Image source) { return GetConfiguration((IConfigurationProvider)source); } public static Configuration GetConfiguration(this ImageFrame source) { return GetConfiguration((IConfigurationProvider)source); } private static Configuration GetConfiguration(IConfigurationProvider source) { return source?.Configuration ?? Configuration.Default; } public static IMemoryGroup GetPixelMemoryGroup(this ImageFrame source) where TPixel : unmanaged, IPixel { return source?.PixelBuffer.FastMemoryGroup.View ?? throw new ArgumentNullException("source"); } public static IMemoryGroup GetPixelMemoryGroup(this Image source) where TPixel : unmanaged, IPixel { return source?.Frames.RootFrame.GetPixelMemoryGroup() ?? throw new ArgumentNullException("source"); } public static Memory DangerousGetPixelRowMemory(this ImageFrame source, int rowIndex) where TPixel : unmanaged, IPixel { Guard.NotNull(source, "source"); Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, "rowIndex"); Guard.MustBeLessThan(rowIndex, source.Height, "rowIndex"); return source.PixelBuffer.GetSafeRowMemory(rowIndex); } public static Memory DangerousGetPixelRowMemory(this Image source, int rowIndex) where TPixel : unmanaged, IPixel { Guard.NotNull(source, "source"); Guard.MustBeGreaterThanOrEqualTo(rowIndex, 0, "rowIndex"); Guard.MustBeLessThan(rowIndex, source.Height, "rowIndex"); return source.Frames.RootFrame.PixelBuffer.GetSafeRowMemory(rowIndex); } internal static MemoryAllocator GetMemoryAllocator(this IConfigurationProvider source) { return GetConfiguration(source).MemoryAllocator; } } [ExcludeFromCodeCoverage] internal static class AotCompilerTools { [Preserve] private static void SeedPixelFormats() { try { Unsafe.SizeOf(); Unsafe.SizeOf(); Unsafe.SizeOf(); Unsafe.SizeOf(); Unsafe.SizeOf(); Unsafe.SizeOf(); Unsafe.SizeOf(); Unsafe.SizeOf(); Unsafe.SizeOf(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); Seed(); } catch { } throw new InvalidOperationException("This method is used for AOT code generation only. Do not call it at runtime."); } [Preserve] private static void Seed() where TPixel : unmanaged, IPixel { AotCompileImage(); AotCompileImageProcessingContextFactory(); AotCompileImageEncoderInternals(); AotCompileImageDecoderInternals(); AotCompileImageEncoders(); AotCompileImageDecoders(); AotCompileImageProcessors(); AotCompileGenericImageProcessors(); AotCompileResamplers(); AotCompileQuantizers(); AotCompilePixelSamplingStrategys(); AotCompileDithers(); AotCompileMemoryManagers(); Unsafe.SizeOf(); } [Preserve] private static void AotCompileImage() where TPixel : unmanaged, IPixel { ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ((Image)null).CloneAs((Configuration)null); ImageFrame.LoadPixelData(null, default(ReadOnlySpan), 0, 0); ImageFrame.LoadPixelData(null, default(ReadOnlySpan), 0, 0); } [Preserve] private static void AotCompileImageProcessingContextFactory() where TPixel : unmanaged, IPixel { ((DefaultImageOperationsProviderFactory)null).CreateImageProcessingContext((Configuration)null, (Image)null, mutate: false); } [Preserve] private static void AotCompileImageEncoderInternals() where TPixel : unmanaged, IPixel { ((WebpEncoderCore)null).Encode((Image)null, (Stream)null, default(CancellationToken)); ((BmpEncoderCore)null).Encode((Image)null, (Stream)null, default(CancellationToken)); ((GifEncoderCore)null).Encode((Image)null, (Stream)null, default(CancellationToken)); ((JpegEncoderCore)null).Encode((Image)null, (Stream)null, default(CancellationToken)); ((PbmEncoderCore)null).Encode((Image)null, (Stream)null, default(CancellationToken)); ((PngEncoderCore)null).Encode((Image)null, (Stream)null, default(CancellationToken)); ((TgaEncoderCore)null).Encode((Image)null, (Stream)null, default(CancellationToken)); ((TiffEncoderCore)null).Encode((Image)null, (Stream)null, default(CancellationToken)); } [Preserve] private static void AotCompileImageDecoderInternals() where TPixel : unmanaged, IPixel { ((IImageDecoderInternals)null).Decode(null, null, default(CancellationToken)); ((IImageDecoderInternals)null).Decode(null, null, default(CancellationToken)); ((IImageDecoderInternals)null).Decode(null, null, default(CancellationToken)); ((IImageDecoderInternals)null).Decode(null, null, default(CancellationToken)); ((IImageDecoderInternals)null).Decode(null, null, default(CancellationToken)); ((IImageDecoderInternals)null).Decode(null, null, default(CancellationToken)); ((IImageDecoderInternals)null).Decode(null, null, default(CancellationToken)); ((IImageDecoderInternals)null).Decode(null, null, default(CancellationToken)); } [Preserve] private static void AotCompileImageEncoders() where TPixel : unmanaged, IPixel { AotCompileImageEncoder(); AotCompileImageEncoder(); AotCompileImageEncoder(); AotCompileImageEncoder(); AotCompileImageEncoder(); AotCompileImageEncoder(); AotCompileImageEncoder(); AotCompileImageEncoder(); } [Preserve] private static void AotCompileImageDecoders() where TPixel : unmanaged, IPixel { AotCompileImageDecoder(); AotCompileImageDecoder(); AotCompileImageDecoder(); AotCompileImageDecoder(); AotCompileImageDecoder(); AotCompileImageDecoder(); AotCompileImageDecoder(); AotCompileImageDecoder(); } [Preserve] private static void AotCompileImageEncoder() where TPixel : unmanaged, IPixel where TEncoder : class, IImageEncoder { ((IImageEncoder)null).Encode((Image)null, (Stream)null); ((IImageEncoder)null).EncodeAsync((Image)null, (Stream)null, default(CancellationToken)); } [Preserve] private static void AotCompileImageDecoder() where TPixel : unmanaged, IPixel where TDecoder : class, IImageDecoder { ((IImageDecoder)null).Decode((Configuration)null, (Stream)null, default(CancellationToken)); } [Preserve] private static void AotCompileImageProcessors() where TPixel : unmanaged, IPixel { AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompileImageProcessor(); AotCompilerCloningImageProcessor(); AotCompilerCloningImageProcessor(); AotCompilerCloningImageProcessor(); AotCompilerCloningImageProcessor(); AotCompilerCloningImageProcessor(); AotCompilerCloningImageProcessor(); AotCompilerCloningImageProcessor(); } [Preserve] private static void AotCompileImageProcessor() where TPixel : unmanaged, IPixel where TProc : class, IImageProcessor { ((IImageProcessor)null).CreatePixelSpecificProcessor((Configuration)null, (Image)null, default(Rectangle)); } [Preserve] private static void AotCompilerCloningImageProcessor() where TPixel : unmanaged, IPixel where TProc : class, ICloningImageProcessor { ((ICloningImageProcessor)null).CreatePixelSpecificCloningProcessor((Configuration)null, (Image)null, default(Rectangle)); } [Preserve] private static void AotCompileGenericImageProcessors() where TPixel : unmanaged, IPixel { AotCompileGenericCloningImageProcessor>(); AotCompileGenericCloningImageProcessor>(); AotCompileGenericCloningImageProcessor>(); AotCompileGenericCloningImageProcessor>(); AotCompileGenericCloningImageProcessor>(); } [Preserve] private static void AotCompileGenericCloningImageProcessor() where TPixel : unmanaged, IPixel where TProc : class, ICloningImageProcessor { ((ICloningImageProcessor)null).CloneAndExecute(); } [Preserve] private static void AotCompileResamplers() where TPixel : unmanaged, IPixel { AotCompileResampler(); AotCompileResampler(); AotCompileResampler(); AotCompileResampler(); AotCompileResampler(); AotCompileResampler(); AotCompileResampler(); } [Preserve] private static void AotCompileResampler() where TPixel : unmanaged, IPixel where TResampler : struct, IResampler { default(TResampler).ApplyTransform(null); ((AffineTransformProcessor)null).ApplyTransform(default(TResampler)); ((ProjectiveTransformProcessor)null).ApplyTransform(default(TResampler)); ((ResizeProcessor)null).ApplyTransform(default(TResampler)); ((AffineTransformProcessor)null).ApplyTransform(default(TResampler)); } [Preserve] private static void AotCompileQuantizers() where TPixel : unmanaged, IPixel { AotCompileQuantizer(); AotCompileQuantizer(); AotCompileQuantizer(); AotCompileQuantizer(); AotCompileQuantizer(); } [Preserve] private static void AotCompileQuantizer() where TPixel : unmanaged, IPixel where TQuantizer : class, IQuantizer { ((IQuantizer)null).CreatePixelSpecificQuantizer((Configuration)null); ((IQuantizer)null).CreatePixelSpecificQuantizer((Configuration)null, (QuantizerOptions)null); } [Preserve] private static void AotCompilePixelSamplingStrategys() where TPixel : unmanaged, IPixel { ((DefaultPixelSamplingStrategy)null).EnumeratePixelRegions((Image)null); ((ExtensivePixelSamplingStrategy)null).EnumeratePixelRegions((Image)null); } [Preserve] private static void AotCompileDithers() where TPixel : unmanaged, IPixel { AotCompileDither(); AotCompileDither(); } [Preserve] private static void AotCompileDither() where TPixel : unmanaged, IPixel where TDither : struct, IDither { OctreeQuantizer quantizer = default(OctreeQuantizer); default(TDither).ApplyQuantizationDither, TPixel>(ref quantizer, null, null, default(Rectangle)); PaletteQuantizer quantizer2 = default(PaletteQuantizer); default(TDither).ApplyQuantizationDither, TPixel>(ref quantizer2, null, null, default(Rectangle)); WuQuantizer quantizer3 = default(WuQuantizer); default(TDither).ApplyQuantizationDither, TPixel>(ref quantizer3, null, null, default(Rectangle)); default(TDither).ApplyPaletteDither.DitherProcessor, TPixel>(default(PaletteDitherProcessor.DitherProcessor), null, default(Rectangle)); } [Preserve] private static void AotCompileMemoryManagers() where TPixel : unmanaged, IPixel { AotCompileMemoryManager(); AotCompileMemoryManager(); } [Preserve] private static void AotCompileMemoryManager() where TPixel : unmanaged, IPixel where TBuffer : MemoryAllocator { ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); ((MemoryAllocator)null).Allocate(0, AllocationOptions.None); } } internal interface IConfigurationProvider { Configuration Configuration { get; } } public interface IImageVisitor { void Visit(Image image) where TPixel : unmanaged, IPixel; } public interface IImageVisitorAsync { Task VisitAsync(Image image, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel; } internal interface IPixelSource { Buffer2D PixelBuffer { get; } } internal interface IPixelSource where TPixel : unmanaged, IPixel { Buffer2D PixelBuffer { get; } } public interface IRowIntervalOperation { void Invoke(in RowInterval rows); } public interface IRowIntervalOperation where TBuffer : unmanaged { void Invoke(in RowInterval rows, Span span); } public interface IRowOperation { void Invoke(int y); } public interface IRowOperation where TBuffer : unmanaged { void Invoke(int y, Span span); } public readonly struct ParallelExecutionSettings { public const int DefaultMinimumPixelsProcessedPerTask = 4096; public MemoryAllocator MemoryAllocator { get; } public int MaxDegreeOfParallelism { get; } public int MinimumPixelsProcessedPerTask { get; } public ParallelExecutionSettings(int maxDegreeOfParallelism, int minimumPixelsProcessedPerTask, MemoryAllocator memoryAllocator) { if (maxDegreeOfParallelism == 0 || maxDegreeOfParallelism < -1) { throw new ArgumentOutOfRangeException("maxDegreeOfParallelism"); } Guard.MustBeGreaterThan(minimumPixelsProcessedPerTask, 0, "minimumPixelsProcessedPerTask"); Guard.NotNull(memoryAllocator, "memoryAllocator"); MaxDegreeOfParallelism = maxDegreeOfParallelism; MinimumPixelsProcessedPerTask = minimumPixelsProcessedPerTask; MemoryAllocator = memoryAllocator; } public ParallelExecutionSettings(int maxDegreeOfParallelism, MemoryAllocator memoryAllocator) : this(maxDegreeOfParallelism, 4096, memoryAllocator) { } public ParallelExecutionSettings MultiplyMinimumPixelsPerTask(int multiplier) { Guard.MustBeGreaterThan(multiplier, 0, "multiplier"); return new ParallelExecutionSettings(MaxDegreeOfParallelism, MinimumPixelsProcessedPerTask * multiplier, MemoryAllocator); } public static ParallelExecutionSettings FromConfiguration(Configuration configuration) { return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator); } } public static class ParallelRowIterator { private readonly struct RowOperationWrapper where T : struct, IRowOperation { private readonly int minY; private readonly int maxY; private readonly int stepY; private readonly T action; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperationWrapper(int minY, int maxY, int stepY, in T action) { this.minY = minY; this.maxY = maxY; this.stepY = stepY; this.action = action; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int i) { int num = minY + i * stepY; if (num < maxY) { int num2 = Math.Min(num + stepY, maxY); for (int j = num; j < num2; j++) { Unsafe.AsRef(in action).Invoke(j); } } } } private readonly struct RowOperationWrapper where T : struct, IRowOperation where TBuffer : unmanaged { private readonly int minY; private readonly int maxY; private readonly int stepY; private readonly int width; private readonly MemoryAllocator allocator; private readonly T action; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowOperationWrapper(int minY, int maxY, int stepY, int width, MemoryAllocator allocator, in T action) { this.minY = minY; this.maxY = maxY; this.stepY = stepY; this.width = width; this.allocator = allocator; this.action = action; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int i) { int num = minY + i * stepY; if (num >= maxY) { return; } int num2 = Math.Min(num + stepY, maxY); using IMemoryOwner memoryOwner = allocator.Allocate(width); Span span = memoryOwner.Memory.Span; for (int j = num; j < num2; j++) { Unsafe.AsRef(in action).Invoke(j, span); } } } private readonly struct RowIntervalOperationWrapper where T : struct, IRowIntervalOperation { private readonly int minY; private readonly int maxY; private readonly int stepY; private readonly T operation; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowIntervalOperationWrapper(int minY, int maxY, int stepY, in T operation) { this.minY = minY; this.maxY = maxY; this.stepY = stepY; this.operation = operation; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int i) { int num = minY + i * stepY; if (num < maxY) { int max = Math.Min(num + stepY, maxY); RowInterval rows = new RowInterval(num, max); Unsafe.AsRef(in operation).Invoke(in rows); } } } private readonly struct RowIntervalOperationWrapper where T : struct, IRowIntervalOperation where TBuffer : unmanaged { private readonly int minY; private readonly int maxY; private readonly int stepY; private readonly int width; private readonly MemoryAllocator allocator; private readonly T operation; [MethodImpl(MethodImplOptions.AggressiveInlining)] public RowIntervalOperationWrapper(int minY, int maxY, int stepY, int width, MemoryAllocator allocator, in T operation) { this.minY = minY; this.maxY = maxY; this.stepY = stepY; this.width = width; this.allocator = allocator; this.operation = operation; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(int i) { int num = minY + i * stepY; if (num >= maxY) { return; } int max = Math.Min(num + stepY, maxY); RowInterval rows = new RowInterval(num, max); using IMemoryOwner memoryOwner = allocator.Allocate(width); Unsafe.AsRef(in operation).Invoke(in rows, memoryOwner.Memory.Span); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation) where T : struct, IRowOperation { IterateRows(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), in operation); } public static void IterateRows(Rectangle rectangle, in ParallelExecutionSettings parallelSettings, in T operation) where T : struct, IRowOperation { ValidateRectangle(rectangle); int top = rectangle.Top; int bottom = rectangle.Bottom; int width = rectangle.Width; int height = rectangle.Height; int val = DivideCeil((long)width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); int num = Math.Min(parallelSettings.MaxDegreeOfParallelism, val); if (num == 1) { for (int i = top; i < bottom; i++) { Unsafe.AsRef(in operation).Invoke(i); } } else { int stepY = DivideCeil(rectangle.Height, num); ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = num }; RowOperationWrapper rowOperationWrapper = new RowOperationWrapper(top, bottom, stepY, in operation); Parallel.For(0, num, parallelOptions, ((RowOperationWrapper)rowOperationWrapper).Invoke); } } public static void IterateRows(Configuration configuration, Rectangle rectangle, in T operation) where T : struct, IRowOperation where TBuffer : unmanaged { IterateRows(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), in operation); } public static void IterateRows(Rectangle rectangle, in ParallelExecutionSettings parallelSettings, in T operation) where T : struct, IRowOperation where TBuffer : unmanaged { ValidateRectangle(rectangle); int top = rectangle.Top; int bottom = rectangle.Bottom; int width = rectangle.Width; int height = rectangle.Height; int val = DivideCeil((long)width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); int num = Math.Min(parallelSettings.MaxDegreeOfParallelism, val); MemoryAllocator memoryAllocator = parallelSettings.MemoryAllocator; if (num == 1) { using (IMemoryOwner memoryOwner = memoryAllocator.Allocate(width)) { Span span = memoryOwner.Memory.Span; for (int i = top; i < bottom; i++) { Unsafe.AsRef(in operation).Invoke(i, span); } return; } } int stepY = DivideCeil(height, num); ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = num }; RowOperationWrapper rowOperationWrapper = new RowOperationWrapper(top, bottom, stepY, width, memoryAllocator, in operation); Parallel.For(0, num, parallelOptions, ((RowOperationWrapper)rowOperationWrapper).Invoke); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IterateRowIntervals(Configuration configuration, Rectangle rectangle, in T operation) where T : struct, IRowIntervalOperation { IterateRowIntervals(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), in operation); } public static void IterateRowIntervals(Rectangle rectangle, in ParallelExecutionSettings parallelSettings, in T operation) where T : struct, IRowIntervalOperation { ValidateRectangle(rectangle); int top = rectangle.Top; int bottom = rectangle.Bottom; int width = rectangle.Width; int height = rectangle.Height; int val = DivideCeil((long)width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); int num = Math.Min(parallelSettings.MaxDegreeOfParallelism, val); if (num == 1) { RowInterval rows = new RowInterval(top, bottom); Unsafe.AsRef(in operation).Invoke(in rows); return; } int stepY = DivideCeil(rectangle.Height, num); ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = num }; RowIntervalOperationWrapper rowIntervalOperationWrapper = new RowIntervalOperationWrapper(top, bottom, stepY, in operation); Parallel.For(0, num, parallelOptions, ((RowIntervalOperationWrapper)rowIntervalOperationWrapper).Invoke); } public static void IterateRowIntervals(Configuration configuration, Rectangle rectangle, in T operation) where T : struct, IRowIntervalOperation where TBuffer : unmanaged { IterateRowIntervals(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), in operation); } public static void IterateRowIntervals(Rectangle rectangle, in ParallelExecutionSettings parallelSettings, in T operation) where T : struct, IRowIntervalOperation where TBuffer : unmanaged { ValidateRectangle(rectangle); int top = rectangle.Top; int bottom = rectangle.Bottom; int width = rectangle.Width; int height = rectangle.Height; int val = DivideCeil((long)width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask); int num = Math.Min(parallelSettings.MaxDegreeOfParallelism, val); MemoryAllocator memoryAllocator = parallelSettings.MemoryAllocator; if (num == 1) { RowInterval rows = new RowInterval(top, bottom); using IMemoryOwner memoryOwner = memoryAllocator.Allocate(width); Unsafe.AsRef(in operation).Invoke(in rows, memoryOwner.Memory.Span); return; } int stepY = DivideCeil(height, num); ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = num }; RowIntervalOperationWrapper rowIntervalOperationWrapper = new RowIntervalOperationWrapper(top, bottom, stepY, width, memoryAllocator, in operation); Parallel.For(0, num, parallelOptions, ((RowIntervalOperationWrapper)rowIntervalOperationWrapper).Invoke); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int DivideCeil(long dividend, int divisor) { return (int)Math.Min(1 + (dividend - 1) / divisor, 2147483647L); } private static void ValidateRectangle(Rectangle rectangle) { Guard.MustBeGreaterThan(rectangle.Width, 0, "rectangle.Width"); Guard.MustBeGreaterThan(rectangle.Height, 0, "rectangle.Height"); } } internal sealed class PreserveAttribute : Attribute { } }