Path: blob/master/thirdparty/basis_universal/encoder/basisu_etc.h
9904 views
// basis_etc.h1// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14#pragma once15#include "../transcoder/basisu.h"16#include "basisu_enc.h"1718namespace basisu19{20enum etc_constants21{22cETC1BytesPerBlock = 8U,2324cETC1SelectorBits = 2U,25cETC1SelectorValues = 1U << cETC1SelectorBits,26cETC1SelectorMask = cETC1SelectorValues - 1U,2728cETC1BlockShift = 2U,29cETC1BlockSize = 1U << cETC1BlockShift,3031cETC1LSBSelectorIndicesBitOffset = 0,32cETC1MSBSelectorIndicesBitOffset = 16,3334cETC1FlipBitOffset = 32,35cETC1DiffBitOffset = 33,3637cETC1IntenModifierNumBits = 3,38cETC1IntenModifierValues = 1 << cETC1IntenModifierNumBits,39cETC1RightIntenModifierTableBitOffset = 34,40cETC1LeftIntenModifierTableBitOffset = 37,4142// Base+Delta encoding (5 bit bases, 3 bit delta)43cETC1BaseColorCompNumBits = 5,44cETC1BaseColorCompMax = 1 << cETC1BaseColorCompNumBits,4546cETC1DeltaColorCompNumBits = 3,47cETC1DeltaColorComp = 1 << cETC1DeltaColorCompNumBits,48cETC1DeltaColorCompMax = 1 << cETC1DeltaColorCompNumBits,4950cETC1BaseColor5RBitOffset = 59,51cETC1BaseColor5GBitOffset = 51,52cETC1BaseColor5BBitOffset = 43,5354cETC1DeltaColor3RBitOffset = 56,55cETC1DeltaColor3GBitOffset = 48,56cETC1DeltaColor3BBitOffset = 40,5758// Absolute (non-delta) encoding (two 4-bit per component bases)59cETC1AbsColorCompNumBits = 4,60cETC1AbsColorCompMax = 1 << cETC1AbsColorCompNumBits,6162cETC1AbsColor4R1BitOffset = 60,63cETC1AbsColor4G1BitOffset = 52,64cETC1AbsColor4B1BitOffset = 44,6566cETC1AbsColor4R2BitOffset = 56,67cETC1AbsColor4G2BitOffset = 48,68cETC1AbsColor4B2BitOffset = 40,6970cETC1ColorDeltaMin = -4,71cETC1ColorDeltaMax = 3,7273// Delta3:74// 0 1 2 3 4 5 6 775// 000 001 010 011 100 101 110 11176// 0 1 2 3 -4 -3 -2 -177};7879extern const int g_etc1_inten_tables[cETC1IntenModifierValues][cETC1SelectorValues];80extern const uint8_t g_etc1_to_selector_index[cETC1SelectorValues];81extern const uint8_t g_selector_index_to_etc1[cETC1SelectorValues];8283struct etc_coord284{85uint8_t m_x, m_y;86};87extern const etc_coord2 g_etc1_pixel_coords[2][2][8]; // [flipped][subblock][subblock_pixel]88extern const uint32_t g_etc1_pixel_indices[2][2][8]; // [flipped][subblock][subblock_pixel]8990struct etc_block91{92// big endian uint64:93// bit ofs: 56 48 40 32 24 16 8 094// byte ofs: b0, b1, b2, b3, b4, b5, b6, b795union96{97uint64_t m_uint64;9899uint8_t m_bytes[8];100};101102inline void clear()103{104assert(sizeof(*this) == 8);105clear_obj(*this);106}107108inline uint64_t get_all_bits() const109{110return read_be64(&m_uint64);111}112113inline uint32_t get_general_bits(uint32_t ofs, uint32_t num) const114{115assert((ofs + num) <= 64U);116assert(num && (num < 32U));117return (uint32_t)(read_be64(&m_uint64) >> ofs) & ((1UL << num) - 1UL);118}119120inline void set_general_bits(uint32_t ofs, uint32_t num, uint32_t bits)121{122assert((ofs + num) <= 64U);123assert(num && (num < 32U));124125uint64_t x = read_be64(&m_uint64);126uint64_t msk = ((1ULL << static_cast<uint64_t>(num)) - 1ULL) << static_cast<uint64_t>(ofs);127x &= ~msk;128x |= (static_cast<uint64_t>(bits) << static_cast<uint64_t>(ofs));129write_be64(&m_uint64, x);130}131132inline uint32_t get_byte_bits(uint32_t ofs, uint32_t num) const133{134assert((ofs + num) <= 64U);135assert(num && (num <= 8U));136assert((ofs >> 3) == ((ofs + num - 1) >> 3));137const uint32_t byte_ofs = 7 - (ofs >> 3);138const uint32_t byte_bit_ofs = ofs & 7;139return (m_bytes[byte_ofs] >> byte_bit_ofs) & ((1 << num) - 1);140}141142inline void set_byte_bits(uint32_t ofs, uint32_t num, uint32_t bits)143{144assert((ofs + num) <= 64U);145assert(num && (num < 32U));146assert((ofs >> 3) == ((ofs + num - 1) >> 3));147assert(bits < (1U << num));148const uint32_t byte_ofs = 7 - (ofs >> 3);149const uint32_t byte_bit_ofs = ofs & 7;150const uint32_t mask = (1 << num) - 1;151m_bytes[byte_ofs] &= ~(mask << byte_bit_ofs);152m_bytes[byte_ofs] |= (bits << byte_bit_ofs);153}154155// false = left/right subblocks156// true = upper/lower subblocks157inline bool get_flip_bit() const158{159return (m_bytes[3] & 1) != 0;160}161162inline void set_flip_bit(bool flip)163{164m_bytes[3] &= ~1;165m_bytes[3] |= static_cast<uint8_t>(flip);166}167168inline bool get_diff_bit() const169{170return (m_bytes[3] & 2) != 0;171}172173inline void set_diff_bit(bool diff)174{175m_bytes[3] &= ~2;176m_bytes[3] |= (static_cast<uint32_t>(diff) << 1);177}178179// Returns intensity modifier table (0-7) used by subblock subblock_id.180// subblock_id=0 left/top (CW 1), 1=right/bottom (CW 2)181inline uint32_t get_inten_table(uint32_t subblock_id) const182{183assert(subblock_id < 2);184const uint32_t ofs = subblock_id ? 2 : 5;185return (m_bytes[3] >> ofs) & 7;186}187188// Sets intensity modifier table (0-7) used by subblock subblock_id (0 or 1)189inline void set_inten_table(uint32_t subblock_id, uint32_t t)190{191assert(subblock_id < 2);192assert(t < 8);193const uint32_t ofs = subblock_id ? 2 : 5;194m_bytes[3] &= ~(7 << ofs);195m_bytes[3] |= (t << ofs);196}197198inline void set_inten_tables_etc1s(uint32_t t)199{200set_inten_table(0, t);201set_inten_table(1, t);202}203204inline bool is_etc1s() const205{206if (get_inten_table(0) != get_inten_table(1))207return false;208209if (get_diff_bit())210{211if (get_delta3_color() != 0)212return false;213}214else215{216if (get_base4_color(0) != get_base4_color(1))217return false;218}219220return true;221}222223// Returned encoded selector value ranges from 0-3 (this is NOT a direct index into g_etc1_inten_tables, see get_selector())224inline uint32_t get_raw_selector(uint32_t x, uint32_t y) const225{226assert((x | y) < 4);227228const uint32_t bit_index = x * 4 + y;229const uint32_t byte_bit_ofs = bit_index & 7;230const uint8_t *p = &m_bytes[7 - (bit_index >> 3)];231const uint32_t lsb = (p[0] >> byte_bit_ofs) & 1;232const uint32_t msb = (p[-2] >> byte_bit_ofs) & 1;233const uint32_t val = lsb | (msb << 1);234235return val;236}237238// Returned selector value ranges from 0-3 and is a direct index into g_etc1_inten_tables.239inline uint32_t get_selector(uint32_t x, uint32_t y) const240{241return g_etc1_to_selector_index[get_raw_selector(x, y)];242}243244// Selector "val" ranges from 0-3 and is a direct index into g_etc1_inten_tables.245inline void set_selector(uint32_t x, uint32_t y, uint32_t val)246{247assert((x | y | val) < 4);248const uint32_t bit_index = x * 4 + y;249250uint8_t *p = &m_bytes[7 - (bit_index >> 3)];251252const uint32_t byte_bit_ofs = bit_index & 7;253const uint32_t mask = 1 << byte_bit_ofs;254255const uint32_t etc1_val = g_selector_index_to_etc1[val];256257const uint32_t lsb = etc1_val & 1;258const uint32_t msb = etc1_val >> 1;259260p[0] &= ~mask;261p[0] |= (lsb << byte_bit_ofs);262263p[-2] &= ~mask;264p[-2] |= (msb << byte_bit_ofs);265}266267// Selector "etc1_val" ranges from 0-3 and is a direct (raw) ETC1 selector.268inline void set_raw_selector(uint32_t x, uint32_t y, uint32_t etc1_val)269{270assert((x | y | etc1_val) < 4);271const uint32_t bit_index = x * 4 + y;272273uint8_t* p = &m_bytes[7 - (bit_index >> 3)];274275const uint32_t byte_bit_ofs = bit_index & 7;276const uint32_t mask = 1 << byte_bit_ofs;277278const uint32_t lsb = etc1_val & 1;279const uint32_t msb = etc1_val >> 1;280281p[0] &= ~mask;282p[0] |= (lsb << byte_bit_ofs);283284p[-2] &= ~mask;285p[-2] |= (msb << byte_bit_ofs);286}287288inline uint32_t get_raw_selector_bits() const289{290return m_bytes[4] | (m_bytes[5] << 8) | (m_bytes[6] << 16) | (m_bytes[7] << 24);291}292293inline void set_raw_selector_bits(uint32_t bits)294{295m_bytes[4] = static_cast<uint8_t>(bits);296m_bytes[5] = static_cast<uint8_t>(bits >> 8);297m_bytes[6] = static_cast<uint8_t>(bits >> 16);298m_bytes[7] = static_cast<uint8_t>(bits >> 24);299}300301inline void set_raw_selector_bits(uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3)302{303m_bytes[4] = byte0;304m_bytes[5] = byte1;305m_bytes[6] = byte2;306m_bytes[7] = byte3;307}308309inline void set_base4_color(uint32_t idx, uint16_t c)310{311if (idx)312{313set_byte_bits(cETC1AbsColor4R2BitOffset, 4, (c >> 8) & 15);314set_byte_bits(cETC1AbsColor4G2BitOffset, 4, (c >> 4) & 15);315set_byte_bits(cETC1AbsColor4B2BitOffset, 4, c & 15);316}317else318{319set_byte_bits(cETC1AbsColor4R1BitOffset, 4, (c >> 8) & 15);320set_byte_bits(cETC1AbsColor4G1BitOffset, 4, (c >> 4) & 15);321set_byte_bits(cETC1AbsColor4B1BitOffset, 4, c & 15);322}323}324325inline uint16_t get_base4_color(uint32_t idx) const326{327uint32_t r, g, b;328if (idx)329{330r = get_byte_bits(cETC1AbsColor4R2BitOffset, 4);331g = get_byte_bits(cETC1AbsColor4G2BitOffset, 4);332b = get_byte_bits(cETC1AbsColor4B2BitOffset, 4);333}334else335{336r = get_byte_bits(cETC1AbsColor4R1BitOffset, 4);337g = get_byte_bits(cETC1AbsColor4G1BitOffset, 4);338b = get_byte_bits(cETC1AbsColor4B1BitOffset, 4);339}340return static_cast<uint16_t>(b | (g << 4U) | (r << 8U));341}342343inline void set_base5_color(uint16_t c)344{345set_byte_bits(cETC1BaseColor5RBitOffset, 5, (c >> 10) & 31);346set_byte_bits(cETC1BaseColor5GBitOffset, 5, (c >> 5) & 31);347set_byte_bits(cETC1BaseColor5BBitOffset, 5, c & 31);348}349350inline uint16_t get_base5_color() const351{352const uint32_t r = get_byte_bits(cETC1BaseColor5RBitOffset, 5);353const uint32_t g = get_byte_bits(cETC1BaseColor5GBitOffset, 5);354const uint32_t b = get_byte_bits(cETC1BaseColor5BBitOffset, 5);355return static_cast<uint16_t>(b | (g << 5U) | (r << 10U));356}357358void set_delta3_color(uint16_t c)359{360set_byte_bits(cETC1DeltaColor3RBitOffset, 3, (c >> 6) & 7);361set_byte_bits(cETC1DeltaColor3GBitOffset, 3, (c >> 3) & 7);362set_byte_bits(cETC1DeltaColor3BBitOffset, 3, c & 7);363}364365inline uint16_t get_delta3_color() const366{367const uint32_t r = get_byte_bits(cETC1DeltaColor3RBitOffset, 3);368const uint32_t g = get_byte_bits(cETC1DeltaColor3GBitOffset, 3);369const uint32_t b = get_byte_bits(cETC1DeltaColor3BBitOffset, 3);370return static_cast<uint16_t>(b | (g << 3U) | (r << 6U));371}372373uint64_t determine_selectors(const color_rgba* pSource_pixels, bool perceptual, uint32_t begin_subblock = 0, uint32_t end_subblock = 2)374{375uint64_t total_error = 0;376377for (uint32_t subblock = begin_subblock; subblock < end_subblock; subblock++)378{379color_rgba block_colors[4];380get_block_colors(block_colors, subblock);381382if (get_flip_bit())383{384for (uint32_t y = 0; y < 2; y++)385{386for (uint32_t x = 0; x < 4; x++)387{388uint32_t best_selector = 0;389uint64_t best_error = UINT64_MAX;390391for (uint32_t s = 0; s < 4; s++)392{393uint64_t err = color_distance(perceptual, block_colors[s], pSource_pixels[x + (subblock * 2 + y) * 4], false);394if (err < best_error)395{396best_error = err;397best_selector = s;398}399}400401set_selector(x, subblock * 2 + y, best_selector);402403total_error += best_error;404}405}406}407else408{409for (uint32_t y = 0; y < 4; y++)410{411for (uint32_t x = 0; x < 2; x++)412{413uint32_t best_selector = 0;414uint64_t best_error = UINT64_MAX;415416for (uint32_t s = 0; s < 4; s++)417{418uint64_t err = color_distance(perceptual, block_colors[s], pSource_pixels[(subblock * 2) + x + y * 4], false);419if (err < best_error)420{421best_error = err;422best_selector = s;423}424}425426set_selector(subblock * 2 + x, y, best_selector);427428total_error += best_error;429}430}431}432}433434return total_error;435}436437color_rgba get_block_color(uint32_t subblock_index, bool scaled) const438{439color_rgba b;440441if (get_diff_bit())442{443if (subblock_index)444unpack_color5(b, get_base5_color(), get_delta3_color(), scaled);445else446unpack_color5(b, get_base5_color(), scaled);447}448else449{450b = unpack_color4(get_base4_color(subblock_index), scaled);451}452453return b;454}455456uint32_t get_subblock_index(uint32_t x, uint32_t y) const457{458if (get_flip_bit())459return y >= 2;460else461return x >= 2;462}463464bool get_block_colors(color_rgba* pBlock_colors, uint32_t subblock_index) const465{466color_rgba b;467468if (get_diff_bit())469{470if (subblock_index)471unpack_color5(b, get_base5_color(), get_delta3_color(), true);472else473unpack_color5(b, get_base5_color(), true);474}475else476{477b = unpack_color4(get_base4_color(subblock_index), true);478}479480const int* pInten_table = g_etc1_inten_tables[get_inten_table(subblock_index)];481482bool dc = false;483484pBlock_colors[0].set(clamp255(b.r + pInten_table[0], dc), clamp255(b.g + pInten_table[0], dc), clamp255(b.b + pInten_table[0], dc), 255);485pBlock_colors[1].set(clamp255(b.r + pInten_table[1], dc), clamp255(b.g + pInten_table[1], dc), clamp255(b.b + pInten_table[1], dc), 255);486pBlock_colors[2].set(clamp255(b.r + pInten_table[2], dc), clamp255(b.g + pInten_table[2], dc), clamp255(b.b + pInten_table[2], dc), 255);487pBlock_colors[3].set(clamp255(b.r + pInten_table[3], dc), clamp255(b.g + pInten_table[3], dc), clamp255(b.b + pInten_table[3], dc), 255);488489return dc;490}491492void get_block_colors_etc1s(color_rgba* pBlock_colors) const493{494color_rgba b;495496unpack_color5(b, get_base5_color(), true);497498const int* pInten_table = g_etc1_inten_tables[get_inten_table(0)];499500pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);501pBlock_colors[1].set(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);502pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);503pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);504}505506static void get_block_colors_etc1s(color_rgba* pBlock_colors, const color_rgba &base5_color, uint32_t inten_table)507{508color_rgba b;509b.r = (base5_color.r << 3U) | (base5_color.r >> 2U);510b.g = (base5_color.g << 3U) | (base5_color.g >> 2U);511b.b = (base5_color.b << 3U) | (base5_color.b >> 2U);512513const int* pInten_table = g_etc1_inten_tables[inten_table];514515pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);516pBlock_colors[1].set(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);517pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);518pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);519}520521void get_block_color(color_rgba& color, uint32_t subblock_index, uint32_t selector_index) const522{523color_rgba b;524525if (get_diff_bit())526{527if (subblock_index)528unpack_color5(b, get_base5_color(), get_delta3_color(), true);529else530unpack_color5(b, get_base5_color(), true);531}532else533{534b = unpack_color4(get_base4_color(subblock_index), true);535}536537const int* pInten_table = g_etc1_inten_tables[get_inten_table(subblock_index)];538539color.set(clamp255(b.r + pInten_table[selector_index]), clamp255(b.g + pInten_table[selector_index]), clamp255(b.b + pInten_table[selector_index]), 255);540}541542bool get_block_low_high_colors(color_rgba* pBlock_colors, uint32_t subblock_index) const543{544color_rgba b;545546if (get_diff_bit())547{548if (subblock_index)549unpack_color5(b, get_base5_color(), get_delta3_color(), true);550else551unpack_color5(b, get_base5_color(), true);552}553else554{555b = unpack_color4(get_base4_color(subblock_index), true);556}557558const int* pInten_table = g_etc1_inten_tables[get_inten_table(subblock_index)];559560bool dc = false;561562pBlock_colors[0].set(clamp255(b.r + pInten_table[0], dc), clamp255(b.g + pInten_table[0], dc), clamp255(b.b + pInten_table[0], dc), 255);563pBlock_colors[1].set(clamp255(b.r + pInten_table[3], dc), clamp255(b.g + pInten_table[3], dc), clamp255(b.b + pInten_table[3], dc), 255);564565return dc;566}567568static void get_block_colors5(color_rgba *pBlock_colors, const color_rgba &base_color5, uint32_t inten_table, bool scaled = false)569{570color_rgba b(base_color5);571572if (!scaled)573{574b.r = (b.r << 3) | (b.r >> 2);575b.g = (b.g << 3) | (b.g >> 2);576b.b = (b.b << 3) | (b.b >> 2);577}578579const int* pInten_table = g_etc1_inten_tables[inten_table];580581pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);582pBlock_colors[1].set(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);583pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);584pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);585}586587static void get_block_colors4(color_rgba *pBlock_colors, const color_rgba &base_color4, uint32_t inten_table, bool scaled = false)588{589color_rgba b(base_color4);590591if (!scaled)592{593b.r = (b.r << 4) | b.r;594b.g = (b.g << 4) | b.g;595b.b = (b.b << 4) | b.b;596}597598const int* pInten_table = g_etc1_inten_tables[inten_table];599600pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);601pBlock_colors[1].set(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);602pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);603pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);604}605606uint64_t evaluate_etc1_error(const color_rgba* pBlock_pixels, bool perceptual, int subblock_index = -1) const;607void get_subblock_pixels(color_rgba* pPixels, int subblock_index = -1) const;608609void get_selector_range(uint32_t& low, uint32_t& high) const610{611low = 3;612high = 0;613for (uint32_t y = 0; y < 4; y++)614{615for (uint32_t x = 0; x < 4; x++)616{617const uint32_t s = get_selector(x, y);618low = minimum(low, s);619high = maximum(high, s);620}621}622}623624void set_block_color4(const color_rgba &c0_unscaled, const color_rgba &c1_unscaled)625{626set_diff_bit(false);627628set_base4_color(0, pack_color4(c0_unscaled, false));629set_base4_color(1, pack_color4(c1_unscaled, false));630}631632void set_block_color5(const color_rgba &c0_unscaled, const color_rgba &c1_unscaled)633{634set_diff_bit(true);635636set_base5_color(pack_color5(c0_unscaled, false));637638int dr = c1_unscaled.r - c0_unscaled.r;639int dg = c1_unscaled.g - c0_unscaled.g;640int db = c1_unscaled.b - c0_unscaled.b;641642set_delta3_color(pack_delta3(dr, dg, db));643}644645void set_block_color5_etc1s(const color_rgba &c_unscaled)646{647set_diff_bit(true);648649set_base5_color(pack_color5(c_unscaled, false));650set_delta3_color(pack_delta3(0, 0, 0));651}652653bool set_block_color5_check(const color_rgba &c0_unscaled, const color_rgba &c1_unscaled)654{655set_diff_bit(true);656657set_base5_color(pack_color5(c0_unscaled, false));658659int dr = c1_unscaled.r - c0_unscaled.r;660int dg = c1_unscaled.g - c0_unscaled.g;661int db = c1_unscaled.b - c0_unscaled.b;662663if (((dr < cETC1ColorDeltaMin) || (dr > cETC1ColorDeltaMax)) ||664((dg < cETC1ColorDeltaMin) || (dg > cETC1ColorDeltaMax)) ||665((db < cETC1ColorDeltaMin) || (db > cETC1ColorDeltaMax)))666return false;667668set_delta3_color(pack_delta3(dr, dg, db));669670return true;671}672673bool set_block_color5_clamp(const color_rgba &c0_unscaled, const color_rgba &c1_unscaled)674{675set_diff_bit(true);676set_base5_color(pack_color5(c0_unscaled, false));677678int dr = c1_unscaled.r - c0_unscaled.r;679int dg = c1_unscaled.g - c0_unscaled.g;680int db = c1_unscaled.b - c0_unscaled.b;681682dr = clamp<int>(dr, cETC1ColorDeltaMin, cETC1ColorDeltaMax);683dg = clamp<int>(dg, cETC1ColorDeltaMin, cETC1ColorDeltaMax);684db = clamp<int>(db, cETC1ColorDeltaMin, cETC1ColorDeltaMax);685686set_delta3_color(pack_delta3(dr, dg, db));687688return true;689}690color_rgba get_selector_color(uint32_t x, uint32_t y, uint32_t s) const691{692color_rgba block_colors[4];693694get_block_colors(block_colors, get_subblock_index(x, y));695696return block_colors[s];697}698699// Base color 5700static uint16_t pack_color5(const color_rgba& color, bool scaled, uint32_t bias = 127U);701static uint16_t pack_color5(uint32_t r, uint32_t g, uint32_t b, bool scaled, uint32_t bias = 127U);702703static color_rgba unpack_color5(uint16_t packed_color5, bool scaled, uint32_t alpha = 255U);704static void unpack_color5(uint32_t& r, uint32_t& g, uint32_t& b, uint16_t packed_color, bool scaled);705static void unpack_color5(color_rgba& result, uint16_t packed_color5, bool scaled);706707static bool unpack_color5(color_rgba& result, uint16_t packed_color5, uint16_t packed_delta3, bool scaled, uint32_t alpha = 255U);708static bool unpack_color5(uint32_t& r, uint32_t& g, uint32_t& b, uint16_t packed_color5, uint16_t packed_delta3, bool scaled, uint32_t alpha = 255U);709710// Delta color 3711// Inputs range from -4 to 3 (cETC1ColorDeltaMin to cETC1ColorDeltaMax)712static uint16_t pack_delta3(const color_rgba_i16& color);713static uint16_t pack_delta3(int r, int g, int b);714715// Results range from -4 to 3 (cETC1ColorDeltaMin to cETC1ColorDeltaMax)716static color_rgba_i16 unpack_delta3(uint16_t packed_delta3);717static void unpack_delta3(int& r, int& g, int& b, uint16_t packed_delta3);718719static bool try_pack_color5_delta3(const color_rgba *pColor5_unscaled)720{721int dr = pColor5_unscaled[1].r - pColor5_unscaled[0].r;722int dg = pColor5_unscaled[1].g - pColor5_unscaled[0].g;723int db = pColor5_unscaled[1].b - pColor5_unscaled[0].b;724725if ((minimum(dr, dg, db) < cETC1ColorDeltaMin) || (maximum(dr, dg, db) > cETC1ColorDeltaMax))726return false;727728return true;729}730731// Abs color 4732static uint16_t pack_color4(const color_rgba& color, bool scaled, uint32_t bias = 127U);733static uint16_t pack_color4(uint32_t r, uint32_t g, uint32_t b, bool scaled, uint32_t bias = 127U);734735static color_rgba unpack_color4(uint16_t packed_color4, bool scaled, uint32_t alpha = 255U);736static void unpack_color4(uint32_t& r, uint32_t& g, uint32_t& b, uint16_t packed_color4, bool scaled);737738// subblock colors739static void get_diff_subblock_colors(color_rgba* pDst, uint16_t packed_color5, uint32_t table_idx);740static bool get_diff_subblock_colors(color_rgba* pDst, uint16_t packed_color5, uint16_t packed_delta3, uint32_t table_idx);741static void get_abs_subblock_colors(color_rgba* pDst, uint16_t packed_color4, uint32_t table_idx);742743static inline void unscaled_to_scaled_color(color_rgba& dst, const color_rgba& src, bool color4)744{745if (color4)746{747dst.r = src.r | (src.r << 4);748dst.g = src.g | (src.g << 4);749dst.b = src.b | (src.b << 4);750}751else752{753dst.r = (src.r >> 2) | (src.r << 3);754dst.g = (src.g >> 2) | (src.g << 3);755dst.b = (src.b >> 2) | (src.b << 3);756}757dst.a = src.a;758}759760private:761static uint8_t clamp255(int x, bool &did_clamp)762{763if (x < 0)764{765did_clamp = true;766return 0;767}768else if (x > 255)769{770did_clamp = true;771return 255;772}773774return static_cast<uint8_t>(x);775}776777static uint8_t clamp255(int x)778{779if (x < 0)780return 0;781else if (x > 255)782return 255;783784return static_cast<uint8_t>(x);785}786};787788typedef basisu::vector<etc_block> etc_block_vec;789790// Returns false if the unpack fails (could be bogus data or ETC2)791bool unpack_etc1(const etc_block& block, color_rgba *pDst, bool preserve_alpha = false);792793enum basis_etc_quality794{795cETCQualityFast,796cETCQualityMedium,797cETCQualitySlow,798cETCQualityUber,799cETCQualityTotal,800};801802struct basis_etc1_pack_params803{804basis_etc_quality m_quality;805bool m_perceptual;806bool m_cluster_fit;807bool m_force_etc1s;808bool m_use_color4;809float m_flip_bias;810811inline basis_etc1_pack_params()812{813clear();814}815816void clear()817{818m_quality = cETCQualitySlow;819m_perceptual = true;820m_cluster_fit = true;821m_force_etc1s = false;822m_use_color4 = true;823m_flip_bias = 0.0f;824}825};826827struct etc1_solution_coordinates828{829inline etc1_solution_coordinates() :830m_unscaled_color(0, 0, 0, 0),831m_inten_table(0),832m_color4(false)833{834}835836inline etc1_solution_coordinates(uint32_t r, uint32_t g, uint32_t b, uint32_t inten_table, bool color4) :837m_unscaled_color((uint8_t)r, (uint8_t)g, (uint8_t)b, 255),838m_inten_table((uint8_t)inten_table),839m_color4(color4)840{841}842843inline etc1_solution_coordinates(const color_rgba& c, uint32_t inten_table, bool color4) :844m_unscaled_color(c),845m_inten_table(inten_table),846m_color4(color4)847{848}849850inline etc1_solution_coordinates(const etc1_solution_coordinates& other)851{852*this = other;853}854855inline etc1_solution_coordinates& operator= (const etc1_solution_coordinates& rhs)856{857m_unscaled_color = rhs.m_unscaled_color;858m_inten_table = rhs.m_inten_table;859m_color4 = rhs.m_color4;860return *this;861}862863inline void clear()864{865m_unscaled_color.clear();866m_inten_table = 0;867m_color4 = false;868}869870inline void init(const color_rgba& c, uint32_t inten_table, bool color4)871{872m_unscaled_color = c;873m_inten_table = inten_table;874m_color4 = color4;875}876877inline color_rgba get_scaled_color() const878{879int br, bg, bb;880if (m_color4)881{882br = m_unscaled_color.r | (m_unscaled_color.r << 4);883bg = m_unscaled_color.g | (m_unscaled_color.g << 4);884bb = m_unscaled_color.b | (m_unscaled_color.b << 4);885}886else887{888br = (m_unscaled_color.r >> 2) | (m_unscaled_color.r << 3);889bg = (m_unscaled_color.g >> 2) | (m_unscaled_color.g << 3);890bb = (m_unscaled_color.b >> 2) | (m_unscaled_color.b << 3);891}892return color_rgba((uint8_t)br, (uint8_t)bg, (uint8_t)bb, 255);893}894895// returns true if anything was clamped896inline void get_block_colors(color_rgba* pBlock_colors)897{898int br, bg, bb;899if (m_color4)900{901br = m_unscaled_color.r | (m_unscaled_color.r << 4);902bg = m_unscaled_color.g | (m_unscaled_color.g << 4);903bb = m_unscaled_color.b | (m_unscaled_color.b << 4);904}905else906{907br = (m_unscaled_color.r >> 2) | (m_unscaled_color.r << 3);908bg = (m_unscaled_color.g >> 2) | (m_unscaled_color.g << 3);909bb = (m_unscaled_color.b >> 2) | (m_unscaled_color.b << 3);910}911const int* pInten_table = g_etc1_inten_tables[m_inten_table];912pBlock_colors[0].set(br + pInten_table[0], bg + pInten_table[0], bb + pInten_table[0], 255);913pBlock_colors[1].set(br + pInten_table[1], bg + pInten_table[1], bb + pInten_table[1], 255);914pBlock_colors[2].set(br + pInten_table[2], bg + pInten_table[2], bb + pInten_table[2], 255);915pBlock_colors[3].set(br + pInten_table[3], bg + pInten_table[3], bb + pInten_table[3], 255);916}917918color_rgba m_unscaled_color;919uint32_t m_inten_table;920bool m_color4;921};922923class etc1_optimizer924{925BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(etc1_optimizer);926927public:928etc1_optimizer()929{930clear();931}932933void clear()934{935m_pParams = nullptr;936m_pResult = nullptr;937m_pSorted_luma = nullptr;938m_pSorted_luma_indices = nullptr;939}940941struct params;942943typedef bool(*evaluate_solution_override_func)(uint64_t &error, const params &p, const color_rgba* pBlock_colors, const uint8_t* pSelectors, const etc1_solution_coordinates& coords);944945struct params : basis_etc1_pack_params946{947params()948{949clear();950}951952params(const basis_etc1_pack_params& base_params)953{954clear_optimizer_params();955956*static_cast<basis_etc1_pack_params *>(this) = base_params;957}958959void clear()960{961clear_optimizer_params();962}963964void clear_optimizer_params()965{966basis_etc1_pack_params::clear();967968m_num_src_pixels = 0;969m_pSrc_pixels = 0;970971m_use_color4 = false;972static const int s_default_scan_delta[] = { 0 };973m_pScan_deltas = s_default_scan_delta;974m_scan_delta_size = 1;975976m_base_color5.clear();977m_constrain_against_base_color5 = false;978979m_refinement = true;980981m_pForce_selectors = nullptr;982}983984uint32_t m_num_src_pixels;985const color_rgba* m_pSrc_pixels;986987bool m_use_color4;988const int* m_pScan_deltas;989uint32_t m_scan_delta_size;990991color_rgba m_base_color5;992bool m_constrain_against_base_color5;993994bool m_refinement;995996const uint8_t* m_pForce_selectors;997};998999struct results1000{1001uint64_t m_error;1002color_rgba m_block_color_unscaled;1003uint32_t m_block_inten_table;1004uint32_t m_n;1005uint8_t* m_pSelectors;1006bool m_block_color4;10071008inline results& operator= (const results& rhs)1009{1010m_block_color_unscaled = rhs.m_block_color_unscaled;1011m_block_color4 = rhs.m_block_color4;1012m_block_inten_table = rhs.m_block_inten_table;1013m_error = rhs.m_error;1014memcpy(m_pSelectors, rhs.m_pSelectors, minimum(rhs.m_n, m_n));1015return *this;1016}1017};10181019void init(const params& params, results& result);1020bool compute();10211022const params* get_params() const { return m_pParams; }10231024private:1025struct potential_solution1026{1027potential_solution() : m_coords(), m_error(UINT64_MAX), m_valid(false)1028{1029}10301031etc1_solution_coordinates m_coords;1032basisu::vector<uint8_t> m_selectors;1033uint64_t m_error;1034bool m_valid;10351036void clear()1037{1038m_coords.clear();1039m_selectors.resize(0);1040m_error = UINT64_MAX;1041m_valid = false;1042}10431044bool are_selectors_all_equal() const1045{1046if (!m_selectors.size())1047return false;1048const uint32_t s = m_selectors[0];1049for (uint32_t i = 1; i < m_selectors.size(); i++)1050if (m_selectors[i] != s)1051return false;1052return true;1053}1054};10551056const params* m_pParams;1057results* m_pResult;10581059int m_limit;10601061vec3F m_avg_color;1062int m_br, m_bg, m_bb;1063int m_max_comp_spread;1064basisu::vector<uint16_t> m_luma;1065basisu::vector<uint32_t> m_sorted_luma;1066basisu::vector<uint32_t> m_sorted_luma_indices;1067const uint32_t* m_pSorted_luma_indices;1068uint32_t* m_pSorted_luma;10691070basisu::vector<uint8_t> m_selectors;1071basisu::vector<uint8_t> m_best_selectors;10721073potential_solution m_best_solution;1074potential_solution m_trial_solution;1075basisu::vector<uint8_t> m_temp_selectors;10761077enum { cSolutionsTriedHashBits = 10, cTotalSolutionsTriedHashSize = 1 << cSolutionsTriedHashBits, cSolutionsTriedHashMask = cTotalSolutionsTriedHashSize - 1 };1078uint8_t m_solutions_tried[cTotalSolutionsTriedHashSize / 8];10791080void get_nearby_inten_tables(uint32_t idx, int &first_inten_table, int &last_inten_table)1081{1082first_inten_table = maximum<int>(idx - 1, 0);1083last_inten_table = minimum<int>(cETC1IntenModifierValues, idx + 1);1084}10851086bool check_for_redundant_solution(const etc1_solution_coordinates& coords);1087bool evaluate_solution_slow(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution);1088bool evaluate_solution_fast(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution);10891090inline bool evaluate_solution(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution)1091{1092if (m_pParams->m_quality >= cETCQualityMedium)1093return evaluate_solution_slow(coords, trial_solution, pBest_solution);1094else1095return evaluate_solution_fast(coords, trial_solution, pBest_solution);1096}10971098void refine_solution(uint32_t max_refinement_trials);1099void compute_internal_neighborhood(int scan_r, int scan_g, int scan_b);1100void compute_internal_cluster_fit(uint32_t total_perms_to_try);1101};11021103struct pack_etc1_block_context1104{1105etc1_optimizer m_optimizer;1106};11071108void pack_etc1_solid_color_init();1109uint64_t pack_etc1_block_solid_color(etc_block& block, const uint8_t* pColor);11101111// ETC EAC1112extern const int8_t g_etc2_eac_tables[16][8];1113extern const int8_t g_etc2_eac_tables8[16][8];11141115const uint32_t ETC2_EAC_MIN_VALUE_SELECTOR = 3, ETC2_EAC_MAX_VALUE_SELECTOR = 7;11161117struct eac_a8_block1118{1119uint16_t m_base : 8;1120uint16_t m_table : 4;1121uint16_t m_multiplier : 4;11221123uint8_t m_selectors[6];11241125inline uint32_t get_selector(uint32_t x, uint32_t y, uint64_t selector_bits) const1126{1127assert((x < 4) && (y < 4));1128return static_cast<uint32_t>((selector_bits >> (45 - (y + x * 4) * 3)) & 7);1129}11301131inline uint64_t get_selector_bits() const1132{1133uint64_t pixels = ((uint64_t)m_selectors[0] << 40) | ((uint64_t)m_selectors[1] << 32) | ((uint64_t)m_selectors[2] << 24) | ((uint64_t)m_selectors[3] << 16) | ((uint64_t)m_selectors[4] << 8) | m_selectors[5];1134return pixels;1135}11361137inline void set_selector_bits(uint64_t pixels)1138{1139m_selectors[0] = (uint8_t)(pixels >> 40);1140m_selectors[1] = (uint8_t)(pixels >> 32);1141m_selectors[2] = (uint8_t)(pixels >> 24);1142m_selectors[3] = (uint8_t)(pixels >> 16);1143m_selectors[4] = (uint8_t)(pixels >> 8);1144m_selectors[5] = (uint8_t)(pixels);1145}11461147void set_selector(uint32_t x, uint32_t y, uint32_t s)1148{1149assert((x < 4) && (y < 4) && (s < 8));11501151const uint32_t ofs = 45 - (y + x * 4) * 3;11521153uint64_t pixels = get_selector_bits();11541155pixels &= ~(7ULL << ofs);1156pixels |= (static_cast<uint64_t>(s) << ofs);11571158set_selector_bits(pixels);1159}1160};11611162struct etc2_rgba_block1163{1164eac_a8_block m_alpha;1165etc_block m_rgb;1166};11671168struct pack_eac_a8_results1169{1170uint32_t m_base;1171uint32_t m_table;1172uint32_t m_multiplier;1173uint8_vec m_selectors;1174uint8_vec m_selectors_temp;1175};11761177uint64_t pack_eac_a8(pack_eac_a8_results& results, const uint8_t* pPixels, uint32_t num_pixels, uint32_t base_search_rad, uint32_t mul_search_rad, uint32_t table_mask = UINT32_MAX);1178void pack_eac_a8(eac_a8_block* pBlock, const uint8_t* pPixels, uint32_t base_search_rad, uint32_t mul_search_rad, uint32_t table_mask = UINT32_MAX);11791180} // namespace basisu118111821183