910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#[derive(Debug, PartialEq, Eq)]
pub struct BlockMetadata {
    pub base: [[i32; 3]; 2],
    pub table_idx_1: u32,
    pub table_idx_2: u32,
    pub flip: bool,
}

// Extract base color in the individual mode.
// Return [base_color1, base_color2]
// See "Khronos Data Format Specification v1.4.0", Section 21, "In the
// 'individual mode'..."
fn parse_individual_base(patch: u64) -> [[i32; 3]; 2] {
    // The bit layout for individual mode is:
    // 63..60: base color 1 R
    // 59..56: base color 2 R
    // 55..52: base color 1 G
    // 51..48: base color 2 G
    // 49..44: base color 1 B
    // 43..40: base color 2 B
    // The remaining bits are handled in 'parse_block_metadata'

    let base = [0, 1].map(|j| {
        // i = 0,1,2 maps to [R, G, B] respectively
        [0, 1, 2].map(|i| {
            let shift = 60 - 4 * (i * 2 + j);
            let read_4bit = ((patch >> shift) & 0b1111) as i32;
            scale_4bit_to_8bit(read_4bit)
        })
    });
    return base;
}

// Expands a 4-bit color component to an 8-bit component
// according to the specification.
//
// We want to scale value in [0, 15] to value in [0, 255].
// The ideal scaling  is (input / 15) * 255 = input * 17.
// The bitwise operation of this function:
//      (input << 4) | input
// is equivalent to:
//      (input * 2^4) + input = input * 17.
// Thus, the two results are identical.
//
// In addition, the operation of this function is hardware-friendly and
// correctly maps the endpoints:
// - Minimum: 0b_0000(0) becomes 0b_00000000(0).
// - Maximum: 0b_1111(15) becomes 0b_11111111(255).
pub fn scale_4bit_to_8bit(input: i32) -> i32 {
    assert!(input >= 0 && input < 16);
    input << 4 | input
}

// Extract base color in the differential mode.
// Return [base_color1, base_color2], where base_color2 is base_color1 + delta.
// See "Khronos Data Format Specification v1.4.0", Section 21, "In the
// 'differential mode'..."
fn parse_differential_base(patch: u64) -> [[i32; 3]; 2] {
    // The bit layout for differential mode is
    // 63..59: base color R
    // 58..56: color delta R
    // 55..51: base color G
    // 50..48: color delta G
    // 47..43: base color B
    // 42..40: color delta B
    // The remaining bits are handled in 'parse_block_metadata'

    // i = 0,1,2 maps to [R, G, B] respectively
    let base_1_5bit = [0, 1, 2].map(|i| {
        let shift = 59 - 8 * i;
        ((patch >> shift) & 0b11111) as i32
    });

    let base_1 = base_1_5bit.map(|i| scale_5bit_to_8bit(i));

    let base_2 = [0, 1, 2].map(|i| {
        let shift = 56 - 8 * i;
        let delta = read_delta_bits(((patch >> shift) & 0b111) as u32);
        let base_2_5bit = (base_1_5bit[i] + delta).clamp(0, 31);
        scale_5bit_to_8bit(base_2_5bit)
    });
    [base_1, base_2]
}

// Parses a 3-bit binary string as a two's complement signed integer.
// The range covered is [-4,3].
pub fn read_delta_bits(input: u32) -> i32 {
    assert!(input < 8);
    if input >= 4 {
        (input as i32) - 8
    } else {
        input as i32
    }
}

// Expands a 5-bit color component to an 8-bit component
// according to the specification.
//
// We want to scale value in [0, 31] to value in [0, 255].
// The ideal scaling  is (input / 31) * 255 ~ input * 8.22580...
// The bitwise operation of this function:
//      (input <<3) | (input >> 2)
// is equivalent to:
//      (input * 2^3) + floor(input / 2^2) ~ input * 8.25.
// The scaling value (8.25) approximates the ideal scaling (8.225...).
//
// In addition, the operation of this function is hardware-friendly and
// correctly maps the endpoints:
// - Minimum: 0b_00000(0) becomes 0b_00000000(0).
// - Maximum: 0b_11111(31) becomes 0b_11111111(255).
pub fn scale_5bit_to_8bit(input: i32) -> i32 {
    assert!(input >= 0 && input < 32);
    input << 3 | input >> 2
}

// Extract block meta-data from the upper half of 64 bits.
// (See "Khronos Data Format Specification v1.4.0", Section21, Table 138)
// There are 2 ways to extract the base colors.
// One is 'individual' mode (See Table 138 part a), and the other is
// 'differential' mode (See Table 138 part b).
pub fn parse_block_metadata(etc1_block: u64) -> BlockMetadata {
    let diff0 = ((etc1_block >> 33) & 0b1) == 0;
    let base =
        if diff0 { parse_individual_base(etc1_block) } else { parse_differential_base(etc1_block) };
    BlockMetadata {
        base,
        table_idx_1: ((etc1_block >> 37) & 0b111) as u32,
        table_idx_2: ((etc1_block >> 34) & 0b111) as u32,
        flip: ((etc1_block >> 32) & 0b1) == 1,
    }
}