Browse Source

Fix ColorNumerics and FromVector4

pull/3096/head
James Jackson-South 3 months ago
parent
commit
0390442fee
  1. 42
      src/ImageSharp/Common/Helpers/ColorNumerics.cs
  2. 14
      src/ImageSharp/PixelFormats/PixelImplementations/Rgb96.cs
  3. 15
      src/ImageSharp/PixelFormats/PixelImplementations/Rgba128.cs

42
src/ImageSharp/Common/Helpers/ColorNumerics.cs

@ -97,7 +97,7 @@ internal static class ColorNumerics
/// 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>
/// <param name="component">The 16 bit component value.</param>
/// <returns>The <see cref="byte"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte From16BitTo8Bit(ushort component) =>
@ -133,13 +133,47 @@ internal static class ColorNumerics
(byte)(((component * 255) + 32895) >> 16);
/// <summary>
/// Scales a value from an 32 bit <see cref="uint"/> to
/// an 8 bit <see cref="byte"/> equivalent.
/// Scales a value from a 32 bit <see cref="uint"/> to an
/// 8 bit <see cref="byte"/> equivalent.
/// </summary>
/// <param name="component">The 32 bit component value.</param>
/// <returns>The <see cref="byte"/> value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte From32BitTo8Bit(uint component) => (byte)(component >> 24);
public static byte From32BitTo8Bit(uint component) =>
// To scale to 8 bits from a 32-bit value V the required value is:
//
// (V * 255) / 4294967295
//
// Since:
//
// 4294967295 = 255 * 16843009
//
// this reduces exactly to:
//
// V / 16843009
//
// To round to nearest using integer arithmetic we add half the divisor
// before dividing:
//
// (V + 16843009 / 2) / 16843009
//
// where:
//
// 16843009 / 2 = 8421504.5
//
// Using 8421504 ensures correct round-to-nearest behaviour:
//
// 8421504 -> 0
// 8421505 -> 1
//
// The addition must be performed in 64-bit to avoid overflow for large
// input values (for example uint.MaxValue).
//
// Final exact integer implementation:
//
// ((ulong)V + 8421504) / 16843009
(byte)((component + 8421504UL) / 16843009UL);
/// <summary>
/// Scales a value from an 8 bit <see cref="byte"/> to

14
src/ImageSharp/PixelFormats/PixelImplementations/Rgb96.cs

@ -19,7 +19,12 @@ public partial struct Rgb96 : IPixel<Rgb96>, IEquatable<Rgb96>
{
private const float InvMax = 1.0f / uint.MaxValue;
private const float Max = uint.MaxValue;
// Use double here because at this magnitude a float cannot represent all 32-bit
// integer values exactly. A float only has 24 bits of precision, so around
// uint.MaxValue it can only represent multiples of 256 and will round
// 4294967295 up to 4294967296. Double has 53 bits of precision and can
// represent all uint values exactly, avoiding precision loss before scaling.
private const double Max = uint.MaxValue;
/// <summary>
/// Gets the red component.
@ -96,8 +101,11 @@ public partial struct Rgb96 : IPixel<Rgb96>, IEquatable<Rgb96>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rgb96 FromVector4(Vector4 source)
{
source = Numerics.Clamp(source, Vector4.Zero, Vector4.One) * Max;
return new Rgb96((uint)MathF.Round(source.X), (uint)MathF.Round(source.Y), (uint)MathF.Round(source.Z));
source = Numerics.Clamp(source, Vector4.Zero, Vector4.One);
return new Rgb96(
(uint)Math.Round(source.X * Max),
(uint)Math.Round(source.Y * Max),
(uint)Math.Round(source.Z * Max));
}
/// <inheritdoc/>

15
src/ImageSharp/PixelFormats/PixelImplementations/Rgba128.cs

@ -19,7 +19,12 @@ public partial struct Rgba128 : IPixel<Rgba128>, IEquatable<Rgba128>
{
private const float InvMax = 1.0f / uint.MaxValue;
private const float Max = uint.MaxValue;
// Use double here because at this magnitude a float cannot represent all 32-bit
// integer values exactly. A float only has 24 bits of precision, so around
// uint.MaxValue it can only represent multiples of 256 and will round
// 4294967295 up to 4294967296. Double has 53 bits of precision and can
// represent all uint values exactly, avoiding precision loss before scaling.
private const double Max = uint.MaxValue;
/// <summary>
/// Gets the red component.
@ -96,8 +101,12 @@ public partial struct Rgba128 : IPixel<Rgba128>, IEquatable<Rgba128>
/// <inheritdoc/>
public static Rgba128 FromVector4(Vector4 source)
{
source = Numerics.Clamp(source, Vector4.Zero, Vector4.One) * Max;
return new Rgba128((uint)MathF.Round(source.X), (uint)MathF.Round(source.Y), (uint)MathF.Round(source.Z), (uint)MathF.Round(source.W));
source = Numerics.Clamp(source, Vector4.Zero, Vector4.One);
return new Rgba128(
(uint)Math.Round(source.X * Max),
(uint)Math.Round(source.Y * Max),
(uint)Math.Round(source.Z * Max),
(uint)Math.Round(source.W * Max));
}
/// <inheritdoc/>

Loading…
Cancel
Save