1 | // basisu_basis_file.cpp |
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 | #include "basisu_basis_file.h" |
16 | #include "../transcoder/basisu_transcoder.h" |
17 | |
18 | // The output file version. Keep in sync with BASISD_SUPPORTED_BASIS_VERSION. |
19 | #define BASIS_FILE_VERSION (0x13) |
20 | |
21 | namespace basisu |
22 | { |
23 | void basisu_file::(const basisu_backend_output &encoder_output, basist::basis_texture_type tex_type, uint32_t userdata0, uint32_t userdata1, bool y_flipped, uint32_t us_per_frame) |
24 | { |
25 | m_header.m_header_size = sizeof(basist::basis_file_header); |
26 | |
27 | m_header.m_data_size = m_total_file_size - sizeof(basist::basis_file_header); |
28 | |
29 | m_header.m_total_slices = (uint32_t)encoder_output.m_slice_desc.size(); |
30 | |
31 | m_header.m_total_images = 0; |
32 | for (uint32_t i = 0; i < encoder_output.m_slice_desc.size(); i++) |
33 | m_header.m_total_images = maximum<uint32_t>(m_header.m_total_images, encoder_output.m_slice_desc[i].m_source_file_index + 1); |
34 | |
35 | m_header.m_tex_format = (int)encoder_output.m_tex_format; |
36 | m_header.m_flags = 0; |
37 | |
38 | if (encoder_output.m_etc1s) |
39 | { |
40 | assert(encoder_output.m_tex_format == basist::basis_tex_format::cETC1S); |
41 | m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagETC1S; |
42 | } |
43 | else |
44 | { |
45 | assert(encoder_output.m_tex_format != basist::basis_tex_format::cETC1S); |
46 | } |
47 | |
48 | if (y_flipped) |
49 | m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagYFlipped; |
50 | if (encoder_output.m_uses_global_codebooks) |
51 | m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagUsesGlobalCodebook; |
52 | if (encoder_output.m_srgb) |
53 | m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagSRGB; |
54 | |
55 | for (uint32_t i = 0; i < encoder_output.m_slice_desc.size(); i++) |
56 | { |
57 | if (encoder_output.m_slice_desc[i].m_alpha) |
58 | { |
59 | m_header.m_flags = m_header.m_flags | basist::cBASISHeaderFlagHasAlphaSlices; |
60 | break; |
61 | } |
62 | } |
63 | |
64 | m_header.m_tex_type = static_cast<uint8_t>(tex_type); |
65 | m_header.m_us_per_frame = clamp<uint32_t>(us_per_frame, 0, basist::cBASISMaxUSPerFrame); |
66 | |
67 | m_header.m_userdata0 = userdata0; |
68 | m_header.m_userdata1 = userdata1; |
69 | |
70 | m_header.m_total_endpoints = encoder_output.m_num_endpoints; |
71 | if (!encoder_output.m_uses_global_codebooks) |
72 | { |
73 | m_header.m_endpoint_cb_file_ofs = m_endpoint_cb_file_ofs; |
74 | m_header.m_endpoint_cb_file_size = (uint32_t)encoder_output.m_endpoint_palette.size(); |
75 | } |
76 | else |
77 | { |
78 | assert(!m_endpoint_cb_file_ofs); |
79 | } |
80 | |
81 | m_header.m_total_selectors = encoder_output.m_num_selectors; |
82 | if (!encoder_output.m_uses_global_codebooks) |
83 | { |
84 | m_header.m_selector_cb_file_ofs = m_selector_cb_file_ofs; |
85 | m_header.m_selector_cb_file_size = (uint32_t)encoder_output.m_selector_palette.size(); |
86 | } |
87 | else |
88 | { |
89 | assert(!m_selector_cb_file_ofs); |
90 | } |
91 | |
92 | m_header.m_tables_file_ofs = m_tables_file_ofs; |
93 | m_header.m_tables_file_size = (uint32_t)encoder_output.m_slice_image_tables.size(); |
94 | |
95 | m_header.m_slice_desc_file_ofs = m_slice_descs_file_ofs; |
96 | } |
97 | |
98 | bool basisu_file::create_image_descs(const basisu_backend_output &encoder_output) |
99 | { |
100 | const basisu_backend_slice_desc_vec &slice_descs = encoder_output.m_slice_desc; |
101 | |
102 | m_images_descs.resize(slice_descs.size()); |
103 | |
104 | uint64_t cur_slice_file_ofs = m_first_image_file_ofs; |
105 | for (uint32_t i = 0; i < slice_descs.size(); i++) |
106 | { |
107 | clear_obj(m_images_descs[i]); |
108 | |
109 | m_images_descs[i].m_image_index = slice_descs[i].m_source_file_index; |
110 | m_images_descs[i].m_level_index = slice_descs[i].m_mip_index; |
111 | |
112 | if (slice_descs[i].m_alpha) |
113 | m_images_descs[i].m_flags = m_images_descs[i].m_flags | basist::cSliceDescFlagsHasAlpha; |
114 | if (slice_descs[i].m_iframe) |
115 | m_images_descs[i].m_flags = m_images_descs[i].m_flags | basist::cSliceDescFlagsFrameIsIFrame; |
116 | |
117 | m_images_descs[i].m_orig_width = slice_descs[i].m_orig_width; |
118 | m_images_descs[i].m_orig_height = slice_descs[i].m_orig_height; |
119 | m_images_descs[i].m_num_blocks_x = slice_descs[i].m_num_blocks_x; |
120 | m_images_descs[i].m_num_blocks_y = slice_descs[i].m_num_blocks_y; |
121 | m_images_descs[i].m_slice_data_crc16 = encoder_output.m_slice_image_crcs[i]; |
122 | |
123 | if (encoder_output.m_slice_image_data[i].size() > UINT32_MAX) |
124 | { |
125 | error_printf("basisu_file::create_image_descs: Basis file too large\n" ); |
126 | return false; |
127 | } |
128 | |
129 | const uint32_t image_size = (uint32_t)encoder_output.m_slice_image_data[i].size(); |
130 | |
131 | m_images_descs[i].m_file_ofs = (uint32_t)cur_slice_file_ofs; |
132 | m_images_descs[i].m_file_size = image_size; |
133 | |
134 | cur_slice_file_ofs += image_size; |
135 | if (cur_slice_file_ofs > UINT32_MAX) |
136 | { |
137 | error_printf("basisu_file::create_image_descs: Basis file too large\n" ); |
138 | return false; |
139 | } |
140 | } |
141 | |
142 | assert(cur_slice_file_ofs == m_total_file_size); |
143 | return true; |
144 | } |
145 | |
146 | void basisu_file::create_comp_data(const basisu_backend_output &encoder_output) |
147 | { |
148 | const basisu_backend_slice_desc_vec &slice_descs = encoder_output.m_slice_desc; |
149 | |
150 | append_vector(m_comp_data, reinterpret_cast<const uint8_t *>(&m_header), sizeof(m_header)); |
151 | |
152 | assert(m_comp_data.size() == m_slice_descs_file_ofs); |
153 | append_vector(m_comp_data, reinterpret_cast<const uint8_t*>(&m_images_descs[0]), m_images_descs.size() * sizeof(m_images_descs[0])); |
154 | |
155 | if (!encoder_output.m_uses_global_codebooks) |
156 | { |
157 | if (encoder_output.m_endpoint_palette.size()) |
158 | { |
159 | assert(m_comp_data.size() == m_endpoint_cb_file_ofs); |
160 | append_vector(m_comp_data, reinterpret_cast<const uint8_t*>(&encoder_output.m_endpoint_palette[0]), encoder_output.m_endpoint_palette.size()); |
161 | } |
162 | |
163 | if (encoder_output.m_selector_palette.size()) |
164 | { |
165 | assert(m_comp_data.size() == m_selector_cb_file_ofs); |
166 | append_vector(m_comp_data, reinterpret_cast<const uint8_t*>(&encoder_output.m_selector_palette[0]), encoder_output.m_selector_palette.size()); |
167 | } |
168 | } |
169 | |
170 | if (encoder_output.m_slice_image_tables.size()) |
171 | { |
172 | assert(m_comp_data.size() == m_tables_file_ofs); |
173 | append_vector(m_comp_data, reinterpret_cast<const uint8_t*>(&encoder_output.m_slice_image_tables[0]), encoder_output.m_slice_image_tables.size()); |
174 | } |
175 | |
176 | assert(m_comp_data.size() == m_first_image_file_ofs); |
177 | for (uint32_t i = 0; i < slice_descs.size(); i++) |
178 | append_vector(m_comp_data, &encoder_output.m_slice_image_data[i][0], encoder_output.m_slice_image_data[i].size()); |
179 | |
180 | assert(m_comp_data.size() == m_total_file_size); |
181 | } |
182 | |
183 | void basisu_file::fixup_crcs() |
184 | { |
185 | basist::basis_file_header * = reinterpret_cast<basist::basis_file_header *>(&m_comp_data[0]); |
186 | |
187 | pHeader->m_data_size = m_total_file_size - sizeof(basist::basis_file_header); |
188 | pHeader->m_data_crc16 = basist::crc16(&m_comp_data[0] + sizeof(basist::basis_file_header), m_total_file_size - sizeof(basist::basis_file_header), 0); |
189 | |
190 | pHeader->m_header_crc16 = basist::crc16(&pHeader->m_data_size, sizeof(basist::basis_file_header) - BASISU_OFFSETOF(basist::basis_file_header, m_data_size), 0); |
191 | |
192 | pHeader->m_sig = basist::basis_file_header::cBASISSigValue; |
193 | pHeader->m_ver = BASIS_FILE_VERSION;// basist::basis_file_header::cBASISFirstVersion; |
194 | } |
195 | |
196 | bool basisu_file::init(const basisu_backend_output &encoder_output, basist::basis_texture_type tex_type, uint32_t userdata0, uint32_t userdata1, bool y_flipped, uint32_t us_per_frame) |
197 | { |
198 | clear(); |
199 | |
200 | const basisu_backend_slice_desc_vec &slice_descs = encoder_output.m_slice_desc; |
201 | |
202 | // The Basis file uses 32-bit fields for lots of stuff, so make sure it's not too large. |
203 | uint64_t check_size = 0; |
204 | if (!encoder_output.m_uses_global_codebooks) |
205 | { |
206 | check_size = (uint64_t)sizeof(basist::basis_file_header) + (uint64_t)sizeof(basist::basis_slice_desc) * slice_descs.size() + |
207 | (uint64_t)encoder_output.m_endpoint_palette.size() + (uint64_t)encoder_output.m_selector_palette.size() + (uint64_t)encoder_output.m_slice_image_tables.size(); |
208 | } |
209 | else |
210 | { |
211 | check_size = (uint64_t)sizeof(basist::basis_file_header) + (uint64_t)sizeof(basist::basis_slice_desc) * slice_descs.size() + |
212 | (uint64_t)encoder_output.m_slice_image_tables.size(); |
213 | } |
214 | if (check_size >= 0xFFFF0000ULL) |
215 | { |
216 | error_printf("basisu_file::init: File is too large!\n" ); |
217 | return false; |
218 | } |
219 | |
220 | m_header_file_ofs = 0; |
221 | m_slice_descs_file_ofs = sizeof(basist::basis_file_header); |
222 | if (encoder_output.m_tex_format == basist::basis_tex_format::cETC1S) |
223 | { |
224 | if (encoder_output.m_uses_global_codebooks) |
225 | { |
226 | m_endpoint_cb_file_ofs = 0; |
227 | m_selector_cb_file_ofs = 0; |
228 | m_tables_file_ofs = m_slice_descs_file_ofs + sizeof(basist::basis_slice_desc) * (uint32_t)slice_descs.size(); |
229 | } |
230 | else |
231 | { |
232 | m_endpoint_cb_file_ofs = m_slice_descs_file_ofs + sizeof(basist::basis_slice_desc) * (uint32_t)slice_descs.size(); |
233 | m_selector_cb_file_ofs = m_endpoint_cb_file_ofs + (uint32_t)encoder_output.m_endpoint_palette.size(); |
234 | m_tables_file_ofs = m_selector_cb_file_ofs + (uint32_t)encoder_output.m_selector_palette.size(); |
235 | } |
236 | m_first_image_file_ofs = m_tables_file_ofs + (uint32_t)encoder_output.m_slice_image_tables.size(); |
237 | } |
238 | else |
239 | { |
240 | m_endpoint_cb_file_ofs = 0; |
241 | m_selector_cb_file_ofs = 0; |
242 | m_tables_file_ofs = 0; |
243 | m_first_image_file_ofs = m_slice_descs_file_ofs + sizeof(basist::basis_slice_desc) * (uint32_t)slice_descs.size(); |
244 | } |
245 | |
246 | uint64_t total_file_size = m_first_image_file_ofs; |
247 | for (uint32_t i = 0; i < encoder_output.m_slice_image_data.size(); i++) |
248 | total_file_size += encoder_output.m_slice_image_data[i].size(); |
249 | if (total_file_size >= 0xFFFF0000ULL) |
250 | { |
251 | error_printf("basisu_file::init: File is too large!\n" ); |
252 | return false; |
253 | } |
254 | |
255 | m_total_file_size = (uint32_t)total_file_size; |
256 | |
257 | create_header(encoder_output, tex_type, userdata0, userdata1, y_flipped, us_per_frame); |
258 | |
259 | if (!create_image_descs(encoder_output)) |
260 | return false; |
261 | |
262 | create_comp_data(encoder_output); |
263 | |
264 | fixup_crcs(); |
265 | |
266 | return true; |
267 | } |
268 | |
269 | } // namespace basisu |
270 | |