Compare commits

...
217 Commits
Author SHA1 Message Date
Jeremy Pritts e7b9db0dd6 Bump version 2023-12-12 02:25:17 -05:00
dependabot[bot] 1e7ef0c44c Bump NUnit.Analyzers from 3.9.0 to 3.10.0
Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 3.9.0 to 3.10.0.
- [Release notes](https://github.com/nunit/nunit.analyzers/releases)
- [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.txt)
- [Commits](https://github.com/nunit/nunit.analyzers/compare/3.9.0...3.10.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-12-10 21:32:47 -05:00
dependabot[bot] 0f2a6fdec1 Bump BenchmarkDotNet from 0.13.10 to 0.13.11
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.10 to 0.13.11.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.10...v0.13.11)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-12-10 21:32:38 -05:00
dependabot[bot] 058a14953f Bump NUnit from 4.0.0 to 4.0.1
Bumps [NUnit](https://github.com/nunit/nunit) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/nunit/nunit/releases)
- [Changelog](https://github.com/nunit/nunit/blob/master/CHANGES.md)
- [Commits](https://github.com/nunit/nunit/compare/v4.0.0...v4.0.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-12-10 21:32:30 -05:00
dependabot[bot] 94f1a9ab8e Bump actions/setup-dotnet from 3.2.0 to 4.0.0
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3.2.0 to 4.0.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3.2.0...v4.0.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-12-05 00:59:49 -05:00
Jeremy Pritts 5953a2baf7 Use Span in EtcDecoder
* Resolves #58
2023-11-28 23:58:15 -05:00
Jeremy Pritts 4b398e6c94 Use collection expressions 2023-11-28 23:40:37 -05:00
Jeremy Pritts a25a42dbd4 Add Etc correctness tests
* Resolves #57
2023-11-28 23:34:43 -05:00
Jeremy Pritts e9674d1c39 Use cached delegates for EtcTests 2023-11-28 22:59:21 -05:00
Jeremy Pritts 05eaeb81dc Refactor EtcTests 2023-11-28 22:44:43 -05:00
Jeremy Pritts 76a8b2f837 Update PvrtcTests to assess correctness 2023-11-28 22:30:15 -05:00
dependabot[bot] c113649b6e Bump NUnit from 3.14.0 to 4.0.0
Bumps [NUnit](https://github.com/nunit/nunit) from 3.14.0 to 4.0.0.
- [Release notes](https://github.com/nunit/nunit/releases)
- [Changelog](https://github.com/nunit/nunit/blob/master/CHANGES.md)
- [Commits](https://github.com/nunit/nunit/compare/v3.14.0...v4.0.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-11-26 21:30:43 -05:00
Jeremy Pritts 07002c77ee Static properties for black and white 2023-11-17 11:44:08 -05:00
Jeremy Pritts bed45ee60e Rename IColorBase to IColor 2023-11-17 11:01:39 -05:00
Jeremy Pritts d8918771a9 Change IColor filename 2023-11-17 10:47:33 -05:00
Jeremy Pritts 5dba4cdb19 Use collection expressions 2023-11-17 10:21:55 -05:00
Jeremy Pritts b1803e8a35 Greatly reduce Bc7 allocations 2023-11-17 09:47:56 -05:00
Jeremy Pritts 5ec29c6a82 Span2D ref structs 2023-11-17 09:46:10 -05:00
Jeremy Pritts c6a1240499 Bc improvements:
* Expose BlockSize as public constants
* Refactor Bc7 to use more constants
* Add a partial block test
2023-11-16 20:12:13 -05:00
Jeremy Pritts 12c6750a88 Make Astc use safe code 2023-11-16 19:58:47 -05:00
Jeremy Pritts 5dacd489d0 Use InlineArray for PvrtcDecoder 2023-11-16 19:18:20 -05:00
Jeremy Pritts 509a567aa5 Publish AOT 2023-11-16 18:55:03 -05:00
Jeremy Pritts 1f1dd55e85 .NET 8 2023-11-16 18:42:55 -05:00
dependabot[bot] 5e28554f00 Bump Microsoft.SourceLink.GitHub from 1.1.1 to 8.0.0
Bumps [Microsoft.SourceLink.GitHub](https://github.com/dotnet/sourcelink) from 1.1.1 to 8.0.0.
- [Release notes](https://github.com/dotnet/sourcelink/releases)
- [Commits](https://github.com/dotnet/sourcelink/commits)

---
updated-dependencies:
- dependency-name: Microsoft.SourceLink.GitHub
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-11-15 22:49:24 -05:00
dependabot[bot] d4cd1d5560 Bump Microsoft.NET.Test.Sdk from 17.7.2 to 17.8.0
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.7.2 to 17.8.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.7.2...v17.8.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]>
2023-11-08 22:53:08 -05:00
dependabot[bot] 9ae8296532 Bump NUnit from 3.13.3 to 3.14.0
Bumps [NUnit](https://github.com/nunit/nunit) from 3.13.3 to 3.14.0.
- [Release notes](https://github.com/nunit/nunit/releases)
- [Changelog](https://github.com/nunit/nunit/blob/master/CHANGES.md)
- [Commits](https://github.com/nunit/nunit/compare/v3.13.3...v3.14.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-11-05 21:31:55 -05:00
dependabot[bot] 637a53fc6f Bump BenchmarkDotNet from 0.13.9 to 0.13.10
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.9 to 0.13.10.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.9...v0.13.10)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-11-01 21:57:58 -04:00
dependabot[bot] 53f177cae3 Bump NUnit.Analyzers from 3.8.0 to 3.9.0
Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 3.8.0 to 3.9.0.
- [Release notes](https://github.com/nunit/nunit.analyzers/releases)
- [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.txt)
- [Commits](https://github.com/nunit/nunit.analyzers/compare/3.8.0...3.9.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-10-29 21:27:13 -04:00
Jeremy Pritts c57303ff6b Simplify NumericConversion for signed integers 2023-10-06 23:22:05 -04:00
Jeremy Pritts bd834212ad Support nint, nuint, and NFloat 2023-10-06 23:16:16 -04:00
Jeremy Pritts f0bdada27c global using InteropServices 2023-10-06 22:59:04 -04:00
Jeremy Pritts 4a386d57d7 Change rgb SetConvertedChannels extension to use ColorRGB explicitly 2023-10-06 14:35:23 -04:00
Jeremy Pritts d96d8e6c35 Generic color struct support for DXT3 and DXT5 2023-10-06 14:21:31 -04:00
Jeremy Pritts c16bec4431 Generic color struct constructors 2023-10-06 13:56:34 -04:00
Jeremy Pritts 548d5e8aec Generic DXT1 support 2023-10-06 13:09:17 -04:00
Jeremy Pritts 9e21272b2b Remove method implementation attributes in Yuy2Decoder 2023-10-06 13:03:03 -04:00
Jeremy Pritts df1ab3c1e4 Simplify Yuy2Decoder to make nongeneric use generic 2023-10-06 12:25:39 -04:00
Jeremy Pritts 7c30eb44ee Parameterize BcTests 2023-10-06 12:04:22 -04:00
dependabot[bot] e32f934043 Bump BenchmarkDotNet from 0.13.8 to 0.13.9
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.8 to 0.13.9.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.8...v0.13.9)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-10-05 22:30:10 -04:00
Jeremy Pritts 79295aa258 Bump version to 2.0.0 2023-09-30 12:44:40 -04:00
Jeremy Pritts b721294e62 Use safe min and max methods for generic colors 2023-09-30 12:20:30 -04:00
Jeremy Pritts f3f7b69954 Color.GetChannelCount 2023-09-30 12:16:45 -04:00
Jeremy Pritts 6980a32759 More efficient Yuy2 decoding
* Rename ColorExtensions to Color
* Remove constraints on NumericConversion.GetXXXValue()
2023-09-30 12:13:49 -04:00
Jeremy Pritts 81e2a38829 Refactor Color Metadata
* Remove RgbaAttribute
* Add IColorBase interface
* Add ChannelType static property
* Add ChannelsAreFullyUtilized static property
2023-09-30 11:39:15 -04:00
Jeremy Pritts d496a13d63 Rename type parameters 2023-09-30 11:02:04 -04:00
Jeremy Pritts 850f7f335a Formatting 2023-09-30 10:49:53 -04:00
Jeremy Pritts 78a8f18375 Only use safe code for AtcDecoder 2023-09-30 10:31:17 -04:00
dependabot[bot] 696a677bc3 Bump NUnit.Analyzers from 3.7.0 to 3.8.0
Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 3.7.0 to 3.8.0.
- [Release notes](https://github.com/nunit/nunit.analyzers/releases)
- [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.txt)
- [Commits](https://github.com/nunit/nunit.analyzers/compare/3.7.0...3.8.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-09-26 01:53:06 -04:00
Jeremy Pritts 1ef781f072 Astc span output 2023-09-17 00:42:01 -04:00
Jeremy Pritts 2a41577d28 Astc BitTransferSigned use ref instead of pointer 2023-09-17 00:26:00 -04:00
Jeremy Pritts b3371b34d3 Use span for some Astc helper methods 2023-09-17 00:19:01 -04:00
Jeremy Pritts 05c961fcac AstcDecoder: replace input pointers with span 2023-09-16 23:56:00 -04:00
Jeremy Pritts 061968eeb8 Bump nuget references 2023-09-16 19:09:49 -04:00
dependabot[bot] 9a02c7149b Bump BenchmarkDotNet from 0.13.7 to 0.13.8
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.7 to 0.13.8.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.7...v0.13.8)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-09-10 22:17:37 -04:00
Jeremy Pritts 705dc88bbb Use more Span in AstcDecoder 2023-09-05 23:48:38 -04:00
Jeremy Pritts 3187e92d6b ref BlockData instead of BlockData* 2023-09-05 23:38:29 -04:00
Jeremy Pritts b4863dcae7 Use less pointers in AstcDecoder 2023-09-05 23:22:01 -04:00
Jeremy Pritts d416ad0318 Astc tests 2023-09-05 23:19:30 -04:00
Jeremy Pritts 8220d27370 Android Textures 2023-09-05 23:18:58 -04:00
Jeremy Pritts 43d7db671e Project for paths 2023-09-05 21:44:59 -04:00
dependabot[bot] 6fb2c430af Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-09-04 22:21:42 -04:00
dependabot[bot] 14f22a1b28 Bump Microsoft.NET.Test.Sdk from 17.7.1 to 17.7.2
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.7.1 to 17.7.2.
- [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.7.1...v17.7.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-08-29 21:34:49 -04:00
dependabot[bot] 52d6613263 Bump Microsoft.NET.Test.Sdk from 17.7.0 to 17.7.1
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.7.0 to 17.7.1.
- [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.7.0...v17.7.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-08-16 22:16:19 -04:00
dependabot[bot] 301212b278 Bump BenchmarkDotNet from 0.13.6 to 0.13.7
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.6 to 0.13.7.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.6...v0.13.7)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-08-06 22:00:39 -04:00
dependabot[bot] 07812e1f48 Bump Microsoft.NET.Test.Sdk from 17.6.3 to 17.7.0
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.3 to 17.7.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.6.3...v17.7.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]>
2023-08-06 21:55:12 -04:00
Jeremy Pritts 16cdf61406 Editor config 2023-08-02 20:17:34 -04:00
Jeremy Pritts e5da67340b Aggressive Optimization 2023-08-02 20:14:28 -04:00
Jeremy Pritts 750dae931e Remove test method in NumericConversion 2023-08-02 20:05:12 -04:00
Jeremy Pritts f05609904a Revert to double computations in ColorRGB9e5 2023-08-02 20:03:19 -04:00
Jeremy Pritts f90871bd03 Support Int128 and UInt128 2023-08-02 20:00:50 -04:00
Jeremy Pritts c578ab204b Remove ColorRGB32Half
* Related: #17
2023-08-02 17:27:57 -04:00
Jeremy Pritts 699e437511 Higher precision for ColorRGB9e5 2023-08-02 17:20:04 -04:00
dependabot[bot] 8b7644d2bf Bump BenchmarkDotNet from 0.13.5 to 0.13.6
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.5 to 0.13.6.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.5...v0.13.6)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-08-02 11:56:34 -04:00
Jeremy Pritts aea54da07a Generic Color Structs
* Resolves #42
* Removes many of the nongeneric structs
* Unit tests for ColorRGB32Half are disabled (#17)
2023-08-02 03:52:34 -04:00
Jeremy Pritts 18a5b27333 Fix bug in NumericConversion 2023-08-02 01:33:37 -04:00
Jeremy Pritts ddcb97be28 Support int, uint, long, ulong, and decimal in IColor<T> 2023-08-01 21:51:29 -04:00
Jeremy Pritts 959f6584b2 Remove HalfConstants 2023-08-01 17:58:11 -04:00
Jeremy Pritts 8f4390a315 Apply readonly to color properties and methods 2023-08-01 16:50:17 -04:00
Jeremy Pritts ccd0276d99 Fix build error 2023-08-01 16:41:39 -04:00
Jeremy Pritts 4064c799a7 Mark some methods as safe 2023-08-01 10:01:34 -04:00
Jeremy Pritts 093a810e30 DirectBitmap Improvements:
* Make readonly
* Cache ImageWriter instance
* Use IColor<T> static properties
2023-08-01 09:59:02 -04:00
Jeremy Pritts 20c835e821 Add static abstract properties to IColor<T> 2023-08-01 09:54:32 -04:00
Jeremy Pritts 243caa5bf8 .NET 7 only 2023-08-01 09:45:05 -04:00
Jeremy Pritts 958b831205 Bump version and add source link 2023-07-01 14:22:50 -04:00
Jeremy Pritts 2085dbcce4 Add methods to DirectBitmap 2023-07-01 14:16:50 -04:00
Jeremy Pritts d6ad4d5a6f More ETC memory safety 2023-07-01 14:01:24 -04:00
Jeremy Pritts 1a5ca1405e Validate ASTC block dimensions 2023-07-01 13:56:53 -04:00
Jeremy Pritts d5da591f92 Use PolySharp instead of Nullable and IsExternalInit 2023-07-01 12:27:04 -04:00
Jeremy Pritts dd129401c7 Use ArrayPool when decompressing Bc4 and Bc5 2023-06-30 00:51:57 -04:00
Jeremy Pritts 6855b6b287 Decompress Bc6h as Half instead of float:
* Half is the "natural" decompression result
* Avoids converting pixels twice
2023-06-30 00:34:28 -04:00
Jeremy Pritts 0274cb1a05 Use BinaryPrimitives instead of MemoryMarshal 2023-06-29 23:55:58 -04:00
Jeremy Pritts e3e7779739 Small optimizations:
* Make mode a readonly variable
* Cache ActualBitsCount
* Unquantize in a separate pass
2023-06-29 23:36:34 -04:00
Jeremy Pritts 40326e20bc Move CompressedSize methods and BlockSize constants 2023-06-29 22:57:18 -04:00
Jeremy Pritts fa94774143 Separate BcDecoder into 7 classes 2023-06-29 22:45:23 -04:00
Jeremy Pritts 58b113e1e0 Remove some unnecessary variable assignments 2023-06-29 22:28:59 -04:00
Jeremy Pritts 862995196f Use stackalloc in Bc6h 2023-06-29 22:21:45 -04:00
Jeremy Pritts 691796b919 Small refactor:
* Use properties instead of fields
* Add lengths to BC array initializers
2023-06-29 22:08:20 -04:00
dependabot[bot] a6f54dd9b1 Bump Microsoft.NET.Test.Sdk from 17.6.2 to 17.6.3
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.2 to 17.6.3.
- [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.6.2...v17.6.3)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-06-28 07:47:26 -04:00
Jeremy Pritts fe93d46a6c Resolve #19 2023-06-22 11:41:18 -04:00
Jeremy Pritts 7c6ac4ba0e Support double precision channels 2023-06-22 10:45:36 -04:00
Jeremy Pritts 977800b5bb Update csproj file 2023-06-21 09:59:10 -04:00
dependabot[bot] f9c8dd980f Bump Microsoft.NET.Test.Sdk from 17.6.1 to 17.6.2
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.6.1 to 17.6.2.
- [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.6.1...v17.6.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-06-06 22:06:06 -04:00
dependabot[bot] aea23c3be6 Bump Microsoft.NET.Test.Sdk from 17.5.0 to 17.6.1
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.5.0 to 17.6.1.
- [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.5.0...v17.6.1)

---
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]>
2023-06-01 22:31:56 -04:00
dependabot[bot] 6b65475bc1 Bump coverlet.collector from 3.2.0 to 6.0.0
Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 3.2.0 to 6.0.0.
- [Release notes](https://github.com/coverlet-coverage/coverlet/releases)
- [Commits](https://github.com/coverlet-coverage/coverlet/compare/v3.2.0...v6.0.0)

---
updated-dependencies:
- dependency-name: coverlet.collector
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-05-31 06:06:49 -04:00
dependabot[bot] 4835e38ec6 Bump NUnit3TestAdapter from 4.4.2 to 4.5.0
Bumps [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) from 4.4.2 to 4.5.0.
- [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases)
- [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V4.4.2...V4.5.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-05-31 06:04:34 -04:00
dependabot[bot] 874bef99eb Bump actions/setup-dotnet from 3.1.0 to 3.2.0
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3.1.0...v3.2.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-05-29 22:10:00 -04:00
dependabot[bot] 6908984ff2 Bump actions/setup-dotnet from 3.0.3 to 3.1.0
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3.0.3 to 3.1.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3.0.3...v3.1.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-05-25 23:14:49 -04:00
Jeremy Pritts f012b3a4d9 Add program for integer conversion
* Related to #19
2023-03-15 15:54:11 -04:00
dependabot[bot] 541176ad79 Bump NUnit.Analyzers from 3.6.0 to 3.6.1
Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 3.6.0 to 3.6.1.
- [Release notes](https://github.com/nunit/nunit.analyzers/releases)
- [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.txt)
- [Commits](https://github.com/nunit/nunit.analyzers/compare/3.6.0...3.6.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-03-15 12:56:53 -04:00
dependabot[bot] a44a788060 Bump NUnit3TestAdapter from 4.3.1 to 4.4.2
Bumps [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) from 4.3.1 to 4.4.2.
- [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases)
- [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V4.3.1...V4.4.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-03-01 22:19:35 -05:00
dependabot[bot] 5c937aca17 Bump Microsoft.NET.Test.Sdk from 17.4.1 to 17.5.0
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.1 to 17.5.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.4.1...v17.5.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]>
2023-02-22 13:59:17 -05:00
dependabot[bot] 04b086c372 Bump NUnit.Analyzers from 3.5.0 to 3.6.0
Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 3.5.0 to 3.6.0.
- [Release notes](https://github.com/nunit/nunit.analyzers/releases)
- [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.txt)
- [Commits](https://github.com/nunit/nunit.analyzers/compare/3.5.0...3.6.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2023-02-22 13:59:07 -05:00
dependabot[bot] 99b5d670f3 Bump BenchmarkDotNet from 0.13.4 to 0.13.5
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.4 to 0.13.5.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.4...v0.13.5)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-02-20 22:58:37 -05:00
dependabot[bot] 53beeff97e Bump BenchmarkDotNet from 0.13.3 to 0.13.4
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.3 to 0.13.4.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.3...v0.13.4)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-01-15 22:16:24 -05:00
dependabot[bot] c566ecc668 Bump BenchmarkDotNet from 0.13.2 to 0.13.3
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.2 to 0.13.3.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.2...v0.13.3)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-12-27 13:41:44 -05:00
dependabot[bot] 3d3746e2d7 Bump Microsoft.NET.Test.Sdk from 17.4.0 to 17.4.1
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.0 to 17.4.1.
- [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.4.0...v17.4.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-12-27 13:41:36 -05:00
ds5678 cd37722a53 Delete .gitattributes 2022-11-23 23:14:11 -05:00
Jeremy Pritts 7cea861443 Refactor source generation to include the RGB console method 2022-11-23 19:06:54 -05:00
Jeremy Pritts fb48fa3b94 Add back support for earlier .NET versions, which was previously removed in 92d1909562 2022-11-23 16:18:31 -05:00
dependabot[bot] a6bc637d47 Bump NUnit3TestAdapter from 4.3.0 to 4.3.1
Bumps [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases)
- [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V4.3.0...V4.3.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-11-20 21:39:16 -05:00
Jeremy Pritts ba40e2c7d7 Use .NET 7 in the GitHub workflow 2022-11-17 20:09:56 -05:00
Jeremy Pritts e2fba56e81 More optimized code generation:
* Use Half.One instead of (Half)1
* No generic instantiation for GetChannels or SetChannels
* In SetChannels, only set properties that are actually used
2022-11-17 19:47:13 -05:00
Jeremy Pritts 92d1909562 Bump to .NET 7 2022-11-17 19:27:19 -05:00
dependabot[bot] bb346aabbc Bump Microsoft.NET.Test.Sdk from 17.3.2 to 17.4.0
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.3.2 to 17.4.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.3.2...v17.4.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]>
2022-11-11 10:41:19 -05:00
Jeremy Pritts 18abd1731b Standardize naming for floating point rgb formats 2022-11-07 09:58:53 -05:00
Naomi b4ad9bcb39 Add R11G11B10FloatToBGRA32 (#17)
* Add R11G11B10FloatToBGRA32 and ColorRGB32Half

* Requested changes.

* spaces to tabs

* Assert.Multiple
2022-11-06 23:26:54 -05:00
Jeremy Pritts e4c873b3b0 Use unsigned bitwise logic in UInt4Pair 2022-11-05 12:30:59 -04:00
Jeremy Pritts 9c89b6fac5 Add a UInt4Pair struct to clean up code in ARGB16 and RGBA16 2022-11-05 12:26:53 -04:00
Jeremy Pritts ce581c0ea6 Lossless conversion tests 2022-11-05 12:00:14 -04:00
Jeremy Pritts b992959c24 Find ChannelType implicitly in GenerationData 2022-11-05 11:58:10 -04:00
Jeremy Pritts 07c653873b Add an RgbaAttribute to all RGB formats 2022-11-05 00:03:58 -04:00
Jeremy Pritts 03373afddb Clean up the color source generator 2022-11-04 23:41:15 -04:00
Jeremy Pritts 35ed50dc26 Signed RGB color support with new source generation for standard RGB formats 2022-11-04 21:06:28 -04:00
Jeremy Pritts a5cfc5cbf7 IColor.Convert extension method 2022-11-04 16:22:42 -04:00
Jeremy Pritts dc1ecc5d46 Add support for sbyte and short in IColor<T> 2022-11-04 16:02:42 -04:00
Jeremy Pritts 89b76dafd3 Throw invalid cast for invalid number conversion when compiling on Debug mode 2022-11-04 15:09:41 -04:00
dependabot[bot] 56ba562fd5 Bump coverlet.collector from 3.1.2 to 3.2.0
Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 3.1.2 to 3.2.0.
- [Release notes](https://github.com/coverlet-coverage/coverlet/releases)
- [Commits](https://github.com/coverlet-coverage/coverlet/commits/v3.2.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-10-30 22:23:03 -04:00
dependabot[bot] 1b2208b38a Bump NUnit3TestAdapter from 4.2.1 to 4.3.0
Bumps [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) from 4.2.1 to 4.3.0.
- [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases)
- [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V4.2.1...V4.3.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-10-30 22:22:37 -04:00
dependabot[bot] 28e3a22265 Bump actions/setup-dotnet from 3.0.2 to 3.0.3
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3.0.2...v3.0.3)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-10-27 21:28:15 -04:00
Jeremy Pritts 5705e12af0 Atc input span 2022-10-24 00:23:46 -04:00
dependabot[bot] 3e7eae579e Bump NUnit.Analyzers from 3.3.0 to 3.5.0
Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 3.3.0 to 3.5.0.
- [Release notes](https://github.com/nunit/nunit.analyzers/releases)
- [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.txt)
- [Commits](https://github.com/nunit/nunit.analyzers/compare/3.3.0...3.5.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-10-24 00:05:05 -04:00
Jeremy Pritts 7fb7530a37 Formatting 2022-10-24 00:03:49 -04:00
Jeremy Pritts b1dfd0b6d8 Inline variable declarations in BcHelpers 2022-10-23 23:51:03 -04:00
Jeremy Pritts 9259884731 Remove hacky C++ half to float conversion 2022-10-23 23:25:44 -04:00
Jeremy Pritts 4e1251649b Bc6h output span 2022-10-23 23:21:36 -04:00
Jeremy Pritts 26fcfb695e Bc6h input span 2022-10-23 22:58:26 -04:00
Jeremy Pritts 1c156c4cd3 Output span for Bc7 2022-10-23 22:45:18 -04:00
Jeremy Pritts c019bc5a04 Input span for Bc7 2022-10-23 22:38:03 -04:00
Jeremy Pritts 099daf3044 Make BcHelpers.SwapValues use ref instead of pointers 2022-10-23 22:30:23 -04:00
Jeremy Pritts 5de28c63c3 Use bool instead of int for isSigned in BC6H 2022-10-18 01:13:11 -04:00
dependabot[bot] f4d37ff16c Bump actions/setup-dotnet from 3.0.1 to 3.0.2
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3.0.1...v3.0.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-10-13 22:11:17 -04:00
Jeremy Pritts 61c3a861f7 Bump version 2022-10-13 00:43:56 -04:00
Jeremy Pritts 437db55ae8 update gitignore 2022-10-13 00:42:25 -04:00
dependabot[bot] 6ed0c07a83 Bump actions/setup-dotnet from 3.0.0 to 3.0.1
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3.0.0...v3.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]>
2022-10-04 22:42:28 -04:00
dependabot[bot] 54e3e20d0c Bump actions/setup-dotnet from 2 to 3.0.0
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 2 to 3.0.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v2...v3.0.0)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-09-29 23:07:12 -04:00
dependabot[bot] 1281377f2e Bump Microsoft.NET.Test.Sdk from 17.3.1 to 17.3.2
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.3.1 to 17.3.2.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.3.1...v17.3.2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-09-27 00:50:01 -04:00
dependabot[bot] 7693de3ef9 Bump Microsoft.NET.Test.Sdk from 17.3.0 to 17.3.1
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.3.0 to 17.3.1.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.3.0...v17.3.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-08-30 23:22:44 -04:00
dependabot[bot] 4040a0a7a7 Bump BenchmarkDotNet from 0.13.1 to 0.13.2
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.1 to 0.13.2.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.1...v0.13.2)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-08-29 10:59:25 -04:00
dependabot[bot] 07d932c1a1 Bump Microsoft.NET.Test.Sdk from 17.2.0 to 17.3.0
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.2.0 to 17.3.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.2.0...v17.3.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]>
2022-08-11 21:57:32 -04:00
Jeremy Pritts be63ebdb23 Ported Bc code 2022-07-16 15:49:16 -04:00
Jeremy Pritts 8458eb5866 RGB Half and Single 2022-07-14 10:00:05 -04:00
Jeremy Pritts 19204e4dd2 Make DirectBitmap a generic class 2022-07-14 09:52:13 -04:00
Jeremy Pritts 8aa1840eb4 Make DirectBitmap a struct 2022-07-14 09:37:21 -04:00
Jeremy Pritts 45267fc7fd replace System.Drawing.Common with StbImageWriteSharp 2022-07-14 09:30:54 -04:00
Jeremy Pritts 3ad6884592 mark as trimmable 2022-07-14 09:27:41 -04:00
Jeremy Pritts 7c65daebaa rgb9e5 benchmark 2022-07-12 10:20:35 -04:00
Jeremy Pritts 068140150b aggressive optimization 2022-07-12 09:56:02 -04:00
Jeremy Pritts 5f2e7dc5c3 add a benchmarks project 2022-07-12 09:35:08 -04:00
Jeremy Pritts 84eeba1e36 Remove specialized console application projects 2022-07-12 09:11:41 -04:00
Jeremy Pritts 88c33f5dfa move test files 2022-07-12 09:09:03 -04:00
Jeremy Pritts 9555cbf25c add a unified console application 2022-07-12 08:56:45 -04:00
Jeremy Pritts cab5e7cfb8 rename test generator 2022-07-11 23:05:35 -04:00
Jeremy Pritts 2611088bd9 move texture decoder project 2022-07-11 23:02:04 -04:00
Jeremy Pritts 2256161eab generic support to yuy2 2022-07-11 22:05:40 -04:00
Jeremy Pritts d9cbcc48d8 color extensions 2022-07-11 21:43:44 -04:00
Jeremy Pritts 113b793a6e aggressive conversion optimization 2022-07-11 20:51:26 -04:00
Jeremy Pritts fac3ca64df Make RGB9e5 a double precision color type 2022-07-11 20:37:06 -04:00
Jeremy Pritts 1485a246f1 add support for double and identity conversions 2022-07-11 20:30:52 -04:00
Jeremy Pritts 81ecc464e5 new rgb conversion code 2022-07-10 09:43:35 -04:00
Jeremy Pritts d158e90448 fix RGBAFloat 2022-07-10 09:29:53 -04:00
Jeremy Pritts 7f3808b075 Support for converting RG32, RGB48, and RGBA64 2022-07-07 00:38:12 -04:00
Jeremy Pritts 23c8e2c64a astc decoder return bytes read 2022-07-06 20:48:22 -04:00
Jeremy Pritts ea1c9c34ce rgb tests 2022-07-06 20:30:34 -04:00
Jeremy Pritts d55e1e69a8 return number of bytes read for pvrtc decoder methods 2022-07-06 20:00:28 -04:00
Jeremy Pritts f6ea7a9269 add a decompression method to pvrtc decoder with an out byte[] parameter 2022-07-06 11:15:49 -04:00
Jeremy Pritts 07b0e7d407 replace all pointer use in pvrtc decoder with span 2022-07-06 11:12:47 -04:00
Jeremy Pritts 56a70a2895 add a Pvrtc test 2022-07-06 10:15:28 -04:00
Jeremy Pritts f967a521b0 Dxt tests 2022-07-06 08:59:00 -04:00
Jeremy Pritts 4f775c4eff use more span in etc decoder 2022-07-06 08:53:08 -04:00
dependabot[bot] 9a5da95897 Bump Microsoft.NET.Test.Sdk from 17.1.0 to 17.2.0
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.1.0 to 17.2.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.1.0...v17.2.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]>
2022-07-05 22:06:42 -04:00
Jeremy Pritts 4e037d8ddc use span more in atc decoder 2022-07-05 21:52:16 -04:00
Jeremy Pritts 5d2f86b18f return bytes read for atc decoding methods 2022-07-05 21:25:26 -04:00
Jeremy Pritts d701ff24fc ETC changes:
* Use span in the decoder more
* Return bytes read for all public decoding methods
* Make the tests read all the mip maps
2022-07-05 20:58:06 -04:00
Jeremy Pritts 7be1d516f1 re-enable etc tests 2022-07-05 20:26:44 -04:00
Jeremy Pritts 578a0633d1 use partial classes for astc and pvrtc 2022-07-05 19:00:38 -04:00
Jeremy Pritts 94e4994027 make rgb converter return bytes read 2022-07-05 18:52:32 -04:00
Jeremy Pritts 74d081ca21 add 3 new overloads to DxtDecoder for consistency with other decoders 2022-07-05 18:37:15 -04:00
Jeremy Pritts 8e1792bfaf disable atc and etc tests 2022-07-05 18:32:46 -04:00
Jeremy Pritts 734f158f7f add a real test project 2022-07-05 16:48:28 -04:00
Jeremy Pritts 7aa9794941 etc and yuy2 console app printing fix 2022-07-05 16:47:32 -04:00
Jeremy Pritts 9675ef0943 return the number of bytes read for dxt and yuy2 2022-07-05 11:59:09 -04:00
Jeremy Pritts d4f7b94dea string interpolation 2022-07-05 11:22:42 -04:00
Jeremy Pritts 20d450ac1f use out parameters instead of return for outputted byte arrays 2022-07-05 10:30:38 -04:00
Jeremy Pritts 2711b749db Resolve #4 2022-06-27 09:26:44 -04:00
Jeremy Pritts 6465aa35c3 formatting 2022-06-22 22:38:10 -04:00
Jeremy Pritts 80d8c58d3a remove empty documentation 2022-06-22 22:31:04 -04:00
Jeremy Pritts f8bee850bd use span for rgb 2022-06-22 22:24:08 -04:00
dependabot[bot] eeb8866c41 Bump actions/setup-dotnet from 1 to 2
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 1 to 2.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v1...v2)

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

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-02 21:06:36 -05:00
dependabot[bot] 129576d48e Bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-02 21:06:25 -05:00
ds5678 f28d991abe Create dependabot.yml 2022-03-02 21:00:00 -05:00
Jeremy Pritts b1056490ec change utinyripper license file extension 2022-02-15 18:24:26 -05:00
Jeremy Pritts cab5cabc4e update package info, add net 5 target, and bump version 2022-02-11 20:44:02 -05:00
Jeremy Pritts b0038a5184 bump version 2022-02-05 23:50:03 -05:00
Jeremy Pritts 6665fab8d9 fix overflow in RGBAHalfToBGRA32 2022-02-05 23:48:53 -05:00
Jeremy Pritts 6998127fe8 bump version 2022-02-05 04:17:33 -05:00
Jeremy Pritts 8f68931c40 xml stub documentation 2022-02-05 04:04:38 -05:00
Jeremy Pritts eb9536456e clamp bytes during rgb conversion 2022-02-05 03:51:25 -05:00
241 changed files with 13925 additions and 2721 deletions

No files matched your search

+251
View File
@@ -0,0 +1,251 @@
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# C# files
[*.cs]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 4
indent_style = tab
tab_width = 4
# New line preferences
end_of_line = crlf
insert_final_newline =true
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
# this. and Me. preferences
dotnet_style_qualification_for_event = false:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_property = false:silent
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
# Expression-level preferences
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_object_initializer = false:suggestion
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = false:silent
dotnet_style_prefer_conditional_expression_over_return = false:silent
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
# Field preferences
dotnet_style_readonly_field = true:suggestion
# Parameter preferences
dotnet_code_quality_unused_parameters = all:suggestion
#### C# Coding Conventions ####
# var preferences
csharp_style_var_elsewhere = false:warning
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:suggestion
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
# Null-checking preferences
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
# Code-block preferences
csharp_prefer_braces = true:suggestion
csharp_prefer_simple_using_statement = true:suggestion
# Expression-level preferences
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:silent
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
csharp_style_prefer_parameter_null_checking = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion
csharp_style_prefer_top_level_statements = false:silent
dotnet_diagnostic.CA1309.severity = error
dotnet_diagnostic.CA1311.severity = suggestion
dotnet_diagnostic.SYSLIB1054.severity = warning
[*.{cs,vb}]
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_object_initializer = false:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = lf
dotnet_style_prefer_conditional_expression_over_assignment = false:silent
dotnet_style_prefer_conditional_expression_over_return = false:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_style_readonly_field = true:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
dotnet_code_quality_unused_parameters = all:suggestion
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
-63
View File
@@ -1,63 +0,0 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
+18
View File
@@ -0,0 +1,18 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "nuget" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
# Workflow files stored in the
# default location of `.github/workflows`
directory: "/"
schedule:
interval: "daily"
+3 -3
View File
@@ -12,11 +12,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4.0.0
with:
dotnet-version: 6.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
+4 -1
View File
@@ -3,4 +3,7 @@ obj
*.csproj.user
.vs
.idea
.vscode
.vscode
launchSettings.json
test.png
textureformats/
@@ -0,0 +1,25 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
using BenchmarkDotNet.Attributes;
namespace AssetRipper.TextureDecoder.Benchmarks;
public class Argb32Decoding
{
private const int Width = 256;//ignoring smaller mips
private const int Height = 256;//ignoring smaller mips
private readonly byte[] inputData;
private readonly byte[] outputData;
public Argb32Decoding()
{
inputData = File.ReadAllBytes(PathConstants.PathToTestFiles + "Rgb/" + "test.argb32");
outputData = new byte[Width * Height * sizeof(uint)];
}
[Benchmark]
public int OriginalMethod() => RgbConverter.ARGB32ToBGRA32(inputData, Width, Height, outputData);
[Benchmark]
public int GenericMethod() => RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(inputData, Width, Height, outputData);
}
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.11" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,7 @@
namespace AssetRipper.TextureDecoder.Benchmarks;
internal static class PathConstants
{
internal const string PathToRepositoryRoot = "../../../../../../../../";
internal const string PathToTestFiles = PathToRepositoryRoot + "TestFiles/";
}
@@ -0,0 +1,11 @@
using BenchmarkDotNet.Running;
namespace AssetRipper.TextureDecoder.Benchmarks;
internal class Program
{
static void Main()
{
BenchmarkRunner.Run(typeof(Program).Assembly);
}
}
@@ -0,0 +1,25 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
using BenchmarkDotNet.Attributes;
namespace AssetRipper.TextureDecoder.Benchmarks;
public class Rgb9e5Decoding
{
private const int Width = 256;//ignoring smaller mips
private const int Height = 256;//ignoring smaller mips
private readonly byte[] inputData;
private readonly byte[] outputData;
public Rgb9e5Decoding()
{
inputData = File.ReadAllBytes(PathConstants.PathToTestFiles + "Rgb/" + "test.rgb9e5float");
outputData = new byte[Width * Height * sizeof(uint)];
}
[Benchmark]
public int OriginalMethod() => RgbConverter.RGB9e5FloatToBGRA32(inputData, Width, Height, outputData);
[Benchmark]
public int GenericMethod() => RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(inputData, Width, Height, outputData);
}
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetRipper.TextureDecoder.SourceGeneration.Common\AssetRipper.TextureDecoder.SourceGeneration.Common.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,286 @@
using AssetRipper.Text.SourceGeneration;
using AssetRipper.TextureDecoder.SourceGeneration.Common;
using System.CodeDom.Compiler;
using System.Diagnostics;
namespace AssetRipper.TextureDecoder.ColorGenerator;
internal static class NumericConversionGenerator
{
private const string OutputNamespace = "AssetRipper.TextureDecoder.Rgb";
private const string OutputDirectory = "../../../../AssetRipper.TextureDecoder/Rgb/";
private const string ClassName = "NumericConversion";
private const string ChangeSign = "ChangeSign";
public static void Run()
{
Console.WriteLine(ClassName);
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(OutputDirectory, ClassName);
Write(writer);
}
private static void Write(IndentedTextWriter writer)
{
writer.WriteGeneratedCodeWarning();
writer.WriteLine();
writer.WriteFileScopedNamespace(OutputNamespace);
writer.WriteLine();
writer.WriteLine($"static partial class {ClassName}");
using (new CurlyBrackets(writer))
{
WritePrimaryConvertMethod(writer);
foreach (CSharpPrimitives.Data from in CSharpPrimitives.List)
{
WriteConvertMethod(writer, from);
}
WriteGetValueMethod(writer, "GetMinimumValue");
WriteGetValueMethod(writer, "GetMaximumValue");
WriteChangeSignMethods(writer);
}
}
private static void WriteChangeSignMethods(IndentedTextWriter writer)
{
foreach ((CSharpPrimitives.Data unsignedType, CSharpPrimitives.Data signedType) in GetIntegerTypes())
{
if (!unsignedType.HasPointerSize(out _, out _))
{
WriteMethod(writer, unsignedType, signedType, unsignedType);
WriteMethod(writer, signedType, unsignedType, unsignedType);
}
}
static void WriteMethod(IndentedTextWriter writer, CSharpPrimitives.Data parameterType, CSharpPrimitives.Data returnType, CSharpPrimitives.Data unsignedType)
{
writer.WriteLine("[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]");
writer.WriteLine($"private static {returnType.LangName} {ChangeSign}({parameterType.LangName} value)");
using (new CurlyBrackets(writer))
{
string methodName = parameterType == unsignedType ? "ToSignedNumber" : "ToUnsignedNumber";
writer.WriteLine($"return {methodName}<{parameterType.LangName}, {returnType.LangName}>(value);");
}
writer.WriteLineNoTabs();
}
static IEnumerable<(CSharpPrimitives.Data, CSharpPrimitives.Data)> GetIntegerTypes()
{
foreach (CSharpPrimitives.Data data in CSharpPrimitives.List)
{
if (data.IsSignedInteger(out CSharpPrimitives.Data? unsignedData))
{
yield return (unsignedData, data);
}
}
}
}
private static void WritePrimaryConvertMethod(IndentedTextWriter writer)
{
writer.WriteLine("[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]");
writer.WriteLine($"public static TTo Convert<TFrom, TTo>(TFrom value) where TFrom : unmanaged where TTo : unmanaged");
using (new CurlyBrackets(writer))
{
writer.WriteLine("if (typeof(TFrom) == typeof(TTo))");
using (new CurlyBrackets(writer))
{
writer.WriteLine("return Unsafe.As<TFrom, TTo>(ref value);");
}
foreach (CSharpPrimitives.Data from in CSharpPrimitives.List)
{
writer.WriteLine($"else if (typeof(TFrom) == typeof({from.LangName}))");
using (new CurlyBrackets(writer))
{
writer.WriteLine($"return {ConvertMethodName(from.Type)}<TTo>(Unsafe.As<TFrom, {from.LangName}>(ref value));");
}
}
writer.WriteLine("else");
using (new CurlyBrackets(writer))
{
writer.WriteLine("return ThrowOrReturnDefault<TTo>();");
}
}
writer.WriteLineNoTabs();
}
private static void WriteConvertMethod(IndentedTextWriter writer, CSharpPrimitives.Data from)
{
string methodName = ConvertMethodName(from.Type);
writer.WriteLine("[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]");
writer.WriteLine($"private static TTo {methodName}<TTo>({from.LangName} value) where TTo : unmanaged");
using (new CurlyBrackets(writer))
{
if (from.HasPointerSize(out CSharpPrimitives.Data? bit32From, out CSharpPrimitives.Data? bit64From))
{
using (new If(writer, "IntPtr.Size == sizeof(int)"))
{
writer.WriteLine($"return {ConvertMethodName(bit32From.Type)}<TTo>(({bit32From.LangName})value);");
}
using (new Else(writer))
{
writer.WriteLine($"return {ConvertMethodName(bit64From.Type)}<TTo>(({bit64From.LangName})value);");
}
}
else if (from.IsSignedInteger(out CSharpPrimitives.Data? unsignedFrom))
{
writer.WriteLine($"return {ConvertMethodName(unsignedFrom.Type)}<TTo>({ChangeSign}(value));");
}
else
{
foreach (CSharpPrimitives.Data to in CSharpPrimitives.List)
{
string ifOrElseIf = to == CSharpPrimitives.FirstData ? "if" : "else if";
writer.WriteLine($"{ifOrElseIf} (typeof(TTo) == typeof({to.LangName}))");
using (new CurlyBrackets(writer))
{
if (from == to)
{
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref value);");
}
else if (to.HasPointerSize(out CSharpPrimitives.Data? bit32To, out CSharpPrimitives.Data? bit64To))
{
using (new If(writer, "IntPtr.Size == sizeof(int)"))
{
writer.WriteLine($"{to.LangName} converted = ({to.LangName}){methodName}<{bit32To.LangName}>(value);");
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
}
using (new Else(writer))
{
writer.WriteLine($"{to.LangName} converted = ({to.LangName}){methodName}<{bit64To.LangName}>(value);");
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
}
}
else if (to.IsSignedInteger(out CSharpPrimitives.Data? unsignedTo))
{
writer.WriteLine($"{to.LangName} converted = {ChangeSign}({methodName}<{unsignedTo.LangName}>(value));");
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
}
else if (from.IsFloatingPoint)
{
if (to.IsFloatingPoint)
{
writer.WriteLine($"{to.LangName} converted = ({to.LangName})value;");
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
}
else
{
Debug.Assert(to.IsUnsignedInteger);
if (from.Type == typeof(Half))
{
writer.WriteComment("We use float because it has enough precision to convert from Half to any integer type.");
writer.WriteLine($"return {ConvertMethodName(typeof(float))}<TTo>((float)value);");
}
else
{
writer.WriteComment("x must be clamped because of rounding errors.");
string minValue = to.MinValue;
string maxValue = to.MaxValue;
writer.WriteLine($"{from.LangName} x = value * ({from.LangName}){maxValue};");
writer.WriteLine($"{to.LangName} converted = ({from.LangName}){maxValue} < x ? {maxValue} : (x > ({from.LangName}){minValue} ? ({to.LangName})x : {minValue});");
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
}
}
}
else
{
Debug.Assert(from.IsUnsignedInteger);
if (to.IsFloatingPoint)
{
if (to.Type == typeof(Half))
{
writer.WriteComment("There isn't enough precision to convert from anything bigger than byte to Half, so we convert to float first.");
writer.WriteLine($"float x = {methodName}<float>(value);");
writer.WriteLine("Half converted = (Half)x;");
writer.WriteLine("return Unsafe.As<Half, TTo>(ref converted);");
}
else
{
writer.WriteLine($"{to.LangName} converted = ({to.LangName})value / ({to.LangName}){from.MaxValue};");
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
}
}
else
{
Debug.Assert(to.IsUnsignedInteger);
writer.WriteComment("See https://github.com/AssetRipper/TextureDecoder/issues/19");
if (from.Size < to.Size)
{
int divided = to.Size / from.Size;
string conversionType = to.Size < sizeof(uint) ? "uint" : to.LangName;
writer.WriteLine("unchecked");
using (new CurlyBrackets(writer))
{
writer.Write($"{to.LangName} converted = ({to.LangName})(");
for (int i = divided - 1; i > 0; i--)
{
writer.Write($"(({conversionType})value << {i * from.Size * 8}) | ");
}
writer.WriteLine("value);");
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
}
}
else
{
Debug.Assert(from.Size > to.Size);
if (from.Type == typeof(ushort) && to.Type == typeof(byte))
{
writer.WriteComment("This is a special case where we already know an optimal algorithm.");
writer.WriteLine("uint x = (value * 255u + 32895u) >> 16;");
writer.WriteLine("byte converted = unchecked((byte)x);");
writer.WriteLine("return Unsafe.As<byte, TTo>(ref converted);");
}
else
{
writer.WriteComment($"There are more accurate ways to map {from.TypeName} onto {to.TypeName}, but this is the simplest.");
string conversionType = from.Size < sizeof(uint) ? "uint" : from.LangName;
writer.WriteLine("unchecked");
using (new CurlyBrackets(writer))
{
int offset = (from.Size / to.Size - 1) * to.Size * 8;
writer.WriteLine($"{to.LangName} converted = ({to.LangName})(({conversionType})value >> {offset});");
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref converted);");
}
}
}
}
}
}
}
writer.WriteLine("else");
using (new CurlyBrackets(writer))
{
writer.WriteLine("return ThrowOrReturnDefault<TTo>();");
}
}
}
writer.WriteLineNoTabs();
}
private static void WriteGetValueMethod(IndentedTextWriter writer, string methodName)
{
writer.WriteLine("[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]");
writer.WriteLine($"public static T {methodName}<T>() where T : unmanaged");
using (new CurlyBrackets(writer))
{
foreach (CSharpPrimitives.Data from in CSharpPrimitives.List)
{
string ifOrElseIf = from == CSharpPrimitives.FirstData ? "if" : "else if";
writer.WriteLine($"{ifOrElseIf} (typeof(T) == typeof({from.LangName}))");
using (new CurlyBrackets(writer))
{
writer.WriteLine($"{from.LangName} value = {methodName}Safe<{from.LangName}>();");
writer.WriteLine($"return Unsafe.As<{from.LangName}, T>(ref value);");
}
}
writer.WriteLine("else");
using (new CurlyBrackets(writer))
{
writer.WriteLine("return ThrowOrReturnDefault<T>();");
}
}
writer.WriteLineNoTabs();
}
private static string ConvertMethodName(Type from)
{
return $"Convert{from.Name}";
}
}
@@ -0,0 +1,347 @@
using AssetRipper.Text.SourceGeneration;
using AssetRipper.TextureDecoder.SourceGeneration.Common;
using System.CodeDom.Compiler;
using System.Text;
namespace AssetRipper.TextureDecoder.ColorGenerator;
internal static partial class Program
{
private const string AttributeNamespace = "AssetRipper.TextureDecoder.Attributes";
private const string OutputNamespace = "AssetRipper.TextureDecoder.Rgb.Formats";
private const string OutputFolder = "../../../../AssetRipper.TextureDecoder/Rgb/Formats/";
/// <summary>
/// Name, Type, Red, Blue, Green, Alpha, Fully Utilized
/// </summary>
private static readonly List<(string, Type, bool, bool, bool, bool, bool)> CustomColors = new()
{
( "ColorARGB16", typeof(byte), true, true, true, true, false ),
( "ColorARGB32", typeof(byte), true, true, true, true, true ),
( "ColorBGRA32", typeof(byte), true, true, true, true, true ),
( "ColorRGB16", typeof(byte), true, true, true, false, false ),
( "ColorRGB9e5", typeof(double), true, true, true, false, false ),
( "ColorRGBA16", typeof(byte), true, true, true, true, false ),
};
/// <summary>
/// Name, Red, Blue, Green, Alpha
/// </summary>
private static readonly List<(string, bool, bool, bool, bool)> GenericColors = new()
{
( "ColorR", true, false, false, false ),
( "ColorRG", true, true, false, false ),
( "ColorRGB", true, true, true, false ),
( "ColorRGBA", true, true, true, true ),
( "ColorA", false, false, false, true ),
};
static void Main()
{
foreach (var customColor in CustomColors)
{
WriteCustomColor(customColor);
}
foreach (var genericColor in GenericColors)
{
WriteGenericColor(genericColor);
}
NumericConversionGenerator.Run();
Console.WriteLine("Done!");
}
private static void WriteCustomColor((string, Type, bool, bool, bool, bool, bool) details)
{
(string name, Type type, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized) = details;
Console.WriteLine(name);
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(OutputFolder, name);
WriteCustomColor(writer, name, type, hasRed, hasGreen, hasBlue, hasAlpha, fullyUtilized);
}
private static void WriteGenericColor((string, bool, bool, bool, bool) details)
{
(string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha) = details;
Console.WriteLine(name);
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(OutputFolder, name);
WriteGenericColor(writer, name, hasRed, hasGreen, hasBlue, hasAlpha);
}
private static void WriteCustomColor(IndentedTextWriter writer, string name, Type type, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized)
{
string typeName = CSharpPrimitives.Dictionary[type].LangName;
writer.WriteGeneratedCodeWarning();
writer.WriteLine();
using (new Namespace(writer, OutputNamespace))
{
writer.WriteLine($"public partial struct {name} : IColor<{typeName}>");
using (new CurlyBrackets(writer))
{
WriteNonGenericStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, fullyUtilized, typeName);
writer.WriteLineNoTabs();
WriteToString(writer, hasRed, hasGreen, hasBlue, hasAlpha);
}
}
}
private static void WriteToString(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha)
{
writer.WriteLine("public override string ToString()");
using (new CurlyBrackets(writer))
{
StringBuilder sb = new();
sb.Append("return $\"{{ ");
bool first = true;
if (hasRed)
{
sb.Append("R: {R}");
first = false;
}
if (hasGreen)
{
if (!first)
{
sb.Append(", ");
}
sb.Append("G: {G}");
first = false;
}
if (hasBlue)
{
if (!first)
{
sb.Append(", ");
}
sb.Append("B: {B}");
first = false;
}
if (hasAlpha)
{
if (!first)
{
sb.Append(", ");
}
sb.Append("A: {A}");
}
sb.Append(" }}\";");
writer.WriteLine(sb.ToString());
}
}
private static void WriteNonGenericStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized, string typeName)
{
writer.WriteLine($"static bool IColor.HasRedChannel => {hasRed.ToLowerString()};");
writer.WriteLine($"static bool IColor.HasGreenChannel => {hasGreen.ToLowerString()};");
writer.WriteLine($"static bool IColor.HasBlueChannel => {hasBlue.ToLowerString()};");
writer.WriteLine($"static bool IColor.HasAlphaChannel => {hasAlpha.ToLowerString()};");
writer.WriteLine($"static bool IColor.ChannelsAreFullyUtilized => {fullyUtilized.ToLowerString()};");
writer.WriteLine($"static Type IColor.ChannelType => typeof({typeName});");
}
private static void WriteBlackWhiteStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, string colorTypeName, string channelTypeName)
{
string minValue = $"NumericConversion.GetMinimumValueSafe<{channelTypeName}>()";
string maxValue = $"NumericConversion.GetMaximumValueSafe<{channelTypeName}>()";
//Black
{
writer.Write($"public static {colorTypeName}<{channelTypeName}> Black => new(");
int rgbCount = CountRGB(hasRed, hasGreen, hasBlue);
for (int i = 0; i < rgbCount; i++)
{
if (i != 0)
{
writer.Write(", ");
}
writer.Write(minValue);
}
if (hasAlpha)
{
if (rgbCount != 0)
{
writer.Write(", ");
}
writer.Write(maxValue);
}
writer.WriteLine(");");
}
//White
if (hasRed || hasGreen || hasBlue)
{
writer.Write($"public static {colorTypeName}<{channelTypeName}> White => new(");
int rgbaCount = CountRGBA(hasRed, hasGreen, hasBlue, hasAlpha);
for (int i = 0; i < rgbaCount; i++)
{
if (i != 0)
{
writer.Write(", ");
}
writer.Write(maxValue);
}
writer.WriteLine(");");
}
else
{
writer.WriteLine($"static {colorTypeName}<{channelTypeName}> IColor<{colorTypeName}<{channelTypeName}>, {channelTypeName}>.White => throw new NotSupportedException();");
}
static int CountRGB(bool hasRed, bool hasGreen, bool hasBlue)
{
return (hasRed ? 1 : 0) + (hasGreen ? 1 : 0) + (hasBlue ? 1 : 0);
}
static int CountRGBA(bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha)
{
return CountRGB(hasRed, hasGreen, hasBlue) + (hasAlpha ? 1 : 0);
}
}
private static void WriteGenericColor(IndentedTextWriter writer, string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha)
{
const string typeName = "T";
const string minValue = "NumericConversion.GetMinimumValueSafe<T>()";
const string maxValue = "NumericConversion.GetMaximumValueSafe<T>()";
writer.WriteGeneratedCodeWarning();
writer.WriteLineNoTabs();
writer.WriteFileScopedNamespace(OutputNamespace);
writer.WriteLineNoTabs();
writer.WriteLine($"public partial struct {name}<T> : IColor<{name}<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>");
using (new CurlyBrackets(writer))
{
WriteProperty(writer, hasRed, typeName, 'R', minValue);
writer.WriteLineNoTabs();
WriteProperty(writer, hasGreen, typeName, 'G', minValue);
writer.WriteLineNoTabs();
WriteProperty(writer, hasBlue, typeName, 'B', minValue);
writer.WriteLineNoTabs();
WriteProperty(writer, hasAlpha, typeName, 'A', maxValue);
writer.WriteLineNoTabs();
WriteConstructor(writer, name, hasRed, hasGreen, hasBlue, hasAlpha, typeName);
writer.WriteLineNoTabs();
WriteGetChannels(writer, typeName);
writer.WriteLineNoTabs();
WriteSetChannels(writer, typeName, hasRed, hasGreen, hasBlue, hasAlpha);
writer.WriteLineNoTabs();
WriteNonGenericStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, true, typeName);
writer.WriteLineNoTabs();
WriteBlackWhiteStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, name, typeName);
writer.WriteLineNoTabs();
WriteToString(writer, hasRed, hasGreen, hasBlue, hasAlpha);
}
}
private static void WriteConstructor(IndentedTextWriter writer, string declaringTypeName, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, string channelTypeName)
{
writer.Write($"public {declaringTypeName}(");
if (hasRed)
{
writer.Write($"{channelTypeName} r");
if (hasGreen || hasBlue || hasAlpha)
{
writer.Write(", ");
}
}
if (hasGreen)
{
writer.Write($"{channelTypeName} g");
if (hasBlue || hasAlpha)
{
writer.Write(", ");
}
}
if (hasBlue)
{
writer.Write($"{channelTypeName} b");
if (hasAlpha)
{
writer.Write(", ");
}
}
if (hasAlpha)
{
writer.Write($"{channelTypeName} a");
}
writer.WriteLine(")");
using (new CurlyBrackets(writer))
{
if (hasRed)
{
writer.WriteLine("R = r;");
}
if (hasGreen)
{
writer.WriteLine("G = g;");
}
if (hasBlue)
{
writer.WriteLine("B = b;");
}
if (hasAlpha)
{
writer.WriteLine("A = a;");
}
}
}
private static void WriteGetChannels(IndentedTextWriter writer, string typeName)
{
writer.WriteLine($"public readonly void GetChannels(out {typeName} r, out {typeName} g, out {typeName} b, out {typeName} a)");
using (new CurlyBrackets(writer))
{
writer.WriteLine("r = R;");
writer.WriteLine("g = G;");
writer.WriteLine("b = B;");
writer.WriteLine("a = A;");
}
}
private static void WriteSetChannels(IndentedTextWriter writer, string typeName, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha)
{
writer.WriteLine($"public void SetChannels({typeName} r, {typeName} g, {typeName} b, {typeName} a)");
using (new CurlyBrackets(writer))
{
if (hasRed)
{
writer.WriteLine("R = r;");
}
if (hasGreen)
{
writer.WriteLine("G = g;");
}
if (hasBlue)
{
writer.WriteLine("B = b;");
}
if (hasAlpha)
{
writer.WriteLine("A = a;");
}
}
}
private static void WriteProperty(IndentedTextWriter writer, bool hasColor, string typeName, char channel, string defaultValue)
{
if (hasColor)
{
writer.WriteLine($"public {typeName} {channel} {{ get; set; }}");
}
else
{
writer.WriteLine($"public readonly {typeName} {channel} ");
using (new CurlyBrackets(writer))
{
writer.WriteLine($"get => {defaultValue};");
writer.WriteLine("set { }");
}
}
}
private static string ToLowerString(this bool value)
{
return value ? "true" : "false";
}
}
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<PublishAot>true</PublishAot>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="StbImageWriteSharp" Version="1.16.7" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,132 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
using StbImageWriteSharp;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace AssetRipper.TextureDecoder.ConsoleApp;
public readonly struct DirectBitmap<TColor, TColorArg>
where TColorArg : unmanaged
where TColor : unmanaged, IColor<TColorArg>
{
private static readonly ImageWriter imageWriter = new();
public DirectBitmap(int width, int height)
{
Width = width;
Height = height;
Data = new byte[CalculateByteSize(width, height)];
}
public DirectBitmap(int width, int height, byte[] data)
{
if (data.Length < CalculateByteSize(width, height))
{
throw new ArgumentException("Data does not have enough space.", nameof(data));
}
Width = width;
Height = height;
Data = data;
}
public void FlipY()
{
Span<TColor> pixels = Pixels;
for (int row = 0, irow = Height - 1; row < irow; row++, irow--)
{
Span<TColor> rowTop = pixels.Slice(row * Width, Width);
Span<TColor> rowBottom = pixels.Slice(irow * Width, Width);
for (int i = 0; i < Width; i++)
{
(rowTop[i], rowBottom[i]) = (rowBottom[i], rowTop[i]);
}
}
}
public void SaveAsBmp(string path)
{
GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components);
using Stream stream = File.OpenWrite(path);
lock (imageWriter)
{
imageWriter.WriteBmp(data, Width, Height, components, stream);
}
}
public void SaveAsHdr(string path)
{
GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components);
using Stream stream = File.OpenWrite(path);
lock (imageWriter)
{
imageWriter.WriteHdr(data, Width, Height, components, stream);
}
}
public void SaveAsJpg(string path)
{
GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components);
using Stream stream = File.OpenWrite(path);
lock (imageWriter)
{
imageWriter.WriteJpg(data, Width, Height, components, stream, default);
}
}
public void SaveAsPng(string path)
{
GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components);
using Stream stream = File.OpenWrite(path);
lock (imageWriter)
{
imageWriter.WritePng(data, Width, Height, components, stream);
}
}
public void SaveAsTga(string path)
{
GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components);
using Stream stream = File.OpenWrite(path);
lock (imageWriter)
{
imageWriter.WriteTga(data, Width, Height, components, stream);
}
}
private void GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components)
{
if (typeof(TColor) == typeof(ColorRGBA<byte>))
{
data = Data;
components = ColorComponents.RedGreenBlueAlpha;
}
else if (typeof(TColor) == typeof(ColorRGB<byte>))
{
data = Data;
components = ColorComponents.RedGreenBlue;
}
else if (TColor.HasAlphaChannel)
{
RgbConverter.Convert<TColor, TColorArg, ColorRGBA<byte>, byte>(Bits, Width, Height, out data);
components = ColorComponents.RedGreenBlueAlpha;
}
else
{
RgbConverter.Convert<TColor, TColorArg, ColorRGB<byte>, byte>(Bits, Width, Height, out data);
components = ColorComponents.RedGreenBlue;
}
}
public int Height { get; }
public int Width { get; }
public int ByteSize => CalculateByteSize(Width, Height);
public Span<byte> Bits => new Span<byte>(Data, 0, ByteSize);
public Span<TColor> Pixels => MemoryMarshal.Cast<byte, TColor>(Bits);
private byte[] Data { get; }
private static int CalculateByteSize(int width, int height)
{
return width * height * Unsafe.SizeOf<TColor>();
}
}
@@ -0,0 +1,242 @@
using AssetRipper.TextureDecoder.Astc;
using AssetRipper.TextureDecoder.Atc;
using AssetRipper.TextureDecoder.Bc;
using AssetRipper.TextureDecoder.Dxt;
using AssetRipper.TextureDecoder.Etc;
using AssetRipper.TextureDecoder.Pvrtc;
using AssetRipper.TextureDecoder.Rgb.Formats;
using AssetRipper.TextureDecoder.Yuy2;
namespace AssetRipper.TextureDecoder.ConsoleApp;
internal static partial class Program
{
static void Main(string[] args)
{
if (args.Length < 4)
{
Console.WriteLine("Format: {path} {inputType} {outputType} {width} {height} {args5} {args6}");
return;
}
string path = args[0];
string inputType = args[1].ToLowerInvariant();
string outputType = args[2].ToLowerInvariant();
int width = int.Parse(args[3]);
int height = int.Parse(args[4]);
string args5 = args.GetArgument(5);
string args6 = args.GetArgument(6);
byte[] data = File.ReadAllBytes(path);
DirectBitmap<ColorBGRA32, byte> bitmap = new DirectBitmap<ColorBGRA32, byte>(width, height);
switch (inputType)
{
case "astc":
DecodeAstc(data, width, height, args5, args6, bitmap.Bits);
break;
case "atc":
DecodeAtc(data, width, height, args5, bitmap.Bits);
break;
case "bc":
DecodeBc(data, width, height, args5, args6, bitmap.Bits);
break;
case "dxt":
DecodeDxt(data, width, height, args5, bitmap.Bits);
break;
case "etc":
DecodeEtc(data, width, height, args5, bitmap.Bits);
break;
case "pvrtc":
DecodePvrtc(data, width, height, args5, bitmap.Bits);
break;
case "rgb":
DecodeRgb(data, width, height, args5, bitmap.Bits);
break;
case "yuy2":
Yuy2Decoder.DecompressYUY2(data, width, height, bitmap.Bits);
break;
default:
throw new NotSupportedException(inputType);
}
string dirPath = Path.GetDirectoryName(path) ?? Environment.CurrentDirectory;
string name = Path.GetFileName(path);
switch (outputType)
{
case "png":
{
string newPath = Path.Combine(dirPath, name + ".png");
bitmap.FlipY();
bitmap.SaveAsPng(newPath);
}
break;
case "bgra":
{
string newPath = Path.Combine(dirPath, name + ".bgra");
WriteAllBytes(newPath, bitmap.Bits);
}
break;
default:
throw new NotSupportedException(outputType);
}
Console.WriteLine("Done!");
}
private static string GetArgument(this string[] args, int index)
{
return index < args.Length ? args[index] : string.Empty;
}
private static void DecodeAstc(ReadOnlySpan<byte> input, int width, int height, string blockXSizeString, string blockYSizeString, Span<byte> output)
{
Console.WriteLine("Arg at index 5 : blockXSize");
Console.WriteLine("Arg at index 6 : blockYSize");
int blockXSize = int.Parse(blockXSizeString);
int blockYSize = int.Parse(blockYSizeString);
AstcDecoder.DecodeASTC(input, width, height, blockXSize, blockYSize, output);
}
private static void DecodeAtc(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
{
Console.WriteLine("Arg at index 5 : mode");
Console.WriteLine(" 0 - ATC RGB4");
Console.WriteLine(" 1 - ATC RGBA8");
int mode = int.Parse(modeString);
switch (mode)
{
case 0:
AtcDecoder.DecompressAtcRgb4(input, width, height, output);
break;
case 1:
AtcDecoder.DecompressAtcRgba8(input, width, height, output);
break;
default:
throw new NotSupportedException(mode.ToString());
}
}
private static void DecodeBc(ReadOnlySpan<byte> input, int width, int height, string modeString, string isSignedString, Span<byte> output)
{
Console.WriteLine("Arg at index 5 : mode");
Console.WriteLine(" 1 - BC1");
Console.WriteLine(" 2 - BC2");
Console.WriteLine(" 3 - BC3");
Console.WriteLine(" 4 - BC4");
Console.WriteLine(" 5 - BC5");
Console.WriteLine(" 6 - BC6H");
Console.WriteLine(" 7 - BC7");
Console.WriteLine("Arg at index 6 : isSigned (BC6H only)");
int mode = int.Parse(modeString);
switch (mode)
{
case 1:
Bc1.Decompress(input, width, height, output);
break;
case 2:
Bc2.Decompress(input, width, height, output);
break;
case 3:
Bc3.Decompress(input, width, height, output);
break;
case 4:
Bc4.Decompress(input, width, height, output);
break;
case 5:
Bc5.Decompress(input, width, height, output);
break;
case 6:
Bc6h.Decompress(input, width, height, bool.Parse(isSignedString), output);
break;
case 7:
Bc7.Decompress(input, width, height, output);
break;
default:
throw new NotSupportedException(mode.ToString());
}
}
private static void DecodeDxt(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
{
Console.WriteLine("Arg at index 5 : mode");
Console.WriteLine(" 0 - DXT1");
Console.WriteLine(" 1 - DXT3");
Console.WriteLine(" 2 - DXT5");
int mode = int.Parse(modeString);
switch (mode)
{
case 0:
DxtDecoder.DecompressDXT1(input, width, height, output);
break;
case 1:
DxtDecoder.DecompressDXT3(input, width, height, output);
break;
case 2:
DxtDecoder.DecompressDXT5(input, width, height, output);
break;
default:
throw new NotSupportedException(mode.ToString());
}
}
private static void DecodeEtc(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
{
Console.WriteLine("Arg at index 5 : mode");
Console.WriteLine(" 0 - ETC");
Console.WriteLine(" 1 - ETC2");
Console.WriteLine(" 2 - ETC2a1");
Console.WriteLine(" 3 - ETC2a8");
Console.WriteLine(" 4 - EAC R");
Console.WriteLine(" 5 - EAC signed R");
Console.WriteLine(" 6 - EAC RG");
Console.WriteLine(" 7 - EAC signed RG");
int mode = int.Parse(modeString);
switch (mode)
{
case 0:
EtcDecoder.DecompressETC(input, width, height, output);
break;
case 1:
EtcDecoder.DecompressETC2(input, width, height, output);
break;
case 2:
EtcDecoder.DecompressETC2A1(input, width, height, output);
break;
case 3:
EtcDecoder.DecompressETC2A8(input, width, height, output);
break;
case 4:
EtcDecoder.DecompressEACRUnsigned(input, width, height, output);
break;
case 5:
EtcDecoder.DecompressEACRSigned(input, width, height, output);
break;
case 6:
EtcDecoder.DecompressEACRGUnsigned(input, width, height, output);
break;
case 7:
EtcDecoder.DecompressEACRGSigned(input, width, height, output);
break;
default:
throw new NotSupportedException(mode.ToString());
}
}
private static void DecodePvrtc(ReadOnlySpan<byte> input, int width, int height, string do2bitModeString, Span<byte> output)
{
Console.WriteLine("Arg at index 5 : 2bitMode");
bool do2bit = bool.Parse(do2bitModeString);
PvrtcDecoder.DecompressPVRTC(input, width, height, do2bit, output);
}
public static void WriteAllBytes(string path, ReadOnlySpan<byte> bytes)
{
ArgumentException.ThrowIfNullOrEmpty(path, nameof(path));
using Microsoft.Win32.SafeHandles.SafeFileHandle sfh = File.OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read);
RandomAccess.Write(sfh, bytes, 0);
}
}
@@ -0,0 +1,384 @@
// Auto-generated code. Do not modify manually.
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Runtime.InteropServices;
namespace AssetRipper.TextureDecoder.ConsoleApp;
static partial class Program
{
private static void DecodeRgb(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
{
Console.WriteLine("Arg at index 5 : mode");
Console.WriteLine(" 0 - R_SByte");
Console.WriteLine(" 1 - RG_SByte");
Console.WriteLine(" 2 - RGB_SByte");
Console.WriteLine(" 3 - RGBA_SByte");
Console.WriteLine(" 4 - A_SByte");
Console.WriteLine(" 5 - R_Byte");
Console.WriteLine(" 6 - RG_Byte");
Console.WriteLine(" 7 - RGB_Byte");
Console.WriteLine(" 8 - RGBA_Byte");
Console.WriteLine(" 9 - A_Byte");
Console.WriteLine(" 10 - R_Int16");
Console.WriteLine(" 11 - RG_Int16");
Console.WriteLine(" 12 - RGB_Int16");
Console.WriteLine(" 13 - RGBA_Int16");
Console.WriteLine(" 14 - A_Int16");
Console.WriteLine(" 15 - R_UInt16");
Console.WriteLine(" 16 - RG_UInt16");
Console.WriteLine(" 17 - RGB_UInt16");
Console.WriteLine(" 18 - RGBA_UInt16");
Console.WriteLine(" 19 - A_UInt16");
Console.WriteLine(" 20 - R_Int32");
Console.WriteLine(" 21 - RG_Int32");
Console.WriteLine(" 22 - RGB_Int32");
Console.WriteLine(" 23 - RGBA_Int32");
Console.WriteLine(" 24 - A_Int32");
Console.WriteLine(" 25 - R_UInt32");
Console.WriteLine(" 26 - RG_UInt32");
Console.WriteLine(" 27 - RGB_UInt32");
Console.WriteLine(" 28 - RGBA_UInt32");
Console.WriteLine(" 29 - A_UInt32");
Console.WriteLine(" 30 - R_IntPtr");
Console.WriteLine(" 31 - RG_IntPtr");
Console.WriteLine(" 32 - RGB_IntPtr");
Console.WriteLine(" 33 - RGBA_IntPtr");
Console.WriteLine(" 34 - A_IntPtr");
Console.WriteLine(" 35 - R_UIntPtr");
Console.WriteLine(" 36 - RG_UIntPtr");
Console.WriteLine(" 37 - RGB_UIntPtr");
Console.WriteLine(" 38 - RGBA_UIntPtr");
Console.WriteLine(" 39 - A_UIntPtr");
Console.WriteLine(" 40 - R_Int64");
Console.WriteLine(" 41 - RG_Int64");
Console.WriteLine(" 42 - RGB_Int64");
Console.WriteLine(" 43 - RGBA_Int64");
Console.WriteLine(" 44 - A_Int64");
Console.WriteLine(" 45 - R_UInt64");
Console.WriteLine(" 46 - RG_UInt64");
Console.WriteLine(" 47 - RGB_UInt64");
Console.WriteLine(" 48 - RGBA_UInt64");
Console.WriteLine(" 49 - A_UInt64");
Console.WriteLine(" 50 - R_Int128");
Console.WriteLine(" 51 - RG_Int128");
Console.WriteLine(" 52 - RGB_Int128");
Console.WriteLine(" 53 - RGBA_Int128");
Console.WriteLine(" 54 - A_Int128");
Console.WriteLine(" 55 - R_UInt128");
Console.WriteLine(" 56 - RG_UInt128");
Console.WriteLine(" 57 - RGB_UInt128");
Console.WriteLine(" 58 - RGBA_UInt128");
Console.WriteLine(" 59 - A_UInt128");
Console.WriteLine(" 60 - R_Half");
Console.WriteLine(" 61 - RG_Half");
Console.WriteLine(" 62 - RGB_Half");
Console.WriteLine(" 63 - RGBA_Half");
Console.WriteLine(" 64 - A_Half");
Console.WriteLine(" 65 - R_Single");
Console.WriteLine(" 66 - RG_Single");
Console.WriteLine(" 67 - RGB_Single");
Console.WriteLine(" 68 - RGBA_Single");
Console.WriteLine(" 69 - A_Single");
Console.WriteLine(" 70 - R_NFloat");
Console.WriteLine(" 71 - RG_NFloat");
Console.WriteLine(" 72 - RGB_NFloat");
Console.WriteLine(" 73 - RGBA_NFloat");
Console.WriteLine(" 74 - A_NFloat");
Console.WriteLine(" 75 - R_Double");
Console.WriteLine(" 76 - RG_Double");
Console.WriteLine(" 77 - RGB_Double");
Console.WriteLine(" 78 - RGBA_Double");
Console.WriteLine(" 79 - A_Double");
Console.WriteLine(" 80 - R_Decimal");
Console.WriteLine(" 81 - RG_Decimal");
Console.WriteLine(" 82 - RGB_Decimal");
Console.WriteLine(" 83 - RGBA_Decimal");
Console.WriteLine(" 84 - A_Decimal");
Console.WriteLine(" 85 - ARGB16");
Console.WriteLine(" 86 - ARGB32");
Console.WriteLine(" 87 - BGRA32");
Console.WriteLine(" 88 - RGB16");
Console.WriteLine(" 89 - RGB9e5");
Console.WriteLine(" 90 - RGBA16");
int mode = int.Parse(modeString);
switch(mode)
{
case 0:
RgbConverter.Convert<ColorR<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
break;
case 1:
RgbConverter.Convert<ColorRG<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
break;
case 2:
RgbConverter.Convert<ColorRGB<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
break;
case 3:
RgbConverter.Convert<ColorRGBA<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
break;
case 4:
RgbConverter.Convert<ColorA<sbyte>, sbyte, ColorBGRA32, byte>(input, width, height, output);
break;
case 5:
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
break;
case 6:
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
break;
case 7:
RgbConverter.Convert<ColorRGB<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
break;
case 8:
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
break;
case 9:
RgbConverter.Convert<ColorA<byte>, byte, ColorBGRA32, byte>(input, width, height, output);
break;
case 10:
RgbConverter.Convert<ColorR<short>, short, ColorBGRA32, byte>(input, width, height, output);
break;
case 11:
RgbConverter.Convert<ColorRG<short>, short, ColorBGRA32, byte>(input, width, height, output);
break;
case 12:
RgbConverter.Convert<ColorRGB<short>, short, ColorBGRA32, byte>(input, width, height, output);
break;
case 13:
RgbConverter.Convert<ColorRGBA<short>, short, ColorBGRA32, byte>(input, width, height, output);
break;
case 14:
RgbConverter.Convert<ColorA<short>, short, ColorBGRA32, byte>(input, width, height, output);
break;
case 15:
RgbConverter.Convert<ColorR<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
break;
case 16:
RgbConverter.Convert<ColorRG<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
break;
case 17:
RgbConverter.Convert<ColorRGB<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
break;
case 18:
RgbConverter.Convert<ColorRGBA<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
break;
case 19:
RgbConverter.Convert<ColorA<ushort>, ushort, ColorBGRA32, byte>(input, width, height, output);
break;
case 20:
RgbConverter.Convert<ColorR<int>, int, ColorBGRA32, byte>(input, width, height, output);
break;
case 21:
RgbConverter.Convert<ColorRG<int>, int, ColorBGRA32, byte>(input, width, height, output);
break;
case 22:
RgbConverter.Convert<ColorRGB<int>, int, ColorBGRA32, byte>(input, width, height, output);
break;
case 23:
RgbConverter.Convert<ColorRGBA<int>, int, ColorBGRA32, byte>(input, width, height, output);
break;
case 24:
RgbConverter.Convert<ColorA<int>, int, ColorBGRA32, byte>(input, width, height, output);
break;
case 25:
RgbConverter.Convert<ColorR<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
break;
case 26:
RgbConverter.Convert<ColorRG<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
break;
case 27:
RgbConverter.Convert<ColorRGB<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
break;
case 28:
RgbConverter.Convert<ColorRGBA<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
break;
case 29:
RgbConverter.Convert<ColorA<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
break;
case 30:
RgbConverter.Convert<ColorR<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
break;
case 31:
RgbConverter.Convert<ColorRG<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
break;
case 32:
RgbConverter.Convert<ColorRGB<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
break;
case 33:
RgbConverter.Convert<ColorRGBA<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
break;
case 34:
RgbConverter.Convert<ColorA<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
break;
case 35:
RgbConverter.Convert<ColorR<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
break;
case 36:
RgbConverter.Convert<ColorRG<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
break;
case 37:
RgbConverter.Convert<ColorRGB<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
break;
case 38:
RgbConverter.Convert<ColorRGBA<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
break;
case 39:
RgbConverter.Convert<ColorA<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
break;
case 40:
RgbConverter.Convert<ColorR<long>, long, ColorBGRA32, byte>(input, width, height, output);
break;
case 41:
RgbConverter.Convert<ColorRG<long>, long, ColorBGRA32, byte>(input, width, height, output);
break;
case 42:
RgbConverter.Convert<ColorRGB<long>, long, ColorBGRA32, byte>(input, width, height, output);
break;
case 43:
RgbConverter.Convert<ColorRGBA<long>, long, ColorBGRA32, byte>(input, width, height, output);
break;
case 44:
RgbConverter.Convert<ColorA<long>, long, ColorBGRA32, byte>(input, width, height, output);
break;
case 45:
RgbConverter.Convert<ColorR<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
break;
case 46:
RgbConverter.Convert<ColorRG<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
break;
case 47:
RgbConverter.Convert<ColorRGB<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
break;
case 48:
RgbConverter.Convert<ColorRGBA<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
break;
case 49:
RgbConverter.Convert<ColorA<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
break;
case 50:
RgbConverter.Convert<ColorR<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
break;
case 51:
RgbConverter.Convert<ColorRG<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
break;
case 52:
RgbConverter.Convert<ColorRGB<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
break;
case 53:
RgbConverter.Convert<ColorRGBA<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
break;
case 54:
RgbConverter.Convert<ColorA<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
break;
case 55:
RgbConverter.Convert<ColorR<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
break;
case 56:
RgbConverter.Convert<ColorRG<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
break;
case 57:
RgbConverter.Convert<ColorRGB<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
break;
case 58:
RgbConverter.Convert<ColorRGBA<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
break;
case 59:
RgbConverter.Convert<ColorA<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
break;
case 60:
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
break;
case 61:
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
break;
case 62:
RgbConverter.Convert<ColorRGB<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
break;
case 63:
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
break;
case 64:
RgbConverter.Convert<ColorA<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
break;
case 65:
RgbConverter.Convert<ColorR<float>, float, ColorBGRA32, byte>(input, width, height, output);
break;
case 66:
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA32, byte>(input, width, height, output);
break;
case 67:
RgbConverter.Convert<ColorRGB<float>, float, ColorBGRA32, byte>(input, width, height, output);
break;
case 68:
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA32, byte>(input, width, height, output);
break;
case 69:
RgbConverter.Convert<ColorA<float>, float, ColorBGRA32, byte>(input, width, height, output);
break;
case 70:
RgbConverter.Convert<ColorR<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
break;
case 71:
RgbConverter.Convert<ColorRG<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
break;
case 72:
RgbConverter.Convert<ColorRGB<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
break;
case 73:
RgbConverter.Convert<ColorRGBA<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
break;
case 74:
RgbConverter.Convert<ColorA<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
break;
case 75:
RgbConverter.Convert<ColorR<double>, double, ColorBGRA32, byte>(input, width, height, output);
break;
case 76:
RgbConverter.Convert<ColorRG<double>, double, ColorBGRA32, byte>(input, width, height, output);
break;
case 77:
RgbConverter.Convert<ColorRGB<double>, double, ColorBGRA32, byte>(input, width, height, output);
break;
case 78:
RgbConverter.Convert<ColorRGBA<double>, double, ColorBGRA32, byte>(input, width, height, output);
break;
case 79:
RgbConverter.Convert<ColorA<double>, double, ColorBGRA32, byte>(input, width, height, output);
break;
case 80:
RgbConverter.Convert<ColorR<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
break;
case 81:
RgbConverter.Convert<ColorRG<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
break;
case 82:
RgbConverter.Convert<ColorRGB<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
break;
case 83:
RgbConverter.Convert<ColorRGBA<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
break;
case 84:
RgbConverter.Convert<ColorA<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
break;
case 85:
RgbConverter.Convert<ColorARGB16, byte, ColorBGRA32, byte>(input, width, height, output);
break;
case 86:
RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(input, width, height, output);
break;
case 87:
RgbConverter.Convert<ColorBGRA32, byte, ColorBGRA32, byte>(input, width, height, output);
break;
case 88:
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA32, byte>(input, width, height, output);
break;
case 89:
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(input, width, height, output);
break;
case 90:
RgbConverter.Convert<ColorRGBA16, byte, ColorBGRA32, byte>(input, width, height, output);
break;
default:
throw new NotSupportedException(mode.ToString());
}
}
}
@@ -0,0 +1,2 @@
<Project Sdk="Microsoft.NET.Sdk">
</Project>
@@ -0,0 +1,8 @@
namespace AssetRipper.TextureDecoder.Paths;
public static class ProjectFolders
{
public const string ThisProject = "../../../";
public const string RepositoryRoot = ThisProject + "../";
public const string TestProject = RepositoryRoot + "AssetRipper.TextureDecoder.Tests/";
}
@@ -0,0 +1,15 @@
namespace AssetRipper.TextureDecoder.Paths;
public static class TestFileFolders
{
public const string TestFiles = ProjectFolders.RepositoryRoot + "TestFiles/";
public const string AstcTestFiles = TestFiles + "Astc/";
public const string AtcTestFiles = TestFiles + "Atc/";
public const string BcTestFiles = TestFiles + "Bc/";
public const string DxtTestFiles = TestFiles + "Dxt/";
public const string EtcTestFiles = TestFiles + "Etc/";
public const string PvrtcTestFiles = TestFiles + "Pvrtc/";
public const string RgbTestFiles = TestFiles + "Rgb/";
public const string Yuy2TestFiles = TestFiles + "Yuy2/";
public const string AndroidTestFiles = TestFiles + "Android/";
}
@@ -0,0 +1,3 @@
<Project Sdk="Microsoft.NET.Sdk">
</Project>
@@ -0,0 +1,259 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace AssetRipper.TextureDecoder.SourceGeneration.Common;
public static class CSharpPrimitives
{
public static IReadOnlyDictionary<Type, Data> Dictionary { get; } = new Dictionary<Type, Data>()
{
{ typeof(sbyte), new Data<sbyte>() },
{ typeof(byte), new Data<byte>() },
{ typeof(short), new Data<short>() },
{ typeof(ushort), new Data<ushort>() },
{ typeof(int), new Data<int>() },
{ typeof(uint), new Data<uint>() },
{ typeof(nint), new Data<nint>() },
{ typeof(nuint), new Data<nuint>() },
{ typeof(long), new Data<long>() },
{ typeof(ulong), new Data<ulong>() },
{ typeof(Int128), new Data<Int128>() },
{ typeof(UInt128), new Data<UInt128>() },
{ typeof(Half), new Data<Half>() },
{ typeof(float), new Data<float>() },
{ typeof(NFloat), new Data<NFloat>() },
{ typeof(double), new Data<double>() },
{ typeof(decimal), new Data<decimal>() },
};
public static IReadOnlyList<Data> List { get; } = Dictionary.Values.ToList();
public static IReadOnlyList<Type> Types { get; } = Dictionary.Keys.ToList();
public static Type FirstType => Types[0];
public static Data FirstData => List[0];
public static bool HasPointerSize(Type type, [NotNullWhen(true)] out Type? bit32Type, [NotNullWhen(true)] out Type? bit64Type)
{
if (type == typeof(nint))
{
bit32Type = typeof(int);
bit64Type = typeof(long);
return true;
}
else if (type == typeof(nuint))
{
bit32Type = typeof(uint);
bit64Type = typeof(ulong);
return true;
}
else if (type == typeof(NFloat))
{
bit32Type = typeof(float);
bit64Type = typeof(double);
return true;
}
else
{
bit32Type = null;
bit64Type = null;
return false;
}
}
public static bool IsFloatingPoint(Type type)
{
return type == typeof(Half) || type == typeof(float) || type == typeof(NFloat) || type == typeof(double) || type == typeof(decimal);
}
public static bool IsUnsignedInteger(Type type)
{
return type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(nuint) || type == typeof(ulong) || type == typeof(UInt128);
}
public static bool IsSignedInteger(Type type, [NotNullWhen(true)] out Type? unsignedType)
{
if (type == typeof(sbyte))
{
unsignedType = typeof(byte);
}
else if (type == typeof(short))
{
unsignedType = typeof(ushort);
}
else if (type == typeof(int))
{
unsignedType = typeof(uint);
}
else if (type == typeof(nint))
{
unsignedType = typeof(nuint);
}
else if (type == typeof(long))
{
unsignedType = typeof(ulong);
}
else if (type == typeof(Int128))
{
unsignedType = typeof(UInt128);
}
else
{
unsignedType = null;
}
return unsignedType is not null;
}
public static bool CanBeConstant(Type type)
{
return type == typeof(byte)
|| type == typeof(sbyte)
|| type == typeof(ushort)
|| type == typeof(short)
|| type == typeof(uint)
|| type == typeof(int)
|| type == typeof(ulong)
|| type == typeof(long)
|| type == typeof(float)
|| type == typeof(double)
|| type == typeof(bool)
|| type == typeof(char)
|| type == typeof(string);
}
public static string GetLangName(Type type)
{
if (type == typeof(sbyte))
{
return "sbyte";
}
else if (type == typeof(byte))
{
return "byte";
}
else if (type == typeof(short))
{
return "short";
}
else if (type == typeof(ushort))
{
return "ushort";
}
else if (type == typeof(int))
{
return "int";
}
else if (type == typeof(uint))
{
return "uint";
}
else if (type == typeof(nint))
{
return "nint";
}
else if (type == typeof(nuint))
{
return "nuint";
}
else if (type == typeof(long))
{
return "long";
}
else if (type == typeof(ulong))
{
return "ulong";
}
else if (type == typeof(float))
{
return "float";
}
else if (type == typeof(double))
{
return "double";
}
else if (type == typeof(decimal))
{
return "decimal";
}
else if (type == typeof(char))
{
return "char";
}
else if (type == typeof(bool))
{
return "bool";
}
else if (type == typeof(string))
{
return "string";
}
else
{
return type.Name;
}
}
public abstract class Data
{
public abstract Type Type { get; }
public string TypeName => Type.Name;
public string LangName => GetLangName(Type);
public bool CanBeConstant => CanBeConstant(Type);
public bool HasPointerSize([NotNullWhen(true)] out Data? bit32Data, [NotNullWhen(true)] out Data? bit64Data)
{
if (CSharpPrimitives.HasPointerSize(Type, out Type? bit32Type, out Type? bit64Type))
{
bit32Data = Dictionary[bit32Type];
bit64Data = Dictionary[bit64Type];
return true;
}
else
{
bit32Data = null;
bit64Data = null;
return false;
}
}
public bool IsFloatingPoint => IsFloatingPoint(Type);
public bool IsUnsignedInteger => IsUnsignedInteger(Type);
public bool IsSignedInteger([NotNullWhen(true)] out Data? unsignedData)
{
if (CSharpPrimitives.IsSignedInteger(Type, out Type? unsignedType))
{
unsignedData = Dictionary[unsignedType];
return true;
}
else
{
unsignedData = null;
return false;
}
}
public abstract int Size { get; }
public override string ToString() => TypeName;
public string MinValue => IsFloatingPoint ? $"{LangName}.Zero" : $"{LangName}.MinValue";
public string MaxValue => IsFloatingPoint ? $"{LangName}.One" : $"{LangName}.MaxValue";
}
private sealed class Data<T> : Data
{
public override Type Type => typeof(T);
public override int Size
{
get
{
if (CSharpPrimitives.HasPointerSize(Type, out _, out _))
{
return 6;//arbitrary value between 4 and 8
}
else
{
return Unsafe.SizeOf<T>();
}
}
}
}
}
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\AssetRipper.TextureDecoder.Paths\AssetRipper.TextureDecoder.Paths.csproj" />
<ProjectReference Include="..\AssetRipper.TextureDecoder.SourceGeneration.Common\AssetRipper.TextureDecoder.SourceGeneration.Common.csproj" />
<ProjectReference Include="..\AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AssetRipper.Text.SourceGeneration" Version="1.1.0" />
</ItemGroup>
</Project>
@@ -0,0 +1,74 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.SourceGeneration.Common;
namespace AssetRipper.TextureDecoder.TestGenerator
{
internal readonly struct GenerationData
{
public Type ColorType { get; private init; }
public Type ChannelType { get; private init; }
public int ColorSize { get; private init; }
public bool RedChannel { get; private init; }
public bool GreenChannel { get; private init; }
public bool BlueChannel { get; private init; }
public bool AlphaChannel { get; private init; }
public bool FullyUtilizedChannels { get; private init; }
public bool IsFloatingPoint => CSharpPrimitives.IsFloatingPoint(ChannelType);
public int BitSizePerChannel => CSharpPrimitives.Dictionary[ChannelType].Size * 8;
public string ChannelTypeName => CSharpPrimitives.Dictionary[ChannelType].LangName;
public static GenerationData Create<T>(int colorSize) where T : IColor
{
return new GenerationData()
{
ColorType = typeof(T),
ChannelType = T.ChannelType,
ColorSize = colorSize,
RedChannel = T.HasRedChannel,
GreenChannel = T.HasGreenChannel,
BlueChannel = T.HasBlueChannel,
AlphaChannel = T.HasAlphaChannel,
FullyUtilizedChannels = T.ChannelsAreFullyUtilized,
};
}
public bool Contains(GenerationData other)
{
if (!FullyUtilizedChannels)
{
return false;
}
if (IsFloatingPoint != other.IsFloatingPoint)
{
return false;
}
if (other.RedChannel && !RedChannel)
{
return false;
}
if (other.GreenChannel && !GreenChannel)
{
return false;
}
if (other.BlueChannel && !BlueChannel)
{
return false;
}
if (other.AlphaChannel && !AlphaChannel)
{
return false;
}
return BitSizePerChannel >= other.BitSizePerChannel;
}
}
}
@@ -0,0 +1,97 @@
using AssetRipper.Text.SourceGeneration;
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
using AssetRipper.TextureDecoder.SourceGeneration.Common;
using System.CodeDom.Compiler;
using System.Runtime.InteropServices;
namespace AssetRipper.TextureDecoder.TestGenerator
{
internal static class Program
{
internal static readonly List<GenerationData> dataList = new()
{
GenerationData.Create<ColorARGB16>(2),
GenerationData.Create<ColorARGB32>(4),
GenerationData.Create<ColorBGRA32>(4),
GenerationData.Create<ColorRGB16>(2),
GenerationData.Create<ColorRGB9e5>(4),
GenerationData.Create<ColorRGBA16>(2),
};
static void Main()
{
TestClassGenerator.GenerateTestClasses();
GenerateRgbConsoleMethod();
TextureDataClassGenerator.Generate();
Console.WriteLine("Done!");
}
private static void GenerateRgbConsoleMethod()
{
Console.WriteLine("Program");
using IndentedTextWriter writer = IndentedTextWriterFactory.Create("../../../../AssetRipper.TextureDecoder.ConsoleApp", "Program");
writer.WriteGeneratedCodeWarning();
writer.WriteLine();
writer.WriteLine($"using {typeof(RgbConverter).Namespace};");
writer.WriteLine($"using {typeof(ColorBGRA32).Namespace};");
writer.WriteLine($"using {typeof(NFloat).Namespace};");
writer.WriteLine();
writer.WriteFileScopedNamespace("AssetRipper.TextureDecoder.ConsoleApp");
writer.WriteLine("static partial class Program");
using (new CurlyBrackets(writer))
{
writer.WriteLine("private static void DecodeRgb(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)");
using (new CurlyBrackets(writer))
{
List<(string, string, string)> list = GetColorNames().ToList();
writer.WriteLine("Console.WriteLine(\"Arg at index 5 : mode\");");
for (int i = 0; i < list.Count; i++)
{
writer.WriteLine($"Console.WriteLine(\" {i} - {list[i].Item1.Substring(5)}\");");
}
writer.WriteLine("int mode = int.Parse(modeString);");
writer.WriteLine("switch(mode)");
using (new CurlyBrackets(writer))
{
for (int i = 0 ; i < list.Count; i++)
{
(string prettyName, string colorName, string channelName) = list[i];
writer.WriteLine($"case {i}:");
using (new Indented(writer))
{
writer.WriteLine($"RgbConverter.Convert<{colorName}, {channelName}, ColorBGRA32, byte>(input, width, height, output);");
writer.WriteLine("break;");
}
}
{
writer.WriteLine("default:");
using (new Indented(writer))
{
writer.WriteLine("throw new NotSupportedException(mode.ToString());");
}
}
}
}
}
static IEnumerable<(string, string, string)> GetColorNames()
{
foreach (CSharpPrimitives.Data primitiveData in CSharpPrimitives.List)
{
foreach (string genericColorName in TestClassGenerator.GetGenericColorNames())
{
string prettyName = $"{genericColorName}_{primitiveData.TypeName}";
yield return (prettyName, $"{genericColorName}<{primitiveData.LangName}>", primitiveData.LangName);
}
}
foreach (GenerationData data in dataList)
{
yield return (data.ColorType.Name, data.ColorType.Name, data.ChannelTypeName);
}
}
}
}
}
@@ -0,0 +1,357 @@
using AssetRipper.Text.SourceGeneration;
using AssetRipper.TextureDecoder.Rgb.Formats;
using AssetRipper.TextureDecoder.SourceGeneration.Common;
using AssetRipper.TextureDecoder.TestGenerator;
using System.CodeDom.Compiler;
internal static class TestClassGenerator
{
private const string OutputNamespace = "AssetRipper.TextureDecoder.Tests.Formats";
private const string OutputFolder = "../../../../AssetRipper.TextureDecoder.Tests/Formats/";
private const string GenericOutputNamespace = OutputNamespace + ".Generic";
private const string GenericOutputFolder = OutputFolder + "Generic/";
private static readonly char[] channels = ['R', 'G', 'B', 'A'];
internal static void GenerateTestClasses()
{
Directory.CreateDirectory(OutputFolder);
MakeGenericColorTests();
MakeLosslessColorTests();
foreach (GenerationData data in Program.dataList)
{
using StringWriter streamWriter = new();
using IndentedTextWriter textWriter = new IndentedTextWriter(streamWriter, "\t");
streamWriter.NewLine = "\n";
textWriter.NewLine = "\n";
textWriter.WriteGeneratedCodeWarning();
textWriter.WriteLineNoTabs();
textWriter.WriteNamespaceUsings(data.ColorType.Namespace);
textWriter.WriteFileScopedNamespace();
textWriter.WriteClassDefinitionOpen(data);
textWriter.Indent++;
textWriter.WriteCorrectSizeTest(data);
textWriter.WriteLineNoTabs();
if (data.ColorType != typeof(ColorRGB9e5))//Rounding errors cause these to fail
{
textWriter.WritePropertySymmetryTests(data);
textWriter.WriteIndependenceTests(data);
}
textWriter.WriteGetMethodMatchesProperties();
textWriter.WriteLineNoTabs();
textWriter.WriteMethodSymmetryTest();
textWriter.WriteLineNoTabs();
textWriter.WriteMakeRandomColor(data);
textWriter.WriteLineNoTabs();
textWriter.WriteMakeRandomValue(data);
foreach (GenerationData otherData in Program.dataList)
{
if (otherData.Contains(data))
{
textWriter.WriteLineNoTabs();
textWriter.WriteLosslessConversionTest(data, otherData);
}
}
foreach (CSharpPrimitives.Data channelData in CSharpPrimitives.List)
{
Type channelType = channelData.Type;
string channelTypeName = channelData.LangName;
if (CSharpPrimitives.IsFloatingPoint(channelType) != CSharpPrimitives.IsFloatingPoint(data.ChannelType))
{
continue;
}
if (channelType == typeof(decimal) || data.ChannelType == typeof(decimal))
{
continue;
}
if (channelData.Size < CSharpPrimitives.Dictionary[data.ChannelType].Size)
{
continue;
}
textWriter.WriteLineNoTabs();
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorRGBA<byte>)}<{channelTypeName}>", channelTypeName);
if (!data.AlphaChannel)
{
textWriter.WriteLineNoTabs();
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorRGB<byte>)}<{channelTypeName}>", channelTypeName);
if (!data.BlueChannel)
{
textWriter.WriteLineNoTabs();
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorRG<byte>)}<{channelTypeName}>", channelTypeName);
if (!data.GreenChannel)
{
textWriter.WriteLineNoTabs();
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorR<byte>)}<{channelTypeName}>", channelTypeName);
}
}
}
if (!data.RedChannel && !data.GreenChannel && !data.BlueChannel)
{
textWriter.WriteLineNoTabs();
textWriter.WriteLosslessConversionTest(data.ColorType.Name, data.ChannelTypeName, $"{nameof(ColorA<byte>)}<{channelTypeName}>", channelTypeName);
}
}
textWriter.Indent--;
textWriter.WriteClassDefinitionClose();
textWriter.Flush();
streamWriter.Flush();
string outputFilePath = $"{OutputFolder}{data.ColorType.Name}Tests.g.cs";
File.WriteAllText(outputFilePath, streamWriter.ToString());
Console.WriteLine(data.ColorType.Name);
}
}
private static void MakeGenericColorTests()
{
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(GenericOutputFolder, "GenericColorTests");
writer.WriteGeneratedCodeWarning();
writer.WriteLine();
writer.WriteUsing(typeof(ColorR<>).Namespace!);
writer.WriteLine();
writer.WriteFileScopedNamespace(GenericOutputNamespace);
foreach (string channeName in CSharpPrimitives.List.Select(d => d.LangName))
{
foreach (string colorName in GetGenericColorNames())
{
writer.WriteLine($"[TestFixture(TypeArgs = new Type[] {{ typeof({colorName}<{channeName}>), typeof({channeName}) }})]");
}
}
writer.WriteLine("partial class GenericColorTests<TColor, TChannel>");
using (new CurlyBrackets(writer))
{
}
}
internal static IEnumerable<string> GetGenericColorNames()
{
yield return nameof(ColorR<byte>);
yield return nameof(ColorRG<byte>);
yield return nameof(ColorRGB<byte>);
yield return nameof(ColorRGBA<byte>);
yield return nameof(ColorA<byte>);
}
private static void MakeLosslessColorTests()
{
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(GenericOutputFolder, "LosslessColorTests");
writer.WriteGeneratedCodeWarning();
writer.WriteLine();
writer.WriteUsing(typeof(ColorR<>).Namespace!);
writer.WriteLine();
writer.WriteFileScopedNamespace(GenericOutputNamespace);
foreach (CSharpPrimitives.Data t1 in CSharpPrimitives.List)
{
foreach (CSharpPrimitives.Data t2 in CSharpPrimitives.List)
{
if (t1.IsFloatingPoint != t2.IsFloatingPoint)
{
// one is floating point, the other is not
continue;
}
if (t1.Type == typeof(decimal) || t2.Type == typeof(decimal))
{
// decimal has rounding errors when converting to/from double and float
continue;
}
if (t1.Size > t2.Size)
{
continue;
}
writer.WriteLine($"[TestFixture(TypeArgs = new Type[] {{ typeof({t1.LangName}), typeof({t2.LangName}) }})]");
}
}
writer.WriteLine("partial class LosslessColorTests<T1, T2>");
using (new CurlyBrackets(writer))
{
}
}
private static void WriteClassDefinitionClose(this IndentedTextWriter textWriter)
{
textWriter.WriteLine("}");
}
private static void WriteClassDefinitionOpen(this IndentedTextWriter textWriter, GenerationData data)
{
textWriter.WriteLine($"public partial class {data.ColorType.Name}Tests");
textWriter.WriteLine("{");
}
private static void WriteCorrectSizeTest(this IndentedTextWriter textWriter, GenerationData data)
{
textWriter.WriteLine("[Test]");
textWriter.WriteLine("public void CorrectSizeTest()");
textWriter.WriteLine("{");
textWriter.Indent += 1;
textWriter.WriteLine($"Assert.That(Unsafe.SizeOf<{data.ColorType.Name}>(), Is.EqualTo({data.ColorSize}));");
textWriter.Indent -= 1;
textWriter.WriteLine("}");
}
private static void WriteFileScopedNamespace(this IndentedTextWriter textWriter)
{
textWriter.WriteLine($"namespace {OutputNamespace};");
textWriter.WriteLine();//An empty line after the namespace declaration
}
private static void WriteGetMethodMatchesProperties(this IndentedTextWriter textWriter)
{
textWriter.WriteLine("[Test]");
textWriter.WriteLine($"public void GetMethodMatchesProperties()");
textWriter.WriteLine("{");
textWriter.Indent += 1;
textWriter.WriteLine("var color = MakeRandomColor();");
textWriter.WriteLine("color.GetChannels(out var r, out var g, out var b, out var a);");
textWriter.WriteLine("Assert.Multiple(() =>");
textWriter.WriteLine("{");
textWriter.Indent += 1;
foreach (char channel in channels)
{
textWriter.WriteLine($"Assert.That(color.{channel}, Is.EqualTo({char.ToLowerInvariant(channel)}));");
}
textWriter.Indent -= 1;
textWriter.WriteLine("});");
textWriter.Indent -= 1;
textWriter.WriteLine("}");
}
private static void WriteIndependenceTest(this IndentedTextWriter textWriter, GenerationData data, char channel)
{
textWriter.WriteLine("[Test]");
textWriter.WriteLine($"public void ChannelsAreIndependent_{channel}()");
textWriter.WriteLine("{");
textWriter.Indent += 1;
textWriter.WriteLine("var color = MakeRandomColor();");
foreach (char otherChannel in channels.Where(c => c != channel))
{
textWriter.WriteLine($"var {char.ToLowerInvariant(otherChannel)} = color.{otherChannel};");
}
textWriter.WriteLine($"color.{channel} = MakeRandomValue();");
textWriter.WriteLine("Assert.Multiple(() =>");
textWriter.WriteLine("{");
textWriter.Indent += 1;
foreach (char otherChannel in channels.Where(c => c != channel))
{
textWriter.WriteLine($"Assert.That(color.{otherChannel}, Is.EqualTo({char.ToLowerInvariant(otherChannel)}));");
}
textWriter.Indent -= 1;
textWriter.WriteLine("});");
textWriter.Indent -= 1;
textWriter.WriteLine("}");
}
private static void WriteIndependenceTests(this IndentedTextWriter textWriter, GenerationData data)
{
foreach (char channel in channels)
{
textWriter.WriteIndependenceTest(data, channel);
textWriter.WriteLine();
}
}
private static void WriteLosslessConversionTest(this IndentedTextWriter textWriter, GenerationData currentData, GenerationData containingData)
{
textWriter.WriteLosslessConversionTest(currentData.ColorType.Name, currentData.ChannelTypeName, containingData.ColorType.Name, containingData.ChannelTypeName);
}
private static void WriteLosslessConversionTest(this IndentedTextWriter writer, string t1Color, string t1Channel, string t2Color, string t2Channel)
{
writer.WriteLine("[Test]");
writer.WriteLine($"public void ConversionIsLosslessTo{t2Color.Replace("<","_").Replace(">", "")}()");
using (new CurlyBrackets(writer))
{
writer.WriteLine($"LosslessConversion.Assert<{t1Color}, {t1Channel}, {t2Color}, {t2Channel}>();");
}
}
private static void WriteMakeRandomColor(this IndentedTextWriter textWriter, GenerationData data)
{
textWriter.WriteLine($"public static {data.ColorType.Name} MakeRandomColor() => ColorRandom<{data.ColorType.Name}, {data.ChannelTypeName}>.MakeRandomColor();");
}
private static void WriteMakeRandomValue(this IndentedTextWriter textWriter, GenerationData data)
{
textWriter.WriteLine($"public static {data.ChannelTypeName} MakeRandomValue() => ColorRandom<{data.ColorType.Name}, {data.ChannelTypeName}>.MakeRandomValue();");
}
private static void WriteMethodSymmetryTest(this IndentedTextWriter textWriter)
{
textWriter.WriteLine("[Test]");
textWriter.WriteLine($"public void MethodsAreSymmetric()");
textWriter.WriteLine("{");
textWriter.Indent += 1;
textWriter.WriteLine("var color = MakeRandomColor();");
textWriter.WriteLine("color.GetChannels(out var r, out var g, out var b, out var a);");
textWriter.WriteLine("color.SetChannels(r, g, b, a);");
textWriter.WriteLine("Assert.Multiple(() =>");
textWriter.WriteLine("{");
textWriter.Indent += 1;
foreach (char channel in channels)
{
textWriter.WriteLine($"Assert.That(color.{channel}, Is.EqualTo({char.ToLowerInvariant(channel)}));");
}
textWriter.Indent -= 1;
textWriter.WriteLine("});");
textWriter.Indent -= 1;
textWriter.WriteLine("}");
}
private static void WriteNamespaceUsings(this IndentedTextWriter textWriter, string? colorNamespace)
{
textWriter.WriteLine($"using {colorNamespace};");
textWriter.WriteLine($"using {typeof(System.Runtime.CompilerServices.Unsafe).Namespace};");
textWriter.WriteLine();//An empty line after the usings
}
private static void WritePropertySymmetryTest(this IndentedTextWriter textWriter, GenerationData data, char channel)
{
textWriter.WriteLine("[Test]");
textWriter.WriteLine($"public void PropertyIsSymmetric_{channel}()");
textWriter.WriteLine("{");
textWriter.Indent += 1;
textWriter.WriteLine("var color = MakeRandomColor();");
char variableName = char.ToLowerInvariant(channel);
textWriter.WriteLine($"var {variableName} = color.{channel};");
textWriter.WriteLine($"color.{channel} = {variableName};");
textWriter.WriteLine($"Assert.That(color.{channel}, Is.EqualTo({variableName}));");
textWriter.Indent -= 1;
textWriter.WriteLine("}");
}
private static void WritePropertySymmetryTests(this IndentedTextWriter textWriter, GenerationData data)
{
foreach (char channel in channels)
{
textWriter.WritePropertySymmetryTest(data, channel);
textWriter.WriteLine();
}
}
}
@@ -0,0 +1,129 @@
using AssetRipper.Text.SourceGeneration;
using System.CodeDom.Compiler;
namespace AssetRipper.TextureDecoder.TestGenerator;
internal static class TextureDataClassGenerator
{
private const string TestFilesFolder = $"{nameof(TestFileFolders)}.{nameof(TestFileFolders.AndroidTestFiles)}";
private const string TextureType = "TextureType";
private const string TextureFormat = "TextureFormat";
private const string ITexture = "ITexture";
private const string Namespace = "AssetRipper.TextureDecoder.Tests";
public static void Generate()
{
IReadOnlyList<TextureMetadata> metadataList = LoadMetadata(TestFileFolders.AndroidTestFiles);
GenerateTextureType(metadataList);
GenerateTextureFormat(metadataList);
GenerateITexture();
GenerateAndroidTextures(metadataList);
}
private static void GenerateTextureType(IReadOnlyList<TextureMetadata> metadataList)
{
GenerateEnum(TextureType, metadataList.Select(m => m.Type).Distinct().Order());
}
private static void GenerateTextureFormat(IReadOnlyList<TextureMetadata> metadataList)
{
GenerateEnum(TextureFormat, metadataList.Select(m => m.Format).Distinct().Order());
}
private static void GenerateEnum(string name, IEnumerable<string?> values)
{
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(ProjectFolders.TestProject, name);
writer.WriteGeneratedCodeWarning();
writer.WriteLine();
writer.WriteFileScopedNamespace(Namespace);
writer.WriteLine();
writer.WriteLine($"public enum {name}");
using (new CurlyBrackets(writer))
{
foreach (string? value in values)
{
writer.WriteLine($"{value},");
}
}
}
private static void GenerateITexture()
{
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(ProjectFolders.TestProject, ITexture);
writer.WriteGeneratedCodeWarning();
writer.WriteLine();
writer.WriteFileScopedNamespace(Namespace);
writer.WriteLine();
writer.WriteLine($"public partial interface {ITexture}");
using (new CurlyBrackets(writer))
{
writer.WriteLine($"static abstract {TextureType} {nameof(TextureMetadata.Type)} {{ get; }}");
writer.WriteLine($"static abstract {TextureFormat} {nameof(TextureMetadata.Format)} {{ get; }}");
writer.WriteLine($"static abstract int {nameof(TextureMetadata.FileSize)} {{ get; }}");
writer.WriteLine($"static abstract int {nameof(TextureMetadata.ImageSize)} {{ get; }}");
writer.WriteLine($"static abstract int {nameof(TextureMetadata.ImageCount)} {{ get; }}");
writer.WriteLine($"static abstract bool {nameof(TextureMetadata.Mips)} {{ get; }}");
writer.WriteLine($"static abstract int {nameof(TextureMetadata.Width)} {{ get; }}");
writer.WriteLine($"static abstract int {nameof(TextureMetadata.Height)} {{ get; }}");
writer.WriteLine("static abstract string Path { get; }");
writer.WriteLine("static abstract byte[] Data { get; }");
}
}
private static IReadOnlyList<TextureMetadata> LoadMetadata(string directory)
{
string[] jsonFiles = Directory.GetFiles(directory, "*.json");
List<TextureMetadata> metadataList = new(jsonFiles.Length);
foreach (string jsonFile in jsonFiles)
{
metadataList.Add(TextureMetadata.FromFile(jsonFile));
}
return metadataList;
}
private static void GenerateAndroidTextures(IReadOnlyList<TextureMetadata> metadataList)
{
const string AndroidTextures = "AndroidTextures";
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(ProjectFolders.TestProject, AndroidTextures);
writer.WriteGeneratedCodeWarning();
writer.WriteLine();
writer.WriteFileScopedNamespace(Namespace);
writer.WriteLine();
writer.WriteLine($"public static partial class {AndroidTextures}");
using (new CurlyBrackets(writer))
{
foreach (TextureMetadata metadata in metadataList)
{
string withOrWithout = metadata.Mips ? "with" : "without";
writer.WriteSummaryDocumentation($"{metadata.Format} {metadata.Width}x{metadata.Height} {withOrWithout} mips");
writer.WriteLine($"public sealed class {metadata.Name} : {ITexture}");
using (new CurlyBrackets(writer))
{
writer.WriteLine($"public const {TextureType} {nameof(TextureMetadata.Type)} = {TextureType}.{metadata.Type};");
writer.WriteLine($"public const {TextureFormat} {nameof(TextureMetadata.Format)} = {TextureFormat}.{metadata.Format};");
writer.WriteLine($"public const int {nameof(TextureMetadata.FileSize)} = {metadata.FileSize};");
writer.WriteLine($"public const int {nameof(TextureMetadata.ImageSize)} = {metadata.ImageSize};");
writer.WriteLine($"public const int {nameof(TextureMetadata.ImageCount)} = {metadata.ImageCount};");
writer.WriteLine($"public const bool {nameof(TextureMetadata.Mips)} = {metadata.Mips.ToString().ToLowerInvariant()};");
writer.WriteLine($"public const int {nameof(TextureMetadata.Width)} = {metadata.Width};");
writer.WriteLine($"public const int {nameof(TextureMetadata.Height)} = {metadata.Height};");
writer.WriteLine($"public const string Path = {TestFilesFolder} + \"{metadata.Name}\";");
writer.WriteSummaryDocumentation($"Length: {metadata.FileSize}");
writer.WriteLine("public static byte[] Data => File.ReadAllBytes(Path);");
writer.WriteLine();
writer.WriteLine($"private {metadata.Name}() {{ }}");
writer.WriteLine($"static {TextureType} {ITexture}.{nameof(TextureMetadata.Type)} => {nameof(TextureMetadata.Type)};");
writer.WriteLine($"static {TextureFormat} {ITexture}.{nameof(TextureMetadata.Format)} => {nameof(TextureMetadata.Format)};");
writer.WriteLine($"static int {ITexture}.{nameof(TextureMetadata.FileSize)} => {nameof(TextureMetadata.FileSize)};");
writer.WriteLine($"static int {ITexture}.{nameof(TextureMetadata.ImageSize)} => {nameof(TextureMetadata.ImageSize)};");
writer.WriteLine($"static int {ITexture}.{nameof(TextureMetadata.ImageCount)} => {nameof(TextureMetadata.ImageCount)};");
writer.WriteLine($"static bool {ITexture}.{nameof(TextureMetadata.Mips)} => {nameof(TextureMetadata.Mips)};");
writer.WriteLine($"static int {ITexture}.{nameof(TextureMetadata.Width)} => {nameof(TextureMetadata.Width)};");
writer.WriteLine($"static int {ITexture}.{nameof(TextureMetadata.Height)} => {nameof(TextureMetadata.Height)};");
writer.WriteLine($"static string {ITexture}.Path => Path;");
}
writer.WriteLine();
}
}
}
}
@@ -0,0 +1,27 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetRipper.TextureDecoder.TestGenerator;
public sealed class TextureMetadata
{
[JsonIgnore]
public string? Name { get; set; }
public string? Type { get; set; }
public string? Format { get; set; }
public int FileSize { get; set; }
public int ImageSize { get; set; }
public int ImageCount { get; set; }
public bool Mips { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public static TextureMetadata FromFile(string path)
{
using FileStream stream = File.OpenRead(path);
TextureMetadata result = JsonSerializer.Deserialize(stream, TextureMetadataSerializerContext.Default.TextureMetadata)
?? throw new NullReferenceException(nameof(TextureMetadata));
result.Name = Path.GetFileNameWithoutExtension(path);
return result;
}
}
@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;
namespace AssetRipper.TextureDecoder.TestGenerator;
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(TextureMetadata))]
internal sealed partial class TextureMetadataSerializerContext : JsonSerializerContext
{
}
@@ -0,0 +1 @@
global using AssetRipper.TextureDecoder.Paths;
@@ -0,0 +1,720 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Tests;
public static partial class AndroidTextures
{
/// <summary>
/// BGRA32_14 512x512 without mips
/// </summary>
public sealed class Logo_00 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.BGRA32_14;
public const int FileSize = 1048576;
public const int ImageSize = 1048576;
public const int ImageCount = 1;
public const bool Mips = false;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_00";
/// <summary>
/// Length: 1048576
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_00() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_12x12 512x512 with mips
/// </summary>
public sealed class Logo_01 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_12x12;
public const int FileSize = 40112;
public const int ImageSize = 40112;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_01";
/// <summary>
/// Length: 40112
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_01() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_RGB_10x10 512x512 with mips
/// </summary>
public sealed class Logo_02 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_RGB_10x10;
public const int FileSize = 57952;
public const int ImageSize = 57952;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_02";
/// <summary>
/// Length: 57952
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_02() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_RGB_8x8 512x512 with mips
/// </summary>
public sealed class Logo_03 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_RGB_8x8;
public const int FileSize = 87424;
public const int ImageSize = 87424;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_03";
/// <summary>
/// Length: 87424
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_03() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_6x6 512x512 with mips
/// </summary>
public sealed class Logo_04 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_6x6;
public const int FileSize = 158432;
public const int ImageSize = 158432;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_04";
/// <summary>
/// Length: 158432
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_04() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_5x5 512x512 with mips
/// </summary>
public sealed class Logo_05 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_5x5;
public const int FileSize = 227680;
public const int ImageSize = 227680;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_05";
/// <summary>
/// Length: 227680
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_05() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_RGB_4x4 512x512 with mips
/// </summary>
public sealed class Logo_06 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_RGB_4x4;
public const int FileSize = 349552;
public const int ImageSize = 349552;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_06";
/// <summary>
/// Length: 349552
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_06() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ETC2_RGBA8 512x512 with mips
/// </summary>
public sealed class Logo_07 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ETC2_RGBA8;
public const int FileSize = 349552;
public const int ImageSize = 349552;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_07";
/// <summary>
/// Length: 349552
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_07() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ETC2_RGBA1 512x512 with mips
/// </summary>
public sealed class Logo_08 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ETC2_RGBA1;
public const int FileSize = 174776;
public const int ImageSize = 174776;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_08";
/// <summary>
/// Length: 174776
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_08() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// PVRTC_4BPP_RGBA 512x512 with mips
/// </summary>
public sealed class Logo_09 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.PVRTC_4BPP_RGBA;
public const int FileSize = 174848;
public const int ImageSize = 174848;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_09";
/// <summary>
/// Length: 174848
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_09() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// PVRTC_RGBA2 512x512 with mips
/// </summary>
public sealed class Logo_10 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.PVRTC_RGBA2;
public const int FileSize = 87488;
public const int ImageSize = 87488;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_10";
/// <summary>
/// Length: 87488
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_10() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ETC2_RGB 512x512 with mips
/// </summary>
public sealed class Logo_11 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ETC2_RGB;
public const int FileSize = 174776;
public const int ImageSize = 174776;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_11";
/// <summary>
/// Length: 174776
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_11() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ETC_RGB4 512x512 with mips
/// </summary>
public sealed class Logo_12 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ETC_RGB4;
public const int FileSize = 174776;
public const int ImageSize = 174776;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_12";
/// <summary>
/// Length: 174776
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_12() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// PVRTC_4BPP_RGB 512x512 with mips
/// </summary>
public sealed class Logo_13 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.PVRTC_4BPP_RGB;
public const int FileSize = 174848;
public const int ImageSize = 174848;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_13";
/// <summary>
/// Length: 174848
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_13() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// PVRTC_2BPP_RGB 512x512 with mips
/// </summary>
public sealed class Logo_14 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.PVRTC_2BPP_RGB;
public const int FileSize = 87488;
public const int ImageSize = 87488;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_14";
/// <summary>
/// Length: 87488
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_14() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// EAC_RG 512x512 with mips
/// </summary>
public sealed class Logo_15 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.EAC_RG;
public const int FileSize = 349552;
public const int ImageSize = 349552;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_15";
/// <summary>
/// Length: 349552
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_15() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// EAC_R 512x512 with mips
/// </summary>
public sealed class Logo_16 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.EAC_R;
public const int FileSize = 174776;
public const int ImageSize = 174776;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_16";
/// <summary>
/// Length: 174776
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_16() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_HDR_12x12 512x512 with mips
/// </summary>
public sealed class Logo_17 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_HDR_12x12;
public const int FileSize = 40112;
public const int ImageSize = 40112;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_17";
/// <summary>
/// Length: 40112
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_17() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_HDR_10x10 512x512 with mips
/// </summary>
public sealed class Logo_18 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_HDR_10x10;
public const int FileSize = 57952;
public const int ImageSize = 57952;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_18";
/// <summary>
/// Length: 57952
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_18() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_HDR_8x8 512x512 with mips
/// </summary>
public sealed class Logo_19 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_HDR_8x8;
public const int FileSize = 87424;
public const int ImageSize = 87424;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_19";
/// <summary>
/// Length: 87424
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_19() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_HDR_6x6 512x512 with mips
/// </summary>
public sealed class Logo_20 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_HDR_6x6;
public const int FileSize = 158432;
public const int ImageSize = 158432;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_20";
/// <summary>
/// Length: 158432
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_20() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_HDR_5x5 512x512 with mips
/// </summary>
public sealed class Logo_21 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_HDR_5x5;
public const int FileSize = 227680;
public const int ImageSize = 227680;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_21";
/// <summary>
/// Length: 227680
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_21() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
/// <summary>
/// ASTC_HDR_4x4 512x512 with mips
/// </summary>
public sealed class Logo_22 : ITexture
{
public const TextureType Type = TextureType.Texture2D;
public const TextureFormat Format = TextureFormat.ASTC_HDR_4x4;
public const int FileSize = 349552;
public const int ImageSize = 349552;
public const int ImageCount = 1;
public const bool Mips = true;
public const int Width = 512;
public const int Height = 512;
public const string Path = TestFileFolders.AndroidTestFiles + "Logo_22";
/// <summary>
/// Length: 349552
/// </summary>
public static byte[] Data => File.ReadAllBytes(Path);
private Logo_22() { }
static TextureType ITexture.Type => Type;
static TextureFormat ITexture.Format => Format;
static int ITexture.FileSize => FileSize;
static int ITexture.ImageSize => ImageSize;
static int ITexture.ImageCount => ImageCount;
static bool ITexture.Mips => Mips;
static int ITexture.Width => Width;
static int ITexture.Height => Height;
static string ITexture.Path => Path;
}
}
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="4.0.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.10.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetRipper.TextureDecoder.Paths\AssetRipper.TextureDecoder.Paths.csproj" />
<ProjectReference Include="..\AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,77 @@
using AssetRipper.TextureDecoder.Astc;
namespace AssetRipper.TextureDecoder.Tests;
public sealed class AstcTests
{
private const double MaxMeanDeviation4x4 = 0.2;
private const double MaxStandardDeviation4x4 = 1;
private const double MaxMeanDeviation5x5 = 0.2;
private const double MaxStandardDeviation5x5 = 1;
private const double MaxMeanDeviation6x6 = 0.2;
private const double MaxStandardDeviation6x6 = 1;
private const double MaxMeanDeviation8x8 = 0.2;
private const double MaxStandardDeviation8x8 = 2;
private const double MaxMeanDeviation10x10 = 0.2;
private const double MaxStandardDeviation10x10 = 2.5;
private const double MaxMeanDeviation12x12 = 0.3;
private const double MaxStandardDeviation12x12 = 3;
[Test]
public void DecompressAstc4x4() => AssertCorrectDecompression<AndroidTextures.Logo_06>(4, 4, MaxMeanDeviation4x4, MaxStandardDeviation4x4);
[Test]
public void DecompressAstc5x5() => AssertCorrectDecompression<AndroidTextures.Logo_05>(5, 5, MaxMeanDeviation5x5, MaxStandardDeviation5x5);
[Test]
public void DecompressAstc6x6() => AssertCorrectDecompression<AndroidTextures.Logo_04>(6, 6, MaxMeanDeviation6x6, MaxStandardDeviation6x6);
[Test]
public void DecompressAstc8x8() => AssertCorrectDecompression<AndroidTextures.Logo_03>(8, 8, MaxMeanDeviation8x8, MaxStandardDeviation8x8);
[Test]
public void DecompressAstc10x10() => AssertCorrectDecompression<AndroidTextures.Logo_02>(10, 10, MaxMeanDeviation10x10, MaxStandardDeviation10x10);
[Test]
public void DecompressAstc12x12() => AssertCorrectDecompression<AndroidTextures.Logo_01>(12, 12, MaxMeanDeviation12x12, MaxStandardDeviation12x12);
[Test]
[Ignore("High dynamic range textures are not supported yet.")]
public void DecompressAstc4x4HDR() => AssertCorrectDecompression<AndroidTextures.Logo_22>(4, 4, MaxMeanDeviation4x4, MaxStandardDeviation4x4);
[Test]
[Ignore("High dynamic range textures are not supported yet.")]
public void DecompressAstc5x5HDR() => AssertCorrectDecompression<AndroidTextures.Logo_21>(5, 5, MaxMeanDeviation5x5, MaxStandardDeviation5x5);
[Test]
[Ignore("High dynamic range textures are not supported yet.")]
public void DecompressAstc6x6HDR() => AssertCorrectDecompression<AndroidTextures.Logo_20>(6, 6, MaxMeanDeviation6x6, MaxStandardDeviation6x6);
[Test]
[Ignore("High dynamic range textures are not supported yet.")]
public void DecompressAstc8x8HDR() => AssertCorrectDecompression<AndroidTextures.Logo_19>(8, 8, MaxMeanDeviation8x8, MaxStandardDeviation8x8);
[Test]
[Ignore("High dynamic range textures are not supported yet.")]
public void DecompressAstc10x10HDR() => AssertCorrectDecompression<AndroidTextures.Logo_18>(10, 10, MaxMeanDeviation10x10, MaxStandardDeviation10x10);
[Test]
[Ignore("High dynamic range textures are not supported yet.")]
public void DecompressAstc12x12HDR() => AssertCorrectDecompression<AndroidTextures.Logo_17>(4, 4, MaxMeanDeviation12x12, MaxStandardDeviation12x12);
private static void AssertCorrectDecompression<T>(int blockWidth, int blockHeight, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
{
ReadOnlySpan<byte> data = T.Data;
int bytesRead = AstcDecoder.DecodeASTC(data, T.Width, T.Height, blockWidth, blockHeight, out byte[] decompressedData);
if (!T.Mips)
{
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
ByteArrayDeviation.AssertMinimalDeviation(decompressedData, AndroidTextures.Logo_00.Data, maxMeanDeviation, maxStandardDeviation);
}
}
@@ -0,0 +1,29 @@
namespace AssetRipper.TextureDecoder.Tests
{
public sealed class AtcTests
{
[Test]
public void DecompressAtcRgb4Test()
{
byte[] data = File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgb4");
int bytesRead = Atc.AtcDecoder.DecompressAtcRgb4(data, 256, 256, out byte[] decodedData);
Assert.Multiple(() =>
{
Assert.That(bytesRead, Is.EqualTo(data.Length));
Assert.That(decodedData, Is.EqualTo(File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgb4_decoded")));
});
}
[Test]
public void DecompressAtcRgba8Test()
{
byte[] data = File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgba8");
int bytesRead = Atc.AtcDecoder.DecompressAtcRgba8(data, 256, 256, out byte[] decodedData);
Assert.Multiple(() =>
{
Assert.That(bytesRead, Is.EqualTo(data.Length));
Assert.That(decodedData, Is.EqualTo(File.ReadAllBytes(TestFileFolders.AtcTestFiles + "test.atc_rgba8_decoded")));
});
}
}
}
+148
View File
@@ -0,0 +1,148 @@
using AssetRipper.TextureDecoder.Bc;
using AssetRipper.TextureDecoder.Dxt;
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Runtime.CompilerServices;
namespace AssetRipper.TextureDecoder.Tests
{
public sealed class BcTests
{
private const double MaxMeanDeviationBc6h = 0.5;
private const double MaxMeanDeviationBc7 = 0.1;//Bc7 is more accurate than Bc6h
private const double MaxStandardDeviationBc6h = 0.8;
private const double MaxStandardDeviationBc7 = 0.8;
private static byte[] originalBgra32LogoData = Array.Empty<byte>();
[SetUp]
public void InitializeOriginalData()
{
originalBgra32LogoData = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bgra32");
}
[Test]
public void DecompressDXT1Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt1");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
{
int bytesRead = Bc1.Decompress(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
DxtDecoder.DecompressDXT1(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
totalBytesRead += bytesRead;
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
}
Assert.That(totalBytesRead + 16, Is.EqualTo(data.Length));
}
[Test]
public void DecompressDXT3Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt3");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
{
int bytesRead = Bc2.Decompress(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
DxtDecoder.DecompressDXT3(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
totalBytesRead += bytesRead;
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
}
Assert.That(totalBytesRead + 32, Is.EqualTo(data.Length));
}
[Test]
public void DecompressDXT5Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt5");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
{
int bytesRead = Bc3.Decompress(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
DxtDecoder.DecompressDXT5(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
totalBytesRead += bytesRead;
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
}
Assert.That(totalBytesRead + 32, Is.EqualTo(data.Length));
}
[Test]
public void DecompressBC4Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc4");
int bytesRead = Bc4.Decompress(data, 512, 512, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[Test]
public void DecompressBC5Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc5");
int bytesRead = Bc5.Decompress(data, 512, 512, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[TestCase("test.bc6h_fast")]
[TestCase("test.bc6h_normal")]
[TestCase("test.bc6h_best")]
public void DecompressBC6HTest(string fileName)
{
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + fileName, 512, 512, false);
}
[TestCase("test.bc7_fast")]
[TestCase("test.bc7_normal")]
[TestCase("test.bc7_best")]
public void DecompressBC7Test(string fileName)
{
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + fileName, 512, 512);
}
[Test]
[Ignore("Not implemented")]
public void BC7PartialBlockTest([Range(1, 4)] int width, [Range(1, 4)] int height)
{
Assert.Multiple(() =>
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc7_best").AsSpan()[..Bc7.BlockSize];
int bytesRead = Bc7.Decompress(data, width, height, out byte[] decodedData);
Assert.That(bytesRead, Is.EqualTo(data.Length));
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA32>()));
});
}
private static void AssertCorrectBC6HDecompression(string path, int width, int height, bool isSigned)
{
ReadOnlySpan<byte> data = File.ReadAllBytes(path);
int bytesRead = Bc6h.Decompress(data, width, height, isSigned, out byte[] decodedData);
Assert.That(bytesRead, Is.EqualTo(data.Length));
ByteArrayDeviation.AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc6h, MaxStandardDeviationBc6h);
}
private static void AssertCorrectBC7Decompression(string path, int width, int height)
{
ReadOnlySpan<byte> data = File.ReadAllBytes(path);
int bytesRead = Bc7.Decompress(data, width, height, out byte[] decodedData);
Assert.That(bytesRead, Is.EqualTo(data.Length));
ByteArrayDeviation.AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc7, MaxStandardDeviationBc7);
}
private static void AssertAlmostEqual(byte[] array1, byte[] array2, int maxDifference = 2)
{
if (array1.Length != array2.Length)
{
Assert.Fail($"Differing array lengths\nArray 1 length {array1.Length}\nArray 2 length {array2.Length}");
return;
}
for (int i = 0; i < array1.Length; i++)
{
int difference = Math.Abs(array1[i] - array2[i]);
if (difference > maxDifference)
{
Assert.Fail($"Differing values at index {i}\nArray 1: {array1[i]}\nArray 2: {array2[i]}");
return;
}
}
}
}
}
@@ -0,0 +1,40 @@
namespace AssetRipper.TextureDecoder.Tests;
internal static class ByteArrayDeviation
{
internal static void AssertMinimalDeviation(byte[] decoded, byte[] original, double maxMeanDeviation, double maxStandardDeviation)
{
if (decoded.Length != original.Length)
{
Assert.Fail($"Differing array lengths\nDecoded length {decoded.Length}\nOriginal length {original.Length}");
return;
}
long differenceSum = 0;
for (int i = 0; i < decoded.Length; i++)
{
differenceSum += decoded[i] - original[i];
}
double mean = differenceSum / (double)decoded.Length;
double sumOfSquaredDeviations = 0;
for (int i = 0; i < decoded.Length; i++)
{
double deviation = decoded[i] - original[i] - mean;
sumOfSquaredDeviations += deviation * deviation;
}
double standardDeviation = Math.Sqrt(sumOfSquaredDeviations / (decoded.Length - 1));
//Not sure if Bessel's correction is needed here, but it doesn't hurt, especially since length is around 1 million in the current use.
Assert.Multiple(() =>
{
Assert.That(mean, Is.LessThan(maxMeanDeviation), "Mean too far positive");
Assert.That(mean, Is.GreaterThan(-maxMeanDeviation), "Mean too far negative");
Assert.That(standardDeviation, Is.LessThan(maxStandardDeviation), "Standard deviation too large");
});
}
}
@@ -0,0 +1,44 @@
namespace AssetRipper.TextureDecoder.Tests
{
public sealed class DxtTests
{
[Test]
public void DecompressDXT1Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt1");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = Dxt.DxtDecoder.DecompressDXT1(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void DecompressDXT3Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt3");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = Dxt.DxtDecoder.DecompressDXT3(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void DecompressDXT5Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt5");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = Dxt.DxtDecoder.DecompressDXT5(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
}
}
@@ -0,0 +1,102 @@
using AssetRipper.TextureDecoder.Etc;
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
namespace AssetRipper.TextureDecoder.Tests;
public sealed class EtcTests
{
private static DecodingDelegate ETCDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC(data, width, height, out decompressedData);
private static DecodingDelegate ETC2Delegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC2(data, width, height, out decompressedData);
private static DecodingDelegate ETC2A1Delegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC2A1(data, width, height, out decompressedData);
private static DecodingDelegate ETC2A8Delegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressETC2A8(data, width, height, out decompressedData);
private static DecodingDelegate EACRSignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRSigned(data, width, height, out decompressedData);
private static DecodingDelegate EACRUnsignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRUnsigned(data, width, height, out decompressedData);
private static DecodingDelegate EACRGSignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRGSigned(data, width, height, out decompressedData);
private static DecodingDelegate EACRGUnsignedDelegate { get; } = (ArraySegment<byte> data, int width, int height, out byte[] decompressedData) => EtcDecoder.DecompressEACRGUnsigned(data, width, height, out decompressedData);
[Test]
public void DecompressETCTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc", ETCDelegate);
[Test]
public void DecompressETC2Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2", ETC2Delegate);
[Test]
public void DecompressETC2A1Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2a1", ETC2A1Delegate);
[Test]
public void DecompressETC2A8Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2a8", ETC2A8Delegate);
[Test]
public void DecompressEACRSignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_r", EACRSignedDelegate);
[Test]
public void DecompressEACRUnsignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_r", EACRUnsignedDelegate);
[Test]
public void DecompressEACRGSignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_rg", EACRGSignedDelegate);
[Test]
public void DecompressEACRGUnsignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_rg", EACRGUnsignedDelegate);
[Test]
public void CorrectnessETCTest() => AssertCorrectDecompression<AndroidTextures.Logo_12, ColorRGB<byte>>(ETCDelegate, .05, 2.1);
[Test]
public void CorrectnessETC2Test() => AssertCorrectDecompression<AndroidTextures.Logo_11, ColorRGB<byte>>(ETC2Delegate, .15, 1.6);
[Test]
public void CorrectnessETC2A1Test() => AssertCorrectDecompression<AndroidTextures.Logo_08>(ETC2A1Delegate, .15, 1.7);
[Test]
public void CorrectnessETC2A8Test() => AssertCorrectDecompression<AndroidTextures.Logo_07>(ETC2A8Delegate, .15, 1.6);
[Test]
public void CorrectnessEACRUnsignedTest() => AssertCorrectDecompression<AndroidTextures.Logo_16, ColorR<byte>>(EACRUnsignedDelegate, .05, .5);
[Test]
public void CorrectnessEACRGUnsignedTest() => AssertCorrectDecompression<AndroidTextures.Logo_15, ColorRG<byte>>(EACRGUnsignedDelegate, .1, .6);
private delegate int DecodingDelegate(ArraySegment<byte> data, int width, int height, out byte[] decompressedData);
private static void AssertCorrectByteCountReadFor256SquareWithMips(string path, DecodingDelegate decoder)
{
byte[] data = File.ReadAllBytes(path);
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = decoder.Invoke(new ArraySegment<byte>(data, totalBytesRead, data.Length - totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
private static void AssertCorrectDecompression<T>(DecodingDelegate decoder, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
{
AssertCorrectDecompression<T, ColorRGBA<byte>>(decoder, maxMeanDeviation, maxStandardDeviation);
}
private static void AssertCorrectDecompression<TTexture, TColor>(DecodingDelegate decoder, double maxMeanDeviation, double maxStandardDeviation) where TTexture : ITexture where TColor : unmanaged, IColor<TColor, byte>
{
byte[] data = TTexture.Data;
int bytesRead = decoder.Invoke(new ArraySegment<byte>(data), TTexture.Width, TTexture.Height, out byte[] decompressedData);
if (!TTexture.Mips)
{
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
byte[] comparisonData = AndroidTextures.Logo_00.Data;
//Remove unused color channels
if (typeof(TColor) != typeof(ColorBGRA32) && typeof(TColor) != typeof(ColorRGBA<byte>))
{
Span<ColorBGRA32> pixels = MemoryMarshal.Cast<byte, ColorBGRA32>((Span<byte>)comparisonData);
for (int i = 0; i < pixels.Length; i++)
{
pixels[i] = pixels[i].Convert<ColorBGRA32, byte, TColor, byte>().Convert<TColor, byte, ColorBGRA32, byte>();
}
}
ByteArrayDeviation.AssertMinimalDeviation(decompressedData, comparisonData, maxMeanDeviation, maxStandardDeviation);
}
}
@@ -0,0 +1,232 @@
// Auto-generated code. Do not modify manually.
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Runtime.CompilerServices;
namespace AssetRipper.TextureDecoder.Tests.Formats;
public partial class ColorARGB16Tests
{
[Test]
public void CorrectSizeTest()
{
Assert.That(Unsafe.SizeOf<ColorARGB16>(), Is.EqualTo(2));
}
[Test]
public void PropertyIsSymmetric_R()
{
var color = MakeRandomColor();
var r = color.R;
color.R = r;
Assert.That(color.R, Is.EqualTo(r));
}
[Test]
public void PropertyIsSymmetric_G()
{
var color = MakeRandomColor();
var g = color.G;
color.G = g;
Assert.That(color.G, Is.EqualTo(g));
}
[Test]
public void PropertyIsSymmetric_B()
{
var color = MakeRandomColor();
var b = color.B;
color.B = b;
Assert.That(color.B, Is.EqualTo(b));
}
[Test]
public void PropertyIsSymmetric_A()
{
var color = MakeRandomColor();
var a = color.A;
color.A = a;
Assert.That(color.A, Is.EqualTo(a));
}
[Test]
public void ChannelsAreIndependent_R()
{
var color = MakeRandomColor();
var g = color.G;
var b = color.B;
var a = color.A;
color.R = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_G()
{
var color = MakeRandomColor();
var r = color.R;
var b = color.B;
var a = color.A;
color.G = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_B()
{
var color = MakeRandomColor();
var r = color.R;
var g = color.G;
var a = color.A;
color.B = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_A()
{
var color = MakeRandomColor();
var r = color.R;
var g = color.G;
var b = color.B;
color.A = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
});
}
[Test]
public void GetMethodMatchesProperties()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void MethodsAreSymmetric()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
color.SetChannels(r, g, b, a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
public static ColorARGB16 MakeRandomColor() => ColorRandom<ColorARGB16, byte>.MakeRandomColor();
public static byte MakeRandomValue() => ColorRandom<ColorARGB16, byte>.MakeRandomValue();
[Test]
public void ConversionIsLosslessToColorARGB32()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorARGB32, byte>();
}
[Test]
public void ConversionIsLosslessToColorBGRA32()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorBGRA32, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_sbyte()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<sbyte>, sbyte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_byte()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<byte>, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_short()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<short>, short>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_ushort()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<ushort>, ushort>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_int()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<int>, int>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_uint()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<uint>, uint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_nint()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<nint>, nint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_nuint()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<nuint>, nuint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_long()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<long>, long>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_ulong()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<ulong>, ulong>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_Int128()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<Int128>, Int128>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_UInt128()
{
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<UInt128>, UInt128>();
}
}
@@ -0,0 +1,232 @@
// Auto-generated code. Do not modify manually.
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Runtime.CompilerServices;
namespace AssetRipper.TextureDecoder.Tests.Formats;
public partial class ColorARGB32Tests
{
[Test]
public void CorrectSizeTest()
{
Assert.That(Unsafe.SizeOf<ColorARGB32>(), Is.EqualTo(4));
}
[Test]
public void PropertyIsSymmetric_R()
{
var color = MakeRandomColor();
var r = color.R;
color.R = r;
Assert.That(color.R, Is.EqualTo(r));
}
[Test]
public void PropertyIsSymmetric_G()
{
var color = MakeRandomColor();
var g = color.G;
color.G = g;
Assert.That(color.G, Is.EqualTo(g));
}
[Test]
public void PropertyIsSymmetric_B()
{
var color = MakeRandomColor();
var b = color.B;
color.B = b;
Assert.That(color.B, Is.EqualTo(b));
}
[Test]
public void PropertyIsSymmetric_A()
{
var color = MakeRandomColor();
var a = color.A;
color.A = a;
Assert.That(color.A, Is.EqualTo(a));
}
[Test]
public void ChannelsAreIndependent_R()
{
var color = MakeRandomColor();
var g = color.G;
var b = color.B;
var a = color.A;
color.R = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_G()
{
var color = MakeRandomColor();
var r = color.R;
var b = color.B;
var a = color.A;
color.G = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_B()
{
var color = MakeRandomColor();
var r = color.R;
var g = color.G;
var a = color.A;
color.B = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_A()
{
var color = MakeRandomColor();
var r = color.R;
var g = color.G;
var b = color.B;
color.A = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
});
}
[Test]
public void GetMethodMatchesProperties()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void MethodsAreSymmetric()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
color.SetChannels(r, g, b, a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
public static ColorARGB32 MakeRandomColor() => ColorRandom<ColorARGB32, byte>.MakeRandomColor();
public static byte MakeRandomValue() => ColorRandom<ColorARGB32, byte>.MakeRandomValue();
[Test]
public void ConversionIsLosslessToColorARGB32()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorARGB32, byte>();
}
[Test]
public void ConversionIsLosslessToColorBGRA32()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorBGRA32, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_sbyte()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<sbyte>, sbyte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_byte()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<byte>, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_short()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<short>, short>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_ushort()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<ushort>, ushort>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_int()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<int>, int>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_uint()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<uint>, uint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_nint()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<nint>, nint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_nuint()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<nuint>, nuint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_long()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<long>, long>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_ulong()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<ulong>, ulong>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_Int128()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<Int128>, Int128>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_UInt128()
{
LosslessConversion.Assert<ColorARGB32, byte, ColorRGBA<UInt128>, UInt128>();
}
}
@@ -0,0 +1,232 @@
// Auto-generated code. Do not modify manually.
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Runtime.CompilerServices;
namespace AssetRipper.TextureDecoder.Tests.Formats;
public partial class ColorBGRA32Tests
{
[Test]
public void CorrectSizeTest()
{
Assert.That(Unsafe.SizeOf<ColorBGRA32>(), Is.EqualTo(4));
}
[Test]
public void PropertyIsSymmetric_R()
{
var color = MakeRandomColor();
var r = color.R;
color.R = r;
Assert.That(color.R, Is.EqualTo(r));
}
[Test]
public void PropertyIsSymmetric_G()
{
var color = MakeRandomColor();
var g = color.G;
color.G = g;
Assert.That(color.G, Is.EqualTo(g));
}
[Test]
public void PropertyIsSymmetric_B()
{
var color = MakeRandomColor();
var b = color.B;
color.B = b;
Assert.That(color.B, Is.EqualTo(b));
}
[Test]
public void PropertyIsSymmetric_A()
{
var color = MakeRandomColor();
var a = color.A;
color.A = a;
Assert.That(color.A, Is.EqualTo(a));
}
[Test]
public void ChannelsAreIndependent_R()
{
var color = MakeRandomColor();
var g = color.G;
var b = color.B;
var a = color.A;
color.R = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_G()
{
var color = MakeRandomColor();
var r = color.R;
var b = color.B;
var a = color.A;
color.G = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_B()
{
var color = MakeRandomColor();
var r = color.R;
var g = color.G;
var a = color.A;
color.B = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_A()
{
var color = MakeRandomColor();
var r = color.R;
var g = color.G;
var b = color.B;
color.A = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
});
}
[Test]
public void GetMethodMatchesProperties()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void MethodsAreSymmetric()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
color.SetChannels(r, g, b, a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
public static ColorBGRA32 MakeRandomColor() => ColorRandom<ColorBGRA32, byte>.MakeRandomColor();
public static byte MakeRandomValue() => ColorRandom<ColorBGRA32, byte>.MakeRandomValue();
[Test]
public void ConversionIsLosslessToColorARGB32()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorARGB32, byte>();
}
[Test]
public void ConversionIsLosslessToColorBGRA32()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorBGRA32, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_sbyte()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<sbyte>, sbyte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_byte()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<byte>, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_short()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<short>, short>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_ushort()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<ushort>, ushort>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_int()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<int>, int>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_uint()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<uint>, uint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_nint()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<nint>, nint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_nuint()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<nuint>, nuint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_long()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<long>, long>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_ulong()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<ulong>, ulong>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_Int128()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<Int128>, Int128>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_UInt128()
{
LosslessConversion.Assert<ColorBGRA32, byte, ColorRGBA<UInt128>, UInt128>();
}
}
@@ -0,0 +1,304 @@
// Auto-generated code. Do not modify manually.
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Runtime.CompilerServices;
namespace AssetRipper.TextureDecoder.Tests.Formats;
public partial class ColorRGB16Tests
{
[Test]
public void CorrectSizeTest()
{
Assert.That(Unsafe.SizeOf<ColorRGB16>(), Is.EqualTo(2));
}
[Test]
public void PropertyIsSymmetric_R()
{
var color = MakeRandomColor();
var r = color.R;
color.R = r;
Assert.That(color.R, Is.EqualTo(r));
}
[Test]
public void PropertyIsSymmetric_G()
{
var color = MakeRandomColor();
var g = color.G;
color.G = g;
Assert.That(color.G, Is.EqualTo(g));
}
[Test]
public void PropertyIsSymmetric_B()
{
var color = MakeRandomColor();
var b = color.B;
color.B = b;
Assert.That(color.B, Is.EqualTo(b));
}
[Test]
public void PropertyIsSymmetric_A()
{
var color = MakeRandomColor();
var a = color.A;
color.A = a;
Assert.That(color.A, Is.EqualTo(a));
}
[Test]
public void ChannelsAreIndependent_R()
{
var color = MakeRandomColor();
var g = color.G;
var b = color.B;
var a = color.A;
color.R = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_G()
{
var color = MakeRandomColor();
var r = color.R;
var b = color.B;
var a = color.A;
color.G = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_B()
{
var color = MakeRandomColor();
var r = color.R;
var g = color.G;
var a = color.A;
color.B = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_A()
{
var color = MakeRandomColor();
var r = color.R;
var g = color.G;
var b = color.B;
color.A = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
});
}
[Test]
public void GetMethodMatchesProperties()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void MethodsAreSymmetric()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
color.SetChannels(r, g, b, a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
public static ColorRGB16 MakeRandomColor() => ColorRandom<ColorRGB16, byte>.MakeRandomColor();
public static byte MakeRandomValue() => ColorRandom<ColorRGB16, byte>.MakeRandomValue();
[Test]
public void ConversionIsLosslessToColorARGB32()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorARGB32, byte>();
}
[Test]
public void ConversionIsLosslessToColorBGRA32()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorBGRA32, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_sbyte()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<sbyte>, sbyte>();
}
[Test]
public void ConversionIsLosslessToColorRGB_sbyte()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<sbyte>, sbyte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_byte()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<byte>, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGB_byte()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<byte>, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_short()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<short>, short>();
}
[Test]
public void ConversionIsLosslessToColorRGB_short()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<short>, short>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_ushort()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<ushort>, ushort>();
}
[Test]
public void ConversionIsLosslessToColorRGB_ushort()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<ushort>, ushort>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_int()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<int>, int>();
}
[Test]
public void ConversionIsLosslessToColorRGB_int()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<int>, int>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_uint()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<uint>, uint>();
}
[Test]
public void ConversionIsLosslessToColorRGB_uint()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<uint>, uint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_nint()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<nint>, nint>();
}
[Test]
public void ConversionIsLosslessToColorRGB_nint()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<nint>, nint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_nuint()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<nuint>, nuint>();
}
[Test]
public void ConversionIsLosslessToColorRGB_nuint()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<nuint>, nuint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_long()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<long>, long>();
}
[Test]
public void ConversionIsLosslessToColorRGB_long()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<long>, long>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_ulong()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<ulong>, ulong>();
}
[Test]
public void ConversionIsLosslessToColorRGB_ulong()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<ulong>, ulong>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_Int128()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<Int128>, Int128>();
}
[Test]
public void ConversionIsLosslessToColorRGB_Int128()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<Int128>, Int128>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_UInt128()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<UInt128>, UInt128>();
}
[Test]
public void ConversionIsLosslessToColorRGB_UInt128()
{
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<UInt128>, UInt128>();
}
}
@@ -0,0 +1,60 @@
// Auto-generated code. Do not modify manually.
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Runtime.CompilerServices;
namespace AssetRipper.TextureDecoder.Tests.Formats;
public partial class ColorRGB9e5Tests
{
[Test]
public void CorrectSizeTest()
{
Assert.That(Unsafe.SizeOf<ColorRGB9e5>(), Is.EqualTo(4));
}
[Test]
public void GetMethodMatchesProperties()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void MethodsAreSymmetric()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
color.SetChannels(r, g, b, a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
public static ColorRGB9e5 MakeRandomColor() => ColorRandom<ColorRGB9e5, double>.MakeRandomColor();
public static double MakeRandomValue() => ColorRandom<ColorRGB9e5, double>.MakeRandomValue();
[Test]
public void ConversionIsLosslessToColorRGBA_double()
{
LosslessConversion.Assert<ColorRGB9e5, double, ColorRGBA<double>, double>();
}
[Test]
public void ConversionIsLosslessToColorRGB_double()
{
LosslessConversion.Assert<ColorRGB9e5, double, ColorRGB<double>, double>();
}
}
@@ -0,0 +1,232 @@
// Auto-generated code. Do not modify manually.
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Runtime.CompilerServices;
namespace AssetRipper.TextureDecoder.Tests.Formats;
public partial class ColorRGBA16Tests
{
[Test]
public void CorrectSizeTest()
{
Assert.That(Unsafe.SizeOf<ColorRGBA16>(), Is.EqualTo(2));
}
[Test]
public void PropertyIsSymmetric_R()
{
var color = MakeRandomColor();
var r = color.R;
color.R = r;
Assert.That(color.R, Is.EqualTo(r));
}
[Test]
public void PropertyIsSymmetric_G()
{
var color = MakeRandomColor();
var g = color.G;
color.G = g;
Assert.That(color.G, Is.EqualTo(g));
}
[Test]
public void PropertyIsSymmetric_B()
{
var color = MakeRandomColor();
var b = color.B;
color.B = b;
Assert.That(color.B, Is.EqualTo(b));
}
[Test]
public void PropertyIsSymmetric_A()
{
var color = MakeRandomColor();
var a = color.A;
color.A = a;
Assert.That(color.A, Is.EqualTo(a));
}
[Test]
public void ChannelsAreIndependent_R()
{
var color = MakeRandomColor();
var g = color.G;
var b = color.B;
var a = color.A;
color.R = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_G()
{
var color = MakeRandomColor();
var r = color.R;
var b = color.B;
var a = color.A;
color.G = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_B()
{
var color = MakeRandomColor();
var r = color.R;
var g = color.G;
var a = color.A;
color.B = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_A()
{
var color = MakeRandomColor();
var r = color.R;
var g = color.G;
var b = color.B;
color.A = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
});
}
[Test]
public void GetMethodMatchesProperties()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void MethodsAreSymmetric()
{
var color = MakeRandomColor();
color.GetChannels(out var r, out var g, out var b, out var a);
color.SetChannels(r, g, b, a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
public static ColorRGBA16 MakeRandomColor() => ColorRandom<ColorRGBA16, byte>.MakeRandomColor();
public static byte MakeRandomValue() => ColorRandom<ColorRGBA16, byte>.MakeRandomValue();
[Test]
public void ConversionIsLosslessToColorARGB32()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorARGB32, byte>();
}
[Test]
public void ConversionIsLosslessToColorBGRA32()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorBGRA32, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_sbyte()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<sbyte>, sbyte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_byte()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<byte>, byte>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_short()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<short>, short>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_ushort()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<ushort>, ushort>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_int()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<int>, int>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_uint()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<uint>, uint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_nint()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<nint>, nint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_nuint()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<nuint>, nuint>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_long()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<long>, long>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_ulong()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<ulong>, ulong>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_Int128()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<Int128>, Int128>();
}
[Test]
public void ConversionIsLosslessToColorRGBA_UInt128()
{
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<UInt128>, UInt128>();
}
}
@@ -0,0 +1,18 @@
using AssetRipper.TextureDecoder.Rgb;
namespace AssetRipper.TextureDecoder.Tests.Formats;
internal static class ColorRandom<TColor, TChannel> where TColor : unmanaged, IColor<TChannel> where TChannel : unmanaged
{
public static TChannel MakeRandomValue()
{
ulong value = TestContext.CurrentContext.Random.NextULong();
return NumericConversion.Convert<ulong, TChannel>(value);
}
public static TColor MakeRandomColor()
{
TColor color = default;
color.SetChannels(MakeRandomValue(), MakeRandomValue(), MakeRandomValue(), MakeRandomValue());
return color;
}
}
@@ -0,0 +1,146 @@
using AssetRipper.TextureDecoder.Rgb;
using System.Runtime.CompilerServices;
namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
internal partial class GenericColorTests<TColor, TChannel> where TColor : unmanaged, IColor<TChannel> where TChannel : unmanaged
{
[Test]
public void CorrectSizeTest()
{
Assert.That(Unsafe.SizeOf<TColor>(), Is.EqualTo(Color.GetChannelCount<TColor>() * Unsafe.SizeOf<TChannel>()));
}
[Test]
public void PropertyIsSymmetric_R()
{
TColor color = MakeRandomColor();
TChannel r = color.R;
color.R = r;
Assert.That(color.R, Is.EqualTo(r));
}
[Test]
public void PropertyIsSymmetric_G()
{
TColor color = MakeRandomColor();
TChannel g = color.G;
color.G = g;
Assert.That(color.G, Is.EqualTo(g));
}
[Test]
public void PropertyIsSymmetric_B()
{
TColor color = MakeRandomColor();
TChannel b = color.B;
color.B = b;
Assert.That(color.B, Is.EqualTo(b));
}
[Test]
public void PropertyIsSymmetric_A()
{
TColor color = MakeRandomColor();
TChannel a = color.A;
color.A = a;
Assert.That(color.A, Is.EqualTo(a));
}
[Test]
public void ChannelsAreIndependent_R()
{
TColor color = MakeRandomColor();
TChannel g = color.G;
TChannel b = color.B;
TChannel a = color.A;
color.R = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_G()
{
TColor color = MakeRandomColor();
TChannel r = color.R;
TChannel b = color.B;
TChannel a = color.A;
color.G = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_B()
{
TColor color = MakeRandomColor();
TChannel r = color.R;
TChannel g = color.G;
TChannel a = color.A;
color.B = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void ChannelsAreIndependent_A()
{
TColor color = MakeRandomColor();
TChannel r = color.R;
TChannel g = color.G;
TChannel b = color.B;
color.A = MakeRandomValue();
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
});
}
[Test]
public void GetMethodMatchesProperties()
{
TColor color = MakeRandomColor();
color.GetChannels(out TChannel r, out TChannel g, out TChannel b, out TChannel a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
[Test]
public void MethodsAreSymmetric()
{
TColor color = MakeRandomColor();
color.GetChannels(out TChannel r, out TChannel g, out TChannel b, out TChannel a);
color.SetChannels(r, g, b, a);
Assert.Multiple(() =>
{
Assert.That(color.R, Is.EqualTo(r));
Assert.That(color.G, Is.EqualTo(g));
Assert.That(color.B, Is.EqualTo(b));
Assert.That(color.A, Is.EqualTo(a));
});
}
private static TChannel MakeRandomValue() => ColorRandom<TColor, TChannel>.MakeRandomValue();
private static TColor MakeRandomColor() => ColorRandom<TColor, TChannel>.MakeRandomColor();
}
@@ -0,0 +1,93 @@
// Auto-generated code. Do not modify manually.
using AssetRipper.TextureDecoder.Rgb.Formats;
namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<sbyte>), typeof(sbyte) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<sbyte>), typeof(sbyte) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<sbyte>), typeof(sbyte) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<sbyte>), typeof(sbyte) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<sbyte>), typeof(sbyte) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<byte>), typeof(byte) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<byte>), typeof(byte) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<byte>), typeof(byte) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<byte>), typeof(byte) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<byte>), typeof(byte) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<short>), typeof(short) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<short>), typeof(short) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<short>), typeof(short) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<short>), typeof(short) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<short>), typeof(short) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<ushort>), typeof(ushort) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<ushort>), typeof(ushort) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<ushort>), typeof(ushort) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<ushort>), typeof(ushort) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<ushort>), typeof(ushort) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<int>), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<int>), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<int>), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<int>), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<int>), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<uint>), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<uint>), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<uint>), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<uint>), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<uint>), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<nint>), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<nint>), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<nint>), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<nint>), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<nint>), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<nuint>), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<nuint>), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<nuint>), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<nuint>), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<nuint>), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<long>), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<long>), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<long>), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<long>), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<long>), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<ulong>), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<ulong>), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<ulong>), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<ulong>), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<ulong>), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<Int128>), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<Int128>), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<Int128>), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<Int128>), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<Int128>), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<UInt128>), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<UInt128>), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<UInt128>), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<UInt128>), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<UInt128>), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<Half>), typeof(Half) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<Half>), typeof(Half) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<Half>), typeof(Half) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<Half>), typeof(Half) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<Half>), typeof(Half) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<float>), typeof(float) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<float>), typeof(float) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<float>), typeof(float) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<float>), typeof(float) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<float>), typeof(float) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<NFloat>), typeof(NFloat) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<NFloat>), typeof(NFloat) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<NFloat>), typeof(NFloat) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<NFloat>), typeof(NFloat) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<NFloat>), typeof(NFloat) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<double>), typeof(double) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<double>), typeof(double) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<double>), typeof(double) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<double>), typeof(double) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<double>), typeof(double) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorR<decimal>), typeof(decimal) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<decimal>), typeof(decimal) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<decimal>), typeof(decimal) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<decimal>), typeof(decimal) })]
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<decimal>), typeof(decimal) })]
partial class GenericColorTests<TColor, TChannel>
{
}
@@ -0,0 +1,38 @@
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Numerics;
namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
/// <summary>
/// Tests for lossless color conversions.
/// </summary>
/// <typeparam name="T1">The "smaller" type that can be losslessly converted into <typeparamref name="T2"/>.</typeparam>
/// <typeparam name="T2">The "larger" type capable of storing <typeparamref name="T1"/> losslessly.</typeparam>
internal partial class LosslessColorTests<T1, T2>
where T1 : unmanaged, INumberBase<T1>, IMinMaxValue<T1>
where T2 : unmanaged, INumberBase<T2>, IMinMaxValue<T2>
{
[Test]
public void ConversionIsLossless_R_T1_R_T2() => LosslessConversion.Assert<ColorR<T1>, T1, ColorR<T2>, T2>();
[Test]
public void ConversionIsLossless_R_T1_RG_T2() => LosslessConversion.Assert<ColorR<T1>, T1, ColorRG<T2>, T2>();
[Test]
public void ConversionIsLossless_R_T1_RGB_T2() => LosslessConversion.Assert<ColorR<T1>, T1, ColorRGB<T2>, T2>();
[Test]
public void ConversionIsLossless_R_T1_RGBA_T2() => LosslessConversion.Assert<ColorR<T1>, T1, ColorRGBA<T2>, T2>();
[Test]
public void ConversionIsLossless_RG_T1_RGB_T2() => LosslessConversion.Assert<ColorRG<T1>, T1, ColorRGB<T2>, T2>();
[Test]
public void ConversionIsLossless_RG_T1_RGBA_T2() => LosslessConversion.Assert<ColorRG<T1>, T1, ColorRGBA<T2>, T2>();
[Test]
public void ConversionIsLossless_RGB_T1_RGBA_T2() => LosslessConversion.Assert<ColorRGB<T1>, T1, ColorRGBA<T2>, T2>();
[Test]
public void ConversionIsLossless_A_T1_RGBA_T2() => LosslessConversion.Assert<ColorA<T1>, T1, ColorRGBA<T2>, T2>();
}
@@ -0,0 +1,102 @@
// Auto-generated code. Do not modify manually.
using AssetRipper.TextureDecoder.Rgb.Formats;
namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(sbyte) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(byte) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(short) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(ushort) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(sbyte) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(byte) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(short) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(ushort) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(short) })]
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(ushort) })]
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(short) })]
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(ushort) })]
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(int) })]
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(uint) })]
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(nint) })]
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(nuint) })]
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ulong), typeof(long) })]
[TestFixture(TypeArgs = new Type[] { typeof(ulong), typeof(ulong) })]
[TestFixture(TypeArgs = new Type[] { typeof(ulong), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(ulong), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(Int128), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(Int128), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(UInt128), typeof(Int128) })]
[TestFixture(TypeArgs = new Type[] { typeof(UInt128), typeof(UInt128) })]
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(Half) })]
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(float) })]
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(NFloat) })]
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(double) })]
[TestFixture(TypeArgs = new Type[] { typeof(float), typeof(float) })]
[TestFixture(TypeArgs = new Type[] { typeof(float), typeof(NFloat) })]
[TestFixture(TypeArgs = new Type[] { typeof(float), typeof(double) })]
[TestFixture(TypeArgs = new Type[] { typeof(NFloat), typeof(NFloat) })]
[TestFixture(TypeArgs = new Type[] { typeof(NFloat), typeof(double) })]
[TestFixture(TypeArgs = new Type[] { typeof(double), typeof(double) })]
partial class LosslessColorTests<T1, T2>
{
}
@@ -0,0 +1,34 @@
using AssetRipper.TextureDecoder.Rgb;
using System.Numerics;
namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
[TestFixture(TypeArgs = [typeof(sbyte), typeof(byte)])]
[TestFixture(TypeArgs = [typeof(short), typeof(ushort)])]
[TestFixture(TypeArgs = [typeof(int), typeof(uint)])]
[TestFixture(TypeArgs = [typeof(long), typeof(ulong)])]
[TestFixture(TypeArgs = [typeof(Int128), typeof(UInt128)])]
internal class NumericConversionExtremesTests<TSigned, TUnsigned>
where TSigned : unmanaged, IMinMaxValue<TSigned>
where TUnsigned : unmanaged, IMinMaxValue<TUnsigned>
{
[Test]
public void SignedToUnsignedExtremesAreCorrect()
{
Assert.Multiple(() =>
{
Assert.That(NumericConversion.Convert<TSigned, TUnsigned>(TSigned.MinValue), Is.EqualTo(TUnsigned.MinValue));
Assert.That(NumericConversion.Convert<TSigned, TUnsigned>(TSigned.MaxValue), Is.EqualTo(TUnsigned.MaxValue));
});
}
[Test]
public void UnsignedToSignedExtremesAreCorrect()
{
Assert.Multiple(() =>
{
Assert.That(NumericConversion.Convert<TUnsigned, TSigned>(TUnsigned.MinValue), Is.EqualTo(TSigned.MinValue));
Assert.That(NumericConversion.Convert<TUnsigned, TSigned>(TUnsigned.MaxValue), Is.EqualTo(TSigned.MaxValue));
});
}
}
@@ -0,0 +1,17 @@
using AssetRipper.TextureDecoder.Rgb;
namespace AssetRipper.TextureDecoder.Tests.Formats;
internal static class LosslessConversion
{
internal static void Assert<TColor1, TChannel1, TColor2, TChannel2>()
where TChannel1 : unmanaged
where TChannel2 : unmanaged
where TColor1 : unmanaged, IColor<TChannel1>
where TColor2 : unmanaged, IColor<TChannel2>
{
TColor1 original = ColorRandom<TColor1, TChannel1>.MakeRandomColor();
TColor2 converted = original.Convert<TColor1, TChannel1, TColor2, TChannel2>();
TColor1 convertedBack = converted.Convert<TColor2, TChannel2, TColor1, TChannel1>();
NUnit.Framework.Assert.That(convertedBack, Is.EqualTo(original));
}
}
@@ -0,0 +1,17 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Tests;
public partial interface ITexture
{
static abstract TextureType Type { get; }
static abstract TextureFormat Format { get; }
static abstract int FileSize { get; }
static abstract int ImageSize { get; }
static abstract int ImageCount { get; }
static abstract bool Mips { get; }
static abstract int Width { get; }
static abstract int Height { get; }
static abstract string Path { get; }
static abstract byte[] Data { get; }
}
@@ -0,0 +1,242 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
namespace AssetRipper.TextureDecoder.Tests
{
public sealed class NewRgbTests
{
[Test]
public void ConvertA8Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.alpha8");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = RgbConverter.A8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
RgbConverter.Convert<ColorA<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
totalBytesRead += bytesRead;
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertARGB16Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.argb4444");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = RgbConverter.Convert<ColorARGB16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGB24Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb24");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = RgbConverter.RGB24ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRGB<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
totalBytesRead += bytesRead;
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGBA32Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba32");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = RgbConverter.RGBA32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
totalBytesRead += bytesRead;
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertARGB32Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.argb32");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = RgbConverter.ARGB32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
totalBytesRead += bytesRead;
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGB16Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb565");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = RgbConverter.RGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
totalBytesRead += bytesRead;
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertR16Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.r16");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = RgbConverter.Convert<ColorR<ushort>, ushort, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGBA16Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba4444");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = RgbConverter.Convert<ColorRGBA16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRG16Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg16");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = RgbConverter.RG16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
totalBytesRead += bytesRead;
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertR8Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.r8");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = RgbConverter.R8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
totalBytesRead += bytesRead;
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRHalfTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rhalf");
RgbConverter.RHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
[Test]
public void ConvertRGHalfTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rghalf");
RgbConverter.RGHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
[Test]
public void ConvertRGBAHalfTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbahalf");
RgbConverter.RGBAHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
[Test]
public void ConvertRFloatTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rfloat");
RgbConverter.RFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
RgbConverter.Convert<ColorR<float>, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
[Test]
public void ConvertRGFloatTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgfloat");
RgbConverter.RGFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
[Test]
public void ConvertRGBAFloatTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbafloat");
RgbConverter.RGBAFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
[Test]
public void ConvertRGB9e5FloatTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb9e5float");
RgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
[Test]
public void ConvertRG32Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg32");
RgbConverter.RG32ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRG<ushort>, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
[Test]
public void ConvertRGB48Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb48");
RgbConverter.RGB48ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRGB<ushort>, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
[Test]
public void ConvertRGBA64Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba64");
RgbConverter.RGBA64ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
RgbConverter.Convert<ColorRGBA<ushort>, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
}
}
}
@@ -0,0 +1,42 @@
using AssetRipper.TextureDecoder.Pvrtc;
namespace AssetRipper.TextureDecoder.Tests;
public sealed class PvrtcTests
{
[Test]
public void DecompressPVRTCTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.PvrtcTestFiles + "test.pvrtc4");
int totalBytesRead = 0;
foreach (int size in new int[] { 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = PvrtcDecoder.DecompressPVRTC(data, size, size, false, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
private const double MaxMeanDeviation4 = 0.05;
private const double MaxStandardDeviation4 = 1.4;
private const double MaxMeanDeviation2 = 0.10;
private const double MaxStandardDeviation2 = 2.6;
[Test]
public void Decompress_4() => AssertCorrectDecompression<AndroidTextures.Logo_13>(false, MaxMeanDeviation4, MaxStandardDeviation4);
[Test]
public void Decompress_2() => AssertCorrectDecompression<AndroidTextures.Logo_14>(true, MaxMeanDeviation2, MaxStandardDeviation2);
private static void AssertCorrectDecompression<T>(bool do2bitMode, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
{
ReadOnlySpan<byte> data = T.Data;
int bytesRead = PvrtcDecoder.DecompressPVRTC(data, T.Width, T.Height, do2bitMode, out byte[] decompressedData);
if (!T.Mips)
{
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
ByteArrayDeviation.AssertMinimalDeviation(decompressedData, AndroidTextures.Logo_00.Data, maxMeanDeviation, maxStandardDeviation);
}
}
@@ -0,0 +1,176 @@
namespace AssetRipper.TextureDecoder.Tests
{
public sealed class RgbTests
{
[Test]
public void ConvertA8Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.alpha8");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = Rgb.RgbConverter.A8ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGB24Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb24");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = Rgb.RgbConverter.RGB24ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGBA32Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba32");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = Rgb.RgbConverter.RGBA32ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertARGB32Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.argb32");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = Rgb.RgbConverter.ARGB32ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGB16Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb565");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = Rgb.RgbConverter.RGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRG16Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg16");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = Rgb.RgbConverter.RG16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertR8Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.r8");
int totalBytesRead = 0;
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
{
int bytesRead = Rgb.RgbConverter.R8ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
totalBytesRead += bytesRead;
}
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRHalfTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rhalf");
int bytesRead = Rgb.RgbConverter.RHalfToBGRA32(data, 256, 256, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGHalfTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rghalf");
int bytesRead = Rgb.RgbConverter.RGHalfToBGRA32(data, 256, 256, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGBAHalfTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbahalf");
int bytesRead = Rgb.RgbConverter.RGBAHalfToBGRA32(data, 256, 256, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRFloatTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rfloat");
int bytesRead = Rgb.RgbConverter.RFloatToBGRA32(data, 256, 256, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGFloatTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgfloat");
int bytesRead = Rgb.RgbConverter.RGFloatToBGRA32(data, 256, 256, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGBAFloatTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgbafloat");
int bytesRead = Rgb.RgbConverter.RGBAFloatToBGRA32(data, 256, 256, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGB9e5FloatTest()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb9e5float");
int bytesRead = Rgb.RgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRG32Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rg32");
int bytesRead = Rgb.RgbConverter.RG32ToBGRA32(data, 512, 512, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGB48Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgb48");
int bytesRead = Rgb.RgbConverter.RGB48ToBGRA32(data, 512, 512, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
[Test]
public void ConvertRGBA64Test()
{
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.RgbTestFiles + "test.rgba64");
int bytesRead = Rgb.RgbConverter.RGBA64ToBGRA32(data, 512, 512, out _);
Assert.That(bytesRead, Is.EqualTo(data.Length));
}
}
}
@@ -0,0 +1,30 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Tests;
public enum TextureFormat
{
ASTC_12x12,
ASTC_5x5,
ASTC_6x6,
ASTC_HDR_10x10,
ASTC_HDR_12x12,
ASTC_HDR_4x4,
ASTC_HDR_5x5,
ASTC_HDR_6x6,
ASTC_HDR_8x8,
ASTC_RGB_10x10,
ASTC_RGB_4x4,
ASTC_RGB_8x8,
BGRA32_14,
EAC_R,
EAC_RG,
ETC_RGB4,
ETC2_RGB,
ETC2_RGBA1,
ETC2_RGBA8,
PVRTC_2BPP_RGB,
PVRTC_4BPP_RGB,
PVRTC_4BPP_RGBA,
PVRTC_RGBA2,
}
@@ -0,0 +1,8 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Tests;
public enum TextureType
{
Texture2D,
}
@@ -0,0 +1,3 @@
global using AssetRipper.TextureDecoder.Paths;
global using NUnit.Framework;
global using System.Runtime.InteropServices;
@@ -0,0 +1,24 @@
using AssetRipper.TextureDecoder.Rgb.Formats;
using AssetRipper.TextureDecoder.Yuy2;
using System.Runtime.CompilerServices;
namespace AssetRipper.TextureDecoder.Tests
{
public sealed class Yuy2Tests
{
private const int TestImageWidth = 256;
private const int TestImageHeight = 256;
[Test]
public void DecompressYUY2Test()
{
byte[] input = File.ReadAllBytes(TestFileFolders.Yuy2TestFiles + "test.yuy2");
int bytesRead = Yuy2Decoder.DecompressYUY2<ColorBGRA32, byte>(input, TestImageWidth, TestImageHeight, out byte[] decodedData);
Assert.Multiple(() =>
{
Assert.That(bytesRead, Is.EqualTo(input.Length));
Assert.That(decodedData, Has.Length.EqualTo(TestImageWidth * TestImageHeight * Unsafe.SizeOf<ColorBGRA32>()));
});
}
}
}
@@ -1,37 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<LangVersion>Latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsTrimmable>true</IsTrimmable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<Authors>ds5678</Authors>
<Company>AssetRipper</Company>
<Version>1.0.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<PackageId>TextureDecoder</PackageId>
<PackageId>AssetRipper.TextureDecoder</PackageId>
<PackageTags>C# Texture</PackageTags>
<RepositoryUrl>https://github.com/AssetRipper/TextureDecoder</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>git</RepositoryType>
<PackageProjectUrl>https://github.com/AssetRipper/TextureDecoder</PackageProjectUrl>
<Copyright>Copyright (c) 2022 ds5678</Copyright>
<Copyright>Copyright (c) 2022 - 2023 ds5678</Copyright>
<Description>Managed library for decoding textures in multiple formats.</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DocumentationFile>bin\TextureDecoder.xml</DocumentationFile>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Half" Version="1.0.0" />
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup>
<DocumentationFile>bin\TextureDecoder.xml</DocumentationFile>
</PropertyGroup>
</Project>
@@ -0,0 +1,35 @@
namespace AssetRipper.TextureDecoder.Astc;
public static partial class AstcDecoder
{
private struct BlockData
{
public int bw;
public int bh;
public int width;
public int height;
public int part_num;
public int dual_plane;
public int plane_selector;
public int weight_range;
/// <summary>
/// max: 120
/// </summary>
public int weight_num;
public IntBuffer4 cem;
public int cem_range;
/// <summary>
/// max: 32
/// </summary>
public int endpoint_value_num;
/// <summary>
/// 4 * 8
/// </summary>
public EndpointBuffer endpoints;
/// <summary>
/// 144 * 2
/// </summary>
public IntBuffer288 weights;
public IntBuffer144 partition;
}
}
@@ -0,0 +1,16 @@
namespace AssetRipper.TextureDecoder.Astc;
public static partial class AstcDecoder
{
[InlineArray(4)]
private struct EndpointBuffer
{
private Endpoint _element0;
[InlineArray(8)]
public struct Endpoint
{
private int _element0;
}
}
}
@@ -0,0 +1,10 @@
namespace AssetRipper.TextureDecoder.Astc;
public static partial class AstcDecoder
{
[InlineArray(144)]
private struct IntBuffer144
{
private int _element0;
}
}
@@ -0,0 +1,10 @@
namespace AssetRipper.TextureDecoder.Astc;
public static partial class AstcDecoder
{
[InlineArray(288)]
private struct IntBuffer288
{
private int _element0;
}
}
@@ -0,0 +1,10 @@
namespace AssetRipper.TextureDecoder.Astc;
public static partial class AstcDecoder
{
[InlineArray(4)]
private struct IntBuffer4
{
private int _element0;
}
}
@@ -0,0 +1,10 @@
namespace AssetRipper.TextureDecoder.Astc;
public static partial class AstcDecoder
{
private struct IntSeqData
{
public int bits;
public int nonbits;
}
}
File diff suppressed because it is too large. Load diff
@@ -1,106 +1,85 @@
using System;
using System.Runtime.CompilerServices;
using System.Buffers.Binary;
namespace TextureDecoder.Atc
namespace AssetRipper.TextureDecoder.Atc
{
public static class AtcDecoder
{
public static byte[] DecompressAtcRgb4(byte[] input, int width, int height)
public static int DecompressAtcRgb4(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
byte[] output = new byte[width * height * 4];
DecompressAtcRgb4(input, width, height, output);
return output;
output = new byte[width * height * 4];
return DecompressAtcRgb4(input, width, height, output);
}
public unsafe static void DecompressAtcRgb4(byte[] input, int width, int height, byte[] output)
{
fixed (byte* inputPtr = input)
{
fixed (byte* outputPtr = output)
{
DecompressAtcRgb4(inputPtr, width, height, outputPtr);
}
}
}
public unsafe static void DecompressAtcRgb4(byte* input, int width, int height, byte* output)
public static int DecompressAtcRgb4(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
uint* buf = stackalloc uint[16];
Span<uint> buf = stackalloc uint[16];
int inputOffset = 0;
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, input += 8)
for (int s = 0; s < bcw; s++, inputOffset += 8)
{
DecodeAtcRgb4Block(input, buf);
DecodeAtcRgb4Block(input.Slice(inputOffset, 8), buf);
int clen = s < bcw - 1 ? 4 : clen_last;
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
uint* bufPtr = buf;
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
outputPtr += width;
bufPtr += 4;
int outputOffset = t * 16 * width + s * 16 + i * width * sizeof(uint);
for (int j = 0; j < clen; j++)
{
BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(outputOffset + j * sizeof(uint)), buf[j + 4 * i]);
}
}
}
}
return inputOffset;
}
public static byte[] DecompressAtcRgba8(byte[] input, int width, int height)
public static int DecompressAtcRgba8(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
byte[] output = new byte[width * height * 4];
DecompressAtcRgba8(input, width, height, output);
return output;
output = new byte[width * height * 4];
return DecompressAtcRgba8(input, width, height, output);
}
public unsafe static void DecompressAtcRgba8(byte[] input, int width, int height, byte[] output)
{
fixed (byte* inputPtr = input)
{
fixed (byte* outputPtr = output)
{
DecompressAtcRgba8(inputPtr, width, height, outputPtr);
}
}
}
public unsafe static void DecompressAtcRgba8(byte* input, int width, int height, byte* output)
public static int DecompressAtcRgba8(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
uint* buf = stackalloc uint[16];
Span<uint> buf = stackalloc uint[16];
int inputOffset = 0;
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, input += 16)
for (int s = 0; s < bcw; s++, inputOffset += 16)
{
DecodeAtcRgba8Block(input, buf);
DecodeAtcRgba8Block(input.Slice(inputOffset, 16), buf);
int clen = s < bcw - 1 ? 4 : clen_last;
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
uint* bufPtr = buf;
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
outputPtr += width;
bufPtr += 4;
int outputOffset = t * 16 * width + s * 16 + i * width * sizeof(uint);
for (int j = 0; j < clen; j++)
{
BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(outputOffset + j * sizeof(uint)), buf[j + 4 * i]);
}
}
}
}
return inputOffset;
}
private unsafe static void DecodeAtcRgb4Block(byte* input, uint* output)
private static void DecodeAtcRgb4Block(ReadOnlySpan<byte> input, Span<uint> output)
{
int* colors = stackalloc int[16];
int c0 = *((ushort*)(input + 0));
int c1 = *((ushort*)(input + 2));
uint cindex = *((uint*)(input + 4));
Span<int> colors = stackalloc int[16];
int c0 = input.ReadAtOffset<ushort>(0);
int c1 = input.ReadAtOffset<ushort>(2);
uint cindex = input.ReadAtOffset<uint>(4);
DecodeColors(colors, c0, c1);
for (int i = 0; i < 4 * 4; i++)
{
uint cidx = cindex & 3;
int cidx = unchecked((int)(cindex & 3));
int cb = colors[cidx * 4 + 0];
int cg = colors[cidx * 4 + 1];
int cr = colors[cidx * 4 + 2];
@@ -109,18 +88,18 @@ namespace TextureDecoder.Atc
}
}
private unsafe static void DecodeAtcRgba8Block(byte* input, uint* output)
private static void DecodeAtcRgba8Block(ReadOnlySpan<byte> input, Span<uint> output)
{
int* alphas = stackalloc int[16];
ulong avalue = *((ulong*)input);
Span<int> alphas = stackalloc int[16];
ulong avalue = BinaryPrimitives.ReadUInt64LittleEndian(input);
int a0 = unchecked((int)(avalue >> 0) & 0xFF);
int a1 = unchecked((int)(avalue >> 8) & 0xFF);
ulong aindex = avalue >> 16;
int* colors = stackalloc int[16];
int c0 = *((ushort*)(input + 8));
int c1 = *((ushort*)(input + 10));
uint cindex = *((uint*)(input + 12));
Span<int> colors = stackalloc int[16];
int c0 = input.ReadAtOffset<ushort>(8);
int c1 = input.ReadAtOffset<ushort>(10);
uint cindex = input.ReadAtOffset<uint>(12);
DecodeColors(colors, c0, c1);
DecodeAlphas(alphas, a0, a1);
@@ -139,8 +118,14 @@ namespace TextureDecoder.Atc
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static void DecodeColors(int* colors, int c0, int c1)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static T ReadAtOffset<T>(this ReadOnlySpan<byte> input, int offset) where T : unmanaged
{
return MemoryMarshal.Read<T>(input.Slice(offset));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void DecodeColors(Span<int> colors, int c0, int c1)
{
if ((c0 & 0x8000) == 0)
{
@@ -180,8 +165,8 @@ namespace TextureDecoder.Atc
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static void DecodeAlphas(int* alphas, int a0, int a1)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void DecodeAlphas(Span<int> alphas, int a0, int a1)
{
alphas[0] = a0;
alphas[1] = a1;
@@ -205,14 +190,14 @@ namespace TextureDecoder.Atc
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static int Extend(int value, int from, int to)
{
// bit-pattern replicating scaling (can at most double the bits)
return (value << (to - from)) | (value >> (from * 2 - to));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static uint Color(int r, int g, int b, int a)
{
return unchecked((uint)(r << 16 | g << 8 | b | a << 24));
+36
View File
@@ -0,0 +1,36 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
namespace AssetRipper.TextureDecoder.Bc;
public static class Bc1
{
/// <summary>
/// The size of an encoded block, in bytes.
/// </summary>
public const int BlockSize = 8;
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
return Decompress(input, width, height, output);
}
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
int inputOffset = 0;
for (int i = 0; i < height; i += 4)
{
for (int j = 0; j < width; j += 4)
{
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
BcHelpers.DecompressBc1(input.Slice(inputOffset, BlockSize), output.Slice(outputOffset), width * 4);
inputOffset += BlockSize;
}
}
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
return inputOffset;
}
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
}
+36
View File
@@ -0,0 +1,36 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
namespace AssetRipper.TextureDecoder.Bc;
public static class Bc2
{
/// <summary>
/// The size of an encoded block, in bytes.
/// </summary>
public const int BlockSize = 16;
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
return Decompress(input, width, height, output);
}
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
int inputOffset = 0;
for (int i = 0; i < height; i += 4)
{
for (int j = 0; j < width; j += 4)
{
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
BcHelpers.DecompressBc2(input.Slice(inputOffset), output.Slice(outputOffset), width * 4);
inputOffset += BlockSize;
}
}
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
return inputOffset;
}
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
}
+36
View File
@@ -0,0 +1,36 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
namespace AssetRipper.TextureDecoder.Bc;
public static class Bc3
{
/// <summary>
/// The size of an encoded block, in bytes.
/// </summary>
public const int BlockSize = 16;
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
return Decompress(input, width, height, output);
}
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
int inputOffset = 0;
for (int i = 0; i < height; i += 4)
{
for (int j = 0; j < width; j += 4)
{
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
BcHelpers.DecompressBc3(input.Slice(inputOffset), output.Slice(outputOffset), width * 4);
inputOffset += BlockSize;
}
}
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
return inputOffset;
}
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
}
+41
View File
@@ -0,0 +1,41 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Buffers;
namespace AssetRipper.TextureDecoder.Bc;
public static class Bc4
{
/// <summary>
/// The size of an encoded block, in bytes.
/// </summary>
public const int BlockSize = 8;
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
return Decompress(input, width, height, output);
}
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
int bufferSize = width * height * Unsafe.SizeOf<ColorR<byte>>();
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
int inputOffset = 0;
for (int i = 0; i < height; i += 4)
{
for (int j = 0; j < width; j += 4)
{
int bufferOffset = ((i * width) + j) * Unsafe.SizeOf<ColorR<byte>>();
BcHelpers.DecompressBc4(input.Slice(inputOffset), buffer.Slice(bufferOffset), width);
inputOffset += BlockSize;
}
}
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(buffer, width, height, output);
ArrayPool<byte>.Shared.Return(bufferArray);
return inputOffset;
}
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
}
+41
View File
@@ -0,0 +1,41 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Buffers;
namespace AssetRipper.TextureDecoder.Bc;
public static class Bc5
{
/// <summary>
/// The size of an encoded block, in bytes.
/// </summary>
public const int BlockSize = 16;
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
return Decompress(input, width, height, output);
}
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
int bufferSize = width * height * Unsafe.SizeOf<ColorRG<byte>>();
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
int inputOffset = 0;
for (int i = 0; i < height; i += 4)
{
for (int j = 0; j < width; j += 4)
{
int bufferOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRG<byte>>();
BcHelpers.DecompressBc5(input.Slice(inputOffset), buffer.Slice(bufferOffset), width * 2);
inputOffset += BlockSize;
}
}
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(buffer, width, height, output);
ArrayPool<byte>.Shared.Return(bufferArray);
return inputOffset;
}
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
}
+45
View File
@@ -0,0 +1,45 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Buffers;
namespace AssetRipper.TextureDecoder.Bc;
public static class Bc6h
{
/// <summary>
/// The size of an encoded block, in bytes.
/// </summary>
public const int BlockSize = 16;
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, bool isSigned, out byte[] output)
{
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
return Decompress(input, width, height, isSigned, output);
}
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, bool isSigned, Span<byte> output)
{
int bufferSize = width * height * Unsafe.SizeOf<ColorRGB<Half>>();
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
int inputOffset = 0;
for (int i = 0; i < height; i += 4)
{
for (int j = 0; j < width; j += 4)
{
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGB<Half>>();
BcHelpers.DecompressBc6h(
input.Slice(inputOffset, BlockSize),
buffer.Slice(outputOffset),
width * 3,
isSigned);
inputOffset += BlockSize;
}
}
RgbConverter.Convert<ColorRGB<Half>, Half, ColorBGRA32, byte>(buffer, width, height, output);
ArrayPool<byte>.Shared.Return(bufferArray);
return inputOffset;
}
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
}
+217
View File
@@ -0,0 +1,217 @@
namespace AssetRipper.TextureDecoder.Bc;
internal static class Bc6hTables
{
internal static byte[][] ActualBitsCount { get; } =
[
[10, 7, 11, 11, 11, 9, 8, 8, 8, 6, 10, 11, 12, 16],
[5, 6, 5, 4, 4, 5, 6, 5, 5, 6, 10, 9, 8, 4],
[5, 6, 4, 5, 4, 5, 5, 6, 5, 6, 10, 9, 8, 4],
[5, 6, 4, 4, 5, 5, 5, 5, 6, 6, 10, 9, 8, 4]
];
/// <summary>
/// There are 32 possible partition sets for a two-region tile.
/// Each 4x4 block represents a single shape.
/// Here also every fix-up index has MSB bit set.
/// </summary>
internal static byte[][][] PartitionSets { get; } =
[
[
[128, 0, 1, 1],
[0, 0, 1, 1],
[0, 0, 1, 1],
[0, 0, 1, 129]
],
[
[128, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 129]
],
[
[128, 1, 1, 1],
[0, 1, 1, 1],
[0, 1, 1, 1],
[0, 1, 1, 129]
],
[
[128, 0, 0, 1],
[0, 0, 1, 1],
[0, 0, 1, 1],
[0, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 1, 129]
],
[
[128, 0, 1, 1],
[0, 1, 1, 1],
[0, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 1],
[0, 0, 1, 1],
[0, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 1],
[0, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 129]
],
[
[128, 0, 1, 1],
[0, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 1],
[0, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 1, 1, 129]
],
[
[128, 0, 0, 1],
[0, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[1, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[1, 0, 0, 0],
[1, 1, 1, 0],
[1, 1, 1, 129]
],
[
[128, 1, 129, 1],
[0, 0, 0, 1],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[129, 0, 0, 0],
[1, 1, 1, 0]
],
[
[128, 1, 129, 1],
[0, 0, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 0]
],
[
[128, 0, 129, 1],
[0, 0, 0, 1],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[128, 0, 0, 0],
[1, 0, 0, 0],
[129, 1, 0, 0],
[1, 1, 1, 0]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[129, 0, 0, 0],
[1, 1, 0, 0]
],
[
[128, 1, 1, 1],
[0, 0, 1, 1],
[0, 0, 1, 1],
[0, 0, 0, 129]
],
[
[128, 0, 129, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 0]
],
[
[128, 0, 0, 0],
[1, 0, 0, 0],
[129, 0, 0, 0],
[1, 1, 0, 0]
],
[
[128, 1, 129, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 1, 1, 0]
],
[
[128, 0, 129, 1],
[0, 1, 1, 0],
[0, 1, 1, 0],
[1, 1, 0, 0]
],
[
[128, 0, 0, 1],
[0, 1, 1, 1],
[129, 1, 1, 0],
[1, 0, 0, 0]
],
[
[128, 0, 0, 0],
[1, 1, 1, 1],
[129, 1, 1, 1],
[0, 0, 0, 0]
],
[
[128, 1, 129, 1],
[0, 0, 0, 1],
[1, 0, 0, 0],
[1, 1, 1, 0]
],
[
[128, 0, 129, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 0, 0]
]
];
internal static int[] AWeight3 { get; } = [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];
}
+51
View File
@@ -0,0 +1,51 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
namespace AssetRipper.TextureDecoder.Bc;
public static class Bc7
{
/// <summary>
/// The size of an encoded block, in bytes.
/// </summary>
public const int BlockSize = 16;
/// <summary>
/// The width of a decoded block, in pixels.
/// </summary>
private const int BlockWidth = 4;
/// <summary>
/// The height of a decoded block, in pixels.
/// </summary>
private const int BlockHeight = 4;
/// <summary>
/// The size of the natural pixel type.
/// </summary>
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
return Decompress(input, width, height, output);
}
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
int inputOffset = 0;
for (int i = 0; i < height; i += BlockHeight)
{
for (int j = 0; j < width; j += BlockWidth)
{
int outputOffset = ((i * width) + j) * PixelSize;
BcHelpers.DecompressBc7(
input.Slice(inputOffset, BlockSize),
output.Slice(outputOffset),
width * 4);
inputOffset += BlockSize;
}
}
RgbConverter.Convert<ColorRGBA<byte>, byte, ColorBGRA32, byte>(output, width, height, output);
return inputOffset;
}
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
}
+799
View File
@@ -0,0 +1,799 @@
namespace AssetRipper.TextureDecoder.Bc;
internal static class Bc7Tables
{
internal static byte[][] ActualBitsCount { get; } =
[
[4, 6, 5, 7, 5, 7, 7, 5],
[0, 0, 0, 0, 6, 8, 7, 5]
];
/// <summary>
/// There are 64 possible partition sets for a two-region tile.
/// Each 4x4 block represents a single shape.
/// Here also every fix-up index has MSB bit set.
/// </summary>
internal static byte[][][][] PartitionSets { get; } =
[
[
[
[128, 0, 1, 1],
[0, 0, 1, 1],
[0, 0, 1, 1],
[0, 0, 1, 129]
],
[
[128, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 129]
],
[
[128, 1, 1, 1],
[0, 1, 1, 1],
[0, 1, 1, 1],
[0, 1, 1, 129]
],
[
[128, 0, 0, 1],
[0, 0, 1, 1],
[0, 0, 1, 1],
[0, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 1, 129]
],
[
[128, 0, 1, 1],
[0, 1, 1, 1],
[0, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 1],
[0, 0, 1, 1],
[0, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 1],
[0, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 129]
],
[
[128, 0, 1, 1],
[0, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 1],
[0, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 1, 1, 129]
],
[
[128, 0, 0, 1],
[0, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[1, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 1, 1, 129]
],
[
[128, 0, 0, 0],
[1, 0, 0, 0],
[1, 1, 1, 0],
[1, 1, 1, 129]
],
[
[128, 1, 129, 1],
[0, 0, 0, 1],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[129, 0, 0, 0],
[1, 1, 1, 0]
],
[
[128, 1, 129, 1],
[0, 0, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 0]
],
[
[128, 0, 129, 1],
[0, 0, 0, 1],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[128, 0, 0, 0],
[1, 0, 0, 0],
[129, 1, 0, 0],
[1, 1, 1, 0]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[129, 0, 0, 0],
[1, 1, 0, 0]
],
[
[128, 1, 1, 1],
[0, 0, 1, 1],
[0, 0, 1, 1],
[0, 0, 0, 129]
],
[
[128, 0, 129, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 0]
],
[
[128, 0, 0, 0],
[1, 0, 0, 0],
[129, 0, 0, 0],
[1, 1, 0, 0]
],
[
[128, 1, 129, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 1, 1, 0]
],
[
[128, 0, 129, 1],
[0, 1, 1, 0],
[0, 1, 1, 0],
[1, 1, 0, 0]
],
[
[128, 0, 0, 1],
[0, 1, 1, 1],
[129, 1, 1, 0],
[1, 0, 0, 0]
],
[
[128, 0, 0, 0],
[1, 1, 1, 1],
[129, 1, 1, 1],
[0, 0, 0, 0]
],
[
[128, 1, 129, 1],
[0, 0, 0, 1],
[1, 0, 0, 0],
[1, 1, 1, 0]
],
[
[128, 0, 129, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 0, 0]
],
[
[128, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 129]
],
[
[128, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0],
[1, 1, 1, 129]
],
[
[128, 1, 0, 1],
[1, 0, 129, 0],
[0, 1, 0, 1],
[1, 0, 1, 0]
],
[
[128, 0, 1, 1],
[0, 0, 1, 1],
[129, 1, 0, 0],
[1, 1, 0, 0]
],
[
[128, 0, 129, 1],
[1, 1, 0, 0],
[0, 0, 1, 1],
[1, 1, 0, 0]
],
[
[128, 1, 0, 1],
[0, 1, 0, 1],
[129, 0, 1, 0],
[1, 0, 1, 0]
],
[
[128, 1, 1, 0],
[1, 0, 0, 1],
[0, 1, 1, 0],
[1, 0, 0, 129]
],
[
[128, 1, 0, 1],
[1, 0, 1, 0],
[1, 0, 1, 0],
[0, 1, 0, 129]
],
[
[128, 1, 129, 1],
[0, 0, 1, 1],
[1, 1, 0, 0],
[1, 1, 1, 0]
],
[
[128, 0, 0, 1],
[0, 0, 1, 1],
[129, 1, 0, 0],
[1, 0, 0, 0]
],
[
[128, 0, 129, 1],
[0, 0, 1, 0],
[0, 1, 0, 0],
[1, 1, 0, 0]
],
[
[128, 0, 129, 1],
[1, 0, 1, 1],
[1, 1, 0, 1],
[1, 1, 0, 0]
],
[
[128, 1, 129, 0],
[1, 0, 0, 1],
[1, 0, 0, 1],
[0, 1, 1, 0]
],
[
[128, 0, 1, 1],
[1, 1, 0, 0],
[1, 1, 0, 0],
[0, 0, 1, 129]
],
[
[128, 1, 1, 0],
[0, 1, 1, 0],
[1, 0, 0, 1],
[1, 0, 0, 129]
],
[
[128, 0, 0, 0],
[0, 1, 129, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]
],
[
[128, 1, 0, 0],
[1, 1, 129, 0],
[0, 1, 0, 0],
[0, 0, 0, 0]
],
[
[128, 0, 129, 0],
[0, 1, 1, 1],
[0, 0, 1, 0],
[0, 0, 0, 0]
],
[
[128, 0, 0, 0],
[0, 0, 129, 0],
[0, 1, 1, 1],
[0, 0, 1, 0]
],
[
[128, 0, 0, 0],
[0, 1, 0, 0],
[129, 1, 1, 0],
[0, 1, 0, 0]
],
[
[128, 1, 1, 0],
[1, 1, 0, 0],
[1, 0, 0, 1],
[0, 0, 1, 129]
],
[
[128, 0, 1, 1],
[0, 1, 1, 0],
[1, 1, 0, 0],
[1, 0, 0, 129]
],
[
[128, 1, 129, 0],
[0, 0, 1, 1],
[1, 0, 0, 1],
[1, 1, 0, 0]
],
[
[128, 0, 129, 1],
[1, 0, 0, 1],
[1, 1, 0, 0],
[0, 1, 1, 0]
],
[
[128, 1, 1, 0],
[1, 1, 0, 0],
[1, 1, 0, 0],
[1, 0, 0, 129]
],
[
[128, 1, 1, 0],
[0, 0, 1, 1],
[0, 0, 1, 1],
[1, 0, 0, 129]
],
[
[128, 1, 1, 1],
[1, 1, 1, 0],
[1, 0, 0, 0],
[0, 0, 0, 129]
],
[
[128, 0, 0, 1],
[1, 0, 0, 0],
[1, 1, 1, 0],
[0, 1, 1, 129]
],
[
[128, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 1, 1],
[0, 0, 1, 129]
],
[
[128, 0, 129, 1],
[0, 0, 1, 1],
[1, 1, 1, 1],
[0, 0, 0, 0]
],
[
[128, 0, 129, 0],
[0, 0, 1, 0],
[1, 1, 1, 0],
[1, 1, 1, 0]
],
[
[128, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 1, 1],
[0, 1, 1, 129]
]
],
[
[
[128, 0, 1, 129],
[0, 0, 1, 1],
[0, 2, 2, 1],
[2, 2, 2, 130]
],
[
[128, 0, 0, 129],
[0, 0, 1, 1],
[130, 2, 1, 1],
[2, 2, 2, 1]
],
[
[128, 0, 0, 0],
[2, 0, 0, 1],
[130, 2, 1, 1],
[2, 2, 1, 129]
],
[
[128, 2, 2, 130],
[0, 0, 2, 2],
[0, 0, 1, 1],
[0, 1, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[129, 1, 2, 2],
[1, 1, 2, 130]
],
[
[128, 0, 1, 129],
[0, 0, 1, 1],
[0, 0, 2, 2],
[0, 0, 2, 130]
],
[
[128, 0, 2, 130],
[0, 0, 2, 2],
[1, 1, 1, 1],
[1, 1, 1, 129]
],
[
[128, 0, 1, 1],
[0, 0, 1, 1],
[130, 2, 1, 1],
[2, 2, 1, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[129, 1, 1, 1],
[2, 2, 2, 130]
],
[
[128, 0, 0, 0],
[1, 1, 1, 1],
[129, 1, 1, 1],
[2, 2, 2, 130]
],
[
[128, 0, 0, 0],
[1, 1, 129, 1],
[2, 2, 2, 2],
[2, 2, 2, 130]
],
[
[128, 0, 1, 2],
[0, 0, 129, 2],
[0, 0, 1, 2],
[0, 0, 1, 130]
],
[
[128, 1, 1, 2],
[0, 1, 129, 2],
[0, 1, 1, 2],
[0, 1, 1, 130]
],
[
[128, 1, 2, 2],
[0, 129, 2, 2],
[0, 1, 2, 2],
[0, 1, 2, 130]
],
[
[128, 0, 1, 129],
[0, 1, 1, 2],
[1, 1, 2, 2],
[1, 2, 2, 130]
],
[
[128, 0, 1, 129],
[2, 0, 0, 1],
[130, 2, 0, 0],
[2, 2, 2, 0]
],
[
[128, 0, 0, 129],
[0, 0, 1, 1],
[0, 1, 1, 2],
[1, 1, 2, 130]
],
[
[128, 1, 1, 129],
[0, 0, 1, 1],
[130, 0, 0, 1],
[2, 2, 0, 0]
],
[
[128, 0, 0, 0],
[1, 1, 2, 2],
[129, 1, 2, 2],
[1, 1, 2, 130]
],
[
[128, 0, 2, 130],
[0, 0, 2, 2],
[0, 0, 2, 2],
[1, 1, 1, 129]
],
[
[128, 1, 1, 129],
[0, 1, 1, 1],
[0, 2, 2, 2],
[0, 2, 2, 130]
],
[
[128, 0, 0, 129],
[0, 0, 0, 1],
[130, 2, 2, 1],
[2, 2, 2, 1]
],
[
[128, 0, 0, 0],
[0, 0, 129, 1],
[0, 1, 2, 2],
[0, 1, 2, 130]
],
[
[128, 0, 0, 0],
[1, 1, 0, 0],
[130, 2, 129, 0],
[2, 2, 1, 0]
],
[
[128, 1, 2, 130],
[0, 129, 2, 2],
[0, 0, 1, 1],
[0, 0, 0, 0]
],
[
[128, 0, 1, 2],
[0, 0, 1, 2],
[129, 1, 2, 2],
[2, 2, 2, 130]
],
[
[128, 1, 1, 0],
[1, 2, 130, 1],
[129, 2, 2, 1],
[0, 1, 1, 0]
],
[
[128, 0, 0, 0],
[0, 1, 129, 0],
[1, 2, 130, 1],
[1, 2, 2, 1]
],
[
[128, 0, 2, 2],
[1, 1, 0, 2],
[129, 1, 0, 2],
[0, 0, 2, 130]
],
[
[128, 1, 1, 0],
[0, 129, 1, 0],
[2, 0, 0, 2],
[2, 2, 2, 130]
],
[
[128, 0, 1, 1],
[0, 1, 2, 2],
[0, 1, 130, 2],
[0, 0, 1, 129]
],
[
[128, 0, 0, 0],
[2, 0, 0, 0],
[130, 2, 1, 1],
[2, 2, 2, 129]
],
[
[128, 0, 0, 0],
[0, 0, 0, 2],
[129, 1, 2, 2],
[1, 2, 2, 130]
],
[
[128, 2, 2, 130],
[0, 0, 2, 2],
[0, 0, 1, 2],
[0, 0, 1, 129]
],
[
[128, 0, 1, 129],
[0, 0, 1, 2],
[0, 0, 2, 2],
[0, 2, 2, 130]
],
[
[128, 1, 2, 0],
[0, 129, 2, 0],
[0, 1, 130, 0],
[0, 1, 2, 0]
],
[
[128, 0, 0, 0],
[1, 1, 129, 1],
[2, 2, 130, 2],
[0, 0, 0, 0]
],
[
[128, 1, 2, 0],
[1, 2, 0, 1],
[130, 0, 129, 2],
[0, 1, 2, 0]
],
[
[128, 1, 2, 0],
[2, 0, 1, 2],
[129, 130, 0, 1],
[0, 1, 2, 0]
],
[
[128, 0, 1, 1],
[2, 2, 0, 0],
[1, 1, 130, 2],
[0, 0, 1, 129]
],
[
[128, 0, 1, 1],
[1, 1, 130, 2],
[2, 2, 0, 0],
[0, 0, 1, 129]
],
[
[128, 1, 0, 129],
[0, 1, 0, 1],
[2, 2, 2, 2],
[2, 2, 2, 130]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[130, 1, 2, 1],
[2, 1, 2, 129]
],
[
[128, 0, 2, 2],
[1, 129, 2, 2],
[0, 0, 2, 2],
[1, 1, 2, 130]
],
[
[128, 0, 2, 130],
[0, 0, 1, 1],
[0, 0, 2, 2],
[0, 0, 1, 129]
],
[
[128, 2, 2, 0],
[1, 2, 130, 1],
[0, 2, 2, 0],
[1, 2, 2, 129]
],
[
[128, 1, 0, 1],
[2, 2, 130, 2],
[2, 2, 2, 2],
[0, 1, 0, 129]
],
[
[128, 0, 0, 0],
[2, 1, 2, 1],
[130, 1, 2, 1],
[2, 1, 2, 129]
],
[
[128, 1, 0, 129],
[0, 1, 0, 1],
[0, 1, 0, 1],
[2, 2, 2, 130]
],
[
[128, 2, 2, 130],
[0, 1, 1, 1],
[0, 2, 2, 2],
[0, 1, 1, 129]
],
[
[128, 0, 0, 2],
[1, 129, 1, 2],
[0, 0, 0, 2],
[1, 1, 1, 130]
],
[
[128, 0, 0, 0],
[2, 129, 1, 2],
[2, 1, 1, 2],
[2, 1, 1, 130]
],
[
[128, 2, 2, 2],
[0, 129, 1, 1],
[0, 1, 1, 1],
[0, 2, 2, 130]
],
[
[128, 0, 0, 2],
[1, 1, 1, 2],
[129, 1, 1, 2],
[0, 0, 0, 130]
],
[
[128, 1, 1, 0],
[0, 129, 1, 0],
[0, 1, 1, 0],
[2, 2, 2, 130]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[2, 1, 129, 2],
[2, 1, 1, 130]
],
[
[128, 1, 1, 0],
[0, 129, 1, 0],
[2, 2, 2, 2],
[2, 2, 2, 130]
],
[
[128, 0, 2, 2],
[0, 0, 1, 1],
[0, 0, 129, 1],
[0, 0, 2, 130]
],
[
[128, 0, 2, 2],
[1, 1, 2, 2],
[129, 1, 2, 2],
[0, 0, 2, 130]
],
[
[128, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[2, 129, 1, 130]
],
[
[128, 0, 0, 130],
[0, 0, 0, 1],
[0, 0, 0, 2],
[0, 0, 0, 129]
],
[
[128, 2, 2, 2],
[1, 2, 2, 2],
[0, 2, 2, 2],
[129, 2, 2, 130]
],
[
[128, 1, 0, 129],
[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 130]
],
[
[128, 1, 1, 129],
[2, 0, 1, 1],
[130, 2, 0, 1],
[2, 2, 2, 0]
]
]
];
internal static int[] AWeight2 { get; } = [0, 21, 43, 64];
internal static int[] AWeight3 { get; } = [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 const byte bcdec_bc7_sModeHasPBits = 0b11001011;
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,45 @@
namespace AssetRipper.TextureDecoder.Bc;
internal struct BitStream
{
public ulong low;
public ulong high;
public int ReadBits(int numBits)
{
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;
}
public int ReadBit()
{
return this.ReadBits(1);
}
/// <summary>
/// Reversed bits pulling, used in BC6H decoding
/// </summary>
/// <param name="numBits"></param>
/// <returns></returns>
public int ReadBitsReversed(int numBits)
{
int bits = this.ReadBits(numBits);
// Reverse the bits.
int result = 0;
while (numBits-- != 0)
{
result <<= 1;
result |= bits & 1;
bits >>= 1;
}
return result;
}
}
+42
View File
@@ -0,0 +1,42 @@
/* bcdec.h - v0.94
provides functions to decompress blocks of BC compressed images
written by Sergii "iOrange" Kudlai in 2022
This library does not allocate memory and is trying to use as less stack as possible
The library was never optimized specifically for speed but for the overall size
it has zero external dependencies and is not using any runtime functions
Supported BC formats:
BC1 (also known as DXT1) + it's "binary alpha" variant BC1A (DXT1A)
BC2 (also known as DXT3)
BC3 (also known as DXT5)
BC4 (also known as ATI1N)
BC5 (also known as ATI2N)
BC6H (HDR format)
BC7
BC1/BC2/BC3/BC7 are expected to decompress into 4*4 RGBA blocks 8bit per component (32bit pixel)
BC4/BC5 are expected to decompress into 4*4 R/RG blocks 8bit per component (8bit and 16bit pixel)
BC6H is expected to decompress into 4*4 RGB blocks of either 32bit float or 16bit "half" per
component (96bit or 48bit pixel)
For more info, issues and suggestions please visit https://github.com/iOrange/bcdec
CREDITS:
Aras Pranckevicius (@aras-p) - BC1/BC3 decoders optimizations (up to 3x the speed)
- BC6H/BC7 bits pulling routines optimizations
- Split BC6H decompression function into 'half' and
'float' variants
LICENSE: See end of file for license information.
*/
/* Used information sources:
https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression
https://docs.microsoft.com/en-us/windows/win32/direct3d11/bc6h-format
https://docs.microsoft.com/en-us/windows/win32/direct3d11/bc7-format
https://docs.microsoft.com/en-us/windows/win32/direct3d11/bc7-format-mode-reference
! WARNING ! Khronos's BPTC partitions tables contain mistakes, do not use them!
https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#BPTC
! Use tables from here instead !
https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression_bptc.txt
Leaving it here as it's a nice read
https://fgiesen.wordpress.com/2021/10/04/gpu-bcn-decoding/
Fast half to float function from here
https://gist.github.com/rygorous/2144712
*/
@@ -0,0 +1,444 @@
using AssetRipper.TextureDecoder.Rgb;
using AssetRipper.TextureDecoder.Rgb.Formats;
using System.Buffers.Binary;
namespace AssetRipper.TextureDecoder.Dxt
{
public static class DxtDecoder
{
/// <summary>
/// Decompress a DXT1 image
/// </summary>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer of size 4 * width * height.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT1(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
return DecompressDXT1<ColorBGRA32, byte>(input, width, height, out output);
}
/// <summary>
/// Decompress a DXT1 image
/// </summary>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
return DecompressDXT1<ColorBGRA32, byte>(input, width, height, output);
}
/// <summary>
/// Decompress a DXT1 image
/// </summary>
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT1<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
where TOutputChannel : unmanaged
where TOutputColor : unmanaged, IColor<TOutputChannel>
{
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
return DecompressDXT1<TOutputColor, TOutputChannel>(input, width, height, output);
}
/// <summary>
/// Decompress a DXT1 image
/// </summary>
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT1<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
where TOutputChannel : unmanaged
where TOutputColor : unmanaged, IColor<TOutputChannel>
{
return DecompressDXT1<TOutputColor, TOutputChannel>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
}
/// <summary>
/// Decompress a DXT1 image
/// </summary>
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT1<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
where TOutputChannel : unmanaged
where TOutputColor : unmanaged, IColor<TOutputChannel>
{
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
int offset = 0;
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
Span<TOutputColor> buffer = stackalloc TOutputColor[16];
Span<TOutputColor> colors = stackalloc TOutputColor[4];
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, offset += 8)
{
int q0 = input[offset + 0] | input[offset + 1] << 8;
int q1 = input[offset + 2] | input[offset + 3] << 8;
Rgb565(q0, out byte r0, out byte g0, out byte b0);
Rgb565(q1, out byte r1, out byte g1, out byte b1);
colors[0].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(r0, g0, b0);
colors[1].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(r1, g1, b1);
if (q0 > q1)
{
colors[2].SetConvertedChannels<TOutputColor, TOutputChannel, byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
colors[3].SetConvertedChannels<TOutputColor, TOutputChannel, byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
}
else
{
colors[2].SetConvertedChannels<TOutputColor, TOutputChannel, byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
colors[3].SetBlack<TOutputColor, TOutputChannel>();
}
uint d = ToUInt32(input, offset + 4);
for (int i = 0; i < 16; i++, d >>= 2)
{
buffer[i] = colors[unchecked((int)(d & 3))];
}
int clen = s < bcw - 1 ? 4 : clen_last;
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
BlockCopy(buffer, i * 4, output, y * width + s * 4, clen);
}
}
}
return offset;
}
/// <summary>
/// Decompress a DXT3 image
/// </summary>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer of size 4 * width * height.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
return DecompressDXT3<ColorBGRA32, byte>(input, width, height, out output);
}
/// <summary>
/// Decompress a DXT3 image
/// </summary>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
return DecompressDXT3<ColorBGRA32, byte>(input, width, height, output);
}
/// <summary>
/// Decompress a DXT3 image
/// </summary>
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT3<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
where TOutputChannel : unmanaged
where TOutputColor : unmanaged, IColor<TOutputChannel>
{
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
return DecompressDXT3<TOutputColor, TOutputChannel>(input, width, height, output);
}
/// <summary>
/// Decompress a DXT1 image
/// </summary>
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT3<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
where TOutputChannel : unmanaged
where TOutputColor : unmanaged, IColor<TOutputChannel>
{
return DecompressDXT3<TOutputColor, TOutputChannel>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
}
/// <summary>
/// Decompress a DXT3 image
/// </summary>
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT3<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
where TOutputChannel : unmanaged
where TOutputColor : unmanaged, IColor<TOutputChannel>
{
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
int offset = 0;
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
Span<TOutputColor> buffer = stackalloc TOutputColor[16];
Span<ColorRGB<byte>> colors = stackalloc ColorRGB<byte>[4];
Span<byte> alphas = stackalloc byte[16];
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, offset += 16)
{
for (int i = 0; i < 4; i++)
{
int alpha = input[offset + i * 2] | input[offset + i * 2 + 1] << 8;
alphas[i * 4 + 0] = (byte)(((alpha >> 0) & 0xF) * 0x11);
alphas[i * 4 + 1] = (byte)(((alpha >> 4) & 0xF) * 0x11);
alphas[i * 4 + 2] = (byte)(((alpha >> 8) & 0xF) * 0x11);
alphas[i * 4 + 3] = (byte)(((alpha >> 12) & 0xF) * 0x11);
}
int q0 = input[offset + 8] | input[offset + 9] << 8;
int q1 = input[offset + 10] | input[offset + 11] << 8;
Rgb565(q0, out byte r0, out byte g0, out byte b0);
Rgb565(q1, out byte r1, out byte g1, out byte b1);
colors[0] = new ColorRGB<byte>(r0, g0, b0);
colors[1] = new ColorRGB<byte>(r1, g1, b1);
if (q0 > q1)
{
colors[2] = new ColorRGB<byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
colors[3] = new ColorRGB<byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
}
else
{
colors[2] = new ColorRGB<byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
colors[3].SetBlack<ColorRGB<byte>, byte>();
}
uint d = ToUInt32(input, offset + 12);
for (int i = 0; i < 16; i++, d >>= 2)
{
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(colors[unchecked((int)(d & 3))], alphas[i]);
}
int clen = s < bcw - 1 ? 4 : clen_last;
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
BlockCopy(buffer, i * 4, output, y * width + s * 4, clen);
}
}
}
return offset;
}
/// <summary>
/// Decompress a DXT5 image
/// </summary>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer of size 4 * width * height.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
return DecompressDXT5<ColorBGRA32, byte>(input, width, height, out output);
}
/// <summary>
/// Decompress a DXT5 image
/// </summary>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
return DecompressDXT5<ColorBGRA32, byte>(input, width, height, output);
}
/// <summary>
/// Decompress a DXT5 image
/// </summary>
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT5<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
where TOutputChannel : unmanaged
where TOutputColor : unmanaged, IColor<TOutputChannel>
{
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
return DecompressDXT5<TOutputColor, TOutputChannel>(input, width, height, output);
}
/// <summary>
/// Decompress a DXT1 image
/// </summary>
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT5<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
where TOutputChannel : unmanaged
where TOutputColor : unmanaged, IColor<TOutputChannel>
{
return DecompressDXT5<TOutputColor, TOutputChannel>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
}
/// <summary>
/// Decompress a DXT5 image
/// </summary>
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
/// <param name="input">Input buffer containing the compressed image.</param>
/// <param name="width">Pixel width of the image.</param>
/// <param name="height">Pixel height of the image.</param>
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
/// <returns>Number of bytes read from <paramref name="input"/></returns>
public static int DecompressDXT5<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
where TOutputChannel : unmanaged
where TOutputColor : unmanaged, IColor<TOutputChannel>
{
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
int offset = 0;
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
Span<TOutputColor> buffer = stackalloc TOutputColor[16];
Span<ColorRGB<byte>> colors = stackalloc ColorRGB<byte>[4];
Span<byte> alphas = stackalloc byte[8];
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, offset += 16)
{
byte a0 = input[offset + 0];
byte a1 = input[offset + 1];
alphas[0] = a0;
alphas[1] = a1;
if (a0 > a1)
{
alphas[2] = (byte)((a0 * 6 + a1) / 7);
alphas[3] = (byte)((a0 * 5 + a1 * 2) / 7);
alphas[4] = (byte)((a0 * 4 + a1 * 3) / 7);
alphas[5] = (byte)((a0 * 3 + a1 * 4) / 7);
alphas[6] = (byte)((a0 * 2 + a1 * 5) / 7);
alphas[7] = (byte)((a0 + a1 * 6) / 7);
}
else
{
alphas[2] = (byte)((a0 * 4 + a1) / 5);
alphas[3] = (byte)((a0 * 3 + a1 * 2) / 5);
alphas[4] = (byte)((a0 * 2 + a1 * 3) / 5);
alphas[5] = (byte)((a0 + a1 * 4) / 5);
alphas[6] = byte.MinValue;
alphas[7] = byte.MaxValue;
}
int q0 = input[offset + 8] | input[offset + 9] << 8;
int q1 = input[offset + 10] | input[offset + 11] << 8;
Rgb565(q0, out byte r0, out byte g0, out byte b0);
Rgb565(q1, out byte r1, out byte g1, out byte b1);
colors[0] = new ColorRGB<byte>(r0, g0, b0);
colors[1] = new ColorRGB<byte>(r1, g1, b1);
if (q0 > q1)
{
colors[2] = new ColorRGB<byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
colors[3] = new ColorRGB<byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
}
else
{
colors[2] = new ColorRGB<byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
colors[3].SetBlack<ColorRGB<byte>, byte>();
}
ulong da = ToUInt64(input, offset) >> 16;
uint dc = ToUInt32(input, offset + 12);
for (int i = 0; i < 16; i++, da >>= 3, dc >>= 2)
{
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(colors[unchecked((int)(dc & 3))], alphas[unchecked((int)(da & 7))]);
}
int clen = s < bcw - 1 ? 4 : clen_last;
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
BlockCopy(buffer, i * 4, output, y * width + s * 4, clen);
}
}
}
return offset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void Rgb565(int c, out byte r, out byte g, out byte b)
{
r = unchecked((byte)((c & 0xf800) >> 8));
g = unchecked((byte)((c & 0x07e0) >> 3));
b = unchecked((byte)((c & 0x001f) << 3));
r |= (byte)(r >> 5);
g |= (byte)(g >> 6);
b |= (byte)(b >> 5);
}
/// <summary>
/// Based on <see cref="Buffer.BlockCopy(Array, int, Array, int, int)"/>
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="sourceOffset">The zero-based offset into source.</param>
/// <param name="destination">The destination buffer.</param>
/// <param name="destinationOffset">The zero-based offset into destination.</param>
/// <param name="count">The number of items to copy.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void BlockCopy<T>(ReadOnlySpan<T> source, int sourceOffset, Span<T> destination, int destinationOffset, int count)
{
source.Slice(sourceOffset, count).CopyTo(destination.Slice(destinationOffset));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static uint ToUInt32(ReadOnlySpan<byte> input, int offset)
{
return BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(offset));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static ulong ToUInt64(ReadOnlySpan<byte> input, int offset)
{
return BinaryPrimitives.ReadUInt64LittleEndian(input.Slice(offset));
}
}
}
@@ -1,373 +1,358 @@
using System;
using System.Runtime.CompilerServices;
namespace TextureDecoder.Etc
namespace AssetRipper.TextureDecoder.Etc
{
public static class EtcDecoder
{
public static byte[] DecompressETC(byte[] input, int width, int height)
public static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
byte[] output = new byte[width * height * 4];
DecompressETC(input, width, height, output);
return output;
output = new byte[width * height * 4];
return DecompressETC(input, width, height, output);
}
public unsafe static void DecompressETC(byte[] input, int width, int height, byte[] output)
public static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
fixed (byte* inputPtr = input)
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
if (input.Length < requiredLength)
{
fixed (byte* outputPtr = output)
{
DecompressETC(inputPtr, width, height, outputPtr);
}
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
}
}
public unsafe static void DecompressETC(byte* input, int width, int height, byte* output)
{
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
uint* buf = stackalloc uint[16];
Span<uint> buf = stackalloc uint[16];
int inputOffset = 0;
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, input += 8)
for (int s = 0; s < bcw; s++, inputOffset += 8)
{
DecodeEtc1Block(input, buf);
DecodeEtc1Block(input.Slice(inputOffset, 8), buf);
int clen = s < bcw - 1 ? 4 : clen_last;
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
uint* bufPtr = buf;
int outputOffset = t * 16 * width + s * 16;
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
outputPtr += width;
bufPtr += 4;
for (int j = 0; j < clen; j++)
{
outputPtr[j + i * width] = buf[j + 4 * i];
}
}
}
}
return inputOffset;
}
public static byte[] DecompressETC2(byte[] input, int width, int height)
public static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
byte[] output = new byte[width * height * 4];
DecompressETC2(input, width, height, output);
return output;
output = new byte[width * height * 4];
return DecompressETC2(input, width, height, output);
}
public unsafe static void DecompressETC2(byte[] input, int width, int height, byte[] output)
public static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
fixed (byte* inputPtr = input)
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
if (input.Length < requiredLength)
{
fixed (byte* outputPtr = output)
{
DecompressETC2(inputPtr, width, height, outputPtr);
}
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
}
}
public unsafe static void DecompressETC2(byte* input, int width, int height, byte* output)
{
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
uint* buf = stackalloc uint[16];
Span<uint> buf = stackalloc uint[16];
int inputOffset = 0;
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, input += 8)
for (int s = 0; s < bcw; s++, inputOffset += 8)
{
DecodeEtc2Block(input, buf);
DecodeEtc2Block(input.Slice(inputOffset, 8), buf);
int clen = s < bcw - 1 ? 4 : clen_last;
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
uint* bufPtr = buf;
int outputOffset = t * 16 * width + s * 16;
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
outputPtr += width;
bufPtr += 4;
for (int j = 0; j < clen; j++)
{
outputPtr[j + i * width] = buf[j + 4 * i];
}
}
}
}
return inputOffset;
}
public static byte[] DecompressETC2A1(byte[] input, int width, int height)
public static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
byte[] output = new byte[width * height * 4];
DecompressETC2A1(input, width, height, output);
return output;
output = new byte[width * height * 4];
return DecompressETC2A1(input, width, height, output);
}
public unsafe static void DecompressETC2A1(byte[] input, int width, int height, byte[] output)
public static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
fixed (byte* inputPtr = input)
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
if (input.Length < requiredLength)
{
fixed (byte* outputPtr = output)
{
DecompressETC2A1(inputPtr, width, height, outputPtr);
}
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
}
}
public unsafe static void DecompressETC2A1(byte* input, int width, int height, byte* output)
{
int width4 = width * 4;
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
uint* buf = stackalloc uint[16];
Span<uint> buf = stackalloc uint[16];
int inputOffset = 0;
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, input += 8)
for (int s = 0; s < bcw; s++, inputOffset += 8)
{
DecodeEtc2a1Block(input, buf);
DecodeEtc2a1Block(input.Slice(inputOffset, 8), buf);
int clen = s < bcw - 1 ? 4 : clen_last;
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
uint* bufPtr = buf;
int outputOffset = t * 16 * width + s * 16;
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
outputPtr += width;
bufPtr += 4;
for (int j = 0; j < clen; j++)
{
outputPtr[j + i * width] = buf[j + 4 * i];
}
}
}
}
return inputOffset;
}
public static byte[] DecompressETC2A8(byte[] input, int width, int height)
public static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
byte[] output = new byte[width * height * 4];
DecompressETC2A8(input, width, height, output);
return output;
output = new byte[width * height * 4];
return DecompressETC2A8(input, width, height, output);
}
public unsafe static void DecompressETC2A8(byte[] input, int width, int height, byte[] output)
public static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
fixed (byte* inputPtr = input)
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 16;
if (input.Length < requiredLength)
{
fixed (byte* outputPtr = output)
{
DecompressETC2A8(inputPtr, width, height, outputPtr);
}
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
}
}
public unsafe static void DecompressETC2A8(byte* input, int width, int height, byte* output)
{
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
uint* buf = stackalloc uint[16];
Span<uint> buf = stackalloc uint[16];
int inputOffset = 0;
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, input += 16)
for (int s = 0; s < bcw; s++, inputOffset += 16)
{
DecodeEtc2Block(input + 8, buf);
DecodeEtc2a8Block(input, buf);
DecodeEtc2Block(input.Slice(inputOffset + 8, 8), buf);
DecodeEtc2a8Block(input.Slice(inputOffset + 0, 8), buf);
int clen = s < bcw - 1 ? 4 : clen_last;
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
uint* bufPtr = buf;
int outputOffset = t * 16 * width + s * 16;
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
outputPtr += width;
bufPtr += 4;
for (int j = 0; j < clen; j++)
{
outputPtr[j + i * width] = buf[j + 4 * i];
}
}
}
}
return inputOffset;
}
public static byte[] DecompressEACRUnsigned(byte[] input, int width, int height)
public static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
byte[] output = new byte[width * height * 4];
DecompressEACRUnsigned(input, width, height, output);
return output;
output = new byte[width * height * 4];
return DecompressEACRUnsigned(input, width, height, output);
}
public unsafe static void DecompressEACRUnsigned(byte[] input, int width, int height, byte[] output)
public static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
fixed (byte* inputPtr = input)
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
if (input.Length < requiredLength)
{
fixed (byte* outputPtr = output)
{
DecompressEACRUnsigned(inputPtr, width, height, outputPtr);
}
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
}
}
public unsafe static void DecompressEACRUnsigned(byte* input, int width, int height, byte* output)
{
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
uint* buf = stackalloc uint[16];
Span<uint> buf = stackalloc uint[16];
for (int i = 0; i < 16; i++)
{
buf[i] = 0xFF000000;
}
int inputOffset = 0;
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, input += 8)
for (int s = 0; s < bcw; s++, inputOffset += 8)
{
DecodeEacUnsignedBlock(input, buf, 2);
DecodeEacUnsignedBlock(input.Slice(inputOffset, 8), buf, 2);
int clen = s < bcw - 1 ? 4 : clen_last;
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
uint* bufPtr = buf;
int outputOffset = t * 16 * width + s * 16;
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
outputPtr += width;
bufPtr += 4;
for (int j = 0; j < clen; j++)
{
outputPtr[j + i * width] = buf[j + 4 * i];
}
}
}
}
return inputOffset;
}
public static byte[] DecompressEACRSigned(byte[] input, int width, int height)
public static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
byte[] output = new byte[width * height * 4];
DecompressEACRSigned(input, width, height, output);
return output;
output = new byte[width * height * 4];
return DecompressEACRSigned(input, width, height, output);
}
public unsafe static void DecompressEACRSigned(byte[] input, int width, int height, byte[] output)
public static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
fixed (byte* inputPtr = input)
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
if (input.Length < requiredLength)
{
fixed (byte* outputPtr = output)
{
DecompressEACRSigned(inputPtr, width, height, outputPtr);
}
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
}
}
public unsafe static void DecompressEACRSigned(byte* input, int width, int height, byte* output)
{
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
uint* buf = stackalloc uint[16];
Span<uint> buf = stackalloc uint[16];
for (int i = 0; i < 16; i++)
{
buf[i] = 0xFF000000;
}
int inputOffset = 0;
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, input += 8)
for (int s = 0; s < bcw; s++, inputOffset += 8)
{
DecodeEacSignedBlock(input, buf, 2);
DecodeEacSignedBlock(input.Slice(inputOffset, 8), buf, 2);
int clen = s < bcw - 1 ? 4 : clen_last;
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
uint* bufPtr = buf;
int outputOffset = t * 16 * width + s * 16;
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
outputPtr += width;
bufPtr += 4;
for (int j = 0; j < clen; j++)
{
outputPtr[j + i * width] = buf[j + 4 * i];
}
}
}
}
return inputOffset;
}
public static byte[] DecompressEACRGUnsigned(byte[] input, int width, int height)
public static int DecompressEACRGUnsigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
byte[] output = new byte[width * height * 4];
DecompressEACRGUnsigned(input, width, height, output);
return output;
output = new byte[width * height * 4];
return DecompressEACRGUnsigned(input, width, height, output);
}
public unsafe static void DecompressEACRGUnsigned(byte[] input, int width, int height, byte[] output)
{
fixed (byte* inputPtr = input)
{
fixed (byte* outputPtr = output)
{
DecompressEACRGUnsigned(inputPtr, width, height, outputPtr);
}
}
}
public unsafe static void DecompressEACRGUnsigned(byte* input, int width, int height, byte* output)
public static int DecompressEACRGUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
if (input.Length < bcw * bch * 16)
{
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {bcw * bch * 16}", nameof(input));
}
int clen_last = (width + 3) % 4 + 1;
uint* buf = stackalloc uint[16];
Span<uint> buf = stackalloc uint[16];
for (int i = 0; i < 16; i++)
{
buf[i] = 0xFF000000;
}
int inputOffset = 0;
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, input += 16)
for (int s = 0; s < bcw; s++, inputOffset += 16)
{
DecodeEacUnsignedBlock(input + 0, buf, 2);
DecodeEacUnsignedBlock(input + 8, buf, 1);
DecodeEacUnsignedBlock(input.Slice(inputOffset + 0, 8), buf, 2);
DecodeEacUnsignedBlock(input.Slice(inputOffset + 8, 8), buf, 1);
int clen = s < bcw - 1 ? 4 : clen_last;
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
uint* bufPtr = buf;
int outputOffset = t * 16 * width + s * 16;
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
outputPtr += width;
bufPtr += 4;
for (int j = 0; j < clen; j++)
{
outputPtr[j + i * width] = buf[j + 4 * i];
}
}
}
}
return inputOffset;
}
public static byte[] DecompressEACRGSigned(byte[] input, int width, int height)
public static int DecompressEACRGSigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
{
byte[] output = new byte[width * height * 4];
DecompressEACRGSigned(input, width, height, output);
return output;
output = new byte[width * height * 4];
return DecompressEACRGSigned(input, width, height, output);
}
public unsafe static void DecompressEACRGSigned(byte[] input, int width, int height, byte[] output)
public static int DecompressEACRGSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
{
fixed (byte* inputPtr = input)
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 16;
if (input.Length < requiredLength)
{
fixed (byte* outputPtr = output)
{
DecompressEACRGSigned(inputPtr, width, height, outputPtr);
}
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
}
}
public unsafe static void DecompressEACRGSigned(byte* input, int width, int height, byte* output)
{
int bcw = (width + 3) / 4;
int bch = (height + 3) / 4;
int clen_last = (width + 3) % 4 + 1;
uint* buf = stackalloc uint[16];
Span<uint> buf = stackalloc uint[16];
for (int i = 0; i < 16; i++)
{
buf[i] = 0xFF000000;
}
int inputOffset = 0;
for (int t = 0; t < bch; t++)
{
for (int s = 0; s < bcw; s++, input += 16)
for (int s = 0; s < bcw; s++, inputOffset += 16)
{
DecodeEacSignedBlock(input + 0, buf, 2);
DecodeEacSignedBlock(input + 8, buf, 1);
DecodeEacSignedBlock(input.Slice(inputOffset + 0, 8), buf, 2);
DecodeEacSignedBlock(input.Slice(inputOffset + 8, 8), buf, 1);
int clen = s < bcw - 1 ? 4 : clen_last;
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
uint* bufPtr = buf;
int outputOffset = t * 16 * width + s * 16;
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
{
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
outputPtr += width;
bufPtr += 4;
for (int j = 0; j < clen; j++)
{
outputPtr[j + i * width] = buf[j + 4 * i];
}
}
}
}
return inputOffset;
}
private unsafe static void DecodeEtc1Block(byte* input, uint* output)
/// <summary>
/// Parses an 8-byte Etc1 block from <paramref name="input"/>.
/// </summary>
private static void DecodeEtc1Block(ReadOnlySpan<byte> input, Span<uint> output)
{
byte i3 = input[3];
int* code = stackalloc int[2];
code[0] = (i3 >> 5) * 4;
code[1] = (i3 >> 2 & 7) * 4;
byte* c = stackalloc byte[6];
ReadOnlySpan<int> code =
[
(i3 >> 5) * 4,
(i3 >> 2 & 7) * 4
];
Span<byte> c = stackalloc byte[6];
int ti = (i3 & 1) * 16;
if ((i3 & 2) != 0)
{
@@ -414,11 +399,11 @@ namespace TextureDecoder.Etc
}
}
private unsafe static void DecodeEtc2Block(byte* input, uint* output)
private static void DecodeEtc2Block(ReadOnlySpan<byte> input, Span<uint> output)
{
int j = input[6] << 8 | input[7];
int k = input[4] << 8 | input[5];
byte* c = stackalloc byte[3 * 3];
Span<byte> c = stackalloc byte[3 * 3];
if ((input[3] & 2) != 0)
{
@@ -438,13 +423,13 @@ namespace TextureDecoder.Etc
}
int ti = input[3] >> 1 & 6 | input[3] & 1;
byte d = Etc2DistanceTable[ti];
uint* color_set = stackalloc uint[]
{
ReadOnlySpan<uint> color_set =
[
ApplicateColorRaw(c, 0),
ApplicateColor(c, 1, d),
ApplicateColorRaw(c, 1),
ApplicateColor(c, 1, -d)
};
];
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
{
@@ -479,13 +464,13 @@ namespace TextureDecoder.Etc
++di;
}
byte d = Etc2DistanceTable[di];
uint* color_set = stackalloc uint[]
{
ReadOnlySpan<uint> color_set =
[
ApplicateColor(c, 0, d),
ApplicateColor(c, 0, -d),
ApplicateColor(c, 1, d),
ApplicateColor(c, 1, -d)
};
];
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
{
int index = k << 1 & 2 | j & 1;
@@ -519,9 +504,9 @@ namespace TextureDecoder.Etc
{
for (int x = 0; x < 4; x++, i++)
{
int ri = Clamp255((x * (c[1 * 3 + 0] - c[0 * 3 + 0]) + y * (c[2 * 3 + 0] - c[0 * 3 + 0]) + 4 * c[0 * 3 + 0] + 2) >> 2);
int gi = Clamp255((x * (c[1 * 3 + 1] - c[0 * 3 + 1]) + y * (c[2 * 3 + 1] - c[0 * 3 + 1]) + 4 * c[0 * 3 + 1] + 2) >> 2);
int bi = Clamp255((x * (c[1 * 3 + 2] - c[0 * 3 + 2]) + y * (c[2 * 3 + 2] - c[0 * 3 + 2]) + 4 * c[0 * 3 + 2] + 2) >> 2);
byte ri = Clamp255((x * (c[1 * 3 + 0] - c[0 * 3 + 0]) + y * (c[2 * 3 + 0] - c[0 * 3 + 0]) + 4 * c[0 * 3 + 0] + 2) >> 2);
byte gi = Clamp255((x * (c[1 * 3 + 1] - c[0 * 3 + 1]) + y * (c[2 * 3 + 1] - c[0 * 3 + 1]) + 4 * c[0 * 3 + 1] + 2) >> 2);
byte bi = Clamp255((x * (c[1 * 3 + 2] - c[0 * 3 + 2]) + y * (c[2 * 3 + 2] - c[0 * 3 + 2]) + 4 * c[0 * 3 + 2] + 2) >> 2);
output[i] = Color(ri, gi, bi, 255);
}
}
@@ -529,9 +514,11 @@ namespace TextureDecoder.Etc
else
{
// differential
int* code = stackalloc int[2];
code[0] = (input[3] >> 5) * 4;
code[1] = (input[3] >> 2 & 7) * 4;
ReadOnlySpan<int> code =
[
(input[3] >> 5) * 4,
(input[3] >> 2 & 7) * 4
];
int ti = (input[3] & 1) * 16;
unchecked
{
@@ -562,9 +549,11 @@ namespace TextureDecoder.Etc
else
{
// individual
int* code = stackalloc int[2];
code[0] = (input[3] >> 5) * 4;
code[1] = (input[3] >> 2 & 7) * 4;
ReadOnlySpan<int> code =
[
(input[3] >> 5) * 4,
(input[3] >> 2 & 7) * 4
];
int ti = (input[3] & 1) * 16;
unchecked
{
@@ -588,8 +577,8 @@ namespace TextureDecoder.Etc
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static void DecodeEtc2a1Block(byte* input, uint* output)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void DecodeEtc2a1Block(ReadOnlySpan<byte> input, Span<uint> output)
{
if ((input[3] & 2) != 0)
{
@@ -602,11 +591,11 @@ namespace TextureDecoder.Etc
}
}
private unsafe static void DecodeEtc2PunchThrowBlock(byte* input, uint* output)
private static void DecodeEtc2PunchThrowBlock(ReadOnlySpan<byte> input, Span<uint> output)
{
int j = input[6] << 8 | input[7];
int k = input[4] << 8 | input[5];
byte* c = stackalloc byte[3 * 3];
Span<byte> c = stackalloc byte[3 * 3];
int r = input[0] & 0xf8;
int dr = (input[0] << 3 & 0x18) - (input[0] << 3 & 0x20);
if (r + dr < 0 || r + dr > 255)
@@ -623,13 +612,13 @@ namespace TextureDecoder.Etc
}
int ti = input[3] >> 1 & 6 | input[3] & 1;
byte d = Etc2DistanceTable[ti];
uint* color_set = stackalloc uint[]
{
ReadOnlySpan<uint> color_set =
[
ApplicateColorRaw(c, 0),
ApplicateColor(c, 1, d),
ApplicateColorRaw(c, 1),
ApplicateColor(c, 1, -d)
};
];
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
{
@@ -665,13 +654,13 @@ namespace TextureDecoder.Etc
++di;
}
byte d = Etc2DistanceTable[di];
uint* color_set = stackalloc uint[]
{
ReadOnlySpan<uint> color_set =
[
ApplicateColor(c, 0, d),
ApplicateColor(c, 0, -d),
ApplicateColor(c, 1, d),
ApplicateColor(c, 1, -d)
};
];
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
{
int index = k << 1 & 2 | j & 1;
@@ -706,9 +695,9 @@ namespace TextureDecoder.Etc
{
for (int x = 0; x < 4; x++, i++)
{
int ri = Clamp255((x * (c[1 * 3 + 0] - c[0 * 3 + 0]) + y * (c[2 * 3 + 0] - c[0 * 3 + 0]) + 4 * c[0 * 3 + 0] + 2) >> 2);
int gi = Clamp255((x * (c[1 * 3 + 1] - c[0 * 3 + 1]) + y * (c[2 * 3 + 1] - c[0 * 3 + 1]) + 4 * c[0 * 3 + 1] + 2) >> 2);
int bi = Clamp255((x * (c[1 * 3 + 2] - c[0 * 3 + 2]) + y * (c[2 * 3 + 2] - c[0 * 3 + 2]) + 4 * c[0 * 3 + 2] + 2) >> 2);
byte ri = Clamp255((x * (c[1 * 3 + 0] - c[0 * 3 + 0]) + y * (c[2 * 3 + 0] - c[0 * 3 + 0]) + 4 * c[0 * 3 + 0] + 2) >> 2);
byte gi = Clamp255((x * (c[1 * 3 + 1] - c[0 * 3 + 1]) + y * (c[2 * 3 + 1] - c[0 * 3 + 1]) + 4 * c[0 * 3 + 1] + 2) >> 2);
byte bi = Clamp255((x * (c[1 * 3 + 2] - c[0 * 3 + 2]) + y * (c[2 * 3 + 2] - c[0 * 3 + 2]) + 4 * c[0 * 3 + 2] + 2) >> 2);
output[i] = Color(ri, gi, bi, 255);
}
}
@@ -716,9 +705,11 @@ namespace TextureDecoder.Etc
else
{
// differential (Etc1Block + mask + specific mod table)
int* code = stackalloc int[2];
code[0] = (input[3] >> 5) * 4;
code[1] = (input[3] >> 2 & 7) * 4;
ReadOnlySpan<int> code =
[
(input[3] >> 5) * 4,
(input[3] >> 2 & 7) * 4
];
int ti = (input[3] & 1) * 16;
unchecked
{
@@ -748,7 +739,7 @@ namespace TextureDecoder.Etc
}
}
private unsafe static void DecodeEtc2a8Block(byte* input, uint* output)
private static void DecodeEtc2a8Block(ReadOnlySpan<byte> input, Span<uint> output)
{
int @base = input[0];
int data1 = input[1];
@@ -757,53 +748,53 @@ namespace TextureDecoder.Etc
{
for (int i = 0; i < 16; i++)
{
DecodeEac11Block((byte*)output + 3, @base);
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(3), @base);
}
}
else
{
int table = data1 & 0xF;
ulong l = Get6SwapedBytes(input);
DecodeEac11Block((byte*)output + 3, @base, table, mul, l);
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(3), @base, table, mul, l);
}
}
private unsafe static void DecodeEacUnsignedBlock(byte* input, uint* output, int channel)
private static void DecodeEacUnsignedBlock(ReadOnlySpan<byte> input, Span<uint> output, int channel)
{
int @base = input[0];
int data1 = input[1];
int mul = data1 >> 4;
if (mul == 0)
{
DecodeEac11Block((byte*)output + channel, @base);
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base);
}
else
{
int table = data1 & 0xF;
ulong l = Get6SwapedBytes(input);
DecodeEac11Block((byte*)output + channel, @base, table, mul, l);
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base, table, mul, l);
}
}
private unsafe static void DecodeEacSignedBlock(byte* input, uint* output, int channel)
private static void DecodeEacSignedBlock(ReadOnlySpan<byte> input, Span<uint> output, int channel)
{
int @base = 127 + unchecked((sbyte)input[0]);
int data1 = input[1];
int mul = data1 >> 4;
if (mul == 0)
{
DecodeEac11Block((byte*)output + channel, @base);
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base);
}
else
{
int table = data1 & 0xF;
ulong l = Get6SwapedBytes(input);
DecodeEac11Block((byte*)output + channel, @base, table, mul, l);
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base, table, mul, l);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static void DecodeEac11Block(byte* output, int @base)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void DecodeEac11Block(Span<byte> output, int @base)
{
for (int i = 0; i < 16; i++)
{
@@ -811,64 +802,62 @@ namespace TextureDecoder.Etc
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static void DecodeEac11Block(byte* output, int @base, int ti, int mul, ulong l)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void DecodeEac11Block(Span<byte> output, int @base, int ti, int mul, ulong l)
{
fixed (sbyte* table = &Etc2AlphaModTable[ti * 8])
ReadOnlySpan<sbyte> table = Etc2AlphaModTable.AsSpan(ti * 8, 8);
for (int i = 0; i < 16; i++, l >>= 3)
{
for (int i = 0; i < 16; i++, l >>= 3)
{
int val = @base + mul * table[l & 7];
output[WriteOrderTableRev[i] * 4] = (byte)(Clamp255(val));
}
int val = @base + mul * table[unchecked((int)(l & 0b111))];
output[WriteOrderTableRev[i] * 4] = Clamp255(val);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static uint Color(int r, int g, int b, int a)
{
return unchecked((uint)(r << 16 | g << 8 | b | a << 24));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Clamp255(int n)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static byte Clamp255(int n)
{
return n < 0 ? 0 : n > 255 ? 255 : n;
return n < 0 ? (byte)0 : n > 255 ? (byte)255 : unchecked((byte)n);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static uint ApplicateColor(byte* c, int o, int m)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static uint ApplicateColor(ReadOnlySpan<byte> c, int o, int m)
{
return Color(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), 255);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static uint ApplicateColor(int* c, int o, int m)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static uint ApplicateColor(ReadOnlySpan<int> c, int o, int m)
{
return Color(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), 255);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static uint ApplicateColorRaw(byte* c, int o)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static uint ApplicateColorRaw(ReadOnlySpan<byte> c, int o)
{
return Color(c[o * 3 + 0], c[o * 3 + 1], c[o * 3 + 2], 255);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static ulong Get6SwapedBytes(byte* data)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static ulong Get6SwapedBytes(ReadOnlySpan<byte> data)
{
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 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 =
{
[
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 =
{
[
2, 8, -2, -8,
5, 17, -5, -17,
9, 29, -9, -29,
@@ -877,9 +866,9 @@ namespace TextureDecoder.Etc
24, 80, -24, -80,
33, 106, -33, -106,
47, 183, -47, -183,
};
];
private static readonly int[] PunchthroughModifierTable =
{
[
0, 8, 0, -8,
0, 17, 0, -17,
0, 29, 0, -29,
@@ -888,10 +877,10 @@ namespace TextureDecoder.Etc
0, 80, 0, -80,
0, 106, 0, -106,
0, 183, 0, -183,
};
private static readonly byte[] Etc2DistanceTable = { 3, 6, 11, 16, 23, 32, 41, 64 };
];
private static readonly byte[] Etc2DistanceTable = [3, 6, 11, 16, 23, 32, 41, 64];
private static readonly sbyte[] Etc2AlphaModTable =
{
[
-3, -6, -9, -15, 2, 5, 8, 14,
-3, -7, -10, -13, 2, 6, 9, 12,
-2, -5, -8, -13, 1, 4, 7, 12,
@@ -908,7 +897,7 @@ namespace TextureDecoder.Etc
-1, -2, -3, -10, 0, 1, 2, 9,
-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 readonly uint[] PunchthroughMaskTable = [0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF];
}
}
@@ -0,0 +1,5 @@
global using System;
global using System.Diagnostics.CodeAnalysis;
global using System.Numerics;
global using System.Runtime.CompilerServices;
global using System.Runtime.InteropServices;
@@ -0,0 +1,11 @@
namespace AssetRipper.TextureDecoder.Pvrtc;
public static partial class PvrtcDecoder
{
/// <summary>
/// 8 bytes (64 bits) per block
/// </summary>
private readonly record struct AmtcBlock(uint PackedData0, uint PackedData1)
{
}
}
@@ -0,0 +1,16 @@
namespace AssetRipper.TextureDecoder.Pvrtc;
public static partial class PvrtcDecoder
{
/// <summary>
/// Low precision colours extracted from the blocks
/// </summary>
/// <remarks>
/// 2 * 4 integers
/// </remarks>
[InlineArray(8)]
private struct Colours5554
{
private int _element0;
}
}
@@ -1,34 +1,24 @@
//#define DISABLE_TWINDDLING_ROUTINE
#define ASSUME_IMAGE_TILING
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
namespace TextureDecoder.Pvrtc
namespace AssetRipper.TextureDecoder.Pvrtc
{
public static class PvrtcDecoder
public static partial class PvrtcDecoder
{
private struct AmtcBlock
{
public AmtcBlock(uint v0, uint v1)
{
PackedData0 = v0;
PackedData1 = v1;
}
// Uses 64 bits pre block
public readonly uint PackedData0;
public readonly uint PackedData1;
}
/// <summary>
/// Low precision colours extracted from the blocks
/// Decompresses PVRTC to RGBA 8888
/// </summary>
private unsafe struct Colours5554
/// <param name="input">The PVRTC texture data to decompress</param>
/// <param name="xDim">X dimension (width) of the texture</param>
/// <param name="yDim">Y dimension (height) of the texture</param>
/// <param name="output">The decompressed texture data</param>
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
/// <returns>The number of bytes read from <paramref name="input"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, out byte[] output)
{
public fixed int Reps[2 * 4];
output = new byte[xDim * yDim * sizeof(uint)];
return DecompressPVRTC(input, xDim, yDim, do2bitMode, output);
}
/// <summary>
@@ -39,16 +29,9 @@ namespace TextureDecoder.Pvrtc
/// <param name="yDim">Y dimension (height) of the texture</param>
/// <param name="output">The decompressed texture data</param>
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
/// <returns></returns>
public unsafe static void DecompressPVRTC(byte[] input, int xDim, int yDim, byte[] output, bool do2bitMode)
{
fixed (byte* inputPtr = input)
{
PVRDecompress(inputPtr, do2bitMode, xDim, yDim, output);
}
}
private unsafe static void PVRDecompress(byte* compressedData, bool do2bitMode, int xDim, int yDim, byte[] output)
/// <returns>The number of bytes read from <paramref name="input"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<byte> output)
{
int xBlockSize = do2bitMode ? BlockX2bpp : BlockX4bpp;
// for MBX don't allow the sizes to get too small
@@ -60,13 +43,17 @@ namespace TextureDecoder.Pvrtc
uint pblocki10 = uint.MaxValue;
uint pblocki11 = uint.MaxValue;
Colours5554* m_colors = stackalloc Colours5554[2 * 2];
uint maxBlockIndexRead = uint.MinValue;
Span<Colours5554> m_colors = stackalloc Colours5554[2 * 2];
// interpolated A colors for the pixel
int* aSig = stackalloc int[4];
Span<int> aSig = stackalloc int[4];
// interpolated B colors for the pixel
int* bSig = stackalloc int[4];
int* modulationVals = stackalloc int[8 * 16];
int* modulationModes = stackalloc int[8 * 16];
Span<int> bSig = stackalloc int[4];
Span<int> modulationVals = stackalloc int[8 * 16];
Span<int> modulationModes = stackalloc int[8 * 16];
ReadOnlySpan<AmtcBlock> blocks = MemoryMarshal.Cast<byte, AmtcBlock>(input);
// step through the pixels of the image decompressing each one in turn.
// Note that this is a hideously inefficient way to do this!
@@ -95,28 +82,30 @@ namespace TextureDecoder.Pvrtc
bool changed = blocki00 != pblocki00 || blocki01 != pblocki01 || blocki10 != pblocki10 || blocki11 != pblocki11;
if (changed)
{
AmtcBlock* block00 = &((AmtcBlock*)compressedData)[blocki00];
Unpack5554Colour(block00, m_colors[0].Reps);
AmtcBlock block00 = blocks[(int)blocki00];
Unpack5554Colour(block00, m_colors[0]);
UnpackModulations(block00, do2bitMode, modulationVals, modulationModes, 0, 0);
AmtcBlock* block01 = &((AmtcBlock*)compressedData)[blocki01];
Unpack5554Colour(block01, m_colors[1].Reps);
AmtcBlock block01 = blocks[(int)blocki01];
Unpack5554Colour(block01, m_colors[1]);
UnpackModulations(block01, do2bitMode, modulationVals, modulationModes, xBlockSize, 0);
AmtcBlock* block10 = &((AmtcBlock*)compressedData)[blocki10];
Unpack5554Colour(block10, m_colors[2].Reps);
AmtcBlock block10 = blocks[(int)blocki10];
Unpack5554Colour(block10, m_colors[2]);
UnpackModulations(block10, do2bitMode, modulationVals, modulationModes, 0, BlockYSize);
AmtcBlock* block11 = &((AmtcBlock*)compressedData)[blocki11];
Unpack5554Colour(block11, m_colors[3].Reps);
AmtcBlock block11 = blocks[(int)blocki11];
Unpack5554Colour(block11, m_colors[3]);
UnpackModulations(block11, do2bitMode, modulationVals, modulationModes, xBlockSize, BlockYSize);
pblocki00 = blocki00;
pblocki01 = blocki01;
pblocki10 = blocki10;
pblocki11 = blocki11;
maxBlockIndexRead = Math.Max(maxBlockIndexRead, Math.Max(Math.Max(blocki00, blocki01), Math.Max(blocki10, blocki11)));
}
// decompress the pixel. First compute the interpolated A and B signals
InterpolateColours(m_colors[0].Reps, m_colors[1].Reps, m_colors[2].Reps, m_colors[3].Reps, 0, do2bitMode, x, y, aSig);
InterpolateColours(m_colors[0].Reps, m_colors[1].Reps, m_colors[2].Reps, m_colors[3].Reps, 1, do2bitMode, x, y, bSig);
InterpolateColours(m_colors[0], m_colors[1], m_colors[2], m_colors[3], 0, do2bitMode, x, y, aSig);
InterpolateColours(m_colors[0], m_colors[1], m_colors[2], m_colors[3], 1, do2bitMode, x, y, bSig);
GetModulationValue(x, y, do2bitMode, modulationVals, modulationModes, out int mod, out bool doPT);
// compute the modulated color. Swap red and blue channel
@@ -127,6 +116,8 @@ namespace TextureDecoder.Pvrtc
output[position + 3] = doPT ? (byte)0 : (byte)((aSig[3] * 8 + mod * (bSig[3] - aSig[3])) >> 3);
}
}
return ((int)maxBlockIndexRead + 1) * Unsafe.SizeOf<AmtcBlock>();
}
/// <summary>
@@ -150,11 +141,11 @@ namespace TextureDecoder.Pvrtc
{
throw new Exception();
}
if (!IsPowerOf2((uint)ySize))
if (!BitOperations.IsPow2(ySize))
{
throw new Exception();
}
if (!IsPowerOf2((uint)xSize))
if (!BitOperations.IsPow2(xSize))
{
throw new Exception();
}
@@ -210,15 +201,14 @@ namespace TextureDecoder.Pvrtc
/// <summary>
/// Get the modulation value as a numerator of a fraction of 8ths
/// </summary>
private unsafe static void GetModulationValue(int x, int y, bool do2bitMode, int* modulationVals, int* modulationModes, out int mod, out bool doPT)
private static void GetModulationValue(int x, int y, bool do2bitMode, ReadOnlySpan<int> modulationVals, ReadOnlySpan<int> modulationModes, out int mod, out bool doPT)
{
// Map X and Y into the local 2x2 block
y = (y & 0x3) | ((~y & 0x2) << 1);
if (do2bitMode)
x = (x & 0x7) | ((~x & 0x4) << 1);
else
x = (x & 0x3) | ((~x & 0x2) << 1);
x = do2bitMode
? (x & 0x7) | ((~x & 0x4) << 1)
: (x & 0x3) | ((~x & 0x2) << 1);
// assume no PT for now
doPT = false;
@@ -267,7 +257,7 @@ namespace TextureDecoder.Pvrtc
/// This performs a HW bit accurate interpolation of either the A or B colours for a particular pixel
/// <para>NOTE: It is assumed that the source colours are in ARGB 5554 format - This means that some "preparation" of the values will be necessary</para>
/// </summary>
private unsafe static void InterpolateColours(int* colorP, int* colorQ, int* colorR, int* colorS, int ci, bool do2bitMode, int x, int y, int* result)
private static void InterpolateColours(ReadOnlySpan<int> colorP, ReadOnlySpan<int> colorQ, ReadOnlySpan<int> colorR, ReadOnlySpan<int> colorS, int ci, bool do2bitMode, int x, int y, Span<int> result)
{
// put the x and y values into the right range and get the u and v scale amounts
int v = ((y & 0x3) | ((~y & 0x2) << 1)) - BlockYSize / 2;
@@ -333,10 +323,10 @@ namespace TextureDecoder.Pvrtc
/// <summary>
/// Given the block and the texture type and it's relative position in the 2x2 group of blocks, extract the bit patterns for the fully defined pixels.
/// </summary>
private unsafe static void UnpackModulations(AmtcBlock* block, bool do2bitMode, int* modulationVals, int* modulationModes, int startX, int startY)
private static void UnpackModulations(AmtcBlock block, bool do2bitMode, Span<int> modulationVals, Span<int> modulationModes, int startX, int startY)
{
int blockModMode = (int)(block->PackedData1 & 1);
uint modulationBits = block->PackedData0;
int blockModMode = (int)(block.PackedData1 & 1);
uint modulationBits = block.PackedData0;
// if it's in an interpolated mode
if (do2bitMode && (blockModMode != 0))
@@ -404,14 +394,16 @@ namespace TextureDecoder.Pvrtc
/// <summary>
/// Given a block, extract the colour information and convert to 5554 formats
/// </summary>
private unsafe static void Unpack5554Colour(AmtcBlock* block, int* abColors)
private static void Unpack5554Colour(AmtcBlock block, Span<int> abColors)
{
uint* rawBits = stackalloc uint[2];
// extract A and B
// 15 bits (shifted up by one)
rawBits[0] = block->PackedData1 & (0xFFFE);
// 16 bits
rawBits[1] = block->PackedData1 >> 16;
ReadOnlySpan<uint> rawBits =
[
// extract A and B
// 15 bits (shifted up by one)
block.PackedData1 & (0xFFFE),
// 16 bits
block.PackedData1 >> 16,
];
// step through both colours
for (int i = 0; i < 2; i++)
@@ -463,27 +455,10 @@ namespace TextureDecoder.Pvrtc
}
}
/// <summary>
/// Check that a number is an integer power of two, i.e. 1, 2, 4, 8, ... etc. Returns false for zero
/// </summary>
/// <param name="input">A number</param>
/// <returns>True if the number is an integer power of two, else false</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsPowerOf2(uint input)
{
if (input == 0)
{
return false;
}
uint minus1 = input - 1;
return ((input | minus1) == (input ^ minus1));
}
/// <summary>
/// Define an expression to either wrap or clamp large or small vals to the legal coordinate range
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static int LimitCoord(int value, int size)
{
#if ASSUME_IMAGE_TILING
@@ -512,7 +487,7 @@ namespace TextureDecoder.Pvrtc
private const int BlockX2bpp = 8;
private const int BlockX4bpp = 4;
private static readonly int[] m_repVals0 = new int[] { 0, 3, 5, 8 };
private static readonly int[] m_repVals1 = new int[] { 0, 4, 4, 8 };
private static readonly int[] m_repVals0 = [0, 3, 5, 8];
private static readonly int[] m_repVals1 = [0, 4, 4, 8];
}
}
@@ -0,0 +1,34 @@
namespace AssetRipper.TextureDecoder;
public readonly ref struct ReadOnlySpan2D<T>
{
public int Width { get; }
public int Height { get; }
public ReadOnlySpan<T> Data { get; }
public ReadOnlySpan2D(ReadOnlySpan<T> data, int width, int height)
{
Validate(data, width, height);
Data = data;
Width = width;
Height = height;
static void Validate(ReadOnlySpan<T> data, int width, int height)
{
ArgumentOutOfRangeException.ThrowIfNegative(width);
ArgumentOutOfRangeException.ThrowIfNegative(height);
if (data.Length != width * height)
{
throw new ArgumentException("Data length does not match width and height.", nameof(data));
}
}
}
//public ReadOnlySpan<T> this[int y] => Data.Slice(y * Width, Width);
public T this[int x, int y] => Data[y * Width + x];
public static explicit operator ReadOnlySpan2D<T>(T[] array) => new(array, array.Length, 1);
public static explicit operator ReadOnlySpan2D<T>(ReadOnlySpan<T> span) => new(span, span.Length, 1);
}
+95
View File
@@ -0,0 +1,95 @@
using AssetRipper.TextureDecoder.Rgb.Formats;
namespace AssetRipper.TextureDecoder.Rgb;
public static class Color
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void SetBlack<TThis, TThisChannel>(this ref TThis color)
where TThisChannel : unmanaged
where TThis : unmanaged, IColor<TThisChannel>
{
color.SetChannels(
NumericConversion.GetMinimumValue<TThisChannel>(),
NumericConversion.GetMinimumValue<TThisChannel>(),
NumericConversion.GetMinimumValue<TThisChannel>(),
NumericConversion.GetMaximumValue<TThisChannel>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void SetWhite<TThis, TThisChannel>(this ref TThis color)
where TThisChannel : unmanaged
where TThis : unmanaged, IColor<TThisChannel>
{
color.SetChannels(
NumericConversion.GetMaximumValue<TThisChannel>(),
NumericConversion.GetMaximumValue<TThisChannel>(),
NumericConversion.GetMaximumValue<TThisChannel>(),
NumericConversion.GetMaximumValue<TThisChannel>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void SetConvertedChannels<TThis, TThisChannel, TSourceChannel>(this ref TThis color, TSourceChannel r, TSourceChannel g, TSourceChannel b)
where TThisChannel : unmanaged
where TSourceChannel : unmanaged
where TThis : unmanaged, IColor<TThisChannel>
{
color.SetChannels(
NumericConversion.Convert<TSourceChannel, TThisChannel>(r),
NumericConversion.Convert<TSourceChannel, TThisChannel>(g),
NumericConversion.Convert<TSourceChannel, TThisChannel>(b),
NumericConversion.GetMaximumValue<TThisChannel>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void SetConvertedChannels<TThis, TThisChannel, TSourceChannel>(this ref TThis color, TSourceChannel r, TSourceChannel g, TSourceChannel b, TSourceChannel a)
where TThisChannel : unmanaged
where TSourceChannel : unmanaged
where TThis : unmanaged, IColor<TThisChannel>
{
color.SetChannels(
NumericConversion.Convert<TSourceChannel, TThisChannel>(r),
NumericConversion.Convert<TSourceChannel, TThisChannel>(g),
NumericConversion.Convert<TSourceChannel, TThisChannel>(b),
NumericConversion.Convert<TSourceChannel, TThisChannel>(a));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void SetConvertedChannels<TThis, TThisChannel, TSourceChannel>(this ref TThis color, ColorRGB<TSourceChannel> rgb, TSourceChannel a)
where TThisChannel : unmanaged
where TSourceChannel : unmanaged, INumberBase<TSourceChannel>, IMinMaxValue<TSourceChannel>
where TThis : unmanaged, IColor<TThisChannel>
{
color.SetChannels(
NumericConversion.Convert<TSourceChannel, TThisChannel>(rgb.R),
NumericConversion.Convert<TSourceChannel, TThisChannel>(rgb.G),
NumericConversion.Convert<TSourceChannel, TThisChannel>(rgb.B),
NumericConversion.Convert<TSourceChannel, TThisChannel>(a));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static int GetChannelCount<T>() where T : IColor
{
return (T.HasRedChannel ? 1 : 0) + (T.HasGreenChannel ? 1 : 0) + (T.HasBlueChannel ? 1 : 0) + (T.HasAlphaChannel ? 1 : 0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static TTarget Convert<TThis, TThisChannel, TTarget, TTargetChannel>(this TThis color)
where TThisChannel : unmanaged
where TTargetChannel : unmanaged
where TThis : unmanaged, IColor<TThisChannel>
where TTarget : unmanaged, IColor<TTargetChannel>
{
if (typeof(TThis) == typeof(TTarget))
{
return Unsafe.As<TThis, TTarget>(ref color);
}
else
{
TTarget destination = default;
color.GetChannels(out TThisChannel r, out TThisChannel g, out TThisChannel b, out TThisChannel a);
destination.SetConvertedChannels<TTarget, TTargetChannel, TThisChannel>(r, g, b, a);
return destination;
}
}
}
@@ -0,0 +1,27 @@
namespace AssetRipper.TextureDecoder.Rgb
{
internal static class DefaultColorMethods
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void GetChannels<T, TArg>(T color, out TArg r, out TArg g, out TArg b, out TArg a)
where TArg : unmanaged
where T : unmanaged, IColor<TArg>
{
r = color.R;
g = color.G;
b = color.B;
a = color.A;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal static void SetChannels<T, TArg>(ref T color, TArg r, TArg g, TArg b, TArg a)
where TArg : unmanaged
where T : unmanaged, IColor<TArg>
{
color.R = r;
color.G = g;
color.B = b;
color.A = a;
}
}
}
@@ -0,0 +1,59 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Rgb.Formats;
public partial struct ColorA<T> : IColor<ColorA<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
{
public readonly T R
{
get => NumericConversion.GetMinimumValueSafe<T>();
set { }
}
public readonly T G
{
get => NumericConversion.GetMinimumValueSafe<T>();
set { }
}
public readonly T B
{
get => NumericConversion.GetMinimumValueSafe<T>();
set { }
}
public T A { get; set; }
public ColorA(T a)
{
A = a;
}
public readonly void GetChannels(out T r, out T g, out T b, out T a)
{
r = R;
g = G;
b = B;
a = A;
}
public void SetChannels(T r, T g, T b, T a)
{
A = a;
}
static bool IColor.HasRedChannel => false;
static bool IColor.HasGreenChannel => false;
static bool IColor.HasBlueChannel => false;
static bool IColor.HasAlphaChannel => true;
static bool IColor.ChannelsAreFullyUtilized => true;
static Type IColor.ChannelType => typeof(T);
public static ColorA<T> Black => new(NumericConversion.GetMaximumValueSafe<T>());
static ColorA<T> IColor<ColorA<T>, T>.White => throw new NotSupportedException();
public override string ToString()
{
return $"{{ A: {A} }}";
}
}
@@ -0,0 +1,42 @@
namespace AssetRipper.TextureDecoder.Rgb.Formats
{
public partial struct ColorARGB16 : IColor<byte>
{
private UInt4Pair gb;
private UInt4Pair ar;
public byte B
{
readonly get => gb.LowValue;
set => gb.LowValue = value;
}
public byte G
{
readonly get => gb.HighValue;
set => gb.HighValue = value;
}
public byte R
{
readonly get => ar.LowValue;
set => ar.LowValue = value;
}
public byte A
{
readonly get => ar.HighValue;
set => ar.HighValue = value;
}
public readonly void GetChannels(out byte r, out byte g, out byte b, out byte a)
{
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
}
public void SetChannels(byte r, byte g, byte b, byte a)
{
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
}
}
}
@@ -0,0 +1,19 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Rgb.Formats
{
public partial struct ColorARGB16 : IColor<byte>
{
static bool IColor.HasRedChannel => true;
static bool IColor.HasGreenChannel => true;
static bool IColor.HasBlueChannel => true;
static bool IColor.HasAlphaChannel => true;
static bool IColor.ChannelsAreFullyUtilized => false;
static Type IColor.ChannelType => typeof(byte);
public override string ToString()
{
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
}
}
}
@@ -0,0 +1,20 @@
namespace AssetRipper.TextureDecoder.Rgb.Formats
{
public partial struct ColorARGB32 : IColor<byte>
{
public byte A { get; set; }
public byte R { get; set; }
public byte G { get; set; }
public byte B { get; set; }
public readonly void GetChannels(out byte r, out byte g, out byte b, out byte a)
{
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
}
public void SetChannels(byte r, byte g, byte b, byte a)
{
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
}
}
}
@@ -0,0 +1,19 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Rgb.Formats
{
public partial struct ColorARGB32 : IColor<byte>
{
static bool IColor.HasRedChannel => true;
static bool IColor.HasGreenChannel => true;
static bool IColor.HasBlueChannel => true;
static bool IColor.HasAlphaChannel => true;
static bool IColor.ChannelsAreFullyUtilized => true;
static Type IColor.ChannelType => typeof(byte);
public override string ToString()
{
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
}
}
}
@@ -0,0 +1,20 @@
namespace AssetRipper.TextureDecoder.Rgb.Formats
{
public partial struct ColorBGRA32 : IColor<byte>
{
public byte B { get; set; }
public byte G { get; set; }
public byte R { get; set; }
public byte A { get; set; }
public readonly void GetChannels(out byte r, out byte g, out byte b, out byte a)
{
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
}
public void SetChannels(byte r, byte g, byte b, byte a)
{
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
}
}
}
@@ -0,0 +1,19 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Rgb.Formats
{
public partial struct ColorBGRA32 : IColor<byte>
{
static bool IColor.HasRedChannel => true;
static bool IColor.HasGreenChannel => true;
static bool IColor.HasBlueChannel => true;
static bool IColor.HasAlphaChannel => true;
static bool IColor.ChannelsAreFullyUtilized => true;
static Type IColor.ChannelType => typeof(byte);
public override string ToString()
{
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
}
}
}
@@ -0,0 +1,59 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Rgb.Formats;
public partial struct ColorR<T> : IColor<ColorR<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
{
public T R { get; set; }
public readonly T G
{
get => NumericConversion.GetMinimumValueSafe<T>();
set { }
}
public readonly T B
{
get => NumericConversion.GetMinimumValueSafe<T>();
set { }
}
public readonly T A
{
get => NumericConversion.GetMaximumValueSafe<T>();
set { }
}
public ColorR(T r)
{
R = r;
}
public readonly void GetChannels(out T r, out T g, out T b, out T a)
{
r = R;
g = G;
b = B;
a = A;
}
public void SetChannels(T r, T g, T b, T a)
{
R = r;
}
static bool IColor.HasRedChannel => true;
static bool IColor.HasGreenChannel => false;
static bool IColor.HasBlueChannel => false;
static bool IColor.HasAlphaChannel => false;
static bool IColor.ChannelsAreFullyUtilized => true;
static Type IColor.ChannelType => typeof(T);
public static ColorR<T> Black => new(NumericConversion.GetMinimumValueSafe<T>());
public static ColorR<T> White => new(NumericConversion.GetMaximumValueSafe<T>());
public override string ToString()
{
return $"{{ R: {R} }}";
}
}
@@ -0,0 +1,57 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Rgb.Formats;
public partial struct ColorRG<T> : IColor<ColorRG<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
{
public T R { get; set; }
public T G { get; set; }
public readonly T B
{
get => NumericConversion.GetMinimumValueSafe<T>();
set { }
}
public readonly T A
{
get => NumericConversion.GetMaximumValueSafe<T>();
set { }
}
public ColorRG(T r, T g)
{
R = r;
G = g;
}
public readonly void GetChannels(out T r, out T g, out T b, out T a)
{
r = R;
g = G;
b = B;
a = A;
}
public void SetChannels(T r, T g, T b, T a)
{
R = r;
G = g;
}
static bool IColor.HasRedChannel => true;
static bool IColor.HasGreenChannel => true;
static bool IColor.HasBlueChannel => false;
static bool IColor.HasAlphaChannel => false;
static bool IColor.ChannelsAreFullyUtilized => true;
static Type IColor.ChannelType => typeof(T);
public static ColorRG<T> Black => new(NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>());
public static ColorRG<T> White => new(NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
public override string ToString()
{
return $"{{ R: {R}, G: {G} }}";
}
}
@@ -0,0 +1,55 @@
// Auto-generated code. Do not modify manually.
namespace AssetRipper.TextureDecoder.Rgb.Formats;
public partial struct ColorRGB<T> : IColor<ColorRGB<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
{
public T R { get; set; }
public T G { get; set; }
public T B { get; set; }
public readonly T A
{
get => NumericConversion.GetMaximumValueSafe<T>();
set { }
}
public ColorRGB(T r, T g, T b)
{
R = r;
G = g;
B = b;
}
public readonly void GetChannels(out T r, out T g, out T b, out T a)
{
r = R;
g = G;
b = B;
a = A;
}
public void SetChannels(T r, T g, T b, T a)
{
R = r;
G = g;
B = b;
}
static bool IColor.HasRedChannel => true;
static bool IColor.HasGreenChannel => true;
static bool IColor.HasBlueChannel => true;
static bool IColor.HasAlphaChannel => false;
static bool IColor.ChannelsAreFullyUtilized => true;
static Type IColor.ChannelType => typeof(T);
public static ColorRGB<T> Black => new(NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>());
public static ColorRGB<T> White => new(NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
public override string ToString()
{
return $"{{ R: {R}, G: {G}, B: {B} }}";
}
}
@@ -0,0 +1,62 @@
namespace AssetRipper.TextureDecoder.Rgb.Formats
{
/// <summary>
/// Also called RGB 565
/// </summary>
public partial struct ColorRGB16 : IColor<byte>
{
private ushort bits;
/// <summary>
/// 5 bits
/// </summary>
public byte R
{
readonly get => (byte)(((uint)bits >> 8) & 0xF8);
set
{
bits = (ushort)((((uint)value << 8) & 0xF800u) | (bits & ~0xF800u));
}
}
/// <summary>
/// 6 bits
/// </summary>
public byte G
{
readonly get => (byte)(((uint)bits >> 3) & 0xFC);
set
{
bits = (ushort)((((uint)value << 3) & 0x07E0u) | (bits & ~0x07E0u));
}
}
/// <summary>
/// 5 bits
/// </summary>
public byte B
{
readonly get => (byte)(((uint)bits << 3) & 0xF8);
set
{
bits = (ushort)((((uint)value >> 3) & 0x001Fu) | (bits & ~0x001Fu));
}
}
public readonly byte A
{
get => byte.MaxValue;
set { }
}
public readonly void GetChannels(out byte r, out byte g, out byte b, out byte a)
{
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
}
public void SetChannels(byte r, byte g, byte b, byte a)
{
bits = (ushort)(((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | ((b & 0xF8u) >> 3));
}
}
}
Loaded 100 of 241 files, more files were not shown because too many files have changed in this diff. Show more