/src/libheif/libheif/codecs/uncompressed/unc_boxes.cc
Line | Count | Source |
1 | | /* |
2 | | * HEIF codec. |
3 | | * Copyright (c) 2023 Dirk Farin <dirk.farin@gmail.com> |
4 | | * |
5 | | * This file is part of libheif. |
6 | | * |
7 | | * libheif is free software: you can redistribute it and/or modify |
8 | | * it under the terms of the GNU Lesser General Public License as |
9 | | * published by the Free Software Foundation, either version 3 of |
10 | | * the License, or (at your option) any later version. |
11 | | * |
12 | | * libheif is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public License |
18 | | * along with libheif. If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | |
22 | | #include <cstdint> |
23 | | #include <cstring> |
24 | | #include <algorithm> |
25 | | #include <map> |
26 | | #include <iostream> |
27 | | #include <cassert> |
28 | | #include <limits> |
29 | | |
30 | | #include "libheif/heif.h" |
31 | | #include "libheif/heif_experimental.h" |
32 | | #include "unc_types.h" |
33 | | #include "unc_boxes.h" |
34 | | |
35 | | |
36 | | /** |
37 | | * Check for valid component format. |
38 | | * |
39 | | * @param format the format value to check |
40 | | * @return true if the format is a valid value, or false otherwise |
41 | | */ |
42 | | bool is_valid_component_format(uint8_t format) |
43 | 459 | { |
44 | 459 | return format <= component_format_max_valid; |
45 | 459 | } |
46 | | |
47 | | static std::map<heif_uncompressed_component_format, const char*> sNames_uncompressed_component_format{ |
48 | | {component_format_unsigned, "unsigned"}, |
49 | | {component_format_signed, "signed"}, |
50 | | {component_format_float, "float"}, |
51 | | {component_format_complex, "complex"} |
52 | | }; |
53 | | |
54 | | |
55 | | /** |
56 | | * Check for valid interleave mode. |
57 | | * |
58 | | * @param interleave the interleave value to check |
59 | | * @return true if the interleave mode is valid, or false otherwise |
60 | | */ |
61 | | bool is_valid_interleave_mode(uint8_t interleave) |
62 | 18 | { |
63 | 18 | return interleave <= interleave_mode_max_valid; |
64 | 18 | } |
65 | | |
66 | | static std::map<heif_uncompressed_interleave_mode, const char*> sNames_uncompressed_interleave_mode{ |
67 | | {interleave_mode_component, "component"}, |
68 | | {interleave_mode_pixel, "pixel"}, |
69 | | {interleave_mode_mixed, "mixed"}, |
70 | | {interleave_mode_row, "row"}, |
71 | | {interleave_mode_tile_component, "tile-component"}, |
72 | | {interleave_mode_multi_y, "multi-y"} |
73 | | }; |
74 | | |
75 | | |
76 | | /** |
77 | | * Check for valid sampling mode. |
78 | | * |
79 | | * @param sampling the sampling value to check |
80 | | * @return true if the sampling mode is valid, or false otherwise |
81 | | */ |
82 | | bool is_valid_sampling_mode(uint8_t sampling) |
83 | 19 | { |
84 | 19 | return sampling <= sampling_mode_max_valid; |
85 | 19 | } |
86 | | |
87 | | static std::map<heif_uncompressed_sampling_mode, const char*> sNames_uncompressed_sampling_mode{ |
88 | | {sampling_mode_no_subsampling, "no subsampling"}, |
89 | | {sampling_mode_422, "4:2:2"}, |
90 | | {sampling_mode_420, "4:2:0"}, |
91 | | {sampling_mode_411, "4:1:1"} |
92 | | }; |
93 | | |
94 | | |
95 | | bool is_predefined_component_type(uint16_t type) |
96 | 0 | { |
97 | | // check whether the component type can be mapped to heif_uncompressed_component_type and we have a name defined for |
98 | | // it in sNames_uncompressed_component_type. |
99 | 0 | return type <= heif_cmpd_component_type_max_valid; |
100 | 0 | } |
101 | | |
102 | | static std::map<heif_cmpd_component_type, const char*> sNames_uncompressed_component_type{ |
103 | | {heif_cmpd_component_type_monochrome, "monochrome"}, |
104 | | {heif_cmpd_component_type_Y, "Y"}, |
105 | | {heif_cmpd_component_type_Cb, "Cb"}, |
106 | | {heif_cmpd_component_type_Cr, "Cr"}, |
107 | | {heif_cmpd_component_type_red, "red"}, |
108 | | {heif_cmpd_component_type_green, "green"}, |
109 | | {heif_cmpd_component_type_blue, "blue"}, |
110 | | {heif_cmpd_component_type_alpha, "alpha"}, |
111 | | {heif_cmpd_component_type_depth, "depth"}, |
112 | | {heif_cmpd_component_type_disparity, "disparity"}, |
113 | | {heif_cmpd_component_type_palette, "palette"}, |
114 | | {heif_cmpd_component_type_filter_array, "filter-array"}, |
115 | | {heif_cmpd_component_type_padded, "padded"}, |
116 | | {heif_cmpd_component_type_cyan, "cyan"}, |
117 | | {heif_cmpd_component_type_magenta, "magenta"}, |
118 | | {heif_cmpd_component_type_yellow, "yellow"}, |
119 | | {heif_cmpd_component_type_key_black, "key (black)"} |
120 | | }; |
121 | | |
122 | | template <typename T> const char* get_name(T val, const std::map<T, const char*>& table) |
123 | 0 | { |
124 | 0 | auto iter = table.find(val); |
125 | 0 | if (iter == table.end()) { |
126 | 0 | return "unknown"; |
127 | 0 | } |
128 | 0 | else { |
129 | 0 | return iter->second; |
130 | 0 | } |
131 | 0 | } Unexecuted instantiation: char const* get_name<heif_cmpd_component_type>(heif_cmpd_component_type, std::__1::map<heif_cmpd_component_type, char const*, std::__1::less<heif_cmpd_component_type>, std::__1::allocator<std::__1::pair<heif_cmpd_component_type const, char const*> > > const&) Unexecuted instantiation: char const* get_name<heif_uncompressed_component_format>(heif_uncompressed_component_format, std::__1::map<heif_uncompressed_component_format, char const*, std::__1::less<heif_uncompressed_component_format>, std::__1::allocator<std::__1::pair<heif_uncompressed_component_format const, char const*> > > const&) Unexecuted instantiation: char const* get_name<heif_uncompressed_sampling_mode>(heif_uncompressed_sampling_mode, std::__1::map<heif_uncompressed_sampling_mode, char const*, std::__1::less<heif_uncompressed_sampling_mode>, std::__1::allocator<std::__1::pair<heif_uncompressed_sampling_mode const, char const*> > > const&) Unexecuted instantiation: char const* get_name<heif_uncompressed_interleave_mode>(heif_uncompressed_interleave_mode, std::__1::map<heif_uncompressed_interleave_mode, char const*, std::__1::less<heif_uncompressed_interleave_mode>, std::__1::allocator<std::__1::pair<heif_uncompressed_interleave_mode const, char const*> > > const&) |
132 | | |
133 | | void Box_cmpd::set_components(const std::vector<uint16_t>& components) |
134 | 0 | { |
135 | 0 | m_components.clear(); |
136 | |
|
137 | 0 | for (const auto& component : components) { |
138 | 0 | m_components.push_back({component, {}}); |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | | |
143 | | Error Box_cmpd::parse(BitstreamRange& range, const heif_security_limits* limits) |
144 | 49 | { |
145 | 49 | uint32_t component_count = range.read32(); |
146 | | |
147 | 49 | if (limits->max_components && component_count > limits->max_components) { |
148 | 3 | std::stringstream sstr; |
149 | 3 | sstr << "cmpd box should contain " << component_count << " components, but security limit is set to " |
150 | 3 | << limits->max_components << " components"; |
151 | | |
152 | 3 | return {heif_error_Invalid_input, |
153 | 3 | heif_suberror_Security_limit_exceeded, |
154 | 3 | sstr.str() |
155 | 3 | }; |
156 | 3 | } |
157 | | |
158 | 817 | for (unsigned int i = 0; i < component_count ; i++) { |
159 | 792 | if (range.eof()) { |
160 | 21 | std::stringstream sstr; |
161 | 21 | sstr << "cmpd box should contain " << component_count << " components, but box only contained " |
162 | 21 | << i << " components"; |
163 | | |
164 | 21 | return {heif_error_Invalid_input, |
165 | 21 | heif_suberror_End_of_data, |
166 | 21 | sstr.str() |
167 | 21 | }; |
168 | 21 | } |
169 | | |
170 | 771 | Component component; |
171 | 771 | component.component_type = range.read16(); |
172 | 771 | if (component.component_type >= 0x8000) { |
173 | 157 | component.component_type_uri = range.read_string(); |
174 | 157 | } |
175 | 614 | else { |
176 | 614 | component.component_type_uri = std::string(); |
177 | 614 | } |
178 | 771 | m_components.push_back(component); |
179 | 771 | } |
180 | | |
181 | 25 | return range.get_error(); |
182 | 46 | } |
183 | | |
184 | | std::string Box_cmpd::Component::get_component_type_name(uint16_t component_type) |
185 | 0 | { |
186 | 0 | std::stringstream sstr; |
187 | |
|
188 | 0 | if (is_predefined_component_type(component_type)) { |
189 | 0 | sstr << get_name(heif_cmpd_component_type(component_type), sNames_uncompressed_component_type) << "\n"; |
190 | 0 | } |
191 | 0 | else { |
192 | 0 | sstr << "0x" << std::hex << component_type << std::dec << "\n"; |
193 | 0 | } |
194 | |
|
195 | 0 | return sstr.str(); |
196 | 0 | } |
197 | | |
198 | | |
199 | | bool Box_cmpd::has_component(heif_cmpd_component_type type) const |
200 | 0 | { |
201 | 0 | return std::any_of(m_components.begin(), m_components.end(), |
202 | 0 | [type](const auto& cmp) { return cmp.component_type == type; }); |
203 | 0 | } |
204 | | |
205 | | |
206 | | void Box_cmpd::dump(std::ostream& sstr, Indent& indent, bool full_log) const |
207 | 0 | { |
208 | 0 | Box::dump(sstr, indent, full_log); |
209 | |
|
210 | 0 | for (const auto& component : m_components) { |
211 | 0 | sstr << indent << "component_type: " << component.get_component_type_name(); |
212 | |
|
213 | 0 | if (component.component_type >= 0x8000) { |
214 | 0 | sstr << indent << "| component_type_uri: " << component.component_type_uri << "\n"; |
215 | 0 | } |
216 | 0 | } |
217 | |
|
218 | 0 | return; |
219 | 0 | } |
220 | | |
221 | | Error Box_cmpd::write(StreamWriter& writer) const |
222 | 0 | { |
223 | 0 | size_t box_start = reserve_box_header_space(writer); |
224 | |
|
225 | 0 | writer.write32((uint32_t) m_components.size()); |
226 | 0 | for (const auto& component : m_components) { |
227 | 0 | writer.write16(component.component_type); |
228 | 0 | if (component.component_type >= 0x8000) { |
229 | 0 | writer.write(component.component_type_uri); |
230 | 0 | } |
231 | 0 | } |
232 | |
|
233 | 0 | prepend_header(writer, box_start); |
234 | |
|
235 | 0 | return Error::Ok; |
236 | 0 | } |
237 | | |
238 | | Error Box_uncC::parse(BitstreamRange& range, const heif_security_limits* limits) |
239 | 1.18k | { |
240 | 1.18k | parse_full_box_header(range); |
241 | | |
242 | 1.18k | m_profile = range.read32(); |
243 | | |
244 | 1.18k | if (get_version() == 1) { |
245 | 1.13k | switch (m_profile) { |
246 | 22 | case fourcc("rgb3"): |
247 | 1.10k | case fourcc("rgba"): |
248 | 1.10k | case fourcc("abgr"): |
249 | 1.10k | case fourcc("2vuy"): |
250 | 1.10k | case fourcc("yuv2"): |
251 | 1.10k | case fourcc("yvyu"): |
252 | 1.10k | case fourcc("vyuy"): |
253 | 1.10k | case fourcc("yuv1"): |
254 | 1.10k | case fourcc("v308"): |
255 | 1.10k | case fourcc("v408"): |
256 | 1.10k | case fourcc("y210"): |
257 | 1.10k | case fourcc("v410"): |
258 | 1.11k | case fourcc("v210"): |
259 | 1.11k | case fourcc("i420"): |
260 | 1.11k | case fourcc("nv12"): |
261 | 1.11k | case fourcc("nv21"): |
262 | 1.11k | case fourcc("yu22"): |
263 | 1.11k | case fourcc("yv22"): |
264 | 1.11k | case fourcc("yv20"): |
265 | 1.11k | break; |
266 | 18 | default: |
267 | 18 | return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Unknown uncC v1 profile"}; |
268 | 1.13k | } |
269 | 1.13k | } else if (get_version() == 0) { |
270 | | |
271 | 41 | uint32_t component_count = range.read32(); |
272 | | |
273 | 41 | if (limits->max_components && component_count > limits->max_components) { |
274 | 9 | std::stringstream sstr; |
275 | 9 | sstr << "Number of image components (" << component_count << ") exceeds security limit (" |
276 | 9 | << limits->max_components << ")"; |
277 | | |
278 | 9 | return {heif_error_Invalid_input, |
279 | 9 | heif_suberror_Security_limit_exceeded, |
280 | 9 | sstr.str()}; |
281 | 9 | } |
282 | | |
283 | 478 | for (uint32_t i = 0; i < component_count && !range.error() && !range.eof(); i++) { |
284 | 459 | Component component; |
285 | 459 | component.component_index = range.read16(); |
286 | 459 | component.component_bit_depth = uint16_t(range.read8() + 1); |
287 | 459 | component.component_format = range.read8(); |
288 | 459 | component.component_align_size = range.read8(); |
289 | 459 | m_components.push_back(component); |
290 | | |
291 | 459 | if (!is_valid_component_format(component.component_format)) { |
292 | 11 | return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid component format"}; |
293 | 11 | } |
294 | | |
295 | | // When component_align_size != 0, the component is padded up to that many bytes. |
296 | | // It therefore must be large enough to hold the component's bit depth; otherwise |
297 | | // the decoder computes a negative pad-bits count and shifts by a negative amount. |
298 | 448 | if (component.component_align_size != 0 && |
299 | 102 | uint32_t(component.component_align_size) * 8 < component.component_bit_depth) { |
300 | 2 | std::stringstream sstr; |
301 | 2 | sstr << "Component alignment (" << int(component.component_align_size) |
302 | 2 | << " bytes) is too small for component bit depth (" << component.component_bit_depth << " bits)"; |
303 | 2 | return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, sstr.str()}; |
304 | 2 | } |
305 | 448 | } |
306 | | |
307 | 19 | m_sampling_type = range.read8(); |
308 | 19 | if (!is_valid_sampling_mode(m_sampling_type)) { |
309 | 1 | return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid sampling mode"}; |
310 | 1 | } |
311 | | |
312 | 18 | m_interleave_type = range.read8(); |
313 | 18 | if (!is_valid_interleave_mode(m_interleave_type)) { |
314 | 1 | return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid interleave mode"}; |
315 | 1 | } |
316 | | |
317 | 17 | m_block_size = range.read8(); |
318 | | |
319 | 17 | uint8_t flags = range.read8(); |
320 | 17 | m_components_little_endian = !!(flags & 0x80); |
321 | 17 | m_block_pad_lsb = !!(flags & 0x40); |
322 | 17 | m_block_little_endian = !!(flags & 0x20); |
323 | 17 | m_block_reversed = !!(flags & 0x10); |
324 | 17 | m_pad_unknown = !!(flags & 0x08); |
325 | | |
326 | 17 | m_pixel_size = range.read32(); |
327 | | |
328 | 17 | if (limits->version >= 4 && |
329 | 17 | limits->max_iso23001_17_pixel_size_bytes != 0 && |
330 | 17 | m_pixel_size > limits->max_iso23001_17_pixel_size_bytes) { |
331 | 2 | std::stringstream sstr; |
332 | 2 | sstr << "uncC pixel_size (" << m_pixel_size << " bytes) exceeds security limit of " |
333 | 2 | << limits->max_iso23001_17_pixel_size_bytes << " bytes"; |
334 | 2 | return {heif_error_Memory_allocation_error, |
335 | 2 | heif_suberror_Security_limit_exceeded, |
336 | 2 | sstr.str()}; |
337 | 2 | } |
338 | | |
339 | 15 | m_row_align_size = range.read32(); |
340 | | |
341 | 15 | m_tile_align_size = range.read32(); |
342 | | |
343 | 15 | uint32_t num_tile_cols_minus_one = range.read32(); |
344 | 15 | uint32_t num_tile_rows_minus_one = range.read32(); |
345 | | |
346 | | // The field is stored as `count - 1`, so 0xFFFFFFFF would mean 2^32 tiles, |
347 | | // which we cannot represent in our uint32 m_num_tile_cols/rows. Reject this |
348 | | // before the security-limit check so that disabling the security limit does |
349 | | // not silently turn this into a divide-by-zero in get_heif_image_tiling(). |
350 | 15 | if (num_tile_cols_minus_one == 0xFFFFFFFF || num_tile_rows_minus_one == 0xFFFFFFFF) { |
351 | 2 | return {heif_error_Unsupported_feature, |
352 | 2 | heif_suberror_Invalid_parameter_value, |
353 | 2 | "uncC num_tile_cols/rows_minus_one of 0xFFFFFFFF (2^32 tiles) exceeds the supported range"}; |
354 | 2 | } |
355 | | |
356 | 13 | if (limits->max_number_of_tiles && |
357 | 13 | static_cast<uint64_t>(num_tile_cols_minus_one) + 1 > limits->max_number_of_tiles / (static_cast<uint64_t>(num_tile_rows_minus_one) + 1)) { |
358 | 3 | std::stringstream sstr; |
359 | 3 | sstr << "Tiling size " |
360 | 3 | << ((uint64_t)num_tile_cols_minus_one + 1) << " x " << ((uint64_t)num_tile_rows_minus_one + 1) |
361 | 3 | << " exceeds the maximum allowed number " << limits->max_number_of_tiles << " set as security limit"; |
362 | 3 | return {heif_error_Memory_allocation_error, |
363 | 3 | heif_suberror_Security_limit_exceeded, |
364 | 3 | sstr.str()}; |
365 | 3 | } |
366 | | |
367 | 10 | m_num_tile_cols = num_tile_cols_minus_one + 1; |
368 | 10 | m_num_tile_rows = num_tile_rows_minus_one + 1; |
369 | 10 | } |
370 | | |
371 | 1.13k | return range.get_error(); |
372 | 1.18k | } |
373 | | |
374 | | |
375 | | |
376 | | void Box_uncC::dump(std::ostream& sstr, Indent& indent, bool full_log) const |
377 | 0 | { |
378 | 0 | Box::dump(sstr, indent, full_log); |
379 | |
|
380 | 0 | sstr << indent << "profile: " << m_profile; |
381 | 0 | if (m_profile != 0) { |
382 | 0 | sstr << " (" << fourcc_to_string(m_profile) << ")"; |
383 | 0 | } |
384 | 0 | sstr << "\n"; |
385 | 0 | if (get_version() == 0) { |
386 | 0 | for (const auto& component : m_components) { |
387 | 0 | sstr << indent << "component_index: " << component.component_index << "\n"; |
388 | 0 | indent++; |
389 | 0 | sstr << indent << "component_bit_depth: " << (int) component.component_bit_depth << "\n"; |
390 | 0 | sstr << indent << "component_format: " << get_name(heif_uncompressed_component_format(component.component_format), sNames_uncompressed_component_format) << "\n"; |
391 | 0 | sstr << indent << "component_align_size: " << (int) component.component_align_size << "\n"; |
392 | 0 | indent--; |
393 | 0 | } |
394 | |
|
395 | 0 | sstr << indent << "sampling_type: " << get_name(heif_uncompressed_sampling_mode(m_sampling_type), sNames_uncompressed_sampling_mode) << "\n"; |
396 | |
|
397 | 0 | sstr << indent << "interleave_type: " << get_name(heif_uncompressed_interleave_mode(m_interleave_type), sNames_uncompressed_interleave_mode) << "\n"; |
398 | |
|
399 | 0 | sstr << indent << "block_size: " << (int) m_block_size << "\n"; |
400 | |
|
401 | 0 | sstr << indent << "components_little_endian: " << m_components_little_endian << "\n"; |
402 | 0 | sstr << indent << "block_pad_lsb: " << m_block_pad_lsb << "\n"; |
403 | 0 | sstr << indent << "block_little_endian: " << m_block_little_endian << "\n"; |
404 | 0 | sstr << indent << "block_reversed: " << m_block_reversed << "\n"; |
405 | 0 | sstr << indent << "pad_unknown: " << m_pad_unknown << "\n"; |
406 | |
|
407 | 0 | sstr << indent << "pixel_size: " << m_pixel_size << "\n"; |
408 | |
|
409 | 0 | sstr << indent << "row_align_size: " << m_row_align_size << "\n"; |
410 | |
|
411 | 0 | sstr << indent << "tile_align_size: " << m_tile_align_size << "\n"; |
412 | |
|
413 | 0 | sstr << indent << "num_tile_cols: " << m_num_tile_cols << "\n"; |
414 | |
|
415 | 0 | sstr << indent << "num_tile_rows: " << m_num_tile_rows << "\n"; |
416 | 0 | } |
417 | 0 | return; |
418 | 0 | } |
419 | | |
420 | | |
421 | | Error Box_uncC::write(StreamWriter& writer) const |
422 | 0 | { |
423 | 0 | size_t box_start = reserve_box_header_space(writer); |
424 | 0 | writer.write32(m_profile); |
425 | 0 | if (get_version() == 1) { |
426 | 0 | } |
427 | 0 | else if (get_version() == 0) { |
428 | 0 | writer.write32((uint32_t)m_components.size()); |
429 | 0 | for (const auto &component : m_components) { |
430 | 0 | if (component.component_bit_depth < 1 || component.component_bit_depth > 256) { |
431 | 0 | return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "component bit-depth out of range [1..256]"}; |
432 | 0 | } |
433 | | |
434 | 0 | if (component.component_index > 0xFFFF) { |
435 | 0 | return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "component index must be 16 bit"}; |
436 | 0 | } |
437 | 0 | writer.write16(static_cast<uint16_t>(component.component_index)); |
438 | 0 | writer.write8(uint8_t(component.component_bit_depth - 1)); |
439 | 0 | writer.write8(component.component_format); |
440 | 0 | writer.write8(component.component_align_size); |
441 | 0 | } |
442 | 0 | writer.write8(m_sampling_type); |
443 | 0 | writer.write8(m_interleave_type); |
444 | 0 | writer.write8(m_block_size); |
445 | 0 | uint8_t flags = 0; |
446 | 0 | flags |= (m_components_little_endian ? 0x80 : 0); |
447 | 0 | flags |= (m_block_pad_lsb ? 0x40 : 0); |
448 | 0 | flags |= (m_block_little_endian ? 0x20 : 0); |
449 | 0 | flags |= (m_block_reversed ? 0x10 : 0); |
450 | 0 | flags |= (m_pad_unknown ? 0x08 : 0); |
451 | 0 | writer.write8(flags); |
452 | 0 | writer.write32(m_pixel_size); |
453 | 0 | writer.write32(m_row_align_size); |
454 | 0 | writer.write32(m_tile_align_size); |
455 | 0 | writer.write32(m_num_tile_cols - 1); |
456 | 0 | writer.write32(m_num_tile_rows - 1); |
457 | 0 | } |
458 | 0 | prepend_header(writer, box_start); |
459 | |
|
460 | 0 | return Error::Ok; |
461 | 0 | } |
462 | | |
463 | | |
464 | | void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC, |
465 | | std::shared_ptr<Box_cmpd>& cmpd) |
466 | 3.27k | { |
467 | 3.27k | if (uncC->get_version() != 1 || cmpd) { |
468 | 63 | return; |
469 | 63 | } |
470 | | |
471 | | // Return cached synthetic cmpd if we already created one. |
472 | 3.21k | if (auto synthetic = uncC->get_synthetic_cmpd()) { |
473 | 2.13k | cmpd = synthetic; |
474 | 2.13k | return; |
475 | 2.13k | } |
476 | | |
477 | 1.07k | uint32_t profile = uncC->get_profile(); |
478 | 1.07k | cmpd = std::make_shared<Box_cmpd>(); |
479 | | |
480 | | // Profiles from ISO/IEC 23001-17 Table 5. |
481 | | // Format: {profile, [{component_type, bit_depth_minus_1}, ...], sampling_type, interleave_type} |
482 | | // The implicit cmpd is the unique component_types in order of first appearance. |
483 | | // The uncC component_index refers into that cmpd. |
484 | | |
485 | 1.07k | if (profile == fourcc("rgb3")) { |
486 | | // {'rgb3', [{4,7},{5,7},{6,7}], 0, 1} |
487 | 15 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
488 | 15 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
489 | 15 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
490 | 15 | cmpd->add_component({heif_cmpd_component_type_red}); |
491 | 15 | cmpd->add_component({heif_cmpd_component_type_green}); |
492 | 15 | cmpd->add_component({heif_cmpd_component_type_blue}); |
493 | 15 | uncC->set_sampling_type(sampling_mode_no_subsampling); |
494 | 15 | uncC->set_interleave_type(interleave_mode_pixel); |
495 | 15 | } |
496 | 1.06k | else if (profile == fourcc("rgba")) { |
497 | | // {'rgba', [{4,7},{5,7},{6,7},{7,7}], 0, 1} |
498 | 1.05k | uncC->add_component({0, 8, component_format_unsigned, 0}); |
499 | 1.05k | uncC->add_component({1, 8, component_format_unsigned, 0}); |
500 | 1.05k | uncC->add_component({2, 8, component_format_unsigned, 0}); |
501 | 1.05k | uncC->add_component({3, 8, component_format_unsigned, 0}); |
502 | 1.05k | cmpd->add_component({heif_cmpd_component_type_red}); |
503 | 1.05k | cmpd->add_component({heif_cmpd_component_type_green}); |
504 | 1.05k | cmpd->add_component({heif_cmpd_component_type_blue}); |
505 | 1.05k | cmpd->add_component({heif_cmpd_component_type_alpha}); |
506 | 1.05k | uncC->set_sampling_type(sampling_mode_no_subsampling); |
507 | 1.05k | uncC->set_interleave_type(interleave_mode_pixel); |
508 | 1.05k | } |
509 | 6 | else if (profile == fourcc("abgr")) { |
510 | | // {'abgr', [{7,7},{6,7},{5,7},{4,7}], 0, 1} |
511 | 2 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
512 | 2 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
513 | 2 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
514 | 2 | uncC->add_component({3, 8, component_format_unsigned, 0}); |
515 | 2 | cmpd->add_component({heif_cmpd_component_type_alpha}); |
516 | 2 | cmpd->add_component({heif_cmpd_component_type_blue}); |
517 | 2 | cmpd->add_component({heif_cmpd_component_type_green}); |
518 | 2 | cmpd->add_component({heif_cmpd_component_type_red}); |
519 | 2 | uncC->set_sampling_type(sampling_mode_no_subsampling); |
520 | 2 | uncC->set_interleave_type(interleave_mode_pixel); |
521 | 2 | } |
522 | 4 | else if (profile == fourcc("2vuy")) { |
523 | | // {'2vuy', [{2,7},{1,7},{3,7},{1,7}], 1, 5} Cb Y0 Cr Y1 |
524 | | // cmpd: Cb(0) Y(1) Cr(2) |
525 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
526 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
527 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
528 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
529 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
530 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
531 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
532 | 0 | uncC->set_sampling_type(sampling_mode_422); |
533 | 0 | uncC->set_interleave_type(interleave_mode_multi_y); |
534 | 0 | } |
535 | 4 | else if (profile == fourcc("yuv2")) { |
536 | | // {'yuv2', [{1,7},{2,7},{1,7},{3,7}], 1, 5} Y0 Cb Y1 Cr |
537 | | // cmpd: Y(0) Cb(1) Cr(2) |
538 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
539 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
540 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
541 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
542 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
543 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
544 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
545 | 0 | uncC->set_sampling_type(sampling_mode_422); |
546 | 0 | uncC->set_interleave_type(interleave_mode_multi_y); |
547 | 0 | } |
548 | 4 | else if (profile == fourcc("yvyu")) { |
549 | | // {'yvyu', [{1,7},{3,7},{1,7},{2,7}], 1, 5} Y0 Cr Y1 Cb |
550 | | // cmpd: Y(0) Cr(1) Cb(2) |
551 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
552 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
553 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
554 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
555 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
556 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
557 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
558 | 0 | uncC->set_sampling_type(sampling_mode_422); |
559 | 0 | uncC->set_interleave_type(interleave_mode_multi_y); |
560 | 0 | } |
561 | 4 | else if (profile == fourcc("vyuy")) { |
562 | | // {'vyuy', [{3,7},{1,7},{2,7},{1,7}], 1, 5} Cr Y0 Cb Y1 |
563 | | // cmpd: Cr(0) Y(1) Cb(2) |
564 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
565 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
566 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
567 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
568 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
569 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
570 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
571 | 0 | uncC->set_sampling_type(sampling_mode_422); |
572 | 0 | uncC->set_interleave_type(interleave_mode_multi_y); |
573 | 0 | } |
574 | 4 | else if (profile == fourcc("yuv1")) { |
575 | | // {'yuv1', [{1,7},{1,7},{2,7},{1,7},{1,7},{3,7}], 3, 5} Y0 Y1 Cb Y2 Y3 Cr |
576 | | // cmpd: Y(0) Cb(1) Cr(2) |
577 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
578 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
579 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
580 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
581 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
582 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
583 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
584 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
585 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
586 | 0 | uncC->set_sampling_type(sampling_mode_411); |
587 | 0 | uncC->set_interleave_type(interleave_mode_multi_y); |
588 | 0 | } |
589 | 4 | else if (profile == fourcc("v308")) { |
590 | | // {'v308', [{3,7},{1,7},{2,7}], 0, 1} Cr Y Cb |
591 | | // cmpd: Cr(0) Y(1) Cb(2) |
592 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
593 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
594 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
595 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
596 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
597 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
598 | 0 | uncC->set_sampling_type(sampling_mode_no_subsampling); |
599 | 0 | uncC->set_interleave_type(interleave_mode_pixel); |
600 | 0 | } |
601 | 4 | else if (profile == fourcc("v408")) { |
602 | | // {'v408', [{2,7},{1,7},{3,7},{7,7}], 0, 1} Cb Y Cr A |
603 | | // cmpd: Cb(0) Y(1) Cr(2) alpha(3) |
604 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
605 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
606 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
607 | 0 | uncC->add_component({3, 8, component_format_unsigned, 0}); |
608 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
609 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
610 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
611 | 0 | cmpd->add_component({heif_cmpd_component_type_alpha}); |
612 | 0 | uncC->set_sampling_type(sampling_mode_no_subsampling); |
613 | 0 | uncC->set_interleave_type(interleave_mode_pixel); |
614 | 0 | } |
615 | 4 | else if (profile == fourcc("y210")) { |
616 | | // {'y210', [{1,9},{2,9},{1,9},{3,9}], 1, 5} Y0 Cb Y1 Cr |
617 | | // block_size=2, block_little_endian=1, block_pad_lsb=1 |
618 | | // cmpd: Y(0) Cb(1) Cr(2) |
619 | 0 | uncC->add_component({0, 10, component_format_unsigned, 0}); |
620 | 0 | uncC->add_component({1, 10, component_format_unsigned, 0}); |
621 | 0 | uncC->add_component({0, 10, component_format_unsigned, 0}); |
622 | 0 | uncC->add_component({2, 10, component_format_unsigned, 0}); |
623 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
624 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
625 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
626 | 0 | uncC->set_sampling_type(sampling_mode_422); |
627 | 0 | uncC->set_interleave_type(interleave_mode_multi_y); |
628 | 0 | uncC->set_block_size(2); |
629 | 0 | uncC->set_block_little_endian(true); |
630 | 0 | uncC->set_block_pad_lsb(true); |
631 | 0 | } |
632 | 4 | else if (profile == fourcc("v410")) { |
633 | | // {'v410', [{2,9},{1,9},{3,9}], 0, 1} Cb Y Cr |
634 | | // block_size=4, block_little_endian=1, block_pad_lsb=1, block_reversed=1 |
635 | | // cmpd: Cb(0) Y(1) Cr(2) |
636 | 0 | uncC->add_component({0, 10, component_format_unsigned, 0}); |
637 | 0 | uncC->add_component({1, 10, component_format_unsigned, 0}); |
638 | 0 | uncC->add_component({2, 10, component_format_unsigned, 0}); |
639 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
640 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
641 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
642 | 0 | uncC->set_sampling_type(sampling_mode_no_subsampling); |
643 | 0 | uncC->set_interleave_type(interleave_mode_pixel); |
644 | 0 | uncC->set_block_size(4); |
645 | 0 | uncC->set_block_little_endian(true); |
646 | 0 | uncC->set_block_pad_lsb(true); |
647 | 0 | uncC->set_block_reversed(true); |
648 | 0 | } |
649 | 4 | else if (profile == fourcc("v210")) { |
650 | | // {'v210', [{2,9},{1,9},{3,9},{1,9}], 1, 5} Cb Y0 Cr Y1 |
651 | | // block_size=4, block_little_endian=1, block_reversed=1 |
652 | | // cmpd: Cb(0) Y(1) Cr(2) |
653 | 4 | uncC->add_component({0, 10, component_format_unsigned, 0}); |
654 | 4 | uncC->add_component({1, 10, component_format_unsigned, 0}); |
655 | 4 | uncC->add_component({2, 10, component_format_unsigned, 0}); |
656 | 4 | uncC->add_component({1, 10, component_format_unsigned, 0}); |
657 | 4 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
658 | 4 | cmpd->add_component({heif_cmpd_component_type_Y}); |
659 | 4 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
660 | 4 | uncC->set_sampling_type(sampling_mode_422); |
661 | 4 | uncC->set_interleave_type(interleave_mode_multi_y); |
662 | 4 | uncC->set_block_size(4); |
663 | 4 | uncC->set_block_little_endian(true); |
664 | 4 | uncC->set_block_reversed(true); |
665 | 4 | } |
666 | 0 | else if (profile == fourcc("i420")) { |
667 | | // {'i420', [{1,7},{2,7},{3,7}], 2, 0} planar YCbCr |
668 | | // cmpd: Y(0) Cb(1) Cr(2) |
669 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
670 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
671 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
672 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
673 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
674 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
675 | 0 | uncC->set_sampling_type(sampling_mode_420); |
676 | 0 | uncC->set_interleave_type(interleave_mode_component); |
677 | 0 | } |
678 | 0 | else if (profile == fourcc("nv12")) { |
679 | | // {'nv12', [{1,7},{2,7},{3,7}], 2, 2} semi-planar YCbCr |
680 | | // cmpd: Y(0) Cb(1) Cr(2) |
681 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
682 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
683 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
684 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
685 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
686 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
687 | 0 | uncC->set_sampling_type(sampling_mode_420); |
688 | 0 | uncC->set_interleave_type(interleave_mode_mixed); |
689 | 0 | } |
690 | 0 | else if (profile == fourcc("nv21")) { |
691 | | // {'nv21', [{1,7},{3,7},{2,7}], 2, 2} semi-planar YCrCb |
692 | | // cmpd: Y(0) Cr(1) Cb(2) |
693 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
694 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
695 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
696 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
697 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
698 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
699 | 0 | uncC->set_sampling_type(sampling_mode_420); |
700 | 0 | uncC->set_interleave_type(interleave_mode_mixed); |
701 | 0 | } |
702 | 0 | else if (profile == fourcc("yu22")) { |
703 | | // {'yu22', [{1,7},{2,7},{3,7}], 1, 0} planar YCbCr |
704 | | // cmpd: Y(0) Cb(1) Cr(2) |
705 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
706 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
707 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
708 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
709 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
710 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
711 | 0 | uncC->set_sampling_type(sampling_mode_422); |
712 | 0 | uncC->set_interleave_type(interleave_mode_component); |
713 | 0 | } |
714 | 0 | else if (profile == fourcc("yv22")) { |
715 | | // {'yv22', [{1,7},{3,7},{2,7}], 1, 0} planar YCrCb |
716 | | // cmpd: Y(0) Cr(1) Cb(2) |
717 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
718 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
719 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
720 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
721 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
722 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
723 | 0 | uncC->set_sampling_type(sampling_mode_422); |
724 | 0 | uncC->set_interleave_type(interleave_mode_component); |
725 | 0 | } |
726 | 0 | else if (profile == fourcc("yv20")) { |
727 | | // {'yv20', [{1,7},{3,7},{2,7}], 2, 0} planar YCrCb |
728 | | // cmpd: Y(0) Cr(1) Cb(2) |
729 | 0 | uncC->add_component({0, 8, component_format_unsigned, 0}); |
730 | 0 | uncC->add_component({1, 8, component_format_unsigned, 0}); |
731 | 0 | uncC->add_component({2, 8, component_format_unsigned, 0}); |
732 | 0 | cmpd->add_component({heif_cmpd_component_type_Y}); |
733 | 0 | cmpd->add_component({heif_cmpd_component_type_Cr}); |
734 | 0 | cmpd->add_component({heif_cmpd_component_type_Cb}); |
735 | 0 | uncC->set_sampling_type(sampling_mode_420); |
736 | 0 | uncC->set_interleave_type(interleave_mode_component); |
737 | 0 | } |
738 | 0 | else { |
739 | 0 | cmpd.reset(); |
740 | 0 | return; |
741 | 0 | } |
742 | | |
743 | 1.07k | uncC->set_synthetic_cmpd(cmpd); |
744 | 1.07k | } |
745 | | |
746 | | |
747 | | Error Box_cmpC::parse(BitstreamRange& range, const heif_security_limits* limits) |
748 | 1.06k | { |
749 | 1.06k | parse_full_box_header(range); |
750 | | |
751 | 1.06k | if (get_version() != 0) { |
752 | 1 | return unsupported_version_error("cmpC"); |
753 | 1 | } |
754 | | |
755 | 1.06k | m_compression_type = range.read32(); |
756 | | |
757 | 1.06k | uint8_t unit_type = range.read8(); |
758 | 1.06k | if (unit_type > heif_cmpC_compressed_unit_type_image_pixel) { |
759 | 1 | return {heif_error_Invalid_input, |
760 | 1 | heif_suberror_Unsupported_parameter, |
761 | 1 | "Unsupported cmpC compressed unit type"}; |
762 | 1.06k | }; |
763 | | |
764 | 1.06k | m_compressed_unit_type = static_cast<heif_cmpC_compressed_unit_type>(unit_type); |
765 | | |
766 | 1.06k | return range.get_error(); |
767 | 1.06k | } |
768 | | |
769 | | |
770 | | void Box_cmpC::dump(std::ostream& sstr, Indent& indent, bool full_log) const |
771 | 0 | { |
772 | 0 | Box::dump(sstr, indent, full_log); |
773 | 0 | sstr << indent << "compression_type: " << fourcc_to_string(m_compression_type) << "\n"; |
774 | 0 | sstr << indent << "compressed_entity_type: " << (int)m_compressed_unit_type << "\n"; |
775 | 0 | return; |
776 | 0 | } |
777 | | |
778 | | Error Box_cmpC::write(StreamWriter& writer) const |
779 | 0 | { |
780 | 0 | size_t box_start = reserve_box_header_space(writer); |
781 | |
|
782 | 0 | writer.write32(m_compression_type); |
783 | 0 | writer.write8(m_compressed_unit_type); |
784 | |
|
785 | 0 | prepend_header(writer, box_start); |
786 | |
|
787 | 0 | return Error::Ok; |
788 | 0 | } |
789 | | |
790 | | |
791 | | static uint8_t unit_offset_bits_table[] = {0, 16, 24, 32, 64 }; |
792 | | static uint8_t unit_size_bits_table[] = {8, 16, 24, 32, 64 }; |
793 | | |
794 | | Error Box_icef::parse(BitstreamRange& range, const heif_security_limits* limits) |
795 | 50 | { |
796 | 50 | parse_full_box_header(range); |
797 | | |
798 | 50 | if (get_version() != 0) { |
799 | 1 | return unsupported_version_error("icef"); |
800 | 1 | } |
801 | 49 | uint8_t codes = range.read8(); |
802 | 49 | uint8_t unit_offset_code = (codes & 0b11100000) >> 5; |
803 | 49 | uint8_t unit_size_code = (codes & 0b00011100) >> 2; |
804 | 49 | uint32_t num_compressed_units = range.read32(); |
805 | 49 | uint64_t implied_offset = 0; |
806 | | |
807 | 49 | if (unit_offset_code > 4) { |
808 | 1 | return {heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported icef unit offset code"}; |
809 | 1 | } |
810 | | |
811 | 48 | if (unit_size_code > 4) { |
812 | 1 | return {heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported icef unit size code"}; |
813 | 1 | } |
814 | | |
815 | | // --- precompute fields lengths |
816 | | |
817 | 47 | uint8_t unit_offset_bits = unit_offset_bits_table[unit_offset_code]; |
818 | 47 | uint8_t unit_size_bits = unit_size_bits_table[unit_size_code]; |
819 | | |
820 | | // --- check if box is large enough for all the data |
821 | | |
822 | | // range.get_remaining_bytes() is bounded by the real file size: Box::read() rejects any box whose |
823 | | // declared size exceeds either the bytes actually available in the file (wait_for_available_bytes()) |
824 | | // or the parent box's remaining bytes. So this check already prevents an unbounded allocation: |
825 | | // num_compressed_units cannot exceed the bytes present in the file. |
826 | | // It is, however, not sufficient on its own. With unit_offset_code==0 and unit_size_code==0 a unit |
827 | | // occupies only 1 byte in the file but allocates a 16-byte CompressedUnitInfo, a ~16x amplification |
828 | | // that bypasses the configured memory budget. The alloc() below therefore additionally enforces the |
829 | | // security limits (mirroring Box_snuc). |
830 | | |
831 | 47 | uint64_t data_size_bytes = static_cast<uint64_t>(num_compressed_units) * (unit_offset_bits + unit_size_bits) / 8; |
832 | 47 | if (data_size_bytes > range.get_remaining_bytes()) { |
833 | 7 | uint64_t contained_units = range.get_remaining_bytes() / ((unit_offset_bits + unit_size_bits) * 8); |
834 | 7 | std::stringstream sstr; |
835 | 7 | sstr << "icef box declares " << num_compressed_units << " units, but only " << contained_units |
836 | 7 | << " were contained in the file"; |
837 | 7 | return {heif_error_Invalid_input, |
838 | 7 | heif_suberror_End_of_data, |
839 | 7 | sstr.str()}; |
840 | 7 | } |
841 | | |
842 | | // --- read box content |
843 | | |
844 | 40 | if (auto err = m_memory_handle.alloc(num_compressed_units, sizeof(CompressedUnitInfo), |
845 | 40 | limits, "icef box compressed unit infos")) { |
846 | 0 | return err; |
847 | 0 | } |
848 | | |
849 | 40 | m_unit_infos.resize(num_compressed_units); |
850 | | |
851 | 915 | for (uint32_t r = 0; r < num_compressed_units; r++) { |
852 | 884 | CompressedUnitInfo unitInfo; |
853 | 884 | if (unit_offset_code == 0) { |
854 | 432 | unitInfo.unit_offset = implied_offset; |
855 | 452 | } else { |
856 | 452 | unitInfo.unit_offset = range.read_uint(unit_offset_bits); |
857 | 452 | } |
858 | | |
859 | 884 | unitInfo.unit_size = range.read_uint(unit_size_bits); |
860 | | |
861 | | // Reject unit_offset + unit_size wrapping past the 64 bit range. |
862 | 884 | if (unitInfo.unit_size >= UINT64_MAX - unitInfo.unit_offset) { |
863 | 9 | return {heif_error_Invalid_input, |
864 | 9 | heif_suberror_Invalid_parameter_value, |
865 | 9 | "icef unit offset + size exceeds 64 bit range"}; |
866 | 9 | } |
867 | | |
868 | | // implied_offset is only used as unit_offset when unit_offset_code==0. |
869 | | // Accumulating it for other offset codes would be unused and could overflow, so only do it if needed. |
870 | 875 | if (unit_offset_code == 0) { |
871 | 428 | implied_offset += unitInfo.unit_size; |
872 | 428 | } |
873 | | |
874 | 875 | if (range.get_error() != Error::Ok) { |
875 | 0 | return range.get_error(); |
876 | 0 | } |
877 | | |
878 | 875 | m_unit_infos[r] = unitInfo; |
879 | 875 | } |
880 | | |
881 | 31 | return range.get_error(); |
882 | 40 | } |
883 | | |
884 | | |
885 | | void Box_icef::dump(std::ostream& sstr, Indent& indent, bool full_log) const |
886 | 0 | { |
887 | 0 | Box::dump(sstr, indent, full_log); |
888 | 0 | sstr << indent << "num_compressed_units: " << m_unit_infos.size() << "\n"; |
889 | 0 | for (CompressedUnitInfo unit_info: m_unit_infos) { |
890 | 0 | sstr << indent << "unit_offset: " << unit_info.unit_offset << ", unit_size: " << unit_info.unit_size << "\n"; |
891 | 0 | } |
892 | 0 | return; |
893 | 0 | } |
894 | | |
895 | | Error Box_icef::write(StreamWriter& writer) const |
896 | 0 | { |
897 | | // check that all units have a non-zero size |
898 | |
|
899 | 0 | for (const CompressedUnitInfo& unit_info: m_unit_infos) { |
900 | 0 | if (unit_info.unit_size == 0) { |
901 | 0 | return { |
902 | 0 | heif_error_Usage_error, |
903 | 0 | heif_suberror_Unspecified, |
904 | 0 | "tiled 'unci' image has an undefined tile." |
905 | 0 | }; |
906 | 0 | } |
907 | 0 | } |
908 | | |
909 | 0 | size_t box_start = reserve_box_header_space(writer); |
910 | |
|
911 | 0 | uint8_t unit_offset_code = 1; |
912 | 0 | uint8_t unit_size_code = 0; |
913 | 0 | uint64_t implied_offset = 0; |
914 | 0 | bool can_use_implied_offsets = true; |
915 | 0 | for (const CompressedUnitInfo& unit_info: m_unit_infos) { |
916 | 0 | if (unit_info.unit_offset != implied_offset) { |
917 | 0 | can_use_implied_offsets = false; |
918 | 0 | } |
919 | 0 | if (unit_info.unit_size > (std::numeric_limits<uint64_t>::max() - implied_offset)) { |
920 | 0 | can_use_implied_offsets = false; |
921 | 0 | } else { |
922 | 0 | implied_offset += unit_info.unit_size; |
923 | 0 | } |
924 | 0 | uint8_t required_offset_code = get_required_offset_code(unit_info.unit_offset); |
925 | 0 | if (required_offset_code > unit_offset_code) { |
926 | 0 | unit_offset_code = required_offset_code; |
927 | 0 | } |
928 | 0 | uint8_t required_size_code = get_required_size_code(unit_info.unit_size); |
929 | 0 | if (required_size_code > unit_size_code) { |
930 | 0 | unit_size_code = required_size_code; |
931 | 0 | } |
932 | 0 | } |
933 | 0 | if (can_use_implied_offsets) { |
934 | 0 | unit_offset_code = 0; |
935 | 0 | } |
936 | 0 | uint8_t code_bits = (uint8_t)((unit_offset_code << 5) | (unit_size_code << 2)); |
937 | 0 | writer.write8(code_bits); |
938 | 0 | writer.write32((uint32_t)m_unit_infos.size()); |
939 | 0 | for (CompressedUnitInfo unit_info: m_unit_infos) { |
940 | 0 | if (unit_offset_code == 0) { |
941 | | // nothing |
942 | 0 | } else if (unit_offset_code == 1) { |
943 | 0 | writer.write16((uint16_t)unit_info.unit_offset); |
944 | 0 | } else if (unit_offset_code == 2) { |
945 | 0 | writer.write24((uint32_t)unit_info.unit_offset); |
946 | 0 | } else if (unit_offset_code == 3) { |
947 | 0 | writer.write32((uint32_t)unit_info.unit_offset); |
948 | 0 | } else { |
949 | 0 | writer.write64(unit_info.unit_offset); |
950 | 0 | } |
951 | 0 | if (unit_size_code == 0) { |
952 | 0 | writer.write8((uint8_t)unit_info.unit_size); |
953 | 0 | } else if (unit_size_code == 1) { |
954 | 0 | writer.write16((uint16_t)unit_info.unit_size); |
955 | 0 | } else if (unit_size_code == 2) { |
956 | 0 | writer.write24((uint32_t)unit_info.unit_size); |
957 | 0 | } else if (unit_size_code == 3) { |
958 | 0 | writer.write32((uint32_t)unit_info.unit_size); |
959 | 0 | } else { |
960 | 0 | writer.write64(unit_info.unit_size); |
961 | 0 | } |
962 | 0 | } |
963 | 0 | prepend_header(writer, box_start); |
964 | |
|
965 | 0 | return Error::Ok; |
966 | 0 | } |
967 | | |
968 | | static uint64_t MAX_OFFSET_UNIT_CODE_1 = std::numeric_limits<uint16_t>::max(); |
969 | | static uint64_t MAX_OFFSET_UNIT_CODE_2 = (1ULL << 24) - 1; |
970 | | static uint64_t MAX_OFFSET_UNIT_CODE_3 = std::numeric_limits<uint32_t>::max(); |
971 | | |
972 | | const uint8_t Box_icef::get_required_offset_code(uint64_t offset) const |
973 | 0 | { |
974 | 0 | if (offset <= MAX_OFFSET_UNIT_CODE_1) { |
975 | 0 | return 1; |
976 | 0 | } |
977 | 0 | if (offset <= MAX_OFFSET_UNIT_CODE_2) { |
978 | 0 | return 2; |
979 | 0 | } |
980 | 0 | if (offset <= MAX_OFFSET_UNIT_CODE_3) { |
981 | 0 | return 3; |
982 | 0 | } |
983 | 0 | return 4; |
984 | 0 | } |
985 | | |
986 | | static uint64_t MAX_SIZE_UNIT_CODE_0 = std::numeric_limits<uint8_t>::max(); |
987 | | static uint64_t MAX_SIZE_UNIT_CODE_1 = std::numeric_limits<uint16_t>::max(); |
988 | | static uint64_t MAX_SIZE_UNIT_CODE_2 = (1ULL << 24) - 1; |
989 | | static uint64_t MAX_SIZE_UNIT_CODE_3 = std::numeric_limits<uint32_t>::max(); |
990 | | |
991 | | const uint8_t Box_icef::get_required_size_code(uint64_t size) const |
992 | 0 | { |
993 | 0 | if (size <= MAX_SIZE_UNIT_CODE_0) { |
994 | 0 | return 0; |
995 | 0 | } |
996 | 0 | if (size <= MAX_SIZE_UNIT_CODE_1) { |
997 | 0 | return 1; |
998 | 0 | } |
999 | 0 | if (size <= MAX_SIZE_UNIT_CODE_2) { |
1000 | 0 | return 2; |
1001 | 0 | } |
1002 | 0 | if (size <= MAX_SIZE_UNIT_CODE_3) { |
1003 | 0 | return 3; |
1004 | 0 | } |
1005 | 0 | return 4; |
1006 | 0 | } |
1007 | | |
1008 | | |
1009 | | Error Box_cpat::parse(BitstreamRange& range, const heif_security_limits* limits) |
1010 | 18 | { |
1011 | 18 | parse_full_box_header(range); |
1012 | | |
1013 | 18 | if (get_version() != 0) { |
1014 | 1 | return unsupported_version_error("cpat"); |
1015 | 1 | } |
1016 | | |
1017 | 17 | m_pattern.pattern_width = range.read16(); |
1018 | 17 | m_pattern.pattern_height = range.read16(); |
1019 | | |
1020 | 17 | if (m_pattern.pattern_width == 0 || m_pattern.pattern_height == 0) { |
1021 | 2 | return {heif_error_Invalid_input, |
1022 | 2 | heif_suberror_Invalid_parameter_value, |
1023 | 2 | "Zero Bayer pattern size."}; |
1024 | 2 | } |
1025 | | |
1026 | 15 | auto max_bayer_pattern_size = limits->max_bayer_pattern_pixels; |
1027 | 15 | if (max_bayer_pattern_size && m_pattern.pattern_height > max_bayer_pattern_size / m_pattern.pattern_width) { |
1028 | 3 | return {heif_error_Invalid_input, |
1029 | 3 | heif_suberror_Security_limit_exceeded, |
1030 | 3 | "Maximum Bayer pattern size exceeded."}; |
1031 | 3 | } |
1032 | | |
1033 | 12 | size_t num_pixels = size_t{m_pattern.pattern_width} * m_pattern.pattern_height; |
1034 | 12 | m_pattern.pixels.resize(num_pixels); |
1035 | | |
1036 | 1.10k | for (size_t i = 0; i < num_pixels; i++) { |
1037 | 1.09k | BayerPatternPixelCmpd pixel{}; |
1038 | 1.09k | pixel.cmpd_index = range.read32(); |
1039 | 1.09k | pixel.component_gain = range.read_float32(); |
1040 | 1.09k | m_pattern.pixels[i] = pixel; |
1041 | 1.09k | } |
1042 | | |
1043 | 12 | return range.get_error(); |
1044 | 15 | } |
1045 | | |
1046 | | |
1047 | | void Box_cpat::dump(std::ostream& sstr, Indent& indent, bool full_log) const |
1048 | 0 | { |
1049 | |
|
1050 | 0 | FullBox::dump(sstr, indent, full_log); |
1051 | 0 | sstr << indent << "pattern_width: " << get_pattern_width() << "\n"; |
1052 | 0 | sstr << indent << "pattern_height: " << get_pattern_height() << "\n"; |
1053 | |
|
1054 | 0 | for (const auto& pixel : m_pattern.pixels) { |
1055 | 0 | sstr << indent << "component index: " << pixel.cmpd_index << ", gain: " << pixel.component_gain << "\n"; |
1056 | 0 | } |
1057 | 0 | return; |
1058 | 0 | } |
1059 | | |
1060 | | |
1061 | | Error Box_cpat::write(StreamWriter& writer) const |
1062 | 0 | { |
1063 | 0 | size_t box_start = reserve_box_header_space(writer); |
1064 | |
|
1065 | 0 | if (m_pattern.pattern_width * size_t{m_pattern.pattern_height} != m_pattern.pixels.size()) { |
1066 | 0 | return {heif_error_Usage_error, |
1067 | 0 | heif_suberror_Invalid_parameter_value, |
1068 | 0 | "incorrect number of pattern components"}; |
1069 | 0 | } |
1070 | | |
1071 | 0 | writer.write16(m_pattern.pattern_width); |
1072 | 0 | writer.write16(m_pattern.pattern_height); |
1073 | |
|
1074 | 0 | for (const auto& pixel : m_pattern.pixels) { |
1075 | 0 | writer.write32(pixel.cmpd_index); |
1076 | 0 | writer.write_float32(pixel.component_gain); |
1077 | 0 | } |
1078 | |
|
1079 | 0 | prepend_header(writer, box_start); |
1080 | |
|
1081 | 0 | return Error::Ok; |
1082 | 0 | } |
1083 | | |
1084 | | |
1085 | | Error Box_splz::parse(BitstreamRange& range, const heif_security_limits* limits) |
1086 | 26 | { |
1087 | 26 | parse_full_box_header(range); |
1088 | | |
1089 | 26 | if (get_version() != 0) { |
1090 | 1 | return unsupported_version_error("splz"); |
1091 | 1 | } |
1092 | | |
1093 | 25 | uint32_t component_count = range.read32(); |
1094 | 25 | if (limits->max_components && component_count > limits->max_components) { |
1095 | 1 | return {heif_error_Invalid_input, |
1096 | 1 | heif_suberror_Security_limit_exceeded, |
1097 | 1 | "Number of components in splz box exceeds the security limits."}; |
1098 | 1 | } |
1099 | | |
1100 | 24 | m_pattern.component_ids.resize(component_count); |
1101 | 674 | for (uint32_t i = 0; i < component_count; i++) { |
1102 | 650 | m_pattern.component_ids[i] = range.read32(); |
1103 | 650 | } |
1104 | | |
1105 | 24 | m_pattern.pattern_width = range.read16(); |
1106 | 24 | m_pattern.pattern_height = range.read16(); |
1107 | | |
1108 | 24 | if (m_pattern.pattern_width == 0 || m_pattern.pattern_height == 0) { |
1109 | 6 | return {heif_error_Invalid_input, |
1110 | 6 | heif_suberror_Invalid_parameter_value, |
1111 | 6 | "Zero polarization pattern size."}; |
1112 | 6 | } |
1113 | | |
1114 | 18 | auto max_pattern_size = limits->max_bayer_pattern_pixels; |
1115 | 18 | if (max_pattern_size && m_pattern.pattern_height > max_pattern_size / m_pattern.pattern_width) { |
1116 | 4 | return {heif_error_Invalid_input, |
1117 | 4 | heif_suberror_Security_limit_exceeded, |
1118 | 4 | "Maximum polarization pattern size exceeded."}; |
1119 | 4 | } |
1120 | | |
1121 | 14 | size_t num_pixels = size_t{m_pattern.pattern_width} * m_pattern.pattern_height; |
1122 | 14 | m_pattern.polarization_angles.resize(num_pixels); |
1123 | | |
1124 | 1.05k | for (size_t i = 0; i < num_pixels; i++) { |
1125 | 1.04k | m_pattern.polarization_angles[i] = range.read_float32(); |
1126 | 1.04k | } |
1127 | | |
1128 | 14 | return range.get_error(); |
1129 | 18 | } |
1130 | | |
1131 | | |
1132 | | void Box_splz::dump(std::ostream& sstr, Indent& indent, bool full_log) const |
1133 | 0 | { |
1134 | |
|
1135 | 0 | FullBox::dump(sstr, indent, full_log); |
1136 | |
|
1137 | 0 | sstr << indent << "component_count: " << m_pattern.component_ids.size() << "\n"; |
1138 | 0 | for (size_t i = 0; i < m_pattern.component_ids.size(); i++) { |
1139 | 0 | sstr << indent << " component_index[" << i << "]: " << m_pattern.component_ids[i] << "\n"; |
1140 | 0 | } |
1141 | |
|
1142 | 0 | sstr << indent << "pattern_width: " << m_pattern.pattern_width << "\n"; |
1143 | 0 | sstr << indent << "pattern_height: " << m_pattern.pattern_height << "\n"; |
1144 | |
|
1145 | 0 | for (uint16_t y = 0; y < m_pattern.pattern_height; y++) { |
1146 | 0 | for (uint16_t x = 0; x < m_pattern.pattern_width; x++) { |
1147 | 0 | float angle = m_pattern.polarization_angles[y * m_pattern.pattern_width + x]; |
1148 | 0 | if (heif_polarization_angle_is_no_filter(angle)) { |
1149 | 0 | sstr << indent << " [" << x << "," << y << "]: no filter\n"; |
1150 | 0 | } |
1151 | 0 | else { |
1152 | 0 | sstr << indent << " [" << x << "," << y << "]: " << angle << " degrees\n"; |
1153 | 0 | } |
1154 | 0 | } |
1155 | 0 | } |
1156 | |
|
1157 | 0 | return; |
1158 | 0 | } |
1159 | | |
1160 | | |
1161 | | Error Box_splz::write(StreamWriter& writer) const |
1162 | 0 | { |
1163 | 0 | size_t box_start = reserve_box_header_space(writer); |
1164 | |
|
1165 | 0 | if (m_pattern.pattern_width * size_t{m_pattern.pattern_height} != m_pattern.polarization_angles.size()) { |
1166 | 0 | return {heif_error_Usage_error, |
1167 | 0 | heif_suberror_Invalid_parameter_value, |
1168 | 0 | "incorrect number of polarization pattern angles"}; |
1169 | 0 | } |
1170 | | |
1171 | 0 | writer.write32(static_cast<uint32_t>(m_pattern.component_ids.size())); |
1172 | 0 | for (uint32_t idx : m_pattern.component_ids) { |
1173 | 0 | writer.write32(idx); |
1174 | 0 | } |
1175 | |
|
1176 | 0 | writer.write16(m_pattern.pattern_width); |
1177 | 0 | writer.write16(m_pattern.pattern_height); |
1178 | |
|
1179 | 0 | for (float angle : m_pattern.polarization_angles) { |
1180 | 0 | writer.write_float32(angle); |
1181 | 0 | } |
1182 | |
|
1183 | 0 | prepend_header(writer, box_start); |
1184 | |
|
1185 | 0 | return Error::Ok; |
1186 | 0 | } |
1187 | | |
1188 | | |
1189 | | Error Box_sbpm::parse(BitstreamRange& range, const heif_security_limits* limits) |
1190 | 0 | { |
1191 | 0 | parse_full_box_header(range); |
1192 | |
|
1193 | 0 | if (get_version() != 0) { |
1194 | 0 | return unsupported_version_error("sbpm"); |
1195 | 0 | } |
1196 | | |
1197 | 0 | uint32_t component_count = range.read32(); |
1198 | |
|
1199 | 0 | if (limits->max_components && component_count > limits->max_components) { |
1200 | 0 | return {heif_error_Invalid_input, |
1201 | 0 | heif_suberror_Security_limit_exceeded, |
1202 | 0 | "sbpm component_count exceeds security limit."}; |
1203 | 0 | } |
1204 | | |
1205 | 0 | m_map.component_ids.resize(component_count); |
1206 | 0 | for (uint32_t i = 0; i < component_count; i++) { |
1207 | 0 | m_map.component_ids[i] = range.read32(); |
1208 | 0 | } |
1209 | |
|
1210 | 0 | uint8_t flags = range.read8(); |
1211 | 0 | m_map.correction_applied = !!(flags & 0x80); |
1212 | |
|
1213 | 0 | uint32_t num_bad_rows = range.read32(); |
1214 | 0 | uint32_t num_bad_cols = range.read32(); |
1215 | 0 | uint32_t num_bad_pixels = range.read32(); |
1216 | | |
1217 | | // Security check: limit total number of entries |
1218 | 0 | uint64_t total_entries = static_cast<uint64_t>(num_bad_rows) + num_bad_cols + num_bad_pixels; |
1219 | 0 | if (limits->max_bad_pixels && total_entries > limits->max_bad_pixels) { |
1220 | 0 | return {heif_error_Invalid_input, |
1221 | 0 | heif_suberror_Security_limit_exceeded, |
1222 | 0 | "sbpm total bad pixel entries exceed security limit."}; |
1223 | 0 | } |
1224 | | |
1225 | 0 | m_map.bad_rows.resize(num_bad_rows); |
1226 | 0 | for (uint32_t i = 0; i < num_bad_rows; i++) { |
1227 | 0 | m_map.bad_rows[i] = range.read32(); |
1228 | 0 | } |
1229 | |
|
1230 | 0 | m_map.bad_columns.resize(num_bad_cols); |
1231 | 0 | for (uint32_t i = 0; i < num_bad_cols; i++) { |
1232 | 0 | m_map.bad_columns[i] = range.read32(); |
1233 | 0 | } |
1234 | |
|
1235 | 0 | m_map.bad_pixels.resize(num_bad_pixels); |
1236 | 0 | for (uint32_t i = 0; i < num_bad_pixels; i++) { |
1237 | 0 | m_map.bad_pixels[i].row = range.read32(); |
1238 | 0 | m_map.bad_pixels[i].column = range.read32(); |
1239 | 0 | } |
1240 | |
|
1241 | 0 | return range.get_error(); |
1242 | 0 | } |
1243 | | |
1244 | | |
1245 | | void Box_sbpm::dump(std::ostream& sstr, Indent& indent, bool full_log) const |
1246 | 0 | { |
1247 | |
|
1248 | 0 | FullBox::dump(sstr, indent, full_log); |
1249 | |
|
1250 | 0 | sstr << indent << "component_count: " << m_map.component_ids.size() << "\n"; |
1251 | 0 | for (size_t i = 0; i < m_map.component_ids.size(); i++) { |
1252 | 0 | sstr << indent << " component_index[" << i << "]: " << m_map.component_ids[i] << "\n"; |
1253 | 0 | } |
1254 | |
|
1255 | 0 | sstr << indent << "correction_applied: " << m_map.correction_applied << "\n"; |
1256 | |
|
1257 | 0 | sstr << indent << "num_bad_rows: " << m_map.bad_rows.size() << "\n"; |
1258 | 0 | for (size_t i = 0; i < m_map.bad_rows.size(); i++) { |
1259 | 0 | sstr << indent << " bad_row[" << i << "]: " << m_map.bad_rows[i] << "\n"; |
1260 | 0 | } |
1261 | |
|
1262 | 0 | sstr << indent << "num_bad_columns: " << m_map.bad_columns.size() << "\n"; |
1263 | 0 | for (size_t i = 0; i < m_map.bad_columns.size(); i++) { |
1264 | 0 | sstr << indent << " bad_column[" << i << "]: " << m_map.bad_columns[i] << "\n"; |
1265 | 0 | } |
1266 | |
|
1267 | 0 | sstr << indent << "num_bad_pixels: " << m_map.bad_pixels.size() << "\n"; |
1268 | 0 | for (size_t i = 0; i < m_map.bad_pixels.size(); i++) { |
1269 | 0 | sstr << indent << " bad_pixel[" << i << "]: row=" << m_map.bad_pixels[i].row |
1270 | 0 | << ", column=" << m_map.bad_pixels[i].column << "\n"; |
1271 | 0 | } |
1272 | |
|
1273 | 0 | return; |
1274 | 0 | } |
1275 | | |
1276 | | |
1277 | | Error Box_sbpm::write(StreamWriter& writer) const |
1278 | 0 | { |
1279 | 0 | size_t box_start = reserve_box_header_space(writer); |
1280 | |
|
1281 | 0 | writer.write32(static_cast<uint32_t>(m_map.component_ids.size())); |
1282 | 0 | for (uint32_t idx : m_map.component_ids) { |
1283 | 0 | writer.write32(idx); |
1284 | 0 | } |
1285 | |
|
1286 | 0 | uint8_t flags = m_map.correction_applied ? 0x80 : 0; |
1287 | 0 | writer.write8(flags); |
1288 | |
|
1289 | 0 | writer.write32(static_cast<uint32_t>(m_map.bad_rows.size())); |
1290 | 0 | writer.write32(static_cast<uint32_t>(m_map.bad_columns.size())); |
1291 | 0 | writer.write32(static_cast<uint32_t>(m_map.bad_pixels.size())); |
1292 | |
|
1293 | 0 | for (uint32_t row : m_map.bad_rows) { |
1294 | 0 | writer.write32(row); |
1295 | 0 | } |
1296 | |
|
1297 | 0 | for (uint32_t col : m_map.bad_columns) { |
1298 | 0 | writer.write32(col); |
1299 | 0 | } |
1300 | |
|
1301 | 0 | for (const auto& pixel : m_map.bad_pixels) { |
1302 | 0 | writer.write32(pixel.row); |
1303 | 0 | writer.write32(pixel.column); |
1304 | 0 | } |
1305 | |
|
1306 | 0 | prepend_header(writer, box_start); |
1307 | |
|
1308 | 0 | return Error::Ok; |
1309 | 0 | } |
1310 | | |
1311 | | |
1312 | | Error Box_snuc::parse(BitstreamRange& range, const heif_security_limits* limits) |
1313 | 63 | { |
1314 | 63 | parse_full_box_header(range); |
1315 | | |
1316 | 63 | if (get_version() != 0) { |
1317 | 1 | return unsupported_version_error("snuc"); |
1318 | 1 | } |
1319 | | |
1320 | 62 | uint32_t component_count = range.read32(); |
1321 | | |
1322 | 62 | if (limits->max_components && component_count > limits->max_components) { |
1323 | 2 | return {heif_error_Invalid_input, |
1324 | 2 | heif_suberror_Security_limit_exceeded, |
1325 | 2 | "snuc component_count exceeds security limit."}; |
1326 | 2 | } |
1327 | | |
1328 | 60 | m_nuc.component_ids.resize(component_count); |
1329 | 2.73k | for (uint32_t i = 0; i < component_count; i++) { |
1330 | 2.67k | m_nuc.component_ids[i] = range.read32(); |
1331 | 2.67k | } |
1332 | | |
1333 | 60 | uint8_t flags = range.read8(); |
1334 | 60 | m_nuc.nuc_is_applied = !!(flags & 0x80); |
1335 | | |
1336 | 60 | m_nuc.image_width = range.read32(); |
1337 | 60 | m_nuc.image_height = range.read32(); |
1338 | | |
1339 | 60 | if (m_nuc.image_width == 0 || m_nuc.image_height == 0) { |
1340 | 6 | return {heif_error_Invalid_input, |
1341 | 6 | heif_suberror_Invalid_parameter_value, |
1342 | 6 | "snuc image width and height must be non-zero."}; |
1343 | 6 | } |
1344 | | |
1345 | 54 | uint64_t num_pixels = static_cast<uint64_t>(m_nuc.image_width) * m_nuc.image_height; |
1346 | | |
1347 | 54 | if (limits->max_image_size_pixels && num_pixels > limits->max_image_size_pixels) { |
1348 | 45 | return {heif_error_Invalid_input, |
1349 | 45 | heif_suberror_Security_limit_exceeded, |
1350 | 45 | "snuc image dimensions exceed security limit."}; |
1351 | 45 | } |
1352 | | |
1353 | 9 | Error err = m_memory_handle.alloc(num_pixels, 2 * sizeof(float), limits, "snuc box"); |
1354 | 9 | if (err) { |
1355 | 1 | return err; |
1356 | 1 | } |
1357 | | |
1358 | 8 | m_nuc.nuc_gains.resize(num_pixels); |
1359 | 7.89M | for (uint64_t i = 0; i < num_pixels; i++) { |
1360 | 7.89M | m_nuc.nuc_gains[i] = range.read_float32(); |
1361 | 7.89M | } |
1362 | | |
1363 | 8 | m_nuc.nuc_offsets.resize(num_pixels); |
1364 | 7.89M | for (uint64_t i = 0; i < num_pixels; i++) { |
1365 | 7.89M | m_nuc.nuc_offsets[i] = range.read_float32(); |
1366 | 7.89M | } |
1367 | | |
1368 | 8 | return range.get_error(); |
1369 | 9 | } |
1370 | | |
1371 | | |
1372 | | void Box_snuc::dump(std::ostream& sstr, Indent& indent, bool full_log) const |
1373 | 0 | { |
1374 | |
|
1375 | 0 | FullBox::dump(sstr, indent, full_log); |
1376 | |
|
1377 | 0 | sstr << indent << "component_count: " << m_nuc.component_ids.size() << "\n"; |
1378 | 0 | for (size_t i = 0; i < m_nuc.component_ids.size(); i++) { |
1379 | 0 | sstr << indent << " component_index[" << i << "]: " << m_nuc.component_ids[i] << "\n"; |
1380 | 0 | } |
1381 | |
|
1382 | 0 | sstr << indent << "nuc_is_applied: " << m_nuc.nuc_is_applied << "\n"; |
1383 | 0 | sstr << indent << "image_width: " << m_nuc.image_width << "\n"; |
1384 | 0 | sstr << indent << "image_height: " << m_nuc.image_height << "\n"; |
1385 | |
|
1386 | 0 | uint64_t num_pixels = static_cast<uint64_t>(m_nuc.image_width) * m_nuc.image_height; |
1387 | 0 | sstr << indent << "nuc_gains: " << num_pixels << " values\n"; |
1388 | 0 | sstr << indent << "nuc_offsets: " << num_pixels << " values\n"; |
1389 | |
|
1390 | 0 | return; |
1391 | 0 | } |
1392 | | |
1393 | | |
1394 | | Error Box_snuc::write(StreamWriter& writer) const |
1395 | 0 | { |
1396 | 0 | size_t box_start = reserve_box_header_space(writer); |
1397 | |
|
1398 | 0 | writer.write32(static_cast<uint32_t>(m_nuc.component_ids.size())); |
1399 | 0 | for (uint32_t idx : m_nuc.component_ids) { |
1400 | 0 | writer.write32(idx); |
1401 | 0 | } |
1402 | |
|
1403 | 0 | uint8_t flags = m_nuc.nuc_is_applied ? 0x80 : 0; |
1404 | 0 | writer.write8(flags); |
1405 | |
|
1406 | 0 | writer.write32(m_nuc.image_width); |
1407 | 0 | writer.write32(m_nuc.image_height); |
1408 | |
|
1409 | 0 | for (float gain : m_nuc.nuc_gains) { |
1410 | 0 | writer.write_float32(gain); |
1411 | 0 | } |
1412 | |
|
1413 | 0 | for (float offset : m_nuc.nuc_offsets) { |
1414 | 0 | writer.write_float32(offset); |
1415 | 0 | } |
1416 | |
|
1417 | 0 | prepend_header(writer, box_start); |
1418 | |
|
1419 | 0 | return Error::Ok; |
1420 | 0 | } |
1421 | | |
1422 | | |
1423 | | Error Box_cloc::parse(BitstreamRange& range, const heif_security_limits* limits) |
1424 | 8 | { |
1425 | 8 | parse_full_box_header(range); |
1426 | | |
1427 | 8 | if (get_version() != 0) { |
1428 | 1 | return unsupported_version_error("cloc"); |
1429 | 1 | } |
1430 | | |
1431 | 7 | m_chroma_location = range.read8(); |
1432 | | |
1433 | 7 | if (m_chroma_location > 6) { |
1434 | 1 | return {heif_error_Invalid_input, |
1435 | 1 | heif_suberror_Invalid_parameter_value, |
1436 | 1 | "cloc chroma_location value out of range (must be 0-6)."}; |
1437 | 1 | } |
1438 | | |
1439 | 6 | return range.get_error(); |
1440 | 7 | } |
1441 | | |
1442 | | |
1443 | | void Box_cloc::dump(std::ostream& sstr, Indent& indent, bool full_log) const |
1444 | 0 | { |
1445 | |
|
1446 | 0 | FullBox::dump(sstr, indent, full_log); |
1447 | |
|
1448 | 0 | static const char* location_names[] = { |
1449 | 0 | "h=0, v=0.5", // 0 |
1450 | 0 | "h=0.5, v=0.5", // 1 |
1451 | 0 | "h=0, v=0", // 2 |
1452 | 0 | "h=0.5, v=0", // 3 |
1453 | 0 | "h=0, v=1", // 4 |
1454 | 0 | "h=0.5, v=1", // 5 |
1455 | 0 | "Cr:0,0 / Cb:1,0" // 6 |
1456 | 0 | }; |
1457 | |
|
1458 | 0 | sstr << indent << "chroma_location: " << static_cast<int>(m_chroma_location); |
1459 | 0 | if (m_chroma_location <= 6) { |
1460 | 0 | sstr << " (" << location_names[m_chroma_location] << ")"; |
1461 | 0 | } |
1462 | 0 | sstr << "\n"; |
1463 | |
|
1464 | 0 | return; |
1465 | 0 | } |
1466 | | |
1467 | | |
1468 | | Error Box_cloc::write(StreamWriter& writer) const |
1469 | 0 | { |
1470 | 0 | size_t box_start = reserve_box_header_space(writer); |
1471 | |
|
1472 | 0 | writer.write8(m_chroma_location); |
1473 | |
|
1474 | 0 | prepend_header(writer, box_start); |
1475 | |
|
1476 | 0 | return Error::Ok; |
1477 | 0 | } |
1478 | | |
1479 | | |
1480 | | Error Box_gimi_component_content_ids::parse(BitstreamRange& range, const heif_security_limits* limits) |
1481 | 26 | { |
1482 | 26 | uint32_t number_of_components = range.read32(); |
1483 | | |
1484 | 26 | if (limits->max_components && number_of_components > limits->max_components) { |
1485 | 10 | std::stringstream sstr; |
1486 | 10 | sstr << "GIMI component content IDs box contains " << number_of_components |
1487 | 10 | << " components, but security limit is set to " << limits->max_components << " components"; |
1488 | 10 | return {heif_error_Invalid_input, |
1489 | 10 | heif_suberror_Security_limit_exceeded, |
1490 | 10 | sstr.str()}; |
1491 | 10 | } |
1492 | | |
1493 | 377 | for (uint32_t i = 0; i < number_of_components; i++) { |
1494 | 374 | if (range.eof()) { |
1495 | 13 | return {heif_error_Invalid_input, |
1496 | 13 | heif_suberror_End_of_data, |
1497 | 13 | "Not enough data for all component content IDs"}; |
1498 | 13 | } |
1499 | 361 | m_content_ids.push_back(range.read_string()); |
1500 | 361 | } |
1501 | | |
1502 | 3 | return range.get_error(); |
1503 | 16 | } |
1504 | | |
1505 | | |
1506 | | Error Box_gimi_component_content_ids::write(StreamWriter& writer) const |
1507 | 0 | { |
1508 | 0 | size_t box_start = reserve_box_header_space(writer); |
1509 | |
|
1510 | 0 | writer.write32(static_cast<uint32_t>(m_content_ids.size())); |
1511 | |
|
1512 | 0 | for (const auto& id : m_content_ids) { |
1513 | 0 | writer.write(id); |
1514 | 0 | } |
1515 | |
|
1516 | 0 | prepend_header(writer, box_start); |
1517 | |
|
1518 | 0 | return Error::Ok; |
1519 | 0 | } |
1520 | | |
1521 | | |
1522 | | void Box_gimi_component_content_ids::dump(std::ostream& sstr, Indent& indent, bool full_log) const |
1523 | 0 | { |
1524 | 0 | Box::dump(sstr, indent, full_log); |
1525 | |
|
1526 | 0 | for (size_t i = 0; i < m_content_ids.size(); i++) { |
1527 | 0 | sstr << indent << "[" << i << "] content ID: " << m_content_ids[i] << "\n"; |
1528 | 0 | } |
1529 | |
|
1530 | 0 | return; |
1531 | 0 | } |