Coverage Report

Created: 2025-07-18 06:38

/src/libheif/libheif/codecs/uncompressed/unc_codec.cc
Line
Count
Source (jump to first uncovered line)
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
#include "unc_codec.h"
22
23
#include "common_utils.h"
24
#include "context.h"
25
#include "error.h"
26
#include "libheif/heif.h"
27
#include "unc_types.h"
28
#include "unc_boxes.h"
29
30
#include "decoder_abstract.h"
31
#include "decoder_component_interleave.h"
32
#include "decoder_pixel_interleave.h"
33
#include "decoder_mixed_interleave.h"
34
#include "decoder_row_interleave.h"
35
#include "decoder_tile_component_interleave.h"
36
37
#include <algorithm>
38
#include <map>
39
#include <iostream>
40
#include <cassert>
41
#include "security_limits.h"
42
43
44
bool isKnownUncompressedFrameConfigurationBoxProfile(const std::shared_ptr<const Box_uncC>& uncC)
45
0
{
46
0
  return ((uncC != nullptr) && (uncC->get_version() == 1) && ((uncC->get_profile() == fourcc("rgb3")) || (uncC->get_profile() == fourcc("rgba")) || (uncC->get_profile() == fourcc("abgr"))));
47
0
}
48
49
50
static Error uncompressed_image_type_is_supported(const std::shared_ptr<const Box_uncC>& uncC,
51
                                                  const std::shared_ptr<const Box_cmpd>& cmpd)
52
0
{
53
0
  if (isKnownUncompressedFrameConfigurationBoxProfile(uncC)) {
54
0
    return Error::Ok;
55
0
  }
56
0
  if (!cmpd) {
57
0
    return Error(heif_error_Unsupported_feature,
58
0
                 heif_suberror_Unsupported_data_version,
59
0
                 "Missing required cmpd box (no match in uncC box) for uncompressed codec");
60
0
  }
61
62
0
  for (Box_uncC::Component component : uncC->get_components()) {
63
0
    uint16_t component_index = component.component_index;
64
0
    uint16_t component_type = cmpd->get_components()[component_index].component_type;
65
0
    if ((component_type > 7) && (component_type != component_type_padded) && (component_type != component_type_filter_array)) {
66
0
      std::stringstream sstr;
67
0
      sstr << "Uncompressed image with component_type " << ((int) component_type) << " is not implemented yet";
68
0
      return Error(heif_error_Unsupported_feature,
69
0
                   heif_suberror_Unsupported_data_version,
70
0
                   sstr.str());
71
0
    }
72
73
0
    if ((component.component_bit_depth > 16)) {
74
0
      std::stringstream sstr;
75
0
      sstr << "Uncompressed image with component_bit_depth " << ((int) component.component_bit_depth) << " is not implemented yet";
76
0
      return Error(heif_error_Unsupported_feature,
77
0
                   heif_suberror_Unsupported_data_version,
78
0
                   sstr.str());
79
0
    }
80
0
    if (component.component_format != component_format_unsigned) {
81
0
      std::stringstream sstr;
82
0
      sstr << "Uncompressed image with component_format " << ((int) component.component_format) << " is not implemented yet";
83
0
      return Error(heif_error_Unsupported_feature,
84
0
                   heif_suberror_Unsupported_data_version,
85
0
                   sstr.str());
86
0
    }
87
0
    if (component.component_align_size > 2) {
88
0
      std::stringstream sstr;
89
0
      sstr << "Uncompressed image with component_align_size " << ((int) component.component_align_size) << " is not implemented yet";
90
0
      return Error(heif_error_Unsupported_feature,
91
0
                   heif_suberror_Unsupported_data_version,
92
0
                   sstr.str());
93
0
    }
94
0
  }
95
0
  if ((uncC->get_sampling_type() != sampling_mode_no_subsampling)
96
0
      && (uncC->get_sampling_type() != sampling_mode_422)
97
0
      && (uncC->get_sampling_type() != sampling_mode_420)
98
0
      ) {
99
0
    std::stringstream sstr;
100
0
    sstr << "Uncompressed sampling_type of " << ((int) uncC->get_sampling_type()) << " is not implemented yet";
101
0
    return Error(heif_error_Unsupported_feature,
102
0
                 heif_suberror_Unsupported_data_version,
103
0
                 sstr.str());
104
0
  }
105
0
  if ((uncC->get_interleave_type() != interleave_mode_component)
106
0
      && (uncC->get_interleave_type() != interleave_mode_pixel)
107
0
      && (uncC->get_interleave_type() != interleave_mode_mixed)
108
0
      && (uncC->get_interleave_type() != interleave_mode_row)
109
0
      && (uncC->get_interleave_type() != interleave_mode_tile_component)
110
0
      ) {
111
0
    std::stringstream sstr;
112
0
    sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet";
113
0
    return Error(heif_error_Unsupported_feature,
114
0
                 heif_suberror_Unsupported_data_version,
115
0
                 sstr.str());
116
0
  }
117
  // Validity checks per ISO/IEC 23001-17 Section 5.2.1.5.3
118
0
  if (uncC->get_sampling_type() == sampling_mode_422) {
119
    // We check Y Cb and Cr appear in the chroma test
120
    // TODO: error for tile width not multiple of 2
121
0
    if ((uncC->get_interleave_type() != interleave_mode_component)
122
0
        && (uncC->get_interleave_type() != interleave_mode_mixed)
123
0
        && (uncC->get_interleave_type() != interleave_mode_multi_y)) {
124
0
      std::stringstream sstr;
125
0
      sstr << "YCbCr 4:2:2 subsampling is only valid with component, mixed or multi-Y interleave mode (ISO/IEC 23001-17 5.2.1.5.3).";
126
0
      return Error(heif_error_Invalid_input,
127
0
                   heif_suberror_Invalid_parameter_value,
128
0
                   sstr.str());
129
0
    }
130
0
    if ((uncC->get_row_align_size() != 0) && (uncC->get_interleave_type() == interleave_mode_component)) {
131
0
      if (uncC->get_row_align_size() % 2 != 0) {
132
0
        std::stringstream sstr;
133
0
        sstr << "YCbCr 4:2:2 subsampling with component interleave requires row_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.3).";
134
0
        return Error(heif_error_Invalid_input,
135
0
                     heif_suberror_Invalid_parameter_value,
136
0
                     sstr.str());
137
0
      }
138
0
    }
139
0
    if (uncC->get_tile_align_size() != 0) {
140
0
      if (uncC->get_tile_align_size() % 2 != 0) {
141
0
        std::stringstream sstr;
142
0
        sstr << "YCbCr 4:2:2 subsampling requires tile_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.3).";
143
0
        return Error(heif_error_Invalid_input,
144
0
                     heif_suberror_Invalid_parameter_value,
145
0
                     sstr.str());
146
0
      }
147
0
    }
148
0
  }
149
  // Validity checks per ISO/IEC 23001-17 Section 5.2.1.5.4
150
0
  if (uncC->get_sampling_type() == sampling_mode_422) {
151
    // We check Y Cb and Cr appear in the chroma test
152
    // TODO: error for tile width not multiple of 2
153
0
    if ((uncC->get_interleave_type() != interleave_mode_component)
154
0
        && (uncC->get_interleave_type() != interleave_mode_mixed)) {
155
0
      std::stringstream sstr;
156
0
      sstr << "YCbCr 4:2:0 subsampling is only valid with component or mixed interleave mode (ISO/IEC 23001-17 5.2.1.5.4).";
157
0
      return Error(heif_error_Invalid_input,
158
0
                   heif_suberror_Invalid_parameter_value,
159
0
                   sstr.str());
160
0
    }
161
0
    if ((uncC->get_row_align_size() != 0) && (uncC->get_interleave_type() == interleave_mode_component)) {
162
0
      if (uncC->get_row_align_size() % 2 != 0) {
163
0
        std::stringstream sstr;
164
0
        sstr << "YCbCr 4:2:2 subsampling with component interleave requires row_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.4).";
165
0
        return Error(heif_error_Invalid_input,
166
0
                     heif_suberror_Invalid_parameter_value,
167
0
                     sstr.str());
168
0
      }
169
0
    }
170
0
    if (uncC->get_tile_align_size() != 0) {
171
0
      if (uncC->get_tile_align_size() % 4 != 0) {
172
0
        std::stringstream sstr;
173
0
        sstr << "YCbCr 4:2:2 subsampling requires tile_align_size to be a multiple of 4 (ISO/IEC 23001-17 5.2.1.5.3).";
174
0
        return Error(heif_error_Invalid_input,
175
0
                     heif_suberror_Invalid_parameter_value,
176
0
                     sstr.str());
177
0
      }
178
0
    }
179
0
  }
180
0
  if ((uncC->get_interleave_type() == interleave_mode_mixed) && (uncC->get_sampling_type() == sampling_mode_no_subsampling)) {
181
0
    std::stringstream sstr;
182
0
    sstr << "Interleave interleave mode is not valid with subsampling mode (ISO/IEC 23001-17 5.2.1.6.4).";
183
0
    return Error(heif_error_Invalid_input,
184
0
                 heif_suberror_Invalid_parameter_value,
185
0
                 sstr.str());
186
0
  }
187
0
  if ((uncC->get_interleave_type() == interleave_mode_multi_y)
188
0
      && ((uncC->get_sampling_type() != sampling_mode_422) && (uncC->get_sampling_type() != sampling_mode_411))) {
189
0
    std::stringstream sstr;
190
0
    sstr << "Multi-Y interleave mode is only valid with 4:2:2 and 4:1:1 subsampling modes (ISO/IEC 23001-17 5.2.1.6.7).";
191
0
    return Error(heif_error_Invalid_input,
192
0
                 heif_suberror_Invalid_parameter_value,
193
0
                 sstr.str());
194
0
  }
195
  // TODO: throw error if mixed and Cb and Cr are not adjacent.
196
197
0
  if (uncC->get_block_size() != 0) {
198
0
    std::stringstream sstr;
199
0
    sstr << "Uncompressed block_size of " << ((int) uncC->get_block_size()) << " is not implemented yet";
200
0
    return Error(heif_error_Unsupported_feature,
201
0
                 heif_suberror_Unsupported_data_version,
202
0
                 sstr.str());
203
0
  }
204
205
0
  if (uncC->is_components_little_endian()) {
206
0
    const auto& comps = uncC->get_components();
207
0
    bool all_8_bit = std::all_of(comps.begin(), comps.end(),
208
0
                                 [](const Box_uncC::Component& c) { return c.component_bit_depth==8; });
209
0
    if (!all_8_bit) {
210
0
      return Error(heif_error_Unsupported_feature,
211
0
                   heif_suberror_Unsupported_data_version,
212
0
                   "Uncompressed components_little_endian == 1 is not implemented yet");
213
0
    }
214
0
  }
215
216
0
  if (uncC->is_block_pad_lsb()) {
217
0
    return Error(heif_error_Unsupported_feature,
218
0
                 heif_suberror_Unsupported_data_version,
219
0
                 "Uncompressed block_pad_lsb == 1 is not implemented yet");
220
0
  }
221
0
  if (uncC->is_block_little_endian()) {
222
0
    return Error(heif_error_Unsupported_feature,
223
0
                 heif_suberror_Unsupported_data_version,
224
0
                 "Uncompressed block_little_endian == 1 is not implemented yet");
225
0
  }
226
0
  if (uncC->is_block_reversed()) {
227
0
    return Error(heif_error_Unsupported_feature,
228
0
                 heif_suberror_Unsupported_data_version,
229
0
                 "Uncompressed block_reversed == 1 is not implemented yet");
230
0
  }
231
0
  if ((uncC->get_pixel_size() != 0) && ((uncC->get_interleave_type() != interleave_mode_pixel) && (uncC->get_interleave_type() != interleave_mode_multi_y))) {
232
0
    std::stringstream sstr;
233
0
    sstr << "Uncompressed pixel_size of " << ((int) uncC->get_pixel_size()) << " is only valid with interleave_type 1 or 5 (ISO/IEC 23001-17 5.2.1.7)";
234
0
    return Error(heif_error_Invalid_input,
235
0
                 heif_suberror_Invalid_parameter_value,
236
0
                 sstr.str());
237
0
  }
238
0
  return Error::Ok;
239
0
}
240
241
242
Error UncompressedImageCodec::get_heif_chroma_uncompressed(const std::shared_ptr<const Box_uncC>& uncC,
243
                                                           const std::shared_ptr<const Box_cmpd>& cmpd,
244
                                                           heif_chroma* out_chroma, heif_colorspace* out_colourspace,
245
                                                           bool* out_has_alpha)
246
0
{
247
0
  bool dummy_has_alpha;
248
0
  if (out_has_alpha == nullptr) {
249
0
    out_has_alpha = &dummy_has_alpha;
250
0
  }
251
252
0
  *out_chroma = heif_chroma_undefined;
253
0
  *out_colourspace = heif_colorspace_undefined;
254
0
  *out_has_alpha = false;
255
256
0
  Error error = check_header_validity(std::nullopt, cmpd, uncC);
257
0
  if (error) {
258
0
    return error;
259
0
  }
260
261
262
0
  if (uncC != nullptr && uncC->get_version() == 1) {
263
0
    switch (uncC->get_profile()) {
264
0
      case fourcc("rgb3"):
265
0
        *out_chroma = heif_chroma_444;
266
0
        *out_colourspace = heif_colorspace_RGB;
267
0
        *out_has_alpha = false;
268
0
        return Error::Ok;
269
270
0
      case fourcc("abgr"):
271
0
      case fourcc("rgba"):
272
0
        *out_chroma = heif_chroma_444;
273
0
        *out_colourspace = heif_colorspace_RGB;
274
0
        *out_has_alpha = true;
275
0
        return Error::Ok;
276
277
0
      default:
278
0
        return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_image_type,
279
0
                     "unci image has unsupported profile");
280
0
    }
281
0
  }
282
283
284
  // each 1-bit represents an existing component in the image
285
0
  uint16_t componentSet = 0;
286
287
0
  for (Box_uncC::Component component : uncC->get_components()) {
288
0
    uint16_t component_index = component.component_index;
289
0
    uint16_t component_type = cmpd->get_components()[component_index].component_type;
290
291
0
    if (component_type > component_type_max_valid) {
292
0
      std::stringstream sstr;
293
0
      sstr << "a component_type > " << component_type_max_valid << " is not supported";
294
0
      return {heif_error_Unsupported_feature, heif_suberror_Invalid_parameter_value, sstr.str()};
295
0
    }
296
0
    if (component_type == component_type_padded) {
297
      // not relevant for determining chroma
298
0
      continue;
299
0
    }
300
0
    componentSet |= (1 << component_type);
301
0
  }
302
303
0
  *out_has_alpha = (componentSet & (1 << component_type_alpha)) != 0;
304
305
0
  if (componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue)) ||
306
0
      componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue) | (1 << component_type_alpha))) {
307
0
    *out_chroma = heif_chroma_444;
308
0
    *out_colourspace = heif_colorspace_RGB;
309
0
  }
310
311
0
  if (componentSet == ((1 << component_type_Y) | (1 << component_type_Cb) | (1 << component_type_Cr))) {
312
0
    switch (uncC->get_sampling_type()) {
313
0
      case sampling_mode_no_subsampling:
314
0
        *out_chroma = heif_chroma_444;
315
0
        break;
316
0
      case sampling_mode_422:
317
0
        *out_chroma = heif_chroma_422;
318
0
        break;
319
0
      case sampling_mode_420:
320
0
        *out_chroma = heif_chroma_420;
321
0
        break;
322
0
    }
323
0
    *out_colourspace = heif_colorspace_YCbCr;
324
0
  }
325
326
0
  if (componentSet == ((1 << component_type_monochrome)) || componentSet == ((1 << component_type_monochrome) | (1 << component_type_alpha))) {
327
    // mono or mono + alpha input, mono output.
328
0
    *out_chroma = heif_chroma_monochrome;
329
0
    *out_colourspace = heif_colorspace_monochrome;
330
0
  }
331
332
0
  if (componentSet == (1 << component_type_filter_array)) {
333
    // TODO - we should look up the components
334
0
    *out_chroma = heif_chroma_monochrome;
335
0
    *out_colourspace = heif_colorspace_monochrome;
336
0
  }
337
338
  // TODO: more combinations
339
340
0
  if (*out_chroma == heif_chroma_undefined) {
341
0
    return Error(heif_error_Unsupported_feature,
342
0
                 heif_suberror_Unsupported_data_version,
343
0
                 "Could not determine chroma");
344
0
  }
345
0
  else if (*out_colourspace == heif_colorspace_undefined) {
346
0
    return Error(heif_error_Unsupported_feature,
347
0
                 heif_suberror_Unsupported_data_version,
348
0
                 "Could not determine colourspace");
349
0
  }
350
0
  else {
351
0
    return Error::Ok;
352
0
  }
353
0
}
354
355
bool map_uncompressed_component_to_channel(const std::shared_ptr<const Box_cmpd>& cmpd,
356
                                           const std::shared_ptr<const Box_uncC>& uncC,
357
                                           Box_uncC::Component component,
358
                                           heif_channel* channel)
359
0
{
360
0
  uint16_t component_index = component.component_index;
361
0
  if (isKnownUncompressedFrameConfigurationBoxProfile(uncC)) {
362
0
    if (uncC->get_profile() == fourcc("rgb3")) {
363
0
      switch (component_index) {
364
0
        case 0:
365
0
          *channel = heif_channel_R;
366
0
          return true;
367
0
        case 1:
368
0
          *channel = heif_channel_G;
369
0
          return true;
370
0
        case 2:
371
0
          *channel = heif_channel_B;
372
0
          return true;
373
0
      }
374
0
    }
375
0
    else if (uncC->get_profile() == fourcc("rgba")) {
376
0
      switch (component_index) {
377
0
        case 0:
378
0
          *channel = heif_channel_R;
379
0
          return true;
380
0
        case 1:
381
0
          *channel = heif_channel_G;
382
0
          return true;
383
0
        case 2:
384
0
          *channel = heif_channel_B;
385
0
          return true;
386
0
        case 3:
387
0
          *channel = heif_channel_Alpha;
388
0
          return true;
389
0
      }
390
0
    }
391
0
    else if (uncC->get_profile() == fourcc("abgr")) {
392
0
      switch (component_index) {
393
0
        case 0:
394
0
          *channel = heif_channel_Alpha;
395
0
          return true;
396
0
        case 1:
397
0
          *channel = heif_channel_B;
398
0
          return true;
399
0
        case 2:
400
0
          *channel = heif_channel_G;
401
0
          return true;
402
0
        case 3:
403
0
          *channel = heif_channel_R;
404
0
          return true;
405
0
      }
406
0
    }
407
0
  }
408
0
  uint16_t component_type = cmpd->get_components()[component_index].component_type;
409
410
0
  switch (component_type) {
411
0
    case component_type_monochrome:
412
0
      *channel = heif_channel_Y;
413
0
      return true;
414
0
    case component_type_Y:
415
0
      *channel = heif_channel_Y;
416
0
      return true;
417
0
    case component_type_Cb:
418
0
      *channel = heif_channel_Cb;
419
0
      return true;
420
0
    case component_type_Cr:
421
0
      *channel = heif_channel_Cr;
422
0
      return true;
423
0
    case component_type_red:
424
0
      *channel = heif_channel_R;
425
0
      return true;
426
0
    case component_type_green:
427
0
      *channel = heif_channel_G;
428
0
      return true;
429
0
    case component_type_blue:
430
0
      *channel = heif_channel_B;
431
0
      return true;
432
0
    case component_type_alpha:
433
0
      *channel = heif_channel_Alpha;
434
0
      return true;
435
0
    case component_type_filter_array:
436
      // TODO: this is just a temporary hack
437
0
      *channel = heif_channel_Y;
438
0
      return true;
439
0
    case component_type_padded:
440
0
      return false;
441
0
    default:
442
0
      return false;
443
0
  }
444
0
}
445
446
447
448
static AbstractDecoder* makeDecoder(uint32_t width, uint32_t height, const std::shared_ptr<const Box_cmpd>& cmpd, const std::shared_ptr<const Box_uncC>& uncC)
449
0
{
450
0
  switch (uncC->get_interleave_type()) {
451
0
    case interleave_mode_component:
452
0
      return new ComponentInterleaveDecoder(width, height, cmpd, uncC);
453
0
    case interleave_mode_pixel:
454
0
      return new PixelInterleaveDecoder(width, height, cmpd, uncC);
455
0
    case interleave_mode_mixed:
456
0
      return new MixedInterleaveDecoder(width, height, cmpd, uncC);
457
0
    case interleave_mode_row:
458
0
      return new RowInterleaveDecoder(width, height, cmpd, uncC);
459
0
    case interleave_mode_tile_component:
460
0
      return new TileComponentInterleaveDecoder(width, height, cmpd, uncC);
461
0
    default:
462
0
      return nullptr;
463
0
  }
464
0
}
465
466
467
Result<std::shared_ptr<HeifPixelImage>> UncompressedImageCodec::create_image(const std::shared_ptr<const Box_cmpd> cmpd,
468
                                                                             const std::shared_ptr<const Box_uncC> uncC,
469
                                                                             uint32_t width,
470
                                                                             uint32_t height,
471
                                                                             const heif_security_limits* limits)
472
0
{
473
0
  auto img = std::make_shared<HeifPixelImage>();
474
0
  heif_chroma chroma = heif_chroma_undefined;
475
0
  heif_colorspace colourspace = heif_colorspace_undefined;
476
477
0
  Error error = get_heif_chroma_uncompressed(uncC, cmpd, &chroma, &colourspace, nullptr);
478
0
  if (error) {
479
0
    return error;
480
0
  }
481
0
  img->create(width, height,
482
0
              colourspace,
483
0
              chroma);
484
485
0
  for (Box_uncC::Component component : uncC->get_components()) {
486
0
    heif_channel channel;
487
0
    if (map_uncompressed_component_to_channel(cmpd, uncC, component, &channel)) {
488
0
      if (img->has_channel(channel)) {
489
0
        return Error{heif_error_Unsupported_feature,
490
0
                     heif_suberror_Unspecified,
491
0
                     "Cannot generate image with several similar heif_channels."};
492
0
      }
493
494
0
      if ((channel == heif_channel_Cb) || (channel == heif_channel_Cr)) {
495
0
        if (auto err = img->add_plane(channel, (width / chroma_h_subsampling(chroma)), (height / chroma_v_subsampling(chroma)), component.component_bit_depth,
496
0
                                      limits)) {
497
0
          return err;
498
0
        }
499
0
      }
500
0
      else {
501
0
        if (auto err = img->add_plane(channel, width, height, component.component_bit_depth, limits)) {
502
0
          return err;
503
0
        }
504
0
      }
505
0
    }
506
0
  }
507
508
0
  return img;
509
0
}
510
511
512
Error UncompressedImageCodec::decode_uncompressed_image_tile(const HeifContext* context,
513
                                                             heif_item_id ID,
514
                                                             std::shared_ptr<HeifPixelImage>& img,
515
                                                             uint32_t tile_x0, uint32_t tile_y0)
516
0
{
517
0
  auto file = context->get_heif_file();
518
0
  auto image = context->get_image(ID, false);
519
0
  if (!image) {
520
0
    return {heif_error_Invalid_input,
521
0
            heif_suberror_Nonexisting_item_referenced};
522
0
  }
523
524
0
  UncompressedImageCodec::unci_properties properties;
525
0
  properties.fill_from_image_item(image);
526
527
0
  auto ispe = properties.ispe;
528
0
  auto uncC = properties.uncC;
529
0
  auto cmpd = properties.cmpd;
530
531
0
  Error error = check_header_validity(ispe, cmpd, uncC);
532
0
  if (error) {
533
0
    return error;
534
0
  }
535
536
0
  uint32_t tile_width = ispe->get_width() / uncC->get_number_of_tile_columns();
537
0
  uint32_t tile_height = ispe->get_height() / uncC->get_number_of_tile_rows();
538
539
0
  Result<std::shared_ptr<HeifPixelImage>> createImgResult = create_image(cmpd, uncC, tile_width, tile_height, context->get_security_limits());
540
0
  if (createImgResult.error) {
541
0
    return createImgResult.error;
542
0
  }
543
544
0
  img = createImgResult.value;
545
546
547
0
  AbstractDecoder* decoder = makeDecoder(ispe->get_width(), ispe->get_height(), cmpd, uncC);
548
0
  if (decoder == nullptr) {
549
0
    std::stringstream sstr;
550
0
    sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet";
551
0
    return Error(heif_error_Unsupported_feature,
552
0
                 heif_suberror_Unsupported_data_version,
553
0
                 sstr.str());
554
0
  }
555
556
0
  decoder->buildChannelList(img);
557
558
0
  DataExtent dataExtent;
559
0
  dataExtent.set_from_image_item(file, ID);
560
561
0
  Error result = decoder->decode_tile(dataExtent, properties, img, 0, 0,
562
0
                                      ispe->get_width(), ispe->get_height(),
563
0
                                      tile_x0, tile_y0);
564
0
  delete decoder;
565
0
  return result;
566
0
}
567
568
569
Error UncompressedImageCodec::check_header_validity(std::optional<const std::shared_ptr<const Box_ispe>> ispe,
570
                                                    const std::shared_ptr<const Box_cmpd>& cmpd,
571
                                                    const std::shared_ptr<const Box_uncC>& uncC)
572
0
{
573
  // if we miss a required box, show error
574
575
0
  if (!uncC) {
576
0
    return {heif_error_Unsupported_feature,
577
0
            heif_suberror_Unsupported_data_version,
578
0
            "Missing required uncC box for uncompressed codec"};
579
0
  }
580
581
0
  if (!cmpd && (uncC->get_version() != 1)) {
582
0
    return {heif_error_Unsupported_feature,
583
0
            heif_suberror_Unsupported_data_version,
584
0
            "Missing required cmpd or uncC version 1 box for uncompressed codec"};
585
0
  }
586
587
0
  if (cmpd) {
588
0
    for (const auto& comp : uncC->get_components()) {
589
0
      if (comp.component_index > cmpd->get_components().size()) {
590
0
        return {heif_error_Invalid_input,
591
0
                heif_suberror_Unspecified,
592
0
                "Invalid component index in uncC box"};
593
0
      }
594
0
    }
595
0
  }
596
597
598
0
  if (ispe) {
599
0
    if (!*ispe) {
600
0
      return {heif_error_Unsupported_feature,
601
0
              heif_suberror_Unsupported_data_version,
602
0
              "Missing required ispe box for uncompressed codec"};
603
0
    }
604
605
0
    if (uncC->get_number_of_tile_rows() > (*ispe)->get_height() ||
606
0
        uncC->get_number_of_tile_columns() > (*ispe)->get_width()) {
607
0
      return {heif_error_Invalid_input,
608
0
              heif_suberror_Unspecified,
609
0
              "More tiles than pixels in uncC box"};
610
0
    }
611
612
0
    if ((*ispe)->get_height() % uncC->get_number_of_tile_rows() != 0 ||
613
0
        (*ispe)->get_width() % uncC->get_number_of_tile_columns() != 0) {
614
0
      return {heif_error_Invalid_input,
615
0
              heif_suberror_Unspecified,
616
0
              "Invalid tile size (image size not a multiple of the tile size)"};
617
0
    }
618
0
  }
619
620
0
  return Error::Ok;
621
0
}
622
623
624
// TODO: this should be deprecated and replaced with the function taking unci_properties/DataExtent
625
Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* context,
626
                                                        heif_item_id ID,
627
                                                        std::shared_ptr<HeifPixelImage>& img)
628
0
{
629
  // Get the properties for this item
630
  // We need: ispe, cmpd, uncC
631
0
  std::vector<std::shared_ptr<Box>> item_properties;
632
0
  Error error = context->get_heif_file()->get_properties(ID, item_properties);
633
0
  if (error) {
634
0
    return error;
635
0
  }
636
637
0
  auto image = context->get_image(ID, false);
638
0
  if (!image) {
639
0
    return {heif_error_Invalid_input,
640
0
            heif_suberror_Nonexisting_item_referenced};
641
0
  }
642
643
0
  UncompressedImageCodec::unci_properties properties;
644
0
  properties.fill_from_image_item(image);
645
646
0
  auto ispe = properties.ispe;
647
0
  auto uncC = properties.uncC;
648
0
  auto cmpd = properties.cmpd;
649
650
0
  error = check_header_validity(ispe, cmpd, uncC);
651
0
  if (error) {
652
0
    return error;
653
0
  }
654
655
  // check if we support the type of image
656
657
0
  error = uncompressed_image_type_is_supported(uncC, cmpd); // TODO TODO TODO
658
0
  if (error) {
659
0
    return error;
660
0
  }
661
662
0
  assert(ispe);
663
0
  uint32_t width = ispe->get_width();
664
0
  uint32_t height = ispe->get_height();
665
0
  error = check_for_valid_image_size(context->get_security_limits(), width, height);
666
0
  if (error) {
667
0
    return error;
668
0
  }
669
670
0
  Result<std::shared_ptr<HeifPixelImage>> createImgResult = create_image(cmpd, uncC, width, height, context->get_security_limits());
671
0
  if (createImgResult.error) {
672
0
    return createImgResult.error;
673
0
  }
674
0
  else {
675
0
    img = *createImgResult;
676
0
  }
677
678
0
  AbstractDecoder* decoder = makeDecoder(width, height, cmpd, uncC);
679
0
  if (decoder == nullptr) {
680
0
    std::stringstream sstr;
681
0
    sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet";
682
0
    return Error(heif_error_Unsupported_feature,
683
0
                 heif_suberror_Unsupported_data_version,
684
0
                 sstr.str());
685
0
  }
686
687
0
  decoder->buildChannelList(img);
688
689
0
  uint32_t tile_width = width / uncC->get_number_of_tile_columns();
690
0
  uint32_t tile_height = height / uncC->get_number_of_tile_rows();
691
692
0
  DataExtent dataExtent;
693
0
  dataExtent.set_from_image_item(context->get_heif_file(), ID);
694
695
0
  for (uint32_t tile_y0 = 0; tile_y0 < height; tile_y0 += tile_height)
696
0
    for (uint32_t tile_x0 = 0; tile_x0 < width; tile_x0 += tile_width) {
697
0
      error = decoder->decode_tile(dataExtent, properties, img, tile_x0, tile_y0,
698
0
                                   width, height,
699
0
                                   tile_x0 / tile_width, tile_y0 / tile_height);
700
0
      if (error) {
701
0
        delete decoder;
702
0
        return error;
703
0
      }
704
0
    }
705
706
  //Error result = decoder->decode(source_data, img);
707
0
  delete decoder;
708
0
  return Error::Ok;
709
0
}
710
711
712
void UncompressedImageCodec::unci_properties::fill_from_image_item(const std::shared_ptr<const ImageItem>& image)
713
0
{
714
0
  ispe = image->get_property<Box_ispe>();
715
0
  cmpd = image->get_property<Box_cmpd>();
716
0
  uncC = image->get_property<Box_uncC>();
717
0
  cmpC = image->get_property<Box_cmpC>();
718
0
  icef = image->get_property<Box_icef>();
719
0
}
720
721
722
Result<std::shared_ptr<HeifPixelImage>>
723
UncompressedImageCodec::decode_uncompressed_image(const UncompressedImageCodec::unci_properties& properties,
724
                                                  const DataExtent& extent,
725
                                                  const heif_security_limits* securityLimits)
726
0
{
727
0
  std::shared_ptr<HeifPixelImage> img;
728
729
0
  const std::shared_ptr<const Box_ispe>& ispe = properties.ispe;
730
0
  const std::shared_ptr<const Box_cmpd>& cmpd = properties.cmpd;
731
0
  const std::shared_ptr<const Box_uncC>& uncC = properties.uncC;
732
733
0
  Error error = check_header_validity(ispe, cmpd, uncC);
734
0
  if (error) {
735
0
    return error;
736
0
  }
737
738
  // check if we support the type of image
739
740
0
  error = uncompressed_image_type_is_supported(uncC, cmpd); // TODO TODO TODO
741
0
  if (error) {
742
0
    return error;
743
0
  }
744
745
0
  assert(ispe);
746
0
  uint32_t width = ispe->get_width();
747
0
  uint32_t height = ispe->get_height();
748
0
  error = check_for_valid_image_size(securityLimits, width, height);
749
0
  if (error) {
750
0
    return error;
751
0
  }
752
753
0
  Result<std::shared_ptr<HeifPixelImage>> createImgResult = create_image(cmpd, uncC, width, height, securityLimits);
754
0
  if (createImgResult.error) {
755
0
    return createImgResult.error;
756
0
  }
757
0
  else {
758
0
    img = *createImgResult;
759
0
  }
760
761
0
  AbstractDecoder* decoder = makeDecoder(width, height, cmpd, uncC);
762
0
  if (decoder == nullptr) {
763
0
    std::stringstream sstr;
764
0
    sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet";
765
0
    return Error(heif_error_Unsupported_feature,
766
0
                 heif_suberror_Unsupported_data_version,
767
0
                 sstr.str());
768
0
  }
769
770
0
  decoder->buildChannelList(img);
771
772
0
  uint32_t tile_width = width / uncC->get_number_of_tile_columns();
773
0
  uint32_t tile_height = height / uncC->get_number_of_tile_rows();
774
775
0
  for (uint32_t tile_y0 = 0; tile_y0 < height; tile_y0 += tile_height)
776
0
    for (uint32_t tile_x0 = 0; tile_x0 < width; tile_x0 += tile_width) {
777
0
      error = decoder->decode_tile(extent, properties, img, tile_x0, tile_y0,
778
0
                                   width, height,
779
0
                                   tile_x0 / tile_width, tile_y0 / tile_height);
780
0
      if (error) {
781
0
        delete decoder;
782
0
        return error;
783
0
      }
784
0
    }
785
786
  //Error result = decoder->decode(source_data, img);
787
0
  delete decoder;
788
0
  return img;
789
0
}
790
791
792
Error fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& cmpd,
793
                         std::shared_ptr<Box_uncC>& uncC,
794
                         const std::shared_ptr<const HeifPixelImage>& image,
795
                         const heif_unci_image_parameters* parameters)
796
0
{
797
0
  uint32_t nTileColumns = parameters->image_width / parameters->tile_width;
798
0
  uint32_t nTileRows = parameters->image_height / parameters->tile_height;
799
800
0
  const heif_colorspace colourspace = image->get_colorspace();
801
0
  if (colourspace == heif_colorspace_YCbCr) {
802
0
    if (!(image->has_channel(heif_channel_Y) && image->has_channel(heif_channel_Cb) && image->has_channel(heif_channel_Cr))) {
803
0
      return Error(heif_error_Unsupported_feature,
804
0
                   heif_suberror_Unsupported_data_version,
805
0
                   "Invalid colourspace / channel combination - YCbCr");
806
0
    }
807
0
    Box_cmpd::Component yComponent = {component_type_Y};
808
0
    cmpd->add_component(yComponent);
809
0
    Box_cmpd::Component cbComponent = {component_type_Cb};
810
0
    cmpd->add_component(cbComponent);
811
0
    Box_cmpd::Component crComponent = {component_type_Cr};
812
0
    cmpd->add_component(crComponent);
813
0
    uint8_t bpp_y = image->get_bits_per_pixel(heif_channel_Y);
814
0
    Box_uncC::Component component0 = {0, bpp_y, component_format_unsigned, 0};
815
0
    uncC->add_component(component0);
816
0
    uint8_t bpp_cb = image->get_bits_per_pixel(heif_channel_Cb);
817
0
    Box_uncC::Component component1 = {1, bpp_cb, component_format_unsigned, 0};
818
0
    uncC->add_component(component1);
819
0
    uint8_t bpp_cr = image->get_bits_per_pixel(heif_channel_Cr);
820
0
    Box_uncC::Component component2 = {2, bpp_cr, component_format_unsigned, 0};
821
0
    uncC->add_component(component2);
822
0
    if (image->get_chroma_format() == heif_chroma_444) {
823
0
      uncC->set_sampling_type(sampling_mode_no_subsampling);
824
0
    }
825
0
    else if (image->get_chroma_format() == heif_chroma_422) {
826
0
      uncC->set_sampling_type(sampling_mode_422);
827
0
    }
828
0
    else if (image->get_chroma_format() == heif_chroma_420) {
829
0
      uncC->set_sampling_type(sampling_mode_420);
830
0
    }
831
0
    else {
832
0
      return Error(heif_error_Unsupported_feature,
833
0
                   heif_suberror_Unsupported_data_version,
834
0
                   "Unsupported YCbCr sub-sampling type");
835
0
    }
836
0
    uncC->set_interleave_type(interleave_mode_component);
837
0
    uncC->set_block_size(0);
838
0
    uncC->set_components_little_endian(false);
839
0
    uncC->set_block_pad_lsb(false);
840
0
    uncC->set_block_little_endian(false);
841
0
    uncC->set_block_reversed(false);
842
0
    uncC->set_pad_unknown(false);
843
0
    uncC->set_pixel_size(0);
844
0
    uncC->set_row_align_size(0);
845
0
    uncC->set_tile_align_size(0);
846
0
    uncC->set_number_of_tile_columns(nTileColumns);
847
0
    uncC->set_number_of_tile_rows(nTileRows);
848
0
  }
849
0
  else if (colourspace == heif_colorspace_RGB) {
850
0
    if (!((image->get_chroma_format() == heif_chroma_444) ||
851
0
          (image->get_chroma_format() == heif_chroma_interleaved_RGB) ||
852
0
          (image->get_chroma_format() == heif_chroma_interleaved_RGBA) ||
853
0
          (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_BE) ||
854
0
          (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) ||
855
0
          (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) ||
856
0
          (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE))) {
857
0
      return Error(heif_error_Unsupported_feature,
858
0
                   heif_suberror_Unsupported_data_version,
859
0
                   "Unsupported colourspace / chroma combination - RGB");
860
0
    }
861
0
    Box_cmpd::Component rComponent = {component_type_red};
862
0
    cmpd->add_component(rComponent);
863
0
    Box_cmpd::Component gComponent = {component_type_green};
864
0
    cmpd->add_component(gComponent);
865
0
    Box_cmpd::Component bComponent = {component_type_blue};
866
0
    cmpd->add_component(bComponent);
867
0
    if ((image->get_chroma_format() == heif_chroma_interleaved_RGBA) ||
868
0
        (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) ||
869
0
        (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE) ||
870
0
        (image->has_channel(heif_channel_Alpha))) {
871
0
      Box_cmpd::Component alphaComponent = {component_type_alpha};
872
0
      cmpd->add_component(alphaComponent);
873
0
    }
874
0
    if ((image->get_chroma_format() == heif_chroma_interleaved_RGB) ||
875
0
        (image->get_chroma_format() == heif_chroma_interleaved_RGBA) ||
876
0
        (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_BE) ||
877
0
        (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) ||
878
0
        (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) ||
879
0
        (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) {
880
0
      uncC->set_interleave_type(interleave_mode_pixel);
881
0
      int bpp = image->get_bits_per_pixel(heif_channel_interleaved);
882
0
      uint8_t component_align = 1;
883
0
      if (bpp == 8) {
884
0
        component_align = 0;
885
0
      }
886
0
      else if (bpp > 8) {
887
0
        component_align = 2;
888
0
      }
889
0
      Box_uncC::Component component0 = {0, (uint8_t) (bpp), component_format_unsigned, component_align};
890
0
      uncC->add_component(component0);
891
0
      Box_uncC::Component component1 = {1, (uint8_t) (bpp), component_format_unsigned, component_align};
892
0
      uncC->add_component(component1);
893
0
      Box_uncC::Component component2 = {2, (uint8_t) (bpp), component_format_unsigned, component_align};
894
0
      uncC->add_component(component2);
895
0
      if ((image->get_chroma_format() == heif_chroma_interleaved_RGBA) ||
896
0
          (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) ||
897
0
          (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) {
898
0
        Box_uncC::Component component3 = {
899
0
            3, (uint8_t) (bpp), component_format_unsigned, component_align};
900
0
        uncC->add_component(component3);
901
0
      }
902
0
    }
903
0
    else {
904
0
      uncC->set_interleave_type(interleave_mode_component);
905
0
      int bpp_red = image->get_bits_per_pixel(heif_channel_R);
906
0
      Box_uncC::Component component0 = {0, (uint8_t) (bpp_red), component_format_unsigned, 0};
907
0
      uncC->add_component(component0);
908
0
      int bpp_green = image->get_bits_per_pixel(heif_channel_G);
909
0
      Box_uncC::Component component1 = {1, (uint8_t) (bpp_green), component_format_unsigned, 0};
910
0
      uncC->add_component(component1);
911
0
      int bpp_blue = image->get_bits_per_pixel(heif_channel_B);
912
0
      Box_uncC::Component component2 = {2, (uint8_t) (bpp_blue), component_format_unsigned, 0};
913
0
      uncC->add_component(component2);
914
0
      if (image->has_channel(heif_channel_Alpha)) {
915
0
        int bpp_alpha = image->get_bits_per_pixel(heif_channel_Alpha);
916
0
        Box_uncC::Component component3 = {3, (uint8_t) (bpp_alpha), component_format_unsigned, 0};
917
0
        uncC->add_component(component3);
918
0
      }
919
0
    }
920
0
    uncC->set_sampling_type(sampling_mode_no_subsampling);
921
0
    uncC->set_block_size(0);
922
0
    if ((image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) ||
923
0
        (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) {
924
0
      uncC->set_components_little_endian(true);
925
0
    }
926
0
    else {
927
0
      uncC->set_components_little_endian(false);
928
0
    }
929
0
    uncC->set_block_pad_lsb(false);
930
0
    uncC->set_block_little_endian(false);
931
0
    uncC->set_block_reversed(false);
932
0
    uncC->set_pad_unknown(false);
933
0
    uncC->set_pixel_size(0);
934
0
    uncC->set_row_align_size(0);
935
0
    uncC->set_tile_align_size(0);
936
0
    uncC->set_number_of_tile_columns(nTileColumns);
937
0
    uncC->set_number_of_tile_rows(nTileRows);
938
0
  }
939
0
  else if (colourspace == heif_colorspace_monochrome) {
940
0
    Box_cmpd::Component monoComponent = {component_type_monochrome};
941
0
    cmpd->add_component(monoComponent);
942
0
    if (image->has_channel(heif_channel_Alpha)) {
943
0
      Box_cmpd::Component alphaComponent = {component_type_alpha};
944
0
      cmpd->add_component(alphaComponent);
945
0
    }
946
0
    int bpp = image->get_bits_per_pixel(heif_channel_Y);
947
0
    Box_uncC::Component component0 = {0, (uint8_t) (bpp), component_format_unsigned, 0};
948
0
    uncC->add_component(component0);
949
0
    if (image->has_channel(heif_channel_Alpha)) {
950
0
      bpp = image->get_bits_per_pixel(heif_channel_Alpha);
951
0
      Box_uncC::Component component1 = {1, (uint8_t) (bpp), component_format_unsigned, 0};
952
0
      uncC->add_component(component1);
953
0
    }
954
0
    uncC->set_sampling_type(sampling_mode_no_subsampling);
955
0
    uncC->set_interleave_type(interleave_mode_component);
956
0
    uncC->set_block_size(0);
957
0
    uncC->set_components_little_endian(false);
958
0
    uncC->set_block_pad_lsb(false);
959
0
    uncC->set_block_little_endian(false);
960
0
    uncC->set_block_reversed(false);
961
0
    uncC->set_pad_unknown(false);
962
0
    uncC->set_pixel_size(0);
963
0
    uncC->set_row_align_size(0);
964
0
    uncC->set_tile_align_size(0);
965
0
    uncC->set_number_of_tile_columns(nTileColumns);
966
0
    uncC->set_number_of_tile_rows(nTileRows);
967
0
  }
968
0
  else {
969
0
    return Error(heif_error_Unsupported_feature,
970
0
                 heif_suberror_Unsupported_data_version,
971
0
                 "Unsupported colourspace");
972
0
  }
973
0
  return Error::Ok;
974
0
}