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