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