mirror of https://github.com/SixLabors/ImageSharp
23 changed files with 287 additions and 304 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,86 @@ |
|||||
|
// 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>
|
||||
|
/// <returns>The <see cref="Vector4"/></returns>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public static Vector4 Premultiply(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(Vector4 source) |
||||
|
{ |
||||
|
float w = source.W; |
||||
|
Vector4 unpremultiplied = source / w; |
||||
|
unpremultiplied.W = w; |
||||
|
return unpremultiplied; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Bulk variant of <see cref="Premultiply(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); |
||||
|
var s = new Vector4(v.W) |
||||
|
{ |
||||
|
W = 1 |
||||
|
}; |
||||
|
v *= s; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Bulk variant of <see cref="UnPremultiply(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); |
||||
|
var s = new Vector4(1 / v.W) |
||||
|
{ |
||||
|
W = 1 |
||||
|
}; |
||||
|
v *= s; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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(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(v)).ToArray(); |
||||
|
|
||||
|
Vector4Utils.UnPremultiply(source); |
||||
|
|
||||
|
Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue