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)
77 static 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 */
IsWEBP(const unsigned char * magick,const size_t length)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
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
ReadWebPLSBWord(const unsigned char * magick_restrict data)142 static inline uint32_t ReadWebPLSBWord(
143 const unsigned char *magick_restrict data)
144 {
145 register const unsigned char
146 *p;
147
148 register uint32_t
149 value;
150
151 p=data;
152 value=(uint32_t) (*p++);
153 value|=((uint32_t) (*p++)) << 8;
154 value|=((uint32_t) (*p++)) << 16;
155 value|=((uint32_t) (*p++)) << 24;
156 return(value);
157 }
158
IsWEBPImageLossless(const unsigned char * stream,const size_t length)159 static MagickBooleanType IsWEBPImageLossless(const unsigned char *stream,
160 const size_t length)
161 {
162 #define VP8_CHUNK_INDEX 15
163 #define LOSSLESS_FLAG 'L'
164 #define EXTENDED_HEADER 'X'
165 #define VP8_CHUNK_HEADER "VP8"
166 #define VP8_CHUNK_HEADER_SIZE 3
167 #define RIFF_HEADER_SIZE 12
168 #define VP8X_CHUNK_SIZE 10
169 #define TAG_SIZE 4
170 #define CHUNK_SIZE_BYTES 4
171 #define CHUNK_HEADER_SIZE 8
172 #define MAX_CHUNK_PAYLOAD (~0U-CHUNK_HEADER_SIZE-1)
173
174 ssize_t
175 offset;
176
177 /*
178 Read simple header.
179 */
180 if (stream[VP8_CHUNK_INDEX] != EXTENDED_HEADER)
181 return(stream[VP8_CHUNK_INDEX] == LOSSLESS_FLAG ? MagickTrue : MagickFalse);
182 /*
183 Read extended header.
184 */
185 offset=RIFF_HEADER_SIZE+TAG_SIZE+CHUNK_SIZE_BYTES+VP8X_CHUNK_SIZE;
186 while (offset <= (ssize_t) length)
187 {
188 uint32_t
189 chunk_size,
190 chunk_size_pad;
191
192 chunk_size=ReadWebPLSBWord(stream+offset+TAG_SIZE);
193 if (chunk_size > MAX_CHUNK_PAYLOAD)
194 break;
195 chunk_size_pad=(CHUNK_HEADER_SIZE+chunk_size+1) & ~1;
196 if (memcmp(stream+offset,VP8_CHUNK_HEADER,VP8_CHUNK_HEADER_SIZE) == 0)
197 return(*(stream+offset+VP8_CHUNK_HEADER_SIZE) == LOSSLESS_FLAG ?
198 MagickTrue : MagickFalse);
199 offset+=chunk_size_pad;
200 }
201 return(MagickFalse);
202 }
203
ReadWEBPImage(const ImageInfo * image_info,ExceptionInfo * exception)204 static 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 */
RegisterWEBPImage(void)396 ModuleExport 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 */
UnregisterWEBPImage(void)442 ModuleExport 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
WebPEncodeProgress(int percent,const WebPPicture * picture)475 static 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
WebPEncodeWriter(const unsigned char * stream,size_t length,const WebPPicture * const picture)491 static 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
WriteWEBPImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)501 static 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