Coverage Report

Created: 2025-11-16 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/pixelimage.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2017 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 "pixelimage.h"
23
#include "common_utils.h"
24
#include "security_limits.h"
25
26
#include <cassert>
27
#include <cstring>
28
#include <utility>
29
#include <limits>
30
#include <algorithm>
31
#include <color-conversion/colorconversion.h>
32
33
34
heif_chroma chroma_from_subsampling(int h, int v)
35
0
{
36
0
  if (h == 2 && v == 2) {
37
0
    return heif_chroma_420;
38
0
  }
39
0
  else if (h == 2 && v == 1) {
40
0
    return heif_chroma_422;
41
0
  }
42
0
  else if (h == 1 && v == 1) {
43
0
    return heif_chroma_444;
44
0
  }
45
0
  else {
46
0
    assert(false);
47
0
    return heif_chroma_undefined;
48
0
  }
49
0
}
50
51
52
uint32_t chroma_width(uint32_t w, heif_chroma chroma)
53
36
{
54
36
  switch (chroma) {
55
0
    case heif_chroma_420:
56
0
    case heif_chroma_422:
57
0
      return (w+1)/2;
58
36
    default:
59
36
      return w;
60
36
  }
61
36
}
62
63
uint32_t chroma_height(uint32_t h, heif_chroma chroma)
64
36
{
65
36
  switch (chroma) {
66
0
    case heif_chroma_420:
67
0
      return (h+1)/2;
68
36
    default:
69
36
      return h;
70
36
  }
71
36
}
72
73
uint32_t channel_width(uint32_t w, heif_chroma chroma, heif_channel channel)
74
40
{
75
40
  if (channel == heif_channel_Cb || channel == heif_channel_Cr) {
76
24
    return chroma_width(w, chroma);
77
24
  }
78
16
  else {
79
16
    return w;
80
16
  }
81
40
}
82
83
uint32_t channel_height(uint32_t h, heif_chroma chroma, heif_channel channel)
84
40
{
85
40
  if (channel == heif_channel_Cb || channel == heif_channel_Cr) {
86
24
    return chroma_height(h, chroma);
87
24
  }
88
16
  else {
89
16
    return h;
90
16
  }
91
40
}
92
93
94
ImageExtraData::~ImageExtraData()
95
56.7k
{
96
56.7k
  heif_tai_timestamp_packet_release(m_tai_timestamp);
97
56.7k
}
98
99
100
bool ImageExtraData::has_nclx_color_profile() const
101
48.6k
{
102
48.6k
  return m_color_profile_nclx != nclx_profile::defaults();
103
48.6k
}
104
105
106
nclx_profile ImageExtraData::get_color_profile_nclx_with_fallback() const
107
2.51k
{
108
2.51k
  if (has_nclx_color_profile()) {
109
2.45k
    return get_color_profile_nclx();
110
2.45k
  }
111
60
  else {
112
60
    return nclx_profile::defaults();
113
60
  }
114
2.51k
}
115
116
117
std::shared_ptr<Box_clli> ImageExtraData::get_clli_box() const
118
0
{
119
0
  if (!has_clli()) {
120
0
    return {};
121
0
  }
122
123
0
  auto clli = std::make_shared<Box_clli>();
124
0
  clli->clli = get_clli();
125
126
0
  return clli;
127
0
}
128
129
130
std::shared_ptr<Box_mdcv> ImageExtraData::get_mdcv_box() const
131
0
{
132
0
  if (!has_mdcv()) {
133
0
    return {};
134
0
  }
135
136
0
  auto mdcv = std::make_shared<Box_mdcv>();
137
0
  mdcv->mdcv = get_mdcv();
138
139
0
  return mdcv;
140
0
}
141
142
143
std::shared_ptr<Box_pasp> ImageExtraData::get_pasp_box() const
144
0
{
145
0
  if (!has_nonsquare_pixel_ratio()) {
146
0
    return {};
147
0
  }
148
149
0
  auto pasp = std::make_shared<Box_pasp>();
150
0
  pasp->hSpacing = m_PixelAspectRatio_h;
151
0
  pasp->vSpacing = m_PixelAspectRatio_v;
152
153
0
  return pasp;
154
0
}
155
156
157
std::shared_ptr<Box_colr> ImageExtraData::get_colr_box_nclx() const
158
5.83k
{
159
5.83k
  if (!has_nclx_color_profile()) {
160
1.37k
    return {};
161
1.37k
  }
162
163
4.45k
  auto colr = std::make_shared<Box_colr>();
164
4.45k
  colr->set_color_profile(std::make_shared<color_profile_nclx>(get_color_profile_nclx()));
165
4.45k
  return colr;
166
5.83k
}
167
168
169
std::shared_ptr<Box_colr> ImageExtraData::get_colr_box_icc() const
170
3.27k
{
171
3.27k
  if (!has_icc_color_profile()) {
172
0
    return {};
173
0
  }
174
175
3.27k
  auto colr = std::make_shared<Box_colr>();
176
3.27k
  colr->set_color_profile(get_color_profile_icc());
177
3.27k
  return colr;
178
3.27k
}
179
180
181
std::vector<std::shared_ptr<Box>> ImageExtraData::generate_property_boxes() const
182
0
{
183
0
  std::vector<std::shared_ptr<Box>> properties;
184
185
  // --- write PASP property
186
187
0
  if (has_nonsquare_pixel_ratio()) {
188
0
    auto pasp = std::make_shared<Box_pasp>();
189
0
    get_pixel_ratio(&pasp->hSpacing, &pasp->vSpacing);
190
191
0
    properties.push_back(pasp);
192
0
  }
193
194
195
  // --- write CLLI property
196
197
0
  if (has_clli()) {
198
0
    properties.push_back(get_clli_box());
199
0
  }
200
201
202
  // --- write MDCV property
203
204
0
  if (has_mdcv()) {
205
0
    auto mdcv = std::make_shared<Box_mdcv>();
206
0
    mdcv->mdcv = get_mdcv();
207
208
0
    properties.push_back(mdcv);
209
0
  }
210
211
212
  // --- write TAI property
213
214
0
  if (auto* tai = get_tai_timestamp()) {
215
0
    auto itai = std::make_shared<Box_itai>();
216
0
    itai->set_from_tai_timestamp_packet(tai);
217
218
0
    properties.push_back(itai);
219
0
  }
220
221
  // --- colr (nclx)
222
223
0
  if (has_nclx_color_profile()) {
224
0
    properties.push_back(get_colr_box_nclx());
225
0
  }
226
227
  // --- colr (icc)
228
229
0
  if (has_icc_color_profile()) {
230
0
    properties.push_back(get_colr_box_icc());
231
0
  }
232
233
0
  return properties;
234
0
}
235
236
237
238
HeifPixelImage::~HeifPixelImage()
239
5.18k
{
240
15.1k
  for (auto& iter : m_planes) {
241
15.1k
    delete[] iter.second.allocated_mem;
242
15.1k
  }
243
5.18k
}
244
245
246
int num_interleaved_pixels_per_plane(heif_chroma chroma)
247
17.6k
{
248
17.6k
  switch (chroma) {
249
0
    case heif_chroma_undefined:
250
500
    case heif_chroma_monochrome:
251
2.40k
    case heif_chroma_420:
252
2.58k
    case heif_chroma_422:
253
17.6k
    case heif_chroma_444:
254
17.6k
      return 1;
255
256
0
    case heif_chroma_interleaved_RGB:
257
0
    case heif_chroma_interleaved_RRGGBB_BE:
258
0
    case heif_chroma_interleaved_RRGGBB_LE:
259
0
      return 3;
260
261
0
    case heif_chroma_interleaved_RGBA:
262
0
    case heif_chroma_interleaved_RRGGBBAA_BE:
263
0
    case heif_chroma_interleaved_RRGGBBAA_LE:
264
0
      return 4;
265
17.6k
  }
266
267
17.6k
  assert(false);
268
0
  return 0;
269
0
}
270
271
272
bool is_integer_multiple_of_chroma_size(uint32_t width,
273
                                        uint32_t height,
274
                                        heif_chroma chroma)
275
0
{
276
0
  switch (chroma) {
277
0
    case heif_chroma_444:
278
0
    case heif_chroma_monochrome:
279
0
      return true;
280
0
    case heif_chroma_422:
281
0
      return (width & 1) == 0;
282
0
    case heif_chroma_420:
283
0
      return (width & 1) == 0 && (height & 1) == 0;
284
0
    default:
285
0
      assert(false);
286
0
      return false;
287
0
  }
288
0
}
289
290
291
std::vector<heif_chroma> get_valid_chroma_values_for_colorspace(heif_colorspace colorspace)
292
2.56k
{
293
2.56k
  switch (colorspace) {
294
2.39k
    case heif_colorspace_YCbCr:
295
2.39k
      return {heif_chroma_420, heif_chroma_422, heif_chroma_444};
296
297
0
    case heif_colorspace_RGB:
298
0
      return {heif_chroma_444,
299
0
              heif_chroma_interleaved_RGB,
300
0
              heif_chroma_interleaved_RGBA,
301
0
              heif_chroma_interleaved_RRGGBB_BE,
302
0
              heif_chroma_interleaved_RRGGBBAA_BE,
303
0
              heif_chroma_interleaved_RRGGBB_LE,
304
0
              heif_chroma_interleaved_RRGGBBAA_LE};
305
306
172
    case heif_colorspace_monochrome:
307
172
      return {heif_chroma_monochrome};
308
309
0
    case heif_colorspace_nonvisual:
310
0
      return {heif_chroma_undefined};
311
312
0
    default:
313
0
      return {};
314
2.56k
  }
315
2.56k
}
316
317
318
void HeifPixelImage::create(uint32_t width, uint32_t height, heif_colorspace colorspace, heif_chroma chroma)
319
5.18k
{
320
5.18k
  m_width = width;
321
5.18k
  m_height = height;
322
5.18k
  m_colorspace = colorspace;
323
5.18k
  m_chroma = chroma;
324
5.18k
}
325
326
static uint32_t rounded_size(uint32_t s)
327
30.2k
{
328
30.2k
  s = (s + 1U) & ~1U;
329
330
30.2k
  if (s < 64) {
331
10.1k
    s = 64;
332
10.1k
  }
333
334
30.2k
  return s;
335
30.2k
}
336
337
Error HeifPixelImage::add_plane(heif_channel channel, uint32_t width, uint32_t height, int bit_depth,
338
                                const heif_security_limits* limits)
339
15.1k
{
340
15.1k
  assert(!has_channel(channel));
341
342
15.1k
  ImagePlane plane;
343
15.1k
  int num_interleaved_pixels = num_interleaved_pixels_per_plane(m_chroma);
344
345
  // for backwards compatibility, allow for 24/32 bits for RGB/RGBA interleaved chromas
346
347
15.1k
  if (m_chroma == heif_chroma_interleaved_RGB && bit_depth == 24) {
348
0
    bit_depth = 8;
349
0
  }
350
351
15.1k
  if (m_chroma == heif_chroma_interleaved_RGBA && bit_depth == 32) {
352
0
    bit_depth = 8;
353
0
  }
354
355
15.1k
  if (auto err = plane.alloc(width, height, heif_channel_datatype_unsigned_integer, bit_depth, num_interleaved_pixels, limits, m_memory_handle)) {
356
29
    return err;
357
29
  }
358
15.1k
  else {
359
15.1k
    m_planes.insert(std::make_pair(channel, plane));
360
15.1k
    return Error::Ok;
361
15.1k
  }
362
15.1k
}
363
364
365
Error HeifPixelImage::add_channel(heif_channel channel, uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth,
366
                                  const heif_security_limits* limits)
367
0
{
368
0
  ImagePlane plane;
369
0
  if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits, m_memory_handle)) {
370
0
    return err;
371
0
  }
372
0
  else {
373
0
    m_planes.insert(std::make_pair(channel, plane));
374
0
    return Error::Ok;
375
0
  }
376
0
}
377
378
379
Error HeifPixelImage::ImagePlane::alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth,
380
                                        int num_interleaved_components,
381
                                        const heif_security_limits* limits,
382
                                        MemoryHandle& memory_handle)
383
15.1k
{
384
15.1k
  assert(bit_depth >= 1);
385
15.1k
  assert(bit_depth <= 128);
386
387
15.1k
  if (width == 0 || height == 0) {
388
0
    return {heif_error_Usage_error,
389
0
            heif_suberror_Unspecified,
390
0
            "Invalid image size"};
391
0
  }
392
393
  // use 16 byte alignment (enough for 128 bit data-types). Every row is an integer number of data-elements.
394
15.1k
  uint16_t alignment = 16; // must be power of two
395
396
15.1k
  m_width = width;
397
15.1k
  m_height = height;
398
399
15.1k
  m_mem_width = rounded_size(width);
400
15.1k
  m_mem_height = rounded_size(height);
401
402
15.1k
  assert(num_interleaved_components > 0 && num_interleaved_components <= 255);
403
404
15.1k
  m_bit_depth = static_cast<uint8_t>(bit_depth);
405
15.1k
  m_num_interleaved_components = static_cast<uint8_t>(num_interleaved_components);
406
15.1k
  m_datatype = datatype;
407
408
409
15.1k
  int bytes_per_component = get_bytes_per_pixel();
410
15.1k
  int bytes_per_pixel = num_interleaved_components * bytes_per_component;
411
412
15.1k
  stride = m_mem_width * bytes_per_pixel;
413
15.1k
  stride = (stride + alignment - 1U) & ~(alignment - 1U);
414
415
15.1k
  assert(alignment>=1);
416
417
15.1k
  if (limits &&
418
15.1k
      limits->max_image_size_pixels &&
419
15.1k
      limits->max_image_size_pixels / height < width) {
420
421
20
    std::stringstream sstr;
422
20
    sstr << "Allocating an image of size " << width << "x" << height << " exceeds the security limit of "
423
20
         << limits->max_image_size_pixels << " pixels";
424
425
20
    return {heif_error_Memory_allocation_error,
426
20
            heif_suberror_Security_limit_exceeded,
427
20
            sstr.str()};
428
20
  }
429
430
15.1k
  allocation_size = static_cast<size_t>(m_mem_height) * stride + alignment - 1;
431
432
15.1k
  if (auto err = memory_handle.alloc(allocation_size, limits, "image data")) {
433
9
    return err;
434
9
  }
435
436
    // --- allocate memory
437
438
15.1k
  allocated_mem = new (std::nothrow) uint8_t[allocation_size];
439
15.1k
  if (allocated_mem == nullptr) {
440
0
    std::stringstream sstr;
441
0
    sstr << "Allocating " << allocation_size << " bytes failed";
442
443
0
    return {heif_error_Memory_allocation_error,
444
0
            heif_suberror_Unspecified,
445
0
            sstr.str()};
446
0
  }
447
448
15.1k
  uint8_t* mem_8 = allocated_mem;
449
450
  // shift beginning of image data to aligned memory position
451
452
15.1k
  auto mem_start_addr = (uint64_t) mem_8;
453
15.1k
  auto mem_start_offset = (mem_start_addr & (alignment - 1U));
454
15.1k
  if (mem_start_offset != 0) {
455
0
    mem_8 += alignment - mem_start_offset;
456
0
  }
457
458
15.1k
  mem = mem_8;
459
460
15.1k
  return Error::Ok;
461
15.1k
}
462
463
464
Error HeifPixelImage::extend_padding_to_size(uint32_t width, uint32_t height, bool adjust_size,
465
                                             const heif_security_limits* limits)
466
0
{
467
0
  for (auto& planeIter : m_planes) {
468
0
    auto* plane = &planeIter.second;
469
470
0
    uint32_t subsampled_width, subsampled_height;
471
0
    get_subsampled_size(width, height, planeIter.first, m_chroma,
472
0
                        &subsampled_width, &subsampled_height);
473
474
0
    uint32_t old_width = plane->m_width;
475
0
    uint32_t old_height = plane->m_height;
476
477
0
    int bytes_per_pixel = get_storage_bits_per_pixel(planeIter.first) / 8;
478
479
0
    if (plane->m_mem_width < subsampled_width ||
480
0
        plane->m_mem_height < subsampled_height) {
481
482
0
      ImagePlane newPlane;
483
0
      if (auto err = newPlane.alloc(subsampled_width, subsampled_height, plane->m_datatype, plane->m_bit_depth,
484
0
                                    num_interleaved_pixels_per_plane(m_chroma),
485
0
                                    limits, m_memory_handle))
486
0
      {
487
0
        return err;
488
0
      }
489
490
      // This is not needed, but we have to silence the clang-tidy false positive.
491
0
      if (!newPlane.mem) {
492
0
        return Error::InternalError;
493
0
      }
494
495
      // copy the visible part of the old plane into the new plane
496
497
0
      for (uint32_t y = 0; y < plane->m_height; y++) {
498
0
        memcpy(static_cast<uint8_t*>(newPlane.mem) + y * newPlane.stride,
499
0
               static_cast<uint8_t*>(plane->mem) + y * plane->stride,
500
0
               plane->m_width * bytes_per_pixel);
501
0
      }
502
503
0
      planeIter.second = newPlane;
504
0
      plane = &planeIter.second;
505
0
    }
506
507
    // extend plane size
508
509
0
    if (old_width != subsampled_width) {
510
0
      for (uint32_t y = 0; y < old_height; y++) {
511
0
        for (uint32_t x = old_width; x < subsampled_width; x++) {
512
0
          memcpy(static_cast<uint8_t*>(plane->mem) + y * plane->stride + x * bytes_per_pixel,
513
0
                 static_cast<uint8_t*>(plane->mem) + y * plane->stride + (old_width - 1) * bytes_per_pixel,
514
0
                 bytes_per_pixel);
515
0
        }
516
0
      }
517
0
    }
518
519
0
    for (uint32_t y = old_height; y < subsampled_height; y++) {
520
0
      memcpy(static_cast<uint8_t*>(plane->mem) + y * plane->stride,
521
0
             static_cast<uint8_t*>(plane->mem) + (old_height - 1) * plane->stride,
522
0
             subsampled_width * bytes_per_pixel);
523
0
    }
524
525
526
0
    if (adjust_size) {
527
0
      plane->m_width = subsampled_width;
528
0
      plane->m_height = subsampled_height;
529
0
    }
530
0
  }
531
532
  // modify logical image size, if requested
533
534
0
  if (adjust_size) {
535
0
    m_width = width;
536
0
    m_height = height;
537
0
  }
538
539
0
  return Error::Ok;
540
0
}
541
542
543
Error HeifPixelImage::extend_to_size_with_zero(uint32_t width, uint32_t height, const heif_security_limits* limits)
544
0
{
545
0
  for (auto& planeIter : m_planes) {
546
0
    auto* plane = &planeIter.second;
547
548
0
    uint32_t subsampled_width, subsampled_height;
549
0
    get_subsampled_size(width, height, planeIter.first, m_chroma,
550
0
                        &subsampled_width, &subsampled_height);
551
552
0
    uint32_t old_width = plane->m_width;
553
0
    uint32_t old_height = plane->m_height;
554
555
0
    int bytes_per_pixel = get_storage_bits_per_pixel(planeIter.first) / 8;
556
557
0
    if (plane->m_mem_width < subsampled_width ||
558
0
        plane->m_mem_height < subsampled_height) {
559
560
0
      ImagePlane newPlane;
561
0
      if (auto err = newPlane.alloc(subsampled_width, subsampled_height, plane->m_datatype, plane->m_bit_depth, num_interleaved_pixels_per_plane(m_chroma), limits, m_memory_handle)) {
562
0
        return err;
563
0
      }
564
565
      // This is not needed, but we have to silence the clang-tidy false positive.
566
0
      if (!newPlane.mem) {
567
0
        return Error::InternalError;
568
0
      }
569
570
      // copy the visible part of the old plane into the new plane
571
572
0
      for (uint32_t y = 0; y < plane->m_height; y++) {
573
0
        memcpy(static_cast<uint8_t*>(newPlane.mem) + y * newPlane.stride,
574
0
               static_cast<uint8_t*>(plane->mem) + y * plane->stride,
575
0
               plane->m_width * bytes_per_pixel);
576
0
      }
577
578
0
      planeIter.second = newPlane;
579
0
      plane = &planeIter.second;
580
0
    }
581
582
    // extend plane size
583
584
0
    uint8_t fill = 0;
585
0
    if (bytes_per_pixel == 1 && (planeIter.first == heif_channel_Cb || planeIter.first == heif_channel_Cr)) {
586
0
      fill = 128;
587
0
    }
588
589
0
    if (old_width != subsampled_width) {
590
0
      for (uint32_t y = 0; y < old_height; y++) {
591
0
        memset(static_cast<uint8_t*>(plane->mem) + y * plane->stride + old_width * bytes_per_pixel,
592
0
               fill,
593
0
               bytes_per_pixel * (subsampled_width - old_width));
594
0
      }
595
0
    }
596
597
0
    for (uint32_t y = old_height; y < subsampled_height; y++) {
598
0
      memset(static_cast<uint8_t*>(plane->mem) + y * plane->stride,
599
0
             fill,
600
0
             subsampled_width * bytes_per_pixel);
601
0
    }
602
603
604
0
    plane->m_width = subsampled_width;
605
0
    plane->m_height = subsampled_height;
606
0
  }
607
608
  // modify the logical image size
609
610
0
  m_width = width;
611
0
  m_height = height;
612
613
0
  return Error::Ok;
614
0
}
615
616
bool HeifPixelImage::has_channel(heif_channel channel) const
617
22.6k
{
618
22.6k
  return (m_planes.find(channel) != m_planes.end());
619
22.6k
}
620
621
622
bool HeifPixelImage::has_alpha() const
623
4
{
624
4
  return has_channel(heif_channel_Alpha) ||
625
3
         get_chroma_format() == heif_chroma_interleaved_RGBA ||
626
3
         get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE ||
627
3
         get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE;
628
4
}
629
630
631
uint32_t HeifPixelImage::get_width(enum heif_channel channel) const
632
2.43k
{
633
2.43k
  auto iter = m_planes.find(channel);
634
2.43k
  if (iter == m_planes.end()) {
635
0
    return 0;
636
0
  }
637
638
2.43k
  return iter->second.m_width;
639
2.43k
}
640
641
642
uint32_t HeifPixelImage::get_height(enum heif_channel channel) const
643
2.43k
{
644
2.43k
  auto iter = m_planes.find(channel);
645
2.43k
  if (iter == m_planes.end()) {
646
0
    return 0;
647
0
  }
648
649
2.43k
  return iter->second.m_height;
650
2.43k
}
651
652
653
std::set<heif_channel> HeifPixelImage::get_channel_set() const
654
2.52k
{
655
2.52k
  std::set<heif_channel> channels;
656
657
7.37k
  for (const auto& plane : m_planes) {
658
7.37k
    channels.insert(plane.first);
659
7.37k
  }
660
661
2.52k
  return channels;
662
2.52k
}
663
664
665
uint8_t HeifPixelImage::get_storage_bits_per_pixel(enum heif_channel channel) const
666
6.77k
{
667
6.77k
  if (channel == heif_channel_interleaved) {
668
0
    auto chroma = get_chroma_format();
669
0
    switch (chroma) {
670
0
      case heif_chroma_interleaved_RGB:
671
0
        return 24;
672
0
      case heif_chroma_interleaved_RGBA:
673
0
        return 32;
674
0
      case heif_chroma_interleaved_RRGGBB_BE:
675
0
      case heif_chroma_interleaved_RRGGBB_LE:
676
0
        return 48;
677
0
      case heif_chroma_interleaved_RRGGBBAA_BE:
678
0
      case heif_chroma_interleaved_RRGGBBAA_LE:
679
0
        return 64;
680
0
      default:
681
0
        return -1; // invalid channel/chroma specification
682
0
    }
683
0
  }
684
6.77k
  else {
685
6.77k
    uint32_t bpp = (get_bits_per_pixel(channel) + 7U) & ~7U;
686
6.77k
    assert(bpp <= 255);
687
6.77k
    return static_cast<uint8_t>(bpp);
688
6.77k
  }
689
6.77k
}
690
691
692
uint8_t HeifPixelImage::get_bits_per_pixel(enum heif_channel channel) const
693
30.0k
{
694
30.0k
  auto iter = m_planes.find(channel);
695
30.0k
  if (iter == m_planes.end()) {
696
0
    return -1;
697
0
  }
698
699
30.0k
  return iter->second.m_bit_depth;
700
30.0k
}
701
702
703
uint8_t HeifPixelImage::get_visual_image_bits_per_pixel() const
704
2.51k
{
705
2.51k
  switch (m_colorspace) {
706
127
    case heif_colorspace_monochrome:
707
127
      return get_bits_per_pixel(heif_channel_Y);
708
0
      break;
709
2.33k
    case heif_colorspace_YCbCr:
710
2.33k
      return std::max(get_bits_per_pixel(heif_channel_Y),
711
2.33k
                      std::max(get_bits_per_pixel(heif_channel_Cb),
712
2.33k
                               get_bits_per_pixel(heif_channel_Cr)));
713
0
      break;
714
51
    case heif_colorspace_RGB:
715
51
      if (m_chroma == heif_chroma_444) {
716
51
        return std::max(get_bits_per_pixel(heif_channel_R),
717
51
             std::max(get_bits_per_pixel(heif_channel_G),
718
51
                        get_bits_per_pixel(heif_channel_B)));
719
51
      }
720
0
      else {
721
0
        assert(has_channel(heif_channel_interleaved));
722
0
        return get_bits_per_pixel(heif_channel_interleaved);
723
0
      }
724
0
      break;
725
0
    case heif_colorspace_nonvisual:
726
0
      return 0;
727
0
      break;
728
0
    default:
729
0
      assert(false);
730
0
      return 0;
731
0
      break;
732
2.51k
  }
733
2.51k
}
734
735
736
heif_channel_datatype HeifPixelImage::get_datatype(enum heif_channel channel) const
737
0
{
738
0
  auto iter = m_planes.find(channel);
739
0
  if (iter == m_planes.end()) {
740
0
    return heif_channel_datatype_undefined;
741
0
  }
742
743
0
  return iter->second.m_datatype;
744
0
}
745
746
747
int HeifPixelImage::get_number_of_interleaved_components(heif_channel channel) const
748
0
{
749
0
  auto iter = m_planes.find(channel);
750
0
  if (iter == m_planes.end()) {
751
0
    return 0;
752
0
  }
753
754
0
  return iter->second.m_num_interleaved_components;
755
0
}
756
757
758
Error HeifPixelImage::copy_new_plane_from(const std::shared_ptr<const HeifPixelImage>& src_image,
759
                                          heif_channel src_channel,
760
                                          heif_channel dst_channel,
761
                                          const heif_security_limits* limits)
762
0
{
763
0
  assert(src_image->has_channel(src_channel));
764
0
  assert(!has_channel(dst_channel));
765
766
0
  uint32_t width = src_image->get_width(src_channel);
767
0
  uint32_t height = src_image->get_height(src_channel);
768
769
0
  auto src_plane_iter = src_image->m_planes.find(src_channel);
770
0
  assert(src_plane_iter != src_image->m_planes.end());
771
0
  const auto& src_plane = src_plane_iter->second;
772
773
0
  auto err = add_channel(dst_channel, width, height,
774
0
                         src_plane.m_datatype,
775
0
                         src_image->get_bits_per_pixel(src_channel), limits);
776
0
  if (err) {
777
0
    return err;
778
0
  }
779
780
0
  uint8_t* dst;
781
0
  size_t dst_stride = 0;
782
783
0
  const uint8_t* src;
784
0
  size_t src_stride = 0;
785
786
0
  src = src_image->get_plane(src_channel, &src_stride);
787
0
  dst = get_plane(dst_channel, &dst_stride);
788
789
0
  uint32_t bpl = width * (src_image->get_storage_bits_per_pixel(src_channel) / 8);
790
791
0
  for (uint32_t y = 0; y < height; y++) {
792
0
    memcpy(dst + y * dst_stride, src + y * src_stride, bpl);
793
0
  }
794
795
0
  return Error::Ok;
796
0
}
797
798
799
Error HeifPixelImage::extract_alpha_from_RGBA(const std::shared_ptr<const HeifPixelImage>& src_image,
800
                                              const heif_security_limits* limits)
801
0
{
802
0
  uint32_t width = src_image->get_width();
803
0
  uint32_t height = src_image->get_height();
804
805
0
  if (Error err = add_plane(heif_channel_Y, width, height, src_image->get_bits_per_pixel(heif_channel_interleaved), limits)) {
806
0
    return err;
807
0
  }
808
809
0
  uint8_t* dst;
810
0
  size_t dst_stride = 0;
811
812
0
  const uint8_t* src;
813
0
  size_t src_stride = 0;
814
815
0
  src = src_image->get_plane(heif_channel_interleaved, &src_stride);
816
0
  dst = get_plane(heif_channel_Y, &dst_stride);
817
818
  //int bpl = width * (src_image->get_storage_bits_per_pixel(src_channel) / 8);
819
820
0
  for (uint32_t y = 0; y < height; y++) {
821
0
    for (uint32_t x = 0; x < width; x++) {
822
0
      dst[y * dst_stride + x] = src[y * src_stride + 4 * x + 3];
823
0
    }
824
0
  }
825
826
0
  return Error::Ok;
827
0
}
828
829
830
Error HeifPixelImage::fill_new_plane(heif_channel dst_channel, uint16_t value, int width, int height, int bpp,
831
                                     const heif_security_limits* limits)
832
0
{
833
0
  if (Error err = add_plane(dst_channel, width, height, bpp, limits)) {
834
0
    return err;
835
0
  }
836
837
0
  fill_plane(dst_channel, value);
838
839
0
  return Error::Ok;
840
0
}
841
842
843
void HeifPixelImage::fill_plane(heif_channel dst_channel, uint16_t value)
844
0
{
845
0
  int num_interleaved = num_interleaved_pixels_per_plane(m_chroma);
846
847
0
  int bpp = get_bits_per_pixel(dst_channel);
848
0
  uint32_t width = get_width(dst_channel);
849
0
  uint32_t height = get_height(dst_channel);
850
851
0
  if (bpp <= 8) {
852
0
    uint8_t* dst;
853
0
    size_t dst_stride = 0;
854
0
    dst = get_plane(dst_channel, &dst_stride);
855
0
    uint32_t width_bytes = width * num_interleaved;
856
857
0
    for (uint32_t y = 0; y < height; y++) {
858
0
      memset(dst + y * dst_stride, value, width_bytes);
859
0
    }
860
0
  }
861
0
  else {
862
0
    uint16_t* dst;
863
0
    size_t dst_stride = 0;
864
0
    dst = get_channel<uint16_t>(dst_channel, &dst_stride);
865
866
0
    for (uint32_t y = 0; y < height; y++) {
867
0
      for (uint32_t x = 0; x < width * num_interleaved; x++) {
868
0
        dst[y * dst_stride + x] = value;
869
0
      }
870
0
    }
871
0
  }
872
0
}
873
874
875
void HeifPixelImage::transfer_plane_from_image_as(const std::shared_ptr<HeifPixelImage>& source,
876
                                                  heif_channel src_channel,
877
                                                  heif_channel dst_channel)
878
60
{
879
  // TODO: check that dst_channel does not exist yet
880
881
60
  ImagePlane plane = source->m_planes[src_channel];
882
60
  source->m_planes.erase(src_channel);
883
60
  source->m_memory_handle.free(plane.allocation_size);
884
885
60
  m_planes.insert(std::make_pair(dst_channel, plane));
886
887
  // Note: we assume that image planes are never transferred between heif_contexts
888
60
  m_memory_handle.alloc(plane.allocation_size,
889
60
                        source->m_memory_handle.get_security_limits(),
890
60
                        "transferred image data");
891
60
}
892
893
894
bool is_interleaved_with_alpha(heif_chroma chroma)
895
2.45k
{
896
2.45k
  switch (chroma) {
897
0
    case heif_chroma_undefined:
898
105
    case heif_chroma_monochrome:
899
668
    case heif_chroma_420:
900
726
    case heif_chroma_422:
901
2.45k
    case heif_chroma_444:
902
2.45k
    case heif_chroma_interleaved_RGB:
903
2.45k
    case heif_chroma_interleaved_RRGGBB_BE:
904
2.45k
    case heif_chroma_interleaved_RRGGBB_LE:
905
2.45k
      return false;
906
907
0
    case heif_chroma_interleaved_RGBA:
908
0
    case heif_chroma_interleaved_RRGGBBAA_BE:
909
0
    case heif_chroma_interleaved_RRGGBBAA_LE:
910
0
      return true;
911
2.45k
  }
912
913
2.45k
  assert(false);
914
0
  return false;
915
0
}
916
917
918
Error HeifPixelImage::copy_image_to(const std::shared_ptr<const HeifPixelImage>& source, uint32_t x0, uint32_t y0)
919
8
{
920
8
  std::set<enum heif_channel> channels = source->get_channel_set();
921
922
8
  uint32_t w = get_width();
923
8
  uint32_t h = get_height();
924
8
  heif_chroma chroma = get_chroma_format();
925
926
927
20
  for (heif_channel channel : channels) {
928
929
20
    size_t tile_stride;
930
20
    const uint8_t* tile_data = source->get_plane(channel, &tile_stride);
931
932
20
    size_t out_stride;
933
20
    uint8_t* out_data = get_plane(channel, &out_stride);
934
935
20
    if (w <= x0 || h <= y0) {
936
0
      return {heif_error_Invalid_input,
937
0
              heif_suberror_Invalid_grid_data};
938
0
    }
939
940
20
    if (source->get_bits_per_pixel(channel) != get_bits_per_pixel(channel)) {
941
0
      return {heif_error_Invalid_input,
942
0
              heif_suberror_Wrong_tile_image_pixel_depth};
943
0
    }
944
945
20
    uint32_t src_width = source->get_width(channel);
946
20
    uint32_t src_height = source->get_height(channel);
947
948
20
    uint32_t copy_width = std::min(src_width, channel_width(w - x0, chroma, channel));
949
20
    uint32_t copy_height = std::min(src_height, channel_height(h - y0, chroma, channel));
950
951
20
    copy_width *= source->get_storage_bits_per_pixel(channel) / 8;
952
953
20
    uint32_t xs = channel_width(x0, chroma, channel);
954
20
    uint32_t ys = channel_height(y0, chroma, channel);
955
20
    xs *= source->get_storage_bits_per_pixel(channel) / 8;
956
957
164
    for (uint32_t py = 0; py < copy_height; py++) {
958
144
      memcpy(out_data + xs + (ys + py) * out_stride,
959
144
             tile_data + py * tile_stride,
960
144
             copy_width);
961
144
    }
962
20
  }
963
964
8
  return Error::Ok;
965
8
}
966
967
968
Result<std::shared_ptr<HeifPixelImage>> HeifPixelImage::rotate_ccw(int angle_degrees, const heif_security_limits* limits)
969
0
{
970
  // --- for some subsampled chroma colorspaces, we have to transform to 4:4:4 before rotation
971
972
0
  bool need_conversion = false;
973
974
0
  if (get_chroma_format() == heif_chroma_422) {
975
0
    if (angle_degrees == 90 || angle_degrees == 270) {
976
0
      need_conversion = true;
977
0
    }
978
0
    else if (angle_degrees == 180 && has_odd_height()) {
979
0
      need_conversion = true;
980
0
    }
981
0
  }
982
0
  else if (get_chroma_format() == heif_chroma_420) {
983
0
    if (angle_degrees == 90 && has_odd_width()) {
984
0
      need_conversion = true;
985
0
    }
986
0
    else if (angle_degrees == 180 && (has_odd_width() || has_odd_height())) {
987
0
      need_conversion = true;
988
0
    }
989
0
    else if (angle_degrees == 270 && has_odd_height()) {
990
0
      need_conversion = true;
991
0
    }
992
0
  }
993
994
0
  if (need_conversion) {
995
0
    heif_color_conversion_options options{};
996
0
    heif_color_conversion_options_set_defaults(&options);
997
998
0
    auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444,
999
0
                                                     nclx_profile(), // default, undefined
1000
0
                                                     get_bits_per_pixel(heif_channel_Y), options, nullptr, limits);
1001
0
    if (!converted_image_result) {
1002
0
      return converted_image_result.error();
1003
0
    }
1004
1005
0
    return (*converted_image_result)->rotate_ccw(angle_degrees, limits);
1006
0
  }
1007
1008
1009
  // --- create output image
1010
1011
0
  if (angle_degrees == 0) {
1012
0
    return shared_from_this();
1013
0
  }
1014
1015
0
  uint32_t out_width = m_width;
1016
0
  uint32_t out_height = m_height;
1017
1018
0
  if (angle_degrees == 90 || angle_degrees == 270) {
1019
0
    std::swap(out_width, out_height);
1020
0
  }
1021
1022
0
  std::shared_ptr<HeifPixelImage> out_img = std::make_shared<HeifPixelImage>();
1023
0
  out_img->create(out_width, out_height, m_colorspace, m_chroma);
1024
1025
1026
  // --- rotate all channels
1027
1028
0
  for (const auto &plane_pair: m_planes) {
1029
0
    heif_channel channel = plane_pair.first;
1030
0
    const ImagePlane &plane = plane_pair.second;
1031
1032
    /*
1033
    if (plane.bit_depth != 8) {
1034
      return Error(heif_error_Unsupported_feature,
1035
                   heif_suberror_Unspecified,
1036
                   "Can currently only rotate images with 8 bits per pixel");
1037
    }
1038
    */
1039
1040
0
    uint32_t out_plane_width = plane.m_width;
1041
0
    uint32_t out_plane_height = plane.m_height;
1042
1043
0
    if (angle_degrees == 90 || angle_degrees == 270) {
1044
0
      std::swap(out_plane_width, out_plane_height);
1045
0
    }
1046
1047
0
    Error err = out_img->add_channel(channel, out_plane_width, out_plane_height, plane.m_datatype, plane.m_bit_depth, limits);
1048
0
    if (err) {
1049
0
      return err;
1050
0
    }
1051
1052
0
    auto out_plane_iter = out_img->m_planes.find(channel);
1053
0
    assert(out_plane_iter != out_img->m_planes.end());
1054
0
    ImagePlane& out_plane = out_plane_iter->second;
1055
1056
0
    if (plane.m_bit_depth <= 8) {
1057
0
      plane.rotate_ccw<uint8_t>(angle_degrees, out_plane);
1058
0
    }
1059
0
    else if (plane.m_bit_depth <= 16) {
1060
0
      plane.rotate_ccw<uint16_t>(angle_degrees, out_plane);
1061
0
    }
1062
0
    else if (plane.m_bit_depth <= 32) {
1063
0
      plane.rotate_ccw<uint32_t>(angle_degrees, out_plane);
1064
0
    }
1065
0
    else if (plane.m_bit_depth <= 64) {
1066
0
      plane.rotate_ccw<uint64_t>(angle_degrees, out_plane);
1067
0
    }
1068
0
    else if (plane.m_bit_depth <= 128) {
1069
0
      plane.rotate_ccw<heif_complex64>(angle_degrees, out_plane);
1070
0
    }
1071
0
  }
1072
  // --- pass the color profiles to the new image
1073
1074
0
  out_img->set_color_profile_nclx(get_color_profile_nclx());
1075
0
  out_img->set_color_profile_icc(get_color_profile_icc());
1076
1077
0
  out_img->add_warnings(get_warnings());
1078
1079
0
  return out_img;
1080
0
}
1081
1082
template<typename T>
1083
void HeifPixelImage::ImagePlane::rotate_ccw(int angle_degrees,
1084
                                            ImagePlane& out_plane) const
1085
0
{
1086
0
  uint32_t w = m_width;
1087
0
  uint32_t h = m_height;
1088
1089
0
  uint32_t in_stride = stride / uint32_t(sizeof(T));
1090
0
  const T* in_data = static_cast<const T*>(mem);
1091
1092
0
  uint32_t out_stride = out_plane.stride / uint32_t(sizeof(T));
1093
0
  T* out_data = static_cast<T*>(out_plane.mem);
1094
1095
0
  if (angle_degrees == 270) {
1096
0
    for (uint32_t x = 0; x < h; x++)
1097
0
      for (uint32_t y = 0; y < w; y++) {
1098
0
        out_data[y * out_stride + x] = in_data[(h - 1 - x) * in_stride + y];
1099
0
      }
1100
0
  } else if (angle_degrees == 180) {
1101
0
    for (uint32_t y = 0; y < h; y++)
1102
0
      for (uint32_t x = 0; x < w; x++) {
1103
0
        out_data[y * out_stride + x] = in_data[(h - 1 - y) * in_stride + (w - 1 - x)];
1104
0
      }
1105
0
  } else if (angle_degrees == 90) {
1106
0
    for (uint32_t x = 0; x < h; x++)
1107
0
      for (uint32_t y = 0; y < w; y++) {
1108
0
        out_data[y * out_stride + x] = in_data[x * in_stride + (w - 1 - y)];
1109
0
      }
1110
0
  }
1111
0
}
Unexecuted instantiation: void HeifPixelImage::ImagePlane::rotate_ccw<unsigned char>(int, HeifPixelImage::ImagePlane&) const
Unexecuted instantiation: void HeifPixelImage::ImagePlane::rotate_ccw<unsigned short>(int, HeifPixelImage::ImagePlane&) const
Unexecuted instantiation: void HeifPixelImage::ImagePlane::rotate_ccw<unsigned int>(int, HeifPixelImage::ImagePlane&) const
Unexecuted instantiation: void HeifPixelImage::ImagePlane::rotate_ccw<unsigned long>(int, HeifPixelImage::ImagePlane&) const
Unexecuted instantiation: void HeifPixelImage::ImagePlane::rotate_ccw<heif_complex64>(int, HeifPixelImage::ImagePlane&) const
1112
1113
1114
template<typename T>
1115
void HeifPixelImage::ImagePlane::mirror_inplace(heif_transform_mirror_direction direction)
1116
0
{
1117
0
  uint32_t w = m_width;
1118
0
  uint32_t h = m_height;
1119
1120
0
  T* data = static_cast<T*>(mem);
1121
1122
0
  if (direction == heif_transform_mirror_direction_horizontal) {
1123
0
    for (uint32_t y = 0; y < h; y++) {
1124
0
      for (uint32_t x = 0; x < w / 2; x++)
1125
0
        std::swap(data[y * stride / sizeof(T) + x], data[y * stride / sizeof(T) + w - 1 - x]);
1126
0
    }
1127
0
  } else {
1128
0
    for (uint32_t y = 0; y < h / 2; y++) {
1129
0
      for (uint32_t x = 0; x < w; x++)
1130
0
        std::swap(data[y * stride / sizeof(T) + x], data[(h - 1 - y) * stride / sizeof(T) + x]);
1131
0
    }
1132
0
  }
1133
0
}
Unexecuted instantiation: void HeifPixelImage::ImagePlane::mirror_inplace<unsigned char>(heif_transform_mirror_direction)
Unexecuted instantiation: void HeifPixelImage::ImagePlane::mirror_inplace<unsigned short>(heif_transform_mirror_direction)
Unexecuted instantiation: void HeifPixelImage::ImagePlane::mirror_inplace<unsigned int>(heif_transform_mirror_direction)
Unexecuted instantiation: void HeifPixelImage::ImagePlane::mirror_inplace<unsigned long>(heif_transform_mirror_direction)
Unexecuted instantiation: void HeifPixelImage::ImagePlane::mirror_inplace<heif_complex64>(heif_transform_mirror_direction)
1134
1135
1136
Result<std::shared_ptr<HeifPixelImage>> HeifPixelImage::mirror_inplace(heif_transform_mirror_direction direction,
1137
                                                                       const heif_security_limits* limits)
1138
0
{
1139
  // --- for some subsampled chroma colorspaces, we have to transform to 4:4:4 before rotation
1140
1141
0
  bool need_conversion = false;
1142
1143
0
  if (get_chroma_format() == heif_chroma_422) {
1144
0
    if (direction == heif_transform_mirror_direction_horizontal && has_odd_width()) {
1145
0
      need_conversion = true;
1146
0
    }
1147
0
  }
1148
0
  else if (get_chroma_format() == heif_chroma_420) {
1149
0
    if (has_odd_width() || has_odd_height()) {
1150
0
      need_conversion = true;
1151
0
    }
1152
0
  }
1153
1154
0
  if (need_conversion) {
1155
0
    heif_color_conversion_options options{};
1156
0
    heif_color_conversion_options_set_defaults(&options);
1157
1158
0
    auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444,
1159
0
                                                     nclx_profile(), // default, undefined
1160
0
                                                     get_bits_per_pixel(heif_channel_Y), options, nullptr, limits);
1161
0
    if (!converted_image_result) {
1162
0
      return converted_image_result.error();
1163
0
    }
1164
1165
0
    return (*converted_image_result)->mirror_inplace(direction, limits);
1166
0
  }
1167
1168
1169
0
  for (auto& plane_pair : m_planes) {
1170
0
    ImagePlane& plane = plane_pair.second;
1171
1172
0
    if (plane.m_bit_depth <= 8) {
1173
0
      plane.mirror_inplace<uint8_t>(direction);
1174
0
    }
1175
0
    else if (plane.m_bit_depth <= 16) {
1176
0
      plane.mirror_inplace<uint16_t>(direction);
1177
0
    }
1178
0
    else if (plane.m_bit_depth <= 32) {
1179
0
      plane.mirror_inplace<uint32_t>(direction);
1180
0
    }
1181
0
    else if (plane.m_bit_depth <= 64) {
1182
0
      plane.mirror_inplace<uint64_t>(direction);
1183
0
    }
1184
0
    else if (plane.m_bit_depth <= 128) {
1185
0
      plane.mirror_inplace<heif_complex64>(direction);
1186
0
    }
1187
0
    else {
1188
0
      std::stringstream sstr;
1189
0
      sstr << "Cannot mirror images with " << plane.m_bit_depth << " bits per pixel";
1190
0
      return Error{heif_error_Unsupported_feature,
1191
0
                   heif_suberror_Unspecified,
1192
0
                   sstr.str()};
1193
0
    }
1194
0
  }
1195
1196
0
  return shared_from_this();
1197
0
}
1198
1199
1200
int HeifPixelImage::ImagePlane::get_bytes_per_pixel() const
1201
15.1k
{
1202
15.1k
  if (m_bit_depth <= 8) {
1203
7.29k
    return 1;
1204
7.29k
  }
1205
7.84k
  else if (m_bit_depth <= 16) {
1206
7.84k
    return 2;
1207
7.84k
  }
1208
0
  else if (m_bit_depth <= 32) {
1209
0
    return 4;
1210
0
  }
1211
0
  else if (m_bit_depth <= 64) {
1212
0
    return 8;
1213
0
  }
1214
0
  else {
1215
0
    assert(m_bit_depth <= 128);
1216
0
    return 16;
1217
0
  }
1218
15.1k
}
1219
1220
1221
Result<std::shared_ptr<HeifPixelImage>> HeifPixelImage::crop(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom,
1222
                                                             const heif_security_limits* limits) const
1223
0
{
1224
  // --- for some subsampled chroma colorspaces, we have to transform to 4:4:4 before cropping
1225
1226
0
  bool need_conversion = false;
1227
1228
0
  if (get_chroma_format() == heif_chroma_422 && (left & 1) == 1) {
1229
0
      need_conversion = true;
1230
0
  }
1231
0
  else if (get_chroma_format() == heif_chroma_420 &&
1232
0
           ((left & 1) == 1 || (top & 1) == 1)) {
1233
0
    need_conversion = true;
1234
0
  }
1235
1236
0
  if (need_conversion) {
1237
0
    heif_color_conversion_options options{};
1238
0
    heif_color_conversion_options_set_defaults(&options);
1239
1240
0
    auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444,
1241
0
                                                     nclx_profile(), // default, undefined
1242
0
                                                     get_bits_per_pixel(heif_channel_Y), options, nullptr, limits);
1243
1244
0
    if (!converted_image_result) {
1245
0
      return converted_image_result.error();
1246
0
    }
1247
1248
0
    return (*converted_image_result)->crop(left, right, top, bottom, limits);
1249
0
  }
1250
1251
1252
1253
0
  auto out_img = std::make_shared<HeifPixelImage>();
1254
0
  out_img->create(right - left + 1, bottom - top + 1, m_colorspace, m_chroma);
1255
1256
1257
  // --- crop all channels
1258
1259
0
  for (const auto& plane_pair : m_planes) {
1260
0
    heif_channel channel = plane_pair.first;
1261
0
    const ImagePlane& plane = plane_pair.second;
1262
1263
0
    uint32_t plane_left = get_subsampled_size_h(left, channel, m_chroma, scaling_mode::is_divisible); // is always divisible
1264
0
    uint32_t plane_right = get_subsampled_size_h(right, channel, m_chroma, scaling_mode::round_down); // this keeps enough chroma since 'right' is a coordinate and not the width
1265
0
    uint32_t plane_top = get_subsampled_size_v(top, channel, m_chroma, scaling_mode::is_divisible);
1266
0
    uint32_t plane_bottom = get_subsampled_size_v(bottom, channel, m_chroma, scaling_mode::round_down);
1267
1268
0
    auto err = out_img->add_channel(channel,
1269
0
                                    plane_right - plane_left + 1,
1270
0
                                    plane_bottom - plane_top + 1,
1271
0
                                    plane.m_datatype,
1272
0
                                    plane.m_bit_depth,
1273
0
                                    limits);
1274
0
    if (err) {
1275
0
      return err;
1276
0
    }
1277
1278
0
    auto out_plane_iter = out_img->m_planes.find(channel);
1279
0
    assert(out_plane_iter != out_img->m_planes.end());
1280
0
    ImagePlane& out_plane = out_plane_iter->second;
1281
1282
0
    int bytes_per_pixel = plane.get_bytes_per_pixel();
1283
0
    plane.crop(plane_left, plane_right, plane_top, plane_bottom, bytes_per_pixel, out_plane);
1284
0
  }
1285
1286
  // --- pass the color profiles to the new image
1287
1288
0
  out_img->set_color_profile_nclx(get_color_profile_nclx());
1289
0
  out_img->set_color_profile_icc(get_color_profile_icc());
1290
1291
0
  out_img->add_warnings(get_warnings());
1292
1293
0
  return out_img;
1294
0
}
1295
1296
1297
void HeifPixelImage::ImagePlane::crop(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom,
1298
                                      int bytes_per_pixel, ImagePlane& out_plane) const
1299
0
{
1300
0
  uint32_t in_stride = stride;
1301
0
  auto* in_data = static_cast<const uint8_t*>(mem);
1302
1303
0
  uint32_t out_stride = out_plane.stride;
1304
0
  auto* out_data = static_cast<uint8_t*>(out_plane.mem);
1305
1306
0
  for (uint32_t y = top; y <= bottom; y++) {
1307
0
    memcpy(&out_data[(y - top) * out_stride],
1308
0
           &in_data[y * in_stride + left * bytes_per_pixel],
1309
0
           (right - left + 1) * bytes_per_pixel);
1310
0
  }
1311
0
}
1312
1313
1314
Error HeifPixelImage::fill_RGB_16bit(uint16_t r, uint16_t g, uint16_t b, uint16_t a)
1315
325
{
1316
1.30k
  for (const auto& channel : {heif_channel_R, heif_channel_G, heif_channel_B, heif_channel_Alpha}) {
1317
1318
1.30k
    const auto plane_iter = m_planes.find(channel);
1319
1.30k
    if (plane_iter == m_planes.end()) {
1320
1321
      // alpha channel is optional, R,G,B is required
1322
325
      if (channel == heif_channel_Alpha) {
1323
325
        continue;
1324
325
      }
1325
1326
0
      return {heif_error_Usage_error,
1327
0
              heif_suberror_Nonexisting_image_channel_referenced};
1328
1329
325
    }
1330
1331
975
    ImagePlane& plane = plane_iter->second;
1332
1333
975
    if (plane.m_bit_depth != 8) {
1334
0
      return {heif_error_Unsupported_feature,
1335
0
              heif_suberror_Unspecified,
1336
0
              "Can currently only fill images with 8 bits per pixel"};
1337
0
    }
1338
1339
975
    size_t h = plane.m_height;
1340
1341
975
    size_t stride = plane.stride;
1342
975
    auto* data = static_cast<uint8_t*>(plane.mem);
1343
1344
975
    uint16_t val16;
1345
975
    switch (channel) {
1346
325
      case heif_channel_R:
1347
325
        val16 = r;
1348
325
        break;
1349
325
      case heif_channel_G:
1350
325
        val16 = g;
1351
325
        break;
1352
325
      case heif_channel_B:
1353
325
        val16 = b;
1354
325
        break;
1355
0
      case heif_channel_Alpha:
1356
0
        val16 = a;
1357
0
        break;
1358
0
      default:
1359
        // initialization only to avoid warning of uninitialized variable.
1360
0
        val16 = 0;
1361
        // Should already be detected by the check above ("m_planes.find").
1362
0
        assert(false);
1363
975
    }
1364
1365
975
    auto val8 = static_cast<uint8_t>(val16 >> 8U);
1366
1367
1368
    // memset() even when h * stride > sizeof(size_t)
1369
1370
975
    if (std::numeric_limits<size_t>::max() / stride > h) {
1371
      // can fill in one step
1372
975
      memset(data, val8, stride * h);
1373
975
    }
1374
0
    else {
1375
      // fill line by line
1376
0
      auto* p = data;
1377
1378
0
      for (size_t y=0;y<h;y++) {
1379
0
        memset(p, val8, stride);
1380
0
        p += stride;
1381
0
      }
1382
0
    }
1383
975
  }
1384
1385
325
  return Error::Ok;
1386
325
}
1387
1388
1389
uint32_t negate_negative_int32(int32_t x)
1390
0
{
1391
0
  assert(x <= 0);
1392
1393
0
  if (x == INT32_MIN) {
1394
0
    return static_cast<uint32_t>(INT32_MAX) + 1;
1395
0
  }
1396
0
  else {
1397
0
    return static_cast<uint32_t>(-x);
1398
0
  }
1399
0
}
1400
1401
1402
Error HeifPixelImage::overlay(std::shared_ptr<HeifPixelImage>& overlay, int32_t dx, int32_t dy)
1403
2
{
1404
2
  std::set<enum heif_channel> channels = overlay->get_channel_set();
1405
1406
2
  bool has_alpha = overlay->has_channel(heif_channel_Alpha);
1407
  //bool has_alpha_me = has_channel(heif_channel_Alpha);
1408
1409
2
  size_t alpha_stride = 0;
1410
2
  uint8_t* alpha_p;
1411
2
  alpha_p = overlay->get_plane(heif_channel_Alpha, &alpha_stride);
1412
1413
4
  for (heif_channel channel : channels) {
1414
4
    if (!has_channel(channel)) {
1415
0
      continue;
1416
0
    }
1417
1418
4
    size_t in_stride = 0;
1419
4
    const uint8_t* in_p;
1420
1421
4
    size_t out_stride = 0;
1422
4
    uint8_t* out_p;
1423
1424
4
    in_p = overlay->get_plane(channel, &in_stride);
1425
4
    out_p = get_plane(channel, &out_stride);
1426
1427
4
    uint32_t in_w = overlay->get_width(channel);
1428
4
    uint32_t in_h = overlay->get_height(channel);
1429
1430
4
    uint32_t out_w = get_width(channel);
1431
4
    uint32_t out_h = get_height(channel);
1432
1433
1434
    // --- check whether overlay image overlaps with current image
1435
1436
4
    if (dx > 0 && static_cast<uint32_t>(dx) >= out_w) {
1437
      // the overlay image is completely outside the right border -> skip overlaying
1438
1
      return Error::Ok;
1439
1
    }
1440
3
    else if (dx < 0 && in_w <= negate_negative_int32(dx)) {
1441
      // the overlay image is completely outside the left border -> skip overlaying
1442
0
      return Error::Ok;
1443
0
    }
1444
1445
3
    if (dy > 0 && static_cast<uint32_t>(dy) >= out_h) {
1446
      // the overlay image is completely outside the bottom border -> skip overlaying
1447
0
      return Error::Ok;
1448
0
    }
1449
3
    else if (dy < 0 && in_h <= negate_negative_int32(dy)) {
1450
      // the overlay image is completely outside the top border -> skip overlaying
1451
0
      return Error::Ok;
1452
0
    }
1453
1454
1455
    // --- compute overlapping area
1456
1457
    // top-left points where to start copying in source and destination
1458
3
    uint32_t in_x0;
1459
3
    uint32_t in_y0;
1460
3
    uint32_t out_x0;
1461
3
    uint32_t out_y0;
1462
1463
    // right border
1464
3
    if (dx + static_cast<int64_t>(in_w) > out_w) {
1465
      // overlay image extends partially outside of right border
1466
      // Notes:
1467
      // - (out_w-dx) cannot underflow because dx<out_w is ensured above
1468
      // - (out_w-dx) cannot overflow (for dx<0) because, as just checked, out_w-dx < in_w
1469
      //              and in_w fits into uint32_t
1470
0
      in_w = static_cast<uint32_t>(static_cast<int64_t>(out_w) - dx);
1471
0
    }
1472
1473
    // bottom border
1474
3
    if (dy + static_cast<int64_t>(in_h) > out_h) {
1475
      // overlay image extends partially outside of bottom border
1476
3
      in_h = static_cast<uint32_t>(static_cast<int64_t>(out_h) - dy);
1477
3
    }
1478
1479
    // left border
1480
3
    if (dx < 0) {
1481
      // overlay image starts partially outside of left border
1482
1483
0
      in_x0 = negate_negative_int32(dx);
1484
0
      out_x0 = 0;
1485
0
      in_w = in_w - in_x0; // in_x0 < in_w because in_w > -dx = in_x0
1486
0
    }
1487
3
    else {
1488
3
      in_x0 = 0;
1489
3
      out_x0 = static_cast<uint32_t>(dx);
1490
3
    }
1491
1492
    // top border
1493
3
    if (dy < 0) {
1494
      // overlay image started partially outside of top border
1495
1496
0
      in_y0 = negate_negative_int32(dy);
1497
0
      out_y0 = 0;
1498
0
      in_h = in_h - in_y0; // in_y0 < in_h because in_h > -dy = in_y0
1499
0
    }
1500
3
    else {
1501
3
      in_y0 = 0;
1502
3
      out_y0 = static_cast<uint32_t>(dy);
1503
3
    }
1504
1505
    // --- computer overlay in overlapping area
1506
1507
6
    for (uint32_t y = in_y0; y < in_h; y++) {
1508
3
      if (!has_alpha) {
1509
3
        memcpy(out_p + out_x0 + (out_y0 + y - in_y0) * out_stride,
1510
3
               in_p + in_x0 + y * in_stride,
1511
3
               in_w);
1512
3
      }
1513
0
      else {
1514
0
        for (uint32_t x = in_x0; x < in_w; x++) {
1515
0
          uint8_t* outptr = &out_p[out_x0 + (out_y0 + y - in_y0) * out_stride + x];
1516
0
          uint8_t in_val = in_p[in_x0 + y * in_stride + x];
1517
0
          uint8_t alpha_val = alpha_p[in_x0 + y * in_stride + x];
1518
1519
0
          *outptr = (uint8_t) ((in_val * alpha_val + *outptr * (255 - alpha_val)) / 255);
1520
0
        }
1521
0
      }
1522
3
    }
1523
3
  }
1524
1525
1
  return Error::Ok;
1526
2
}
1527
1528
1529
Error HeifPixelImage::scale_nearest_neighbor(std::shared_ptr<HeifPixelImage>& out_img,
1530
                                             uint32_t width, uint32_t height,
1531
                                             const heif_security_limits* limits) const
1532
52
{
1533
52
  out_img = std::make_shared<HeifPixelImage>();
1534
52
  out_img->create(width, height, m_colorspace, m_chroma);
1535
1536
1537
  // --- create output image with scaled planes
1538
1539
52
  if (has_channel(heif_channel_interleaved)) {
1540
0
    if (auto err = out_img->add_plane(heif_channel_interleaved, width, height, get_bits_per_pixel(heif_channel_interleaved), limits)) {
1541
0
      return err;
1542
0
    }
1543
0
  }
1544
52
  else {
1545
52
    if (get_colorspace() == heif_colorspace_RGB) {
1546
0
      if (!has_channel(heif_channel_R) ||
1547
0
          !has_channel(heif_channel_G) ||
1548
0
          !has_channel(heif_channel_B)) {
1549
0
        return {heif_error_Invalid_input, heif_suberror_Unspecified, "RGB input without R,G,B, planes"};
1550
0
      }
1551
1552
0
      if (auto err = out_img->add_plane(heif_channel_R, width, height, get_bits_per_pixel(heif_channel_R), limits)) {
1553
0
        return err;
1554
0
      }
1555
0
      if (auto err = out_img->add_plane(heif_channel_G, width, height, get_bits_per_pixel(heif_channel_G), limits)) {
1556
0
        return err;
1557
0
      }
1558
0
      if (auto err = out_img->add_plane(heif_channel_B, width, height, get_bits_per_pixel(heif_channel_B), limits)) {
1559
0
        return err;
1560
0
      }
1561
0
    }
1562
52
    else if (get_colorspace() == heif_colorspace_monochrome) {
1563
36
      if (!has_channel(heif_channel_Y)) {
1564
0
        return {heif_error_Invalid_input, heif_suberror_Unspecified, "monochrome input with no Y plane"};
1565
0
      }
1566
1567
36
      if (auto err = out_img->add_plane(heif_channel_Y, width, height, get_bits_per_pixel(heif_channel_Y), limits)) {
1568
0
        return err;
1569
0
      }
1570
36
    }
1571
16
    else if (get_colorspace() == heif_colorspace_YCbCr) {
1572
16
      if (!has_channel(heif_channel_Y) ||
1573
16
          !has_channel(heif_channel_Cb) ||
1574
16
          !has_channel(heif_channel_Cr)) {
1575
0
        return {heif_error_Invalid_input, heif_suberror_Unspecified, "YCbCr image without Y,Cb,Cr planes"};
1576
0
      }
1577
1578
16
      uint32_t cw, ch;
1579
16
      get_subsampled_size(width, height, heif_channel_Cb, get_chroma_format(), &cw, &ch);
1580
16
      if (auto err = out_img->add_plane(heif_channel_Y, width, height, get_bits_per_pixel(heif_channel_Y), limits)) {
1581
0
        return err;
1582
0
      }
1583
16
      if (auto err = out_img->add_plane(heif_channel_Cb, cw, ch, get_bits_per_pixel(heif_channel_Cb), limits)) {
1584
0
        return err;
1585
0
      }
1586
16
      if (auto err = out_img->add_plane(heif_channel_Cr, cw, ch, get_bits_per_pixel(heif_channel_Cr), limits)) {
1587
0
        return err;
1588
0
      }
1589
16
    }
1590
0
    else {
1591
0
      return {heif_error_Invalid_input, heif_suberror_Unspecified, "unknown color configuration"};
1592
0
    }
1593
1594
52
    if (has_channel(heif_channel_Alpha)) {
1595
0
      if (auto err = out_img->add_plane(heif_channel_Alpha, width, height, get_bits_per_pixel(heif_channel_Alpha), limits)) {
1596
0
        return err;
1597
0
      }
1598
0
    }
1599
52
  }
1600
1601
1602
  // --- scale all channels
1603
1604
52
  int nInterleaved = num_interleaved_pixels_per_plane(m_chroma);
1605
52
  if (nInterleaved > 1) {
1606
0
    auto plane_iter = m_planes.find(heif_channel_interleaved);
1607
0
    assert(plane_iter != m_planes.end()); // the plane must exist since we have an interleaved chroma format
1608
0
    const ImagePlane& plane = plane_iter->second;
1609
1610
0
    uint32_t out_w = out_img->get_width(heif_channel_interleaved);
1611
0
    uint32_t out_h = out_img->get_height(heif_channel_interleaved);
1612
1613
0
    if (plane.m_bit_depth <= 8) {
1614
      // SDR interleaved
1615
1616
0
      size_t in_stride = plane.stride;
1617
0
      const auto* in_data = static_cast<const uint8_t*>(plane.mem);
1618
1619
0
      size_t out_stride = 0;
1620
0
      auto* out_data = out_img->get_plane(heif_channel_interleaved, &out_stride);
1621
1622
0
      for (uint32_t y = 0; y < out_h; y++) {
1623
0
        uint32_t iy = y * m_height / height;
1624
1625
0
        for (uint32_t x = 0; x < out_w; x++) {
1626
0
          uint32_t ix = x * m_width / width;
1627
1628
0
          for (int c = 0; c < nInterleaved; c++) {
1629
0
            out_data[y * out_stride + x * nInterleaved + c] = in_data[iy * in_stride + ix * nInterleaved + c];
1630
0
          }
1631
0
        }
1632
0
      }
1633
0
    }
1634
0
    else {
1635
      // HDR interleaved
1636
      // TODO: untested
1637
1638
0
      size_t in_stride = plane.stride;
1639
0
      const uint16_t* in_data = static_cast<const uint16_t*>(plane.mem);
1640
1641
0
      size_t out_stride = 0;
1642
0
      uint16_t* out_data = out_img->get_channel<uint16_t>(heif_channel_interleaved, &out_stride);
1643
1644
0
      in_stride /= 2;
1645
1646
0
      for (uint32_t y = 0; y < out_h; y++) {
1647
0
        uint32_t iy = y * m_height / height;
1648
1649
0
        for (uint32_t x = 0; x < out_w; x++) {
1650
0
          uint32_t ix = x * m_width / width;
1651
1652
0
          for (int c = 0; c < nInterleaved; c++) {
1653
0
            out_data[y * out_stride + x * nInterleaved + c] = in_data[iy * in_stride + ix * nInterleaved + c];
1654
0
          }
1655
0
        }
1656
0
      }
1657
0
    }
1658
0
  }
1659
52
  else {
1660
84
    for (const auto& plane_pair : m_planes) {
1661
84
      heif_channel channel = plane_pair.first;
1662
84
      const ImagePlane& plane = plane_pair.second;
1663
1664
84
      if (!out_img->has_channel(channel)) {
1665
0
        return {heif_error_Invalid_input, heif_suberror_Unspecified, "scaling input has extra color plane"};
1666
0
      }
1667
1668
1669
84
      uint32_t out_w = out_img->get_width(channel);
1670
84
      uint32_t out_h = out_img->get_height(channel);
1671
1672
84
      if (plane.m_bit_depth <= 8) {
1673
        // SDR planar
1674
1675
35
        size_t in_stride = plane.stride;
1676
35
        const auto* in_data = static_cast<const uint8_t*>(plane.mem);
1677
1678
35
        size_t out_stride = 0;
1679
35
        auto* out_data = out_img->get_plane(channel, &out_stride);
1680
1681
561
        for (uint32_t y = 0; y < out_h; y++) {
1682
526
          uint32_t iy = y * m_height / height;
1683
1684
21.9k
          for (uint32_t x = 0; x < out_w; x++) {
1685
21.3k
            uint32_t ix = x * m_width / width;
1686
1687
21.3k
            out_data[y * out_stride + x] = in_data[iy * in_stride + ix];
1688
21.3k
          }
1689
526
        }
1690
35
      }
1691
49
      else {
1692
        // HDR planar
1693
1694
49
        size_t in_stride = plane.stride;
1695
49
        const uint16_t* in_data = static_cast<const uint16_t*>(plane.mem);
1696
1697
49
        size_t out_stride = 0;
1698
49
        uint16_t* out_data = out_img->get_channel<uint16_t>(channel, &out_stride);
1699
1700
49
        in_stride /= 2;
1701
1702
976
        for (uint32_t y = 0; y < out_h; y++) {
1703
927
          uint32_t iy = y * m_height / height;
1704
1705
75.8k
          for (uint32_t x = 0; x < out_w; x++) {
1706
74.9k
            uint32_t ix = x * m_width / width;
1707
1708
74.9k
            out_data[y * out_stride + x] = in_data[iy * in_stride + ix];
1709
74.9k
          }
1710
927
        }
1711
49
      }
1712
84
    }
1713
52
  }
1714
1715
52
  return Error::Ok;
1716
52
}
1717
1718
1719
void HeifPixelImage::forward_all_metadata_from(const std::shared_ptr<const HeifPixelImage>& src_image)
1720
8
{
1721
8
  set_color_profile_nclx(src_image->get_color_profile_nclx());
1722
8
  set_color_profile_icc(src_image->get_color_profile_icc());
1723
1724
8
  if (src_image->has_nonsquare_pixel_ratio()) {
1725
0
    uint32_t h,v;
1726
0
    src_image->get_pixel_ratio(&h,&v);
1727
0
    set_pixel_ratio(h,v);
1728
0
  }
1729
1730
8
  if (src_image->has_clli()) {
1731
0
    set_clli(src_image->get_clli());
1732
0
  }
1733
1734
8
  if (src_image->has_mdcv()) {
1735
0
    set_mdcv(src_image->get_mdcv());
1736
0
  }
1737
1738
8
  set_premultiplied_alpha(src_image->is_premultiplied_alpha());
1739
1740
  // TODO: TAI timestamp and contentID (once we merge that branch)
1741
1742
  // TODO: should we also forward the warnings? It might be better to do that in ImageItem_Grid.
1743
8
}
1744
1745
1746
void HeifPixelImage::debug_dump() const
1747
0
{
1748
0
  auto channels = get_channel_set();
1749
0
  for (auto c : channels) {
1750
0
    size_t stride = 0;
1751
0
    const uint8_t* p = get_plane(c, &stride);
1752
1753
0
    for (int y = 0; y < 8; y++) {
1754
0
      for (int x = 0; x < 8; x++) {
1755
0
        printf("%02x ", p[y * stride + x]);
1756
0
      }
1757
0
      printf("\n");
1758
0
    }
1759
0
  }
1760
0
}
1761
1762
Error HeifPixelImage::create_clone_image_at_new_size(const std::shared_ptr<const HeifPixelImage>& source, uint32_t w, uint32_t h,
1763
                                                     const heif_security_limits* limits)
1764
8
{
1765
8
  heif_colorspace colorspace = source->get_colorspace();
1766
8
  heif_chroma chroma = source->get_chroma_format();
1767
1768
8
  create(w, h, colorspace, chroma);
1769
1770
8
  switch (colorspace) {
1771
2
    case heif_colorspace_monochrome:
1772
2
      if (auto err = add_plane(heif_channel_Y, w, h, source->get_bits_per_pixel(heif_channel_Y), limits)) {
1773
0
        return err;
1774
0
      }
1775
2
      break;
1776
6
    case heif_colorspace_YCbCr:
1777
6
      if (auto err = add_plane(heif_channel_Y, w, h, source->get_bits_per_pixel(heif_channel_Y), limits)) {
1778
0
        return err;
1779
0
      }
1780
6
      if (auto err = add_plane(heif_channel_Cb, chroma_width(w, chroma), chroma_height(h, chroma), source->get_bits_per_pixel(heif_channel_Cb), limits)) {
1781
0
        return err;
1782
0
      }
1783
6
      if (auto err = add_plane(heif_channel_Cr, chroma_width(w, chroma), chroma_height(h, chroma), source->get_bits_per_pixel(heif_channel_Cr), limits)) {
1784
0
        return err;
1785
0
      }
1786
6
      break;
1787
6
    case heif_colorspace_RGB:
1788
0
      if (chroma == heif_chroma_444) {
1789
0
        if (auto err = add_plane(heif_channel_R, w, h, source->get_bits_per_pixel(heif_channel_R), limits)) {
1790
0
          return err;
1791
0
        }
1792
0
        if (auto err = add_plane(heif_channel_G, w, h, source->get_bits_per_pixel(heif_channel_G), limits)) {
1793
0
          return err;
1794
0
        }
1795
0
        if (auto err = add_plane(heif_channel_B, w, h, source->get_bits_per_pixel(heif_channel_B), limits)) {
1796
0
          return err;
1797
0
        }
1798
0
      }
1799
0
      else {
1800
0
        if (auto err = add_plane(heif_channel_interleaved, w, h, source->get_bits_per_pixel(heif_channel_interleaved), limits)) {
1801
0
          return err;
1802
0
        }
1803
0
      }
1804
0
      break;
1805
0
    default:
1806
0
      assert(false);
1807
0
      break;
1808
8
  }
1809
1810
8
  if (source->has_channel(heif_channel_Alpha)) {
1811
0
      if (auto err = add_plane(heif_channel_Alpha, w, h, source->get_bits_per_pixel(heif_channel_Alpha), limits)) {
1812
0
        return err;
1813
0
      }
1814
0
  }
1815
1816
8
  return Error::Ok;
1817
8
}
1818
1819
1820
Result<std::shared_ptr<HeifPixelImage>>
1821
HeifPixelImage::extract_image_area(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h,
1822
                                   const heif_security_limits* limits) const
1823
0
{
1824
0
  uint32_t minW = std::min(w, get_width() - x0);
1825
0
  uint32_t minH = std::min(h, get_height() - y0);
1826
1827
0
  auto areaImg = std::make_shared<HeifPixelImage>();
1828
0
  Error err = areaImg->create_clone_image_at_new_size(shared_from_this(), minW, minH, limits);
1829
0
  if (err) {
1830
0
    return err;
1831
0
  }
1832
1833
0
  std::set<enum heif_channel> channels = get_channel_set();
1834
0
  heif_chroma chroma = get_chroma_format();
1835
1836
0
  for (heif_channel channel : channels) {
1837
1838
0
    size_t src_stride;
1839
0
    const uint8_t* src_data = get_plane(channel, &src_stride);
1840
1841
0
    size_t out_stride;
1842
0
    uint8_t* out_data = areaImg->get_plane(channel, &out_stride);
1843
1844
0
    if (areaImg->get_bits_per_pixel(channel) != get_bits_per_pixel(channel)) {
1845
0
      return Error{
1846
0
        heif_error_Invalid_input,
1847
0
        heif_suberror_Wrong_tile_image_pixel_depth
1848
0
      };
1849
0
    }
1850
1851
0
    uint32_t copy_width = channel_width(minW, chroma, channel);
1852
0
    uint32_t copy_height = channel_height(minH, chroma, channel);
1853
1854
0
    copy_width *= get_storage_bits_per_pixel(channel) / 8;
1855
1856
0
    uint32_t xs = channel_width(x0, chroma, channel);
1857
0
    uint32_t ys = channel_height(y0, chroma, channel);
1858
0
    xs *= get_storage_bits_per_pixel(channel) / 8;
1859
1860
0
    for (uint32_t py = 0; py < copy_height; py++) {
1861
0
      memcpy(out_data + py * out_stride,
1862
0
             src_data + xs + (ys + py) * src_stride,
1863
0
             copy_width);
1864
0
    }
1865
0
  }
1866
1867
0
  err = areaImg->extend_to_size_with_zero(w,h,limits);
1868
0
  if (err) {
1869
0
    return err;
1870
0
  }
1871
1872
0
  return areaImg;
1873
0
}