| // Copyright 2009 Google Inc. |
| // |
| // 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 "GLcommon/rgtc.h" |
| #include <cstring> |
| #include <assert.h> |
| #include <type_traits> |
| |
| // From https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_compression_rgtc.txt |
| // according to the spec |
| // RGTC1_RED = BC4_UNORM, |
| // RGTC1_SIGNED_RED = BC4_SNORM, |
| // RGTC2_RG = BC5_UNORM, |
| // RGTC2_SIGNED_RG = BC5_SNORM. |
| // the full codec spec can be found here |
| // https://docs.microsoft.com/en-gb/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#bc5 |
| |
| static constexpr int kBlockSize = 4; |
| |
| inline size_t rgtc_get_block_size(RGTCImageFormat format) { |
| switch (format) { |
| case BC4_UNORM: |
| case BC4_SNORM: |
| return 8; |
| case BC5_UNORM: |
| case BC5_SNORM: |
| return 16; |
| default: |
| assert(0); |
| return 0; |
| } |
| } |
| |
| size_t rgtc_get_decoded_pixel_size(RGTCImageFormat format) { |
| switch (format) { |
| case BC4_UNORM: |
| case BC4_SNORM: |
| return 1; |
| case BC5_UNORM: |
| case BC5_SNORM: |
| return 2; |
| default: |
| assert(0); |
| return 0; |
| } |
| } |
| |
| template <typename genType> |
| struct get_expand_type {}; |
| |
| template <> |
| struct get_expand_type<int8_t> { |
| typedef int32_t type; |
| }; |
| |
| template <> |
| struct get_expand_type<uint8_t> { |
| typedef uint32_t type; |
| }; |
| |
| template <class T> |
| void rgtc_decode_subblock(const uint32_t* data, T* out, int step, T d0, T d1 ) { |
| T r0 = static_cast<T>(data[0] & 0xff); |
| T r1 = static_cast<T>((data[0] >> 8) & 0xff); |
| uint64_t color_indexs = ((uint64_t)data[1] << 32 | data[0]) >> 16; |
| T colors[8] = {r0, r1,}; |
| typename get_expand_type<T>::type c0 = r0; |
| typename get_expand_type<T>::type c1 = r1; |
| if (c0 > c1) { |
| // 6 interpolated color values |
| for (int i = 2; i < 8; i++) { |
| colors[i] = static_cast<T>((c0 * (8 - i) + c1 * (i - 1)) / 7.0f + 0.5f); |
| } |
| } else { |
| // 4 interpolated color values |
| for (int i = 2; i < 6; i++) { |
| colors[i] = static_cast<T>((c0 * (6 - i) + c1 * (i - 1)) / 5.0f + 0.5f); |
| } |
| colors[6] = d0; |
| colors[7] = d1; |
| } |
| uint64_t index = color_indexs; |
| for (int i = 0 ; i < 16 ; i ++) { |
| *out = colors[index & 0x7]; |
| out += step; |
| index >>= 3; |
| } |
| } |
| |
| int rgtc_decode_image(const uint8_t* in, RGTCImageFormat format, uint8_t* out, uint32_t width, |
| uint32_t height, uint32_t stride) { |
| size_t data_block_size = rgtc_get_block_size(format); |
| size_t texel_size = rgtc_get_decoded_pixel_size(format); |
| const uint8_t* data_in = in; |
| // BC5 2 bytes per pixel |
| uint8_t pixels[kBlockSize * kBlockSize * 2]; |
| for (uint32_t y = 0; y < height; y += kBlockSize) { |
| uint32_t yEnd = height - y; |
| if (yEnd > kBlockSize) { |
| yEnd = kBlockSize; |
| } |
| for (uint32_t x = 0; x < width; x += kBlockSize) { |
| uint32_t xEnd = width - x; |
| if (xEnd > kBlockSize) { |
| xEnd = kBlockSize; |
| } |
| switch (format) { |
| case BC4_UNORM: |
| rgtc_decode_subblock<uint8_t>((const uint32_t*)data_in, pixels, 1, 0, 1); |
| break; |
| case BC4_SNORM: |
| rgtc_decode_subblock<int8_t>((const uint32_t*)data_in, (int8_t*)pixels, 1, -1, 1); |
| break; |
| case BC5_UNORM: |
| rgtc_decode_subblock<uint8_t>((const uint32_t*)data_in, pixels, 2, 0, 1); |
| rgtc_decode_subblock<uint8_t>((const uint32_t*)data_in + 2, pixels + 1, 2, 0, 1); |
| break; |
| case BC5_SNORM: |
| rgtc_decode_subblock<int8_t>((const uint32_t*)data_in, (int8_t*)pixels, 2, -1, 1); |
| rgtc_decode_subblock<int8_t>((const uint32_t*)data_in + 2, (int8_t*)(pixels + 1), |
| 2, -1, 1); |
| break; |
| } |
| for (uint32_t cy = 0; cy < yEnd; cy ++) { |
| uint8_t* data_out = out + (y + cy) * stride + x * texel_size; |
| std::memcpy(data_out, pixels + kBlockSize * texel_size * cy, texel_size * xEnd); |
| } |
| data_in += data_block_size; |
| } |
| } |
| return 0; |
| } |
| |
| size_t rgtc_get_encoded_image_size(RGTCImageFormat format, uint32_t width, uint32_t height) { |
| uint32_t w = (width + kBlockSize - 1) / kBlockSize; |
| uint32_t h = (height + kBlockSize - 1) / kBlockSize; |
| return w * h * rgtc_get_block_size(format); |
| } |