| // SPDX-License-Identifier: Apache-2.0 |
| // ---------------------------------------------------------------------------- |
| // Copyright 2011-2021 Arm Limited |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy |
| // of the License at: |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations |
| // under the License. |
| // ---------------------------------------------------------------------------- |
| |
| #include <utility> |
| |
| /** |
| * @brief Functions for color unquantization. |
| */ |
| |
| #include "astcenc_internal.h" |
| |
| /** |
| * @brief Unquantize a color. |
| * |
| * This function uses a lookup table as the quantization is encoded to make |
| * hardware implementations easier, and is not a simple lerp. |
| * |
| * @param quant_level The quantization level to use. |
| * @param inputq The input quantized color. |
| * |
| * @return The unquantized color. |
| */ |
| static ASTCENC_SIMD_INLINE vint4 unquant_color( |
| quant_method quant_level, |
| vint4 inputq |
| ) { |
| const uint8_t* unq = color_unquant_tables[quant_level - QUANT_6]; |
| return vint4(unq[inputq.lane<0>()], unq[inputq.lane<1>()], |
| unq[inputq.lane<2>()], unq[inputq.lane<3>()]); |
| } |
| |
| /** |
| * @brief Determine the quantized value given a quantization level. |
| * |
| * @param quant_level The quantization level to use. |
| * @param value The value to convert. This may be outside of the 0-255 range and will be |
| * clamped before the value is looked up. |
| * |
| * @return The encoded quantized value. These are not necessarily in order; the compressor |
| * scrambles the values slightly to make hardware implementation easier. |
| */ |
| static inline uint8_t unquant_color( |
| quant_method quant_level, |
| int value |
| ) { |
| return color_unquant_tables[quant_level - QUANT_6][value]; |
| } |
| |
| /** |
| * @brief Un-blue-contract a color. |
| * |
| * This function reverses any applied blue contraction. |
| * |
| * @param input The input color that has been blue-contracted. |
| * |
| * @return The uncontracted color. |
| */ |
| static ASTCENC_SIMD_INLINE vint4 uncontract_color( |
| vint4 input |
| ) { |
| vmask4 mask(true, true, false, false); |
| vint4 bc0 = asr<1>(input + input.lane<2>()); |
| return select(input, bc0, mask); |
| } |
| |
| /** |
| * @brief Unpack an LDR RGBA color that uses delta encoding. |
| * |
| * @param input0q The raw quantized endpoint 0 color. |
| * @param input1q The raw quantized endpoint 1 color deltas. |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void rgba_delta_unpack( |
| vint4 input0q, |
| vint4 input1q, |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| // Unquantize color endpoints |
| vint4 input0 = unquant_color(quant_level, input0q); |
| vint4 input1 = unquant_color(quant_level, input1q); |
| |
| // Apply bit transfer |
| bit_transfer_signed(input1, input0); |
| |
| // Apply blue-uncontraction if needed |
| int rgb_sum = hadd_rgb_s(input1); |
| input1 = input1 + input0; |
| if (rgb_sum < 0) |
| { |
| input0 = uncontract_color(input0); |
| input1 = uncontract_color(input1); |
| std::swap(input0, input1); |
| } |
| |
| output0 = clamp(0, 255, input0); |
| output1 = clamp(0, 255, input1); |
| } |
| |
| /** |
| * @brief Unpack an LDR RGB color that uses delta encoding. |
| * |
| * Output alpha set to 255. |
| * |
| * @param input0q The raw quantized endpoint 0 color. |
| * @param input1q The raw quantized endpoint 1 color deltas. |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void rgb_delta_unpack( |
| vint4 input0q, |
| vint4 input1q, |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| rgba_delta_unpack(input0q, input1q, quant_level, output0, output1); |
| output0.set_lane<3>(255); |
| output1.set_lane<3>(255); |
| } |
| |
| /** |
| * @brief Unpack an LDR RGBA color that uses direct encoding. |
| * |
| * @param input0q The raw quantized endpoint 0 color. |
| * @param input1q The raw quantized endpoint 1 color. |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void rgba_unpack( |
| vint4 input0q, |
| vint4 input1q, |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| // Unquantize color endpoints |
| vint4 input0 = unquant_color(quant_level, input0q); |
| vint4 input1 = unquant_color(quant_level, input1q); |
| |
| // Apply blue-uncontraction if needed |
| if (hadd_rgb_s(input0) > hadd_rgb_s(input1)) |
| { |
| input0 = uncontract_color(input0); |
| input1 = uncontract_color(input1); |
| std::swap(input0, input1); |
| } |
| |
| output0 = input0; |
| output1 = input1; |
| } |
| |
| /** |
| * @brief Unpack an LDR RGB color that uses direct encoding. |
| * |
| * Output alpha set to 255. |
| * |
| * @param input0q The raw quantized endpoint 0 color. |
| * @param input1q The raw quantized endpoint 1 color. |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void rgb_unpack( |
| vint4 input0q, |
| vint4 input1q, |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| rgba_unpack(input0q, input1q, quant_level, output0, output1); |
| output0.set_lane<3>(255); |
| output1.set_lane<3>(255); |
| } |
| |
| /** |
| * @brief Unpack an LDR RGBA color that uses scaled encoding. |
| * |
| * Note only the RGB channels use the scaled encoding, alpha uses direct. |
| * |
| * @param input0q The raw quantized endpoint 0 color. |
| * @param alpha1q The raw quantized endpoint 1 alpha value. |
| * @param scaleq The raw quantized scale. |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void rgb_scale_alpha_unpack( |
| vint4 input0q, |
| uint8_t alpha1q, |
| uint8_t scaleq, |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| // Unquantize color endpoints |
| vint4 input = unquant_color(quant_level, input0q); |
| uint8_t alpha1 = unquant_color(quant_level, alpha1q); |
| uint8_t scale = unquant_color(quant_level, scaleq); |
| |
| output1 = input; |
| output1.set_lane<3>(alpha1); |
| |
| output0 = asr<8>(input * scale); |
| output0.set_lane<3>(input.lane<3>()); |
| } |
| |
| /** |
| * @brief Unpack an LDR RGB color that uses scaled encoding. |
| * |
| * Output alpha is 255. |
| * |
| * @param input0q The raw quantized endpoint 0 color. |
| * @param scaleq The raw quantized scale. |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void rgb_scale_unpack( |
| vint4 input0q, |
| int scaleq, |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| vint4 input = unquant_color(quant_level, input0q); |
| int scale = unquant_color(quant_level, scaleq); |
| |
| output1 = input; |
| output1.set_lane<3>(255); |
| |
| output0 = asr<8>(input * scale); |
| output0.set_lane<3>(255); |
| } |
| |
| /** |
| * @brief Unpack an LDR L color that uses direct encoding. |
| * |
| * Output alpha is 255. |
| * |
| * @param input The raw quantized endpoints. |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void luminance_unpack( |
| const uint8_t input[2], |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| int lum0 = unquant_color(quant_level, input[0]); |
| int lum1 = unquant_color(quant_level, input[1]); |
| output0 = vint4(lum0, lum0, lum0, 255); |
| output1 = vint4(lum1, lum1, lum1, 255); |
| } |
| |
| /** |
| * @brief Unpack an LDR L color that uses delta encoding. |
| * |
| * Output alpha is 255. |
| * |
| * @param input The raw quantized endpoints (L0, L1). |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void luminance_delta_unpack( |
| const uint8_t input[2], |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| int v0 = unquant_color(quant_level, input[0]); |
| int v1 = unquant_color(quant_level, input[1]); |
| int l0 = (v0 >> 2) | (v1 & 0xC0); |
| int l1 = l0 + (v1 & 0x3F); |
| |
| l1 = astc::min(l1, 255); |
| |
| output0 = vint4(l0, l0, l0, 255); |
| output1 = vint4(l1, l1, l1, 255); |
| } |
| |
| /** |
| * @brief Unpack an LDR LA color that uses direct encoding. |
| * |
| * @param input The raw quantized endpoints (L0, L1, A0, A1). |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void luminance_alpha_unpack( |
| const uint8_t input[4], |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| int lum0 = unquant_color(quant_level, input[0]); |
| int lum1 = unquant_color(quant_level, input[1]); |
| int alpha0 = unquant_color(quant_level, input[2]); |
| int alpha1 = unquant_color(quant_level, input[3]); |
| output0 = vint4(lum0, lum0, lum0, alpha0); |
| output1 = vint4(lum1, lum1, lum1, alpha1); |
| } |
| |
| /** |
| * @brief Unpack an LDR LA color that uses delta encoding. |
| * |
| * @param input The raw quantized endpoints (L0, L1, A0, A1). |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void luminance_alpha_delta_unpack( |
| const uint8_t input[4], |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| int lum0 = unquant_color(quant_level, input[0]); |
| int lum1 = unquant_color(quant_level, input[1]); |
| int alpha0 = unquant_color(quant_level, input[2]); |
| int alpha1 = unquant_color(quant_level, input[3]); |
| |
| lum0 |= (lum1 & 0x80) << 1; |
| alpha0 |= (alpha1 & 0x80) << 1; |
| lum1 &= 0x7F; |
| alpha1 &= 0x7F; |
| if (lum1 & 0x40) |
| lum1 -= 0x80; |
| if (alpha1 & 0x40) |
| alpha1 -= 0x80; |
| |
| lum0 >>= 1; |
| lum1 >>= 1; |
| alpha0 >>= 1; |
| alpha1 >>= 1; |
| lum1 += lum0; |
| alpha1 += alpha0; |
| |
| lum1 = astc::clamp(lum1, 0, 255); |
| alpha1 = astc::clamp(alpha1, 0, 255); |
| |
| output0 = vint4(lum0, lum0, lum0, alpha0); |
| output1 = vint4(lum1, lum1, lum1, alpha1); |
| } |
| |
| /** |
| * @brief Unpack an HDR RGB + offset encoding. |
| * |
| * @param input The raw quantized endpoints (packed and modal). |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void hdr_rgbo_unpack( |
| const uint8_t input[4], |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| int v0 = unquant_color(quant_level, input[0]); |
| int v1 = unquant_color(quant_level, input[1]); |
| int v2 = unquant_color(quant_level, input[2]); |
| int v3 = unquant_color(quant_level, input[3]); |
| |
| int modeval = ((v0 & 0xC0) >> 6) | (((v1 & 0x80) >> 7) << 2) | (((v2 & 0x80) >> 7) << 3); |
| |
| int majcomp; |
| int mode; |
| if ((modeval & 0xC) != 0xC) |
| { |
| majcomp = modeval >> 2; |
| mode = modeval & 3; |
| } |
| else if (modeval != 0xF) |
| { |
| majcomp = modeval & 3; |
| mode = 4; |
| } |
| else |
| { |
| majcomp = 0; |
| mode = 5; |
| } |
| |
| int red = v0 & 0x3F; |
| int green = v1 & 0x1F; |
| int blue = v2 & 0x1F; |
| int scale = v3 & 0x1F; |
| |
| int bit0 = (v1 >> 6) & 1; |
| int bit1 = (v1 >> 5) & 1; |
| int bit2 = (v2 >> 6) & 1; |
| int bit3 = (v2 >> 5) & 1; |
| int bit4 = (v3 >> 7) & 1; |
| int bit5 = (v3 >> 6) & 1; |
| int bit6 = (v3 >> 5) & 1; |
| |
| int ohcomp = 1 << mode; |
| |
| if (ohcomp & 0x30) |
| green |= bit0 << 6; |
| if (ohcomp & 0x3A) |
| green |= bit1 << 5; |
| if (ohcomp & 0x30) |
| blue |= bit2 << 6; |
| if (ohcomp & 0x3A) |
| blue |= bit3 << 5; |
| |
| if (ohcomp & 0x3D) |
| scale |= bit6 << 5; |
| if (ohcomp & 0x2D) |
| scale |= bit5 << 6; |
| if (ohcomp & 0x04) |
| scale |= bit4 << 7; |
| |
| if (ohcomp & 0x3B) |
| red |= bit4 << 6; |
| if (ohcomp & 0x04) |
| red |= bit3 << 6; |
| |
| if (ohcomp & 0x10) |
| red |= bit5 << 7; |
| if (ohcomp & 0x0F) |
| red |= bit2 << 7; |
| |
| if (ohcomp & 0x05) |
| red |= bit1 << 8; |
| if (ohcomp & 0x0A) |
| red |= bit0 << 8; |
| |
| if (ohcomp & 0x05) |
| red |= bit0 << 9; |
| if (ohcomp & 0x02) |
| red |= bit6 << 9; |
| |
| if (ohcomp & 0x01) |
| red |= bit3 << 10; |
| if (ohcomp & 0x02) |
| red |= bit5 << 10; |
| |
| // expand to 12 bits. |
| static const int shamts[6] { 1, 1, 2, 3, 4, 5 }; |
| int shamt = shamts[mode]; |
| red <<= shamt; |
| green <<= shamt; |
| blue <<= shamt; |
| scale <<= shamt; |
| |
| // on modes 0 to 4, the values stored for "green" and "blue" are differentials, |
| // not absolute values. |
| if (mode != 5) |
| { |
| green = red - green; |
| blue = red - blue; |
| } |
| |
| // switch around components. |
| int temp; |
| switch (majcomp) |
| { |
| case 1: |
| temp = red; |
| red = green; |
| green = temp; |
| break; |
| case 2: |
| temp = red; |
| red = blue; |
| blue = temp; |
| break; |
| default: |
| break; |
| } |
| |
| int red0 = red - scale; |
| int green0 = green - scale; |
| int blue0 = blue - scale; |
| |
| // clamp to [0,0xFFF]. |
| if (red < 0) |
| red = 0; |
| if (green < 0) |
| green = 0; |
| if (blue < 0) |
| blue = 0; |
| |
| if (red0 < 0) |
| red0 = 0; |
| if (green0 < 0) |
| green0 = 0; |
| if (blue0 < 0) |
| blue0 = 0; |
| |
| output0 = vint4(red0 << 4, green0 << 4, blue0 << 4, 0x7800); |
| output1 = vint4(red << 4, green << 4, blue << 4, 0x7800); |
| } |
| |
| /** |
| * @brief Unpack an HDR RGB direct encoding. |
| * |
| * @param input The raw quantized endpoints (packed and modal). |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void hdr_rgb_unpack( |
| const uint8_t input[6], |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| |
| int v0 = unquant_color(quant_level, input[0]); |
| int v1 = unquant_color(quant_level, input[1]); |
| int v2 = unquant_color(quant_level, input[2]); |
| int v3 = unquant_color(quant_level, input[3]); |
| int v4 = unquant_color(quant_level, input[4]); |
| int v5 = unquant_color(quant_level, input[5]); |
| |
| // extract all the fixed-placement bitfields |
| int modeval = ((v1 & 0x80) >> 7) | (((v2 & 0x80) >> 7) << 1) | (((v3 & 0x80) >> 7) << 2); |
| |
| int majcomp = ((v4 & 0x80) >> 7) | (((v5 & 0x80) >> 7) << 1); |
| |
| if (majcomp == 3) |
| { |
| output0 = vint4(v0 << 8, v2 << 8, (v4 & 0x7F) << 9, 0x7800); |
| output1 = vint4(v1 << 8, v3 << 8, (v5 & 0x7F) << 9, 0x7800); |
| return; |
| } |
| |
| int a = v0 | ((v1 & 0x40) << 2); |
| int b0 = v2 & 0x3f; |
| int b1 = v3 & 0x3f; |
| int c = v1 & 0x3f; |
| int d0 = v4 & 0x7f; |
| int d1 = v5 & 0x7f; |
| |
| // get hold of the number of bits in 'd0' and 'd1' |
| static const int dbits_tab[8] { 7, 6, 7, 6, 5, 6, 5, 6 }; |
| int dbits = dbits_tab[modeval]; |
| |
| // extract six variable-placement bits |
| int bit0 = (v2 >> 6) & 1; |
| int bit1 = (v3 >> 6) & 1; |
| int bit2 = (v4 >> 6) & 1; |
| int bit3 = (v5 >> 6) & 1; |
| int bit4 = (v4 >> 5) & 1; |
| int bit5 = (v5 >> 5) & 1; |
| |
| // and prepend the variable-placement bits depending on mode. |
| int ohmod = 1 << modeval; // one-hot-mode |
| if (ohmod & 0xA4) |
| a |= bit0 << 9; |
| if (ohmod & 0x8) |
| a |= bit2 << 9; |
| if (ohmod & 0x50) |
| a |= bit4 << 9; |
| |
| if (ohmod & 0x50) |
| a |= bit5 << 10; |
| if (ohmod & 0xA0) |
| a |= bit1 << 10; |
| |
| if (ohmod & 0xC0) |
| a |= bit2 << 11; |
| |
| if (ohmod & 0x4) |
| c |= bit1 << 6; |
| if (ohmod & 0xE8) |
| c |= bit3 << 6; |
| |
| if (ohmod & 0x20) |
| c |= bit2 << 7; |
| |
| if (ohmod & 0x5B) |
| { |
| b0 |= bit0 << 6; |
| b1 |= bit1 << 6; |
| } |
| |
| if (ohmod & 0x12) |
| { |
| b0 |= bit2 << 7; |
| b1 |= bit3 << 7; |
| } |
| |
| if (ohmod & 0xAF) |
| { |
| d0 |= bit4 << 5; |
| d1 |= bit5 << 5; |
| } |
| |
| if (ohmod & 0x5) |
| { |
| d0 |= bit2 << 6; |
| d1 |= bit3 << 6; |
| } |
| |
| // sign-extend 'd0' and 'd1' |
| // note: this code assumes that signed right-shift actually sign-fills, not zero-fills. |
| int32_t d0x = d0; |
| int32_t d1x = d1; |
| int sx_shamt = 32 - dbits; |
| d0x <<= sx_shamt; |
| d0x >>= sx_shamt; |
| d1x <<= sx_shamt; |
| d1x >>= sx_shamt; |
| d0 = d0x; |
| d1 = d1x; |
| |
| // expand all values to 12 bits, with left-shift as needed. |
| int val_shamt = (modeval >> 1) ^ 3; |
| a <<= val_shamt; |
| b0 <<= val_shamt; |
| b1 <<= val_shamt; |
| c <<= val_shamt; |
| d0 <<= val_shamt; |
| d1 <<= val_shamt; |
| |
| // then compute the actual color values. |
| int red1 = a; |
| int green1 = a - b0; |
| int blue1 = a - b1; |
| int red0 = a - c; |
| int green0 = a - b0 - c - d0; |
| int blue0 = a - b1 - c - d1; |
| |
| // clamp the color components to [0,2^12 - 1] |
| red0 = astc::clamp(red0, 0, 4095); |
| green0 = astc::clamp(green0, 0, 4095); |
| blue0 = astc::clamp(blue0, 0, 4095); |
| |
| red1 = astc::clamp(red1, 0, 4095); |
| green1 = astc::clamp(green1, 0, 4095); |
| blue1 = astc::clamp(blue1, 0, 4095); |
| |
| // switch around the color components |
| int temp0, temp1; |
| switch (majcomp) |
| { |
| case 1: // switch around red and green |
| temp0 = red0; |
| temp1 = red1; |
| red0 = green0; |
| red1 = green1; |
| green0 = temp0; |
| green1 = temp1; |
| break; |
| case 2: // switch around red and blue |
| temp0 = red0; |
| temp1 = red1; |
| red0 = blue0; |
| red1 = blue1; |
| blue0 = temp0; |
| blue1 = temp1; |
| break; |
| case 0: // no switch |
| break; |
| } |
| |
| output0 = vint4(red0 << 4, green0 << 4, blue0 << 4, 0x7800); |
| output1 = vint4(red1 << 4, green1 << 4, blue1 << 4, 0x7800); |
| } |
| |
| /** |
| * @brief Unpack an HDR RGB + LDR A direct encoding. |
| * |
| * @param input The raw quantized endpoints (packed and modal). |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void hdr_rgb_ldr_alpha_unpack( |
| const uint8_t input[8], |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| hdr_rgb_unpack(input, quant_level, output0, output1); |
| |
| int v6 = unquant_color(quant_level, input[6]); |
| int v7 = unquant_color(quant_level, input[7]); |
| output0.set_lane<3>(v6); |
| output1.set_lane<3>(v7); |
| } |
| |
| /** |
| * @brief Unpack an HDR L (small range) direct encoding. |
| * |
| * @param input The raw quantized endpoints (packed and modal). |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void hdr_luminance_small_range_unpack( |
| const uint8_t input[2], |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| int v0 = unquant_color(quant_level, input[0]); |
| int v1 = unquant_color(quant_level, input[1]); |
| |
| int y0, y1; |
| if (v0 & 0x80) |
| { |
| y0 = ((v1 & 0xE0) << 4) | ((v0 & 0x7F) << 2); |
| y1 = (v1 & 0x1F) << 2; |
| } |
| else |
| { |
| y0 = ((v1 & 0xF0) << 4) | ((v0 & 0x7F) << 1); |
| y1 = (v1 & 0xF) << 1; |
| } |
| |
| y1 += y0; |
| if (y1 > 0xFFF) |
| y1 = 0xFFF; |
| |
| output0 = vint4(y0 << 4, y0 << 4, y0 << 4, 0x7800); |
| output1 = vint4(y1 << 4, y1 << 4, y1 << 4, 0x7800); |
| } |
| |
| /** |
| * @brief Unpack an HDR L (large range) direct encoding. |
| * |
| * @param input The raw quantized endpoints (packed and modal). |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void hdr_luminance_large_range_unpack( |
| const uint8_t input[2], |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| int v0 = unquant_color(quant_level, input[0]); |
| int v1 = unquant_color(quant_level, input[1]); |
| |
| int y0, y1; |
| if (v1 >= v0) |
| { |
| y0 = v0 << 4; |
| y1 = v1 << 4; |
| } |
| else |
| { |
| y0 = (v1 << 4) + 8; |
| y1 = (v0 << 4) - 8; |
| } |
| |
| output0 = vint4(y0 << 4, y0 << 4, y0 << 4, 0x7800); |
| output1 = vint4(y1 << 4, y1 << 4, y1 << 4, 0x7800); |
| } |
| |
| /** |
| * @brief Unpack an HDR A direct encoding. |
| * |
| * @param input The raw quantized endpoints (packed and modal). |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void hdr_alpha_unpack( |
| const uint8_t input[2], |
| quant_method quant_level, |
| int& output0, |
| int& output1 |
| ) { |
| |
| int v6 = unquant_color(quant_level, input[0]); |
| int v7 = unquant_color(quant_level, input[1]); |
| |
| int selector = ((v6 >> 7) & 1) | ((v7 >> 6) & 2); |
| v6 &= 0x7F; |
| v7 &= 0x7F; |
| if (selector == 3) |
| { |
| output0 = v6 << 5; |
| output1 = v7 << 5; |
| } |
| else |
| { |
| v6 |= (v7 << (selector + 1)) & 0x780; |
| v7 &= (0x3f >> selector); |
| v7 ^= 32 >> selector; |
| v7 -= 32 >> selector; |
| v6 <<= (4 - selector); |
| v7 <<= (4 - selector); |
| v7 += v6; |
| |
| if (v7 < 0) |
| v7 = 0; |
| else if (v7 > 0xFFF) |
| v7 = 0xFFF; |
| |
| output0 = v6; |
| output1 = v7; |
| } |
| |
| output0 <<= 4; |
| output1 <<= 4; |
| } |
| |
| /** |
| * @brief Unpack an HDR RGBA direct encoding. |
| * |
| * @param input The raw quantized endpoints (packed and modal). |
| * @param quant_level The quantization level to use. |
| * @param[out] output0 The unpacked and unquantized endpoint 0 color. |
| * @param[out] output1 The unpacked and unquantized endpoint 1 color. |
| */ |
| static void hdr_rgb_hdr_alpha_unpack( |
| const uint8_t input[8], |
| quant_method quant_level, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| hdr_rgb_unpack(input, quant_level, output0, output1); |
| |
| int alpha0, alpha1; |
| hdr_alpha_unpack(input + 6, quant_level, alpha0, alpha1); |
| |
| output0.set_lane<3>(alpha0); |
| output1.set_lane<3>(alpha1); |
| } |
| |
| /* See header for documentation. */ |
| void unpack_color_endpoints( |
| astcenc_profile decode_mode, |
| int format, |
| quant_method quant_level, |
| const uint8_t* input, |
| bool& rgb_hdr, |
| bool& alpha_hdr, |
| vint4& output0, |
| vint4& output1 |
| ) { |
| // Assume no NaNs and LDR endpoints unless set later |
| rgb_hdr = false; |
| alpha_hdr = false; |
| |
| bool alpha_hdr_default = false; |
| |
| switch (format) |
| { |
| case FMT_LUMINANCE: |
| luminance_unpack(input, quant_level, output0, output1); |
| break; |
| |
| case FMT_LUMINANCE_DELTA: |
| luminance_delta_unpack(input, quant_level, output0, output1); |
| break; |
| |
| case FMT_HDR_LUMINANCE_SMALL_RANGE: |
| rgb_hdr = true; |
| alpha_hdr_default = true; |
| hdr_luminance_small_range_unpack(input, quant_level, output0, output1); |
| break; |
| |
| case FMT_HDR_LUMINANCE_LARGE_RANGE: |
| rgb_hdr = true; |
| alpha_hdr_default = true; |
| hdr_luminance_large_range_unpack(input, quant_level, output0, output1); |
| break; |
| |
| case FMT_LUMINANCE_ALPHA: |
| luminance_alpha_unpack(input, quant_level, output0, output1); |
| break; |
| |
| case FMT_LUMINANCE_ALPHA_DELTA: |
| luminance_alpha_delta_unpack(input, quant_level, output0, output1); |
| break; |
| |
| case FMT_RGB_SCALE: |
| { |
| vint4 input0q(input[0], input[1], input[2], 0); |
| uint8_t scale = input[3]; |
| rgb_scale_unpack(input0q, scale, quant_level, output0, output1); |
| } |
| break; |
| |
| case FMT_RGB_SCALE_ALPHA: |
| { |
| vint4 input0q(input[0], input[1], input[2], input[4]); |
| uint8_t alpha1q = input[5]; |
| uint8_t scaleq = input[3]; |
| rgb_scale_alpha_unpack(input0q, alpha1q, scaleq, quant_level, output0, output1); |
| } |
| break; |
| |
| case FMT_HDR_RGB_SCALE: |
| rgb_hdr = true; |
| alpha_hdr_default = true; |
| hdr_rgbo_unpack(input, quant_level,output0, output1); |
| break; |
| |
| case FMT_RGB: |
| { |
| vint4 input0q(input[0], input[2], input[4], 0); |
| vint4 input1q(input[1], input[3], input[5], 0); |
| rgb_unpack(input0q, input1q, quant_level, output0, output1); |
| } |
| break; |
| |
| case FMT_RGB_DELTA: |
| { |
| vint4 input0q(input[0], input[2], input[4], 0); |
| vint4 input1q(input[1], input[3], input[5], 0); |
| rgb_delta_unpack(input0q, input1q, quant_level, output0, output1); |
| } |
| break; |
| |
| case FMT_HDR_RGB: |
| rgb_hdr = true; |
| alpha_hdr_default = true; |
| hdr_rgb_unpack(input, quant_level, output0, output1); |
| break; |
| |
| case FMT_RGBA: |
| { |
| vint4 input0q(input[0], input[2], input[4], input[6]); |
| vint4 input1q(input[1], input[3], input[5], input[7]); |
| rgba_unpack(input0q, input1q, quant_level, output0, output1); |
| } |
| break; |
| |
| case FMT_RGBA_DELTA: |
| { |
| vint4 input0q(input[0], input[2], input[4], input[6]); |
| vint4 input1q(input[1], input[3], input[5], input[7]); |
| rgba_delta_unpack(input0q, input1q, quant_level, output0, output1); |
| } |
| break; |
| |
| case FMT_HDR_RGB_LDR_ALPHA: |
| rgb_hdr = true; |
| hdr_rgb_ldr_alpha_unpack(input, quant_level, output0, output1); |
| break; |
| |
| case FMT_HDR_RGBA: |
| rgb_hdr = true; |
| alpha_hdr = true; |
| hdr_rgb_hdr_alpha_unpack(input, quant_level, output0, output1); |
| break; |
| } |
| |
| // Assign a correct default alpha |
| if (alpha_hdr_default) |
| { |
| if (decode_mode == ASTCENC_PRF_HDR) |
| { |
| output0.set_lane<3>(0x7800); |
| output1.set_lane<3>(0x7800); |
| alpha_hdr = true; |
| } |
| else |
| { |
| output0.set_lane<3>(0x00FF); |
| output1.set_lane<3>(0x00FF); |
| alpha_hdr = false; |
| } |
| } |
| |
| vint4 ldr_scale(257); |
| vint4 hdr_scale(1); |
| vint4 output_scale = ldr_scale; |
| |
| // An LDR profile image |
| if ((decode_mode == ASTCENC_PRF_LDR) || |
| (decode_mode == ASTCENC_PRF_LDR_SRGB)) |
| { |
| // Also matches HDR alpha, as cannot have HDR alpha without HDR RGB |
| if (rgb_hdr == true) |
| { |
| output0 = vint4(0xFF00, 0x0000, 0xFF00, 0xFF00); |
| output1 = vint4(0xFF00, 0x0000, 0xFF00, 0xFF00); |
| output_scale = hdr_scale; |
| |
| rgb_hdr = false; |
| alpha_hdr = false; |
| } |
| } |
| // An HDR profile image |
| else |
| { |
| vmask4 hdr_lanes(rgb_hdr, rgb_hdr, rgb_hdr, alpha_hdr); |
| output_scale = select(ldr_scale, hdr_scale, hdr_lanes); |
| } |
| |
| output0 = output0 * output_scale; |
| output1 = output1 * output_scale; |
| } |