mirror of https://github.com/SixLabors/ImageSharp
7 changed files with 345 additions and 9 deletions
@ -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) / 255F); |
|||
} |
|||
|
|||
/// <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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue