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