Skip to content

UASTC HDR 6x6 Intermediate File Format (Basis GPU Photo 6x6)

Rich Geldreich edited this page Oct 17, 2025 · 37 revisions

Note: This is a preliminary and in-progress draft specification, and is subject to change.

Intro

This document describes the low-level file structure of our custom supercompressed Universal ASTC HDR 6x6 texture "intermediate" format files, first supported by Basis Universal v1.60 (Jan. 2025 release, project internal name "Basis GPU Photo 6x6"). With a strong encoder, the system's compressed bitrate typically ranges between .75 - 3.0 bits/pixel. The actual compressed bitrate depends heavily on the image's content and the amount of Rate-distortion optimization applied. The decoded bitrate is always 3.56 bits/pixel (i.e. standard ASTC HDR 6x6).

This format supports a robust subset of ASTC features: 75 unique ASTC block configurations, solid color (void extent) blocks, single or dual planes, 1-3 subsets, all unique 2- or 3-subset ASTC partition patterns, CEM (Color Endpoint Mode) 7 or 11, and weight grid upsampling. It has been developed and heavily tested with both LDR/SDR image and texture content (stored in linear light RGB, typically scaled to 80-100 nits), or positive linear light RGB HDR content.

This format is quite usable on a wide range of LDR/SDR and HDR photographic content with a strong (SSIM-based) encoder using various aggressive block artifact reduction techniques.

High-Level Format Description

This is a relatively simple compressed intermediate format which can be rapidly transcoded into RGB-only standard ASTC HDR 6x6 blocks. No entropy coding is utilized. This intermediate format uses a small number of variable bit length command codes which can output one or more blocks, and which can reuse parts of previously encoded blocks. A deep understanding of the Khronos ASTC format specification and Integer Sequence Encoding is highly recommended to understand this format.

A decoder for this format outputs a 2D array of "physical" (or "transcoded") standard ASTC HDR 6x6 blocks. However, internally a decoder also needs to store the previously decoded four rows of "logical" ASTC HDR 6x6 blocks (or five rows total, including the current row being decoded). Coded logical blocks may use endpoint/weight grid ISE ranges, or smaller weight grid sizes, that are not valid in standard ASTC until the ISE values are requantized and/or the weight grid values are upsampled. An encoder can reference and reuse various ISE encoded values from these previously decoded logical ASTC blocks to greatly increase compression.

A pseudo-code version of a transcoder is below.

Referenced Documents

This document refers to the ISE tables in the UASTC HDR 4x4 specification. See "Standard ASTC ISE Range Table Reference". Also, this document references the UASTC LDR 4x4 specification, see "UASTC BISE Encoding Example". The way weight grid values are ISE coded in this specification matches the way UASTC LDR 4x4 ISE codes endpoint values.

Storage of Variable Length Bit Codes

Variable length bit codes are packed into the output stream starting at the LSB (Least Significant Bit) of each output byte, matching how codes are packed in the ETC1S supercompressed texture format, or zlib (RFC 1951). No entropy coding (such as Huffman coding) is utilized in this format, however Truncated Binary Encoding (also called phase-in codes, or economy codes) are utilized. Additionally, a variant of BISE (Bounded Integer Sequence Encoding) from ASTC is also used to store quantized weight grids efficiently.

Header

The header, which is always at the very beginning of the compressed data stream, contains 3 fields: an ID, width, and height. Each field is 16 bits.

The first code in the header is a 16-bit compression type ID field. It must always be 0xABCD, or in our next release in early 2026, 0xABCE (which signifies a small upcoming change in how 2x2 weight grids are upsampled to 4x4). Therefore, the first byte in the stream will always be 0xCD, and the subsequent byte in the stream will be 0xAB.

The next two codes are also 16 bits each, or totaling 32 bits. These fields are the width and height of the texture, in texels. These values do not need to be a multiple of 6 texels, and can range from [1,32768] texels. These fields must match the expected image's dimensions. Images or textures which are not a multiple of 6 texels in either dimension are typically padded by replicating rows or columns.

The number of unpacked ASTC HDR 6x6 blocks in the file will be floor((width + 5) / 6) * floor((height + 5) / 6).

High-Level Block Commands

Immediately following the header are the block commands describing the ASTC HDR 6x6 blocks. There can be, at most, one block command per ASTC HDR 6x6 block, but in practice there will be less due to runs of repeated blocks. The blocks are decoded in simple 2D raster order, starting from the top-left ASTC block and ending at the bottom-right ASTC block. Each row of blocks is decoded in left-to-right order.

There must be at least one command, and the total number of blocks output by all commands must not exceed and must match the number of unpacked ASTC HDR 6x6 blocks, as computed from the header's width and height using the equation above.

There are 4 high-level command types:

  • RUN: Code length 3 bits: code value: 0b000
  • SOLID: Code length: 3 bits, code value: 0b100
  • REUSE: Code length: 2 bits, code value: 0b10
  • BLOCK: Code length: 1 bits, code value: 0b1

RUN Command

A run command cannot appear until at least one block has been decoded and written to the output.

The total number of blocks in a run command is stored using a simple 5-bit VLC (Variable Length Code) scheme. The run's length is stored using one or more 6-bit packets. Bits 0-4 of each packet contain additional run length bits, starting from the LSB of the run length. Bit 5 of each packet indicates whether another 6-bit packet follows or not. The total number of decoded run length bits cannot exceed 30-bits (or 6 packets). The actual run length stored will be run_length-1.

The previously output block is repeated run length times. This referenced/repeated block may lie on the current row, or at the very end (the rightmost block) in the previous row of output blocks.

SOLID command

This command describes a solid color (or ASTC Void Extent) block. Three 15-bit codes follow, containing the positive half float R, G, and B values to use for all texels in the block. The sign bit, which must always be 0, is not stored. This representation does not permit negative values, and the half float value cannot be infinity or NaN, but may be denormal.

The block's alpha will always be the half float representation of 1.0.

REUSE command

This command reuses the block configuration of a previously emitted block. A 5-bit code is sent which indicates which previously decoded block's block configuration to reuse. (See Appendix B - Reuse XY "Delta" Codes.) The referenced block cannot be a SOLID (void extent) block. (This may seem limiting, but in practice the RDO encoder in basisu will avoid using SOLID blocks anyway because they are very expensive to send.) The referenced block cannot be at an invalid decoded 2D block coordinate (i.e. it must reference a block already decoded, and it cannot refer to a block before the leftmost column or the uppermost row).

The referenced block's block configuration index [0,74] is reused for this block, as well as the endpoint values, and the partition index (if 2-3 subsets). The block's ISE encoded weight values will immediately follow the 5-bit reuse delta code. The block's ISE encoded weight grid values may need to be requantized and upsampled, in order to output a valid (standard) physical ASTC block.

BLOCK command

BLOCK commands code a single ASTC block. Immediately following the BLOCK command is the block configuration [0,74], which is stored using Truncated Binary Encoding.

Immediately following the block configuration is the Endpoint Mode [0,4], which is also stored using Truncated Binary Encoding. The supported Endpoint Modes are:

  1. Raw: For 2 or 3 subset configurations, the unique partition pattern index is decoded using Truncated Binary Encoding. This unique pattern index is then converted to a standard 10-bit ASTC partition pattern seed value using a table lookup. The full endpoints are decoded using ISE coding, followed by the ISE encoded weight grid samples.
  2. Use Left: Reuse endpoints from the left block (cannot be used on the leftmost column of blocks), followed by ISE encoded weight grid samples. Only 1-subset blocks can use this Endpoint Mode.
  3. Use Upper: Reuse endpoints from the upper block (cannot be used the first row of blocks), followed by ISE encoded weight grid samples. Only 1-subset blocks can use this Endpoint Mode.
  4. Use Left Delta: Reuse endpoints from the left block with 5-bit DPCM deltas applied (cannot be used on the leftmost column of blocks), followed by ISE encoded weight grid samples. Only 1-subset blocks can use this Endpoint Mode.
  5. Use Upper Delta: Reuse endpoints from the upper block with 5-bit DPCM deltas applied (cannot be used the first row of blocks), followed by ISE encoded weight grid samples. Only 1-subset blocks can use this Endpoint Mode.

Importantly, the ISE encoded values (for both endpoints and weights) stored in the compressed stream for logical blocks don't always match the ISE encoded endpoints the decompressor must output for physical blocks. The ASTC specification places various limitations on which ISE encoded endpoint/weight grid ranges are valid to use, which are sometimes suboptimal for the purposes of rate-distortion optimization. In these cases, the ISE values must be requantized to the nearest representable quantized ISE values. Additionally, some block configurations store a smaller weight grid than is codable using standard ASTC. In these cases, the weight grid is upsampled.

  • For endpoint modes [0,1] (use left or upper): The left or upper logical (or coded) endpoint values are reused in the current block. The ISE encoded weight grid samples are decoded, potentially requantized, and potentially upsampled.

  • For endpoint modes [2,3] (use left or upper delta): 5-bit codes, representing DPCM deltas ranging between [-16,15], will be applied to the referenced block's logical (or coded) endpoint values. (Note the endpoint values are ISE coded, so the actual ISE values may not be in a monotonic order depending on the range. Lookup tables can be used to compensate.) These delta codes are immediately followed by the ISE encoded weight grid samples. The final ISE encoded endpoints may need to be requantized, and the weight grid may need to be requantized and/or upsampled.

  • For endpoint mode 4 (raw), both the endpoints and weight grid values are stored using ISE coding. These values may need to be requantized and potentially upsampled.

Final marker

A 16-bit value, 0xA742, will be present at the end of the stream.


Appendix A - Supported ASTC HDR 6x6 Block Configurations

There are 75 supported ASTC HDR 6x6 configurations, each assigned an index between [0,74]:

struct block_mode_desc
{
    bool m_dp; // Dual plane flag
    uint32_t m_cem; // ASTC Color Endpoint Mode
    uint32_t m_num_partitions; // Number of partitions, [1,3]
    uint32_t m_grid_x; // Grid width: [2,6]
    uint32_t m_grid_y; // Grid height: [2,6]

    // The logical or coding ISE ranges (which may not be valid ASTC ranges for this configuration):
    // See the UASTC LDR 4x4 specification for the array of ISE ranges.
    uint32_t m_endpoint_ise_range;
    uint32_t m_weight_ise_range;

    // The physical/output ASTC decompression ISE ranges (i.e. what the decompressor must output).
    uint32_t m_transcode_endpoint_ise_range;
    uint32_t m_transcode_weight_ise_range;

    uint32_t m_flags; // Flags (see BASIST_HDR_6x6_LEVEL0, etc.)
    int m_dp_channel; // Dual plane mode channel
};

// Flags (only used by the encoder, here for reference):
const uint32_t BASIST_HDR_6X6_LEVEL0 = 1;
const uint32_t BASIST_HDR_6X6_LEVEL1 = 2;
const uint32_t BASIST_HDR_6X6_LEVEL2 = 4;

const block_mode_desc g_block_mode_descs[TOTAL_BLOCK_MODE_DECS] =
{
    // ------ CEM 11
    { false, 11, 1, 6, 6, BISE_256_LEVELS, BISE_3_LEVELS, BISE_256_LEVELS, BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 11, 1, 6, 6, BISE_80_LEVELS, BISE_4_LEVELS,   BISE_80_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    { false, 11, 1, 6, 5, BISE_96_LEVELS, BISE_5_LEVELS,  BISE_96_LEVELS, BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 11, 1, 5, 6, BISE_96_LEVELS, BISE_5_LEVELS,  BISE_96_LEVELS, BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    { false, 11, 1, 6, 4, BISE_80_LEVELS, BISE_8_LEVELS,   BISE_80_LEVELS, BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 11, 1, 4, 6, BISE_80_LEVELS, BISE_8_LEVELS,   BISE_80_LEVELS, BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    { false, 11, 1, 6, 3, BISE_80_LEVELS, BISE_16_LEVELS,   BISE_80_LEVELS, BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 11, 1, 3, 6, BISE_80_LEVELS, BISE_16_LEVELS,   BISE_80_LEVELS, BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    { false, 11, 1, 5, 5, BISE_64_LEVELS, BISE_8_LEVELS,  BISE_64_LEVELS, BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 11, 1, 4, 4, BISE_192_LEVELS, BISE_16_LEVELS,  BISE_192_LEVELS, BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    { false, 11, 1, 3, 3, BISE_256_LEVELS, BISE_16_LEVELS, BISE_256_LEVELS, BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    // ------ CEM 7
    { false, 7, 1, 6, 6, BISE_96_LEVELS, BISE_5_LEVELS,   BISE_96_LEVELS,  BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    { false, 7, 1, 6, 6, BISE_256_LEVELS, BISE_3_LEVELS,  BISE_256_LEVELS, BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 7, 1, 6, 6, BISE_256_LEVELS, BISE_4_LEVELS,  BISE_256_LEVELS, BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },

    { false, 7, 1, 5, 6, BISE_256_LEVELS, BISE_6_LEVELS,   BISE_256_LEVELS,  BISE_6_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 7, 1, 6, 5, BISE_256_LEVELS, BISE_6_LEVELS,   BISE_256_LEVELS,  BISE_6_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },

    { false, 7, 1, 3, 6, BISE_256_LEVELS, BISE_20_LEVELS,   BISE_256_LEVELS,  BISE_20_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 7, 1, 6, 3, BISE_256_LEVELS, BISE_20_LEVELS,   BISE_256_LEVELS,  BISE_20_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    // ------ CEM 11, 2 subset
    { false, 11, 2, 6, 6, BISE_32_LEVELS, BISE_2_LEVELS,  BISE_32_LEVELS, BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    // 6x3/3x6
    { false, 11, 2, 6, 3, BISE_48_LEVELS, BISE_3_LEVELS,  BISE_48_LEVELS, BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 11, 2, 3, 6, BISE_48_LEVELS, BISE_3_LEVELS,  BISE_48_LEVELS, BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    // 3x6/6x3
    { false, 11, 2, 3, 6, BISE_32_LEVELS, BISE_4_LEVELS,  BISE_32_LEVELS, BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 11, 2, 6, 3, BISE_32_LEVELS, BISE_4_LEVELS,  BISE_32_LEVELS, BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    // 3x6/6x3
    { false, 11, 2, 4, 6, BISE_32_LEVELS, BISE_3_LEVELS,  BISE_32_LEVELS, BISE_3_LEVELS, 0, 0 },
    { false, 11, 2, 6, 4, BISE_32_LEVELS, BISE_3_LEVELS,  BISE_32_LEVELS, BISE_3_LEVELS, 0, 0 },

    // ------ CEM 7, 2 subset

    // 6x5/5x6
    { false, 7, 2, 5, 6, BISE_80_LEVELS, BISE_3_LEVELS,   BISE_80_LEVELS,  BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 7, 2, 6, 5, BISE_80_LEVELS, BISE_3_LEVELS,   BISE_80_LEVELS,  BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },

    // 6x4/4x6
    { false, 7, 2, 4, 6, BISE_80_LEVELS, BISE_4_LEVELS,   BISE_80_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 7, 2, 6, 4, BISE_80_LEVELS, BISE_4_LEVELS,   BISE_80_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },

    // 6x6
    { false, 7, 2, 6, 6, BISE_32_LEVELS, BISE_3_LEVELS,   BISE_32_LEVELS,  BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },

    // 6x6
    { false, 7, 2, 6, 6, BISE_192_LEVELS, BISE_2_LEVELS,   BISE_192_LEVELS,  BISE_2_LEVELS, 0, 0 },

    // 5x5
    { false, 7, 2, 5, 5, BISE_64_LEVELS, BISE_4_LEVELS,   BISE_64_LEVELS,  BISE_4_LEVELS, 0, 0 },

    // 6x3/3x6
    { false, 7, 2, 3, 6, BISE_48_LEVELS, BISE_8_LEVELS,   BISE_48_LEVELS,  BISE_8_LEVELS, 0, 0 },
    { false, 7, 2, 6, 3, BISE_48_LEVELS, BISE_8_LEVELS,   BISE_48_LEVELS,  BISE_8_LEVELS, 0, 0 },

    // 6x3/3x6
    { false, 7, 2, 3, 6, BISE_80_LEVELS, BISE_6_LEVELS,   BISE_80_LEVELS,  BISE_6_LEVELS, 0, 0 },
    { false, 7, 2, 6, 3, BISE_80_LEVELS, BISE_6_LEVELS,   BISE_80_LEVELS,  BISE_6_LEVELS, 0, 0 },

    // ------ Dual Plane

    // 3x6
    { true, 11, 1, 3, 6, BISE_64_LEVELS, BISE_4_LEVELS,   BISE_64_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { true, 11, 1, 3, 6, BISE_64_LEVELS, BISE_4_LEVELS,   BISE_64_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 1 },
    { true, 11, 1, 3, 6, BISE_64_LEVELS, BISE_4_LEVELS,   BISE_64_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 2 },

    // 6x3
    { true, 11, 1, 6, 3, BISE_64_LEVELS, BISE_4_LEVELS,   BISE_64_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { true, 11, 1, 6, 3, BISE_64_LEVELS, BISE_4_LEVELS,   BISE_64_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 1 },
    { true, 11, 1, 6, 3, BISE_64_LEVELS, BISE_4_LEVELS,   BISE_64_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 2 },

    // 3x3
    { true, 11, 1, 3, 3, BISE_64_LEVELS, BISE_16_LEVELS,   BISE_64_LEVELS,  BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { true, 11, 1, 3, 3, BISE_64_LEVELS, BISE_16_LEVELS,   BISE_64_LEVELS,  BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 1 },
    { true, 11, 1, 3, 3, BISE_64_LEVELS, BISE_16_LEVELS,   BISE_64_LEVELS,  BISE_16_LEVELS, BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 2 },

    // 4x4
    { true, 11, 1, 4, 4, BISE_48_LEVELS, BISE_5_LEVELS,   BISE_48_LEVELS,  BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { true, 11, 1, 4, 4, BISE_48_LEVELS, BISE_5_LEVELS,   BISE_48_LEVELS,  BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL2, 1 },
    { true, 11, 1, 4, 4, BISE_48_LEVELS, BISE_5_LEVELS,   BISE_48_LEVELS,  BISE_5_LEVELS, BASIST_HDR_6X6_LEVEL2, 2 },

    // 5x5
    { true, 11, 1, 5, 5, BISE_256_LEVELS, BISE_2_LEVELS,   BISE_256_LEVELS,  BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { true, 11, 1, 5, 5, BISE_256_LEVELS, BISE_2_LEVELS,   BISE_256_LEVELS,  BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 1 },
    { true, 11, 1, 5, 5, BISE_256_LEVELS, BISE_2_LEVELS,   BISE_256_LEVELS,  BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 2 },

    // ------ 2x2 modes for RDO
    // note 2x2 modes will be upsampled to 4x4 during transcoding (the min # of weight bits is 7 in ASTC)
    { true, 11, 1, 2, 2, BISE_64_LEVELS, BISE_4_LEVELS,   BISE_256_LEVELS,  BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },
    { true, 11, 1, 2, 2, BISE_64_LEVELS, BISE_4_LEVELS,   BISE_256_LEVELS,  BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 1 },
    { true, 11, 1, 2, 2, BISE_64_LEVELS, BISE_4_LEVELS,   BISE_256_LEVELS,  BISE_8_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 2 },
    { false, 11, 1, 2, 2, BISE_128_LEVELS, BISE_2_LEVELS,   BISE_256_LEVELS, BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL0 | BASIST_HDR_6X6_LEVEL1 | BASIST_HDR_6X6_LEVEL2, 0 },

    // ------ 3 subsets

    // 6x6
    { false, 7, 3, 6, 6, BISE_32_LEVELS, BISE_2_LEVELS,   BISE_32_LEVELS,  BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },

    // 5x5
    { false, 7, 3, 5, 5, BISE_64_LEVELS, BISE_2_LEVELS,   BISE_64_LEVELS,  BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },

    // 4x4
    { false, 7, 3, 4, 4, BISE_64_LEVELS, BISE_3_LEVELS,   BISE_64_LEVELS,  BISE_3_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 7, 3, 4, 4, BISE_40_LEVELS, BISE_4_LEVELS,   BISE_40_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 7, 3, 4, 4, BISE_32_LEVELS, BISE_5_LEVELS,   BISE_32_LEVELS,  BISE_5_LEVELS, 0, 0 },

    // 3x3
    { false, 7, 3, 3, 3, BISE_64_LEVELS, BISE_8_LEVELS,   BISE_64_LEVELS,  BISE_8_LEVELS, 0, 0 },

    // 6x4 
    { false, 7, 3, 6, 4, BISE_64_LEVELS, BISE_2_LEVELS,   BISE_64_LEVELS,  BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 7, 3, 4, 6, BISE_64_LEVELS, BISE_2_LEVELS,   BISE_64_LEVELS,  BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },

    // 6x4
    { false, 7, 3, 6, 4, BISE_32_LEVELS, BISE_3_LEVELS,   BISE_32_LEVELS,  BISE_3_LEVELS, 0, 0 },
    { false, 7, 3, 4, 6, BISE_32_LEVELS, BISE_3_LEVELS,   BISE_32_LEVELS,  BISE_3_LEVELS, 0, 0 },

    // 6x5
    { false, 7, 3, 6, 5, BISE_48_LEVELS, BISE_2_LEVELS,   BISE_48_LEVELS,  BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 7, 3, 5, 6, BISE_48_LEVELS, BISE_2_LEVELS,   BISE_48_LEVELS,  BISE_2_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },

    // 6x3
    { false, 7, 3, 6, 3, BISE_48_LEVELS, BISE_3_LEVELS,   BISE_48_LEVELS,  BISE_3_LEVELS, 0, 0 },
    { false, 7, 3, 3, 6, BISE_48_LEVELS, BISE_3_LEVELS,   BISE_48_LEVELS,  BISE_3_LEVELS, 0, 0 },

    // 6x3
    { false, 7, 3, 6, 3, BISE_32_LEVELS, BISE_4_LEVELS,   BISE_32_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },
    { false, 7, 3, 3, 6, BISE_32_LEVELS, BISE_4_LEVELS,   BISE_32_LEVELS,  BISE_4_LEVELS, BASIST_HDR_6X6_LEVEL2, 0 },

    // 6x3
    { false, 7, 3, 6, 3, BISE_24_LEVELS, BISE_5_LEVELS,   BISE_24_LEVELS,  BISE_5_LEVELS, 0, 0 },
    { false, 7, 3, 3, 6, BISE_24_LEVELS, BISE_5_LEVELS,   BISE_24_LEVELS,  BISE_5_LEVELS, 0, 0 },

    // 5x4
    { false, 7, 3, 5, 4, BISE_40_LEVELS, BISE_3_LEVELS,   BISE_40_LEVELS,  BISE_3_LEVELS, 0, 0 },
    { false, 7, 3, 4, 5, BISE_40_LEVELS, BISE_3_LEVELS,   BISE_40_LEVELS,  BISE_3_LEVELS, 0, 0 },
};

Appendix B: Reuse XY "Delta" Codes

const uint32_t REUSE_XY_DELTA_BITS = 5;
const uint32_t NUM_REUSE_XY_DELTAS = 1 << REUSE_XY_DELTA_BITS;

struct reuse_xy_delta
{
  int8_t m_x, m_y;
};

const reuse_xy_delta g_reuse_xy_deltas[NUM_REUSE_XY_DELTAS] =
{
  { -1, 0 }, { -2, 0 }, { -3, 0 }, { -4, 0 },
  { 3, -1 }, { 2, -1 }, { 1, -1 }, { 0, -1 }, { -1, -1 }, { -2, -1 }, { -3, -1 }, { -4, -1 },
  { 3, -2 }, { 2, -2 }, { 1, -2 }, { 0, -2 }, { -1, -2 }, { -2, -2 }, { -3, -2 }, { -4, -2 },
  { 3, -3 }, { 2, -3 }, { 1, -3 }, { 0, -3 }, { -1, -3 }, { -2, -3 }, { -3, -3 }, { -4, -3 },
  { 3, -4 }, { 2, -4 }, { 1, -4 }, { 0, -4 }
};

UASTC HDR 6x6 Intermediate Transcoder Pseudo-Code

The below function decode_file() decodes a compressed UASTC 6x6 HDR stream to "logical" ASTC blocks. It leverages a set of low-level ASTC texture format helper functions located in transcoder/basisu_astc_helpers.h. These helper functions handle packing and unpacking standard format "physical" 128-bit ASTC LDR/HDR format blocks. Refer to the ASTC standard for details on how ASTC blocks are stored. (Reproducing these helpers here would essentially be reproducing in pseudo-code what is already fully documented in the Khronos ASTC specification.)

In UASTC HDR 6x6, ISE encoded values are packed in the same way as by UASTC LDR 4x4. This is a simplified, but slightly more efficient variant of the type of ISE used by full ASTC.

Bitstream values are stored in the same way as in ETC1S or Deflate, i.e. ordered from lowest to highest bits in each byte. Truncated Binary Encoding is described here.

Class vector2D below is a resizable 2D array of objects, stored in row (or scanline) major order. Class uint8_vec is a vector of uint8_t's.

const uint32_t MAX_BLOCK_W = 6, MAX_BLOCK_H = 6;

// Number of ASTC ISE encoded values for CEM 11 and 7.
const uint32_t NUM_MODE11_ENDPOINTS = 6, NUM_MODE7_ENDPOINTS = 4;

const uint32_t RUN_CODE = 0b000, RUN_CODE_LEN = 3;
const uint32_t SOLID_CODE = 0b100, SOLID_CODE_LEN = 3;
const uint32_t REUSE_CODE = 0b10, REUSE_CODE_LEN = 2;
const uint32_t BLOCK_CODE = 0b1, BLOCK_CODE_LEN = 1;

const uint32_t NUM_UNIQUE_PARTITIONS2 = 521;
const uint32_t NUM_UNIQUE_PARTITIONS3 = 333;

enum class endpoint_mode
{
    cInvalid = -1,

    cRaw = 0,
    cUseLeft,
    cUseUpper,
    cUseLeftDelta,
    cUseUpperDelta,

    cTotal
};

enum class block_mode
{
    cInvalid = -1,

    cBMTotalModes = TOTAL_BLOCK_MODE_DECS
};

enum class encoding_type
{
    cInvalid = -1,
    cRun = 0,
    cSolid = 1,
    cReuse = 2,
    cBlock = 3,
    cTotal
};

const uint32_t NUM_ENDPOINT_DELTA_BITS = 5;

// Maps unique 2 subset 6x6 partition indices to 10-bit ASTC partition seed values.
const uint32_t g_part2_unique_index_to_seed[NUM_UNIQUE_PARTITIONS2] =
{
    86, 959, 936, 476, 1007, 672, 447, 423, 488, 422, 273, 65, 267, 786, 585, 195, 108, 731, 878, 812, 264, 125, 868, 581, 258, 390, 549, 872, 661, 352, 645, 543, 988, 
    906, 903, 616, 482, 529, 3, 286, 272, 303, 151, 504, 498, 260, 79, 66, 608, 769, 305, 610, 1014, 967, 835, 789, 7, 951, 691, 15, 763, 976, 438, 314, 601, 673, 177, 
    252, 615, 436, 220, 899, 623, 433, 674, 278, 797, 107, 847, 114, 470, 760, 821, 490, 329, 945, 387, 471, 225, 172, 83, 418, 966, 439, 316, 247, 43, 343, 625, 798, 
    1, 61, 73, 307, 136, 474, 42, 664, 1013, 249, 389, 227, 374, 121, 48, 538, 226, 309, 554, 802, 834, 335, 495, 10, 955, 461, 293, 508, 153, 101, 63, 139, 31, 687, 
    132, 174, 324, 545, 289, 39, 178, 594, 963, 854, 222, 323, 998, 964, 598, 475, 720, 1019, 983, 91, 703, 614, 394, 612, 281, 207, 930, 758, 586, 128, 517, 426, 306, 
    168, 713, 36, 458, 876, 368, 780, 5, 9, 214, 109, 553, 726, 175, 103, 753, 684, 44, 665, 53, 500, 367, 611, 119, 732, 639, 326, 203, 156, 686, 910, 255, 62, 392, 591, 
    112, 88, 213, 19, 1022, 478, 90, 486, 799, 702, 730, 414, 99, 1008, 142, 886, 373, 216, 69, 393, 299, 648, 415, 822, 912, 110, 567, 550, 693, 2, 138, 59, 271, 562, 295, 
    714, 719, 199, 893, 831, 1006, 662, 235, 262, 78, 51, 902, 298, 190, 169, 583, 347, 890, 958, 909, 49, 987, 696, 633, 480, 50, 764, 826, 1023, 1016, 437, 891, 774, 257, 
    724, 791, 526, 593, 690, 638, 858, 895, 794, 995, 130, 87, 877, 819, 318, 649, 376, 211, 284, 937, 370, 688, 229, 994, 115, 842, 60, 521, 95, 694, 804, 146, 754, 487, 55, 
    17, 770, 450, 223, 4, 137, 911, 236, 683, 523, 47, 181, 24, 270, 602, 736, 11, 355, 148, 351, 762, 1009, 16, 210, 619, 805, 874, 807, 887, 403, 999, 810, 27, 402, 551, 135, 
    778, 33, 409, 993, 71, 363, 159, 183, 77, 596, 670, 380, 968, 811, 404, 348, 539, 158, 578, 196, 621, 68, 530, 193, 100, 167, 919, 353, 366, 327, 643, 948, 518, 756, 801, 558, 
    28, 705, 116, 94, 898, 453, 622, 647, 231, 445, 652, 230, 191, 277, 292, 254, 198, 766, 386, 232, 29, 70, 942, 740, 291, 607, 411, 496, 839, 8, 675, 319, 742, 21, 547, 627, 716, 
    663, 23, 914, 631, 595, 499, 685, 950, 510, 54, 587, 432, 45, 646, 25, 122, 947, 171, 862, 441, 808, 722, 14, 74, 658, 129, 266, 1001, 534, 395, 527, 250, 206, 237, 67, 897, 634, 
    572, 569, 533, 37, 341, 89, 463, 419, 75, 134, 283, 943, 519, 362, 144, 681, 407, 954, 131, 455, 934, 46, 513, 339, 194, 361, 606, 852, 546, 655, 1015, 147, 506, 240, 56, 836, 76, 
    98, 600, 430, 388, 980, 695, 817, 279, 58, 215, 149, 170, 531, 870, 18, 727, 154, 26, 938, 929, 302, 697, 452, 218, 700, 524, 828, 751, 869, 217, 440, 354
};

// Maps unique 3 subset 6x6 partition indices to 10-bit ASTC partition seed values.
const uint32_t g_part3_unique_index_to_seed[NUM_UNIQUE_PARTITIONS3] =
{
    0, 8, 11, 14, 15, 17, 18, 19, 26, 31, 34, 35, 36, 38, 44, 47, 48, 49, 51, 56,
    59, 61, 70, 74, 76, 82, 88, 90, 96, 100, 103, 104, 108, 110, 111, 117, 122, 123,
    126, 127, 132, 133, 135, 139, 147, 150, 151, 152, 156, 157, 163, 166, 168, 171,
    175, 176, 179, 181, 182, 183, 186, 189, 192, 199, 203, 205, 207, 210, 214, 216,
    222, 247, 249, 250, 252, 254, 260, 261, 262, 263, 266, 272, 273, 275, 276, 288,
    291, 292, 293, 294, 297, 302, 309, 310, 313, 314, 318, 327, 328, 331, 335, 337,
    346, 356, 357, 358, 363, 365, 368, 378, 381, 384, 386, 390, 391, 392, 396, 397,
    398, 399, 401, 410, 411, 419, 427, 430, 431, 437, 439, 440, 451, 455, 457, 458,
    459, 460, 462, 468, 470, 471, 472, 474, 475, 477, 479, 482, 483, 488, 493, 495,
    496, 502, 503, 504, 507, 510, 511, 512, 515, 516, 518, 519, 522, 523, 525, 526,
    527, 538, 543, 544, 546, 547, 549, 550, 552, 553, 554, 562, 570, 578, 579, 581,
    582, 588, 589, 590, 593, 595, 600, 606, 611, 613, 618, 623, 625, 632, 637, 638,
    645, 646, 650, 651, 658, 659, 662, 666, 667, 669, 670, 678, 679, 685, 686, 687,
    688, 691, 694, 696, 698, 699, 700, 701, 703, 704, 707, 713, 714, 715, 717, 719,
    722, 724, 727, 730, 731, 734, 738, 739, 743, 747, 748, 750, 751, 753, 758, 760,
    764, 766, 769, 775, 776, 783, 784, 785, 787, 791, 793, 798, 799, 802, 804, 805,
    806, 807, 808, 809, 810, 813, 822, 823, 825, 831, 835, 837, 838, 839, 840, 842,
    845, 846, 848, 853, 854, 858, 859, 860, 866, 874, 882, 884, 887, 888, 892, 894,
    898, 902, 907, 914, 915, 918, 919, 922, 923, 925, 927, 931, 932, 937, 938, 940,
    943, 944, 945, 953, 955, 958, 959, 963, 966, 971, 974, 979, 990, 991, 998, 999,
    1007, 1010, 1011, 1012, 1015, 1020, 1023
};

// Read truncated binary value from bitstream
uint32_t bitwise_decoder::decode_truncated_binary(uint32_t n)
{
    assert(n >= 2);

    const uint32_t k = basisu::floor_log2i(n);
    const uint32_t u = (1 << (k + 1)) - n;

    uint32_t result = get_bits(k);

    if (result >= u)
        result = ((result << 1) | get_bits(1)) - u;

    return result;
}

// Read a simple variable length code (VLC) from a bitstream.
inline uint32_t bitwise_decoder::decode_vlc(uint32_t chunk_bits)
{
    assert(chunk_bits);

    const uint32_t chunk_size = 1 << chunk_bits;
    const uint32_t chunk_mask = chunk_size - 1;
            
    uint32_t v = 0;
    uint32_t ofs = 0;

    for ( ; ; )
    {
        uint32_t s = get_bits(chunk_bits + 1);
        v |= ((s & chunk_mask) << ofs);
        ofs += chunk_bits;

        if ((s & chunk_size) == 0)
            break;
        
        if (ofs >= 32)
        {
            assert(0);
            break;
        }
    }

    return v;
}

bool is_in_bounds(int v, int l, int h)
{
    return (v >= l) && (v < h);
}

// Copy weights, possibly upsampling to 4x4 and requantizing if the grid size is 2x2.
void copy_weight_grid(bool dual_plane, uint32_t grid_x, uint32_t grid_y, const uint8_t* transcode_weights, astc_helpers::log_astc_block& decomp_blk)
{
    assert(decomp_blk.m_weight_ise_range >= astc_helpers::BISE_2_LEVELS);
    assert(decomp_blk.m_weight_ise_range <= astc_helpers::BISE_32_LEVELS);

    // Special case for 2x2 which isn't typically valid ASTC (too few weight bits without dual plane). Upsample to 4x4.
    if ((!dual_plane) && (grid_x == 2) && (grid_y == 2))
    {
        decomp_blk.m_grid_width = 4;
        decomp_blk.m_grid_height = 4;

        //const uint32_t total_weight_levels = astc_helpers::bise_levels(decomp_blk.m_weight_ise_range);
        const auto& dequant_weight = astc_helpers::g_dequant_tables.get_weight_tab(decomp_blk.m_weight_ise_range).m_ISE_to_val;
        const auto& quant_weight = astc_helpers::g_dequant_tables.get_weight_tab(decomp_blk.m_weight_ise_range).m_val_to_ise;

        astc_helpers::weighted_sample weights[16];

        astc_helpers::compute_upsample_weights(4, 4, 2, 2, weights); // Compute standard ASTC upsample weights for 2x2 grid to 4x4 block size.

        for (uint32_t y = 0; y < 4; y++)
        {
            for (uint32_t x = 0; x < 4; x++)
            {
                const astc_helpers::weighted_sample& sample = weights[x + y * 4];

                uint32_t total_weight = 8;

                for (uint32_t yo = 0; yo < 2; yo++)
                {
                    for (uint32_t xo = 0; xo < 2; xo++)
                    {
                        if (!sample.m_weights[yo][xo])
                            continue;
                                                
                       // The original release always samples the 1st or 2nd sample here, which very slightly hurts R-D performance (but still results in correct encoding/decoding). The next release (in early 2026) will be changing this to the corrected remarked out variant. In practice, the difference is extremely minor. The header's prefix will be upped to 0xABCE on this release to signify this change. For now, this is the code to use.
                        total_weight += dequant_weight[transcode_weights[in_bounds((x + xo) + (y + yo) * grid_x, 0, grid_x * grid_y)]] * sample.m_weights[yo][xo];
                        //total_weight += dequant_weight[transcode_weights[(sample.m_src_x + xo) + (sample.m_src_y + yo) * grid_x]] * sample.m_weights[yo][xo];
                    } // x
                } // y

                total_weight >>= 4;

                assert(total_weight <= 64);

                decomp_blk.m_weights[x + y * 4] = quant_weight[total_weight];
            }
        }
    }
    else
    {
        const uint32_t num_planes = dual_plane ? 2 : 1;

        decomp_blk.m_grid_width = (uint8_t)grid_x;
        decomp_blk.m_grid_height = (uint8_t)grid_y;
        memcpy(decomp_blk.m_weights, transcode_weights, grid_x * grid_y * num_planes);
    }
}

// Decode a simplified variant of ISE (this is the same method used by UASTC LDR 4x4).
bool decode_values(bitwise_decoder& decoder, uint32_t total_values, uint32_t ise_range, uint8_t* pValues)
{
    assert(ise_range <= astc_helpers::BISE_256_LEVELS);

    const uint32_t ep_bits = astc_helpers::g_ise_range_table[ise_range][0];
    const uint32_t ep_trits = astc_helpers::g_ise_range_table[ise_range][1];
    const uint32_t ep_quints = astc_helpers::g_ise_range_table[ise_range][2];

    uint32_t total_tqs = 0;
    uint32_t bundle_size = 0, mul = 0;
    if (ep_trits)
    {
        total_tqs = (total_values + 4) / 5;
        bundle_size = 5;
        mul = 3;
    }
    else if (ep_quints)
    {
        total_tqs = (total_values + 2) / 3;
        bundle_size = 3;
        mul = 5;
    }

    const uint32_t MAX_TQ_VALUES = 32;
    assert(total_tqs <= MAX_TQ_VALUES);
    uint32_t tq_values[MAX_TQ_VALUES];

    for (uint32_t i = 0; i < total_tqs; i++)
    {
        uint32_t num_bits = ep_trits ? 8 : 7;

        if (i == (total_tqs - 1))
        {
            uint32_t num_remaining = total_values - (total_tqs - 1) * bundle_size;
            if (ep_trits)
            {
                switch (num_remaining)
                {
                case 1: num_bits = 2; break;
                case 2: num_bits = 4; break;
                case 3: num_bits = 5; break;
                case 4: num_bits = 7; break;
                default: break;
                }
            }
            else if (ep_quints)
            {
                switch (num_remaining)
                {
                case 1: num_bits = 3; break;
                case 2: num_bits = 5; break;
                default: break;
                }
            }
        }

        tq_values[i] = (uint32_t)decoder.get_bits(num_bits);
    } // i

    uint32_t accum = 0;
    uint32_t accum_remaining = 0;
    uint32_t next_tq_index = 0;

    for (uint32_t i = 0; i < total_values; i++)
    {
        uint32_t value = (uint32_t)decoder.get_bits(ep_bits);

        if (total_tqs)
        {
            if (!accum_remaining)
            {
                assert(next_tq_index < total_tqs);
                accum = tq_values[next_tq_index++];
                accum_remaining = bundle_size;
            }

            uint32_t v = accum % mul;
            accum /= mul;
            accum_remaining--;

            value |= (v << ep_bits);
        }

        pValues[i] = (uint8_t)value;
    }

    return true;
}

// Maps ASTC ISE encoded weights from a source to destination range.
void requantize_astc_weights(uint32_t n, const uint8_t* pSrc_ise_vals, uint32_t from_ise_range, uint8_t* pDst_ise_vals, uint32_t to_ise_range)
{
    if (from_ise_range == to_ise_range)
    {
        if (pDst_ise_vals != pSrc_ise_vals)
            memcpy(pDst_ise_vals, pSrc_ise_vals, n);
        return;
    }

    const auto& dequant_tab = astc_helpers::g_dequant_tables.get_weight_tab(from_ise_range).m_ISE_to_val;
    const auto& quant_tab = astc_helpers::g_dequant_tables.get_weight_tab(to_ise_range).m_val_to_ise;

    for (uint32_t i = 0; i < n; i++)
        pDst_ise_vals[i] = quant_tab[dequant_tab[pSrc_ise_vals[i]]];
}

uint8_t g_quantize_tables_preserve2[21 - 1][256]; // astc_helpers::TOTAL_ISE_RANGES=21, valid for >= BISE_6_LEVELS
uint8_t g_quantize_tables_preserve3[21 - 1][256]; // valid for >= BISE_8_LEVELS

// Initializes g_quantize_tables_preserve2[] and g_quantize_tables_preserve3[] (also used by ASTC encoders).
static void init_quantize_tables()
{
    for (uint32_t ise_range = astc_helpers::BISE_192_LEVELS; ise_range >= astc_helpers::BISE_6_LEVELS; ise_range--)
    {
        const uint32_t num_levels = astc_helpers::get_ise_levels(ise_range);
        const auto& ise_to_val_tab = astc_helpers::g_dequant_tables.get_endpoint_tab(ise_range).m_ISE_to_val;

        for (uint32_t desired_val = 0; desired_val < 256; desired_val++)
        {
            {
                uint32_t best_err = UINT32_MAX;
                int best_ise_val = -1;

                for (uint32_t ise_val = 0; ise_val < num_levels; ise_val++)
                {
                    const uint32_t quant_val = ise_to_val_tab[ise_val];

                    if ((quant_val & 0b11000000) != (desired_val & 0b11000000))
                        continue;

                    uint32_t err = basisu::squarei((int)quant_val - (int)desired_val);
                    if (err < best_err)
                    {
                        best_err = err;
                        best_ise_val = ise_val;
                    }

                } // ise_val

                assert(best_ise_val != -1);

                g_quantize_tables_preserve2[ise_range][desired_val] = (uint8_t)best_ise_val;
            }

            if (ise_range >= astc_helpers::BISE_8_LEVELS)
            {
                uint32_t best_err = UINT32_MAX;
                int best_ise_val = -1;

                for (uint32_t ise_val = 0; ise_val < num_levels; ise_val++)
                {
                    const uint32_t quant_val = ise_to_val_tab[ise_val];

                    if ((quant_val & 0b11100000) != (desired_val & 0b11100000))
                        continue;

                    uint32_t err = basisu::squarei((int)quant_val - (int)desired_val);
                    if (err < best_err)
                    {
                        best_err = err;
                        best_ise_val = ise_val;
                    }

                } // ise_val

                assert(best_ise_val != -1);

                g_quantize_tables_preserve3[ise_range][desired_val] = (uint8_t)best_ise_val;
            }

        } // desired_val

    } // ise_range
}

void pack_bit(
    int& dst, int dst_bit,
    int src_val, int src_bit = 0)
{
    assert(dst_bit >= 0 && dst_bit <= 31);
    int bit = get_bit(src_val, src_bit);
    dst |= (bit << dst_bit);
}

// Maps ASTC ISE encoded endpoints from a source to destination range, preserving MSB's as needed.
void requantize_ise_endpoints(uint32_t cem, uint32_t src_ise_endpoint_range, const uint8_t* pSrc_endpoints, uint32_t dst_ise_endpoint_range, uint8_t* pDst_endpoints)
{
    assert(pSrc_endpoints != pDst_endpoints);
    assert((src_ise_endpoint_range >= astc_helpers::FIRST_VALID_ENDPOINT_ISE_RANGE) && (src_ise_endpoint_range <= astc_helpers::LAST_VALID_ENDPOINT_ISE_RANGE));
    assert((dst_ise_endpoint_range >= astc_helpers::FIRST_VALID_ENDPOINT_ISE_RANGE) && (dst_ise_endpoint_range <= astc_helpers::LAST_VALID_ENDPOINT_ISE_RANGE));

    // must be >=12 ISE levels for g_quantize_tables_preserve2 etc.
    assert(dst_ise_endpoint_range >= astc_helpers::BISE_12_LEVELS);

    const uint32_t n = (cem == 11) ? NUM_MODE11_ENDPOINTS : NUM_MODE7_ENDPOINTS;

    if (src_ise_endpoint_range == dst_ise_endpoint_range)
    {
        memcpy(pDst_endpoints, pSrc_endpoints, n);
        return;
    }

    uint8_t temp_endpoints[NUM_MODE11_ENDPOINTS];
    if (src_ise_endpoint_range != astc_helpers::BISE_256_LEVELS)
    {
        assert(n <= NUM_MODE11_ENDPOINTS);

        const auto& endpoint_dequant_tab = astc_helpers::g_dequant_tables.get_endpoint_tab(src_ise_endpoint_range).m_ISE_to_val;

        for (uint32_t i = 0; i < n; i++)
            temp_endpoints[i] = endpoint_dequant_tab[pSrc_endpoints[i]];

        pSrc_endpoints = temp_endpoints;
    }

    if (dst_ise_endpoint_range == astc_helpers::BISE_256_LEVELS)
    {
        memcpy(pDst_endpoints, pSrc_endpoints, n);
        return;
    }

    const auto& quant_tab = astc_helpers::g_dequant_tables.get_endpoint_tab(dst_ise_endpoint_range).m_val_to_ise;

    const auto& dequant_tab = astc_helpers::g_dequant_tables.get_endpoint_tab(dst_ise_endpoint_range).m_ISE_to_val;

    // A smarter value quantization that preserves the key upper bits. (If these bits get corrupted, the entire meaning of the encoding can get lost.)
    if (cem == 11)
    {
        assert(n == 6);

        int maj_comp = 0;
        pack_bit(maj_comp, 0, pSrc_endpoints[4], 7);
        pack_bit(maj_comp, 1, pSrc_endpoints[5], 7);

        if (maj_comp == 3)
        {
            // Direct
            pDst_endpoints[0] = quant_tab[pSrc_endpoints[0]];
            pDst_endpoints[1] = quant_tab[pSrc_endpoints[1]];
            pDst_endpoints[2] = quant_tab[pSrc_endpoints[2]];
            pDst_endpoints[3] = quant_tab[pSrc_endpoints[3]];
            // No need for preserve1 tables, we can use the regular quantization tables because they preserve the MSB.
            pDst_endpoints[4] = quant_tab[pSrc_endpoints[4]];
            pDst_endpoints[5] = quant_tab[pSrc_endpoints[5]];

            assert((dequant_tab[pDst_endpoints[4]] & 128) == (pSrc_endpoints[4] & 128));
            assert((dequant_tab[pDst_endpoints[5]] & 128) == (pSrc_endpoints[5] & 128));
        }
        else
        {
            pDst_endpoints[0] = quant_tab[pSrc_endpoints[0]];
            pDst_endpoints[1] = g_quantize_tables_preserve2[dst_ise_endpoint_range][pSrc_endpoints[1]];
            pDst_endpoints[2] = g_quantize_tables_preserve2[dst_ise_endpoint_range][pSrc_endpoints[2]];
            pDst_endpoints[3] = g_quantize_tables_preserve2[dst_ise_endpoint_range][pSrc_endpoints[3]];
            pDst_endpoints[4] = g_quantize_tables_preserve3[dst_ise_endpoint_range][pSrc_endpoints[4]];
            pDst_endpoints[5] = g_quantize_tables_preserve3[dst_ise_endpoint_range][pSrc_endpoints[5]];

            assert((dequant_tab[pDst_endpoints[1]] & 0b11000000) == (pSrc_endpoints[1] & 0b11000000));
            assert((dequant_tab[pDst_endpoints[2]] & 0b11000000) == (pSrc_endpoints[2] & 0b11000000));
            assert((dequant_tab[pDst_endpoints[3]] & 0b11000000) == (pSrc_endpoints[3] & 0b11000000));
            assert((dequant_tab[pDst_endpoints[4]] & 0b11100000) == (pSrc_endpoints[4] & 0b11100000));
            assert((dequant_tab[pDst_endpoints[5]] & 0b11100000) == (pSrc_endpoints[5] & 0b11100000));
        }
    }
    else if (cem == 7)
    {
        assert(n == 4);

        pDst_endpoints[0] = g_quantize_tables_preserve2[dst_ise_endpoint_range][pSrc_endpoints[0]];
        pDst_endpoints[1] = g_quantize_tables_preserve3[dst_ise_endpoint_range][pSrc_endpoints[1]];
        pDst_endpoints[2] = g_quantize_tables_preserve3[dst_ise_endpoint_range][pSrc_endpoints[2]];
        pDst_endpoints[3] = g_quantize_tables_preserve3[dst_ise_endpoint_range][pSrc_endpoints[3]];

        assert((dequant_tab[pDst_endpoints[0]] & 0b11000000) == (pSrc_endpoints[0] & 0b11000000));
        assert((dequant_tab[pDst_endpoints[1]] & 0b11100000) == (pSrc_endpoints[1] & 0b11100000));
        assert((dequant_tab[pDst_endpoints[2]] & 0b11100000) == (pSrc_endpoints[2] & 0b11100000));
        assert((dequant_tab[pDst_endpoints[3]] & 0b11100000) == (pSrc_endpoints[3] & 0b11100000));
    }
    else
    {
        assert(0);
    }
}

// Transcodes a UASTC HDR 6x6 stream to a 2D array of ASTC logical blocks descriptors (which can then be losslessly packed to standard physical ASTC blocks).
static bool decode_file(const uint8_vec& comp_data, vector2D<astc_helpers::astc_block>& decoded_blocks, uint32_t &width, uint32_t &height)
{
    const uint32_t BLOCK_W = 6, BLOCK_H = 6;

    width = 0;
    height = 0;

    if (comp_data.size() <= 2*3)
        return false;

    bitwise_decoder decoder;
    if (!decoder.init(comp_data.data(), comp_data.size_u32()))
        return false;

    if (decoder.get_bits(16) != 0xABCD)
        return false;

    width = decoder.get_bits(16);
    height = decoder.get_bits(16);
        
    if (!width || !height)
        return false;

    const uint32_t num_blocks_x = (width + BLOCK_W - 1) / BLOCK_W;
    const uint32_t num_blocks_y = (height + BLOCK_H - 1) / BLOCK_H;
    const uint32_t total_blocks = num_blocks_x * num_blocks_y;

    decoded_blocks.resize(num_blocks_x, num_blocks_y);

    vector2D<astc_helpers::log_astc_block> decoded_log_blocks(num_blocks_x, num_blocks_y);

    uint32_t cur_bx = 0, cur_by = 0;
        
    while (cur_by < num_blocks_y)
    {
        if (decoder.get_bits_remaining() < 1)
            return false;

        encoding_type et = encoding_type::cBlock;

        uint32_t b0 = decoder.get_bits(1);
        if (!b0)
        {
            uint32_t b1 = decoder.get_bits(1);
            if (b1)
                et = encoding_type::cReuse;
            else
            {
                uint32_t b2 = decoder.get_bits(1);
                if (b2)
                    et = encoding_type::cSolid;
                else
                    et = encoding_type::cRun;
            }
        }

        switch (et)
        {
        case encoding_type::cRun:
        {
            if (!cur_bx && !cur_by)
                return false;

            const uint32_t run_len = decoder.decode_vlc(5) + 1;
            
            uint32_t num_blocks_remaining = total_blocks - (cur_bx + cur_by * num_blocks_x);
            if (run_len > num_blocks_remaining)
                return false;
                        
            uint32_t prev_bx = cur_bx, prev_by = cur_by;

            if (cur_bx)
                prev_bx--;
            else
            {
                prev_bx = num_blocks_x - 1;
                prev_by--;
            }

            const astc_helpers::log_astc_block& prev_log_blk = decoded_log_blocks(prev_bx, prev_by);
            const astc_helpers::astc_block& prev_phys_blk = decoded_blocks(prev_bx, prev_by);

            for (uint32_t i = 0; i < run_len; i++)
            {
                decoded_log_blocks(cur_bx, cur_by) = prev_log_blk;
                decoded_blocks(cur_bx, cur_by) = prev_phys_blk;

                cur_bx++;
                if (cur_bx == num_blocks_x)
                {
                    cur_bx = 0;
                    cur_by++;
                }
            }

            break;
        }
        case encoding_type::cSolid:
        {
            const half_float rh = (half_float)decoder.get_bits(15);
            const half_float gh = (half_float)decoder.get_bits(15);
            const half_float bh = (half_float)decoder.get_bits(15);

            astc_helpers::log_astc_block& log_blk = decoded_log_blocks(cur_bx, cur_by);

            log_blk.clear();
            log_blk.m_solid_color_flag_hdr = true;
            log_blk.m_solid_color[0] = rh;
            log_blk.m_solid_color[1] = gh;
            log_blk.m_solid_color[2] = bh;
            log_blk.m_solid_color[3] = float_to_half(1.0f);

            bool status = astc_helpers::pack_astc_block(decoded_blocks(cur_bx, cur_by), log_blk);
            if (!status)
                return false;

            cur_bx++;
            if (cur_bx == num_blocks_x)
            {
                cur_bx = 0;
                cur_by++;
            }
            
            break;
        }
        case encoding_type::cReuse:
        {
            if (!cur_bx && !cur_by)
                return false;

            const uint32_t reuse_delta_index = decoder.get_bits(REUSE_XY_DELTA_BITS);

            const int reuse_delta_x = g_reuse_xy_deltas[reuse_delta_index].m_x;
            const int reuse_delta_y = g_reuse_xy_deltas[reuse_delta_index].m_y;

            const int prev_bx = cur_bx + reuse_delta_x, prev_by = cur_by + reuse_delta_y;
            if ((prev_bx < 0) || (prev_bx >= (int)num_blocks_x))
                return false;
            if (prev_by < 0)
                return false;
            
            const astc_helpers::log_astc_block& prev_log_blk = decoded_log_blocks(prev_bx, prev_by);
            const astc_helpers::astc_block& prev_phys_blk = decoded_blocks(prev_bx, prev_by);

            if (prev_log_blk.m_solid_color_flag_hdr)
                return false;

            astc_helpers::log_astc_block& log_blk = decoded_log_blocks(cur_bx, cur_by);
            astc_helpers::astc_block& phys_blk = decoded_blocks(cur_bx, cur_by);
            
            log_blk = prev_log_blk;

            const uint32_t total_grid_weights = log_blk.m_grid_width * log_blk.m_grid_height * (log_blk.m_dual_plane ? 2 : 1);

            bool status = decode_values(decoder, total_grid_weights, log_blk.m_weight_ise_range, log_blk.m_weights);
            if (!status)
                return false;

            astc_helpers::log_astc_block decomp_blk;
            status = astc_helpers::unpack_block(&prev_phys_blk, decomp_blk, BLOCK_W, BLOCK_H);
            if (!status)
                return false;
            
            uint8_t transcode_weights[MAX_BLOCK_W * MAX_BLOCK_H * 2];
            requantize_astc_weights(total_grid_weights, log_blk.m_weights, log_blk.m_weight_ise_range, transcode_weights, decomp_blk.m_weight_ise_range);

            copy_weight_grid(log_blk.m_dual_plane, log_blk.m_grid_width, log_blk.m_grid_height, transcode_weights, decomp_blk);

            status = astc_helpers::pack_astc_block(phys_blk, decomp_blk);
            if (!status)
                return false;

            cur_bx++;
            if (cur_bx == num_blocks_x)
            {
                cur_bx = 0;
                cur_by++;
            }

            break;
        }
        case encoding_type::cBlock:
        {
            const block_mode bm = (block_mode)decoder.decode_truncated_binary((uint32_t)block_mode::cBMTotalModes);
            const endpoint_mode em = (endpoint_mode)decoder.decode_truncated_binary((uint32_t)endpoint_mode::cTotal);

            switch (em)
            {
            case endpoint_mode::cUseLeft:
            case endpoint_mode::cUseUpper:
            {
                int neighbor_bx = cur_bx, neighbor_by = cur_by;
                
                if (em == endpoint_mode::cUseLeft)
                    neighbor_bx--;
                else
                    neighbor_by--;

                if ((neighbor_bx < 0) || (neighbor_by < 0))
                    return false;

                const astc_helpers::log_astc_block& neighbor_blk = decoded_log_blocks(neighbor_bx, neighbor_by);
                if (!neighbor_blk.m_color_endpoint_modes[0])
                    return false;

                const block_mode_desc& bmd = g_block_mode_descs[(uint32_t)bm];
                const uint32_t num_endpoint_values = get_num_endpoint_vals(bmd.m_cem);

                if (bmd.m_cem != neighbor_blk.m_color_endpoint_modes[0])
                    return false;

                astc_helpers::log_astc_block& log_blk = decoded_log_blocks(cur_bx, cur_by);
                astc_helpers::astc_block& phys_blk = decoded_blocks(cur_bx, cur_by);

                log_blk.clear();
                log_blk.m_num_partitions = 1;
                log_blk.m_color_endpoint_modes[0] = (uint8_t)bmd.m_cem;
                log_blk.m_endpoint_ise_range = neighbor_blk.m_endpoint_ise_range;
                log_blk.m_weight_ise_range = (uint8_t)bmd.m_weight_ise_range;
                log_blk.m_grid_width = (uint8_t)bmd.m_grid_x;
                log_blk.m_grid_height = (uint8_t)bmd.m_grid_y;
                log_blk.m_dual_plane = (uint8_t)bmd.m_dp;
                log_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;

                memcpy(log_blk.m_endpoints, neighbor_blk.m_endpoints, num_endpoint_values);

                const uint32_t total_grid_weights = bmd.m_grid_x * bmd.m_grid_y * (bmd.m_dp ? 2 : 1);

                bool status = decode_values(decoder, total_grid_weights, bmd.m_weight_ise_range, log_blk.m_weights);
                if (!status)
                    return false;

                astc_helpers::log_astc_block decomp_blk;
                decomp_blk.clear();

                decomp_blk.m_num_partitions = 1;
                decomp_blk.m_color_endpoint_modes[0] = (uint8_t)bmd.m_cem;
                decomp_blk.m_endpoint_ise_range = (uint8_t)bmd.m_transcode_endpoint_ise_range;
                decomp_blk.m_weight_ise_range = (uint8_t)bmd.m_transcode_weight_ise_range;
                decomp_blk.m_dual_plane = bmd.m_dp;
                decomp_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;

                requantize_ise_endpoints(bmd.m_cem, log_blk.m_endpoint_ise_range, log_blk.m_endpoints, bmd.m_transcode_endpoint_ise_range, decomp_blk.m_endpoints);

                uint8_t transcode_weights[MAX_BLOCK_W * MAX_BLOCK_H * 2];
                requantize_astc_weights(total_grid_weights, log_blk.m_weights, bmd.m_weight_ise_range, transcode_weights, bmd.m_transcode_weight_ise_range);

                copy_weight_grid(bmd.m_dp, bmd.m_grid_x, bmd.m_grid_y, transcode_weights, decomp_blk);

                status = astc_helpers::pack_astc_block(phys_blk, decomp_blk);
                if (!status)
                    return false;

                cur_bx++;
                if (cur_bx == num_blocks_x)
                {
                    cur_bx = 0;
                    cur_by++;
                }

                break;
            }
            case endpoint_mode::cUseLeftDelta:
            case endpoint_mode::cUseUpperDelta:
            {
                int neighbor_bx = cur_bx, neighbor_by = cur_by;

                if (em == endpoint_mode::cUseLeftDelta)
                    neighbor_bx--;
                else
                    neighbor_by--;

                if ((neighbor_bx < 0) || (neighbor_by < 0))
                    return false;

                const astc_helpers::log_astc_block& neighbor_blk = decoded_log_blocks(neighbor_bx, neighbor_by);
                if (!neighbor_blk.m_color_endpoint_modes[0])
                    return false;

                const block_mode_desc& bmd = g_block_mode_descs[(uint32_t)bm];
                const uint32_t num_endpoint_values = get_num_endpoint_vals(bmd.m_cem);

                if (bmd.m_cem != neighbor_blk.m_color_endpoint_modes[0])
                    return false;

                astc_helpers::log_astc_block& log_blk = decoded_log_blocks(cur_bx, cur_by);
                astc_helpers::astc_block& phys_blk = decoded_blocks(cur_bx, cur_by);

                log_blk.clear();
                log_blk.m_num_partitions = 1;
                log_blk.m_color_endpoint_modes[0] = (uint8_t)bmd.m_cem;
                log_blk.m_dual_plane = bmd.m_dp;
                log_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;
                
                log_blk.m_endpoint_ise_range = (uint8_t)bmd.m_endpoint_ise_range;
                requantize_ise_endpoints(bmd.m_cem, neighbor_blk.m_endpoint_ise_range, neighbor_blk.m_endpoints, bmd.m_endpoint_ise_range, log_blk.m_endpoints);

                const int total_endpoint_delta_vals = 1 << NUM_ENDPOINT_DELTA_BITS;
                const int low_delta_limit = -(total_endpoint_delta_vals / 2); // high_delta_limit = (total_endpoint_delta_vals / 2) - 1;

                const auto& ise_to_rank = astc_helpers::g_dequant_tables.get_endpoint_tab(log_blk.m_endpoint_ise_range).m_ISE_to_rank;
                const auto& rank_to_ise = astc_helpers::g_dequant_tables.get_endpoint_tab(log_blk.m_endpoint_ise_range).m_rank_to_ISE;
                const int total_endpoint_levels = astc_helpers::get_ise_levels(log_blk.m_endpoint_ise_range);

                for (uint32_t i = 0; i < num_endpoint_values; i++)
                {
                    int cur_val = ise_to_rank[log_blk.m_endpoints[i]];
                    
                    int delta = (int)decoder.get_bits(NUM_ENDPOINT_DELTA_BITS) + low_delta_limit;

                    cur_val += delta;
                    if ((cur_val < 0) || (cur_val >= total_endpoint_levels))
                        return false;

                    log_blk.m_endpoints[i] = rank_to_ise[cur_val];
                }

                log_blk.m_weight_ise_range = (uint8_t)bmd.m_weight_ise_range;
                log_blk.m_grid_width = (uint8_t)bmd.m_grid_x;
                log_blk.m_grid_height = (uint8_t)bmd.m_grid_y;

                const uint32_t total_grid_weights = bmd.m_grid_x * bmd.m_grid_y * (bmd.m_dp ? 2 : 1);

                bool status = decode_values(decoder, total_grid_weights, bmd.m_weight_ise_range, log_blk.m_weights);
                if (!status)
                    return false;

                astc_helpers::log_astc_block decomp_blk;
                decomp_blk.clear();

                decomp_blk.m_num_partitions = 1;
                decomp_blk.m_color_endpoint_modes[0] = (uint8_t)bmd.m_cem;
                decomp_blk.m_endpoint_ise_range = (uint8_t)bmd.m_transcode_endpoint_ise_range;
                decomp_blk.m_weight_ise_range = (uint8_t)bmd.m_transcode_weight_ise_range;
                decomp_blk.m_dual_plane = (uint8_t)bmd.m_dp;
                decomp_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;

                requantize_ise_endpoints(bmd.m_cem, log_blk.m_endpoint_ise_range, log_blk.m_endpoints, bmd.m_transcode_endpoint_ise_range, decomp_blk.m_endpoints);

                uint8_t transcode_weights[MAX_BLOCK_W * MAX_BLOCK_H * 2];
                requantize_astc_weights(total_grid_weights, log_blk.m_weights, bmd.m_weight_ise_range, transcode_weights, bmd.m_transcode_weight_ise_range);

                copy_weight_grid(bmd.m_dp, bmd.m_grid_x, bmd.m_grid_y, transcode_weights, decomp_blk);

                status = astc_helpers::pack_astc_block(phys_blk, decomp_blk);
                if (!status)
                    return false;

                cur_bx++;
                if (cur_bx == num_blocks_x)
                {
                    cur_bx = 0;
                    cur_by++;
                }

                break;
            }
            case endpoint_mode::cRaw:
            {
                const block_mode_desc& bmd = g_block_mode_descs[(uint32_t)bm];

                const uint32_t num_endpoint_values = get_num_endpoint_vals(bmd.m_cem);

                astc_helpers::log_astc_block& log_blk = decoded_log_blocks(cur_bx, cur_by);
                astc_helpers::astc_block& phys_blk = decoded_blocks(cur_bx, cur_by);

                log_blk.clear();
                log_blk.m_num_partitions = (uint8_t)bmd.m_num_partitions;
                
                for (uint32_t p = 0; p < bmd.m_num_partitions; p++)
                    log_blk.m_color_endpoint_modes[p] = (uint8_t)bmd.m_cem;

                log_blk.m_endpoint_ise_range = (uint8_t)bmd.m_endpoint_ise_range;
                log_blk.m_weight_ise_range = (uint8_t)bmd.m_weight_ise_range;

                log_blk.m_grid_width = (uint8_t)bmd.m_grid_x;
                log_blk.m_grid_height = (uint8_t)bmd.m_grid_y;
                log_blk.m_dual_plane = (uint8_t)bmd.m_dp;
                log_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;

                if (bmd.m_num_partitions == 2)
                {
                    const uint32_t unique_partition_index = decoder.decode_truncated_binary(NUM_UNIQUE_PARTITIONS2);
                    log_blk.m_partition_id = (uint16_t)g_part2_unique_index_to_seed[unique_partition_index];
                }
                else if (bmd.m_num_partitions == 3)
                {
                    const uint32_t unique_partition_index = decoder.decode_truncated_binary(NUM_UNIQUE_PARTITIONS3);
                    log_blk.m_partition_id = (uint16_t)g_part3_unique_index_to_seed[unique_partition_index];
                }
                
                bool status = decode_values(decoder, num_endpoint_values * bmd.m_num_partitions, bmd.m_endpoint_ise_range, log_blk.m_endpoints);
                if (!status)
                    return false;

                const uint32_t total_grid_weights = bmd.m_grid_x * bmd.m_grid_y * (bmd.m_dp ? 2 : 1);

                status = decode_values(decoder, total_grid_weights, bmd.m_weight_ise_range, log_blk.m_weights);
                if (!status)
                    return false;

                astc_helpers::log_astc_block decomp_blk;
                decomp_blk.clear();
                
                decomp_blk.m_dual_plane = bmd.m_dp;
                decomp_blk.m_color_component_selector = (uint8_t)bmd.m_dp_channel;
                decomp_blk.m_partition_id = log_blk.m_partition_id;
                                
                decomp_blk.m_num_partitions = (uint8_t)bmd.m_num_partitions;
                
                for (uint32_t p = 0; p < bmd.m_num_partitions; p++)
                    decomp_blk.m_color_endpoint_modes[p] = (uint8_t)bmd.m_cem;

                decomp_blk.m_endpoint_ise_range = (uint8_t)bmd.m_transcode_endpoint_ise_range;
                decomp_blk.m_weight_ise_range = (uint8_t)bmd.m_transcode_weight_ise_range;

                for (uint32_t p = 0; p < bmd.m_num_partitions; p++)
                    requantize_ise_endpoints(bmd.m_cem, bmd.m_endpoint_ise_range, log_blk.m_endpoints + num_endpoint_values * p, bmd.m_transcode_endpoint_ise_range, decomp_blk.m_endpoints + num_endpoint_values * p);

                uint8_t transcode_weights[MAX_BLOCK_W * MAX_BLOCK_H * 2];
                requantize_astc_weights(total_grid_weights, log_blk.m_weights, bmd.m_weight_ise_range, transcode_weights, bmd.m_transcode_weight_ise_range);

                copy_weight_grid(bmd.m_dp, bmd.m_grid_x, bmd.m_grid_y, transcode_weights, decomp_blk);

                status = astc_helpers::pack_astc_block(phys_blk, decomp_blk);
                if (!status)
                    return false;

                cur_bx++;
                if (cur_bx == num_blocks_x)
                {
                    cur_bx = 0;
                    cur_by++;
                }

                break;
            }
            default:
            {
                assert(0);
                return false;
            }
            }

            break;
        }
        default:
        {
            assert(0);
            return false;
        }
        }
    }

    if (decoder.get_bits(16) != 0xA742)
    {
        // Expected end marker not found
        return false;
    }

    return true;
}
Clone this wiki locally