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