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