├── .gitignore ├── resources └── tests │ ├── .gitignore │ ├── ferris_512.png │ ├── textures │ ├── BC1.ktx2 │ ├── BC2.ktx2 │ ├── BC3.ktx2 │ ├── BC4.ktx2 │ ├── BC5.ktx2 │ ├── BC7.ktx2 │ ├── ATC_RGB.dds │ ├── BC1A.ktx2 │ ├── BC6H.ktx2 │ ├── DXT2.ktx2 │ ├── DXT4.ktx2 │ ├── ASTC_4x4.ktx2 │ ├── ASTC_5x4.ktx2 │ ├── ASTC_5x5.ktx2 │ ├── ASTC_6x5.ktx2 │ ├── ASTC_6x6.ktx2 │ ├── ASTC_8x5.ktx2 │ ├── ASTC_8x6.ktx2 │ ├── ASTC_8x8.ktx2 │ ├── EAC_R11.ktx2 │ ├── EAC_RG11.ktx2 │ ├── ETC1_RGB.ktx2 │ ├── ETC2_RGB.ktx2 │ ├── ASTC_10x10.ktx2 │ ├── ASTC_10x5.ktx2 │ ├── ASTC_10x6.ktx2 │ ├── ASTC_10x8.ktx2 │ ├── ASTC_12x10.ktx2 │ ├── ASTC_12x12.ktx2 │ ├── CRUNCH_DXN.crn │ ├── CRUNCH_DXT1.crn │ ├── CRUNCH_DXT5.crn │ ├── CRUNCH_DXT5A.crn │ ├── ETC2_RGBA.ktx2 │ ├── ETC2_RGB_A1.ktx2 │ ├── BASISU_ETC1S.ktx2 │ ├── BASISU_UASTC.ktx2 │ ├── PVRTCII_2bpp.ktx2 │ ├── PVRTCII_4bpp.ktx2 │ ├── ATC_RGBA_Explicit.dds │ ├── PVRTCI_2bpp_RGB.ktx2 │ ├── PVRTCI_2bpp_RGBA.ktx2 │ ├── PVRTCI_4bpp_RGB.ktx2 │ ├── PVRTCI_4bpp_RGBA.ktx2 │ ├── UNITYCRUNCH_DXN.crn │ ├── UNITYCRUNCH_DXT1.crn │ ├── UNITYCRUNCH_DXT5.crn │ ├── UNITYCRUNCH_DXT5A.crn │ ├── UNITYCRUNCH_ETC1.crn │ ├── UNITYCRUNCH_ETC1S.crn │ ├── UNITYCRUNCH_ETC2.crn │ ├── UNITYCRUNCH_ETC2A.crn │ ├── UNITYCRUNCH_ETC2AS.crn │ ├── ATC_RGBA_Interpolated.dds │ └── SharedExponentR9G9B9E5.ktx2 │ └── README.md ├── .gitattributes.txt ├── bindings └── python │ ├── .gitignore │ ├── .cargo │ └── config.toml │ ├── pyproject.toml │ ├── LICENSE-MIT │ ├── Cargo.toml │ ├── texture2ddecoder_rs.pyi │ ├── README.md │ ├── src │ └── lib.rs │ ├── tests │ └── test_main.py │ └── LICENSE-APACHE ├── src ├── bcn │ ├── bc4.rs │ ├── bc5.rs │ ├── bc2.rs │ ├── bc3.rs │ ├── bc1.rs │ ├── consts.rs │ └── bc7.rs ├── crunch │ ├── crn_utils.rs │ ├── crn_consts.rs │ ├── crn_decomp.rs │ ├── crn_static_huffman_data_model.rs │ └── crn_symbol_codec.rs ├── etc.rs ├── unitycrunch │ └── crn_decomp.rs ├── bcn.rs ├── etc │ ├── consts.rs │ ├── eac.rs │ ├── etc1.rs │ └── etc2.rs ├── bitreader.rs ├── crunch.rs ├── unitycrunch.rs ├── color.rs ├── macros.rs ├── atc.rs ├── lib.rs ├── crnlib.rs └── f16.rs ├── .github └── workflows │ ├── publish.yml │ ├── ci.yml │ └── publish_py.yml ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── LICENSE-APACHE └── tests └── test.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /resources/tests/.gitignore: -------------------------------------------------------------------------------- 1 | /decompressed -------------------------------------------------------------------------------- /.gitattributes.txt: -------------------------------------------------------------------------------- 1 | *.rs text eol=lf 2 | -------------------------------------------------------------------------------- /bindings/python/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /bindings/python/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | PYO3_USE_ABI3_FORWARD_COMPATIBILITY = "1" 3 | -------------------------------------------------------------------------------- /resources/tests/ferris_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/ferris_512.png -------------------------------------------------------------------------------- /resources/tests/textures/BC1.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/BC1.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/BC2.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/BC2.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/BC3.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/BC3.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/BC4.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/BC4.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/BC5.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/BC5.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/BC7.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/BC7.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ATC_RGB.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ATC_RGB.dds -------------------------------------------------------------------------------- /resources/tests/textures/BC1A.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/BC1A.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/BC6H.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/BC6H.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/DXT2.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/DXT2.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/DXT4.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/DXT4.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_4x4.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_4x4.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_5x4.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_5x4.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_5x5.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_5x5.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_6x5.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_6x5.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_6x6.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_6x6.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_8x5.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_8x5.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_8x6.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_8x6.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_8x8.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_8x8.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/EAC_R11.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/EAC_R11.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/EAC_RG11.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/EAC_RG11.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ETC1_RGB.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ETC1_RGB.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ETC2_RGB.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ETC2_RGB.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_10x10.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_10x10.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_10x5.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_10x5.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_10x6.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_10x6.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_10x8.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_10x8.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_12x10.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_12x10.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ASTC_12x12.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ASTC_12x12.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/CRUNCH_DXN.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/CRUNCH_DXN.crn -------------------------------------------------------------------------------- /resources/tests/textures/CRUNCH_DXT1.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/CRUNCH_DXT1.crn -------------------------------------------------------------------------------- /resources/tests/textures/CRUNCH_DXT5.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/CRUNCH_DXT5.crn -------------------------------------------------------------------------------- /resources/tests/textures/CRUNCH_DXT5A.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/CRUNCH_DXT5A.crn -------------------------------------------------------------------------------- /resources/tests/textures/ETC2_RGBA.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ETC2_RGBA.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ETC2_RGB_A1.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ETC2_RGB_A1.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/BASISU_ETC1S.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/BASISU_ETC1S.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/BASISU_UASTC.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/BASISU_UASTC.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/PVRTCII_2bpp.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/PVRTCII_2bpp.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/PVRTCII_4bpp.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/PVRTCII_4bpp.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/ATC_RGBA_Explicit.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ATC_RGBA_Explicit.dds -------------------------------------------------------------------------------- /resources/tests/textures/PVRTCI_2bpp_RGB.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/PVRTCI_2bpp_RGB.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/PVRTCI_2bpp_RGBA.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/PVRTCI_2bpp_RGBA.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/PVRTCI_4bpp_RGB.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/PVRTCI_4bpp_RGB.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/PVRTCI_4bpp_RGBA.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/PVRTCI_4bpp_RGBA.ktx2 -------------------------------------------------------------------------------- /resources/tests/textures/UNITYCRUNCH_DXN.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/UNITYCRUNCH_DXN.crn -------------------------------------------------------------------------------- /resources/tests/textures/UNITYCRUNCH_DXT1.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/UNITYCRUNCH_DXT1.crn -------------------------------------------------------------------------------- /resources/tests/textures/UNITYCRUNCH_DXT5.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/UNITYCRUNCH_DXT5.crn -------------------------------------------------------------------------------- /resources/tests/textures/UNITYCRUNCH_DXT5A.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/UNITYCRUNCH_DXT5A.crn -------------------------------------------------------------------------------- /resources/tests/textures/UNITYCRUNCH_ETC1.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/UNITYCRUNCH_ETC1.crn -------------------------------------------------------------------------------- /resources/tests/textures/UNITYCRUNCH_ETC1S.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/UNITYCRUNCH_ETC1S.crn -------------------------------------------------------------------------------- /resources/tests/textures/UNITYCRUNCH_ETC2.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/UNITYCRUNCH_ETC2.crn -------------------------------------------------------------------------------- /resources/tests/textures/UNITYCRUNCH_ETC2A.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/UNITYCRUNCH_ETC2A.crn -------------------------------------------------------------------------------- /resources/tests/textures/UNITYCRUNCH_ETC2AS.crn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/UNITYCRUNCH_ETC2AS.crn -------------------------------------------------------------------------------- /resources/tests/textures/ATC_RGBA_Interpolated.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/ATC_RGBA_Interpolated.dds -------------------------------------------------------------------------------- /resources/tests/textures/SharedExponentR9G9B9E5.ktx2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/HEAD/resources/tests/textures/SharedExponentR9G9B9E5.ktx2 -------------------------------------------------------------------------------- /src/bcn/bc4.rs: -------------------------------------------------------------------------------- 1 | use crate::bcn::bc3::decode_bc3_alpha; 2 | 3 | #[inline] 4 | pub fn decode_bc4_block(data: &[u8], outbuf: &mut [u32]) { 5 | decode_bc3_alpha(data, outbuf, 2); 6 | } 7 | -------------------------------------------------------------------------------- /src/bcn/bc5.rs: -------------------------------------------------------------------------------- 1 | use crate::bcn::bc3::decode_bc3_alpha; 2 | 3 | #[inline] 4 | pub fn decode_bc5_block(data: &[u8], outbuf: &mut [u32]) { 5 | decode_bc3_alpha(data, outbuf, 2); 6 | decode_bc3_alpha(&data[8..], outbuf, 1); 7 | } 8 | -------------------------------------------------------------------------------- /resources/tests/README.md: -------------------------------------------------------------------------------- 1 | The [ferris_512.png](/ferris_512.png) is just a repositioned [original Ferris](https://www.rustacean.net/assets/rustacean-orig-noshadow.png) from [rustacean.net](https://www.rustacean.net/). 2 | 3 | Most textures were created using [PVRTexTool](https://developer.imaginationtech.com/pvrtextool/). 4 | ETC1_RGB, ATC_RGB, ATC_RGBA were created using [Compressonator](https://github.com/GPUOpen-Tools/compressonator). -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | workflow_run: 5 | workflows: CI 6 | branches: master 7 | types: completed 8 | 9 | jobs: 10 | publish: 11 | name: Publish 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 45 14 | if: ${{ github.event.workflow_run.conclusion == 'success'}} 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: dtolnay/rust-toolchain@nightly 18 | - run: cargo package 19 | - run: cargo publish --token ${{ secrets.CARGO_PUBLISH_TOKEN }} 20 | -------------------------------------------------------------------------------- /bindings/python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=1.0,<2.0"] 3 | build-backend = "maturin" 4 | 5 | [tool.maturin] 6 | sdist-include = [ 7 | "../../Cargo.toml", 8 | "../../src", 9 | "../../Cargo.lock", 10 | "../../README.md", 11 | "../../LICENSE-MIT", 12 | "../../LICENSE-APACHE", 13 | "./texture2ddecoder_rs.pyi", 14 | "./pyproject.toml", 15 | "./Cargo.toml", 16 | "./src", 17 | "./README.md", 18 | "./LICENSE-MIT", 19 | "./LICENSE-APACHE", 20 | ] 21 | python-source = "python" 22 | module-name = "texture2ddecoder_rs" 23 | bindings = 'pyo3' 24 | -------------------------------------------------------------------------------- /src/crunch/crn_utils.rs: -------------------------------------------------------------------------------- 1 | #[inline] 2 | pub fn ceil_log2i(v: u32) -> u32 { 3 | let mut l: u32 = v.ilog2(); 4 | if (l != 32) && (v > (1 << l)) { 5 | l += 1; 6 | } 7 | l 8 | } 9 | 10 | // Returns the total number of bits needed to encode v. 11 | #[inline] 12 | pub fn total_bits(mut v: u32) -> u32 { 13 | let mut l: u32 = 0; 14 | while v > 0 { 15 | v >>= 1; 16 | l += 1; 17 | } 18 | l 19 | } 20 | 21 | #[inline] 22 | pub fn limit(x: &mut u32, n: u32) { 23 | let v: i32 = (*x as i32) - n as i32; 24 | let msk: i32 = v >> 31; 25 | *x = (((*x as i32) & msk) | (v & !msk)) as u32; 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | permissions: 9 | contents: read 10 | 11 | env: 12 | RUSTFLAGS: -Dwarnings 13 | 14 | jobs: 15 | test: 16 | name: Test 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 45 19 | steps: 20 | - uses: actions/checkout@v3 21 | - uses: dtolnay/rust-toolchain@nightly 22 | - run: cargo test 23 | 24 | lint: 25 | name: Lint 26 | runs-on: ubuntu-latest 27 | timeout-minutes: 45 28 | steps: 29 | - uses: actions/checkout@v3 30 | - uses: dtolnay/rust-toolchain@nightly 31 | - run: rustup component add clippy 32 | - run: cargo clippy -- -D warnings 33 | -------------------------------------------------------------------------------- /src/bcn/bc2.rs: -------------------------------------------------------------------------------- 1 | use crate::bcn::bc1::decode_bc1_block; 2 | 3 | #[inline] 4 | pub fn decode_bc2_alpha(data: &[u8], outbuf: &mut [u32], channel: usize) { 5 | let channel_shift = channel * 8; 6 | let channel_mask = 0xFFFFFFFF ^ (0xFF << channel_shift); 7 | outbuf[0..16].iter_mut().enumerate().for_each(|(i, p)| { 8 | // 4 bit alpha encoding - one byte for two pixels 9 | // -> byte index increases every two entriees 10 | // -> bit offset switched between 0 and 4 11 | let byte_idx = i >> 1; 12 | let bit_off = (i & 1) << 2; 13 | let mut av = (data[byte_idx] >> bit_off) & 0xF; 14 | av = (av << 4) | av; 15 | *p = (*p & channel_mask) | ((av as u32) << channel_shift); 16 | }) 17 | } 18 | 19 | #[inline] 20 | pub fn decode_bc2_block(data: &[u8], outbuf: &mut [u32]) { 21 | decode_bc1_block(&data[8..], outbuf); 22 | decode_bc2_alpha(data, outbuf, 3); 23 | } 24 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "texture2ddecoder" 3 | version = "0.1.2" 4 | edition = "2021" 5 | include = [ 6 | "src/*", 7 | "Cargo.toml", 8 | "README.md", 9 | "LICENSE-APACHE", 10 | "LICENSE-MIT", 11 | "COPYRIGHT" 12 | ] 13 | authors = ["Rudolf Kolbe "] 14 | description = "pure Rust no-std texture decoder" 15 | documentation = "https://docs.rs/texture2ddecoder" 16 | homepage = "https://crates.io/crates/texture2ddecoder" 17 | license = "MIT OR Apache-2.0" 18 | readme = "README.md" 19 | repository = "https://github.com/UniversalGameExtraction/texture2ddecoder" 20 | categories = [ 21 | "graphics", 22 | "no-std", 23 | "no-std::no-alloc", 24 | "multimedia::encoding", 25 | "multimedia::images" 26 | ] 27 | 28 | 29 | [dependencies] 30 | paste = "^1.0.12" 31 | 32 | [features] 33 | default = ["alloc"] 34 | alloc = [] 35 | 36 | [dev-dependencies] 37 | ddsfile = "^0.5.1" 38 | image = "^0.24" 39 | ktx2 = "^0.3.0" 40 | lazy_static = "^1.4.0" 41 | half = {version="2.3.1", features = []} 42 | -------------------------------------------------------------------------------- /src/etc.rs: -------------------------------------------------------------------------------- 1 | use crate::macros::block_decoder; 2 | 3 | pub(crate) mod consts; 4 | pub(crate) mod eac; 5 | pub(crate) mod etc1; 6 | pub(crate) mod etc2; 7 | 8 | pub use eac::{ 9 | decode_eac_block, decode_eac_signed_block, decode_eacr_block, decode_eacr_signed_block, 10 | decode_eacrg_block, decode_eacrg_signed_block, 11 | }; 12 | pub use etc1::decode_etc1_block; 13 | pub use etc2::{ 14 | decode_etc2_a8_block, decode_etc2_rgb_block, decode_etc2_rgba1_block, decode_etc2_rgba8_block, 15 | }; 16 | 17 | block_decoder!("etc1", 4, 4, 8, decode_etc1_block); 18 | block_decoder!("etc2_rgb", 4, 4, 8, decode_etc2_rgb_block); 19 | block_decoder!("etc2_rgba1", 4, 4, 8, decode_etc2_rgba1_block); 20 | block_decoder!("etc2_rgba8", 4, 4, 16, decode_etc2_rgba8_block); 21 | 22 | // TODO: set alpha to 0xff 23 | block_decoder!("eacr", 4, 4, 8, decode_eacr_block); 24 | block_decoder!("eacr_signed", 4, 4, 8, decode_eacr_signed_block); 25 | block_decoder!("eacrg", 4, 4, 16, decode_eacrg_block); 26 | block_decoder!("eacrg_signed", 4, 4, 16, decode_eacrg_signed_block); 27 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /bindings/python/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /bindings/python/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "texture2ddecoder_rs" 3 | version = "0.1.2" 4 | edition = "2021" 5 | include = [ 6 | "src/*", 7 | "texture2ddecoder_rs.pyi", 8 | "Cargo.toml", 9 | "README.md", 10 | "LICENSE-APACHE", 11 | "LICENSE-MIT", 12 | "COPYRIGHT", 13 | "../../src/*", 14 | "../../Cargo.toml", 15 | "../../README.md", 16 | "../../LICENSE-APACHE", 17 | "../../LICENSE-MIT", 18 | ] 19 | authors = ["Rudolf Kolbe "] 20 | description = "texture2ddecoder python bindings" 21 | documentation = "https://docs.rs/texture2ddecoder" 22 | homepage = "https://crates.io/crates/texture2ddecoder" 23 | license = "MIT OR Apache-2.0" 24 | readme = "README.md" 25 | repository = "https://github.com/UniversalGameExtraction/texture2ddecoder" 26 | categories = [ 27 | "graphics", 28 | "no-std", 29 | "no-std::no-alloc", 30 | "multimedia::encoding", 31 | "multimedia::images", 32 | ] 33 | 34 | [lib] 35 | name = "texture2ddecoder_rs" 36 | crate-type = ["cdylib"] 37 | 38 | [dependencies] 39 | texture2ddecoder = { path = "../.." } 40 | pyo3 = { version = "^0.20.2", features = ["extension-module"] } 41 | paste = "^1.0.12" 42 | -------------------------------------------------------------------------------- /src/bcn/bc3.rs: -------------------------------------------------------------------------------- 1 | use crate::bcn::bc1::decode_bc1_block; 2 | 3 | #[inline] 4 | pub fn decode_bc3_alpha(data: &[u8], outbuf: &mut [u32], channel: usize) { 5 | // use u16 to avoid overflow and replicate equivalent behavior to C++ code 6 | let mut a: [u16; 8] = [data[0] as u16, data[1] as u16, 0, 0, 0, 0, 0, 0]; 7 | if a[0] > a[1] { 8 | a[2] = (a[0] * 6 + a[1]) / 7; 9 | a[3] = (a[0] * 5 + a[1] * 2) / 7; 10 | a[4] = (a[0] * 4 + a[1] * 3) / 7; 11 | a[5] = (a[0] * 3 + a[1] * 4) / 7; 12 | a[6] = (a[0] * 2 + a[1] * 5) / 7; 13 | a[7] = (a[0] + a[1] * 6) / 7; 14 | } else { 15 | a[2] = (a[0] * 4 + a[1]) / 5; 16 | a[3] = (a[0] * 3 + a[1] * 2) / 5; 17 | a[4] = (a[0] * 2 + a[1] * 3) / 5; 18 | a[5] = (a[0] + a[1] * 4) / 5; 19 | a[6] = 0; 20 | a[7] = 255; 21 | } 22 | 23 | let mut d: usize = (u64::from_le_bytes(data[..8].try_into().unwrap()) >> 16) as usize; 24 | 25 | let channel_shift = channel * 8; 26 | let channel_mask = 0xFFFFFFFF ^ (0xFF << channel_shift); 27 | outbuf.iter_mut().for_each(|p| { 28 | *p = (*p & channel_mask) | (a[d & 7] as u32) << channel_shift; 29 | d >>= 3; 30 | }); 31 | } 32 | 33 | #[inline] 34 | pub fn decode_bc3_block(data: &[u8], outbuf: &mut [u32]) { 35 | decode_bc1_block(&data[8..], outbuf); 36 | decode_bc3_alpha(data, outbuf, 3); 37 | } 38 | -------------------------------------------------------------------------------- /src/unitycrunch/crn_decomp.rs: -------------------------------------------------------------------------------- 1 | use super::crn_unpacker::*; 2 | use crate::crnlib::CrnFormat; 3 | use crate::crunch::crn_consts::*; 4 | extern crate alloc; 5 | 6 | pub fn crnd_unpack_begin(p_data: &[u8], data_size: u32) -> Result { 7 | if data_size < CRNHEADER_MIN_SIZE as u32 { 8 | return Err("Data size is below the minimum allowed."); 9 | } 10 | let mut p = CrnUnpacker::default(); 11 | if !p.init(p_data, data_size) { 12 | return Err("Failed to initialize Crunch decompressor."); 13 | } 14 | Ok(p) 15 | } 16 | 17 | pub fn crnd_get_crn_format_bits_per_texel(fmt: &mut CrnFormat) -> Result { 18 | match fmt { 19 | CrnFormat::Dxt1 20 | | CrnFormat::Dxt5a 21 | | CrnFormat::Etc1 22 | | CrnFormat::Etc2 23 | | CrnFormat::Etc1s => Ok(4), 24 | 25 | CrnFormat::Dxt3 26 | | CrnFormat::CCrnfmtDxt5 27 | | CrnFormat::DxnXy 28 | | CrnFormat::DxnYx 29 | | CrnFormat::Dxt5CcxY 30 | | CrnFormat::Dxt5XGxR 31 | | CrnFormat::Dxt5XGbr 32 | | CrnFormat::Dxt5Agbr 33 | | CrnFormat::Etc2a 34 | | CrnFormat::Etc2as => Ok(8), 35 | 36 | _ => Err("Texture format is not supported."), 37 | } 38 | } 39 | 40 | pub fn crnd_get_bytes_per_dxt_block(fmt: &mut CrnFormat) -> Result { 41 | Ok((crnd_get_crn_format_bits_per_texel(fmt)? << 4) >> 3) 42 | } 43 | -------------------------------------------------------------------------------- /src/bcn.rs: -------------------------------------------------------------------------------- 1 | use crate::macros::block_decoder; 2 | 3 | pub(crate) mod bc1; 4 | pub(crate) mod bc2; 5 | pub(crate) mod bc3; 6 | pub(crate) mod bc4; 7 | pub(crate) mod bc5; 8 | pub(crate) mod bc6; 9 | pub(crate) mod bc7; 10 | pub(crate) mod consts; 11 | 12 | pub use bc1::decode_bc1_block; 13 | pub use bc1::decode_bc1a_block; 14 | pub use bc2::decode_bc2_block; 15 | pub use bc3::decode_bc3_block; 16 | pub use bc4::decode_bc4_block; 17 | pub use bc5::decode_bc5_block; 18 | pub use bc6::{decode_bc6_block, decode_bc6_block_signed, decode_bc6_block_unsigned}; 19 | pub use bc7::decode_bc7_block; 20 | 21 | block_decoder!("bc1", 4, 4, 8, decode_bc1_block); 22 | block_decoder!("bc1a", 4, 4, 8, decode_bc1a_block); 23 | block_decoder!("bc2", 4, 4, 16, decode_bc2_block); 24 | block_decoder!("bc3", 4, 4, 16, decode_bc3_block); 25 | block_decoder!("bc4", 4, 4, 8, decode_bc4_block); 26 | block_decoder!("bc5", 4, 4, 16, decode_bc5_block); 27 | block_decoder!("bc6_signed", 4, 4, 16, decode_bc6_block_signed); 28 | block_decoder!("bc6_unsigned", 4, 4, 16, decode_bc6_block_unsigned); 29 | block_decoder!("bc7", 4, 4, 16, decode_bc7_block); 30 | 31 | pub fn decode_bc6( 32 | data: &[u8], 33 | width: usize, 34 | height: usize, 35 | image: &mut [u32], 36 | signed: bool, 37 | ) -> Result<(), &'static str> { 38 | match signed { 39 | true => decode_bc6_signed(data, width, height, image), 40 | false => decode_bc6_unsigned(data, width, height, image), 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/bcn/bc1.rs: -------------------------------------------------------------------------------- 1 | use crate::color::{color, rgb565_le}; 2 | 3 | #[inline] 4 | fn _decode_bc1_block(data: &[u8], outbuf: &mut [u32], use_alpha: bool) { 5 | let q0 = u16::from_le_bytes([data[0], data[1]]); 6 | let q1 = u16::from_le_bytes([data[2], data[3]]); 7 | let (r0, g0, b0) = rgb565_le(q0); 8 | let (r1, g1, b1) = rgb565_le(q1); 9 | 10 | let mut c: [u32; 4] = [color(r0, g0, b0, 255), color(r1, g1, b1, 255), 0, 0]; 11 | 12 | // C insanity..... 13 | let r0 = r0 as u16; 14 | let g0 = g0 as u16; 15 | let b0 = b0 as u16; 16 | let r1 = r1 as u16; 17 | let g1 = g1 as u16; 18 | let b1 = b1 as u16; 19 | 20 | if q0 > q1 { 21 | c[2] = color( 22 | ((r0 * 2 + r1) / 3) as u8, 23 | ((g0 * 2 + g1) / 3) as u8, 24 | ((b0 * 2 + b1) / 3) as u8, 25 | 255, 26 | ); 27 | c[3] = color( 28 | ((r0 + r1 * 2) / 3) as u8, 29 | ((g0 + g1 * 2) / 3) as u8, 30 | ((b0 + b1 * 2) / 3) as u8, 31 | 255, 32 | ); 33 | } else { 34 | c[2] = color( 35 | ((r0 + r1) / 2) as u8, 36 | ((g0 + g1) / 2) as u8, 37 | ((b0 + b1) / 2) as u8, 38 | 255, 39 | ); 40 | c[3] = color(0, 0, 0, if use_alpha { 0 } else { 255 }); 41 | } 42 | let mut d: usize = u32::from_le_bytes(data[4..8].try_into().unwrap()) as usize; 43 | (0..16).for_each(|i| { 44 | outbuf[i] = c[d & 3]; 45 | d >>= 2; 46 | }); 47 | } 48 | 49 | #[inline] 50 | pub fn decode_bc1_block(data: &[u8], outbuf: &mut [u32]) { 51 | _decode_bc1_block(data, outbuf, false) 52 | } 53 | 54 | #[inline] 55 | pub fn decode_bc1a_block(data: &[u8], outbuf: &mut [u32]) { 56 | _decode_bc1_block(data, outbuf, true); 57 | } 58 | -------------------------------------------------------------------------------- /src/etc/consts.rs: -------------------------------------------------------------------------------- 1 | pub(crate) static WRITE_ORDER_TABLE: [usize; 16] = 2 | [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15]; 3 | pub(crate) static WRITE_ORDER_TABLE_REV: [usize; 16] = 4 | [15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0]; 5 | pub(crate) static ETC1_MODIFIER_TABLE: [[i16; 2]; 8] = [ 6 | [2, 8], 7 | [5, 17], 8 | [9, 29], 9 | [13, 42], 10 | [18, 60], 11 | [24, 80], 12 | [33, 106], 13 | [47, 183], 14 | ]; 15 | pub(crate) static ETC2A_MODIFIER_TABLE: [[[i16; 2]; 8]; 2] = [ 16 | [ 17 | [0, 8], 18 | [0, 17], 19 | [0, 29], 20 | [0, 42], 21 | [0, 60], 22 | [0, 80], 23 | [0, 106], 24 | [0, 183], 25 | ], 26 | [ 27 | [2, 8], 28 | [5, 17], 29 | [9, 29], 30 | [13, 42], 31 | [18, 60], 32 | [24, 80], 33 | [33, 106], 34 | [47, 183], 35 | ], 36 | ]; 37 | pub(crate) static ETC1_SUBBLOCK_TABLE: [[usize; 16]; 2] = [ 38 | [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], 39 | [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], 40 | ]; 41 | pub(crate) static ETC2_DISTANCE_TABLE: [i16; 8] = [3, 6, 11, 16, 23, 32, 41, 64]; 42 | pub(crate) static ETC2_ALPHA_MOD_TABLE: [[i8; 8]; 16] = [ 43 | [-3, -6, -9, -15, 2, 5, 8, 14], 44 | [-3, -7, -10, -13, 2, 6, 9, 12], 45 | [-2, -5, -8, -13, 1, 4, 7, 12], 46 | [-2, -4, -6, -13, 1, 3, 5, 12], 47 | [-3, -6, -8, -12, 2, 5, 7, 11], 48 | [-3, -7, -9, -11, 2, 6, 8, 10], 49 | [-4, -7, -8, -11, 3, 6, 7, 10], 50 | [-3, -5, -8, -11, 2, 4, 7, 10], 51 | [-2, -6, -8, -10, 1, 5, 7, 9], 52 | [-2, -5, -8, -10, 1, 4, 7, 9], 53 | [-2, -4, -8, -10, 1, 3, 7, 9], 54 | [-2, -5, -7, -10, 1, 4, 6, 9], 55 | [-3, -4, -7, -10, 2, 3, 6, 9], 56 | [-1, -2, -3, -10, 0, 1, 2, 9], 57 | [-4, -6, -8, -9, 3, 5, 7, 8], 58 | [-3, -5, -7, -9, 2, 4, 6, 8], 59 | ]; 60 | -------------------------------------------------------------------------------- /src/bitreader.rs: -------------------------------------------------------------------------------- 1 | #[inline] 2 | fn getbits_raw(buf: &[u8], bit_offset: usize, num_bits: usize, dst: &mut [u8]) { 3 | let bytes_offset = bit_offset / 8; 4 | let bytes_end: usize = (bit_offset + num_bits).div_ceil(8); 5 | dst[0..(bytes_end - bytes_offset)].copy_from_slice(&buf[bytes_offset..bytes_end]); 6 | } 7 | 8 | #[inline] 9 | pub fn getbits(buf: &[u8], bit_offset: usize, num_bits: usize) -> i32 { 10 | let shift = bit_offset % 8; 11 | 12 | let mut raw = [0u8; 4]; 13 | getbits_raw(buf, bit_offset, num_bits, &mut raw); 14 | 15 | // shift the bits we don't need out 16 | i32::from_le_bytes(raw) >> shift & ((1 << num_bits) - 1) 17 | } 18 | 19 | #[inline] 20 | pub fn getbits64(buf: &[u8], bit: isize, len: usize) -> u64 { 21 | let mask: u64 = if len == 64 { 22 | 0xffffffffffffffff 23 | } else { 24 | (1 << len) - 1 25 | }; 26 | if len == 0 { 27 | 0 28 | } else if bit >= 64 { 29 | u64::from_le_bytes(buf[8..16].try_into().unwrap()) >> (bit - 64) & mask 30 | } else if bit <= 0 { 31 | u64::from_le_bytes(buf[..8].try_into().unwrap()) << 0u64.overflowing_sub(bit as u64).0 32 | & mask 33 | } else if bit as usize + len <= 64 { 34 | u64::from_le_bytes(buf[..8].try_into().unwrap()) >> bit & mask 35 | } else { 36 | u64::from_le_bytes(buf[..8].try_into().unwrap()) >> bit 37 | | u64::from_le_bytes(buf[8..16].try_into().unwrap()) << (64 - bit) & mask 38 | } 39 | } 40 | 41 | pub struct BitReader<'a> { 42 | data: &'a [u8], 43 | bit_pos: usize, 44 | } 45 | 46 | impl BitReader<'_> { 47 | #[inline] 48 | pub const fn new(data: &[u8], bit_pos: usize) -> BitReader { 49 | BitReader { data, bit_pos } 50 | } 51 | 52 | #[inline] 53 | pub fn read(&mut self, num_bits: usize) -> u16 { 54 | let ret = self.peek(0, num_bits); 55 | self.bit_pos += num_bits; 56 | ret 57 | } 58 | 59 | #[inline] 60 | pub fn peek(&self, offset: usize, num_bits: usize) -> u16 { 61 | let bit_pos = self.bit_pos + offset; 62 | let shift = bit_pos & 7; 63 | 64 | let mut raw = [0u8; 4]; 65 | getbits_raw(self.data, bit_pos, num_bits, &mut raw); 66 | let data: u32 = u32::from_le_bytes(raw); 67 | 68 | (data >> shift as u32) as u16 & ((1 << num_bits as u16) - 1) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/crunch.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod crn_consts; 2 | pub(crate) mod crn_decomp; 3 | pub(crate) mod crn_static_huffman_data_model; 4 | pub(crate) mod crn_symbol_codec; 5 | pub(crate) mod crn_unpacker; 6 | pub(crate) mod crn_utils; 7 | use super::crnlib::{CrnFormat, CrnTextureInfo}; 8 | use crate::bcn; 9 | use core::cmp::max; 10 | extern crate alloc; 11 | 12 | pub struct CrunchDecodeHandler { 13 | pub format: CrnFormat, 14 | pub dxt_data: alloc::vec::Vec, 15 | pub faces: u32, 16 | } 17 | 18 | pub fn crunch_unpack_level( 19 | data: &[u8], 20 | data_size: u32, 21 | level_index: u32, 22 | ) -> Result { 23 | let mut tex_info: CrnTextureInfo = CrnTextureInfo::default(); 24 | if !tex_info.crnd_get_texture_info(data, data_size) { 25 | return Err("Invalid crunch texture encoding."); 26 | } 27 | let mut p_context: crn_unpacker::CrnUnpacker<'_> = 28 | crn_decomp::crnd_unpack_begin(data, data_size)?; 29 | let width = max(1, tex_info.width >> level_index); 30 | let height = max(1, tex_info.height >> level_index); 31 | let blocks_x: u32 = max(1, (width + 3) >> 2); 32 | let blocks_y: u32 = max(1, (height + 3) >> 2); 33 | let row_pitch: u32 = blocks_x * crn_decomp::crnd_get_bytes_per_dxt_block(&mut tex_info.format)?; 34 | let total_face_size: u32 = row_pitch * blocks_y; 35 | match p_context.crnd_unpack_level(total_face_size, row_pitch, level_index) { 36 | Ok(res) => Ok(CrunchDecodeHandler { 37 | format: tex_info.format, 38 | dxt_data: res, 39 | faces: tex_info.faces, 40 | }), 41 | Err(err) => Err(err), 42 | } 43 | } 44 | 45 | pub fn decode_crunch( 46 | data: &[u8], 47 | width: usize, 48 | height: usize, 49 | image: &mut [u32], 50 | ) -> Result<(), &'static str> { 51 | let handler = crunch_unpack_level(data, data.len() as u32, 0)?; 52 | match handler.format { 53 | CrnFormat::Dxt1 => bcn::decode_bc1(&handler.dxt_data, width, height, image), 54 | 55 | CrnFormat::CCrnfmtDxt5 56 | | CrnFormat::Dxt5CcxY 57 | | CrnFormat::Dxt5XGbr 58 | | CrnFormat::Dxt5Agbr 59 | | CrnFormat::Dxt5XGxR => bcn::decode_bc3(&handler.dxt_data, width, height, image), 60 | 61 | CrnFormat::Dxt5a => bcn::decode_bc4(&handler.dxt_data, width, height, image), 62 | 63 | CrnFormat::DxnXy | CrnFormat::DxnYx => { 64 | bcn::decode_bc5(&handler.dxt_data, width, height, image) 65 | } 66 | _ => Err("Invalid crunch format."), 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/etc/eac.rs: -------------------------------------------------------------------------------- 1 | use crate::color::color; 2 | use crate::etc::consts::{ETC2_ALPHA_MOD_TABLE, WRITE_ORDER_TABLE_REV}; 3 | 4 | #[inline] 5 | pub fn decode_eac_block(data: &[u8], color: usize, outbuf: &mut [u32]) { 6 | let mut multiplier: i32 = (data[1] >> 1 & 0x78) as i32; 7 | if multiplier == 0 { 8 | multiplier = 1; 9 | } 10 | let table = ETC2_ALPHA_MOD_TABLE[(data[1] & 0xf) as usize]; 11 | let mut l: usize = u64::from_le_bytes(data[0..8].try_into().unwrap()) as usize; 12 | let mut block: [u8; 4] = [0, 0, 0, 0]; 13 | for i in 0..16 { 14 | let val: i32 = data[0] as i32 * 8 + multiplier * table[l & 7] as i32 + 4; 15 | block[color] = if val < 0 { 16 | 0 17 | } else if val >= 2048 { 18 | 255 19 | } else { 20 | (val >> 3) as u8 21 | }; 22 | outbuf[WRITE_ORDER_TABLE_REV[i]] |= u32::from_le_bytes(block); 23 | l >>= 3; 24 | } 25 | } 26 | 27 | #[inline] 28 | pub fn decode_eac_signed_block(data: &[u8], color: usize, outbuf: &mut [u32]) { 29 | let base: i32 = (data[0] as i8) as i32; 30 | let mut multiplier: i32 = (data[1] >> 1 & 0x78) as i32; 31 | if multiplier == 0 { 32 | multiplier = 1; 33 | } 34 | let table = ETC2_ALPHA_MOD_TABLE[(data[1] & 0xf) as usize]; 35 | let mut l: usize = u64::from_le_bytes(data[0..8].try_into().unwrap()) as usize; 36 | let mut block: [u8; 4] = [0, 0, 0, 0]; 37 | for i in 0..16 { 38 | let val: i32 = base * 8 + multiplier * table[l & 7] as i32 + 1023; 39 | block[color] = if val < 0 { 40 | 0 41 | } else if val >= 2048 { 42 | 255 43 | } else { 44 | (val >> 3) as u8 45 | }; 46 | outbuf[WRITE_ORDER_TABLE_REV[i]] |= u32::from_le_bytes(block); 47 | l >>= 3; 48 | } 49 | } 50 | 51 | #[inline] 52 | pub fn decode_eacr_block(data: &[u8], outbuf: &mut [u32]) { 53 | outbuf[0..16].fill(color(0, 0, 0, 255)); 54 | decode_eac_block(data, 2, outbuf); 55 | } 56 | 57 | #[inline] 58 | pub fn decode_eacr_signed_block(data: &[u8], outbuf: &mut [u32]) { 59 | outbuf[0..16].fill(color(0, 0, 0, 255)); 60 | decode_eac_signed_block(data, 2, outbuf); 61 | } 62 | 63 | #[inline] 64 | pub fn decode_eacrg_block(data: &[u8], outbuf: &mut [u32]) { 65 | outbuf[0..16].fill(color(0, 0, 0, 255)); 66 | decode_eac_block(data, 2, outbuf); 67 | decode_eac_block(&data[8..], 1, outbuf); 68 | } 69 | 70 | #[inline] 71 | pub fn decode_eacrg_signed_block(data: &[u8], outbuf: &mut [u32]) { 72 | outbuf[0..16].fill(color(0, 0, 0, 255)); 73 | decode_eac_signed_block(data, 2, outbuf); 74 | decode_eac_signed_block(&data[8..], 1, outbuf); 75 | } 76 | -------------------------------------------------------------------------------- /src/unitycrunch.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod crn_decomp; 2 | pub(crate) mod crn_unpacker; 3 | use super::crnlib::{CrnFormat, CrnTextureInfo}; 4 | use crate::bcn; 5 | use crate::crunch::CrunchDecodeHandler; 6 | use crate::{decode_etc1, decode_etc2_rgb, decode_etc2_rgba8}; 7 | extern crate alloc; 8 | 9 | pub fn unity_crunch_unpack_level( 10 | data: &[u8], 11 | data_size: u32, 12 | level_index: u32, 13 | ) -> Result { 14 | let mut tex_info: CrnTextureInfo = CrnTextureInfo::default(); 15 | if !tex_info.crnd_get_texture_info(data, data_size) { 16 | return Err("Invalid crunch texture encoding."); 17 | } 18 | let mut p_context: crn_unpacker::CrnUnpacker<'_> = 19 | crn_decomp::crnd_unpack_begin(data, data_size)?; 20 | let width = core::cmp::max(1, tex_info.width >> level_index); 21 | let height = core::cmp::max(1, tex_info.height >> level_index); 22 | let blocks_x: u32 = core::cmp::max(1, (width + 3) >> 2); 23 | let blocks_y: u32 = core::cmp::max(1, (height + 3) >> 2); 24 | let row_pitch: u32 = blocks_x * crn_decomp::crnd_get_bytes_per_dxt_block(&mut tex_info.format)?; 25 | let total_face_size: u32 = row_pitch * blocks_y; 26 | match p_context.crnd_unpack_level(total_face_size, row_pitch, level_index) { 27 | Ok(res) => Ok(CrunchDecodeHandler { 28 | format: tex_info.format, 29 | dxt_data: res, 30 | faces: tex_info.faces, 31 | }), 32 | Err(err) => Err(err), 33 | } 34 | } 35 | 36 | pub fn decode_unity_crunch( 37 | data: &[u8], 38 | width: usize, 39 | height: usize, 40 | image: &mut [u32], 41 | ) -> Result<(), &'static str> { 42 | let handler = unity_crunch_unpack_level(data, data.len() as u32, 0)?; 43 | match handler.format { 44 | CrnFormat::Dxt1 => bcn::decode_bc1(&handler.dxt_data, width, height, image), 45 | 46 | CrnFormat::Etc1 | CrnFormat::Etc1s => decode_etc1(&handler.dxt_data, width, height, image), 47 | 48 | CrnFormat::CCrnfmtDxt5 49 | | CrnFormat::Dxt5CcxY 50 | | CrnFormat::Dxt5XGbr 51 | | CrnFormat::Dxt5Agbr 52 | | CrnFormat::Dxt5XGxR => bcn::decode_bc3(&handler.dxt_data, width, height, image), 53 | 54 | CrnFormat::Dxt5a => bcn::decode_bc4(&handler.dxt_data, width, height, image), 55 | 56 | CrnFormat::DxnXy | CrnFormat::DxnYx => { 57 | bcn::decode_bc5(&handler.dxt_data, width, height, image) 58 | } 59 | 60 | CrnFormat::Etc2 => decode_etc2_rgb(&handler.dxt_data, width, height, image), 61 | 62 | CrnFormat::Etc2a | CrnFormat::Etc2as => { 63 | decode_etc2_rgba8(&handler.dxt_data, width, height, image) 64 | } 65 | 66 | _ => Err("Invalid crunch format."), 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /bindings/python/texture2ddecoder_rs.pyi: -------------------------------------------------------------------------------- 1 | # generic bindings 2 | # atc 3 | def decode_atc_rgb4(data: bytes, width: int, height: int) -> bytes: ... 4 | def decode_atc_rgba8(data: bytes, width: int, height: int) -> bytes: ... 5 | 6 | # astc 7 | def decode_astc_4_4(data: bytes, width: int, height: int) -> bytes: ... 8 | def decode_astc_5_4(data: bytes, width: int, height: int) -> bytes: ... 9 | def decode_astc_5_5(data: bytes, width: int, height: int) -> bytes: ... 10 | def decode_astc_6_5(data: bytes, width: int, height: int) -> bytes: ... 11 | def decode_astc_6_6(data: bytes, width: int, height: int) -> bytes: ... 12 | def decode_astc_8_5(data: bytes, width: int, height: int) -> bytes: ... 13 | def decode_astc_8_6(data: bytes, width: int, height: int) -> bytes: ... 14 | def decode_astc_8_8(data: bytes, width: int, height: int) -> bytes: ... 15 | def decode_astc_10_5(data: bytes, width: int, height: int) -> bytes: ... 16 | def decode_astc_10_6(data: bytes, width: int, height: int) -> bytes: ... 17 | def decode_astc_10_8(data: bytes, width: int, height: int) -> bytes: ... 18 | def decode_astc_10_10(data: bytes, width: int, height: int) -> bytes: ... 19 | def decode_astc_12_10(data: bytes, width: int, height: int) -> bytes: ... 20 | def decode_astc_12_12(data: bytes, width: int, height: int) -> bytes: ... 21 | 22 | # bcn 23 | def decode_bc1(data: bytes, width: int, height: int) -> bytes: ... 24 | def decode_bc1a(data: bytes, width: int, height: int) -> bytes: ... 25 | def decode_bc2(data: bytes, width: int, height: int) -> bytes: ... 26 | def decode_bc3(data: bytes, width: int, height: int) -> bytes: ... 27 | def decode_bc4(data: bytes, width: int, height: int) -> bytes: ... 28 | def decode_bc5(data: bytes, width: int, height: int) -> bytes: ... 29 | def decode_bc6_signed(data: bytes, width: int, height: int) -> bytes: ... 30 | def decode_bc6_unsigned(data: bytes, width: int, height: int) -> bytes: ... 31 | def decode_bc7(data: bytes, width: int, height: int) -> bytes: ... 32 | 33 | # etc 34 | def decode_etc1(data: bytes, width: int, height: int) -> bytes: ... 35 | def decode_etc2_rgb(data: bytes, width: int, height: int) -> bytes: ... 36 | def decode_etc2_rgba1(data: bytes, width: int, height: int) -> bytes: ... 37 | def decode_etc2_rgba8(data: bytes, width: int, height: int) -> bytes: ... 38 | def decode_eacr(data: bytes, width: int, height: int) -> bytes: ... 39 | def decode_eacr_signed(data: bytes, width: int, height: int) -> bytes: ... 40 | def decode_eacrg(data: bytes, width: int, height: int) -> bytes: ... 41 | def decode_eacrg_signed(data: bytes, width: int, height: int) -> bytes: ... 42 | 43 | # pvrtc 44 | def decode_pvrtc_2bpp(data: bytes, width: int, height: int) -> bytes: ... 45 | def decode_pvrtc_4bpp(data: bytes, width: int, height: int) -> bytes: ... 46 | 47 | # crunch 48 | def decode_crunch(data: bytes, width: int, height: int) -> bytes: ... 49 | def decode_unity_crunch(data: bytes, width: int, height: int) -> bytes: ... 50 | 51 | # custom bindings 52 | def decode_astc( 53 | data: bytes, width: int, height: int, block_width: int, block_height: int 54 | ) -> bytes: ... 55 | -------------------------------------------------------------------------------- /bindings/python/README.md: -------------------------------------------------------------------------------- 1 | # texture2ddecoder_rs 2 | 3 | Python bindings for texture2ddecoder. 4 | 5 | # usage 6 | 7 | This module provides a set of functions for decoding 2D textures. Each function takes a byte array representing the texture data, along with the width and height of the texture. The functions return a byte array representing the decoded texture data. 8 | 9 | ### ATC Decoding 10 | 11 | - `decode_atc_rgb4(data: bytes, width: int, height: int) -> bytes` 12 | - `decode_atc_rgba8(data: bytes, width: int, height: int) -> bytes` 13 | 14 | ### ASTC Decoding 15 | 16 | - `decode_astc_4_4(data: bytes, width: int, height: int) -> bytes` 17 | - `decode_astc_5_4(data: bytes, width: int, height: int) -> bytes` 18 | - `decode_astc_5_5(data: bytes, width: int, height: int) -> bytes` 19 | - `decode_astc_6_5(data: bytes, width: int, height: int) -> bytes` 20 | - `decode_astc_6_6(data: bytes, width: int, height: int) -> bytes` 21 | - `decode_astc_8_5(data: bytes, width: int, height: int) -> bytes` 22 | - `decode_astc_8_6(data: bytes, width: int, height: int) -> bytes` 23 | - `decode_astc_8_8(data: bytes, width: int, height: int) -> bytes` 24 | - `decode_astc_10_5(data: bytes, width: int, height: int) -> bytes` 25 | - `decode_astc_10_6(data: bytes, width: int, height: int) -> bytes` 26 | - `decode_astc_10_8(data: bytes, width: int, height: int) -> bytes` 27 | - `decode_astc_10_10(data: bytes, width: int, height: int) -> bytes` 28 | - `decode_astc_12_10(data: bytes, width: int, height: int) -> bytes` 29 | - `decode_astc_12_12(data: bytes, width: int, height: int) -> bytes` 30 | - `decode_astc(data: bytes, width: int, height: int, block_width: int, block_height: int) -> bytes` 31 | 32 | ### BCN Decoding 33 | 34 | - `decode_bc1(data: bytes, width: int, height: int) -> bytes` 35 | - `decode_bc3(data: bytes, width: int, height: int) -> bytes` 36 | - `decode_bc4(data: bytes, width: int, height: int) -> bytes` 37 | - `decode_bc5(data: bytes, width: int, height: int) -> bytes` 38 | - `decode_bc6_signed(data: bytes, width: int, height: int) -> bytes` 39 | - `decode_bc6_unsigned(data: bytes, width: int, height: int) -> bytes` 40 | - `decode_bc7(data: bytes, width: int, height: int) -> bytes` 41 | 42 | ### ETC Decoding 43 | 44 | - `decode_etc1(data: bytes, width: int, height: int) -> bytes` 45 | - `decode_etc2_rgb(data: bytes, width: int, height: int) -> bytes` 46 | - `decode_etc2_rgba1(data: bytes, width: int, height: int) -> bytes` 47 | - `decode_etc2_rgba8(data: bytes, width: int, height: int) -> bytes` 48 | - `decode_eacr(data: bytes, width: int, height: int) -> bytes` 49 | - `decode_eacr_signed(data: bytes, width: int, height: int) -> bytes` 50 | - `decode_eacrg(data: bytes, width: int, height: int) -> bytes` 51 | - `decode_eacrg_signed(data: bytes, width: int, height: int) -> bytes` 52 | 53 | ### PVRTC Decoding 54 | 55 | - `decode_pvrtc_2bpp(data: bytes, width: int, height: int) -> bytes` 56 | - `decode_pvrtc_4bpp(data: bytes, width: int, height: int) -> bytes` 57 | 58 | ### Crunch Decoding 59 | 60 | - `decode_crunch(data: bytes, width: int, height: int) -> bytes` 61 | - `decode_unity_crunch(data: bytes, width: int, height: int) -> bytes` 62 | -------------------------------------------------------------------------------- /src/color.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::too_many_arguments)] 2 | 3 | pub static TRANSPARENT_MASK: u32 = { 4 | #[cfg(target_endian = "little")] 5 | { 6 | 0x00ffffff 7 | } 8 | #[cfg(target_endian = "big")] 9 | { 10 | 0xffffff00 11 | } 12 | }; 13 | 14 | pub static TRANSPARENT_SHIFT: u32 = { 15 | #[cfg(target_endian = "little")] 16 | { 17 | 24 18 | } 19 | #[cfg(target_endian = "big")] 20 | { 21 | 0 22 | } 23 | }; 24 | 25 | #[inline] 26 | pub const fn color(r: u8, g: u8, b: u8, a: u8) -> u32 { 27 | u32::from_le_bytes([b, g, r, a]) 28 | } 29 | 30 | // #[cfg(target_endian = "little")] 31 | // #[inline] 32 | // pub fn alpha_mask(a: u8) -> u32 { 33 | // TRANSPARENT_MASK | (a as u32) << 24 34 | // } 35 | 36 | // #[cfg(target_endian = "big")] 37 | // #[inline] 38 | // pub fn alpha_mask(a: u8) -> u32 { 39 | // TRANSPARENT_MASK | a as u32 40 | // } 41 | 42 | // #[cfg(target_endian = "little")] 43 | #[inline] 44 | pub const fn rgb565_le(d: u16) -> (u8, u8, u8) { 45 | ( 46 | (d >> 8 & 0xf8) as u8 | (d >> 13) as u8, 47 | (d >> 3 & 0xfc) as u8 | (d >> 9 & 3) as u8, 48 | (d << 3) as u8 | (d >> 2 & 7) as u8, 49 | ) 50 | } 51 | 52 | // #[cfg(target_endian = "big")] 53 | // #[inline] 54 | // pub fn rgb565_le(d: u16) -> (u8, u8, u8) { 55 | // ( 56 | // (d & 0xf8) as u8 | (d >> 5 & 7) as u8, 57 | // (d << 5 & 0xe0) as u8 | (d >> 11 & 0x1c) as u8 | (d >> 1 & 3) as u8, 58 | // (d >> 5 & 0xf8) as u8 | (d >> 10 & 0x7) as u8, 59 | // ) 60 | // } 61 | 62 | // #[cfg(target_endian = "little")] 63 | // #[inline] 64 | // pub fn rgb565_be(d: u16) -> (u8, u8, u8) { 65 | // ( 66 | // (d & 0xf8) as u8 | (d >> 5 & 7) as u8, 67 | // (d << 5 & 0xe0) as u8 | (d >> 11 & 0x1c) as u8 | (d >> 1 & 3) as u8, 68 | // (d >> 5 & 0xf8) as u8 | (d >> 10 & 0x7) as u8, 69 | // ) 70 | // } 71 | 72 | // #[cfg(target_endian = "big")] 73 | // #[inline] 74 | // pub fn rgb565_be(d: u16) -> (u8, u8, u8) { 75 | // ( 76 | // (d >> 8 & 0xf8) as u8 | (d >> 13) as u8, 77 | // (d >> 3 & 0xfc) as u8 | (d >> 9 & 3) as u8, 78 | // (d << 3) as u8 | (d >> 2 & 7) as u8, 79 | // ) 80 | // } 81 | 82 | #[inline] 83 | pub fn copy_block_buffer( 84 | bx: usize, 85 | by: usize, 86 | w: usize, 87 | h: usize, 88 | bw: usize, 89 | bh: usize, 90 | buffer: &[u32], 91 | image: &mut [u32], 92 | ) { 93 | let x: usize = bw * bx; 94 | let copy_width: usize = if bw * (bx + 1) > w { w - bw * bx } else { bw }; 95 | 96 | let y_0 = by * bh; 97 | let copy_height: usize = if bh * (by + 1) > h { h - y_0 } else { bh }; 98 | let mut buffer_offset = 0; 99 | 100 | for y in y_0..y_0 + copy_height { 101 | let image_offset = y * w + x; 102 | image[image_offset..image_offset + copy_width] 103 | .copy_from_slice(&buffer[buffer_offset..buffer_offset + copy_width]); 104 | 105 | buffer_offset += bw; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/etc/etc1.rs: -------------------------------------------------------------------------------- 1 | use crate::color::color; 2 | use crate::etc::consts::{ETC1_MODIFIER_TABLE, ETC1_SUBBLOCK_TABLE, WRITE_ORDER_TABLE}; 3 | 4 | #[inline] 5 | pub(crate) const fn clamp(n: i32) -> u8 { 6 | (if n < 0 { 7 | 0 8 | } else if n > 255 { 9 | 255 10 | } else { 11 | n 12 | }) as u8 13 | } 14 | 15 | #[inline] 16 | pub(crate) const fn applicate_color(c: [u8; 3], m: i16) -> u32 { 17 | color( 18 | clamp(c[0] as i32 + m as i32), 19 | clamp(c[1] as i32 + m as i32), 20 | clamp(c[2] as i32 + m as i32), 21 | 255, 22 | ) 23 | } 24 | 25 | #[inline] 26 | pub(crate) const fn applicate_color_alpha(c: [u8; 3], m: i16, transparent: bool) -> u32 { 27 | color( 28 | clamp(c[0] as i32 + m as i32), 29 | clamp(c[1] as i32 + m as i32), 30 | clamp(c[2] as i32 + m as i32), 31 | if transparent { 0 } else { 255 }, 32 | ) 33 | } 34 | 35 | #[inline] 36 | pub(crate) const fn applicate_color_raw(c: [u8; 3]) -> u32 { 37 | color(c[0], c[1], c[2], 255) 38 | } 39 | 40 | #[inline] 41 | pub fn decode_etc1_block(data: &[u8], outbuf: &mut [u32]) { 42 | let code: [u8; 2] = [(data[3] >> 5), (data[3] >> 2 & 7)]; // Table codewords 43 | let table: [usize; 16] = ETC1_SUBBLOCK_TABLE[(data[3] & 1) as usize]; 44 | let mut c: [[u8; 3]; 2] = [[0; 3]; 2]; 45 | if (data[3] & 2) > 0 { 46 | // diff bit == 1 47 | c[0][0] = data[0] & 0xf8; 48 | c[0][1] = data[1] & 0xf8; 49 | c[0][2] = data[2] & 0xf8; 50 | c[1][0] = c[0][0] 51 | .overflowing_add(data[0] << 3 & 0x18) 52 | .0 53 | .overflowing_sub(data[0] << 3 & 0x20) 54 | .0; 55 | c[1][1] = c[0][1] 56 | .overflowing_add(data[1] << 3 & 0x18) 57 | .0 58 | .overflowing_sub(data[1] << 3 & 0x20) 59 | .0; 60 | c[1][2] = c[0][2] 61 | .overflowing_add(data[2] << 3 & 0x18) 62 | .0 63 | .overflowing_sub(data[2] << 3 & 0x20) 64 | .0; 65 | c[0][0] |= c[0][0] >> 5; 66 | c[0][1] |= c[0][1] >> 5; 67 | c[0][2] |= c[0][2] >> 5; 68 | c[1][0] |= c[1][0] >> 5; 69 | c[1][1] |= c[1][1] >> 5; 70 | c[1][2] |= c[1][2] >> 5; 71 | } else { 72 | // diff bit == 0 73 | c[0][0] = (data[0] & 0xf0) | data[0] >> 4; 74 | c[1][0] = (data[0] & 0x0f) | data[0] << 4; 75 | c[0][1] = (data[1] & 0xf0) | data[1] >> 4; 76 | c[1][1] = (data[1] & 0x0f) | data[1] << 4; 77 | c[0][2] = (data[2] & 0xf0) | data[2] >> 4; 78 | c[1][2] = (data[2] & 0x0f) | data[2] << 4; 79 | } 80 | 81 | let mut j: usize = u16::from_be_bytes([data[6], data[7]]) as usize; // less significant pixel index bits 82 | let mut k: usize = u16::from_be_bytes([data[4], data[5]]) as usize; // more significant pixel index bits 83 | 84 | for i in 0..16 { 85 | let s: usize = table[i]; 86 | let m: i16 = ETC1_MODIFIER_TABLE[code[s] as usize][j & 1]; 87 | outbuf[WRITE_ORDER_TABLE[i]] = applicate_color(c[s], if k & 1 > 0 { -m } else { m }); 88 | j >>= 1; 89 | k >>= 1 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | // macro to generate generic block decoder functions 2 | macro_rules! block_decoder{ 3 | ($name: expr, $block_width: expr, $block_height: expr, $raw_block_size: expr, $block_decode_func: expr) => { 4 | paste::item! { 5 | #[doc = "Decodes a " $name " encoded texture into an image"] 6 | pub fn [](data: &[u8], width: usize, height: usize, image: &mut [u32]) -> Result<(), &'static str> { 7 | const BLOCK_WIDTH: usize = $block_width; 8 | const BLOCK_HEIGHT: usize = $block_height; 9 | const BLOCK_SIZE: usize = BLOCK_WIDTH * BLOCK_HEIGHT; 10 | let num_blocks_x: usize = width.div_ceil(BLOCK_WIDTH); 11 | let num_blocks_y: usize = height.div_ceil(BLOCK_HEIGHT); 12 | let mut buffer: [u32; BLOCK_SIZE] = [crate::color::color(0,0,0,255); BLOCK_SIZE]; 13 | 14 | if data.len() < num_blocks_x * num_blocks_y * $raw_block_size { 15 | return Err("Not enough data to decode image!"); 16 | } 17 | 18 | if image.len() < width * height { 19 | return Err("Image buffer is too small!"); 20 | } 21 | 22 | let mut data_offset = 0; 23 | (0..num_blocks_y).for_each(|by| { 24 | (0..num_blocks_x).for_each(|bx| { 25 | $block_decode_func(&data[data_offset..], &mut buffer); 26 | crate::color::copy_block_buffer( 27 | bx, 28 | by, 29 | width, 30 | height, 31 | BLOCK_WIDTH, 32 | BLOCK_HEIGHT, 33 | &buffer, 34 | image, 35 | ); 36 | data_offset += $raw_block_size; 37 | }); 38 | }); 39 | Ok(()) 40 | } 41 | 42 | } 43 | }; 44 | } 45 | 46 | macro_rules! CRND_HUFF_DECODE { 47 | ($codec: expr, $model: expr, $symbol: expr) => { 48 | $symbol = match $codec.decode($model) { 49 | Ok(s) => s, 50 | Err(_) => return false, 51 | } 52 | }; 53 | } 54 | 55 | macro_rules! WRITE_TO_INT_BUFFER { 56 | ($buf: expr, $index: expr, $val: expr) => { 57 | let t_index = ($index * 4) as usize; 58 | #[cfg(target_endian = "little")] 59 | let tiles = $val.to_le_bytes(); 60 | #[cfg(target_endian = "big")] 61 | let tiles = $val.to_be_bytes(); 62 | $buf[t_index] = tiles[0]; 63 | $buf[t_index + 1] = tiles[1]; 64 | $buf[t_index + 2] = tiles[2]; 65 | $buf[t_index + 3] = tiles[3] 66 | }; 67 | } 68 | 69 | macro_rules! WRITE_OR_U8_INTO_U16_BUFFER { 70 | ($buf: expr, $index: expr, $val: expr) => { 71 | let t_index = ($index >> 1) as usize; 72 | let t_val = $buf[t_index].to_be_bytes(); 73 | if $index & 1 != 1 { 74 | $buf[t_index] = ((t_val[0] as u16) << 8) | ((t_val[1] as u16) | $val as u16); 75 | } else { 76 | $buf[t_index] = (((t_val[0] as u16) | ($val as u16)) << 8) | (t_val[1] as u16); 77 | } 78 | }; 79 | } 80 | 81 | pub(crate) use { 82 | block_decoder, CRND_HUFF_DECODE, WRITE_OR_U8_INTO_U16_BUFFER, WRITE_TO_INT_BUFFER, 83 | }; 84 | -------------------------------------------------------------------------------- /src/atc.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::identity_op)] 2 | use crate::bcn::bc3::decode_bc3_alpha; 3 | use crate::color::color; 4 | use crate::macros::block_decoder; 5 | use core::cmp::max; 6 | use core::result::Result; 7 | 8 | #[inline] 9 | const fn expand_quantized(v: u8, bits: u8) -> u8 { 10 | let v = v << (8 - bits); 11 | v | (v >> bits) 12 | } 13 | 14 | #[inline] 15 | pub fn decode_atc_rgb4_block(data: &[u8], outbuf: &mut [u32]) { 16 | let mut colors: [u8; 16] = [0; 16]; 17 | let c0: u32 = u16::from_le_bytes([data[0], data[1]]) as u32; 18 | let c1: u32 = u16::from_le_bytes([data[2], data[3]]) as u32; 19 | 20 | if 0 == (c0 & 0x8000) { 21 | colors[0] = expand_quantized(((c0 >> 0) & 0x1f) as u8, 5); 22 | colors[1] = expand_quantized(((c0 >> 5) & 0x1f) as u8, 5); 23 | colors[2] = expand_quantized(((c0 >> 10) & 0x1f) as u8, 5); 24 | 25 | colors[12] = expand_quantized(((c1 >> 0) & 0x1f) as u8, 5); 26 | colors[13] = expand_quantized(((c1 >> 5) & 0x3f) as u8, 6); 27 | colors[14] = expand_quantized(((c1 >> 11) & 0x1f) as u8, 5); 28 | 29 | #[inline] 30 | const fn interop_colors(c0: u8, c1: u8) -> u8 { 31 | ((5 * c0 as u16 + 3 * c1 as u16) / 8) as u8 32 | } 33 | // colors[4] = (5 * colors[0] + 3 * colors[12]) / 8; 34 | // colors[5] = (5 * colors[1] + 3 * colors[13]) / 8; 35 | // colors[6] = (5 * colors[2] + 3 * colors[14]) / 8; 36 | 37 | // colors[8] = (3 * colors[0] + 5 * colors[12]) / 8; 38 | // colors[9] = (3 * colors[1] + 5 * colors[13]) / 8; 39 | // colors[10] = (3 * colors[2] + 5 * colors[14]) / 8; 40 | 41 | colors[4] = interop_colors(colors[0], colors[12]); 42 | colors[5] = interop_colors(colors[1], colors[13]); 43 | colors[6] = interop_colors(colors[2], colors[14]); 44 | 45 | colors[8] = interop_colors(colors[12], colors[0]); 46 | colors[9] = interop_colors(colors[13], colors[1]); 47 | colors[10] = interop_colors(colors[14], colors[2]); 48 | } else { 49 | colors[0] = 0; 50 | colors[1] = 0; 51 | colors[2] = 0; 52 | 53 | colors[8] = expand_quantized(((c0 >> 0) & 0x1f) as u8, 5); 54 | colors[9] = expand_quantized(((c0 >> 5) & 0x1f) as u8, 5); 55 | colors[10] = expand_quantized(((c0 >> 10) & 0x1f) as u8, 5); 56 | 57 | colors[12] = expand_quantized(((c1 >> 0) & 0x1f) as u8, 5); 58 | colors[13] = expand_quantized(((c1 >> 5) & 0x3f) as u8, 6); 59 | colors[14] = expand_quantized(((c1 >> 11) & 0x1f) as u8, 5); 60 | 61 | colors[4] = max( 62 | 0, 63 | ((colors[8] as u16).overflowing_sub(colors[12] as u16).0 / 4) as u8, 64 | ); 65 | colors[5] = max( 66 | 0, 67 | ((colors[9] as u16).overflowing_sub(colors[13] as u16).0 / 4) as u8, 68 | ); 69 | colors[6] = max( 70 | 0, 71 | ((colors[10] as u16).overflowing_sub(colors[14] as u16).0 / 4) as u8, 72 | ); 73 | } 74 | 75 | let mut next = 8 * 4; 76 | (0..16).for_each(|i| { 77 | let idx = (((data[next >> 3] >> (next & 7)) & 3) * 4) as usize; 78 | outbuf[i] = color(colors[idx + 2], colors[idx + 1], colors[idx + 0], 255); 79 | next += 2; 80 | }); 81 | } 82 | 83 | #[inline] 84 | pub fn decode_atc_rgba8_block(data: &[u8], outbuf: &mut [u32]) { 85 | decode_atc_rgb4_block(&data[8..], outbuf); 86 | decode_bc3_alpha(data, outbuf, 3); 87 | } 88 | 89 | block_decoder!("atc_rgb4", 4, 4, 8, decode_atc_rgb4_block); 90 | block_decoder!("atc_rgba8", 4, 4, 16, decode_atc_rgba8_block); 91 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A pure Rust no-std texture decoder for the following formats: 2 | //! - [ATC - Adreno Texture Compression](https://registry.khronos.org/OpenGL/extensions/AMD/AMD_compressed_ATC_texture.txt) 3 | //! - [ASTC - Adaptive Scalable Texture Compression](https://en.wikipedia.org/wiki/Adaptive_Scalable_Texture_Compression) 4 | //! - [BCn - Block Compression](https://en.wikipedia.org/wiki/S3_Texture_Compression) 5 | //! - [ETC - Ericsson Texture Compression](https://en.wikipedia.org/wiki/Ericsson_Texture_Compression) 6 | //! - [PVRTC - PowerVR Texture Compression](https://en.wikipedia.org/wiki/PVRTC) 7 | //! - [Crunch](https://github.com/BinomialLLC/crunch) & [Unity's Crunch](https://github.com/Unity-Technologies/crunch) 8 | //! 9 | //! ## Functions 10 | //! Provides a decode function for each format, as well as a block decode function all formats besides PVRTC. 11 | //! Besides some exceptions, the signature of the decode functions is as follows: 12 | //! ```ignore 13 | //! fn decode_format(data: &[u8], width: usize, height: usize, image: &mut [u32]) -> Result<(), &'static str> 14 | //! // data: the compressed data, expected to be width * height / block_size in size 15 | //! // width: the width of the image 16 | //! // height: the height of the image 17 | //! // image: the buffer to write the decoded image to, expected to be width * height in size 18 | //! fn decode_format_block(data: &[u8], image: &mut [u32]) -> Result<(), &'static str> 19 | //! // data: the compressed data (block), expected to be block_size in size 20 | //! // image: the buffer to write the decoded image to, expected to be block_size in size 21 | //! ``` 22 | //! The exceptions are: 23 | //! - ASTC: the (block) decode function takes the block size as an additional parameter 24 | //! - BC6: there are two additional decode functions for the signed and unsigned variants 25 | //! - PVRTC: the decode function takes the block size as an additional parameter, and there are two additional decode functions for the 2bpp and 4bpp variants 26 | //! - Crunch & Unity's Crunch: The texture's dimensions and metadata are stored in the file itself, one's must parse the header with crnd_get_texture_info() from crn_texture_info struct first, then pass the metadata to the decoder as in the format. There's no block decomp. function. 27 | //! 28 | //! To make these excetions easier to use, there are helper functions to enable decode functions with identical arguments and returns. 29 | //! 30 | //! Here is a list of the formats and their corresponding functions: 31 | //! - ATC 32 | //! - [`decode_atc_rgb4()`] 33 | //! - [`decode_atc_rgb4_block()`] 34 | //! - [`decode_atc_rgba8()`] 35 | //! - [`decode_atc_rgba8_block()`] 36 | //! - ASTC 37 | //! - [`decode_astc()`] 38 | //! - [`decode_astc_block()`] 39 | //! - various decode_astc_(block_)_x_y functions, where x and y are the block size 40 | //! - BCn 41 | //! - [`decode_bc1()`] 42 | //! - [`decode_bc1_block()`] 43 | //! - [`decode_bc3()`] 44 | //! - [`decode_bc3_block()`] 45 | //! - [`decode_bc4()`] 46 | //! - [`decode_bc4_block()`] 47 | //! - [`decode_bc5()`] 48 | //! - [`decode_bc5_block()`] 49 | //! - [`decode_bc6()`] 50 | //! - [`decode_bc6_block()`] 51 | //! - [`decode_bc6_signed()`] 52 | //! - [`decode_bc6_block_signed()`] 53 | //! - [`decode_bc6_unsigned()`] 54 | //! - [`decode_bc6_block_unsigned()`] 55 | //! - [`decode_bc7()`] 56 | //! - [`decode_bc7_block()`] 57 | //! - ETC 58 | //! - [`decode_etc1()`] 59 | //! - [`decode_etc1_block()`] 60 | //! - [`decode_etc2_rgb()`] 61 | //! - [`decode_etc2_rgb_block()`] 62 | //! - [`decode_etc2_rgba1()`] 63 | //! - [`decode_etc2_rgba1_block()`] 64 | //! - [`decode_etc2_rgba8()`] 65 | //! - [`decode_etc2_rgba8_block()`] 66 | //! - [`decode_eacr()`] 67 | //! - [`decode_eacr_block()`] 68 | //! - [`decode_eacr_signed()`] 69 | //! - [`decode_eacr_signed_block()`] 70 | //! - [`decode_eacrg()`] 71 | //! - [`decode_eacrg_block()`] 72 | //! - PVRTC 73 | //! - [`decode_pvrtc()`] 74 | //! - [`decode_pvrtc_2bpp()`] 75 | //! - [`decode_pvrtc_4bpp()`] 76 | //! - Crunch 77 | //! - [`decode_crunch()`] 78 | //! - Unity Crunch 79 | //! - [`decode_unity_crunch()`] 80 | //! 81 | #![no_std] 82 | 83 | mod bitreader; 84 | mod color; 85 | mod f16; 86 | mod macros; 87 | 88 | mod astc; 89 | mod atc; 90 | mod bcn; 91 | mod crnlib; 92 | #[cfg(feature = "alloc")] 93 | mod crunch; 94 | mod etc; 95 | mod pvrtc; 96 | #[cfg(feature = "alloc")] 97 | mod unitycrunch; 98 | 99 | // import decode functions 100 | pub use astc::*; 101 | pub use atc::*; 102 | pub use bcn::*; 103 | pub use crnlib::CrnTextureInfo; 104 | pub use crunch::decode_crunch; 105 | pub use etc::*; 106 | pub use pvrtc::*; 107 | pub use unitycrunch::decode_unity_crunch; 108 | -------------------------------------------------------------------------------- /src/crnlib.rs: -------------------------------------------------------------------------------- 1 | use crate::crunch::crn_decomp::CrnHeader; 2 | 3 | // Supported compressed pixel formats. 4 | // Basically all the standard DX9 formats, with some swizzled DXT5 formats 5 | // (most of them supported by ATI's Compressonator), along with some ATI/X360 GPU specific formats. 6 | #[derive(PartialEq, PartialOrd)] 7 | #[repr(u32)] 8 | pub enum CrnFormat { 9 | Invalid = 4294967295, // u32 -1, 10 | 11 | Dxt1 = 0, 12 | 13 | // cCRNFmtFirstValid = crn_format::cCRNFmtDXT1 as isize, // Rust doesn't allow same value enums, and as far as I see this is not used in the lib. 14 | 15 | // cCRNFmtDXT3 is not currently supported when writing to CRN - only DDS. 16 | Dxt3, 17 | 18 | CCrnfmtDxt5, 19 | 20 | // Various DXT5 derivatives 21 | Dxt5CcxY, // Luma-chroma 22 | Dxt5XGxR, // Swizzled 2-component 23 | Dxt5XGbr, // Swizzled 3-component 24 | Dxt5Agbr, // Swizzled 4-component 25 | 26 | // ATI 3DC and X360 DXN 27 | DxnXy, 28 | DxnYx, 29 | 30 | // DXT5 alpha blocks only 31 | Dxt5a, 32 | 33 | Etc1, 34 | Etc2, 35 | Etc2a, 36 | Etc1s, 37 | Etc2as, 38 | 39 | Total, 40 | } 41 | 42 | #[repr(C)] 43 | pub struct CrnTextureInfo { 44 | pub struct_size: u32, 45 | pub width: u32, 46 | pub height: u32, 47 | pub levels: u32, 48 | pub faces: u32, 49 | pub bytes_per_block: u32, 50 | pub userdata0: u32, 51 | pub userdata1: u32, 52 | pub format: CrnFormat, 53 | } 54 | 55 | impl CrnTextureInfo { 56 | pub const fn default() -> Self { 57 | Self { 58 | struct_size: core::mem::size_of::() as u32, 59 | width: 0, 60 | height: 0, 61 | levels: 0, 62 | faces: 0, 63 | bytes_per_block: 0, 64 | userdata0: 0, 65 | userdata1: 0, 66 | format: CrnFormat::Invalid, // Init as invalid? 67 | } 68 | } 69 | 70 | pub fn crnd_get_texture_info(&mut self, p_data: &[u8], data_size: u32) -> bool { 71 | if data_size < CrnHeader::MIN_SIZE { 72 | return false; 73 | } 74 | 75 | if self.struct_size != core::mem::size_of::() as u32 { 76 | return false; 77 | } 78 | 79 | let mut p_header: CrnHeader = CrnHeader::default(); 80 | let res: bool = p_header.crnd_get_header(p_data, data_size); 81 | if !res { 82 | return res; 83 | } 84 | 85 | self.width = p_header.width.cast_to_uint(); 86 | self.height = p_header.height.cast_to_uint(); 87 | self.levels = p_header.levels.cast_to_uint(); 88 | self.faces = p_header.faces.cast_to_uint(); 89 | self.format = match p_header.format.cast_to_uint() { 90 | // -1 => crn_format::cCRNFmtInvalid, 91 | 0 => CrnFormat::Dxt1, 92 | 93 | // 0 => crn_formatcCRNFmtFirstValid, 94 | 95 | // cCRNFmtDXT3 is not currently supported when writing to CRN - only DDS. 96 | 1 => CrnFormat::Dxt3, 97 | 98 | 2 => CrnFormat::CCrnfmtDxt5, 99 | 100 | // Various DXT5 derivatives 101 | 3 => CrnFormat::Dxt5CcxY, // Luma-chroma 102 | 4 => CrnFormat::Dxt5XGxR, // Swizzled 2-component 103 | 5 => CrnFormat::Dxt5XGbr, // Swizzled 3-component 104 | 6 => CrnFormat::Dxt5Agbr, // Swizzled 4-component 105 | 106 | // ATI 3DC and X360 DXN 107 | 7 => CrnFormat::DxnXy, 108 | 8 => CrnFormat::DxnYx, 109 | 110 | // DXT5 alpha blocks only 111 | 9 => CrnFormat::Dxt5a, 112 | 113 | 10 => CrnFormat::Etc1, 114 | 11 => CrnFormat::Etc2, 115 | 12 => CrnFormat::Etc2a, 116 | 13 => CrnFormat::Etc1s, 117 | 14 => CrnFormat::Etc2as, 118 | 119 | 15 => CrnFormat::Total, 120 | 121 | _ => CrnFormat::Invalid, 122 | }; 123 | if self.format == CrnFormat::Invalid { 124 | return false; 125 | } 126 | if (p_header.format.cast_to_uint() == CrnFormat::Dxt1 as u32) 127 | || (p_header.format.cast_to_uint() == CrnFormat::Dxt5a as u32) 128 | || (p_header.format.cast_to_uint() == CrnFormat::Etc1 as u32) 129 | || (p_header.format.cast_to_uint() == CrnFormat::Etc2 as u32) 130 | || (p_header.format.cast_to_uint() == CrnFormat::Etc1s as u32) 131 | { 132 | self.bytes_per_block = 8; 133 | } else { 134 | self.bytes_per_block = 16; 135 | } 136 | self.userdata0 = p_header.userdata0.cast_to_uint(); 137 | self.userdata1 = p_header.userdata1.cast_to_uint(); 138 | true 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/crunch/crn_consts.rs: -------------------------------------------------------------------------------- 1 | // Moved Consts from structs. 2 | pub const CRNHEADER_MIN_SIZE: usize = 62; 3 | 4 | pub const CRNSIG_VALUE: u16 = ('H' as u16) << 8 | 'x' as u16; 5 | 6 | pub const MAX_EXPECTED_CODE_SIZE: usize = 16; 7 | 8 | pub const MAX_SUPPORTED_SYMS: usize = 8192; 9 | 10 | pub const MAX_TABLE_BITS: usize = 11; 11 | 12 | pub const MAGIC_VALUE: u32 = 0x1EF9CABD; 13 | 14 | pub const BIT_BUF_SIZE: usize = 32; 15 | 16 | pub const CRNMAX_LEVELS: u32 = 16; 17 | 18 | // The crnd library assumes all allocation blocks have at least CRND_MIN_ALLOC_ALIGNMENT alignment. 19 | // pub const CRND_MIN_ALLOC_ALIGNMENT: usize = 8; 20 | 21 | // Code length encoding symbols: 22 | // 0-16 - actual code lengths 23 | pub const MAX_CODELENGTH_CODES: usize = 21; 24 | 25 | pub const SMALL_ZERO_RUN_CODE: usize = 17; 26 | pub const LARGE_ZERO_RUN_CODE: usize = 18; 27 | pub const SMALL_REPEAT_CODE: usize = 19; 28 | pub const LARGE_REPEAT_CODE: usize = 20; 29 | 30 | pub const MIN_SMALL_ZERO_RUN_SIZE: usize = 3; 31 | // pub const cMaxSmallZeroRunSize: usize = 10; 32 | pub const MIN_LARGE_ZERO_RUN_SIZE: usize = 11; 33 | // pub const cMaxLargeZeroRunSize: usize = 138; 34 | 35 | pub const SMALL_MIN_NON_ZERO_RUN_SIZE: usize = 3; 36 | // pub const cSmallMaxNonZeroRunSize: usize = 6; 37 | pub const LARGE_MIN_NON_ZERO_RUN_SIZE: usize = 7; 38 | // pub const cLargeMaxNonZeroRunSize: usize = 70; 39 | 40 | pub const SMALL_ZERO_RUN_EXTRA_BITS: usize = 3; 41 | pub const LARGE_ZERO_RUN_EXTRA_BITS: usize = 7; 42 | pub const SMALL_NON_ZERO_RUN_EXTRA_BITS: usize = 2; 43 | pub const LARGE_NON_ZERO_RUN_EXTRA_BITS: usize = 6; 44 | 45 | pub const MOST_PROBABLE_CODELENGTH_CODES: [u8; 21] = [ 46 | SMALL_ZERO_RUN_CODE as u8, 47 | LARGE_ZERO_RUN_CODE as u8, 48 | SMALL_REPEAT_CODE as u8, 49 | LARGE_REPEAT_CODE as u8, 50 | 0, 51 | 8, 52 | 7, 53 | 9, 54 | 6, 55 | 10, 56 | 5, 57 | 11, 58 | 4, 59 | 12, 60 | 3, 61 | 13, 62 | 2, 63 | 14, 64 | 1, 65 | 15, 66 | 16, 67 | ]; 68 | 69 | // pub const cNumMostProbableCodelengthCodes: usize = 21; 70 | 71 | #[cfg(target_endian = "little")] 72 | pub const CRND_LITTLE_ENDIAN_PLATFORM: bool = true; 73 | // #[cfg(target_endian = "little")] 74 | // pub const c_crnd_big_endian_platform: bool = false; 75 | 76 | #[cfg(target_endian = "big")] 77 | pub const CRND_LITTLE_ENDIAN_PLATFORM: bool = false; 78 | // #[cfg(target_endian = "big")] 79 | // pub const c_crnd_big_endian_platform: bool = true; 80 | 81 | // pub const cDXTBlockShift: usize = 2; 82 | // pub const cDXTBlockSize: usize = 1 << cDXTBlockShift; 83 | // pub const cDXT1BytesPerBlock: usize = 8; 84 | // pub const cDXT5NBytesPerBlock: usize = 16; 85 | pub const DXT1_SELECTOR_BITS: usize = 2; 86 | pub const DXT1_SELECTOR_VALUES: usize = 1 << DXT1_SELECTOR_BITS; 87 | // pub const cDXT1SelectorMask: usize = cDXT1SelectorValues - 1; 88 | pub const DXT5_SELECTOR_BITS: usize = 3; 89 | pub const DXT5_SELECTOR_VALUES: usize = 1 << DXT5_SELECTOR_BITS; 90 | // pub const cDXT5SelectorMask: usize = cDXT5SelectorValues - 1; 91 | 92 | // pub const g_dxt1_to_linear: [u8; cDXT1SelectorValues as usize] = [0, 3, 1, 2]; 93 | pub const DXT1_FROM_LINEAR: [u8; DXT1_SELECTOR_VALUES] = [0, 2, 3, 1]; 94 | // pub const g_dxt5_to_linear: [u8; cDXT5SelectorValues as usize] = [0, 7, 1, 2, 3, 4, 5, 6]; 95 | pub const DXT5_FROM_LINEAR: [u8; DXT5_SELECTOR_VALUES] = [0, 2, 3, 4, 5, 6, 7, 1]; 96 | // pub const g_six_alpha_invert_table: [u8; cDXT5SelectorValues as usize] = [1, 0, 5, 4, 3, 2, 6, 7]; 97 | // pub const g_eight_alpha_invert_table: [u8; cDXT5SelectorValues as usize] = [1, 0, 7, 6, 5, 4, 3, 2]; 98 | 99 | pub const NUM_CHUNK_ENCODINGS: usize = 8; 100 | 101 | #[allow(non_camel_case_types)] 102 | pub struct crnd_encoding_tile_indices { 103 | pub tiles: [u8; 4], 104 | } 105 | 106 | pub const CRND_CHUNK_ENCODING_TILES: [crnd_encoding_tile_indices; NUM_CHUNK_ENCODINGS] = [ 107 | { 108 | crnd_encoding_tile_indices { 109 | tiles: [0, 0, 0, 0], 110 | } 111 | }, 112 | { 113 | crnd_encoding_tile_indices { 114 | tiles: [0, 0, 1, 1], 115 | } 116 | }, 117 | { 118 | crnd_encoding_tile_indices { 119 | tiles: [0, 1, 0, 1], 120 | } 121 | }, 122 | { 123 | crnd_encoding_tile_indices { 124 | tiles: [0, 0, 1, 2], 125 | } 126 | }, 127 | { 128 | crnd_encoding_tile_indices { 129 | tiles: [1, 2, 0, 0], 130 | } 131 | }, 132 | { 133 | crnd_encoding_tile_indices { 134 | tiles: [0, 1, 0, 2], 135 | } 136 | }, 137 | { 138 | crnd_encoding_tile_indices { 139 | tiles: [1, 0, 2, 0], 140 | } 141 | }, 142 | { 143 | crnd_encoding_tile_indices { 144 | tiles: [0, 1, 2, 3], 145 | } 146 | }, 147 | ]; 148 | 149 | pub const CRND_CHUNK_ENCODING_NUM_TILES: [u8; NUM_CHUNK_ENCODINGS] = [1, 2, 2, 3, 3, 3, 3, 4]; 150 | -------------------------------------------------------------------------------- /bindings/python/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::types::PyBytes; 3 | use pyo3::wrap_pyfunction; 4 | 5 | #[pymodule] 6 | fn texture2ddecoder_rs(_py: Python<'_>, m: &PyModule) -> PyResult<()> { 7 | // generic bindings 8 | // atc 9 | m.add_function(wrap_pyfunction!(decode_atc_rgb4, m)?)?; 10 | m.add_function(wrap_pyfunction!(decode_atc_rgba8, m)?)?; 11 | // astc 12 | m.add_function(wrap_pyfunction!(decode_astc_4_4, m)?)?; 13 | m.add_function(wrap_pyfunction!(decode_astc_5_4, m)?)?; 14 | m.add_function(wrap_pyfunction!(decode_astc_5_5, m)?)?; 15 | m.add_function(wrap_pyfunction!(decode_astc_6_5, m)?)?; 16 | m.add_function(wrap_pyfunction!(decode_astc_6_6, m)?)?; 17 | m.add_function(wrap_pyfunction!(decode_astc_8_5, m)?)?; 18 | m.add_function(wrap_pyfunction!(decode_astc_8_6, m)?)?; 19 | m.add_function(wrap_pyfunction!(decode_astc_8_8, m)?)?; 20 | m.add_function(wrap_pyfunction!(decode_astc_10_5, m)?)?; 21 | m.add_function(wrap_pyfunction!(decode_astc_10_6, m)?)?; 22 | m.add_function(wrap_pyfunction!(decode_astc_10_8, m)?)?; 23 | m.add_function(wrap_pyfunction!(decode_astc_10_10, m)?)?; 24 | m.add_function(wrap_pyfunction!(decode_astc_12_10, m)?)?; 25 | m.add_function(wrap_pyfunction!(decode_astc_12_12, m)?)?; 26 | // bcn 27 | m.add_function(wrap_pyfunction!(decode_bc1, m)?)?; 28 | m.add_function(wrap_pyfunction!(decode_bc1a, m)?)?; 29 | m.add_function(wrap_pyfunction!(decode_bc2, m)?)?; 30 | m.add_function(wrap_pyfunction!(decode_bc3, m)?)?; 31 | m.add_function(wrap_pyfunction!(decode_bc4, m)?)?; 32 | m.add_function(wrap_pyfunction!(decode_bc5, m)?)?; 33 | m.add_function(wrap_pyfunction!(decode_bc6_signed, m)?)?; 34 | m.add_function(wrap_pyfunction!(decode_bc6_unsigned, m)?)?; 35 | m.add_function(wrap_pyfunction!(decode_bc7, m)?)?; 36 | // etc 37 | m.add_function(wrap_pyfunction!(decode_etc1, m)?)?; 38 | m.add_function(wrap_pyfunction!(decode_etc2_rgb, m)?)?; 39 | m.add_function(wrap_pyfunction!(decode_etc2_rgba1, m)?)?; 40 | m.add_function(wrap_pyfunction!(decode_etc2_rgba8, m)?)?; 41 | m.add_function(wrap_pyfunction!(decode_eacr, m)?)?; 42 | m.add_function(wrap_pyfunction!(decode_eacr_signed, m)?)?; 43 | m.add_function(wrap_pyfunction!(decode_eacrg, m)?)?; 44 | m.add_function(wrap_pyfunction!(decode_eacrg_signed, m)?)?; 45 | // pvrtc 46 | m.add_function(wrap_pyfunction!(decode_pvrtc_2bpp, m)?)?; 47 | m.add_function(wrap_pyfunction!(decode_pvrtc_4bpp, m)?)?; 48 | // crunch 49 | m.add_function(wrap_pyfunction!(decode_crunch, m)?)?; 50 | m.add_function(wrap_pyfunction!(decode_unity_crunch, m)?)?; 51 | 52 | // custom bindings 53 | m.add_function(wrap_pyfunction!(decode_astc, m)?)?; 54 | 55 | // --- 56 | Ok(()) 57 | } 58 | 59 | macro_rules! pybind { 60 | ($name:expr) => { 61 | paste::item! { 62 | #[pyfunction] 63 | pub fn $name<'a>(py: Python<'a>, data: &'a PyBytes, width: usize, height: usize) -> PyResult<&'a PyBytes> { 64 | PyBytes::new_with(py, width * height * 4, |image: & mut[u8]|{ 65 | let image_u32 = unsafe { std::mem::transmute(image) }; 66 | match texture2ddecoder::$name(data.as_bytes(), width, height, image_u32){ 67 | Ok(_) => {} 68 | Err(e) => return Err(PyErr::new::(e.to_string())) 69 | } 70 | Ok(()) 71 | }) 72 | } 73 | } 74 | }; 75 | } 76 | 77 | // generic bindings 78 | // atc 79 | pybind!(decode_atc_rgb4); 80 | pybind!(decode_atc_rgba8); 81 | // astc 82 | pybind!(decode_astc_4_4); 83 | pybind!(decode_astc_5_4); 84 | pybind!(decode_astc_5_5); 85 | pybind!(decode_astc_6_5); 86 | pybind!(decode_astc_6_6); 87 | pybind!(decode_astc_8_5); 88 | pybind!(decode_astc_8_6); 89 | pybind!(decode_astc_8_8); 90 | pybind!(decode_astc_10_5); 91 | pybind!(decode_astc_10_6); 92 | pybind!(decode_astc_10_8); 93 | pybind!(decode_astc_10_10); 94 | pybind!(decode_astc_12_10); 95 | pybind!(decode_astc_12_12); 96 | // bcn 97 | pybind!(decode_bc1); 98 | pybind!(decode_bc1a); 99 | pybind!(decode_bc2); 100 | pybind!(decode_bc3); 101 | pybind!(decode_bc4); 102 | pybind!(decode_bc5); 103 | pybind!(decode_bc6_signed); 104 | pybind!(decode_bc6_unsigned); 105 | pybind!(decode_bc7); 106 | // etc 107 | pybind!(decode_etc1); 108 | pybind!(decode_etc2_rgb); 109 | pybind!(decode_etc2_rgba1); 110 | pybind!(decode_etc2_rgba8); 111 | pybind!(decode_eacr); 112 | pybind!(decode_eacr_signed); 113 | pybind!(decode_eacrg); 114 | pybind!(decode_eacrg_signed); 115 | // pvrtc 116 | pybind!(decode_pvrtc_2bpp); 117 | pybind!(decode_pvrtc_4bpp); 118 | // crunch 119 | pybind!(decode_crunch); 120 | pybind!(decode_unity_crunch); 121 | 122 | // custom bindings 123 | #[pyfunction] 124 | pub fn decode_astc<'a>( 125 | py: Python<'a>, 126 | data: &'a PyBytes, 127 | width: usize, 128 | height: usize, 129 | block_width: usize, 130 | block_height: usize, 131 | ) -> PyResult<&'a PyBytes> { 132 | PyBytes::new_with(py, width * height * 4, |image: &mut [u8]| { 133 | let image_u32 = unsafe { std::mem::transmute(image) }; 134 | texture2ddecoder::decode_astc( 135 | data.as_bytes(), 136 | width, 137 | height, 138 | block_width, 139 | block_height, 140 | image_u32, 141 | ) 142 | .unwrap_err(); 143 | Ok(()) 144 | }) 145 | } 146 | -------------------------------------------------------------------------------- /.github/workflows/publish_py.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python Bindings 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | tags: 8 | - "*" 9 | pull_request: 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: read 14 | 15 | defaults: 16 | run: 17 | working-directory: ./bindings/python 18 | 19 | jobs: 20 | lint_and_test: 21 | runs-on: ubuntu-latest 22 | strategy: 23 | matrix: 24 | python-version: ["3.8", "pypy3.8"] 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | submodules: recursive 29 | 30 | - uses: actions/setup-python@v5 31 | with: 32 | python-version: ${{ matrix.python-version }} 33 | 34 | - name: Build wheels 35 | uses: PyO3/maturin-action@v1 36 | with: 37 | target: ${{ matrix.target }} 38 | args: --release --out dist -i ${{ matrix.python-version }} 39 | sccache: "true" 40 | working-directory: ./bindings/python 41 | 42 | - name: Install Just 43 | uses: extractions/setup-just@v2 44 | - name: Run Cargo Tests 45 | run: | 46 | cargo test 47 | 48 | - name: Run pytest 49 | run: | 50 | # just venv pytest 51 | rm -rf .venv 52 | python3 -m venv .venv 53 | . .venv/bin/activate 54 | .venv/bin/pip install wheel pytest mkdocs-material pillow 55 | maturin develop 56 | .venv/bin/pytest 57 | 58 | linux: 59 | if: "startsWith(github.ref, 'refs/tags/')" 60 | runs-on: ubuntu-latest 61 | needs: lint_and_test 62 | strategy: 63 | matrix: 64 | platform: 65 | - target: x64 66 | interpreter: 3.8 3.9 3.10 3.11 3.12 3.13 pypy3.8 pypy3.9 pypy3.10 67 | - target: aarch64 68 | interpreter: 3.8 3.9 3.10 3.11 3.12 3.13 pypy3.8 pypy3.9 pypy3.10 69 | - target: armv7 70 | interpreter: 3.8 3.9 3.10 3.11 3.12 3.13 pypy3.8 pypy3.9 pypy3.10 71 | 72 | steps: 73 | - uses: actions/checkout@v4 74 | with: 75 | submodules: recursive 76 | - name: Build wheels 77 | uses: PyO3/maturin-action@v1 78 | with: 79 | target: ${{ matrix.platform.target }} 80 | args: --release --out dist -i ${{ matrix.platform.interpreter }} 81 | sccache: "true" 82 | manylinux: auto 83 | working-directory: ./bindings/python 84 | - name: Upload wheels 85 | uses: actions/upload-artifact@v4 86 | with: 87 | name: linux 88 | path: bindings/python/dist 89 | retention-days: 1 90 | 91 | musllinux: 92 | if: "startsWith(github.ref, 'refs/tags/')" 93 | runs-on: ubuntu-latest 94 | needs: lint_and_test 95 | strategy: 96 | matrix: 97 | platform: 98 | - target: x86_64-unknown-linux-musl 99 | arch: x86_64 100 | interpreter: 3.8 3.9 3.10 3.11 3.12 3.13 pypy3.8 pypy3.9 pypy3.10 101 | - target: i686-unknown-linux-musl 102 | arch: x86 103 | interpreter: 3.8 3.9 3.10 3.11 3.12 3.13 pypy3.8 pypy3.9 pypy3.10 104 | - target: aarch64-unknown-linux-musl 105 | arch: aarch64 106 | interpreter: 3.8 3.9 3.10 3.11 3.12 3.13 pypy3.8 pypy3.9 pypy3.10 107 | # all values: [x86_64, x86, aarch64, armhf, armv7, ppc64le, riscv64, s390x] 108 | # { target: "armv7-unknown-linux-musleabihf", image_tag: "armv7" }, 109 | # { target: "powerpc64le-unknown-linux-musl", image_tag: "ppc64le" }, 110 | steps: 111 | - uses: actions/checkout@v4 112 | with: 113 | submodules: recursive 114 | - name: Setup QEMU 115 | uses: docker/setup-qemu-action@v3 116 | - name: Build wheels 117 | uses: PyO3/maturin-action@v1 118 | with: 119 | target: ${{ matrix.platform.target }} 120 | args: --release --out dist -i ${{ matrix.platform.interpreter }} 121 | sccache: "true" 122 | manylinux: musllinux_1_1 123 | working-directory: ./bindings/python 124 | - name: Upload wheels 125 | uses: actions/upload-artifact@v4 126 | with: 127 | name: musllinux 128 | path: bindings/python/dist 129 | retention-days: 1 130 | 131 | windows: 132 | if: "startsWith(github.ref, 'refs/tags/')" 133 | runs-on: windows-latest 134 | needs: lint_and_test 135 | strategy: 136 | matrix: 137 | platform: 138 | - target: x64 139 | interpreter: 3.8 3.9 3.10 3.11 3.12 3.13 140 | - target: x86 141 | interpreter: 3.8 3.9 3.10 3.11 3.12 3.13 142 | steps: 143 | - uses: actions/checkout@v4 144 | with: 145 | submodules: recursive 146 | - name: Build wheels 147 | uses: PyO3/maturin-action@v1 148 | with: 149 | target: ${{ matrix.platform.target }} 150 | args: --release --out dist -i ${{ matrix.platform.interpreter }} 151 | sccache: "true" 152 | working-directory: ./bindings/python 153 | - name: Upload wheels 154 | uses: actions/upload-artifact@v4 155 | with: 156 | name: windows 157 | path: bindings/python/dist 158 | retention-days: 1 159 | 160 | macos: 161 | if: "startsWith(github.ref, 'refs/tags/')" 162 | runs-on: macos-latest 163 | needs: lint_and_test 164 | strategy: 165 | matrix: 166 | platform: 167 | - target: x64 168 | interpreter: 3.8 3.9 3.10 3.11 3.12 3.13 pypy3.8 pypy3.9 pypy3.10 169 | - target: aarch64 170 | interpreter: 3.8 3.9 3.10 3.11 3.12 3.13 pypy3.8 pypy3.9 pypy3.10 171 | steps: 172 | - uses: actions/checkout@v4 173 | with: 174 | submodules: recursive 175 | - name: Build wheels 176 | uses: PyO3/maturin-action@v1 177 | with: 178 | target: ${{ matrix.platform.target }} 179 | args: --release --out dist -i ${{ matrix.platform.interpreter }} 180 | sccache: "true" 181 | working-directory: ./bindings/python 182 | - name: Upload wheels 183 | uses: actions/upload-artifact@v4 184 | with: 185 | name: macos 186 | path: bindings/python/dist 187 | retention-days: 1 188 | 189 | sdist: 190 | if: "startsWith(github.ref, 'refs/tags/')" 191 | runs-on: ubuntu-latest 192 | needs: lint_and_test 193 | steps: 194 | - uses: actions/checkout@v4 195 | with: 196 | submodules: recursive 197 | - name: Build sdist 198 | uses: PyO3/maturin-action@v1 199 | with: 200 | command: sdist 201 | args: --out dist 202 | working-directory: ./bindings/python 203 | - name: Upload sdist 204 | uses: actions/upload-artifact@v4 205 | with: 206 | name: sdist 207 | path: bindings/python/dist 208 | 209 | release: 210 | name: Release 211 | runs-on: ubuntu-latest 212 | if: "startsWith(github.ref, 'refs/tags/')" 213 | needs: [linux, windows, macos, sdist, musllinux] 214 | steps: 215 | - uses: actions/download-artifact@v4 216 | with: 217 | path: dist 218 | merge-multiple: true 219 | - name: Publish package distributions to PyPI 220 | uses: pypa/gh-action-pypi-publish@release/v1 221 | with: 222 | skip-existing: true 223 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # texture2ddecoder [![Build Status]][actions] [![Latest Version]][crates.io] [![Docs]][docs.rs] [![License_MIT]][license_mit] [![License_APACHE]][license_apache] 2 | 3 | [Build Status]: https://img.shields.io/github/actions/workflow/status/UniversalGameExtraction/texture2ddecoder/ci.yml?branch=master 4 | [actions]: https://github.com/UniversalGameExtraction/texture2ddecoder/actions?query=branch%3Amain 5 | [Latest Version]: https://img.shields.io/crates/v/texture2ddecoder.svg 6 | [crates.io]: https://crates.io/crates/texture2ddecoder 7 | [Docs]: https://docs.rs/texture2ddecoder/badge.svg 8 | [docs.rs]: https://docs.rs/crate/texture2ddecoder/ 9 | [License_MIT]: https://img.shields.io/badge/License-MIT-yellow.svg 10 | [license_mit]: https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/main/LICENSE-MIT 11 | [License_APACHE]: https://img.shields.io/badge/License-Apache%202.0-blue.svg 12 | [license_apache]: https://raw.githubusercontent.com/UniversalGameExtraction/texture2ddecoder/main/LICENSE-APACHE 13 | 14 | A pure Rust no-std texture decoder for the following formats: 15 | - [ATC - Adreno Texture Compression](https://registry.khronos.org/OpenGL/extensions/AMD/AMD_compressed_ATC_texture.txt) ([detailed paper](http://www.guildsoftware.com/papers/2012.Converting.DXTC.to.ATC.pdf)) 16 | - [ASTC - Adaptive Scalable Texture Compression](https://en.wikipedia.org/wiki/Adaptive_Scalable_Texture_Compression) 17 | - [BCn - Block Compression](https://en.wikipedia.org/wiki/S3_Texture_Compression) 18 | - [ETC - Ericsson Texture Compression](https://en.wikipedia.org/wiki/Ericsson_Texture_Compression) 19 | - [PVRTC - PowerVR Texture Compression](https://en.wikipedia.org/wiki/PVRTC) 20 | 21 | and with alloc: 22 | - [Crunch](https://github.com/BinomialLLC/crunch) & [Unity's Crunch (unity branch)](https://github.com/Unity-Technologies/crunch/tree/unity) 23 | 24 | ## Features 25 | 26 | ### alloc (optional, default) 27 | 28 | - ~35% faster pvrtc decoding 29 | - crunch decoding 30 | 31 | ## Functions 32 | Provides a decode function for each format, as well as a block decode function all formats besides PVRTC. 33 | Besides some exceptions, the signature of the decode functions is as follows: 34 | ```rust 35 | fn decode_format(data: &[u8], width: usize, height: usize, image: &mut [u32]) -> Result<(), &'static str> 36 | // data: the compressed data, expected to be width * height / block_size in size 37 | // width: the width of the image 38 | // height: the height of the image 39 | // image: the buffer to write the decoded image to, expected to be width * height in size 40 | fn decode_format_block(data: &[u8], outbuf: &mut [u32]) -> Result<(), &'static str> 41 | // data: the compressed data (block), expected to be block_size in size 42 | // outbuf: the buffer to write the decoded image to, expected to be block_size in size 43 | ``` 44 | The exceptions are: 45 | - ASTC: the (block) decode function takes the block size as an additional parameter 46 | - BC6: there are two additional decode functions for the signed and unsigned variants 47 | - PVRTC: the decode function takes the block size as an additional parameter, and there are two additional decode functions for the 2bpp and 4bpp variants 48 | - Crunch & Unity's Crunch: The texture's dimensions and metadata are stored in the file itself, the header must be parsed with crnd_get_texture_info() from CrnTextureInfo struct first, then pass the metadata to the decoder. There's no block decompression function. 49 | 50 | Here is a list of the formats and their corresponding functions: 51 | - ATC 52 | - decode_atc_rgb4 53 | - decode_atc_rgb4_block 54 | - decode_atc_rgba8 55 | - decode_atc_rgba8_block 56 | - ASTC 57 | - decode_astc 58 | - decode_astc_block 59 | - various decode_astc_(block_)_x_y functions, where x and y are the block size 60 | - BCn 61 | - decode_bc1 62 | - decode_bc1_block 63 | - decode_bc3 64 | - decode_bc3_block 65 | - decode_bc4 66 | - decode_bc4_block 67 | - decode_bc5 68 | - decode_bc5_block 69 | - decode_bc6 70 | - decode_bc6_block 71 | - decode_bc6_signed 72 | - decode_bc6_block_signed 73 | - decode_bc6_unsigned 74 | - decode_bc6_block_unsigned 75 | - decode_bc7 76 | - decode_bc7_block 77 | - ETC 78 | - decode_etc1 79 | - decode_etc1_block 80 | - decode_etc2_rgb 81 | - decode_etc2_rgb_block 82 | - decode_etc2_rgba1 83 | - decode_etc2_rgba1_block 84 | - decode_etc2_rgba8 85 | - decode_etc2_rgba8_block 86 | - decode_eacr 87 | - decode_eacr_block 88 | - decode_eacr_signed 89 | - decode_eacr_signed_block 90 | - decode_eacrg 91 | - decode_eacrg_block 92 | - PVRTC 93 | - decode_pvrtc 94 | - decode_pvrtc_2bpp 95 | - decode_pvrtc_4bpp 96 | - Crunch 97 | - decode_crunch 98 | - Unity Crunch 99 | - decode_unity_crunch 100 | 101 | ## Roadmap 102 | - documentation 103 | - replacing u32 color output with RGBA structure 104 | - finding the original sources for the decoders 105 | - supporting more than BGRA32 output 106 | - adding additional formats 107 | 108 | ### Format Progress 109 | 110 | - [x] ATC-RGB 111 | - [x] ATC-RGBA 112 | - [x] ASTC 113 | - [x] BC1 114 | - [x] BC3 115 | - [x] BC4 116 | - [x] BC5 117 | - [x] BC6 118 | - [x] BC7 119 | - [x] EAC-R 120 | - [x] EAC-RG 121 | - [x] ETC1 122 | - [x] ETC2 123 | - [x] ETC2-A1 124 | - [x] ETC2-A8 125 | - [x] PVRTCI-2bpp 126 | - [x] PVRTCI-4bpp 127 | - [x] Crunched 128 | - [x] DXT1 129 | - [x] DXT5 130 | - [x] ETC1 131 | - [x] ETC2-A8 132 | 133 | ## License & Credits 134 | 135 | This crate itself is dual-licensed under MIT + Apache2. 136 | 137 | The texture compression codecs themselves have following licenses: 138 | | Codec | License | Source | 139 | |----------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------| 140 | | ATC | MIT | [Perfare/AssetStudio - Texture2DDecoderNative/atc.cpp](https://github.com/Perfare/AssetStudio/blob/master/Texture2DDecoderNative/atc.cpp)| 141 | | ASTC | MIT | [Ishotihadus/mikunyan - ext/decoders/native/astc.c](https://github.com/Ishotihadus/mikunyan/blob/master/ext/decoders/native/astc.c) | 142 | | BCn | MIT | [Perfare/AssetStudio - Texture2DDecoderNative/bcn.cpp](https://github.com/Perfare/AssetStudio/blob/master/Texture2DDecoderNative/bcn.cpp)| 143 | | ETC | MIT | [Ishotihadus/mikunyan - ext/decoders/native/etc.c](https://github.com/Ishotihadus/mikunyan/blob/master/ext/decoders/native/etc.c) | 144 | | f16 | MIT | [Maratyszcza/FP16](https://github.com/Maratyszcza/FP16) | 145 | | PVRTC | MIT | [Ishotihadus/mikunyan - ext/decoders/native/pvrtc.c](https://github.com/Ishotihadus/mikunyan/blob/master/ext/decoders/native/pvrtc.c) | 146 | | Crunch | PUBLIC DOMAIN | [BinomialLLC/crunch](https://github.com/BinomialLLC/crunch) | 147 | | Crunch (Unity) | ZLIB | [Unity-Technologies/crunch](https://github.com/Unity-Technologies/crunch) | 148 | -------------------------------------------------------------------------------- /src/crunch/crn_decomp.rs: -------------------------------------------------------------------------------- 1 | use super::crn_consts::*; 2 | use super::crn_unpacker::*; 3 | use super::CrnFormat; 4 | extern crate alloc; 5 | 6 | // #[repr(C)] 7 | // pub struct crn_file_info{ 8 | // struct_size: u32, 9 | // actual_data_size: u32, 10 | // header_size: u32, 11 | // total_palette_size: u32, 12 | // tables_size: u32, 13 | // levels: u32, 14 | // level_compressed_size: [u32; cCRNMaxLevels as usize], 15 | // color_endpoint_palette_entries: u32, 16 | // color_selector_palette_entries: u32, 17 | // alpha_endpoint_palette_entries: u32, 18 | // alpha_selector_palette_entries: u32 19 | // } 20 | 21 | // impl crn_file_info{ 22 | // pub fn default() -> crn_file_info{ 23 | // return crn_file_info { 24 | // struct_size: core::mem::size_of::() as u32, 25 | // actual_data_size: 0, 26 | // header_size: 0, 27 | // total_palette_size: 0, 28 | // tables_size: 0, 29 | // levels: 0, 30 | // level_compressed_size: [0; cCRNMaxLevels as usize], 31 | // color_endpoint_palette_entries: 0, 32 | // color_selector_palette_entries: 0, 33 | // alpha_endpoint_palette_entries: 0, 34 | // alpha_selector_palette_entries: 0 35 | // } 36 | // } 37 | // } 38 | 39 | #[repr(C)] 40 | pub struct CrnPackedUint { 41 | pub buf: [u8; N], 42 | } 43 | 44 | // no-std, so we can not use std::ops 45 | impl CrnPackedUint { 46 | pub fn assign_from_buffer(&mut self, other: &[u8]) { 47 | self.buf.copy_from_slice(&other[0..N]) 48 | } 49 | 50 | pub fn cast_to_uint(&mut self) -> u32 { 51 | match N { 52 | 1 => self.buf[0] as u32, 53 | 2 => u16::from_be_bytes([self.buf[0], self.buf[1]]) as u32, 54 | 3 => (self.buf[0] as u32) << 16 | u16::from_be_bytes([self.buf[1], self.buf[2]]) as u32, 55 | 4 => u32::from_be_bytes([self.buf[0], self.buf[1], self.buf[2], self.buf[3]]), 56 | _ => panic!("Packed integer can hold a 4 byte buffer at max!"), 57 | } 58 | } 59 | } 60 | 61 | impl Default for CrnPackedUint { 62 | fn default() -> Self { 63 | CrnPackedUint { buf: [0; N] } 64 | } 65 | } 66 | 67 | #[derive(Default)] 68 | #[repr(C)] 69 | pub struct CrnPalette { 70 | pub ofs: CrnPackedUint<3>, 71 | pub size: CrnPackedUint<3>, 72 | pub num: CrnPackedUint<2>, 73 | } 74 | 75 | impl CrnPalette { 76 | pub fn assign_from_buffer(&mut self, other: &[u8]) { 77 | self.ofs.assign_from_buffer(&other[0..]); 78 | self.size.assign_from_buffer(&other[3..]); 79 | self.num.assign_from_buffer(&other[6..]); 80 | } 81 | } 82 | 83 | #[derive(Default)] 84 | #[repr(C)] 85 | pub struct CrnHeader { 86 | pub sig: CrnPackedUint<2>, 87 | pub header_size: CrnPackedUint<2>, 88 | pub header_crc16: CrnPackedUint<2>, 89 | 90 | pub data_size: CrnPackedUint<4>, 91 | pub data_crc16: CrnPackedUint<2>, 92 | 93 | pub width: CrnPackedUint<2>, 94 | pub height: CrnPackedUint<2>, 95 | 96 | pub levels: CrnPackedUint<1>, 97 | pub faces: CrnPackedUint<1>, 98 | 99 | pub format: CrnPackedUint<1>, 100 | pub flags: CrnPackedUint<2>, 101 | 102 | pub reserved: CrnPackedUint<4>, 103 | pub userdata0: CrnPackedUint<4>, 104 | pub userdata1: CrnPackedUint<4>, 105 | 106 | pub color_endpoints: CrnPalette, 107 | pub color_selectors: CrnPalette, 108 | 109 | pub alpha_endpoints: CrnPalette, 110 | pub alpha_selectors: CrnPalette, 111 | 112 | pub tables_size: CrnPackedUint<2>, 113 | pub tables_ofs: CrnPackedUint<3>, 114 | 115 | pub level_ofs: alloc::vec::Vec>, 116 | } 117 | 118 | impl CrnHeader { 119 | // size of CrnHeader in C++, which uses a hacky struct definition which shouldn't be used in Rust 120 | pub const MIN_SIZE: u32 = 74; 121 | 122 | pub fn crnd_get_header(&mut self, p_data: &[u8], data_size: u32) -> bool { 123 | if data_size < CrnHeader::MIN_SIZE { 124 | return false; 125 | } 126 | *self = CrnHeader::default(); 127 | self.sig.assign_from_buffer(&p_data[0..]); 128 | self.header_size.assign_from_buffer(&p_data[2..]); 129 | self.header_crc16.assign_from_buffer(&p_data[4..]); 130 | self.data_size.assign_from_buffer(&p_data[6..]); 131 | self.data_crc16.assign_from_buffer(&p_data[10..]); 132 | self.width.assign_from_buffer(&p_data[12..]); 133 | self.height.assign_from_buffer(&p_data[14..]); 134 | self.levels.assign_from_buffer(&p_data[16..]); 135 | self.faces.assign_from_buffer(&p_data[17..]); 136 | self.format.assign_from_buffer(&p_data[18..]); 137 | self.flags.assign_from_buffer(&p_data[19..]); 138 | self.reserved.assign_from_buffer(&p_data[21..]); 139 | self.userdata0.assign_from_buffer(&p_data[25..]); 140 | self.userdata1.assign_from_buffer(&p_data[29..]); 141 | self.color_endpoints.assign_from_buffer(&p_data[33..]); 142 | self.color_selectors.assign_from_buffer(&p_data[41..]); 143 | self.alpha_endpoints.assign_from_buffer(&p_data[49..]); 144 | self.alpha_selectors.assign_from_buffer(&p_data[57..]); 145 | self.tables_size.assign_from_buffer(&p_data[65..]); 146 | self.tables_ofs.assign_from_buffer(&p_data[67..]); 147 | self.level_ofs = alloc::vec![]; 148 | for i in 0..self.levels.cast_to_uint() as usize { 149 | self.level_ofs.push(CrnPackedUint { buf: [0, 0, 0, 0] }); 150 | self.level_ofs[i].assign_from_buffer(&p_data[70 + (i * 4)..]); 151 | } 152 | if self.sig.cast_to_uint() as u16 != CRNSIG_VALUE { 153 | return false; 154 | } 155 | if self.header_size.cast_to_uint() < CrnHeader::MIN_SIZE 156 | || data_size < self.data_size.cast_to_uint() 157 | { 158 | return false; 159 | } 160 | true 161 | } 162 | } 163 | 164 | // #[repr(C)] 165 | // pub struct crn_level_info{ 166 | // struct_size: u32, 167 | // width: u32, 168 | // height: u32, 169 | // faces: u32, 170 | // blocks_x: u32, 171 | // blocks_y: u32, 172 | // bytes_per_block: u32, 173 | // format: crn_format, 174 | // } 175 | 176 | // impl crn_level_info{ 177 | // pub fn default() -> crn_level_info{ 178 | // return crn_level_info { 179 | // struct_size: core::mem::size_of::() as u32, 180 | // width: 0, 181 | // height: 0, 182 | // faces: 0, 183 | // blocks_x: 0, 184 | // blocks_y: 0, 185 | // bytes_per_block: 0, 186 | // format: crn_format::cCRNFmtInvalid // Init as invalid? 187 | // } 188 | // } 189 | // } 190 | 191 | pub fn crnd_unpack_begin(p_data: &[u8], data_size: u32) -> Result { 192 | if data_size < CRNHEADER_MIN_SIZE as u32 { 193 | return Err("Data size is below the minimum allowed."); 194 | } 195 | let mut p = CrnUnpacker::default(); 196 | if !p.init(p_data, data_size) { 197 | return Err("Failed to initialize Crunch decompressor."); 198 | } 199 | Ok(p) 200 | } 201 | 202 | pub fn crnd_get_crn_format_bits_per_texel(fmt: &mut CrnFormat) -> Result { 203 | match fmt { 204 | CrnFormat::Dxt1 | CrnFormat::Dxt5a | CrnFormat::Etc1 => Ok(4), 205 | 206 | CrnFormat::Dxt3 207 | | CrnFormat::CCrnfmtDxt5 208 | | CrnFormat::DxnXy 209 | | CrnFormat::DxnYx 210 | | CrnFormat::Dxt5CcxY 211 | | CrnFormat::Dxt5XGxR 212 | | CrnFormat::Dxt5XGbr 213 | | CrnFormat::Dxt5Agbr => Ok(8), 214 | 215 | _ => Err("Texture format is not supported."), 216 | } 217 | } 218 | 219 | pub fn crnd_get_bytes_per_dxt_block(fmt: &mut CrnFormat) -> Result { 220 | Ok((crnd_get_crn_format_bits_per_texel(fmt)? << 4) >> 3) 221 | } 222 | -------------------------------------------------------------------------------- /bindings/python/tests/test_main.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import os 3 | from time import time_ns 4 | 5 | from PIL import Image 6 | 7 | from texture2ddecoder_rs import * 8 | 9 | 10 | def test_ATC_RGB(): 11 | _test_format("ATC_RGB", "dds", decode_atc_rgb4) 12 | 13 | 14 | def test_ATC_RGBA_Explicit(): 15 | _test_format("ATC_RGBA_Explicit", "dds", decode_atc_rgba8) 16 | 17 | 18 | def test_ATC_RGBA_Interpolated(): 19 | _test_format("ATC_RGBA_Interpolated", "dds", decode_atc_rgba8) 20 | 21 | 22 | def test_ASTC_4x4(): 23 | _test_format("ASTC_4x4", "ktx2", decode_astc_4_4) 24 | 25 | 26 | def test_ASTC_5x4(): 27 | _test_format("ASTC_5x4", "ktx2", decode_astc_5_4) 28 | 29 | 30 | def test_ASTC_5x5(): 31 | _test_format("ASTC_5x5", "ktx2", decode_astc_5_5) 32 | 33 | 34 | def test_ASTC_6x5(): 35 | _test_format("ASTC_6x5", "ktx2", decode_astc_6_5) 36 | 37 | 38 | def test_ASTC_6x6(): 39 | _test_format("ASTC_6x6", "ktx2", decode_astc_6_6) 40 | 41 | 42 | def test_ASTC_8x5(): 43 | _test_format("ASTC_8x5", "ktx2", decode_astc_8_5) 44 | 45 | 46 | def test_ASTC_8x6(): 47 | _test_format("ASTC_8x6", "ktx2", decode_astc_8_6) 48 | 49 | 50 | def test_ASTC_8x8(): 51 | _test_format("ASTC_8x8", "ktx2", decode_astc_8_8) 52 | 53 | 54 | def test_BC1(): 55 | _test_format("BC1", "ktx2", decode_bc1) 56 | 57 | 58 | def test_BC1A(): 59 | _test_format("BC1A", "ktx2", decode_bc1a) 60 | 61 | 62 | def test_BC2(): 63 | _test_format("BC1", "ktx2", decode_bc2) 64 | 65 | 66 | def test_BC3(): 67 | _test_format("BC3", "ktx2", decode_bc3) 68 | 69 | 70 | def test_BC4(): 71 | _test_format("BC4", "ktx2", decode_bc4) 72 | 73 | 74 | def test_BC5(): 75 | _test_format("BC5", "ktx2", decode_bc5) 76 | 77 | 78 | def test_BC6H(): 79 | _test_format("BC6H", "ktx2", decode_bc6_unsigned) 80 | 81 | 82 | def test_BC7(): 83 | _test_format("BC7", "ktx2", decode_bc7) 84 | 85 | 86 | def test_ETC1_RGB(): 87 | _test_format("ETC1_RGB", "ktx2", decode_etc1) 88 | 89 | 90 | def test_ETC2_RGB(): 91 | _test_format("ETC2_RGB", "ktx2", decode_etc2_rgb) 92 | 93 | 94 | def test_ETC2_RGBA(): 95 | _test_format("ETC2_RGBA", "ktx2", decode_etc2_rgba8) 96 | 97 | 98 | def test_ETC2_RGB_A1(): 99 | _test_format("ETC2_RGB_A1", "ktx2", decode_etc2_rgba1) 100 | 101 | 102 | def test_PVRTCI_2bpp_RGB(): 103 | _test_format("PVRTCI_2bpp_RGB", "ktx2", decode_pvrtc_2bpp) 104 | 105 | 106 | def test_PVRTCI_2bpp_RGBA(): 107 | _test_format("PVRTCI_2bpp_RGBA", "ktx2", decode_pvrtc_2bpp) 108 | 109 | 110 | def test_PVRTCI_4bpp_RGB(): 111 | _test_format("PVRTCI_4bpp_RGB", "ktx2", decode_pvrtc_4bpp) 112 | 113 | 114 | def test_PVRTCI_4bpp_RGBA(): 115 | _test_format("PVRTCI_4bpp_RGBA", "ktx2", decode_pvrtc_4bpp) 116 | 117 | 118 | def test_EAC_R11(): 119 | _test_format("EAC_R11", "ktx2", decode_eacr) 120 | 121 | 122 | def test_EAC_RG11(): 123 | _test_format("EAC_RG11", "ktx2", decode_eacrg) 124 | 125 | 126 | def test_CRUNCH_DXT1(): 127 | _test_format("CRUNCH_DXT1", "crn", decode_crunch) 128 | 129 | 130 | def test_CRUNCH_DXT5(): 131 | _test_format("CRUNCH_DXT5", "crn", decode_crunch) 132 | 133 | 134 | def test_CRUNCH_DXT5A(): 135 | _test_format("CRUNCH_DXT5A", "crn", decode_crunch) 136 | 137 | 138 | def test_CRUNCH_DXN(): 139 | _test_format("CRUNCH_DXN", "crn", decode_crunch) 140 | 141 | 142 | def test_UNITYCRUNCH_DXT1(): 143 | _test_format("UNITYCRUNCH_DXT1", "crn", decode_unity_crunch) 144 | 145 | 146 | def test_UNITYCRUNCH_DXT5(): 147 | _test_format("UNITYCRUNCH_DXT5", "crn", decode_unity_crunch) 148 | 149 | 150 | def test_UNITYCRUNCH_DXT5A(): 151 | _test_format("UNITYCRUNCH_DXT5A", "crn", decode_unity_crunch) 152 | 153 | 154 | def test_UNITYCRUNCH_DXN(): 155 | _test_format("UNITYCRUNCH_DXN", "crn", decode_unity_crunch) 156 | 157 | 158 | def test_UNITYCRUNCH_ETC1(): 159 | _test_format("UNITYCRUNCH_ETC1", "crn", decode_unity_crunch) 160 | 161 | 162 | def test_UNITYCRUNCH_ETC1S(): 163 | _test_format("UNITYCRUNCH_ETC1S", "crn", decode_unity_crunch) 164 | 165 | 166 | def test_UNITYCRUNCH_ETC2(): 167 | _test_format("UNITYCRUNCH_ETC2", "crn", decode_unity_crunch) 168 | 169 | 170 | def test_UNITYCRUNCH_ETC2A(): 171 | _test_format("UNITYCRUNCH_ETC2A", "crn", decode_unity_crunch) 172 | 173 | 174 | def test_UNITYCRUNCH_ETC2AS(): 175 | _test_format("UNITYCRUNCH_ETC2AS", "crn", decode_unity_crunch) 176 | 177 | 178 | class Texture: 179 | width: int 180 | height: int 181 | data: bytes 182 | 183 | def __init__(self, width: int, height: int, data: bytes) -> None: 184 | self.width = width 185 | self.height = height 186 | self.data = data 187 | 188 | @classmethod 189 | def from_file(cls, fp: str) -> Texture: 190 | extension = os.path.splitext(fp)[1][1:].lower() 191 | data = open(fp, "rb").read() 192 | 193 | if extension == "ktx2": 194 | return cls.from_ktx2(data) 195 | elif extension == "dds": 196 | return cls.from_dds(data) 197 | elif extension == "crn": 198 | return cls.from_crn(data) 199 | else: 200 | raise ("Unsupported file format") 201 | 202 | @classmethod 203 | def from_ktx2(cls, data: bytes) -> Texture: 204 | assert data[:12] == b"\xabKTX 20\xbb\r\n\x1a\n" 205 | width = int.from_bytes(data[20:24], "little") 206 | height = int.from_bytes(data[24:28], "little") 207 | level_0_offset = int.from_bytes(data[80:88], "little") 208 | level_0_size = int.from_bytes(data[88:96], "little") 209 | print(width, height, level_0_offset, level_0_size) 210 | tex_data = data[level_0_offset : level_0_offset + level_0_size] 211 | return Texture(width, height, tex_data) 212 | 213 | @classmethod 214 | def from_dds(cls, data: bytes) -> Texture: 215 | assert data[:4] == b"DDS " 216 | height = int.from_bytes(data[12:16], "little") 217 | width = int.from_bytes(data[16:20], "little") 218 | offset = int.from_bytes(data[4:8], "little") + 4 219 | fourcc = int.from_bytes(data[84:88], "little", signed=False) 220 | block_bytes = 8 if fourcc == 0x31545844 else 16 221 | 222 | tex_data = data[offset : offset + (width * height * block_bytes) // 16] 223 | return Texture(width, height, tex_data) 224 | 225 | @classmethod 226 | def from_crn(cls, data: bytes) -> Texture: 227 | width = int.from_bytes(data[12:14], "big") 228 | height = int.from_bytes(data[14:16], "big") 229 | return Texture(width, height, data) 230 | 231 | def _decode(self, decode_func: callable) -> bytes: 232 | start = time_ns() 233 | ret = decode_func(self.data, self.width, self.height) 234 | duration = time_ns() - start 235 | print("Time elapsed in decoding is: ", duration) 236 | return ret 237 | 238 | def save_as_image(self, path: str, decode_func: callable): 239 | image_data = self._decode(decode_func) 240 | _image = Image.frombuffer( 241 | "RGBA", (self.width, self.height), image_data, "raw", "BGRA" 242 | ) 243 | # image.save(path) 244 | 245 | 246 | def get_texture_fp(name: str) -> str: 247 | return os.path.abspath( 248 | os.path.join( 249 | os.path.realpath(__file__), 250 | "..", 251 | "..", 252 | "..", 253 | "..", 254 | "resources", 255 | "tests", 256 | "textures", 257 | name, 258 | ) 259 | ) 260 | 261 | 262 | def get_image_fp(name: str) -> str: 263 | return os.path.join( 264 | os.path.realpath(__file__), 265 | "..", 266 | "..", 267 | "..", 268 | "..", 269 | "resources", 270 | "tests", 271 | "decompressed", 272 | name, 273 | ) 274 | 275 | 276 | def _test_format(name: str, sample_extension: str, decode_func: callable): 277 | print("Testing ", name) 278 | src_fp = get_texture_fp(f"{name}.{sample_extension}") 279 | dst_fp = get_image_fp(f"{name}.png") 280 | 281 | texture = Texture.from_file(src_fp) 282 | texture.save_as_image(dst_fp, decode_func) 283 | 284 | 285 | if __name__ == "__main__": 286 | for key, val in list(locals().items()): 287 | if key.startswith("test_") and callable(val): 288 | try: 289 | val() 290 | except Exception as e: 291 | print("Error in ", key) 292 | raise e 293 | -------------------------------------------------------------------------------- /src/f16.rs: -------------------------------------------------------------------------------- 1 | /* 2 | License Information 3 | 4 | FP16 library is derived from https://github.com/Maratyszcza/FP16. 5 | The library is licensed under the MIT License shown below. 6 | 7 | 8 | The MIT License (MIT) 9 | 10 | Copyright (c) 2017 Facebook Inc. 11 | Copyright (c) 2017 Georgia Institute of Technology 12 | Copyright 2019 Google LLC 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 15 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 16 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 20 | Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 23 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 24 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 25 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | // Helper functions for Rust for converting between IEEE single-precision and half-precision floating-point 29 | #[inline] 30 | const fn __builtin_clz(x: u32) -> u32 { 31 | let mut ret: u32 = 0; 32 | let mut x = x; 33 | let mut i = 0; 34 | // use loop instead of for so we can use const 35 | loop { 36 | if (x & 0x80000000 != 0) | (i == 32) { 37 | break; 38 | } 39 | ret += 1; 40 | x <<= 1; 41 | i += 1; 42 | } 43 | ret 44 | } 45 | 46 | #[inline] 47 | fn fp32_from_bits(x: u32) -> f32 { 48 | f32::from_le_bytes(u32::to_le_bytes(x)) 49 | } 50 | 51 | #[inline] 52 | fn fp32_to_bits(x: f32) -> u32 { 53 | u32::from_le_bytes(f32::to_le_bytes(x)) 54 | } 55 | 56 | /// Convert a 16-bit floating-point number in IEEE half-precision format, in bit representation, to 57 | /// a 32-bit floating-point number in IEEE single-precision format. 58 | /// The implementation relies on IEEE-like (no assumption about rounding mode and no operations on denormals) 59 | /// floating-point operations and bitcasts between integer and floating-point variables. 60 | #[inline] 61 | pub fn fp16_ieee_to_fp32_value(h: u16) -> f32 { 62 | // Extend the half-precision floating-point number to 32 bits and shift to the upper part of the 32-bit word: 63 | // +---+-----+------------+-------------------+ 64 | // | S |EEEEE|MM MMMM MMMM|0000 0000 0000 0000| 65 | // +---+-----+------------+-------------------+ 66 | // Bits 31 26-30 16-25 0-15 67 | // 68 | // S - sign bit, E - bits of the biased exponent, M - bits of the mantissa, 0 - zero bits. 69 | 70 | let w: u32 = (h as u32) << 16; 71 | 72 | // Extract the sign of the input number into the high bit of the 32-bit word: 73 | // 74 | // +---+----------------------------------+ 75 | // | S |0000000 00000000 00000000 00000000| 76 | // +---+----------------------------------+ 77 | // Bits 31 0-31 78 | 79 | let sign: u32 = w & 0x80000000; 80 | 81 | // Extract mantissa and biased exponent of the input number into the high bits of the 32-bit word: 82 | // 83 | // +-----+------------+---------------------+ 84 | // |EEEEE|MM MMMM MMMM|0 0000 0000 0000 0000| 85 | // +-----+------------+---------------------+ 86 | // Bits 27-31 17-26 0-16 87 | 88 | let two_w: u32 = w.overflowing_add(w).0; 89 | 90 | // Shift mantissa and exponent into bits 23-28 and bits 13-22 so they become mantissa and exponent 91 | // of a single-precision floating-point number: 92 | // 93 | // S|Exponent | Mantissa 94 | // +-+---+-----+------------+----------------+ 95 | // |0|000|EEEEE|MM MMMM MMMM|0 0000 0000 0000| 96 | // +-+---+-----+------------+----------------+ 97 | // Bits | 23-31 | 0-22 98 | // 99 | // Next, there are some adjustments to the exponent: 100 | // - The exponent needs to be corrected by the difference in exponent bias between single-precision and half-precision 101 | // formats (0x7F - 0xF = 0x70) 102 | // - Inf and NaN values in the inputs should become Inf and NaN values after conversion to the single-precision number. 103 | // Therefore, if the biased exponent of the half-precision input was 0x1F (max possible value), the biased exponent 104 | // of the single-precision output must be 0xFF (max possible value). We do this correction in two steps: 105 | // - First, we adjust the exponent by (0xFF - 0x1F) = 0xE0 (see exp_offset below) rather than by 0x70 suggested 106 | // by the difference in the exponent bias (see above). 107 | // - Then we multiply the single-precision result of exponent adjustment by 2**(-112) to reverse the effect of 108 | // exponent adjustment by 0xE0 less the necessary exponent adjustment by 0x70 due to difference in exponent bias. 109 | // The floating-point multiplication hardware would ensure than Inf and NaN would retain their value on at least 110 | // partially IEEE754-compliant implementations. 111 | // 112 | // Note that the above operations do not handle denormal inputs (where biased exponent == 0). However, they also do not 113 | // operate on denormal inputs, and do not produce denormal results. 114 | 115 | let exp_offset: u32 = 0xE0 << 23; 116 | // #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || defined(__GNUC__) && !defined(__STRICT_ANSI__) 117 | // let exp_scale: f32 = 0x1.0p-112f; 118 | // #else 119 | let exp_scale: f32 = fp32_from_bits(0x7800000); 120 | // #endif 121 | let normalized_value: f32 = fp32_from_bits((two_w >> 4) + exp_offset) * exp_scale; 122 | 123 | // Convert denormalized half-precision inputs into single-precision results (always normalized). 124 | // Zero inputs are also handled here. 125 | // 126 | // In a denormalized number the biased exponent is zero, and mantissa has on-zero bits. 127 | // First, we shift mantissa into bits 0-9 of the 32-bit word. 128 | // 129 | // zeros | mantissa 130 | // +---------------------------+------------+ 131 | // |0000 0000 0000 0000 0000 00|MM MMMM MMMM| 132 | // +---------------------------+------------+ 133 | // Bits 10-31 0-9 134 | // 135 | // Now, remember that denormalized half-precision numbers are represented as: 136 | // FP16 = mantissa * 2**(-24). 137 | // The trick is to construct a normalized single-precision number with the same mantissa and thehalf-precision input 138 | // and with an exponent which would scale the corresponding mantissa bits to 2**(-24). 139 | // A normalized single-precision floating-point number is represented as: 140 | // FP32 = (1 + mantissa * 2**(-23)) * 2**(exponent - 127) 141 | // Therefore, when the biased exponent is 126, a unit change in the mantissa of the input denormalized half-precision 142 | // number causes a change of the constructud single-precision number by 2**(-24), i.e. the same ammount. 143 | // 144 | // The last step is to adjust the bias of the constructed single-precision number. When the input half-precision number 145 | // is zero, the constructed single-precision number has the value of 146 | // FP32 = 1 * 2**(126 - 127) = 2**(-1) = 0.5 147 | // Therefore, we need to subtract 0.5 from the constructed single-precision number to get the numerical equivalent of 148 | // the input half-precision number. 149 | 150 | let magic_mask: u32 = 126 << 23; 151 | let magic_bias: f32 = 0.5; 152 | let denormalized_value: f32 = fp32_from_bits((two_w >> 17) | magic_mask) - magic_bias; 153 | 154 | // - Choose either results of conversion of input as a normalized number, or as a denormalized number, depending on the 155 | // input exponent. The variable two_w contains input exponent in bits 27-31, therefore if its smaller than 2**27, the 156 | // input is either a denormal number, or zero. 157 | // - Combine the result of conversion of exponent and mantissa with the sign of the input number. 158 | 159 | let denormalized_cutoff: u32 = 1 << 27; 160 | let result: u32 = sign 161 | | (if two_w < denormalized_cutoff { 162 | fp32_to_bits(denormalized_value) 163 | } else { 164 | fp32_to_bits(normalized_value) 165 | }); 166 | fp32_from_bits(result) 167 | } 168 | -------------------------------------------------------------------------------- /src/crunch/crn_static_huffman_data_model.rs: -------------------------------------------------------------------------------- 1 | use super::crn_consts::*; 2 | use super::crn_utils::*; 3 | use core::cmp::{max, min}; 4 | extern crate alloc; 5 | 6 | #[derive(Default)] 7 | pub struct DecoderTables { 8 | pub num_syms: u32, 9 | pub total_used_syms: u32, 10 | pub table_bits: u32, 11 | pub table_shift: u32, 12 | pub table_max_code: u32, 13 | pub decode_start_code_size: u32, 14 | 15 | pub min_code_size: u8, 16 | pub max_code_size: u8, 17 | 18 | pub max_codes: [u32; MAX_EXPECTED_CODE_SIZE + 1], 19 | pub val_ptrs: [i32; MAX_EXPECTED_CODE_SIZE + 1], 20 | 21 | pub cur_lookup_size: u32, 22 | pub lookup: alloc::vec::Vec, 23 | 24 | pub cur_sorted_symbol_order_size: u32, 25 | pub sorted_symbol_order: alloc::vec::Vec, 26 | } 27 | 28 | impl DecoderTables { 29 | pub fn init(&mut self, num_syms: u32, p_codesizes: &[u8], mut table_bits: u32) -> bool { 30 | let mut min_codes = [0_u32; MAX_EXPECTED_CODE_SIZE]; 31 | 32 | if num_syms == 0_u32 || table_bits > MAX_TABLE_BITS as u32 { 33 | return false; 34 | } 35 | self.num_syms = num_syms; 36 | let mut num_codes = [0_u32; (MAX_EXPECTED_CODE_SIZE + 1)]; 37 | 38 | for &c in p_codesizes.iter().take(num_syms as usize) { 39 | if c != 0 { 40 | num_codes[c as usize] += 1; 41 | } 42 | } 43 | 44 | let mut sorted_positions = [0_u32; (MAX_EXPECTED_CODE_SIZE + 1)]; 45 | let mut cur_code: u32 = 0; 46 | let mut total_used_syms: u32 = 0; 47 | let mut max_code_size: u32 = 0; 48 | let mut min_code_size: u32 = u32::MAX; 49 | 50 | for i in 1..=MAX_EXPECTED_CODE_SIZE { 51 | let n = num_codes[i]; 52 | 53 | if n == 0 { 54 | self.max_codes[i - 1] = 0; 55 | } else { 56 | min_code_size = min(min_code_size, i as u32); 57 | max_code_size = max(max_code_size, i as u32); 58 | 59 | min_codes[i - 1] = cur_code; 60 | 61 | self.max_codes[i - 1] = cur_code + n - 1; 62 | self.max_codes[i - 1] = 63 | 1 + ((self.max_codes[i - 1] << (16 - i)) | ((1 << (16 - i)) - 1)); 64 | self.val_ptrs[i - 1] = total_used_syms as i32; 65 | sorted_positions[i] = total_used_syms; 66 | 67 | cur_code += n; 68 | total_used_syms += n; 69 | } 70 | cur_code <<= 1; 71 | } 72 | 73 | self.total_used_syms = total_used_syms; 74 | if total_used_syms > self.cur_sorted_symbol_order_size { 75 | self.cur_sorted_symbol_order_size = total_used_syms; 76 | 77 | if !total_used_syms.is_power_of_two() { 78 | self.cur_sorted_symbol_order_size = 79 | min(num_syms, total_used_syms.next_power_of_two()); 80 | } 81 | 82 | self.sorted_symbol_order = alloc::vec![0; self.cur_sorted_symbol_order_size as usize]; 83 | } 84 | 85 | self.min_code_size = min_code_size as u8; 86 | self.max_code_size = max_code_size as u8; 87 | 88 | for (i, &code_size) in p_codesizes.iter().enumerate().take(num_syms as usize) { 89 | let c: u32 = code_size as u32; 90 | 91 | if c != 0 { 92 | let code_index = c as usize; 93 | 94 | if num_codes[code_index] == 0 { 95 | return false; 96 | } 97 | 98 | let sorted_pos: u32 = sorted_positions[code_index]; 99 | sorted_positions[code_index] += 1; 100 | 101 | if sorted_pos >= total_used_syms { 102 | return false; 103 | } 104 | 105 | self.sorted_symbol_order[sorted_pos as usize] = i as u16; 106 | } 107 | } 108 | 109 | if table_bits <= self.min_code_size as u32 { 110 | table_bits = 0; 111 | } 112 | 113 | self.table_bits = table_bits; 114 | if table_bits != 0 { 115 | let table_size: u32 = 1 << table_bits; 116 | 117 | if table_size > self.cur_lookup_size { 118 | self.cur_lookup_size = table_size; 119 | self.lookup = alloc::vec![0; table_size as usize]; 120 | } 121 | 122 | for codesize in 1..=table_bits { 123 | if num_codes[codesize as usize] == 0 { 124 | continue; 125 | } 126 | let fillsize = table_bits - codesize; 127 | let fillnum: u32 = 1 << fillsize; 128 | let min_code: u32 = min_codes[(codesize - 1) as usize]; 129 | 130 | let max_code: u32 = match self.get_unshifted_max_code(codesize) { 131 | Ok(s) => s, 132 | Err(_) => return false, 133 | }; 134 | 135 | let val_ptr: u32 = self.val_ptrs[(codesize - 1) as usize] as u32; 136 | 137 | for code in min_code..=max_code { 138 | let sym_index: u32 = 139 | self.sorted_symbol_order[(val_ptr + code - min_code) as usize] as u32; 140 | 141 | if p_codesizes[sym_index as usize] as u32 != codesize { 142 | return false; 143 | } 144 | 145 | for j in 0..fillnum { 146 | let t: u32 = j + (code << fillsize); 147 | if t >= (1 << table_bits) { 148 | return false; 149 | } 150 | self.lookup[t as usize] = sym_index | (codesize << 16); 151 | } 152 | } 153 | } 154 | } 155 | 156 | for (val_ptr, &min_code) in self.val_ptrs.iter_mut().zip(min_codes.iter()) { 157 | *val_ptr -= min_code as i32; 158 | } 159 | 160 | self.table_max_code = 0; 161 | self.decode_start_code_size = self.min_code_size as u32; 162 | 163 | if table_bits != 0 { 164 | let mut i: u32 = table_bits; 165 | 166 | while i >= 1 { 167 | if num_codes[i as usize] != 0 { 168 | self.table_max_code = self.max_codes[(i - 1) as usize]; 169 | break; 170 | } 171 | i -= 1; 172 | } 173 | 174 | if i >= 1 { 175 | self.decode_start_code_size = table_bits + 1; 176 | for j in table_bits + 1..=max_code_size { 177 | if num_codes[j as usize] != 0 { 178 | self.decode_start_code_size = j; 179 | break; 180 | } 181 | } 182 | } 183 | } 184 | 185 | if self.table_max_code == 0 { 186 | self.table_max_code = 0; 187 | } 188 | // sentinels 189 | self.max_codes[MAX_EXPECTED_CODE_SIZE] = u32::MAX; 190 | self.val_ptrs[MAX_EXPECTED_CODE_SIZE] = 0xFFFFF; 191 | 192 | self.table_shift = 32 - self.table_bits; 193 | true 194 | } 195 | 196 | #[inline] 197 | fn get_unshifted_max_code(&mut self, len: u32) -> Result { 198 | if !(len >= 1 && len <= MAX_EXPECTED_CODE_SIZE as u32) { 199 | return Err(false); 200 | } 201 | let k: u32 = self.max_codes[(len - 1) as usize]; 202 | if k == 0 { 203 | return Ok(u32::MAX); 204 | } 205 | Ok((k - 1) >> (16 - len)) 206 | } 207 | } 208 | 209 | #[derive(Default)] 210 | pub struct StaticHuffmanDataModel { 211 | pub total_syms: u32, 212 | pub code_sizes: alloc::vec::Vec, 213 | pub p_decode_tables: DecoderTables, 214 | } 215 | 216 | impl StaticHuffmanDataModel { 217 | pub fn clear(&mut self) { 218 | *self = StaticHuffmanDataModel::default(); 219 | } 220 | 221 | // pub fn init(&mut self, total_syms: u32, pCode_sizes: &[u8], mut code_size_limit: u32) -> bool{ 222 | // code_size_limit = min(code_size_limit, cMaxExpectedCodeSize as u32); 223 | // self.m_code_sizes.resize(total_syms as usize, 0 as u8); 224 | // let mut min_code_size = u32::MAX; 225 | // let mut max_code_size = 0; 226 | // for i in 0..total_syms as usize{ 227 | // let s: u32 = pCode_sizes[i] as u32; 228 | // self.m_code_sizes[i] = s as u8; 229 | // min_code_size = min(min_code_size, s); 230 | // max_code_size = max(max_code_size, s); 231 | // } 232 | // if max_code_size < (1 as u32) || max_code_size > (32 as u32) || min_code_size > code_size_limit { 233 | // return false; 234 | // } 235 | // if max_code_size > code_size_limit { 236 | // return false; 237 | // } 238 | // let table_bits = self.compute_decoder_table_bits(); 239 | // if self.m_pDecode_tables.init(self.m_total_syms, &self.m_code_sizes, table_bits) == false {a 240 | // return false; 241 | // } 242 | // return true; 243 | // } 244 | 245 | pub fn compute_decoder_table_bits(&mut self) -> u32 { 246 | let mut decoder_table_bits: u32 = 0; 247 | if self.total_syms > 16 { 248 | decoder_table_bits = min(1 + ceil_log2i(self.total_syms), MAX_TABLE_BITS as u32); 249 | } 250 | decoder_table_bits 251 | } 252 | 253 | pub fn prepare_decoder_tables(&mut self) -> bool { 254 | let total_syms = self.code_sizes.len(); 255 | if !(total_syms >= 1 && total_syms as u32 <= MAX_SUPPORTED_SYMS as u32) { 256 | return false; 257 | } 258 | self.total_syms = total_syms as u32; 259 | let table_bits = self.compute_decoder_table_bits(); 260 | self.p_decode_tables 261 | .init(self.total_syms, &self.code_sizes, table_bits) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /bindings/python/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | type DecodeFunction = fn(&[u8], usize, usize, &mut [u32]) -> Result<(), &'static str>; 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | // benchmarking 8 | use std::time::Instant; 9 | // path resolving 10 | use std::{ 11 | fs, 12 | path::{Path, PathBuf}, 13 | }; 14 | // decoder function import 15 | use texture2ddecoder::*; 16 | 17 | use crate::DecodeFunction; 18 | // image saving 19 | extern crate image; 20 | // texture file decoder 21 | extern crate ddsfile; 22 | extern crate ktx2; 23 | 24 | #[test] 25 | fn test_ATC_RGB() { 26 | test_format("ATC_RGB", "dds", decode_atc_rgb4) 27 | } 28 | 29 | #[test] 30 | fn test_ATC_RGBA_Explicit() { 31 | test_format("ATC_RGBA_Explicit", "dds", decode_atc_rgba8) 32 | } 33 | 34 | #[test] 35 | fn test_ATC_RGBA_Interpolated() { 36 | test_format("ATC_RGBA_Interpolated", "dds", decode_atc_rgba8) 37 | } 38 | 39 | #[test] 40 | fn test_ASTC_4x4() { 41 | test_format("ASTC_4x4", "ktx2", decode_astc_4_4) 42 | } 43 | 44 | #[test] 45 | fn test_ASTC_5x4() { 46 | test_format("ASTC_5x4", "ktx2", decode_astc_5_4) 47 | } 48 | 49 | #[test] 50 | fn test_ASTC_5x5() { 51 | test_format("ASTC_5x5", "ktx2", decode_astc_5_5) 52 | } 53 | 54 | #[test] 55 | fn test_ASTC_6x5() { 56 | test_format("ASTC_6x5", "ktx2", decode_astc_6_5) 57 | } 58 | 59 | #[test] 60 | fn test_ASTC_6x6() { 61 | test_format("ASTC_6x6", "ktx2", decode_astc_6_6) 62 | } 63 | 64 | #[test] 65 | fn test_ASTC_8x5() { 66 | test_format("ASTC_8x5", "ktx2", decode_astc_8_5) 67 | } 68 | 69 | #[test] 70 | fn test_ASTC_8x6() { 71 | test_format("ASTC_8x6", "ktx2", decode_astc_8_6) 72 | } 73 | 74 | #[test] 75 | fn test_ASTC_8x8() { 76 | test_format("ASTC_8x8", "ktx2", decode_astc_8_8) 77 | } 78 | 79 | #[test] 80 | fn test_BC1() { 81 | test_format("BC1", "ktx2", decode_bc1) 82 | } 83 | 84 | #[test] 85 | fn test_BC1A() { 86 | test_format("BC1A", "ktx2", decode_bc1a) 87 | } 88 | 89 | #[test] 90 | fn test_BC2() { 91 | test_format("BC2", "ktx2", decode_bc2) 92 | } 93 | 94 | #[test] 95 | fn test_BC3() { 96 | test_format("BC3", "ktx2", decode_bc3) 97 | } 98 | 99 | #[test] 100 | fn test_BC4() { 101 | test_format("BC4", "ktx2", decode_bc4) 102 | } 103 | 104 | #[test] 105 | fn test_BC5() { 106 | test_format("BC5", "ktx2", decode_bc5) 107 | } 108 | 109 | #[test] 110 | fn test_BC6H() { 111 | test_format("BC6H", "ktx2", decode_bc6_unsigned) 112 | } 113 | 114 | #[test] 115 | fn test_BC7() { 116 | test_format("BC7", "ktx2", decode_bc7) 117 | } 118 | 119 | #[test] 120 | fn test_ETC1_RGB() { 121 | test_format("ETC1_RGB", "ktx2", decode_etc1) 122 | } 123 | 124 | #[test] 125 | fn test_ETC2_RGB() { 126 | test_format("ETC2_RGB", "ktx2", decode_etc2_rgb) 127 | } 128 | 129 | #[test] 130 | fn test_ETC2_RGBA() { 131 | test_format("ETC2_RGBA", "ktx2", decode_etc2_rgba8) 132 | } 133 | 134 | #[test] 135 | fn test_ETC2_RGB_A1() { 136 | test_format("ETC2_RGB_A1", "ktx2", decode_etc2_rgba1) 137 | } 138 | 139 | #[test] 140 | fn test_PVRTCI_2bpp_RGB() { 141 | test_format("PVRTCI_2bpp_RGB", "ktx2", decode_pvrtc_2bpp) 142 | } 143 | 144 | #[test] 145 | fn test_PVRTCI_2bpp_RGBA() { 146 | test_format("PVRTCI_2bpp_RGBA", "ktx2", decode_pvrtc_2bpp) 147 | } 148 | 149 | #[test] 150 | fn test_PVRTCI_4bpp_RGB() { 151 | test_format("PVRTCI_4bpp_RGB", "ktx2", decode_pvrtc_4bpp) 152 | } 153 | 154 | #[test] 155 | fn test_PVRTCI_4bpp_RGBA() { 156 | test_format("PVRTCI_4bpp_RGBA", "ktx2", decode_pvrtc_4bpp) 157 | } 158 | 159 | #[test] 160 | fn test_EAC_R11() { 161 | test_format("EAC_R11", "ktx2", decode_eacr) 162 | } 163 | 164 | #[test] 165 | fn test_EAC_RG11() { 166 | test_format("EAC_RG11", "ktx2", decode_eacrg) 167 | } 168 | 169 | #[test] 170 | fn test_CRUNCH_DXT1() { 171 | test_format("CRUNCH_DXT1", "crn", decode_crunch) 172 | } 173 | 174 | #[test] 175 | fn test_CRUNCH_DXT5() { 176 | test_format("CRUNCH_DXT5", "crn", decode_crunch) 177 | } 178 | 179 | #[test] 180 | fn test_CRUNCH_DXT5A() { 181 | test_format("CRUNCH_DXT5A", "crn", decode_crunch) 182 | } 183 | 184 | #[test] 185 | fn test_CRUNCH_DXN() { 186 | test_format("CRUNCH_DXN", "crn", decode_crunch) 187 | } 188 | 189 | #[test] 190 | fn test_UNITYCRUNCH_DXT1() { 191 | test_format("UNITYCRUNCH_DXT1", "crn", decode_unity_crunch) 192 | } 193 | 194 | #[test] 195 | fn test_UNITYCRUNCH_DXT5() { 196 | test_format("UNITYCRUNCH_DXT5", "crn", decode_unity_crunch) 197 | } 198 | 199 | #[test] 200 | fn test_UNITYCRUNCH_DXT5A() { 201 | test_format("UNITYCRUNCH_DXT5A", "crn", decode_unity_crunch) 202 | } 203 | 204 | #[test] 205 | fn test_UNITYCRUNCH_DXN() { 206 | test_format("UNITYCRUNCH_DXN", "crn", decode_unity_crunch) 207 | } 208 | 209 | #[test] 210 | fn test_UNITYCRUNCH_ETC1() { 211 | test_format("UNITYCRUNCH_ETC1", "crn", decode_unity_crunch) 212 | } 213 | 214 | #[test] 215 | fn test_UNITYCRUNCH_ETC1S() { 216 | test_format("UNITYCRUNCH_ETC1S", "crn", decode_unity_crunch) 217 | } 218 | 219 | #[test] 220 | fn test_UNITYCRUNCH_ETC2() { 221 | test_format("UNITYCRUNCH_ETC2", "crn", decode_unity_crunch) 222 | } 223 | 224 | #[test] 225 | fn test_UNITYCRUNCH_ETC2A() { 226 | test_format("UNITYCRUNCH_ETC2A", "crn", decode_unity_crunch) 227 | } 228 | 229 | #[test] 230 | fn test_UNITYCRUNCH_ETC2AS() { 231 | test_format("UNITYCRUNCH_ETC2AS", "crn", decode_unity_crunch) 232 | } 233 | 234 | // helper structs and functions 235 | struct Texture { 236 | width: u32, 237 | height: u32, 238 | data: Vec, 239 | } 240 | 241 | impl Texture { 242 | fn new(width: u32, height: u32, data: Vec) -> Self { 243 | Self { 244 | width, 245 | height, 246 | data, 247 | } 248 | } 249 | 250 | fn from_file(fp: &str) -> Texture { 251 | let extension = Path::new(fp).extension().unwrap().to_str().unwrap(); 252 | match extension { 253 | "ktx2" | "KTX2" => Texture::from_ktx2_file(fp), 254 | "dds" | "DDS" => Texture::from_dds_file(fp), 255 | "crn" | "CRN" => Texture::from_crn_file(fp), 256 | _ => panic!("Unsupported file format"), 257 | } 258 | } 259 | 260 | fn from_ktx2_file(fp: &str) -> Texture { 261 | let ktx2_data = fs::read(fp).unwrap(); 262 | let reader = ktx2::Reader::new(ktx2_data).expect("Can't create reader"); // Crate instance of reader. 263 | 264 | // Get general texture information. 265 | let header = reader.header(); 266 | 267 | // Read iterator over slices of each mipmap level. 268 | let levels = reader.levels().collect::>(); 269 | 270 | Texture::new(header.pixel_width, header.pixel_height, levels[0].to_vec()) 271 | } 272 | 273 | fn from_dds_file(fp: &str) -> Texture { 274 | let dds_data = fs::read(fp).unwrap(); 275 | let dds = ddsfile::Dds::read(&dds_data[..]).unwrap(); 276 | 277 | Texture::new(dds.header.width, dds.header.height, dds.data) 278 | } 279 | 280 | fn from_crn_file(fp: &str) -> Texture { 281 | let crn_data = fs::read(fp).unwrap(); 282 | let mut tex_info = CrnTextureInfo::default(); 283 | tex_info.crnd_get_texture_info(&crn_data, crn_data.len() as u32); 284 | Texture::new( 285 | core::cmp::max(1, tex_info.width), 286 | core::cmp::max(1, tex_info.height), 287 | crn_data, 288 | ) 289 | } 290 | 291 | fn _decode(&self, decode_func: DecodeFunction) -> Vec { 292 | let mut image: Vec = vec![0; (self.width * self.height) as usize]; 293 | let start = Instant::now(); 294 | decode_func( 295 | &self.data, 296 | self.width as usize, 297 | self.height as usize, 298 | &mut image, 299 | ) 300 | .unwrap(); 301 | let duration = start.elapsed(); 302 | println!("Time elapsed in decoding is: {:?}", duration); 303 | image 304 | } 305 | fn save_as_image(&self, path: &str, decode_func: DecodeFunction) { 306 | let image = self._decode(decode_func); 307 | let image_buf = image 308 | .iter() 309 | .flat_map(|x| { 310 | let v = x.to_le_bytes(); 311 | [v[2], v[1], v[0], v[3]] 312 | }) 313 | .collect::>(); 314 | 315 | image::save_buffer( 316 | Path::new(path), 317 | &image_buf, 318 | self.width, 319 | self.height, 320 | image::ColorType::Rgba8, 321 | ) 322 | .unwrap(); 323 | } 324 | } 325 | 326 | fn get_texture_fp(name: &str) -> String { 327 | let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 328 | d.push("resources/tests/textures"); 329 | d.push(name); 330 | d.to_str().unwrap().to_string() 331 | } 332 | 333 | fn get_image_fp(name: &str) -> String { 334 | let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 335 | d.push("resources/tests/decompressed"); 336 | fs::create_dir_all(&d).unwrap(); 337 | d.push(name); 338 | d.to_str().unwrap().to_string() 339 | } 340 | 341 | fn test_format(name: &str, sample_extension: &str, decode_func: DecodeFunction) { 342 | println!("Testing {}", name); 343 | let src_fp = get_texture_fp(&format!("{}.{}", name, sample_extension)); 344 | let dst_fp = get_image_fp(&format!("{}.png", name)); 345 | 346 | let texture = Texture::from_file(&src_fp); 347 | texture.save_as_image(&dst_fp, decode_func); 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /src/crunch/crn_symbol_codec.rs: -------------------------------------------------------------------------------- 1 | use super::crn_consts::*; 2 | use super::crn_static_huffman_data_model::*; 3 | use super::crn_utils::*; 4 | 5 | #[allow(non_camel_case_types)] 6 | pub struct symbol_codec<'slice> { 7 | pub p_decode_buf: &'slice [u8], 8 | pub p_decode_buf_next: &'slice [u8], 9 | pub p_decode_buf_end: *const u8, 10 | pub decode_buf_size: u32, 11 | pub bit_buf: u32, 12 | pub bit_count: i32, 13 | } 14 | 15 | impl Default for symbol_codec<'_> { 16 | fn default() -> Self { 17 | symbol_codec { 18 | p_decode_buf: &[0; 0], 19 | p_decode_buf_next: &[0; 0], 20 | p_decode_buf_end: core::ptr::null(), 21 | decode_buf_size: 0, 22 | bit_buf: 0, 23 | bit_count: 0, 24 | } 25 | } 26 | } 27 | 28 | impl<'slice> symbol_codec<'slice> { 29 | pub fn start_decoding(&mut self, p_buf: &'slice [u8], buf_size: u32) -> bool { 30 | if buf_size == 0 { 31 | return false; 32 | } 33 | self.p_decode_buf = p_buf; 34 | self.p_decode_buf_next = p_buf; 35 | self.decode_buf_size = buf_size; 36 | self.p_decode_buf_end = (&p_buf[(buf_size - 1) as usize]) as *const u8; 37 | self.get_bits_init(); 38 | true 39 | } 40 | 41 | pub fn decode_receive_static_data_model(&mut self, model: &mut StaticHuffmanDataModel) -> bool { 42 | let total_used_syms = match self.decode_bits(total_bits(MAX_SUPPORTED_SYMS as u32)) { 43 | Ok(total_used_syms) => total_used_syms, 44 | Err(_) => return false, 45 | }; 46 | 47 | if total_used_syms == 0 { 48 | model.clear(); 49 | } 50 | 51 | model.code_sizes.resize(total_used_syms as usize, 0); 52 | 53 | let num_codelength_codes_to_send = match self.decode_bits(5) { 54 | Ok(num_codelength_codes_to_send) => num_codelength_codes_to_send, 55 | Err(_) => return false, 56 | }; 57 | 58 | if (num_codelength_codes_to_send < 1) 59 | || (num_codelength_codes_to_send > MAX_CODELENGTH_CODES as u32) 60 | { 61 | return false; 62 | } 63 | 64 | let mut dm = StaticHuffmanDataModel::default(); 65 | dm.code_sizes.resize(MAX_CODELENGTH_CODES, 0); 66 | 67 | for &code_length_code in MOST_PROBABLE_CODELENGTH_CODES 68 | .iter() 69 | .take(num_codelength_codes_to_send as usize) 70 | { 71 | dm.code_sizes[code_length_code as usize] = match self.decode_bits(3) { 72 | Ok(s) => s as u8, 73 | Err(_) => return false, 74 | }; 75 | } 76 | 77 | if !dm.prepare_decoder_tables() { 78 | return false; 79 | } 80 | 81 | let mut ofs: u32 = 0; 82 | while ofs < total_used_syms { 83 | let num_remaining: u32 = total_used_syms - ofs; 84 | 85 | let code: usize = match self.decode(&dm) { 86 | Ok(s) => s as usize, 87 | Err(_) => return false, 88 | }; 89 | 90 | if code <= 16 { 91 | model.code_sizes[ofs as usize] = code as u8; 92 | ofs += 1; 93 | } else if code == SMALL_ZERO_RUN_CODE { 94 | let len = match self.decode_bits(SMALL_ZERO_RUN_EXTRA_BITS as u32) { 95 | Ok(s) => s, 96 | Err(_) => return false, 97 | } + MIN_SMALL_ZERO_RUN_SIZE as u32; 98 | 99 | if len > num_remaining { 100 | return false; 101 | } 102 | 103 | ofs += len; 104 | } else if code == LARGE_ZERO_RUN_CODE { 105 | let len = match self.decode_bits(LARGE_ZERO_RUN_EXTRA_BITS as u32) { 106 | Ok(s) => s, 107 | Err(_) => return false, 108 | } + MIN_LARGE_ZERO_RUN_SIZE as u32; 109 | 110 | if len > num_remaining { 111 | return false; 112 | } 113 | 114 | ofs += len; 115 | } else if (code == SMALL_REPEAT_CODE) || (code == LARGE_REPEAT_CODE) { 116 | let len: u32; 117 | 118 | if code == SMALL_REPEAT_CODE { 119 | match self.decode_bits(SMALL_NON_ZERO_RUN_EXTRA_BITS as u32) { 120 | Ok(s) => { 121 | len = s + SMALL_MIN_NON_ZERO_RUN_SIZE as u32; 122 | } 123 | Err(_) => return false, 124 | }; 125 | } else { 126 | match self.decode_bits(LARGE_NON_ZERO_RUN_EXTRA_BITS as u32) { 127 | Ok(s) => { 128 | len = s + LARGE_MIN_NON_ZERO_RUN_SIZE as u32; 129 | } 130 | Err(_) => return false, 131 | }; 132 | } 133 | 134 | if ofs == 0 || len > num_remaining { 135 | return false; 136 | } 137 | 138 | let prev: u32 = model.code_sizes[(ofs - 1) as usize] as u32; 139 | if prev == 0 { 140 | return false; 141 | } 142 | 143 | let end = ofs + len; 144 | while ofs < end { 145 | model.code_sizes[ofs as usize] = prev as u8; 146 | ofs += 1; 147 | } 148 | } else { 149 | return false; 150 | } 151 | } 152 | model.prepare_decoder_tables() 153 | } 154 | 155 | pub fn decode_bits(&mut self, num_bits: u32) -> Result { 156 | if num_bits == 0_u32 { 157 | return Ok(0_u32); 158 | } 159 | if num_bits > 16_u32 { 160 | let a = match self.get_bits(num_bits - 16) { 161 | Ok(s) => s, 162 | Err(_) => return Err(false), 163 | }; 164 | let b = match self.get_bits(16) { 165 | Ok(s) => s, 166 | Err(_) => return Err(false), 167 | }; 168 | Ok((a << 16) | b) 169 | } else { 170 | self.get_bits(num_bits) 171 | } 172 | } 173 | 174 | pub fn decode(&mut self, model: &StaticHuffmanDataModel) -> Result { 175 | let p_tables = &model.p_decode_tables; 176 | if self.bit_count < 24 { 177 | if self.bit_count < 16 { 178 | let mut c0: u32 = 0; 179 | let mut c1: u32 = 0; 180 | let mut p = self.p_decode_buf_next; 181 | if (&(p[0]) as *const u8) <= self.p_decode_buf_end { 182 | c0 = p[0] as u32; 183 | if (&(p[0]) as *const u8) < self.p_decode_buf_end { 184 | p = &p[1..] 185 | } 186 | }; 187 | if (&(p[0]) as *const u8) <= self.p_decode_buf_end { 188 | c1 = p[0] as u32; 189 | if (&(p[0]) as *const u8) < self.p_decode_buf_end { 190 | p = &p[1..] 191 | } 192 | }; 193 | self.p_decode_buf_next = p; 194 | self.bit_count += 16; 195 | let c: u32 = (c0 << 8) | c1; 196 | self.bit_buf |= c << (32 - self.bit_count); 197 | } else { 198 | let c: u32; 199 | if (&(self.p_decode_buf_next[0]) as *const u8) <= self.p_decode_buf_end { 200 | c = self.p_decode_buf_next[0] as u32; 201 | if (&(self.p_decode_buf_next[0]) as *const u8) < self.p_decode_buf_end { 202 | self.p_decode_buf_next = &self.p_decode_buf_next[1..]; 203 | } 204 | } else { 205 | c = 0 206 | }; 207 | self.bit_count += 8; 208 | self.bit_buf |= c << (32 - self.bit_count); 209 | } 210 | } 211 | let k: u32 = (self.bit_buf >> 16) + 1; 212 | let sym: u32; 213 | let mut len: u32; 214 | if k <= p_tables.table_max_code { 215 | let t = p_tables.lookup[(self.bit_buf >> (32 - p_tables.table_bits)) as usize]; 216 | if t == u32::MAX { 217 | return Err(false); 218 | } 219 | sym = t & (u16::MAX as u32); 220 | len = t >> 16; 221 | if model.code_sizes[sym as usize] as u32 != len { 222 | return Err(false); 223 | } 224 | } else { 225 | len = p_tables.decode_start_code_size; 226 | loop { 227 | if k <= p_tables.max_codes[(len - 1) as usize] { 228 | break; 229 | } 230 | len += 1; 231 | } 232 | let val_ptr: i32 = 233 | p_tables.val_ptrs[(len - 1) as usize] + (self.bit_buf >> (32 - len)) as i32; 234 | if (val_ptr as u32) >= model.total_syms { 235 | return Err(false); 236 | } 237 | sym = p_tables.sorted_symbol_order[val_ptr as usize] as u32; 238 | } 239 | self.bit_buf <<= len; 240 | self.bit_count -= len as i32; 241 | Ok(sym) 242 | } 243 | 244 | pub fn stop_decoding(&mut self) {} 245 | 246 | fn get_bits_init(&mut self) { 247 | self.bit_buf = 0; 248 | self.bit_count = 0; 249 | } 250 | 251 | fn get_bits(&mut self, num_bits: u32) -> Result { 252 | if num_bits > 32 { 253 | return Err(false); 254 | } 255 | while self.bit_count < num_bits as i32 { 256 | let mut c: u32 = 0; 257 | if self.p_decode_buf_next[0] as *const u8 <= self.p_decode_buf_end { 258 | c = self.p_decode_buf_next[0] as u32; 259 | if (self.p_decode_buf_next[0] as *const u8) < self.p_decode_buf_end { 260 | self.p_decode_buf_next = &self.p_decode_buf_next[1..]; 261 | } 262 | } 263 | self.bit_count += 8; 264 | if self.bit_count > BIT_BUF_SIZE as i32 { 265 | return Err(false); 266 | } 267 | self.bit_buf |= c << (BIT_BUF_SIZE - self.bit_count as usize); 268 | } 269 | let result: u32 = self.bit_buf >> (BIT_BUF_SIZE - num_bits as usize); 270 | self.bit_buf <<= num_bits; 271 | self.bit_count -= num_bits as i32; 272 | Ok(result) 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/bcn/consts.rs: -------------------------------------------------------------------------------- 1 | // BC 6 & 7 2 | pub(crate) static S_BPTC_A2: [usize; 64] = [ 3 | 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 8, 2, 2, 8, 8, 15, 2, 8, 4 | 2, 2, 8, 8, 2, 2, 15, 15, 6, 8, 2, 8, 15, 15, 2, 8, 2, 2, 2, 15, 15, 6, 6, 2, 6, 8, 15, 15, 2, 5 | 2, 15, 15, 15, 15, 15, 2, 2, 15, 6 | ]; 7 | 8 | // BC7 9 | pub(crate) static S_BPTC_A3: [[usize; 64]; 2] = [ 10 | [ 11 | 3, 3, 15, 15, 8, 3, 15, 15, 8, 8, 6, 6, 6, 5, 3, 3, 3, 3, 8, 15, 3, 3, 6, 10, 5, 8, 8, 6, 12 | 8, 5, 15, 15, 8, 15, 3, 5, 6, 10, 8, 15, 15, 3, 15, 5, 15, 15, 15, 15, 3, 15, 5, 5, 5, 8, 13 | 5, 10, 5, 10, 8, 13, 15, 12, 3, 3, 14 | ], 15 | [ 16 | 15, 8, 8, 3, 15, 15, 3, 8, 15, 15, 15, 15, 15, 15, 15, 8, 15, 8, 15, 3, 15, 8, 15, 8, 3, 17 | 15, 6, 10, 15, 15, 10, 8, 15, 3, 15, 10, 10, 8, 9, 10, 6, 15, 8, 15, 3, 6, 6, 8, 15, 3, 15, 18 | 15, 15, 15, 15, 15, 15, 15, 15, 15, 3, 15, 15, 8, 19 | ], 20 | ]; 21 | 22 | // BC 6 & 7 23 | pub(crate) static S_BPTC_FACTORS: [[u8; 16]; 3] = [ 24 | [0, 21, 43, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 25 | [0, 9, 18, 27, 37, 46, 55, 64, 0, 0, 0, 0, 0, 0, 0, 0], 26 | [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64], 27 | ]; 28 | 29 | // BC 6 & 7 30 | pub(crate) static S_BPTC_P2: [usize; 64] = [ 31 | // 3210 0000000000 1111111111 2222222222 3333333333 32 | 0xcccc, // 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 33 | 0x8888, // 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 34 | 0xeeee, // 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 35 | 0xecc8, // 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1 36 | 0xc880, // 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1 37 | 0xfeec, // 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 38 | 0xfec8, // 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 39 | 0xec80, // 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1 40 | 0xc800, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1 41 | 0xffec, // 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 42 | 0xfe80, // 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1 43 | 0xe800, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1 44 | 0xffe8, // 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 45 | 0xff00, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 46 | 0xfff0, // 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 47 | 0xf000, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 48 | 0xf710, // 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1 49 | 0x008e, // 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 50 | 0x7100, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0 51 | 0x08ce, // 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0 52 | 0x008c, // 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 53 | 0x7310, // 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0 54 | 0x3100, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 55 | 0x8cce, // 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1 56 | 0x088c, // 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 57 | 0x3110, // 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 58 | 0x6666, // 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0 59 | 0x366c, // 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0 60 | 0x17e8, // 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0 61 | 0x0ff0, // 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 62 | 0x718e, // 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0 63 | 0x399c, // 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0 64 | 0xaaaa, // 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 65 | 0xf0f0, // 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 66 | 0x5a5a, // 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0 67 | 0x33cc, // 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0 68 | 0x3c3c, // 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0 69 | 0x55aa, // 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0 70 | 0x9696, // 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1 71 | 0xa55a, // 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1 72 | 0x73ce, // 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0 73 | 0x13c8, // 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 74 | 0x324c, // 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0 75 | 0x3bdc, // 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 76 | 0x6996, // 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 77 | 0xc33c, // 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1 78 | 0x9966, // 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1 79 | 0x0660, // 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 80 | 0x0272, // 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 81 | 0x04e4, // 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 82 | 0x4e40, // 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0 83 | 0x2720, // 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0 84 | 0xc936, // 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1 85 | 0x936c, // 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1 86 | 0x39c6, // 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0 87 | 0x639c, // 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0 88 | 0x9336, // 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1 89 | 0x9cc6, // 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1 90 | 0x817e, // 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1 91 | 0xe718, // 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1 92 | 0xccf0, // 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 93 | 0x0fcc, // 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 94 | 0x7744, // 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0 95 | 0xee22, // 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1 96 | ]; 97 | 98 | // BC7 99 | pub(crate) static S_BPTC_P3: [usize; 64] = [ 100 | // 76543210 0000 1111 2222 3333 4444 5555 6666 7777 101 | 0xaa685050, // 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 1, 2, 2, 2, 2 102 | 0x6a5a5040, // 0, 0, 0, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1 103 | 0x5a5a4200, // 0, 0, 0, 0, 2, 0, 0, 1, 2, 2, 1, 1, 2, 2, 1, 1 104 | 0x5450a0a8, // 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0, 1, 1, 1 105 | 0xa5a50000, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2 106 | 0xa0a05050, // 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 2, 2 107 | 0x5555a0a0, // 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 108 | 0x5a5a5050, // 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1 109 | 0xaa550000, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2 110 | 0xaa555500, // 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2 111 | 0xaaaa5500, // 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2 112 | 0x90909090, // 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2 113 | 0x94949494, // 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2 114 | 0xa4a4a4a4, // 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2 115 | 0xa9a59450, // 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2 116 | 0x2a0a4250, // 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 2, 0 117 | 0xa5945040, // 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2 118 | 0x0a425054, // 0, 1, 1, 1, 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0 119 | 0xa5a5a500, // 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2 120 | 0x55a0a0a0, // 0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1 121 | 0xa8a85454, // 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2 122 | 0x6a6a4040, // 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 2, 1 123 | 0xa4a45000, // 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2 124 | 0x1a1a0500, // 0, 0, 0, 0, 1, 1, 0, 0, 2, 2, 1, 0, 2, 2, 1, 0 125 | 0x0050a4a4, // 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0 126 | 0xaaa59090, // 0, 0, 1, 2, 0, 0, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2 127 | 0x14696914, // 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 1, 1, 0 128 | 0x69691400, // 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1 129 | 0xa08585a0, // 0, 0, 2, 2, 1, 1, 0, 2, 1, 1, 0, 2, 0, 0, 2, 2 130 | 0xaa821414, // 0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 0, 2, 2, 2, 2, 2 131 | 0x50a4a450, // 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1 132 | 0x6a5a0200, // 0, 0, 0, 0, 2, 0, 0, 0, 2, 2, 1, 1, 2, 2, 2, 1 133 | 0xa9a58000, // 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 2, 2, 2 134 | 0x5090a0a8, // 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 2, 0, 0, 1, 1 135 | 0xa8a09050, // 0, 0, 1, 1, 0, 0, 1, 2, 0, 0, 2, 2, 0, 2, 2, 2 136 | 0x24242424, // 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0 137 | 0x00aa5500, // 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0 138 | 0x24924924, // 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0 139 | 0x24499224, // 0, 1, 2, 0, 2, 0, 1, 2, 1, 2, 0, 1, 0, 1, 2, 0 140 | 0x50a50a50, // 0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2, 0, 0, 1, 1 141 | 0x500aa550, // 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1 142 | 0xaaaa4444, // 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2 143 | 0x66660000, // 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1 144 | 0xa5a0a5a0, // 0, 0, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2, 1, 1, 2, 2 145 | 0x50a050a0, // 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1 146 | 0x69286928, // 0, 2, 2, 0, 1, 2, 2, 1, 0, 2, 2, 0, 1, 2, 2, 1 147 | 0x44aaaa44, // 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 1 148 | 0x66666600, // 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 149 | 0xaa444444, // 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2 150 | 0x54a854a8, // 0, 2, 2, 2, 0, 1, 1, 1, 0, 2, 2, 2, 0, 1, 1, 1 151 | 0x95809580, // 0, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2, 1, 1, 1, 2 152 | 0x96969600, // 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2 153 | 0xa85454a8, // 0, 2, 2, 2, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2 154 | 0x80959580, // 0, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2 155 | 0xaa141414, // 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2 156 | 0x96960000, // 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2 157 | 0xaaaa1414, // 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2 158 | 0xa05050a0, // 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2 159 | 0xa0a5a5a0, // 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2 160 | 0x96000000, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2 161 | 0x40804080, // 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1 162 | 0xa9a8a9a8, // 0, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2 163 | 0xaaaaaa44, // 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 164 | 0x2a4a5254, // 0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 2, 0 165 | ]; 166 | -------------------------------------------------------------------------------- /src/bcn/bc7.rs: -------------------------------------------------------------------------------- 1 | use crate::bcn::consts::{S_BPTC_A2, S_BPTC_A3, S_BPTC_FACTORS, S_BPTC_P2, S_BPTC_P3}; 2 | use crate::bitreader::BitReader; 3 | use crate::color::color; 4 | use core::mem::swap; 5 | 6 | struct Bc7ModeInfo { 7 | num_subsets: usize, 8 | partition_bits: usize, 9 | rotation_bits: usize, 10 | index_selection_bits: usize, 11 | color_bits: usize, 12 | alpha_bits: usize, 13 | endpoint_pbits: usize, 14 | shared_pbits: usize, 15 | index_bits: [usize; 2], 16 | } 17 | 18 | static S_BP7_MODE_INFO: [Bc7ModeInfo; 8] = [ 19 | // +---------------------------- num subsets 20 | // | +------------------------- partition bits 21 | // | | +---------------------- rotation bits 22 | // | | | +------------------- index selection bits 23 | // | | | | +---------------- color bits 24 | // | | | | | +------------- alpha bits 25 | // | | | | | | +---------- endpoint P-bits 26 | // | | | | | | | +------- shared P-bits 27 | // | | | | | | | | +-- 2x index bits 28 | // { 3, 4, 0, 0, 4, 0, 1, 0, { 3, 0 } }, // 0 29 | // { 2, 6, 0, 0, 6, 0, 0, 1, { 3, 0 } }, // 1 30 | // { 3, 6, 0, 0, 5, 0, 0, 0, { 2, 0 } }, // 2 31 | // { 2, 6, 0, 0, 7, 0, 1, 0, { 2, 0 } }, // 3 32 | // { 1, 0, 2, 1, 5, 6, 0, 0, { 2, 3 } }, // 4 33 | // { 1, 0, 2, 0, 7, 8, 0, 0, { 2, 2 } }, // 5 34 | // { 1, 0, 0, 0, 7, 7, 1, 0, { 4, 0 } }, // 6 35 | // { 2, 6, 0, 0, 5, 5, 1, 0, { 2, 0 } }, // 7 36 | Bc7ModeInfo { 37 | num_subsets: 3, 38 | partition_bits: 4, 39 | rotation_bits: 0, 40 | index_selection_bits: 0, 41 | color_bits: 4, 42 | alpha_bits: 0, 43 | endpoint_pbits: 1, 44 | shared_pbits: 0, 45 | index_bits: [3, 0], 46 | }, 47 | Bc7ModeInfo { 48 | num_subsets: 2, 49 | partition_bits: 6, 50 | rotation_bits: 0, 51 | index_selection_bits: 0, 52 | color_bits: 6, 53 | alpha_bits: 0, 54 | endpoint_pbits: 0, 55 | shared_pbits: 1, 56 | index_bits: [3, 0], 57 | }, 58 | Bc7ModeInfo { 59 | num_subsets: 3, 60 | partition_bits: 6, 61 | rotation_bits: 0, 62 | index_selection_bits: 0, 63 | color_bits: 5, 64 | alpha_bits: 0, 65 | endpoint_pbits: 0, 66 | shared_pbits: 0, 67 | index_bits: [2, 0], 68 | }, 69 | Bc7ModeInfo { 70 | num_subsets: 2, 71 | partition_bits: 6, 72 | rotation_bits: 0, 73 | index_selection_bits: 0, 74 | color_bits: 7, 75 | alpha_bits: 0, 76 | endpoint_pbits: 1, 77 | shared_pbits: 0, 78 | index_bits: [2, 0], 79 | }, 80 | Bc7ModeInfo { 81 | num_subsets: 1, 82 | partition_bits: 0, 83 | rotation_bits: 2, 84 | index_selection_bits: 1, 85 | color_bits: 5, 86 | alpha_bits: 6, 87 | endpoint_pbits: 0, 88 | shared_pbits: 0, 89 | index_bits: [2, 3], 90 | }, 91 | Bc7ModeInfo { 92 | num_subsets: 1, 93 | partition_bits: 0, 94 | rotation_bits: 2, 95 | index_selection_bits: 0, 96 | color_bits: 7, 97 | alpha_bits: 8, 98 | endpoint_pbits: 0, 99 | shared_pbits: 0, 100 | index_bits: [2, 2], 101 | }, 102 | Bc7ModeInfo { 103 | num_subsets: 1, 104 | partition_bits: 0, 105 | rotation_bits: 0, 106 | index_selection_bits: 0, 107 | color_bits: 7, 108 | alpha_bits: 7, 109 | endpoint_pbits: 1, 110 | shared_pbits: 0, 111 | index_bits: [4, 0], 112 | }, 113 | Bc7ModeInfo { 114 | num_subsets: 2, 115 | partition_bits: 6, 116 | rotation_bits: 0, 117 | index_selection_bits: 0, 118 | color_bits: 5, 119 | alpha_bits: 5, 120 | endpoint_pbits: 1, 121 | shared_pbits: 0, 122 | index_bits: [2, 0], 123 | }, 124 | ]; 125 | 126 | #[inline] 127 | fn expand_quantized(v: u8, bits: usize) -> u8 { 128 | let s = ((v as u16) << (8 - bits as u16)) as u8; 129 | s | s.overflowing_shr(bits as u32).0 130 | } 131 | 132 | pub fn decode_bc7_block(data: &[u8], outbuf: &mut [u32]) { 133 | let mut bit = BitReader::new(data, 0); 134 | let mode = { 135 | let mut mode = 0; 136 | while 0 == bit.read(1) && mode < 8 { 137 | mode += 1; 138 | } 139 | mode 140 | }; 141 | 142 | if mode == 8 { 143 | outbuf[0..16].fill(0); 144 | return; 145 | } 146 | 147 | let mi: &Bc7ModeInfo = &S_BP7_MODE_INFO[mode]; 148 | let mode_pbits: usize = if 0 != mi.endpoint_pbits { 149 | mi.endpoint_pbits 150 | } else { 151 | mi.shared_pbits 152 | }; 153 | 154 | let partition_set_idx: usize = bit.read(mi.partition_bits) as usize; 155 | let rotation_mode: u8 = bit.read(mi.rotation_bits) as u8; 156 | let index_selection_mode: usize = bit.read(mi.index_selection_bits) as usize; 157 | 158 | let mut ep_r: [u8; 6] = [0; 6]; 159 | let mut ep_g: [u8; 6] = [0; 6]; 160 | let mut ep_b: [u8; 6] = [0; 6]; 161 | let mut ep_a: [u8; 6] = [0; 6]; 162 | 163 | (0..mi.num_subsets).for_each(|ii| { 164 | ep_r[ii * 2] = (bit.read(mi.color_bits) << mode_pbits) as u8; 165 | ep_r[ii * 2 + 1] = (bit.read(mi.color_bits) << mode_pbits) as u8; 166 | }); 167 | 168 | (0..mi.num_subsets).for_each(|ii| { 169 | ep_g[ii * 2] = (bit.read(mi.color_bits) << mode_pbits) as u8; 170 | ep_g[ii * 2 + 1] = (bit.read(mi.color_bits) << mode_pbits) as u8; 171 | }); 172 | 173 | (0..mi.num_subsets).for_each(|ii| { 174 | ep_b[ii * 2] = (bit.read(mi.color_bits) << mode_pbits) as u8; 175 | ep_b[ii * 2 + 1] = (bit.read(mi.color_bits) << mode_pbits) as u8; 176 | }); 177 | 178 | if mi.alpha_bits > 0 { 179 | (0..mi.num_subsets).for_each(|ii| { 180 | ep_a[ii * 2] = (bit.read(mi.alpha_bits) << mode_pbits) as u8; 181 | ep_a[ii * 2 + 1] = (bit.read(mi.alpha_bits) << mode_pbits) as u8; 182 | }); 183 | } else { 184 | ep_a = [0xff; 6]; 185 | } 186 | 187 | if 0 != mode_pbits { 188 | (0..mi.num_subsets).for_each(|ii| { 189 | let pda: u8 = bit.read(mode_pbits) as u8; 190 | let pdb: u8 = if 0 == mi.shared_pbits { 191 | bit.read(mode_pbits) as u8 192 | } else { 193 | pda 194 | }; 195 | 196 | ep_r[ii * 2] |= pda; 197 | ep_r[ii * 2 + 1] |= pdb; 198 | ep_g[ii * 2] |= pda; 199 | ep_g[ii * 2 + 1] |= pdb; 200 | ep_b[ii * 2] |= pda; 201 | ep_b[ii * 2 + 1] |= pdb; 202 | ep_a[ii * 2] |= pda; 203 | ep_a[ii * 2 + 1] |= pdb; 204 | }); 205 | } 206 | 207 | let color_bits: usize = mi.color_bits + mode_pbits; 208 | 209 | (0..mi.num_subsets).for_each(|ii| { 210 | ep_r[ii * 2] = expand_quantized(ep_r[ii * 2], color_bits); 211 | ep_r[ii * 2 + 1] = expand_quantized(ep_r[ii * 2 + 1], color_bits); 212 | ep_g[ii * 2] = expand_quantized(ep_g[ii * 2], color_bits); 213 | ep_g[ii * 2 + 1] = expand_quantized(ep_g[ii * 2 + 1], color_bits); 214 | ep_b[ii * 2] = expand_quantized(ep_b[ii * 2], color_bits); 215 | ep_b[ii * 2 + 1] = expand_quantized(ep_b[ii * 2 + 1], color_bits); 216 | }); 217 | 218 | if mi.alpha_bits > 0 { 219 | let alpha_bits = mi.alpha_bits + mode_pbits; 220 | 221 | (0..mi.num_subsets).for_each(|ii| { 222 | ep_a[ii * 2] = expand_quantized(ep_a[ii * 2], alpha_bits); 223 | ep_a[ii * 2 + 1] = expand_quantized(ep_a[ii * 2 + 1], alpha_bits); 224 | }); 225 | } 226 | 227 | let has_index_bits1: bool = 0 != mi.index_bits[1]; 228 | 229 | let factors: [[u8; 16]; 2] = [ 230 | S_BPTC_FACTORS[mi.index_bits[0] - 2], 231 | if has_index_bits1 { 232 | S_BPTC_FACTORS[mi.index_bits[1] - 2] 233 | } else { 234 | S_BPTC_FACTORS[mi.index_bits[0] - 2] 235 | }, 236 | ]; 237 | 238 | let mut offset: [usize; 2] = [0, mi.num_subsets * (16 * mi.index_bits[0] - 1)]; 239 | 240 | (0..4_usize).for_each(|yy| { 241 | (0..4_usize).for_each(|xx| { 242 | let idx = yy * 4 + xx; 243 | 244 | let mut subset_index: usize = 0; 245 | let mut index_anchor: usize = 0; 246 | match mi.num_subsets { 247 | 2 => { 248 | subset_index = (S_BPTC_P2[partition_set_idx] >> idx) & 1; 249 | index_anchor = if 0 != subset_index { 250 | S_BPTC_A2[partition_set_idx] 251 | } else { 252 | 0 253 | }; 254 | } 255 | 3 => { 256 | subset_index = (S_BPTC_P3[partition_set_idx] >> (2 * idx)) & 3; 257 | index_anchor = if 0 != subset_index { 258 | S_BPTC_A3[subset_index - 1][partition_set_idx] 259 | } else { 260 | 0 261 | }; 262 | } 263 | _ => {} 264 | } 265 | 266 | let anchor = idx == index_anchor; 267 | let num: [usize; 2] = [ 268 | (mi.index_bits[0] - anchor as usize), 269 | if has_index_bits1 { 270 | mi.index_bits[1] - anchor as usize 271 | } else { 272 | 0 273 | }, 274 | ]; 275 | 276 | let index: [usize; 2] = { 277 | let index_0 = bit.peek(offset[0], num[0]) as usize; 278 | [ 279 | index_0, 280 | if has_index_bits1 { 281 | bit.peek(offset[1], num[1]) as usize 282 | } else { 283 | index_0 284 | }, 285 | ] 286 | }; 287 | 288 | offset[0] += num[0]; 289 | offset[1] += num[1]; 290 | 291 | // index selection mode 0 or 1 292 | // !index_selection_mode == 1-index_selection_mode 293 | let fc: u16 = factors[index_selection_mode][index[index_selection_mode]] as u16; 294 | let fa: u16 = factors[1 - index_selection_mode][index[1 - index_selection_mode]] as u16; 295 | 296 | let fca: u16 = 64 - fc; 297 | let fcb: u16 = fc; 298 | let faa: u16 = 64 - fa; 299 | let fab: u16 = fa; 300 | 301 | subset_index *= 2; 302 | let mut rr: u8 = 303 | ((ep_r[subset_index] as u16 * fca + ep_r[subset_index + 1] as u16 * fcb + 32) >> 6) 304 | as u8; 305 | let mut gg: u8 = 306 | ((ep_g[subset_index] as u16 * fca + ep_g[subset_index + 1] as u16 * fcb + 32) >> 6) 307 | as u8; 308 | let mut bb: u8 = 309 | ((ep_b[subset_index] as u16 * fca + ep_b[subset_index + 1] as u16 * fcb + 32) >> 6) 310 | as u8; 311 | let mut aa: u8 = 312 | ((ep_a[subset_index] as u16 * faa + ep_a[subset_index + 1] as u16 * fab + 32) >> 6) 313 | as u8; 314 | 315 | match rotation_mode { 316 | 1 => { 317 | swap(&mut aa, &mut rr); 318 | } 319 | 2 => { 320 | swap(&mut aa, &mut gg); 321 | } 322 | 3 => { 323 | swap(&mut aa, &mut bb); 324 | } 325 | _ => {} 326 | } 327 | outbuf[idx] = color(rr, gg, bb, aa); 328 | }); 329 | }); 330 | } 331 | -------------------------------------------------------------------------------- /src/etc/etc2.rs: -------------------------------------------------------------------------------- 1 | use crate::color::{color, TRANSPARENT_MASK, TRANSPARENT_SHIFT}; 2 | use crate::etc::consts::{ 3 | ETC1_MODIFIER_TABLE, ETC1_SUBBLOCK_TABLE, ETC2A_MODIFIER_TABLE, ETC2_ALPHA_MOD_TABLE, 4 | ETC2_DISTANCE_TABLE, WRITE_ORDER_TABLE, WRITE_ORDER_TABLE_REV, 5 | }; 6 | use crate::etc::etc1::{applicate_color, applicate_color_alpha, applicate_color_raw, clamp}; 7 | 8 | #[inline] 9 | pub fn decode_etc2_rgb_block(data: &[u8], outbuf: &mut [u32]) { 10 | let mut j: usize = u16::from_be_bytes([data[6], data[7]]) as usize; // 15 -> 0 11 | let mut k: usize = u16::from_be_bytes([data[4], data[5]]) as usize; // 31 -> 16 12 | let mut c: [[u8; 3]; 3] = [[0; 3]; 3]; 13 | 14 | if (data[3] & 2) > 0 { 15 | // diff bit == 1 16 | let r: i32 = (data[0] & 0xf8) as i32; 17 | let dr: i32 = ((data[0] as i32) << 3 & 0x18) - ((data[0] as i32) << 3 & 0x20); 18 | let g: i32 = (data[1] & 0xf8) as i32; 19 | let dg: i32 = ((data[1] as i32) << 3 & 0x18) - ((data[1] as i32) << 3 & 0x20); 20 | let b: i32 = (data[2] & 0xf8) as i32; 21 | let db: i32 = ((data[2] as i32) << 3 & 0x18) - ((data[2] as i32) << 3 & 0x20); 22 | if r + dr < 0 || r + dr > 255 { 23 | // T 24 | c[0][0] = (data[0] << 3 & 0xc0) 25 | | (data[0] << 4 & 0x30) 26 | | (data[0] >> 1 & 0xc) 27 | | (data[0] & 3); 28 | c[0][1] = (data[1] & 0xf0) | data[1] >> 4; 29 | c[0][2] = (data[1] & 0x0f) | data[1] << 4; 30 | c[1][0] = (data[2] & 0xf0) | data[2] >> 4; 31 | c[1][1] = (data[2] & 0x0f) | data[2] << 4; 32 | c[1][2] = (data[3] & 0xf0) | data[3] >> 4; 33 | let d: i16 = ETC2_DISTANCE_TABLE[((data[3] >> 1 & 6) | (data[3] & 1)) as usize]; 34 | let color_set: [u32; 4] = [ 35 | applicate_color_raw(c[0]), 36 | applicate_color(c[1], d), 37 | applicate_color_raw(c[1]), 38 | applicate_color(c[1], -d), 39 | ]; 40 | k <<= 1; 41 | for i in 0..16 { 42 | outbuf[WRITE_ORDER_TABLE[i]] = color_set[(k & 2) | (j & 1)]; 43 | j >>= 1; 44 | k >>= 1 45 | } 46 | } else if g + dg < 0 || g + dg > 255 { 47 | // H 48 | c[0][0] = (data[0] << 1 & 0xf0) | (data[0] >> 3 & 0xf); 49 | c[0][1] = (data[0] << 5 & 0xe0) | (data[1] & 0x10); 50 | c[0][1] |= c[0][1] >> 4; 51 | c[0][2] = (data[1] & 8) | (data[1] << 1 & 6) | data[2] >> 7; 52 | c[0][2] |= c[0][2] << 4; 53 | c[1][0] = (data[2] << 1 & 0xf0) | (data[2] >> 3 & 0xf); 54 | c[1][1] = (data[2] << 5 & 0xe0) | (data[3] >> 3 & 0x10); 55 | c[1][1] |= c[1][1] >> 4; 56 | c[1][2] = (data[3] << 1 & 0xf0) | (data[3] >> 3 & 0xf); 57 | let mut d: u8 = (data[3] & 4) | (data[3] << 1 & 2); 58 | if c[0][0] > c[1][0] 59 | || (c[0][0] == c[1][0] 60 | && (c[0][1] > c[1][1] || (c[0][1] == c[1][1] && c[0][2] >= c[1][2]))) 61 | { 62 | d += 1; 63 | } 64 | let d: i16 = ETC2_DISTANCE_TABLE[d as usize]; 65 | let color_set: [u32; 4] = [ 66 | applicate_color(c[0], d), 67 | applicate_color(c[0], -d), 68 | applicate_color(c[1], d), 69 | applicate_color(c[1], -d), 70 | ]; 71 | k <<= 1; 72 | for i in 0..16 { 73 | outbuf[WRITE_ORDER_TABLE[i]] = color_set[(k & 2) | (j & 1)]; 74 | j >>= 1; 75 | k >>= 1 76 | } 77 | } else if b + db < 0 || b + db > 255 { 78 | // planar 79 | c[0][0] = (data[0] << 1 & 0xfc) | (data[0] >> 5 & 3); 80 | c[0][1] = (data[0] << 7 & 0x80) | (data[1] & 0x7e) | (data[0] & 1); 81 | c[0][2] = (data[1] << 7 & 0x80) 82 | | (data[2] << 2 & 0x60) 83 | | (data[2] << 3 & 0x18) 84 | | (data[3] >> 5 & 4); 85 | c[0][2] |= c[0][2] >> 6; 86 | c[1][0] = (data[3] << 1 & 0xf8) | (data[3] << 2 & 4) | (data[3] >> 5 & 3); 87 | c[1][1] = (data[4] & 0xfe) | data[4] >> 7; 88 | c[1][2] = (data[4] << 7 & 0x80) | (data[5] >> 1 & 0x7c); 89 | c[1][2] |= c[1][2] >> 6; 90 | c[2][0] = (data[5] << 5 & 0xe0) | (data[6] >> 3 & 0x1c) | (data[5] >> 1 & 3); 91 | c[2][1] = (data[6] << 3 & 0xf8) | (data[7] >> 5 & 0x6) | (data[6] >> 4 & 1); 92 | c[2][2] = data[7] << 2 | (data[7] >> 4 & 3); 93 | let mut i: usize = 0; 94 | for y in 0..4 { 95 | for x in 0..4 { 96 | let r: u8 = clamp( 97 | (x * (c[1][0] as i32 - c[0][0] as i32) 98 | + y * (c[2][0] as i32 - c[0][0] as i32) 99 | + 4 * c[0][0] as i32 100 | + 2) 101 | >> 2, 102 | ); 103 | let g: u8 = clamp( 104 | (x * (c[1][1] as i32 - c[0][1] as i32) 105 | + y * (c[2][1] as i32 - c[0][1] as i32) 106 | + 4 * c[0][1] as i32 107 | + 2) 108 | >> 2, 109 | ); 110 | let b: u8 = clamp( 111 | (x * (c[1][2] as i32 - c[0][2] as i32) 112 | + y * (c[2][2] as i32 - c[0][2] as i32) 113 | + 4 * c[0][2] as i32 114 | + 2) 115 | >> 2, 116 | ); 117 | outbuf[i] = color(r, g, b, 255); 118 | i += 1; 119 | } 120 | } 121 | } else { 122 | // differential 123 | let code: [u8; 2] = [(data[3] >> 5), (data[3] >> 2 & 7)]; 124 | let table = ETC1_SUBBLOCK_TABLE[(data[3] & 1) as usize]; 125 | c[0][0] = (r | r >> 5) as u8; 126 | c[0][1] = (g | g >> 5) as u8; 127 | c[0][2] = (b | b >> 5) as u8; 128 | c[1][0] = (r + dr) as u8; 129 | c[1][1] = (g + dg) as u8; 130 | c[1][2] = (b + db) as u8; 131 | c[1][0] |= c[1][0] >> 5; 132 | c[1][1] |= c[1][1] >> 5; 133 | c[1][2] |= c[1][2] >> 5; 134 | for i in 0..16 { 135 | let s: usize = table[i]; 136 | let m: i16 = ETC1_MODIFIER_TABLE[code[s] as usize][j & 1]; 137 | outbuf[WRITE_ORDER_TABLE[i]] = 138 | applicate_color(c[s], if k & 1 > 0 { -m } else { m }); 139 | j >>= 1; 140 | k >>= 1; 141 | } 142 | } 143 | } else { 144 | // individual (diff bit == 0) 145 | let code: [u8; 2] = [(data[3] >> 5), (data[3] >> 2 & 7)]; 146 | let table = ETC1_SUBBLOCK_TABLE[(data[3] & 1) as usize]; 147 | c[0][0] = (data[0] & 0xf0) | data[0] >> 4; 148 | c[1][0] = (data[0] & 0x0f) | data[0] << 4; 149 | c[0][1] = (data[1] & 0xf0) | data[1] >> 4; 150 | c[1][1] = (data[1] & 0x0f) | data[1] << 4; 151 | c[0][2] = (data[2] & 0xf0) | data[2] >> 4; 152 | c[1][2] = (data[2] & 0x0f) | data[2] << 4; 153 | for i in 0..16 { 154 | let s: usize = table[i]; 155 | let m: i16 = ETC1_MODIFIER_TABLE[code[s] as usize][j & 1]; 156 | outbuf[WRITE_ORDER_TABLE[i]] = applicate_color(c[s], if k & 1 > 0 { -m } else { m }); 157 | j >>= 1; 158 | k >>= 1; 159 | } 160 | } 161 | } 162 | 163 | #[inline] 164 | pub fn decode_etc2_rgba1_block(data: &[u8], outbuf: &mut [u32]) { 165 | let mut j: usize = u16::from_be_bytes([data[6], data[7]]) as usize; // 15 -> 0 166 | let mut k: usize = u16::from_be_bytes([data[4], data[5]]) as usize; // 31 -> 16 167 | let mut c: [[u8; 3]; 3] = [[0; 3]; 3]; 168 | 169 | let obaq: bool = (data[3] >> 1 & 1) > 0; 170 | 171 | // diff bit == 1 172 | let r: i32 = (data[0] & 0xf8) as i32; 173 | let dr: i32 = ((data[0] as i32) << 3 & 0x18) - ((data[0] as i32) << 3 & 0x20); 174 | let g: i32 = (data[1] & 0xf8) as i32; 175 | let dg: i32 = ((data[1] as i32) << 3 & 0x18) - ((data[1] as i32) << 3 & 0x20); 176 | let b: i32 = (data[2] & 0xf8) as i32; 177 | let db: i32 = ((data[2] as i32) << 3 & 0x18) - ((data[2] as i32) << 3 & 0x20); 178 | if r + dr < 0 || r + dr > 255 { 179 | // T 180 | c[0][0] = 181 | (data[0] << 3 & 0xc0) | (data[0] << 4 & 0x30) | (data[0] >> 1 & 0xc) | (data[0] & 3); 182 | c[0][1] = (data[1] & 0xf0) | data[1] >> 4; 183 | c[0][2] = (data[1] & 0x0f) | data[1] << 4; 184 | c[1][0] = (data[2] & 0xf0) | data[2] >> 4; 185 | c[1][1] = (data[2] & 0x0f) | data[2] << 4; 186 | c[1][2] = (data[3] & 0xf0) | data[3] >> 4; 187 | let d: i16 = ETC2_DISTANCE_TABLE[((data[3] >> 1 & 6) | (data[3] & 1)) as usize]; 188 | let color_set: [u32; 4] = [ 189 | applicate_color_raw(c[0]), 190 | applicate_color(c[1], d), 191 | applicate_color_raw(c[1]), 192 | applicate_color(c[1], -d), 193 | ]; 194 | k <<= 1; 195 | for i in 0..16 { 196 | let index = (k & 2) | (j & 1); 197 | outbuf[WRITE_ORDER_TABLE[i]] = color_set[index]; 198 | if !obaq && index == 2 { 199 | outbuf[WRITE_ORDER_TABLE[i]] &= TRANSPARENT_MASK; 200 | } 201 | j >>= 1; 202 | k >>= 1; 203 | } 204 | } else if g + dg < 0 || g + dg > 255 { 205 | // H 206 | c[0][0] = (data[0] << 1 & 0xf0) | (data[0] >> 3 & 0xf); 207 | c[0][1] = (data[0] << 5 & 0xe0) | (data[1] & 0x10); 208 | c[0][1] |= c[0][1] >> 4; 209 | c[0][2] = (data[1] & 8) | (data[1] << 1 & 6) | data[2] >> 7; 210 | c[0][2] |= c[0][2] << 4; 211 | c[1][0] = (data[2] << 1 & 0xf0) | (data[2] >> 3 & 0xf); 212 | c[1][1] = (data[2] << 5 & 0xe0) | (data[3] >> 3 & 0x10); 213 | c[1][1] |= c[1][1] >> 4; 214 | c[1][2] = (data[3] << 1 & 0xf0) | (data[3] >> 3 & 0xf); 215 | let mut d: u8 = (data[3] & 4) | (data[3] << 1 & 2); 216 | if c[0][0] > c[1][0] 217 | || (c[0][0] == c[1][0] 218 | && (c[0][1] > c[1][1] || (c[0][1] == c[1][1] && c[0][2] >= c[1][2]))) 219 | { 220 | d += 1; 221 | } 222 | let d = ETC2_DISTANCE_TABLE[d as usize]; 223 | let color_set: [u32; 4] = [ 224 | applicate_color(c[0], d), 225 | applicate_color(c[0], -d), 226 | applicate_color(c[1], d), 227 | applicate_color(c[1], -d), 228 | ]; 229 | k <<= 1; 230 | for i in 0..16 { 231 | let index: usize = (k & 2) | (j & 1); 232 | outbuf[WRITE_ORDER_TABLE[i]] = color_set[index]; 233 | if !obaq && index == 2 { 234 | outbuf[WRITE_ORDER_TABLE[i]] &= TRANSPARENT_MASK; 235 | } 236 | j >>= 1; 237 | k >>= 1; 238 | } 239 | } else if b + db < 0 || b + db > 255 { 240 | // planar 241 | c[0][0] = (data[0] << 1 & 0xfc) | (data[0] >> 5 & 3); 242 | c[0][1] = (data[0] << 7 & 0x80) | (data[1] & 0x7e) | (data[0] & 1); 243 | c[0][2] = (data[1] << 7 & 0x80) 244 | | (data[2] << 2 & 0x60) 245 | | (data[2] << 3 & 0x18) 246 | | (data[3] >> 5 & 4); 247 | c[0][2] |= c[0][2] >> 6; 248 | c[1][0] = (data[3] << 1 & 0xf8) | (data[3] << 2 & 4) | (data[3] >> 5 & 3); 249 | c[1][1] = (data[4] & 0xfe) | data[4] >> 7; 250 | c[1][2] = (data[4] << 7 & 0x80) | (data[5] >> 1 & 0x7c); 251 | c[1][2] |= c[1][2] >> 6; 252 | c[2][0] = (data[5] << 5 & 0xe0) | (data[6] >> 3 & 0x1c) | (data[5] >> 1 & 3); 253 | c[2][1] = (data[6] << 3 & 0xf8) | (data[7] >> 5 & 0x6) | (data[6] >> 4 & 1); 254 | c[2][2] = data[7] << 2 | (data[7] >> 4 & 3); 255 | let mut i: usize = 0; 256 | for y in 0..4 { 257 | for x in 0..4 { 258 | let r: u8 = clamp( 259 | (x * (c[1][0] as i32 - c[0][0] as i32) 260 | + y * (c[2][0] as i32 - c[0][0] as i32) 261 | + 4 * c[0][0] as i32 262 | + 2) 263 | >> 2, 264 | ); 265 | let g: u8 = clamp( 266 | (x * (c[1][1] as i32 - c[0][1] as i32) 267 | + y * (c[2][1] as i32 - c[0][1] as i32) 268 | + 4 * c[0][1] as i32 269 | + 2) 270 | >> 2, 271 | ); 272 | let b: u8 = clamp( 273 | (x * (c[1][2] as i32 - c[0][2] as i32) 274 | + y * (c[2][2] as i32 - c[0][2] as i32) 275 | + 4 * c[0][2] as i32 276 | + 2) 277 | >> 2, 278 | ); 279 | outbuf[i] = color(r, g, b, 255); 280 | i += 1; 281 | } 282 | } 283 | } else { 284 | // differential 285 | let code: [u8; 2] = [(data[3] >> 5), (data[3] >> 2 & 7)]; 286 | let table = ETC1_SUBBLOCK_TABLE[(data[3] & 1) as usize]; 287 | c[0][0] = (r | r >> 5) as u8; 288 | c[0][1] = (g | g >> 5) as u8; 289 | c[0][2] = (b | b >> 5) as u8; 290 | c[1][0] = (r + dr) as u8; 291 | c[1][1] = (g + dg) as u8; 292 | c[1][2] = (b + db) as u8; 293 | c[1][0] |= c[1][0] >> 5; 294 | c[1][1] |= c[1][1] >> 5; 295 | c[1][2] |= c[1][2] >> 5; 296 | for i in 0..16 { 297 | let s: usize = table[i]; 298 | let m: i16 = ETC2A_MODIFIER_TABLE[obaq as usize][code[s] as usize][j & 1]; 299 | outbuf[WRITE_ORDER_TABLE[i]] = applicate_color_alpha( 300 | c[s], 301 | if k & 1 > 0 { -m } else { m }, 302 | !obaq && (k & 1 != 0) && j & 1 == 0, 303 | ); 304 | j >>= 1; 305 | k >>= 1; 306 | } 307 | } 308 | } 309 | 310 | #[inline] 311 | pub fn decode_etc2_a8_block(data: &[u8], outbuf: &mut [u32]) { 312 | // default alpha value is 255 from previous step 313 | if data[1] & 0xf0 > 0 { 314 | // multiplier != 0 315 | let multiplier: i32 = (data[1] >> 4) as i32; 316 | let table = ETC2_ALPHA_MOD_TABLE[(data[1] & 0xf) as usize]; 317 | let mut l: usize = u64::from_be_bytes(data[0..8].try_into().unwrap()) as usize; 318 | for i in 0..16 { 319 | let alpha = (clamp((data[0] as i32) + multiplier * (table[l & 7] as i32)) as u32) 320 | << TRANSPARENT_SHIFT; 321 | let x: &mut u32 = &mut outbuf[WRITE_ORDER_TABLE_REV[i]]; 322 | *x = (*x & TRANSPARENT_MASK) | alpha; 323 | l >>= 3; 324 | } 325 | } else { 326 | // multiplier == 0 (always same as base codeword) 327 | let alpha = (data[0] as u32) << TRANSPARENT_SHIFT; 328 | for x in outbuf.iter_mut().take(16) { 329 | *x = (*x & TRANSPARENT_MASK) | alpha; 330 | } 331 | } 332 | } 333 | 334 | #[inline] 335 | pub fn decode_etc2_rgba8_block(data: &[u8], outbuf: &mut [u32]) { 336 | decode_etc2_rgb_block(&data[8..], outbuf); 337 | decode_etc2_a8_block(data, outbuf); 338 | } 339 | --------------------------------------------------------------------------------