1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 **************************************************************************/
28 #include "vogl_core.h"
30 #include "vogl_radix_sort.h"
31 #include "vogl_ryg_dxt.hpp"
35 const int g_etc1_inten_tables[cETC1IntenModifierValues][cETC1SelectorValues] =
37 { -8, -2, 2, 8 }, { -17, -5, 5, 17 }, { -29, -9, 9, 29 }, { -42, -13, 13, 42 },
38 { -60, -18, 18, 60 }, { -80, -24, 24, 80 }, { -106, -33, 33, 106 }, { -183, -47, 47, 183 }
41 const uint8 g_etc1_to_selector_index[cETC1SelectorValues] = { 2, 3, 1, 0 };
42 const uint8 g_selector_index_to_etc1[cETC1SelectorValues] = { 3, 2, 0, 1 };
44 // [flip][subblock][pixel_index]
45 const etc1_coord2 g_etc1_pixel_coords[2][2][8] =
47 { { { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 },
48 { 1, 0 }, { 1, 1 }, { 1, 2 }, { 1, 3 } },
49 { { 2, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 },
50 { 3, 0 }, { 3, 1 }, { 3, 2 }, { 3, 3 } } },
51 { { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 },
52 { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1 } },
53 { { 0, 2 }, { 1, 2 }, { 2, 2 }, { 3, 2 },
54 { 0, 3 }, { 1, 3 }, { 2, 3 }, { 3, 3 } }, }
57 // Given an ETC1 diff/inten_table/selector, and an 8-bit desired color, this table encodes the best packed_color in the low byte, and the abs error in the high byte.
58 static uint16 g_etc1_inverse_lookup[2 * 8 * 4][256]; // [diff/inten_table/selector][desired_color]
60 // g_color8_to_etc_block_config[color][table_index] = Supplies for each 8-bit color value a list of packed ETC1 diff/intensity table/selectors/packed_colors that map to that color.
61 // To pack: diff | (inten << 1) | (selector << 4) | (packed_c << 8)
62 static const uint16 g_color8_to_etc_block_config_0_255[2][33] =
64 { 0x0000, 0x0010, 0x0002, 0x0012, 0x0004, 0x0014, 0x0006, 0x0016, 0x0008, 0x0018, 0x000A, 0x001A, 0x000C, 0x001C, 0x000E, 0x001E,
65 0x0001, 0x0011, 0x0003, 0x0013, 0x0005, 0x0015, 0x0007, 0x0017, 0x0009, 0x0019, 0x000B, 0x001B, 0x000D, 0x001D, 0x000F, 0x001F, 0xFFFF },
66 { 0x0F20, 0x0F30, 0x0E32, 0x0F22, 0x0E34, 0x0F24, 0x0D36, 0x0F26, 0x0C38, 0x0E28, 0x0B3A, 0x0E2A, 0x093C, 0x0E2C, 0x053E, 0x0D2E,
67 0x1E31, 0x1F21, 0x1D33, 0x1F23, 0x1C35, 0x1E25, 0x1A37, 0x1E27, 0x1839, 0x1D29, 0x163B, 0x1C2B, 0x133D, 0x1B2D, 0x093F, 0x1A2F, 0xFFFF },
70 // Really only [254][11].
71 static const uint16 g_color8_to_etc_block_config_1_to_254[254][12] =
73 { 0x021C, 0x0D0D, 0xFFFF }, { 0x0020, 0x0021, 0x0A0B, 0x061F, 0xFFFF }, { 0x0113, 0x0217, 0xFFFF }, { 0x0116, 0x031E,
74 0x0B0E, 0x0405, 0xFFFF },
75 { 0x0022, 0x0204, 0x050A, 0x0023, 0xFFFF }, { 0x0111, 0x0319, 0x0809, 0x170F, 0xFFFF }, { 0x0303, 0x0215, 0x0607, 0xFFFF }, { 0x0030, 0x0114, 0x0408, 0x0031, 0x0201, 0x051D, 0xFFFF }, { 0x0100, 0x0024, 0x0306,
76 0x0025, 0x041B, 0x0E0D, 0xFFFF },
77 { 0x021A, 0x0121, 0x0B0B, 0x071F, 0xFFFF }, { 0x0213, 0x0317, 0xFFFF }, { 0x0112,
79 { 0x0026, 0x070C, 0x0123, 0x0027, 0xFFFF }, { 0x0211, 0x0909, 0xFFFF }, { 0x0110, 0x0315, 0x0707,
80 0x0419, 0x180F, 0xFFFF },
81 { 0x0218, 0x0131, 0x0301, 0x0403, 0x061D, 0xFFFF }, { 0x0032, 0x0202, 0x0033, 0x0125, 0x051B,
83 { 0x0028, 0x031C, 0x0221, 0x0029, 0xFFFF }, { 0x0120, 0x0313, 0x0C0B, 0x081F, 0xFFFF }, { 0x0605,
85 { 0x0216, 0x041E, 0x0C0E, 0x0223, 0x0127, 0xFFFF }, { 0x0122, 0x0304, 0x060A, 0x0311, 0x0A09, 0xFFFF }, { 0x0519, 0x190F, 0xFFFF }, { 0x002A, 0x0231, 0x0503, 0x0415, 0x0807, 0x002B, 0x071D, 0xFFFF }, { 0x0130, 0x0214,
86 0x0508, 0x0401, 0x0133, 0x0225, 0x061B, 0xFFFF },
87 { 0x0200, 0x0124, 0x0406, 0x0321, 0x0129, 0x100D, 0xFFFF }, { 0x031A,
88 0x0D0B, 0x091F, 0xFFFF },
89 { 0x0413, 0x0705, 0x0517, 0xFFFF }, { 0x0212, 0x0034, 0x0323, 0x0035, 0x0227, 0xFFFF }, { 0x0126, 0x080C, 0x0B09, 0xFFFF }, { 0x0411, 0x0619, 0x1A0F, 0xFFFF }, { 0x0210, 0x0331, 0x0603, 0x0515, 0x0907, 0x012B,
91 { 0x0318, 0x002C, 0x0501, 0x0233, 0x0325, 0x071B, 0x002D, 0x081D, 0xFFFF }, { 0x0132, 0x0302, 0x0229, 0x110D,
93 { 0x0128, 0x041C, 0x0421, 0x0E0B, 0x0A1F, 0xFFFF }, { 0x0220, 0x0513, 0x0617, 0xFFFF }, { 0x0135, 0x0805,
95 { 0x0316, 0x051E, 0x0D0E, 0x0423, 0xFFFF }, { 0x0222, 0x0404, 0x070A, 0x0511, 0x0719, 0x0C09, 0x1B0F,
97 { 0x0703, 0x0615, 0x0A07, 0x022B, 0xFFFF }, { 0x012A, 0x0431, 0x0601, 0x0333, 0x012D, 0x091D, 0xFFFF }, { 0x0230, 0x0314, 0x0036, 0x0608, 0x0425, 0x0037, 0x0329, 0x081B, 0x120D, 0xFFFF }, { 0x0300, 0x0224, 0x0506, 0x0521,
98 0x0F0B, 0x0B1F, 0xFFFF },
99 { 0x041A, 0x0613, 0x0717, 0xFFFF }, { 0x0235, 0x0905, 0xFFFF }, { 0x0312, 0x0134, 0x0523,
101 { 0x0226, 0x090C, 0x002E, 0x0611, 0x0D09, 0x002F, 0xFFFF }, { 0x0715, 0x0B07, 0x0819, 0x032B, 0x1C0F,
103 { 0x0310, 0x0531, 0x0701, 0x0803, 0x022D, 0x0A1D, 0xFFFF }, { 0x0418, 0x012C, 0x0433, 0x0525, 0x0137, 0x091B,
105 { 0x0232, 0x0402, 0x0621, 0x0429, 0xFFFF }, { 0x0228, 0x051C, 0x0713, 0x100B, 0x0C1F, 0xFFFF }, { 0x0320, 0x0335, 0x0A05, 0x0817, 0xFFFF }, { 0x0623, 0x0527, 0xFFFF }, { 0x0416, 0x061E, 0x0E0E, 0x0711, 0x0E09, 0x012F,
107 { 0x0322, 0x0504, 0x080A, 0x0919, 0x1D0F, 0xFFFF }, { 0x0631, 0x0903, 0x0815, 0x0C07, 0x042B, 0x032D, 0x0B1D,
109 { 0x022A, 0x0801, 0x0533, 0x0625, 0x0237, 0x0A1B, 0xFFFF }, { 0x0330, 0x0414, 0x0136, 0x0708, 0x0721, 0x0529,
111 { 0x0400, 0x0324, 0x0606, 0x0038, 0x0039, 0x110B, 0x0D1F, 0xFFFF }, { 0x051A, 0x0813, 0x0B05, 0x0917,
113 { 0x0723, 0x0435, 0x0627, 0xFFFF }, { 0x0412, 0x0234, 0x0F09, 0x022F, 0xFFFF }, { 0x0326, 0x0A0C, 0x012E,
114 0x0811, 0x0A19, 0x1E0F, 0xFFFF },
115 { 0x0731, 0x0A03, 0x0915, 0x0D07, 0x052B, 0xFFFF }, { 0x0410, 0x0901, 0x0633, 0x0725,
116 0x0337, 0x0B1B, 0x042D, 0x0C1D, 0xFFFF },
117 { 0x0518, 0x022C, 0x0629, 0x150D, 0xFFFF }, { 0x0332, 0x0502, 0x0821, 0x0139,
118 0x120B, 0x0E1F, 0xFFFF },
119 { 0x0328, 0x061C, 0x0913, 0x0A17, 0xFFFF }, { 0x0420, 0x0535, 0x0C05, 0x0727, 0xFFFF }, { 0x0823, 0x032F, 0xFFFF }, { 0x0516, 0x071E, 0x0F0E, 0x0911, 0x0B19, 0x1009, 0x1F0F, 0xFFFF }, { 0x0422, 0x0604, 0x090A,
120 0x0B03, 0x0A15, 0x0E07, 0x062B, 0xFFFF },
121 { 0x0831, 0x0A01, 0x0733, 0x052D, 0x0D1D, 0xFFFF }, { 0x032A, 0x0825, 0x0437,
122 0x0729, 0x0C1B, 0x160D, 0xFFFF },
123 { 0x0430, 0x0514, 0x0236, 0x0808, 0x0921, 0x0239, 0x130B, 0x0F1F, 0xFFFF }, { 0x0500,
124 0x0424, 0x0706, 0x0138, 0x0A13, 0x0B17, 0xFFFF },
125 { 0x061A, 0x0635, 0x0D05, 0xFFFF }, { 0x0923, 0x0827, 0xFFFF }, { 0x0512, 0x0334, 0x003A, 0x0A11, 0x1109, 0x003B, 0x042F, 0xFFFF }, { 0x0426, 0x0B0C, 0x022E, 0x0B15, 0x0F07, 0x0C19,
127 { 0x0931, 0x0B01, 0x0C03, 0x062D, 0x0E1D, 0xFFFF }, { 0x0510, 0x0833, 0x0925, 0x0537, 0x0D1B, 0x170D,
129 { 0x0618, 0x032C, 0x0A21, 0x0339, 0x0829, 0xFFFF }, { 0x0432, 0x0602, 0x0B13, 0x140B, 0x101F, 0xFFFF }, { 0x0428, 0x071C, 0x0735, 0x0E05, 0x0C17, 0xFFFF }, { 0x0520, 0x0A23, 0x0927, 0xFFFF }, { 0x0B11, 0x1209, 0x013B, 0x052F,
131 { 0x0616, 0x081E, 0x0D19, 0xFFFF }, { 0x0522, 0x0704, 0x0A0A, 0x0A31, 0x0D03, 0x0C15, 0x1007, 0x082B, 0x072D,
133 { 0x0C01, 0x0933, 0x0A25, 0x0637, 0x0E1B, 0xFFFF }, { 0x042A, 0x0B21, 0x0929, 0x180D, 0xFFFF }, { 0x0530, 0x0614, 0x0336, 0x0908, 0x0439, 0x150B, 0x111F, 0xFFFF }, { 0x0600, 0x0524, 0x0806, 0x0238, 0x0C13, 0x0F05,
135 { 0x071A, 0x0B23, 0x0835, 0x0A27, 0xFFFF }, { 0x1309, 0x023B, 0x062F, 0xFFFF }, { 0x0612, 0x0434,
136 0x013A, 0x0C11, 0x0E19, 0xFFFF },
137 { 0x0526, 0x0C0C, 0x032E, 0x0B31, 0x0E03, 0x0D15, 0x1107, 0x092B, 0xFFFF }, { 0x0D01,
138 0x0A33, 0x0B25, 0x0737, 0x0F1B, 0x082D, 0x101D, 0xFFFF },
139 { 0x0610, 0x0A29, 0x190D, 0xFFFF }, { 0x0718, 0x042C, 0x0C21,
140 0x0539, 0x160B, 0x121F, 0xFFFF },
141 { 0x0532, 0x0702, 0x0D13, 0x0E17, 0xFFFF }, { 0x0528, 0x081C, 0x0935, 0x1005, 0x0B27,
143 { 0x0620, 0x0C23, 0x033B, 0x072F, 0xFFFF }, { 0x0D11, 0x0F19, 0x1409, 0xFFFF }, { 0x0716, 0x003C, 0x091E,
144 0x0F03, 0x0E15, 0x1207, 0x0A2B, 0x003D, 0xFFFF },
145 { 0x0622, 0x0804, 0x0B0A, 0x0C31, 0x0E01, 0x0B33, 0x092D, 0x111D,
147 { 0x0C25, 0x0837, 0x0B29, 0x101B, 0x1A0D, 0xFFFF }, { 0x052A, 0x0D21, 0x0639, 0x170B, 0x131F, 0xFFFF }, { 0x0630, 0x0714, 0x0436, 0x0A08, 0x0E13, 0x0F17, 0xFFFF }, { 0x0700, 0x0624, 0x0906, 0x0338, 0x0A35, 0x1105, 0xFFFF }, { 0x081A, 0x0D23, 0x0C27, 0xFFFF }, { 0x0E11, 0x1509, 0x043B, 0x082F, 0xFFFF }, { 0x0712, 0x0534, 0x023A, 0x0F15, 0x1307,
148 0x1019, 0x0B2B, 0x013D, 0xFFFF },
149 { 0x0626, 0x0D0C, 0x042E, 0x0D31, 0x0F01, 0x1003, 0x0A2D, 0x121D, 0xFFFF }, { 0x0C33,
150 0x0D25, 0x0937, 0x111B, 0x1B0D, 0xFFFF },
151 { 0x0710, 0x0E21, 0x0739, 0x0C29, 0xFFFF }, { 0x0818, 0x052C, 0x0F13, 0x180B,
153 { 0x0632, 0x0802, 0x0B35, 0x1205, 0x1017, 0xFFFF }, { 0x0628, 0x091C, 0x0E23, 0x0D27, 0xFFFF }, { 0x0720, 0x0F11, 0x1609, 0x053B, 0x092F, 0xFFFF }, { 0x1119, 0x023D, 0xFFFF }, { 0x0816, 0x013C, 0x0A1E, 0x0E31, 0x1103,
154 0x1015, 0x1407, 0x0C2B, 0x0B2D, 0x131D, 0xFFFF },
155 { 0x0722, 0x0904, 0x0C0A, 0x1001, 0x0D33, 0x0E25, 0x0A37, 0x121B,
157 { 0x0F21, 0x0D29, 0x1C0D, 0xFFFF }, { 0x062A, 0x0839, 0x190B, 0x151F, 0xFFFF }, { 0x0730, 0x0814, 0x0536,
158 0x0B08, 0x1013, 0x1305, 0x1117, 0xFFFF },
159 { 0x0800, 0x0724, 0x0A06, 0x0438, 0x0F23, 0x0C35, 0x0E27, 0xFFFF }, { 0x091A,
160 0x1709, 0x063B, 0x0A2F, 0xFFFF },
161 { 0x1011, 0x1219, 0x033D, 0xFFFF }, { 0x0812, 0x0634, 0x033A, 0x0F31, 0x1203, 0x1115,
162 0x1507, 0x0D2B, 0xFFFF },
163 { 0x0726, 0x0E0C, 0x052E, 0x1101, 0x0E33, 0x0F25, 0x0B37, 0x131B, 0x0C2D, 0x141D, 0xFFFF }, { 0x0E29, 0x1D0D, 0xFFFF }, { 0x0810, 0x1021, 0x0939, 0x1A0B, 0x161F, 0xFFFF }, { 0x0918, 0x062C, 0x1113, 0x1217, 0xFFFF }, { 0x0732, 0x0902, 0x0D35, 0x1405, 0x0F27, 0xFFFF }, { 0x0728, 0x0A1C, 0x1023, 0x073B, 0x0B2F, 0xFFFF }, { 0x0820,
164 0x1111, 0x1319, 0x1809, 0xFFFF },
165 { 0x1303, 0x1215, 0x1607, 0x0E2B, 0x043D, 0xFFFF }, { 0x0916, 0x023C, 0x0B1E, 0x1031,
166 0x1201, 0x0F33, 0x0D2D, 0x151D, 0xFFFF },
167 { 0x0822, 0x0A04, 0x0D0A, 0x1025, 0x0C37, 0x0F29, 0x141B, 0x1E0D, 0xFFFF }, { 0x1121, 0x0A39, 0x1B0B, 0x171F, 0xFFFF }, { 0x072A, 0x1213, 0x1317, 0xFFFF }, { 0x0830, 0x0914, 0x0636, 0x0C08, 0x0E35,
169 { 0x0900, 0x0824, 0x0B06, 0x0538, 0x1123, 0x1027, 0xFFFF }, { 0x0A1A, 0x1211, 0x1909, 0x083B, 0x0C2F,
171 { 0x1315, 0x1707, 0x1419, 0x0F2B, 0x053D, 0xFFFF }, { 0x0912, 0x0734, 0x043A, 0x1131, 0x1301, 0x1403, 0x0E2D,
173 { 0x0826, 0x0F0C, 0x062E, 0x1033, 0x1125, 0x0D37, 0x151B, 0x1F0D, 0xFFFF }, { 0x1221, 0x0B39, 0x1029,
175 { 0x0910, 0x1313, 0x1C0B, 0x181F, 0xFFFF }, { 0x0A18, 0x072C, 0x0F35, 0x1605, 0x1417, 0xFFFF }, { 0x0832,
176 0x0A02, 0x1223, 0x1127, 0xFFFF },
177 { 0x0828, 0x0B1C, 0x1311, 0x1A09, 0x093B, 0x0D2F, 0xFFFF }, { 0x0920, 0x1519, 0x063D,
179 { 0x1231, 0x1503, 0x1415, 0x1807, 0x102B, 0x0F2D, 0x171D, 0xFFFF }, { 0x0A16, 0x033C, 0x0C1E, 0x1401, 0x1133,
180 0x1225, 0x0E37, 0x161B, 0xFFFF },
181 { 0x0922, 0x0B04, 0x0E0A, 0x1321, 0x1129, 0xFFFF }, { 0x0C39, 0x1D0B, 0x191F, 0xFFFF }, { 0x082A, 0x1413, 0x1705, 0x1517, 0xFFFF }, { 0x0930, 0x0A14, 0x0736, 0x0D08, 0x1323, 0x1035, 0x1227, 0xFFFF }, { 0x0A00, 0x0924, 0x0C06, 0x0638, 0x1B09, 0x0A3B, 0x0E2F, 0xFFFF }, { 0x0B1A, 0x1411, 0x1619, 0x073D, 0xFFFF }, { 0x1331,
182 0x1603, 0x1515, 0x1907, 0x112B, 0xFFFF },
183 { 0x0A12, 0x0834, 0x053A, 0x1501, 0x1233, 0x1325, 0x0F37, 0x171B, 0x102D,
185 { 0x0926, 0x072E, 0x1229, 0xFFFF }, { 0x1421, 0x0D39, 0x1E0B, 0x1A1F, 0xFFFF }, { 0x0A10, 0x1513,
187 { 0x0B18, 0x082C, 0x1135, 0x1805, 0x1327, 0xFFFF }, { 0x0932, 0x0B02, 0x1423, 0x0B3B, 0x0F2F, 0xFFFF }, { 0x0928, 0x0C1C, 0x1511, 0x1719, 0x1C09, 0xFFFF }, { 0x0A20, 0x1703, 0x1615, 0x1A07, 0x122B, 0x083D, 0xFFFF }, { 0x1431, 0x1601, 0x1333, 0x112D, 0x191D, 0xFFFF }, { 0x0B16, 0x043C, 0x0D1E, 0x1425, 0x1037, 0x1329, 0x181B, 0xFFFF }, { 0x0A22, 0x0C04, 0x0F0A, 0x1521, 0x0E39, 0x1F0B, 0x1B1F, 0xFFFF }, { 0x1613, 0x1717, 0xFFFF }, { 0x092A, 0x1235, 0x1905,
189 { 0x0A30, 0x0B14, 0x0836, 0x0E08, 0x1523, 0x1427, 0xFFFF }, { 0x0B00, 0x0A24, 0x0D06, 0x0738, 0x1611, 0x1D09,
190 0x0C3B, 0x102F, 0xFFFF },
191 { 0x0C1A, 0x1715, 0x1B07, 0x1819, 0x132B, 0x093D, 0xFFFF }, { 0x1531, 0x1701, 0x1803, 0x122D,
193 { 0x0B12, 0x0934, 0x063A, 0x1433, 0x1525, 0x1137, 0x191B, 0xFFFF }, { 0x0A26, 0x003E, 0x082E, 0x1621,
194 0x0F39, 0x1429, 0x003F, 0xFFFF },
195 { 0x1713, 0x1C1F, 0xFFFF }, { 0x0B10, 0x1335, 0x1A05, 0x1817, 0xFFFF }, { 0x0C18,
196 0x092C, 0x1623, 0x1527, 0xFFFF },
197 { 0x0A32, 0x0C02, 0x1711, 0x1E09, 0x0D3B, 0x112F, 0xFFFF }, { 0x0A28, 0x0D1C, 0x1919,
199 { 0x0B20, 0x1631, 0x1903, 0x1815, 0x1C07, 0x142B, 0x132D, 0x1B1D, 0xFFFF }, { 0x1801, 0x1533, 0x1625,
200 0x1237, 0x1A1B, 0xFFFF },
201 { 0x0C16, 0x053C, 0x0E1E, 0x1721, 0x1529, 0x013F, 0xFFFF }, { 0x0B22, 0x0D04, 0x1039, 0x1D1F,
203 { 0x1813, 0x1B05, 0x1917, 0xFFFF }, { 0x0A2A, 0x1723, 0x1435, 0x1627, 0xFFFF }, { 0x0B30, 0x0C14, 0x0936,
204 0x0F08, 0x1F09, 0x0E3B, 0x122F, 0xFFFF },
205 { 0x0C00, 0x0B24, 0x0E06, 0x0838, 0x1811, 0x1A19, 0x0B3D, 0xFFFF }, { 0x0D1A,
206 0x1731, 0x1A03, 0x1915, 0x1D07, 0x152B, 0xFFFF },
207 { 0x1901, 0x1633, 0x1725, 0x1337, 0x1B1B, 0x142D, 0x1C1D, 0xFFFF }, { 0x0C12, 0x0A34, 0x073A, 0x1629, 0x023F, 0xFFFF }, { 0x0B26, 0x013E, 0x092E, 0x1821, 0x1139, 0x1E1F, 0xFFFF }, { 0x1913,
209 { 0x0C10, 0x1535, 0x1C05, 0x1727, 0xFFFF }, { 0x0D18, 0x0A2C, 0x1823, 0x0F3B, 0x132F, 0xFFFF }, { 0x0B32, 0x0D02, 0x1911, 0x1B19, 0xFFFF }, { 0x0B28, 0x0E1C, 0x1B03, 0x1A15, 0x1E07, 0x162B, 0x0C3D, 0xFFFF }, { 0x0C20,
210 0x1831, 0x1A01, 0x1733, 0x152D, 0x1D1D, 0xFFFF },
211 { 0x1825, 0x1437, 0x1729, 0x1C1B, 0x033F, 0xFFFF }, { 0x0D16, 0x063C,
212 0x0F1E, 0x1921, 0x1239, 0x1F1F, 0xFFFF },
213 { 0x0C22, 0x0E04, 0x1A13, 0x1B17, 0xFFFF }, { 0x1635, 0x1D05, 0xFFFF }, { 0x0B2A, 0x1923, 0x1827, 0xFFFF }, { 0x0C30, 0x0D14, 0x0A36, 0x1A11, 0x103B, 0x142F, 0xFFFF }, { 0x0D00, 0x0C24, 0x0F06,
214 0x0938, 0x1B15, 0x1F07, 0x1C19, 0x172B, 0x0D3D, 0xFFFF },
215 { 0x0E1A, 0x1931, 0x1B01, 0x1C03, 0x162D, 0x1E1D, 0xFFFF }, { 0x1833, 0x1925, 0x1537, 0x1D1B, 0xFFFF }, { 0x0D12, 0x0B34, 0x083A, 0x1A21, 0x1339, 0x1829, 0x043F, 0xFFFF }, { 0x0C26,
216 0x023E, 0x0A2E, 0x1B13, 0xFFFF },
217 { 0x1735, 0x1E05, 0x1C17, 0xFFFF }, { 0x0D10, 0x1A23, 0x1927, 0xFFFF }, { 0x0E18,
218 0x0B2C, 0x1B11, 0x113B, 0x152F, 0xFFFF },
219 { 0x0C32, 0x0E02, 0x1D19, 0x0E3D, 0xFFFF }, { 0x0C28, 0x0F1C, 0x1A31, 0x1D03,
220 0x1C15, 0x182B, 0x172D, 0x1F1D, 0xFFFF },
221 { 0x0D20, 0x1C01, 0x1933, 0x1A25, 0x1637, 0x1E1B, 0xFFFF }, { 0x1B21, 0x1929,
223 { 0x0E16, 0x073C, 0x1439, 0xFFFF }, { 0x0D22, 0x0F04, 0x1C13, 0x1F05, 0x1D17, 0xFFFF }, { 0x1B23,
224 0x1835, 0x1A27, 0xFFFF },
225 { 0x0C2A, 0x123B, 0x162F, 0xFFFF }, { 0x0D30, 0x0E14, 0x0B36, 0x1C11, 0x1E19, 0x0F3D, 0xFFFF }, { 0x0E00, 0x0D24, 0x0A38, 0x1B31, 0x1E03, 0x1D15, 0x192B, 0xFFFF }, { 0x0F1A, 0x1D01, 0x1A33, 0x1B25, 0x1737, 0x1F1B,
227 { 0x1A29, 0x063F, 0xFFFF }, { 0x0E12, 0x0C34, 0x093A, 0x1C21, 0x1539, 0xFFFF }, { 0x0D26, 0x033E,
228 0x0B2E, 0x1D13, 0x1E17, 0xFFFF },
229 { 0x1935, 0x1B27, 0xFFFF }, { 0x0E10, 0x1C23, 0x133B, 0x172F, 0xFFFF }, { 0x0F18,
230 0x0C2C, 0x1D11, 0x1F19, 0xFFFF },
231 { 0x0D32, 0x0F02, 0x1F03, 0x1E15, 0x1A2B, 0x103D, 0xFFFF }, { 0x0D28, 0x1C31, 0x1E01,
232 0x1B33, 0x192D, 0xFFFF },
233 { 0x0E20, 0x1C25, 0x1837, 0x1B29, 0x073F, 0xFFFF }, { 0x1D21, 0x1639, 0xFFFF }, { 0x0F16,
234 0x083C, 0x1E13, 0x1F17, 0xFFFF },
235 { 0x0E22, 0x1A35, 0xFFFF }, { 0x1D23, 0x1C27, 0xFFFF }, { 0x0D2A, 0x1E11, 0x143B,
237 { 0x0E30, 0x0F14, 0x0C36, 0x1F15, 0x1B2B, 0x113D, 0xFFFF }, { 0x0F00, 0x0E24, 0x0B38, 0x1D31, 0x1F01,
239 { 0x1C33, 0x1D25, 0x1937, 0xFFFF }, { 0x1E21, 0x1739, 0x1C29, 0x083F, 0xFFFF }, { 0x0F12, 0x0D34,
240 0x0A3A, 0x1F13, 0xFFFF },
241 { 0x0E26, 0x043E, 0x0C2E, 0x1B35, 0xFFFF }, { 0x1E23, 0x1D27, 0xFFFF }, { 0x0F10, 0x1F11,
242 0x153B, 0x192F, 0xFFFF },
243 { 0x0D2C, 0x123D, 0xFFFF },
246 uint16 etc1_block::pack_color5(const color_quad_u8 &color, bool scaled, uint bias)
248 return pack_color5(color.r, color.g, color.b, scaled, bias);
251 uint16 etc1_block::pack_color5(uint r, uint g, uint b, bool scaled, uint bias)
255 r = (r * 31U + bias) / 255U;
256 g = (g * 31U + bias) / 255U;
257 b = (b * 31U + bias) / 255U;
260 r = math::minimum(r, 31U);
261 g = math::minimum(g, 31U);
262 b = math::minimum(b, 31U);
264 return static_cast<uint16>(b | (g << 5U) | (r << 10U));
267 color_quad_u8 etc1_block::unpack_color5(uint16 packed_color5, bool scaled, uint alpha)
269 uint b = packed_color5 & 31U;
270 uint g = (packed_color5 >> 5U) & 31U;
271 uint r = (packed_color5 >> 10U) & 31U;
275 b = (b << 3U) | (b >> 2U);
276 g = (g << 3U) | (g >> 2U);
277 r = (r << 3U) | (r >> 2U);
280 return color_quad_u8(cNoClamp, r, g, b, math::minimum(alpha, 255U));
283 void etc1_block::unpack_color5(uint &r, uint &g, uint &b, uint16 packed_color5, bool scaled)
285 color_quad_u8 c(unpack_color5(packed_color5, scaled, 0));
291 bool etc1_block::unpack_color5(color_quad_u8 &result, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha)
293 color_quad_i16 dc(unpack_delta3(packed_delta3));
295 int b = (packed_color5 & 31U) + dc.b;
296 int g = ((packed_color5 >> 5U) & 31U) + dc.g;
297 int r = ((packed_color5 >> 10U) & 31U) + dc.r;
300 if (static_cast<uint>(r | g | b) > 31U)
303 r = math::clamp<int>(r, 0, 31);
304 g = math::clamp<int>(g, 0, 31);
305 b = math::clamp<int>(b, 0, 31);
310 b = (b << 3U) | (b >> 2U);
311 g = (g << 3U) | (g >> 2U);
312 r = (r << 3U) | (r >> 2U);
315 result.set_noclamp_rgba(r, g, b, math::minimum(alpha, 255U));
319 bool etc1_block::unpack_color5(uint &r, uint &g, uint &b, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha)
321 color_quad_u8 result;
322 const bool success = unpack_color5(result, packed_color5, packed_delta3, scaled, alpha);
329 uint16 etc1_block::pack_delta3(const color_quad_i16 &color)
331 return pack_delta3(color.r, color.g, color.b);
334 uint16 etc1_block::pack_delta3(int r, int g, int b)
336 VOGL_ASSERT((r >= cETC1ColorDeltaMin) && (r <= cETC1ColorDeltaMax));
337 VOGL_ASSERT((g >= cETC1ColorDeltaMin) && (g <= cETC1ColorDeltaMax));
338 VOGL_ASSERT((b >= cETC1ColorDeltaMin) && (b <= cETC1ColorDeltaMax));
345 return static_cast<uint16>(b | (g << 3) | (r << 6));
348 color_quad_i16 etc1_block::unpack_delta3(uint16 packed_delta3)
350 int r = (packed_delta3 >> 6) & 7;
351 int g = (packed_delta3 >> 3) & 7;
352 int b = packed_delta3 & 7;
359 return color_quad_i16(r, g, b, 0);
362 void etc1_block::unpack_delta3(int &r, int &g, int &b, uint16 packed_delta3)
364 r = (packed_delta3 >> 6) & 7;
365 g = (packed_delta3 >> 3) & 7;
366 b = packed_delta3 & 7;
375 uint16 etc1_block::pack_color4(const color_quad_u8 &color, bool scaled, uint bias)
377 return pack_color4(color.r, color.g, color.b, scaled, bias);
380 uint16 etc1_block::pack_color4(uint r, uint g, uint b, bool scaled, uint bias)
384 r = (r * 15U + bias) / 255U;
385 g = (g * 15U + bias) / 255U;
386 b = (b * 15U + bias) / 255U;
389 r = math::minimum(r, 15U);
390 g = math::minimum(g, 15U);
391 b = math::minimum(b, 15U);
393 return static_cast<uint16>(b | (g << 4U) | (r << 8U));
396 color_quad_u8 etc1_block::unpack_color4(uint16 packed_color4, bool scaled, uint alpha)
398 uint b = packed_color4 & 15U;
399 uint g = (packed_color4 >> 4U) & 15U;
400 uint r = (packed_color4 >> 8U) & 15U;
409 return color_quad_u8(cNoClamp, r, g, b, math::minimum(alpha, 255U));
412 void etc1_block::unpack_color4(uint &r, uint &g, uint &b, uint16 packed_color4, bool scaled)
414 color_quad_u8 c(unpack_color4(packed_color4, scaled, 0));
420 void etc1_block::get_diff_subblock_colors(color_quad_u8 *pDst, uint16 packed_color5, uint table_idx)
422 VOGL_ASSERT(table_idx < cETC1IntenModifierValues);
423 const int *pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
426 unpack_color5(r, g, b, packed_color5, true);
428 const int ir = static_cast<int>(r), ig = static_cast<int>(g), ib = static_cast<int>(b);
430 const int y0 = pInten_modifer_table[0];
431 pDst[0].set(ir + y0, ig + y0, ib + y0);
433 const int y1 = pInten_modifer_table[1];
434 pDst[1].set(ir + y1, ig + y1, ib + y1);
436 const int y2 = pInten_modifer_table[2];
437 pDst[2].set(ir + y2, ig + y2, ib + y2);
439 const int y3 = pInten_modifer_table[3];
440 pDst[3].set(ir + y3, ig + y3, ib + y3);
443 bool etc1_block::get_diff_subblock_colors(color_quad_u8 *pDst, uint16 packed_color5, uint16 packed_delta3, uint table_idx)
445 VOGL_ASSERT(table_idx < cETC1IntenModifierValues);
446 const int *pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
449 bool success = unpack_color5(r, g, b, packed_color5, packed_delta3, true);
451 const int ir = static_cast<int>(r), ig = static_cast<int>(g), ib = static_cast<int>(b);
453 const int y0 = pInten_modifer_table[0];
454 pDst[0].set(ir + y0, ig + y0, ib + y0);
456 const int y1 = pInten_modifer_table[1];
457 pDst[1].set(ir + y1, ig + y1, ib + y1);
459 const int y2 = pInten_modifer_table[2];
460 pDst[2].set(ir + y2, ig + y2, ib + y2);
462 const int y3 = pInten_modifer_table[3];
463 pDst[3].set(ir + y3, ig + y3, ib + y3);
468 void etc1_block::get_abs_subblock_colors(color_quad_u8 *pDst, uint16 packed_color4, uint table_idx)
470 VOGL_ASSERT(table_idx < cETC1IntenModifierValues);
471 const int *pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
474 unpack_color4(r, g, b, packed_color4, true);
476 const int ir = static_cast<int>(r), ig = static_cast<int>(g), ib = static_cast<int>(b);
478 const int y0 = pInten_modifer_table[0];
479 pDst[0].set(ir + y0, ig + y0, ib + y0);
481 const int y1 = pInten_modifer_table[1];
482 pDst[1].set(ir + y1, ig + y1, ib + y1);
484 const int y2 = pInten_modifer_table[2];
485 pDst[2].set(ir + y2, ig + y2, ib + y2);
487 const int y3 = pInten_modifer_table[3];
488 pDst[3].set(ir + y3, ig + y3, ib + y3);
491 bool unpack_etc1(const etc1_block &block, color_quad_u8 *pDst, bool preserve_alpha)
493 const bool diff_flag = block.get_diff_bit();
494 const bool flip_flag = block.get_flip_bit();
495 const uint table_index0 = block.get_inten_table(0);
496 const uint table_index1 = block.get_inten_table(1);
498 color_quad_u8 subblock_colors0[4];
499 color_quad_u8 subblock_colors1[4];
504 const uint16 base_color5 = block.get_base5_color();
505 const uint16 delta_color3 = block.get_delta3_color();
506 etc1_block::get_diff_subblock_colors(subblock_colors0, base_color5, table_index0);
508 if (!etc1_block::get_diff_subblock_colors(subblock_colors1, base_color5, delta_color3, table_index1))
513 const uint16 base_color4_0 = block.get_base4_color(0);
514 etc1_block::get_abs_subblock_colors(subblock_colors0, base_color4_0, table_index0);
516 const uint16 base_color4_1 = block.get_base4_color(1);
517 etc1_block::get_abs_subblock_colors(subblock_colors1, base_color4_1, table_index1);
524 for (uint y = 0; y < 2; y++)
526 pDst[0].set_rgb(subblock_colors0[block.get_selector(0, y)]);
527 pDst[1].set_rgb(subblock_colors0[block.get_selector(1, y)]);
528 pDst[2].set_rgb(subblock_colors0[block.get_selector(2, y)]);
529 pDst[3].set_rgb(subblock_colors0[block.get_selector(3, y)]);
533 for (uint y = 2; y < 4; y++)
535 pDst[0].set_rgb(subblock_colors1[block.get_selector(0, y)]);
536 pDst[1].set_rgb(subblock_colors1[block.get_selector(1, y)]);
537 pDst[2].set_rgb(subblock_colors1[block.get_selector(2, y)]);
538 pDst[3].set_rgb(subblock_colors1[block.get_selector(3, y)]);
544 for (uint y = 0; y < 4; y++)
546 pDst[0].set_rgb(subblock_colors0[block.get_selector(0, y)]);
547 pDst[1].set_rgb(subblock_colors0[block.get_selector(1, y)]);
548 pDst[2].set_rgb(subblock_colors1[block.get_selector(2, y)]);
549 pDst[3].set_rgb(subblock_colors1[block.get_selector(3, y)]);
562 for (uint y = 0; y < 2; y++)
564 pDst[0] = subblock_colors0[block.get_selector(0, y)];
565 pDst[1] = subblock_colors0[block.get_selector(1, y)];
566 pDst[2] = subblock_colors0[block.get_selector(2, y)];
567 pDst[3] = subblock_colors0[block.get_selector(3, y)];
571 for (uint y = 2; y < 4; y++)
573 pDst[0] = subblock_colors1[block.get_selector(0, y)];
574 pDst[1] = subblock_colors1[block.get_selector(1, y)];
575 pDst[2] = subblock_colors1[block.get_selector(2, y)];
576 pDst[3] = subblock_colors1[block.get_selector(3, y)];
586 for (uint y = 0; y < 4; y++)
588 pDst[0] = subblock_colors0[block.get_selector(0, y)];
589 pDst[1] = subblock_colors0[block.get_selector(1, y)];
590 pDst[2] = subblock_colors1[block.get_selector(2, y)];
591 pDst[3] = subblock_colors1[block.get_selector(3, y)];
600 bool etc1_optimizer::compute()
602 const uint n = m_pParams->m_num_src_pixels;
603 const int scan_delta_size = m_pParams->m_scan_delta_size;
605 // Scan through a subset of the 3D lattice centered around the avg block color trying each 3D (555 or 444) lattice point as a potential block color.
606 // Each time a better solution is found try to refine the current solution's block color based of the current selectors and intensity table index.
607 for (int zdi = 0; zdi < scan_delta_size; zdi++)
609 const int zd = m_pParams->m_pScan_deltas[zdi];
610 const int mbb = m_bb + zd;
613 else if (mbb > m_limit)
616 for (int ydi = 0; ydi < scan_delta_size; ydi++)
618 const int yd = m_pParams->m_pScan_deltas[ydi];
619 const int mbg = m_bg + yd;
622 else if (mbg > m_limit)
625 for (int xdi = 0; xdi < scan_delta_size; xdi++)
627 const int xd = m_pParams->m_pScan_deltas[xdi];
628 const int mbr = m_br + xd;
631 else if (mbr > m_limit)
634 etc1_solution_coordinates coords(mbr, mbg, mbb, 0, m_pParams->m_use_color4);
635 if (m_pParams->m_quality == cCRNETCQualitySlow)
637 if (!evaluate_solution(coords, m_trial_solution, &m_best_solution))
642 if (!evaluate_solution_fast(coords, m_trial_solution, &m_best_solution))
646 // Now we have the input block, the avg. color of the input pixels, a set of trial selector indices, and the block color+intensity index.
647 // Now, for each component, attempt to refine the current solution by solving a simple linear equation. For example, for 4 colors:
649 // pixel0 - (block_color+inten_table[selector0]) + pixel1 - (block_color+inten_table[selector1]) + pixel2 - (block_color+inten_table[selector2]) + pixel3 - (block_color+inten_table[selector3]) = 0
651 // (pixel0 + pixel1 + pixel2 + pixel3) - (block_color+inten_table[selector0]) - (block_color+inten_table[selector1]) - (block_color+inten_table[selector2]) - (block_color+inten_table[selector3]) = 0
652 // (pixel0 + pixel1 + pixel2 + pixel3) - block_color - inten_table[selector0] - block_color-inten_table[selector1] - block_color-inten_table[selector2] - block_color-inten_table[selector3] = 0
653 // (pixel0 + pixel1 + pixel2 + pixel3) - 4*block_color - inten_table[selector0] - inten_table[selector1] - inten_table[selector2] - inten_table[selector3] = 0
654 // (pixel0 + pixel1 + pixel2 + pixel3) - 4*block_color - (inten_table[selector0] + inten_table[selector1] + inten_table[selector2] + inten_table[selector3]) = 0
655 // (pixel0 + pixel1 + pixel2 + pixel3)/4 - block_color - (inten_table[selector0] + inten_table[selector1] + inten_table[selector2] + inten_table[selector3])/4 = 0
656 // block_color = (pixel0 + pixel1 + pixel2 + pixel3)/4 - (inten_table[selector0] + inten_table[selector1] + inten_table[selector2] + inten_table[selector3])/4
657 // So what this means:
658 // optimal_block_color = avg_input - avg_inten_delta
659 // So the optimal block color can be computed by taking the average block color and subtracting the current average of the intensity delta.
660 // Unfortunately, optimal_block_color must then be quantized to 555 or 444 so it's not always possible to improve matters using this formula.
661 // Also, the above formula is for unclamped intensity deltas. The actual implementation takes into account clamping.
663 const uint max_refinement_trials = (m_pParams->m_quality == cCRNETCQualityFast) ? 2 : (((xd | yd | zd) == 0) ? 4 : 2);
664 for (uint refinement_trial = 0; refinement_trial < max_refinement_trials; refinement_trial++)
666 const uint8 *pSelectors = m_best_solution.m_selectors.get_ptr();
667 const int *pInten_table = g_etc1_inten_tables[m_best_solution.m_coords.m_inten_table];
669 int delta_sum_r = 0, delta_sum_g = 0, delta_sum_b = 0;
670 const color_quad_u8 base_color(m_best_solution.m_coords.get_scaled_color());
671 for (uint r = 0; r < n; r++)
673 const uint s = *pSelectors++;
674 const int yd = pInten_table[s];
675 // Compute actual delta being applied to each pixel, taking into account clamping.
676 delta_sum_r += math::clamp<int>(base_color.r + yd, 0, 255) - base_color.r;
677 delta_sum_g += math::clamp<int>(base_color.g + yd, 0, 255) - base_color.g;
678 delta_sum_b += math::clamp<int>(base_color.b + yd, 0, 255) - base_color.b;
680 if ((!delta_sum_r) && (!delta_sum_g) && (!delta_sum_b))
682 const float avg_delta_r_f = static_cast<float>(delta_sum_r) / n;
683 const float avg_delta_g_f = static_cast<float>(delta_sum_g) / n;
684 const float avg_delta_b_f = static_cast<float>(delta_sum_b) / n;
685 const int br1 = math::clamp<int>(static_cast<uint>((m_avg_color[0] - avg_delta_r_f) * m_limit / 255.0f + .5f), 0, m_limit);
686 const int bg1 = math::clamp<int>(static_cast<uint>((m_avg_color[1] - avg_delta_g_f) * m_limit / 255.0f + .5f), 0, m_limit);
687 const int bb1 = math::clamp<int>(static_cast<uint>((m_avg_color[2] - avg_delta_b_f) * m_limit / 255.0f + .5f), 0, m_limit);
691 if ((mbr == br1) && (mbg == bg1) && (mbb == bb1))
693 else if ((br1 == m_best_solution.m_coords.m_unscaled_color.r) && (bg1 == m_best_solution.m_coords.m_unscaled_color.g) && (bb1 == m_best_solution.m_coords.m_unscaled_color.b))
695 else if ((m_br == br1) && (m_bg == bg1) && (m_bb == bb1))
701 etc1_solution_coordinates coords1(br1, bg1, bb1, 0, m_pParams->m_use_color4);
702 if (m_pParams->m_quality == cCRNETCQualitySlow)
704 if (!evaluate_solution(coords1, m_trial_solution, &m_best_solution))
709 if (!evaluate_solution_fast(coords1, m_trial_solution, &m_best_solution))
713 } // refinement_trial
719 if (!m_best_solution.m_valid)
721 m_pResult->m_error = cUINT32_MAX;
725 const uint8 *pSelectors = m_best_solution.m_selectors.get_ptr();
727 #ifdef VOGL_BUILD_DEBUG
729 color_quad_u8 block_colors[4];
730 m_best_solution.m_coords.get_block_colors(block_colors);
732 const color_quad_u8 *pSrc_pixels = m_pParams->m_pSrc_pixels;
733 uint64_t actual_error = 0;
734 for (uint i = 0; i < n; i++)
735 actual_error += color::elucidian_distance(pSrc_pixels[i], block_colors[pSelectors[i]], false);
737 VOGL_ASSERT(actual_error == m_best_solution.m_error);
741 m_pResult->m_error = m_best_solution.m_error;
743 m_pResult->m_block_color_unscaled = m_best_solution.m_coords.m_unscaled_color;
744 m_pResult->m_block_color4 = m_best_solution.m_coords.m_color4;
746 m_pResult->m_block_inten_table = m_best_solution.m_coords.m_inten_table;
747 memcpy(m_pResult->m_pSelectors, pSelectors, n);
753 void etc1_optimizer::init(const params ¶ms, results &result)
758 const uint n = m_pParams->m_num_src_pixels;
760 m_selectors.resize(n);
761 m_best_selectors.resize(n);
762 m_temp_selectors.resize(n);
763 m_trial_solution.m_selectors.resize(n);
764 m_best_solution.m_selectors.resize(n);
766 m_limit = m_pParams->m_use_color4 ? 15 : 31;
768 vec3F avg_color(0.0f);
771 m_sorted_luma[0].resize(n);
772 m_sorted_luma[1].resize(n);
774 for (uint i = 0; i < n; i++)
776 const color_quad_u8 &c = m_pParams->m_pSrc_pixels[i];
777 const vec3F fc(c.r, c.g, c.b);
781 m_luma[i] = static_cast<uint16>(c.r + c.g + c.b);
782 m_sorted_luma[0][i] = i;
784 avg_color /= static_cast<float>(n);
785 m_avg_color = avg_color;
787 m_br = math::clamp<int>(static_cast<uint>(m_avg_color[0] * m_limit / 255.0f + .5f), 0, m_limit);
788 m_bg = math::clamp<int>(static_cast<uint>(m_avg_color[1] * m_limit / 255.0f + .5f), 0, m_limit);
789 m_bb = math::clamp<int>(static_cast<uint>(m_avg_color[2] * m_limit / 255.0f + .5f), 0, m_limit);
791 if (m_pParams->m_quality <= cCRNETCQualityMedium)
793 m_pSorted_luma_indices = indirect_radix_sort(n, m_sorted_luma[0].get_ptr(), m_sorted_luma[1].get_ptr(), m_luma.get_ptr(), 0, sizeof(m_luma[0]), false);
794 m_pSorted_luma = m_sorted_luma[0].get_ptr();
795 if (m_pSorted_luma_indices == m_sorted_luma[0].get_ptr())
796 m_pSorted_luma = m_sorted_luma[1].get_ptr();
798 for (uint i = 0; i < n; i++)
799 m_pSorted_luma[i] = m_luma[m_pSorted_luma_indices[i]];
802 m_best_solution.m_coords.clear();
803 m_best_solution.m_valid = false;
804 m_best_solution.m_error = cUINT64_MAX;
807 bool etc1_optimizer::evaluate_solution(const etc1_solution_coordinates &coords, potential_solution &trial_solution, potential_solution *pBest_solution)
809 trial_solution.m_valid = false;
811 if (m_pParams->m_constrain_against_base_color5)
813 const int dr = coords.m_unscaled_color.r - m_pParams->m_base_color5.r;
814 const int dg = coords.m_unscaled_color.g - m_pParams->m_base_color5.g;
815 const int db = coords.m_unscaled_color.b - m_pParams->m_base_color5.b;
817 if ((math::minimum(dr, dg, db) < cETC1ColorDeltaMin) || (math::maximum(dr, dg, db) > cETC1ColorDeltaMax))
821 const color_quad_u8 base_color(coords.get_scaled_color());
823 const uint n = m_pParams->m_num_src_pixels;
824 VOGL_ASSERT(trial_solution.m_selectors.size() == n);
826 trial_solution.m_error = cUINT64_MAX;
828 for (uint inten_table = 0; inten_table < cETC1IntenModifierValues; inten_table++)
830 const int *pInten_table = g_etc1_inten_tables[inten_table];
832 color_quad_u8 block_colors[4];
833 for (uint s = 0; s < 4; s++)
835 const int yd = pInten_table[s];
836 block_colors[s].set(base_color.r + yd, base_color.g + yd, base_color.b + yd, 0);
839 uint64_t total_error = 0;
841 const color_quad_u8 *pSrc_pixels = m_pParams->m_pSrc_pixels;
842 for (uint c = 0; c < n; c++)
844 const color_quad_u8 &src_pixel = *pSrc_pixels++;
846 uint best_selector_index = 0;
847 uint best_error = math::square(src_pixel.r - block_colors[0].r) + math::square(src_pixel.g - block_colors[0].g) + math::square(src_pixel.b - block_colors[0].b);
849 uint trial_error = math::square(src_pixel.r - block_colors[1].r) + math::square(src_pixel.g - block_colors[1].g) + math::square(src_pixel.b - block_colors[1].b);
850 if (trial_error < best_error)
852 best_error = trial_error;
853 best_selector_index = 1;
856 trial_error = math::square(src_pixel.r - block_colors[2].r) + math::square(src_pixel.g - block_colors[2].g) + math::square(src_pixel.b - block_colors[2].b);
857 if (trial_error < best_error)
859 best_error = trial_error;
860 best_selector_index = 2;
863 trial_error = math::square(src_pixel.r - block_colors[3].r) + math::square(src_pixel.g - block_colors[3].g) + math::square(src_pixel.b - block_colors[3].b);
864 if (trial_error < best_error)
866 best_error = trial_error;
867 best_selector_index = 3;
870 m_temp_selectors[c] = static_cast<uint8>(best_selector_index);
872 total_error += best_error;
873 if (total_error >= trial_solution.m_error)
877 if (total_error < trial_solution.m_error)
879 trial_solution.m_error = total_error;
880 trial_solution.m_coords.m_inten_table = inten_table;
881 trial_solution.m_selectors.swap(m_temp_selectors);
882 trial_solution.m_valid = true;
885 trial_solution.m_coords.m_unscaled_color = coords.m_unscaled_color;
886 trial_solution.m_coords.m_color4 = m_pParams->m_use_color4;
888 bool success = false;
891 if (trial_solution.m_error < pBest_solution->m_error)
893 *pBest_solution = trial_solution;
901 bool etc1_optimizer::evaluate_solution_fast(const etc1_solution_coordinates &coords, potential_solution &trial_solution, potential_solution *pBest_solution)
903 if (m_pParams->m_constrain_against_base_color5)
905 const int dr = coords.m_unscaled_color.r - m_pParams->m_base_color5.r;
906 const int dg = coords.m_unscaled_color.g - m_pParams->m_base_color5.g;
907 const int db = coords.m_unscaled_color.b - m_pParams->m_base_color5.b;
909 if ((math::minimum(dr, dg, db) < cETC1ColorDeltaMin) || (math::maximum(dr, dg, db) > cETC1ColorDeltaMax))
911 trial_solution.m_valid = false;
916 const color_quad_u8 base_color(coords.get_scaled_color());
918 const uint n = m_pParams->m_num_src_pixels;
919 VOGL_ASSERT(trial_solution.m_selectors.size() == n);
921 trial_solution.m_error = cUINT64_MAX;
923 for (int inten_table = cETC1IntenModifierValues - 1; inten_table >= 0; --inten_table)
925 const int *pInten_table = g_etc1_inten_tables[inten_table];
928 color_quad_u8 block_colors[4];
929 for (uint s = 0; s < 4; s++)
931 const int yd = pInten_table[s];
932 color_quad_u8 block_color(base_color.r + yd, base_color.g + yd, base_color.b + yd, 0);
933 block_colors[s] = block_color;
934 block_inten[s] = block_color.r + block_color.g + block_color.b;
937 // evaluate_solution_fast() enforces/assumesd a total ordering of the input colors along the intensity (1,1,1) axis to more quickly classify the inputs to selectors.
938 // The inputs colors have been presorted along the projection onto this axis, and ETC1 block colors are always ordered along the intensity axis, so this classification is fast.
941 const uint block_inten_midpoints[3] = { block_inten[0] + block_inten[1], block_inten[1] + block_inten[2], block_inten[2] + block_inten[3] };
943 uint64_t total_error = 0;
944 const color_quad_u8 *pSrc_pixels = m_pParams->m_pSrc_pixels;
945 if ((m_pSorted_luma[n - 1] * 2) < block_inten_midpoints[0])
947 if (block_inten[0] > m_pSorted_luma[n - 1])
949 const uint min_error = static_cast<uint>(labs(block_inten[0] - m_pSorted_luma[n - 1]));
950 if (min_error >= trial_solution.m_error)
954 memset(&m_temp_selectors[0], 0, n);
956 for (uint c = 0; c < n; c++)
957 total_error += color::elucidian_distance(block_colors[0], pSrc_pixels[c], false);
959 else if ((m_pSorted_luma[0] * 2) >= block_inten_midpoints[2])
961 if (m_pSorted_luma[0] > block_inten[3])
963 const uint min_error = static_cast<uint>(labs(m_pSorted_luma[0] - block_inten[3]));
964 if (min_error >= trial_solution.m_error)
968 memset(&m_temp_selectors[0], 3, n);
970 for (uint c = 0; c < n; c++)
971 total_error += color::elucidian_distance(block_colors[3], pSrc_pixels[c], false);
975 uint cur_selector = 0, c;
976 for (c = 0; c < n; c++)
978 const uint y = m_pSorted_luma[c];
979 while ((y * 2) >= block_inten_midpoints[cur_selector])
980 if (++cur_selector > 2)
982 const uint sorted_pixel_index = m_pSorted_luma_indices[c];
983 m_temp_selectors[sorted_pixel_index] = static_cast<uint8>(cur_selector);
984 total_error += color::elucidian_distance(block_colors[cur_selector], pSrc_pixels[sorted_pixel_index], false);
989 const uint sorted_pixel_index = m_pSorted_luma_indices[c];
990 m_temp_selectors[sorted_pixel_index] = 3;
991 total_error += color::elucidian_distance(block_colors[3], pSrc_pixels[sorted_pixel_index], false);
996 if (total_error < trial_solution.m_error)
998 trial_solution.m_error = total_error;
999 trial_solution.m_coords.m_inten_table = inten_table;
1000 trial_solution.m_selectors.swap(m_temp_selectors);
1001 trial_solution.m_valid = true;
1006 trial_solution.m_coords.m_unscaled_color = coords.m_unscaled_color;
1007 trial_solution.m_coords.m_color4 = m_pParams->m_use_color4;
1009 bool success = false;
1012 if (trial_solution.m_error < pBest_solution->m_error)
1014 *pBest_solution = trial_solution;
1022 // Dither function from RYG's public domain real-time DXT1 compressor, modified for 555.
1023 static void DitherBlock(color_quad_u8 *dest, const color_quad_u8 *block)
1025 int err[8], *ep1 = err, *ep2 = err + 4;
1026 uint8 *quant = ryg_dxt::QuantRBTab + 8;
1028 // process channels seperately
1029 for (int ch = 0; ch < 3; ch++)
1031 uint8 *bp = (uint8 *)block;
1032 uint8 *dp = (uint8 *)dest;
1036 memset(err, 0, sizeof(err));
1038 for (int y = 0; y < 4; y++)
1041 dp[0] = quant[bp[0] + ((3 * ep2[1] + 5 * ep2[0]) >> 4)];
1042 ep1[0] = bp[0] - dp[0];
1045 dp[4] = quant[bp[4] + ((7 * ep1[0] + 3 * ep2[2] + 5 * ep2[1] + ep2[0]) >> 4)];
1046 ep1[1] = bp[4] - dp[4];
1049 dp[8] = quant[bp[8] + ((7 * ep1[1] + 3 * ep2[3] + 5 * ep2[2] + ep2[1]) >> 4)];
1050 ep1[2] = bp[8] - dp[8];
1053 dp[12] = quant[bp[12] + ((7 * ep1[2] + 5 * ep2[3] + ep2[2]) >> 4)];
1054 ep1[3] = bp[12] - dp[12];
1056 // advance to next line
1057 std::swap(ep1, ep2);
1064 static uint etc1_decode_value(uint diff, uint inten, uint selector, uint packed_c)
1066 const uint limit = diff ? 32 : 16;
1067 VOGL_NOTE_UNUSED(limit);
1068 VOGL_ASSERT((diff < 2) && (inten < 8) && (selector < 4) && (packed_c < limit));
1071 c = (packed_c >> 2) | (packed_c << 3);
1073 c = packed_c | (packed_c << 4);
1074 c += g_etc1_inten_tables[inten][selector];
1075 c = math::clamp<int>(c, 0, 255);
1079 void pack_etc1_block_init()
1081 for (uint diff = 0; diff < 2; diff++)
1083 const uint limit = diff ? 32 : 16;
1085 for (uint inten = 0; inten < 8; inten++)
1087 for (uint selector = 0; selector < 4; selector++)
1089 const uint inverse_table_index = diff + (inten << 1) + (selector << 4);
1090 for (uint color = 0; color < 256; color++)
1092 uint best_error = cUINT32_MAX, best_packed_c = 0;
1093 for (uint packed_c = 0; packed_c < limit; packed_c++)
1095 int v = etc1_decode_value(diff, inten, selector, packed_c);
1096 uint err = static_cast<uint>(labs(v - static_cast<int>(color)));
1097 if (err < best_error)
1100 best_packed_c = packed_c;
1105 VOGL_ASSERT(best_error <= 255);
1106 g_etc1_inverse_lookup[inverse_table_index][color] = static_cast<uint16>(best_packed_c | (best_error << 8));
1113 // Packs solid color blocks efficiently using a set of small precomputed tables.
1114 // For random 888 inputs, MSE results are better than Erricson's ETC1 packer in "slow" mode ~9.5% of the time, is slightly worse only ~.01% of the time, and is equal the rest of the time.
1115 static uint64_t pack_etc1_block_solid_color(etc1_block &block, const uint8 *pColor, vogl_etc1_pack_params &pack_params, pack_etc1_block_context &context)
1117 VOGL_ASSERT(g_etc1_inverse_lookup[0][255]);
1119 VOGL_NOTE_UNUSED(context), VOGL_NOTE_UNUSED(pack_params);
1120 static uint s_next_comp[4] = { 1, 2, 0, 1 };
1122 uint best_error = cUINT32_MAX, best_i = 0;
1123 int best_x = 0, best_packed_c1 = 0, best_packed_c2 = 0;
1125 // For each possible 8-bit value, there is a precomputed list of diff/inten/selector configurations that allow that 8-bit value to be encoded with no error.
1126 for (uint i = 0; i < 3; i++)
1128 const uint c1 = pColor[s_next_comp[i]], c2 = pColor[s_next_comp[i + 1]];
1130 const int delta_range = 1;
1131 for (int delta = -delta_range; delta <= delta_range; delta++)
1133 const int c_plus_delta = math::clamp<int>(pColor[i] + delta, 0, 255);
1135 const uint16 *pTable;
1137 pTable = g_color8_to_etc_block_config_0_255[0];
1138 else if (c_plus_delta == 255)
1139 pTable = g_color8_to_etc_block_config_0_255[1];
1141 pTable = g_color8_to_etc_block_config_1_to_254[c_plus_delta - 1];
1145 const uint x = *pTable++;
1147 #ifdef VOGL_BUILD_DEBUG
1148 const uint diff = x & 1;
1149 const uint inten = (x >> 1) & 7;
1150 const uint selector = (x >> 4) & 3;
1151 const uint p0 = (x >> 8) & 255;
1152 VOGL_ASSERT(etc1_decode_value(diff, inten, selector, p0) == (uint)c_plus_delta);
1155 const uint16 *pInverse_table = g_etc1_inverse_lookup[x & 0xFF];
1156 uint16 p1 = pInverse_table[c1];
1157 uint16 p2 = pInverse_table[c2];
1158 const uint trial_error = math::square(c_plus_delta - pColor[i]) + math::square(p1 >> 8) + math::square(p2 >> 8);
1159 if (trial_error < best_error)
1161 best_error = trial_error;
1163 best_packed_c1 = p1 & 0xFF;
1164 best_packed_c2 = p2 & 0xFF;
1167 goto found_perfect_match;
1169 } while (*pTable != 0xFFFF);
1172 found_perfect_match:
1174 const uint diff = best_x & 1;
1175 const uint inten = (best_x >> 1) & 7;
1177 block.m_bytes[3] = static_cast<uint8>(((inten | (inten << 3)) << 2) | (diff << 1));
1179 const uint etc1_selector = g_selector_index_to_etc1[(best_x >> 4) & 3];
1180 *reinterpret_cast<uint16 *>(&block.m_bytes[4]) = (etc1_selector & 2) ? 0xFFFF : 0;
1181 *reinterpret_cast<uint16 *>(&block.m_bytes[6]) = (etc1_selector & 1) ? 0xFFFF : 0;
1183 const uint best_packed_c0 = (best_x >> 8) & 255;
1186 block.m_bytes[best_i] = static_cast<uint8>(best_packed_c0 << 3);
1187 block.m_bytes[s_next_comp[best_i]] = static_cast<uint8>(best_packed_c1 << 3);
1188 block.m_bytes[s_next_comp[best_i + 1]] = static_cast<uint8>(best_packed_c2 << 3);
1192 block.m_bytes[best_i] = static_cast<uint8>(best_packed_c0 | (best_packed_c0 << 4));
1193 block.m_bytes[s_next_comp[best_i]] = static_cast<uint8>(best_packed_c1 | (best_packed_c1 << 4));
1194 block.m_bytes[s_next_comp[best_i + 1]] = static_cast<uint8>(best_packed_c2 | (best_packed_c2 << 4));
1200 static uint pack_etc1_block_solid_color_constrained(
1201 etc1_optimizer::results &results,
1202 uint num_colors, const uint8 *pColor,
1203 vogl_etc1_pack_params &pack_params,
1204 pack_etc1_block_context &context,
1206 const color_quad_u8 *pBase_color5_unscaled)
1208 VOGL_ASSERT(g_etc1_inverse_lookup[0][255]);
1210 VOGL_NOTE_UNUSED(context), VOGL_NOTE_UNUSED(pack_params);
1211 static uint s_next_comp[4] = { 1, 2, 0, 1 };
1213 uint best_error = cUINT32_MAX, best_i = 0;
1214 int best_x = 0, best_packed_c1 = 0, best_packed_c2 = 0;
1216 // For each possible 8-bit value, there is a precomputed list of diff/inten/selector configurations that allow that 8-bit value to be encoded with no error.
1217 for (uint i = 0; i < 3; i++)
1219 const uint c1 = pColor[s_next_comp[i]], c2 = pColor[s_next_comp[i + 1]];
1221 const int delta_range = 1;
1222 for (int delta = -delta_range; delta <= delta_range; delta++)
1224 const int c_plus_delta = math::clamp<int>(pColor[i] + delta, 0, 255);
1226 const uint16 *pTable;
1228 pTable = g_color8_to_etc_block_config_0_255[0];
1229 else if (c_plus_delta == 255)
1230 pTable = g_color8_to_etc_block_config_0_255[1];
1232 pTable = g_color8_to_etc_block_config_1_to_254[c_plus_delta - 1];
1236 const uint x = *pTable++;
1237 const uint diff = x & 1;
1238 if (static_cast<uint>(use_diff) != diff)
1240 if (*pTable == 0xFFFF)
1245 if ((diff) && (pBase_color5_unscaled))
1247 const int p0 = (x >> 8) & 255;
1248 int delta = p0 - static_cast<int>(pBase_color5_unscaled->c[i]);
1249 if ((delta < cETC1ColorDeltaMin) || (delta > cETC1ColorDeltaMax))
1251 if (*pTable == 0xFFFF)
1257 #ifdef VOGL_BUILD_DEBUG
1259 const uint inten = (x >> 1) & 7;
1260 const uint selector = (x >> 4) & 3;
1261 const uint p0 = (x >> 8) & 255;
1262 VOGL_ASSERT(etc1_decode_value(diff, inten, selector, p0) == (uint)c_plus_delta);
1266 const uint16 *pInverse_table = g_etc1_inverse_lookup[x & 0xFF];
1267 uint16 p1 = pInverse_table[c1];
1268 uint16 p2 = pInverse_table[c2];
1270 if ((diff) && (pBase_color5_unscaled))
1272 int delta1 = (p1 & 0xFF) - static_cast<int>(pBase_color5_unscaled->c[s_next_comp[i]]);
1273 int delta2 = (p2 & 0xFF) - static_cast<int>(pBase_color5_unscaled->c[s_next_comp[i + 1]]);
1274 if ((delta1 < cETC1ColorDeltaMin) || (delta1 > cETC1ColorDeltaMax) || (delta2 < cETC1ColorDeltaMin) || (delta2 > cETC1ColorDeltaMax))
1276 if (*pTable == 0xFFFF)
1282 const uint trial_error = math::square(c_plus_delta - pColor[i]) + math::square(p1 >> 8) + math::square(p2 >> 8);
1283 if (trial_error < best_error)
1285 best_error = trial_error;
1287 best_packed_c1 = p1 & 0xFF;
1288 best_packed_c2 = p2 & 0xFF;
1291 goto found_perfect_match;
1293 } while (*pTable != 0xFFFF);
1296 found_perfect_match:
1298 if (best_error == cUINT32_MAX)
1301 best_error *= num_colors;
1303 results.m_n = num_colors;
1304 results.m_block_color4 = !(best_x & 1);
1305 results.m_block_inten_table = (best_x >> 1) & 7;
1306 memset(results.m_pSelectors, (best_x >> 4) & 3, num_colors);
1308 const uint best_packed_c0 = (best_x >> 8) & 255;
1309 results.m_block_color_unscaled[best_i] = static_cast<uint8>(best_packed_c0);
1310 results.m_block_color_unscaled[s_next_comp[best_i]] = static_cast<uint8>(best_packed_c1);
1311 results.m_block_color_unscaled[s_next_comp[best_i + 1]] = static_cast<uint8>(best_packed_c2);
1312 results.m_error = best_error;
1317 uint64_t pack_etc1_block(etc1_block &dst_block, const color_quad_u8 *pSrc_pixels, vogl_etc1_pack_params &pack_params, pack_etc1_block_context &context)
1319 color_quad_u8 src_pixel0(pSrc_pixels[0]);
1322 for (r = 15; r >= 1; --r)
1323 if ((pSrc_pixels[r].r != src_pixel0.r) || (pSrc_pixels[r].g != src_pixel0.g) || (pSrc_pixels[r].b != src_pixel0.b))
1326 return 16 * pack_etc1_block_solid_color(dst_block, &pSrc_pixels[0].r, pack_params, context);
1328 color_quad_u8 dithered_pixels[16];
1329 if (pack_params.m_dithering)
1331 DitherBlock(dithered_pixels, pSrc_pixels);
1332 pSrc_pixels = dithered_pixels;
1335 uint64_t best_error = cUINT64_MAX;
1336 uint best_flip = false, best_use_color4 = false;
1338 uint8 best_selectors[2][8];
1339 etc1_optimizer::results best_results[2];
1340 for (uint i = 0; i < 2; i++)
1342 best_results[i].m_n = 8;
1343 best_results[i].m_pSelectors = best_selectors[i];
1346 uint8 selectors[3][8];
1347 etc1_optimizer::results results[3];
1349 for (uint i = 0; i < 3; i++)
1352 results[i].m_pSelectors = selectors[i];
1355 color_quad_u8 subblock_pixels[8];
1357 etc1_optimizer::params params(pack_params);
1358 params.m_num_src_pixels = 8;
1359 params.m_pSrc_pixels = subblock_pixels;
1361 for (uint flip = 0; flip < 2; flip++)
1363 for (uint use_color4 = 0; use_color4 < 2; use_color4++)
1365 uint64_t trial_error = 0;
1368 for (subblock = 0; subblock < 2; subblock++)
1371 memcpy(subblock_pixels, pSrc_pixels + subblock * 8, sizeof(color_quad_u8) * 8);
1374 const color_quad_u8 *pSrc_col = pSrc_pixels + subblock * 2;
1375 subblock_pixels[0] = pSrc_col[0];
1376 subblock_pixels[1] = pSrc_col[4];
1377 subblock_pixels[2] = pSrc_col[8];
1378 subblock_pixels[3] = pSrc_col[12];
1379 subblock_pixels[4] = pSrc_col[1];
1380 subblock_pixels[5] = pSrc_col[5];
1381 subblock_pixels[6] = pSrc_col[9];
1382 subblock_pixels[7] = pSrc_col[13];
1385 results[2].m_error = cUINT64_MAX;
1386 if ((params.m_quality >= cCRNETCQualityMedium) && ((subblock) || (use_color4)))
1388 color_quad_u8 subblock_pixel0(subblock_pixels[0]);
1389 for (r = 7; r >= 1; --r)
1390 if ((subblock_pixels[r].r != subblock_pixel0.r) || (subblock_pixels[r].g != subblock_pixel0.g) || (subblock_pixels[r].b != subblock_pixel0.b))
1394 pack_etc1_block_solid_color_constrained(results[2], 8, &subblock_pixel0.r, pack_params, context, !use_color4, (subblock && !use_color4) ? &results[0].m_block_color_unscaled : NULL);
1398 params.m_use_color4 = (use_color4 != 0);
1399 params.m_constrain_against_base_color5 = false;
1401 if ((!use_color4) && (subblock))
1403 params.m_constrain_against_base_color5 = true;
1404 params.m_base_color5 = results[0].m_block_color_unscaled;
1407 if (params.m_quality == cCRNETCQualitySlow)
1409 static const int s_scan_delta_0_to_4[] = { -4, -3, -2, -1, 0, 1, 2, 3, 4 };
1410 params.m_scan_delta_size = VOGL_ARRAY_SIZE(s_scan_delta_0_to_4);
1411 params.m_pScan_deltas = s_scan_delta_0_to_4;
1413 else if (params.m_quality == cCRNETCQualityMedium)
1415 static const int s_scan_delta_0_to_1[] = { -1, 0, 1 };
1416 params.m_scan_delta_size = VOGL_ARRAY_SIZE(s_scan_delta_0_to_1);
1417 params.m_pScan_deltas = s_scan_delta_0_to_1;
1421 static const int s_scan_delta_0[] = { 0 };
1422 params.m_scan_delta_size = VOGL_ARRAY_SIZE(s_scan_delta_0);
1423 params.m_pScan_deltas = s_scan_delta_0;
1426 context.m_optimizer.init(params, results[subblock]);
1428 if (!context.m_optimizer.compute())
1431 // Fairly arbitrary/unrefined thresholds that control how far away to scan for potentially better solutions.
1432 const uint refinement_error_thresh0 = 3000;
1433 const uint refinement_error_thresh1 = 6000;
1434 if ((params.m_quality >= cCRNETCQualityMedium) && (results[subblock].m_error > refinement_error_thresh0))
1436 if (params.m_quality == cCRNETCQualityMedium)
1438 static const int s_scan_delta_2_to_3[] = { -3, -2, 2, 3 };
1439 params.m_scan_delta_size = VOGL_ARRAY_SIZE(s_scan_delta_2_to_3);
1440 params.m_pScan_deltas = s_scan_delta_2_to_3;
1444 static const int s_scan_delta_5_to_5[] = { -5, 5 };
1445 static const int s_scan_delta_5_to_8[] = { -8, -7, -6, -5, 5, 6, 7, 8 };
1446 if (results[subblock].m_error > refinement_error_thresh1)
1448 params.m_scan_delta_size = VOGL_ARRAY_SIZE(s_scan_delta_5_to_8);
1449 params.m_pScan_deltas = s_scan_delta_5_to_8;
1453 params.m_scan_delta_size = VOGL_ARRAY_SIZE(s_scan_delta_5_to_5);
1454 params.m_pScan_deltas = s_scan_delta_5_to_5;
1458 if (!context.m_optimizer.compute())
1462 if (results[2].m_error < results[subblock].m_error)
1463 results[subblock] = results[2];
1465 trial_error += results[subblock].m_error;
1466 if (trial_error >= best_error)
1473 best_error = trial_error;
1474 best_results[0] = results[0];
1475 best_results[1] = results[1];
1477 best_use_color4 = use_color4;
1483 int dr = best_results[1].m_block_color_unscaled.r - best_results[0].m_block_color_unscaled.r;
1484 int dg = best_results[1].m_block_color_unscaled.g - best_results[0].m_block_color_unscaled.g;
1485 int db = best_results[1].m_block_color_unscaled.b - best_results[0].m_block_color_unscaled.b;
1486 if (!best_use_color4)
1488 if ((math::minimum(dr, dg, db) < cETC1ColorDeltaMin) || (math::maximum(dr, dg, db) > cETC1ColorDeltaMax))
1490 // Shouldn't ever happen
1495 if (best_use_color4)
1497 dst_block.m_bytes[0] = static_cast<uint8>(best_results[1].m_block_color_unscaled.r | (best_results[0].m_block_color_unscaled.r << 4));
1498 dst_block.m_bytes[1] = static_cast<uint8>(best_results[1].m_block_color_unscaled.g | (best_results[0].m_block_color_unscaled.g << 4));
1499 dst_block.m_bytes[2] = static_cast<uint8>(best_results[1].m_block_color_unscaled.b | (best_results[0].m_block_color_unscaled.b << 4));
1509 dst_block.m_bytes[0] = static_cast<uint8>((best_results[0].m_block_color_unscaled.r << 3) | dr);
1510 dst_block.m_bytes[1] = static_cast<uint8>((best_results[0].m_block_color_unscaled.g << 3) | dg);
1511 dst_block.m_bytes[2] = static_cast<uint8>((best_results[0].m_block_color_unscaled.b << 3) | db);
1514 dst_block.m_bytes[3] = static_cast<uint8>((best_results[1].m_block_inten_table << 2) | (best_results[0].m_block_inten_table << 5) | ((~best_use_color4 & 1) << 1) | best_flip);
1516 uint selector0 = 0, selector1 = 0;
1520 // { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 },
1521 // { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1 }
1523 // { 0, 2 }, { 1, 2 }, { 2, 2 }, { 3, 2 },
1524 // { 0, 3 }, { 1, 3 }, { 2, 3 }, { 3, 3 }
1525 const uint8 *pSelectors0 = best_results[0].m_pSelectors;
1526 const uint8 *pSelectors1 = best_results[1].m_pSelectors;
1527 for (int x = 3; x >= 0; --x)
1530 b = g_selector_index_to_etc1[pSelectors1[4 + x]];
1531 selector0 = (selector0 << 1) | (b & 1);
1532 selector1 = (selector1 << 1) | (b >> 1);
1534 b = g_selector_index_to_etc1[pSelectors1[x]];
1535 selector0 = (selector0 << 1) | (b & 1);
1536 selector1 = (selector1 << 1) | (b >> 1);
1538 b = g_selector_index_to_etc1[pSelectors0[4 + x]];
1539 selector0 = (selector0 << 1) | (b & 1);
1540 selector1 = (selector1 << 1) | (b >> 1);
1542 b = g_selector_index_to_etc1[pSelectors0[x]];
1543 selector0 = (selector0 << 1) | (b & 1);
1544 selector1 = (selector1 << 1) | (b >> 1);
1550 // { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 },
1551 // { 1, 0 }, { 1, 1 }, { 1, 2 }, { 1, 3 }
1553 // { 2, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 },
1554 // { 3, 0 }, { 3, 1 }, { 3, 2 }, { 3, 3 }
1555 for (int subblock = 1; subblock >= 0; --subblock)
1557 const uint8 *pSelectors = best_results[subblock].m_pSelectors + 4;
1558 for (uint i = 0; i < 2; i++)
1561 b = g_selector_index_to_etc1[pSelectors[3]];
1562 selector0 = (selector0 << 1) | (b & 1);
1563 selector1 = (selector1 << 1) | (b >> 1);
1565 b = g_selector_index_to_etc1[pSelectors[2]];
1566 selector0 = (selector0 << 1) | (b & 1);
1567 selector1 = (selector1 << 1) | (b >> 1);
1569 b = g_selector_index_to_etc1[pSelectors[1]];
1570 selector0 = (selector0 << 1) | (b & 1);
1571 selector1 = (selector1 << 1) | (b >> 1);
1573 b = g_selector_index_to_etc1[pSelectors[0]];
1574 selector0 = (selector0 << 1) | (b & 1);
1575 selector1 = (selector1 << 1) | (b >> 1);
1582 dst_block.m_bytes[4] = static_cast<uint8>(selector1 >> 8);
1583 dst_block.m_bytes[5] = static_cast<uint8>(selector1 & 0xFF);
1584 dst_block.m_bytes[6] = static_cast<uint8>(selector0 >> 8);
1585 dst_block.m_bytes[7] = static_cast<uint8>(selector0 & 0xFF);