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