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