]> granicus.if.org Git - imagemagick/blob - coders/heic.c
https://github.com/ImageMagick/ImageMagick/issues/1131
[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 %                                 Dirk Farin                                  %
16 %                                 April 2018                                  %
17 %                                                                             %
18 %                         Copyright 2018 Struktur AG                          %
19 %                                                                             %
20 %                               Anton Kortunov                                %
21 %                               December 2017                                 %
22 %                                                                             %
23 %                      Copyright 2017-2018 YANDEX LLC.                        %
24 %                                                                             %
25 %  You may not use this file except in compliance with the License.  You may  %
26 %  obtain a copy of the License at                                            %
27 %                                                                             %
28 %    https://www.imagemagick.org/script/license.php                           %
29 %                                                                             %
30 %  Unless required by applicable law or agreed to in writing, software        %
31 %  distributed under the License is distributed on an "AS IS" BASIS,          %
32 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
33 %  See the License for the specific language governing permissions and        %
34 %  limitations under the License.                                             %
35 %                                                                             %
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37 %
38 %
39 */
40
41 /*
42   Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/blob.h"
47 #include "MagickCore/blob-private.h"
48 #include "MagickCore/client.h"
49 #include "MagickCore/colorspace-private.h"
50 #include "MagickCore/property.h"
51 #include "MagickCore/display.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/image.h"
55 #include "MagickCore/image-private.h"
56 #include "MagickCore/list.h"
57 #include "MagickCore/magick.h"
58 #include "MagickCore/monitor.h"
59 #include "MagickCore/monitor-private.h"
60 #include "MagickCore/montage.h"
61 #include "MagickCore/transform.h"
62 #include "MagickCore/memory_.h"
63 #include "MagickCore/memory-private.h"
64 #include "MagickCore/option.h"
65 #include "MagickCore/pixel-accessor.h"
66 #include "MagickCore/quantum-private.h"
67 #include "MagickCore/static.h"
68 #include "MagickCore/string_.h"
69 #include "MagickCore/string-private.h"
70 #include "MagickCore/module.h"
71 #include "MagickCore/utility.h"
72 #if defined(MAGICKCORE_HEIC_DELEGATE)
73 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
74 #include <heif.h>
75 #else
76 #include <libheif/heif.h>
77 #endif
78 #endif
79
80
81 #if defined(MAGICKCORE_HEIC_DELEGATE)
82
83 #if !defined(MAGICKCORE_WINDOWS_SUPPORT)
84 static MagickBooleanType
85   WriteHEICImage(const ImageInfo *,Image *,ExceptionInfo *);
86 #endif
87
88 /*
89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 %                                                                             %
91 %                                                                             %
92 %                                                                             %
93 %   R e a d H E I C I m a g e                                                 %
94 %                                                                             %
95 %                                                                             %
96 %                                                                             %
97 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98 %
99 %  ReadHEICImage retrieves an image via a file descriptor, decodes the image,
100 %  and returns it.  It allocates the memory necessary for the new Image
101 %  structure and returns a pointer to the new image.
102 %
103 %  The format of the ReadHEICImage method is:
104 %
105 %      Image *ReadHEICImage(const ImageInfo *image_info,
106 %        ExceptionInfo *exception)
107 %
108 %  A description of each parameter follows:
109 %
110 %    o image_info: the image info.
111 %
112 %    o exception: return any errors or warnings in this structure.
113 %
114 */
115 static MagickBooleanType IsHeifSuccess(struct heif_error *error,Image *image,
116   ExceptionInfo *exception)
117 {
118   if (error->code == 0)
119     return(MagickTrue);
120
121   ThrowBinaryException(CorruptImageError,error->message,image->filename);
122 }
123
124 static Image *ReadHEICImage(const ImageInfo *image_info,
125   ExceptionInfo *exception)
126 {
127   heif_item_id
128     exif_id;
129
130   Image
131     *image;
132
133   int
134     count,
135     stride_y,
136     stride_cb,
137     stride_cr;
138
139   MagickBooleanType
140     status;
141
142   MagickSizeType
143     length;
144
145   ssize_t
146     y;
147
148   struct heif_context
149     *heif_context;
150
151   struct heif_error
152     error;
153
154   struct heif_image
155     *heif_image;
156
157   struct heif_image_handle
158     *image_handle;
159
160   uint8_t
161     *p_y,
162     *p_cb,
163     *p_cr;
164
165   void
166     *file_data;
167
168   /*
169     Open image file.
170   */
171   assert(image_info != (const ImageInfo *) NULL);
172   assert(image_info->signature == MagickCoreSignature);
173   if (image_info->debug != MagickFalse)
174     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
175       image_info->filename);
176   assert(exception != (ExceptionInfo *) NULL);
177   assert(exception->signature == MagickCoreSignature);
178   image=AcquireImage(image_info,exception);
179   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
180   if (status == MagickFalse)
181     return(DestroyImageList(image));
182   length=GetBlobSize(image);
183   file_data=AcquireMagickMemory(length);
184   if (file_data == (void *) NULL)
185     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
186   if (ReadBlob(image,length,file_data) != (ssize_t) length)
187     {
188       file_data=RelinquishMagickMemory(file_data);
189       ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
190     }
191   /*
192     Decode HEIF file
193   */
194   heif_context=heif_context_alloc();
195   error=heif_context_read_from_memory(heif_context,file_data,length,NULL);
196   file_data=RelinquishMagickMemory(file_data);
197   if (IsHeifSuccess(&error,image,exception) == MagickFalse)
198     {
199       heif_context_free(heif_context);
200       return(DestroyImageList(image));
201     }
202   image_handle=(struct heif_image_handle *) NULL;
203   error=heif_context_get_primary_image_handle(heif_context,&image_handle);
204   if (IsHeifSuccess(&error,image,exception) == MagickFalse)
205     {
206       heif_context_free(heif_context);
207       return(DestroyImageList(image));
208     }
209   /*
210     Read Exif data from HEIC file
211   */
212   count=heif_image_handle_get_list_of_metadata_block_IDs(image_handle,"Exif",
213     &exif_id,1);
214   if (count > 0)
215     {
216       size_t
217         exif_size;
218
219       void
220         *exif_buffer;
221
222       exif_size=heif_image_handle_get_metadata_size(image_handle,exif_id);
223       if (exif_size > GetBlobSize(image))
224         {
225           heif_image_handle_release(image_handle);
226           heif_context_free(heif_context);
227           ThrowReaderException(CorruptImageError,
228             "InsufficientImageDataInFile");
229         }
230       exif_buffer=AcquireMagickMemory(exif_size);
231       error=heif_image_handle_get_metadata(image_handle,exif_id,exif_buffer);
232       if (error.code == 0)
233         {
234           StringInfo
235             *profile;
236
237           profile=BlobToStringInfo(exif_buffer,exif_size);
238           SetImageProfile(image,"exif",profile,exception);
239           profile=DestroyStringInfo(profile);
240       }
241       exif_buffer=RelinquishMagickMemory(exif_buffer);
242   }
243   /*
244     Set image size
245    */
246   image->depth=8;
247   image->columns=(size_t) heif_image_handle_get_width(image_handle);
248   image->rows=(size_t) heif_image_handle_get_height(image_handle);
249   if (image_info->ping != MagickFalse)
250     {
251       image->colorspace=YCbCrColorspace;
252       heif_image_handle_release(image_handle);
253       heif_context_free(heif_context);
254       return(GetFirstImageInList(image));
255     }
256   status=SetImageExtent(image,image->columns,image->rows,exception);
257   if (status == MagickFalse)
258     {
259       heif_image_handle_release(image_handle);
260       heif_context_free(heif_context);
261       return(DestroyImageList(image));
262     }
263   /*
264     Copy HEIF image into ImageMagick data structures
265   */
266   (void) SetImageColorspace(image,YCbCrColorspace,exception);
267   error=heif_decode_image(image_handle,&heif_image,heif_colorspace_YCbCr,
268     heif_chroma_420,NULL);
269   if (IsHeifSuccess(&error,image,exception) == MagickFalse)
270     {
271       heif_image_handle_release(image_handle);
272       heif_context_free(heif_context);
273       return(DestroyImageList(image));
274     }
275   p_y=heif_image_get_plane(heif_image,heif_channel_Y,&stride_y);
276   p_cb=heif_image_get_plane(heif_image,heif_channel_Cb,&stride_cb);
277   p_cr=heif_image_get_plane(heif_image,heif_channel_Cr,&stride_cr);
278   for (y=0; y < (ssize_t) image->rows; y++)
279   {
280     Quantum
281       *q;
282
283     register ssize_t
284       x;
285
286     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
287     if (q == (Quantum *) NULL)
288       break;
289     for (x=0; x < (long) image->columns; x++)
290     {
291       SetPixelRed(image,ScaleCharToQuantum(p_y[y*stride_y + x]),q);
292       SetPixelGreen(image,ScaleCharToQuantum(p_cb[(y/2)*stride_cb + x/2]),q);
293       SetPixelBlue(image,ScaleCharToQuantum(p_cr[(y/2)*stride_cr + x/2]),q);
294       q+=GetPixelChannels(image);
295     }
296     if (SyncAuthenticPixels(image,exception) == MagickFalse)
297       break;
298   }
299   heif_image_release(heif_image);
300   heif_image_handle_release(image_handle);
301   heif_context_free(heif_context);
302   return(GetFirstImageInList(image));
303 }
304 #endif
305
306 /*
307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
308 %                                                                             %
309 %                                                                             %
310 %                                                                             %
311 %   I s H E I C                                                               %
312 %                                                                             %
313 %                                                                             %
314 %                                                                             %
315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316 %
317 %  IsHEIC() returns MagickTrue if the image format type, identified by the
318 %  magick string, is Heic.
319 %
320 %  The format of the IsHEIC method is:
321 %
322 %      MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
323 %
324 %  A description of each parameter follows:
325 %
326 %    o magick: compare image format pattern against these bytes.
327 %
328 %    o length: Specifies the length of the magick string.
329 %
330 */
331 static MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
332 {
333   if (length < 12)
334     return(MagickFalse);
335   if (LocaleNCompare((const char *) magick+8,"heic",4) == 0)
336     return(MagickTrue);
337   return(MagickFalse);
338 }
339
340 /*
341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
342 %                                                                             %
343 %                                                                             %
344 %                                                                             %
345 %   R e g i s t e r H E I C I m a g e                                         %
346 %                                                                             %
347 %                                                                             %
348 %                                                                             %
349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
350 %
351 %  RegisterHEICImage() adds attributes for the HEIC image format to the list of
352 %  supported formats.  The attributes include the image format tag, a method
353 %  to read and/or write the format, whether the format supports the saving of
354 %  more than one frame to the same file or blob, whether the format supports
355 %  native in-memory I/O, and a brief description of the format.
356 %
357 %  The format of the RegisterHEICImage method is:
358 %
359 %      size_t RegisterHEICImage(void)
360 %
361 */
362 ModuleExport size_t RegisterHEICImage(void)
363 {
364   MagickInfo
365     *entry;
366
367   entry=AcquireMagickInfo("HEIC","HEIC","High Efficiency Image Format");
368 #if defined(MAGICKCORE_HEIC_DELEGATE)
369   entry->decoder=(DecodeImageHandler *) ReadHEICImage;
370 #if !defined(MAGICKCORE_WINDOWS_SUPPORT)
371   entry->encoder=(EncodeImageHandler *) WriteHEICImage;
372 #endif
373 #endif
374   entry->magick=(IsImageFormatHandler *) IsHEIC;
375   entry->mime_type=ConstantString("image/x-heic");
376   entry->flags|=CoderDecoderSeekableStreamFlag;
377   entry->flags^=CoderAdjoinFlag;
378   entry->flags^=CoderDecoderThreadSupportFlag;
379   (void) RegisterMagickInfo(entry);
380   return(MagickImageCoderSignature);
381 }
382
383 /*
384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
385 %                                                                             %
386 %                                                                             %
387 %                                                                             %
388 %   U n r e g i s t e r H E I C I m a g e                                     %
389 %                                                                             %
390 %                                                                             %
391 %                                                                             %
392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
393 %
394 %  UnregisterHEICImage() removes format registrations made by the HEIC module
395 %  from the list of supported formats.
396 %
397 %  The format of the UnregisterHEICImage method is:
398 %
399 %      UnregisterHEICImage(void)
400 %
401 */
402 ModuleExport void UnregisterHEICImage(void)
403 {
404   (void) UnregisterMagickInfo("HEIC");
405 }
406
407
408 /*
409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
410 %                                                                             %
411 %                                                                             %
412 %                                                                             %
413 %   W r i t e H E I C I m a g e                                               %
414 %                                                                             %
415 %                                                                             %
416 %                                                                             %
417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418 %
419 %  WriteHEICImage() writes an HEIF image using the libheif library.
420 %
421 %  The format of the WriteHEICImage method is:
422 %
423 %      MagickBooleanType WriteHEICImage(const ImageInfo *image_info,
424 %        Image *image)
425 %
426 %  A description of each parameter follows.
427 %
428 %    o image_info: the image info.
429 %
430 %    o image:  The image.
431 %
432 %    o exception:  return any errors or warnings in this structure.
433 %
434 */
435 #if defined(MAGICKCORE_HEIC_DELEGATE) && !defined(MAGICKCORE_WINDOWS_SUPPORT)
436 static struct heif_error heif_write_func(struct heif_context *ctx,const void* data,
437   size_t size,void* userdata)
438 {
439   Image
440     *image;
441
442   struct heif_error
443     error_ok;
444
445   (void) ctx;
446   image=(Image*) userdata;
447   (void) WriteBlob(image,size,data);
448   error_ok.code=heif_error_Ok;
449   error_ok.subcode=heif_suberror_Unspecified;
450   error_ok.message="ok";
451   return(error_ok);
452 }
453
454 static MagickBooleanType WriteHEICImage(const ImageInfo *image_info,Image *image,
455   ExceptionInfo *exception)
456 {
457   long
458     x,
459     y;
460
461   MagickBooleanType
462     status;
463
464   MagickOffsetType
465     scene;
466
467   struct heif_context
468     *heif_context;
469
470   struct heif_encoder
471     *heif_encoder;
472
473   struct heif_image
474     *heif_image;
475
476   /*
477     Open output image file.
478   */
479   assert(image_info != (const ImageInfo *) NULL);
480   assert(image_info->signature == MagickCoreSignature);
481   assert(image != (Image *) NULL);
482   assert(image->signature == MagickCoreSignature);
483   if (image->debug != MagickFalse)
484     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
485   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
486   if (status == MagickFalse)
487     return(status);
488   scene=0;
489   heif_context=heif_context_alloc();
490   heif_image=(struct heif_image*) NULL;
491   heif_encoder=(struct heif_encoder*) NULL;
492   do
493   {
494     const Quantum
495       *p;
496
497     int
498       stride_y,
499       stride_cb,
500       stride_cr;
501
502     struct heif_error
503       error;
504
505     struct heif_writer
506       writer;
507
508     uint8_t
509       *p_y,
510       *p_cb,
511       *p_cr;
512
513     /*
514       Transform colorspace to YCbCr.
515     */
516     if (image->colorspace != YCbCrColorspace)
517       status=TransformImageColorspace(image,YCbCrColorspace,exception);
518     if (status == MagickFalse)
519       break;
520     /*
521       Initialize HEIF encoder context
522     */
523     error=heif_image_create((int) image->columns,(int) image->rows,
524       heif_colorspace_YCbCr,heif_chroma_420,&heif_image);
525     status=IsHeifSuccess(&error,image,exception);
526     if (status == MagickFalse)
527       break;
528     error=heif_image_add_plane(heif_image,heif_channel_Y,(int) image->columns,
529       (int) image->rows,8);
530     status=IsHeifSuccess(&error,image,exception);
531     if (status == MagickFalse)
532       break;
533     error=heif_image_add_plane(heif_image,heif_channel_Cb,
534       ((int) image->columns+1)/2,((int) image->rows+1)/2,8);
535     status=IsHeifSuccess(&error,image,exception);
536     if (status == MagickFalse)
537       break;
538     error=heif_image_add_plane(heif_image,heif_channel_Cr,
539       ((int) image->columns+1)/2,((int) image->rows+1)/2,8);
540     status=IsHeifSuccess(&error,image,exception);
541     if (status == MagickFalse)
542       break;
543     p_y=heif_image_get_plane(heif_image,heif_channel_Y,&stride_y);
544     p_cb=heif_image_get_plane(heif_image,heif_channel_Cb,&stride_cb);
545     p_cr=heif_image_get_plane(heif_image,heif_channel_Cr,&stride_cr);
546     /*
547       Copy image to heif_image
548     */
549     for (y=0; y < (long) image->rows; y++)
550     {
551       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
552       if (p == (const Quantum *) NULL)
553         {
554           status=MagickFalse;
555           break;
556         }
557       if ((y & 1)==0)
558         {
559           for (x=0; x < (long) image->columns; x+=2)
560             {
561               p_y[y*stride_y+x]=ScaleQuantumToChar(GetPixelRed(image,p));
562               p_cb[y/2*stride_cb+x/2]=ScaleQuantumToChar(GetPixelGreen(image,
563                 p));
564               p_cr[y/2*stride_cr+x/2]=ScaleQuantumToChar(GetPixelBlue(image,
565                 p));
566               p+=GetPixelChannels(image);
567
568               if (x+1 < image->columns)
569                 {
570                   p_y[y*stride_y + x+1]=ScaleQuantumToChar(GetPixelRed(image,
571                     p));
572                   p+=GetPixelChannels(image);
573                 }
574             }
575         }
576       else
577         {
578           for (x=0; x < (long) image->columns; x++)
579           {
580             p_y[y*stride_y + x]=ScaleQuantumToChar(GetPixelRed(image,p));
581             p+=GetPixelChannels(image);
582           }
583         }
584       if (image->previous == (Image *) NULL)
585         {
586           status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
587             image->rows);
588           if (status == MagickFalse)
589             break;
590         }
591     }
592     if (status == MagickFalse)
593       break;
594     /*
595       Code and actually write the HEIC image
596     */
597     error=heif_context_get_encoder_for_format(heif_context,
598       heif_compression_HEVC,&heif_encoder);
599     status=IsHeifSuccess(&error,image,exception);
600     if (status == MagickFalse)
601       break;
602     if (image_info->quality != UndefinedCompressionQuality)
603       {
604         error=heif_encoder_set_lossy_quality(heif_encoder,
605           (int) image_info->quality);
606         status=IsHeifSuccess(&error,image,exception);
607         if (status == MagickFalse)
608           break;
609       }
610     error=heif_context_encode_image(heif_context,heif_image,heif_encoder,
611       (const struct heif_encoding_options*) NULL,
612       (struct heif_image_handle**) NULL);
613     status=IsHeifSuccess(&error,image,exception);
614     if (status == MagickFalse)
615       break;
616     writer.writer_api_version=1;
617     writer.write=heif_write_func;
618     error=heif_context_write(heif_context,&writer,image);
619     status=IsHeifSuccess(&error,image,exception);
620     if (status == MagickFalse)
621       break;
622     if (GetNextImageInList(image) == (Image *) NULL)
623       break;
624     image=SyncNextImageInList(image);
625     status=SetImageProgress(image,SaveImagesTag,scene,
626       GetImageListLength(image));
627     if (status == MagickFalse)
628       break;
629     heif_encoder_release(heif_encoder);
630     heif_encoder=(struct heif_encoder*) NULL;
631     heif_image_release(heif_image);
632     heif_image=(struct heif_image*) NULL;
633     scene++;
634   } while (image_info->adjoin != MagickFalse);
635
636   if (heif_encoder != (struct heif_encoder*) NULL)
637     heif_encoder_release(heif_encoder);
638   if (heif_image != (struct heif_image*) NULL)
639     heif_image_release(heif_image);
640   heif_context_free(heif_context);
641
642   (void) CloseBlob(image);
643   return(status);
644 }
645 #endif