Coverage Report

Created: 2026-05-16 06:41

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
3.03k
{
44
3.03k
  return format <= component_format_max_valid;
45
3.03k
}
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
1.07k
{
63
1.07k
  return interleave <= interleave_mode_max_valid;
64
1.07k
}
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
1.07k
{
84
1.07k
  return sampling <= sampling_mode_max_valid;
85
1.07k
}
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
4.88k
{
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
4.88k
  return type <= heif_unci_component_type_max_valid;
100
4.88k
}
101
102
static std::map<heif_unci_component_type, const char*> sNames_uncompressed_component_type{
103
    {heif_unci_component_type_monochrome,   "monochrome"},
104
    {heif_unci_component_type_Y,            "Y"},
105
    {heif_unci_component_type_Cb,           "Cb"},
106
    {heif_unci_component_type_Cr,           "Cr"},
107
    {heif_unci_component_type_red,          "red"},
108
    {heif_unci_component_type_green,        "green"},
109
    {heif_unci_component_type_blue,         "blue"},
110
    {heif_unci_component_type_alpha,        "alpha"},
111
    {heif_unci_component_type_depth,        "depth"},
112
    {heif_unci_component_type_disparity,    "disparity"},
113
    {heif_unci_component_type_palette,      "palette"},
114
    {heif_unci_component_type_filter_array, "filter-array"},
115
    {heif_unci_component_type_padded,       "padded"},
116
    {heif_unci_component_type_cyan,         "cyan"},
117
    {heif_unci_component_type_magenta,      "magenta"},
118
    {heif_unci_component_type_yellow,       "yellow"},
119
    {heif_unci_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
5.32k
{
124
5.32k
  auto iter = table.find(val);
125
5.32k
  if (iter == table.end()) {
126
0
    return "unknown";
127
0
  }
128
5.32k
  else {
129
5.32k
    return iter->second;
130
5.32k
  }
131
5.32k
}
char const* get_name<heif_unci_component_type>(heif_unci_component_type, std::__1::map<heif_unci_component_type, char const*, std::__1::less<heif_unci_component_type>, std::__1::allocator<std::__1::pair<heif_unci_component_type const, char const*> > > const&)
Line
Count
Source
123
1.88k
{
124
1.88k
  auto iter = table.find(val);
125
1.88k
  if (iter == table.end()) {
126
0
    return "unknown";
127
0
  }
128
1.88k
  else {
129
1.88k
    return iter->second;
130
1.88k
  }
131
1.88k
}
char const* get_name<heif_uncompressed_component_format>(heif_uncompressed_component_format, std::__1::map<heif_uncompressed_component_format, char const*, std::__1::less<heif_uncompressed_component_format>, std::__1::allocator<std::__1::pair<heif_uncompressed_component_format const, char const*> > > const&)
Line
Count
Source
123
1.74k
{
124
1.74k
  auto iter = table.find(val);
125
1.74k
  if (iter == table.end()) {
126
0
    return "unknown";
127
0
  }
128
1.74k
  else {
129
1.74k
    return iter->second;
130
1.74k
  }
131
1.74k
}
char const* get_name<heif_uncompressed_sampling_mode>(heif_uncompressed_sampling_mode, std::__1::map<heif_uncompressed_sampling_mode, char const*, std::__1::less<heif_uncompressed_sampling_mode>, std::__1::allocator<std::__1::pair<heif_uncompressed_sampling_mode const, char const*> > > const&)
Line
Count
Source
123
847
{
124
847
  auto iter = table.find(val);
125
847
  if (iter == table.end()) {
126
0
    return "unknown";
127
0
  }
128
847
  else {
129
847
    return iter->second;
130
847
  }
131
847
}
char const* get_name<heif_uncompressed_interleave_mode>(heif_uncompressed_interleave_mode, std::__1::map<heif_uncompressed_interleave_mode, char const*, std::__1::less<heif_uncompressed_interleave_mode>, std::__1::allocator<std::__1::pair<heif_uncompressed_interleave_mode const, char const*> > > const&)
Line
Count
Source
123
847
{
124
847
  auto iter = table.find(val);
125
847
  if (iter == table.end()) {
126
0
    return "unknown";
127
0
  }
128
847
  else {
129
847
    return iter->second;
130
847
  }
131
847
}
132
133
void Box_cmpd::set_components(const std::vector<uint16_t>& components)
134
18
{
135
18
  m_components.clear();
136
137
54
  for (const auto& component : components) {
138
54
    m_components.push_back({component, {}});
139
54
  }
140
18
}
141
142
143
Error Box_cmpd::parse(BitstreamRange& range, const heif_security_limits* limits)
144
810
{
145
810
  uint32_t component_count = range.read32();
146
147
810
  if (limits->max_components && component_count > limits->max_components) {
148
41
    std::stringstream sstr;
149
41
    sstr << "cmpd box should contain " << component_count << " components, but security limit is set to "
150
41
         << limits->max_components << " components";
151
152
41
    return {heif_error_Invalid_input,
153
41
            heif_suberror_Security_limit_exceeded,
154
41
            sstr.str()
155
41
    };
156
41
  }
157
158
8.53k
  for (unsigned int i = 0; i < component_count ; i++) {
159
7.85k
    if (range.eof()) {
160
83
      std::stringstream sstr;
161
83
      sstr << "cmpd box should contain " << component_count << " components, but box only contained "
162
83
           << i << " components";
163
164
83
      return {heif_error_Invalid_input,
165
83
              heif_suberror_End_of_data,
166
83
              sstr.str()
167
83
      };
168
83
    }
169
170
7.76k
    Component component;
171
7.76k
    component.component_type = range.read16();
172
7.76k
    if (component.component_type >= 0x8000) {
173
1.65k
      component.component_type_uri = range.read_string();
174
1.65k
    }
175
6.11k
    else {
176
6.11k
      component.component_type_uri = std::string();
177
6.11k
    }
178
7.76k
    m_components.push_back(component);
179
7.76k
  }
180
181
686
  return range.get_error();
182
769
}
183
184
std::string Box_cmpd::Component::get_component_type_name(uint16_t component_type)
185
4.88k
{
186
4.88k
  std::stringstream sstr;
187
188
4.88k
  if (is_predefined_component_type(component_type)) {
189
1.88k
    sstr << get_name(heif_unci_component_type(component_type), sNames_uncompressed_component_type) << "\n";
190
1.88k
  }
191
2.99k
  else {
192
2.99k
    sstr << "0x" << std::hex << component_type << std::dec << "\n";
193
2.99k
  }
194
195
4.88k
  return sstr.str();
196
4.88k
}
197
198
199
bool Box_cmpd::has_component(heif_unci_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
615
{
208
615
  std::ostringstream sstr;
209
615
  sstr << Box::dump(indent);
210
211
4.88k
  for (const auto& component : m_components) {
212
4.88k
    sstr << indent << "component_type: " << component.get_component_type_name();
213
214
4.88k
    if (component.component_type >= 0x8000) {
215
898
      sstr << indent << "| component_type_uri: " << component.component_type_uri << "\n";
216
898
    }
217
4.88k
  }
218
219
615
  return sstr.str();
220
615
}
221
222
Error Box_cmpd::write(StreamWriter& writer) const
223
18
{
224
18
  size_t box_start = reserve_box_header_space(writer);
225
226
18
  writer.write32((uint32_t) m_components.size());
227
54
  for (const auto& component : m_components) {
228
54
    writer.write16(component.component_type);
229
54
    if (component.component_type >= 0x8000) {
230
0
      writer.write(component.component_type_uri);
231
0
    }
232
54
  }
233
234
18
  prepend_header(writer, box_start);
235
236
18
  return Error::Ok;
237
18
}
238
239
Error Box_uncC::parse(BitstreamRange& range, const heif_security_limits* limits)
240
5.57k
{
241
5.57k
  parse_full_box_header(range);
242
243
5.57k
  m_profile = range.read32();
244
245
5.57k
  if (get_version() == 1) {
246
2.01k
    switch (m_profile) {
247
312
      case fourcc("rgb3"):
248
506
      case fourcc("rgba"):
249
1.01k
      case fourcc("abgr"):
250
1.20k
      case fourcc("2vuy"):
251
1.27k
      case fourcc("yuv2"):
252
1.27k
      case fourcc("yvyu"):
253
1.27k
      case fourcc("vyuy"):
254
1.29k
      case fourcc("yuv1"):
255
1.31k
      case fourcc("v308"):
256
1.32k
      case fourcc("v408"):
257
1.32k
      case fourcc("y210"):
258
1.32k
      case fourcc("v410"):
259
1.32k
      case fourcc("v210"):
260
1.34k
      case fourcc("i420"):
261
1.36k
      case fourcc("nv12"):
262
1.36k
      case fourcc("nv21"):
263
1.36k
      case fourcc("yu22"):
264
1.43k
      case fourcc("yv22"):
265
1.45k
      case fourcc("yv20"):
266
1.45k
        break;
267
565
      default:
268
565
        return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Unknown uncC v1 profile"};
269
2.01k
    }
270
3.56k
  } else if (get_version() == 0) {
271
272
1.11k
    uint32_t component_count = range.read32();
273
274
1.11k
    if (limits->max_components && component_count > limits->max_components) {
275
24
      std::stringstream sstr;
276
24
      sstr << "Number of image components (" << component_count << ") exceeds security limit ("
277
24
           << limits->max_components << ")";
278
279
24
      return {heif_error_Invalid_input,
280
24
              heif_suberror_Security_limit_exceeded,
281
24
              sstr.str()};
282
24
    }
283
284
4.11k
    for (uint32_t i = 0; i < component_count && !range.error() && !range.eof(); i++) {
285
3.03k
      Component component;
286
3.03k
      component.component_index = range.read16();
287
3.03k
      component.component_bit_depth = uint16_t(range.read8() + 1);
288
3.03k
      component.component_format = range.read8();
289
3.03k
      component.component_align_size = range.read8();
290
3.03k
      m_components.push_back(component);
291
292
3.03k
      if (!is_valid_component_format(component.component_format)) {
293
16
        return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid component format"};
294
16
      }
295
3.03k
    }
296
297
1.07k
    m_sampling_type = range.read8();
298
1.07k
    if (!is_valid_sampling_mode(m_sampling_type)) {
299
1
      return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid sampling mode"};
300
1
    }
301
302
1.07k
    m_interleave_type = range.read8();
303
1.07k
    if (!is_valid_interleave_mode(m_interleave_type)) {
304
1
      return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid interleave mode"};
305
1
    }
306
307
1.07k
    m_block_size = range.read8();
308
309
1.07k
    uint8_t flags = range.read8();
310
1.07k
    m_components_little_endian = !!(flags & 0x80);
311
1.07k
    m_block_pad_lsb = !!(flags & 0x40);
312
1.07k
    m_block_little_endian = !!(flags & 0x20);
313
1.07k
    m_block_reversed = !!(flags & 0x10);
314
1.07k
    m_pad_unknown = !!(flags & 0x08);
315
316
1.07k
    m_pixel_size = range.read32();
317
318
1.07k
    m_row_align_size = range.read32();
319
320
1.07k
    m_tile_align_size = range.read32();
321
322
1.07k
    uint32_t num_tile_cols_minus_one = range.read32();
323
1.07k
    uint32_t num_tile_rows_minus_one = range.read32();
324
325
1.07k
    if (limits->max_number_of_tiles &&
326
1.07k
        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
2
      std::stringstream sstr;
328
2
      sstr << "Tiling size "
329
2
           << ((uint64_t)num_tile_cols_minus_one + 1) << " x " << ((uint64_t)num_tile_rows_minus_one + 1)
330
2
           << " exceeds the maximum allowed number " << limits->max_number_of_tiles << " set as security limit";
331
2
      return {heif_error_Memory_allocation_error,
332
2
              heif_suberror_Security_limit_exceeded,
333
2
              sstr.str()};
334
2
    }
335
336
1.07k
    m_num_tile_cols = num_tile_cols_minus_one + 1;
337
1.07k
    m_num_tile_rows = num_tile_rows_minus_one + 1;
338
1.07k
  }
339
340
4.96k
  return range.get_error();
341
5.57k
}
342
343
344
345
std::string Box_uncC::dump(Indent& indent) const
346
3.01k
{
347
3.01k
  std::ostringstream sstr;
348
3.01k
  sstr << Box::dump(indent);
349
350
3.01k
  sstr << indent << "profile: " << m_profile;
351
3.01k
  if (m_profile != 0) {
352
2.49k
    sstr << " (" << fourcc_to_string(m_profile) << ")";
353
2.49k
  }
354
3.01k
  sstr << "\n";
355
3.01k
  if (get_version() == 0) {
356
1.74k
    for (const auto& component : m_components) {
357
1.74k
      sstr << indent << "component_index: " << component.component_index << "\n";
358
1.74k
      indent++;
359
1.74k
      sstr << indent << "component_bit_depth: " << (int) component.component_bit_depth << "\n";
360
1.74k
      sstr << indent << "component_format: " << get_name(heif_uncompressed_component_format(component.component_format), sNames_uncompressed_component_format) << "\n";
361
1.74k
      sstr << indent << "component_align_size: " << (int) component.component_align_size << "\n";
362
1.74k
      indent--;
363
1.74k
    }
364
365
847
    sstr << indent << "sampling_type: " << get_name(heif_uncompressed_sampling_mode(m_sampling_type), sNames_uncompressed_sampling_mode) << "\n";
366
367
847
    sstr << indent << "interleave_type: " << get_name(heif_uncompressed_interleave_mode(m_interleave_type), sNames_uncompressed_interleave_mode) << "\n";
368
369
847
    sstr << indent << "block_size: " << (int) m_block_size << "\n";
370
371
847
    sstr << indent << "components_little_endian: " << m_components_little_endian << "\n";
372
847
    sstr << indent << "block_pad_lsb: " << m_block_pad_lsb << "\n";
373
847
    sstr << indent << "block_little_endian: " << m_block_little_endian << "\n";
374
847
    sstr << indent << "block_reversed: " << m_block_reversed << "\n";
375
847
    sstr << indent << "pad_unknown: " << m_pad_unknown << "\n";
376
377
847
    sstr << indent << "pixel_size: " << m_pixel_size << "\n";
378
379
847
    sstr << indent << "row_align_size: " << m_row_align_size << "\n";
380
381
847
    sstr << indent << "tile_align_size: " << m_tile_align_size << "\n";
382
383
847
    sstr << indent << "num_tile_cols: " << m_num_tile_cols << "\n";
384
385
847
    sstr << indent << "num_tile_rows: " << m_num_tile_rows << "\n";
386
847
  }
387
3.01k
  return sstr.str();
388
3.01k
}
389
390
391
Error Box_uncC::write(StreamWriter& writer) const
392
18
{
393
18
  size_t box_start = reserve_box_header_space(writer);
394
18
  writer.write32(m_profile);
395
18
  if (get_version() == 1) {
396
0
  }
397
18
  else if (get_version() == 0) {
398
18
    writer.write32((uint32_t)m_components.size());
399
54
    for (const auto &component : m_components) {
400
54
      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
54
      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
54
      writer.write16(static_cast<uint16_t>(component.component_index));
408
54
      writer.write8(uint8_t(component.component_bit_depth - 1));
409
54
      writer.write8(component.component_format);
410
54
      writer.write8(component.component_align_size);
411
54
    }
412
18
    writer.write8(m_sampling_type);
413
18
    writer.write8(m_interleave_type);
414
18
    writer.write8(m_block_size);
415
18
    uint8_t flags = 0;
416
18
    flags |= (m_components_little_endian ? 0x80 : 0);
417
18
    flags |= (m_block_pad_lsb ? 0x40 : 0);
418
18
    flags |= (m_block_little_endian ? 0x20 : 0);
419
18
    flags |= (m_block_reversed ? 0x10 : 0);
420
18
    flags |= (m_pad_unknown ? 0x08 : 0);
421
18
    writer.write8(flags);
422
18
    writer.write32(m_pixel_size);
423
18
    writer.write32(m_row_align_size);
424
18
    writer.write32(m_tile_align_size);
425
18
    writer.write32(m_num_tile_cols - 1);
426
18
    writer.write32(m_num_tile_rows - 1);
427
18
  }
428
18
  prepend_header(writer, box_start);
429
430
18
  return Error::Ok;
431
18
}
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
0
{
437
0
  if (uncC->get_version() != 1 || cmpd) {
438
0
    return;
439
0
  }
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_unci_component_type_red});
461
0
    cmpd->add_component({heif_unci_component_type_green});
462
0
    cmpd->add_component({heif_unci_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_unci_component_type_red});
473
0
    cmpd->add_component({heif_unci_component_type_green});
474
0
    cmpd->add_component({heif_unci_component_type_blue});
475
0
    cmpd->add_component({heif_unci_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_unci_component_type_alpha});
486
0
    cmpd->add_component({heif_unci_component_type_blue});
487
0
    cmpd->add_component({heif_unci_component_type_green});
488
0
    cmpd->add_component({heif_unci_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_unci_component_type_Cb});
500
0
    cmpd->add_component({heif_unci_component_type_Y});
501
0
    cmpd->add_component({heif_unci_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_unci_component_type_Y});
513
0
    cmpd->add_component({heif_unci_component_type_Cb});
514
0
    cmpd->add_component({heif_unci_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_unci_component_type_Y});
526
0
    cmpd->add_component({heif_unci_component_type_Cr});
527
0
    cmpd->add_component({heif_unci_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_unci_component_type_Cr});
539
0
    cmpd->add_component({heif_unci_component_type_Y});
540
0
    cmpd->add_component({heif_unci_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_unci_component_type_Y});
554
0
    cmpd->add_component({heif_unci_component_type_Cb});
555
0
    cmpd->add_component({heif_unci_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_unci_component_type_Cr});
566
0
    cmpd->add_component({heif_unci_component_type_Y});
567
0
    cmpd->add_component({heif_unci_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_unci_component_type_Cb});
579
0
    cmpd->add_component({heif_unci_component_type_Y});
580
0
    cmpd->add_component({heif_unci_component_type_Cr});
581
0
    cmpd->add_component({heif_unci_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_unci_component_type_Y});
594
0
    cmpd->add_component({heif_unci_component_type_Cb});
595
0
    cmpd->add_component({heif_unci_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_unci_component_type_Cb});
610
0
    cmpd->add_component({heif_unci_component_type_Y});
611
0
    cmpd->add_component({heif_unci_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_unci_component_type_Cb});
628
0
    cmpd->add_component({heif_unci_component_type_Y});
629
0
    cmpd->add_component({heif_unci_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_unci_component_type_Y});
643
0
    cmpd->add_component({heif_unci_component_type_Cb});
644
0
    cmpd->add_component({heif_unci_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_unci_component_type_Y});
655
0
    cmpd->add_component({heif_unci_component_type_Cb});
656
0
    cmpd->add_component({heif_unci_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_unci_component_type_Y});
667
0
    cmpd->add_component({heif_unci_component_type_Cr});
668
0
    cmpd->add_component({heif_unci_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_unci_component_type_Y});
679
0
    cmpd->add_component({heif_unci_component_type_Cb});
680
0
    cmpd->add_component({heif_unci_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_unci_component_type_Y});
691
0
    cmpd->add_component({heif_unci_component_type_Cr});
692
0
    cmpd->add_component({heif_unci_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_unci_component_type_Y});
703
0
    cmpd->add_component({heif_unci_component_type_Cr});
704
0
    cmpd->add_component({heif_unci_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
705
{
719
705
  parse_full_box_header(range);
720
721
705
  if (get_version() != 0) {
722
6
    return unsupported_version_error("cmpC");
723
6
  }
724
725
699
  m_compression_type = range.read32();
726
727
699
  uint8_t unit_type = range.read8();
728
699
  if (unit_type > heif_cmpC_compressed_unit_type_image_pixel) {
729
2
    return {heif_error_Invalid_input,
730
2
            heif_suberror_Unsupported_parameter,
731
2
            "Unsupported cmpC compressed unit type"};
732
697
  };
733
734
697
  m_compressed_unit_type = static_cast<heif_cmpC_compressed_unit_type>(unit_type);
735
736
697
  return range.get_error();
737
699
}
738
739
740
std::string Box_cmpC::dump(Indent& indent) const
741
599
{
742
599
  std::ostringstream sstr;
743
599
  sstr << Box::dump(indent);
744
599
  sstr << indent << "compression_type: " << fourcc_to_string(m_compression_type) << "\n";
745
599
  sstr << indent << "compressed_entity_type: " << (int)m_compressed_unit_type << "\n";
746
599
  return sstr.str();
747
599
}
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
805
{
767
805
  parse_full_box_header(range);
768
769
805
  if (get_version() != 0) {
770
3
    return unsupported_version_error("icef");
771
3
  }
772
802
  uint8_t codes = range.read8();
773
802
  uint8_t unit_offset_code = (codes & 0b11100000) >> 5;
774
802
  uint8_t unit_size_code = (codes & 0b00011100) >> 2;
775
802
  uint32_t num_compressed_units = range.read32();
776
802
  uint64_t implied_offset = 0;
777
778
802
  if (unit_offset_code > 4) {
779
2
    return {heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported icef unit offset code"};
780
2
  }
781
782
800
  if (unit_size_code > 4) {
783
5
    return {heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported icef unit size code"};
784
5
  }
785
786
  // --- precompute fields lengths
787
788
795
  uint8_t unit_offset_bits = unit_offset_bits_table[unit_offset_code];
789
795
  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
795
  uint64_t data_size_bytes = static_cast<uint64_t>(num_compressed_units) * (unit_offset_bits + unit_size_bits) / 8;
794
795
  if (data_size_bytes > range.get_remaining_bytes()) {
795
15
    uint64_t contained_units = range.get_remaining_bytes() / ((unit_offset_bits + unit_size_bits) * 8);
796
15
    std::stringstream sstr;
797
15
    sstr << "icef box declares " << num_compressed_units << " units, but only " << contained_units
798
15
         << " were contained in the file";
799
15
    return {heif_error_Invalid_input,
800
15
            heif_suberror_End_of_data,
801
15
            sstr.str()};
802
15
  }
803
804
  // TODO: should we impose some security limit?
805
806
  // --- read box content
807
808
780
  m_unit_infos.resize(num_compressed_units);
809
810
1.40M
  for (uint32_t r = 0; r < num_compressed_units; r++) {
811
1.40M
    struct CompressedUnitInfo unitInfo;
812
1.40M
    if (unit_offset_code == 0) {
813
1.40M
      unitInfo.unit_offset = implied_offset;
814
1.40M
    } else {
815
411
      unitInfo.unit_offset = range.read_uint(unit_offset_bits);
816
411
    }
817
818
1.40M
    unitInfo.unit_size = range.read_uint(unit_size_bits);
819
820
1.40M
    if (unitInfo.unit_size >= UINT64_MAX - implied_offset) {
821
3
      return {heif_error_Invalid_input,
822
3
              heif_suberror_Invalid_parameter_value,
823
3
              "cumulative offsets too large for 64 bit file size"};
824
3
    }
825
826
1.40M
    implied_offset += unitInfo.unit_size;
827
828
1.40M
    if (range.get_error() != Error::Ok) {
829
0
      return range.get_error();
830
0
    }
831
832
1.40M
    m_unit_infos[r] = unitInfo;
833
1.40M
  }
834
835
777
  return range.get_error();
836
780
}
837
838
839
std::string Box_icef::dump(Indent& indent) const
840
771
{
841
771
  std::ostringstream sstr;
842
771
  sstr << Box::dump(indent);
843
771
  sstr << indent << "num_compressed_units: " << m_unit_infos.size() << "\n";
844
1.40M
  for (CompressedUnitInfo unit_info: m_unit_infos) {
845
1.40M
    sstr << indent << "unit_offset: " << unit_info.unit_offset << ", unit_size: " << unit_info.unit_size << "\n";
846
1.40M
  }
847
771
  return sstr.str();
848
771
}
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
724
{
966
724
  parse_full_box_header(range);
967
968
724
  if (get_version() != 0) {
969
1
    return unsupported_version_error("cpat");
970
1
  }
971
972
723
  m_pattern.pattern_width = range.read16();
973
723
  m_pattern.pattern_height = range.read16();
974
975
723
  if (m_pattern.pattern_width == 0 || m_pattern.pattern_height == 0) {
976
17
    return {heif_error_Invalid_input,
977
17
            heif_suberror_Invalid_parameter_value,
978
17
            "Zero Bayer pattern size."};
979
17
  }
980
981
706
  auto max_bayer_pattern_size = limits->max_bayer_pattern_pixels;
982
706
  if (max_bayer_pattern_size && m_pattern.pattern_height > max_bayer_pattern_size / m_pattern.pattern_width) {
983
11
    return {heif_error_Invalid_input,
984
11
            heif_suberror_Security_limit_exceeded,
985
11
            "Maximum Bayer pattern size exceeded."};
986
11
  }
987
988
695
  size_t num_pixels = size_t{m_pattern.pattern_width} * m_pattern.pattern_height;
989
695
  m_pattern.pixels.resize(num_pixels);
990
991
5.62k
  for (size_t i = 0; i < num_pixels; i++) {
992
4.92k
    BayerPatternPixelCmpd pixel{};
993
4.92k
    pixel.cmpd_index = range.read32();
994
4.92k
    pixel.component_gain = range.read_float32();
995
4.92k
    m_pattern.pixels[i] = pixel;
996
4.92k
  }
997
998
695
  return range.get_error();
999
706
}
1000
1001
1002
std::string Box_cpat::dump(Indent& indent) const
1003
530
{
1004
530
  std::ostringstream sstr;
1005
1006
530
  sstr << FullBox::dump(indent);
1007
530
  sstr << indent << "pattern_width: " << get_pattern_width() << "\n";
1008
530
  sstr << indent << "pattern_height: " << get_pattern_height() << "\n";
1009
1010
2.56k
  for (const auto& pixel : m_pattern.pixels) {
1011
2.56k
    sstr << indent << "component index: " << pixel.cmpd_index << ", gain: " << pixel.component_gain << "\n";
1012
2.56k
  }
1013
530
  return sstr.str();
1014
530
}
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.cmpd_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_ids.resize(component_count);
1057
0
  for (uint32_t i = 0; i < component_count; i++) {
1058
0
    m_pattern.component_ids[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_ids.size() << "\n";
1095
0
  for (size_t i = 0; i < m_pattern.component_ids.size(); i++) {
1096
0
    sstr << indent << "  component_index[" << i << "]: " << m_pattern.component_ids[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_ids.size()));
1129
0
  for (uint32_t idx : m_pattern.component_ids) {
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
0
{
1148
0
  parse_full_box_header(range);
1149
1150
0
  if (get_version() != 0) {
1151
0
    return unsupported_version_error("sbpm");
1152
0
  }
1153
1154
0
  uint32_t component_count = range.read32();
1155
1156
0
  if (limits->max_components && component_count > limits->max_components) {
1157
0
    return {heif_error_Invalid_input,
1158
0
            heif_suberror_Security_limit_exceeded,
1159
0
            "sbpm component_count exceeds security limit."};
1160
0
  }
1161
1162
0
  m_map.component_ids.resize(component_count);
1163
0
  for (uint32_t i = 0; i < component_count; i++) {
1164
0
    m_map.component_ids[i] = range.read32();
1165
0
  }
1166
1167
0
  uint8_t flags = range.read8();
1168
0
  m_map.correction_applied = !!(flags & 0x80);
1169
1170
0
  uint32_t num_bad_rows = range.read32();
1171
0
  uint32_t num_bad_cols = range.read32();
1172
0
  uint32_t num_bad_pixels = range.read32();
1173
1174
  // Security check: limit total number of entries
1175
0
  uint64_t total_entries = static_cast<uint64_t>(num_bad_rows) + num_bad_cols + num_bad_pixels;
1176
0
  if (limits->max_bad_pixels && total_entries > limits->max_bad_pixels) {
1177
0
    return {heif_error_Invalid_input,
1178
0
            heif_suberror_Security_limit_exceeded,
1179
0
            "sbpm total bad pixel entries exceed security limit."};
1180
0
  }
1181
1182
0
  m_map.bad_rows.resize(num_bad_rows);
1183
0
  for (uint32_t i = 0; i < num_bad_rows; i++) {
1184
0
    m_map.bad_rows[i] = range.read32();
1185
0
  }
1186
1187
0
  m_map.bad_columns.resize(num_bad_cols);
1188
0
  for (uint32_t i = 0; i < num_bad_cols; i++) {
1189
0
    m_map.bad_columns[i] = range.read32();
1190
0
  }
1191
1192
0
  m_map.bad_pixels.resize(num_bad_pixels);
1193
0
  for (uint32_t i = 0; i < num_bad_pixels; i++) {
1194
0
    m_map.bad_pixels[i].row = range.read32();
1195
0
    m_map.bad_pixels[i].column = range.read32();
1196
0
  }
1197
1198
0
  return range.get_error();
1199
0
}
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_ids.size() << "\n";
1209
0
  for (size_t i = 0; i < m_map.component_ids.size(); i++) {
1210
0
    sstr << indent << "  component_index[" << i << "]: " << m_map.component_ids[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_ids.size()));
1240
0
  for (uint32_t idx : m_map.component_ids) {
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_ids.resize(component_count);
1287
0
  for (uint32_t i = 0; i < component_count; i++) {
1288
0
    m_nuc.component_ids[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_ids.size() << "\n";
1344
0
  for (size_t i = 0; i < m_nuc.component_ids.size(); i++) {
1345
0
    sstr << indent << "  component_index[" << i << "]: " << m_nuc.component_ids[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_ids.size()));
1365
0
  for (uint32_t idx : m_nuc.component_ids) {
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
0
{
1391
0
  parse_full_box_header(range);
1392
1393
0
  if (get_version() != 0) {
1394
0
    return unsupported_version_error("cloc");
1395
0
  }
1396
1397
0
  m_chroma_location = range.read8();
1398
1399
0
  if (m_chroma_location > 6) {
1400
0
    return {heif_error_Invalid_input,
1401
0
            heif_suberror_Invalid_parameter_value,
1402
0
            "cloc chroma_location value out of range (must be 0-6)."};
1403
0
  }
1404
1405
0
  return range.get_error();
1406
0
}
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
0
{
1449
0
  uint32_t number_of_components = range.read32();
1450
1451
0
  if (limits->max_components && number_of_components > limits->max_components) {
1452
0
    std::stringstream sstr;
1453
0
    sstr << "GIMI component content IDs box contains " << number_of_components
1454
0
         << " components, but security limit is set to " << limits->max_components << " components";
1455
0
    return {heif_error_Invalid_input,
1456
0
            heif_suberror_Security_limit_exceeded,
1457
0
            sstr.str()};
1458
0
  }
1459
1460
0
  for (uint32_t i = 0; i < number_of_components; i++) {
1461
0
    if (range.eof()) {
1462
0
      return {heif_error_Invalid_input,
1463
0
              heif_suberror_End_of_data,
1464
0
              "Not enough data for all component content IDs"};
1465
0
    }
1466
0
    m_content_ids.push_back(range.read_string());
1467
0
  }
1468
1469
0
  return range.get_error();
1470
0
}
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
}