mirror of https://github.com/SixLabors/ImageSharp
45 changed files with 763 additions and 770 deletions
@ -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,257 +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 used for image processing.
|
|||
/// </summary>
|
|||
internal static class ImageMath |
|||
{ |
|||
/// <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>
|
|||
/// 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>
|
|||
/// 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 = Numerics.Clamp(GetMaxY(bitmap) + 1, 0, height); |
|||
bottomRight.X = Numerics.Clamp(GetMaxX(bitmap) + 1, 0, width); |
|||
|
|||
return GetBoundingRectangle(topLeft, bottomRight); |
|||
} |
|||
} |
|||
} |
|||
@ -1,169 +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 Vector4Utils |
|||
{ |
|||
private const int BlendAlphaControl = 0b_10_00_10_00; |
|||
private const int ShuffleAlphaControl = 0b_11_11_11_11; |
|||
|
|||
/// <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 (Numerics.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 (Numerics.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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,56 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Numerics; |
|||
|
|||
using Xunit; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Helpers |
|||
{ |
|||
public class Vector4UtilsTests |
|||
{ |
|||
private readonly ApproximateFloatComparer approximateFloatComparer = new ApproximateFloatComparer(1e-6f); |
|||
|
|||
[Theory] |
|||
[InlineData(0)] |
|||
[InlineData(1)] |
|||
[InlineData(30)] |
|||
[InlineData(63)] |
|||
public void Premultiply_VectorSpan(int length) |
|||
{ |
|||
var rnd = new Random(42); |
|||
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); |
|||
Vector4[] expected = source.Select(v => |
|||
{ |
|||
Vector4Utils.Premultiply(ref v); |
|||
return v; |
|||
}).ToArray(); |
|||
|
|||
Vector4Utils.Premultiply(source); |
|||
|
|||
Assert.Equal(expected, source, this.approximateFloatComparer); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(0)] |
|||
[InlineData(1)] |
|||
[InlineData(30)] |
|||
[InlineData(63)] |
|||
public void UnPremultiply_VectorSpan(int length) |
|||
{ |
|||
var rnd = new Random(42); |
|||
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); |
|||
Vector4[] expected = source.Select(v => |
|||
{ |
|||
Vector4Utils.UnPremultiply(ref v); |
|||
return v; |
|||
}).ToArray(); |
|||
|
|||
Vector4Utils.UnPremultiply(source); |
|||
|
|||
Assert.Equal(expected, source, this.approximateFloatComparer); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue