mirror of https://github.com/SixLabors/ImageSharp
29 changed files with 539 additions and 469 deletions
@ -0,0 +1,89 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.ColorSpaces.Companding |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Implements sRGB companding
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// For more info see:
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
|
||||
|
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
|
||||
|
/// </remarks>
|
||||
|
public static class SRgbCompanding |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Expands the companded vectors to their linear equivalents with respect to the energy.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vectors">The span of vectors.</param>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static void Expand(Span<Vector4> vectors) |
||||
|
{ |
||||
|
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
||||
|
|
||||
|
for (int i = 0; i < vectors.Length; i++) |
||||
|
{ |
||||
|
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
||||
|
v.X = Expand(v.X); |
||||
|
v.Y = Expand(v.Y); |
||||
|
v.Z = Expand(v.Z); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compresses the uncompanded vectors to their nonlinear equivalents with respect to the energy.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vectors">The span of vectors.</param>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static void Compress(Span<Vector4> vectors) |
||||
|
{ |
||||
|
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
||||
|
|
||||
|
for (int i = 0; i < vectors.Length; i++) |
||||
|
{ |
||||
|
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
||||
|
v.X = Compress(v.X); |
||||
|
v.Y = Compress(v.Y); |
||||
|
v.Z = Compress(v.Z); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Expands a companded vector to its linear equivalent with respect to the energy.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector.</param>
|
||||
|
/// <returns>The <see cref="Vector4"/> representing the linear channel values.</returns>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static Vector4 Expand(Vector4 vector) => new Vector4(Expand(vector.X), Expand(vector.Y), Expand(vector.Z), vector.W); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compresses an uncompanded vector (linear) to its nonlinear equivalent.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector.</param>
|
||||
|
/// <returns>The <see cref="Vector4"/> representing the nonlinear channel values.</returns>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static Vector4 Compress(Vector4 vector) => new Vector4(Compress(vector.X), Compress(vector.Y), Compress(vector.Z), vector.W); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Expands a companded channel to its linear equivalent with respect to the energy.
|
||||
|
/// </summary>
|
||||
|
/// <param name="channel">The channel value.</param>
|
||||
|
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static float Expand(float channel) => channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
|
||||
|
/// </summary>
|
||||
|
/// <param name="channel">The channel value.</param>
|
||||
|
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static float Compress(float channel) => channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; |
||||
|
} |
||||
|
} |
||||
@ -1,35 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Implements sRGB companding
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// For more info see:
|
|
||||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html"/>
|
|
||||
/// <see href="http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_RGB.html"/>
|
|
||||
/// </remarks>
|
|
||||
public static class SRgbCompanding |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Expands a companded channel to its linear equivalent with respect to the energy.
|
|
||||
/// </summary>
|
|
||||
/// <param name="channel">The channel value</param>
|
|
||||
/// <returns>The <see cref="float"/> representing the linear channel value.</returns>
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public static float Expand(float channel) => channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
|
|
||||
/// </summary>
|
|
||||
/// <param name="channel">The channel value</param>
|
|
||||
/// <returns>The <see cref="float"/> representing the nonlinear channel value.</returns>
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public static float Compress(float channel) => channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; |
|
||||
} |
|
||||
} |
|
||||
@ -1,156 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Extension methods for the <see cref="Vector4"/> struct.
|
|
||||
/// </summary>
|
|
||||
internal static class Vector4Extensions |
|
||||
{ |
|
||||
/// <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>
|
|
||||
/// <returns>The <see cref="Vector4"/></returns>
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public static Vector4 Premultiply(this Vector4 source) |
|
||||
{ |
|
||||
float w = source.W; |
|
||||
Vector4 premultiplied = source * w; |
|
||||
premultiplied.W = w; |
|
||||
return premultiplied; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reverses the result of premultiplying a vector via <see cref="Premultiply(Vector4)"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
|
|
||||
/// <returns>The <see cref="Vector4"/></returns>
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public static Vector4 UnPremultiply(this Vector4 source) |
|
||||
{ |
|
||||
float w = source.W; |
|
||||
Vector4 unpremultiplied = source / w; |
|
||||
unpremultiplied.W = w; |
|
||||
return unpremultiplied; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Bulk variant of <see cref="Premultiply(System.Numerics.Vector4)"/>
|
|
||||
/// </summary>
|
|
||||
/// <param name="vectors">The span of vectors</param>
|
|
||||
public static void Premultiply(Span<Vector4> vectors) |
|
||||
{ |
|
||||
// TODO: This method can be AVX2 optimized using Vector<float>
|
|
||||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|
||||
|
|
||||
for (int i = 0; i < vectors.Length; i++) |
|
||||
{ |
|
||||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|
||||
var s = new Vector4(v.W) |
|
||||
{ |
|
||||
W = 1 |
|
||||
}; |
|
||||
v *= s; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Bulk variant of <see cref="UnPremultiply(System.Numerics.Vector4)"/>
|
|
||||
/// </summary>
|
|
||||
/// <param name="vectors">The span of vectors</param>
|
|
||||
public static void UnPremultiply(Span<Vector4> vectors) |
|
||||
{ |
|
||||
// TODO: This method can be AVX2 optimized using Vector<float>
|
|
||||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|
||||
|
|
||||
for (int i = 0; i < vectors.Length; i++) |
|
||||
{ |
|
||||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|
||||
var s = new Vector4(1 / v.W) |
|
||||
{ |
|
||||
W = 1 |
|
||||
}; |
|
||||
v *= s; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Compresses a linear color signal to its sRGB equivalent.
|
|
||||
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
|
|
||||
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
|
|
||||
/// </summary>
|
|
||||
/// <param name="linear">The <see cref="Vector4"/> whose signal to compress.</param>
|
|
||||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public static Vector4 Compress(this Vector4 linear) |
|
||||
{ |
|
||||
// TODO: Is there a faster way to do this?
|
|
||||
return new Vector4( |
|
||||
SRgbCompanding.Compress(linear.X), |
|
||||
SRgbCompanding.Compress(linear.Y), |
|
||||
SRgbCompanding.Compress(linear.Z), |
|
||||
linear.W); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Expands an sRGB color signal to its linear equivalent.
|
|
||||
/// <see href="http://www.4p8.com/eric.brasseur/gamma.html#formulas"/>
|
|
||||
/// <see href="http://entropymine.com/imageworsener/srgbformula/"/>
|
|
||||
/// </summary>
|
|
||||
/// <param name="gamma">The <see cref="Rgba32"/> whose signal to expand.</param>
|
|
||||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public static Vector4 Expand(this Vector4 gamma) |
|
||||
{ |
|
||||
// TODO: Is there a faster way to do this?
|
|
||||
return new Vector4( |
|
||||
SRgbCompanding.Expand(gamma.X), |
|
||||
SRgbCompanding.Expand(gamma.Y), |
|
||||
SRgbCompanding.Expand(gamma.Z), |
|
||||
gamma.W); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Bulk variant of <see cref="Compress(System.Numerics.Vector4)"/>
|
|
||||
/// </summary>
|
|
||||
/// <param name="vectors">The span of vectors</param>
|
|
||||
public static void Compress(Span<Vector4> vectors) |
|
||||
{ |
|
||||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|
||||
|
|
||||
for (int i = 0; i < vectors.Length; i++) |
|
||||
{ |
|
||||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|
||||
v.X = SRgbCompanding.Compress(v.X); |
|
||||
v.Y = SRgbCompanding.Compress(v.Y); |
|
||||
v.Z = SRgbCompanding.Compress(v.Z); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Bulk variant of <see cref="Expand(System.Numerics.Vector4)"/>
|
|
||||
/// </summary>
|
|
||||
/// <param name="vectors">The span of vectors</param>
|
|
||||
public static void Expand(Span<Vector4> vectors) |
|
||||
{ |
|
||||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|
||||
|
|
||||
for (int i = 0; i < vectors.Length; i++) |
|
||||
{ |
|
||||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|
||||
v.X = SRgbCompanding.Expand(v.X); |
|
||||
v.Y = SRgbCompanding.Expand(v.Y); |
|
||||
v.Z = SRgbCompanding.Expand(v.Z); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,124 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Primitives; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Extension methods for <see cref="DenseMatrix{T}"/>.
|
||||
|
/// </summary>
|
||||
|
internal static class DenseMatrixUtils |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the kernel weight values.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
|
/// <param name="matrix">The dense matrix.</param>
|
||||
|
/// <param name="sourcePixels">The source frame.</param>
|
||||
|
/// <param name="targetRow">The target row.</param>
|
||||
|
/// <param name="row">The current row.</param>
|
||||
|
/// <param name="column">The current column.</param>
|
||||
|
/// <param name="maxRow">The maximum working area row.</param>
|
||||
|
/// <param name="maxColumn">The maximum working area column.</param>
|
||||
|
/// <param name="offsetColumn">The column offset to apply to source sampling.</param>
|
||||
|
public static void Convolve<TPixel>( |
||||
|
in DenseMatrix<float> matrix, |
||||
|
Buffer2D<TPixel> sourcePixels, |
||||
|
Span<Vector4> targetRow, |
||||
|
int row, |
||||
|
int column, |
||||
|
int maxRow, |
||||
|
int maxColumn, |
||||
|
int offsetColumn) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
Vector4 vector = default; |
||||
|
int matrixHeight = matrix.Rows; |
||||
|
int matrixWidth = matrix.Columns; |
||||
|
int radiusY = matrixHeight >> 1; |
||||
|
int radiusX = matrixWidth >> 1; |
||||
|
int sourceOffsetColumnBase = column + offsetColumn; |
||||
|
|
||||
|
for (int y = 0; y < matrixHeight; y++) |
||||
|
{ |
||||
|
int offsetY = (row + y - radiusY).Clamp(0, maxRow); |
||||
|
Span<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY); |
||||
|
|
||||
|
for (int x = 0; x < matrixWidth; x++) |
||||
|
{ |
||||
|
int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(offsetColumn, maxColumn); |
||||
|
var currentColor = sourceRowSpan[offsetX].ToVector4(); |
||||
|
Vector4Utils.Premultiply(ref currentColor); |
||||
|
|
||||
|
vector += matrix[y, x] * currentColor; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ref Vector4 target = ref targetRow[column]; |
||||
|
vector.W = target.W; |
||||
|
Vector4Utils.UnPremultiply(ref vector); |
||||
|
target = vector; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the two kernel weight values.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
|
/// <param name="matrixY">The vertical dense matrix.</param>
|
||||
|
/// <param name="matrixX">The horizontal dense matrix.</param>
|
||||
|
/// <param name="sourcePixels">The source frame.</param>
|
||||
|
/// <param name="targetRow">The target row.</param>
|
||||
|
/// <param name="row">The current row.</param>
|
||||
|
/// <param name="column">The current column.</param>
|
||||
|
/// <param name="maxRow">The maximum working area row.</param>
|
||||
|
/// <param name="maxColumn">The maximum working area column.</param>
|
||||
|
/// <param name="offsetColumn">The column offset to apply to source sampling.</param>
|
||||
|
public static void Convolve2D<TPixel>( |
||||
|
in DenseMatrix<float> matrixY, |
||||
|
in DenseMatrix<float> matrixX, |
||||
|
Buffer2D<TPixel> sourcePixels, |
||||
|
Span<Vector4> targetRow, |
||||
|
int row, |
||||
|
int column, |
||||
|
int maxRow, |
||||
|
int maxColumn, |
||||
|
int offsetColumn) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
Vector4 vectorY = default; |
||||
|
Vector4 vectorX = default; |
||||
|
int matrixHeight = matrixY.Rows; |
||||
|
int matrixWidth = matrixY.Columns; |
||||
|
int radiusY = matrixHeight >> 1; |
||||
|
int radiusX = matrixWidth >> 1; |
||||
|
int sourceOffsetColumnBase = column + offsetColumn; |
||||
|
|
||||
|
for (int y = 0; y < matrixHeight; y++) |
||||
|
{ |
||||
|
int offsetY = (row + y - radiusY).Clamp(0, maxRow); |
||||
|
Span<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY); |
||||
|
|
||||
|
for (int x = 0; x < matrixWidth; x++) |
||||
|
{ |
||||
|
int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(offsetColumn, maxColumn); |
||||
|
var currentColor = sourceRowSpan[offsetX].ToVector4(); |
||||
|
Vector4Utils.Premultiply(ref currentColor); |
||||
|
|
||||
|
vectorX += matrixX[y, x] * currentColor; |
||||
|
vectorY += matrixY[y, x] * currentColor; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var vector = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY)); |
||||
|
ref Vector4 target = ref targetRow[column]; |
||||
|
vector.W = target.W; |
||||
|
Vector4Utils.UnPremultiply(ref vector); |
||||
|
target = vector; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// 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>
|
||||
|
/// Utility methods for the <see cref="Vector4"/> struct.
|
||||
|
/// </summary>
|
||||
|
internal static class Vector4Utils |
||||
|
{ |
||||
|
/// <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) |
||||
|
{ |
||||
|
// TODO: This method can be AVX2 optimized using Vector<float>
|
||||
|
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) |
||||
|
{ |
||||
|
// TODO: This method can be AVX2 optimized using Vector<float>
|
||||
|
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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization |
||||
|
{ |
||||
|
public class Premultiply |
||||
|
{ |
||||
|
[Benchmark(Baseline = true)] |
||||
|
public Vector4 PremultiplyByVal() |
||||
|
{ |
||||
|
var input = new Vector4(.5F); |
||||
|
return Vector4Utils.Premultiply(input); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public Vector4 PremultiplyByRef() |
||||
|
{ |
||||
|
var input = new Vector4(.5F); |
||||
|
Vector4Utils.PremultiplyRef(ref input); |
||||
|
return input; |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public Vector4 PremultiplyRefWithPropertyAssign() |
||||
|
{ |
||||
|
var input = new Vector4(.5F); |
||||
|
Vector4Utils.PremultiplyRefWithPropertyAssign(ref input); |
||||
|
return input; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal static class Vector4Utils |
||||
|
{ |
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static Vector4 Premultiply(Vector4 source) |
||||
|
{ |
||||
|
float w = source.W; |
||||
|
Vector4 premultiplied = source * w; |
||||
|
premultiplied.W = w; |
||||
|
return premultiplied; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static void PremultiplyRef(ref Vector4 source) |
||||
|
{ |
||||
|
float w = source.W; |
||||
|
source *= w; |
||||
|
source.W = w; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static void PremultiplyRefWithPropertyAssign(ref Vector4 source) |
||||
|
{ |
||||
|
float w = source.W; |
||||
|
source *= new Vector4(w) { W = 1 }; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,74 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// 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 Vector4ExtensionsTests |
|
||||
{ |
|
||||
[Theory] |
|
||||
[InlineData(0)] |
|
||||
[InlineData(1)] |
|
||||
[InlineData(30)] |
|
||||
public void Premultiply_VectorSpan(int length) |
|
||||
{ |
|
||||
var rnd = new Random(42); |
|
||||
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); |
|
||||
Vector4[] expected = source.Select(v => v.Premultiply()).ToArray(); |
|
||||
|
|
||||
Vector4Extensions.Premultiply(source); |
|
||||
|
|
||||
Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[InlineData(0)] |
|
||||
[InlineData(1)] |
|
||||
[InlineData(30)] |
|
||||
public void UnPremultiply_VectorSpan(int length) |
|
||||
{ |
|
||||
var rnd = new Random(42); |
|
||||
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); |
|
||||
Vector4[] expected = source.Select(v => v.UnPremultiply()).ToArray(); |
|
||||
|
|
||||
Vector4Extensions.UnPremultiply(source); |
|
||||
|
|
||||
Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[InlineData(0)] |
|
||||
[InlineData(1)] |
|
||||
[InlineData(30)] |
|
||||
public void Expand_VectorSpan(int length) |
|
||||
{ |
|
||||
var rnd = new Random(42); |
|
||||
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); |
|
||||
Vector4[] expected = source.Select(v => v.Expand()).ToArray(); |
|
||||
|
|
||||
Vector4Extensions.Expand(source); |
|
||||
|
|
||||
Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[InlineData(0)] |
|
||||
[InlineData(1)] |
|
||||
[InlineData(30)] |
|
||||
public void Compress_VectorSpan(int length) |
|
||||
{ |
|
||||
var rnd = new Random(42); |
|
||||
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); |
|
||||
Vector4[] expected = source.Select(v => v.Compress()).ToArray(); |
|
||||
|
|
||||
Vector4Extensions.Compress(source); |
|
||||
|
|
||||
Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,44 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// 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 |
||||
|
{ |
||||
|
[Theory] |
||||
|
[InlineData(0)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(30)] |
||||
|
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, new ApproximateFloatComparer(1e-6f)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(0)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(30)] |
||||
|
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, new ApproximateFloatComparer(1e-6f)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue