Coverage Report

Created: 2026-10-27 06:57

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