| // basisu_pvrtc1_4.cpp |
| // 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_gpu_texture.h" |
| |
| namespace basisu |
| { |
| enum |
| { |
| PVRTC2_MIN_WIDTH = 16, |
| PVRTC2_MIN_HEIGHT = 8, |
| PVRTC4_MIN_WIDTH = 8, |
| PVRTC4_MIN_HEIGHT = 8 |
| }; |
| |
| struct pvrtc4_block |
| { |
| uint32_t m_modulation; |
| uint32_t m_endpoints; |
| |
| pvrtc4_block() : m_modulation(0), m_endpoints(0) { } |
| |
| inline bool operator== (const pvrtc4_block& rhs) const |
| { |
| return (m_modulation == rhs.m_modulation) && (m_endpoints == rhs.m_endpoints); |
| } |
| |
| inline void clear() |
| { |
| m_modulation = 0; |
| m_endpoints = 0; |
| } |
| |
| inline bool get_block_uses_transparent_modulation() const |
| { |
| return (m_endpoints & 1) != 0; |
| } |
| |
| inline bool is_endpoint_opaque(uint32_t endpoint_index) const |
| { |
| static const uint32_t s_bitmasks[2] = { 0x8000U, 0x80000000U }; |
| return (m_endpoints & s_bitmasks[open_range_check(endpoint_index, 2U)]) != 0; |
| } |
| |
| // Returns raw endpoint or 8888 |
| color_rgba get_endpoint(uint32_t endpoint_index, bool unpack) const; |
| |
| color_rgba get_endpoint_5554(uint32_t endpoint_index) const; |
| |
| static uint32_t get_component_precision_in_bits(uint32_t c, uint32_t endpoint_index, bool opaque_endpoint) |
| { |
| static const uint32_t s_comp_prec[4][4] = |
| { |
| // R0 G0 B0 A0 R1 G1 B1 A1 |
| { 4, 4, 3, 3 }, { 4, 4, 4, 3 }, // transparent endpoint |
| |
| { 5, 5, 4, 0 }, { 5, 5, 5, 0 } // opaque endpoint |
| }; |
| return s_comp_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)][open_range_check(c, 4U)]; |
| } |
| |
| static color_rgba get_color_precision_in_bits(uint32_t endpoint_index, bool opaque_endpoint) |
| { |
| static const color_rgba s_color_prec[4] = |
| { |
| color_rgba(4, 4, 3, 3), color_rgba(4, 4, 4, 3), // transparent endpoint |
| color_rgba(5, 5, 4, 0), color_rgba(5, 5, 5, 0) // opaque endpoint |
| }; |
| return s_color_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)]; |
| } |
| |
| inline uint32_t get_modulation(uint32_t x, uint32_t y) const |
| { |
| assert((x < 4) && (y < 4)); |
| return (m_modulation >> ((y * 4 + x) * 2)) & 3; |
| } |
| |
| // Scaled by 8 |
| inline const uint32_t* get_scaled_modulation_values(bool block_uses_transparent_modulation) const |
| { |
| static const uint32_t s_block_scales[2][4] = { { 0, 3, 5, 8 }, { 0, 4, 4, 8 } }; |
| return s_block_scales[block_uses_transparent_modulation]; |
| } |
| |
| // Scaled by 8 |
| inline uint32_t get_scaled_modulation(uint32_t x, uint32_t y) const |
| { |
| return get_scaled_modulation_values(get_block_uses_transparent_modulation())[get_modulation(x, y)]; |
| } |
| |
| inline void byte_swap() |
| { |
| m_modulation = byteswap32(m_modulation); |
| m_endpoints = byteswap32(m_endpoints); |
| } |
| }; |
| |
| typedef vector2D<pvrtc4_block> pvrtc4_block_vector2D; |
| |
| uint32_t pvrtc4_swizzle_uv(uint32_t XSize, uint32_t YSize, uint32_t XPos, uint32_t YPos); |
| |
| class pvrtc4_image |
| { |
| public: |
| inline pvrtc4_image() : |
| m_width(0), m_height(0), m_block_width(0), m_block_height(0), m_wrap_addressing(false), m_uses_alpha(false) |
| { |
| } |
| |
| inline pvrtc4_image(uint32_t width, uint32_t height, bool wrap_addressing = false) : |
| m_width(0), m_height(0), m_block_width(0), m_block_height(0), m_wrap_addressing(false), m_uses_alpha(false) |
| { |
| resize(width, height); |
| set_wrap_addressing(wrap_addressing); |
| } |
| |
| inline void clear() |
| { |
| m_width = 0; |
| m_height = 0; |
| m_block_width = 0; |
| m_block_height = 0; |
| m_blocks.clear(); |
| m_uses_alpha = false; |
| m_wrap_addressing = false; |
| } |
| |
| inline void resize(uint32_t width, uint32_t height) |
| { |
| if ((width == m_width) && (height == m_height)) |
| return; |
| |
| m_width = width; |
| m_height = height; |
| |
| m_block_width = (width + 3) >> 2; |
| m_block_height = (height + 3) >> 2; |
| |
| m_blocks.resize(m_block_width, m_block_height); |
| } |
| |
| inline uint32_t get_width() const { return m_width; } |
| inline uint32_t get_height() const { return m_height; } |
| |
| inline uint32_t get_block_width() const { return m_block_width; } |
| inline uint32_t get_block_height() const { return m_block_height; } |
| |
| inline const pvrtc4_block_vector2D &get_blocks() const { return m_blocks; } |
| inline pvrtc4_block_vector2D &get_blocks() { return m_blocks; } |
| |
| inline uint32_t get_total_blocks() const { return m_block_width * m_block_height; } |
| |
| inline bool get_uses_alpha() const { return m_uses_alpha; } |
| inline void set_uses_alpha(bool uses_alpha) { m_uses_alpha = uses_alpha; } |
| |
| inline void set_wrap_addressing(bool wrapping) { m_wrap_addressing = wrapping; } |
| inline bool get_wrap_addressing() const { return m_wrap_addressing; } |
| |
| inline bool are_blocks_equal(const pvrtc4_image& rhs) const |
| { |
| return m_blocks == rhs.m_blocks; |
| } |
| |
| inline void set_to_black() |
| { |
| memset(m_blocks.get_ptr(), 0, m_blocks.size_in_bytes()); |
| } |
| |
| inline bool get_block_uses_transparent_modulation(uint32_t bx, uint32_t by) const |
| { |
| return m_blocks(bx, by).get_block_uses_transparent_modulation(); |
| } |
| |
| inline bool is_endpoint_opaque(uint32_t bx, uint32_t by, uint32_t endpoint_index) const |
| { |
| return m_blocks(bx, by).is_endpoint_opaque(endpoint_index); |
| } |
| |
| color_rgba get_endpoint(uint32_t bx, uint32_t by, uint32_t endpoint_index, bool unpack) const |
| { |
| assert((bx < m_block_width) && (by < m_block_height)); |
| return m_blocks(bx, by).get_endpoint(endpoint_index, unpack); |
| } |
| |
| inline uint32_t get_modulation(uint32_t x, uint32_t y) const |
| { |
| assert((x < m_width) && (y < m_height)); |
| return m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3); |
| } |
| |
| // Returns true if the block uses transparent modulation. |
| bool get_interpolated_colors(uint32_t x, uint32_t y, color_rgba* pColors) const; |
| |
| color_rgba get_pixel(uint32_t x, uint32_t y, uint32_t m) const; |
| |
| inline color_rgba get_pixel(uint32_t x, uint32_t y) const |
| { |
| assert((x < m_width) && (y < m_height)); |
| return get_pixel(x, y, m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3)); |
| } |
| |
| void deswizzle() |
| { |
| pvrtc4_block_vector2D temp(m_blocks); |
| |
| for (uint32_t y = 0; y < m_block_height; y++) |
| for (uint32_t x = 0; x < m_block_width; x++) |
| m_blocks(x, y) = temp[pvrtc4_swizzle_uv(m_block_width, m_block_height, x, y)]; |
| } |
| |
| void swizzle() |
| { |
| pvrtc4_block_vector2D temp(m_blocks); |
| |
| for (uint32_t y = 0; y < m_block_height; y++) |
| for (uint32_t x = 0; x < m_block_width; x++) |
| m_blocks[pvrtc4_swizzle_uv(m_block_width, m_block_height, x, y)] = temp(x, y); |
| } |
| |
| void unpack_all_pixels(image& img) const |
| { |
| img.crop(m_width, m_height); |
| |
| for (uint32_t y = 0; y < m_height; y++) |
| for (uint32_t x = 0; x < m_width; x++) |
| img(x, y) = get_pixel(x, y); |
| } |
| |
| void unpack_block(image &dst, uint32_t block_x, uint32_t block_y) |
| { |
| for (uint32_t y = 0; y < 4; y++) |
| for (uint32_t x = 0; x < 4; x++) |
| dst(x, y) = get_pixel(block_x * 4 + x, block_y * 4 + y); |
| } |
| |
| inline int wrap_or_clamp_x(int x) const |
| { |
| return m_wrap_addressing ? posmod(x, m_width) : clamp<int>(x, 0, m_width - 1); |
| } |
| |
| inline int wrap_or_clamp_y(int y) const |
| { |
| return m_wrap_addressing ? posmod(y, m_height) : clamp<int>(y, 0, m_height - 1); |
| } |
| |
| inline int wrap_or_clamp_block_x(int bx) const |
| { |
| return m_wrap_addressing ? posmod(bx, m_block_width) : clamp<int>(bx, 0, m_block_width - 1); |
| } |
| |
| inline int wrap_or_clamp_block_y(int by) const |
| { |
| return m_wrap_addressing ? posmod(by, m_block_height) : clamp<int>(by, 0, m_block_height - 1); |
| } |
| |
| inline vec2F get_interpolation_factors(uint32_t x, uint32_t y) const |
| { |
| // 0 1 2 3 |
| // 2 3 0 1 |
| // .5 .75 0 .25 |
| static const float s_interp[4] = { 2, 3, 0, 1 }; |
| return vec2F(s_interp[x & 3], s_interp[y & 3]); |
| } |
| |
| inline color_rgba interpolate(int x, int y, |
| const color_rgba& p, const color_rgba& q, |
| const color_rgba& r, const color_rgba& s) const |
| { |
| static const int s_interp[4] = { 2, 3, 0, 1 }; |
| const int u_interp = s_interp[x & 3]; |
| const int v_interp = s_interp[y & 3]; |
| |
| color_rgba result; |
| |
| for (uint32_t c = 0; c < 4; c++) |
| { |
| int t = p[c] * 4 + u_interp * ((int)q[c] - (int)p[c]); |
| int b = r[c] * 4 + u_interp * ((int)s[c] - (int)r[c]); |
| int v = t * 4 + v_interp * (b - t); |
| if (c < 3) |
| { |
| v >>= 1; |
| v += (v >> 5); |
| } |
| else |
| { |
| v += (v >> 4); |
| } |
| assert((v >= 0) && (v < 256)); |
| result[c] = static_cast<uint8_t>(v); |
| } |
| |
| return result; |
| } |
| |
| uint32_t m_width, m_height; |
| pvrtc4_block_vector2D m_blocks; |
| uint32_t m_block_width, m_block_height; |
| |
| bool m_wrap_addressing; |
| bool m_uses_alpha; |
| }; |
| |
| } // namespace basisu |