Compare commits

..
11 Commits
Author SHA1 Message Date
ds5678 19b2469595 Bump version 2024-08-03 10:25:22 -07:00
ds5678 c5a2382733 Fix bc7 mode reading regression 2024-08-02 20:56:59 -07:00
ds5678 9ce8311e1b Bc optimizations 2024-08-01 23:33:37 -07:00
ds5678 e39b439ccd Use UInt128 in BitStream 2024-08-01 23:09:20 -07:00
ds5678 f2da43bcbb BitStream optimizations 2024-08-01 22:43:08 -07:00
ds5678 128eb58864 Use unsigned integers for Bc decoding 2024-08-01 22:34:02 -07:00
dependabot[bot] 9f7a61172a Bump NUnit3TestAdapter from 4.5.0 to 4.6.0
Bumps [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases)
- [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V4.5.0...V4.6.0)

---
updated-dependencies:
- dependency-name: NUnit3TestAdapter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-07-25 21:26:33 -07:00
dependabot[bot] 93c177232f Bump NUnit.Analyzers from 4.1.0 to 4.2.0
Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/nunit/nunit.analyzers/releases)
- [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.md)
- [Commits](https://github.com/nunit/nunit.analyzers/compare/4.1.0...4.2.0)

---
updated-dependencies:
- dependency-name: NUnit.Analyzers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-07-09 21:24:01 -07:00
dependabot[bot] 8b62957cdb Bump Microsoft.NET.Test.Sdk from 17.9.0 to 17.10.0
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.9.0 to 17.10.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.9.0...v17.10.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-07-09 21:23:49 -07:00
dependabot[bot] 95ecf5b1ac Bump actions/setup-dotnet from 4.0.0 to 4.0.1
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v4.0.0...v4.0.1)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-07-09 21:23:32 -07:00
ds5678 f8d17ad290 Use ReadOnlySpan for constant data 2024-04-14 20:24:19 -07:00
11 changed files with 220 additions and 128 deletions

No files matched your search

+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/[email protected].0
uses: actions/[email protected].1
with:
dotnet-version: 8.0.x
- name: Restore dependencies
@@ -5,10 +5,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.1.0">
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
+16 -14
View File
@@ -883,7 +883,7 @@ namespace AssetRipper.TextureDecoder.Astc
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static int GetBits(ReadOnlySpan<byte> input, int bit, int len)
{
ReadOnlySpan<byte> slice = input[(bit / 8)..];
ReadOnlySpan<byte> slice = input[(bit / 8)..];
int bitBuffer;
if (slice.Length >= sizeof(int))
{
@@ -894,7 +894,7 @@ namespace AssetRipper.TextureDecoder.Astc
Span<byte> temp = stackalloc byte[sizeof(int)];
slice.CopyTo(temp);
bitBuffer = BinaryPrimitives.ReadInt32LittleEndian(temp);
}
}
return (bitBuffer >> (bit % 8)) & ((1 << len) - 1);
}
@@ -996,10 +996,12 @@ namespace AssetRipper.TextureDecoder.Astc
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static byte SelectColor(int v0, int v1, int weight)
{
return (byte)(((((v0 << 8 | v0) * (64 - weight) + (v1 << 8 | v1) * weight + 32) >> 6) * 255 + 32768) / 65536);
const int SignAdjustment = short.MaxValue + 1;
const int MaxValue = ushort.MaxValue + 1;
return (byte)(((((v0 << 8 | v0) * (64 - weight) + (v1 << 8 | v1) * weight + 32) >> 6) * byte.MaxValue + SignAdjustment) / MaxValue);
}
private static readonly byte[] BitReverseTable =
private static ReadOnlySpan<byte> BitReverseTable =>
[
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
@@ -1019,15 +1021,15 @@ namespace AssetRipper.TextureDecoder.Astc
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
];
private static readonly int[] WeightPrecTableA = [0, 0, 0, 3, 0, 5, 3, 0, 0, 0, 5, 3, 0, 5, 3, 0];
private static readonly int[] WeightPrecTableB = [0, 0, 1, 0, 2, 0, 1, 3, 0, 0, 1, 2, 4, 2, 3, 5];
private static ReadOnlySpan<int> WeightPrecTableA => [0, 0, 0, 3, 0, 5, 3, 0, 0, 0, 5, 3, 0, 5, 3, 0];
private static ReadOnlySpan<int> WeightPrecTableB => [0, 0, 1, 0, 2, 0, 1, 3, 0, 0, 1, 2, 4, 2, 3, 5];
private static readonly int[] CemTableA = [0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 0, 0];
private static readonly int[] CemTableB = [8, 6, 5, 7, 5, 4, 6, 4, 3, 5, 3, 2, 4, 2, 1, 3, 1, 2, 1];
private static ReadOnlySpan<int> CemTableA => [0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 0, 0];
private static ReadOnlySpan<int> CemTableB => [8, 6, 5, 7, 5, 4, 6, 4, 3, 5, 3, 2, 4, 2, 1, 3, 1, 2, 1];
private static readonly int[] DImt = [0, 2, 4, 5, 7];
private static readonly int[] DImq = [0, 3, 5];
private static readonly int[] DITritsTable =
private static ReadOnlySpan<int> DImt => [0, 2, 4, 5, 7];
private static ReadOnlySpan<int> DImq => [0, 3, 5];
private static ReadOnlySpan<int> DITritsTable =>
[
0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2,
0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1,
@@ -1035,14 +1037,14 @@ namespace AssetRipper.TextureDecoder.Astc
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
];
private static readonly int[] DIQuintsTable =
private static ReadOnlySpan<int> DIQuintsTable =>
[
0, 1, 2, 3, 4, 0, 4, 4, 0, 1, 2, 3, 4, 1, 4, 4, 0, 1, 2, 3, 4, 2, 4, 4, 0, 1, 2, 3, 4, 3, 4, 4, 0, 1, 2, 3, 4, 0, 4, 0, 0, 1, 2, 3, 4, 1, 4, 1, 0, 1, 2, 3, 4, 2, 4, 2, 0, 1, 2, 3, 4, 3, 4, 3, 0, 1, 2, 3, 4, 0, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 0, 1, 2, 3, 4, 2, 2, 3, 0, 1, 2, 3, 4, 3, 2, 3, 0, 1, 2, 3, 4, 0, 0, 1, 0, 1, 2, 3, 4, 1, 0, 1, 0, 1, 2, 3, 4, 2, 0, 1, 0, 1, 2, 3, 4, 3, 0, 1,
0, 0, 0, 0, 0, 4, 4, 4, 1, 1, 1, 1, 1, 4, 4, 4, 2, 2, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 4, 0, 4, 1, 1, 1, 1, 1, 4, 1, 4, 2, 2, 2, 2, 2, 4, 2, 4, 3, 3, 3, 3, 3, 4, 3, 4, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3,
0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 3, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4,
];
private static readonly int[] DETritsTable = [0, 204, 93, 44, 22, 11, 5];
private static readonly int[] DEQuintsTable = [0, 113, 54, 26, 13, 6];
private static ReadOnlySpan<int> DETritsTable => [0, 204, 93, 44, 22, 11, 5];
private static ReadOnlySpan<int> DEQuintsTable => [0, 113, 54, 26, 13, 6];
}
}
+2 -2
View File
@@ -211,7 +211,7 @@ internal static class Bc6hTables
]
];
internal static int[] AWeight3 { get; } = [0, 9, 18, 27, 37, 46, 55, 64];
internal static ReadOnlySpan<int> AWeight3 => [0, 9, 18, 27, 37, 46, 55, 64];
internal static int[] AWeight4 { get; } = [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64];
internal static ReadOnlySpan<int> AWeight4 => [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64];
}
+3 -3
View File
@@ -789,11 +789,11 @@ internal static class Bc7Tables
]
];
internal static int[] AWeight2 { get; } = [0, 21, 43, 64];
internal static ReadOnlySpan<int> AWeight2 => [0, 21, 43, 64];
internal static int[] AWeight3 { get; } = [0, 9, 18, 27, 37, 46, 55, 64];
internal static ReadOnlySpan<int> AWeight3 => [0, 9, 18, 27, 37, 46, 55, 64];
internal static int[] AWeight4 { get; } = [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64];
internal static ReadOnlySpan<int> AWeight4 => [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64];
internal const byte bcdec_bc7_sModeHasPBits = 0b11001011;
}
+98 -58
View File
@@ -34,14 +34,10 @@ internal static class BcHelpers
public static void DecompressBc6h(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock, int destinationPitch, bool isSigned)
{
BitStream bstream = new()
{
low = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock),
high = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock.Slice(sizeof(ulong)))
};
Span<int> r = stackalloc int[4]; // wxyz
Span<int> g = stackalloc int[4];
Span<int> b = stackalloc int[4];
BitStream bstream = new(compressedBlock);
Span<uint> r = stackalloc uint[4]; // wxyz
Span<uint> g = stackalloc uint[4];
Span<uint> b = stackalloc uint[4];
int decompressedOffset = 0;
@@ -52,7 +48,7 @@ internal static class BcHelpers
int mode;
//modes >= 11 (10 in my code) are using 0 one, others will read it from the bitstream
int partition;
uint partition;
switch (ReadBc6hModeBits(ref bstream))
{
@@ -477,7 +473,7 @@ internal static class BcHelpers
// Mode 11 (like Mode 10) does not use delta compression,
// and instead stores both color endpoints explicitly.
if ((mode != 9 && mode != 10) || isSigned)
if ((mode is not 9 and not 10) || isSigned)
{
for (int i = 1; i < (numPartitions + 1) * 2; ++i)
{
@@ -487,7 +483,7 @@ internal static class BcHelpers
}
}
if (mode != 9 && mode != 10)
if (mode is not 9 and not 10)
{
for (int i = 1; i < (numPartitions + 1) * 2; ++i)
{
@@ -520,7 +516,7 @@ internal static class BcHelpers
}
partitionSet &= 0x01;
int index = bstream.ReadBits(indexBits);
int index = bstream.ReadBits(indexBits).ToSigned();
int ep_i = (partitionSet * 2);
@@ -542,12 +538,12 @@ internal static class BcHelpers
decompressedOffset += destinationPitch * sizeof(ushort);
}
static int ReadBc6hModeBits(ref BitStream bstream)
static uint ReadBc6hModeBits(ref BitStream bstream)
{
int twoBits = bstream.ReadBits(2);
uint twoBits = bstream.ReadBits(2);
if (twoBits > 1)
{
int threeBits = bstream.ReadBits(3);
uint threeBits = bstream.ReadBits(3);
return (threeBits << 2) | twoBits;
}
else
@@ -559,12 +555,8 @@ internal static class BcHelpers
public static void DecompressBc7(ReadOnlySpan<byte> compressedBlock, Span<byte> decompressedBlock, int destinationPitch)
{
BitStream bstream = new()
{
low = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock),
high = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock.Slice(sizeof(ulong)))
};
Span2D<int> endpoints = new(stackalloc int[6 * 4], 6, 4);
BitStream bstream = new(compressedBlock);
Span2D<uint> endpoints = new(stackalloc uint[6 * 4], 6, 4);
Span2D<int> indices = new(stackalloc int[4 * 4], 4, 4);
int decompressedOffset = 0;
@@ -589,29 +581,36 @@ internal static class BcHelpers
return;
}
int partition = 0;
uint partition = 0;
int numPartitions = 1;
int rotation = 0;
int indexSelectionBit = 0;
uint rotation = 0;
uint indexSelectionBit = 0;
if (mode == 0 || mode == 1 || mode == 2 || mode == 3 || mode == 7)
switch (mode)
{
numPartitions = (mode == 0 || mode == 2) ? 3 : 2;
partition = bstream.ReadBits((mode == 0) ? 4 : 6);
case 0:
numPartitions = 3;
partition = bstream.ReadBits(4);
break;
case 1 or 3 or 7:
numPartitions = 2;
partition = bstream.ReadBits(6);
break;
case 2:
numPartitions = 3;
partition = bstream.ReadBits(6);
break;
case 4:
rotation = bstream.ReadBits(2);
indexSelectionBit = bstream.ReadBit();
break;
case 5:
rotation = bstream.ReadBits(2);
break;
}
int numEndpoints = numPartitions * 2;
if (mode == 4 || mode == 5)
{
rotation = bstream.ReadBits(2);
if (mode == 4)
{
indexSelectionBit = bstream.ReadBit();
}
}
// Extract endpoints
// RGB
for (int i = 0; i < 3; ++i)
@@ -646,8 +645,8 @@ internal static class BcHelpers
// if P-bit is shared
if (mode == 1)
{
int i = bstream.ReadBit();
int j = bstream.ReadBit();
uint i = bstream.ReadBit();
uint j = bstream.ReadBit();
// rgb component-wise insert pbits
for (int k = 0; k < 3; ++k)
@@ -663,7 +662,7 @@ internal static class BcHelpers
// unique P-bit per endpoint
for (int i = 0; i < numEndpoints; ++i)
{
int j = bstream.ReadBit();
uint j = bstream.ReadBit();
for (int k = 0; k < 4; ++k)
{
endpoints[i, k] |= j;
@@ -705,10 +704,25 @@ internal static class BcHelpers
}
// Determine weights tables
int indexBits = (mode == 0 || mode == 1) ? 3 : ((mode == 6) ? 4 : 2);
int indexBits2 = (mode == 4) ? 3 : ((mode == 5) ? 2 : 0);
int[] weights = (indexBits == 2) ? Bc7Tables.AWeight2 : ((indexBits == 3) ? Bc7Tables.AWeight3 : Bc7Tables.AWeight4);
int[] weights2 = (indexBits2 == 2) ? Bc7Tables.AWeight2 : Bc7Tables.AWeight3;
int indexBits = mode switch
{
0 or 1 => 3,
6 => 4,
_ => 2
};
int indexBits2 = mode switch
{
4 => 3,
5 => 2,
_ => 0
};
ReadOnlySpan<int> weights = indexBits switch
{
2 => Bc7Tables.AWeight2,
3 => Bc7Tables.AWeight3,
_ => Bc7Tables.AWeight4
};
ReadOnlySpan<int> weights2 = (indexBits2 == 2) ? Bc7Tables.AWeight2 : Bc7Tables.AWeight3;
// Quite inconvenient that indices aren't interleaved so we have to make 2 passes here
// Pass #1: collecting color indices
@@ -726,7 +740,7 @@ internal static class BcHelpers
indexBits--;
}
indices[i, j] = bstream.ReadBits(indexBits);
indices[i, j] = bstream.ReadBits(indexBits).ToSigned();
}
}
@@ -742,10 +756,10 @@ internal static class BcHelpers
int index = indices[i, j];
int r;
int g;
int b;
int a;
uint r;
uint g;
uint b;
uint a;
if (indexBits2 == 0)
{
@@ -756,7 +770,7 @@ internal static class BcHelpers
}
else
{
int index2 = bstream.ReadBits((i | j) != 0 ? indexBits2 : (indexBits2 - 1));
int index2 = bstream.ReadBits((i | j) != 0 ? indexBits2 : (indexBits2 - 1)).ToSigned();
// The index value for interpolating color comes from the secondary index bits for the texel
// if the mode has an index selection bit and its value is one, and from the primary index bits otherwise.
// The alpha index comes from the secondary index bits if the block has a secondary index
@@ -811,12 +825,13 @@ internal static class BcHelpers
static int ReadBc7Mode(ref BitStream bstream)
{
int i = 0;
while (i < 8 && bstream.ReadBit() == 0)
{
i++;
}
return i;
// Read bits until we find a non-zero bit, up to 8 bits.
// The mode is the number of bits read.
uint eightBits = bstream.PeakBits(8);
int zeros = BitOperations.TrailingZeroCount(eightBits);
int bitCount = zeros >= 8 ? 8 : zeros + 1;
bstream.Advance(bitCount);
return int.Min(zeros, 8);
}
}
@@ -949,6 +964,11 @@ internal static class BcHelpers
return (val << (32 - bits)) >> (32 - bits);
}
private static uint ExtendSign(uint val, int bits)
{
return ExtendSign(val.ToSigned(), bits).ToUnsigned();
}
public static int TransformInverse(int val, int a0, int bits, bool isSigned)
{
// If the precision of A0 is "p" bits, then the transform algorithm is:
@@ -961,6 +981,11 @@ internal static class BcHelpers
return val;
}
private static uint TransformInverse(uint val, uint a0, int bits, bool isSigned)
{
return TransformInverse(val.ToSigned(), a0.ToSigned(), bits, isSigned).ToUnsigned();
}
/// <summary>
/// Essentially copy-paste from documentation
/// </summary>
@@ -1028,11 +1053,21 @@ internal static class BcHelpers
return unq;
}
private static uint Unquantize(uint val, int bits, bool isSigned)
{
return Unquantize(val.ToSigned(), bits, isSigned).ToUnsigned();
}
public static int Interpolate(int a, int b, ReadOnlySpan<int> weights, int index)
{
return ((a * (64 - weights[index])) + (b * weights[index]) + 32) >> 6;
}
private static uint Interpolate(uint a, uint b, ReadOnlySpan<int> weights, int index)
{
return Interpolate(a.ToSigned(), b.ToSigned(), weights, index).ToUnsigned();
}
public static ushort FinishUnquantize(int val, bool isSigned)
{
if (!isSigned)
@@ -1041,7 +1076,7 @@ internal static class BcHelpers
}
else
{
val = (val < 0) ? -(((-val) * 31) >> 5) : (val * 31) >> 5; // scale the magnitude by 31 / 32
val = (val < 0) ? (((-val) * 31) >> 5) : (val * 31) >> 5; // scale the magnitude by 31 / 32
int s = 0;
if (val < 0)
{
@@ -1052,7 +1087,12 @@ internal static class BcHelpers
}
}
public static void SwapValues(ref int a, ref int b)
private static ushort FinishUnquantize(uint val, bool isSigned)
{
return FinishUnquantize(val.ToSigned(), isSigned);
}
public static void SwapValues(ref uint a, ref uint b)
{
(a, b) = (b, a);
}
+54 -28
View File
@@ -1,27 +1,59 @@
namespace AssetRipper.TextureDecoder.Bc;
using System.Buffers.Binary;
internal struct BitStream
namespace AssetRipper.TextureDecoder.Bc;
internal ref struct BitStream
{
public ulong low;
public ulong high;
public int ReadBits(int numBits)
private UInt128 value;
private readonly uint BottomBits
{
uint mask = (uint)((1 << numBits) - 1);
// Read the low N bits
uint bits = (uint)(this.low & mask);
this.low >>= numBits;
// Put the low N bits of "high" into the high 64-N bits of "low".
this.low |= (this.high & mask) << ((sizeof(ulong) * 8) - numBits);
this.high >>= numBits;
return (int)bits;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
get => unchecked((uint)this.value);
}
public int ReadBit()
public BitStream(UInt128 value)
{
return this.ReadBits(1);
this.value = value;
}
public BitStream(ReadOnlySpan<byte> span) : this(BinaryPrimitives.ReadUInt128LittleEndian(span))
{
}
public BitStream(ulong low, ulong high)
{
value = new UInt128(high, low);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public readonly uint PeakBits(int numBits)
{
uint mask = (1u << numBits) - 1u;
uint bits = BottomBits & mask;
return bits;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Advance(int numBits)
{
this.value >>= numBits;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public uint ReadBits(int numBits)
{
uint bits = PeakBits(numBits);
Advance(numBits);
return bits;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public uint ReadBit()
{
uint result = BottomBits & 1u;
Advance(1);
return result;
}
/// <summary>
@@ -29,17 +61,11 @@ internal struct BitStream
/// </summary>
/// <param name="numBits"></param>
/// <returns></returns>
public int ReadBitsReversed(int numBits)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public uint ReadBitsReversed(int numBits)
{
int bits = this.ReadBits(numBits);
uint bits = this.ReadBits(numBits);
// Reverse the bits.
int result = 0;
while (numBits-- != 0)
{
result <<= 1;
result |= bits & 1;
bits >>= 1;
}
return result;
return bits.ReverseBits() >> (32 - numBits);
}
}
@@ -0,0 +1,24 @@
namespace AssetRipper.TextureDecoder.Bc;
internal static class NumericExtensions
{
public static uint ReverseBits(this uint n)
{
n = (n >> 1) & 0x55555555 | (n << 1) & 0xAAAAAAAA;
n = (n >> 2) & 0x33333333 | (n << 2) & 0xCCCCCCCC;
n = (n >> 4) & 0x0F0F0F0F | (n << 4) & 0xF0F0F0F0;
n = (n >> 8) & 0x00FF00FF | (n << 8) & 0xFF00FF00;
n = (n >> 16) & 0x0000FFFF | (n << 16) & 0xFFFF0000;
return n;
}
public static uint ToUnsigned(this int value)
{
return unchecked((uint)value);
}
public static int ToSigned(this uint value)
{
return unchecked((int)value);
}
}
+9 -9
View File
@@ -805,7 +805,7 @@ namespace AssetRipper.TextureDecoder.Etc
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void DecodeEac11Block(Span<byte> output, int @base, int ti, int mul, ulong l)
{
ReadOnlySpan<sbyte> table = Etc2AlphaModTable.AsSpan(ti * 8, 8);
ReadOnlySpan<sbyte> table = Etc2AlphaModTable.Slice(ti * 8, 8);
for (int i = 0; i < 16; i++, l >>= 3)
{
int val = @base + mul * table[unchecked((int)(l & 0b111))];
@@ -849,14 +849,14 @@ namespace AssetRipper.TextureDecoder.Etc
return data[7] | (uint)data[6] << 8 | (uint)data[5] << 16 | (uint)data[4] << 24 | (ulong)data[3] << 32 | (ulong)data[2] << 40;
}
private static readonly byte[] WriteOrderTable = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15];
private static readonly byte[] WriteOrderTableRev = [15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0];
private static readonly int[] Etc1SubblockTable =
private static ReadOnlySpan<byte> WriteOrderTable => [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15];
private static ReadOnlySpan<byte> WriteOrderTableRev => [15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0];
private static ReadOnlySpan<int> Etc1SubblockTable =>
[
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
];
private static readonly int[] Etc1ModifierTable =
private static ReadOnlySpan<int> Etc1ModifierTable =>
[
2, 8, -2, -8,
5, 17, -5, -17,
@@ -867,7 +867,7 @@ namespace AssetRipper.TextureDecoder.Etc
33, 106, -33, -106,
47, 183, -47, -183,
];
private static readonly int[] PunchthroughModifierTable =
private static ReadOnlySpan<int> PunchthroughModifierTable =>
[
0, 8, 0, -8,
0, 17, 0, -17,
@@ -878,8 +878,8 @@ namespace AssetRipper.TextureDecoder.Etc
0, 106, 0, -106,
0, 183, 0, -183,
];
private static readonly byte[] Etc2DistanceTable = [3, 6, 11, 16, 23, 32, 41, 64];
private static readonly sbyte[] Etc2AlphaModTable =
private static ReadOnlySpan<byte> Etc2DistanceTable => [3, 6, 11, 16, 23, 32, 41, 64];
private static ReadOnlySpan<sbyte> Etc2AlphaModTable =>
[
-3, -6, -9, -15, 2, 5, 8, 14,
-3, -7, -10, -13, 2, 6, 9, 12,
@@ -898,6 +898,6 @@ namespace AssetRipper.TextureDecoder.Etc
-4, -6, -8, -9, 3, 5, 7, 8,
-3, -5, -7, -9, 2, 4, 6, 8,
];
private static readonly uint[] PunchthroughMaskTable = [0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF];
private static ReadOnlySpan<uint> PunchthroughMaskTable => [0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF];
}
}
@@ -217,36 +217,36 @@ namespace AssetRipper.TextureDecoder.Pvrtc
int modVal;
if (modulationModes[y * 16 + x] == 0)
{
modVal = m_repVals0[modulationVals[y * 16 + x]];
modVal = RepVals0[modulationVals[y * 16 + x]];
}
else if (do2bitMode)
{
// if this is a stored value
if (((x ^ y) & 1) == 0)
{
modVal = m_repVals0[modulationVals[y * 16 + x]];
modVal = RepVals0[modulationVals[y * 16 + x]];
}
// else average from the neighbours if H&V interpolation..
else if (modulationModes[y * 16 + x] == 1)
{
modVal = (m_repVals0[modulationVals[(y - 1) * 16 + x]] + m_repVals0[modulationVals[(y + 1) * 16 + x]] +
m_repVals0[modulationVals[y * 16 + x - 1]] + m_repVals0[modulationVals[y * 16 + x + 1]] + 2) / 4;
modVal = (RepVals0[modulationVals[(y - 1) * 16 + x]] + RepVals0[modulationVals[(y + 1) * 16 + x]] +
RepVals0[modulationVals[y * 16 + x - 1]] + RepVals0[modulationVals[y * 16 + x + 1]] + 2) / 4;
}
// else if H-Only
else if (modulationModes[y * 16 + x] == 2)
{
modVal = (m_repVals0[modulationVals[y * 16 + x - 1]] + m_repVals0[modulationVals[y * 16 + x + 1]] + 1) / 2;
modVal = (RepVals0[modulationVals[y * 16 + x - 1]] + RepVals0[modulationVals[y * 16 + x + 1]] + 1) / 2;
}
// else it's V-Only
else
{
modVal = (m_repVals0[modulationVals[(y - 1) * 16 + x]] + m_repVals0[modulationVals[(y + 1) * 16 + x]] + 1) / 2;
modVal = (RepVals0[modulationVals[(y - 1) * 16 + x]] + RepVals0[modulationVals[(y + 1) * 16 + x]] + 1) / 2;
}
}
// else it's 4BPP and PT encoding
else
{
modVal = m_repVals1[modulationVals[y * 16 + x]];
modVal = RepVals1[modulationVals[y * 16 + x]];
doPT = modulationVals[y * 16 + x] == PTIndex;
}
@@ -487,7 +487,7 @@ namespace AssetRipper.TextureDecoder.Pvrtc
private const int BlockX2bpp = 8;
private const int BlockX4bpp = 4;
private static readonly int[] m_repVals0 = [0, 3, 5, 8];
private static readonly int[] m_repVals1 = [0, 4, 4, 8];
private static ReadOnlySpan<int> RepVals0 => [0, 3, 5, 8];
private static ReadOnlySpan<int> RepVals1 => [0, 4, 4, 8];
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
<IsAotCompatible>true</IsAotCompatible>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<Version>2.2.0</Version>
<Version>2.2.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<Copyright>Copyright © ds5678</Copyright>
<Authors>ds5678</Authors>