diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 0ea1202132..234976b487 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -16,9 +16,7 @@ namespace ImageSharp /// /// Initializes a new instance of the struct. /// - /// - /// The alpha component - /// + /// The alpha component public Alpha8(float alpha) { this.PackedValue = Pack(alpha); diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index bf3febb50a..50aa67914c 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -11,7 +11,7 @@ namespace ImageSharp /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit. /// - public struct Bgra5551 : IPackedPixel, IEquatable, IPackedVector + public struct Bgra5551 : IPackedPixel, IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index 5ba35d037a..9b4be57110 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -3,7 +3,6 @@ // Licensed under the Apache License, Version 2.0. // - namespace ImageSharp { using System; @@ -12,7 +11,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. /// - public struct Byte4 : IPackedPixel, IEquatable, IPackedVector + public struct Byte4 : IPackedPixel, IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs new file mode 100644 index 0000000000..09811228a2 --- /dev/null +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -0,0 +1,160 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + + /// + /// Packed pixel type containing a single 16 bit floating point value. + /// + public struct HalfSingle : IPackedPixel, IEquatable + { + /// + /// The maximum byte value. + /// + private static readonly Vector4 MaxBytes = new Vector4(255); + + /// + /// The half vector value. + /// + private static readonly Vector4 Half = new Vector4(0.5F); + + /// + /// Initializes a new instance of the struct. + /// + /// The single component. + public HalfSingle(float single) + { + this.PackedValue = HalfTypeHelper.Pack(single); + } + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// + /// 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. + /// + public static bool operator ==(HalfSingle left, HalfSingle right) + { + return left.PackedValue == right.PackedValue; + } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + public static bool operator !=(HalfSingle left, HalfSingle right) + { + return left.PackedValue != right.PackedValue; + } + + /// + /// Expands the packed representation into a . + /// + /// The . + public float ToSingle() + { + return HalfTypeHelper.Unpack(this.PackedValue); + } + + /// + public void PackFromVector4(Vector4 vector) + { + this.PackedValue = HalfTypeHelper.Pack(vector.X); + } + + /// + public Vector4 ToVector4() + { + return new Vector4(this.ToSingle(), 0, 0, 1); + } + + /// + public void PackFromBytes(byte x, byte y, byte z, byte w) + { + this.PackFromVector4(new Vector4(x, y, z, w) / 255F); + } + + /// + public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder) + { + Vector4 vector = this.ToVector4(); + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + + switch (componentOrder) + { + case ComponentOrder.ZYX: + bytes[startIndex] = (byte)vector.Z; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.X; + break; + case ComponentOrder.ZYXW: + bytes[startIndex] = (byte)vector.Z; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.X; + bytes[startIndex + 3] = (byte)vector.W; + break; + case ComponentOrder.XYZ: + bytes[startIndex] = (byte)vector.X; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.Z; + break; + case ComponentOrder.XYZW: + bytes[startIndex] = (byte)vector.X; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.Z; + bytes[startIndex + 3] = (byte)vector.W; + break; + default: + throw new NotSupportedException(); + } + } + + /// + public override bool Equals(object obj) + { + return (obj is HalfSingle) && this.Equals((HalfSingle)obj); + } + + /// + public bool Equals(HalfSingle other) + { + return this.PackedValue == other.PackedValue; + } + + /// + public override string ToString() + { + return this.ToSingle().ToString(); + } + + /// + public override int GetHashCode() + { + return this.PackedValue.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs b/src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs new file mode 100644 index 0000000000..ae71294bc9 --- /dev/null +++ b/src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs @@ -0,0 +1,144 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Runtime.InteropServices; + + /// + /// Helper methods for packing and unpacking floating point values + /// + internal class HalfTypeHelper + { + /// + /// Packs a into an + /// + /// The float to pack + /// The + internal static ushort Pack(float value) + { + Uif uif = new Uif { F = value }; + return Pack(uif.I); + } + + /// + /// Packs an into a + /// + /// The integer to pack. + /// The + internal static ushort Pack(int value) + { + int s = (value >> 16) & 0x00008000; + int e = ((value >> 23) & 0x000000ff) - (127 - 15); + int m = value & 0x007fffff; + + if (e <= 0) + { + if (e < -10) + { + return (ushort)s; + } + + m = m | 0x00800000; + + int t = 14 - e; + int a = (1 << (t - 1)) - 1; + int b = (m >> t) & 1; + + m = (m + a + b) >> t; + + return (ushort)(s | m); + } + + if (e == 0xff - (127 - 15)) + { + if (m == 0) + { + return (ushort)(s | 0x7c00); + } + + m >>= 13; + return (ushort)(s | 0x7c00 | m | ((m == 0) ? 1 : 0)); + } + + m = m + 0x00000fff + ((m >> 13) & 1); + + if ((m & 0x00800000) != 0) + { + m = 0; + e += 1; + } + + if (e > 30) + { + return (ushort)(s | 0x7c00); + } + + return (ushort)(s | (e << 10) | (m >> 13)); + } + + /// + /// Unpacks a into a . + /// + /// The value. + /// The . + internal static float Unpack(ushort value) + { + uint result; + uint mantissa = (uint)(value & 1023); + uint exponent = 0xfffffff2; + + if ((value & -33792) == 0) + { + if (mantissa != 0) + { + while ((mantissa & 1024) == 0) + { + exponent--; + mantissa = mantissa << 1; + } + mantissa &= 0xfffffbff; + result = ((uint)((((uint)value & 0x8000) << 16) | ((exponent + 127) << 23))) | (mantissa << 13); + } + else + { + result = (uint)((value & 0x8000) << 16); + } + } + else + { + result = ((((uint)value & 0x8000) << 16) | ((((((uint)value >> 10) & 0x1f) - 15) + 127) << 23)) | (mantissa << 13); + } + + Uif uif = new Uif { U = result }; + return uif.F; + } + + /// + /// Maps the position of number types in memory + /// + [StructLayout(LayoutKind.Explicit)] + private struct Uif + { + /// + /// The float. + /// + [FieldOffset(0)] + public float F; + + /// + /// The integer. + /// + [FieldOffset(0)] + public int I; + + /// + /// The unsigned integer. + /// + [FieldOffset(0)] + public uint U; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/PackedVectorTests.cs b/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs similarity index 88% rename from tests/ImageSharp.Tests/Colors/PackedVectorTests.cs rename to tests/ImageSharp.Tests/Colors/PackedPixelTests.cs index 5ba7a3e9a7..a30fecc1c8 100644 --- a/tests/ImageSharp.Tests/Colors/PackedVectorTests.cs +++ b/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -11,9 +11,9 @@ namespace ImageSharp.Tests.Colors using Xunit; /// - /// The packed vector tests. + /// The packed pixel tests. /// - public class PackedVectorTests + public class PackedPixelTests { [Fact] public void Alpha8() @@ -251,6 +251,40 @@ namespace ImageSharp.Tests.Colors Assert.Equal(bgra, new byte[] { 0, 0, 128, 0 }); } + [Fact] + public void HalfSingle() + { + // Test limits + Assert.Equal(15360, new HalfSingle(1F).PackedValue); + Assert.Equal(0, new HalfSingle(0F).PackedValue); + Assert.Equal(48128, new HalfSingle(-1F).PackedValue); + + // Test values + Assert.Equal(11878, new HalfSingle(0.1F).PackedValue); + Assert.Equal(46285, new HalfSingle(-0.3F).PackedValue); + + // Test ordering + float x = .5F; + Assert.True(Equal(new Vector4(x, 0, 0, 1), new HalfSingle(x).ToVector4())); + + byte[] rgb = new byte[3]; + byte[] rgba = new byte[4]; + byte[] bgr = new byte[3]; + byte[] bgra = new byte[4]; + + new HalfSingle(x).ToBytes(rgb, 0, ComponentOrder.XYZ); + Assert.Equal(rgb, new byte[] { 128, 0, 0 }); + + new HalfSingle(x).ToBytes(rgba, 0, ComponentOrder.XYZW); + Assert.Equal(rgba, new byte[] { 128, 0, 0, 255 }); + + new HalfSingle(x).ToBytes(bgr, 0, ComponentOrder.ZYX); + Assert.Equal(bgr, new byte[] { 0, 0, 128 }); + + new HalfSingle(x).ToBytes(bgra, 0, ComponentOrder.ZYXW); + Assert.Equal(bgra, new byte[] { 0, 0, 128, 255 }); + } + // Comparison helpers with small tolerance to allow for floating point rounding during computations. public static bool Equal(float a, float b) { diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index f9a44f574c..24ff333611 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -56,6 +56,7 @@ namespace ImageSharp.Tests // Image image = file.CreateImage().To(); // Image image = file.CreateImage().To(); // Image image = file.CreateImage().To(); + // Image image = file.CreateImage().To(); using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) {