2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % H H EEEEE IIIII CCCC %
10 % H H EEEEE IIIII CCCC %
13 % Read/Write Heic Image Format %
20 % Copyright 2017-2018 YANDEX LLC. %
22 % You may not use this file except in compliance with the License. You may %
23 % obtain a copy of the License at %
25 % https://www.imagemagick.org/script/license.php %
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. %
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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>
76 #if defined(MAGICKCORE_HEIC_DELEGATE)
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
84 typedef struct _HEICItemInfo
93 assocs[MAX_ASSOCS_COUNT];
105 typedef struct _HEICItemProp
117 typedef struct _HEICGrid
135 typedef struct _HEICImageContext
150 itemProps[MAX_ITEM_PROPS];
161 de265_decoder_context
168 typedef struct _DataBuffer {
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); \
185 #define ThrowAndReturn(msg) { \
186 ThrowImproperImageHeader(msg) \
187 return(MagickFalse); \
190 inline static unsigned int readInt(const unsigned char* data)
192 unsigned int val = 0;
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]);
202 inline static MagickSizeType DBChop(DataBuffer *head, DataBuffer *db, size_t size)
204 if (size > (db->size - db->pos)) {
208 head->data = db->data + db->pos;
217 inline static uint32_t DBReadUInt(DataBuffer *db)
221 if (db->size - db->pos < 4) {
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];
236 inline static uint16_t DBReadUShort(DataBuffer *db)
240 if (db->size - db->pos < 2) {
245 val=(uint16_t) db->data[db->pos+0] << 8;
246 val|=(uint16_t) db->data[db->pos+1];
253 inline static uint8_t DBReadUChar(DataBuffer *db)
257 if (db->size - db->pos < 2) {
262 val=(uint8_t) db->data[db->pos];
268 inline static size_t DBGetSize(DataBuffer *db)
270 return db->size - db->pos;
273 inline static void DBSkip(DataBuffer *db, size_t skip)
275 if (db->pos + skip > db->size)
283 static MagickBooleanType ParseAtom(Image *image, DataBuffer *db,
284 HEICImageContext *ctx, ExceptionInfo *exception);
286 static MagickBooleanType ParseFullBox(Image *image, DataBuffer *db,
287 unsigned int atom, HEICImageContext *ctx, ExceptionInfo *exception)
292 flags = DBReadUInt(db);
293 version = flags >> 24;
299 if (DBGetSize(db) < 4) {
300 ThrowAndReturn("atom is too short");
303 for (i = 0; i < MAX_ATOMS_IN_BOX && DBGetSize(db) > 0; i++) {
304 if (ParseAtom(image, db, ctx, exception) == MagickFalse)
311 static MagickBooleanType ParseBox(Image *image, DataBuffer *db,
312 unsigned int atom, HEICImageContext *ctx, ExceptionInfo *exception)
317 for (i = 0; i < MAX_ATOMS_IN_BOX && DBGetSize(db) > 0; i++) {
318 if (ParseAtom(image, db, ctx, exception) == MagickFalse)
325 static MagickBooleanType ParseHvcCAtom(HEICItemProp *prop, ExceptionInfo *exception)
331 buffer[MAX_HVCC_ATOM_SIZE];
339 memcpy(buffer, prop->data, size);
343 ThrowAndReturn("hvcC atom is too short");
346 count = buffer[pos++];
348 for (i = 0; i < count && pos < size-3; i++) {
352 naluType = buffer[pos++] & 0x3f;
354 num = buffer[pos++] << 8;
355 num += buffer[pos++];
357 for (j = 0; j < num && pos < size-2; j++) {
361 naluSize = buffer[pos++] << 8;
362 naluSize += buffer[pos++];
364 if ((pos + naluSize > size) ||
365 (p + naluSize > prop->data + prop->size)) {
366 ThrowAndReturn("hvcC atom is too short");
369 /* AnnexB NALU header */
375 memcpy(p, buffer + pos, naluSize);
381 prop->size = p - prop->data;
385 static MagickBooleanType ParseIpcoAtom(Image *image, DataBuffer *db,
386 HEICImageContext *ctx, ExceptionInfo *exception)
395 property indicies starts from 1
397 for (ctx->itemPropsCount = 1; ctx->itemPropsCount < MAX_ITEM_PROPS && DBGetSize(db) > 8; ctx->itemPropsCount++) {
401 length = DBReadUInt(db);
402 atom = DBReadUInt(db);
404 if (ctx->itemPropsCount == MAX_ITEM_PROPS) {
405 ThrowAndReturn("too many item properties");
408 prop = &(ctx->itemProps[ctx->itemPropsCount]);
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");
420 memcpy(prop->data, propDb.data, prop->size);
422 switch (prop->type) {
423 case ATOM('h', 'v', 'c', 'C'):
424 ParseHvcCAtom(prop, exception);
434 static MagickBooleanType ParseIinfAtom(Image *image, DataBuffer *db,
435 HEICImageContext *ctx, ExceptionInfo *exception)
438 version, flags, count, i;
440 if (DBGetSize(db) < 4) {
441 ThrowAndReturn("atom is too short");
444 flags = DBReadUInt(db);
445 version = flags >> 24;
449 count = DBReadUShort(db);
451 count = DBReadUInt(db);
455 item indicies starts from 1
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",
463 ctx->itemInfo = (HEICItemInfo *)AcquireMagickMemory(sizeof(HEICItemInfo)*(count+1));
464 if (ctx->itemInfo == (HEICItemInfo *) NULL)
465 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
468 memset(ctx->itemInfo, 0, sizeof(HEICItemInfo)*(count+1));
470 for (i = 0; i < count && DBGetSize(db) > 0; i++)
472 (void) ParseAtom(image, db, ctx, exception);
478 static MagickBooleanType ParseInfeAtom(Image *image, DataBuffer *db,
479 HEICImageContext *ctx, ExceptionInfo *exception)
482 version, flags, id, type;
484 if (DBGetSize(db) < 9) {
485 ThrowAndReturn("atom is too short");
488 flags = DBReadUInt(db);
489 version = flags >> 24;
493 ThrowAndReturn("unsupported infe atom version");
496 id = DBReadUShort(db);
497 DBSkip(db, 2); /* item protection index */
498 type = DBReadUInt(db);
501 item indicies starts from 1
503 if ((id > (ssize_t) ctx->idsCount) ||
504 (ctx->itemInfo == (HEICItemInfo *) NULL))
505 ThrowAndReturn("item id is incorrect");
507 ctx->itemInfo[id].type = type;
512 static MagickBooleanType ParseIpmaAtom(Image *image, DataBuffer *db,
513 HEICImageContext *ctx, ExceptionInfo *exception)
516 version, flags, count, i;
518 if (DBGetSize(db) < 9) {
519 ThrowAndReturn("atom is too short");
522 flags = DBReadUInt(db);
523 version = flags >> 24;
526 count = DBReadUInt(db);
528 for (i = 0; i < count && DBGetSize(db) > 2; i++) {
533 id = DBReadUShort(db);
539 item indicies starts from 1
541 if ((id > (ssize_t) ctx->idsCount) ||
542 (ctx->itemInfo == (HEICItemInfo *) NULL))
543 ThrowAndReturn("item id is incorrect");
545 assoc_count = DBReadUChar(db);
547 if (assoc_count >= MAX_ASSOCS_COUNT) {
548 ThrowAndReturn("too many associations");
551 for (j = 0; j < assoc_count && DBGetSize(db) > 0; j++) {
552 ctx->itemInfo[id].assocs[j] = DBReadUChar(db);
555 ctx->itemInfo[id].assocsCount = j;
561 static MagickBooleanType ParseIlocAtom(Image *image, DataBuffer *db,
562 HEICImageContext *ctx, ExceptionInfo *exception)
565 version, flags, tmp, count, i;
567 if (DBGetSize(db) < 9) {
568 ThrowAndReturn("atom is too short");
571 flags = DBReadUInt(db);
572 version = flags >> 24;
575 tmp = DBReadUChar(db);
577 ThrowAndReturn("only offset_size=4 and length_size=4 are supported");
579 tmp = DBReadUChar(db);
581 ThrowAndReturn("only base_offset_size=0 and index_size=0 are supported");
585 count = DBReadUShort(db);
587 count = DBReadUInt(db);
590 for (i = 0; i < count && DBGetSize(db) > 2; i++) {
597 id = DBReadUShort(db);
600 item indicies starts from 1
602 if ((id > (ssize_t) ctx->idsCount) ||
603 (ctx->itemInfo == (HEICItemInfo *) NULL))
604 ThrowAndReturn("item id is incorrect");
606 item = &ctx->itemInfo[id];
608 if (version == 1 || version == 2) {
609 item->dataSource = DBReadUShort(db);
616 ext_count = DBReadUShort(db);
618 if (ext_count != 1) {
619 ThrowAndReturn("only one excention per item is supported");
622 item->offset = DBReadUInt(db);
623 item->size = DBReadUInt(db);
629 static MagickBooleanType ParseAtom(Image *image, DataBuffer *db,
630 HEICImageContext *ctx, ExceptionInfo *exception)
644 if (DBGetSize(db) < 8)
646 ThrowAndReturn("atom is too short");
649 atom_size = DBReadUInt(db);
650 atom = DBReadUInt(db);
652 if (atom_size == 1) {
653 /* Only 32 bit atom size are supported */
655 atom_size = DBReadUInt(db);
658 if (atom_size - 8 > DBGetSize(db))
660 ThrowAndReturn("atom is too short");
663 if (DBChop(&atomDb, db, atom_size - 8) != MagickTrue)
665 ThrowAndReturn("unable to read atom");
672 case ATOM('i', 'r', 'e', 'f'):
673 status = ParseFullBox(image, &atomDb, atom, ctx, exception);
675 case ATOM('i', 'p', 'r', 'p'):
676 status = ParseBox(image, &atomDb, atom, ctx, exception);
678 case ATOM('i', 'i', 'n', 'f'):
679 status = ParseIinfAtom(image, &atomDb, ctx, exception);
681 case ATOM('i', 'n', 'f', 'e'):
682 status = ParseInfeAtom(image, &atomDb, ctx, exception);
684 case ATOM('i', 'p', 'c', 'o'):
685 status = ParseIpcoAtom(image, &atomDb, ctx, exception);
687 case ATOM('i', 'p', 'm', 'a'):
688 status = ParseIpmaAtom(image, &atomDb, ctx, exception);
690 case ATOM('i', 'l', 'o', 'c'):
691 status = ParseIlocAtom(image, &atomDb, ctx, exception);
693 case ATOM('i', 'd', 'a', 't'):
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",
703 memcpy(ctx->idat, atomDb.data, ctx->idatSize);
714 static MagickBooleanType ParseRootAtom(Image *image,MagickSizeType *size,
715 HEICImageContext *ctx,ExceptionInfo *exception)
727 ThrowAndReturn("atom is too short");
729 atom_size = ReadBlobMSBLong(image);
730 atom = ReadBlobMSBLong(image);
732 if (atom_size == 1) {
733 ReadBlobMSBLong(image);
734 atom_size = ReadBlobMSBLong(image);
738 if (atom_size > *size)
739 ThrowAndReturn("atom is too short");
745 case ATOM('f', 't', 'y', 'p'):
746 DiscardBlobBytes(image, atom_size-8);
748 case ATOM('m', 'e', 't', 'a'):
757 db.size = atom_size - 8;
758 db.data = (unsigned char *) AcquireMagickMemory(db.size);
760 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
763 count = ReadBlob(image, db.size, db.data);
764 if (count != db.size) {
765 RelinquishMagickMemory((void *)db.data);
766 ThrowAndReturn("unable to read data");
770 * Meta flags and version
772 /* DBSkip(&db, 4); */
773 status = ParseFullBox(image, &db, atom, ctx, exception);
774 RelinquishMagickMemory((void *)db.data);
777 case ATOM('m', 'd', 'a', 't'):
778 ctx->finished = MagickTrue;
781 DiscardBlobBytes(image, atom_size-8);
784 *size=*size-atom_size;
788 static MagickBooleanType decodeGrid(HEICImageContext *ctx,
789 ExceptionInfo *exception)
794 if (ctx->itemInfo == (HEICItemInfo *) NULL)
795 ThrowAndReturn("no atoms defined");
796 for (i = 1; i <= (ssize_t) ctx->idsCount; i++) {
798 *info = &ctx->itemInfo[i];
799 if (info->type != ATOM('g','r','i','d'))
801 if (info->dataSource != 1) {
802 ThrowAndReturn("unsupport data source type");
805 if (ctx->idatSize < 8) {
806 ThrowAndReturn("idat is too small");
809 flags = ctx->idat[1];
811 ctx->grid.rowsMinusOne = ctx->idat[2];
812 ctx->grid.columnsMinusOne = ctx->idat[3];
815 ThrowAndReturn("Only 16 bits sizes are supported");
818 ctx->grid.imageWidth = (ctx->idat[4] << 8) + ctx->idat[5];
819 ctx->grid.imageHeight = (ctx->idat[6] << 8) + ctx->idat[7];
828 static MagickBooleanType decodeH265Image(Image *image, HEICImageContext *ctx, unsigned int id, ExceptionInfo *exception)
837 count, pos, nal_unit_size;
849 de265_reset(ctx->h265Ctx);
851 x_offset = 512 * ((id-1) % (ctx->grid.columnsMinusOne + 1));
852 y_offset = 512 * ((id-1) / (ctx->grid.columnsMinusOne + 1));
854 for (i = 0; i < (ssize_t) ctx->itemInfo[id].assocsCount; i++) {
858 assoc = ctx->itemInfo[id].assocs[i] & 0x7f;
859 if (assoc > ctx->itemPropsCount) {
860 ThrowImproperImageHeader("incorrect item property index");
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");
872 pos += ctx->itemProps[assoc].size;
874 case ATOM('c', 'o', 'l', 'r'):
879 if (ctx->itemProps[assoc].size < 16)
882 profile=BlobToStringInfo(ctx->itemProps[assoc].data + 4, ctx->itemProps[assoc].size - 4);
883 (void) SetImageProfile(image, "icc", profile, exception);
884 profile=DestroyStringInfo(profile);
890 buffer = (unsigned char *) AcquireMagickMemory(ctx->itemInfo[id].size);
891 if (buffer == NULL) {
892 (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
893 "MemoryAllocationFailed","`%s'",image->filename);
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");
907 for (p = buffer; p < buffer + ctx->itemInfo[id].size; /* void */) {
908 nal_unit_size = readInt(p);
913 p += nal_unit_size + 4;
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");
922 err = de265_flush_data(ctx->h265Ctx);
923 if (err != DE265_OK) {
924 ThrowImproperImageHeader("unable to flush data");
931 err = de265_decode(ctx->h265Ctx, &more);
932 if (err != DE265_OK) {
933 ThrowImproperImageHeader("unable to decode data");
938 de265_error warning = de265_get_warning(ctx->h265Ctx);
939 if (warning==DE265_OK) {
943 ThrowBinaryException(CoderWarning,(const char *)NULL,
944 de265_get_error_text(warning));
947 const struct de265_image* img = de265_get_next_picture(ctx->h265Ctx);
949 const uint8_t *planes[3];
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);
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);
974 for (y = 0; y < 256; y++) {
976 register const uint8_t *p1 = planes[1] + y * strides[1];
977 register const uint8_t *p2 = planes[2] + y * strides[2];
979 q = QueueAuthenticPixels(chroma, 0, y, 256, 1, exception);
984 for (x = 0; x < 256; x++) {
985 SetPixelGreen(chroma, ScaleCharToQuantum(*p1++), q);
986 SetPixelBlue(chroma, ScaleCharToQuantum(*p2++), q);
987 q+=GetPixelChannels(chroma);
990 if (SyncAuthenticPixels(chroma, exception) == MagickFalse) {
995 Image* resized_chroma = ResizeImage(chroma, 512, 512, TriangleFilter, exception);
996 if (resized_chroma == NULL) {
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];
1005 q = QueueAuthenticPixels(image, x_offset, y_offset + y, 512, 1, exception);
1010 p = GetVirtualPixels(resized_chroma, 0, y, 512, 1, exception);
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);
1020 q+=GetPixelChannels(image);
1021 p+=GetPixelChannels(resized_chroma);
1024 if (SyncAuthenticPixels(image, exception) == MagickFalse) {
1030 resized_chroma = DestroyImage(resized_chroma);
1033 de265_release_next_picture(ctx->h265Ctx);
1038 resized_chroma = DestroyImage(resized_chroma);
1040 de265_release_next_picture(ctx->h265Ctx);
1046 de265_reset(ctx->h265Ctx);
1047 buffer = (unsigned char *) RelinquishMagickMemory(buffer);
1051 de265_reset(ctx->h265Ctx);
1052 buffer = (unsigned char *) RelinquishMagickMemory(buffer);
1057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1061 % R e a d H E I C I m a g e %
1065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
1071 % The format of the ReadHEICImage method is:
1073 % Image *ReadHEICImage(const ImageInfo *image_info,
1074 % ExceptionInfo *exception)
1076 % A description of each parameter follows:
1078 % o image_info: the image info.
1080 % o exception: return any errors or warnings in this structure.
1083 static Image *ReadHEICImage(const ImageInfo *image_info,
1084 ExceptionInfo *exception)
1108 memset(&ctx, 0, sizeof(ctx));
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)
1124 image=DestroyImageList(image);
1125 return((Image *) NULL);
1127 cropped=(Image *) NULL;
1129 length=GetBlobSize(image);
1130 count = MAX_ATOMS_IN_BOX;
1131 while (length && ctx.finished == MagickFalse && count--)
1133 if (ParseRootAtom(image, &length, &ctx, exception) == MagickFalse)
1137 if (ctx.finished != MagickTrue)
1141 Initialize h265 decoder
1143 ctx.h265Ctx = de265_new_decoder();
1144 if (ctx.h265Ctx == NULL) {
1145 ThrowImproperImageHeader("unable to initialize decode");
1149 if (decodeGrid(&ctx, exception) != MagickTrue)
1152 count = (ctx.grid.rowsMinusOne + 1) * (ctx.grid.columnsMinusOne + 1);
1154 image->columns = 512 * (ctx.grid.columnsMinusOne + 1);
1155 image->rows = 512 * (ctx.grid.rowsMinusOne + 1);
1158 status=SetImageExtent(image,image->columns,image->rows,exception);
1159 if (status == MagickFalse)
1162 if (image_info->ping == MagickFalse)
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);
1171 DuplicateBlob(ctx.tmp, image);
1173 for (i = 0; i < count; i++) {
1174 decodeH265Image(image, &ctx, i+1, exception);
1181 for (i = 0; i < ctx.itemInfo[ctx.grid.id].assocsCount; i++) {
1185 assoc = ctx.itemInfo[ctx.grid.id].assocs[i] & 0x7f;
1186 if (assoc > ctx.itemPropsCount) {
1187 ThrowImproperImageHeader("incorrect item property index");
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");
1197 crop_info.width = readInt(ctx.itemProps[assoc].data+4);
1198 crop_info.height = readInt(ctx.itemProps[assoc].data+8);
1201 case ATOM('i', 'r', 'o', 't'):
1205 if (ctx.itemProps[assoc].size < 1) {
1206 ThrowImproperImageHeader("irot atom is too short");
1210 switch (ctx.itemProps[assoc].data[0])
1213 image->orientation = TopLeftOrientation;
1217 image->orientation = RightTopOrientation;
1221 image->orientation = BottomRightOrientation;
1225 image->orientation = LeftTopOrientation;
1232 SetImageProperty(image, "exif:Orientation", value, exception);
1238 for (i = 1; i <= ctx.idsCount; i++) {
1246 *info = &ctx.itemInfo[i];
1248 if (info->type != ATOM('E','x','i','f'))
1251 buffer = (unsigned char *) AcquireMagickMemory(info->size);
1252 if (buffer == NULL) {
1253 (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1254 "MemoryAllocationFailed","`%s'",image->filename);
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);
1263 profile = DestroyStringInfo(profile);
1264 RelinquishMagickMemory(buffer);
1267 cropped = CropImage(image, &crop_info, exception);
1268 image = DestroyImage(image);
1269 if (cropped != NULL)
1271 if (image_info->ping != MagickFalse)
1272 cropped->colorspace=YCbCrColorspace;
1274 SetImageColorspace(cropped,YCbCrColorspace,exception);
1279 image = DestroyImage(image);
1282 de265_free_decoder(ctx.h265Ctx);
1285 ctx.tmp = DestroyImage(ctx.tmp);
1288 ctx.idat = (uint8_t *) RelinquishMagickMemory(ctx.idat);
1291 ctx.itemInfo = (HEICItemInfo *) RelinquishMagickMemory(ctx.itemInfo);
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);
1303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1313 % IsHEIC() returns MagickTrue if the image format type, identified by the
1314 % magick string, is Heic.
1316 % The format of the IsHEIC method is:
1318 % MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
1320 % A description of each parameter follows:
1322 % o magick: compare image format pattern against these bytes.
1324 % o length: Specifies the length of the magick string.
1327 static MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
1330 return(MagickFalse);
1331 if (LocaleNCompare((const char *) magick+8,"heic",4) == 0)
1333 return(MagickFalse);
1337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1341 % R e g i s t e r H E I C I m a g e %
1345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
1353 % The format of the RegisterHEICImage method is:
1355 % size_t RegisterHEICImage(void)
1358 ModuleExport size_t RegisterHEICImage(void)
1363 entry=AcquireMagickInfo("HEIC","HEIC","Apple High efficiency Image Format");
1364 #if defined(MAGICKCORE_HEIC_DELEGATE)
1365 entry->decoder=(DecodeImageHandler *) ReadHEICImage;
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);
1376 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1380 % U n r e g i s t e r H E I C I m a g e %
1384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1386 % UnregisterHEICImage() removes format registrations made by the HEIC module
1387 % from the list of supported formats.
1389 % The format of the UnregisterHEICImage method is:
1391 % UnregisterHEICImage(void)
1394 ModuleExport void UnregisterHEICImage(void)
1396 (void) UnregisterMagickInfo("HEIC");