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