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/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>
75 #if defined(MAGICKCORE_HEIC_DELEGATE)
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
83 typedef struct _HEICItemInfo
92 assocs[MAX_ASSOCS_COUNT];
104 typedef struct _HEICItemProp
116 typedef struct _HEICGrid
134 typedef struct _HEICImageContext
149 itemProps[MAX_ITEM_PROPS];
160 de265_decoder_context
167 typedef struct _DataBuffer {
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); \
184 #define ThrowAndReturn(msg) { \
185 ThrowImproperImageHeader(msg) \
186 return(MagickFalse); \
189 inline static unsigned int readInt(const unsigned char* data)
191 unsigned int val = 0;
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]);
201 inline static MagickSizeType DBChop(DataBuffer *head, DataBuffer *db, size_t size)
203 if (size > (db->size - db->pos)) {
207 head->data = db->data + db->pos;
216 inline static uint32_t DBReadUInt(DataBuffer *db)
220 if (db->size - db->pos < 4) {
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]);
235 inline static uint16_t DBReadUShort(DataBuffer *db)
239 if (db->size - db->pos < 2) {
244 val = (unsigned char)(db->data[db->pos+0]) << 8;
245 val += (unsigned char)(db->data[db->pos+1]);
252 inline static uint8_t DBReadUChar(DataBuffer *db)
256 if (db->size - db->pos < 2) {
261 val = (unsigned char)(db->data[db->pos]);
267 inline static size_t DBGetSize(DataBuffer *db)
269 return db->size - db->pos;
272 inline static void DBSkip(DataBuffer *db, size_t skip)
274 if (db->pos + skip > db->size)
282 static MagickBooleanType ParseAtom(Image *image, DataBuffer *db,
283 HEICImageContext *ctx, ExceptionInfo *exception);
285 static MagickBooleanType ParseFullBox(Image *image, DataBuffer *db,
286 unsigned int atom, HEICImageContext *ctx, ExceptionInfo *exception)
291 flags = DBReadUInt(db);
292 version = flags >> 24;
298 if (DBGetSize(db) < 4) {
299 ThrowAndReturn("atom is too short");
302 for (i = 0; i < MAX_ATOMS_IN_BOX && DBGetSize(db) > 0; i++) {
303 (void) ParseAtom(image, db, ctx, exception);
309 static MagickBooleanType ParseBox(Image *image, DataBuffer *db,
310 unsigned int atom, HEICImageContext *ctx, ExceptionInfo *exception)
315 for (i = 0; i < MAX_ATOMS_IN_BOX && DBGetSize(db) > 0; i++) {
316 (void) ParseAtom(image, db, ctx, exception);
322 static MagickBooleanType ParseHvcCAtom(HEICItemProp *prop, ExceptionInfo *exception)
328 buffer[MAX_HVCC_ATOM_SIZE];
336 memcpy(buffer, prop->data, size);
340 ThrowAndReturn("hvcC atom is too short");
343 count = buffer[pos++];
345 for (i = 0; i < count && pos < size-3; i++) {
349 naluType = buffer[pos++] & 0x3f;
351 num = buffer[pos++] << 8;
352 num += buffer[pos++];
354 for (j = 0; j < num && pos < size-2; j++) {
358 naluSize = buffer[pos++] << 8;
359 naluSize += buffer[pos++];
361 if ((pos + naluSize > size) ||
362 (p + naluSize > prop->data + prop->size)) {
363 ThrowAndReturn("hvcC atom is too short");
366 /* AnnexB NALU header */
372 memcpy(p, buffer + pos, naluSize);
378 prop->size = p - prop->data;
382 static MagickBooleanType ParseIpcoAtom(Image *image, DataBuffer *db,
383 HEICImageContext *ctx, ExceptionInfo *exception)
392 property indicies starts from 1
394 for (ctx->itemPropsCount = 1; ctx->itemPropsCount < MAX_ITEM_PROPS && DBGetSize(db) > 8; ctx->itemPropsCount++) {
398 length = DBReadUInt(db);
399 atom = DBReadUInt(db);
401 if (ctx->itemPropsCount == MAX_ITEM_PROPS) {
402 ThrowAndReturn("too many item properties");
405 prop = &(ctx->itemProps[ctx->itemPropsCount]);
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");
412 memcpy(prop->data, propDb.data, prop->size);
414 switch (prop->type) {
415 case ATOM('h', 'v', 'c', 'C'):
416 ParseHvcCAtom(prop, exception);
426 static MagickBooleanType ParseIinfAtom(Image *image, DataBuffer *db,
427 HEICImageContext *ctx, ExceptionInfo *exception)
430 version, flags, count, i;
432 if (DBGetSize(db) < 4) {
433 ThrowAndReturn("atom is too short");
436 flags = DBReadUInt(db);
437 version = flags >> 24;
441 count = DBReadUShort(db);
443 count = DBReadUInt(db);
447 item indicies starts from 1
449 ctx->idsCount = count;
450 ctx->itemInfo = (HEICItemInfo *)AcquireMagickMemory(sizeof(HEICItemInfo)*(count+1));
451 if (ctx->itemInfo == (HEICItemInfo *) NULL)
452 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
455 memset(ctx->itemInfo, 0, sizeof(HEICItemInfo)*(count+1));
457 for (i = 0; i < count && DBGetSize(db) > 0; i++)
459 (void) ParseAtom(image, db, ctx, exception);
465 static MagickBooleanType ParseInfeAtom(Image *image, DataBuffer *db,
466 HEICImageContext *ctx, ExceptionInfo *exception)
469 version, flags, id, type;
471 if (DBGetSize(db) < 9) {
472 ThrowAndReturn("atom is too short");
475 flags = DBReadUInt(db);
476 version = flags >> 24;
480 ThrowAndReturn("unsupported infe atom version");
483 id = DBReadUShort(db);
484 DBSkip(db, 2); /* item protection index */
485 type = DBReadUInt(db);
488 item indicies starts from 1
490 if (id > (ssize_t) ctx->idsCount) {
491 ThrowAndReturn("item id is incorrect");
494 ctx->itemInfo[id].type = type;
499 static MagickBooleanType ParseIpmaAtom(Image *image, DataBuffer *db,
500 HEICImageContext *ctx, ExceptionInfo *exception)
503 version, flags, count, i;
505 if (DBGetSize(db) < 9) {
506 ThrowAndReturn("atom is too short");
509 flags = DBReadUInt(db);
510 version = flags >> 24;
513 count = DBReadUInt(db);
515 for (i = 0; i < count && DBGetSize(db) > 2; i++) {
520 id = DBReadUShort(db);
526 item indicies starts from 1
528 if (id > (ssize_t) ctx->idsCount) {
529 ThrowAndReturn("item id is incorrect");
532 assoc_count = DBReadUChar(db);
534 if (assoc_count > MAX_ASSOCS_COUNT) {
535 ThrowAndReturn("too many associations");
538 for (j = 0; j < assoc_count && DBGetSize(db) > 0; j++) {
539 ctx->itemInfo[id].assocs[j] = DBReadUChar(db);
542 ctx->itemInfo[id].assocsCount = j;
548 static MagickBooleanType ParseIlocAtom(Image *image, DataBuffer *db,
549 HEICImageContext *ctx, ExceptionInfo *exception)
552 version, flags, tmp, count, i;
554 if (DBGetSize(db) < 9) {
555 ThrowAndReturn("atom is too short");
558 flags = DBReadUInt(db);
559 version = flags >> 24;
562 tmp = DBReadUChar(db);
564 ThrowAndReturn("only offset_size=4 and length_size=4 are supported");
566 tmp = DBReadUChar(db);
568 ThrowAndReturn("only base_offset_size=0 and index_size=0 are supported");
572 count = DBReadUShort(db);
574 count = DBReadUInt(db);
577 for (i = 0; i < count && DBGetSize(db) > 2; i++) {
584 id = DBReadUShort(db);
587 item indicies starts from 1
589 if (id > (ssize_t) ctx->idsCount) {
590 ThrowAndReturn("item id is incorrect");
593 item = &ctx->itemInfo[id];
595 if (version == 1 || version == 2) {
596 item->dataSource = DBReadUShort(db);
603 ext_count = DBReadUShort(db);
605 if (ext_count != 1) {
606 ThrowAndReturn("only one excention per item is supported");
609 item->offset = DBReadUInt(db);
610 item->size = DBReadUInt(db);
616 static MagickBooleanType ParseAtom(Image *image, DataBuffer *db,
617 HEICImageContext *ctx, ExceptionInfo *exception)
631 if (DBGetSize(db) < 8)
633 ThrowAndReturn("atom is too short");
636 atom_size = DBReadUInt(db);
637 atom = DBReadUInt(db);
639 if (atom_size == 1) {
640 /* Only 32 bit atom size are supported */
642 atom_size = DBReadUInt(db);
645 if (atom_size - 8 > DBGetSize(db))
647 ThrowAndReturn("atom is too short");
650 if (DBChop(&atomDb, db, atom_size - 8) != MagickTrue)
652 ThrowAndReturn("unable to read atom");
659 case ATOM('i', 'r', 'e', 'f'):
660 status = ParseFullBox(image, &atomDb, atom, ctx, exception);
662 case ATOM('i', 'p', 'r', 'p'):
663 status = ParseBox(image, &atomDb, atom, ctx, exception);
665 case ATOM('i', 'i', 'n', 'f'):
666 status = ParseIinfAtom(image, &atomDb, ctx, exception);
668 case ATOM('i', 'n', 'f', 'e'):
669 status = ParseInfeAtom(image, &atomDb, ctx, exception);
671 case ATOM('i', 'p', 'c', 'o'):
672 status = ParseIpcoAtom(image, &atomDb, ctx, exception);
674 case ATOM('i', 'p', 'm', 'a'):
675 status = ParseIpmaAtom(image, &atomDb, ctx, exception);
677 case ATOM('i', 'l', 'o', 'c'):
678 status = ParseIlocAtom(image, &atomDb, ctx, exception);
680 case ATOM('i', 'd', 'a', 't'):
682 ctx->idatSize = atom_size - 8;
683 ctx->idat = (uint8_t *) AcquireMagickMemory(ctx->idatSize);
684 if (ctx->idat == NULL)
685 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
688 memcpy(ctx->idat, atomDb.data, ctx->idatSize);
699 static MagickBooleanType ParseRootAtom(Image *image,MagickSizeType *size,
700 HEICImageContext *ctx,ExceptionInfo *exception)
712 ThrowAndReturn("atom is too short");
714 atom_size = ReadBlobMSBLong(image);
715 atom = ReadBlobMSBLong(image);
717 if (atom_size == 1) {
718 ReadBlobMSBLong(image);
719 atom_size = ReadBlobMSBLong(image);
723 if (atom_size > *size)
724 ThrowAndReturn("atom is too short");
730 case ATOM('f', 't', 'y', 'p'):
731 DiscardBlobBytes(image, atom_size-8);
733 case ATOM('m', 'e', 't', 'a'):
742 db.size = atom_size - 8;
743 db.data = (unsigned char *) AcquireMagickMemory(db.size);
745 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
748 count = ReadBlob(image, db.size, db.data);
749 if (count != db.size) {
750 RelinquishMagickMemory((void *)db.data);
751 ThrowAndReturn("unable to read data");
755 * Meta flags and version
757 /* DBSkip(&db, 4); */
758 status = ParseFullBox(image, &db, atom, ctx, exception);
759 RelinquishMagickMemory((void *)db.data);
762 case ATOM('m', 'd', 'a', 't'):
763 ctx->finished = MagickTrue;
766 DiscardBlobBytes(image, atom_size-8);
769 *size=*size-atom_size;
773 static MagickBooleanType decodeGrid(HEICImageContext *ctx,
774 ExceptionInfo *exception)
779 for (i = 1; i <= (ssize_t) ctx->idsCount; i++) {
781 *info = &ctx->itemInfo[i];
782 if (info->type != ATOM('g','r','i','d'))
784 if (info->dataSource != 1) {
785 ThrowAndReturn("unsupport data source type");
788 if (ctx->idatSize < 8) {
789 ThrowAndReturn("idat is too small");
792 flags = ctx->idat[1];
794 ctx->grid.rowsMinusOne = ctx->idat[2];
795 ctx->grid.columnsMinusOne = ctx->idat[3];
798 ThrowAndReturn("Only 16 bits sizes are supported");
801 ctx->grid.imageWidth = (ctx->idat[4] << 8) + ctx->idat[5];
802 ctx->grid.imageHeight = (ctx->idat[6] << 8) + ctx->idat[7];
811 static MagickBooleanType decodeH265Image(Image *image, HEICImageContext *ctx, unsigned int id, ExceptionInfo *exception)
820 count, pos, nal_unit_size;
832 de265_reset(ctx->h265Ctx);
834 x_offset = 512 * ((id-1) % (ctx->grid.columnsMinusOne + 1));
835 y_offset = 512 * ((id-1) / (ctx->grid.columnsMinusOne + 1));
837 for (i = 0; i < (ssize_t) ctx->itemInfo[id].assocsCount; i++) {
841 assoc = ctx->itemInfo[id].assocs[i] & 0x7f;
842 if (assoc > ctx->itemPropsCount) {
843 ThrowImproperImageHeader("incorrect item property index");
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");
855 pos += ctx->itemProps[assoc].size;
857 case ATOM('c', 'o', 'l', 'r'):
862 if (ctx->itemProps[assoc].size < 16)
865 profile=BlobToStringInfo(ctx->itemProps[assoc].data + 4, ctx->itemProps[assoc].size - 4);
866 (void) SetImageProfile(image, "icc", profile, exception);
867 profile=DestroyStringInfo(profile);
873 buffer = (unsigned char *) AcquireMagickMemory(ctx->itemInfo[id].size);
874 if (buffer == NULL) {
875 (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
876 "MemoryAllocationFailed","`%s'",image->filename);
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");
890 for (p = buffer; p < buffer + ctx->itemInfo[id].size; /* void */) {
891 nal_unit_size = readInt(p);
896 p += nal_unit_size + 4;
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");
905 err = de265_flush_data(ctx->h265Ctx);
906 if (err != DE265_OK) {
907 ThrowImproperImageHeader("unable to flush data");
914 err = de265_decode(ctx->h265Ctx, &more);
915 if (err != DE265_OK) {
916 ThrowImproperImageHeader("unable to decode data");
921 de265_error warning = de265_get_warning(ctx->h265Ctx);
922 if (warning==DE265_OK) {
926 ThrowBinaryException(CoderWarning,(const char *)NULL,
927 de265_get_error_text(warning));
930 const struct de265_image* img = de265_get_next_picture(ctx->h265Ctx);
932 const uint8_t *planes[3];
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);
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);
957 for (y = 0; y < 256; y++) {
959 register const uint8_t *p1 = planes[1] + y * strides[1];
960 register const uint8_t *p2 = planes[2] + y * strides[2];
962 q = QueueAuthenticPixels(chroma, 0, y, 256, 1, exception);
967 for (x = 0; x < 256; x++) {
968 SetPixelGreen(chroma, ScaleCharToQuantum(*p1++), q);
969 SetPixelBlue(chroma, ScaleCharToQuantum(*p2++), q);
970 q+=GetPixelChannels(chroma);
973 if (SyncAuthenticPixels(chroma, exception) == MagickFalse) {
978 Image* resized_chroma = ResizeImage(chroma, 512, 512, TriangleFilter, exception);
979 if (resized_chroma == NULL) {
983 for (y = 0; y < 512; y++) {
985 register const Quantum *p;
986 register const uint8_t *l = planes[0] + y * strides[0];
988 q = QueueAuthenticPixels(image, x_offset, y_offset + y, 512, 1, exception);
993 p = GetVirtualPixels(resized_chroma, 0, y, 512, 1, exception);
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);
1003 q+=GetPixelChannels(image);
1004 p+=GetPixelChannels(resized_chroma);
1007 if (SyncAuthenticPixels(image, exception) == MagickFalse) {
1013 resized_chroma = DestroyImage(resized_chroma);
1016 de265_release_next_picture(ctx->h265Ctx);
1021 resized_chroma = DestroyImage(resized_chroma);
1023 de265_release_next_picture(ctx->h265Ctx);
1029 de265_reset(ctx->h265Ctx);
1030 buffer = (unsigned char *) RelinquishMagickMemory(buffer);
1034 de265_reset(ctx->h265Ctx);
1035 buffer = (unsigned char *) RelinquishMagickMemory(buffer);
1040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1044 % R e a d H E I C I m a g e %
1048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
1054 % The format of the ReadHEICImage method is:
1056 % Image *ReadHEICImage(const ImageInfo *image_info,
1057 % ExceptionInfo *exception)
1059 % A description of each parameter follows:
1061 % o image_info: the image info.
1063 % o exception: return any errors or warnings in this structure.
1066 static Image *ReadHEICImage(const ImageInfo *image_info,
1067 ExceptionInfo *exception)
1091 memset(&ctx, 0, sizeof(ctx));
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)
1107 image=DestroyImageList(image);
1108 return((Image *) NULL);
1110 cropped=(Image *) NULL;
1112 length=GetBlobSize(image);
1113 count = MAX_ATOMS_IN_BOX;
1114 while (length && ctx.finished == MagickFalse && count--)
1116 if (ParseRootAtom(image, &length, &ctx, exception) == MagickFalse)
1120 if (ctx.finished != MagickTrue)
1124 Initialize h265 decoder
1126 ctx.h265Ctx = de265_new_decoder();
1127 if (ctx.h265Ctx == NULL) {
1128 ThrowImproperImageHeader("unable to initialize decode");
1132 if (decodeGrid(&ctx, exception) != MagickTrue)
1135 count = (ctx.grid.rowsMinusOne + 1) * (ctx.grid.columnsMinusOne + 1);
1137 image->columns = 512 * (ctx.grid.columnsMinusOne + 1);
1138 image->rows = 512 * (ctx.grid.rowsMinusOne + 1);
1141 status=SetImageExtent(image,image->columns,image->rows,exception);
1142 if (status == MagickFalse)
1145 if (image_info->ping == MagickFalse)
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);
1154 DuplicateBlob(ctx.tmp, image);
1156 for (i = 0; i < count; i++) {
1157 decodeH265Image(image, &ctx, i+1, exception);
1164 for (i = 0; i < ctx.itemInfo[ctx.grid.id].assocsCount; i++) {
1168 assoc = ctx.itemInfo[ctx.grid.id].assocs[i] & 0x7f;
1169 if (assoc > ctx.itemPropsCount) {
1170 ThrowImproperImageHeader("incorrect item property index");
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");
1180 crop_info.width = readInt(ctx.itemProps[assoc].data+4);
1181 crop_info.height = readInt(ctx.itemProps[assoc].data+8);
1184 case ATOM('i', 'r', 'o', 't'):
1188 if (ctx.itemProps[assoc].size < 1) {
1189 ThrowImproperImageHeader("irot atom is too short");
1193 switch (ctx.itemProps[assoc].data[0])
1196 image->orientation = TopLeftOrientation;
1200 image->orientation = RightTopOrientation;
1204 image->orientation = BottomRightOrientation;
1208 image->orientation = LeftTopOrientation;
1215 SetImageProperty(image, "exif:Orientation", value, exception);
1221 for (i = 1; i <= ctx.idsCount; i++) {
1229 *info = &ctx.itemInfo[i];
1231 if (info->type != ATOM('E','x','i','f'))
1234 buffer = (unsigned char *) AcquireMagickMemory(info->size);
1235 if (buffer == NULL) {
1236 (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1237 "MemoryAllocationFailed","`%s'",image->filename);
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);
1246 profile = DestroyStringInfo(profile);
1247 RelinquishMagickMemory(buffer);
1250 cropped = CropImage(image, &crop_info, exception);
1251 image = DestroyImage(image);
1252 if (cropped != NULL)
1254 if (image_info->ping != MagickFalse)
1255 cropped->colorspace=YCbCrColorspace;
1257 SetImageColorspace(cropped,YCbCrColorspace,exception);
1262 image = DestroyImage(image);
1265 de265_free_decoder(ctx.h265Ctx);
1268 ctx.tmp = DestroyImage(ctx.tmp);
1271 ctx.idat = (uint8_t *) RelinquishMagickMemory(ctx.idat);
1274 ctx.itemInfo = (HEICItemInfo *) RelinquishMagickMemory(ctx.itemInfo);
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);
1286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1296 % IsHEIC() returns MagickTrue if the image format type, identified by the
1297 % magick string, is Heic.
1299 % The format of the IsHEIC method is:
1301 % MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
1303 % A description of each parameter follows:
1305 % o magick: compare image format pattern against these bytes.
1307 % o length: Specifies the length of the magick string.
1310 static MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
1313 return(MagickFalse);
1314 if (LocaleNCompare((const char *) magick+8,"heic",4) == 0)
1316 return(MagickFalse);
1320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1324 % R e g i s t e r H E I C I m a g e %
1328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
1336 % The format of the RegisterHEICImage method is:
1338 % size_t RegisterHEICImage(void)
1341 ModuleExport size_t RegisterHEICImage(void)
1346 entry=AcquireMagickInfo("HEIC","HEIC","Apple High efficiency Image Format");
1347 #if defined(MAGICKCORE_HEIC_DELEGATE)
1348 entry->decoder=(DecodeImageHandler *) ReadHEICImage;
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);
1359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1363 % U n r e g i s t e r H E I C I m a g e %
1367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1369 % UnregisterHEICImage() removes format registrations made by the HEIC module
1370 % from the list of supported formats.
1372 % The format of the UnregisterHEICImage method is:
1374 % UnregisterHEICImage(void)
1377 ModuleExport void UnregisterHEICImage(void)
1379 (void) UnregisterMagickInfo("HEIC");