Coverage Report

Created: 2026-04-01 07:49

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