@anhldh/gltf-lod-pipeline 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1266 @@
1
+ //#define _DEBUG
2
+
3
+ #ifndef NULL
4
+ #define NULL 0L
5
+ #endif
6
+
7
+ typedef char int8_t;
8
+ typedef uchar uint8_t;
9
+
10
+ typedef short int16_t;
11
+ typedef ushort uint16_t;
12
+
13
+ typedef int int32_t;
14
+ typedef uint uint32_t;
15
+
16
+ typedef long int64_t;
17
+ typedef ulong uint64_t;
18
+
19
+ typedef uchar4 color_rgba;
20
+
21
+ #define UINT32_MAX 0xFFFFFFFFUL
22
+ #define INT64_MAX LONG_MAX
23
+ #define UINT64_MAX ULONG_MAX
24
+
25
+ int squarei(int a) { return a * a; }
26
+
27
+ #ifdef _DEBUG
28
+ inline void internal_assert(bool x, constant char *pMsg, int line)
29
+ {
30
+ if (!x)
31
+ printf("assert() failed on line %i: %s\n", line, pMsg);
32
+ }
33
+ #define assert(x) internal_assert(x, #x, __LINE__)
34
+ #else
35
+ #define assert(x)
36
+ #endif
37
+
38
+ inline uint8_t clamp255(int x)
39
+ {
40
+ return clamp(x, 0, 255);
41
+ }
42
+
43
+ inline uint8_t clamp255_flag(int x, bool *pDid_clamp)
44
+ {
45
+ if (x < 0)
46
+ {
47
+ *pDid_clamp = true;
48
+ return 0;
49
+ }
50
+ else if (x > 255)
51
+ {
52
+ *pDid_clamp = true;
53
+ return 255;
54
+ }
55
+
56
+ return (uint8_t)(x);
57
+ }
58
+
59
+ typedef struct __attribute__ ((packed)) encode_etc1s_param_struct_tag
60
+ {
61
+ uint32_t m_total_blocks;
62
+ int m_perceptual;
63
+ int m_total_perms;
64
+ } encode_etc1s_param_struct;
65
+
66
+ typedef struct __attribute__ ((packed)) pixel_block_tag
67
+ {
68
+ color_rgba m_pixels[16]; // [y*4+x]
69
+ } pixel_block;
70
+
71
+ uint color_distance(bool perceptual, color_rgba e1, color_rgba e2, bool alpha)
72
+ {
73
+ if (perceptual)
74
+ {
75
+ // This matches the CPU code, which is useful for testing.
76
+ int dr = e1.x - e2.x;
77
+ int dg = e1.y - e2.y;
78
+ int db = e1.z - e2.z;
79
+
80
+ int delta_l = dr * 14 + dg * 45 + db * 5;
81
+ int delta_cr = dr * 64 - delta_l;
82
+ int delta_cb = db * 64 - delta_l;
83
+
84
+ uint id = ((uint)(delta_l * delta_l) >> 5U) +
85
+ ((((uint)(delta_cr * delta_cr) >> 5U) * 26U) >> 7U) +
86
+ ((((uint)(delta_cb * delta_cb) >> 5U) * 3U) >> 7U);
87
+
88
+ if (alpha)
89
+ {
90
+ int da = (e1.w - e2.w) << 7;
91
+ id += ((uint)(da * da) >> 7U);
92
+ }
93
+
94
+ return id;
95
+ }
96
+ else if (alpha)
97
+ {
98
+ int dr = e1.x - e2.x;
99
+ int dg = e1.y - e2.y;
100
+ int db = e1.z - e2.z;
101
+ int da = e1.w - e2.w;
102
+ return dr * dr + dg * dg + db * db + da * da;
103
+ }
104
+ else
105
+ {
106
+ int dr = e1.x - e2.x;
107
+ int dg = e1.y - e2.y;
108
+ int db = e1.z - e2.z;
109
+ return dr * dr + dg * dg + db * db;
110
+ }
111
+ }
112
+
113
+ typedef struct __attribute__ ((packed)) etc_block_tag
114
+ {
115
+ // big endian uint64:
116
+ // bit ofs: 56 48 40 32 24 16 8 0
117
+ // byte ofs: b0, b1, b2, b3, b4, b5, b6, b7
118
+ union
119
+ {
120
+ uint64_t m_uint64;
121
+ uint8_t m_bytes[8];
122
+ };
123
+
124
+ } etc_block;
125
+
126
+ enum etc_constants
127
+ {
128
+ cETC1BytesPerBlock = 8U,
129
+
130
+ cETC1SelectorBits = 2U,
131
+ cETC1SelectorValues = 1U << cETC1SelectorBits,
132
+ cETC1SelectorMask = cETC1SelectorValues - 1U,
133
+
134
+ cETC1BlockShift = 2U,
135
+ cETC1BlockSize = 1U << cETC1BlockShift,
136
+
137
+ cETC1LSBSelectorIndicesBitOffset = 0,
138
+ cETC1MSBSelectorIndicesBitOffset = 16,
139
+
140
+ cETC1FlipBitOffset = 32,
141
+ cETC1DiffBitOffset = 33,
142
+
143
+ cETC1IntenModifierNumBits = 3,
144
+ cETC1IntenModifierValues = 1 << cETC1IntenModifierNumBits,
145
+ cETC1RightIntenModifierTableBitOffset = 34,
146
+ cETC1LeftIntenModifierTableBitOffset = 37,
147
+
148
+ // Base+Delta encoding (5 bit bases, 3 bit delta)
149
+ cETC1BaseColorCompNumBits = 5,
150
+ cETC1BaseColorCompMax = 1 << cETC1BaseColorCompNumBits,
151
+
152
+ cETC1DeltaColorCompNumBits = 3,
153
+ cETC1DeltaColorComp = 1 << cETC1DeltaColorCompNumBits,
154
+ cETC1DeltaColorCompMax = 1 << cETC1DeltaColorCompNumBits,
155
+
156
+ cETC1BaseColor5RBitOffset = 59,
157
+ cETC1BaseColor5GBitOffset = 51,
158
+ cETC1BaseColor5BBitOffset = 43,
159
+
160
+ cETC1DeltaColor3RBitOffset = 56,
161
+ cETC1DeltaColor3GBitOffset = 48,
162
+ cETC1DeltaColor3BBitOffset = 40,
163
+
164
+ // Absolute (non-delta) encoding (two 4-bit per component bases)
165
+ cETC1AbsColorCompNumBits = 4,
166
+ cETC1AbsColorCompMax = 1 << cETC1AbsColorCompNumBits,
167
+
168
+ cETC1AbsColor4R1BitOffset = 60,
169
+ cETC1AbsColor4G1BitOffset = 52,
170
+ cETC1AbsColor4B1BitOffset = 44,
171
+
172
+ cETC1AbsColor4R2BitOffset = 56,
173
+ cETC1AbsColor4G2BitOffset = 48,
174
+ cETC1AbsColor4B2BitOffset = 40,
175
+
176
+ cETC1ColorDeltaMin = -4,
177
+ cETC1ColorDeltaMax = 3,
178
+
179
+ // Delta3:
180
+ // 0 1 2 3 4 5 6 7
181
+ // 000 001 010 011 100 101 110 111
182
+ // 0 1 2 3 -4 -3 -2 -1
183
+ };
184
+
185
+ #define BASISU_ETC1_CLUSTER_FIT_ORDER_TABLE_SIZE (165)
186
+ constant struct { uint8_t m_v[4]; } g_cluster_fit_order_tab[BASISU_ETC1_CLUSTER_FIT_ORDER_TABLE_SIZE] =
187
+ {
188
+ { { 0, 0, 0, 8 } },{ { 0, 5, 2, 1 } },{ { 0, 6, 1, 1 } },{ { 0, 7, 0, 1 } },{ { 0, 7, 1, 0 } },
189
+ { { 0, 0, 8, 0 } },{ { 0, 0, 3, 5 } },{ { 0, 1, 7, 0 } },{ { 0, 0, 4, 4 } },{ { 0, 0, 2, 6 } },
190
+ { { 0, 0, 7, 1 } },{ { 0, 0, 1, 7 } },{ { 0, 0, 5, 3 } },{ { 1, 6, 0, 1 } },{ { 0, 0, 6, 2 } },
191
+ { { 0, 2, 6, 0 } },{ { 2, 4, 2, 0 } },{ { 0, 3, 5, 0 } },{ { 3, 3, 1, 1 } },{ { 4, 2, 0, 2 } },
192
+ { { 1, 5, 2, 0 } },{ { 0, 5, 3, 0 } },{ { 0, 6, 2, 0 } },{ { 2, 4, 1, 1 } },{ { 5, 1, 0, 2 } },
193
+ { { 6, 1, 1, 0 } },{ { 3, 3, 0, 2 } },{ { 6, 0, 0, 2 } },{ { 0, 8, 0, 0 } },{ { 6, 1, 0, 1 } },
194
+ { { 0, 1, 6, 1 } },{ { 1, 6, 1, 0 } },{ { 4, 1, 3, 0 } },{ { 0, 2, 5, 1 } },{ { 5, 0, 3, 0 } },
195
+ { { 5, 3, 0, 0 } },{ { 0, 1, 5, 2 } },{ { 0, 3, 4, 1 } },{ { 2, 5, 1, 0 } },{ { 1, 7, 0, 0 } },
196
+ { { 0, 1, 4, 3 } },{ { 6, 0, 2, 0 } },{ { 0, 4, 4, 0 } },{ { 2, 6, 0, 0 } },{ { 0, 2, 4, 2 } },
197
+ { { 0, 5, 1, 2 } },{ { 0, 6, 0, 2 } },{ { 3, 5, 0, 0 } },{ { 0, 4, 3, 1 } },{ { 3, 4, 1, 0 } },
198
+ { { 4, 3, 1, 0 } },{ { 1, 5, 0, 2 } },{ { 0, 3, 3, 2 } },{ { 1, 4, 1, 2 } },{ { 0, 4, 2, 2 } },
199
+ { { 2, 3, 3, 0 } },{ { 4, 4, 0, 0 } },{ { 1, 2, 4, 1 } },{ { 0, 5, 0, 3 } },{ { 0, 1, 3, 4 } },
200
+ { { 1, 5, 1, 1 } },{ { 1, 4, 2, 1 } },{ { 1, 3, 2, 2 } },{ { 5, 2, 1, 0 } },{ { 1, 3, 3, 1 } },
201
+ { { 0, 1, 2, 5 } },{ { 1, 1, 5, 1 } },{ { 0, 3, 2, 3 } },{ { 2, 5, 0, 1 } },{ { 3, 2, 2, 1 } },
202
+ { { 2, 3, 0, 3 } },{ { 1, 4, 3, 0 } },{ { 2, 2, 1, 3 } },{ { 6, 2, 0, 0 } },{ { 1, 0, 6, 1 } },
203
+ { { 3, 3, 2, 0 } },{ { 7, 1, 0, 0 } },{ { 3, 1, 4, 0 } },{ { 0, 2, 3, 3 } },{ { 0, 4, 1, 3 } },
204
+ { { 0, 4, 0, 4 } },{ { 0, 1, 0, 7 } },{ { 2, 0, 5, 1 } },{ { 2, 0, 4, 2 } },{ { 3, 0, 2, 3 } },
205
+ { { 2, 2, 4, 0 } },{ { 2, 2, 3, 1 } },{ { 4, 0, 3, 1 } },{ { 3, 2, 3, 0 } },{ { 2, 3, 2, 1 } },
206
+ { { 1, 3, 4, 0 } },{ { 7, 0, 1, 0 } },{ { 3, 0, 4, 1 } },{ { 1, 0, 5, 2 } },{ { 8, 0, 0, 0 } },
207
+ { { 3, 0, 1, 4 } },{ { 4, 1, 1, 2 } },{ { 4, 0, 2, 2 } },{ { 1, 2, 5, 0 } },{ { 4, 2, 1, 1 } },
208
+ { { 3, 4, 0, 1 } },{ { 2, 0, 3, 3 } },{ { 5, 0, 1, 2 } },{ { 5, 0, 0, 3 } },{ { 2, 4, 0, 2 } },
209
+ { { 2, 1, 4, 1 } },{ { 4, 0, 1, 3 } },{ { 2, 1, 5, 0 } },{ { 4, 2, 2, 0 } },{ { 4, 0, 4, 0 } },
210
+ { { 1, 0, 4, 3 } },{ { 1, 4, 0, 3 } },{ { 3, 0, 3, 2 } },{ { 4, 3, 0, 1 } },{ { 0, 1, 1, 6 } },
211
+ { { 1, 3, 1, 3 } },{ { 0, 2, 2, 4 } },{ { 2, 0, 2, 4 } },{ { 5, 1, 1, 1 } },{ { 3, 0, 5, 0 } },
212
+ { { 2, 3, 1, 2 } },{ { 3, 0, 0, 5 } },{ { 0, 3, 1, 4 } },{ { 5, 0, 2, 1 } },{ { 2, 1, 3, 2 } },
213
+ { { 2, 0, 6, 0 } },{ { 3, 1, 3, 1 } },{ { 5, 1, 2, 0 } },{ { 1, 0, 3, 4 } },{ { 1, 1, 6, 0 } },
214
+ { { 4, 0, 0, 4 } },{ { 2, 0, 1, 5 } },{ { 0, 3, 0, 5 } },{ { 1, 3, 0, 4 } },{ { 4, 1, 2, 1 } },
215
+ { { 1, 2, 3, 2 } },{ { 3, 1, 0, 4 } },{ { 5, 2, 0, 1 } },{ { 1, 2, 2, 3 } },{ { 3, 2, 1, 2 } },
216
+ { { 2, 2, 2, 2 } },{ { 6, 0, 1, 1 } },{ { 1, 2, 1, 4 } },{ { 1, 1, 4, 2 } },{ { 3, 2, 0, 3 } },
217
+ { { 1, 2, 0, 5 } },{ { 1, 0, 7, 0 } },{ { 3, 1, 2, 2 } },{ { 1, 0, 2, 5 } },{ { 2, 0, 0, 6 } },
218
+ { { 2, 1, 1, 4 } },{ { 2, 2, 0, 4 } },{ { 1, 1, 3, 3 } },{ { 7, 0, 0, 1 } },{ { 1, 0, 0, 7 } },
219
+ { { 2, 1, 2, 3 } },{ { 4, 1, 0, 3 } },{ { 3, 1, 1, 3 } },{ { 1, 1, 2, 4 } },{ { 2, 1, 0, 5 } },
220
+ { { 1, 0, 1, 6 } },{ { 0, 2, 1, 5 } },{ { 0, 2, 0, 6 } },{ { 1, 1, 1, 5 } },{ { 1, 1, 0, 6 } }
221
+ };
222
+
223
+ constant int g_etc1_inten_tables[cETC1IntenModifierValues][cETC1SelectorValues] =
224
+ {
225
+ { -8, -2, 2, 8 }, { -17, -5, 5, 17 }, { -29, -9, 9, 29 }, { -42, -13, 13, 42 },
226
+ { -60, -18, 18, 60 }, { -80, -24, 24, 80 }, { -106, -33, 33, 106 }, { -183, -47, 47, 183 }
227
+ };
228
+
229
+ constant uint8_t g_etc1_to_selector_index[cETC1SelectorValues] = { 2, 3, 1, 0 };
230
+ constant uint8_t g_selector_index_to_etc1[cETC1SelectorValues] = { 3, 2, 0, 1 };
231
+
232
+ uint32_t etc_block_get_byte_bits(const etc_block *p, uint32_t ofs, uint32_t num)
233
+ {
234
+ assert((ofs + num) <= 64U);
235
+ assert(num && (num <= 8U));
236
+ assert((ofs >> 3) == ((ofs + num - 1) >> 3));
237
+ const uint32_t byte_ofs = 7 - (ofs >> 3);
238
+ const uint32_t byte_bit_ofs = ofs & 7;
239
+ return (p->m_bytes[byte_ofs] >> byte_bit_ofs) & ((1 << num) - 1);
240
+ }
241
+
242
+ void etc_block_set_byte_bits(etc_block *p, uint32_t ofs, uint32_t num, uint32_t bits)
243
+ {
244
+ assert((ofs + num) <= 64U);
245
+ assert(num && (num < 32U));
246
+ assert((ofs >> 3) == ((ofs + num - 1) >> 3));
247
+ assert(bits < (1U << num));
248
+ const uint32_t byte_ofs = 7 - (ofs >> 3);
249
+ const uint32_t byte_bit_ofs = ofs & 7;
250
+ const uint32_t mask = (1 << num) - 1;
251
+ p->m_bytes[byte_ofs] &= ~(mask << byte_bit_ofs);
252
+ p->m_bytes[byte_ofs] |= (bits << byte_bit_ofs);
253
+ }
254
+
255
+ bool etc_block_get_flip_bit(const etc_block *p)
256
+ {
257
+ return (p->m_bytes[3] & 1) != 0;
258
+ }
259
+
260
+ void etc_block_set_flip_bit(etc_block *p, bool flip)
261
+ {
262
+ p->m_bytes[3] &= ~1;
263
+ p->m_bytes[3] |= (uint8_t)(flip);
264
+ }
265
+
266
+ bool etc_block_get_diff_bit(const etc_block *p)
267
+ {
268
+ return (p->m_bytes[3] & 2) != 0;
269
+ }
270
+
271
+ void etc_block_set_diff_bit(etc_block *p, bool diff)
272
+ {
273
+ p->m_bytes[3] &= ~2;
274
+ p->m_bytes[3] |= ((uint32_t)(diff) << 1);
275
+ }
276
+
277
+ // Returns intensity modifier table (0-7) used by subblock subblock_id.
278
+ // subblock_id=0 left/top (CW 1), 1=right/bottom (CW 2)
279
+ uint32_t etc_block_get_inten_table(const etc_block *p, uint32_t subblock_id)
280
+ {
281
+ assert(subblock_id < 2);
282
+ const uint32_t ofs = subblock_id ? 2 : 5;
283
+ return (p->m_bytes[3] >> ofs) & 7;
284
+ }
285
+
286
+ // Sets intensity modifier table (0-7) used by subblock subblock_id (0 or 1)
287
+ void etc_block_set_inten_table(etc_block *p, uint32_t subblock_id, uint32_t t)
288
+ {
289
+ assert(subblock_id < 2);
290
+ assert(t < 8);
291
+ const uint32_t ofs = subblock_id ? 2 : 5;
292
+ p->m_bytes[3] &= ~(7 << ofs);
293
+ p->m_bytes[3] |= (t << ofs);
294
+ }
295
+
296
+ void etc_block_set_inten_tables_etc1s(etc_block *p, uint32_t t)
297
+ {
298
+ etc_block_set_inten_table(p, 0, t);
299
+ etc_block_set_inten_table(p, 1, t);
300
+ }
301
+
302
+ uint32_t etc_block_get_raw_selector(const etc_block *pBlock, uint32_t x, uint32_t y)
303
+ {
304
+ assert((x | y) < 4);
305
+
306
+ const uint32_t bit_index = x * 4 + y;
307
+ const uint32_t byte_bit_ofs = bit_index & 7;
308
+ const uint8_t *p = &pBlock->m_bytes[7 - (bit_index >> 3)];
309
+ const uint32_t lsb = (p[0] >> byte_bit_ofs) & 1;
310
+ const uint32_t msb = (p[-2] >> byte_bit_ofs) & 1;
311
+ const uint32_t val = lsb | (msb << 1);
312
+
313
+ return val;
314
+ }
315
+
316
+ // Returned selector value ranges from 0-3 and is a direct index into g_etc1_inten_tables.
317
+ uint32_t etc_block_get_selector(const etc_block *pBlock, uint32_t x, uint32_t y)
318
+ {
319
+ return g_etc1_to_selector_index[etc_block_get_raw_selector(pBlock, x, y)];
320
+ }
321
+
322
+ // Selector "val" ranges from 0-3 and is a direct index into g_etc1_inten_tables.
323
+ void etc_block_set_selector(etc_block *pBlock, uint32_t x, uint32_t y, uint32_t val)
324
+ {
325
+ assert((x | y | val) < 4);
326
+ const uint32_t bit_index = x * 4 + y;
327
+
328
+ uint8_t *p = &pBlock->m_bytes[7 - (bit_index >> 3)];
329
+
330
+ const uint32_t byte_bit_ofs = bit_index & 7;
331
+ const uint32_t mask = 1 << byte_bit_ofs;
332
+
333
+ const uint32_t etc1_val = g_selector_index_to_etc1[val];
334
+
335
+ const uint32_t lsb = etc1_val & 1;
336
+ const uint32_t msb = etc1_val >> 1;
337
+
338
+ p[0] &= ~mask;
339
+ p[0] |= (lsb << byte_bit_ofs);
340
+
341
+ p[-2] &= ~mask;
342
+ p[-2] |= (msb << byte_bit_ofs);
343
+ }
344
+
345
+ void etc_block_set_base4_color(etc_block *pBlock, uint32_t idx, uint16_t c)
346
+ {
347
+ if (idx)
348
+ {
349
+ etc_block_set_byte_bits(pBlock, cETC1AbsColor4R2BitOffset, 4, (c >> 8) & 15);
350
+ etc_block_set_byte_bits(pBlock, cETC1AbsColor4G2BitOffset, 4, (c >> 4) & 15);
351
+ etc_block_set_byte_bits(pBlock, cETC1AbsColor4B2BitOffset, 4, c & 15);
352
+ }
353
+ else
354
+ {
355
+ etc_block_set_byte_bits(pBlock, cETC1AbsColor4R1BitOffset, 4, (c >> 8) & 15);
356
+ etc_block_set_byte_bits(pBlock, cETC1AbsColor4G1BitOffset, 4, (c >> 4) & 15);
357
+ etc_block_set_byte_bits(pBlock, cETC1AbsColor4B1BitOffset, 4, c & 15);
358
+ }
359
+ }
360
+
361
+ uint16_t etc_block_get_base4_color(const etc_block *pBlock, uint32_t idx)
362
+ {
363
+ uint32_t r, g, b;
364
+ if (idx)
365
+ {
366
+ r = etc_block_get_byte_bits(pBlock, cETC1AbsColor4R2BitOffset, 4);
367
+ g = etc_block_get_byte_bits(pBlock, cETC1AbsColor4G2BitOffset, 4);
368
+ b = etc_block_get_byte_bits(pBlock, cETC1AbsColor4B2BitOffset, 4);
369
+ }
370
+ else
371
+ {
372
+ r = etc_block_get_byte_bits(pBlock, cETC1AbsColor4R1BitOffset, 4);
373
+ g = etc_block_get_byte_bits(pBlock, cETC1AbsColor4G1BitOffset, 4);
374
+ b = etc_block_get_byte_bits(pBlock, cETC1AbsColor4B1BitOffset, 4);
375
+ }
376
+ return (uint16_t)(b | (g << 4U) | (r << 8U));
377
+ }
378
+
379
+ void etc_block_set_base5_color(etc_block *pBlock, uint16_t c)
380
+ {
381
+ etc_block_set_byte_bits(pBlock, cETC1BaseColor5RBitOffset, 5, (c >> 10) & 31);
382
+ etc_block_set_byte_bits(pBlock, cETC1BaseColor5GBitOffset, 5, (c >> 5) & 31);
383
+ etc_block_set_byte_bits(pBlock, cETC1BaseColor5BBitOffset, 5, c & 31);
384
+ }
385
+
386
+ uint16_t etc_block_get_base5_color(const etc_block *pBlock)
387
+ {
388
+ const uint32_t r = etc_block_get_byte_bits(pBlock, cETC1BaseColor5RBitOffset, 5);
389
+ const uint32_t g = etc_block_get_byte_bits(pBlock, cETC1BaseColor5GBitOffset, 5);
390
+ const uint32_t b = etc_block_get_byte_bits(pBlock, cETC1BaseColor5BBitOffset, 5);
391
+ return (uint16_t)(b | (g << 5U) | (r << 10U));
392
+ }
393
+
394
+ void etc_block_set_delta3_color(etc_block *pBlock, uint16_t c)
395
+ {
396
+ etc_block_set_byte_bits(pBlock, cETC1DeltaColor3RBitOffset, 3, (c >> 6) & 7);
397
+ etc_block_set_byte_bits(pBlock, cETC1DeltaColor3GBitOffset, 3, (c >> 3) & 7);
398
+ etc_block_set_byte_bits(pBlock, cETC1DeltaColor3BBitOffset, 3, c & 7);
399
+ }
400
+
401
+ uint16_t etc_block_get_delta3_color(const etc_block *pBlock)
402
+ {
403
+ const uint32_t r = etc_block_get_byte_bits(pBlock, cETC1DeltaColor3RBitOffset, 3);
404
+ const uint32_t g = etc_block_get_byte_bits(pBlock, cETC1DeltaColor3GBitOffset, 3);
405
+ const uint32_t b = etc_block_get_byte_bits(pBlock, cETC1DeltaColor3BBitOffset, 3);
406
+ return (uint16_t)(b | (g << 3U) | (r << 6U));
407
+ }
408
+
409
+ void etc_block_unpack_delta3(int *pR, int *pG, int *pB, uint16_t packed_delta3)
410
+ {
411
+ int r = (packed_delta3 >> 6) & 7;
412
+ int g = (packed_delta3 >> 3) & 7;
413
+ int b = packed_delta3 & 7;
414
+ if (r >= 4) r -= 8;
415
+ if (g >= 4) g -= 8;
416
+ if (b >= 4) b -= 8;
417
+ *pR = r;
418
+ *pG = g;
419
+ *pB = b;
420
+ }
421
+
422
+ bool etc_block_unpack_color5_delta3(color_rgba *pResult, uint16_t packed_color5, uint16_t packed_delta3, bool scaled, uint32_t alpha)
423
+ {
424
+ int dr, dg, db;
425
+ etc_block_unpack_delta3(&dr, &dg, &db, packed_delta3);
426
+
427
+ int b = (packed_color5 & 31U) + db;
428
+ int g = ((packed_color5 >> 5U) & 31U) + dg;
429
+ int r = ((packed_color5 >> 10U) & 31U) + dr;
430
+
431
+ bool success = true;
432
+ if ((uint32_t)(r | g | b) > 31U)
433
+ {
434
+ success = false;
435
+ r = clamp(r, 0, 31);
436
+ g = clamp(g, 0, 31);
437
+ b = clamp(b, 0, 31);
438
+ }
439
+
440
+ if (scaled)
441
+ {
442
+ b = (b << 3U) | (b >> 2U);
443
+ g = (g << 3U) | (g >> 2U);
444
+ r = (r << 3U) | (r >> 2U);
445
+ }
446
+
447
+ *pResult = (color_rgba)(r, g, b, min(alpha, 255U));
448
+ return success;
449
+ }
450
+
451
+ color_rgba etc_block_unpack_color5(uint16_t packed_color5, bool scaled, uint32_t alpha)
452
+ {
453
+ uint32_t b = packed_color5 & 31U;
454
+ uint32_t g = (packed_color5 >> 5U) & 31U;
455
+ uint32_t r = (packed_color5 >> 10U) & 31U;
456
+
457
+ if (scaled)
458
+ {
459
+ b = (b << 3U) | (b >> 2U);
460
+ g = (g << 3U) | (g >> 2U);
461
+ r = (r << 3U) | (r >> 2U);
462
+ }
463
+
464
+ return (color_rgba)(r, g, b, min(alpha, 255U));
465
+ }
466
+
467
+ color_rgba etc_block_unpack_color4(uint16_t packed_color4, bool scaled, uint32_t alpha)
468
+ {
469
+ uint32_t b = packed_color4 & 15U;
470
+ uint32_t g = (packed_color4 >> 4U) & 15U;
471
+ uint32_t r = (packed_color4 >> 8U) & 15U;
472
+
473
+ if (scaled)
474
+ {
475
+ b = (b << 4U) | b;
476
+ g = (g << 4U) | g;
477
+ r = (r << 4U) | r;
478
+ }
479
+
480
+ return (color_rgba)(r, g, b, min(alpha, 255U));
481
+ }
482
+
483
+ // false if didn't clamp, true if any component clamped
484
+ bool etc_block_get_block_colors(const etc_block *pBlock, color_rgba* pBlock_colors, uint32_t subblock_index)
485
+ {
486
+ color_rgba b;
487
+
488
+ if (etc_block_get_diff_bit(pBlock))
489
+ {
490
+ if (subblock_index)
491
+ etc_block_unpack_color5_delta3(&b, etc_block_get_base5_color(pBlock), etc_block_get_delta3_color(pBlock), true, 255);
492
+ else
493
+ b = etc_block_unpack_color5(etc_block_get_base5_color(pBlock), true, 255);
494
+ }
495
+ else
496
+ {
497
+ b = etc_block_unpack_color4(etc_block_get_base4_color(pBlock, subblock_index), true, 255);
498
+ }
499
+
500
+ constant int* pInten_table = g_etc1_inten_tables[etc_block_get_inten_table(pBlock, subblock_index)];
501
+
502
+ bool dc = false;
503
+ pBlock_colors[0] = (color_rgba)(clamp255_flag(b.x + pInten_table[0], &dc), clamp255_flag(b.y + pInten_table[0], &dc), clamp255_flag(b.z + pInten_table[0], &dc), 255);
504
+ pBlock_colors[1] = (color_rgba)(clamp255_flag(b.x + pInten_table[1], &dc), clamp255_flag(b.y + pInten_table[1], &dc), clamp255_flag(b.z + pInten_table[1], &dc), 255);
505
+ pBlock_colors[2] = (color_rgba)(clamp255_flag(b.x + pInten_table[2], &dc), clamp255_flag(b.y + pInten_table[2], &dc), clamp255_flag(b.z + pInten_table[2], &dc), 255);
506
+ pBlock_colors[3] = (color_rgba)(clamp255_flag(b.x + pInten_table[3], &dc), clamp255_flag(b.y + pInten_table[3], &dc), clamp255_flag(b.z + pInten_table[3], &dc), 255);
507
+ return dc;
508
+ }
509
+
510
+ void get_block_colors5(color_rgba *pBlock_colors, const color_rgba *pBase_color5, uint32_t inten_table, bool scaled /* false */)
511
+ {
512
+ color_rgba b = *pBase_color5;
513
+
514
+ if (!scaled)
515
+ {
516
+ b.x = (b.x << 3) | (b.x >> 2);
517
+ b.y = (b.y << 3) | (b.y >> 2);
518
+ b.z = (b.z << 3) | (b.z >> 2);
519
+ }
520
+
521
+ constant int* pInten_table = g_etc1_inten_tables[inten_table];
522
+
523
+ pBlock_colors[0] = (color_rgba)(clamp255(b.x + pInten_table[0]), clamp255(b.y + pInten_table[0]), clamp255(b.z + pInten_table[0]), 255);
524
+ pBlock_colors[1] = (color_rgba)(clamp255(b.x + pInten_table[1]), clamp255(b.y + pInten_table[1]), clamp255(b.z + pInten_table[1]), 255);
525
+ pBlock_colors[2] = (color_rgba)(clamp255(b.x + pInten_table[2]), clamp255(b.y + pInten_table[2]), clamp255(b.z + pInten_table[2]), 255);
526
+ pBlock_colors[3] = (color_rgba)(clamp255(b.x + pInten_table[3]), clamp255(b.y + pInten_table[3]), clamp255(b.z + pInten_table[3]), 255);
527
+ }
528
+
529
+ uint64_t etc_block_determine_selectors(etc_block *pBlock, const color_rgba* pSource_pixels, bool perceptual, uint32_t begin_subblock /*= 0*/, uint32_t end_subblock /*= 2*/)
530
+ {
531
+ uint64_t total_error = 0;
532
+
533
+ for (uint32_t subblock = begin_subblock; subblock < end_subblock; subblock++)
534
+ {
535
+ color_rgba block_colors[4];
536
+ etc_block_get_block_colors(pBlock, block_colors, subblock);
537
+
538
+ if (etc_block_get_flip_bit(pBlock))
539
+ {
540
+ for (uint32_t y = 0; y < 2; y++)
541
+ {
542
+ for (uint32_t x = 0; x < 4; x++)
543
+ {
544
+ uint32_t best_selector = 0;
545
+ uint64_t best_error = UINT64_MAX;
546
+
547
+ for (uint32_t s = 0; s < 4; s++)
548
+ {
549
+ uint64_t err = color_distance(perceptual, block_colors[s], pSource_pixels[x + (subblock * 2 + y) * 4], false);
550
+ if (err < best_error)
551
+ {
552
+ best_error = err;
553
+ best_selector = s;
554
+ }
555
+ }
556
+
557
+ etc_block_set_selector(pBlock, x, subblock * 2 + y, best_selector);
558
+
559
+ total_error += best_error;
560
+ }
561
+ }
562
+ }
563
+ else
564
+ {
565
+ for (uint32_t y = 0; y < 4; y++)
566
+ {
567
+ for (uint32_t x = 0; x < 2; x++)
568
+ {
569
+ uint32_t best_selector = 0;
570
+ uint64_t best_error = UINT64_MAX;
571
+
572
+ for (uint32_t s = 0; s < 4; s++)
573
+ {
574
+ uint64_t err = color_distance(perceptual, block_colors[s], pSource_pixels[(subblock * 2) + x + y * 4], false);
575
+ if (err < best_error)
576
+ {
577
+ best_error = err;
578
+ best_selector = s;
579
+ }
580
+ }
581
+
582
+ etc_block_set_selector(pBlock, subblock * 2 + x, y, best_selector);
583
+
584
+ total_error += best_error;
585
+ }
586
+ }
587
+ }
588
+ }
589
+
590
+ return total_error;
591
+ }
592
+
593
+ uint16_t etc_block_pack_color4_rgb(uint32_t r, uint32_t g, uint32_t b, bool scaled)
594
+ {
595
+ uint32_t bias = 127;
596
+
597
+ if (scaled)
598
+ {
599
+ r = (r * 15U + bias) / 255U;
600
+ g = (g * 15U + bias) / 255U;
601
+ b = (b * 15U + bias) / 255U;
602
+ }
603
+
604
+ r = min(r, 15U);
605
+ g = min(g, 15U);
606
+ b = min(b, 15U);
607
+
608
+ return (uint16_t)(b | (g << 4U) | (r << 8U));
609
+ }
610
+
611
+ uint16_t etc_block_pack_color4(color_rgba color, bool scaled)
612
+ {
613
+ uint32_t bias = 127;
614
+ return etc_block_pack_color4_rgb(color.x, color.y, color.z, scaled);
615
+ }
616
+
617
+ uint16_t etc_block_pack_delta3(int r, int g, int b)
618
+ {
619
+ assert((r >= cETC1ColorDeltaMin) && (r <= cETC1ColorDeltaMax));
620
+ assert((g >= cETC1ColorDeltaMin) && (g <= cETC1ColorDeltaMax));
621
+ assert((b >= cETC1ColorDeltaMin) && (b <= cETC1ColorDeltaMax));
622
+ if (r < 0) r += 8;
623
+ if (g < 0) g += 8;
624
+ if (b < 0) b += 8;
625
+ return (uint16_t)(b | (g << 3) | (r << 6));
626
+ }
627
+
628
+ void etc_block_set_block_color4(etc_block *pBlock, color_rgba c0_unscaled, color_rgba c1_unscaled)
629
+ {
630
+ etc_block_set_diff_bit(pBlock, false);
631
+
632
+ etc_block_set_base4_color(pBlock, 0, etc_block_pack_color4(c0_unscaled, false));
633
+ etc_block_set_base4_color(pBlock, 1, etc_block_pack_color4(c1_unscaled, false));
634
+ }
635
+
636
+ uint16_t etc_block_pack_color5_rgb(uint32_t r, uint32_t g, uint32_t b, bool scaled)
637
+ {
638
+ uint32_t bias = 127;
639
+
640
+ if (scaled)
641
+ {
642
+ r = (r * 31U + bias) / 255U;
643
+ g = (g * 31U + bias) / 255U;
644
+ b = (b * 31U + bias) / 255U;
645
+ }
646
+
647
+ r = min(r, 31U);
648
+ g = min(g, 31U);
649
+ b = min(b, 31U);
650
+
651
+ return (uint16_t)(b | (g << 5U) | (r << 10U));
652
+ }
653
+
654
+ uint16_t etc_block_pack_color5(color_rgba c, bool scaled)
655
+ {
656
+ return etc_block_pack_color5_rgb(c.x, c.y, c.z, scaled);
657
+ }
658
+
659
+ void etc_block_set_block_color5(etc_block *pBlock, color_rgba c0_unscaled, color_rgba c1_unscaled)
660
+ {
661
+ etc_block_set_diff_bit(pBlock, true);
662
+
663
+ etc_block_set_base5_color(pBlock, etc_block_pack_color5(c0_unscaled, false));
664
+
665
+ int dr = c1_unscaled.x - c0_unscaled.x;
666
+ int dg = c1_unscaled.y - c0_unscaled.y;
667
+ int db = c1_unscaled.z - c0_unscaled.z;
668
+
669
+ etc_block_set_delta3_color(pBlock, etc_block_pack_delta3(dr, dg, db));
670
+ }
671
+
672
+ void etc_block_set_block_color5_etc1s(etc_block *pBlock, color_rgba c_unscaled)
673
+ {
674
+ etc_block_set_diff_bit(pBlock, true);
675
+
676
+ etc_block_set_base5_color(pBlock, etc_block_pack_color5(c_unscaled, false));
677
+ etc_block_set_delta3_color(pBlock, etc_block_pack_delta3(0, 0, 0));
678
+ }
679
+
680
+ bool etc_block_set_block_color5_check(etc_block *pBlock, color_rgba c0_unscaled, color_rgba c1_unscaled)
681
+ {
682
+ etc_block_set_diff_bit(pBlock, true);
683
+
684
+ etc_block_set_base5_color(pBlock, etc_block_pack_color5(c0_unscaled, false));
685
+
686
+ int dr = c1_unscaled.x - c0_unscaled.x;
687
+ int dg = c1_unscaled.y - c0_unscaled.y;
688
+ int db = c1_unscaled.z - c0_unscaled.z;
689
+
690
+ if (((dr < cETC1ColorDeltaMin) || (dr > cETC1ColorDeltaMax)) ||
691
+ ((dg < cETC1ColorDeltaMin) || (dg > cETC1ColorDeltaMax)) ||
692
+ ((db < cETC1ColorDeltaMin) || (db > cETC1ColorDeltaMax)))
693
+ return false;
694
+
695
+ etc_block_set_delta3_color(pBlock, etc_block_pack_delta3(dr, dg, db));
696
+
697
+ return true;
698
+ }
699
+
700
+ void etc_block_pack_raw_selectors(etc_block *pBlock, const uint8_t *pSelectors)
701
+ {
702
+ uint32_t word3 = 0, word2 = 0;
703
+ for (uint32_t y = 0; y < 4; y++)
704
+ {
705
+ for (uint32_t x = 0; x < 4; x++)
706
+ {
707
+ const uint32_t bit_index = x * 4 + y;
708
+ const uint32_t s = pSelectors[x + y * 4];
709
+
710
+ const uint32_t lsb = s & 1, msb = s >> 1;
711
+
712
+ word3 |= (lsb << bit_index);
713
+ word2 |= (msb << bit_index);
714
+ }
715
+ }
716
+
717
+ pBlock->m_bytes[7] = (uint8_t)(word3);
718
+ pBlock->m_bytes[6] = (uint8_t)(word3 >> 8);
719
+ pBlock->m_bytes[5] = (uint8_t)(word2);
720
+ pBlock->m_bytes[4] = (uint8_t)(word2 >> 8);
721
+ }
722
+
723
+ // ---- EC1S block encoding/endpoint optimization
724
+
725
+ constant uint8_t g_eval_dist_tables[8][256] =
726
+ {
727
+ // 99% threshold
728
+ { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,},
729
+ { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,},
730
+ { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,},
731
+ { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,},
732
+ { 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,},
733
+ { 1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,0,0,0,0,1,0,1,1,0,1,1,1,1,1,0,1,1,1,0,1,1,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,},
734
+ { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,},
735
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,}
736
+ };
737
+
738
+ typedef struct etc1s_optimizer_solution_coordinates_tag
739
+ {
740
+ color_rgba m_unscaled_color;
741
+ uint32_t m_inten_table;
742
+ } etc1s_optimizer_solution_coordinates;
743
+
744
+ color_rgba get_scaled_color(color_rgba unscaled_color)
745
+ {
746
+ int br, bg, bb;
747
+
748
+ br = (unscaled_color.x >> 2) | (unscaled_color.x << 3);
749
+ bg = (unscaled_color.y >> 2) | (unscaled_color.y << 3);
750
+ bb = (unscaled_color.z >> 2) | (unscaled_color.z << 3);
751
+
752
+ return (color_rgba)((uint8_t)br, (uint8_t)bg, (uint8_t)bb, 255);
753
+ }
754
+
755
+ typedef struct etc1s_optimizer_potential_solution_tag
756
+ {
757
+ uint64_t m_error;
758
+ etc1s_optimizer_solution_coordinates m_coords;
759
+
760
+ uint8_t m_selectors[16];
761
+ bool m_valid;
762
+ } etc1s_optimizer_potential_solution;
763
+
764
+ typedef struct etc1s_optimizer_state_tag
765
+ {
766
+ int m_br, m_bg, m_bb;
767
+ float3 m_avg_color;
768
+ int m_max_comp_spread;
769
+ etc1s_optimizer_potential_solution m_best_solution;
770
+ } etc1s_optimizer_state;
771
+
772
+ bool etc1s_optimizer_evaluate_solution(
773
+ etc1s_optimizer_state *pState,
774
+ const global encode_etc1s_param_struct *pParams,
775
+ uint64_t num_pixels, const global color_rgba *pPixels,
776
+ const global uint32_t *pWeights,
777
+ etc1s_optimizer_solution_coordinates coords,
778
+ etc1s_optimizer_potential_solution* pTrial_solution,
779
+ etc1s_optimizer_potential_solution* pBest_solution)
780
+ {
781
+ uint8_t temp_selectors[16];
782
+
783
+ pTrial_solution->m_valid = false;
784
+
785
+ const color_rgba base_color = get_scaled_color(coords.m_unscaled_color);
786
+
787
+ pTrial_solution->m_error = INT64_MAX;
788
+
789
+ for (uint32_t inten_table = 0; inten_table < cETC1IntenModifierValues; inten_table++)
790
+ {
791
+ // TODO: This check is equivalent to medium quality in the C++ version.
792
+ if (!g_eval_dist_tables[inten_table][pState->m_max_comp_spread])
793
+ continue;
794
+
795
+ constant int* pInten_table = g_etc1_inten_tables[inten_table];
796
+
797
+ color_rgba block_colors[4];
798
+ for (uint32_t s = 0; s < 4; s++)
799
+ {
800
+ int yd = pInten_table[s];
801
+ block_colors[s] = (color_rgba)(clamp255(base_color.x + yd), clamp255(base_color.y + yd), clamp255(base_color.z + yd), 255);
802
+ }
803
+
804
+ uint64_t total_error = 0;
805
+
806
+ for (uint64_t c = 0; c < num_pixels; c++)
807
+ {
808
+ color_rgba src_pixel = pPixels[c];
809
+
810
+ uint32_t best_selector_index = 3;
811
+ uint32_t best_error = color_distance(pParams->m_perceptual, src_pixel, block_colors[0], false);
812
+
813
+ uint32_t trial_error = color_distance(pParams->m_perceptual, src_pixel, block_colors[1], false);
814
+ if (trial_error < best_error)
815
+ {
816
+ best_error = trial_error;
817
+ best_selector_index = 2;
818
+ }
819
+
820
+ trial_error = color_distance(pParams->m_perceptual, src_pixel, block_colors[2], false);
821
+ if (trial_error < best_error)
822
+ {
823
+ best_error = trial_error;
824
+ best_selector_index = 0;
825
+ }
826
+
827
+ trial_error = color_distance(pParams->m_perceptual, src_pixel, block_colors[3], false);
828
+ if (trial_error < best_error)
829
+ {
830
+ best_error = trial_error;
831
+ best_selector_index = 1;
832
+ }
833
+
834
+ if (num_pixels <= 16)
835
+ temp_selectors[c] = (uint8_t)(best_selector_index);
836
+
837
+ total_error += pWeights ? (best_error * (uint64_t)pWeights[c]) : best_error;
838
+
839
+ if (total_error >= pTrial_solution->m_error)
840
+ break;
841
+ }
842
+
843
+ if (total_error < pTrial_solution->m_error)
844
+ {
845
+ pTrial_solution->m_error = total_error;
846
+ pTrial_solution->m_coords.m_inten_table = inten_table;
847
+ if (num_pixels <= 16)
848
+ {
849
+ for (uint32_t i = 0; i < num_pixels; i++)
850
+ pTrial_solution->m_selectors[i] = temp_selectors[i];
851
+ }
852
+ pTrial_solution->m_valid = true;
853
+ }
854
+ }
855
+ pTrial_solution->m_coords.m_unscaled_color = coords.m_unscaled_color;
856
+
857
+ bool success = false;
858
+ if (pBest_solution)
859
+ {
860
+ if (pTrial_solution->m_error < pBest_solution->m_error)
861
+ {
862
+ *pBest_solution = *pTrial_solution;
863
+ success = true;
864
+ }
865
+ }
866
+
867
+ return success;
868
+ }
869
+
870
+ void etc1s_optimizer_init(
871
+ etc1s_optimizer_state *pState,
872
+ const global encode_etc1s_param_struct *pParams,
873
+ uint64_t num_pixels, const global color_rgba *pPixels,
874
+ const global uint32_t *pWeights)
875
+ {
876
+ const int LIMIT = 31;
877
+
878
+ color_rgba min_color = 255;
879
+ color_rgba max_color = 0;
880
+ uint64_t total_weight = 0;
881
+ uint64_t sum_r = 0, sum_g = 0, sum_b = 0;
882
+
883
+ for (uint64_t i = 0; i < num_pixels; i++)
884
+ {
885
+ const color_rgba c = pPixels[i];
886
+
887
+ min_color = min(min_color, c);
888
+ max_color = max(max_color, c);
889
+
890
+ if (pWeights)
891
+ {
892
+ uint64_t weight = pWeights[i];
893
+
894
+ sum_r += weight * c.x;
895
+ sum_g += weight * c.y;
896
+ sum_b += weight * c.z;
897
+
898
+ total_weight += weight;
899
+ }
900
+ else
901
+ {
902
+ sum_r += c.x;
903
+ sum_g += c.y;
904
+ sum_b += c.z;
905
+
906
+ total_weight++;
907
+ }
908
+ }
909
+
910
+ float3 avg_color;
911
+ avg_color.x = (float)sum_r / total_weight;
912
+ avg_color.y = (float)sum_g / total_weight;
913
+ avg_color.z = (float)sum_b / total_weight;
914
+
915
+ pState->m_avg_color = avg_color;
916
+ pState->m_max_comp_spread = max(max((int)max_color.x - (int)min_color.x, (int)max_color.y - (int)min_color.y), (int)max_color.z - (int)min_color.z);
917
+
918
+ // TODO: The rounding here could be improved, like with DXT1/BC1.
919
+ pState->m_br = clamp((int)(avg_color.x * (LIMIT / 255.0f) + .5f), 0, LIMIT);
920
+ pState->m_bg = clamp((int)(avg_color.y * (LIMIT / 255.0f) + .5f), 0, LIMIT);
921
+ pState->m_bb = clamp((int)(avg_color.z * (LIMIT / 255.0f) + .5f), 0, LIMIT);
922
+
923
+ pState->m_best_solution.m_valid = false;
924
+ pState->m_best_solution.m_error = UINT64_MAX;
925
+ }
926
+
927
+ void etc1s_optimizer_internal_cluster_fit(
928
+ uint32_t total_perms_to_try,
929
+ etc1s_optimizer_state *pState,
930
+ const global encode_etc1s_param_struct *pParams,
931
+ uint64_t num_pixels, const global color_rgba *pPixels,
932
+ const global uint32_t *pWeights)
933
+ {
934
+ const int LIMIT = 31;
935
+
936
+ etc1s_optimizer_potential_solution trial_solution;
937
+
938
+ etc1s_optimizer_solution_coordinates cur_coords;
939
+ cur_coords.m_unscaled_color = (color_rgba)(pState->m_br, pState->m_bg, pState->m_bb, 255);
940
+ etc1s_optimizer_evaluate_solution(pState, pParams, num_pixels, pPixels, pWeights, cur_coords, &trial_solution, &pState->m_best_solution);
941
+
942
+ if (pState->m_best_solution.m_error == 0)
943
+ return;
944
+
945
+ for (uint32_t i = 0; i < total_perms_to_try; i++)
946
+ {
947
+ int delta_sum_r = 0, delta_sum_g = 0, delta_sum_b = 0;
948
+
949
+ constant int *pInten_table = g_etc1_inten_tables[pState->m_best_solution.m_coords.m_inten_table];
950
+ const color_rgba base_color = get_scaled_color(pState->m_best_solution.m_coords.m_unscaled_color);
951
+
952
+ constant uint8_t *pNum_selectors = g_cluster_fit_order_tab[i].m_v;
953
+
954
+ for (uint32_t q = 0; q < 4; q++)
955
+ {
956
+ const int yd_temp = pInten_table[q];
957
+
958
+ delta_sum_r += pNum_selectors[q] * (clamp(base_color.x + yd_temp, 0, 255) - base_color.x);
959
+ delta_sum_g += pNum_selectors[q] * (clamp(base_color.y + yd_temp, 0, 255) - base_color.y);
960
+ delta_sum_b += pNum_selectors[q] * (clamp(base_color.z + yd_temp, 0, 255) - base_color.z);
961
+ }
962
+
963
+ if ((!delta_sum_r) && (!delta_sum_g) && (!delta_sum_b))
964
+ continue;
965
+
966
+ const float avg_delta_r_f = (float)(delta_sum_r) / 8;
967
+ const float avg_delta_g_f = (float)(delta_sum_g) / 8;
968
+ const float avg_delta_b_f = (float)(delta_sum_b) / 8;
969
+
970
+ const int br1 = clamp((int)((pState->m_avg_color.x - avg_delta_r_f) * (LIMIT / 255.0f) + .5f), 0, LIMIT);
971
+ const int bg1 = clamp((int)((pState->m_avg_color.y - avg_delta_g_f) * (LIMIT / 255.0f) + .5f), 0, LIMIT);
972
+ const int bb1 = clamp((int)((pState->m_avg_color.z - avg_delta_b_f) * (LIMIT / 255.0f) + .5f), 0, LIMIT);
973
+
974
+ cur_coords.m_unscaled_color = (color_rgba)(br1, bg1, bb1, 255);
975
+
976
+ etc1s_optimizer_evaluate_solution(pState, pParams, num_pixels, pPixels, pWeights, cur_coords, &trial_solution, &pState->m_best_solution);
977
+
978
+ if (pState->m_best_solution.m_error == 0)
979
+ break;
980
+ }
981
+ }
982
+
983
+ // Encode an ETC1S block given a 4x4 pixel block.
984
+ kernel void encode_etc1s_blocks(
985
+ const global encode_etc1s_param_struct *pParams,
986
+ const global pixel_block *pInput_blocks,
987
+ global etc_block *pOutput_blocks)
988
+ {
989
+ const uint32_t block_index = get_global_id(0);
990
+
991
+ const global pixel_block *pInput_block = &pInput_blocks[block_index];
992
+
993
+ etc1s_optimizer_state state;
994
+ etc1s_optimizer_init(&state, pParams, 16, pInput_block->m_pixels, NULL);
995
+ etc1s_optimizer_internal_cluster_fit(pParams->m_total_perms, &state, pParams, 16, pInput_block->m_pixels, NULL);
996
+
997
+ etc_block blk;
998
+ etc_block_set_flip_bit(&blk, true);
999
+ etc_block_set_block_color5_etc1s(&blk, state.m_best_solution.m_coords.m_unscaled_color);
1000
+ etc_block_set_inten_tables_etc1s(&blk, state.m_best_solution.m_coords.m_inten_table);
1001
+ etc_block_pack_raw_selectors(&blk, state.m_best_solution.m_selectors);
1002
+
1003
+ pOutput_blocks[block_index] = blk;
1004
+ }
1005
+
1006
+ typedef struct __attribute__ ((packed)) pixel_cluster_tag
1007
+ {
1008
+ uint64_t m_total_pixels;
1009
+ uint64_t m_first_pixel_index;
1010
+ } pixel_cluster;
1011
+
1012
+ // Determine the optimal ETC1S color5/intensity given an arbitrary large array of 4x4 input pixel blocks.
1013
+ kernel void encode_etc1s_from_pixel_cluster(
1014
+ const global encode_etc1s_param_struct *pParams,
1015
+ const global pixel_cluster *pInput_pixel_clusters,
1016
+ const global color_rgba *pInput_pixels,
1017
+ const global uint32_t *pInput_weights,
1018
+ global etc_block *pOutput_blocks)
1019
+ {
1020
+ const uint32_t cluster_index = get_global_id(0);
1021
+
1022
+ const global pixel_cluster *pInput_cluster = &pInput_pixel_clusters[cluster_index];
1023
+
1024
+ uint64_t total_pixels = pInput_cluster->m_total_pixels;
1025
+ const global color_rgba *pPixels = pInput_pixels + pInput_cluster->m_first_pixel_index;
1026
+ const global uint32_t *pWeights = pInput_weights + pInput_cluster->m_first_pixel_index;
1027
+
1028
+ etc1s_optimizer_state state;
1029
+ etc1s_optimizer_init(&state, pParams, total_pixels, pPixels, pWeights);
1030
+ etc1s_optimizer_internal_cluster_fit(pParams->m_total_perms, &state, pParams, total_pixels, pPixels, pWeights);
1031
+
1032
+ etc_block blk;
1033
+ etc_block_set_flip_bit(&blk, true);
1034
+ etc_block_set_block_color5_etc1s(&blk, state.m_best_solution.m_coords.m_unscaled_color);
1035
+ etc_block_set_inten_tables_etc1s(&blk, state.m_best_solution.m_coords.m_inten_table);
1036
+
1037
+ pOutput_blocks[cluster_index] = blk;
1038
+ }
1039
+
1040
+ // ---- refine_endpoint_clusterization
1041
+ typedef struct __attribute__ ((packed)) rec_block_struct_tag
1042
+ {
1043
+ uint16_t m_first_cluster_ofs;
1044
+ uint16_t m_num_clusters;
1045
+ uint16_t m_cur_cluster_index;
1046
+ uint8_t m_cur_cluster_etc_inten;
1047
+ } rec_block_struct;
1048
+
1049
+ typedef struct __attribute__ ((packed)) rec_endpoint_cluster_struct_tag
1050
+ {
1051
+ color_rgba m_unscaled_color;
1052
+ uint8_t m_etc_inten;
1053
+ uint16_t m_cluster_index;
1054
+ } rec_endpoint_cluster_struct;
1055
+
1056
+ typedef struct __attribute__ ((packed)) rec_param_struct_tag
1057
+ {
1058
+ uint32_t m_total_blocks;
1059
+ int m_perceptual;
1060
+ } rec_param_struct;
1061
+
1062
+ // For each input block: find the best endpoint cluster that encodes it.
1063
+ kernel void refine_endpoint_clusterization(
1064
+ const rec_param_struct params,
1065
+ const global pixel_block *pInput_blocks,
1066
+ const global rec_block_struct *pInput_block_info,
1067
+ const global rec_endpoint_cluster_struct *pInput_clusters,
1068
+ const global uint32_t *pSorted_block_indices,
1069
+ global uint32_t *pOutput_indices)
1070
+ {
1071
+ const uint32_t sorted_block_index = get_global_id(0);
1072
+ const uint32_t block_index = pSorted_block_indices[sorted_block_index];
1073
+ const int perceptual = params.m_perceptual;
1074
+
1075
+ const global pixel_block *pInput_block = &pInput_blocks[block_index];
1076
+
1077
+ pixel_block priv_pixel_block;
1078
+ priv_pixel_block = *pInput_block;
1079
+
1080
+ const uint32_t first_cluster_ofs = pInput_block_info[block_index].m_first_cluster_ofs;
1081
+ const uint32_t num_clusters = pInput_block_info[block_index].m_num_clusters;
1082
+ const uint32_t cur_block_cluster_index = pInput_block_info[block_index].m_cur_cluster_index;
1083
+ const uint32_t cur_block_cluster_etc_inten = pInput_block_info[block_index].m_cur_cluster_etc_inten;
1084
+
1085
+ uint64_t overall_best_err = UINT64_MAX;
1086
+ uint32_t best_cluster_index = 0;
1087
+
1088
+ for (uint32_t i = 0; i < num_clusters; i++)
1089
+ {
1090
+ const uint32_t cluster_index = first_cluster_ofs + i;
1091
+ color_rgba unscaled_color = pInput_clusters[cluster_index].m_unscaled_color;
1092
+ const uint8_t etc_inten = pInput_clusters[cluster_index].m_etc_inten;
1093
+ const uint16_t orig_cluster_index = pInput_clusters[cluster_index].m_cluster_index;
1094
+
1095
+ if (etc_inten > cur_block_cluster_etc_inten)
1096
+ continue;
1097
+
1098
+ color_rgba block_colors[4];
1099
+ get_block_colors5(block_colors, &unscaled_color, etc_inten, false);
1100
+
1101
+ uint64_t total_error = 0;
1102
+
1103
+ for (uint32_t c = 0; c < 16; c++)
1104
+ {
1105
+ color_rgba src_pixel = priv_pixel_block.m_pixels[c];
1106
+
1107
+ uint32_t best_error = color_distance(perceptual, src_pixel, block_colors[0], false);
1108
+
1109
+ uint32_t trial_error = color_distance(perceptual, src_pixel, block_colors[1], false);
1110
+ if (trial_error < best_error)
1111
+ best_error = trial_error;
1112
+
1113
+ trial_error = color_distance(perceptual, src_pixel, block_colors[2], false);
1114
+ if (trial_error < best_error)
1115
+ best_error = trial_error;
1116
+
1117
+ trial_error = color_distance(perceptual, src_pixel, block_colors[3], false);
1118
+ if (trial_error < best_error)
1119
+ best_error = trial_error;
1120
+
1121
+ total_error += best_error;
1122
+ }
1123
+
1124
+ if ( (total_error < overall_best_err) ||
1125
+ ((orig_cluster_index == cur_block_cluster_index) && (total_error == overall_best_err))
1126
+ )
1127
+ {
1128
+ overall_best_err = total_error;
1129
+ best_cluster_index = orig_cluster_index;
1130
+ if (!overall_best_err)
1131
+ break;
1132
+ }
1133
+ }
1134
+
1135
+ pOutput_indices[block_index] = best_cluster_index;
1136
+ }
1137
+
1138
+ // ---- find_optimal_selector_clusters_for_each_block
1139
+
1140
+ typedef struct __attribute__ ((packed)) fosc_selector_struct_tag
1141
+ {
1142
+ uint32_t m_packed_selectors; // 4x4 grid of 2-bit selectors
1143
+ } fosc_selector_struct;
1144
+
1145
+ typedef struct __attribute__ ((packed)) fosc_block_struct_tag
1146
+ {
1147
+ color_rgba m_etc_color5_inten; // unscaled 5-bit block color in RGB, alpha has block's intensity index
1148
+ uint32_t m_first_selector; // offset into selector table
1149
+ uint32_t m_num_selectors; // number of selectors to check
1150
+ } fosc_block_struct;
1151
+
1152
+ typedef struct __attribute__ ((packed)) fosc_param_struct_tag
1153
+ {
1154
+ uint32_t m_total_blocks;
1155
+ int m_perceptual;
1156
+ } fosc_param_struct;
1157
+
1158
+ // For each input block: Find the quantized selector which results in the lowest error.
1159
+ kernel void find_optimal_selector_clusters_for_each_block(
1160
+ const fosc_param_struct params,
1161
+ const global pixel_block *pInput_blocks,
1162
+ const global fosc_block_struct *pInput_block_info,
1163
+ const global fosc_selector_struct *pInput_selectors,
1164
+ const global uint32_t *pSelector_cluster_indices,
1165
+ global uint32_t *pOutput_selector_cluster_indices)
1166
+ {
1167
+ const uint32_t block_index = get_global_id(0);
1168
+
1169
+ const global color_rgba *pBlock_pixels = pInput_blocks[block_index].m_pixels;
1170
+ const global fosc_block_struct *pBlock_info = &pInput_block_info[block_index];
1171
+
1172
+ const global fosc_selector_struct *pSelectors = &pInput_selectors[pBlock_info->m_first_selector];
1173
+ const uint32_t num_selectors = pBlock_info->m_num_selectors;
1174
+
1175
+ color_rgba trial_block_colors[4];
1176
+ color_rgba etc_color5_inten = pBlock_info->m_etc_color5_inten;
1177
+ get_block_colors5(trial_block_colors, &etc_color5_inten, etc_color5_inten.w, false);
1178
+
1179
+ uint32_t trial_errors[4][16];
1180
+
1181
+ if (params.m_perceptual)
1182
+ {
1183
+ for (uint32_t sel = 0; sel < 4; ++sel)
1184
+ for (uint32_t i = 0; i < 16; ++i)
1185
+ trial_errors[sel][i] = color_distance(true, pBlock_pixels[i], trial_block_colors[sel], false);
1186
+ }
1187
+ else
1188
+ {
1189
+ for (uint32_t sel = 0; sel < 4; ++sel)
1190
+ for (uint32_t i = 0; i < 16; ++i)
1191
+ trial_errors[sel][i] = color_distance(false, pBlock_pixels[i], trial_block_colors[sel], false);
1192
+ }
1193
+
1194
+ uint64_t best_err = UINT64_MAX;
1195
+ uint32_t best_index = 0;
1196
+
1197
+ for (uint32_t sel_index = 0; sel_index < num_selectors; sel_index++)
1198
+ {
1199
+ uint32_t sels = pSelectors[sel_index].m_packed_selectors;
1200
+
1201
+ uint64_t total_err = 0;
1202
+ for (uint32_t i = 0; i < 16; i++, sels >>= 2)
1203
+ total_err += trial_errors[sels & 3][i];
1204
+
1205
+ if (total_err < best_err)
1206
+ {
1207
+ best_err = total_err;
1208
+ best_index = sel_index;
1209
+
1210
+ if (!best_err)
1211
+ break;
1212
+ }
1213
+ }
1214
+
1215
+ pOutput_selector_cluster_indices[block_index] = pSelector_cluster_indices[pBlock_info->m_first_selector + best_index];
1216
+ }
1217
+
1218
+ // determine_selectors
1219
+
1220
+ typedef struct __attribute__ ((packed)) ds_param_struct_tag
1221
+ {
1222
+ uint32_t m_total_blocks;
1223
+ int m_perceptual;
1224
+ } ds_param_struct;
1225
+
1226
+ // For each input block: Determine the ETC1S selectors that result in the lowest error, given each block's predetermined ETC1S color5/intensities.
1227
+ kernel void determine_selectors(
1228
+ const ds_param_struct params,
1229
+ const global pixel_block *pInput_blocks,
1230
+ const global color_rgba *pInput_etc_color5_and_inten,
1231
+ global etc_block *pOutput_blocks)
1232
+ {
1233
+ const uint32_t block_index = get_global_id(0);
1234
+
1235
+ const global color_rgba *pBlock_pixels = pInput_blocks[block_index].m_pixels;
1236
+
1237
+ color_rgba etc_color5_inten = pInput_etc_color5_and_inten[block_index];
1238
+
1239
+ color_rgba block_colors[4];
1240
+ get_block_colors5(block_colors, &etc_color5_inten, etc_color5_inten.w, false);
1241
+
1242
+ etc_block output_block;
1243
+ etc_block_set_flip_bit(&output_block, true);
1244
+ etc_block_set_block_color5_etc1s(&output_block, etc_color5_inten);
1245
+ etc_block_set_inten_tables_etc1s(&output_block, etc_color5_inten.w);
1246
+
1247
+ for (uint32_t i = 0; i < 16; i++)
1248
+ {
1249
+ color_rgba pixel_color = pBlock_pixels[i];
1250
+
1251
+ uint err0 = color_distance(params.m_perceptual, pixel_color, block_colors[0], false);
1252
+ uint err1 = color_distance(params.m_perceptual, pixel_color, block_colors[1], false);
1253
+ uint err2 = color_distance(params.m_perceptual, pixel_color, block_colors[2], false);
1254
+ uint err3 = color_distance(params.m_perceptual, pixel_color, block_colors[3], false);
1255
+
1256
+ uint best_err = min(min(min(err0, err1), err2), err3);
1257
+
1258
+ uint32_t best_sel = (best_err == err2) ? 2 : 3;
1259
+ best_sel = (best_err == err1) ? 1 : best_sel;
1260
+ best_sel = (best_err == err0) ? 0 : best_sel;
1261
+
1262
+ etc_block_set_selector(&output_block, i & 3, i >> 2, best_sel);
1263
+ }
1264
+
1265
+ pOutput_blocks[block_index] = output_block;
1266
+ }