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