| // basisu_global_selector_palette.h |
| // Copyright (C) 2019 Binomial LLC. All Rights Reserved. |
| // |
| // 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. |
| #pragma once |
| #include "basisu_transcoder_internal.h" |
| #include <algorithm> |
| |
| namespace basist |
| { |
| class etc1_global_palette_entry_modifier |
| { |
| public: |
| enum { cTotalBits = 15, cTotalValues = 1 << cTotalBits }; |
| |
| etc1_global_palette_entry_modifier(uint32_t index = 0) |
| { |
| #ifdef _DEBUG |
| static bool s_tested; |
| if (!s_tested) |
| { |
| s_tested = true; |
| for (uint32_t i = 0; i < cTotalValues; i++) |
| { |
| etc1_global_palette_entry_modifier m(i); |
| etc1_global_palette_entry_modifier n = m; |
| |
| assert(n.get_index() == i); |
| } |
| } |
| #endif |
| |
| set_index(index); |
| } |
| |
| void set_index(uint32_t index) |
| { |
| assert(index < cTotalValues); |
| m_rot = index & 3; |
| m_flip = (index >> 2) & 1; |
| m_inv = (index >> 3) & 1; |
| m_contrast = (index >> 4) & 3; |
| m_shift = (index >> 6) & 1; |
| m_median = (index >> 7) & 1; |
| m_div = (index >> 8) & 1; |
| m_rand = (index >> 9) & 1; |
| m_dilate = (index >> 10) & 1; |
| m_shift_x = (index >> 11) & 1; |
| m_shift_y = (index >> 12) & 1; |
| m_erode = (index >> 13) & 1; |
| m_high_pass = (index >> 14) & 1; |
| } |
| |
| uint32_t get_index() const |
| { |
| return m_rot | (m_flip << 2) | (m_inv << 3) | (m_contrast << 4) | (m_shift << 6) | (m_median << 7) | (m_div << 8) | (m_rand << 9) | (m_dilate << 10) | (m_shift_x << 11) | (m_shift_y << 12) | (m_erode << 13) | (m_high_pass << 14); |
| } |
| |
| void clear() |
| { |
| basisu::clear_obj(*this); |
| } |
| |
| uint8_t m_contrast; |
| bool m_rand; |
| bool m_median; |
| bool m_div; |
| bool m_shift; |
| bool m_inv; |
| bool m_flip; |
| bool m_dilate; |
| bool m_shift_x; |
| bool m_shift_y; |
| bool m_erode; |
| bool m_high_pass; |
| uint8_t m_rot; |
| }; |
| |
| enum modifier_types |
| { |
| cModifierContrast, |
| cModifierRand, |
| cModifierMedian, |
| cModifierDiv, |
| cModifierShift, |
| cModifierInv, |
| cModifierFlippedAndRotated, |
| cModifierDilate, |
| cModifierShiftX, |
| cModifierShiftY, |
| cModifierErode, |
| cModifierHighPass, |
| cTotalModifiers |
| }; |
| |
| #define ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_MOD_BITS (etc1_global_palette_entry_modifier::cTotalBits) |
| |
| struct etc1_selector_palette_entry |
| { |
| etc1_selector_palette_entry() |
| { |
| clear(); |
| } |
| |
| void clear() |
| { |
| basisu::clear_obj(*this); |
| } |
| |
| uint8_t operator[] (uint32_t i) const { assert(i < 16); return m_selectors[i]; } |
| uint8_t&operator[] (uint32_t i) { assert(i < 16); return m_selectors[i]; } |
| |
| void set_uint32(uint32_t v) |
| { |
| for (uint32_t byte_index = 0; byte_index < 4; byte_index++) |
| { |
| uint32_t b = (v >> (byte_index * 8)) & 0xFF; |
| |
| m_selectors[byte_index * 4 + 0] = b & 3; |
| m_selectors[byte_index * 4 + 1] = (b >> 2) & 3; |
| m_selectors[byte_index * 4 + 2] = (b >> 4) & 3; |
| m_selectors[byte_index * 4 + 3] = (b >> 6) & 3; |
| } |
| } |
| |
| uint32_t get_uint32() const |
| { |
| return get_byte(0) | (get_byte(1) << 8) | (get_byte(2) << 16) | (get_byte(3) << 24); |
| } |
| |
| uint32_t get_byte(uint32_t byte_index) const |
| { |
| assert(byte_index < 4); |
| |
| return m_selectors[byte_index * 4 + 0] | |
| (m_selectors[byte_index * 4 + 1] << 2) | |
| (m_selectors[byte_index * 4 + 2] << 4) | |
| (m_selectors[byte_index * 4 + 3] << 6); |
| } |
| |
| uint8_t operator()(uint32_t x, uint32_t y) const { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; } |
| uint8_t&operator()(uint32_t x, uint32_t y) { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; } |
| |
| uint32_t calc_distance(const etc1_selector_palette_entry &other) const |
| { |
| uint32_t dist = 0; |
| for (uint32_t i = 0; i < 8; i++) |
| { |
| int delta = static_cast<int>(m_selectors[i]) - static_cast<int>(other.m_selectors[i]); |
| dist += delta * delta; |
| } |
| return dist; |
| } |
| |
| #if 0 |
| uint32_t calc_hamming_dist(const etc1_selector_palette_entry &other) const |
| { |
| uint32_t dist = 0; |
| for (uint32_t i = 0; i < 4; i++) |
| dist += g_hamming_dist[get_byte(i) ^ other.get_byte(i)]; |
| return dist; |
| } |
| #endif |
| |
| etc1_selector_palette_entry get_inverted() const |
| { |
| etc1_selector_palette_entry result; |
| |
| for (uint32_t i = 0; i < 16; i++) |
| result.m_selectors[i] = 3 - m_selectors[i]; |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_divided() const |
| { |
| etc1_selector_palette_entry result; |
| |
| const uint8_t div_selector[4] = { 2, 0, 3, 1 }; |
| |
| for (uint32_t i = 0; i < 16; i++) |
| result.m_selectors[i] = div_selector[m_selectors[i]]; |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_shifted(int delta) const |
| { |
| etc1_selector_palette_entry result; |
| |
| for (uint32_t i = 0; i < 16; i++) |
| result.m_selectors[i] = static_cast<uint8_t>(basisu::clamp<int>(m_selectors[i] + delta, 0, 3)); |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_randomized() const |
| { |
| uint32_t seed = get_uint32(); |
| |
| etc1_selector_palette_entry result; |
| |
| for (uint32_t y = 0; y < 4; y++) |
| { |
| for (uint32_t x = 0; x < 4; x++) |
| { |
| int s = (*this)(x, y); |
| |
| // between 0 and 10 |
| uint32_t i = basisd_urand(seed, 6) + basisd_urand(seed, 6); |
| if (i == 0) |
| s -= 2; |
| else if (i == 10) |
| s += 2; |
| else if (i < 3) |
| s -= 1; |
| else if (i > 7) |
| s += 1; |
| |
| result(x, y) = static_cast<uint8_t>(basisu::clamp<int>(s, 0, 3)); |
| } |
| } |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_contrast(int table_index) const |
| { |
| assert(table_index < 4); |
| |
| etc1_selector_palette_entry result; |
| |
| static const uint8_t s_contrast_tables[4][4] = |
| { |
| { 0, 1, 2, 3 }, // not used |
| { 0, 0, 3, 3 }, |
| { 1, 1, 2, 2 }, |
| { 1, 1, 3, 3 } |
| }; |
| |
| for (uint32_t i = 0; i < 16; i++) |
| { |
| result[i] = s_contrast_tables[table_index][(*this)[i]]; |
| } |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_dilated() const |
| { |
| etc1_selector_palette_entry result; |
| |
| for (uint32_t y = 0; y < 4; y++) |
| { |
| for (uint32_t x = 0; x < 4; x++) |
| { |
| uint32_t max_selector = 0; |
| |
| for (int yd = -1; yd <= 1; yd++) |
| { |
| int fy = y + yd; |
| if ((fy < 0) || (fy > 3)) |
| continue; |
| |
| for (int xd = -1; xd <= 1; xd++) |
| { |
| int fx = x + xd; |
| if ((fx < 0) || (fx > 3)) |
| continue; |
| |
| max_selector = basisu::maximum<uint32_t>(max_selector, (*this)(fx, fy)); |
| } |
| } |
| |
| result(x, y) = static_cast<uint8_t>(max_selector); |
| } |
| } |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_eroded() const |
| { |
| etc1_selector_palette_entry result; |
| |
| for (uint32_t y = 0; y < 4; y++) |
| { |
| for (uint32_t x = 0; x < 4; x++) |
| { |
| uint32_t min_selector = 99; |
| |
| for (int yd = -1; yd <= 1; yd++) |
| { |
| int fy = y + yd; |
| if ((fy < 0) || (fy > 3)) |
| continue; |
| |
| for (int xd = -1; xd <= 1; xd++) |
| { |
| int fx = x + xd; |
| if ((fx < 0) || (fx > 3)) |
| continue; |
| |
| min_selector = basisu::minimum<uint32_t>(min_selector, (*this)(fx, fy)); |
| } |
| } |
| |
| result(x, y) = static_cast<uint8_t>(min_selector); |
| } |
| } |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_shift_x() const |
| { |
| etc1_selector_palette_entry result; |
| |
| for (uint32_t y = 0; y < 4; y++) |
| { |
| for (uint32_t x = 0; x < 4; x++) |
| { |
| int sx = x - 1; |
| if (sx < 0) |
| sx = 0; |
| |
| result(x, y) = (*this)(sx, y); |
| } |
| } |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_shift_y() const |
| { |
| etc1_selector_palette_entry result; |
| |
| for (uint32_t y = 0; y < 4; y++) |
| { |
| int sy = y - 1; |
| if (sy < 0) |
| sy = 3; |
| |
| for (uint32_t x = 0; x < 4; x++) |
| result(x, y) = (*this)(x, sy); |
| } |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_median() const |
| { |
| etc1_selector_palette_entry result; |
| |
| for (uint32_t y = 0; y < 4; y++) |
| { |
| for (uint32_t x = 0; x < 4; x++) |
| { |
| // ABC |
| // D F |
| // GHI |
| |
| uint8_t selectors[8]; |
| uint32_t n = 0; |
| |
| for (int yd = -1; yd <= 1; yd++) |
| { |
| int fy = y + yd; |
| if ((fy < 0) || (fy > 3)) |
| continue; |
| |
| for (int xd = -1; xd <= 1; xd++) |
| { |
| if ((xd | yd) == 0) |
| continue; |
| |
| int fx = x + xd; |
| if ((fx < 0) || (fx > 3)) |
| continue; |
| |
| selectors[n++] = (*this)(fx, fy); |
| } |
| } |
| |
| std::sort(selectors, selectors + n); |
| |
| result(x, y) = selectors[n / 2]; |
| } |
| } |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_high_pass() const |
| { |
| etc1_selector_palette_entry result; |
| |
| static const int kernel[3][3] = |
| { |
| { 0, -1, 0 }, |
| { -1, 8, -1 }, |
| { 0, -1, 0 } |
| }; |
| |
| for (uint32_t y = 0; y < 4; y++) |
| { |
| for (uint32_t x = 0; x < 4; x++) |
| { |
| // ABC |
| // D F |
| // GHI |
| |
| int sum = 0; |
| |
| for (int yd = -1; yd <= 1; yd++) |
| { |
| int fy = y + yd; |
| fy = basisu::clamp<int>(fy, 0, 3); |
| |
| for (int xd = -1; xd <= 1; xd++) |
| { |
| int fx = x + xd; |
| fx = basisu::clamp<int>(fx, 0, 3); |
| |
| int k = (*this)(fx, fy); |
| sum += k * kernel[yd + 1][xd + 1]; |
| } |
| } |
| |
| sum = sum / 4; |
| |
| result(x, y) = static_cast<uint8_t>(basisu::clamp<int>(sum, 0, 3)); |
| } |
| } |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_flipped_and_rotated(bool flip, uint32_t rotation_index) const |
| { |
| etc1_selector_palette_entry temp; |
| |
| if (flip) |
| { |
| for (uint32_t y = 0; y < 4; y++) |
| for (uint32_t x = 0; x < 4; x++) |
| temp(x, y) = (*this)(x, 3 - y); |
| } |
| else |
| { |
| temp = *this; |
| } |
| |
| etc1_selector_palette_entry result; |
| |
| switch (rotation_index) |
| { |
| case 0: |
| result = temp; |
| break; |
| case 1: |
| for (uint32_t y = 0; y < 4; y++) |
| for (uint32_t x = 0; x < 4; x++) |
| result(x, y) = temp(y, 3 - x); |
| break; |
| case 2: |
| for (uint32_t y = 0; y < 4; y++) |
| for (uint32_t x = 0; x < 4; x++) |
| result(x, y) = temp(3 - x, 3 - y); |
| break; |
| case 3: |
| for (uint32_t y = 0; y < 4; y++) |
| for (uint32_t x = 0; x < 4; x++) |
| result(x, y) = temp(3 - y, x); |
| break; |
| default: |
| assert(0); |
| break; |
| } |
| |
| return result; |
| } |
| |
| etc1_selector_palette_entry get_modified(const etc1_global_palette_entry_modifier &modifier) const |
| { |
| etc1_selector_palette_entry r(*this); |
| |
| if (modifier.m_shift_x) |
| r = r.get_shift_x(); |
| |
| if (modifier.m_shift_y) |
| r = r.get_shift_y(); |
| |
| r = r.get_flipped_and_rotated(modifier.m_flip != 0, modifier.m_rot); |
| |
| if (modifier.m_dilate) |
| r = r.get_dilated(); |
| |
| if (modifier.m_erode) |
| r = r.get_eroded(); |
| |
| if (modifier.m_high_pass) |
| r = r.get_high_pass(); |
| |
| if (modifier.m_rand) |
| r = r.get_randomized(); |
| |
| if (modifier.m_div) |
| r = r.get_divided(); |
| |
| if (modifier.m_shift) |
| r = r.get_shifted(1); |
| |
| if (modifier.m_contrast) |
| r = r.get_contrast(modifier.m_contrast); |
| |
| if (modifier.m_inv) |
| r = r.get_inverted(); |
| |
| if (modifier.m_median) |
| r = r.get_median(); |
| |
| return r; |
| } |
| |
| etc1_selector_palette_entry apply_modifier(modifier_types mod_type, const etc1_global_palette_entry_modifier &modifier) const |
| { |
| switch (mod_type) |
| { |
| case cModifierContrast: |
| return get_contrast(modifier.m_contrast); |
| case cModifierRand: |
| return get_randomized(); |
| case cModifierMedian: |
| return get_median(); |
| case cModifierDiv: |
| return get_divided(); |
| case cModifierShift: |
| return get_shifted(1); |
| case cModifierInv: |
| return get_inverted(); |
| case cModifierFlippedAndRotated: |
| return get_flipped_and_rotated(modifier.m_flip != 0, modifier.m_rot); |
| case cModifierDilate: |
| return get_dilated(); |
| case cModifierShiftX: |
| return get_shift_x(); |
| case cModifierShiftY: |
| return get_shift_y(); |
| case cModifierErode: |
| return get_eroded(); |
| case cModifierHighPass: |
| return get_high_pass(); |
| default: |
| assert(0); |
| break; |
| } |
| |
| return *this; |
| } |
| |
| etc1_selector_palette_entry get_modified(const etc1_global_palette_entry_modifier &modifier, uint32_t num_order, const modifier_types *pOrder) const |
| { |
| etc1_selector_palette_entry r(*this); |
| |
| for (uint32_t i = 0; i < num_order; i++) |
| { |
| r = r.apply_modifier(pOrder[i], modifier); |
| } |
| |
| return r; |
| } |
| |
| bool operator< (const etc1_selector_palette_entry &other) const |
| { |
| for (uint32_t i = 0; i < 16; i++) |
| { |
| if (m_selectors[i] < other.m_selectors[i]) |
| return true; |
| else if (m_selectors[i] != other.m_selectors[i]) |
| return false; |
| } |
| |
| return false; |
| } |
| |
| bool operator== (const etc1_selector_palette_entry &other) const |
| { |
| for (uint32_t i = 0; i < 16; i++) |
| { |
| if (m_selectors[i] != other.m_selectors[i]) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| private: |
| uint8_t m_selectors[16]; |
| }; |
| |
| typedef std::vector<etc1_selector_palette_entry> etc1_selector_palette_entry_vec; |
| |
| extern const uint32_t g_global_selector_cb[]; |
| extern const uint32_t g_global_selector_cb_size; |
| |
| #define ETC1_GLOBAL_SELECTOR_CODEBOOK_MAX_PAL_BITS (12) |
| |
| struct etc1_global_selector_codebook_entry_id |
| { |
| uint32_t m_palette_index; |
| etc1_global_palette_entry_modifier m_modifier; |
| |
| etc1_global_selector_codebook_entry_id(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) : m_palette_index(palette_index), m_modifier(modifier) { } |
| |
| etc1_global_selector_codebook_entry_id() { } |
| |
| void set(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) { m_palette_index = palette_index; m_modifier = modifier; } |
| }; |
| |
| typedef std::vector<etc1_global_selector_codebook_entry_id> etc1_global_selector_codebook_entry_id_vec; |
| |
| class etc1_global_selector_codebook |
| { |
| public: |
| etc1_global_selector_codebook() { } |
| etc1_global_selector_codebook(uint32_t N, const uint32_t *pEntries) { init(N, pEntries); } |
| |
| void init(uint32_t N, const uint32_t* pEntries); |
| |
| void print_code(FILE *pFile); |
| |
| void clear() |
| { |
| m_palette.clear(); |
| } |
| |
| uint32_t size() const { return (uint32_t)m_palette.size(); } |
| |
| const etc1_selector_palette_entry_vec &get_palette() const |
| { |
| return m_palette; |
| } |
| |
| etc1_selector_palette_entry get_entry(uint32_t palette_index) const |
| { |
| return m_palette[palette_index]; |
| } |
| |
| etc1_selector_palette_entry get_entry(uint32_t palette_index, const etc1_global_palette_entry_modifier &modifier) const |
| { |
| return m_palette[palette_index].get_modified(modifier); |
| } |
| |
| etc1_selector_palette_entry get_entry(const etc1_global_selector_codebook_entry_id &id) const |
| { |
| return m_palette[id.m_palette_index].get_modified(id.m_modifier); |
| } |
| |
| etc1_selector_palette_entry_vec m_palette; |
| }; |
| |
| } // namespace basist |