Skip to content

Commit 1ba622f

Browse files
committed
Implement ASTC decoding for KTX1
1 parent b2ed142 commit 1ba622f

128 files changed

Lines changed: 2679 additions & 547 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/ImageSharp.Textures/Common/Helpers/FloatHelper.cs

Lines changed: 67 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,70 +3,85 @@
33

44
using System.Runtime.CompilerServices;
55

6-
namespace SixLabors.ImageSharp.Textures.Common.Helpers
6+
namespace SixLabors.ImageSharp.Textures.Common.Helpers;
7+
8+
internal static class FloatHelper
79
{
8-
internal static class FloatHelper
9-
{
10-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
11-
public static float UnpackFloat32ToFloat(uint value) => Unsafe.As<uint, float>(ref value);
10+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
11+
public static float UnpackFloat32ToFloat(uint value) => Unsafe.As<uint, float>(ref value);
1212

13-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
14-
public static uint PackFloatToFloat32(float value) => Unsafe.As<float, uint>(ref value);
13+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
14+
public static uint PackFloatToFloat32(float value) => Unsafe.As<float, uint>(ref value);
1515

16-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
17-
public static float UnpackFloat16ToFloat(uint value)
18-
{
19-
uint result =
20-
((value >> 15) << 31) |
21-
((((value >> 10) & 0x1f) - 15 + 127) << 23) |
22-
((value & 0x3ff) << 13);
23-
return Unsafe.As<uint, float>(ref result);
24-
}
16+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
17+
public static float UnpackFloat16ToFloat(uint value) => (float)BitConverter.UInt16BitsToHalf((ushort)value);
2518

26-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
27-
public static uint PackFloatToFloat16(float value)
28-
{
29-
uint temp = Unsafe.As<float, uint>(ref value);
30-
return
31-
((temp >> 31) << 15) |
32-
((((temp >> 23) & 0xff) - 127 + 15) << 10) |
33-
((temp & 0x7fffff) >> 13);
34-
}
19+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
20+
public static uint PackFloatToFloat16(float value) => BitConverter.HalfToUInt16Bits((Half)value);
3521

36-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
37-
public static float UnpackFloat10ToFloat(uint value)
38-
{
39-
uint result =
40-
((((value >> 5) & 0x1f) - 10 + 127) << 23) |
41-
((value & 0x1f) << 18);
42-
return Unsafe.As<uint, float>(ref result);
43-
}
22+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
23+
public static float UnpackFloat10ToFloat(uint value, uint bias = 10)
24+
{
25+
uint e = (value >> 5) & 0x1Fu;
26+
uint m = value & 0x1Fu;
4427

45-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
46-
public static uint PackFloatToFloat10(float value)
28+
return e switch
4729
{
48-
uint temp = Unsafe.As<float, uint>(ref value);
49-
return
50-
((((temp >> 23) & 0xff) - 127 + 10) << 5) |
51-
((temp & 0x7fffff) >> 18);
52-
}
30+
// Zero
31+
0 when m == 0 => 0f,
32+
33+
// Denormalized
34+
0 => m * BitConverter.UInt32BitsToSingle((128u - bias - 5u) << 23),
35+
36+
// Inf/NaN
37+
31 => BitConverter.UInt32BitsToSingle((0xFFu << 23) | (m << 18)),
5338

54-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
55-
public static float UnpackFloat11ToFloat(uint value)
39+
// Normalized
40+
_ => BitConverter.UInt32BitsToSingle(((e + 127u - bias) << 23) | (m << 18)),
41+
};
42+
}
43+
44+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
45+
public static uint PackFloatToFloat10(float value)
46+
{
47+
uint temp = Unsafe.As<float, uint>(ref value);
48+
return
49+
((((temp >> 23) & 0xff) - 127 + 10) << 5) |
50+
((temp & 0x7fffff) >> 18);
51+
}
52+
53+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
54+
public static float UnpackFloat11ToFloat(uint value, uint bias = 11)
55+
{
56+
uint e = (value >> 6) & 0x1Fu;
57+
uint m = value & 0x3Fu;
58+
59+
if (e == 0)
5660
{
57-
uint result =
58-
((((value >> 6) & 0x1f) - 11 + 127) << 23) |
59-
((value & 0x3f) << 17);
60-
return Unsafe.As<uint, float>(ref result);
61+
if (m == 0)
62+
{
63+
return 0f;
64+
}
65+
66+
// Denormalized: m * 2^(1 - bias - 6)
67+
return m * BitConverter.UInt32BitsToSingle((128u - bias - 6u) << 23);
6168
}
6269

63-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
64-
public static uint PackFloatToFloat11(float value)
70+
if (e == 31)
6571
{
66-
uint temp = Unsafe.As<float, uint>(ref value);
67-
return
68-
((((temp >> 23) & 0xff) - 127 + 11) << 6) |
69-
((temp & 0x7fffff) >> 17);
72+
uint ieee = (0xFFu << 23) | (m << 17);
73+
return BitConverter.UInt32BitsToSingle(ieee);
7074
}
75+
76+
return BitConverter.UInt32BitsToSingle(((e + 127u - bias) << 23) | (m << 17));
77+
}
78+
79+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
80+
public static uint PackFloatToFloat11(float value)
81+
{
82+
uint temp = Unsafe.As<float, uint>(ref value);
83+
return
84+
((((temp >> 23) & 0xff) - 127 + 11) << 6) |
85+
((temp & 0x7fffff) >> 17);
7186
}
7287
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Buffers.Binary;
5+
using System.Runtime.InteropServices;
6+
using SixLabors.ImageSharp.Textures.Common.Exceptions;
7+
8+
namespace SixLabors.ImageSharp.Textures.Formats.Ktx;
9+
10+
/// <summary>
11+
/// Handles endianness conversions for KTX texture data. Reads header fields using the file's
12+
/// declared endianness and optionally swaps pixel bytes when the file and host disagree.
13+
/// </summary>
14+
internal sealed class EndianHandler
15+
{
16+
private readonly bool isFileLittleEndian;
17+
private readonly bool swapPixelData;
18+
19+
public EndianHandler(bool isFileLittleEndian)
20+
{
21+
this.isFileLittleEndian = isFileLittleEndian;
22+
this.swapPixelData = isFileLittleEndian != BitConverter.IsLittleEndian;
23+
}
24+
25+
public uint ReadUInt32(ReadOnlySpan<byte> buffer)
26+
=> this.isFileLittleEndian
27+
? BinaryPrimitives.ReadUInt32LittleEndian(buffer)
28+
: BinaryPrimitives.ReadUInt32BigEndian(buffer);
29+
30+
public void ConvertPixelData(Span<byte> data, uint typeSize)
31+
{
32+
if (!this.swapPixelData)
33+
{
34+
return;
35+
}
36+
37+
if (typeSize == 2)
38+
{
39+
if (data.Length % 2 != 0)
40+
{
41+
throw new TextureFormatException("Pixel data length is not a multiple of 2 for a 16-bit typed format");
42+
}
43+
44+
BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<byte, ushort>(data), MemoryMarshal.Cast<byte, ushort>(data));
45+
}
46+
else if (typeSize == 4)
47+
{
48+
if (data.Length % 4 != 0)
49+
{
50+
throw new TextureFormatException("Pixel data length is not a multiple of 4 for a 32-bit typed format");
51+
}
52+
53+
BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<byte, uint>(data), MemoryMarshal.Cast<byte, uint>(data));
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)