Browse Source

Merge remote-tracking branch 'refs/remotes/origin/feature/packedpixel'

af/merge-core
James Jackson-South 9 years ago
parent
commit
9034950e86
  1. 32
      README.md
  2. 19
      src/ImageSharp/Colors/Color.cs
  3. 153
      src/ImageSharp/Colors/PackedPixel/Alpha8.cs
  4. 170
      src/ImageSharp/Colors/PackedPixel/Bgr565.cs
  5. 164
      src/ImageSharp/Colors/PackedPixel/Bgra4444.cs
  6. 171
      src/ImageSharp/Colors/PackedPixel/Bgra5551.cs
  7. 177
      src/ImageSharp/Colors/PackedPixel/Byte4.cs
  8. 160
      src/ImageSharp/Colors/PackedPixel/HalfSingle.cs
  9. 144
      src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs
  10. 187
      src/ImageSharp/Colors/PackedPixel/HalfVector2.cs
  11. 182
      src/ImageSharp/Colors/PackedPixel/HalfVector4.cs
  12. 199
      src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs
  13. 197
      src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs
  14. 206
      src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs
  15. 203
      src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs
  16. 276
      src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs
  17. 3
      src/ImageSharp/Colors/PackedPixel/README.md
  18. 173
      src/ImageSharp/Colors/PackedPixel/Rg32.cs
  19. 172
      src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs
  20. 170
      src/ImageSharp/Colors/PackedPixel/Rgba64.cs
  21. 200
      src/ImageSharp/Colors/PackedPixel/Short2.cs
  22. 216
      src/ImageSharp/Colors/PackedPixel/Short4.cs
  23. 62
      src/ImageSharp/Image/Image.cs
  24. 56
      src/ImageSharp/Image/ImageFrame.cs
  25. 2
      tests/ImageSharp.Benchmarks/project.json
  26. 841
      tests/ImageSharp.Tests/Colors/PackedPixelTests.cs
  27. 17
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

32
README.md

@ -28,7 +28,7 @@ To clone it locally click the "Clone in Windows" button above or run the followi
git clone https://github.com/JimBobSquarePants/ImageSharp
```
###What works so far/ What is planned?
### What works so far/ What is planned?
- Encoding/decoding of image formats (plugable).
- [x] Jpeg (Includes Subsampling. Progressive writing required)
@ -43,16 +43,34 @@ git clone https://github.com/JimBobSquarePants/ImageSharp
- [x] Xiaolin Wu
- [x] Palette
- Basic color structs with implicit operators.
- [x] Color - 32bit color in RGBA order.
- [x] BGRA32
- [x] Color - 32bit color in RGBA order (IPackedPixel\<TPacked\>).
- [x] Bgra32
- [x] CIE Lab
- [x] CIE XYZ
- [x] CMYK
- [x] HSV
- [x] HSL
- [x] YCbCr
- Basic shape primitives (Vector backed)
- [x] Rectangle (Doesn't contain all System.Drawing methods)
- IPackedPixel\<TPacked\> representations of color models. Compatible with Microsoft XNA Game Studio and MonoGame.
- [x] Alpha8
- [x] Bgr565
- [x] Bgra444
- [x] Bgra565
- [x] Byte4
- [x] HalfSingle
- [x] HalfVector2
- [x] HalfVector4
- [x] NormalizedByte2
- [x] NormalizedByte4
- [x] NormalizedShort2
- [x] NormalizedShort4
- [x] Rg32
- [x] Rgba1010102
- [x] Rgba64
- [x] Short2
- [x] Short4
- Basic shape primitives.
- [x] Rectangle
- [x] Size
- [x] Point
- [x] Ellipse
@ -131,9 +149,9 @@ git clone https://github.com/JimBobSquarePants/ImageSharp
- [x] Threshold
- Drawing
- [ ] Path brush (Need help)
- [ ] Pattern brush (Need help)
- [ ] Hatch brush (Need help)
- [ ] Elliptical brush (Need help)
- [ ] Gradient brush (vignette? Need help)
- [ ] Gradient brush (Need help)
- Other stuff I haven't thought of.
### What might never happen

19
src/ImageSharp/Colors/Color.cs

@ -10,7 +10,7 @@ namespace ImageSharp
using System.Numerics;
/// <summary>
/// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
/// </summary>
/// <remarks>
@ -19,9 +19,24 @@ namespace ImageSharp
/// </remarks>
public partial struct Color : IPackedPixel<uint>, IEquatable<Color>
{
/// <summary>
/// The shift count for the red component
/// </summary>
private const int RedShift = 0;
/// <summary>
/// The shift count for the green component
/// </summary>
private const int GreenShift = 8;
/// <summary>
/// The shift count for the blue component
/// </summary>
private const int BlueShift = 16;
/// <summary>
/// The shift count for the alpha component
/// </summary>
private const int AlphaShift = 24;
/// <summary>
@ -219,7 +234,7 @@ namespace ImageSharp
/// <param name="left">The <see cref="Color"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Color"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Color left, Color right)
{

153
src/ImageSharp/Colors/PackedPixel/Alpha8.cs

@ -0,0 +1,153 @@
// <copyright file="Alpha8.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing a single 8 bit normalized W values that is ranging from 0 to 1.
/// </summary>
public struct Alpha8 : IPackedPixel<byte>, IEquatable<Alpha8>
{
/// <summary>
/// Initializes a new instance of the <see cref="Alpha8"/> struct.
/// </summary>
/// <param name="alpha">The alpha component</param>
public Alpha8(float alpha)
{
this.PackedValue = Pack(alpha);
}
/// <inheritdoc />
public byte PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Alpha8"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Alpha8"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Alpha8"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Alpha8 left, Alpha8 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Alpha8"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Alpha8"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Alpha8"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Alpha8 left, Alpha8 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.W);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(0, 0, 0, this.PackedValue / 255F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackedValue = w;
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
switch (componentOrder)
{
case ComponentOrder.ZYX:
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
break;
case ComponentOrder.ZYXW:
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = this.PackedValue;
break;
case ComponentOrder.XYZ:
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
break;
case ComponentOrder.XYZW:
bytes[startIndex] = 0;
bytes[startIndex + 1] = 0;
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = this.PackedValue;
break;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Compares an object with the packed vector.
/// </summary>
/// <param name="obj">The object to compare.</param>
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Alpha8) && this.Equals((Alpha8)obj);
}
/// <summary>
/// Compares another Alpha8 packed vector with the packed vector.
/// </summary>
/// <param name="other">The Alpha8 packed vector to compare.</param>
/// <returns>True if the packed vectors are equal.</returns>
public bool Equals(Alpha8 other)
{
return this.PackedValue == other.PackedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return (this.PackedValue / 255F).ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs a <see cref="float"/> into a byte.
/// </summary>
/// <param name="alpha">The float containing the value to pack.</param>
/// <returns>The <see cref="byte"/> containing the packed values.</returns>
private static byte Pack(float alpha)
{
return (byte)Math.Round(alpha.Clamp(0, 1) * 255F);
}
}
}

170
src/ImageSharp/Colors/PackedPixel/Bgr565.cs

@ -0,0 +1,170 @@
// <copyright file="Bgr565.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits.
/// </summary>
public struct Bgr565 : IPackedPixel<ushort>, IEquatable<Bgr565>
{
/// <summary>
/// Initializes a new instance of the <see cref="Bgr565"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
public Bgr565(float x, float y, float z)
{
this.PackedValue = Pack(x, y, z);
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgr565"/> struct.
/// </summary>
/// <param name="vector">
/// The vector containing the components for the packed value.
/// </param>
public Bgr565(Vector3 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z);
}
/// <inheritdoc />
public ushort PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Bgr565"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgr565"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgr565"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Bgr565 left, Bgr565 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Bgr565"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgr565"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgr565"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Bgr565 left, Bgr565 right)
{
return left.PackedValue != right.PackedValue;
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector3"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector3"/>.</returns>
public Vector3 ToVector3()
{
return new Vector3(
((this.PackedValue >> 11) & 0x1F) * (1F / 31F),
((this.PackedValue >> 5) & 0x3F) * (1F / 63F),
(this.PackedValue & 0x1F) * (1F / 31F));
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(this.ToVector3(), 1F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4() * 255F;
switch (componentOrder)
{
case ComponentOrder.ZYX:
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
break;
case ComponentOrder.ZYXW:
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
break;
case ComponentOrder.XYZ:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
break;
case ComponentOrder.XYZW:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
break;
default:
throw new NotSupportedException();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Bgr565) && this.Equals((Bgr565)obj);
}
/// <inheritdoc />
public bool Equals(Bgr565 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector3().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <returns>The <see cref="ushort"/> containing the packed values.</returns>
private static ushort Pack(float x, float y, float z)
{
return (ushort)((((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 11) |
(((int)Math.Round(y.Clamp(0, 1) * 63F) & 0x3F) << 5) |
((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F));
}
}
}

164
src/ImageSharp/Colors/PackedPixel/Bgra4444.cs

@ -0,0 +1,164 @@
// <copyright file="Bgra4444.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w.
/// </summary>
public struct Bgra4444 : IPackedPixel<ushort>, IEquatable<Bgra4444>
{
/// <summary>
/// Initializes a new instance of the <see cref="Bgra4444"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Bgra4444(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra4444"/> struct.
/// </summary>
/// <param name="vector">The vector containing the components for the packed vector.</param>
public Bgra4444(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public ushort PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Bgra4444"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgra4444"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgra4444"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Bgra4444 left, Bgra4444 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Bgra4444"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgra4444"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgra4444"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Bgra4444 left, Bgra4444 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public Vector4 ToVector4()
{
const float Max = 1 / 15F;
return new Vector4(
((this.PackedValue >> 8) & 0x0F) * Max,
((this.PackedValue >> 4) & 0x0F) * Max,
(this.PackedValue & 0x0F) * Max,
((this.PackedValue >> 12) & 0x0F) * Max);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4() * 255F;
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Bgra4444) && this.Equals((Bgra4444)obj);
}
/// <inheritdoc />
public bool Equals(Bgra4444 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ushort"/> containing the packed values.</returns>
private static ushort Pack(float x, float y, float z, float w)
{
return (ushort)((((int)Math.Round(w.Clamp(0, 1) * 15F) & 0x0F) << 12) |
(((int)Math.Round(x.Clamp(0, 1) * 15F) & 0x0F) << 8) |
(((int)Math.Round(y.Clamp(0, 1) * 15F) & 0x0F) << 4) |
((int)Math.Round(z.Clamp(0, 1) * 15F) & 0x0F));
}
}
}

171
src/ImageSharp/Colors/PackedPixel/Bgra5551.cs

@ -0,0 +1,171 @@
// <copyright file="Bgra5551.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// 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.
/// </summary>
public struct Bgra5551 : IPackedPixel<ushort>, IEquatable<Bgra5551>
{
/// <summary>
/// Initializes a new instance of the <see cref="Bgra5551"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Bgra5551(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <summary>
/// Initializes a new instance of the <see cref="Bgra5551"/> struct.
/// </summary>
/// <param name="vector">
/// The vector containing the components for the packed vector.
/// </param>
public Bgra5551(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public ushort PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Bgra5551"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgra5551"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgra5551"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Bgra5551 left, Bgra5551 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Bgra5551"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Bgra5551"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Bgra5551"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Bgra5551 left, Bgra5551 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
((this.PackedValue >> 10) & 0x1F) / 31F,
((this.PackedValue >> 5) & 0x1F) / 31F,
((this.PackedValue >> 0) & 0x1F) / 31F,
(this.PackedValue >> 15) & 0x01);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4() * 255F;
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Bgra5551) && this.Equals((Bgra5551)obj);
}
/// <inheritdoc />
public bool Equals(Bgra5551 other)
{
return this.PackedValue == other.PackedValue;
}
/// <summary>
/// Gets a string representation of the packed vector.
/// </summary>
/// <returns>A string representation of the packed vector.</returns>
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <summary>
/// Gets a hash code of the packed vector.
/// </summary>
/// <returns>The hash code for the packed vector.</returns>
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ushort"/> containing the packed values.</returns>
private static ushort Pack(float x, float y, float z, float w)
{
return (ushort)(
(((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 10) |
(((int)Math.Round(y.Clamp(0, 1) * 31F) & 0x1F) << 5) |
(((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F) << 0) |
(((int)Math.Round(w.Clamp(0, 1)) & 0x1) << 15));
}
}
}

177
src/ImageSharp/Colors/PackedPixel/Byte4.cs

@ -0,0 +1,177 @@
// <copyright file="Byte4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255.
/// </summary>
public struct Byte4 : IPackedPixel<uint>, IEquatable<Byte4>
{
/// <summary>
/// Initializes a new instance of the <see cref="Byte4"/> struct.
/// </summary>
/// <param name="vector">
/// A vector containing the initial values for the components of the Byte4 structure.
/// </param>
public Byte4(Vector4 vector)
{
this.PackedValue = Pack(ref vector);
}
/// <summary>
/// Initializes a new instance of the <see cref="Byte4"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Byte4(float x, float y, float z, float w)
{
Vector4 vector = new Vector4(x, y, z, w);
this.PackedValue = Pack(ref vector);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Byte4"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Byte4"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Byte4"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Byte4 left, Byte4 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Byte4"/> objects for equality.
/// </summary>
/// <param name="left">The <see cref="Byte4"/> on the left side of the operand.</param>
/// <param name="right">The <see cref="Byte4"/> on the right side of the operand.</param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Byte4 left, Byte4 right)
{
return left.PackedValue != right.PackedValue;
}
/// <summary>
/// Sets the packed representation from a Vector4.
/// </summary>
/// <param name="vector">The vector to create the packed representation from.</param>
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(ref vector);
}
/// <summary>
/// Expands the packed representation into a Vector4.
/// </summary>
/// <returns>The expanded vector.</returns>
public Vector4 ToVector4()
{
return new Vector4(
this.PackedValue & 0xFF,
(this.PackedValue >> 0x8) & 0xFF,
(this.PackedValue >> 0x10) & 0xFF,
(this.PackedValue >> 0x18) & 0xFF);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4() * 255F;
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Byte4) && this.Equals((Byte4)obj);
}
/// <inheritdoc />
public bool Equals(Byte4 other)
{
return this == other;
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Returns a string representation of the current instance.
/// </summary>
/// <returns>String that represents the object.</returns>
public override string ToString()
{
return this.PackedValue.ToString("x8");
}
/// <summary>
/// Packs a vector into a uint.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(ref Vector4 vector)
{
const float Max = 255F;
const float Min = 0F;
// Clamp the value between min and max values
uint byte4 = (uint)Math.Round(vector.X.Clamp(Min, Max)) & 0xFF;
uint byte3 = ((uint)Math.Round(vector.Y.Clamp(Min, Max)) & 0xFF) << 0x8;
uint byte2 = ((uint)Math.Round(vector.Z.Clamp(Min, Max)) & 0xFF) << 0x10;
uint byte1 = ((uint)Math.Round(vector.W.Clamp(Min, Max)) & 0xFF) << 0x18;
return byte4 | byte3 | byte2 | byte1;
}
}
}

160
src/ImageSharp/Colors/PackedPixel/HalfSingle.cs

@ -0,0 +1,160 @@
// <copyright file="HalfSingle.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing a single 16 bit floating point value.
/// </summary>
public struct HalfSingle : IPackedPixel<ushort>, IEquatable<HalfSingle>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half vector value.
/// </summary>
private static readonly Vector4 Half = new Vector4(0.5F);
/// <summary>
/// Initializes a new instance of the <see cref="HalfSingle"/> struct.
/// </summary>
/// <param name="single">The single component.</param>
public HalfSingle(float single)
{
this.PackedValue = HalfTypeHelper.Pack(single);
}
/// <inheritdoc />
public ushort PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="HalfSingle"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfSingle"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfSingle"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(HalfSingle left, HalfSingle right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="HalfSingle"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfSingle"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfSingle"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(HalfSingle left, HalfSingle right)
{
return left.PackedValue != right.PackedValue;
}
/// <summary>
/// Expands the packed representation into a <see cref="float"/>.
/// </summary>
/// <returns>The <see cref="float"/>.</returns>
public float ToSingle()
{
return HalfTypeHelper.Unpack(this.PackedValue);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = HalfTypeHelper.Pack(vector.X);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(this.ToSingle(), 0, 0, 1);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes);
}
/// <inheritdoc />
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is HalfSingle) && this.Equals((HalfSingle)obj);
}
/// <inheritdoc />
public bool Equals(HalfSingle other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToSingle().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
}
}

144
src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs

@ -0,0 +1,144 @@
// <copyright file="HalfTypeHelper.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System.Runtime.InteropServices;
/// <summary>
/// Helper methods for packing and unpacking floating point values
/// </summary>
internal class HalfTypeHelper
{
/// <summary>
/// Packs a <see cref="float"/> into an <see cref="ushort"/>
/// </summary>
/// <param name="value">The float to pack</param>
/// <returns>The <see cref="ushort"/></returns>
internal static ushort Pack(float value)
{
Uif uif = new Uif { F = value };
return Pack(uif.I);
}
/// <summary>
/// Packs an <see cref="int"/> into a <see cref="ushort"/>
/// </summary>
/// <param name="value">The integer to pack.</param>
/// <returns>The <see cref="ushort"/></returns>
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));
}
/// <summary>
/// Unpacks a <see cref="ushort"/> into a <see cref="float"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The <see cref="float"/>.</returns>
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;
}
/// <summary>
/// Maps the position of number types in memory
/// </summary>
[StructLayout(LayoutKind.Explicit)]
private struct Uif
{
/// <summary>
/// The float.
/// </summary>
[FieldOffset(0)]
public float F;
/// <summary>
/// The integer.
/// </summary>
[FieldOffset(0)]
public int I;
/// <summary>
/// The unsigned integer.
/// </summary>
[FieldOffset(0)]
public uint U;
}
}
}

187
src/ImageSharp/Colors/PackedPixel/HalfVector2.cs

@ -0,0 +1,187 @@
// <copyright file="HalfVector2.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing two 16-bit floating-point values.
/// </summary>
public struct HalfVector2 : IPackedPixel<uint>, IEquatable<HalfVector2>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half vector value.
/// </summary>
private static readonly Vector4 Half = new Vector4(0.5F);
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector2"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
public HalfVector2(float x, float y)
{
this.PackedValue = Pack(x, y);
}
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector2"/> struct.
/// </summary>
/// <param name="vector">A vector containing the initial values for the components.</param>
public HalfVector2(Vector2 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="HalfVector2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfVector2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfVector2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(HalfVector2 left, HalfVector2 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="HalfVector2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfVector2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfVector2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(HalfVector2 left, HalfVector2 right)
{
return !left.Equals(right);
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
public Vector2 ToVector2()
{
Vector2 vector;
vector.X = HalfTypeHelper.Unpack((ushort)this.PackedValue);
vector.Y = HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10));
return vector;
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
Vector2 vector = this.ToVector2();
return new Vector4(vector.X, vector.Y, 0F, 1F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes);
}
/// <inheritdoc />
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();
}
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector2().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is HalfVector2) && this.Equals((HalfVector2)obj);
}
/// <inheritdoc />
public bool Equals(HalfVector2 other)
{
return this.PackedValue.Equals(other.PackedValue);
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y)
{
uint num2 = HalfTypeHelper.Pack(x);
uint num = (uint)(HalfTypeHelper.Pack(y) << 0x10);
return num2 | num;
}
}
}

182
src/ImageSharp/Colors/PackedPixel/HalfVector4.cs

@ -0,0 +1,182 @@
// <copyright file="HalfVector4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 16-bit floating-point values.
/// </summary>
public struct HalfVector4 : IPackedPixel<ulong>, IEquatable<HalfVector4>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half vector value.
/// </summary>
private static readonly Vector4 Half = new Vector4(0.5F);
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector4"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
public HalfVector4(float x, float y, float z, float w)
{
var vector = new Vector4(x, y, z, w);
this.PackedValue = PackHelper(ref vector);
}
/// <summary>
/// Initializes a new instance of the <see cref="HalfVector4"/> struct.
/// </summary>
/// <param name="vector">A vector containing the initial values for the components</param>
public HalfVector4(Vector4 vector)
{
this.PackedValue = PackHelper(ref vector);
}
/// <inheritdoc />
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="HalfVector2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfVector2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfVector2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(HalfVector4 left, HalfVector4 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="HalfVector2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="HalfVector2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="HalfVector2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(HalfVector4 left, HalfVector4 right)
{
return !left.Equals(right);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = PackHelper(ref vector);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
HalfTypeHelper.Unpack((ushort)this.PackedValue),
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)),
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x20)),
HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30)));
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes);
}
/// <inheritdoc />
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();
}
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is HalfVector4) && this.Equals((HalfVector4)obj);
}
/// <inheritdoc />
public bool Equals(HalfVector4 other)
{
return this.PackedValue.Equals(other.PackedValue);
}
/// <summary>
/// Packs a <see cref="Vector4"/> into a <see cref="ulong"/>.
/// </summary>
/// <param name="vector">The vector containing the values to pack.</param>
/// <returns>The <see cref="ulong"/> containing the packed values.</returns>
private static ulong PackHelper(ref Vector4 vector)
{
ulong num4 = HalfTypeHelper.Pack(vector.X);
ulong num3 = (ulong)HalfTypeHelper.Pack(vector.Y) << 0x10;
ulong num2 = (ulong)HalfTypeHelper.Pack(vector.Z) << 0x20;
ulong num1 = (ulong)HalfTypeHelper.Pack(vector.W) << 0x30;
return num4 | num3 | num2 | num1;
}
}
}

199
src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs

@ -0,0 +1,199 @@
// <copyright file="NormalizedByte2.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1.
/// </summary>
public struct NormalizedByte2 : IPackedPixel<ushort>, IEquatable<NormalizedByte2>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector4 Half = new Vector4(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector4 Round = new Vector4(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedByte2"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public NormalizedByte2(Vector2 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedByte2"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
public NormalizedByte2(float x, float y)
{
this.PackedValue = Pack(x, y);
}
/// <inheritdoc />
public ushort PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="NormalizedByte2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedByte2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedByte2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(NormalizedByte2 left, NormalizedByte2 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="NormalizedByte2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedByte2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedByte2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(NormalizedByte2 left, NormalizedByte2 right)
{
return left.PackedValue != right.PackedValue;
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
public Vector2 ToVector2()
{
return new Vector2(
(sbyte)((this.PackedValue >> 0) & 0xFF) / 127F,
(sbyte)((this.PackedValue >> 8) & 0xFF) / 127F);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(this.ToVector2(), 0F, 1F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w);
vector -= Round;
vector -= Half;
vector -= Round;
vector /= Half;
this.PackFromVector4(vector);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
switch (componentOrder)
{
case ComponentOrder.ZYX:
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
break;
case ComponentOrder.ZYXW:
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = (byte)vector.X;
bytes[startIndex + 3] = 255;
break;
case ComponentOrder.XYZ:
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = 0;
break;
case ComponentOrder.XYZW:
bytes[startIndex] = (byte)vector.X;
bytes[startIndex + 1] = (byte)vector.Y;
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = 255;
break;
default:
throw new NotSupportedException();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedByte2) && this.Equals((NormalizedByte2)obj);
}
/// <inheritdoc />
public bool Equals(NormalizedByte2 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return this.PackedValue.ToString("X");
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ushort"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <returns>The <see cref="ushort"/> containing the packed values.</returns>
private static ushort Pack(float x, float y)
{
int byte2 = ((ushort)Math.Round(x.Clamp(-1F, 1F) * 127F) & 0xFF) << 0;
int byte1 = ((ushort)Math.Round(y.Clamp(-1F, 1F) * 127F) & 0xFF) << 8;
return (ushort)(byte2 | byte1);
}
}
}

197
src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs

@ -0,0 +1,197 @@
// <copyright file="NormalizedByte4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1.
/// </summary>
public struct NormalizedByte4 : IPackedPixel<uint>, IEquatable<NormalizedByte4>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector4 Half = new Vector4(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector4 Round = new Vector4(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedByte4"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public NormalizedByte4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedByte4"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
public NormalizedByte4(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="NormalizedByte4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedByte4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedByte4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(NormalizedByte4 left, NormalizedByte4 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="NormalizedByte4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedByte4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedByte4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(NormalizedByte4 left, NormalizedByte4 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
(sbyte)((this.PackedValue >> 0) & 0xFF) / 127F,
(sbyte)((this.PackedValue >> 8) & 0xFF) / 127F,
(sbyte)((this.PackedValue >> 16) & 0xFF) / 127F,
(sbyte)((this.PackedValue >> 24) & 0xFF) / 127F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w);
vector -= Round;
vector -= Half;
vector -= Round;
vector /= Half;
this.PackFromVector4(vector);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedByte4) && this.Equals((NormalizedByte4)obj);
}
/// <inheritdoc />
public bool Equals(NormalizedByte4 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return this.PackedValue.ToString("X");
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y, float z, float w)
{
uint byte4 = ((uint)Math.Round(x.Clamp(-1F, 1F) * 127F) & 0xFF) << 0;
uint byte3 = ((uint)Math.Round(y.Clamp(-1F, 1F) * 127F) & 0xFF) << 8;
uint byte2 = ((uint)Math.Round(z.Clamp(-1F, 1F) * 127F) & 0xFF) << 16;
uint byte1 = ((uint)Math.Round(w.Clamp(-1F, 1F) * 127F) & 0xFF) << 24;
return byte4 | byte3 | byte2 | byte1;
}
}
}

206
src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs

@ -0,0 +1,206 @@
// <copyright file="NormalizedShort2.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1.
/// </summary>
public struct NormalizedShort2 : IPackedPixel<uint>, IEquatable<NormalizedShort2>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector4 Half = new Vector4(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector4 Round = new Vector4(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedShort2"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public NormalizedShort2(Vector2 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedShort2"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
public NormalizedShort2(float x, float y)
{
this.PackedValue = Pack(x, y);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="NormalizedShort2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedShort2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedShort2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(NormalizedShort2 left, NormalizedShort2 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="NormalizedShort2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedShort2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedShort2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right)
{
return !left.Equals(right);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(this.ToVector2(), 0, 1);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w);
vector -= Round;
vector -= Half;
vector -= Round;
vector /= Half;
this.PackFromVector4(vector);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
switch (componentOrder)
{
case ComponentOrder.ZYX:
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
break;
case ComponentOrder.ZYXW:
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = 255;
break;
case ComponentOrder.XYZ:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = 0;
break;
case ComponentOrder.XYZW:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = 255;
break;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
public Vector2 ToVector2()
{
const float MaxVal = 0x7FFF;
return new Vector2(
(short)(this.PackedValue & 0xFFFF) / MaxVal,
(short)(this.PackedValue >> 0x10) / MaxVal);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedShort2) && this.Equals((NormalizedShort2)obj);
}
/// <inheritdoc />
public bool Equals(NormalizedShort2 other)
{
return this.PackedValue.Equals(other.PackedValue);
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return this.PackedValue.ToString("X");
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y)
{
const float MaxPos = 0x7FFF;
const float MinNeg = -MaxPos;
// Clamp the value between min and max values
// Round rather than truncate.
uint word2 = (uint)((int)(float)Math.Round(x * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF);
uint word1 = (uint)(((int)(float)Math.Round(y * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x10);
return word2 | word1;
}
}
}

203
src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs

@ -0,0 +1,203 @@
// <copyright file="NormalizedShort4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1.
/// </summary>
public struct NormalizedShort4 : IPackedPixel<ulong>, IEquatable<NormalizedShort4>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector4 Half = new Vector4(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector4 Round = new Vector4(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedShort4"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public NormalizedShort4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Initializes a new instance of the <see cref="NormalizedShort4"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
public NormalizedShort4(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <inheritdoc />
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="NormalizedShort4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedShort4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedShort4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(NormalizedShort4 left, NormalizedShort4 right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="NormalizedShort4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="NormalizedShort4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="NormalizedShort4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right)
{
return !left.Equals(right);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
const float MaxVal = 0x7FFF;
return new Vector4(
(short)((this.PackedValue >> 0x00) & 0xFFFF) / MaxVal,
(short)((this.PackedValue >> 0x10) & 0xFFFF) / MaxVal,
(short)((this.PackedValue >> 0x20) & 0xFFFF) / MaxVal,
(short)((this.PackedValue >> 0x30) & 0xFFFF) / MaxVal);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w);
vector -= Round;
vector -= Half;
vector -= Round;
vector /= Half;
this.PackFromVector4(vector);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
switch (componentOrder)
{
case ComponentOrder.ZYX:
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
break;
case ComponentOrder.ZYXW:
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
break;
case ComponentOrder.XYZ:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
break;
case ComponentOrder.XYZW:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
break;
default:
throw new NotSupportedException();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedShort4) && this.Equals((NormalizedShort4)obj);
}
/// <inheritdoc />
public bool Equals(NormalizedShort4 other)
{
return this.PackedValue.Equals(other.PackedValue);
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return this.PackedValue.ToString("X");
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ulong"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ulong"/> containing the packed values.</returns>
private static ulong Pack(float x, float y, float z, float w)
{
const float MaxPos = 0x7FFF;
const float MinNeg = -MaxPos;
// Clamp the value between min and max values
ulong word4 = ((ulong)(float)Math.Round(x * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x00;
ulong word3 = ((ulong)(float)Math.Round(y * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x10;
ulong word2 = ((ulong)(float)Math.Round(z * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x20;
ulong word1 = ((ulong)(float)Math.Round(w * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x30;
return word4 | word3 | word2 | word1;
}
}
}

276
src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs

@ -0,0 +1,276 @@
// <copyright file="PackedPixelConverterHelper.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Assists with the conversion of known packed pixel formats from one to another.
/// </summary>
internal static class PackedPixelConverterHelper
{
/// <summary>
/// A non operative function. Simply returns the original vector.
/// </summary>
private static readonly Func<Vector4, Vector4> Noop = vector4 => vector4;
/// <summary>
/// Returns the correct scaling function for the given types The compute scale function.
/// </summary>
/// <param name="scaleFunc">The scale function.</param>
/// <typeparam name="TColor">The source pixel format.</typeparam>
/// <typeparam name="TColor2">The target pixel format.</typeparam>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
public static Func<Vector4, Vector4> ComputeScaleFunction<TColor, TColor2>(Func<Vector4, Vector4> scaleFunc)
{
// Custom type with a custom function.
if (scaleFunc != null)
{
return scaleFunc;
}
Type source = typeof(TColor);
Type target = typeof(TColor2);
// Standard to offset
if (IsStandardNormalizedType(source))
{
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Expand the range then offset the center down.
return vector4 => (2F * vector4) - Vector4.One;
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
return v => (65534 * v) - new Vector4(32767);
}
}
// Normalized offsets. All four components.
if (IsOffsetNormalizedType(source))
{
return FromOffsetNormalizedType(target);
}
// Offset. All four components.
if (IsOffsetType(source))
{
return FromOffsetType(target);
}
// Normalized offsets. First component pair only.
if (IsOffsetTwoComponentNormalizedType(source))
{
return FromOffsetTwoComponentNormalizedType(target);
}
// Offsets. First component pair only.
if (IsOffsetTwoComponentType(source))
{
return FromOffsetTwoComponentType(target);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from -1 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetNormalizedType(Type target)
{
if (IsStandardNormalizedType(target))
{
// Compress the range then offset the center up.
return vector4 => (vector4 / 2F) + new Vector4(.5F);
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
// Multiply out the range, two component won't read the last two values.
return vector4 => (vector4 * 32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from -32767 to 32767.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetType(Type target)
{
if (IsStandardNormalizedType(target))
{
// Compress the range then offset the center up.
return vector4 => (vector4 / 65534) + new Vector4(.5F);
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Compress the range. Two component won't read the last two values.
return vector4 => (vector4 / 32767);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector with the first component pair ranging from -1 to 1.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetTwoComponentNormalizedType(Type target)
{
if (IsStandardNormalizedType(target))
{
return vector4 =>
{
// Compress the range then offset the center up for first pair.
Vector4 v = (vector4 / 2F) + new Vector4(.5F);
return new Vector4(v.X, v.Y, 0, 1);
};
}
if (IsOffsetNormalizedType(target))
{
// Copy the first two components and set second pair to 0 and 1 equivalent.
return vector4 => new Vector4(vector4.X, vector4.Y, -1, 1);
}
if (IsOffsetTwoComponentType(target))
{
// Multiply. Two component won't read the last two values.
return vector4 => (vector4 * 32767);
}
if (IsOffsetType(target))
{
return vector4 =>
{
// Multiply the first two components and set second pair to 0 and 1 equivalent.
Vector4 v = vector4 * 32767;
return new Vector4(v.X, v.Y, -32767, 32767);
};
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector with the first component pair ranging from -32767 to 32767.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetTwoComponentType(Type target)
{
if (IsStandardNormalizedType(target))
{
return vector4 =>
{
Vector4 v = (vector4 / 65534) + new Vector4(.5F);
return new Vector4(v.X, v.Y, 0, 1);
};
}
if (IsOffsetType(target))
{
// Copy the first two components and set second pair to 0 and 1 equivalent.
return vector4 => new Vector4(vector4.X, vector4.Y, -32767, 32767);
}
if (IsOffsetNormalizedType(target))
{
return vector4 =>
{
// Divide the first two components and set second pair to 0 and 1 equivalent.
Vector4 v = vector4 / 32767;
return new Vector4(v.X, v.Y, -1, 1);
};
}
if (IsOffsetTwoComponentNormalizedType(target))
{
// Divide. Two component won't read the last two values.
return vector4 => (vector4 / 32767);
}
return Noop;
}
/// <summary>
/// Identifies the type as having vector component values ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsStandardNormalizedType(Type type)
{
return type == typeof(Color)
|| type == typeof(Bgr565)
|| type == typeof(Bgra4444)
|| type == typeof(Bgra5551)
|| type == typeof(Byte4)
|| type == typeof(HalfSingle)
|| type == typeof(HalfVector2)
|| type == typeof(HalfVector4)
|| type == typeof(Rg32)
|| type == typeof(Rgba1010102)
|| type == typeof(Rgba64);
}
/// <summary>
/// Identifies the type as having vector values representing the first component pair ranging from -1 to 1.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetTwoComponentNormalizedType(Type type)
{
return type == typeof(NormalizedByte2)
|| type == typeof(NormalizedShort2);
}
/// <summary>
/// Identifies the type as having vector values representing all four components ranging from -1 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetNormalizedType(Type type)
{
return type == typeof(NormalizedByte4)
|| type == typeof(NormalizedShort4);
}
/// <summary>
/// Identifies the type as having vector values representing the first component pair ranging from -32767 to 32767.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetTwoComponentType(Type type)
{
return type == typeof(Short2);
}
/// <summary>
/// Identifies the type as having vector values representing all four components ranging from -32767 to 32767.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetType(Type type)
{
return type == typeof(Short4);
}
}
}

3
src/ImageSharp/Colors/PackedPixel/README.md

@ -0,0 +1,3 @@
Pixel formats adapted and extended from:
https://github.com/MonoGame/MonoGame

173
src/ImageSharp/Colors/PackedPixel/Rg32.cs

@ -0,0 +1,173 @@
// <copyright file="Rg32.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1.
/// </summary>
public struct Rg32 : IPackedPixel<uint>, IEquatable<Rg32>, IPackedVector
{
/// <summary>
/// Initializes a new instance of the <see cref="Rg32"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
public Rg32(float x, float y)
{
this.PackedValue = Pack(x, y);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rg32"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public Rg32(Vector2 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Rg32"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rg32"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rg32"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Rg32 left, Rg32 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Rg32"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rg32"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rg32"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Rg32 left, Rg32 right)
{
return left.PackedValue != right.PackedValue;
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
public Vector2 ToVector2()
{
return new Vector2(
(this.PackedValue & 0xFFFF) / 65535F,
((this.PackedValue >> 16) & 0xFFFF) / 65535F);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(this.ToVector2(), 0F, 1F);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4() * 255F;
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();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Rg32) && this.Equals((Rg32)obj);
}
/// <inheritdoc />
public bool Equals(Rg32 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector2().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y)
{
return (uint)(
((int)Math.Round(x.Clamp(0, 1) * 65535F) & 0xFFFF) |
(((int)Math.Round(y.Clamp(0, 1) * 65535F) & 0xFFFF) << 16));
}
}
}

172
src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs

@ -0,0 +1,172 @@
// <copyright file="Rgba1010102.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed vector type containing unsigned normalized values ranging from 0 to 1.
/// The x, y and z components use 10 bits, and the w component uses 2 bits.
/// </summary>
public struct Rgba1010102 : IPackedPixel<uint>, IEquatable<Rgba1010102>, IPackedVector
{
/// <summary>
/// Initializes a new instance of the <see cref="Rgba1010102"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Rgba1010102(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgba1010102"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public Rgba1010102(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Rgba1010102"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba1010102"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba1010102"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Rgba1010102 left, Rgba1010102 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Rgba1010102"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba1010102"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba1010102"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Rgba1010102 left, Rgba1010102 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
((this.PackedValue >> 0) & 0x03FF) / 1023F,
((this.PackedValue >> 10) & 0x03FF) / 1023F,
((this.PackedValue >> 20) & 0x03FF) / 1023F,
((this.PackedValue >> 30) & 0x03) / 3F);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4() * 255F;
switch (componentOrder)
{
case ComponentOrder.ZYX:
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
break;
case ComponentOrder.ZYXW:
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
break;
case ComponentOrder.XYZ:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
break;
case ComponentOrder.XYZW:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
break;
default:
throw new NotSupportedException();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Rgba1010102) && this.Equals((Rgba1010102)obj);
}
/// <inheritdoc />
public bool Equals(Rgba1010102 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y, float z, float w)
{
return (uint)(
(((int)Math.Round(x.Clamp(0, 1) * 1023F) & 0x03FF) << 0) |
(((int)Math.Round(y.Clamp(0, 1) * 1023F) & 0x03FF) << 10) |
(((int)Math.Round(z.Clamp(0, 1) * 1023F) & 0x03FF) << 20) |
(((int)Math.Round(w.Clamp(0, 1) * 3F) & 0x03) << 30));
}
}
}

170
src/ImageSharp/Colors/PackedPixel/Rgba64.cs

@ -0,0 +1,170 @@
// <copyright file="Rgba64.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1.
/// </summary>
public struct Rgba64 : IPackedPixel<ulong>, IEquatable<Rgba64>, IPackedVector
{
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
public Rgba64(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <summary>
/// Initializes a new instance of the <see cref="Rgba64"/> struct.
/// </summary>
/// <param name="vector">The vector containing the components values.</param>
public Rgba64(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Rgba64"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba64"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba64"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Rgba64 left, Rgba64 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Rgba64"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Rgba64"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Rgba64"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Rgba64 left, Rgba64 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
(this.PackedValue & 0xFFFF) / 65535F,
((this.PackedValue >> 16) & 0xFFFF) / 65535F,
((this.PackedValue >> 32) & 0xFFFF) / 65535F,
((this.PackedValue >> 48) & 0xFFFF) / 65535F);
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.PackFromVector4(new Vector4(x, y, z, w) / 255F);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4() * 255F;
switch (componentOrder)
{
case ComponentOrder.ZYX:
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
break;
case ComponentOrder.ZYXW:
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
break;
case ComponentOrder.XYZ:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
break;
case ComponentOrder.XYZW:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
break;
default:
throw new NotSupportedException();
}
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Rgba64) && this.Equals((Rgba64)obj);
}
/// <inheritdoc />
public bool Equals(Rgba64 other)
{
return this.PackedValue == other.PackedValue;
}
/// <inheritdoc />
public override string ToString()
{
return this.ToVector4().ToString();
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="ulong"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ulong"/> containing the packed values.</returns>
private static ulong Pack(float x, float y, float z, float w)
{
return (ulong)Math.Round(x.Clamp(0, 1) * 65535F) |
((ulong)Math.Round(y.Clamp(0, 1) * 65535F) << 16) |
((ulong)Math.Round(z.Clamp(0, 1) * 65535F) << 32) |
((ulong)Math.Round(w.Clamp(0, 1) * 65535F) << 48);
}
}
}

200
src/ImageSharp/Colors/PackedPixel/Short2.cs

@ -0,0 +1,200 @@
// <copyright file="Short2.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing two 16-bit signed integer values.
/// </summary>
public struct Short2 : IPackedPixel<uint>, IEquatable<Short2>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector2 MaxBytes = new Vector2(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector2 Half = new Vector2(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector2 Round = new Vector2(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="Short2"/> struct.
/// </summary>
/// <param name="vector">The vector containing the component values.</param>
public Short2(Vector2 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <summary>
/// Initializes a new instance of the <see cref="Short2"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
public Short2(float x, float y)
{
this.PackedValue = Pack(x, y);
}
/// <inheritdoc />
public uint PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Short2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Short2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Short2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Short2 left, Short2 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Short2"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Short2"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Short2"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Short2 left, Short2 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10), 0, 1);
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector2 vector = new Vector2(x, y) / 255;
vector *= 65534;
vector -= new Vector2(32767);
this.PackedValue = Pack(vector.X, vector.Y);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector2 vector = this.ToVector2();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes);
switch (componentOrder)
{
case ComponentOrder.ZYX:
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
break;
case ComponentOrder.ZYXW:
bytes[startIndex] = 0;
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = 255;
break;
case ComponentOrder.XYZ:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = 0;
break;
case ComponentOrder.XYZW:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = 0;
bytes[startIndex + 3] = 255;
break;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Expands the packed representation into a <see cref="Vector2"/>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector2"/>.</returns>
public Vector2 ToVector2()
{
return new Vector2((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10));
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Short2) && this.Equals((Short2)obj);
}
/// <inheritdoc />
public bool Equals(Short2 other)
{
return this == other;
}
/// <inheritdoc />
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <inheritdoc />
public override string ToString()
{
return this.PackedValue.ToString("x8");
}
/// <summary>
/// Packs the <see cref="float"/> components into a <see cref="uint"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <returns>The <see cref="uint"/> containing the packed values.</returns>
private static uint Pack(float x, float y)
{
// Largest two byte positive number 0xFFFF >> 1;
const float MaxPos = 0x7FFF;
const float MinNeg = ~(int)MaxPos;
// Clamp the value between min and max values
uint word2 = (uint)Math.Round(x.Clamp(MinNeg, MaxPos)) & 0xFFFF;
uint word1 = ((uint)Math.Round(y.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x10;
return word2 | word1;
}
}
}

216
src/ImageSharp/Colors/PackedPixel/Short4.cs

@ -0,0 +1,216 @@
// <copyright file="Short4.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Numerics;
/// <summary>
/// Packed pixel type containing four 16-bit signed integer values.
/// </summary>
public struct Short4 : IPackedPixel<ulong>, IEquatable<Short4>
{
/// <summary>
/// The maximum byte value.
/// </summary>
private static readonly Vector4 MaxBytes = new Vector4(255);
/// <summary>
/// The half the maximum byte value.
/// </summary>
private static readonly Vector4 Half = new Vector4(127);
/// <summary>
/// The vector value used for rounding.
/// </summary>
private static readonly Vector4 Round = new Vector4(.5F);
/// <summary>
/// Initializes a new instance of the <see cref="Short4"/> struct.
/// </summary>
/// <param name="vector">A vector containing the initial values for the components.</param>
public Short4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <summary>
/// Initializes a new instance of the <see cref="Short4"/> struct.
/// </summary>
/// <param name="x">The x-component.</param>
/// <param name="y">The y-component.</param>
/// <param name="z">The z-component.</param>
/// <param name="w">The w-component.</param>
public Short4(float x, float y, float z, float w)
{
this.PackedValue = Pack(x, y, z, w);
}
/// <inheritdoc />
public ulong PackedValue { get; set; }
/// <summary>
/// Compares two <see cref="Short4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Short4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Short4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(Short4 left, Short4 right)
{
return left.PackedValue == right.PackedValue;
}
/// <summary>
/// Compares two <see cref="Short4"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Short4"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Short4"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the <paramref name="left"/> parameter is not equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(Short4 left, Short4 right)
{
return left.PackedValue != right.PackedValue;
}
/// <inheritdoc />
public void PackFromVector4(Vector4 vector)
{
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public Vector4 ToVector4()
{
return new Vector4(
(short)(this.PackedValue & 0xFFFF),
(short)((this.PackedValue >> 0x10) & 0xFFFF),
(short)((this.PackedValue >> 0x20) & 0xFFFF),
(short)((this.PackedValue >> 0x30) & 0xFFFF));
}
/// <inheritdoc />
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
Vector4 vector = new Vector4(x, y, z, w) / 255;
vector *= 65534;
vector -= new Vector4(32767);
this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W);
}
/// <inheritdoc />
public void ToBytes(byte[] bytes, int startIndex, ComponentOrder componentOrder)
{
Vector4 vector = this.ToVector4();
vector /= 65534;
vector *= 255;
vector += Half;
vector += Round;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);
switch (componentOrder)
{
case ComponentOrder.ZYX:
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
break;
case ComponentOrder.ZYXW:
bytes[startIndex] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
break;
case ComponentOrder.XYZ:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
break;
case ComponentOrder.XYZW:
bytes[startIndex] = (byte)(float)Math.Round(vector.X);
bytes[startIndex + 1] = (byte)(float)Math.Round(vector.Y);
bytes[startIndex + 2] = (byte)(float)Math.Round(vector.Z);
bytes[startIndex + 3] = (byte)(float)Math.Round(vector.W);
break;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Returns a value that indicates whether the current instance is equal to a specified object.
/// </summary>
/// <param name="obj">The object with which to make the comparison.</param>
/// <returns>true if the current instance is equal to the specified object; false otherwise.</returns>
public override bool Equals(object obj)
{
return (obj is Short4) && this == (Short4)obj;
}
/// <summary>
/// Returns a value that indicates whether the current instance is equal to a specified object.
/// </summary>
/// <param name="other">The object with which to make the comparison.</param>
/// <returns>true if the current instance is equal to the specified object; false otherwise.</returns>
public bool Equals(Short4 other)
{
return this == other;
}
/// <summary>
/// Gets the hash code for the current instance.
/// </summary>
/// <returns>Hash code for the instance.</returns>
public override int GetHashCode()
{
return this.PackedValue.GetHashCode();
}
/// <summary>
/// Returns a string representation of the current instance.
/// </summary>
/// <returns>String that represents the object.</returns>
public override string ToString()
{
return this.PackedValue.ToString("x16");
}
/// <summary>
/// Packs the components into a <see cref="ulong"/>.
/// </summary>
/// <param name="x">The x-component</param>
/// <param name="y">The y-component</param>
/// <param name="z">The z-component</param>
/// <param name="w">The w-component</param>
/// <returns>The <see cref="ulong"/> containing the packed values.</returns>
private static ulong Pack(float x, float y, float z, float w)
{
// Largest two byte positive number 0xFFFF >> 1;
const float MaxPos = 0x7FFF;
// Two's complement
const float MinNeg = ~(int)MaxPos;
// Clamp the value between min and max values
ulong word4 = ((ulong)Math.Round(x.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x00;
ulong word3 = ((ulong)Math.Round(y.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x10;
ulong word2 = ((ulong)Math.Round(z.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x20;
ulong word1 = ((ulong)Math.Round(w.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x30;
return word4 | word3 | word2 | word1;
}
}
}

62
src/ImageSharp/Image/Image.cs

@ -10,7 +10,9 @@ namespace ImageSharp
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Formats;
@ -245,7 +247,7 @@ namespace ImageSharp
/// <summary>
/// Returns a Base64 encoded string from the given image.
/// </summary>
/// <example>data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA==</example>
/// <example><see href="data:image/gif;base64,R0lGODlhAQABAIABAEdJRgAAACwAAAAAAQABAAACAkQBAA=="/></example>
/// <returns>The <see cref="string"/></returns>
public string ToBase64String()
{
@ -257,6 +259,60 @@ namespace ImageSharp
}
}
/// <summary>
/// Returns a copy of the image in the given pixel format.
/// </summary>
/// <param name="scaleFunc">A function that allows for the correction of vector scaling between unknown color formats.</param>
/// <typeparam name="TColor2">The pixel format.</typeparam>
/// <typeparam name="TPacked2">The packed format. <example>uint, long, float.</example></typeparam>
/// <returns>The <see cref="Image{TColor2, TPacked2}"/></returns>
public Image<TColor2, TPacked2> To<TColor2, TPacked2>(Func<Vector4, Vector4> scaleFunc = null)
where TColor2 : struct, IPackedPixel<TPacked2>
where TPacked2 : struct
{
scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TColor, TColor2>(scaleFunc);
Image<TColor2, TPacked2> target = new Image<TColor2, TPacked2>(this.Width, this.Height)
{
Quality = this.Quality,
FrameDelay = this.FrameDelay,
HorizontalResolution = this.HorizontalResolution,
VerticalResolution = this.VerticalResolution,
CurrentImageFormat = this.CurrentImageFormat,
RepeatCount = this.RepeatCount
};
using (PixelAccessor<TColor, TPacked> pixels = this.Lock())
using (PixelAccessor<TColor2, TPacked2> targetPixels = target.Lock())
{
Parallel.For(
0,
target.Height,
Bootstrapper.Instance.ParallelOptions,
y =>
{
for (int x = 0; x < target.Width; x++)
{
TColor2 color = default(TColor2);
color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4()));
targetPixels[x, y] = color;
}
});
}
if (this.ExifProfile != null)
{
target.ExifProfile = new ExifProfile(this.ExifProfile);
}
foreach (ImageFrame<TColor, TPacked> frame in this.Frames)
{
target.Frames.Add(frame.To<TColor2, TPacked2>());
}
return target;
}
/// <summary>
/// Copies the properties from the other <see cref="Image{TColor, TPacked}"/>.
/// </summary>
@ -278,6 +334,10 @@ namespace ImageSharp
}
}
/// <summary>
/// Creates a new <see cref="ImageFrame{TColor,TPacked}"/> from this instance
/// </summary>
/// <returns>The <see cref="ImageFrame{TColor,TPacked}"/></returns>
internal virtual ImageFrame<TColor, TPacked> ToFrame()
{
return new ImageFrame<TColor, TPacked>(this);

56
src/ImageSharp/Image/ImageFrame.cs

@ -5,6 +5,10 @@
namespace ImageSharp
{
using System;
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// Represents a single frame in a animation.
/// </summary>
@ -24,9 +28,7 @@ namespace ImageSharp
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TColor, TPacked}"/> class.
/// </summary>
/// <param name="image">
/// The image to create the frame from.
/// </param>
/// <param name="image">The image to create the frame from.</param>
public ImageFrame(ImageBase<TColor, TPacked> image)
: base(image)
{
@ -38,9 +40,55 @@ namespace ImageSharp
return $"ImageFrame: {this.Width}x{this.Height}";
}
/// <summary>
/// Returns a copy of the image frame in the given pixel format.
/// </summary>
/// <param name="scaleFunc">A function that allows for the correction of vector scaling between unknown color formats.</param>
/// <typeparam name="TColor2">The pixel format.</typeparam>
/// <typeparam name="TPacked2">The packed format. <example>uint, long, float.</example></typeparam>
/// <returns>The <see cref="ImageFrame{TColor2, TPacked2}"/></returns>
public ImageFrame<TColor2, TPacked2> To<TColor2, TPacked2>(Func<Vector4, Vector4> scaleFunc = null)
where TColor2 : struct, IPackedPixel<TPacked2>
where TPacked2 : struct
{
scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TColor, TColor2>(scaleFunc);
ImageFrame<TColor2, TPacked2> target = new ImageFrame<TColor2, TPacked2>
{
Quality = this.Quality,
FrameDelay = this.FrameDelay
};
target.InitPixels(this.Width, this.Height);
using (PixelAccessor<TColor, TPacked> pixels = this.Lock())
using (PixelAccessor<TColor2, TPacked2> targetPixels = target.Lock())
{
Parallel.For(
0,
target.Height,
Bootstrapper.Instance.ParallelOptions,
y =>
{
for (int x = 0; x < target.Width; x++)
{
TColor2 color = default(TColor2);
color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4()));
targetPixels[x, y] = color;
}
});
}
return target;
}
/// <summary>
/// Clones the current instance.
/// </summary>
/// <returns>The <see cref="ImageFrame{TColor, TPacked}"/></returns>
internal virtual ImageFrame<TColor, TPacked> Clone()
{
return new ImageFrame<TColor, TPacked>(this);
}
}
}
}

2
tests/ImageSharp.Benchmarks/project.json

@ -14,7 +14,7 @@
"allowUnsafe": true
},
"dependencies": {
"BenchmarkDotNet.Diagnostics.Windows": "0.9.9.116",
"BenchmarkDotNet.Diagnostics.Windows": "0.10.0",
"ImageSharp": "1.0.0-*"
},
"commands": {

841
tests/ImageSharp.Tests/Colors/PackedPixelTests.cs

@ -0,0 +1,841 @@
// <copyright file="PackedPixelTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests.Colors
{
using System;
using System.Numerics;
using Xunit;
/// <summary>
/// The packed pixel tests.
/// </summary>
public class PackedPixelTests
{
[Fact]
public void Alpha8()
{
// Test the limits.
Assert.Equal(0x0, new Alpha8(0F).PackedValue);
Assert.Equal(0xFF, new Alpha8(1F).PackedValue);
// Test clamping.
Assert.Equal(0x0, new Alpha8(-1234F).PackedValue);
Assert.Equal(0xFF, new Alpha8(1234F).PackedValue);
// Test ordering
Assert.Equal(124, new Alpha8(124F / 0xFF).PackedValue);
Assert.Equal(26, new Alpha8(0.1F).PackedValue);
// Test ordering
Vector4 vector = new Alpha8(.5F).ToVector4();
Assert.Equal(vector.X, 0);
Assert.Equal(vector.Y, 0);
Assert.Equal(vector.Z, 0);
Assert.Equal(vector.W, .5F, 2);
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new Alpha8(.5F).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 0, 0, 0 });
new Alpha8(.5F).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 0, 0, 0, 128 });
new Alpha8(.5F).ToBytes(rgb, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 0, 0, 0 });
new Alpha8(.5F).ToBytes(rgb, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 0, 0, 0, 128 });
}
[Fact]
public void Bgr565()
{
// Test the limits.
Assert.Equal(0x0, new Bgr565(Vector3.Zero).PackedValue);
Assert.Equal(0xFFFF, new Bgr565(Vector3.One).PackedValue);
// Test ToVector3.
Assert.True(Equal(Vector3.One, new Bgr565(Vector3.One).ToVector3()));
Assert.True(Equal(Vector3.Zero, new Bgr565(Vector3.Zero).ToVector3()));
Assert.True(Equal(Vector3.UnitX, new Bgr565(Vector3.UnitX).ToVector3()));
Assert.True(Equal(Vector3.UnitY, new Bgr565(Vector3.UnitY).ToVector3()));
Assert.True(Equal(Vector3.UnitZ, new Bgr565(Vector3.UnitZ).ToVector3()));
// Test clamping.
Assert.True(Equal(Vector3.Zero, new Bgr565(Vector3.One * -1234F).ToVector3()));
Assert.True(Equal(Vector3.One, new Bgr565(Vector3.One * 1234F).ToVector3()));
// Make sure the swizzle is correct.
Assert.Equal(0xF800, new Bgr565(Vector3.UnitX).PackedValue);
Assert.Equal(0x07E0, new Bgr565(Vector3.UnitY).PackedValue);
Assert.Equal(0x001F, new Bgr565(Vector3.UnitZ).PackedValue);
float x = 0.1F;
float y = -0.3F;
float z = 0.5F;
Assert.Equal(6160, new Bgr565(x, y, z).PackedValue);
// Test ordering
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new Bgr565(x, y, z).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 25, 0, 132 });
new Bgr565(x, y, z).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 25, 0, 132, 255 });
new Bgr565(x, y, z).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 132, 0, 25 });
new Bgr565(x, y, z).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 132, 0, 25, 255 });
}
[Fact]
public void Bgra4444()
{
// Test the limits.
Assert.Equal(0x0, new Bgra4444(Vector4.Zero).PackedValue);
Assert.Equal(0xFFFF, new Bgra4444(Vector4.One).PackedValue);
// Test ToVector4.
Assert.True(Equal(Vector4.One, new Bgra4444(Vector4.One).ToVector4()));
Assert.True(Equal(Vector4.Zero, new Bgra4444(Vector4.Zero).ToVector4()));
Assert.True(Equal(Vector4.UnitX, new Bgra4444(Vector4.UnitX).ToVector4()));
Assert.True(Equal(Vector4.UnitY, new Bgra4444(Vector4.UnitY).ToVector4()));
Assert.True(Equal(Vector4.UnitZ, new Bgra4444(Vector4.UnitZ).ToVector4()));
Assert.True(Equal(Vector4.UnitW, new Bgra4444(Vector4.UnitW).ToVector4()));
// Test clamping.
Assert.True(Equal(Vector4.Zero, new Bgra4444(Vector4.One * -1234.0f).ToVector4()));
Assert.True(Equal(Vector4.One, new Bgra4444(Vector4.One * 1234.0f).ToVector4()));
// Make sure the swizzle is correct.
Assert.Equal(0x0F00, new Bgra4444(Vector4.UnitX).PackedValue);
Assert.Equal(0x00F0, new Bgra4444(Vector4.UnitY).PackedValue);
Assert.Equal(0x000F, new Bgra4444(Vector4.UnitZ).PackedValue);
Assert.Equal(0xF000, new Bgra4444(Vector4.UnitW).PackedValue);
float x = 0.1f;
float y = -0.3f;
float z = 0.5f;
float w = -0.7f;
Assert.Equal(520, new Bgra4444(x, y, z, w).PackedValue);
// Test ordering
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new Bgra4444(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 34, 0, 136 });
new Bgra4444(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 34, 0, 136, 0 });
new Bgra4444(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 136, 0, 34 });
new Bgra4444(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 136, 0, 34, 0 });
}
[Fact]
public void Bgra5551()
{
// Test the limits.
Assert.Equal(0x0, new Bgra5551(Vector4.Zero).PackedValue);
Assert.Equal(0xFFFF, new Bgra5551(Vector4.One).PackedValue);
// Test ToVector4
Assert.True(Equal(Vector4.Zero, new Bgra5551(Vector4.Zero).ToVector4()));
Assert.True(Equal(Vector4.One, new Bgra5551(Vector4.One).ToVector4()));
// Test clamping.
Assert.Equal(Vector4.Zero, new Bgra5551(Vector4.One * -1234.0f).ToVector4());
Assert.Equal(Vector4.One, new Bgra5551(Vector4.One * 1234.0f).ToVector4());
// Test Ordering
float x = 0x1a;
float y = 0x16;
float z = 0xd;
float w = 0x1;
Assert.Equal(0xeacd, new Bgra5551(x / 0x1f, y / 0x1f, z / 0x1f, w).PackedValue);
x = 0.1f;
y = -0.3f;
z = 0.5f;
w = -0.7f;
Assert.Equal(3088, new Bgra5551(x, y, z, w).PackedValue);
// Test ordering
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new Bgra5551(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 24, 0, 131 });
new Bgra5551(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 24, 0, 131, 0 });
new Bgra5551(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 131, 0, 24 });
new Bgra5551(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 131, 0, 24, 0 });
}
[Fact]
public void Byte4()
{
// Test the limits.
Assert.Equal((uint)0x0, new Byte4(Vector4.Zero).PackedValue);
Assert.Equal(0xFFFFFFFF, new Byte4(Vector4.One * 255).PackedValue);
// Test ToVector4.
Assert.True(Equal(Vector4.One * 255, new Byte4(Vector4.One * 255).ToVector4()));
Assert.True(Equal(Vector4.Zero, new Byte4(Vector4.Zero).ToVector4()));
Assert.True(Equal(Vector4.UnitX * 255, new Byte4(Vector4.UnitX * 255).ToVector4()));
Assert.True(Equal(Vector4.UnitY * 255, new Byte4(Vector4.UnitY * 255).ToVector4()));
Assert.True(Equal(Vector4.UnitZ * 255, new Byte4(Vector4.UnitZ * 255).ToVector4()));
Assert.True(Equal(Vector4.UnitW * 255, new Byte4(Vector4.UnitW * 255).ToVector4()));
// Test clamping.
Assert.True(Equal(Vector4.Zero, new Byte4(Vector4.One * -1234.0f).ToVector4()));
Assert.True(Equal(Vector4.One * 255, new Byte4(Vector4.One * 1234.0f).ToVector4()));
// Test ordering
float x = 0x2d;
float y = 0x36;
float z = 0x7b;
float w = 0x1a;
Assert.Equal((uint)0x1a7b362d, new Byte4(x, y, z, w).PackedValue);
x = 127.5f;
y = -12.3f;
z = 0.5f;
w = -0.7f;
Assert.Equal((uint)128, new Byte4(x, y, z, w).PackedValue);
// Test ordering
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new Byte4(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 128, 0, 0 });
new Byte4(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 128, 0, 0, 0 });
new Byte4(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 0, 0, 128 });
new Byte4(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
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 });
}
[Fact]
public void HalfVector2()
{
// Test PackedValue
Assert.Equal(0u, new HalfVector2(Vector2.Zero).PackedValue);
Assert.Equal(1006648320u, new HalfVector2(Vector2.One).PackedValue);
Assert.Equal(3033345638u, new HalfVector2(0.1f, -0.3f).PackedValue);
// Test ToVector2
Assert.True(Equal(Vector2.Zero, new HalfVector2(Vector2.Zero).ToVector2()));
Assert.True(Equal(Vector2.One, new HalfVector2(Vector2.One).ToVector2()));
Assert.True(Equal(Vector2.UnitX, new HalfVector2(Vector2.UnitX).ToVector2()));
Assert.True(Equal(Vector2.UnitY, new HalfVector2(Vector2.UnitY).ToVector2()));
// Test ordering
float x = .5F;
float y = .25F;
Assert.True(Equal(new Vector4(x, y, 0, 1), new HalfVector2(x, y).ToVector4()));
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new HalfVector2(x, y).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 128, 64, 0 });
new HalfVector2(x, y).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 128, 64, 0, 255 });
new HalfVector2(x, y).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 0, 64, 128 });
new HalfVector2(x, y).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 0, 64, 128, 255 });
}
[Fact]
public void HalfVector4()
{
// Test PackedValue
Assert.Equal(0uL, new HalfVector4(Vector4.Zero).PackedValue);
Assert.Equal(4323521613979991040uL, new HalfVector4(Vector4.One).PackedValue);
Assert.Equal(13547034390470638592uL, new HalfVector4(-Vector4.One).PackedValue);
Assert.Equal(15360uL, new HalfVector4(Vector4.UnitX).PackedValue);
Assert.Equal(1006632960uL, new HalfVector4(Vector4.UnitY).PackedValue);
Assert.Equal(65970697666560uL, new HalfVector4(Vector4.UnitZ).PackedValue);
Assert.Equal(4323455642275676160uL, new HalfVector4(Vector4.UnitW).PackedValue);
Assert.Equal(4035285078724390502uL, new HalfVector4(0.1f, 0.3f, 0.4f, 0.5f).PackedValue);
// Test ToVector4
Assert.True(Equal(Vector4.Zero, new HalfVector4(Vector4.Zero).ToVector4()));
Assert.True(Equal(Vector4.One, new HalfVector4(Vector4.One).ToVector4()));
Assert.True(Equal(-Vector4.One, new HalfVector4(-Vector4.One).ToVector4()));
Assert.True(Equal(Vector4.UnitX, new HalfVector4(Vector4.UnitX).ToVector4()));
Assert.True(Equal(Vector4.UnitY, new HalfVector4(Vector4.UnitY).ToVector4()));
Assert.True(Equal(Vector4.UnitZ, new HalfVector4(Vector4.UnitZ).ToVector4()));
Assert.True(Equal(Vector4.UnitW, new HalfVector4(Vector4.UnitW).ToVector4()));
// Test ordering
float x = .25F;
float y = .5F;
float z = .75F;
float w = 1F;
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new HalfVector4(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 64, 128, 191 });
new HalfVector4(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 64, 128, 191, 255 });
new HalfVector4(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 191, 128, 64 });
new HalfVector4(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 191, 128, 64, 255 });
}
[Fact]
public void NormalizedByte2()
{
// Test PackedValue
Assert.Equal(0x0, new NormalizedByte2(Vector2.Zero).PackedValue);
Assert.Equal(0x7F7F, new NormalizedByte2(Vector2.One).PackedValue);
Assert.Equal(0x8181, new NormalizedByte2(-Vector2.One).PackedValue);
// Test ToVector2
Assert.True(Equal(Vector2.One, new NormalizedByte2(Vector2.One).ToVector2()));
Assert.True(Equal(Vector2.Zero, new NormalizedByte2(Vector2.Zero).ToVector2()));
Assert.True(Equal(-Vector2.One, new NormalizedByte2(-Vector2.One).ToVector2()));
Assert.True(Equal(Vector2.One, new NormalizedByte2(Vector2.One * 1234.0f).ToVector2()));
Assert.True(Equal(-Vector2.One, new NormalizedByte2(Vector2.One * -1234.0f).ToVector2()));
// Test ToVector4
Assert.True(Equal(new Vector4(1, 1, 0, 1), new NormalizedByte2(Vector2.One).ToVector4()));
Assert.True(Equal(new Vector4(0, 0, 0, 1), new NormalizedByte2(Vector2.Zero).ToVector4()));
// Test Ordering
float x = 0.1f;
float y = -0.3f;
Assert.Equal(0xda0d, new NormalizedByte2(x, y).PackedValue);
NormalizedByte2 n = new NormalizedByte2();
n.PackFromBytes(141, 90, 0, 0);
Assert.Equal(0xda0d, n.PackedValue);
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new NormalizedByte2(x, y).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 141, 90, 0 });
new NormalizedByte2(x, y).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 141, 90, 0, 255 });
new NormalizedByte2(x, y).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 0, 90, 141 });
new NormalizedByte2(x, y).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 0, 90, 141, 255 });
}
[Fact]
public void NormalizedByte4()
{
// Test PackedValue
Assert.Equal((uint)0x0, new NormalizedByte4(Vector4.Zero).PackedValue);
Assert.Equal((uint)0x7F7F7F7F, new NormalizedByte4(Vector4.One).PackedValue);
Assert.Equal(0x81818181, new NormalizedByte4(-Vector4.One).PackedValue);
// Test ToVector4
Assert.True(Equal(Vector4.One, new NormalizedByte4(Vector4.One).ToVector4()));
Assert.True(Equal(Vector4.Zero, new NormalizedByte4(Vector4.Zero).ToVector4()));
Assert.True(Equal(-Vector4.One, new NormalizedByte4(-Vector4.One).ToVector4()));
Assert.True(Equal(Vector4.One, new NormalizedByte4(Vector4.One * 1234.0f).ToVector4()));
Assert.True(Equal(-Vector4.One, new NormalizedByte4(Vector4.One * -1234.0f).ToVector4()));
// Test Ordering
float x = 0.1f;
float y = -0.3f;
float z = 0.5f;
float w = -0.7f;
Assert.Equal(0xA740DA0D, new NormalizedByte4(x, y, z, w).PackedValue);
NormalizedByte4 n = new NormalizedByte4();
n.PackFromBytes(141, 90, 192, 39);
Assert.Equal(0xA740DA0D, n.PackedValue);
Assert.Equal((uint)958796544, new NormalizedByte4(0.0008f, 0.15f, 0.30f, 0.45f).PackedValue);
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new NormalizedByte4(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 141, 90, 192 });
new NormalizedByte4(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 141, 90, 192, 39 });
new NormalizedByte4(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 192, 90, 141 });
new NormalizedByte4(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 192, 90, 141, 39 });
// http://community.monogame.net/t/normalizedbyte4-texture2d-gives-different-results-from-xna/8012/8
NormalizedByte4 r = new NormalizedByte4();
r.PackFromBytes(9, 115, 202, 127);
r.ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 });
r.PackedValue = 0x7FCA7309;
r.ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 });
}
[Fact]
public void NormalizedShort2()
{
Assert.Equal((uint)0x0, new NormalizedShort2(Vector2.Zero).PackedValue);
Assert.Equal((uint)0x7FFF7FFF, new NormalizedShort2(Vector2.One).PackedValue);
Assert.Equal(0x80018001, new NormalizedShort2(-Vector2.One).PackedValue);
Assert.True(Equal(Vector2.One, new NormalizedShort2(Vector2.One).ToVector2()));
Assert.True(Equal(Vector2.Zero, new NormalizedShort2(Vector2.Zero).ToVector2()));
Assert.True(Equal(-Vector2.One, new NormalizedShort2(-Vector2.One).ToVector2()));
Assert.True(Equal(Vector2.One, new NormalizedShort2(Vector2.One * 1234.0f).ToVector2()));
Assert.True(Equal(-Vector2.One, new NormalizedShort2(Vector2.One * -1234.0f).ToVector2()));
Assert.True(Equal(new Vector4(1, 1, 0, 1), (new NormalizedShort2(Vector2.One)).ToVector4()));
Assert.True(Equal(new Vector4(0, 0, 0, 1), (new NormalizedShort2(Vector2.Zero)).ToVector4()));
// Test Ordering
float x = 0.35f;
float y = -0.2f;
Assert.Equal(0xE6672CCC, new NormalizedShort2(x, y).PackedValue);
x = 0.1f;
y = -0.3f;
Assert.Equal(3650751693, new NormalizedShort2(x, y).PackedValue);
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
NormalizedShort2 n = new NormalizedShort2();
n.PackFromBytes(141, 90, 0, 0);
n.ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 141, 90, 0 });
// TODO: I don't think this can ever pass since the bytes are already truncated.
// Assert.Equal(3650751693, n.PackedValue);
new NormalizedShort2(x, y).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 141, 90, 0 });
new NormalizedShort2(x, y).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 141, 90, 0, 255 });
new NormalizedShort2(x, y).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 0, 90, 141 });
new NormalizedShort2(x, y).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 0, 90, 141, 255 });
}
[Fact]
public void NormalizedShort4()
{
// Test PackedValue
Assert.Equal((ulong)0x0, new NormalizedShort4(Vector4.Zero).PackedValue);
Assert.Equal((ulong)0x7FFF7FFF7FFF7FFF, new NormalizedShort4(Vector4.One).PackedValue);
Assert.Equal(0x8001800180018001, new NormalizedShort4(-Vector4.One).PackedValue);
// Test ToVector4
Assert.True(Equal(Vector4.One, new NormalizedShort4(Vector4.One).ToVector4()));
Assert.True(Equal(Vector4.Zero, new NormalizedShort4(Vector4.Zero).ToVector4()));
Assert.True(Equal(-Vector4.One, new NormalizedShort4(-Vector4.One).ToVector4()));
Assert.True(Equal(Vector4.One, new NormalizedShort4(Vector4.One * 1234.0f).ToVector4()));
Assert.True(Equal(-Vector4.One, new NormalizedShort4(Vector4.One * -1234.0f).ToVector4()));
// Test Ordering
float x = 0.1f;
float y = -0.3f;
float z = 0.5f;
float w = -0.7f;
Assert.Equal(0xa6674000d99a0ccd, new NormalizedShort4(x, y, z, w).PackedValue);
Assert.Equal((ulong)4150390751449251866, new NormalizedShort4(0.0008f, 0.15f, 0.30f, 0.45f).PackedValue);
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new NormalizedShort4(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 141, 90, 192 });
new NormalizedShort4(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 141, 90, 192, 39 });
new NormalizedShort4(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 192, 90, 141 });
new NormalizedShort4(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 192, 90, 141, 39 });
NormalizedShort4 r = new NormalizedShort4();
r.PackFromBytes(9, 115, 202, 127);
r.ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 });
}
[Fact]
public void Rg32()
{
// Test the limits.
Assert.Equal((uint)0x0, new Rg32(Vector2.Zero).PackedValue);
Assert.Equal(0xFFFFFFFF, new Rg32(Vector2.One).PackedValue);
// Test ToVector2
Assert.True(Equal(Vector2.Zero, new Rg32(Vector2.Zero).ToVector2()));
Assert.True(Equal(Vector2.One, new Rg32(Vector2.One).ToVector2()));
// Test clamping.
Assert.True(Equal(Vector2.Zero, new Rg32(Vector2.One * -1234.0f).ToVector2()));
Assert.True(Equal(Vector2.One, new Rg32(Vector2.One * 1234.0f).ToVector2()));
// Test Ordering
float x = 0xb6dc;
float y = 0xA59f;
Assert.Equal(0xa59fb6dc, new Rg32(x / 0xffff, y / 0xffff).PackedValue);
x = 0.1f;
y = -0.3f;
Assert.Equal((uint)6554, new Rg32(x, y).PackedValue);
// Test ordering
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new Rg32(x, y).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 25, 0, 0 });
new Rg32(x, y).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 25, 0, 0, 255 });
new Rg32(x, y).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 0, 0, 25 });
new Rg32(x, y).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 0, 0, 25, 255 });
}
[Fact]
public void Rgba1010102()
{
// Test the limits.
Assert.Equal((uint)0x0, new Rgba1010102(Vector4.Zero).PackedValue);
Assert.Equal(0xFFFFFFFF, new Rgba1010102(Vector4.One).PackedValue);
// Test ToVector4
Assert.True(Equal(Vector4.Zero, new Rgba1010102(Vector4.Zero).ToVector4()));
Assert.True(Equal(Vector4.One, new Rgba1010102(Vector4.One).ToVector4()));
// Test clamping.
Assert.True(Equal(Vector4.Zero, new Rgba1010102(Vector4.One * -1234.0f).ToVector4()));
Assert.True(Equal(Vector4.One, new Rgba1010102(Vector4.One * 1234.0f).ToVector4()));
// Test Ordering
float x = 0x2db;
float y = 0x36d;
float z = 0x3b7;
float w = 0x1;
Assert.Equal((uint)0x7B7DB6DB, new Rgba1010102(x / 0x3ff, y / 0x3ff, z / 0x3ff, w / 3).PackedValue);
x = 0.1f;
y = -0.3f;
z = 0.5f;
w = -0.7f;
Assert.Equal((uint)536871014, new Rgba1010102(x, y, z, w).PackedValue);
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new Rgba1010102(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 25, 0, 128 });
new Rgba1010102(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 25, 0, 128, 0 });
new Rgba1010102(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 128, 0, 25 });
new Rgba1010102(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 128, 0, 25, 0 });
// Alpha component accuracy will be awful.
Rgba1010102 r = new Rgba1010102();
r.PackFromBytes(25, 0, 128, 0);
r.ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 25, 0, 128, 0 });
}
[Fact]
public void Rgba64()
{
// Test the limits.
Assert.Equal((ulong)0x0, new Rgba64(Vector4.Zero).PackedValue);
Assert.Equal(0xFFFFFFFFFFFFFFFF, new Rgba64(Vector4.One).PackedValue);
// Test ToVector4
Assert.True(Equal(Vector4.Zero, new Rgba64(Vector4.Zero).ToVector4()));
Assert.True(Equal(Vector4.One, new Rgba64(Vector4.One).ToVector4()));
// Test clamping.
Assert.True(Equal(Vector4.Zero, new Rgba64(Vector4.One * -1234.0f).ToVector4()));
Assert.True(Equal(Vector4.One, new Rgba64(Vector4.One * 1234.0f).ToVector4()));
// Test data ordering
Assert.Equal(0xC7AD8F5C570A1EB8, new Rgba64(((float)0x1EB8) / 0xffff, ((float)0x570A) / 0xffff, ((float)0x8F5C) / 0xffff, ((float)0xC7AD) / 0xffff).PackedValue);
Assert.Equal(0xC7AD8F5C570A1EB8, new Rgba64(0.12f, 0.34f, 0.56f, 0.78f).PackedValue);
float x = 0.08f;
float y = 0.15f;
float z = 0.30f;
float w = 0.45f;
Assert.Equal((ulong)0x73334CCC2666147B, new Rgba64(x, y, z, w).PackedValue);
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new Rgba64(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 20, 38, 76 });
new Rgba64(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 20, 38, 76, 115 });
new Rgba64(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 76, 38, 20 });
new Rgba64(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 76, 38, 20, 115 });
Rgba64 r = new Rgba64();
r.PackFromBytes(20, 38, 76, 115);
r.ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 20, 38, 76, 115 });
}
[Fact]
public void Short2()
{
// Test the limits.
Assert.Equal((uint)0x0, new Short2(Vector2.Zero).PackedValue);
Assert.Equal((uint)0x7FFF7FFF, new Short2(Vector2.One * 0x7FFF).PackedValue);
Assert.Equal(0x80008000, new Short2(Vector2.One * -0x8000).PackedValue);
// Test ToVector2.
Assert.True(Equal(Vector2.One * 0x7FFF, new Short2(Vector2.One * 0x7FFF).ToVector2()));
Assert.True(Equal(Vector2.Zero, new Short2(Vector2.Zero).ToVector2()));
Assert.True(Equal(Vector2.One * -0x8000, new Short2(Vector2.One * -0x8000).ToVector2()));
Assert.True(Equal(Vector2.UnitX * 0x7FFF, new Short2(Vector2.UnitX * 0x7FFF).ToVector2()));
Assert.True(Equal(Vector2.UnitY * 0x7FFF, new Short2(Vector2.UnitY * 0x7FFF).ToVector2()));
// Test clamping.
Assert.True(Equal(Vector2.One * 0x7FFF, new Short2(Vector2.One * 1234567.0f).ToVector2()));
Assert.True(Equal(Vector2.One * -0x8000, new Short2(Vector2.One * -1234567.0f).ToVector2()));
// Test ToVector4.
Assert.True(Equal(new Vector4(0x7FFF, 0x7FFF, 0, 1), (new Short2(Vector2.One * 0x7FFF)).ToVector4()));
Assert.True(Equal(new Vector4(0, 0, 0, 1), (new Short2(Vector2.Zero)).ToVector4()));
Assert.True(Equal(new Vector4(-0x8000, -0x8000, 0, 1), (new Short2(Vector2.One * -0x8000)).ToVector4()));
// Test ordering
float x = 0x2db1;
float y = 0x361d;
Assert.Equal((uint)0x361d2db1, new Short2(x, y).PackedValue);
x = 127.5f;
y = -5.3f;
Assert.Equal(4294639744, new Short2(x, y).PackedValue);
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new Short2(x, y).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 128, 127, 0 });
new Short2(x, y).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 128, 127, 0, 255 });
new Short2(x, y).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 0, 127, 128 });
new Short2(x, y).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 0, 127, 128, 255 });
Short2 r = new Short2();
r.PackFromBytes(20, 38, 0, 255);
r.ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 });
}
[Fact]
public void Short4()
{
// Test the limits.
Assert.Equal((ulong)0x0, new Short4(Vector4.Zero).PackedValue);
Assert.Equal((ulong)0x7FFF7FFF7FFF7FFF, new Short4(Vector4.One * 0x7FFF).PackedValue);
Assert.Equal(0x8000800080008000, new Short4(Vector4.One * -0x8000).PackedValue);
// Test ToVector4.
Assert.Equal(Vector4.One * 0x7FFF, new Short4(Vector4.One * 0x7FFF).ToVector4());
Assert.Equal(Vector4.Zero, new Short4(Vector4.Zero).ToVector4());
Assert.Equal(Vector4.One * -0x8000, new Short4(Vector4.One * -0x8000).ToVector4());
Assert.Equal(Vector4.UnitX * 0x7FFF, new Short4(Vector4.UnitX * 0x7FFF).ToVector4());
Assert.Equal(Vector4.UnitY * 0x7FFF, new Short4(Vector4.UnitY * 0x7FFF).ToVector4());
Assert.Equal(Vector4.UnitZ * 0x7FFF, new Short4(Vector4.UnitZ * 0x7FFF).ToVector4());
Assert.Equal(Vector4.UnitW * 0x7FFF, new Short4(Vector4.UnitW * 0x7FFF).ToVector4());
// Test clamping.
Assert.Equal(Vector4.One * 0x7FFF, new Short4(Vector4.One * 1234567.0f).ToVector4());
Assert.Equal(Vector4.One * -0x8000, new Short4(Vector4.One * -1234567.0f).ToVector4());
// Test Ordering
float x = 0.1f;
float y = -0.3f;
float z = 0.5f;
float w = -0.7f;
Assert.Equal(18446462598732840960, new Short4(x, y, z, w).PackedValue);
x = 11547;
y = 12653;
z = 29623;
w = 193;
Assert.Equal((ulong)0x00c173b7316d2d1b, new Short4(x, y, z, w).PackedValue);
byte[] rgb = new byte[3];
byte[] rgba = new byte[4];
byte[] bgr = new byte[3];
byte[] bgra = new byte[4];
new Short4(x, y, z, w).ToBytes(rgb, 0, ComponentOrder.XYZ);
Assert.Equal(rgb, new byte[] { 172, 177, 243 });
new Short4(x, y, z, w).ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 172, 177, 243, 128 });
new Short4(x, y, z, w).ToBytes(bgr, 0, ComponentOrder.ZYX);
Assert.Equal(bgr, new byte[] { 243, 177, 172 });
new Short4(x, y, z, w).ToBytes(bgra, 0, ComponentOrder.ZYXW);
Assert.Equal(bgra, new byte[] { 243, 177, 172, 128 });
Short4 r = new Short4();
r.PackFromBytes(20, 38, 0, 255);
r.ToBytes(rgba, 0, ComponentOrder.XYZW);
Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 });
}
// Comparison helpers with small tolerance to allow for floating point rounding during computations.
public static bool Equal(float a, float b)
{
return Math.Abs(a - b) < 1e-5;
}
public static bool Equal(Vector2 a, Vector2 b)
{
return Equal(a.X, b.X) && Equal(a.Y, b.Y);
}
public static bool Equal(Vector3 a, Vector3 b)
{
return Equal(a.X, b.X) && Equal(a.Y, b.Y) && Equal(a.Z, b.Z);
}
public static bool Equal(Vector4 a, Vector4 b)
{
return Equal(a.X, b.X) && Equal(a.Y, b.Y) && Equal(a.Z, b.Z) && Equal(a.W, b.W);
}
}
}

17
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -7,6 +7,7 @@ namespace ImageSharp.Tests
{
using System;
using System.IO;
using System.Numerics;
using Xunit;
@ -53,6 +54,22 @@ namespace ImageSharp.Tests
{
Image image = file.CreateImage();
// Image<Bgr565, ushort> image = file.CreateImage().To<Bgr565, ushort>();
// Image<Bgra4444, ushort> image = file.CreateImage().To<Bgra4444, ushort>();
// Image<Bgra5551, ushort> image = file.CreateImage().To<Bgra5551, ushort>();
// Image<Byte4, uint> image = file.CreateImage().To<Byte4, uint>();
// Image<HalfSingle, ushort> image = file.CreateImage().To<HalfSingle, ushort>();
// Image<HalfVector2, uint> image = file.CreateImage().To<HalfVector2, uint>();
// Image<HalfVector4, ulong> image = file.CreateImage().To<HalfVector4, ulong>();
// Image<Rg32, uint> image = file.CreateImage().To<Rg32, uint>();
// Image<Rgba1010102, uint> image = file.CreateImage().To<Rgba1010102, uint>();
// Image<Rgba64, ulong> image = file.CreateImage().To<Rgba64, ulong>();
// Image<NormalizedByte2, ushort> image = file.CreateImage().To<NormalizedByte2, ushort>();
// Image<NormalizedByte4, uint> image = file.CreateImage().To<NormalizedByte4, uint>();
// Image<NormalizedShort2, uint> image = file.CreateImage().To<NormalizedShort2, uint>();
// Image<NormalizedShort4, ulong> image = file.CreateImage().To<NormalizedShort4, ulong>();
// Image<Short2, uint> image = file.CreateImage().To<Short2, uint>();
// Image<Short4, ulong> image = file.CreateImage().To<Short4, ulong>();
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}"))
{
image.Save(output);

Loading…
Cancel
Save