]> granicus.if.org Git - imagemagick/blob - coders/heic.c
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=7329
[imagemagick] / coders / heic.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                        H   H  EEEEE  IIIII   CCCC                           %
7 %                        H   H  E        I    C                               %
8 %                        HHHHH  EEE      I    C                               %
9 %                        H   H  E        I    C                               %
10 %                        H   H  EEEEE  IIIII   CCCC                           %
11 %                                                                             %
12 %                                                                             %
13 %                         Read/Write Heic Image Format                        %
14 %                                                                             %
15 %                              Software Design                                %
16 %                               Anton Kortunov                                %
17 %                               December 2017                                 %
18 %                                                                             %
19 %                                                                             %
20 %                      Copyright 2017-2018 YANDEX LLC.                        %
21 %                                                                             %
22 %  You may not use this file except in compliance with the License.  You may  %
23 %  obtain a copy of the License at                                            %
24 %                                                                             %
25 %    https://www.imagemagick.org/script/license.php                           %
26 %                                                                             %
27 %  Unless required by applicable law or agreed to in writing, software        %
28 %  distributed under the License is distributed on an "AS IS" BASIS,          %
29 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
30 %  See the License for the specific language governing permissions and        %
31 %  limitations under the License.                                             %
32 %                                                                             %
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34 %
35 %
36 */
37
38 /*
39   Include declarations.
40 */
41 #include "MagickCore/studio.h"
42 #include "MagickCore/artifact.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/blob-private.h"
45 #include "MagickCore/client.h"
46 #include "MagickCore/colorspace-private.h"
47 #include "MagickCore/property.h"
48 #include "MagickCore/display.h"
49 #include "MagickCore/exception.h"
50 #include "MagickCore/exception-private.h"
51 #include "MagickCore/image.h"
52 #include "MagickCore/image-private.h"
53 #include "MagickCore/list.h"
54 #include "MagickCore/magick.h"
55 #include "MagickCore/monitor.h"
56 #include "MagickCore/monitor-private.h"
57 #include "MagickCore/montage.h"
58 #include "MagickCore/transform.h"
59 #include "MagickCore/memory_.h"
60 #include "MagickCore/memory-private.h"
61 #include "MagickCore/option.h"
62 #include "MagickCore/pixel-accessor.h"
63 #include "MagickCore/quantum-private.h"
64 #include "MagickCore/static.h"
65 #include "MagickCore/string_.h"
66 #include "MagickCore/string-private.h"
67 #include "MagickCore/module.h"
68 #include "MagickCore/utility.h"
69 #if defined(MAGICKCORE_HEIC_DELEGATE)
70 #include <libde265/de265.h>
71 #endif
72
73 /*
74   Typedef declarations.
75 */
76 #if defined(MAGICKCORE_HEIC_DELEGATE)
77
78 #define MAX_ASSOCS_COUNT 10
79 #define MAX_ITEM_PROPS 100
80 #define MAX_HVCC_ATOM_SIZE 1024
81 #define MAX_ATOMS_IN_BOX 100
82 #define BUFFER_SIZE 100
83
84 typedef struct _HEICItemInfo
85 {
86   unsigned int
87     type;
88
89   unsigned int
90     assocsCount;
91
92   uint8_t
93     assocs[MAX_ASSOCS_COUNT];
94
95   unsigned int
96     dataSource;
97
98   unsigned int
99     offset;
100
101   unsigned int
102     size;
103 } HEICItemInfo;
104
105 typedef struct _HEICItemProp
106 {
107   unsigned int
108     type;
109
110   unsigned int
111     size;
112
113   uint8_t
114     *data;
115 } HEICItemProp;
116
117 typedef struct _HEICGrid
118 {
119   unsigned int
120     id;
121
122   unsigned int
123     rowsMinusOne;
124
125   unsigned int
126     columnsMinusOne;
127
128   unsigned int
129     imageWidth;
130
131   unsigned int
132     imageHeight;
133 } HEICGrid;
134
135 typedef struct _HEICImageContext
136 {
137   MagickBooleanType
138     finished;
139
140   int
141     idsCount;
142
143   HEICItemInfo
144     *itemInfo;
145
146   int
147     itemPropsCount;
148
149   HEICItemProp
150     itemProps[MAX_ITEM_PROPS];
151
152   unsigned int
153     idatSize;
154
155   uint8_t
156     *idat;
157
158   HEICGrid
159     grid;
160
161   de265_decoder_context
162     *h265Ctx;
163
164   Image
165     *tmp;
166 } HEICImageContext;
167
168 typedef struct _DataBuffer {
169     unsigned char
170         *data;
171
172     off_t
173         pos;
174
175     size_t
176         size;
177 } DataBuffer;
178
179
180 #define ATOM(a,b,c,d) ((a << 24) + (b << 16) + (c << 8) + d)
181 #define ThrowImproperImageHeader(msg) { \
182   (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError, \
183     "ImproperImageHeader","`%s'",msg); \
184 }
185 #define ThrowAndReturn(msg) { \
186   ThrowImproperImageHeader(msg) \
187   return(MagickFalse); \
188 }
189
190 inline static unsigned int readInt(const unsigned char* data)
191 {
192   unsigned int val = 0;
193
194   val=(unsigned int)(data[0]) << 24;
195   val|=(unsigned int)(data[1]) << 16;
196   val|=(unsigned int)(data[2]) << 8;
197   val|=(unsigned int)(data[3]);
198
199   return val;
200 }
201
202 inline static MagickSizeType DBChop(DataBuffer *head, DataBuffer *db, size_t size)
203 {
204   if (size > (db->size - db->pos)) {
205     return MagickFalse;
206   }
207
208   head->data = db->data + db->pos;
209   head->pos = 0;
210   head->size = size;
211
212   db->pos += size;
213
214   return MagickTrue;
215 }
216
217 inline static uint32_t DBReadUInt(DataBuffer *db)
218 {
219   uint32_t val = 0;
220
221   if (db->size - db->pos < 4) {
222     db->pos = db->size;
223     return 0;
224   }
225
226   val=(uint32_t) db->data[db->pos+0] << 24;
227   val|=(uint32_t) db->data[db->pos+1] << 16;
228   val|=(uint32_t) db->data[db->pos+2] << 8;
229   val|=(uint32_t) db->data[db->pos+3];
230
231   db->pos += 4;
232
233   return val;
234 }
235
236 inline static uint16_t DBReadUShort(DataBuffer *db)
237 {
238   uint16_t val = 0;
239
240   if (db->size - db->pos < 2) {
241     db->pos = db->size;
242     return 0;
243   }
244
245   val=(uint16_t) db->data[db->pos+0] << 8;
246   val|=(uint16_t) db->data[db->pos+1];
247
248   db->pos += 2;
249
250   return val;
251 }
252
253 inline static uint8_t DBReadUChar(DataBuffer *db)
254 {
255   uint8_t val;
256
257   if (db->size - db->pos < 2) {
258     db->pos = db->size;
259     return 0;
260   }
261
262   val=(uint8_t) db->data[db->pos];
263   db->pos += 1;
264
265   return val;
266 }
267
268 inline static size_t DBGetSize(DataBuffer *db)
269 {
270   return db->size - db->pos;
271 }
272
273 inline static void DBSkip(DataBuffer *db, size_t skip)
274 {
275   if (db->pos + skip > db->size)
276   {
277     db->pos = db->size;
278   } else {
279     db->pos += skip;
280   }
281 }
282
283 static MagickBooleanType ParseAtom(Image *image, DataBuffer *db,
284     HEICImageContext *ctx, ExceptionInfo *exception);
285
286 static MagickBooleanType ParseFullBox(Image *image, DataBuffer *db,
287     unsigned int atom, HEICImageContext *ctx, ExceptionInfo *exception)
288 {
289   unsigned int
290     version, flags, i;
291
292   flags = DBReadUInt(db);
293   version = flags >> 24;
294   flags &= 0xffffff;
295
296   (void) flags;
297   (void) version;
298
299   if (DBGetSize(db) < 4) {
300     ThrowAndReturn("atom is too short");
301   }
302
303   for (i = 0; i < MAX_ATOMS_IN_BOX && DBGetSize(db) > 0; i++) {
304     if (ParseAtom(image, db, ctx, exception) == MagickFalse)
305       return MagickFalse;
306   }
307
308   return MagickTrue;
309 }
310
311 static MagickBooleanType ParseBox(Image *image, DataBuffer *db,
312     unsigned int atom, HEICImageContext *ctx, ExceptionInfo *exception)
313 {
314   unsigned int
315     i;
316
317   for (i = 0; i < MAX_ATOMS_IN_BOX && DBGetSize(db) > 0; i++) {
318     if (ParseAtom(image, db, ctx, exception) == MagickFalse)
319       return MagickFalse;
320   }
321
322   return MagickTrue;
323 }
324
325 static MagickBooleanType ParseHvcCAtom(HEICItemProp *prop, ExceptionInfo *exception)
326 {
327   size_t
328     size, pos, count, i;
329
330   uint8_t
331     buffer[MAX_HVCC_ATOM_SIZE];
332
333   uint8_t
334     *p;
335
336   p = prop->data;
337
338   size = prop->size;
339   memcpy(buffer, prop->data, size);
340
341   pos = 22;
342   if (pos >= size) {
343     ThrowAndReturn("hvcC atom is too short");
344   }
345
346   count = buffer[pos++];
347
348   for (i = 0; i < count && pos < size-3; i++) {
349     size_t
350       naluType, num, j;
351
352     naluType = buffer[pos++] & 0x3f;
353     (void) naluType;
354     num = buffer[pos++] << 8;
355     num += buffer[pos++];
356
357     for (j = 0; j < num && pos < size-2; j++) {
358       size_t
359         naluSize;
360
361       naluSize = buffer[pos++] << 8;
362       naluSize += buffer[pos++];
363
364       if ((pos + naluSize > size) ||
365           (p + naluSize > prop->data + prop->size)) {
366         ThrowAndReturn("hvcC atom is too short");
367       }
368
369       /* AnnexB NALU header */
370       *p++ = 0;
371       *p++ = 0;
372       *p++ = 0;
373       *p++ = 1;
374
375       memcpy(p, buffer + pos, naluSize);
376       p += naluSize;
377       pos += naluSize;
378     }
379   }
380
381   prop->size = p - prop->data;
382   return MagickTrue;
383 }
384
385 static MagickBooleanType ParseIpcoAtom(Image *image, DataBuffer *db,
386     HEICImageContext *ctx, ExceptionInfo *exception)
387 {
388   unsigned int
389     length, atom;
390
391   HEICItemProp
392     *prop;
393
394   /*
395      property indicies starts from 1
396   */
397   for (ctx->itemPropsCount = 1; ctx->itemPropsCount < MAX_ITEM_PROPS && DBGetSize(db) > 8; ctx->itemPropsCount++) {
398     DataBuffer
399       propDb;
400
401     length = DBReadUInt(db);
402     atom = DBReadUInt(db);
403
404     if (ctx->itemPropsCount == MAX_ITEM_PROPS) {
405       ThrowAndReturn("too many item properties");
406     }
407
408     prop = &(ctx->itemProps[ctx->itemPropsCount]);
409     prop->type = atom;
410     prop->size = length - 8;
411     if (prop->size > DBGetSize(db))
412       ThrowAndReturn("insufficient data");
413     if (prop->data != (uint8_t *) NULL)
414       prop->data=(uint8_t *) RelinquishMagickMemory(prop->data);
415     prop->data = (uint8_t *) AcquireCriticalMemory(prop->size+4);
416     (void) memset(prop->data, 0, prop->size+4);
417     if (DBChop(&propDb, db, prop->size) != MagickTrue) {
418       ThrowAndReturn("incorrect read size");
419     }
420     memcpy(prop->data, propDb.data, prop->size);
421
422     switch (prop->type) {
423       case ATOM('h', 'v', 'c', 'C'):
424         ParseHvcCAtom(prop, exception);
425         break;
426       default:
427         break;
428     }
429   }
430
431   return MagickTrue;
432 }
433
434 static MagickBooleanType ParseIinfAtom(Image *image, DataBuffer *db,
435     HEICImageContext *ctx, ExceptionInfo *exception)
436 {
437   unsigned int
438     version, flags, count, i;
439
440   if (DBGetSize(db) < 4) {
441     ThrowAndReturn("atom is too short");
442   }
443
444   flags = DBReadUInt(db);
445   version = flags >> 24;
446   flags = 0xffffff;
447
448   if (version == 0) {
449    count = DBReadUShort(db);
450   } else {
451     count = DBReadUInt(db);
452   }
453
454   /*
455      item indicies starts from 1
456   */
457   ctx->idsCount = count;
458   if (ctx->itemInfo != (HEICItemInfo *) NULL)
459     ctx->itemInfo=(HEICItemInfo *) RelinquishMagickMemory(ctx->itemInfo);
460   if ((8.0*count) > (double)DBGetSize(db))
461     ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile",
462       image->filename);
463   ctx->itemInfo = (HEICItemInfo *)AcquireMagickMemory(sizeof(HEICItemInfo)*(count+1));
464   if (ctx->itemInfo == (HEICItemInfo *) NULL)
465     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
466       image->filename);
467
468   memset(ctx->itemInfo, 0, sizeof(HEICItemInfo)*(count+1));
469
470   for (i = 0; i < count && DBGetSize(db) > 0; i++)
471   {
472     (void) ParseAtom(image, db, ctx, exception);
473   }
474
475   return MagickTrue;
476 }
477
478 static MagickBooleanType ParseInfeAtom(Image *image, DataBuffer *db,
479     HEICImageContext *ctx, ExceptionInfo *exception)
480 {
481   unsigned int
482     version, flags, id, type;
483
484   if (DBGetSize(db) < 9) {
485     ThrowAndReturn("atom is too short");
486   }
487
488   flags = DBReadUInt(db);
489   version = flags >> 24;
490   flags = 0xffffff;
491
492   if (version != 2) {
493     ThrowAndReturn("unsupported infe atom version");
494   }
495
496   id = DBReadUShort(db);
497   DBSkip(db, 2);   /* item protection index */
498   type = DBReadUInt(db);
499
500   /*
501      item indicies starts from 1
502   */
503   if ((id > (ssize_t) ctx->idsCount) ||
504       (ctx->itemInfo == (HEICItemInfo *) NULL))
505     ThrowAndReturn("item id is incorrect");
506
507   ctx->itemInfo[id].type = type;
508
509   return MagickTrue;
510 }
511
512 static MagickBooleanType ParseIpmaAtom(Image *image, DataBuffer *db,
513     HEICImageContext *ctx, ExceptionInfo *exception)
514 {
515   unsigned int
516     version, flags, count, i;
517
518   if (DBGetSize(db) < 9) {
519     ThrowAndReturn("atom is too short");
520   }
521
522   flags = DBReadUInt(db);
523   version = flags >> 24;
524   flags = 0xffffff;
525
526   count = DBReadUInt(db);
527
528   for (i = 0; i < count && DBGetSize(db) > 2; i++) {
529     unsigned int
530       id, assoc_count, j;
531
532     if (version < 1) {
533       id = DBReadUShort(db);
534     } else {
535       id = DBReadUInt(db);
536     }
537
538     /*
539        item indicies starts from 1
540        */
541     if ((id > (ssize_t) ctx->idsCount) ||
542         (ctx->itemInfo == (HEICItemInfo *) NULL))
543       ThrowAndReturn("item id is incorrect");
544
545     assoc_count = DBReadUChar(db);
546
547     if (assoc_count >= MAX_ASSOCS_COUNT) {
548       ThrowAndReturn("too many associations");
549     }
550
551     for (j = 0; j < assoc_count && DBGetSize(db) > 0; j++) {
552       ctx->itemInfo[id].assocs[j] = DBReadUChar(db);
553     }
554
555     ctx->itemInfo[id].assocsCount = j;
556   }
557
558   return MagickTrue;
559 }
560
561 static MagickBooleanType ParseIlocAtom(Image *image, DataBuffer *db,
562     HEICImageContext *ctx, ExceptionInfo *exception)
563 {
564   unsigned int
565     version, flags, tmp, count, i;
566
567   if (DBGetSize(db) < 9) {
568     ThrowAndReturn("atom is too short");
569   }
570
571   flags = DBReadUInt(db);
572   version = flags >> 24;
573   flags = 0xffffff;
574
575   tmp = DBReadUChar(db);
576   if (tmp != 0x44) {
577     ThrowAndReturn("only offset_size=4 and length_size=4 are supported");
578   }
579   tmp = DBReadUChar(db);
580   if (tmp != 0x00) {
581     ThrowAndReturn("only base_offset_size=0 and index_size=0 are supported");
582   }
583
584   if (version < 2) {
585     count = DBReadUShort(db);
586   } else {
587     count = DBReadUInt(db);
588   }
589
590   for (i = 0; i < count && DBGetSize(db) > 2; i++) {
591     unsigned int
592       id, ext_count;
593
594     HEICItemInfo
595       *item;
596
597     id = DBReadUShort(db);
598
599     /*
600        item indicies starts from 1
601     */
602     if ((id > (ssize_t) ctx->idsCount) ||
603         (ctx->itemInfo == (HEICItemInfo *) NULL))
604       ThrowAndReturn("item id is incorrect");
605
606     item = &ctx->itemInfo[id];
607
608     if (version == 1 || version == 2) {
609       item->dataSource = DBReadUShort(db);
610     }
611
612     /*
613      * data ref index
614      */
615     DBSkip(db, 2);
616     ext_count = DBReadUShort(db);
617
618     if (ext_count != 1) {
619       ThrowAndReturn("only one excention per item is supported");
620     }
621
622     item->offset = DBReadUInt(db);
623     item->size = DBReadUInt(db);
624   }
625
626   return MagickTrue;
627 }
628
629 static MagickBooleanType ParseAtom(Image *image, DataBuffer *db,
630     HEICImageContext *ctx, ExceptionInfo *exception)
631 {
632   DataBuffer
633     atomDb;
634
635   MagickBooleanType
636     status;
637
638   MagickSizeType
639     atom_size;
640
641   unsigned int
642     atom;
643
644   if (DBGetSize(db) < 8)
645   {
646     ThrowAndReturn("atom is too short");
647   }
648
649   atom_size = DBReadUInt(db);
650   atom = DBReadUInt(db);
651
652   if (atom_size == 1) {
653     /* Only 32 bit atom size are supported */
654     DBReadUInt(db);
655     atom_size = DBReadUInt(db);
656   }
657
658   if (atom_size - 8 > DBGetSize(db))
659   {
660     ThrowAndReturn("atom is too short");
661   }
662
663   if (DBChop(&atomDb, db, atom_size - 8) != MagickTrue)
664   {
665     ThrowAndReturn("unable to read atom");
666   }
667
668   status = MagickTrue;
669
670   switch (atom)
671   {
672     case ATOM('i', 'r', 'e', 'f'):
673       status = ParseFullBox(image, &atomDb, atom, ctx, exception);
674       break;
675     case ATOM('i', 'p', 'r', 'p'):
676       status = ParseBox(image, &atomDb, atom, ctx, exception);
677       break;
678     case ATOM('i', 'i', 'n', 'f'):
679       status = ParseIinfAtom(image, &atomDb, ctx, exception);
680       break;
681     case ATOM('i', 'n', 'f', 'e'):
682       status = ParseInfeAtom(image, &atomDb, ctx, exception);
683       break;
684     case ATOM('i', 'p', 'c', 'o'):
685       status = ParseIpcoAtom(image, &atomDb, ctx, exception);
686       break;
687     case ATOM('i', 'p', 'm', 'a'):
688       status = ParseIpmaAtom(image, &atomDb, ctx, exception);
689       break;
690     case ATOM('i', 'l', 'o', 'c'):
691       status = ParseIlocAtom(image, &atomDb, ctx, exception);
692       break;
693     case ATOM('i', 'd', 'a', 't'):
694       {
695         ctx->idatSize = atom_size - 8;
696         if (ctx->idat != (uint8_t *) NULL)
697           ctx->idat = (uint8_t *) RelinquishMagickMemory(ctx->idat);
698         ctx->idat = (uint8_t *) AcquireMagickMemory(ctx->idatSize);
699         if (ctx->idat == (uint8_t *) NULL)
700           ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
701             image->filename);
702
703         memcpy(ctx->idat, atomDb.data, ctx->idatSize);
704       }
705       break;
706     default:
707       break;
708   }
709
710   return status;
711 }
712
713
714 static MagickBooleanType ParseRootAtom(Image *image,MagickSizeType *size,
715   HEICImageContext *ctx,ExceptionInfo *exception)
716 {
717   MagickBooleanType
718     status;
719
720   MagickSizeType
721     atom_size;
722
723   unsigned int
724     atom;
725
726   if (*size < 8)
727     ThrowAndReturn("atom is too short");
728
729   atom_size = ReadBlobMSBLong(image);
730   atom = ReadBlobMSBLong(image);
731
732   if (atom_size == 1) {
733     ReadBlobMSBLong(image);
734     atom_size = ReadBlobMSBLong(image);
735   }
736
737
738   if (atom_size > *size)
739     ThrowAndReturn("atom is too short");
740
741   status = MagickTrue;
742
743   switch (atom)
744   {
745     case ATOM('f', 't', 'y', 'p'):
746       DiscardBlobBytes(image, atom_size-8);
747       break;
748     case ATOM('m', 'e', 't', 'a'):
749       {
750         DataBuffer
751           db;
752
753         size_t
754           count;
755
756         db.pos = 0;
757         db.size = atom_size - 8;
758         db.data = (unsigned char *) AcquireMagickMemory(db.size);
759         if (db.data == NULL)
760           ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
761             image->filename);
762
763         count = ReadBlob(image, db.size, db.data);
764         if (count != db.size) {
765           RelinquishMagickMemory((void *)db.data);
766           ThrowAndReturn("unable to read data");
767         }
768
769         /*
770          * Meta flags and version
771          */
772         /* DBSkip(&db, 4); */
773         status = ParseFullBox(image, &db, atom, ctx, exception);
774         RelinquishMagickMemory((void *)db.data);
775       }
776       break;
777     case ATOM('m', 'd', 'a', 't'):
778       ctx->finished = MagickTrue;
779       break;
780     default:
781       DiscardBlobBytes(image, atom_size-8);
782       break;
783   }
784   *size=*size-atom_size;
785   return(status);
786 }
787
788 static MagickBooleanType decodeGrid(HEICImageContext *ctx,
789   ExceptionInfo *exception)
790 {
791   unsigned int
792     i, flags;
793
794   if (ctx->itemInfo == (HEICItemInfo *) NULL)
795     ThrowAndReturn("no atoms defined");
796   for (i = 1; i <= (ssize_t) ctx->idsCount; i++) {
797     HEICItemInfo
798       *info = &ctx->itemInfo[i];
799     if (info->type != ATOM('g','r','i','d'))
800       continue;
801     if (info->dataSource != 1) {
802       ThrowAndReturn("unsupport data source type");
803     }
804
805     if (ctx->idatSize < 8) {
806       ThrowAndReturn("idat is too small");
807     }
808
809     flags = ctx->idat[1];
810
811     ctx->grid.rowsMinusOne = ctx->idat[2];
812     ctx->grid.columnsMinusOne = ctx->idat[3];
813
814     if (flags & 1) {
815       ThrowAndReturn("Only 16 bits sizes are supported");
816     }
817
818     ctx->grid.imageWidth = (ctx->idat[4] << 8) + ctx->idat[5];
819     ctx->grid.imageHeight = (ctx->idat[6] << 8) + ctx->idat[7];
820
821     ctx->grid.id = i;
822
823     return MagickTrue;
824   }
825   return MagickFalse;
826 }
827
828 static MagickBooleanType decodeH265Image(Image *image, HEICImageContext *ctx, unsigned int id, ExceptionInfo *exception)
829 {
830   unsigned char
831     *buffer = NULL;
832
833   unsigned char
834     *p;
835
836   size_t
837     count, pos, nal_unit_size;
838
839   int
840     more, i;
841
842   unsigned int
843     x_offset, y_offset;
844
845   de265_error
846     err;
847
848   pos = 0;
849   de265_reset(ctx->h265Ctx);
850
851   x_offset = 512 * ((id-1) % (ctx->grid.columnsMinusOne + 1));
852   y_offset = 512 * ((id-1) / (ctx->grid.columnsMinusOne + 1));
853
854   for (i = 0; i < (ssize_t) ctx->itemInfo[id].assocsCount; i++) {
855     ssize_t
856       assoc;
857
858     assoc = ctx->itemInfo[id].assocs[i] & 0x7f;
859     if (assoc > ctx->itemPropsCount) {
860       ThrowImproperImageHeader("incorrect item property index");
861       goto err_out_free;
862     }
863
864     switch (ctx->itemProps[assoc].type) {
865       case ATOM('h', 'v', 'c', 'C'):
866         err = de265_push_data(ctx->h265Ctx, ctx->itemProps[assoc].data, ctx->itemProps[assoc].size, pos, (void*)2);
867         if (err != DE265_OK) {
868           ThrowImproperImageHeader("unable to push data");
869           goto err_out_free;
870         }
871
872         pos += ctx->itemProps[assoc].size;
873         break;
874       case ATOM('c', 'o', 'l', 'r'):
875         {
876           StringInfo
877             *profile;
878
879           if (ctx->itemProps[assoc].size < 16)
880               continue;
881
882           profile=BlobToStringInfo(ctx->itemProps[assoc].data + 4, ctx->itemProps[assoc].size - 4);
883           (void) SetImageProfile(image, "icc", profile, exception);
884           profile=DestroyStringInfo(profile);
885           break;
886         }
887     }
888   }
889
890   buffer = (unsigned char *) AcquireMagickMemory(ctx->itemInfo[id].size);
891   if (buffer == NULL) {
892     (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
893       "MemoryAllocationFailed","`%s'",image->filename);
894     goto err_out_free;
895   }
896
897   SeekBlob(image, ctx->itemInfo[id].offset, SEEK_SET);
898   count = ReadBlob(image, ctx->itemInfo[id].size, buffer);
899   if (count != ctx->itemInfo[id].size) {
900     ThrowImproperImageHeader("unable to read data");
901     goto err_out_free;
902   }
903
904   /*
905    * AVCC to AnnexB
906    */
907   for (p = buffer; p < buffer + ctx->itemInfo[id].size; /* void */) {
908     nal_unit_size = readInt(p);
909     p[0] = 0;
910     p[1] = 0;
911     p[2] = 0;
912     p[3] = 1;
913     p += nal_unit_size + 4;
914   }
915
916   err = de265_push_data(ctx->h265Ctx, buffer, ctx->itemInfo[id].size, pos, (void*)2);
917   if (err != DE265_OK) {
918     ThrowImproperImageHeader("unable to push data");
919     goto err_out_free;
920   }
921
922   err = de265_flush_data(ctx->h265Ctx);
923   if (err != DE265_OK) {
924     ThrowImproperImageHeader("unable to flush data");
925     goto err_out_free;
926   }
927
928   more = 0;
929
930   do {
931     err = de265_decode(ctx->h265Ctx, &more);
932     if (err != DE265_OK) {
933       ThrowImproperImageHeader("unable to decode data");
934       goto err_out_free;
935     }
936
937     while (1) {
938       de265_error warning = de265_get_warning(ctx->h265Ctx);
939       if (warning==DE265_OK) {
940         break;
941       }
942
943       ThrowBinaryException(CoderWarning,(const char *)NULL,
944         de265_get_error_text(warning));
945     }
946
947     const struct de265_image* img = de265_get_next_picture(ctx->h265Ctx);
948     if (img) {
949       const uint8_t *planes[3];
950       int dims[3][2];
951       int strides[3];
952
953       int c;
954       for (c = 0; c < 3; c++) {
955         planes[c] = de265_get_image_plane(img, c, &(strides[c]));
956         dims[c][0] = de265_get_image_width(img, c);
957         dims[c][1] = de265_get_image_height(img, c);
958       }
959
960
961       assert(dims[0][0] == 512);
962       assert(dims[0][1] == 512);
963       assert(dims[1][0] == 256);
964       assert(dims[1][1] == 256);
965       assert(dims[2][0] == 256);
966       assert(dims[2][1] == 256);
967
968       Image* chroma;
969
970       chroma = ctx->tmp;
971
972       int x, y;
973
974       for (y = 0; y < 256; y++) {
975         register Quantum *q;
976         register const uint8_t *p1 = planes[1] + y * strides[1];
977         register const uint8_t *p2 = planes[2] + y * strides[2];
978
979         q = QueueAuthenticPixels(chroma, 0, y, 256, 1, exception);
980         if (q == NULL) {
981           goto err_out_free;
982         }
983
984         for (x = 0; x < 256; x++) {
985           SetPixelGreen(chroma, ScaleCharToQuantum(*p1++), q);
986           SetPixelBlue(chroma, ScaleCharToQuantum(*p2++), q);
987           q+=GetPixelChannels(chroma);
988         }
989
990         if (SyncAuthenticPixels(chroma, exception) == MagickFalse) {
991           goto err_out_free;
992         }
993       }
994
995       Image* resized_chroma = ResizeImage(chroma, 512, 512, TriangleFilter, exception);
996       if (resized_chroma == NULL) {
997         goto err_out_free;
998       }
999
1000       for (y = 0; y < 512; y++) {
1001         register Quantum *q;
1002         register const Quantum *p;
1003         register const uint8_t *l = planes[0] + y * strides[0];
1004
1005         q = QueueAuthenticPixels(image, x_offset, y_offset + y, 512, 1, exception);
1006         if (q == NULL) {
1007           goto err_loop_free;
1008         }
1009
1010         p = GetVirtualPixels(resized_chroma, 0, y, 512, 1, exception);
1011         if (p == NULL) {
1012           goto err_loop_free;
1013         }
1014
1015         for (x = 0; x < 512; x++) {
1016           SetPixelRed(image, ScaleCharToQuantum(*l), q);
1017           SetPixelGreen(image, GetPixelGreen(resized_chroma, p), q);
1018           SetPixelBlue(image, GetPixelBlue(resized_chroma, p), q);
1019           l++;
1020           q+=GetPixelChannels(image);
1021           p+=GetPixelChannels(resized_chroma);
1022         }
1023
1024         if (SyncAuthenticPixels(image, exception) == MagickFalse) {
1025           goto err_loop_free;
1026         }
1027       }
1028
1029       if (resized_chroma)
1030         resized_chroma = DestroyImage(resized_chroma);
1031
1032       more = 0;
1033       de265_release_next_picture(ctx->h265Ctx);
1034       break;
1035
1036 err_loop_free:
1037       if (resized_chroma)
1038         resized_chroma = DestroyImage(resized_chroma);
1039
1040       de265_release_next_picture(ctx->h265Ctx);
1041
1042       goto err_out_free;
1043     }
1044   } while (more);
1045
1046   de265_reset(ctx->h265Ctx);
1047   buffer = (unsigned char *) RelinquishMagickMemory(buffer);
1048   return MagickTrue;
1049
1050 err_out_free:
1051   de265_reset(ctx->h265Ctx);
1052   buffer = (unsigned char *) RelinquishMagickMemory(buffer);
1053   return MagickFalse;
1054 }
1055 \f
1056 /*
1057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1058 %                                                                             %
1059 %                                                                             %
1060 %                                                                             %
1061 %   R e a d H E I C I m a g e                                                 %
1062 %                                                                             %
1063 %                                                                             %
1064 %                                                                             %
1065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1066 %
1067 %  ReadHEICImage retrieves an image via a file descriptor, decodes the image,
1068 %  and returns it.  It allocates the memory necessary for the new Image
1069 %  structure and returns a pointer to the new image.
1070 %
1071 %  The format of the ReadHEICImage method is:
1072 %
1073 %      Image *ReadHEICImage(const ImageInfo *image_info,
1074 %        ExceptionInfo *exception)
1075 %
1076 %  A description of each parameter follows:
1077 %
1078 %    o image_info: the image info.
1079 %
1080 %    o exception: return any errors or warnings in this structure.
1081 %
1082 */
1083 static Image *ReadHEICImage(const ImageInfo *image_info,
1084   ExceptionInfo *exception)
1085 {
1086   Image
1087     *image;
1088
1089   Image
1090     *cropped = NULL;
1091
1092   MagickBooleanType
1093     status;
1094
1095   RectangleInfo
1096       crop_info;
1097
1098   MagickSizeType
1099     length;
1100
1101   ssize_t
1102     count,
1103     i;
1104
1105   HEICImageContext
1106     ctx;
1107
1108   memset(&ctx, 0, sizeof(ctx));
1109
1110   /*
1111     Open image file.
1112   */
1113   assert(image_info != (const ImageInfo *) NULL);
1114   assert(image_info->signature == MagickCoreSignature);
1115   if (image_info->debug != MagickFalse)
1116     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1117       image_info->filename);
1118   assert(exception != (ExceptionInfo *) NULL);
1119   assert(exception->signature == MagickCoreSignature);
1120   image=AcquireImage(image_info,exception);
1121   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1122   if (status == MagickFalse)
1123   {
1124     image=DestroyImageList(image);
1125     return((Image *) NULL);
1126   }
1127   cropped=(Image *) NULL;
1128
1129   length=GetBlobSize(image);
1130   count = MAX_ATOMS_IN_BOX;
1131   while (length && ctx.finished == MagickFalse && count--)
1132   {
1133     if (ParseRootAtom(image, &length, &ctx, exception) == MagickFalse)
1134       goto cleanup;
1135   }
1136
1137   if (ctx.finished != MagickTrue)
1138     goto cleanup;
1139
1140   /*
1141      Initialize h265 decoder
1142   */
1143   ctx.h265Ctx = de265_new_decoder();
1144   if (ctx.h265Ctx == NULL) {
1145     ThrowImproperImageHeader("unable to initialize decode");
1146     goto cleanup;
1147   }
1148
1149   if (decodeGrid(&ctx, exception) != MagickTrue)
1150     goto cleanup;
1151
1152   count = (ctx.grid.rowsMinusOne + 1) * (ctx.grid.columnsMinusOne + 1);
1153
1154   image->columns = 512 * (ctx.grid.columnsMinusOne + 1);
1155   image->rows = 512 * (ctx.grid.rowsMinusOne + 1);
1156   image->depth=8;
1157
1158   status=SetImageExtent(image,image->columns,image->rows,exception);
1159   if (status == MagickFalse)
1160     goto cleanup;
1161
1162   if (image_info->ping == MagickFalse)
1163     {
1164       ctx.tmp = CloneImage(image, 256, 256, MagickTrue, exception);
1165       if (ctx.tmp == NULL) {
1166         (void) ThrowMagickException(exception,GetMagickModule(),
1167           ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1168         goto cleanup;
1169       }
1170
1171       DuplicateBlob(ctx.tmp, image);
1172
1173       for (i = 0; i < count; i++) {
1174         decodeH265Image(image, &ctx, i+1, exception);
1175       }
1176     }
1177
1178   crop_info.x = 0;
1179   crop_info.y = 0;
1180
1181   for (i = 0; i < ctx.itemInfo[ctx.grid.id].assocsCount; i++) {
1182     ssize_t
1183       assoc;
1184
1185     assoc = ctx.itemInfo[ctx.grid.id].assocs[i] & 0x7f;
1186     if (assoc > ctx.itemPropsCount) {
1187       ThrowImproperImageHeader("incorrect item property index");
1188       goto cleanup;
1189     }
1190
1191     switch (ctx.itemProps[assoc].type) {
1192       case ATOM('i', 's', 'p', 'e'):
1193         if (ctx.itemProps[assoc].size < 12) {
1194           ThrowImproperImageHeader("ispe atom is too short");
1195           goto cleanup;
1196         }
1197         crop_info.width = readInt(ctx.itemProps[assoc].data+4);
1198         crop_info.height = readInt(ctx.itemProps[assoc].data+8);
1199         break;
1200
1201       case ATOM('i', 'r', 'o', 't'):
1202         {
1203           const char *value;
1204
1205           if (ctx.itemProps[assoc].size < 1) {
1206             ThrowImproperImageHeader("irot atom is too short");
1207             goto cleanup;
1208           }
1209
1210           switch (ctx.itemProps[assoc].data[0])
1211           {
1212             case 0:
1213               image->orientation = TopLeftOrientation;
1214               value = "1";
1215               break;
1216             case 1:
1217               image->orientation = RightTopOrientation;
1218               value = "8";
1219               break;
1220             case 2:
1221               image->orientation = BottomRightOrientation;
1222               value = "3";
1223               break;
1224             case 3:
1225               image->orientation = LeftTopOrientation;
1226               value = "6";
1227               break;
1228             default:
1229               value = "1";
1230           }
1231
1232           SetImageProperty(image, "exif:Orientation", value, exception);
1233         }
1234         break;
1235     }
1236   }
1237
1238   for (i = 1; i <= ctx.idsCount; i++) {
1239     unsigned char
1240       *buffer = NULL;
1241
1242     StringInfo
1243       *profile;
1244
1245     HEICItemInfo
1246       *info = &ctx.itemInfo[i];
1247
1248     if (info->type != ATOM('E','x','i','f'))
1249       continue;
1250
1251     buffer = (unsigned char *) AcquireMagickMemory(info->size);
1252     if (buffer == NULL) {
1253       (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1254         "MemoryAllocationFailed","`%s'",image->filename);
1255       goto cleanup;
1256     }
1257
1258     SeekBlob(image, info->offset+4, SEEK_SET);
1259     count = ReadBlob(image, info->size-4, buffer);
1260     profile=BlobToStringInfo(buffer, count);
1261     SetImageProfile(image, "exif", profile, exception);
1262
1263     profile = DestroyStringInfo(profile);
1264     RelinquishMagickMemory(buffer);
1265   }
1266
1267   cropped = CropImage(image, &crop_info, exception);
1268   image = DestroyImage(image);
1269   if (cropped != NULL)
1270     {
1271       if (image_info->ping != MagickFalse)
1272         cropped->colorspace=YCbCrColorspace;
1273       else
1274         SetImageColorspace(cropped,YCbCrColorspace,exception);
1275     }
1276
1277 cleanup:
1278   if (image) {
1279     image = DestroyImage(image);
1280   }
1281   if (ctx.h265Ctx) {
1282       de265_free_decoder(ctx.h265Ctx);
1283   }
1284   if (ctx.tmp) {
1285       ctx.tmp = DestroyImage(ctx.tmp);
1286   }
1287   if (ctx.idat) {
1288       ctx.idat = (uint8_t *) RelinquishMagickMemory(ctx.idat);
1289   }
1290   if (ctx.itemInfo) {
1291       ctx.itemInfo = (HEICItemInfo *) RelinquishMagickMemory(ctx.itemInfo);
1292   }
1293   for (i = 1; i <= ctx.itemPropsCount; i++) {
1294       if (ctx.itemProps[i].data) {
1295           ctx.itemProps[i].data = (uint8_t *) RelinquishMagickMemory(ctx.itemProps[i].data);
1296       }
1297   }
1298   return cropped;
1299 }
1300 #endif
1301
1302 /*
1303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1304 %                                                                             %
1305 %                                                                             %
1306 %                                                                             %
1307 %   I s H E I C                                                               %
1308 %                                                                             %
1309 %                                                                             %
1310 %                                                                             %
1311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1312 %
1313 %  IsHEIC() returns MagickTrue if the image format type, identified by the
1314 %  magick string, is Heic.
1315 %
1316 %  The format of the IsHEIC method is:
1317 %
1318 %      MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
1319 %
1320 %  A description of each parameter follows:
1321 %
1322 %    o magick: compare image format pattern against these bytes.
1323 %
1324 %    o length: Specifies the length of the magick string.
1325 %
1326 */
1327 static MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
1328 {
1329   if (length < 12)
1330     return(MagickFalse);
1331   if (LocaleNCompare((const char *) magick+8,"heic",4) == 0)
1332     return(MagickTrue);
1333   return(MagickFalse);
1334 }
1335
1336 /*
1337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1338 %                                                                             %
1339 %                                                                             %
1340 %                                                                             %
1341 %   R e g i s t e r H E I C I m a g e                                         %
1342 %                                                                             %
1343 %                                                                             %
1344 %                                                                             %
1345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1346 %
1347 %  RegisterHEICImage() adds attributes for the HEIC image format to the list of
1348 %  supported formats.  The attributes include the image format tag, a method
1349 %  to read and/or write the format, whether the format supports the saving of
1350 %  more than one frame to the same file or blob, whether the format supports
1351 %  native in-memory I/O, and a brief description of the format.
1352 %
1353 %  The format of the RegisterHEICImage method is:
1354 %
1355 %      size_t RegisterHEICImage(void)
1356 %
1357 */
1358 ModuleExport size_t RegisterHEICImage(void)
1359 {
1360   MagickInfo
1361     *entry;
1362
1363   entry=AcquireMagickInfo("HEIC","HEIC","Apple High efficiency Image Format");
1364 #if defined(MAGICKCORE_HEIC_DELEGATE)
1365   entry->decoder=(DecodeImageHandler *) ReadHEICImage;
1366 #endif
1367   entry->magick=(IsImageFormatHandler *) IsHEIC;
1368   entry->mime_type=ConstantString("image/x-heic");
1369   entry->flags|=CoderDecoderSeekableStreamFlag;
1370   entry->flags^=CoderAdjoinFlag;
1371   (void) RegisterMagickInfo(entry);
1372   return(MagickImageCoderSignature);
1373 }
1374
1375 /*
1376 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1377 %                                                                             %
1378 %                                                                             %
1379 %                                                                             %
1380 %   U n r e g i s t e r H E I C I m a g e                                     %
1381 %                                                                             %
1382 %                                                                             %
1383 %                                                                             %
1384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1385 %
1386 %  UnregisterHEICImage() removes format registrations made by the HEIC module
1387 %  from the list of supported formats.
1388 %
1389 %  The format of the UnregisterHEICImage method is:
1390 %
1391 %      UnregisterHEICImage(void)
1392 %
1393 */
1394 ModuleExport void UnregisterHEICImage(void)
1395 {
1396   (void) UnregisterMagickInfo("HEIC");
1397 }