]> granicus.if.org Git - imagemagick/blobdiff - coders/webp.c
https://github.com/ImageMagick/ImageMagick/issues/463
[imagemagick] / coders / webp.c
index 49f81a12ba583bbad81ba3f4c8c22855f549944e..10966ac6c28630939c26ebe61317ad1d398e11df 100644 (file)
 %                                 March 2011                                  %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
 %  obtain a copy of the License at                                            %
 %                                                                             %
-%    http://www.imagemagick.org/script/license.php                            %
+%    https://www.imagemagick.org/script/license.php                           %
 %                                                                             %
 %  Unless required by applicable law or agreed to in writing, software        %
 %  distributed under the License is distributed on an "AS IS" BASIS,          %
   Include declarations.
 */
 #include "MagickCore/studio.h"
+#include "MagickCore/artifact.h"
 #include "MagickCore/blob.h"
 #include "MagickCore/blob-private.h"
 #include "MagickCore/client.h"
+#include "MagickCore/colorspace-private.h"
 #include "MagickCore/display.h"
 #include "MagickCore/exception.h"
 #include "MagickCore/exception-private.h"
@@ -58,6 +60,7 @@
 #include "MagickCore/quantum-private.h"
 #include "MagickCore/static.h"
 #include "MagickCore/string_.h"
+#include "MagickCore/string-private.h"
 #include "MagickCore/module.h"
 #include "MagickCore/utility.h"
 #include "MagickCore/xwindow.h"
 */
 #if defined(MAGICKCORE_WEBP_DELEGATE)
 static MagickBooleanType
-  WriteWEBPImage(const ImageInfo *,Image *);
+  WriteWEBPImage(const ImageInfo *,Image *,ExceptionInfo *);
 #endif
 \f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%   I s W E B P                                                               %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  IsWEBP() returns MagickTrue if the image format type, identified by the
+%  magick string, is WebP.
+%
+%  The format of the IsWEBP method is:
+%
+%      MagickBooleanType IsWEBP(const unsigned char *magick,const size_t length)
+%
+%  A description of each parameter follows:
+%
+%    o magick: compare image format pattern against these bytes.
+%
+%    o length: Specifies the length of the magick string.
+%
+*/
+static MagickBooleanType IsWEBP(const unsigned char *magick,const size_t length)
+{
+  if (length < 12)
+    return(MagickFalse);
+  if (LocaleNCompare((const char *) magick+8,"WEBP",4) == 0)
+    return(MagickTrue);
+  return(MagickFalse);
+}
+\f
 #if defined(MAGICKCORE_WEBP_DELEGATE)
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -101,25 +138,81 @@ static MagickBooleanType
 %    o exception: return any errors or warnings in this structure.
 %
 */
+
+static inline uint32_t ReadWebPLSBWord(
+  const unsigned char *magick_restrict data)
+{
+  register const unsigned char
+    *p;
+
+  register uint32_t
+    value;
+
+  p=data;
+  value=(uint32_t) (*p++);
+  value|=((uint32_t) (*p++)) << 8;
+  value|=((uint32_t) (*p++)) << 16;
+  value|=((uint32_t) (*p++)) << 24;
+  return(value);
+}
+
+static MagickBooleanType IsWEBPImageLossless(const unsigned char *stream,
+  const size_t length)
+{
+#define VP8_CHUNK_INDEX  15
+#define LOSSLESS_FLAG  'L'
+#define EXTENDED_HEADER  'X'
+#define VP8_CHUNK_HEADER  "VP8"
+#define VP8_CHUNK_HEADER_SIZE  3
+#define RIFF_HEADER_SIZE  12
+#define VP8X_CHUNK_SIZE  10
+#define TAG_SIZE  4
+#define CHUNK_SIZE_BYTES  4
+#define CHUNK_HEADER_SIZE  8
+#define MAX_CHUNK_PAYLOAD  (~0U-CHUNK_HEADER_SIZE-1)
+
+  ssize_t
+    offset;
+
+  /*
+    Read simple header.
+  */
+  if (stream[VP8_CHUNK_INDEX] != EXTENDED_HEADER)
+    return(stream[VP8_CHUNK_INDEX] == LOSSLESS_FLAG ? MagickTrue : MagickFalse);
+  /*
+    Read extended header.
+  */
+  offset=RIFF_HEADER_SIZE+TAG_SIZE+CHUNK_SIZE_BYTES+VP8X_CHUNK_SIZE;
+  while (offset <= (ssize_t) length)
+  {
+    uint32_t
+      chunk_size,
+      chunk_size_pad;
+
+    chunk_size=ReadWebPLSBWord(stream+offset+TAG_SIZE);
+    if (chunk_size > MAX_CHUNK_PAYLOAD)
+      break;
+    chunk_size_pad=(CHUNK_HEADER_SIZE+chunk_size+1) & ~1;
+    if (memcmp(stream+offset,VP8_CHUNK_HEADER,VP8_CHUNK_HEADER_SIZE) == 0)
+      return(*(stream+offset+VP8_CHUNK_HEADER_SIZE) == LOSSLESS_FLAG ?
+        MagickTrue : MagickFalse);
+    offset+=chunk_size_pad;
+  }
+  return(MagickFalse);
+}
+
 static Image *ReadWEBPImage(const ImageInfo *image_info,
   ExceptionInfo *exception)
 {
-  int
-    height,
-    width;
-
   Image
     *image;
 
+  int
+    webp_status;
+
   MagickBooleanType
     status;
 
-  register Quantum
-    *q;
-
-  register ssize_t
-    x;
-
   register unsigned char
     *p;
 
@@ -131,41 +224,128 @@ static Image *ReadWEBPImage(const ImageInfo *image_info,
     y;
 
   unsigned char
-    *stream,
-    *pixels;
+    header[12],
+    *stream;
+
+  WebPDecoderConfig
+    configure;
+
+  WebPDecBuffer
+    *magick_restrict webp_image = &configure.output;
+
+  WebPBitstreamFeatures
+    *magick_restrict features = &configure.input;
 
   /*
     Open image file.
   */
   assert(image_info != (const ImageInfo *) NULL);
-  assert(image_info->signature == MagickSignature);
+  assert(image_info->signature == MagickCoreSignature);
   if (image_info->debug != MagickFalse)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
       image_info->filename);
   assert(exception != (ExceptionInfo *) NULL);
-  assert(exception->signature == MagickSignature);
-  image=AcquireImage(image_info);
+  assert(exception->signature == MagickCoreSignature);
+  image=AcquireImage(image_info,exception);
   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
   if (status == MagickFalse)
     {
       image=DestroyImageList(image);
       return((Image *) NULL);
     }
-  length=(size_t) GetBlobSize(image);
+  if (WebPInitDecoderConfig(&configure) == 0)
+    ThrowReaderException(ResourceLimitError,"UnableToDecodeImageFile");
+  webp_image->colorspace=MODE_RGBA;
+  count=ReadBlob(image,12,header);
+  if (count != 12)
+    ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
+  status=IsWEBP(header,count);
+  if (status == MagickFalse)
+    ThrowReaderException(CorruptImageError,"CorruptImage");
+  length=(size_t) (ReadWebPLSBWord(header+4)+8);
+  if (length < 12)
+    ThrowReaderException(CorruptImageError,"CorruptImage");
   stream=(unsigned char *) AcquireQuantumMemory(length,sizeof(*stream));
   if (stream == (unsigned char *) NULL)
     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
-  count=ReadBlob(image,length,stream);
-  if (count != (ssize_t) length)
+  memcpy(stream,header,12);
+  count=ReadBlob(image,length-12,stream+12);
+  if (count != (ssize_t) (length-12))
     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
-  pixels=(unsigned char *) WebPDecodeRGBA(stream,length,&width,&height);
-  if (pixels == (unsigned char *) NULL)
-    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
-  image->columns=(size_t) width;
-  image->rows=(size_t) height;
-  p=pixels;
+  webp_status=WebPGetFeatures(stream,length,features);
+  if (webp_status == VP8_STATUS_OK)
+    {
+      image->columns=(size_t) features->width;
+      image->rows=(size_t) features->height;
+      image->depth=8;
+      image->alpha_trait=features->has_alpha != 0 ? BlendPixelTrait :
+        UndefinedPixelTrait;
+      if (image_info->ping != MagickFalse)
+        {
+          stream=(unsigned char*) RelinquishMagickMemory(stream);
+          (void) CloseBlob(image);
+          return(GetFirstImageInList(image));
+        }
+      status=SetImageExtent(image,image->columns,image->rows,exception);
+      if (status == MagickFalse)
+        return(DestroyImageList(image));
+      webp_status=WebPDecode(stream,length,&configure);
+    }
+  if (webp_status != VP8_STATUS_OK)
+    {
+      stream=(unsigned char*) RelinquishMagickMemory(stream);
+      switch (webp_status)
+      {
+        case VP8_STATUS_OUT_OF_MEMORY:
+        {
+          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
+          break;
+        }
+        case VP8_STATUS_INVALID_PARAM:
+        {
+          ThrowReaderException(CorruptImageError,"invalid parameter");
+          break;
+        }
+        case VP8_STATUS_BITSTREAM_ERROR:
+        {
+          ThrowReaderException(CorruptImageError,"CorruptImage");
+          break;
+        }
+        case VP8_STATUS_UNSUPPORTED_FEATURE:
+        {
+          ThrowReaderException(CoderError,"DataEncodingSchemeIsNotSupported");
+          break;
+        }
+        case VP8_STATUS_SUSPENDED:
+        {
+          ThrowReaderException(CorruptImageError,"decoder suspended");
+          break;
+        }
+        case VP8_STATUS_USER_ABORT:
+        {
+          ThrowReaderException(CorruptImageError,"user abort");
+          break;
+        }
+        case VP8_STATUS_NOT_ENOUGH_DATA:
+        {
+          ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
+          break;
+        }
+        default:
+          ThrowReaderException(CorruptImageError,"CorruptImage");
+      }
+    }
+  if (IsWEBPImageLossless(stream,length) != MagickFalse)
+    image->quality=100;
+  p=(unsigned char *) webp_image->u.RGBA.rgba;
   for (y=0; y < (ssize_t) image->rows; y++)
   {
+    register Quantum
+      *q;
+
+    register ssize_t
+      x;
+
     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
     if (q == (Quantum *) NULL)
       break;
@@ -175,8 +355,6 @@ static Image *ReadWEBPImage(const ImageInfo *image_info,
       SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
       SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
       SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
-      if (GetPixelAlpha(image,q) != OpaqueAlpha)
-        image->matte=MagickTrue;
       q+=GetPixelChannels(image);
     }
     if (SyncAuthenticPixels(image,exception) == MagickFalse)
@@ -186,8 +364,9 @@ static Image *ReadWEBPImage(const ImageInfo *image_info,
     if (status == MagickFalse)
       break;
   }
-  free(pixels);
-  pixels=(unsigned char *) NULL;
+  WebPFreeDecBuffer(webp_image);
+  stream=(unsigned char*) RelinquishMagickMemory(stream);
+  (void) CloseBlob(image);
   return(image);
 }
 #endif
@@ -217,17 +396,27 @@ static Image *ReadWEBPImage(const ImageInfo *image_info,
 */
 ModuleExport size_t RegisterWEBPImage(void)
 {
+  char
+    version[MagickPathExtent];
+
   MagickInfo
     *entry;
 
-  entry=SetMagickInfo("WEBP");
+  *version='\0';
+  entry=AcquireMagickInfo("WEBP","WEBP","WebP Image Format");
 #if defined(MAGICKCORE_WEBP_DELEGATE)
   entry->decoder=(DecodeImageHandler *) ReadWEBPImage;
   entry->encoder=(EncodeImageHandler *) WriteWEBPImage;
+  (void) FormatLocaleString(version,MagickPathExtent,"libwebp %d.%d.%d [%04X]",
+    (WebPGetDecoderVersion() >> 16) & 0xff,
+    (WebPGetDecoderVersion() >> 8) & 0xff,
+    (WebPGetDecoderVersion() >> 0) & 0xff,WEBP_DECODER_ABI_VERSION);
 #endif
-  entry->description=ConstantString("WebP Image Format");
-  entry->adjoin=MagickFalse;
-  entry->module=ConstantString("WEBP");
+  entry->mime_type=ConstantString("image/webp");
+  entry->flags^=CoderAdjoinFlag;
+  entry->magick=(IsImageFormatHandler *) IsWEBP;
+  if (*version != '\0')
+    entry->version=ConstantString(version);
   (void) RegisterMagickInfo(entry);
   return(MagickImageCoderSignature);
 }
@@ -283,7 +472,24 @@ ModuleExport void UnregisterWEBPImage(void)
 %
 */
 
-static int WebPWriter(const unsigned char *stream,size_t length,
+#if WEBP_DECODER_ABI_VERSION >= 0x0100
+static int WebPEncodeProgress(int percent,const WebPPicture* picture)
+{
+#define EncodeImageTag  "Encode/Image"
+
+  Image
+    *image;
+
+  MagickBooleanType
+    status;
+
+  image=(Image *) picture->custom_ptr;
+  status=SetImageProgress(image,EncodeImageTag,percent-1,100);
+  return(status == MagickFalse ? 0 : 1);
+}
+#endif
+
+static int WebPEncodeWriter(const unsigned char *stream,size_t length,
   const WebPPicture *const picture)
 {
   Image
@@ -294,29 +500,26 @@ static int WebPWriter(const unsigned char *stream,size_t length,
 }
 
 static MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
-  Image *image)
+  Image *image,ExceptionInfo *exception)
 {
+  const char
+    *value;
+
   int
     webp_status;
 
   MagickBooleanType
     status;
 
-  register const Quantum
-    *restrict p;
-
-  register ssize_t
-    x;
+  MemoryInfo
+    *pixel_info;
 
-  register unsigned char
-    *restrict q;
+  register uint32_t
+    *magick_restrict q;
 
   ssize_t
     y;
 
-  unsigned char
-    *pixels;
-
   WebPConfig
     configure;
 
@@ -330,54 +533,148 @@ static MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
     Open output image file.
   */
   assert(image_info != (const ImageInfo *) NULL);
-  assert(image_info->signature == MagickSignature);
+  assert(image_info->signature == MagickCoreSignature);
   assert(image != (Image *) NULL);
-  assert(image->signature == MagickSignature);
+  assert(image->signature == MagickCoreSignature);
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
+  if ((image->columns > 16383UL) || (image->rows > 16383UL))
+    ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
+  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
   if (status == MagickFalse)
     return(status);
-  if (WebPPictureInit(&picture) == 0)
-    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
-  picture.writer=WebPWriter;
+  if ((WebPPictureInit(&picture) == 0) || (WebPConfigInit(&configure) == 0))
+    ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
+  picture.writer=WebPEncodeWriter;
   picture.custom_ptr=(void *) image;
+#if WEBP_DECODER_ABI_VERSION >= 0x0100
+  picture.progress_hook=WebPEncodeProgress;
+#endif
   picture.stats=(&statistics);
   picture.width=(int) image->columns;
   picture.height=(int) image->rows;
+  picture.argb_stride=(int) image->columns;
+  picture.use_argb=1;
   if (image->quality != UndefinedCompressionQuality)
     configure.quality=(float) image->quality;
-  if (WebPConfigInit(&configure) == 0)
-    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
-  /*
-    Future: set custom configuration parameters here.
-  */
+  if (image->quality >= 100)
+    configure.lossless=1;
+  value=GetImageOption(image_info,"webp:lossless");
+  if (value != (char *) NULL)
+    configure.lossless=(int) ParseCommandOption(MagickBooleanOptions,
+      MagickFalse,value);
+  value=GetImageOption(image_info,"webp:method");
+  if (value != (char *) NULL)
+    configure.method=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:image-hint");
+  if (value != (char *) NULL)
+    {
+      if (LocaleCompare(value,"default") == 0)
+        configure.image_hint=WEBP_HINT_DEFAULT;
+      if (LocaleCompare(value,"photo") == 0)
+        configure.image_hint=WEBP_HINT_PHOTO;
+      if (LocaleCompare(value,"picture") == 0)
+        configure.image_hint=WEBP_HINT_PICTURE;
+#if WEBP_DECODER_ABI_VERSION >= 0x0200
+      if (LocaleCompare(value,"graph") == 0)
+        configure.image_hint=WEBP_HINT_GRAPH;
+#endif
+    }
+  value=GetImageOption(image_info,"webp:target-size");
+  if (value != (char *) NULL)
+    configure.target_size=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:target-psnr");
+  if (value != (char *) NULL)
+    configure.target_PSNR=(float) StringToDouble(value,(char **) NULL);
+  value=GetImageOption(image_info,"webp:segments");
+  if (value != (char *) NULL)
+    configure.segments=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:sns-strength");
+  if (value != (char *) NULL)
+    configure.sns_strength=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:filter-strength");
+  if (value != (char *) NULL)
+    configure.filter_strength=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:filter-sharpness");
+  if (value != (char *) NULL)
+    configure.filter_sharpness=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:filter-type");
+  if (value != (char *) NULL)
+    configure.filter_type=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:auto-filter");
+  if (value != (char *) NULL)
+    configure.autofilter=(int) ParseCommandOption(MagickBooleanOptions,
+      MagickFalse,value);
+  value=GetImageOption(image_info,"webp:alpha-compression");
+  if (value != (char *) NULL)
+    configure.alpha_compression=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:alpha-filtering");
+  if (value != (char *) NULL)
+    configure.alpha_filtering=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:alpha-quality");
+  if (value != (char *) NULL)
+    configure.alpha_quality=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:pass");
+  if (value != (char *) NULL)
+    configure.pass=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:show-compressed");
+  if (value != (char *) NULL)
+    configure.show_compressed=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:preprocessing");
+  if (value != (char *) NULL)
+    configure.preprocessing=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:partitions");
+  if (value != (char *) NULL)
+    configure.partitions=StringToInteger(value);
+  value=GetImageOption(image_info,"webp:partition-limit");
+  if (value != (char *) NULL)
+    configure.partition_limit=StringToInteger(value);
+#if WEBP_DECODER_ABI_VERSION >= 0x0201
+  value=GetImageOption(image_info,"webp:emulate-jpeg-size");
+  if (value != (char *) NULL)
+    configure.emulate_jpeg_size=(int) ParseCommandOption(MagickBooleanOptions,
+      MagickFalse,value);
+  value=GetImageOption(image_info,"webp:low-memory");
+  if (value != (char *) NULL)
+    configure.low_memory=(int) ParseCommandOption(MagickBooleanOptions,
+      MagickFalse,value);
+  value=GetImageOption(image_info,"webp:thread-level");
+  if (value != (char *) NULL)
+    configure.thread_level=StringToInteger(value);
+#endif
   if (WebPValidateConfig(&configure) == 0)
     ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
   /*
     Allocate memory for pixels.
   */
-  pixels=(unsigned char *) AcquireQuantumMemory(image->columns,
-    (image->matte != MagickFalse ? 4 : 3)*image->rows*sizeof(*pixels));
-  if (pixels == (unsigned char *) NULL)
+  (void) TransformImageColorspace(image,sRGBColorspace,exception);
+  pixel_info=AcquireVirtualMemory(image->columns,image->rows*
+    sizeof(*picture.argb));
+  if (pixel_info == (MemoryInfo *) NULL)
     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
+  picture.argb=(uint32_t *) GetVirtualMemoryBlob(pixel_info);
   /*
     Convert image to WebP raster pixels.
   */
-  q=pixels;
+  q=picture.argb;
   for (y=0; y < (ssize_t) image->rows; y++)
   {
-    p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
+    register const Quantum
+      *magick_restrict p;
+
+    register ssize_t
+      x;
+
+    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
     if (p == (const Quantum *) NULL)
       break;
     for (x=0; x < (ssize_t) image->columns; x++)
     {
-      *q++=ScaleQuantumToChar(GetPixelRed(image,p));
-      *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
-      *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
-      if (image->matte != MagickFalse)
-        *q++=ScaleQuantumToChar((Quantum) (image->matte != MagickFalse ?
-          GetPixelAlpha(image,p) : OpaqueAlpha));
+      *q++=(uint32_t) (image->alpha_trait != UndefinedPixelTrait ?
+        ScaleQuantumToChar(GetPixelAlpha(image,p)) << 24 : 0xff000000) |
+        (ScaleQuantumToChar(GetPixelRed(image,p)) << 16) |
+        (ScaleQuantumToChar(GetPixelGreen(image,p)) << 8) |
+        (ScaleQuantumToChar(GetPixelBlue(image,p)));
       p+=GetPixelChannels(image);
     }
     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
@@ -385,12 +682,78 @@ static MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
     if (status == MagickFalse)
       break;
   }
-  if (image->matte == MagickFalse)
-    webp_status=WebPPictureImportRGB(&picture,pixels,3*picture.width);
-  else
-    webp_status=WebPPictureImportRGBA(&picture,pixels,4*picture.width);
-  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
   webp_status=WebPEncode(&configure,&picture);
+  if (webp_status == 0)
+    {
+      const char
+        *message;
+
+      switch (picture.error_code)
+      {
+        case VP8_ENC_ERROR_OUT_OF_MEMORY:
+        {
+          message="out of memory";
+          break;
+        }
+        case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
+        {
+          message="bitstream out of memory";
+          break;
+        }
+        case VP8_ENC_ERROR_NULL_PARAMETER:
+        {
+          message="NULL parameter";
+          break;
+        }
+        case VP8_ENC_ERROR_INVALID_CONFIGURATION:
+        {
+          message="invalid configuration";
+          break;
+        }
+        case VP8_ENC_ERROR_BAD_DIMENSION:
+        {
+          message="bad dimension";
+          break;
+        }
+        case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
+        {
+          message="partition 0 overflow (> 512K)";
+          break;
+        }
+        case VP8_ENC_ERROR_PARTITION_OVERFLOW:
+        {
+          message="partition overflow (> 16M)";
+          break;
+        }
+        case VP8_ENC_ERROR_BAD_WRITE:
+        {
+          message="bad write";
+          break;
+        }
+        case VP8_ENC_ERROR_FILE_TOO_BIG:
+        {
+          message="file too big (> 4GB)";
+          break;
+        }
+#if WEBP_DECODER_ABI_VERSION >= 0x0100
+        case VP8_ENC_ERROR_USER_ABORT:
+        {
+          message="user abort";
+          break;
+        }
+#endif
+        default:
+        {
+          message="unknown exception";
+          break;
+        }
+      }
+      (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
+        (char *) message,"`%s'",image->filename);
+    }
+  picture.argb=(uint32_t *) NULL;
+  WebPPictureFree(&picture);
+  pixel_info=RelinquishVirtualMemory(pixel_info);
   (void) CloseBlob(image);
   return(webp_status == 0 ? MagickFalse : MagickTrue);
 }