]> granicus.if.org Git - imagemagick/blob - coders/webp.c
https://github.com/ImageMagick/ImageMagick/issues/876
[imagemagick] / coders / webp.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                         W   W  EEEEE  BBBB   PPPP                           %
7 %                         W   W  E      B   B  P   P                          %
8 %                         W W W  EEE    BBBB   PPPP                           %
9 %                         WW WW  E      B   B  P                              %
10 %                         W   W  EEEEE  BBBB   P                              %
11 %                                                                             %
12 %                                                                             %
13 %                         Read/Write WebP Image Format                        %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 March 2011                                  %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://www.imagemagick.org/script/license.php                           %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/blob-private.h"
46 #include "MagickCore/client.h"
47 #include "MagickCore/colorspace-private.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/memory_.h"
58 #include "MagickCore/option.h"
59 #include "MagickCore/pixel-accessor.h"
60 #include "MagickCore/profile.h"
61 #include "MagickCore/quantum-private.h"
62 #include "MagickCore/static.h"
63 #include "MagickCore/string_.h"
64 #include "MagickCore/string-private.h"
65 #include "MagickCore/module.h"
66 #include "MagickCore/utility.h"
67 #include "MagickCore/xwindow.h"
68 #include "MagickCore/xwindow-private.h"
69 #if defined(MAGICKCORE_WEBP_DELEGATE)
70 #include <webp/decode.h>
71 #include <webp/encode.h>
72 #if defined(MAGICKCORE_WEBPMUX_DELEGATE)
73 #include <webp/mux.h>
74 #endif
75 #endif
76 \f
77 /*
78   Forward declarations.
79 */
80 #if defined(MAGICKCORE_WEBP_DELEGATE)
81 static MagickBooleanType
82   WriteWEBPImage(const ImageInfo *,Image *,ExceptionInfo *);
83 #endif
84 \f
85 /*
86 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 %                                                                             %
88 %                                                                             %
89 %                                                                             %
90 %   I s W E B P                                                               %
91 %                                                                             %
92 %                                                                             %
93 %                                                                             %
94 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95 %
96 %  IsWEBP() returns MagickTrue if the image format type, identified by the
97 %  magick string, is WebP.
98 %
99 %  The format of the IsWEBP method is:
100 %
101 %      MagickBooleanType IsWEBP(const unsigned char *magick,const size_t length)
102 %
103 %  A description of each parameter follows:
104 %
105 %    o magick: compare image format pattern against these bytes.
106 %
107 %    o length: Specifies the length of the magick string.
108 %
109 */
110 static MagickBooleanType IsWEBP(const unsigned char *magick,const size_t length)
111 {
112   if (length < 12)
113     return(MagickFalse);
114   if (LocaleNCompare((const char *) magick+8,"WEBP",4) == 0)
115     return(MagickTrue);
116   return(MagickFalse);
117 }
118 \f
119 #if defined(MAGICKCORE_WEBP_DELEGATE)
120 /*
121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122 %                                                                             %
123 %                                                                             %
124 %                                                                             %
125 %   R e a d W E B P I m a g e                                                 %
126 %                                                                             %
127 %                                                                             %
128 %                                                                             %
129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
130 %
131 %  ReadWEBPImage() reads an image in the WebP image format.
132 %
133 %  The format of the ReadWEBPImage method is:
134 %
135 %      Image *ReadWEBPImage(const ImageInfo *image_info,
136 %        ExceptionInfo *exception)
137 %
138 %  A description of each parameter follows:
139 %
140 %    o image_info: the image info.
141 %
142 %    o exception: return any errors or warnings in this structure.
143 %
144 */
145
146 static inline uint32_t ReadWebPLSBWord(
147   const unsigned char *magick_restrict data)
148 {
149   register const unsigned char
150     *p;
151
152   register uint32_t
153     value;
154
155   p=data;
156   value=(uint32_t) (*p++);
157   value|=((uint32_t) (*p++)) << 8;
158   value|=((uint32_t) (*p++)) << 16;
159   value|=((uint32_t) (*p++)) << 24;
160   return(value);
161 }
162
163 static MagickBooleanType IsWEBPImageLossless(const unsigned char *stream,
164   const size_t length)
165 {
166 #define VP8_CHUNK_INDEX  15
167 #define LOSSLESS_FLAG  'L'
168 #define EXTENDED_HEADER  'X'
169 #define VP8_CHUNK_HEADER  "VP8"
170 #define VP8_CHUNK_HEADER_SIZE  3
171 #define RIFF_HEADER_SIZE  12
172 #define VP8X_CHUNK_SIZE  10
173 #define TAG_SIZE  4
174 #define CHUNK_SIZE_BYTES  4
175 #define CHUNK_HEADER_SIZE  8
176 #define MAX_CHUNK_PAYLOAD  (~0U-CHUNK_HEADER_SIZE-1)
177
178   ssize_t
179     offset;
180
181   /*
182     Read simple header.
183   */
184   if (stream[VP8_CHUNK_INDEX] != EXTENDED_HEADER)
185     return(stream[VP8_CHUNK_INDEX] == LOSSLESS_FLAG ? MagickTrue : MagickFalse);
186   /*
187     Read extended header.
188   */
189   offset=RIFF_HEADER_SIZE+TAG_SIZE+CHUNK_SIZE_BYTES+VP8X_CHUNK_SIZE;
190   while (offset <= (ssize_t) length)
191   {
192     uint32_t
193       chunk_size,
194       chunk_size_pad;
195
196     chunk_size=ReadWebPLSBWord(stream+offset+TAG_SIZE);
197     if (chunk_size > MAX_CHUNK_PAYLOAD)
198       break;
199     chunk_size_pad=(CHUNK_HEADER_SIZE+chunk_size+1) & ~1;
200     if (memcmp(stream+offset,VP8_CHUNK_HEADER,VP8_CHUNK_HEADER_SIZE) == 0)
201       return(*(stream+offset+VP8_CHUNK_HEADER_SIZE) == LOSSLESS_FLAG ?
202         MagickTrue : MagickFalse);
203     offset+=chunk_size_pad;
204   }
205   return(MagickFalse);
206 }
207
208 static Image *ReadWEBPImage(const ImageInfo *image_info,
209   ExceptionInfo *exception)
210 {
211 #define ThrowWEBPException(severity,tag) \
212 { \
213   if (stream != (unsigned char *) NULL) \
214     stream=(unsigned char*) RelinquishMagickMemory(stream); \
215   if (webp_image != (WebPDecBuffer *) NULL) \
216     WebPFreeDecBuffer(webp_image); \
217   ThrowReaderException(severity,tag); \
218 }
219
220   Image
221     *image;
222
223   int
224     webp_status;
225
226   MagickBooleanType
227     status;
228
229   register unsigned char
230     *p;
231
232   size_t
233     length;
234
235   ssize_t
236     count,
237     y;
238
239   unsigned char
240     header[12],
241     *stream;
242
243   WebPDecoderConfig
244     configure;
245
246   WebPDecBuffer
247     *magick_restrict webp_image = &configure.output;
248
249   WebPBitstreamFeatures
250     *magick_restrict features = &configure.input;
251
252   /*
253     Open image file.
254   */
255   assert(image_info != (const ImageInfo *) NULL);
256   assert(image_info->signature == MagickCoreSignature);
257   if (image_info->debug != MagickFalse)
258     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
259       image_info->filename);
260   assert(exception != (ExceptionInfo *) NULL);
261   assert(exception->signature == MagickCoreSignature);
262   image=AcquireImage(image_info,exception);
263   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
264   if (status == MagickFalse)
265     {
266       image=DestroyImageList(image);
267       return((Image *) NULL);
268     }
269   stream=(unsigned char *) NULL;
270   if (WebPInitDecoderConfig(&configure) == 0)
271     ThrowReaderException(ResourceLimitError,"UnableToDecodeImageFile");
272   webp_image->colorspace=MODE_RGBA;
273   count=ReadBlob(image,12,header);
274   if (count != 12)
275     ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
276   status=IsWEBP(header,count);
277   if (status == MagickFalse)
278     ThrowWEBPException(CorruptImageError,"CorruptImage");
279   length=(size_t) (ReadWebPLSBWord(header+4)+8);
280   if (length < 12)
281     ThrowWEBPException(CorruptImageError,"CorruptImage");
282   if (length > GetBlobSize(image))
283     ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
284   stream=(unsigned char *) AcquireQuantumMemory(length,sizeof(*stream));
285   if (stream == (unsigned char *) NULL)
286     ThrowWEBPException(ResourceLimitError,"MemoryAllocationFailed");
287   (void) memcpy(stream,header,12);
288   count=ReadBlob(image,length-12,stream+12);
289   if (count != (ssize_t) (length-12))
290     ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
291   webp_status=WebPGetFeatures(stream,length,features);
292   if (webp_status == VP8_STATUS_OK)
293     {
294       image->columns=(size_t) features->width;
295       image->rows=(size_t) features->height;
296       image->depth=8;
297       image->alpha_trait=features->has_alpha != 0 ? BlendPixelTrait :
298         UndefinedPixelTrait;
299       if (IsWEBPImageLossless(stream,length) != MagickFalse)
300         image->quality=100;
301       if (image_info->ping != MagickFalse)
302         {
303           stream=(unsigned char*) RelinquishMagickMemory(stream);
304           (void) CloseBlob(image);
305           return(GetFirstImageInList(image));
306         }
307       status=SetImageExtent(image,image->columns,image->rows,exception);
308       if (status == MagickFalse)
309         {
310           stream=(unsigned char*) RelinquishMagickMemory(stream);
311           (void) CloseBlob(image);
312           return(DestroyImageList(image));
313         }
314       webp_status=WebPDecode(stream,length,&configure);
315     }
316   if (webp_status != VP8_STATUS_OK)
317     switch (webp_status)
318     {
319       case VP8_STATUS_OUT_OF_MEMORY:
320       {
321         ThrowWEBPException(ResourceLimitError,"MemoryAllocationFailed");
322         break;
323       }
324       case VP8_STATUS_INVALID_PARAM:
325       {
326         ThrowWEBPException(CorruptImageError,"invalid parameter");
327         break;
328       }
329       case VP8_STATUS_BITSTREAM_ERROR:
330       {
331         ThrowWEBPException(CorruptImageError,"CorruptImage");
332         break;
333       }
334       case VP8_STATUS_UNSUPPORTED_FEATURE:
335       {
336         ThrowWEBPException(CoderError,"DataEncodingSchemeIsNotSupported");
337         break;
338       }
339       case VP8_STATUS_SUSPENDED:
340       {
341         ThrowWEBPException(CorruptImageError,"decoder suspended");
342         break;
343       }
344       case VP8_STATUS_USER_ABORT:
345       {
346         ThrowWEBPException(CorruptImageError,"user abort");
347         break;
348       }
349       case VP8_STATUS_NOT_ENOUGH_DATA:
350       {
351         ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
352         break;
353       }
354       default:
355         ThrowWEBPException(CorruptImageError,"CorruptImage");
356     }
357   p=(unsigned char *) webp_image->u.RGBA.rgba;
358   for (y=0; y < (ssize_t) image->rows; y++)
359   {
360     register Quantum
361       *q;
362
363     register ssize_t
364       x;
365
366     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
367     if (q == (Quantum *) NULL)
368       break;
369     for (x=0; x < (ssize_t) image->columns; x++)
370     {
371       SetPixelRed(image,ScaleCharToQuantum(*p++),q);
372       SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
373       SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
374       SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
375       q+=GetPixelChannels(image);
376     }
377     if (SyncAuthenticPixels(image,exception) == MagickFalse)
378       break;
379     status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
380       image->rows);
381     if (status == MagickFalse)
382       break;
383   }
384   WebPFreeDecBuffer(webp_image);
385 #if defined(MAGICKCORE_WEBPMUX_DELEGATE)
386   {
387     StringInfo
388       *profile;
389
390     uint32_t
391       webp_flags = 0;
392
393     WebPData
394      content = { stream, length },
395      chunk ={ 0 };
396
397     WebPMux
398       *mux;
399
400     /*
401       Extract any profiles.
402     */
403     mux=WebPMuxCreate(&content,0);
404     WebPMuxGetFeatures(mux,&webp_flags);
405     if (webp_flags & ICCP_FLAG)
406       {
407         WebPMuxGetChunk(mux,"ICCP",&chunk);
408         profile=BlobToStringInfo(chunk.bytes,chunk.size);
409         if (profile != (StringInfo *) NULL)
410           {
411             SetImageProfile(image,"ICC",profile,exception);
412             profile=DestroyStringInfo(profile);
413           }
414       }
415     if (webp_flags & EXIF_FLAG)
416       {
417         WebPMuxGetChunk(mux,"EXIF",&chunk);
418         profile=BlobToStringInfo(chunk.bytes,chunk.size);
419         if (profile != (StringInfo *) NULL)
420           {
421             SetImageProfile(image,"EXIF",profile,exception);
422             profile=DestroyStringInfo(profile);
423           }
424       }
425     if (webp_flags & XMP_FLAG)
426       {
427         WebPMuxGetChunk(mux,"XMP",&chunk);
428         profile=BlobToStringInfo(chunk.bytes,chunk.size);
429         if (profile != (StringInfo *) NULL)
430           {
431             SetImageProfile(image,"XMP",profile,exception);
432             profile=DestroyStringInfo(profile);
433           }
434       }
435     WebPMuxDelete(mux);
436   }
437 #endif
438   stream=(unsigned char*) RelinquishMagickMemory(stream);
439   (void) CloseBlob(image);
440   return(image);
441 }
442 #endif
443 \f
444 /*
445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
446 %                                                                             %
447 %                                                                             %
448 %                                                                             %
449 %   R e g i s t e r W E B P I m a g e                                         %
450 %                                                                             %
451 %                                                                             %
452 %                                                                             %
453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454 %
455 %  RegisterWEBPImage() adds attributes for the WebP image format to
456 %  the list of supported formats.  The attributes include the image format
457 %  tag, a method to read and/or write the format, whether the format
458 %  supports the saving of more than one frame to the same file or blob,
459 %  whether the format supports native in-memory I/O, and a brief
460 %  description of the format.
461 %
462 %  The format of the RegisterWEBPImage method is:
463 %
464 %      size_t RegisterWEBPImage(void)
465 %
466 */
467 ModuleExport size_t RegisterWEBPImage(void)
468 {
469   char
470     version[MagickPathExtent];
471
472   MagickInfo
473     *entry;
474
475   *version='\0';
476   entry=AcquireMagickInfo("WEBP","WEBP","WebP Image Format");
477 #if defined(MAGICKCORE_WEBP_DELEGATE)
478   entry->decoder=(DecodeImageHandler *) ReadWEBPImage;
479   entry->encoder=(EncodeImageHandler *) WriteWEBPImage;
480   (void) FormatLocaleString(version,MagickPathExtent,"libwebp %d.%d.%d [%04X]",
481     (WebPGetDecoderVersion() >> 16) & 0xff,
482     (WebPGetDecoderVersion() >> 8) & 0xff,
483     (WebPGetDecoderVersion() >> 0) & 0xff,WEBP_DECODER_ABI_VERSION);
484 #endif
485   entry->mime_type=ConstantString("image/webp");
486   entry->flags|=CoderDecoderSeekableStreamFlag;
487   entry->flags^=CoderAdjoinFlag;
488   entry->magick=(IsImageFormatHandler *) IsWEBP;
489   if (*version != '\0')
490     entry->version=ConstantString(version);
491   (void) RegisterMagickInfo(entry);
492   return(MagickImageCoderSignature);
493 }
494 \f
495 /*
496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497 %                                                                             %
498 %                                                                             %
499 %                                                                             %
500 %   U n r e g i s t e r W E B P I m a g e                                     %
501 %                                                                             %
502 %                                                                             %
503 %                                                                             %
504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
505 %
506 %  UnregisterWEBPImage() removes format registrations made by the WebP module
507 %  from the list of supported formats.
508 %
509 %  The format of the UnregisterWEBPImage method is:
510 %
511 %      UnregisterWEBPImage(void)
512 %
513 */
514 ModuleExport void UnregisterWEBPImage(void)
515 {
516   (void) UnregisterMagickInfo("WEBP");
517 }
518 #if defined(MAGICKCORE_WEBP_DELEGATE)
519 \f
520 /*
521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522 %                                                                             %
523 %                                                                             %
524 %                                                                             %
525 %   W r i t e W E B P I m a g e                                               %
526 %                                                                             %
527 %                                                                             %
528 %                                                                             %
529 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
530 %
531 %  WriteWEBPImage() writes an image in the WebP image format.
532 %
533 %  The format of the WriteWEBPImage method is:
534 %
535 %      MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
536 %        Image *image)
537 %
538 %  A description of each parameter follows.
539 %
540 %    o image_info: the image info.
541 %
542 %    o image:  The image.
543 %
544 */
545
546 #if WEBP_DECODER_ABI_VERSION >= 0x0100
547 static int WebPEncodeProgress(int percent,const WebPPicture* picture)
548 {
549 #define EncodeImageTag  "Encode/Image"
550
551   Image
552     *image;
553
554   MagickBooleanType
555     status;
556
557   image=(Image *) picture->custom_ptr;
558   status=SetImageProgress(image,EncodeImageTag,percent-1,100);
559   return(status == MagickFalse ? 0 : 1);
560 }
561 #endif
562
563 #if !defined(MAGICKCORE_WEBPMUX_DELEGATE)
564 static int WebPEncodeWriter(const unsigned char *stream,size_t length,
565   const WebPPicture *const picture)
566 {
567   Image
568     *image;
569
570   image=(Image *) picture->custom_ptr;
571   return(length != 0 ? (WriteBlob(image,length,stream) == length) : 1);
572 }
573 #endif
574
575 static MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
576   Image *image,ExceptionInfo *exception)
577 {
578   const char
579     *value;
580
581   int
582     webp_status;
583
584   MagickBooleanType
585     status;
586
587   MemoryInfo
588     *pixel_info;
589
590   register uint32_t
591     *magick_restrict q;
592
593   ssize_t
594     y;
595
596   WebPAuxStats
597     statistics;
598
599   WebPConfig
600     configure;
601
602 #if defined(MAGICKCORE_WEBPMUX_DELEGATE)
603   WebPMemoryWriter
604     writer_info;
605 #endif
606
607   WebPPicture
608     picture;
609
610   /*
611     Open output image file.
612   */
613   assert(image_info != (const ImageInfo *) NULL);
614   assert(image_info->signature == MagickCoreSignature);
615   assert(image != (Image *) NULL);
616   assert(image->signature == MagickCoreSignature);
617   if (image->debug != MagickFalse)
618     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
619   if ((image->columns > 16383UL) || (image->rows > 16383UL))
620     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
621   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
622   if (status == MagickFalse)
623     return(status);
624   if ((WebPPictureInit(&picture) == 0) || (WebPConfigInit(&configure) == 0))
625     ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
626 #if !defined(MAGICKCORE_WEBPMUX_DELEGATE)
627   picture.writer=WebPEncodeWriter;
628   picture.custom_ptr=(void *) image;
629 #else
630   WebPMemoryWriterInit(&writer_info);
631   picture.writer=WebPMemoryWrite;
632   picture.custom_ptr=(&writer_info);
633 #endif
634 #if WEBP_DECODER_ABI_VERSION >= 0x0100
635   picture.progress_hook=WebPEncodeProgress;
636 #endif
637   picture.stats=(&statistics);
638   picture.width=(int) image->columns;
639   picture.height=(int) image->rows;
640   picture.argb_stride=(int) image->columns;
641   picture.use_argb=1;
642   if (image->quality != UndefinedCompressionQuality)
643     configure.quality=(float) image->quality;
644   if (image->quality >= 100)
645     configure.lossless=1;
646   value=GetImageOption(image_info,"webp:lossless");
647   if (value != (char *) NULL)
648     configure.lossless=(int) ParseCommandOption(MagickBooleanOptions,
649       MagickFalse,value);
650   value=GetImageOption(image_info,"webp:method");
651   if (value != (char *) NULL)
652     configure.method=StringToInteger(value);
653   value=GetImageOption(image_info,"webp:image-hint");
654   if (value != (char *) NULL)
655     {
656       if (LocaleCompare(value,"default") == 0)
657         configure.image_hint=WEBP_HINT_DEFAULT;
658       if (LocaleCompare(value,"photo") == 0)
659         configure.image_hint=WEBP_HINT_PHOTO;
660       if (LocaleCompare(value,"picture") == 0)
661         configure.image_hint=WEBP_HINT_PICTURE;
662 #if WEBP_DECODER_ABI_VERSION >= 0x0200
663       if (LocaleCompare(value,"graph") == 0)
664         configure.image_hint=WEBP_HINT_GRAPH;
665 #endif
666     }
667   value=GetImageOption(image_info,"webp:target-size");
668   if (value != (char *) NULL)
669     configure.target_size=StringToInteger(value);
670   value=GetImageOption(image_info,"webp:target-psnr");
671   if (value != (char *) NULL)
672     configure.target_PSNR=(float) StringToDouble(value,(char **) NULL);
673   value=GetImageOption(image_info,"webp:segments");
674   if (value != (char *) NULL)
675     configure.segments=StringToInteger(value);
676   value=GetImageOption(image_info,"webp:sns-strength");
677   if (value != (char *) NULL)
678     configure.sns_strength=StringToInteger(value);
679   value=GetImageOption(image_info,"webp:filter-strength");
680   if (value != (char *) NULL)
681     configure.filter_strength=StringToInteger(value);
682   value=GetImageOption(image_info,"webp:filter-sharpness");
683   if (value != (char *) NULL)
684     configure.filter_sharpness=StringToInteger(value);
685   value=GetImageOption(image_info,"webp:filter-type");
686   if (value != (char *) NULL)
687     configure.filter_type=StringToInteger(value);
688   value=GetImageOption(image_info,"webp:auto-filter");
689   if (value != (char *) NULL)
690     configure.autofilter=(int) ParseCommandOption(MagickBooleanOptions,
691       MagickFalse,value);
692   value=GetImageOption(image_info,"webp:alpha-compression");
693   if (value != (char *) NULL)
694     configure.alpha_compression=StringToInteger(value);
695   value=GetImageOption(image_info,"webp:alpha-filtering");
696   if (value != (char *) NULL)
697     configure.alpha_filtering=StringToInteger(value);
698   value=GetImageOption(image_info,"webp:alpha-quality");
699   if (value != (char *) NULL)
700     configure.alpha_quality=StringToInteger(value);
701   value=GetImageOption(image_info,"webp:pass");
702   if (value != (char *) NULL)
703     configure.pass=StringToInteger(value);
704   value=GetImageOption(image_info,"webp:show-compressed");
705   if (value != (char *) NULL)
706     configure.show_compressed=StringToInteger(value);
707   value=GetImageOption(image_info,"webp:preprocessing");
708   if (value != (char *) NULL)
709     configure.preprocessing=StringToInteger(value);
710   value=GetImageOption(image_info,"webp:partitions");
711   if (value != (char *) NULL)
712     configure.partitions=StringToInteger(value);
713   value=GetImageOption(image_info,"webp:partition-limit");
714   if (value != (char *) NULL)
715     configure.partition_limit=StringToInteger(value);
716 #if WEBP_DECODER_ABI_VERSION >= 0x0201
717   value=GetImageOption(image_info,"webp:emulate-jpeg-size");
718   if (value != (char *) NULL)
719     configure.emulate_jpeg_size=(int) ParseCommandOption(MagickBooleanOptions,
720       MagickFalse,value);
721   value=GetImageOption(image_info,"webp:low-memory");
722   if (value != (char *) NULL)
723     configure.low_memory=(int) ParseCommandOption(MagickBooleanOptions,
724       MagickFalse,value);
725   value=GetImageOption(image_info,"webp:thread-level");
726   if (value != (char *) NULL)
727     configure.thread_level=StringToInteger(value);
728 #endif
729   if (WebPValidateConfig(&configure) == 0)
730     ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
731   /*
732     Allocate memory for pixels.
733   */
734   (void) TransformImageColorspace(image,sRGBColorspace,exception);
735   pixel_info=AcquireVirtualMemory(image->columns,image->rows*
736     sizeof(*picture.argb));
737   if (pixel_info == (MemoryInfo *) NULL)
738     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
739   picture.argb=(uint32_t *) GetVirtualMemoryBlob(pixel_info);
740   /*
741     Convert image to WebP raster pixels.
742   */
743   q=picture.argb;
744   for (y=0; y < (ssize_t) image->rows; y++)
745   {
746     register const Quantum
747       *magick_restrict p;
748
749     register ssize_t
750       x;
751
752     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
753     if (p == (const Quantum *) NULL)
754       break;
755     for (x=0; x < (ssize_t) image->columns; x++)
756     {
757       *q++=(uint32_t) (image->alpha_trait != UndefinedPixelTrait ?
758         ScaleQuantumToChar(GetPixelAlpha(image,p)) << 24 : 0xff000000) |
759         (ScaleQuantumToChar(GetPixelRed(image,p)) << 16) |
760         (ScaleQuantumToChar(GetPixelGreen(image,p)) << 8) |
761         (ScaleQuantumToChar(GetPixelBlue(image,p)));
762       p+=GetPixelChannels(image);
763     }
764     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
765       image->rows);
766     if (status == MagickFalse)
767       break;
768   }
769   webp_status=WebPEncode(&configure,&picture);
770   if (webp_status == 0)
771     {
772       const char
773         *message;
774
775       switch (picture.error_code)
776       {
777         case VP8_ENC_ERROR_OUT_OF_MEMORY:
778         {
779           message="out of memory";
780           break;
781         }
782         case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
783         {
784           message="bitstream out of memory";
785           break;
786         }
787         case VP8_ENC_ERROR_NULL_PARAMETER:
788         {
789           message="NULL parameter";
790           break;
791         }
792         case VP8_ENC_ERROR_INVALID_CONFIGURATION:
793         {
794           message="invalid configuration";
795           break;
796         }
797         case VP8_ENC_ERROR_BAD_DIMENSION:
798         {
799           message="bad dimension";
800           break;
801         }
802         case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
803         {
804           message="partition 0 overflow (> 512K)";
805           break;
806         }
807         case VP8_ENC_ERROR_PARTITION_OVERFLOW:
808         {
809           message="partition overflow (> 16M)";
810           break;
811         }
812         case VP8_ENC_ERROR_BAD_WRITE:
813         {
814           message="bad write";
815           break;
816         }
817         case VP8_ENC_ERROR_FILE_TOO_BIG:
818         {
819           message="file too big (> 4GB)";
820           break;
821         }
822 #if WEBP_DECODER_ABI_VERSION >= 0x0100
823         case VP8_ENC_ERROR_USER_ABORT:
824         {
825           message="user abort";
826           break;
827         }
828 #endif
829         default:
830         {
831           message="unknown exception";
832           break;
833         }
834       }
835       (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
836         (char *) message,"`%s'",image->filename);
837     }
838 #if defined(MAGICKCORE_WEBPMUX_DELEGATE)
839   {
840     const StringInfo
841       *profile;
842
843     WebPData
844       chunk,
845       image_chunk = { writer_info.mem, writer_info.size };
846
847     WebPMux
848       *mux;
849
850     WebPMuxError
851       mux_error;
852
853     /*
854       Set image profiles (if any).
855     */
856     mux_error=WEBP_MUX_OK;
857     chunk.size=0;
858     mux=WebPMuxNew();
859     profile=GetImageProfile(image,"ICC");    
860     if ((profile != (StringInfo *) NULL) && (mux_error == WEBP_MUX_OK))
861       {
862         chunk.bytes=GetStringInfoDatum(profile);
863         chunk.size=GetStringInfoLength(profile);
864         mux_error=WebPMuxSetChunk(mux,"ICCP",&chunk,0);
865       }
866     profile=GetImageProfile(image,"EXIF");    
867     if ((profile != (StringInfo *) NULL) && (mux_error == WEBP_MUX_OK))
868       {
869         chunk.bytes=GetStringInfoDatum(profile);
870         chunk.size=GetStringInfoLength(profile);
871         mux_error=WebPMuxSetChunk(mux,"EXIF",&chunk,0);
872       }
873     profile=GetImageProfile(image,"XMP");    
874     if ((profile != (StringInfo *) NULL) && (mux_error == WEBP_MUX_OK))
875       {
876         chunk.bytes=GetStringInfoDatum(profile);
877         chunk.size=GetStringInfoLength(profile);
878         mux_error=WebPMuxSetChunk(mux,"XMP",&chunk,0);
879       }
880     if (mux_error != WEBP_MUX_OK)
881       (void) ThrowMagickException(exception,GetMagickModule(),
882         ResourceLimitError,"UnableToEncodeImageFile","`%s'",image->filename);
883     if (chunk.size != 0)
884       {
885         WebPData
886           picture_profiles = { writer_info.mem, writer_info.size };
887
888         /*
889           Replace original container with image profile (if any).
890         */
891         WebPMuxSetImage(mux,&image_chunk,1);
892         mux_error=WebPMuxAssemble(mux,&picture_profiles);
893         WebPMemoryWriterClear(&writer_info);
894         writer_info.size=picture_profiles.size;
895         writer_info.mem=(unsigned char *) picture_profiles.bytes;
896       }
897     WebPMuxDelete(mux);
898   }
899   (void) WriteBlob(image,writer_info.size,writer_info.mem);
900 #endif
901   picture.argb=(uint32_t *) NULL;
902   WebPPictureFree(&picture);
903 #if defined(MAGICKCORE_WEBPMUX_DELEGATE)
904   WebPMemoryWriterClear(&writer_info);
905 #endif
906   pixel_info=RelinquishVirtualMemory(pixel_info);
907   (void) CloseBlob(image);
908   return(webp_status == 0 ? MagickFalse : MagickTrue);
909 }
910 #endif