diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index c15e0a732..f9ade4cfe 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs
@@ -13,6 +13,73 @@ namespace SixLabors.ImageSharp
///
internal static class ImageMaths
{
+ ///
+ /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static byte GetBT709LuminanceBytes(byte r, byte g, byte b) => (byte)((r * .2126F) + (g * .7152F) + (b * .0722F));
+
+ ///
+ /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static ushort GetBT709Luminance(ushort r, ushort g, ushort b) => (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F));
+
+ ///
+ /// Scales a value from a 16 bit to it's 8 bit equivalent.
+ ///
+ /// The 8 bit compoonent value.
+ /// The
+ [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);
+ }
+
+ ///
+ /// Scales a value from an 8 bit to it's 16 bit equivalent.
+ ///
+ /// The 8 bit compoonent value.
+ /// The
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257);
+
///
/// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation.
///
diff --git a/src/ImageSharp/PixelFormats/Alpha8.cs b/src/ImageSharp/PixelFormats/Alpha8.cs
index d795931af..177ce81ef 100644
--- a/src/ImageSharp/PixelFormats/Alpha8.cs
+++ b/src/ImageSharp/PixelFormats/Alpha8.cs
@@ -19,10 +19,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// Initializes a new instance of the struct.
///
/// The alpha component
- public Alpha8(float alpha)
- {
- this.PackedValue = Pack(alpha);
- }
+ public Alpha8(float alpha) => this.PackedValue = Pack(alpha);
///
public byte PackedValue { get; set; }
@@ -40,10 +37,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// True if the parameter is equal to the parameter; otherwise, false.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator ==(Alpha8 left, Alpha8 right)
- {
- return left.PackedValue == right.PackedValue;
- }
+ public static bool operator ==(Alpha8 left, Alpha8 right) => left.Equals(right);
///
/// Compares two objects for equality.
@@ -54,77 +48,48 @@ namespace SixLabors.ImageSharp.PixelFormats
/// True if the parameter is not equal to the parameter; otherwise, false.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator !=(Alpha8 left, Alpha8 right)
- {
- return left.PackedValue != right.PackedValue;
- }
+ public static bool operator !=(Alpha8 left, Alpha8 right) => !left.Equals(right);
///
public PixelOperations CreatePixelOperations() => new PixelOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromScaledVector4(Vector4 vector)
- {
- this.PackFromVector4(vector);
- }
+ public void PackFromScaledVector4(Vector4 vector) => this.PackFromVector4(vector);
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector4 ToScaledVector4()
- {
- return this.ToVector4();
- }
+ public Vector4 ToScaledVector4() => this.ToVector4();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromVector4(Vector4 vector)
- {
- this.PackedValue = Pack(vector.W);
- }
+ public void PackFromVector4(Vector4 vector) => this.PackedValue = Pack(vector.W);
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector4 ToVector4()
- {
- return new Vector4(0, 0, 0, this.PackedValue / 255F);
- }
+ public Vector4 ToVector4() => new Vector4(0, 0, 0, this.PackedValue / 255F);
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromRgba32(Rgba32 source)
- {
- this.PackedValue = source.A;
- }
+ public void PackFromRgba32(Rgba32 source) => this.PackedValue = source.A;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromArgb32(Argb32 source)
- {
- this.PackedValue = source.A;
- }
+ public void PackFromArgb32(Argb32 source) => this.PackedValue = source.A;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromBgra32(Bgra32 source)
- {
- this.PackedValue = source.A;
- }
+ public void PackFromBgra32(Bgra32 source) => this.PackedValue = source.A;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void ToRgb24(ref Rgb24 dest)
- {
- dest = default(Rgb24);
- }
+ public void ToRgb24(ref Rgb24 dest) => dest = default;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
- dest.R = 0;
- dest.G = 0;
- dest.B = 0;
+ dest.Rgb = default;
dest.A = this.PackedValue;
}
@@ -140,10 +105,7 @@ namespace SixLabors.ImageSharp.PixelFormats
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void ToBgr24(ref Bgr24 dest)
- {
- dest = default(Bgr24);
- }
+ public void ToBgr24(ref Bgr24 dest) => dest = default;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -161,28 +123,23 @@ namespace SixLabors.ImageSharp.PixelFormats
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void ToRgb48(ref Rgb48 dest)
- {
- dest.R = 0;
- dest.G = 0;
- dest.B = 0;
- }
+ public void ToRgb48(ref Rgb48 dest) => dest = default;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromGray8(Gray8 source) => this.PackedValue = 255;
+ public void PackFromGray8(Gray8 source) => this.PackedValue = byte.MaxValue;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void ToGray8(ref Gray8 dest) => dest.PackedValue = 0;
+ public void ToGray8(ref Gray8 dest) => dest = default;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromGray16(Gray16 source) => this.PackedValue = 255;
+ public void PackFromGray16(Gray16 source) => this.PackedValue = byte.MaxValue;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void ToGray16(ref Gray16 dest) => dest.PackedValue = 0;
+ public void ToGray16(ref Gray16 dest) => dest = default;
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -190,17 +147,18 @@ namespace SixLabors.ImageSharp.PixelFormats
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
+ public void ToRgba64(ref Rgba64 dest)
+ {
+ dest.Rgb = default;
+ dest.A = ImageMaths.UpscaleFrom8BitTo16Bit(this.PackedValue);
+ }
///
/// Compares an object with the packed vector.
///
/// The object to compare.
/// True if the object is equal to the packed vector.
- public override bool Equals(object obj)
- {
- return obj is Alpha8 other && this.Equals(other);
- }
+ public override bool Equals(object obj) => obj is Alpha8 other && this.Equals(other);
///
/// Compares another Alpha8 packed vector with the packed vector.
@@ -208,19 +166,13 @@ namespace SixLabors.ImageSharp.PixelFormats
/// The Alpha8 packed vector to compare.
/// True if the packed vectors are equal.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool Equals(Alpha8 other)
- {
- return this.PackedValue == other.PackedValue;
- }
+ public bool Equals(Alpha8 other) => this.PackedValue.Equals(other.PackedValue);
///
/// Gets a string representation of the packed vector.
///
/// A string representation of the packed vector.
- public override string ToString()
- {
- return (this.PackedValue / 255F).ToString();
- }
+ public override string ToString() => (this.PackedValue / 255F).ToString();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -232,9 +184,6 @@ namespace SixLabors.ImageSharp.PixelFormats
/// The float containing the value to pack.
/// The containing the packed values.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static byte Pack(float alpha)
- {
- return (byte)Math.Round(alpha.Clamp(0, 1) * 255F);
- }
+ private static byte Pack(float alpha) => (byte)Math.Round(alpha.Clamp(0, 1) * 255F);
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/PixelFormats/Gray8.cs b/src/ImageSharp/PixelFormats/Gray8.cs
index 916e199d0..0a41a6ecc 100644
--- a/src/ImageSharp/PixelFormats/Gray8.cs
+++ b/src/ImageSharp/PixelFormats/Gray8.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -30,14 +29,21 @@ namespace SixLabors.ImageSharp.PixelFormats
///
private const float Bx = .0722F;
+ ///
+ /// The maximum byte value.
+ ///
+ private static readonly Vector4 MaxBytes = new Vector4(255F);
+
+ ///
+ /// The half vector value.
+ ///
+ private static readonly Vector4 Half = new Vector4(0.5F);
+
///
/// Initializes a new instance of the struct.
///
/// The gray component
- public Gray8(byte gray)
- {
- this.PackedValue = gray;
- }
+ public Gray8(byte gray) => this.PackedValue = gray;
///
public byte PackedValue { get; set; }
@@ -45,20 +51,13 @@ namespace SixLabors.ImageSharp.PixelFormats
///
/// Compares two objects for equality.
///
- ///
- /// The on the left side of the operand.
- ///
- ///
- /// The on the right side of the operand.
- ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
///
/// True if the parameter is equal to the parameter; otherwise, false.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator ==(Gray8 left, Gray8 right)
- {
- return left.PackedValue == right.PackedValue;
- }
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static bool operator ==(Gray8 left, Gray8 right) => left.PackedValue.Equals(right.PackedValue);
///
/// Compares two objects for equality.
@@ -68,67 +67,54 @@ namespace SixLabors.ImageSharp.PixelFormats
///
/// True if the parameter is not equal to the parameter; otherwise, false.
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator !=(Gray8 left, Gray8 right)
- {
- return left.PackedValue != right.PackedValue;
- }
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static bool operator !=(Gray8 left, Gray8 right) => !left.PackedValue.Equals(right.PackedValue);
///
public PixelOperations CreatePixelOperations() => new PixelOperations();
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromScaledVector4(Vector4 vector)
- {
- this.PackFromVector4(vector);
- }
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void PackFromScaledVector4(Vector4 vector) => this.PackFromVector4(vector);
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Vector4 ToScaledVector4()
- {
- var scaledGray = this.PackedValue / 255f;
- return new Vector4(scaledGray, scaledGray, scaledGray, 1.0f);
- }
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public Vector4 ToScaledVector4() => this.ToVector4();
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void PackFromVector4(Vector4 vector)
{
- this.PackedValue = Pack(vector.X, vector.Y, vector.Z);
+ vector *= MaxBytes;
+ vector += Half;
+ vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
+ float luminance = (vector.X * Rx) + (vector.Y * Gx) + (vector.Z * Bx);
+
+ this.PackedValue = (byte)luminance;
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public Vector4 ToVector4()
{
- return new Vector4(this.PackedValue, this.PackedValue, this.PackedValue, 1.0f);
+ float rgb = this.PackedValue / 255F;
+ return new Vector4(rgb, rgb, rgb, 1F);
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromRgba32(Rgba32 source)
- {
- this.PackedValue = Pack(source.R, source.G, source.B);
- }
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void PackFromRgba32(Rgba32 source) => this.PackedValue = ImageMaths.GetBT709LuminanceBytes(source.R, source.G, source.B);
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromArgb32(Argb32 source)
- {
- this.PackedValue = Pack(source.R, source.G, source.B);
- }
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void PackFromArgb32(Argb32 source) => this.PackedValue = ImageMaths.GetBT709LuminanceBytes(source.R, source.G, source.B);
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromBgra32(Bgra32 source)
- {
- this.PackedValue = Pack(source.R, source.G, source.B);
- }
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void PackFromBgra32(Bgra32 source) => this.PackedValue = ImageMaths.GetBT709LuminanceBytes(source.R, source.G, source.B);
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void ToRgb24(ref Rgb24 dest)
{
dest.R = this.PackedValue;
@@ -137,27 +123,27 @@ namespace SixLabors.ImageSharp.PixelFormats
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void ToRgba32(ref Rgba32 dest)
{
dest.R = this.PackedValue;
dest.G = this.PackedValue;
dest.B = this.PackedValue;
- dest.A = 255;
+ dest.A = byte.MaxValue;
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void ToArgb32(ref Argb32 dest)
{
dest.R = this.PackedValue;
dest.G = this.PackedValue;
dest.B = this.PackedValue;
- dest.A = 255;
+ dest.A = byte.MaxValue;
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void ToBgr24(ref Bgr24 dest)
{
dest.R = this.PackedValue;
@@ -166,101 +152,91 @@ namespace SixLabors.ImageSharp.PixelFormats
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void ToBgra32(ref Bgra32 dest)
{
dest.R = this.PackedValue;
dest.G = this.PackedValue;
dest.B = this.PackedValue;
- dest.A = 255;
+ dest.A = byte.MaxValue;
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromRgb48(Rgb48 source) =>
- this.PackedValue = Pack(source.R, source.G, source.B);
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void PackFromRgb48(Rgb48 source)
+ => this.PackedValue = ImageMaths.GetBT709LuminanceBytes(
+ ImageMaths.DownScaleFrom16BitTo8Bit(source.R),
+ ImageMaths.DownScaleFrom16BitTo8Bit(source.G),
+ ImageMaths.DownScaleFrom16BitTo8Bit(source.B));
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void ToRgb48(ref Rgb48 dest)
{
- ushort gray = (ushort)(this.PackedValue * 255);
- dest.R = gray;
- dest.G = gray;
- dest.B = gray;
+ ushort luminance = ImageMaths.UpscaleFrom8BitTo16Bit(this.PackedValue);
+ dest.R = luminance;
+ dest.G = luminance;
+ dest.B = luminance;
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromRgba64(Rgba64 source) =>
- this.PackFromScaledVector4(source.ToScaledVector4());
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void PackFromRgba64(Rgba64 source)
+ => this.PackedValue = ImageMaths.GetBT709LuminanceBytes(
+ ImageMaths.DownScaleFrom16BitTo8Bit(source.R),
+ ImageMaths.DownScaleFrom16BitTo8Bit(source.G),
+ ImageMaths.DownScaleFrom16BitTo8Bit(source.B));
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void PackFromGray8(Gray8 source) => this.PackedValue = source.PackedValue;
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void PackFromGray16(Gray16 source) => this.PackedValue = (byte)(source.PackedValue / 255);
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void PackFromGray16(Gray16 source) => this.PackedValue = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue);
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4());
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void ToRgba64(ref Rgba64 dest)
+ {
+ ushort luminance = ImageMaths.UpscaleFrom8BitTo16Bit(this.PackedValue);
+ dest.R = luminance;
+ dest.G = luminance;
+ dest.B = luminance;
+ dest.A = ushort.MaxValue;
+ }
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public void ToGray8(ref Gray8 dest) => dest.PackedValue = this.PackedValue;
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void ToGray16(ref Gray16 dest) => dest.PackedValue = (ushort)(this.PackedValue * 255);
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void ToGray16(ref Gray16 dest) => dest.PackedValue = ImageMaths.UpscaleFrom8BitTo16Bit(this.PackedValue);
///
/// Compares an object with the packed vector.
///
/// The object to compare.
/// True if the object is equal to the packed vector.
- public override bool Equals(object obj)
- {
- return obj is Gray8 other && this.Equals(other);
- }
+ public override bool Equals(object obj) => obj is Gray8 other && this.Equals(other);
///
/// Compares another packed vector with the packed vector.
///
/// The Gray8 packed vector to compare.
/// True if the packed vectors are equal.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool Equals(Gray8 other)
- {
- return this.PackedValue == other.PackedValue;
- }
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public bool Equals(Gray8 other) => this.PackedValue.Equals(other.PackedValue);
///
/// Gets a string representation of the packed vector.
///
/// A string representation of the packed vector.
- public override string ToString()
- {
- return (this.PackedValue / 255F).ToString();
- }
+ public override string ToString() => $"Gray8({this.PackedValue}";
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode() => this.PackedValue.GetHashCode();
-
- ///
- /// Packs a into a byte.
- ///
- /// Red value of the color to pack.
- /// Green value of the color to pack.
- /// Blue value of the color to pack.
- /// The containing the packed value.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static byte Pack(float r, float g, float b)
- {
- float val = (r * Rx) + (g * Gx) + (b * Bx);
- return (byte)Math.Round(val * 255);
- }
}
}
\ No newline at end of file