]> granicus.if.org Git - imagemagick/blob - coders/webp.c
https://github.com/ImageMagick/ImageMagick/issues/639
[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/quantum-private.h"
61 #include "MagickCore/static.h"
62 #include "MagickCore/string_.h"
63 #include "MagickCore/string-private.h"
64 #include "MagickCore/module.h"
65 #include "MagickCore/utility.h"
66 #include "MagickCore/xwindow.h"
67 #include "MagickCore/xwindow-private.h"
68 #if defined(MAGICKCORE_WEBP_DELEGATE)
69 #include <webp/decode.h>
70 #include <webp/encode.h>
71 #endif
72 \f
73 /*
74   Forward declarations.
75 */
76 #if defined(MAGICKCORE_WEBP_DELEGATE)
77 static MagickBooleanType
78   WriteWEBPImage(const ImageInfo *,Image *,ExceptionInfo *);
79 #endif
80 \f
81 /*
82 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83 %                                                                             %
84 %                                                                             %
85 %                                                                             %
86 %   I s W E B P                                                               %
87 %                                                                             %
88 %                                                                             %
89 %                                                                             %
90 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 %
92 %  IsWEBP() returns MagickTrue if the image format type, identified by the
93 %  magick string, is WebP.
94 %
95 %  The format of the IsWEBP method is:
96 %
97 %      MagickBooleanType IsWEBP(const unsigned char *magick,const size_t length)
98 %
99 %  A description of each parameter follows:
100 %
101 %    o magick: compare image format pattern against these bytes.
102 %
103 %    o length: Specifies the length of the magick string.
104 %
105 */
106 static MagickBooleanType IsWEBP(const unsigned char *magick,const size_t length)
107 {
108   if (length < 12)
109     return(MagickFalse);
110   if (LocaleNCompare((const char *) magick+8,"WEBP",4) == 0)
111     return(MagickTrue);
112   return(MagickFalse);
113 }
114 \f
115 #if defined(MAGICKCORE_WEBP_DELEGATE)
116 /*
117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
118 %                                                                             %
119 %                                                                             %
120 %                                                                             %
121 %   R e a d W E B P I m a g e                                                 %
122 %                                                                             %
123 %                                                                             %
124 %                                                                             %
125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126 %
127 %  ReadWEBPImage() reads an image in the WebP image format.
128 %
129 %  The format of the ReadWEBPImage method is:
130 %
131 %      Image *ReadWEBPImage(const ImageInfo *image_info,
132 %        ExceptionInfo *exception)
133 %
134 %  A description of each parameter follows:
135 %
136 %    o image_info: the image info.
137 %
138 %    o exception: return any errors or warnings in this structure.
139 %
140 */
141
142 static inline uint32_t ReadWebPLSBWord(
143   const unsigned char *magick_restrict data)
144 {
145   register const unsigned char
146     *p;
147
148   register uint32_t
149     value;
150
151   p=data;
152   value=(uint32_t) (*p++);
153   value|=((uint32_t) (*p++)) << 8;
154   value|=((uint32_t) (*p++)) << 16;
155   value|=((uint32_t) (*p++)) << 24;
156   return(value);
157 }
158
159 static MagickBooleanType IsWEBPImageLossless(const unsigned char *stream,
160   const size_t length)
161 {
162 #define VP8_CHUNK_INDEX  15
163 #define LOSSLESS_FLAG  'L'
164 #define EXTENDED_HEADER  'X'
165 #define VP8_CHUNK_HEADER  "VP8"
166 #define VP8_CHUNK_HEADER_SIZE  3
167 #define RIFF_HEADER_SIZE  12
168 #define VP8X_CHUNK_SIZE  10
169 #define TAG_SIZE  4
170 #define CHUNK_SIZE_BYTES  4
171 #define CHUNK_HEADER_SIZE  8
172 #define MAX_CHUNK_PAYLOAD  (~0U-CHUNK_HEADER_SIZE-1)
173
174   ssize_t
175     offset;
176
177   /*
178     Read simple header.
179   */
180   if (stream[VP8_CHUNK_INDEX] != EXTENDED_HEADER)
181     return(stream[VP8_CHUNK_INDEX] == LOSSLESS_FLAG ? MagickTrue : MagickFalse);
182   /*
183     Read extended header.
184   */
185   offset=RIFF_HEADER_SIZE+TAG_SIZE+CHUNK_SIZE_BYTES+VP8X_CHUNK_SIZE;
186   while (offset <= (ssize_t) length)
187   {
188     uint32_t
189       chunk_size,
190       chunk_size_pad;
191
192     chunk_size=ReadWebPLSBWord(stream+offset+TAG_SIZE);
193     if (chunk_size > MAX_CHUNK_PAYLOAD)
194       break;
195     chunk_size_pad=(CHUNK_HEADER_SIZE+chunk_size+1) & ~1;
196     if (memcmp(stream+offset,VP8_CHUNK_HEADER,VP8_CHUNK_HEADER_SIZE) == 0)
197       return(*(stream+offset+VP8_CHUNK_HEADER_SIZE) == LOSSLESS_FLAG ?
198         MagickTrue : MagickFalse);
199     offset+=chunk_size_pad;
200   }
201   return(MagickFalse);
202 }
203
204 static Image *ReadWEBPImage(const ImageInfo *image_info,
205   ExceptionInfo *exception)
206 {
207 #define ThrowWEBPException(severity,tag) \
208 { \
209   if (stream != (unsigned char *) NULL) \
210     stream=(unsigned char*) RelinquishMagickMemory(stream); \
211   if (webp_image != (WebPDecBuffer *) NULL) \
212     WebPFreeDecBuffer(webp_image); \
213   ThrowReaderException(severity,tag); \
214 }
215
216   Image
217     *image;
218
219   int
220     webp_status;
221
222   MagickBooleanType
223     status;
224
225   register unsigned char
226     *p;
227
228   size_t
229     length;
230
231   ssize_t
232     count,
233     y;
234
235   unsigned char
236     header[12],
237     *stream;
238
239   WebPDecoderConfig
240     configure;
241
242   WebPDecBuffer
243     *magick_restrict webp_image = &configure.output;
244
245   WebPBitstreamFeatures
246     *magick_restrict features = &configure.input;
247
248   /*
249     Open image file.
250   */
251   assert(image_info != (const ImageInfo *) NULL);
252   assert(image_info->signature == MagickCoreSignature);
253   if (image_info->debug != MagickFalse)
254     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
255       image_info->filename);
256   assert(exception != (ExceptionInfo *) NULL);
257   assert(exception->signature == MagickCoreSignature);
258   image=AcquireImage(image_info,exception);
259   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
260   if (status == MagickFalse)
261     {
262       image=DestroyImageList(image);
263       return((Image *) NULL);
264     }
265   stream=(unsigned char *) NULL;
266   if (WebPInitDecoderConfig(&configure) == 0)
267     ThrowReaderException(ResourceLimitError,"UnableToDecodeImageFile");
268   webp_image->colorspace=MODE_RGBA;
269   count=ReadBlob(image,12,header);
270   if (count != 12)
271     ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
272   status=IsWEBP(header,count);
273   if (status == MagickFalse)
274     ThrowWEBPException(CorruptImageError,"CorruptImage");
275   length=(size_t) (ReadWebPLSBWord(header+4)+8);
276   if (length < 12)
277     ThrowWEBPException(CorruptImageError,"CorruptImage");
278   if (length > GetBlobSize(image))
279     ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
280   stream=(unsigned char *) AcquireQuantumMemory(length,sizeof(*stream));
281   if (stream == (unsigned char *) NULL)
282     ThrowWEBPException(ResourceLimitError,"MemoryAllocationFailed");
283   (void) memcpy(stream,header,12);
284   count=ReadBlob(image,length-12,stream+12);
285   if (count != (ssize_t) (length-12))
286     ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
287   webp_status=WebPGetFeatures(stream,length,features);
288   if (webp_status == VP8_STATUS_OK)
289     {
290       image->columns=(size_t) features->width;
291       image->rows=(size_t) features->height;
292       image->depth=8;
293       image->alpha_trait=features->has_alpha != 0 ? BlendPixelTrait :
294         UndefinedPixelTrait;
295       if (IsWEBPImageLossless(stream,length) != MagickFalse)
296         image->quality=100;
297       if (image_info->ping != MagickFalse)
298         {
299           stream=(unsigned char*) RelinquishMagickMemory(stream);
300           (void) CloseBlob(image);
301           return(GetFirstImageInList(image));
302         }
303       status=SetImageExtent(image,image->columns,image->rows,exception);
304       if (status == MagickFalse)
305         {
306           stream=(unsigned char*) RelinquishMagickMemory(stream);
307           (void) CloseBlob(image);
308           return(DestroyImageList(image));
309         }
310       webp_status=WebPDecode(stream,length,&configure);
311     }
312   if (webp_status != VP8_STATUS_OK)
313     switch (webp_status)
314     {
315       case VP8_STATUS_OUT_OF_MEMORY:
316       {
317         ThrowWEBPException(ResourceLimitError,"MemoryAllocationFailed");
318         break;
319       }
320       case VP8_STATUS_INVALID_PARAM:
321       {
322         ThrowWEBPException(CorruptImageError,"invalid parameter");
323         break;
324       }
325       case VP8_STATUS_BITSTREAM_ERROR:
326       {
327         ThrowWEBPException(CorruptImageError,"CorruptImage");
328         break;
329       }
330       case VP8_STATUS_UNSUPPORTED_FEATURE:
331       {
332         ThrowWEBPException(CoderError,"DataEncodingSchemeIsNotSupported");
333         break;
334       }
335       case VP8_STATUS_SUSPENDED:
336       {
337         ThrowWEBPException(CorruptImageError,"decoder suspended");
338         break;
339       }
340       case VP8_STATUS_USER_ABORT:
341       {
342         ThrowWEBPException(CorruptImageError,"user abort");
343         break;
344       }
345       case VP8_STATUS_NOT_ENOUGH_DATA:
346       {
347         ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
348         break;
349       }
350       default:
351         ThrowWEBPException(CorruptImageError,"CorruptImage");
352     }
353   p=(unsigned char *) webp_image->u.RGBA.rgba;
354   for (y=0; y < (ssize_t) image->rows; y++)
355   {
356     register Quantum
357       *q;
358
359     register ssize_t
360       x;
361
362     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
363     if (q == (Quantum *) NULL)
364       break;
365     for (x=0; x < (ssize_t) image->columns; x++)
366     {
367       SetPixelRed(image,ScaleCharToQuantum(*p++),q);
368       SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
369       SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
370       SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
371       q+=GetPixelChannels(image);
372     }
373     if (SyncAuthenticPixels(image,exception) == MagickFalse)
374       break;
375     status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
376       image->rows);
377     if (status == MagickFalse)
378       break;
379   }
380   WebPFreeDecBuffer(webp_image);
381   stream=(unsigned char*) RelinquishMagickMemory(stream);
382   (void) CloseBlob(image);
383   return(image);
384 }
385 #endif
386 \f
387 /*
388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
389 %                                                                             %
390 %                                                                             %
391 %                                                                             %
392 %   R e g i s t e r W E B P I m a g e                                         %
393 %                                                                             %
394 %                                                                             %
395 %                                                                             %
396 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397 %
398 %  RegisterWEBPImage() adds attributes for the WebP image format to
399 %  the list of supported formats.  The attributes include the image format
400 %  tag, a method to read and/or write the format, whether the format
401 %  supports the saving of more than one frame to the same file or blob,
402 %  whether the format supports native in-memory I/O, and a brief
403 %  description of the format.
404 %
405 %  The format of the RegisterWEBPImage method is:
406 %
407 %      size_t RegisterWEBPImage(void)
408 %
409 */
410 ModuleExport size_t RegisterWEBPImage(void)
411 {
412   char
413     version[MagickPathExtent];
414
415   MagickInfo
416     *entry;
417
418   *version='\0';
419   entry=AcquireMagickInfo("WEBP","WEBP","WebP Image Format");
420 #if defined(MAGICKCORE_WEBP_DELEGATE)
421   entry->decoder=(DecodeImageHandler *) ReadWEBPImage;
422   entry->encoder=(EncodeImageHandler *) WriteWEBPImage;
423   (void) FormatLocaleString(version,MagickPathExtent,"libwebp %d.%d.%d [%04X]",
424     (WebPGetDecoderVersion() >> 16) & 0xff,
425     (WebPGetDecoderVersion() >> 8) & 0xff,
426     (WebPGetDecoderVersion() >> 0) & 0xff,WEBP_DECODER_ABI_VERSION);
427 #endif
428   entry->mime_type=ConstantString("image/webp");
429   entry->flags|=CoderDecoderSeekableStreamFlag;
430   entry->flags^=CoderAdjoinFlag;
431   entry->magick=(IsImageFormatHandler *) IsWEBP;
432   if (*version != '\0')
433     entry->version=ConstantString(version);
434   (void) RegisterMagickInfo(entry);
435   return(MagickImageCoderSignature);
436 }
437 \f
438 /*
439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
440 %                                                                             %
441 %                                                                             %
442 %                                                                             %
443 %   U n r e g i s t e r W E B P I m a g e                                     %
444 %                                                                             %
445 %                                                                             %
446 %                                                                             %
447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448 %
449 %  UnregisterWEBPImage() removes format registrations made by the WebP module
450 %  from the list of supported formats.
451 %
452 %  The format of the UnregisterWEBPImage method is:
453 %
454 %      UnregisterWEBPImage(void)
455 %
456 */
457 ModuleExport void UnregisterWEBPImage(void)
458 {
459   (void) UnregisterMagickInfo("WEBP");
460 }
461 #if defined(MAGICKCORE_WEBP_DELEGATE)
462 \f
463 /*
464 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465 %                                                                             %
466 %                                                                             %
467 %                                                                             %
468 %   W r i t e W E B P I m a g e                                               %
469 %                                                                             %
470 %                                                                             %
471 %                                                                             %
472 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
473 %
474 %  WriteWEBPImage() writes an image in the WebP image format.
475 %
476 %  The format of the WriteWEBPImage method is:
477 %
478 %      MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
479 %        Image *image)
480 %
481 %  A description of each parameter follows.
482 %
483 %    o image_info: the image info.
484 %
485 %    o image:  The image.
486 %
487 */
488
489 #if WEBP_DECODER_ABI_VERSION >= 0x0100
490 static int WebPEncodeProgress(int percent,const WebPPicture* picture)
491 {
492 #define EncodeImageTag  "Encode/Image"
493
494   Image
495     *image;
496
497   MagickBooleanType
498     status;
499
500   image=(Image *) picture->custom_ptr;
501   status=SetImageProgress(image,EncodeImageTag,percent-1,100);
502   return(status == MagickFalse ? 0 : 1);
503 }
504 #endif
505
506 static int WebPEncodeWriter(const unsigned char *stream,size_t length,
507   const WebPPicture *const picture)
508 {
509   Image
510     *image;
511
512   image=(Image *) picture->custom_ptr;
513   return(length != 0 ? (int) WriteBlob(image,length,stream) : 1);
514 }
515
516 static MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
517   Image *image,ExceptionInfo *exception)
518 {
519   const char
520     *value;
521
522   int
523     webp_status;
524
525   MagickBooleanType
526     status;
527
528   MemoryInfo
529     *pixel_info;
530
531   register uint32_t
532     *magick_restrict q;
533
534   ssize_t
535     y;
536
537   WebPConfig
538     configure;
539
540   WebPPicture
541     picture;
542
543   WebPAuxStats
544     statistics;
545
546   /*
547     Open output image file.
548   */
549   assert(image_info != (const ImageInfo *) NULL);
550   assert(image_info->signature == MagickCoreSignature);
551   assert(image != (Image *) NULL);
552   assert(image->signature == MagickCoreSignature);
553   if (image->debug != MagickFalse)
554     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
555   if ((image->columns > 16383UL) || (image->rows > 16383UL))
556     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
557   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
558   if (status == MagickFalse)
559     return(status);
560   if ((WebPPictureInit(&picture) == 0) || (WebPConfigInit(&configure) == 0))
561     ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
562   picture.writer=WebPEncodeWriter;
563   picture.custom_ptr=(void *) image;
564 #if WEBP_DECODER_ABI_VERSION >= 0x0100
565   picture.progress_hook=WebPEncodeProgress;
566 #endif
567   picture.stats=(&statistics);
568   picture.width=(int) image->columns;
569   picture.height=(int) image->rows;
570   picture.argb_stride=(int) image->columns;
571   picture.use_argb=1;
572   if (image->quality != UndefinedCompressionQuality)
573     configure.quality=(float) image->quality;
574   if (image->quality >= 100)
575     configure.lossless=1;
576   value=GetImageOption(image_info,"webp:lossless");
577   if (value != (char *) NULL)
578     configure.lossless=(int) ParseCommandOption(MagickBooleanOptions,
579       MagickFalse,value);
580   value=GetImageOption(image_info,"webp:method");
581   if (value != (char *) NULL)
582     configure.method=StringToInteger(value);
583   value=GetImageOption(image_info,"webp:image-hint");
584   if (value != (char *) NULL)
585     {
586       if (LocaleCompare(value,"default") == 0)
587         configure.image_hint=WEBP_HINT_DEFAULT;
588       if (LocaleCompare(value,"photo") == 0)
589         configure.image_hint=WEBP_HINT_PHOTO;
590       if (LocaleCompare(value,"picture") == 0)
591         configure.image_hint=WEBP_HINT_PICTURE;
592 #if WEBP_DECODER_ABI_VERSION >= 0x0200
593       if (LocaleCompare(value,"graph") == 0)
594         configure.image_hint=WEBP_HINT_GRAPH;
595 #endif
596     }
597   value=GetImageOption(image_info,"webp:target-size");
598   if (value != (char *) NULL)
599     configure.target_size=StringToInteger(value);
600   value=GetImageOption(image_info,"webp:target-psnr");
601   if (value != (char *) NULL)
602     configure.target_PSNR=(float) StringToDouble(value,(char **) NULL);
603   value=GetImageOption(image_info,"webp:segments");
604   if (value != (char *) NULL)
605     configure.segments=StringToInteger(value);
606   value=GetImageOption(image_info,"webp:sns-strength");
607   if (value != (char *) NULL)
608     configure.sns_strength=StringToInteger(value);
609   value=GetImageOption(image_info,"webp:filter-strength");
610   if (value != (char *) NULL)
611     configure.filter_strength=StringToInteger(value);
612   value=GetImageOption(image_info,"webp:filter-sharpness");
613   if (value != (char *) NULL)
614     configure.filter_sharpness=StringToInteger(value);
615   value=GetImageOption(image_info,"webp:filter-type");
616   if (value != (char *) NULL)
617     configure.filter_type=StringToInteger(value);
618   value=GetImageOption(image_info,"webp:auto-filter");
619   if (value != (char *) NULL)
620     configure.autofilter=(int) ParseCommandOption(MagickBooleanOptions,
621       MagickFalse,value);
622   value=GetImageOption(image_info,"webp:alpha-compression");
623   if (value != (char *) NULL)
624     configure.alpha_compression=StringToInteger(value);
625   value=GetImageOption(image_info,"webp:alpha-filtering");
626   if (value != (char *) NULL)
627     configure.alpha_filtering=StringToInteger(value);
628   value=GetImageOption(image_info,"webp:alpha-quality");
629   if (value != (char *) NULL)
630     configure.alpha_quality=StringToInteger(value);
631   value=GetImageOption(image_info,"webp:pass");
632   if (value != (char *) NULL)
633     configure.pass=StringToInteger(value);
634   value=GetImageOption(image_info,"webp:show-compressed");
635   if (value != (char *) NULL)
636     configure.show_compressed=StringToInteger(value);
637   value=GetImageOption(image_info,"webp:preprocessing");
638   if (value != (char *) NULL)
639     configure.preprocessing=StringToInteger(value);
640   value=GetImageOption(image_info,"webp:partitions");
641   if (value != (char *) NULL)
642     configure.partitions=StringToInteger(value);
643   value=GetImageOption(image_info,"webp:partition-limit");
644   if (value != (char *) NULL)
645     configure.partition_limit=StringToInteger(value);
646 #if WEBP_DECODER_ABI_VERSION >= 0x0201
647   value=GetImageOption(image_info,"webp:emulate-jpeg-size");
648   if (value != (char *) NULL)
649     configure.emulate_jpeg_size=(int) ParseCommandOption(MagickBooleanOptions,
650       MagickFalse,value);
651   value=GetImageOption(image_info,"webp:low-memory");
652   if (value != (char *) NULL)
653     configure.low_memory=(int) ParseCommandOption(MagickBooleanOptions,
654       MagickFalse,value);
655   value=GetImageOption(image_info,"webp:thread-level");
656   if (value != (char *) NULL)
657     configure.thread_level=StringToInteger(value);
658 #endif
659   if (WebPValidateConfig(&configure) == 0)
660     ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
661   /*
662     Allocate memory for pixels.
663   */
664   (void) TransformImageColorspace(image,sRGBColorspace,exception);
665   pixel_info=AcquireVirtualMemory(image->columns,image->rows*
666     sizeof(*picture.argb));
667   if (pixel_info == (MemoryInfo *) NULL)
668     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
669   picture.argb=(uint32_t *) GetVirtualMemoryBlob(pixel_info);
670   /*
671     Convert image to WebP raster pixels.
672   */
673   q=picture.argb;
674   for (y=0; y < (ssize_t) image->rows; y++)
675   {
676     register const Quantum
677       *magick_restrict p;
678
679     register ssize_t
680       x;
681
682     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
683     if (p == (const Quantum *) NULL)
684       break;
685     for (x=0; x < (ssize_t) image->columns; x++)
686     {
687       *q++=(uint32_t) (image->alpha_trait != UndefinedPixelTrait ?
688         ScaleQuantumToChar(GetPixelAlpha(image,p)) << 24 : 0xff000000) |
689         (ScaleQuantumToChar(GetPixelRed(image,p)) << 16) |
690         (ScaleQuantumToChar(GetPixelGreen(image,p)) << 8) |
691         (ScaleQuantumToChar(GetPixelBlue(image,p)));
692       p+=GetPixelChannels(image);
693     }
694     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
695       image->rows);
696     if (status == MagickFalse)
697       break;
698   }
699   webp_status=WebPEncode(&configure,&picture);
700   if (webp_status == 0)
701     {
702       const char
703         *message;
704
705       switch (picture.error_code)
706       {
707         case VP8_ENC_ERROR_OUT_OF_MEMORY:
708         {
709           message="out of memory";
710           break;
711         }
712         case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
713         {
714           message="bitstream out of memory";
715           break;
716         }
717         case VP8_ENC_ERROR_NULL_PARAMETER:
718         {
719           message="NULL parameter";
720           break;
721         }
722         case VP8_ENC_ERROR_INVALID_CONFIGURATION:
723         {
724           message="invalid configuration";
725           break;
726         }
727         case VP8_ENC_ERROR_BAD_DIMENSION:
728         {
729           message="bad dimension";
730           break;
731         }
732         case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
733         {
734           message="partition 0 overflow (> 512K)";
735           break;
736         }
737         case VP8_ENC_ERROR_PARTITION_OVERFLOW:
738         {
739           message="partition overflow (> 16M)";
740           break;
741         }
742         case VP8_ENC_ERROR_BAD_WRITE:
743         {
744           message="bad write";
745           break;
746         }
747         case VP8_ENC_ERROR_FILE_TOO_BIG:
748         {
749           message="file too big (> 4GB)";
750           break;
751         }
752 #if WEBP_DECODER_ABI_VERSION >= 0x0100
753         case VP8_ENC_ERROR_USER_ABORT:
754         {
755           message="user abort";
756           break;
757         }
758 #endif
759         default:
760         {
761           message="unknown exception";
762           break;
763         }
764       }
765       (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
766         (char *) message,"`%s'",image->filename);
767     }
768   picture.argb=(uint32_t *) NULL;
769   WebPPictureFree(&picture);
770   pixel_info=RelinquishVirtualMemory(pixel_info);
771   (void) CloseBlob(image);
772   return(webp_status == 0 ? MagickFalse : MagickTrue);
773 }
774 #endif