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