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 }