/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.36k | { |
44 | 2.36k | return format <= component_format_max_valid; |
45 | 2.36k | } |
46 | | |
47 | | static std::map<heif_uncompressed_component_format, const char*> sNames_uncompressed_component_format{ |
48 | | {component_format_unsigned, "unsigned"}, |
49 | | {component_format_float, "float"}, |
50 | | {component_format_complex, "complex"} |
51 | | }; |
52 | | |
53 | | |
54 | | /** |
55 | | * Check for valid interleave mode. |
56 | | * |
57 | | * @param interleave the interleave value to check |
58 | | * @return true if the interleave mode is valid, or false otherwise |
59 | | */ |
60 | | bool is_valid_interleave_mode(uint8_t interleave) |
61 | 884 | { |
62 | 884 | return interleave <= interleave_mode_max_valid; |
63 | 884 | } |
64 | | |
65 | | static std::map<heif_uncompressed_interleave_mode, const char*> sNames_uncompressed_interleave_mode{ |
66 | | {interleave_mode_component, "component"}, |
67 | | {interleave_mode_pixel, "pixel"}, |
68 | | {interleave_mode_mixed, "mixed"}, |
69 | | {interleave_mode_row, "row"}, |
70 | | {interleave_mode_tile_component, "tile-component"}, |
71 | | {interleave_mode_multi_y, "multi-y"} |
72 | | }; |
73 | | |
74 | | |
75 | | /** |
76 | | * Check for valid sampling mode. |
77 | | * |
78 | | * @param sampling the sampling value to check |
79 | | * @return true if the sampling mode is valid, or false otherwise |
80 | | */ |
81 | | bool is_valid_sampling_mode(uint8_t sampling) |
82 | 884 | { |
83 | 884 | return sampling <= sampling_mode_max_valid; |
84 | 884 | } |
85 | | |
86 | | static std::map<heif_uncompressed_sampling_mode, const char*> sNames_uncompressed_sampling_mode{ |
87 | | {sampling_mode_no_subsampling, "no subsampling"}, |
88 | | {sampling_mode_422, "4:2:2"}, |
89 | | {sampling_mode_420, "4:2:0"}, |
90 | | {sampling_mode_411, "4:1:1"} |
91 | | }; |
92 | | |
93 | | |
94 | | bool is_predefined_component_type(uint16_t type) |
95 | 7.01k | { |
96 | | // check whether the component type can be mapped to heif_uncompressed_component_type and we have a name defined for |
97 | | // it in sNames_uncompressed_component_type. |
98 | 7.01k | return type <= component_type_max_valid; |
99 | 7.01k | } |
100 | | |
101 | | static std::map<heif_uncompressed_component_type, const char*> sNames_uncompressed_component_type{ |
102 | | {component_type_monochrome, "monochrome"}, |
103 | | {component_type_Y, "Y"}, |
104 | | {component_type_Cb, "Cb"}, |
105 | | {component_type_Cr, "Cr"}, |
106 | | {component_type_red, "red"}, |
107 | | {component_type_green, "green"}, |
108 | | {component_type_blue, "blue"}, |
109 | | {component_type_alpha, "alpha"}, |
110 | | {component_type_depth, "depth"}, |
111 | | {component_type_disparity, "disparity"}, |
112 | | {component_type_palette, "palette"}, |
113 | | {component_type_filter_array, "filter-array"}, |
114 | | {component_type_padded, "padded"}, |
115 | | {component_type_cyan, "cyan"}, |
116 | | {component_type_magenta, "magenta"}, |
117 | | {component_type_yellow, "yellow"}, |
118 | | {component_type_key_black, "key (black)"} |
119 | | }; |
120 | | |
121 | | template <typename T> const char* get_name(T val, const std::map<T, const char*>& table) |
122 | 5.96k | { |
123 | 5.96k | auto iter = table.find(val); |
124 | 5.96k | if (iter == table.end()) { |
125 | 0 | return "unknown"; |
126 | 0 | } |
127 | 5.96k | else { |
128 | 5.96k | return iter->second; |
129 | 5.96k | } |
130 | 5.96k | } 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&) Line | Count | Source | 122 | 2.84k | { | 123 | 2.84k | auto iter = table.find(val); | 124 | 2.84k | if (iter == table.end()) { | 125 | 0 | return "unknown"; | 126 | 0 | } | 127 | 2.84k | else { | 128 | 2.84k | return iter->second; | 129 | 2.84k | } | 130 | 2.84k | } |
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&) Line | Count | Source | 122 | 1.66k | { | 123 | 1.66k | auto iter = table.find(val); | 124 | 1.66k | if (iter == table.end()) { | 125 | 0 | return "unknown"; | 126 | 0 | } | 127 | 1.66k | else { | 128 | 1.66k | return iter->second; | 129 | 1.66k | } | 130 | 1.66k | } |
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&) Line | Count | Source | 122 | 725 | { | 123 | 725 | auto iter = table.find(val); | 124 | 725 | if (iter == table.end()) { | 125 | 0 | return "unknown"; | 126 | 0 | } | 127 | 725 | else { | 128 | 725 | return iter->second; | 129 | 725 | } | 130 | 725 | } |
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&) Line | Count | Source | 122 | 725 | { | 123 | 725 | auto iter = table.find(val); | 124 | 725 | if (iter == table.end()) { | 125 | 0 | return "unknown"; | 126 | 0 | } | 127 | 725 | else { | 128 | 725 | return iter->second; | 129 | 725 | } | 130 | 725 | } |
|
131 | | |
132 | | Error Box_cmpd::parse(BitstreamRange& range, const heif_security_limits* limits) |
133 | 1.17k | { |
134 | 1.17k | uint32_t component_count = range.read32(); |
135 | | |
136 | 1.17k | if (limits->max_components && component_count > limits->max_components) { |
137 | 0 | std::stringstream sstr; |
138 | 0 | sstr << "cmpd box should contain " << component_count << " components, but security limit is set to " |
139 | 0 | << limits->max_components << " components"; |
140 | |
|
141 | 0 | return {heif_error_Invalid_input, |
142 | 0 | heif_suberror_Security_limit_exceeded, |
143 | 0 | sstr.str() |
144 | 0 | }; |
145 | 0 | } |
146 | | |
147 | 11.1k | for (unsigned int i = 0; i < component_count ; i++) { |
148 | 10.0k | if (range.eof()) { |
149 | 49 | std::stringstream sstr; |
150 | 49 | sstr << "cmpd box should contain " << component_count << " components, but box only contained " |
151 | 49 | << i << " components"; |
152 | | |
153 | 49 | return {heif_error_Invalid_input, |
154 | 49 | heif_suberror_End_of_data, |
155 | 49 | sstr.str() |
156 | 49 | }; |
157 | 49 | } |
158 | | |
159 | 9.98k | Component component; |
160 | 9.98k | component.component_type = range.read16(); |
161 | 9.98k | if (component.component_type >= 0x8000) { |
162 | 2.52k | component.component_type_uri = range.read_string(); |
163 | 2.52k | } |
164 | 7.46k | else { |
165 | 7.46k | component.component_type_uri = std::string(); |
166 | 7.46k | } |
167 | 9.98k | m_components.push_back(component); |
168 | 9.98k | } |
169 | | |
170 | 1.12k | return range.get_error(); |
171 | 1.17k | } |
172 | | |
173 | | std::string Box_cmpd::Component::get_component_type_name(uint16_t component_type) |
174 | 7.01k | { |
175 | 7.01k | std::stringstream sstr; |
176 | | |
177 | 7.01k | if (is_predefined_component_type(component_type)) { |
178 | 2.84k | sstr << get_name(heif_uncompressed_component_type(component_type), sNames_uncompressed_component_type) << "\n"; |
179 | 2.84k | } |
180 | 4.17k | else { |
181 | 4.17k | sstr << "0x" << std::hex << component_type << std::dec << "\n"; |
182 | 4.17k | } |
183 | | |
184 | 7.01k | return sstr.str(); |
185 | 7.01k | } |
186 | | |
187 | | |
188 | | bool Box_cmpd::has_component(heif_uncompressed_component_type type) const |
189 | 0 | { |
190 | 0 | return std::any_of(m_components.begin(), m_components.end(), |
191 | 0 | [type](const auto& cmp) { return cmp.component_type == type; }); |
192 | 0 | } |
193 | | |
194 | | |
195 | | std::string Box_cmpd::dump(Indent& indent) const |
196 | 828 | { |
197 | 828 | std::ostringstream sstr; |
198 | 828 | sstr << Box::dump(indent); |
199 | | |
200 | 7.01k | for (const auto& component : m_components) { |
201 | 7.01k | sstr << indent << "component_type: " << component.get_component_type_name(); |
202 | | |
203 | 7.01k | if (component.component_type >= 0x8000) { |
204 | 1.49k | sstr << indent << "| component_type_uri: " << component.component_type_uri << "\n"; |
205 | 1.49k | } |
206 | 7.01k | } |
207 | | |
208 | 828 | return sstr.str(); |
209 | 828 | } |
210 | | |
211 | | Error Box_cmpd::write(StreamWriter& writer) const |
212 | 0 | { |
213 | 0 | size_t box_start = reserve_box_header_space(writer); |
214 | |
|
215 | 0 | writer.write32((uint32_t) m_components.size()); |
216 | 0 | for (const auto& component : m_components) { |
217 | 0 | writer.write16(component.component_type); |
218 | 0 | if (component.component_type >= 0x8000) { |
219 | 0 | writer.write(component.component_type_uri); |
220 | 0 | } |
221 | 0 | } |
222 | |
|
223 | 0 | prepend_header(writer, box_start); |
224 | |
|
225 | 0 | return Error::Ok; |
226 | 0 | } |
227 | | |
228 | | Error Box_uncC::parse(BitstreamRange& range, const heif_security_limits* limits) |
229 | 4.52k | { |
230 | 4.52k | parse_full_box_header(range); |
231 | | |
232 | 4.52k | m_profile = range.read32(); |
233 | | |
234 | 4.52k | if (get_version() == 1) { |
235 | 655 | if (m_profile == fourcc("rgb3")) { |
236 | 208 | Box_uncC::Component component0 = {0, 8, component_format_unsigned, 0}; |
237 | 208 | add_component(component0); |
238 | 208 | Box_uncC::Component component1 = {1, 8, component_format_unsigned, 0}; |
239 | 208 | add_component(component1); |
240 | 208 | Box_uncC::Component component2 = {2, 8, component_format_unsigned, 0}; |
241 | 208 | add_component(component2); |
242 | 208 | } |
243 | 447 | else if ((m_profile == fourcc("rgba")) || (m_profile == fourcc("abgr"))) { |
244 | 446 | Box_uncC::Component component0 = {0, 8, component_format_unsigned, 0}; |
245 | 446 | add_component(component0); |
246 | 446 | Box_uncC::Component component1 = {1, 8, component_format_unsigned, 0}; |
247 | 446 | add_component(component1); |
248 | 446 | Box_uncC::Component component2 = {2, 8, component_format_unsigned, 0}; |
249 | 446 | add_component(component2); |
250 | 446 | Box_uncC::Component component3 = {3, 8, component_format_unsigned, 0}; |
251 | 446 | add_component(component3); |
252 | 446 | } |
253 | 1 | else { |
254 | 1 | return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid component format"}; |
255 | 1 | } |
256 | 3.86k | } else if (get_version() == 0) { |
257 | | |
258 | 893 | uint32_t component_count = range.read32(); |
259 | | |
260 | 893 | if (limits->max_components && component_count > limits->max_components) { |
261 | 2 | std::stringstream sstr; |
262 | 2 | sstr << "Number of image components (" << component_count << ") exceeds security limit (" |
263 | 2 | << limits->max_components << ")"; |
264 | | |
265 | 2 | return {heif_error_Invalid_input, |
266 | 2 | heif_suberror_Security_limit_exceeded, |
267 | 2 | sstr.str()}; |
268 | 2 | } |
269 | | |
270 | 3.25k | for (uint32_t i = 0; i < component_count && !range.error() && !range.eof(); i++) { |
271 | 2.36k | Component component; |
272 | 2.36k | component.component_index = range.read16(); |
273 | 2.36k | component.component_bit_depth = uint16_t(range.read8() + 1); |
274 | 2.36k | component.component_format = range.read8(); |
275 | 2.36k | component.component_align_size = range.read8(); |
276 | 2.36k | m_components.push_back(component); |
277 | | |
278 | 2.36k | if (!is_valid_component_format(component.component_format)) { |
279 | 7 | return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid component format"}; |
280 | 7 | } |
281 | 2.36k | } |
282 | | |
283 | 884 | m_sampling_type = range.read8(); |
284 | 884 | if (!is_valid_sampling_mode(m_sampling_type)) { |
285 | 0 | return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid sampling mode"}; |
286 | 0 | } |
287 | | |
288 | 884 | m_interleave_type = range.read8(); |
289 | 884 | if (!is_valid_interleave_mode(m_interleave_type)) { |
290 | 2 | return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid interleave mode"}; |
291 | 2 | } |
292 | | |
293 | 882 | m_block_size = range.read8(); |
294 | | |
295 | 882 | uint8_t flags = range.read8(); |
296 | 882 | m_components_little_endian = !!(flags & 0x80); |
297 | 882 | m_block_pad_lsb = !!(flags & 0x40); |
298 | 882 | m_block_little_endian = !!(flags & 0x20); |
299 | 882 | m_block_reversed = !!(flags & 0x10); |
300 | 882 | m_pad_unknown = !!(flags & 0x08); |
301 | | |
302 | 882 | m_pixel_size = range.read32(); |
303 | | |
304 | 882 | m_row_align_size = range.read32(); |
305 | | |
306 | 882 | m_tile_align_size = range.read32(); |
307 | | |
308 | 882 | uint32_t num_tile_cols_minus_one = range.read32(); |
309 | 882 | uint32_t num_tile_rows_minus_one = range.read32(); |
310 | | |
311 | 882 | if (limits->max_number_of_tiles && |
312 | 882 | 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)) { |
313 | 8 | std::stringstream sstr; |
314 | 8 | sstr << "Tiling size " |
315 | 8 | << ((uint64_t)num_tile_cols_minus_one + 1) << " x " << ((uint64_t)num_tile_rows_minus_one + 1) |
316 | 8 | << " exceeds the maximum allowed number " << limits->max_number_of_tiles << " set as security limit"; |
317 | 8 | return {heif_error_Memory_allocation_error, |
318 | 8 | heif_suberror_Security_limit_exceeded, |
319 | 8 | sstr.str()}; |
320 | 8 | } |
321 | | |
322 | 874 | m_num_tile_cols = num_tile_cols_minus_one + 1; |
323 | 874 | m_num_tile_rows = num_tile_rows_minus_one + 1; |
324 | 874 | } |
325 | | |
326 | 4.50k | return range.get_error(); |
327 | 4.52k | } |
328 | | |
329 | | |
330 | | |
331 | | std::string Box_uncC::dump(Indent& indent) const |
332 | 2.52k | { |
333 | 2.52k | std::ostringstream sstr; |
334 | 2.52k | sstr << Box::dump(indent); |
335 | | |
336 | 2.52k | sstr << indent << "profile: " << m_profile; |
337 | 2.52k | if (m_profile != 0) { |
338 | 1.87k | sstr << " (" << fourcc_to_string(m_profile) << ")"; |
339 | 1.87k | } |
340 | 2.52k | sstr << "\n"; |
341 | 2.52k | if (get_version() == 0) { |
342 | 1.66k | for (const auto& component : m_components) { |
343 | 1.66k | sstr << indent << "component_index: " << component.component_index << "\n"; |
344 | 1.66k | indent++; |
345 | 1.66k | sstr << indent << "component_bit_depth: " << (int) component.component_bit_depth << "\n"; |
346 | 1.66k | sstr << indent << "component_format: " << get_name(heif_uncompressed_component_format(component.component_format), sNames_uncompressed_component_format) << "\n"; |
347 | 1.66k | sstr << indent << "component_align_size: " << (int) component.component_align_size << "\n"; |
348 | 1.66k | indent--; |
349 | 1.66k | } |
350 | | |
351 | 725 | sstr << indent << "sampling_type: " << get_name(heif_uncompressed_sampling_mode(m_sampling_type), sNames_uncompressed_sampling_mode) << "\n"; |
352 | | |
353 | 725 | sstr << indent << "interleave_type: " << get_name(heif_uncompressed_interleave_mode(m_interleave_type), sNames_uncompressed_interleave_mode) << "\n"; |
354 | | |
355 | 725 | sstr << indent << "block_size: " << (int) m_block_size << "\n"; |
356 | | |
357 | 725 | sstr << indent << "components_little_endian: " << m_components_little_endian << "\n"; |
358 | 725 | sstr << indent << "block_pad_lsb: " << m_block_pad_lsb << "\n"; |
359 | 725 | sstr << indent << "block_little_endian: " << m_block_little_endian << "\n"; |
360 | 725 | sstr << indent << "block_reversed: " << m_block_reversed << "\n"; |
361 | 725 | sstr << indent << "pad_unknown: " << m_pad_unknown << "\n"; |
362 | | |
363 | 725 | sstr << indent << "pixel_size: " << m_pixel_size << "\n"; |
364 | | |
365 | 725 | sstr << indent << "row_align_size: " << m_row_align_size << "\n"; |
366 | | |
367 | 725 | sstr << indent << "tile_align_size: " << m_tile_align_size << "\n"; |
368 | | |
369 | 725 | sstr << indent << "num_tile_cols: " << m_num_tile_cols << "\n"; |
370 | | |
371 | 725 | sstr << indent << "num_tile_rows: " << m_num_tile_rows << "\n"; |
372 | 725 | } |
373 | 2.52k | return sstr.str(); |
374 | 2.52k | } |
375 | | |
376 | | |
377 | | Error Box_uncC::write(StreamWriter& writer) const |
378 | 0 | { |
379 | 0 | size_t box_start = reserve_box_header_space(writer); |
380 | 0 | writer.write32(m_profile); |
381 | 0 | if (get_version() == 1) { |
382 | 0 | } |
383 | 0 | else if (get_version() == 0) { |
384 | 0 | writer.write32((uint32_t)m_components.size()); |
385 | 0 | for (const auto &component : m_components) { |
386 | 0 | if (component.component_bit_depth < 1 || component.component_bit_depth > 256) { |
387 | 0 | return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "component bit-depth out of range [1..256]"}; |
388 | 0 | } |
389 | | |
390 | 0 | writer.write16(component.component_index); |
391 | 0 | writer.write8(uint8_t(component.component_bit_depth - 1)); |
392 | 0 | writer.write8(component.component_format); |
393 | 0 | writer.write8(component.component_align_size); |
394 | 0 | } |
395 | 0 | writer.write8(m_sampling_type); |
396 | 0 | writer.write8(m_interleave_type); |
397 | 0 | writer.write8(m_block_size); |
398 | 0 | uint8_t flags = 0; |
399 | 0 | flags |= (m_components_little_endian ? 0x80 : 0); |
400 | 0 | flags |= (m_block_pad_lsb ? 0x40 : 0); |
401 | 0 | flags |= (m_block_little_endian ? 0x20 : 0); |
402 | 0 | flags |= (m_block_reversed ? 0x10 : 0); |
403 | 0 | flags |= (m_pad_unknown ? 0x08 : 0); |
404 | 0 | writer.write8(flags); |
405 | 0 | writer.write32(m_pixel_size); |
406 | 0 | writer.write32(m_row_align_size); |
407 | 0 | writer.write32(m_tile_align_size); |
408 | 0 | writer.write32(m_num_tile_cols - 1); |
409 | 0 | writer.write32(m_num_tile_rows - 1); |
410 | 0 | } |
411 | 0 | prepend_header(writer, box_start); |
412 | |
|
413 | 0 | return Error::Ok; |
414 | 0 | } |
415 | | |
416 | | |
417 | | uint64_t Box_uncC::compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const |
418 | 0 | { |
419 | 0 | if (m_profile != 0) { |
420 | 0 | switch (m_profile) { |
421 | 0 | case fourcc("rgba"): |
422 | 0 | return 4 * uint64_t{tile_width} * tile_height; |
423 | | |
424 | 0 | case fourcc("rgb3"): |
425 | 0 | return 3 * uint64_t{tile_width} * tile_height; |
426 | | |
427 | 0 | default: |
428 | 0 | assert(false); |
429 | 0 | return 0; |
430 | 0 | } |
431 | 0 | } |
432 | | |
433 | 0 | switch (m_interleave_type) { |
434 | 0 | case interleave_mode_component: |
435 | 0 | case interleave_mode_pixel: { |
436 | 0 | uint32_t bytes_per_pixel = 0; |
437 | |
|
438 | 0 | for (const auto& comp : m_components) { |
439 | 0 | assert(comp.component_bit_depth % 8 == 0); // TODO: component sizes that are no multiples of bytes |
440 | 0 | bytes_per_pixel += comp.component_bit_depth / 8; |
441 | 0 | } |
442 | | |
443 | 0 | return bytes_per_pixel * uint64_t{tile_width} * tile_height; |
444 | 0 | } |
445 | 0 | default: |
446 | 0 | assert(false); |
447 | 0 | return 0; |
448 | 0 | } |
449 | 0 | } |
450 | | |
451 | | |
452 | | Error Box_cmpC::parse(BitstreamRange& range, const heif_security_limits* limits) |
453 | 439 | { |
454 | 439 | parse_full_box_header(range); |
455 | | |
456 | 439 | if (get_version() != 0) { |
457 | 1 | return unsupported_version_error("cmpC"); |
458 | 1 | } |
459 | | |
460 | 438 | m_compression_type = range.read32(); |
461 | | |
462 | 438 | uint8_t unit_type = range.read8(); |
463 | 438 | if (unit_type > heif_cmpC_compressed_unit_type_image_pixel) { |
464 | 0 | return {heif_error_Invalid_input, |
465 | 0 | heif_suberror_Unsupported_parameter, |
466 | 0 | "Unsupported cmpC compressed unit type"}; |
467 | 438 | }; |
468 | | |
469 | 438 | m_compressed_unit_type = static_cast<heif_cmpC_compressed_unit_type>(unit_type); |
470 | | |
471 | 438 | return range.get_error(); |
472 | 438 | } |
473 | | |
474 | | |
475 | | std::string Box_cmpC::dump(Indent& indent) const |
476 | 422 | { |
477 | 422 | std::ostringstream sstr; |
478 | 422 | sstr << Box::dump(indent); |
479 | 422 | sstr << indent << "compression_type: " << fourcc_to_string(m_compression_type) << "\n"; |
480 | 422 | sstr << indent << "compressed_entity_type: " << (int)m_compressed_unit_type << "\n"; |
481 | 422 | return sstr.str(); |
482 | 422 | } |
483 | | |
484 | | Error Box_cmpC::write(StreamWriter& writer) const |
485 | 0 | { |
486 | 0 | size_t box_start = reserve_box_header_space(writer); |
487 | |
|
488 | 0 | writer.write32(m_compression_type); |
489 | 0 | writer.write8(m_compressed_unit_type); |
490 | |
|
491 | 0 | prepend_header(writer, box_start); |
492 | |
|
493 | 0 | return Error::Ok; |
494 | 0 | } |
495 | | |
496 | | |
497 | | static uint8_t unit_offset_bits_table[] = {0, 16, 24, 32, 64 }; |
498 | | static uint8_t unit_size_bits_table[] = {8, 16, 24, 32, 64 }; |
499 | | |
500 | | Error Box_icef::parse(BitstreamRange& range, const heif_security_limits* limits) |
501 | 517 | { |
502 | 517 | parse_full_box_header(range); |
503 | | |
504 | 517 | if (get_version() != 0) { |
505 | 1 | return unsupported_version_error("icef"); |
506 | 1 | } |
507 | 516 | uint8_t codes = range.read8(); |
508 | 516 | uint8_t unit_offset_code = (codes & 0b11100000) >> 5; |
509 | 516 | uint8_t unit_size_code = (codes & 0b00011100) >> 2; |
510 | 516 | uint32_t num_compressed_units = range.read32(); |
511 | 516 | uint64_t implied_offset = 0; |
512 | | |
513 | 516 | if (unit_offset_code > 4) { |
514 | 0 | return {heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported icef unit offset code"}; |
515 | 0 | } |
516 | | |
517 | 516 | if (unit_size_code > 4) { |
518 | 0 | return {heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported icef unit size code"}; |
519 | 0 | } |
520 | | |
521 | | // --- precompute fields lengths |
522 | | |
523 | 516 | uint8_t unit_offset_bits = unit_offset_bits_table[unit_offset_code]; |
524 | 516 | uint8_t unit_size_bits = unit_size_bits_table[unit_size_code]; |
525 | | |
526 | | // --- check if box is large enough for all the data |
527 | | |
528 | 516 | uint64_t data_size_bytes = static_cast<uint64_t>(num_compressed_units) * (unit_offset_bits + unit_size_bits) / 8; |
529 | 516 | if (data_size_bytes > range.get_remaining_bytes()) { |
530 | 2 | uint64_t contained_units = range.get_remaining_bytes() / ((unit_offset_bits + unit_size_bits) * 8); |
531 | 2 | std::stringstream sstr; |
532 | 2 | sstr << "icef box declares " << num_compressed_units << " units, but only " << contained_units |
533 | 2 | << " were contained in the file"; |
534 | 2 | return {heif_error_Invalid_input, |
535 | 2 | heif_suberror_End_of_data, |
536 | 2 | sstr.str()}; |
537 | 2 | } |
538 | | |
539 | | // TODO: should we impose some security limit? |
540 | | |
541 | | // --- read box content |
542 | | |
543 | 514 | m_unit_infos.resize(num_compressed_units); |
544 | | |
545 | 1.76M | for (uint32_t r = 0; r < num_compressed_units; r++) { |
546 | 1.75M | struct CompressedUnitInfo unitInfo; |
547 | 1.75M | if (unit_offset_code == 0) { |
548 | 1.75M | unitInfo.unit_offset = implied_offset; |
549 | 1.75M | } else { |
550 | 381 | unitInfo.unit_offset = range.read_uint(unit_offset_bits); |
551 | 381 | } |
552 | | |
553 | 1.75M | unitInfo.unit_size = range.read_uint(unit_size_bits); |
554 | | |
555 | 1.75M | if (unitInfo.unit_size >= UINT64_MAX - implied_offset) { |
556 | 1 | return {heif_error_Invalid_input, |
557 | 1 | heif_suberror_Invalid_parameter_value, |
558 | 1 | "cumulative offsets too large for 64 bit file size"}; |
559 | 1 | } |
560 | | |
561 | 1.75M | implied_offset += unitInfo.unit_size; |
562 | | |
563 | 1.75M | if (range.get_error() != Error::Ok) { |
564 | 0 | return range.get_error(); |
565 | 0 | } |
566 | | |
567 | 1.75M | m_unit_infos[r] = unitInfo; |
568 | 1.75M | } |
569 | | |
570 | 513 | return range.get_error(); |
571 | 514 | } |
572 | | |
573 | | |
574 | | std::string Box_icef::dump(Indent& indent) const |
575 | 485 | { |
576 | 485 | std::ostringstream sstr; |
577 | 485 | sstr << Box::dump(indent); |
578 | 485 | sstr << indent << "num_compressed_units: " << m_unit_infos.size() << "\n"; |
579 | 1.75M | for (CompressedUnitInfo unit_info: m_unit_infos) { |
580 | 1.75M | sstr << indent << "unit_offset: " << unit_info.unit_offset << ", unit_size: " << unit_info.unit_size << "\n"; |
581 | 1.75M | } |
582 | 485 | return sstr.str(); |
583 | 485 | } |
584 | | |
585 | | Error Box_icef::write(StreamWriter& writer) const |
586 | 0 | { |
587 | | // check that all units have a non-zero size |
588 | |
|
589 | 0 | for (const CompressedUnitInfo& unit_info: m_unit_infos) { |
590 | 0 | if (unit_info.unit_size == 0) { |
591 | 0 | return { |
592 | 0 | heif_error_Usage_error, |
593 | 0 | heif_suberror_Unspecified, |
594 | 0 | "tiled 'unci' image has an undefined tile." |
595 | 0 | }; |
596 | 0 | } |
597 | 0 | } |
598 | | |
599 | 0 | size_t box_start = reserve_box_header_space(writer); |
600 | |
|
601 | 0 | uint8_t unit_offset_code = 1; |
602 | 0 | uint8_t unit_size_code = 0; |
603 | 0 | uint64_t implied_offset = 0; |
604 | 0 | bool can_use_implied_offsets = true; |
605 | 0 | for (const CompressedUnitInfo& unit_info: m_unit_infos) { |
606 | 0 | if (unit_info.unit_offset != implied_offset) { |
607 | 0 | can_use_implied_offsets = false; |
608 | 0 | } |
609 | 0 | if (unit_info.unit_size > (std::numeric_limits<uint64_t>::max() - implied_offset)) { |
610 | 0 | can_use_implied_offsets = false; |
611 | 0 | } else { |
612 | 0 | implied_offset += unit_info.unit_size; |
613 | 0 | } |
614 | 0 | uint8_t required_offset_code = get_required_offset_code(unit_info.unit_offset); |
615 | 0 | if (required_offset_code > unit_offset_code) { |
616 | 0 | unit_offset_code = required_offset_code; |
617 | 0 | } |
618 | 0 | uint8_t required_size_code = get_required_size_code(unit_info.unit_size); |
619 | 0 | if (required_size_code > unit_size_code) { |
620 | 0 | unit_size_code = required_size_code; |
621 | 0 | } |
622 | 0 | } |
623 | 0 | if (can_use_implied_offsets) { |
624 | 0 | unit_offset_code = 0; |
625 | 0 | } |
626 | 0 | uint8_t code_bits = (uint8_t)((unit_offset_code << 5) | (unit_size_code << 2)); |
627 | 0 | writer.write8(code_bits); |
628 | 0 | writer.write32((uint32_t)m_unit_infos.size()); |
629 | 0 | for (CompressedUnitInfo unit_info: m_unit_infos) { |
630 | 0 | if (unit_offset_code == 0) { |
631 | | // nothing |
632 | 0 | } else if (unit_offset_code == 1) { |
633 | 0 | writer.write16((uint16_t)unit_info.unit_offset); |
634 | 0 | } else if (unit_offset_code == 2) { |
635 | 0 | writer.write24((uint32_t)unit_info.unit_offset); |
636 | 0 | } else if (unit_offset_code == 3) { |
637 | 0 | writer.write32((uint32_t)unit_info.unit_offset); |
638 | 0 | } else { |
639 | 0 | writer.write64(unit_info.unit_offset); |
640 | 0 | } |
641 | 0 | if (unit_size_code == 0) { |
642 | 0 | writer.write8((uint8_t)unit_info.unit_size); |
643 | 0 | } else if (unit_size_code == 1) { |
644 | 0 | writer.write16((uint16_t)unit_info.unit_size); |
645 | 0 | } else if (unit_size_code == 2) { |
646 | 0 | writer.write24((uint32_t)unit_info.unit_size); |
647 | 0 | } else if (unit_size_code == 3) { |
648 | 0 | writer.write32((uint32_t)unit_info.unit_size); |
649 | 0 | } else { |
650 | 0 | writer.write64(unit_info.unit_size); |
651 | 0 | } |
652 | 0 | } |
653 | 0 | prepend_header(writer, box_start); |
654 | |
|
655 | 0 | return Error::Ok; |
656 | 0 | } |
657 | | |
658 | | static uint64_t MAX_OFFSET_UNIT_CODE_1 = std::numeric_limits<uint16_t>::max(); |
659 | | static uint64_t MAX_OFFSET_UNIT_CODE_2 = (1ULL << 24) - 1; |
660 | | static uint64_t MAX_OFFSET_UNIT_CODE_3 = std::numeric_limits<uint32_t>::max(); |
661 | | |
662 | | const uint8_t Box_icef::get_required_offset_code(uint64_t offset) const |
663 | 0 | { |
664 | 0 | if (offset <= MAX_OFFSET_UNIT_CODE_1) { |
665 | 0 | return 1; |
666 | 0 | } |
667 | 0 | if (offset <= MAX_OFFSET_UNIT_CODE_2) { |
668 | 0 | return 2; |
669 | 0 | } |
670 | 0 | if (offset <= MAX_OFFSET_UNIT_CODE_3) { |
671 | 0 | return 3; |
672 | 0 | } |
673 | 0 | return 4; |
674 | 0 | } |
675 | | |
676 | | static uint64_t MAX_SIZE_UNIT_CODE_0 = std::numeric_limits<uint8_t>::max(); |
677 | | static uint64_t MAX_SIZE_UNIT_CODE_1 = std::numeric_limits<uint16_t>::max(); |
678 | | static uint64_t MAX_SIZE_UNIT_CODE_2 = (1ULL << 24) - 1; |
679 | | static uint64_t MAX_SIZE_UNIT_CODE_3 = std::numeric_limits<uint32_t>::max(); |
680 | | |
681 | | const uint8_t Box_icef::get_required_size_code(uint64_t size) const |
682 | 0 | { |
683 | 0 | if (size <= MAX_SIZE_UNIT_CODE_0) { |
684 | 0 | return 0; |
685 | 0 | } |
686 | 0 | if (size <= MAX_SIZE_UNIT_CODE_1) { |
687 | 0 | return 1; |
688 | 0 | } |
689 | 0 | if (size <= MAX_SIZE_UNIT_CODE_2) { |
690 | 0 | return 2; |
691 | 0 | } |
692 | 0 | if (size <= MAX_SIZE_UNIT_CODE_3) { |
693 | 0 | return 3; |
694 | 0 | } |
695 | 0 | return 4; |
696 | 0 | } |
697 | | |
698 | | |
699 | | Error Box_cpat::parse(BitstreamRange& range, const heif_security_limits* limits) |
700 | 254 | { |
701 | 254 | parse_full_box_header(range); |
702 | | |
703 | 254 | if (get_version() != 0) { |
704 | 0 | return unsupported_version_error("cpat"); |
705 | 0 | } |
706 | | |
707 | 254 | m_pattern_width = range.read16(); |
708 | 254 | m_pattern_height = range.read16(); |
709 | | |
710 | 254 | if (m_pattern_width == 0 || m_pattern_height == 0) { |
711 | 3 | return {heif_error_Invalid_input, |
712 | 3 | heif_suberror_Invalid_parameter_value, |
713 | 3 | "Zero Bayer pattern size."}; |
714 | 3 | } |
715 | | |
716 | 251 | auto max_bayer_pattern_size = limits->max_bayer_pattern_pixels; |
717 | 251 | if (max_bayer_pattern_size && m_pattern_height > max_bayer_pattern_size / m_pattern_width) { |
718 | 1 | return {heif_error_Invalid_input, |
719 | 1 | heif_suberror_Security_limit_exceeded, |
720 | 1 | "Maximum Bayer pattern size exceeded."}; |
721 | 1 | } |
722 | | |
723 | 250 | m_components.resize(size_t{m_pattern_width} * m_pattern_height); |
724 | | |
725 | 1.64k | for (uint16_t i = 0; i < m_pattern_height; i++) { |
726 | 3.02k | for (uint16_t j = 0; j < m_pattern_width; j++) { |
727 | 1.62k | PatternComponent component{}; |
728 | 1.62k | component.component_index = range.read32(); |
729 | 1.62k | component.component_gain = range.read_float32(); |
730 | 1.62k | m_components[i] = component; |
731 | 1.62k | } |
732 | 1.39k | } |
733 | | |
734 | 250 | return range.get_error(); |
735 | 251 | } |
736 | | |
737 | | |
738 | | std::string Box_cpat::dump(Indent& indent) const |
739 | 237 | { |
740 | 237 | std::ostringstream sstr; |
741 | | |
742 | 237 | sstr << FullBox::dump(indent); |
743 | 237 | sstr << indent << "pattern_width: " << get_pattern_width() << "\n"; |
744 | 237 | sstr << indent << "pattern_height: " << get_pattern_height() << "\n"; |
745 | | |
746 | 1.47k | for (const auto& component : m_components) { |
747 | 1.47k | sstr << indent << "component index: " << component.component_index << ", gain: " << component.component_gain << "\n"; |
748 | 1.47k | } |
749 | 237 | return sstr.str(); |
750 | 237 | } |
751 | | |
752 | | |
753 | | Error Box_cpat::write(StreamWriter& writer) const |
754 | 0 | { |
755 | 0 | size_t box_start = reserve_box_header_space(writer); |
756 | |
|
757 | 0 | if (m_pattern_width * size_t{m_pattern_height} != m_components.size()) { |
758 | | // needs to be rectangular |
759 | 0 | return {heif_error_Usage_error, |
760 | 0 | heif_suberror_Invalid_parameter_value, |
761 | 0 | "incorrect number of pattern components"}; |
762 | 0 | } |
763 | | |
764 | 0 | writer.write16(m_pattern_width); |
765 | 0 | writer.write16(m_pattern_height); |
766 | |
|
767 | 0 | for (const auto& component : m_components) { |
768 | 0 | writer.write32(component.component_index); |
769 | 0 | writer.write_float32(component.component_gain); |
770 | 0 | } |
771 | |
|
772 | 0 | prepend_header(writer, box_start); |
773 | |
|
774 | 0 | return Error::Ok; |
775 | 0 | } |