Path: blob/master/thirdparty/basis_universal/encoder/basisu_pvrtc1_4.h
9902 views
// basisu_pvrtc1_4.cpp1// 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 "basisu_gpu_texture.h"1617namespace basisu18{19enum20{21PVRTC2_MIN_WIDTH = 16,22PVRTC2_MIN_HEIGHT = 8,23PVRTC4_MIN_WIDTH = 8,24PVRTC4_MIN_HEIGHT = 825};2627struct pvrtc4_block28{29uint32_t m_modulation;30uint32_t m_endpoints;3132pvrtc4_block() : m_modulation(0), m_endpoints(0) { }3334inline bool operator== (const pvrtc4_block& rhs) const35{36return (m_modulation == rhs.m_modulation) && (m_endpoints == rhs.m_endpoints);37}3839inline void clear()40{41m_modulation = 0;42m_endpoints = 0;43}4445inline bool get_block_uses_transparent_modulation() const46{47return (m_endpoints & 1) != 0;48}4950inline bool is_endpoint_opaque(uint32_t endpoint_index) const51{52static const uint32_t s_bitmasks[2] = { 0x8000U, 0x80000000U };53return (m_endpoints & s_bitmasks[open_range_check(endpoint_index, 2U)]) != 0;54}5556// Returns raw endpoint or 888857color_rgba get_endpoint(uint32_t endpoint_index, bool unpack) const;5859color_rgba get_endpoint_5554(uint32_t endpoint_index) const;6061static uint32_t get_component_precision_in_bits(uint32_t c, uint32_t endpoint_index, bool opaque_endpoint)62{63static const uint32_t s_comp_prec[4][4] =64{65// R0 G0 B0 A0 R1 G1 B1 A166{ 4, 4, 3, 3 }, { 4, 4, 4, 3 }, // transparent endpoint6768{ 5, 5, 4, 0 }, { 5, 5, 5, 0 } // opaque endpoint69};70return s_comp_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)][open_range_check(c, 4U)];71}7273static color_rgba get_color_precision_in_bits(uint32_t endpoint_index, bool opaque_endpoint)74{75static const color_rgba s_color_prec[4] =76{77color_rgba(4, 4, 3, 3), color_rgba(4, 4, 4, 3), // transparent endpoint78color_rgba(5, 5, 4, 0), color_rgba(5, 5, 5, 0) // opaque endpoint79};80return s_color_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)];81}8283inline uint32_t get_modulation(uint32_t x, uint32_t y) const84{85assert((x < 4) && (y < 4));86return (m_modulation >> ((y * 4 + x) * 2)) & 3;87}8889inline void set_modulation(uint32_t x, uint32_t y, uint32_t s)90{91assert((x < 4) && (y < 4) && (s < 4));92uint32_t n = (y * 4 + x) * 2;93m_modulation = (m_modulation & (~(3 << n))) | (s << n);94assert(get_modulation(x, y) == s);95}9697// Scaled by 898inline const uint32_t* get_scaled_modulation_values(bool block_uses_transparent_modulation) const99{100static const uint32_t s_block_scales[2][4] = { { 0, 3, 5, 8 }, { 0, 4, 4, 8 } };101return s_block_scales[block_uses_transparent_modulation];102}103104// Scaled by 8105inline uint32_t get_scaled_modulation(uint32_t x, uint32_t y) const106{107return get_scaled_modulation_values(get_block_uses_transparent_modulation())[get_modulation(x, y)];108}109110inline void byte_swap()111{112m_modulation = byteswap32(m_modulation);113m_endpoints = byteswap32(m_endpoints);114}115116// opaque endpoints: 554, 555117// transparent endpoints: 3443, 3444118inline void set_endpoint_raw(uint32_t endpoint_index, const color_rgba& c, bool opaque_endpoint)119{120assert(endpoint_index < 2);121const uint32_t m = m_endpoints & 1;122uint32_t r = c[0], g = c[1], b = c[2], a = c[3];123124uint32_t packed;125126if (opaque_endpoint)127{128if (!endpoint_index)129{130// 554131// 1RRRRRGGGGGBBBBM132assert((r < 32) && (g < 32) && (b < 16));133packed = 0x8000 | (r << 10) | (g << 5) | (b << 1) | m;134}135else136{137// 555138// 1RRRRRGGGGGBBBBB139assert((r < 32) && (g < 32) && (b < 32));140packed = 0x8000 | (r << 10) | (g << 5) | b;141}142}143else144{145if (!endpoint_index)146{147// 3443148// 0AAA RRRR GGGG BBBM149assert((r < 16) && (g < 16) && (b < 8) && (a < 8));150packed = (a << 12) | (r << 8) | (g << 4) | (b << 1) | m;151}152else153{154// 3444155// 0AAA RRRR GGGG BBBB156assert((r < 16) && (g < 16) && (b < 16) && (a < 8));157packed = (a << 12) | (r << 8) | (g << 4) | b;158}159}160161assert(packed <= 0xFFFF);162163if (endpoint_index)164m_endpoints = (m_endpoints & 0xFFFFU) | (packed << 16);165else166m_endpoints = (m_endpoints & 0xFFFF0000U) | packed;167}168};169170typedef vector2D<pvrtc4_block> pvrtc4_block_vector2D;171172uint32_t pvrtc4_swizzle_uv(uint32_t XSize, uint32_t YSize, uint32_t XPos, uint32_t YPos);173174class pvrtc4_image175{176public:177inline pvrtc4_image() :178m_width(0), m_height(0), m_block_width(0), m_block_height(0), m_uses_alpha(false)179{180}181182inline pvrtc4_image(uint32_t width, uint32_t height) :183m_width(0), m_height(0), m_block_width(0), m_block_height(0), m_uses_alpha(false)184{185resize(width, height);186}187188inline void clear()189{190m_width = 0;191m_height = 0;192m_block_width = 0;193m_block_height = 0;194m_blocks.clear();195m_uses_alpha = false;196}197198inline void resize(uint32_t width, uint32_t height)199{200if ((width == m_width) && (height == m_height))201return;202203m_width = width;204m_height = height;205206m_block_width = (width + 3) >> 2;207m_block_height = (height + 3) >> 2;208209m_blocks.resize(m_block_width, m_block_height);210}211212inline uint32_t get_width() const { return m_width; }213inline uint32_t get_height() const { return m_height; }214215inline uint32_t get_block_width() const { return m_block_width; }216inline uint32_t get_block_height() const { return m_block_height; }217218inline const pvrtc4_block_vector2D &get_blocks() const { return m_blocks; }219inline pvrtc4_block_vector2D &get_blocks() { return m_blocks; }220221inline uint32_t get_total_blocks() const { return m_block_width * m_block_height; }222223inline bool get_uses_alpha() const { return m_uses_alpha; }224inline void set_uses_alpha(bool uses_alpha) { m_uses_alpha = uses_alpha; }225226inline bool are_blocks_equal(const pvrtc4_image& rhs) const227{228return m_blocks == rhs.m_blocks;229}230231inline void set_to_black()232{233#ifndef __EMSCRIPTEN__234#ifdef __GNUC__235#pragma GCC diagnostic push236#pragma GCC diagnostic ignored "-Wclass-memaccess"237#endif238#endif239memset(m_blocks.get_ptr(), 0, m_blocks.size_in_bytes());240#ifndef __EMSCRIPTEN__241#ifdef __GNUC__242#pragma GCC diagnostic pop243#endif244#endif245}246247inline bool get_block_uses_transparent_modulation(uint32_t bx, uint32_t by) const248{249return m_blocks(bx, by).get_block_uses_transparent_modulation();250}251252inline bool is_endpoint_opaque(uint32_t bx, uint32_t by, uint32_t endpoint_index) const253{254return m_blocks(bx, by).is_endpoint_opaque(endpoint_index);255}256257color_rgba get_endpoint(uint32_t bx, uint32_t by, uint32_t endpoint_index, bool unpack) const258{259assert((bx < m_block_width) && (by < m_block_height));260return m_blocks(bx, by).get_endpoint(endpoint_index, unpack);261}262263inline uint32_t get_modulation(uint32_t x, uint32_t y) const264{265assert((x < m_width) && (y < m_height));266return m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3);267}268269// Returns true if the block uses transparent modulation.270bool get_interpolated_colors(uint32_t x, uint32_t y, color_rgba* pColors) const;271272color_rgba get_pixel(uint32_t x, uint32_t y, uint32_t m) const;273274inline color_rgba get_pixel(uint32_t x, uint32_t y) const275{276assert((x < m_width) && (y < m_height));277return get_pixel(x, y, m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3));278}279280void deswizzle()281{282pvrtc4_block_vector2D temp(m_blocks);283284for (uint32_t y = 0; y < m_block_height; y++)285for (uint32_t x = 0; x < m_block_width; x++)286m_blocks(x, y) = temp[pvrtc4_swizzle_uv(m_block_width, m_block_height, x, y)];287}288289void swizzle()290{291pvrtc4_block_vector2D temp(m_blocks);292293for (uint32_t y = 0; y < m_block_height; y++)294for (uint32_t x = 0; x < m_block_width; x++)295m_blocks[pvrtc4_swizzle_uv(m_block_width, m_block_height, x, y)] = temp(x, y);296}297298void unpack_all_pixels(image& img) const299{300img.crop(m_width, m_height);301302for (uint32_t y = 0; y < m_height; y++)303for (uint32_t x = 0; x < m_width; x++)304img(x, y) = get_pixel(x, y);305}306307void unpack_block(image &dst, uint32_t block_x, uint32_t block_y)308{309for (uint32_t y = 0; y < 4; y++)310for (uint32_t x = 0; x < 4; x++)311dst(x, y) = get_pixel(block_x * 4 + x, block_y * 4 + y);312}313314inline int wrap_x(int x) const315{316return posmod(x, m_width);317}318319inline int wrap_y(int y) const320{321return posmod(y, m_height);322}323324inline int wrap_block_x(int bx) const325{326return posmod(bx, m_block_width);327}328329inline int wrap_block_y(int by) const330{331return posmod(by, m_block_height);332}333334inline vec2F get_interpolation_factors(uint32_t x, uint32_t y) const335{336// 0 1 2 3337// 2 3 0 1338// .5 .75 0 .25339static const float s_interp[4] = { 2, 3, 0, 1 };340return vec2F(s_interp[x & 3], s_interp[y & 3]);341}342343inline color_rgba interpolate(int x, int y,344const color_rgba& p, const color_rgba& q,345const color_rgba& r, const color_rgba& s) const346{347static const int s_interp[4] = { 2, 3, 0, 1 };348const int u_interp = s_interp[x & 3];349const int v_interp = s_interp[y & 3];350351color_rgba result;352353for (uint32_t c = 0; c < 4; c++)354{355int t = p[c] * 4 + u_interp * ((int)q[c] - (int)p[c]);356int b = r[c] * 4 + u_interp * ((int)s[c] - (int)r[c]);357int v = t * 4 + v_interp * (b - t);358if (c < 3)359{360v >>= 1;361v += (v >> 5);362}363else364{365v += (v >> 4);366}367assert((v >= 0) && (v < 256));368result[c] = static_cast<uint8_t>(v);369}370371return result;372}373374inline void set_modulation(uint32_t x, uint32_t y, uint32_t s)375{376assert((x < m_width) && (y < m_height));377return m_blocks(x >> 2, y >> 2).set_modulation(x & 3, y & 3, s);378}379380inline uint64_t map_pixel(uint32_t x, uint32_t y, const color_rgba& c, bool perceptual, bool alpha_is_significant, bool record = true)381{382color_rgba v[4];383get_interpolated_colors(x, y, v);384385uint64_t best_dist = color_distance(perceptual, c, v[0], alpha_is_significant);386uint32_t best_v = 0;387for (uint32_t i = 1; i < 4; i++)388{389uint64_t dist = color_distance(perceptual, c, v[i], alpha_is_significant);390if (dist < best_dist)391{392best_dist = dist;393best_v = i;394}395}396397if (record)398set_modulation(x, y, best_v);399400return best_dist;401}402403inline uint64_t remap_pixels_influenced_by_endpoint(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual, bool alpha_is_significant)404{405uint64_t total_error = 0;406407for (int yd = -3; yd <= 3; yd++)408{409const int y = wrap_y((int)by * 4 + 2 + yd);410411for (int xd = -3; xd <= 3; xd++)412{413const int x = wrap_x((int)bx * 4 + 2 + xd);414415total_error += map_pixel(x, y, orig_img(x, y), perceptual, alpha_is_significant);416}417}418419return total_error;420}421422inline uint64_t evaluate_1x1_endpoint_error(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual, bool alpha_is_significant, uint64_t threshold_error = 0) const423{424uint64_t total_error = 0;425426for (int yd = -3; yd <= 3; yd++)427{428const int y = wrap_y((int)by * 4 + 2 + yd);429430for (int xd = -3; xd <= 3; xd++)431{432const int x = wrap_x((int)bx * 4 + 2 + xd);433434total_error += color_distance(perceptual, get_pixel(x, y), orig_img(x, y), alpha_is_significant);435436if ((threshold_error) && (total_error >= threshold_error))437return total_error;438}439}440441return total_error;442}443444uint64_t local_endpoint_optimization_opaque(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual);445446inline uint64_t map_all_pixels(const image& img, bool perceptual, bool alpha_is_significant)447{448assert(m_width == img.get_width());449assert(m_height == img.get_height());450451uint64_t total_error = 0;452for (uint32_t y = 0; y < img.get_height(); y++)453for (uint32_t x = 0; x < img.get_width(); x++)454total_error += map_pixel(x, y, img(x, y), perceptual, alpha_is_significant);455456return total_error;457}458459public:460uint32_t m_width, m_height;461pvrtc4_block_vector2D m_blocks;462uint32_t m_block_width, m_block_height;463464bool m_uses_alpha;465};466467} // namespace basisu468469470