Coverage Report

Created: 2025-12-21 06:30

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