]> granicus.if.org Git - imagemagick/blob - coders/webp.c
(no commit message)
[imagemagick] / coders / webp.c
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 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/blob-private.h"
45 #include "MagickCore/client.h"
46 #include "MagickCore/display.h"
47 #include "MagickCore/exception.h"
48 #include "MagickCore/exception-private.h"
49 #include "MagickCore/image.h"
50 #include "MagickCore/image-private.h"
51 #include "MagickCore/list.h"
52 #include "MagickCore/magick.h"
53 #include "MagickCore/monitor.h"
54 #include "MagickCore/monitor-private.h"
55 #include "MagickCore/memory_.h"
56 #include "MagickCore/option.h"
57 #include "MagickCore/pixel-accessor.h"
58 #include "MagickCore/quantum-private.h"
59 #include "MagickCore/static.h"
60 #include "MagickCore/string_.h"
61 #include "MagickCore/string-private.h"
62 #include "MagickCore/module.h"
63 #include "MagickCore/utility.h"
64 #include "MagickCore/xwindow.h"
65 #include "MagickCore/xwindow-private.h"
66 #if defined(MAGICKCORE_WEBP_DELEGATE)
67 #include <webp/decode.h>
68 #include <webp/encode.h>
69 #endif
70 \f
71 /*
72   Forward declarations.
73 */
74 #if defined(MAGICKCORE_WEBP_DELEGATE)
75 static MagickBooleanType
76   WriteWEBPImage(const ImageInfo *,Image *,ExceptionInfo *);
77 #endif
78 \f
79 /*
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 %                                                                             %
82 %                                                                             %
83 %                                                                             %
84 %   I s W E B P                                                               %
85 %                                                                             %
86 %                                                                             %
87 %                                                                             %
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89 %
90 %  IsWEBP() returns MagickTrue if the image format type, identified by the
91 %  magick string, is WebP.
92 %
93 %  The format of the IsWEBP method is:
94 %
95 %      MagickBooleanType IsWEBP(const unsigned char *magick,const size_t length)
96 %
97 %  A description of each parameter follows:
98 %
99 %    o magick: compare image format pattern against these bytes.
100 %
101 %    o length: Specifies the length of the magick string.
102 %
103 */
104 static MagickBooleanType IsWEBP(const unsigned char *magick,const size_t length)
105 {
106   if (length < 12)
107     return(MagickFalse);
108   if (LocaleNCompare((const char *) magick+8,"WEBP",4) == 0)
109     return(MagickTrue);
110   return(MagickFalse);
111 }
112 \f
113 #if defined(MAGICKCORE_WEBP_DELEGATE)
114 /*
115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116 %                                                                             %
117 %                                                                             %
118 %                                                                             %
119 %   R e a d W E B P I m a g e                                                 %
120 %                                                                             %
121 %                                                                             %
122 %                                                                             %
123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
124 %
125 %  ReadWEBPImage() reads an image in the WebP image format.
126 %
127 %  The format of the ReadWEBPImage method is:
128 %
129 %      Image *ReadWEBPImage(const ImageInfo *image_info,
130 %        ExceptionInfo *exception)
131 %
132 %  A description of each parameter follows:
133 %
134 %    o image_info: the image info.
135 %
136 %    o exception: return any errors or warnings in this structure.
137 %
138 */
139
140 static inline uint32_t ReadWebPLSBWord(const unsigned char *restrict data)
141 {
142   register const unsigned char
143     *p;
144
145   register uint32_t
146     value;
147
148   p=data;
149   value=(uint32_t) (*p++);
150   value|=((uint32_t) (*p++)) << 8;
151   value|=((uint32_t) (*p++)) << 16;
152   value|=((uint32_t) (*p++)) << 24;
153   return(value);
154 }
155
156 static MagickBooleanType IsWEBPImageLossless(const unsigned char *stream,
157   const size_t length)
158 {
159 #define VP8_CHUNK_INDEX  15
160 #define LOSSLESS_FLAG  'L'
161 #define EXTENDED_HEADER  'X'
162 #define VP8_CHUNK_HEADER  "VP8"
163 #define VP8_CHUNK_HEADER_SIZE  3
164 #define RIFF_HEADER_SIZE  12
165 #define VP8X_CHUNK_SIZE  10
166 #define TAG_SIZE  4
167 #define CHUNK_SIZE_BYTES  4
168 #define CHUNK_HEADER_SIZE  8
169 #define MAX_CHUNK_PAYLOAD  (~0U-CHUNK_HEADER_SIZE-1)
170
171   ssize_t
172     offset;
173
174   /*
175     Read simple header.
176   */
177   if (stream[VP8_CHUNK_INDEX] != EXTENDED_HEADER)
178    return(stream[VP8_CHUNK_INDEX] == LOSSLESS_FLAG ? MagickTrue : MagickFalse);
179   /*
180     Read extended header.
181   */
182   offset=RIFF_HEADER_SIZE+TAG_SIZE+CHUNK_SIZE_BYTES+VP8X_CHUNK_SIZE;
183   while (offset <= length)
184   {
185     uint32_t
186       chunk_size,
187       chunk_size_pad;
188
189     chunk_size=ReadWebPLSBWord(stream+offset+TAG_SIZE);
190     if (chunk_size > MAX_CHUNK_PAYLOAD)
191       break;
192     chunk_size_pad=(CHUNK_HEADER_SIZE+chunk_size+1) & ~1;
193     if (memcmp( stream+offset,VP8_CHUNK_HEADER,VP8_CHUNK_HEADER_SIZE) == 0)
194       return(*(stream+offset+VP8_CHUNK_HEADER_SIZE) == LOSSLESS_FLAG ?
195         MagickTrue : MagickFalse);
196     offset+=chunk_size_pad;
197   }
198   return(MagickFalse);
199 }
200
201 static Image *ReadWEBPImage(const ImageInfo *image_info,
202   ExceptionInfo *exception)
203 {
204   Image
205     *image;
206
207   MagickBooleanType
208     status;
209
210   register unsigned char
211     *p;
212
213   size_t
214     length;
215
216   ssize_t
217     count,
218     y;
219
220   unsigned char
221     *stream;
222
223   WebPDecoderConfig
224     configure;
225
226   WebPDecBuffer
227     *restrict webp_image = &configure.output;
228
229   WebPBitstreamFeatures
230     *restrict features = &configure.input;
231
232   /*
233     Open image file.
234   */
235   assert(image_info != (const ImageInfo *) NULL);
236   assert(image_info->signature == MagickSignature);
237   if (image_info->debug != MagickFalse)
238     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
239       image_info->filename);
240   assert(exception != (ExceptionInfo *) NULL);
241   assert(exception->signature == MagickSignature);
242   image=AcquireImage(image_info,exception);
243   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
244   if (status == MagickFalse)
245     {
246       image=DestroyImageList(image);
247       return((Image *) NULL);
248     }
249   if (WebPInitDecoderConfig(&configure) == 0)
250     ThrowReaderException(ResourceLimitError,"UnableToDecodeImageFile");
251   length=(size_t) GetBlobSize(image);
252   stream=(unsigned char *) AcquireQuantumMemory(length,sizeof(*stream));
253   if (stream == (unsigned char *) NULL)
254     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
255   count=ReadBlob(image,length,stream);
256   if (count != (ssize_t) length)
257     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
258   if (WebPGetFeatures(stream,length,features) != 0)
259     {
260       stream=(unsigned char*) RelinquishMagickMemory(stream);
261       ThrowReaderException(ResourceLimitError,"UnableToDecodeImageFile");
262     }
263   webp_image->colorspace=MODE_RGBA;
264   if (WebPDecode(stream,length,&configure) != 0)
265     {
266       stream=(unsigned char*) RelinquishMagickMemory(stream);
267       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
268     }
269   image->columns=(size_t) webp_image->width;
270   image->rows=(size_t) webp_image->height;
271   image->alpha_trait=features->has_alpha != 0 ? BlendPixelTrait :
272     UndefinedPixelTrait;
273   if (IsWEBPImageLossless(stream,length) != MagickFalse)
274     image->quality=100;
275   p=webp_image->u.RGBA.rgba;
276   for (y=0; y < (ssize_t) image->rows; y++)
277   {
278     register Quantum
279       *q;
280
281     register ssize_t
282       x;
283
284     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
285     if (q == (Quantum *) NULL)
286       break;
287     for (x=0; x < (ssize_t) image->columns; x++)
288     {
289       SetPixelRed(image,ScaleCharToQuantum(*p++),q);
290       SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
291       SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
292       SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
293       q+=GetPixelChannels(image);
294     }
295     if (SyncAuthenticPixels(image,exception) == MagickFalse)
296       break;
297     status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
298       image->rows);
299     if (status == MagickFalse)
300       break;
301   }
302   WebPFreeDecBuffer(webp_image);
303   stream=(unsigned char*) RelinquishMagickMemory(stream);
304   return(image);
305 }
306 #endif
307 \f
308 /*
309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
310 %                                                                             %
311 %                                                                             %
312 %                                                                             %
313 %   R e g i s t e r W E B P I m a g e                                         %
314 %                                                                             %
315 %                                                                             %
316 %                                                                             %
317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
318 %
319 %  RegisterWEBPImage() adds attributes for the WebP image format to
320 %  the list of supported formats.  The attributes include the image format
321 %  tag, a method to read and/or write the format, whether the format
322 %  supports the saving of more than one frame to the same file or blob,
323 %  whether the format supports native in-memory I/O, and a brief
324 %  description of the format.
325 %
326 %  The format of the RegisterWEBPImage method is:
327 %
328 %      size_t RegisterWEBPImage(void)
329 %
330 */
331 ModuleExport size_t RegisterWEBPImage(void)
332 {
333   char
334     version[MaxTextExtent];
335
336   MagickInfo
337     *entry;
338
339   *version='\0';
340   entry=SetMagickInfo("WEBP");
341 #if defined(MAGICKCORE_WEBP_DELEGATE)
342   entry->decoder=(DecodeImageHandler *) ReadWEBPImage;
343   entry->encoder=(EncodeImageHandler *) WriteWEBPImage;
344   (void) FormatLocaleString(version,MaxTextExtent,"libwebp %d.%d.%d",
345     (WebPGetDecoderVersion() >> 16) & 0xff,
346     (WebPGetDecoderVersion() >> 8) & 0xff,
347     (WebPGetDecoderVersion() >> 0) & 0xff);
348 #endif
349   entry->description=ConstantString("WebP Image Format");
350   entry->adjoin=MagickFalse;
351   entry->module=ConstantString("WEBP");
352   entry->magick=(IsImageFormatHandler *) IsWEBP;
353   if (*version != '\0')
354     entry->version=ConstantString(version);
355   (void) RegisterMagickInfo(entry);
356   return(MagickImageCoderSignature);
357 }
358 \f
359 /*
360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
361 %                                                                             %
362 %                                                                             %
363 %                                                                             %
364 %   U n r e g i s t e r W E B P I m a g e                                     %
365 %                                                                             %
366 %                                                                             %
367 %                                                                             %
368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
369 %
370 %  UnregisterWEBPImage() removes format registrations made by the WebP module
371 %  from the list of supported formats.
372 %
373 %  The format of the UnregisterWEBPImage method is:
374 %
375 %      UnregisterWEBPImage(void)
376 %
377 */
378 ModuleExport void UnregisterWEBPImage(void)
379 {
380   (void) UnregisterMagickInfo("WEBP");
381 }
382 #if defined(MAGICKCORE_WEBP_DELEGATE)
383 \f
384 /*
385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
386 %                                                                             %
387 %                                                                             %
388 %                                                                             %
389 %   W r i t e W E B P I m a g e                                               %
390 %                                                                             %
391 %                                                                             %
392 %                                                                             %
393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
394 %
395 %  WriteWEBPImage() writes an image in the WebP image format.
396 %
397 %  The format of the WriteWEBPImage method is:
398 %
399 %      MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
400 %        Image *image)
401 %
402 %  A description of each parameter follows.
403 %
404 %    o image_info: the image info.
405 %
406 %    o image:  The image.
407 %
408 */
409
410 static int WebPWriter(const unsigned char *stream,size_t length,
411   const WebPPicture *const picture)
412 {
413   Image
414     *image;
415
416   image=(Image *) picture->custom_ptr;
417   return(length != 0 ? (int) WriteBlob(image,length,stream) : 1);
418 }
419
420 static MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
421   Image *image,ExceptionInfo *exception)
422 {
423   const char
424     *value;
425
426   int
427     webp_status;
428
429   MagickBooleanType
430     status;
431
432   register uint32_t
433     *restrict q;
434
435   ssize_t
436     y;
437
438   WebPConfig
439     configure;
440
441   WebPPicture
442     picture;
443
444   WebPAuxStats
445     statistics;
446
447   /*
448     Open output image file.
449   */
450   assert(image_info != (const ImageInfo *) NULL);
451   assert(image_info->signature == MagickSignature);
452   assert(image != (Image *) NULL);
453   assert(image->signature == MagickSignature);
454   if (image->debug != MagickFalse)
455     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
456   if ((image->columns > 16383UL) || (image->rows > 16383UL))
457     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
458   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
459   if (status == MagickFalse)
460     return(status);
461   if ((WebPPictureInit(&picture) == 0) || (WebPConfigInit(&configure) == 0))
462     ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
463   picture.writer=WebPWriter;
464   picture.custom_ptr=(void *) image;
465   picture.stats=(&statistics);
466   picture.width=(int) image->columns;
467   picture.height=(int) image->rows;
468   picture.argb_stride=(int) image->columns;
469   picture.use_argb=1;
470   if (image->quality != UndefinedCompressionQuality)
471     configure.quality=(float) image->quality;
472   else
473     if (image->quality >= 100)
474       configure.lossless=1;
475   value=GetImageOption(image_info,"webp:lossless");
476   if (value != (char *) NULL)
477     configure.lossless=ParseCommandOption(MagickBooleanOptions,MagickFalse,
478       value);
479   value=GetImageOption(image_info,"webp:method");
480   if (value != (char *) NULL)
481     configure.method=StringToInteger(value);
482   value=GetImageOption(image_info,"webp:image-hint");
483   if (value != (char *) NULL)
484     {
485       if (LocaleCompare(value,"graph") == 0)
486         configure.image_hint=WEBP_HINT_GRAPH;
487       if (LocaleCompare(value,"photo") == 0)
488         configure.image_hint=WEBP_HINT_PHOTO;
489       if (LocaleCompare(value,"picture") == 0)
490         configure.image_hint=WEBP_HINT_PICTURE;
491     }
492   value=GetImageOption(image_info,"webp:target-size");
493   if (value != (char *) NULL)
494     configure.target_size=StringToInteger(value);
495   value=GetImageOption(image_info,"webp:target-psnr");
496   if (value != (char *) NULL)
497     configure.target_PSNR=(float) StringToDouble(value,(char **) NULL);
498   value=GetImageOption(image_info,"webp:segments");
499   if (value != (char *) NULL)
500     configure.segments=StringToInteger(value);
501   value=GetImageOption(image_info,"webp:sns-strength");
502   if (value != (char *) NULL)
503     configure.sns_strength=StringToInteger(value);
504   value=GetImageOption(image_info,"webp:filter-strength");
505   if (value != (char *) NULL)
506     configure.filter_strength=StringToInteger(value);
507   value=GetImageOption(image_info,"webp:filter-sharpness");
508   if (value != (char *) NULL)
509     configure.filter_sharpness=StringToInteger(value);
510   value=GetImageOption(image_info,"webp:filter-type");
511   if (value != (char *) NULL)
512     configure.filter_type=StringToInteger(value);
513   value=GetImageOption(image_info,"webp:auto-filter");
514   if (value != (char *) NULL)
515     configure.autofilter=ParseCommandOption(MagickBooleanOptions,MagickFalse,
516       value);
517   value=GetImageOption(image_info,"webp:alpha-compression");
518   if (value != (char *) NULL)
519     configure.alpha_compression=StringToInteger(value);
520   value=GetImageOption(image_info,"webp:alpha-filtering");
521   if (value != (char *) NULL)
522     configure.alpha_filtering=StringToInteger(value);
523   value=GetImageOption(image_info,"webp:alpha-quality");
524   if (value != (char *) NULL)
525     configure.alpha_quality=StringToInteger(value);
526   value=GetImageOption(image_info,"webp:pass");
527   if (value != (char *) NULL)
528     configure.pass=StringToInteger(value);
529   value=GetImageOption(image_info,"webp:show-compressed");
530   if (value != (char *) NULL)
531     configure.show_compressed=StringToInteger(value);
532   value=GetImageOption(image_info,"webp:preprocessing");
533   if (value != (char *) NULL)
534     configure.preprocessing=StringToInteger(value);
535   value=GetImageOption(image_info,"webp:partitions");
536   if (value != (char *) NULL)
537     configure.partitions=StringToInteger(value);
538   value=GetImageOption(image_info,"webp:partition-limit");
539   if (value != (char *) NULL)
540     configure.partition_limit=StringToInteger(value);
541   if (WebPValidateConfig(&configure) == 0)
542     ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
543   /*
544     Allocate memory for pixels.
545   */
546   picture.argb=(uint32_t *) AcquireQuantumMemory(image->columns,image->rows*
547     sizeof(*picture.argb));
548   if (picture.argb == (uint32_t *) NULL)
549     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
550   /*
551     Convert image to WebP raster pixels.
552   */
553   q=picture.argb;
554   for (y=0; y < (ssize_t) image->rows; y++)
555   {
556     register const Quantum
557       *restrict p;
558
559     register ssize_t
560       x;
561
562     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
563     if (p == (const Quantum *) NULL)
564       break;
565     for (x=0; x < (ssize_t) image->columns; x++)
566     {
567       *q++=(uint32_t) (image->alpha_trait == BlendPixelTrait ?
568         ScaleQuantumToChar(GetPixelAlpha(image,p)) << 24 : 0xff000000u) |
569         (ScaleQuantumToChar(GetPixelRed(image,p)) << 16) |
570         (ScaleQuantumToChar(GetPixelGreen(image,p)) << 8) |
571         (ScaleQuantumToChar(GetPixelBlue(image,p)));
572       p+=GetPixelChannels(image);
573     }
574     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
575       image->rows);
576     if (status == MagickFalse)
577       break;
578   }
579   webp_status=WebPEncode(&configure,&picture);
580   WebPPictureFree(&picture);
581   picture.argb=(uint32_t *) RelinquishMagickMemory(picture.argb);
582   (void) CloseBlob(image);
583   return(webp_status == 0 ? MagickFalse : MagickTrue);
584 }
585 #endif