1 // Copyright 2009 Google Inc.
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 at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // 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 and
13 // limitations under the License.
14 
15 #include "GLcommon/rgtc.h"
16 #include <cstring>
17 #include <assert.h>
18 #include <type_traits>
19 
20 // From https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_compression_rgtc.txt
21 // according to the spec
22 // RGTC1_RED = BC4_UNORM,
23 // RGTC1_SIGNED_RED = BC4_SNORM,
24 // RGTC2_RG = BC5_UNORM,
25 // RGTC2_SIGNED_RG = BC5_SNORM.
26 // the full codec spec can be found here
27 // https://docs.microsoft.com/en-gb/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#bc5
28 
29 static constexpr int kBlockSize = 4;
30 
rgtc_get_block_size(RGTCImageFormat format)31 inline size_t rgtc_get_block_size(RGTCImageFormat format) {
32     switch (format) {
33     case BC4_UNORM:
34     case BC4_SNORM:
35         return 8;
36     case BC5_UNORM:
37     case BC5_SNORM:
38         return 16;
39     default:
40         assert(0);
41         return 0;
42     }
43 }
44 
rgtc_get_decoded_pixel_size(RGTCImageFormat format)45 size_t rgtc_get_decoded_pixel_size(RGTCImageFormat format) {
46     switch (format) {
47         case BC4_UNORM:
48         case BC4_SNORM:
49             return 1;
50         case BC5_UNORM:
51         case BC5_SNORM:
52             return 2;
53         default:
54             assert(0);
55             return 0;
56     }
57 }
58 
59 template <typename genType>
60 struct get_expand_type {};
61 
62 template <>
63 struct get_expand_type<int8_t> {
64     typedef int32_t type;
65 };
66 
67 template <>
68 struct get_expand_type<uint8_t> {
69     typedef uint32_t type;
70 };
71 
72 template <class T>
rgtc_decode_subblock(const uint32_t * data,T * out,int step,T d0,T d1)73 void rgtc_decode_subblock(const uint32_t* data, T* out, int step, T d0, T d1 ) {
74     T r0 = static_cast<T>(data[0] & 0xff);
75     T r1 = static_cast<T>((data[0] >> 8) & 0xff);
76     uint64_t color_indexs = ((uint64_t)data[1] << 32 | data[0]) >> 16;
77     T colors[8] = {r0, r1,};
78     typename get_expand_type<T>::type c0 = r0;
79     typename get_expand_type<T>::type c1 = r1;
80     if (c0 > c1) {
81         // 6 interpolated color values
82         for (int i = 2; i < 8; i++) {
83             colors[i] = static_cast<T>((c0 * (8 - i) + c1 * (i - 1)) / 7.0f + 0.5f);
84         }
85     } else {
86         // 4 interpolated color values
87         for (int i = 2; i < 6; i++) {
88             colors[i] = static_cast<T>((c0 * (6 - i) + c1 * (i - 1)) / 5.0f + 0.5f);
89         }
90         colors[6] = d0;
91         colors[7] = d1;
92     }
93     uint64_t index = color_indexs;
94     for (int i = 0 ; i < 16 ; i ++) {
95         *out = colors[index & 0x7];
96         out += step;
97         index >>= 3;
98     }
99 }
100 
rgtc_decode_image(const uint8_t * in,RGTCImageFormat format,uint8_t * out,uint32_t width,uint32_t height,uint32_t stride)101 int rgtc_decode_image(const uint8_t* in, RGTCImageFormat format, uint8_t* out, uint32_t width,
102                       uint32_t height, uint32_t stride) {
103     size_t data_block_size = rgtc_get_block_size(format);
104     size_t texel_size = rgtc_get_decoded_pixel_size(format);
105     const uint8_t* data_in = in;
106     // BC5 2 bytes per pixel
107     uint8_t pixels[kBlockSize * kBlockSize * 2];
108     for (uint32_t y = 0; y < height; y += kBlockSize) {
109         uint32_t yEnd = height - y;
110         if (yEnd > kBlockSize) {
111             yEnd = kBlockSize;
112         }
113         for (uint32_t x = 0; x < width; x += kBlockSize) {
114             uint32_t xEnd = width - x;
115             if (xEnd > kBlockSize) {
116                 xEnd = kBlockSize;
117             }
118             switch (format) {
119             case BC4_UNORM:
120                 rgtc_decode_subblock<uint8_t>((const uint32_t*)data_in, pixels, 1, 0, 1);
121                 break;
122             case BC4_SNORM:
123                 rgtc_decode_subblock<int8_t>((const uint32_t*)data_in, (int8_t*)pixels, 1, -1, 1);
124                 break;
125             case BC5_UNORM:
126                 rgtc_decode_subblock<uint8_t>((const uint32_t*)data_in, pixels, 2, 0, 1);
127                 rgtc_decode_subblock<uint8_t>((const uint32_t*)data_in + 2, pixels + 1, 2, 0, 1);
128                 break;
129             case BC5_SNORM:
130                 rgtc_decode_subblock<int8_t>((const uint32_t*)data_in, (int8_t*)pixels, 2, -1, 1);
131                 rgtc_decode_subblock<int8_t>((const uint32_t*)data_in + 2, (int8_t*)(pixels + 1),
132                                              2, -1, 1);
133                 break;
134             }
135             for (uint32_t cy = 0; cy < yEnd; cy ++) {
136                 uint8_t* data_out = out + (y + cy) * stride + x * texel_size;
137                 std::memcpy(data_out, pixels + kBlockSize * texel_size * cy, texel_size * xEnd);
138             }
139             data_in += data_block_size;
140         }
141     }
142     return 0;
143 }
144 
rgtc_get_encoded_image_size(RGTCImageFormat format,uint32_t width,uint32_t height)145 size_t rgtc_get_encoded_image_size(RGTCImageFormat format, uint32_t width, uint32_t height) {
146     uint32_t w = (width + kBlockSize - 1) / kBlockSize;
147     uint32_t h = (height + kBlockSize - 1) / kBlockSize;
148     return w * h * rgtc_get_block_size(format);
149 }