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