Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/basis_universal/encoder/basisu_pvrtc1_4.h
9902 views
1
// basisu_pvrtc1_4.cpp
2
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
3
//
4
// Licensed under the Apache License, Version 2.0 (the "License");
5
// you may not use this file except in compliance with the License.
6
// You may obtain a copy of the License at
7
//
8
// http://www.apache.org/licenses/LICENSE-2.0
9
//
10
// Unless required by applicable law or agreed to in writing, software
11
// distributed under the License is distributed on an "AS IS" BASIS,
12
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
// See the License for the specific language governing permissions and
14
// limitations under the License.
15
#pragma once
16
#include "basisu_gpu_texture.h"
17
18
namespace basisu
19
{
20
enum
21
{
22
PVRTC2_MIN_WIDTH = 16,
23
PVRTC2_MIN_HEIGHT = 8,
24
PVRTC4_MIN_WIDTH = 8,
25
PVRTC4_MIN_HEIGHT = 8
26
};
27
28
struct pvrtc4_block
29
{
30
uint32_t m_modulation;
31
uint32_t m_endpoints;
32
33
pvrtc4_block() : m_modulation(0), m_endpoints(0) { }
34
35
inline bool operator== (const pvrtc4_block& rhs) const
36
{
37
return (m_modulation == rhs.m_modulation) && (m_endpoints == rhs.m_endpoints);
38
}
39
40
inline void clear()
41
{
42
m_modulation = 0;
43
m_endpoints = 0;
44
}
45
46
inline bool get_block_uses_transparent_modulation() const
47
{
48
return (m_endpoints & 1) != 0;
49
}
50
51
inline bool is_endpoint_opaque(uint32_t endpoint_index) const
52
{
53
static const uint32_t s_bitmasks[2] = { 0x8000U, 0x80000000U };
54
return (m_endpoints & s_bitmasks[open_range_check(endpoint_index, 2U)]) != 0;
55
}
56
57
// Returns raw endpoint or 8888
58
color_rgba get_endpoint(uint32_t endpoint_index, bool unpack) const;
59
60
color_rgba get_endpoint_5554(uint32_t endpoint_index) const;
61
62
static uint32_t get_component_precision_in_bits(uint32_t c, uint32_t endpoint_index, bool opaque_endpoint)
63
{
64
static const uint32_t s_comp_prec[4][4] =
65
{
66
// R0 G0 B0 A0 R1 G1 B1 A1
67
{ 4, 4, 3, 3 }, { 4, 4, 4, 3 }, // transparent endpoint
68
69
{ 5, 5, 4, 0 }, { 5, 5, 5, 0 } // opaque endpoint
70
};
71
return s_comp_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)][open_range_check(c, 4U)];
72
}
73
74
static color_rgba get_color_precision_in_bits(uint32_t endpoint_index, bool opaque_endpoint)
75
{
76
static const color_rgba s_color_prec[4] =
77
{
78
color_rgba(4, 4, 3, 3), color_rgba(4, 4, 4, 3), // transparent endpoint
79
color_rgba(5, 5, 4, 0), color_rgba(5, 5, 5, 0) // opaque endpoint
80
};
81
return s_color_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)];
82
}
83
84
inline uint32_t get_modulation(uint32_t x, uint32_t y) const
85
{
86
assert((x < 4) && (y < 4));
87
return (m_modulation >> ((y * 4 + x) * 2)) & 3;
88
}
89
90
inline void set_modulation(uint32_t x, uint32_t y, uint32_t s)
91
{
92
assert((x < 4) && (y < 4) && (s < 4));
93
uint32_t n = (y * 4 + x) * 2;
94
m_modulation = (m_modulation & (~(3 << n))) | (s << n);
95
assert(get_modulation(x, y) == s);
96
}
97
98
// Scaled by 8
99
inline const uint32_t* get_scaled_modulation_values(bool block_uses_transparent_modulation) const
100
{
101
static const uint32_t s_block_scales[2][4] = { { 0, 3, 5, 8 }, { 0, 4, 4, 8 } };
102
return s_block_scales[block_uses_transparent_modulation];
103
}
104
105
// Scaled by 8
106
inline uint32_t get_scaled_modulation(uint32_t x, uint32_t y) const
107
{
108
return get_scaled_modulation_values(get_block_uses_transparent_modulation())[get_modulation(x, y)];
109
}
110
111
inline void byte_swap()
112
{
113
m_modulation = byteswap32(m_modulation);
114
m_endpoints = byteswap32(m_endpoints);
115
}
116
117
// opaque endpoints: 554, 555
118
// transparent endpoints: 3443, 3444
119
inline void set_endpoint_raw(uint32_t endpoint_index, const color_rgba& c, bool opaque_endpoint)
120
{
121
assert(endpoint_index < 2);
122
const uint32_t m = m_endpoints & 1;
123
uint32_t r = c[0], g = c[1], b = c[2], a = c[3];
124
125
uint32_t packed;
126
127
if (opaque_endpoint)
128
{
129
if (!endpoint_index)
130
{
131
// 554
132
// 1RRRRRGGGGGBBBBM
133
assert((r < 32) && (g < 32) && (b < 16));
134
packed = 0x8000 | (r << 10) | (g << 5) | (b << 1) | m;
135
}
136
else
137
{
138
// 555
139
// 1RRRRRGGGGGBBBBB
140
assert((r < 32) && (g < 32) && (b < 32));
141
packed = 0x8000 | (r << 10) | (g << 5) | b;
142
}
143
}
144
else
145
{
146
if (!endpoint_index)
147
{
148
// 3443
149
// 0AAA RRRR GGGG BBBM
150
assert((r < 16) && (g < 16) && (b < 8) && (a < 8));
151
packed = (a << 12) | (r << 8) | (g << 4) | (b << 1) | m;
152
}
153
else
154
{
155
// 3444
156
// 0AAA RRRR GGGG BBBB
157
assert((r < 16) && (g < 16) && (b < 16) && (a < 8));
158
packed = (a << 12) | (r << 8) | (g << 4) | b;
159
}
160
}
161
162
assert(packed <= 0xFFFF);
163
164
if (endpoint_index)
165
m_endpoints = (m_endpoints & 0xFFFFU) | (packed << 16);
166
else
167
m_endpoints = (m_endpoints & 0xFFFF0000U) | packed;
168
}
169
};
170
171
typedef vector2D<pvrtc4_block> pvrtc4_block_vector2D;
172
173
uint32_t pvrtc4_swizzle_uv(uint32_t XSize, uint32_t YSize, uint32_t XPos, uint32_t YPos);
174
175
class pvrtc4_image
176
{
177
public:
178
inline pvrtc4_image() :
179
m_width(0), m_height(0), m_block_width(0), m_block_height(0), m_uses_alpha(false)
180
{
181
}
182
183
inline pvrtc4_image(uint32_t width, uint32_t height) :
184
m_width(0), m_height(0), m_block_width(0), m_block_height(0), m_uses_alpha(false)
185
{
186
resize(width, height);
187
}
188
189
inline void clear()
190
{
191
m_width = 0;
192
m_height = 0;
193
m_block_width = 0;
194
m_block_height = 0;
195
m_blocks.clear();
196
m_uses_alpha = false;
197
}
198
199
inline void resize(uint32_t width, uint32_t height)
200
{
201
if ((width == m_width) && (height == m_height))
202
return;
203
204
m_width = width;
205
m_height = height;
206
207
m_block_width = (width + 3) >> 2;
208
m_block_height = (height + 3) >> 2;
209
210
m_blocks.resize(m_block_width, m_block_height);
211
}
212
213
inline uint32_t get_width() const { return m_width; }
214
inline uint32_t get_height() const { return m_height; }
215
216
inline uint32_t get_block_width() const { return m_block_width; }
217
inline uint32_t get_block_height() const { return m_block_height; }
218
219
inline const pvrtc4_block_vector2D &get_blocks() const { return m_blocks; }
220
inline pvrtc4_block_vector2D &get_blocks() { return m_blocks; }
221
222
inline uint32_t get_total_blocks() const { return m_block_width * m_block_height; }
223
224
inline bool get_uses_alpha() const { return m_uses_alpha; }
225
inline void set_uses_alpha(bool uses_alpha) { m_uses_alpha = uses_alpha; }
226
227
inline bool are_blocks_equal(const pvrtc4_image& rhs) const
228
{
229
return m_blocks == rhs.m_blocks;
230
}
231
232
inline void set_to_black()
233
{
234
#ifndef __EMSCRIPTEN__
235
#ifdef __GNUC__
236
#pragma GCC diagnostic push
237
#pragma GCC diagnostic ignored "-Wclass-memaccess"
238
#endif
239
#endif
240
memset(m_blocks.get_ptr(), 0, m_blocks.size_in_bytes());
241
#ifndef __EMSCRIPTEN__
242
#ifdef __GNUC__
243
#pragma GCC diagnostic pop
244
#endif
245
#endif
246
}
247
248
inline bool get_block_uses_transparent_modulation(uint32_t bx, uint32_t by) const
249
{
250
return m_blocks(bx, by).get_block_uses_transparent_modulation();
251
}
252
253
inline bool is_endpoint_opaque(uint32_t bx, uint32_t by, uint32_t endpoint_index) const
254
{
255
return m_blocks(bx, by).is_endpoint_opaque(endpoint_index);
256
}
257
258
color_rgba get_endpoint(uint32_t bx, uint32_t by, uint32_t endpoint_index, bool unpack) const
259
{
260
assert((bx < m_block_width) && (by < m_block_height));
261
return m_blocks(bx, by).get_endpoint(endpoint_index, unpack);
262
}
263
264
inline uint32_t get_modulation(uint32_t x, uint32_t y) const
265
{
266
assert((x < m_width) && (y < m_height));
267
return m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3);
268
}
269
270
// Returns true if the block uses transparent modulation.
271
bool get_interpolated_colors(uint32_t x, uint32_t y, color_rgba* pColors) const;
272
273
color_rgba get_pixel(uint32_t x, uint32_t y, uint32_t m) const;
274
275
inline color_rgba get_pixel(uint32_t x, uint32_t y) const
276
{
277
assert((x < m_width) && (y < m_height));
278
return get_pixel(x, y, m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3));
279
}
280
281
void deswizzle()
282
{
283
pvrtc4_block_vector2D temp(m_blocks);
284
285
for (uint32_t y = 0; y < m_block_height; y++)
286
for (uint32_t x = 0; x < m_block_width; x++)
287
m_blocks(x, y) = temp[pvrtc4_swizzle_uv(m_block_width, m_block_height, x, y)];
288
}
289
290
void swizzle()
291
{
292
pvrtc4_block_vector2D temp(m_blocks);
293
294
for (uint32_t y = 0; y < m_block_height; y++)
295
for (uint32_t x = 0; x < m_block_width; x++)
296
m_blocks[pvrtc4_swizzle_uv(m_block_width, m_block_height, x, y)] = temp(x, y);
297
}
298
299
void unpack_all_pixels(image& img) const
300
{
301
img.crop(m_width, m_height);
302
303
for (uint32_t y = 0; y < m_height; y++)
304
for (uint32_t x = 0; x < m_width; x++)
305
img(x, y) = get_pixel(x, y);
306
}
307
308
void unpack_block(image &dst, uint32_t block_x, uint32_t block_y)
309
{
310
for (uint32_t y = 0; y < 4; y++)
311
for (uint32_t x = 0; x < 4; x++)
312
dst(x, y) = get_pixel(block_x * 4 + x, block_y * 4 + y);
313
}
314
315
inline int wrap_x(int x) const
316
{
317
return posmod(x, m_width);
318
}
319
320
inline int wrap_y(int y) const
321
{
322
return posmod(y, m_height);
323
}
324
325
inline int wrap_block_x(int bx) const
326
{
327
return posmod(bx, m_block_width);
328
}
329
330
inline int wrap_block_y(int by) const
331
{
332
return posmod(by, m_block_height);
333
}
334
335
inline vec2F get_interpolation_factors(uint32_t x, uint32_t y) const
336
{
337
// 0 1 2 3
338
// 2 3 0 1
339
// .5 .75 0 .25
340
static const float s_interp[4] = { 2, 3, 0, 1 };
341
return vec2F(s_interp[x & 3], s_interp[y & 3]);
342
}
343
344
inline color_rgba interpolate(int x, int y,
345
const color_rgba& p, const color_rgba& q,
346
const color_rgba& r, const color_rgba& s) const
347
{
348
static const int s_interp[4] = { 2, 3, 0, 1 };
349
const int u_interp = s_interp[x & 3];
350
const int v_interp = s_interp[y & 3];
351
352
color_rgba result;
353
354
for (uint32_t c = 0; c < 4; c++)
355
{
356
int t = p[c] * 4 + u_interp * ((int)q[c] - (int)p[c]);
357
int b = r[c] * 4 + u_interp * ((int)s[c] - (int)r[c]);
358
int v = t * 4 + v_interp * (b - t);
359
if (c < 3)
360
{
361
v >>= 1;
362
v += (v >> 5);
363
}
364
else
365
{
366
v += (v >> 4);
367
}
368
assert((v >= 0) && (v < 256));
369
result[c] = static_cast<uint8_t>(v);
370
}
371
372
return result;
373
}
374
375
inline void set_modulation(uint32_t x, uint32_t y, uint32_t s)
376
{
377
assert((x < m_width) && (y < m_height));
378
return m_blocks(x >> 2, y >> 2).set_modulation(x & 3, y & 3, s);
379
}
380
381
inline uint64_t map_pixel(uint32_t x, uint32_t y, const color_rgba& c, bool perceptual, bool alpha_is_significant, bool record = true)
382
{
383
color_rgba v[4];
384
get_interpolated_colors(x, y, v);
385
386
uint64_t best_dist = color_distance(perceptual, c, v[0], alpha_is_significant);
387
uint32_t best_v = 0;
388
for (uint32_t i = 1; i < 4; i++)
389
{
390
uint64_t dist = color_distance(perceptual, c, v[i], alpha_is_significant);
391
if (dist < best_dist)
392
{
393
best_dist = dist;
394
best_v = i;
395
}
396
}
397
398
if (record)
399
set_modulation(x, y, best_v);
400
401
return best_dist;
402
}
403
404
inline uint64_t remap_pixels_influenced_by_endpoint(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual, bool alpha_is_significant)
405
{
406
uint64_t total_error = 0;
407
408
for (int yd = -3; yd <= 3; yd++)
409
{
410
const int y = wrap_y((int)by * 4 + 2 + yd);
411
412
for (int xd = -3; xd <= 3; xd++)
413
{
414
const int x = wrap_x((int)bx * 4 + 2 + xd);
415
416
total_error += map_pixel(x, y, orig_img(x, y), perceptual, alpha_is_significant);
417
}
418
}
419
420
return total_error;
421
}
422
423
inline 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) const
424
{
425
uint64_t total_error = 0;
426
427
for (int yd = -3; yd <= 3; yd++)
428
{
429
const int y = wrap_y((int)by * 4 + 2 + yd);
430
431
for (int xd = -3; xd <= 3; xd++)
432
{
433
const int x = wrap_x((int)bx * 4 + 2 + xd);
434
435
total_error += color_distance(perceptual, get_pixel(x, y), orig_img(x, y), alpha_is_significant);
436
437
if ((threshold_error) && (total_error >= threshold_error))
438
return total_error;
439
}
440
}
441
442
return total_error;
443
}
444
445
uint64_t local_endpoint_optimization_opaque(uint32_t bx, uint32_t by, const image& orig_img, bool perceptual);
446
447
inline uint64_t map_all_pixels(const image& img, bool perceptual, bool alpha_is_significant)
448
{
449
assert(m_width == img.get_width());
450
assert(m_height == img.get_height());
451
452
uint64_t total_error = 0;
453
for (uint32_t y = 0; y < img.get_height(); y++)
454
for (uint32_t x = 0; x < img.get_width(); x++)
455
total_error += map_pixel(x, y, img(x, y), perceptual, alpha_is_significant);
456
457
return total_error;
458
}
459
460
public:
461
uint32_t m_width, m_height;
462
pvrtc4_block_vector2D m_blocks;
463
uint32_t m_block_width, m_block_height;
464
465
bool m_uses_alpha;
466
};
467
468
} // namespace basisu
469
470