mirror of https://github.com/SixLabors/ImageSharp
234 changed files with 6253 additions and 3024 deletions
@ -1,140 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Extension methods for classes that implement <see cref="IComparable{T}"/>.
|
|||
/// </summary>
|
|||
internal static class ComparableExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Restricts a <see cref="byte"/> to be within a specified range.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
|
|||
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="byte"/> representing the clamped value.
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static byte Clamp(this byte value, byte min, byte max) |
|||
{ |
|||
// Order is important here as someone might set min to higher than max.
|
|||
if (value >= max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value <= min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Restricts a <see cref="uint"/> to be within a specified range.
|
|||
/// </summary>
|
|||
/// <param name="value">The The value to clamp.</param>
|
|||
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
|
|||
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/> representing the clamped value.
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static uint Clamp(this uint value, uint min, uint max) |
|||
{ |
|||
if (value >= max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value <= min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Restricts a <see cref="int"/> to be within a specified range.
|
|||
/// </summary>
|
|||
/// <param name="value">The The value to clamp.</param>
|
|||
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
|
|||
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/> representing the clamped value.
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int Clamp(this int value, int min, int max) |
|||
{ |
|||
if (value >= max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value <= min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Restricts a <see cref="float"/> to be within a specified range.
|
|||
/// </summary>
|
|||
/// <param name="value">The The value to clamp.</param>
|
|||
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
|
|||
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/> representing the clamped value.
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static float Clamp(this float value, float min, float max) |
|||
{ |
|||
if (value >= max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value <= min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Restricts a <see cref="double"/> to be within a specified range.
|
|||
/// </summary>
|
|||
/// <param name="value">The The value to clamp.</param>
|
|||
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
|
|||
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="double"/> representing the clamped value.
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static double Clamp(this double value, double min, double max) |
|||
{ |
|||
if (value >= max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value <= min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,177 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized static methods for common mathematical functions specific
|
|||
/// to color processing.
|
|||
/// </summary>
|
|||
internal static class ColorNumerics |
|||
{ |
|||
/// <summary>
|
|||
/// Vector for converting pixel to gray value as specified by
|
|||
/// ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
private static readonly Vector4 Bt709 = new Vector4(.2126f, .7152f, .0722f, 0.0f); |
|||
|
|||
/// <summary>
|
|||
/// Convert a pixel value to grayscale using ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector to get the luminance from.</param>
|
|||
/// <param name="luminanceLevels">
|
|||
/// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images).
|
|||
/// </param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetBT709Luminance(ref Vector4 vector, int luminanceLevels) |
|||
=> (int)MathF.Round(Vector4.Dot(vector, Bt709) * (luminanceLevels - 1)); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula
|
|||
/// as specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="byte"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static byte Get8BitBT709Luminance(byte r, byte g, byte b) |
|||
=> (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula as
|
|||
/// specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="ushort"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) |
|||
=> (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula as specified
|
|||
/// by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="ushort"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static ushort Get16BitBT709Luminance(float r, float g, float b) |
|||
=> (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Scales a value from a 16 bit <see cref="ushort"/> to an
|
|||
/// 8 bit <see cref="byte"/> equivalent.
|
|||
/// </summary>
|
|||
/// <param name="component">The 8 bit component value.</param>
|
|||
/// <returns>The <see cref="byte"/></returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static byte DownScaleFrom16BitTo8Bit(ushort component) |
|||
{ |
|||
// To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is:
|
|||
//
|
|||
// (V * 255) / 65535
|
|||
//
|
|||
// This reduces to round(V / 257), or floor((V + 128.5)/257)
|
|||
//
|
|||
// Represent V as the two byte value vhi.vlo. Make a guess that the
|
|||
// result is the top byte of V, vhi, then the correction to this value
|
|||
// is:
|
|||
//
|
|||
// error = floor(((V-vhi.vhi) + 128.5) / 257)
|
|||
// = floor(((vlo-vhi) + 128.5) / 257)
|
|||
//
|
|||
// This can be approximated using integer arithmetic (and a signed
|
|||
// shift):
|
|||
//
|
|||
// error = (vlo-vhi+128) >> 8;
|
|||
//
|
|||
// The approximate differs from the exact answer only when (vlo-vhi) is
|
|||
// 128; it then gives a correction of +1 when the exact correction is
|
|||
// 0. This gives 128 errors. The exact answer (correct for all 16-bit
|
|||
// input values) is:
|
|||
//
|
|||
// error = (vlo-vhi+128)*65535 >> 24;
|
|||
//
|
|||
// An alternative arithmetic calculation which also gives no errors is:
|
|||
//
|
|||
// (V * 255 + 32895) >> 16
|
|||
return (byte)(((component * 255) + 32895) >> 16); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Scales a value from an 8 bit <see cref="byte"/> to
|
|||
/// an 16 bit <see cref="ushort"/> equivalent.
|
|||
/// </summary>
|
|||
/// <param name="component">The 8 bit component value.</param>
|
|||
/// <returns>The <see cref="ushort"/></returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static ushort UpscaleFrom8BitTo16Bit(byte component) |
|||
=> (ushort)(component * 257); |
|||
|
|||
/// <summary>
|
|||
/// Returns how many bits are required to store the specified number of colors.
|
|||
/// Performs a Log2() on the value.
|
|||
/// </summary>
|
|||
/// <param name="colors">The number of colors.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/>
|
|||
/// </returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetBitsNeededForColorDepth(int colors) |
|||
=> Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2))); |
|||
|
|||
/// <summary>
|
|||
/// Returns how many colors will be created by the specified number of bits.
|
|||
/// </summary>
|
|||
/// <param name="bitDepth">The bit depth.</param>
|
|||
/// <returns>The <see cref="int"/></returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetColorCountForBitDepth(int bitDepth) |
|||
=> 1 << bitDepth; |
|||
|
|||
/// <summary>
|
|||
/// Transforms a vector by the given color matrix.
|
|||
/// </summary>
|
|||
/// <param name="vector">The source vector.</param>
|
|||
/// <param name="matrix">The transformation color matrix.</param>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="Transform(ref Vector4, ref ColorMatrix)"/>.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
/// <param name="matrix">The transformation color matrix.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Transform(Span<Vector4> vectors, ref ColorMatrix matrix) |
|||
{ |
|||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|||
|
|||
for (int i = 0; i < vectors.Length; i++) |
|||
{ |
|||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|||
Transform(ref v, ref matrix); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,379 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides common mathematical methods.
|
|||
/// </summary>
|
|||
internal static class ImageMaths |
|||
{ |
|||
/// <summary>
|
|||
/// Vector for converting pixel to gray value as specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
private static readonly Vector4 Bt709 = new Vector4(.2126f, .7152f, .0722f, 0.0f); |
|||
|
|||
/// <summary>
|
|||
/// Convert a pixel value to grayscale using ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector to get the luminance from.</param>
|
|||
/// <param name="luminanceLevels">The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images)</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int GetBT709Luminance(ref Vector4 vector, int luminanceLevels) |
|||
=> (int)MathF.Round(Vector4.Dot(vector, Bt709) * (luminanceLevels - 1)); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="byte"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static byte Get8BitBT709Luminance(byte r, byte g, byte b) => |
|||
(byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="ushort"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) => |
|||
(ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="ushort"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static ushort Get16BitBT709Luminance(float r, float g, float b) => |
|||
(ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Scales a value from a 16 bit <see cref="ushort"/> to it's 8 bit <see cref="byte"/> equivalent.
|
|||
/// </summary>
|
|||
/// <param name="component">The 8 bit component value.</param>
|
|||
/// <returns>The <see cref="byte"/></returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static byte DownScaleFrom16BitTo8Bit(ushort component) |
|||
{ |
|||
// To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is:
|
|||
//
|
|||
// (V * 255) / 65535
|
|||
//
|
|||
// This reduces to round(V / 257), or floor((V + 128.5)/257)
|
|||
//
|
|||
// Represent V as the two byte value vhi.vlo. Make a guess that the
|
|||
// result is the top byte of V, vhi, then the correction to this value
|
|||
// is:
|
|||
//
|
|||
// error = floor(((V-vhi.vhi) + 128.5) / 257)
|
|||
// = floor(((vlo-vhi) + 128.5) / 257)
|
|||
//
|
|||
// This can be approximated using integer arithmetic (and a signed
|
|||
// shift):
|
|||
//
|
|||
// error = (vlo-vhi+128) >> 8;
|
|||
//
|
|||
// The approximate differs from the exact answer only when (vlo-vhi) is
|
|||
// 128; it then gives a correction of +1 when the exact correction is
|
|||
// 0. This gives 128 errors. The exact answer (correct for all 16-bit
|
|||
// input values) is:
|
|||
//
|
|||
// error = (vlo-vhi+128)*65535 >> 24;
|
|||
//
|
|||
// An alternative arithmetic calculation which also gives no errors is:
|
|||
//
|
|||
// (V * 255 + 32895) >> 16
|
|||
return (byte)(((component * 255) + 32895) >> 16); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Scales a value from an 8 bit <see cref="byte"/> to it's 16 bit <see cref="ushort"/> equivalent.
|
|||
/// </summary>
|
|||
/// <param name="component">The 8 bit component value.</param>
|
|||
/// <returns>The <see cref="ushort"/></returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257); |
|||
|
|||
/// <summary>
|
|||
/// Determine the Greatest CommonDivisor (GCD) of two numbers.
|
|||
/// </summary>
|
|||
public static int GreatestCommonDivisor(int a, int b) |
|||
{ |
|||
while (b != 0) |
|||
{ |
|||
int temp = b; |
|||
b = a % b; |
|||
a = temp; |
|||
} |
|||
|
|||
return a; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine the Least Common Multiple (LCM) of two numbers.
|
|||
/// </summary>
|
|||
public static int LeastCommonMultiple(int a, int b) |
|||
{ |
|||
// https://en.wikipedia.org/wiki/Least_common_multiple#Reduction_by_the_greatest_common_divisor
|
|||
return (a / GreatestCommonDivisor(a, b)) * b; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 2
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int Modulo2(int x) => x & 1; |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 4
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int Modulo4(int x) => x & 3; |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 8
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int Modulo8(int x) => x & 7; |
|||
|
|||
/// <summary>
|
|||
/// Fast (x mod m) calculator, with the restriction that
|
|||
/// <paramref name="m"/> should be power of 2.
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int ModuloP2(int x, int m) => x & (m - 1); |
|||
|
|||
/// <summary>
|
|||
/// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation.
|
|||
/// </summary>
|
|||
/// <param name="x">
|
|||
/// A number that is greater than <see cref="int.MinValue"/>, but less than or equal to <see cref="int.MaxValue"/>
|
|||
/// </param>
|
|||
/// <returns>The <see cref="int"/></returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int FastAbs(int x) |
|||
{ |
|||
int y = x >> 31; |
|||
return (x ^ y) - y; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number raised to the power of 2
|
|||
/// </summary>
|
|||
/// <param name="x">A single-precision floating-point number</param>
|
|||
/// <returns>The number <paramref name="x" /> raised to the power of 2.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static float Pow2(float x) => x * x; |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number raised to the power of 3
|
|||
/// </summary>
|
|||
/// <param name="x">A single-precision floating-point number</param>
|
|||
/// <returns>The number <paramref name="x" /> raised to the power of 3.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static float Pow3(float x) => x * x * x; |
|||
|
|||
/// <summary>
|
|||
/// Returns how many bits are required to store the specified number of colors.
|
|||
/// Performs a Log2() on the value.
|
|||
/// </summary>
|
|||
/// <param name="colors">The number of colors.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/>
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int GetBitsNeededForColorDepth(int colors) => Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2))); |
|||
|
|||
/// <summary>
|
|||
/// Returns how many colors will be created by the specified number of bits.
|
|||
/// </summary>
|
|||
/// <param name="bitDepth">The bit depth.</param>
|
|||
/// <returns>The <see cref="int"/></returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int GetColorCountForBitDepth(int bitDepth) => 1 << bitDepth; |
|||
|
|||
/// <summary>
|
|||
/// Implementation of 1D Gaussian G(x) function
|
|||
/// </summary>
|
|||
/// <param name="x">The x provided to G(x).</param>
|
|||
/// <param name="sigma">The spread of the blur.</param>
|
|||
/// <returns>The Gaussian G(x)</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static float Gaussian(float x, float sigma) |
|||
{ |
|||
const float Numerator = 1.0f; |
|||
float denominator = MathF.Sqrt(2 * MathF.PI) * sigma; |
|||
|
|||
float exponentNumerator = -x * x; |
|||
float exponentDenominator = 2 * Pow2(sigma); |
|||
|
|||
float left = Numerator / denominator; |
|||
float right = MathF.Exp(exponentNumerator / exponentDenominator); |
|||
|
|||
return left * right; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the result of a normalized sine cardinal function for the given value.
|
|||
/// SinC(x) = sin(pi*x)/(pi*x).
|
|||
/// </summary>
|
|||
/// <param name="f">A single-precision floating-point number to calculate the result for.</param>
|
|||
/// <returns>
|
|||
/// The sine cardinal of <paramref name="f" />.
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static float SinC(float f) |
|||
{ |
|||
if (MathF.Abs(f) > Constants.Epsilon) |
|||
{ |
|||
f *= MathF.PI; |
|||
float result = MathF.Sin(f) / f; |
|||
return MathF.Abs(result) < Constants.Epsilon ? 0F : result; |
|||
} |
|||
|
|||
return 1F; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the bounding <see cref="Rectangle"/> from the given points.
|
|||
/// </summary>
|
|||
/// <param name="topLeft">
|
|||
/// The <see cref="Point"/> designating the top left position.
|
|||
/// </param>
|
|||
/// <param name="bottomRight">
|
|||
/// The <see cref="Point"/> designating the bottom right position.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The bounding <see cref="Rectangle"/>.
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight) => new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); |
|||
|
|||
/// <summary>
|
|||
/// Finds the bounding rectangle based on the first instance of any color component other
|
|||
/// than the given one.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="bitmap">The <see cref="Image{TPixel}"/> to search within.</param>
|
|||
/// <param name="componentValue">The color component value to remove.</param>
|
|||
/// <param name="channel">The <see cref="RgbaComponent"/> channel to test against.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Rectangle"/>.
|
|||
/// </returns>
|
|||
public static Rectangle GetFilteredBoundingRectangle<TPixel>(ImageFrame<TPixel> bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
int width = bitmap.Width; |
|||
int height = bitmap.Height; |
|||
Point topLeft = default; |
|||
Point bottomRight = default; |
|||
|
|||
Func<ImageFrame<TPixel>, int, int, float, bool> delegateFunc; |
|||
|
|||
// Determine which channel to check against
|
|||
switch (channel) |
|||
{ |
|||
case RgbaComponent.R: |
|||
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().X - b) > Constants.Epsilon; |
|||
break; |
|||
|
|||
case RgbaComponent.G: |
|||
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Y - b) > Constants.Epsilon; |
|||
break; |
|||
|
|||
case RgbaComponent.B: |
|||
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Z - b) > Constants.Epsilon; |
|||
break; |
|||
|
|||
default: |
|||
delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().W - b) > Constants.Epsilon; |
|||
break; |
|||
} |
|||
|
|||
int GetMinY(ImageFrame<TPixel> pixels) |
|||
{ |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
if (delegateFunc(pixels, x, y, componentValue)) |
|||
{ |
|||
return y; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int GetMaxY(ImageFrame<TPixel> pixels) |
|||
{ |
|||
for (int y = height - 1; y > -1; y--) |
|||
{ |
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
if (delegateFunc(pixels, x, y, componentValue)) |
|||
{ |
|||
return y; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return height; |
|||
} |
|||
|
|||
int GetMinX(ImageFrame<TPixel> pixels) |
|||
{ |
|||
for (int x = 0; x < width; x++) |
|||
{ |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
if (delegateFunc(pixels, x, y, componentValue)) |
|||
{ |
|||
return x; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int GetMaxX(ImageFrame<TPixel> pixels) |
|||
{ |
|||
for (int x = width - 1; x > -1; x--) |
|||
{ |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
if (delegateFunc(pixels, x, y, componentValue)) |
|||
{ |
|||
return x; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return width; |
|||
} |
|||
|
|||
topLeft.Y = GetMinY(bitmap); |
|||
topLeft.X = GetMinX(bitmap); |
|||
bottomRight.Y = (GetMaxY(bitmap) + 1).Clamp(0, height); |
|||
bottomRight.X = (GetMaxX(bitmap) + 1).Clamp(0, width); |
|||
|
|||
return GetBoundingRectangle(topLeft, bottomRight); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,551 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized static methods for trigonometric, logarithmic,
|
|||
/// and other common mathematical functions.
|
|||
/// </summary>
|
|||
internal static class Numerics |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
private const int BlendAlphaControl = 0b_10_00_10_00; |
|||
private const int ShuffleAlphaControl = 0b_11_11_11_11; |
|||
#endif
|
|||
|
|||
/// <summary>
|
|||
/// Determine the Greatest CommonDivisor (GCD) of two numbers.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GreatestCommonDivisor(int a, int b) |
|||
{ |
|||
while (b != 0) |
|||
{ |
|||
int temp = b; |
|||
b = a % b; |
|||
a = temp; |
|||
} |
|||
|
|||
return a; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine the Least Common Multiple (LCM) of two numbers.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int LeastCommonMultiple(int a, int b) |
|||
{ |
|||
// https://en.wikipedia.org/wiki/Least_common_multiple#Reduction_by_the_greatest_common_divisor
|
|||
return (a / GreatestCommonDivisor(a, b)) * b; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 2
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Modulo2(int x) => x & 1; |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 4
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Modulo4(int x) => x & 3; |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 8
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Modulo8(int x) => x & 7; |
|||
|
|||
/// <summary>
|
|||
/// Fast (x mod m) calculator, with the restriction that
|
|||
/// <paramref name="m"/> should be power of 2.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int ModuloP2(int x, int m) => x & (m - 1); |
|||
|
|||
/// <summary>
|
|||
/// Returns the absolute value of a 32-bit signed integer.
|
|||
/// Uses bit shifting to speed up the operation compared to <see cref="Math"/>.
|
|||
/// </summary>
|
|||
/// <param name="x">
|
|||
/// A number that is greater than <see cref="int.MinValue"/>, but less than
|
|||
/// or equal to <see cref="int.MaxValue"/>
|
|||
/// </param>
|
|||
/// <returns>The <see cref="int"/></returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Abs(int x) |
|||
{ |
|||
int y = x >> 31; |
|||
return (x ^ y) - y; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number raised to the power of 2
|
|||
/// </summary>
|
|||
/// <param name="x">A single-precision floating-point number</param>
|
|||
/// <returns>The number <paramref name="x" /> raised to the power of 2.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float Pow2(float x) => x * x; |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number raised to the power of 3
|
|||
/// </summary>
|
|||
/// <param name="x">A single-precision floating-point number</param>
|
|||
/// <returns>The number <paramref name="x" /> raised to the power of 3.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float Pow3(float x) => x * x * x; |
|||
|
|||
/// <summary>
|
|||
/// Implementation of 1D Gaussian G(x) function
|
|||
/// </summary>
|
|||
/// <param name="x">The x provided to G(x).</param>
|
|||
/// <param name="sigma">The spread of the blur.</param>
|
|||
/// <returns>The Gaussian G(x)</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float Gaussian(float x, float sigma) |
|||
{ |
|||
const float Numerator = 1.0f; |
|||
float denominator = MathF.Sqrt(2 * MathF.PI) * sigma; |
|||
|
|||
float exponentNumerator = -x * x; |
|||
float exponentDenominator = 2 * Pow2(sigma); |
|||
|
|||
float left = Numerator / denominator; |
|||
float right = MathF.Exp(exponentNumerator / exponentDenominator); |
|||
|
|||
return left * right; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the result of a normalized sine cardinal function for the given value.
|
|||
/// SinC(x) = sin(pi*x)/(pi*x).
|
|||
/// </summary>
|
|||
/// <param name="f">A single-precision floating-point number to calculate the result for.</param>
|
|||
/// <returns>
|
|||
/// The sine cardinal of <paramref name="f" />.
|
|||
/// </returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float SinC(float f) |
|||
{ |
|||
if (MathF.Abs(f) > Constants.Epsilon) |
|||
{ |
|||
f *= MathF.PI; |
|||
float result = MathF.Sin(f) / f; |
|||
return MathF.Abs(result) < Constants.Epsilon ? 0F : result; |
|||
} |
|||
|
|||
return 1F; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="byte"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static byte Clamp(byte value, byte min, byte max) |
|||
{ |
|||
// Order is important here as someone might set min to higher than max.
|
|||
if (value > max) |
|||
{ |
|||
return max; |
|||
} |
|||
|
|||
if (value < min) |
|||
{ |
|||
return min; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="uint"/>.</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="uint"/>.</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="float"/>.</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="double"/>.</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// 5x Faster than <see cref="Vector4.Clamp(Vector4, Vector4, Vector4)"/>
|
|||
/// on platforms < NET 5.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="Vector4"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Clamp(Vector4 value, Vector4 min, Vector4 max) |
|||
=> Vector4.Min(Vector4.Max(value, min), max); |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<byte> span, byte min, byte max) |
|||
{ |
|||
Span<byte> remainder = span.Slice(ClampReduce(span, min, max)); |
|||
|
|||
if (remainder.Length > 0) |
|||
{ |
|||
for (int i = 0; i < remainder.Length; i++) |
|||
{ |
|||
ref byte v = ref remainder[i]; |
|||
v = Clamp(v, min, max); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<uint> span, uint min, uint max) |
|||
{ |
|||
Span<uint> remainder = span.Slice(ClampReduce(span, min, max)); |
|||
|
|||
if (remainder.Length > 0) |
|||
{ |
|||
for (int i = 0; i < remainder.Length; i++) |
|||
{ |
|||
ref uint v = ref remainder[i]; |
|||
v = Clamp(v, min, max); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<int> span, int min, int max) |
|||
{ |
|||
Span<int> remainder = span.Slice(ClampReduce(span, min, max)); |
|||
|
|||
if (remainder.Length > 0) |
|||
{ |
|||
for (int i = 0; i < remainder.Length; i++) |
|||
{ |
|||
ref int v = ref remainder[i]; |
|||
v = Clamp(v, min, max); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<float> span, float min, float max) |
|||
{ |
|||
Span<float> remainder = span.Slice(ClampReduce(span, min, max)); |
|||
|
|||
if (remainder.Length > 0) |
|||
{ |
|||
for (int i = 0; i < remainder.Length; i++) |
|||
{ |
|||
ref float v = ref remainder[i]; |
|||
v = Clamp(v, min, max); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<double> span, double min, double max) |
|||
{ |
|||
Span<double> remainder = span.Slice(ClampReduce(span, min, max)); |
|||
|
|||
if (remainder.Length > 0) |
|||
{ |
|||
for (int i = 0; i < remainder.Length; i++) |
|||
{ |
|||
ref double v = ref remainder[i]; |
|||
v = Clamp(v, min, max); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static int ClampReduce<T>(Span<T> span, T min, T max) |
|||
where T : unmanaged |
|||
{ |
|||
if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count) |
|||
{ |
|||
int remainder = ModuloP2(span.Length, Vector<T>.Count); |
|||
int adjustedCount = span.Length - remainder; |
|||
|
|||
if (adjustedCount > 0) |
|||
{ |
|||
ClampImpl(span.Slice(0, adjustedCount), min, max); |
|||
} |
|||
|
|||
return adjustedCount; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static void ClampImpl<T>(Span<T> span, T min, T max) |
|||
where T : unmanaged |
|||
{ |
|||
ref T sRef = ref MemoryMarshal.GetReference(span); |
|||
ref Vector<T> vsBase = ref Unsafe.As<T, Vector<T>>(ref MemoryMarshal.GetReference(span)); |
|||
var vmin = new Vector<T>(min); |
|||
var vmax = new Vector<T>(max); |
|||
|
|||
int n = span.Length / Vector<T>.Count; |
|||
int m = Modulo4(n); |
|||
int u = n - m; |
|||
|
|||
for (int i = 0; i < u; i += 4) |
|||
{ |
|||
ref Vector<T> vs0 = ref Unsafe.Add(ref vsBase, i); |
|||
ref Vector<T> vs1 = ref Unsafe.Add(ref vs0, 1); |
|||
ref Vector<T> vs2 = ref Unsafe.Add(ref vs0, 2); |
|||
ref Vector<T> vs3 = ref Unsafe.Add(ref vs0, 3); |
|||
|
|||
vs0 = Vector.Min(Vector.Max(vmin, vs0), vmax); |
|||
vs1 = Vector.Min(Vector.Max(vmin, vs1), vmax); |
|||
vs2 = Vector.Min(Vector.Max(vmin, vs2), vmax); |
|||
vs3 = Vector.Min(Vector.Max(vmin, vs3), vmax); |
|||
} |
|||
|
|||
if (m > 0) |
|||
{ |
|||
for (int i = u; i < n; i++) |
|||
{ |
|||
ref Vector<T> vs0 = ref Unsafe.Add(ref vsBase, i); |
|||
vs0 = Vector.Min(Vector.Max(vmin, vs0), vmax); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
|
|||
/// </summary>
|
|||
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Premultiply(ref Vector4 source) |
|||
{ |
|||
float w = source.W; |
|||
source *= w; |
|||
source.W = w; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reverses the result of premultiplying a vector via <see cref="Premultiply(ref Vector4)"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void UnPremultiply(ref Vector4 source) |
|||
{ |
|||
float w = source.W; |
|||
source /= w; |
|||
source.W = w; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="Premultiply(ref Vector4)"/>
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Premultiply(Span<Vector4> vectors) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
if (Avx2.IsSupported && vectors.Length >= 2) |
|||
{ |
|||
ref Vector256<float> vectorsBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(vectors)); |
|||
|
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
|
|||
ref Vector256<float> vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u)); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) |
|||
{ |
|||
Vector256<float> source = vectorsBase; |
|||
Vector256<float> multiply = Avx.Shuffle(source, source, ShuffleAlphaControl); |
|||
vectorsBase = Avx.Blend(Avx.Multiply(source, multiply), source, BlendAlphaControl); |
|||
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); |
|||
} |
|||
|
|||
if (Modulo2(vectors.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
|
|||
Premultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1))); |
|||
} |
|||
} |
|||
else |
|||
#endif
|
|||
{ |
|||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|||
|
|||
for (int i = 0; i < vectors.Length; i++) |
|||
{ |
|||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|||
Premultiply(ref v); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="UnPremultiply(ref Vector4)"/>
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void UnPremultiply(Span<Vector4> vectors) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
if (Avx2.IsSupported && vectors.Length >= 2) |
|||
{ |
|||
ref Vector256<float> vectorsBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(vectors)); |
|||
|
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
|
|||
ref Vector256<float> vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u)); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) |
|||
{ |
|||
Vector256<float> source = vectorsBase; |
|||
Vector256<float> multiply = Avx.Shuffle(source, source, ShuffleAlphaControl); |
|||
vectorsBase = Avx.Blend(Avx.Divide(source, multiply), source, BlendAlphaControl); |
|||
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); |
|||
} |
|||
|
|||
if (Modulo2(vectors.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
|
|||
UnPremultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1))); |
|||
} |
|||
} |
|||
else |
|||
#endif
|
|||
{ |
|||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|||
|
|||
for (int i = 0; i < vectors.Length; i++) |
|||
{ |
|||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|||
UnPremultiply(ref v); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,181 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Utility methods for the <see cref="Vector4"/> struct.
|
|||
/// </summary>
|
|||
internal static class Vector4Utilities |
|||
{ |
|||
private const int BlendAlphaControl = 0b_10_00_10_00; |
|||
private const int ShuffleAlphaControl = 0b_11_11_11_11; |
|||
|
|||
/// <summary>
|
|||
/// Restricts a vector between a minimum and a maximum value.
|
|||
/// 5x Faster then <see cref="Vector4.Clamp(Vector4, Vector4, Vector4)"/>.
|
|||
/// </summary>
|
|||
/// <param name="x">The vector to restrict.</param>
|
|||
/// <param name="min">The minimum value.</param>
|
|||
/// <param name="max">The maximum value.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static Vector4 FastClamp(Vector4 x, Vector4 min, Vector4 max) |
|||
=> Vector4.Min(Vector4.Max(x, min), max); |
|||
|
|||
/// <summary>
|
|||
/// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
|
|||
/// </summary>
|
|||
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Premultiply(ref Vector4 source) |
|||
{ |
|||
float w = source.W; |
|||
source *= w; |
|||
source.W = w; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reverses the result of premultiplying a vector via <see cref="Premultiply(ref Vector4)"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void UnPremultiply(ref Vector4 source) |
|||
{ |
|||
float w = source.W; |
|||
source /= w; |
|||
source.W = w; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="Premultiply(ref Vector4)"/>
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Premultiply(Span<Vector4> vectors) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
if (Avx2.IsSupported && vectors.Length >= 2) |
|||
{ |
|||
ref Vector256<float> vectorsBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(vectors)); |
|||
|
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
|
|||
ref Vector256<float> vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u)); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) |
|||
{ |
|||
Vector256<float> source = vectorsBase; |
|||
Vector256<float> multiply = Avx.Shuffle(source, source, ShuffleAlphaControl); |
|||
vectorsBase = Avx.Blend(Avx.Multiply(source, multiply), source, BlendAlphaControl); |
|||
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); |
|||
} |
|||
|
|||
if (ImageMaths.Modulo2(vectors.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
|
|||
Premultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1))); |
|||
} |
|||
} |
|||
else |
|||
#endif
|
|||
{ |
|||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|||
|
|||
for (int i = 0; i < vectors.Length; i++) |
|||
{ |
|||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|||
Premultiply(ref v); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="UnPremultiply(ref Vector4)"/>
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void UnPremultiply(Span<Vector4> vectors) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
if (Avx2.IsSupported && vectors.Length >= 2) |
|||
{ |
|||
ref Vector256<float> vectorsBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(vectors)); |
|||
|
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
|
|||
ref Vector256<float> vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u)); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) |
|||
{ |
|||
Vector256<float> source = vectorsBase; |
|||
Vector256<float> multiply = Avx.Shuffle(source, source, ShuffleAlphaControl); |
|||
vectorsBase = Avx.Blend(Avx.Divide(source, multiply), source, BlendAlphaControl); |
|||
vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); |
|||
} |
|||
|
|||
if (ImageMaths.Modulo2(vectors.Length) != 0) |
|||
{ |
|||
// Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
|
|||
UnPremultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1))); |
|||
} |
|||
} |
|||
else |
|||
#endif
|
|||
{ |
|||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|||
|
|||
for (int i = 0; i < vectors.Length; i++) |
|||
{ |
|||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|||
UnPremultiply(ref v); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Transforms a vector by the given matrix.
|
|||
/// </summary>
|
|||
/// <param name="vector">The source vector.</param>
|
|||
/// <param name="matrix">The transformation matrix.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="Transform(ref Vector4, ref ColorMatrix)"/>.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
/// <param name="matrix">The transformation matrix.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Transform(Span<Vector4> vectors, ref ColorMatrix matrix) |
|||
{ |
|||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|||
|
|||
for (int i = 0; i < vectors.Length; i++) |
|||
{ |
|||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|||
Transform(ref v, ref matrix); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal abstract class Avx2JpegColorConverter : VectorizedJpegColorConverter |
|||
{ |
|||
protected Avx2JpegColorConverter(JpegColorSpace colorSpace, int precision) |
|||
: base(colorSpace, precision, 8) |
|||
{ |
|||
} |
|||
|
|||
protected sealed override bool IsAvailable => SimdUtils.HasAvx2; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal abstract class BasicJpegColorConverter : JpegColorConverter |
|||
{ |
|||
protected BasicJpegColorConverter(JpegColorSpace colorSpace, int precision) |
|||
: base(colorSpace, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override bool IsAvailable => true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromCmykAvx2 : Avx2JpegColorConverter |
|||
{ |
|||
public FromCmykAvx2(int precision) |
|||
: base(JpegColorSpace.Cmyk, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> cBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> mBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> yBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
ref Vector256<float> kBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var one = Vector256.Create(1F); |
|||
|
|||
// Used for packing
|
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
Vector256<float> k = Avx2.PermuteVar8x32(Unsafe.Add(ref kBase, i), vcontrol); |
|||
Vector256<float> c = Avx2.PermuteVar8x32(Unsafe.Add(ref cBase, i), vcontrol); |
|||
Vector256<float> m = Avx2.PermuteVar8x32(Unsafe.Add(ref mBase, i), vcontrol); |
|||
Vector256<float> y = Avx2.PermuteVar8x32(Unsafe.Add(ref yBase, i), vcontrol); |
|||
|
|||
k = Avx.Multiply(k, scale); |
|||
|
|||
c = Avx.Multiply(Avx.Multiply(c, k), scale); |
|||
m = Avx.Multiply(Avx.Multiply(m, k), scale); |
|||
y = Avx.Multiply(Avx.Multiply(y, k), scale); |
|||
|
|||
Vector256<float> cmLo = Avx.UnpackLow(c, m); |
|||
Vector256<float> yoLo = Avx.UnpackLow(y, one); |
|||
Vector256<float> cmHi = Avx.UnpackHigh(c, m); |
|||
Vector256<float> yoHi = Avx.UnpackHigh(y, one); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.Shuffle(cmLo, yoLo, 0b01_00_01_00); |
|||
Unsafe.Add(ref destination, 1) = Avx.Shuffle(cmLo, yoLo, 0b11_10_11_10); |
|||
Unsafe.Add(ref destination, 2) = Avx.Shuffle(cmHi, yoHi, 0b01_00_01_00); |
|||
Unsafe.Add(ref destination, 3) = Avx.Shuffle(cmHi, yoHi, 0b11_10_11_10); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromCmykBasic.ConvertCore(values, result, this.MaximumValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.Tuples; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromCmykVector8 : Vector8JpegColorConverter |
|||
{ |
|||
public FromCmykVector8(int precision) |
|||
: base(JpegColorSpace.Cmyk, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
ref Vector<float> cBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> mBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> yBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
ref Vector<float> kBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
ref Vector4Octet resultBase = |
|||
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
Vector4Pair cc = default; |
|||
Vector4Pair mm = default; |
|||
Vector4Pair yy = default; |
|||
ref Vector<float> ccRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref cc); |
|||
ref Vector<float> mmRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref mm); |
|||
ref Vector<float> yyRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref yy); |
|||
|
|||
var scale = new Vector<float>(1 / this.MaximumValue); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
Vector<float> c = Unsafe.Add(ref cBase, i); |
|||
Vector<float> m = Unsafe.Add(ref mBase, i); |
|||
Vector<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector<float> k = Unsafe.Add(ref kBase, i) * scale; |
|||
|
|||
c = (c * k) * scale; |
|||
m = (m * k) * scale; |
|||
y = (y * k) * scale; |
|||
|
|||
ccRefAsVector = c; |
|||
mmRefAsVector = m; |
|||
yyRefAsVector = y; |
|||
|
|||
// Collect (c0,c1...c8) (m0,m1...m8) (y0,y1...y8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
|
|||
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); |
|||
destination.Pack(ref cc, ref mm, ref yy); |
|||
} |
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromCmykBasic.ConvertCore(values, result, this.MaximumValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromGrayscaleAvx2 : Avx2JpegColorConverter |
|||
{ |
|||
public FromGrayscaleAvx2(int precision) |
|||
: base(JpegColorSpace.Grayscale, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> gBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var one = Vector256.Create(1F); |
|||
|
|||
// Used for packing
|
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
Vector256<float> g = Avx.Multiply(Unsafe.Add(ref gBase, i), scale); |
|||
|
|||
g = Avx2.PermuteVar8x32(g, vcontrol); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.Blend(Avx.Permute(g, 0b00_00_00_00), one, 0b1000_1000); |
|||
Unsafe.Add(ref destination, 1) = Avx.Blend(Avx.Shuffle(g, g, 0b01_01_01_01), one, 0b1000_1000); |
|||
Unsafe.Add(ref destination, 2) = Avx.Blend(Avx.Shuffle(g, g, 0b10_10_10_10), one, 0b1000_1000); |
|||
Unsafe.Add(ref destination, 3) = Avx.Blend(Avx.Shuffle(g, g, 0b11_11_11_11), one, 0b1000_1000); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromGrayscaleBasic.ConvertCore(values, result, this.MaximumValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromRgbAvx2 : Avx2JpegColorConverter |
|||
{ |
|||
public FromRgbAvx2(int precision) |
|||
: base(JpegColorSpace.RGB, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> rBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> gBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> bBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var one = Vector256.Create(1F); |
|||
|
|||
// Used for packing
|
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
Vector256<float> r = Avx.Multiply(Avx2.PermuteVar8x32(Unsafe.Add(ref rBase, i), vcontrol), scale); |
|||
Vector256<float> g = Avx.Multiply(Avx2.PermuteVar8x32(Unsafe.Add(ref gBase, i), vcontrol), scale); |
|||
Vector256<float> b = Avx.Multiply(Avx2.PermuteVar8x32(Unsafe.Add(ref bBase, i), vcontrol), scale); |
|||
|
|||
Vector256<float> rgLo = Avx.UnpackLow(r, g); |
|||
Vector256<float> boLo = Avx.UnpackLow(b, one); |
|||
Vector256<float> rgHi = Avx.UnpackHigh(r, g); |
|||
Vector256<float> boHi = Avx.UnpackHigh(b, one); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.Shuffle(rgLo, boLo, 0b01_00_01_00); |
|||
Unsafe.Add(ref destination, 1) = Avx.Shuffle(rgLo, boLo, 0b11_10_11_10); |
|||
Unsafe.Add(ref destination, 2) = Avx.Shuffle(rgHi, boHi, 0b01_00_01_00); |
|||
Unsafe.Add(ref destination, 3) = Avx.Shuffle(rgHi, boHi, 0b11_10_11_10); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromRgbBasic.ConvertCore(values, result, this.MaximumValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.Tuples; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromRgbVector8 : Vector8JpegColorConverter |
|||
{ |
|||
public FromRgbVector8(int precision) |
|||
: base(JpegColorSpace.RGB, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
ref Vector<float> rBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> gBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> bBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector4Octet resultBase = |
|||
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
Vector4Pair rr = default; |
|||
Vector4Pair gg = default; |
|||
Vector4Pair bb = default; |
|||
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr); |
|||
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg); |
|||
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb); |
|||
|
|||
var scale = new Vector<float>(1 / this.MaximumValue); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
Vector<float> r = Unsafe.Add(ref rBase, i); |
|||
Vector<float> g = Unsafe.Add(ref gBase, i); |
|||
Vector<float> b = Unsafe.Add(ref bBase, i); |
|||
r *= scale; |
|||
g *= scale; |
|||
b *= scale; |
|||
|
|||
rrRefAsVector = r; |
|||
ggRefAsVector = g; |
|||
bbRefAsVector = b; |
|||
|
|||
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
|
|||
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); |
|||
destination.Pack(ref rr, ref gg, ref bb); |
|||
} |
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromRgbBasic.ConvertCore(values, result, this.MaximumValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
|
|||
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromYCbCrAvx2 : Avx2JpegColorConverter |
|||
{ |
|||
public FromYCbCrAvx2(int precision) |
|||
: base(JpegColorSpace.YCbCr, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> yBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> cbBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> crBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var chromaOffset = Vector256.Create(-this.HalfValue); |
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var rCrMult = Vector256.Create(1.402F); |
|||
var gCbMult = Vector256.Create(-0.344136F); |
|||
var gCrMult = Vector256.Create(-0.714136F); |
|||
var bCbMult = Vector256.Create(1.772F); |
|||
|
|||
// Used for packing.
|
|||
var va = Vector256.Create(1F); |
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
Vector256<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector256<float> cb = Avx.Add(Unsafe.Add(ref cbBase, i), chromaOffset); |
|||
Vector256<float> cr = Avx.Add(Unsafe.Add(ref crBase, i), chromaOffset); |
|||
|
|||
y = Avx2.PermuteVar8x32(y, vcontrol); |
|||
cb = Avx2.PermuteVar8x32(cb, vcontrol); |
|||
cr = Avx2.PermuteVar8x32(cr, vcontrol); |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
// Adding & multiplying 8 elements at one time:
|
|||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|||
Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|||
|
|||
// TODO: We should be saving to RGBA not Vector4
|
|||
r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale); |
|||
g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale); |
|||
b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale); |
|||
|
|||
Vector256<float> vte = Avx.UnpackLow(r, b); |
|||
Vector256<float> vto = Avx.UnpackLow(g, va); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.UnpackLow(vte, vto); |
|||
Unsafe.Add(ref destination, 1) = Avx.UnpackHigh(vte, vto); |
|||
|
|||
vte = Avx.UnpackHigh(r, b); |
|||
vto = Avx.UnpackHigh(g, va); |
|||
|
|||
Unsafe.Add(ref destination, 2) = Avx.UnpackLow(vte, vto); |
|||
Unsafe.Add(ref destination, 3) = Avx.UnpackHigh(vte, vto); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromYCbCrBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); |
|||
} |
|||
} |
|||
} |
|||
@ -1,182 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
using SixLabors.ImageSharp.Tuples; |
|||
|
|||
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromYCbCrSimdVector8 : JpegColorConverter |
|||
{ |
|||
public FromYCbCrSimdVector8(int precision) |
|||
: base(JpegColorSpace.YCbCr, precision) |
|||
{ |
|||
} |
|||
|
|||
public static bool IsAvailable => Vector.IsHardwareAccelerated && SimdUtils.HasVector8; |
|||
|
|||
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
int remainder = result.Length % 8; |
|||
int simdCount = result.Length - remainder; |
|||
if (simdCount > 0) |
|||
{ |
|||
ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount), this.MaximumValue, this.HalfValue); |
|||
} |
|||
|
|||
FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder), this.MaximumValue, this.HalfValue); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SIMD convert using buffers of sizes divisible by 8.
|
|||
/// </summary>
|
|||
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result, float maxValue, float halfValue) |
|||
{ |
|||
// This implementation is actually AVX specific.
|
|||
// An AVX register is capable of storing 8 float-s.
|
|||
if (!IsAvailable) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"JpegColorConverter.FromYCbCrSimd256 can be used only on architecture having 256 byte floating point SIMD registers!"); |
|||
} |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> yBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> cbBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> crBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var chromaOffset = Vector256.Create(-halfValue); |
|||
var scale = Vector256.Create(1 / maxValue); |
|||
var rCrMult = Vector256.Create(1.402F); |
|||
var gCbMult = Vector256.Create(-0.344136F); |
|||
var gCrMult = Vector256.Create(-0.714136F); |
|||
var bCbMult = Vector256.Create(1.772F); |
|||
|
|||
// Used for packing.
|
|||
var va = Vector256.Create(1F); |
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
Vector256<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector256<float> cb = Avx.Add(Unsafe.Add(ref cbBase, i), chromaOffset); |
|||
Vector256<float> cr = Avx.Add(Unsafe.Add(ref crBase, i), chromaOffset); |
|||
|
|||
y = Avx2.PermuteVar8x32(y, vcontrol); |
|||
cb = Avx2.PermuteVar8x32(cb, vcontrol); |
|||
cr = Avx2.PermuteVar8x32(cr, vcontrol); |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
// Adding & multiplying 8 elements at one time:
|
|||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|||
Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|||
|
|||
// TODO: We should be savving to RGBA not Vector4
|
|||
r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale); |
|||
g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale); |
|||
b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale); |
|||
|
|||
Vector256<float> vte = Avx.UnpackLow(r, b); |
|||
Vector256<float> vto = Avx.UnpackLow(g, va); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.UnpackLow(vte, vto); |
|||
Unsafe.Add(ref destination, 1) = Avx.UnpackHigh(vte, vto); |
|||
|
|||
vte = Avx.UnpackHigh(r, b); |
|||
vto = Avx.UnpackHigh(g, va); |
|||
|
|||
Unsafe.Add(ref destination, 2) = Avx.UnpackLow(vte, vto); |
|||
Unsafe.Add(ref destination, 3) = Avx.UnpackHigh(vte, vto); |
|||
} |
|||
#else
|
|||
ref Vector<float> yBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> cbBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> crBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector4Octet resultBase = |
|||
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
var chromaOffset = new Vector<float>(-halfValue); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
|
|||
Vector4Pair rr = default; |
|||
Vector4Pair gg = default; |
|||
Vector4Pair bb = default; |
|||
|
|||
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr); |
|||
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg); |
|||
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb); |
|||
|
|||
var scale = new Vector<float>(1 / maxValue); |
|||
|
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
Vector<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector<float> cb = Unsafe.Add(ref cbBase, i) + chromaOffset; |
|||
Vector<float> cr = Unsafe.Add(ref crBase, i) + chromaOffset; |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
// Adding & multiplying 8 elements at one time:
|
|||
Vector<float> r = y + (cr * new Vector<float>(1.402F)); |
|||
Vector<float> g = y - (cb * new Vector<float>(0.344136F)) - (cr * new Vector<float>(0.714136F)); |
|||
Vector<float> b = y + (cb * new Vector<float>(1.772F)); |
|||
|
|||
r = r.FastRound(); |
|||
g = g.FastRound(); |
|||
b = b.FastRound(); |
|||
r *= scale; |
|||
g *= scale; |
|||
b *= scale; |
|||
|
|||
rrRefAsVector = r; |
|||
ggRefAsVector = g; |
|||
bbRefAsVector = b; |
|||
|
|||
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
|
|||
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); |
|||
destination.Pack(ref rr, ref gg, ref bb); |
|||
} |
|||
#endif
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.Tuples; |
|||
|
|||
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromYCbCrVector8 : Vector8JpegColorConverter |
|||
{ |
|||
public FromYCbCrVector8(int precision) |
|||
: base(JpegColorSpace.YCbCr, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
ref Vector<float> yBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> cbBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> crBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector4Octet resultBase = |
|||
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
var chromaOffset = new Vector<float>(-this.HalfValue); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
|
|||
Vector4Pair rr = default; |
|||
Vector4Pair gg = default; |
|||
Vector4Pair bb = default; |
|||
|
|||
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr); |
|||
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg); |
|||
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb); |
|||
|
|||
var scale = new Vector<float>(1 / this.MaximumValue); |
|||
|
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
Vector<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector<float> cb = Unsafe.Add(ref cbBase, i) + chromaOffset; |
|||
Vector<float> cr = Unsafe.Add(ref crBase, i) + chromaOffset; |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
// Adding & multiplying 8 elements at one time:
|
|||
Vector<float> r = y + (cr * new Vector<float>(1.402F)); |
|||
Vector<float> g = y - (cb * new Vector<float>(0.344136F)) - (cr * new Vector<float>(0.714136F)); |
|||
Vector<float> b = y + (cb * new Vector<float>(1.772F)); |
|||
|
|||
r = r.FastRound(); |
|||
g = g.FastRound(); |
|||
b = b.FastRound(); |
|||
r *= scale; |
|||
g *= scale; |
|||
b *= scale; |
|||
|
|||
rrRefAsVector = r; |
|||
ggRefAsVector = g; |
|||
bbRefAsVector = b; |
|||
|
|||
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
|
|||
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); |
|||
destination.Pack(ref rr, ref gg, ref bb); |
|||
} |
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromYCbCrBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromYccKAvx2 : Avx2JpegColorConverter |
|||
{ |
|||
public FromYccKAvx2(int precision) |
|||
: base(JpegColorSpace.Ycck, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> yBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> cbBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> crBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
ref Vector256<float> kBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var chromaOffset = Vector256.Create(-this.HalfValue); |
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var max = Vector256.Create(this.MaximumValue); |
|||
var rCrMult = Vector256.Create(1.402F); |
|||
var gCbMult = Vector256.Create(-0.344136F); |
|||
var gCrMult = Vector256.Create(-0.714136F); |
|||
var bCbMult = Vector256.Create(1.772F); |
|||
|
|||
// Used for packing.
|
|||
var va = Vector256.Create(1F); |
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
// k = kVals[i] / 256F;
|
|||
Vector256<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector256<float> cb = Avx.Add(Unsafe.Add(ref cbBase, i), chromaOffset); |
|||
Vector256<float> cr = Avx.Add(Unsafe.Add(ref crBase, i), chromaOffset); |
|||
Vector256<float> k = Avx.Divide(Unsafe.Add(ref kBase, i), max); |
|||
|
|||
y = Avx2.PermuteVar8x32(y, vcontrol); |
|||
cb = Avx2.PermuteVar8x32(cb, vcontrol); |
|||
cr = Avx2.PermuteVar8x32(cr, vcontrol); |
|||
k = Avx2.PermuteVar8x32(k, vcontrol); |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
// Adding & multiplying 8 elements at one time:
|
|||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|||
Vector256<float> g = |
|||
HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|||
|
|||
r = Avx.Subtract(max, Avx.RoundToNearestInteger(r)); |
|||
g = Avx.Subtract(max, Avx.RoundToNearestInteger(g)); |
|||
b = Avx.Subtract(max, Avx.RoundToNearestInteger(b)); |
|||
|
|||
r = Avx.Multiply(Avx.Multiply(r, k), scale); |
|||
g = Avx.Multiply(Avx.Multiply(g, k), scale); |
|||
b = Avx.Multiply(Avx.Multiply(b, k), scale); |
|||
|
|||
Vector256<float> vte = Avx.UnpackLow(r, b); |
|||
Vector256<float> vto = Avx.UnpackLow(g, va); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.UnpackLow(vte, vto); |
|||
Unsafe.Add(ref destination, 1) = Avx.UnpackHigh(vte, vto); |
|||
|
|||
vte = Avx.UnpackHigh(r, b); |
|||
vto = Avx.UnpackHigh(g, va); |
|||
|
|||
Unsafe.Add(ref destination, 2) = Avx.UnpackLow(vte, vto); |
|||
Unsafe.Add(ref destination, 3) = Avx.UnpackHigh(vte, vto); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromYccKBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.Tuples; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromYccKVector8 : Vector8JpegColorConverter |
|||
{ |
|||
public FromYccKVector8(int precision) |
|||
: base(JpegColorSpace.Ycck, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
ref Vector<float> yBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> cbBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> crBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
ref Vector<float> kBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
ref Vector4Octet resultBase = |
|||
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
var chromaOffset = new Vector<float>(-this.HalfValue); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
|
|||
Vector4Pair rr = default; |
|||
Vector4Pair gg = default; |
|||
Vector4Pair bb = default; |
|||
|
|||
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr); |
|||
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg); |
|||
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb); |
|||
|
|||
var scale = new Vector<float>(1 / this.MaximumValue); |
|||
var max = new Vector<float>(this.MaximumValue); |
|||
|
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
// k = kVals[i] / 256F;
|
|||
Vector<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector<float> cb = Unsafe.Add(ref cbBase, i) + chromaOffset; |
|||
Vector<float> cr = Unsafe.Add(ref crBase, i) + chromaOffset; |
|||
Vector<float> k = Unsafe.Add(ref kBase, i) / max; |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
// Adding & multiplying 8 elements at one time:
|
|||
Vector<float> r = y + (cr * new Vector<float>(1.402F)); |
|||
Vector<float> g = y - (cb * new Vector<float>(0.344136F)) - (cr * new Vector<float>(0.714136F)); |
|||
Vector<float> b = y + (cb * new Vector<float>(1.772F)); |
|||
|
|||
r = (max - r.FastRound()) * k; |
|||
g = (max - g.FastRound()) * k; |
|||
b = (max - b.FastRound()) * k; |
|||
r *= scale; |
|||
g *= scale; |
|||
b *= scale; |
|||
|
|||
rrRefAsVector = r; |
|||
ggRefAsVector = g; |
|||
bbRefAsVector = b; |
|||
|
|||
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
|
|||
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); |
|||
destination.Pack(ref rr, ref gg, ref bb); |
|||
} |
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromYccKBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal abstract class Vector8JpegColorConverter : VectorizedJpegColorConverter |
|||
{ |
|||
protected Vector8JpegColorConverter(JpegColorSpace colorSpace, int precision) |
|||
: base(colorSpace, precision, 8) |
|||
{ |
|||
} |
|||
|
|||
protected sealed override bool IsAvailable => SimdUtils.HasVector8; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal abstract class VectorizedJpegColorConverter : JpegColorConverter |
|||
{ |
|||
private readonly int vectorSize; |
|||
|
|||
protected VectorizedJpegColorConverter(JpegColorSpace colorSpace, int precision, int vectorSize) |
|||
: base(colorSpace, precision) |
|||
{ |
|||
this.vectorSize = vectorSize; |
|||
} |
|||
|
|||
public sealed override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
int remainder = result.Length % this.vectorSize; |
|||
int simdCount = result.Length - remainder; |
|||
if (simdCount > 0) |
|||
{ |
|||
// This implementation is actually AVX specific.
|
|||
// An AVX register is capable of storing 8 float-s.
|
|||
if (!this.IsAvailable) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"This converter can be used only on architecture having 256 byte floating point SIMD registers!"); |
|||
} |
|||
|
|||
this.ConvertCoreVectorized(values.Slice(0, simdCount), result.Slice(0, simdCount)); |
|||
} |
|||
|
|||
this.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); |
|||
} |
|||
|
|||
protected abstract void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result); |
|||
|
|||
protected abstract void ConvertCore(in ComponentValues values, Span<Vector4> result); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats |
|||
{ |
|||
/// <summary>
|
|||
/// Provides enumeration of the alpha value transparency behavior of a pixel format.
|
|||
/// </summary>
|
|||
public enum PixelAlphaRepresentation |
|||
{ |
|||
/// <summary>
|
|||
/// Indicates that the pixel format does not contain an alpha channel.
|
|||
/// </summary>
|
|||
None, |
|||
|
|||
/// <summary>
|
|||
/// Indicates that the transparency behavior is premultiplied.
|
|||
/// Each color is first scaled by the alpha value. The alpha value itself is the same
|
|||
/// in both straight and premultiplied alpha. Typically, no color channel value is
|
|||
/// greater than the alpha channel value.
|
|||
/// If a color channel value in a premultiplied format is greater than the alpha
|
|||
/// channel, the standard source-over blending math results in an additive blend.
|
|||
/// </summary>
|
|||
Associated, |
|||
|
|||
/// <summary>
|
|||
/// Indicates that the transparency behavior is not premultiplied.
|
|||
/// The alpha channel indicates the transparency of the color.
|
|||
/// </summary>
|
|||
Unassociated |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,26 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats |
|||
{ |
|||
/// <content>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </content>
|
|||
public partial struct A8 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </summary>
|
|||
internal class PixelOperations : PixelOperations<A8> |
|||
{ |
|||
private static readonly Lazy<PixelTypeInfo> LazyInfo = |
|||
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<A8>(PixelAlphaRepresentation.Unassociated), true); |
|||
|
|||
/// <inheritdoc />
|
|||
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats |
|||
{ |
|||
/// <content>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </content>
|
|||
public partial struct Argb32 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </summary>
|
|||
internal partial class PixelOperations : PixelOperations<Argb32> |
|||
{ |
|||
private static readonly Lazy<PixelTypeInfo> LazyInfo = |
|||
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Argb32>(PixelAlphaRepresentation.Unassociated), true); |
|||
|
|||
/// <inheritdoc />
|
|||
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats |
|||
{ |
|||
/// <content>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </content>
|
|||
public partial struct Bgr24 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </summary>
|
|||
internal partial class PixelOperations : PixelOperations<Bgr24> |
|||
{ |
|||
private static readonly Lazy<PixelTypeInfo> LazyInfo = |
|||
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Bgr24>(PixelAlphaRepresentation.None), true); |
|||
|
|||
/// <inheritdoc />
|
|||
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats |
|||
{ |
|||
/// <content>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </content>
|
|||
public partial struct Bgr565 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </summary>
|
|||
internal class PixelOperations : PixelOperations<Bgr565> |
|||
{ |
|||
private static readonly Lazy<PixelTypeInfo> LazyInfo = |
|||
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Bgr565>(PixelAlphaRepresentation.None), true); |
|||
|
|||
/// <inheritdoc />
|
|||
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats |
|||
{ |
|||
/// <content>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </content>
|
|||
public partial struct Bgra32 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </summary>
|
|||
internal partial class PixelOperations : PixelOperations<Bgra32> |
|||
{ |
|||
private static readonly Lazy<PixelTypeInfo> LazyInfo = |
|||
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Bgra32>(PixelAlphaRepresentation.Unassociated), true); |
|||
|
|||
/// <inheritdoc />
|
|||
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats |
|||
{ |
|||
/// <content>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </content>
|
|||
public partial struct Bgra4444 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </summary>
|
|||
internal class PixelOperations : PixelOperations<Bgra4444> |
|||
{ |
|||
private static readonly Lazy<PixelTypeInfo> LazyInfo = |
|||
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Bgra4444>(PixelAlphaRepresentation.Unassociated), true); |
|||
|
|||
/// <inheritdoc />
|
|||
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats |
|||
{ |
|||
/// <content>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </content>
|
|||
public partial struct Bgra5551 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </summary>
|
|||
internal partial class PixelOperations : PixelOperations<Bgra5551> |
|||
{ |
|||
private static readonly Lazy<PixelTypeInfo> LazyInfo = |
|||
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Bgra5551>(PixelAlphaRepresentation.Unassociated), true); |
|||
|
|||
/// <inheritdoc />
|
|||
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats; |
|||
|
|||
namespace SixLabors.ImageSharp.PixelFormats |
|||
{ |
|||
/// <content>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </content>
|
|||
public partial struct Byte4 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized overrides for bulk operations.
|
|||
/// </summary>
|
|||
internal class PixelOperations : PixelOperations<Byte4> |
|||
{ |
|||
private static readonly Lazy<PixelTypeInfo> LazyInfo = |
|||
new Lazy<PixelTypeInfo>(() => PixelTypeInfo.Create<Byte4>(PixelAlphaRepresentation.Unassociated), true); |
|||
|
|||
/// <inheritdoc />
|
|||
public override PixelTypeInfo GetPixelTypeInfo() => LazyInfo.Value; |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue