1// basisu_backend.h
2// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15#pragma once
16
17#include "../transcoder/basisu.h"
18#include "basisu_enc.h"
19#include "../transcoder/basisu_transcoder_internal.h"
20#include "basisu_frontend.h"
21
22namespace basisu
23{
24 struct etc1_selector_palette_entry
25 {
26 etc1_selector_palette_entry()
27 {
28 clear();
29 }
30
31 void clear()
32 {
33 basisu::clear_obj(*this);
34 }
35
36 uint8_t operator[] (uint32_t i) const { assert(i < 16); return m_selectors[i]; }
37 uint8_t& operator[] (uint32_t i) { assert(i < 16); return m_selectors[i]; }
38
39 void set_uint32(uint32_t v)
40 {
41 for (uint32_t byte_index = 0; byte_index < 4; byte_index++)
42 {
43 uint32_t b = (v >> (byte_index * 8)) & 0xFF;
44
45 m_selectors[byte_index * 4 + 0] = b & 3;
46 m_selectors[byte_index * 4 + 1] = (b >> 2) & 3;
47 m_selectors[byte_index * 4 + 2] = (b >> 4) & 3;
48 m_selectors[byte_index * 4 + 3] = (b >> 6) & 3;
49 }
50 }
51
52 uint32_t get_uint32() const
53 {
54 return get_byte(0) | (get_byte(1) << 8) | (get_byte(2) << 16) | (get_byte(3) << 24);
55 }
56
57 uint32_t get_byte(uint32_t byte_index) const
58 {
59 assert(byte_index < 4);
60
61 return m_selectors[byte_index * 4 + 0] |
62 (m_selectors[byte_index * 4 + 1] << 2) |
63 (m_selectors[byte_index * 4 + 2] << 4) |
64 (m_selectors[byte_index * 4 + 3] << 6);
65 }
66
67 uint8_t operator()(uint32_t x, uint32_t y) const { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
68 uint8_t& operator()(uint32_t x, uint32_t y) { assert((x < 4) && (y < 4)); return m_selectors[x + y * 4]; }
69
70 bool operator< (const etc1_selector_palette_entry& other) const
71 {
72 for (uint32_t i = 0; i < 16; i++)
73 {
74 if (m_selectors[i] < other.m_selectors[i])
75 return true;
76 else if (m_selectors[i] != other.m_selectors[i])
77 return false;
78 }
79
80 return false;
81 }
82
83 bool operator== (const etc1_selector_palette_entry& other) const
84 {
85 for (uint32_t i = 0; i < 16; i++)
86 {
87 if (m_selectors[i] != other.m_selectors[i])
88 return false;
89 }
90
91 return true;
92 }
93
94 private:
95 uint8_t m_selectors[16];
96 };
97
98 typedef basisu::vector<etc1_selector_palette_entry> etc1_selector_palette_entry_vec;
99
100 struct encoder_block
101 {
102 encoder_block()
103 {
104 clear();
105 }
106
107 uint32_t m_endpoint_predictor;
108
109 int m_endpoint_index;
110 int m_selector_index;
111
112 int m_selector_history_buf_index;
113
114 bool m_is_cr_target;
115 void clear()
116 {
117 m_endpoint_predictor = 0;
118
119 m_endpoint_index = 0;
120 m_selector_index = 0;
121
122 m_selector_history_buf_index = 0;
123 m_is_cr_target = false;
124 }
125 };
126
127 typedef basisu::vector<encoder_block> encoder_block_vec;
128 typedef vector2D<encoder_block> encoder_block_vec2D;
129
130 struct etc1_endpoint_palette_entry
131 {
132 etc1_endpoint_palette_entry()
133 {
134 clear();
135 }
136
137 color_rgba m_color5;
138 uint32_t m_inten5;
139 bool m_color5_valid;
140
141 void clear()
142 {
143 clear_obj(*this);
144 }
145 };
146
147 typedef basisu::vector<etc1_endpoint_palette_entry> etc1_endpoint_palette_entry_vec;
148
149 struct basisu_backend_params
150 {
151 bool m_etc1s;
152 bool m_debug, m_debug_images;
153 float m_endpoint_rdo_quality_thresh;
154 float m_selector_rdo_quality_thresh;
155 uint32_t m_compression_level;
156
157 bool m_used_global_codebooks;
158
159 bool m_validate;
160
161 basisu_backend_params()
162 {
163 clear();
164 }
165
166 void clear()
167 {
168 m_etc1s = false;
169 m_debug = false;
170 m_debug_images = false;
171 m_endpoint_rdo_quality_thresh = 0.0f;
172 m_selector_rdo_quality_thresh = 0.0f;
173 m_compression_level = 0;
174 m_used_global_codebooks = false;
175 m_validate = true;
176 }
177 };
178
179 struct basisu_backend_slice_desc
180 {
181 basisu_backend_slice_desc()
182 {
183 clear();
184 }
185
186 void clear()
187 {
188 clear_obj(*this);
189 }
190
191 uint32_t m_first_block_index;
192
193 uint32_t m_orig_width;
194 uint32_t m_orig_height;
195
196 uint32_t m_width;
197 uint32_t m_height;
198
199 uint32_t m_num_blocks_x;
200 uint32_t m_num_blocks_y;
201
202 uint32_t m_num_macroblocks_x;
203 uint32_t m_num_macroblocks_y;
204
205 uint32_t m_source_file_index; // also the basis image index
206 uint32_t m_mip_index;
207 bool m_alpha;
208 bool m_iframe;
209 };
210
211 typedef basisu::vector<basisu_backend_slice_desc> basisu_backend_slice_desc_vec;
212
213 struct basisu_backend_output
214 {
215 basist::basis_tex_format m_tex_format;
216
217 bool m_etc1s;
218 bool m_uses_global_codebooks;
219 bool m_srgb;
220
221 uint32_t m_num_endpoints;
222 uint32_t m_num_selectors;
223
224 uint8_vec m_endpoint_palette;
225 uint8_vec m_selector_palette;
226
227 basisu_backend_slice_desc_vec m_slice_desc;
228
229 uint8_vec m_slice_image_tables;
230 basisu::vector<uint8_vec> m_slice_image_data;
231 uint16_vec m_slice_image_crcs;
232
233 basisu_backend_output()
234 {
235 clear();
236 }
237
238 void clear()
239 {
240 m_tex_format = basist::basis_tex_format::cETC1S;
241 m_etc1s = false;
242 m_uses_global_codebooks = false;
243 m_srgb = true;
244
245 m_num_endpoints = 0;
246 m_num_selectors = 0;
247
248 m_endpoint_palette.clear();
249 m_selector_palette.clear();
250 m_slice_desc.clear();
251 m_slice_image_tables.clear();
252 m_slice_image_data.clear();
253 m_slice_image_crcs.clear();
254 }
255
256 uint32_t get_output_size_estimate() const
257 {
258 uint32_t total_compressed_bytes = (uint32_t)(m_slice_image_tables.size() + m_endpoint_palette.size() + m_selector_palette.size());
259 for (uint32_t i = 0; i < m_slice_image_data.size(); i++)
260 total_compressed_bytes += (uint32_t)m_slice_image_data[i].size();
261
262 return total_compressed_bytes;
263 }
264 };
265
266 class basisu_backend
267 {
268 BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(basisu_backend);
269
270 public:
271
272 basisu_backend();
273
274 void clear();
275
276 void init(basisu_frontend *pFront_end, basisu_backend_params &params, const basisu_backend_slice_desc_vec &slice_desc);
277
278 uint32_t encode();
279
280 const basisu_backend_output &get_output() const { return m_output; }
281 const basisu_backend_params& get_params() const { return m_params; }
282
283 private:
284 basisu_frontend *m_pFront_end;
285 basisu_backend_params m_params;
286 basisu_backend_slice_desc_vec m_slices;
287 basisu_backend_output m_output;
288
289 etc1_endpoint_palette_entry_vec m_endpoint_palette;
290 etc1_selector_palette_entry_vec m_selector_palette;
291
292 struct etc1_global_selector_cb_entry_desc
293 {
294 uint32_t m_pal_index;
295 uint32_t m_mod_index;
296 bool m_was_used;
297 };
298
299 typedef basisu::vector<etc1_global_selector_cb_entry_desc> etc1_global_selector_cb_entry_desc_vec;
300
301 etc1_global_selector_cb_entry_desc_vec m_global_selector_palette_desc;
302
303 basisu::vector<encoder_block_vec2D> m_slice_encoder_blocks;
304
305 // Maps OLD to NEW endpoint/selector indices
306 uint_vec m_endpoint_remap_table_old_to_new;
307 uint_vec m_endpoint_remap_table_new_to_old;
308 bool_vec m_old_endpoint_was_used;
309 bool_vec m_new_endpoint_was_used;
310
311 uint_vec m_selector_remap_table_old_to_new;
312
313 // Maps NEW to OLD endpoint/selector indices
314 uint_vec m_selector_remap_table_new_to_old;
315
316 uint32_t get_total_slices() const
317 {
318 return (uint32_t)m_slices.size();
319 }
320
321 uint32_t get_total_slice_blocks() const
322 {
323 return m_pFront_end->get_total_output_blocks();
324 }
325
326 uint32_t get_block_index(uint32_t slice_index, uint32_t block_x, uint32_t block_y) const
327 {
328 const basisu_backend_slice_desc &slice = m_slices[slice_index];
329
330 assert((block_x < slice.m_num_blocks_x) && (block_y < slice.m_num_blocks_y));
331
332 return slice.m_first_block_index + block_y * slice.m_num_blocks_x + block_x;
333 }
334
335 uint32_t get_total_blocks(uint32_t slice_index) const
336 {
337 return m_slices[slice_index].m_num_blocks_x * m_slices[slice_index].m_num_blocks_y;
338 }
339
340 uint32_t get_total_blocks() const
341 {
342 uint32_t total_blocks = 0;
343 for (uint32_t i = 0; i < m_slices.size(); i++)
344 total_blocks += get_total_blocks(i);
345 return total_blocks;
346 }
347
348 // Returns the total number of input texels, not counting padding up to blocks/macroblocks.
349 uint32_t get_total_input_texels(uint32_t slice_index) const
350 {
351 return m_slices[slice_index].m_orig_width * m_slices[slice_index].m_orig_height;
352 }
353
354 uint32_t get_total_input_texels() const
355 {
356 uint32_t total_texels = 0;
357 for (uint32_t i = 0; i < m_slices.size(); i++)
358 total_texels += get_total_input_texels(i);
359 return total_texels;
360 }
361
362 int find_slice(uint32_t block_index, uint32_t *pBlock_x, uint32_t *pBlock_y) const
363 {
364 for (uint32_t i = 0; i < m_slices.size(); i++)
365 {
366 if ((block_index >= m_slices[i].m_first_block_index) && (block_index < (m_slices[i].m_first_block_index + m_slices[i].m_num_blocks_x * m_slices[i].m_num_blocks_y)))
367 {
368 const uint32_t ofs = block_index - m_slices[i].m_first_block_index;
369 const uint32_t x = ofs % m_slices[i].m_num_blocks_x;
370 const uint32_t y = ofs / m_slices[i].m_num_blocks_x;
371
372 if (pBlock_x) *pBlock_x = x;
373 if (pBlock_y) *pBlock_y = y;
374
375 return i;
376 }
377 }
378 return -1;
379 }
380
381 void create_endpoint_palette();
382
383 void create_selector_palette();
384
385 // endpoint palette
386 // 5:5:5 and predicted 4:4:4 colors, 1 or 2 3-bit intensity table indices
387 // selector palette
388 // 4x4 2-bit selectors
389
390 // per-macroblock:
391 // 4 diff bits
392 // 4 flip bits
393 // Endpoint template index, 1-8 endpoint indices
394 // Alternately, if no template applies, we can send 4 ETC1S bits followed by 4-8 endpoint indices
395 // 4 selector indices
396
397 void reoptimize_and_sort_endpoints_codebook(uint32_t total_block_endpoints_remapped, uint_vec &all_endpoint_indices);
398 void sort_selector_codebook();
399 void create_encoder_blocks();
400 void compute_slice_crcs();
401 bool encode_image();
402 bool encode_endpoint_palette();
403 bool encode_selector_palette();
404 int find_video_frame(int slice_index, int delta);
405 void check_for_valid_cr_blocks();
406 };
407
408} // namespace basisu
409
410