1 /*
2 * HEIF codec.
3 * Copyright (c) 2017 struktur AG, Dirk Farin <farin@struktur.de>
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 "heif_image.h"
23 #include "heif_colorconversion.h"
24
25 #include <cassert>
26 #include <cstring>
27
28 #include <utility>
29
30 using namespace heif;
31
32
chroma_h_subsampling(heif_chroma c)33 uint8_t heif::chroma_h_subsampling(heif_chroma c)
34 {
35 switch (c) {
36 case heif_chroma_monochrome:
37 case heif_chroma_444:
38 return 1;
39
40 case heif_chroma_420:
41 case heif_chroma_422:
42 return 2;
43
44 case heif_chroma_interleaved_RGB:
45 case heif_chroma_interleaved_RGBA:
46 default:
47 assert(false);
48 return 0;
49 }
50 }
51
chroma_v_subsampling(heif_chroma c)52 uint8_t heif::chroma_v_subsampling(heif_chroma c)
53 {
54 switch (c) {
55 case heif_chroma_monochrome:
56 case heif_chroma_444:
57 case heif_chroma_422:
58 return 1;
59
60 case heif_chroma_420:
61 return 2;
62
63 case heif_chroma_interleaved_RGB:
64 case heif_chroma_interleaved_RGBA:
65 default:
66 assert(false);
67 return 0;
68 }
69 }
70
chroma_from_subsampling(int h,int v)71 heif_chroma heif::chroma_from_subsampling(int h, int v)
72 {
73 if (h == 2 && v == 2) {
74 return heif_chroma_420;
75 }
76 else if (h == 2 && v == 1) {
77 return heif_chroma_422;
78 }
79 else if (h == 1 && v == 1) {
80 return heif_chroma_444;
81 }
82 else {
83 assert(false);
84 return heif_chroma_undefined;
85 }
86 }
87
88
~HeifPixelImage()89 HeifPixelImage::~HeifPixelImage()
90 {
91 for (auto& iter : m_planes) {
92 delete[] iter.second.allocated_mem;
93 }
94 }
95
96
num_interleaved_pixels_per_plane(heif_chroma chroma)97 int heif::num_interleaved_pixels_per_plane(heif_chroma chroma)
98 {
99 switch (chroma) {
100 case heif_chroma_undefined:
101 case heif_chroma_monochrome:
102 case heif_chroma_420:
103 case heif_chroma_422:
104 case heif_chroma_444:
105 return 1;
106
107 case heif_chroma_interleaved_RGB:
108 case heif_chroma_interleaved_RRGGBB_BE:
109 case heif_chroma_interleaved_RRGGBB_LE:
110 return 3;
111
112 case heif_chroma_interleaved_RGBA:
113 case heif_chroma_interleaved_RRGGBBAA_BE:
114 case heif_chroma_interleaved_RRGGBBAA_LE:
115 return 4;
116 }
117
118 assert(false);
119 return 0;
120 }
121
122
create(int width,int height,heif_colorspace colorspace,heif_chroma chroma)123 void HeifPixelImage::create(int width, int height, heif_colorspace colorspace, heif_chroma chroma)
124 {
125 m_width = width;
126 m_height = height;
127 m_colorspace = colorspace;
128 m_chroma = chroma;
129 }
130
rounded_size(uint32_t s)131 static uint32_t rounded_size(uint32_t s)
132 {
133 s = (s + 1U) & ~1U;
134
135 if (s < 64) {
136 s = 64;
137 }
138
139 return s;
140 }
141
add_plane(heif_channel channel,int width,int height,int bit_depth)142 bool HeifPixelImage::add_plane(heif_channel channel, int width, int height, int bit_depth)
143 {
144 ImagePlane plane;
145 if (plane.alloc(width, height, bit_depth, m_chroma)) {
146 m_planes.insert(std::make_pair(channel, plane));
147 return true;
148 }
149 else {
150 return false;
151 }
152 }
153
154
alloc(int width,int height,int bit_depth,heif_chroma chroma)155 bool HeifPixelImage::ImagePlane::alloc(int width, int height, int bit_depth, heif_chroma chroma)
156 {
157 assert(width >= 0);
158 assert(height >= 0);
159 assert(bit_depth >= 1);
160 assert(bit_depth <= 32);
161
162 // use 16 byte alignment
163 uint16_t alignment = 16; // must be power of two
164
165 m_width = width;
166 m_height = height;
167
168 m_mem_width = rounded_size(width);
169 m_mem_height = rounded_size(height);
170
171 // for backwards compatibility, allow for 24/32 bits for RGB/RGBA interleaved chromas
172
173 if (chroma == heif_chroma_interleaved_RGB && bit_depth == 24) {
174 bit_depth = 8;
175 }
176
177 if (chroma == heif_chroma_interleaved_RGBA && bit_depth == 32) {
178 bit_depth = 8;
179 }
180
181 assert(m_bit_depth <= 16);
182 m_bit_depth = static_cast<uint8_t>(bit_depth);
183
184
185 int bytes_per_component = (m_bit_depth + 7) / 8;
186 int bytes_per_pixel = num_interleaved_pixels_per_plane(chroma) * bytes_per_component;
187
188 stride = m_mem_width * bytes_per_pixel;
189 stride = (stride + alignment - 1U) & ~(alignment - 1U);
190
191 try {
192 allocated_mem = new uint8_t[m_mem_height * stride + alignment - 1];
193 mem = allocated_mem;
194
195 // shift beginning of image data to aligned memory position
196
197 auto mem_start_addr = (uint64_t) mem;
198 auto mem_start_offset = (mem_start_addr & (alignment - 1U));
199 if (mem_start_offset != 0) {
200 mem += alignment - mem_start_offset;
201 }
202
203 return true;
204 }
205 catch (const std::bad_alloc& excpt) {
206 return false;
207 }
208 }
209
210
get_subsampled_size(int width,int height,heif_channel channel,heif_chroma chroma,int * subsampled_width,int * subsampled_height)211 void heif::get_subsampled_size(int width, int height,
212 heif_channel channel,
213 heif_chroma chroma,
214 int* subsampled_width, int* subsampled_height)
215 {
216 if (channel == heif_channel_Cb ||
217 channel == heif_channel_Cr) {
218 uint8_t chromaSubH = chroma_h_subsampling(chroma);
219 uint8_t chromaSubV = chroma_v_subsampling(chroma);
220
221 *subsampled_width = (width + chromaSubH - 1) / chromaSubH;
222 *subsampled_height = (height + chromaSubV - 1) / chromaSubV;
223 }
224 else {
225 *subsampled_width = width;
226 *subsampled_height = height;
227 }
228 }
229
230
extend_to_size(int width,int height)231 bool HeifPixelImage::extend_to_size(int width, int height)
232 {
233 for (auto& planeIter : m_planes) {
234 auto* plane = &planeIter.second;
235
236 int subsampled_width, subsampled_height;
237 get_subsampled_size(width, height, planeIter.first, m_chroma,
238 &subsampled_width, &subsampled_height);
239
240 int old_width = plane->m_width;
241 int old_height = plane->m_height;
242
243 if (plane->m_mem_width < subsampled_width ||
244 plane->m_mem_height < subsampled_height) {
245
246 ImagePlane newPlane;
247 if (!newPlane.alloc(subsampled_width, subsampled_height, plane->m_bit_depth, m_chroma)) {
248 return false;
249 }
250
251 // copy the visible part of the old plane into the new plane
252
253 for (int y = 0; y < plane->m_height; y++) {
254 memcpy(&newPlane.mem[y * newPlane.stride],
255 &plane->mem[y * plane->stride],
256 plane->m_width);
257 }
258
259 planeIter.second = newPlane;
260 plane = &planeIter.second;
261 }
262
263 // extend plane size
264
265 int nbytes = (plane->m_bit_depth + 7) / 8;
266
267 for (int y = 0; y < old_height; y++) {
268 for (int x = old_width; x < subsampled_width; x++) {
269 memcpy(&plane->mem[y * plane->stride + x * nbytes],
270 &plane->mem[y * plane->stride + (plane->m_width - 1) * nbytes],
271 nbytes);
272 }
273 }
274
275 for (int y = old_height; y < subsampled_height; y++) {
276 memcpy(&plane->mem[y * plane->stride],
277 &plane->mem[(plane->m_height - 1) * plane->stride],
278 subsampled_width * nbytes);
279 }
280
281 plane->m_width = subsampled_width;
282 plane->m_height = subsampled_height;
283 }
284
285 m_width = width;
286 m_height = height;
287
288 return true;
289 }
290
291
has_channel(heif_channel channel) const292 bool HeifPixelImage::has_channel(heif_channel channel) const
293 {
294 return (m_planes.find(channel) != m_planes.end());
295 }
296
297
has_alpha() const298 bool HeifPixelImage::has_alpha() const
299 {
300 return has_channel(heif_channel_Alpha) ||
301 get_chroma_format() == heif_chroma_interleaved_RGBA ||
302 get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE ||
303 get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE;
304 }
305
306
get_width(enum heif_channel channel) const307 int HeifPixelImage::get_width(enum heif_channel channel) const
308 {
309 auto iter = m_planes.find(channel);
310 if (iter == m_planes.end()) {
311 return -1;
312 }
313
314 return iter->second.m_width;
315 }
316
317
get_height(enum heif_channel channel) const318 int HeifPixelImage::get_height(enum heif_channel channel) const
319 {
320 auto iter = m_planes.find(channel);
321 if (iter == m_planes.end()) {
322 return -1;
323 }
324
325 return iter->second.m_height;
326 }
327
328
get_channel_set() const329 std::set<heif_channel> HeifPixelImage::get_channel_set() const
330 {
331 std::set<heif_channel> channels;
332
333 for (const auto& plane : m_planes) {
334 channels.insert(plane.first);
335 }
336
337 return channels;
338 }
339
340
get_storage_bits_per_pixel(enum heif_channel channel) const341 uint8_t HeifPixelImage::get_storage_bits_per_pixel(enum heif_channel channel) const
342 {
343 if (channel == heif_channel_interleaved) {
344 auto chroma = get_chroma_format();
345 switch (chroma) {
346 case heif_chroma_interleaved_RGB:
347 return 24;
348 case heif_chroma_interleaved_RGBA:
349 return 32;
350 case heif_chroma_interleaved_RRGGBB_BE:
351 case heif_chroma_interleaved_RRGGBB_LE:
352 return 48;
353 case heif_chroma_interleaved_RRGGBBAA_BE:
354 case heif_chroma_interleaved_RRGGBBAA_LE:
355 return 64;
356 default:
357 return -1; // invalid channel/chroma specification
358 }
359 }
360 else {
361 uint32_t bpp = (get_bits_per_pixel(channel) + 7U) & ~7U;
362 assert(bpp <= 255);
363 return static_cast<uint8_t>(bpp);
364 }
365 }
366
367
get_bits_per_pixel(enum heif_channel channel) const368 uint8_t HeifPixelImage::get_bits_per_pixel(enum heif_channel channel) const
369 {
370 auto iter = m_planes.find(channel);
371 if (iter == m_planes.end()) {
372 return -1;
373 }
374
375 return iter->second.m_bit_depth;
376 }
377
378
get_plane(enum heif_channel channel,int * out_stride)379 uint8_t* HeifPixelImage::get_plane(enum heif_channel channel, int* out_stride)
380 {
381 auto iter = m_planes.find(channel);
382 if (iter == m_planes.end()) {
383 return nullptr;
384 }
385
386 if (out_stride) {
387 *out_stride = iter->second.stride;
388 }
389
390 return iter->second.mem;
391 }
392
393
get_plane(enum heif_channel channel,int * out_stride) const394 const uint8_t* HeifPixelImage::get_plane(enum heif_channel channel, int* out_stride) const
395 {
396 auto iter = m_planes.find(channel);
397 if (iter == m_planes.end()) {
398 return nullptr;
399 }
400
401 if (out_stride) {
402 *out_stride = iter->second.stride;
403 }
404
405 return iter->second.mem;
406 }
407
408
copy_new_plane_from(const std::shared_ptr<const HeifPixelImage> & src_image,heif_channel src_channel,heif_channel dst_channel)409 void HeifPixelImage::copy_new_plane_from(const std::shared_ptr<const HeifPixelImage>& src_image,
410 heif_channel src_channel,
411 heif_channel dst_channel)
412 {
413 int width = src_image->get_width(src_channel);
414 int height = src_image->get_height(src_channel);
415
416 add_plane(dst_channel, width, height, src_image->get_bits_per_pixel(src_channel));
417
418 uint8_t* dst;
419 int dst_stride = 0;
420
421 const uint8_t* src;
422 int src_stride = 0;
423
424 src = src_image->get_plane(src_channel, &src_stride);
425 dst = get_plane(dst_channel, &dst_stride);
426
427 int bpl = width * (src_image->get_storage_bits_per_pixel(src_channel) / 8);
428
429 for (int y = 0; y < height; y++) {
430 memcpy(dst + y * dst_stride, src + y * src_stride, bpl);
431 }
432 }
433
fill_new_plane(heif_channel dst_channel,uint16_t value,int width,int height,int bpp)434 void HeifPixelImage::fill_new_plane(heif_channel dst_channel, uint16_t value, int width, int height, int bpp)
435 {
436 add_plane(dst_channel, width, height, bpp);
437
438 if (bpp == 8) {
439 uint8_t* dst;
440 int dst_stride = 0;
441 dst = get_plane(dst_channel, &dst_stride);
442
443 for (int y = 0; y < height; y++) {
444 memset(dst + y * dst_stride, value, width);
445 }
446 }
447 else {
448 uint16_t* dst;
449 int dst_stride = 0;
450 dst = (uint16_t*) get_plane(dst_channel, &dst_stride);
451
452 dst_stride /= 2;
453
454 for (int y = 0; y < height; y++) {
455 for (int x = 0; x < width; x++) {
456 dst[y * dst_stride + x] = value;
457 }
458 }
459 }
460 }
461
462
transfer_plane_from_image_as(const std::shared_ptr<HeifPixelImage> & source,heif_channel src_channel,heif_channel dst_channel)463 void HeifPixelImage::transfer_plane_from_image_as(const std::shared_ptr<HeifPixelImage>& source,
464 heif_channel src_channel,
465 heif_channel dst_channel)
466 {
467 // TODO: check that dst_channel does not exist yet
468
469 ImagePlane plane = source->m_planes[src_channel];
470 source->m_planes.erase(src_channel);
471
472 m_planes.insert(std::make_pair(dst_channel, plane));
473 }
474
475
is_chroma_with_alpha(heif_chroma chroma)476 bool heif::is_chroma_with_alpha(heif_chroma chroma)
477 {
478 switch (chroma) {
479 case heif_chroma_undefined:
480 case heif_chroma_monochrome:
481 case heif_chroma_420:
482 case heif_chroma_422:
483 case heif_chroma_444:
484 case heif_chroma_interleaved_RGB:
485 case heif_chroma_interleaved_RRGGBB_BE:
486 case heif_chroma_interleaved_RRGGBB_LE:
487 return false;
488
489 case heif_chroma_interleaved_RGBA:
490 case heif_chroma_interleaved_RRGGBBAA_BE:
491 case heif_chroma_interleaved_RRGGBBAA_LE:
492 return true;
493 }
494
495 assert(false);
496 return false;
497 }
498
499
rotate_ccw(int angle_degrees,std::shared_ptr<HeifPixelImage> & out_img)500 Error HeifPixelImage::rotate_ccw(int angle_degrees,
501 std::shared_ptr<HeifPixelImage>& out_img)
502 {
503 // --- create output image (or simply reuse existing image)
504
505 if (angle_degrees == 0) {
506 out_img = shared_from_this();
507 return Error::Ok;
508 }
509
510 int out_width = m_width;
511 int out_height = m_height;
512
513 if (angle_degrees == 90 || angle_degrees == 270) {
514 std::swap(out_width, out_height);
515 }
516
517 out_img = std::make_shared<HeifPixelImage>();
518 out_img->create(out_width, out_height, m_colorspace, m_chroma);
519
520
521 // --- rotate all channels
522
523 for (const auto& plane_pair : m_planes) {
524 heif_channel channel = plane_pair.first;
525 const ImagePlane& plane = plane_pair.second;
526
527 /*
528 if (plane.bit_depth != 8) {
529 return Error(heif_error_Unsupported_feature,
530 heif_suberror_Unspecified,
531 "Can currently only rotate images with 8 bits per pixel");
532 }
533 */
534
535 int out_plane_width = plane.m_width;
536 int out_plane_height = plane.m_height;
537
538 if (angle_degrees == 90 || angle_degrees == 270) {
539 std::swap(out_plane_width, out_plane_height);
540 }
541
542 out_img->add_plane(channel, out_plane_width, out_plane_height, plane.m_bit_depth);
543
544
545 int w = plane.m_width;
546 int h = plane.m_height;
547
548 int in_stride = plane.stride;
549 const uint8_t* in_data = plane.mem;
550
551 int out_stride = 0;
552 uint8_t* out_data = out_img->get_plane(channel, &out_stride);
553
554 if (plane.m_bit_depth == 8) {
555 if (angle_degrees == 270) {
556 for (int x = 0; x < h; x++)
557 for (int y = 0; y < w; y++) {
558 out_data[y * out_stride + x] = in_data[(h - 1 - x) * in_stride + y];
559 }
560 }
561 else if (angle_degrees == 180) {
562 for (int y = 0; y < h; y++)
563 for (int x = 0; x < w; x++) {
564 out_data[y * out_stride + x] = in_data[(h - 1 - y) * in_stride + (w - 1 - x)];
565 }
566 }
567 else if (angle_degrees == 90) {
568 for (int x = 0; x < h; x++)
569 for (int y = 0; y < w; y++) {
570 out_data[y * out_stride + x] = in_data[x * in_stride + (w - 1 - y)];
571 }
572 }
573 }
574 else { // 16 bit (TODO: unchecked code)
575 if (angle_degrees == 270) {
576 for (int x = 0; x < h; x++)
577 for (int y = 0; y < w; y++) {
578 out_data[y * out_stride + 2 * x] = in_data[(h - 1 - x) * in_stride + 2 * y];
579 out_data[y * out_stride + 2 * x + 1] = in_data[(h - 1 - x) * in_stride + 2 * y + 1];
580 }
581 }
582 else if (angle_degrees == 180) {
583 for (int y = 0; y < h; y++)
584 for (int x = 0; x < w; x++) {
585 out_data[y * out_stride + 2 * x] = in_data[(h - 1 - y) * in_stride + 2 * (w - 1 - x)];
586 out_data[y * out_stride + 2 * x + 1] = in_data[(h - 1 - y) * in_stride + 2 * (w - 1 - x) + 1];
587 }
588 }
589 else if (angle_degrees == 90) {
590 for (int x = 0; x < h; x++)
591 for (int y = 0; y < w; y++) {
592 out_data[y * out_stride + 2 * x] = in_data[x * in_stride + 2 * (w - 1 - y)];
593 out_data[y * out_stride + 2 * x + 1] = in_data[x * in_stride + 2 * (w - 1 - y) + 1];
594 }
595 }
596 }
597 }
598
599 // --- pass the color profiles to the new image
600
601 out_img->set_color_profile_nclx(get_color_profile_nclx());
602 out_img->set_color_profile_icc(get_color_profile_icc());
603
604 return Error::Ok;
605 }
606
607
mirror_inplace(bool horizontal)608 Error HeifPixelImage::mirror_inplace(bool horizontal)
609 {
610 for (auto& plane_pair : m_planes) {
611 ImagePlane& plane = plane_pair.second;
612
613 if (plane.m_bit_depth != 8) {
614 return Error(heif_error_Unsupported_feature,
615 heif_suberror_Unspecified,
616 "Can currently only mirror images with 8 bits per pixel");
617 }
618
619
620 int w = plane.m_width;
621 int h = plane.m_height;
622
623 int stride = plane.stride;
624 uint8_t* data = plane.mem;
625
626 if (horizontal) {
627 for (int y = 0; y < h; y++) {
628 for (int x = 0; x < w / 2; x++)
629 std::swap(data[y * stride + x], data[y * stride + w - 1 - x]);
630 }
631 }
632 else {
633 for (int y = 0; y < h / 2; y++) {
634 for (int x = 0; x < w; x++)
635 std::swap(data[y * stride + x], data[(h - 1 - y) * stride + x]);
636 }
637 }
638 }
639
640 return Error::Ok;
641 }
642
643
crop(int left,int right,int top,int bottom,std::shared_ptr<HeifPixelImage> & out_img) const644 Error HeifPixelImage::crop(int left, int right, int top, int bottom,
645 std::shared_ptr<HeifPixelImage>& out_img) const
646 {
647 out_img = std::make_shared<HeifPixelImage>();
648 out_img->create(right - left + 1, bottom - top + 1, m_colorspace, m_chroma);
649
650
651 // --- crop all channels
652
653 for (const auto& plane_pair : m_planes) {
654 heif_channel channel = plane_pair.first;
655 const ImagePlane& plane = plane_pair.second;
656
657 if (false && plane.m_bit_depth != 8) {
658 return Error(heif_error_Unsupported_feature,
659 heif_suberror_Unspecified,
660 "Can currently only crop images with 8 bits per pixel");
661 }
662
663
664 int w = plane.m_width;
665 int h = plane.m_height;
666
667 int plane_left = left * w / m_width;
668 int plane_right = right * w / m_width;
669 int plane_top = top * h / m_height;
670 int plane_bottom = bottom * h / m_height;
671
672 out_img->add_plane(channel,
673 plane_right - plane_left + 1,
674 plane_bottom - plane_top + 1,
675 plane.m_bit_depth);
676
677 int in_stride = plane.stride;
678 const uint8_t* in_data = plane.mem;
679
680 int out_stride = 0;
681 uint8_t* out_data = out_img->get_plane(channel, &out_stride);
682
683 if (plane.m_bit_depth == 8) {
684 for (int y = plane_top; y <= plane_bottom; y++) {
685 memcpy(&out_data[(y - plane_top) * out_stride],
686 &in_data[y * in_stride + plane_left],
687 plane_right - plane_left + 1);
688 }
689 }
690 else {
691 for (int y = plane_top; y <= plane_bottom; y++) {
692 memcpy(&out_data[(y - plane_top) * out_stride],
693 &in_data[y * in_stride + plane_left * 2],
694 (plane_right - plane_left + 1) * 2);
695 }
696 }
697 }
698
699 // --- pass the color profiles to the new image
700
701 out_img->set_color_profile_nclx(get_color_profile_nclx());
702 out_img->set_color_profile_icc(get_color_profile_icc());
703
704 return Error::Ok;
705 }
706
707
fill_RGB_16bit(uint16_t r,uint16_t g,uint16_t b,uint16_t a)708 Error HeifPixelImage::fill_RGB_16bit(uint16_t r, uint16_t g, uint16_t b, uint16_t a)
709 {
710 for (const auto& channel : {heif_channel_R, heif_channel_G, heif_channel_B, heif_channel_Alpha}) {
711
712 const auto plane_iter = m_planes.find(channel);
713 if (plane_iter == m_planes.end()) {
714
715 // alpha channel is optional, R,G,B is required
716 if (channel == heif_channel_Alpha) {
717 continue;
718 }
719
720 return Error(heif_error_Usage_error,
721 heif_suberror_Nonexisting_image_channel_referenced);
722
723 }
724
725 ImagePlane& plane = plane_iter->second;
726
727 if (plane.m_bit_depth != 8) {
728 return Error(heif_error_Unsupported_feature,
729 heif_suberror_Unspecified,
730 "Can currently only fill images with 8 bits per pixel");
731 }
732
733 int h = plane.m_height;
734
735 int stride = plane.stride;
736 uint8_t* data = plane.mem;
737
738 uint16_t val16;
739 switch (channel) {
740 case heif_channel_R:
741 val16 = r;
742 break;
743 case heif_channel_G:
744 val16 = g;
745 break;
746 case heif_channel_B:
747 val16 = b;
748 break;
749 case heif_channel_Alpha:
750 val16 = a;
751 break;
752 default:
753 // initialization only to avoid warning of uninitalized variable.
754 val16 = 0;
755 // Should already be detected by the check above ("m_planes.find").
756 assert(false);
757 }
758
759 auto val8 = static_cast<uint8_t>(val16 >> 8U);
760
761 memset(data, val8, stride * h);
762 }
763
764 return Error::Ok;
765 }
766
767
overlay(std::shared_ptr<HeifPixelImage> & overlay,int dx,int dy)768 Error HeifPixelImage::overlay(std::shared_ptr<HeifPixelImage>& overlay, int dx, int dy)
769 {
770 std::set<enum heif_channel> channels = overlay->get_channel_set();
771
772 bool has_alpha = overlay->has_channel(heif_channel_Alpha);
773 //bool has_alpha_me = has_channel(heif_channel_Alpha);
774
775 int alpha_stride = 0;
776 uint8_t* alpha_p;
777 alpha_p = overlay->get_plane(heif_channel_Alpha, &alpha_stride);
778
779 for (heif_channel channel : channels) {
780 if (!has_channel(channel)) {
781 continue;
782 }
783
784 int in_stride = 0;
785 const uint8_t* in_p;
786
787 int out_stride = 0;
788 uint8_t* out_p;
789
790 in_p = overlay->get_plane(channel, &in_stride);
791 out_p = get_plane(channel, &out_stride);
792
793 int in_w = overlay->get_width(channel);
794 int in_h = overlay->get_height(channel);
795 assert(in_w >= 0);
796 assert(in_h >= 0);
797
798 int out_w = get_width(channel);
799 int out_h = get_height(channel);
800 assert(out_w >= 0);
801 assert(out_h >= 0);
802
803 // overlay image extends past the right border -> cut width for copy
804 if (dx + in_w > out_w) {
805 in_w = out_w - dx;
806 }
807
808 // overlay image extends past the bottom border -> cut height for copy
809 if (dy + in_h > out_h) {
810 in_h = out_h - dy;
811 }
812
813 // overlay image completely outside right or bottom border -> do not copy
814 if (in_w < 0 || in_h < 0) {
815 return Error(heif_error_Invalid_input,
816 heif_suberror_Overlay_image_outside_of_canvas,
817 "Overlay image outside of right or bottom canvas border");
818 }
819
820
821 // calculate top-left point where to start copying in source and destination
822 int in_x0 = 0;
823 int in_y0 = 0;
824 int out_x0 = dx;
825 int out_y0 = dy;
826
827 // overlay image started outside of left border
828 // -> move start into the image and start at left output column
829 if (dx < 0) {
830 in_x0 = -dx;
831 out_x0 = 0;
832 }
833
834 // overlay image started outside of top border
835 // -> move start into the image and start at top output row
836 if (dy < 0) {
837 in_y0 = -dy;
838 out_y0 = 0;
839 }
840
841 // if overlay image is completely outside at left border, do not copy anything.
842 if (in_w <= in_x0 ||
843 in_h <= in_y0) {
844 return Error(heif_error_Invalid_input,
845 heif_suberror_Overlay_image_outside_of_canvas,
846 "Overlay image outside of left or top canvas border");
847 }
848
849 for (int y = in_y0; y < in_h; y++) {
850 if (!has_alpha) {
851 memcpy(out_p + out_x0 + (out_y0 + y - in_y0) * out_stride,
852 in_p + in_x0 + y * in_stride,
853 in_w - in_x0);
854 }
855 else {
856 for (int x = in_x0; x < in_w; x++) {
857 uint8_t* outptr = &out_p[out_x0 + (out_y0 + y - in_y0) * out_stride + x];
858 uint8_t in_val = in_p[in_x0 + y * in_stride + x];
859 uint8_t alpha_val = alpha_p[in_x0 + y * in_stride + x];
860
861 *outptr = (uint8_t) ((in_val * alpha_val + *outptr * (255 - alpha_val)) / 255);
862 }
863 }
864 }
865 }
866
867 return Error::Ok;
868 }
869
870
scale_nearest_neighbor(std::shared_ptr<HeifPixelImage> & out_img,int width,int height) const871 Error HeifPixelImage::scale_nearest_neighbor(std::shared_ptr<HeifPixelImage>& out_img,
872 int width, int height) const
873 {
874 out_img = std::make_shared<HeifPixelImage>();
875 out_img->create(width, height, m_colorspace, m_chroma);
876
877
878 // --- scale all channels
879
880 for (const auto& plane_pair : m_planes) {
881 heif_channel channel = plane_pair.first;
882 const ImagePlane& plane = plane_pair.second;
883
884 const int bpp = get_storage_bits_per_pixel(channel) / 8;
885
886 int in_w = plane.m_width;
887 int in_h = plane.m_height;
888
889 int out_w = in_w * width / m_width;
890 int out_h = in_h * height / m_height;
891
892 out_img->add_plane(channel,
893 out_w,
894 out_h,
895 plane.m_bit_depth);
896
897 if (!width || !height) {
898 continue;
899 }
900
901 int in_stride = plane.stride;
902 const uint8_t* in_data = plane.mem;
903
904 int out_stride = 0;
905 uint8_t* out_data = out_img->get_plane(channel, &out_stride);
906
907
908 for (int y = 0; y < out_h; y++) {
909 int iy = y * m_height / height;
910
911 if (bpp == 1) {
912 for (int x = 0; x < out_w; x++) {
913 int ix = x * m_width / width;
914
915 out_data[y * out_stride + x] = in_data[iy * in_stride + ix];
916 }
917 }
918 else {
919 for (int x = 0; x < out_w; x++) {
920 int ix = x * m_width / width;
921
922 for (int b = 0; b < bpp; b++) {
923 out_data[y * out_stride + bpp * x + b] = in_data[iy * in_stride + bpp * ix + b];
924 }
925 }
926 }
927 }
928 }
929
930 return Error::Ok;
931 }
932
933
debug_dump() const934 void HeifPixelImage::debug_dump() const
935 {
936 auto channels = get_channel_set();
937 for (auto c : channels) {
938 int stride = 0;
939 const uint8_t* p = get_plane(c, &stride);
940
941 for (int y = 0; y < 8; y++) {
942 for (int x = 0; x < 8; x++) {
943 printf("%02x ", p[y * stride + x]);
944 }
945 printf("\n");
946 }
947 }
948 }
949