Coverage Report

Created: 2026-05-16 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/coders/webp.c
Line
Count
Source
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 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
%    https://imagemagick.org/license/                                         %
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/layer.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/profile-private.h"
62
#include "MagickCore/property.h"
63
#include "MagickCore/quantum-private.h"
64
#include "MagickCore/static.h"
65
#include "MagickCore/string_.h"
66
#include "MagickCore/string-private.h"
67
#include "MagickCore/module.h"
68
#include "MagickCore/utility.h"
69
#include "MagickCore/xwindow.h"
70
#include "MagickCore/xwindow-private.h"
71
#if defined(MAGICKCORE_WEBP_DELEGATE)
72
#include <webp/decode.h>
73
#include <webp/encode.h>
74
#if defined(MAGICKCORE_WEBPMUX_DELEGATE)
75
#include <webp/mux.h>
76
#include <webp/demux.h>
77
#endif
78
#endif
79

80
/*
81
  Forward declarations.
82
*/
83
#if defined(MAGICKCORE_WEBP_DELEGATE)
84
static MagickBooleanType
85
  WriteWEBPImage(const ImageInfo *,Image *,ExceptionInfo *);
86
#endif
87

88
/*
89
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90
%                                                                             %
91
%                                                                             %
92
%                                                                             %
93
%   I s W E B P                                                               %
94
%                                                                             %
95
%                                                                             %
96
%                                                                             %
97
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98
%
99
%  IsWEBP() returns MagickTrue if the image format type, identified by the
100
%  magick string, is WebP.
101
%
102
%  The format of the IsWEBP method is:
103
%
104
%      MagickBooleanType IsWEBP(const unsigned char *magick,const size_t length)
105
%
106
%  A description of each parameter follows:
107
%
108
%    o magick: compare image format pattern against these bytes.
109
%
110
%    o length: Specifies the length of the magick string.
111
%
112
*/
113
static MagickBooleanType IsWEBP(const unsigned char *magick,const size_t length)
114
5.67k
{
115
5.67k
  if (length < 12)
116
0
    return(MagickFalse);
117
5.67k
  if (LocaleNCompare((const char *) magick+8,"WEBP",4) == 0)
118
5.65k
    return(MagickTrue);
119
21
  return(MagickFalse);
120
5.67k
}
121

122
#if defined(MAGICKCORE_WEBP_DELEGATE)
123
/*
124
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125
%                                                                             %
126
%                                                                             %
127
%                                                                             %
128
%   R e a d W E B P I m a g e                                                 %
129
%                                                                             %
130
%                                                                             %
131
%                                                                             %
132
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133
%
134
%  ReadWEBPImage() reads an image in the WebP image format.
135
%
136
%  The format of the ReadWEBPImage method is:
137
%
138
%      Image *ReadWEBPImage(const ImageInfo *image_info,
139
%        ExceptionInfo *exception)
140
%
141
%  A description of each parameter follows:
142
%
143
%    o image_info: the image info.
144
%
145
%    o exception: return any errors or warnings in this structure.
146
%
147
*/
148
149
static inline uint32_t ReadWebPLSBWord(
150
  const unsigned char *magick_restrict data)
151
6.73k
{
152
6.73k
  const unsigned char
153
6.73k
    *p;
154
155
6.73k
  uint32_t
156
6.73k
    value;
157
158
6.73k
  p=data;
159
6.73k
  value=(uint32_t) (*p++);
160
6.73k
  value|=((uint32_t) (*p++)) << 8;
161
6.73k
  value|=((uint32_t) (*p++)) << 16;
162
6.73k
  value|=((uint32_t) (*p++)) << 24;
163
6.73k
  return(value);
164
6.73k
}
165
166
static inline void WriteWebPLSBWord(unsigned char *magick_restrict data,
167
  const size_t value)
168
520
{
169
520
  unsigned char
170
520
    *p;
171
172
520
  p=data;
173
520
  *(p++)=(unsigned char) value;
174
520
  *(p++)=(unsigned char) (value >> 8);
175
520
  *(p++)=(unsigned char) (value >> 16);
176
520
  *(p++)=(unsigned char) (value >> 24);
177
520
}
178
179
static MagickBooleanType IsWEBPImageLossless(const unsigned char *stream,
180
  const size_t length)
181
5.22k
{
182
15.2k
#define VP8_CHUNK_INDEX  15
183
5.22k
#define LOSSLESS_FLAG  'L'
184
5.22k
#define EXTENDED_HEADER  'X'
185
5.22k
#define VP8_CHUNK_HEADER  "VP8"
186
5.22k
#define VP8_CHUNK_HEADER_SIZE  3
187
5.22k
#define RIFF_HEADER_SIZE  12
188
5.22k
#define VP8X_CHUNK_SIZE  10
189
5.22k
#define TAG_SIZE  4
190
5.22k
#define CHUNK_SIZE_BYTES  4
191
5.22k
#define CHUNK_HEADER_SIZE  8
192
5.22k
#define MAX_CHUNK_PAYLOAD  (~0U-CHUNK_HEADER_SIZE-1)
193
194
5.22k
  size_t
195
5.22k
    offset;
196
197
  /*
198
    Read simple header.
199
  */
200
5.22k
  if (length <= VP8_CHUNK_INDEX)
201
6
    return(MagickFalse);
202
5.21k
  if (stream[VP8_CHUNK_INDEX] != EXTENDED_HEADER)
203
4.79k
    return(stream[VP8_CHUNK_INDEX] == LOSSLESS_FLAG ? MagickTrue : MagickFalse);
204
  /*
205
    Read extended header.
206
  */
207
422
  offset=RIFF_HEADER_SIZE+TAG_SIZE+CHUNK_SIZE_BYTES+VP8X_CHUNK_SIZE;
208
1.15k
  while (offset <= (length-TAG_SIZE-TAG_SIZE-4))
209
1.08k
  {
210
1.08k
    uint32_t
211
1.08k
      chunk_size,
212
1.08k
      chunk_size_pad;
213
214
1.08k
    chunk_size=ReadWebPLSBWord(stream+offset+TAG_SIZE);
215
1.08k
    if (chunk_size > MAX_CHUNK_PAYLOAD)
216
2
      break;
217
1.07k
    chunk_size_pad=(CHUNK_HEADER_SIZE+chunk_size+1) & (unsigned int) ~1;
218
1.07k
    if (memcmp(stream+offset,VP8_CHUNK_HEADER,VP8_CHUNK_HEADER_SIZE) == 0)
219
351
      return(*(stream+offset+VP8_CHUNK_HEADER_SIZE) == LOSSLESS_FLAG ?
220
348
        MagickTrue : MagickFalse);
221
728
    offset+=chunk_size_pad;
222
728
  }
223
71
  return(MagickFalse);
224
422
}
225
226
static int FillBasicWEBPInfo(Image *image,const uint8_t *stream,size_t length,
227
  WebPDecoderConfig *configure)
228
11.7k
{
229
11.7k
  int
230
11.7k
    webp_status;
231
232
11.7k
  WebPBitstreamFeatures
233
11.7k
    *magick_restrict features = &configure->input;
234
235
11.7k
  webp_status=(int) WebPGetFeatures(stream,length,features);
236
11.7k
  if (webp_status != VP8_STATUS_OK)
237
193
    return(webp_status);
238
11.5k
  image->columns=(size_t) features->width;
239
11.5k
  image->rows=(size_t) features->height;
240
11.5k
  image->depth=8;
241
11.5k
  image->alpha_trait=features->has_alpha != 0 ? BlendPixelTrait :
242
11.5k
    UndefinedPixelTrait;
243
11.5k
  return(webp_status);
244
11.7k
}
245
246
static int ReadSingleWEBPImage(const ImageInfo *image_info,Image *image,
247
  const uint8_t *stream,size_t length,WebPDecoderConfig *configure,
248
  ExceptionInfo *exception,MagickBooleanType is_first)
249
5.22k
{
250
5.22k
  int
251
5.22k
    webp_status;
252
253
5.22k
  MagickBooleanType
254
5.22k
    status;
255
256
5.22k
  size_t
257
5.22k
    canvas_width,
258
5.22k
    canvas_height,
259
5.22k
    image_width,
260
5.22k
    image_height;
261
262
5.22k
  ssize_t
263
5.22k
    x_offset,
264
5.22k
    y_offset,
265
5.22k
    y;
266
267
5.22k
  unsigned char
268
5.22k
    *p;
269
270
5.22k
  WebPDecBuffer
271
5.22k
    *magick_restrict webp_image;
272
273
5.22k
  webp_image=&configure->output;
274
5.22k
  if (is_first != MagickFalse)
275
323
    {
276
323
      canvas_width=image->columns;
277
323
      canvas_height=image->rows;
278
323
      x_offset=image->page.x;
279
323
      y_offset=image->page.y;
280
323
      image->page.x=0;
281
323
      image->page.y=0;
282
323
    }
283
4.90k
  else
284
4.90k
    {
285
4.90k
      canvas_width=0;
286
4.90k
      canvas_height=0;
287
4.90k
      x_offset=0;
288
4.90k
      y_offset=0;
289
4.90k
    }
290
5.22k
  webp_status=FillBasicWEBPInfo(image,stream,length,configure);
291
5.22k
  image_width=image->columns;
292
5.22k
  image_height=image->rows;
293
5.22k
  if (is_first)
294
323
    {
295
323
      image->columns=canvas_width;
296
323
      image->rows=canvas_height;
297
323
    }
298
5.22k
  if (webp_status != VP8_STATUS_OK)
299
0
    return(webp_status);
300
5.22k
  if (IsWEBPImageLossless((unsigned char *) stream,length) != MagickFalse)
301
1.03k
    image->quality=100;
302
5.22k
  if (image_info->ping != MagickFalse)
303
143
    return(webp_status);
304
5.08k
  status=SetImageExtent(image,image->columns,image->rows,exception);
305
5.08k
  if (status == MagickFalse)
306
1.30k
    return(-1);
307
3.77k
  webp_status=(int) WebPDecode(stream,length,configure);
308
3.77k
  if (webp_status != VP8_STATUS_OK)
309
843
    return(webp_status);
310
2.92k
  p=(unsigned char *) webp_image->u.RGBA.rgba;
311
1.49M
  for (y=0; y < (ssize_t) image->rows; y++)
312
1.48M
  {
313
1.48M
    Quantum
314
1.48M
      *q;
315
316
1.48M
    ssize_t
317
1.48M
      x;
318
319
1.48M
    q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
320
1.48M
    if (q == (Quantum *) NULL)
321
0
      {
322
0
        webp_status=-1;
323
0
        break;
324
0
      }
325
1.33G
    for (x=0; x < (ssize_t) image->columns; x++)
326
1.32G
    {
327
1.32G
      if (((x >= x_offset) && (x < (x_offset+(ssize_t) image_width))) &&
328
1.27G
          ((y >= y_offset) && (y < (y_offset+(ssize_t) image_height))))
329
1.27G
        {
330
1.27G
          SetPixelRed(image,ScaleCharToQuantum(*p++),q);
331
1.27G
          SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
332
1.27G
          SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
333
1.27G
          SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
334
1.27G
        }
335
51.7M
      else
336
51.7M
        {
337
51.7M
          SetPixelRed(image,(Quantum) 0,q);
338
51.7M
          SetPixelGreen(image,(Quantum) 0,q);
339
51.7M
          SetPixelBlue(image,(Quantum) 0,q);
340
51.7M
          SetPixelAlpha(image,(Quantum) 0,q);
341
51.7M
        }
342
1.32G
      q+=(ptrdiff_t) GetPixelChannels(image);
343
1.32G
    }
344
1.48M
    if (SyncAuthenticPixels(image,exception) == MagickFalse)
345
0
      break;
346
1.48M
    status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
347
1.48M
      image->rows);
348
1.48M
    if (status == MagickFalse)
349
0
      break;
350
1.48M
  }
351
2.92k
  WebPFreeDecBuffer(webp_image);
352
2.92k
#if defined(MAGICKCORE_WEBPMUX_DELEGATE)
353
2.92k
  {
354
2.92k
    StringInfo
355
2.92k
      *profile;
356
357
2.92k
    uint32_t
358
2.92k
      webp_flags = 0;
359
360
2.92k
    WebPData
361
2.92k
     chunk,
362
2.92k
     content;
363
364
2.92k
    WebPMux
365
2.92k
      *mux;
366
367
    /*
368
      Extract any profiles:
369
      https://developers.google.com/speed/webp/docs/container-api.
370
    */
371
2.92k
    content.bytes=stream;
372
2.92k
    content.size=length;
373
2.92k
    mux=WebPMuxCreate(&content,0);
374
2.92k
    (void) memset(&chunk,0,sizeof(chunk));
375
2.92k
    (void) WebPMuxGetFeatures(mux,&webp_flags);
376
2.92k
    if ((webp_flags & ICCP_FLAG) &&
377
0
        (WebPMuxGetChunk(mux,"ICCP",&chunk) == WEBP_MUX_OK))
378
0
      if (chunk.size != 0)
379
0
        {
380
0
          profile=BlobToProfileStringInfo("icc",chunk.bytes,chunk.size,
381
0
            exception);
382
0
          (void) SetImageProfilePrivate(image,profile,exception);
383
0
        }
384
2.92k
    if ((webp_flags & EXIF_FLAG) &&
385
62
        (WebPMuxGetChunk(mux,"EXIF",&chunk) == WEBP_MUX_OK))
386
62
      if (chunk.size != 0)
387
61
        {
388
61
          profile=BlobToProfileStringInfo("exif",chunk.bytes,chunk.size,
389
61
            exception);
390
61
          (void) SetImageProfilePrivate(image,profile,exception);
391
61
        }
392
2.92k
    if (((webp_flags & XMP_FLAG) &&
393
3
         (WebPMuxGetChunk(mux,"XMP ",&chunk) == WEBP_MUX_OK)) ||
394
2.92k
         (WebPMuxGetChunk(mux,"XMP\0",&chunk) == WEBP_MUX_OK))
395
5
      if (chunk.size != 0)
396
3
        {
397
3
          profile=BlobToProfileStringInfo("xmp",chunk.bytes,chunk.size,
398
3
            exception);
399
3
          (void) SetImageProfilePrivate(image,profile,exception);
400
3
        }
401
2.92k
    WebPMuxDelete(mux);
402
2.92k
  }
403
2.92k
#endif
404
2.92k
  return(webp_status);
405
3.77k
}
406
407
#if defined(MAGICKCORE_WEBPMUX_DELEGATE)
408
static int ReadAnimatedWEBPImage(const ImageInfo *image_info,Image *image,
409
  uint8_t *stream,size_t length,WebPDecoderConfig *configure,
410
  ExceptionInfo *exception)
411
947
{
412
947
  Image
413
947
    *original_image;
414
415
947
  int
416
947
    image_count,
417
947
    webp_status;
418
419
947
  size_t
420
947
    canvas_width,
421
947
    canvas_height;
422
423
947
  WebPData
424
947
    data;
425
426
947
  WebPDemuxer
427
947
    *demux;
428
429
947
  WebPIterator
430
947
    iter;
431
432
947
  image_count=0;
433
947
  webp_status=0;
434
947
  original_image=image;
435
947
  webp_status=FillBasicWEBPInfo(image,stream,length,configure);
436
947
  canvas_width=image->columns;
437
947
  canvas_height=image->rows;
438
947
  data.bytes=stream;
439
947
  data.size=length;
440
947
  {
441
947
    WebPMux
442
947
      *mux;
443
444
947
    WebPMuxAnimParams
445
947
      params;
446
447
947
    WebPMuxError
448
947
      status;
449
450
947
    mux=WebPMuxCreate(&data,0);
451
947
    status=WebPMuxGetAnimationParams(mux,&params);
452
947
    if (status >= 0)
453
87
      image->iterations=(size_t) params.loop_count;
454
947
    WebPMuxDelete(mux);
455
947
  }
456
947
  demux=WebPDemux(&data);
457
947
  if (WebPDemuxGetFrame(demux,1,&iter))
458
323
    {
459
323
      do
460
774
      {
461
774
        if (image_count != 0)
462
451
          {
463
451
            AcquireNextImage(image_info,image,exception);
464
451
            if (GetNextImageInList(image) == (Image *) NULL)
465
0
              break;
466
451
            image=SyncNextImageInList(image);
467
451
            CloneImageProperties(image,original_image);
468
451
            image->page.x=(ssize_t) iter.x_offset;
469
451
            image->page.y=(ssize_t) iter.y_offset;
470
451
            webp_status=ReadSingleWEBPImage(image_info,image,
471
451
              iter.fragment.bytes,iter.fragment.size,configure,exception,
472
451
              MagickFalse);
473
451
          }
474
323
        else
475
323
          {
476
323
            image->page.x=(ssize_t) iter.x_offset;
477
323
            image->page.y=(ssize_t) iter.y_offset;
478
323
            webp_status=ReadSingleWEBPImage(image_info,image,
479
323
              iter.fragment.bytes,iter.fragment.size,configure,exception,
480
323
              MagickTrue);
481
323
          }
482
774
        if (webp_status != VP8_STATUS_OK)
483
226
          break;
484
548
        image->page.width=canvas_width;
485
548
        image->page.height=canvas_height;
486
548
        image->ticks_per_second=100;
487
548
        image->delay=(size_t) round(iter.duration/10.0);
488
548
        image->dispose=NoneDispose;
489
548
        if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND)
490
135
          image->dispose=BackgroundDispose;
491
548
        (void) SetImageProperty(image,"webp:mux-blend",
492
548
          "AtopPreviousAlphaBlend",exception);
493
548
        if (iter.blend_method == WEBP_MUX_BLEND)
494
390
          (void) SetImageProperty(image,"webp:mux-blend",
495
390
            "AtopBackgroundAlphaBlend",exception);
496
548
        image_count++;
497
548
      } while (WebPDemuxNextFrame(&iter));
498
323
      WebPDemuxReleaseIterator(&iter);
499
323
    }
500
947
  WebPDemuxDelete(demux);
501
947
  return(webp_status);
502
947
}
503
#endif
504
505
static Image *ReadWEBPImage(const ImageInfo *image_info,
506
  ExceptionInfo *exception)
507
5.68k
{
508
5.68k
#define ThrowWEBPException(severity,tag) \
509
1.03k
{ \
510
1.13k
  if (stream != (unsigned char *) NULL) \
511
1.03k
    stream=(unsigned char*) RelinquishMagickMemory(stream); \
512
1.13k
  if (webp_image != (WebPDecBuffer *) NULL) \
513
1.13k
    WebPFreeDecBuffer(webp_image); \
514
1.13k
  ThrowReaderException(severity,tag); \
515
0
}
516
517
5.68k
  Image
518
5.68k
    *image;
519
520
5.68k
  int
521
5.68k
    webp_status;
522
523
5.68k
  MagickBooleanType
524
5.68k
    status;
525
526
5.68k
  size_t
527
5.68k
    blob_size,
528
5.68k
    length;
529
530
5.68k
  ssize_t
531
5.68k
    count;
532
533
5.68k
  unsigned char
534
5.68k
    header[12],
535
5.68k
    *stream;
536
537
5.68k
  WebPDecoderConfig
538
5.68k
    configure;
539
540
5.68k
  WebPDecBuffer
541
5.68k
    *magick_restrict webp_image = &configure.output;
542
543
  /*
544
    Open image file.
545
  */
546
5.68k
  assert(image_info != (const ImageInfo *) NULL);
547
5.68k
  assert(image_info->signature == MagickCoreSignature);
548
5.68k
  assert(exception != (ExceptionInfo *) NULL);
549
5.68k
  assert(exception->signature == MagickCoreSignature);
550
5.68k
  if (IsEventLogging() != MagickFalse)
551
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
552
0
      image_info->filename);
553
5.68k
  image=AcquireImage(image_info,exception);
554
5.68k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
555
5.68k
  if (status == MagickFalse)
556
0
    {
557
0
      image=DestroyImageList(image);
558
0
      return((Image *) NULL);
559
0
    }
560
5.68k
  stream=(unsigned char *) NULL;
561
5.68k
  if (WebPInitDecoderConfig(&configure) == 0)
562
5.68k
    ThrowReaderException(ResourceLimitError,"UnableToDecodeImageFile");
563
5.68k
  webp_image->colorspace=MODE_RGBA;
564
5.68k
  count=ReadBlob(image,12,header);
565
5.68k
  if (count != 12)
566
5.67k
    ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
567
5.67k
  status=IsWEBP(header,(size_t) count);
568
5.67k
  if (status == MagickFalse)
569
5.65k
    ThrowWEBPException(CorruptImageError,"CorruptImage");
570
5.65k
  length=(size_t) (ReadWebPLSBWord(header+4)+8);
571
5.65k
  if (length < 12)
572
5.65k
    ThrowWEBPException(CorruptImageError,"CorruptImage");
573
5.65k
  blob_size=(size_t) GetBlobSize(image);
574
5.65k
  if (length > blob_size)
575
586
    {
576
586
      size_t
577
586
        delta=length-blob_size;
578
579
586
      if (delta != 12 && delta != (12 + 8))
580
520
        ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
581
520
      length-=delta;
582
520
      WriteWebPLSBWord(header+4,length-8);
583
520
    }
584
5.58k
  stream=(unsigned char *) AcquireQuantumMemory(length,sizeof(*stream));
585
5.58k
  if (stream == (unsigned char *) NULL)
586
5.58k
    ThrowWEBPException(ResourceLimitError,"MemoryAllocationFailed");
587
5.58k
  (void) memcpy(stream,header,12);
588
5.58k
  count=ReadBlob(image,length-12,stream+12);
589
5.58k
  if (count != (ssize_t) (length-12))
590
5.58k
    ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
591
5.58k
  webp_status=FillBasicWEBPInfo(image,stream,length,&configure);
592
5.58k
  if (webp_status == VP8_STATUS_OK) {
593
5.39k
    if (configure.input.has_animation) {
594
947
#if defined(MAGICKCORE_WEBPMUX_DELEGATE)
595
947
      webp_status=ReadAnimatedWEBPImage(image_info,image,stream,length,
596
947
        &configure,exception);
597
#else
598
      webp_status=VP8_STATUS_UNSUPPORTED_FEATURE;
599
#endif
600
4.44k
    } else {
601
4.44k
      webp_status=ReadSingleWEBPImage(image_info,image,stream,length,
602
4.44k
        &configure,exception,MagickFalse);
603
4.44k
    }
604
5.39k
  }
605
606
5.58k
  if (webp_status != VP8_STATUS_OK)
607
2.34k
    switch (webp_status)
608
2.34k
    {
609
1.30k
      case -1:
610
1.30k
      {
611
1.30k
        stream=(unsigned char*) RelinquishMagickMemory(stream);
612
1.30k
        if (webp_image != (WebPDecBuffer *) NULL)
613
1.30k
          WebPFreeDecBuffer(webp_image);
614
1.30k
        (void) CloseBlob(image);
615
1.30k
        return(DestroyImageList(image));
616
0
      }
617
17
      case VP8_STATUS_OUT_OF_MEMORY:
618
17
      {
619
17
        ThrowWEBPException(ResourceLimitError,"MemoryAllocationFailed");
620
0
        break;
621
0
      }
622
0
      case VP8_STATUS_INVALID_PARAM:
623
0
      {
624
0
        ThrowWEBPException(CorruptImageError,"invalid parameter");
625
0
        break;
626
0
      }
627
539
      case VP8_STATUS_BITSTREAM_ERROR:
628
539
      {
629
539
        ThrowWEBPException(CorruptImageError,"CorruptImage");
630
0
        break;
631
0
      }
632
0
      case VP8_STATUS_UNSUPPORTED_FEATURE:
633
0
      {
634
0
        ThrowWEBPException(CoderError,"DataEncodingSchemeIsNotSupported");
635
0
        break;
636
0
      }
637
0
      case VP8_STATUS_SUSPENDED:
638
0
      {
639
0
        ThrowWEBPException(CorruptImageError,"decoder suspended");
640
0
        break;
641
0
      }
642
0
      case VP8_STATUS_USER_ABORT:
643
0
      {
644
0
        ThrowWEBPException(CorruptImageError,"user abort");
645
0
        break;
646
0
      }
647
480
      case VP8_STATUS_NOT_ENOUGH_DATA:
648
480
      {
649
480
        ThrowWEBPException(CorruptImageError,"InsufficientImageDataInFile");
650
0
        break;
651
0
      }
652
0
      default:
653
0
        ThrowWEBPException(CorruptImageError,"CorruptImage");
654
2.34k
    }
655
656
3.24k
  stream=(unsigned char*) RelinquishMagickMemory(stream);
657
3.24k
  (void) CloseBlob(image);
658
3.24k
  return(image);
659
5.58k
}
660
#endif
661

662
/*
663
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
664
%                                                                             %
665
%                                                                             %
666
%                                                                             %
667
%   R e g i s t e r W E B P I m a g e                                         %
668
%                                                                             %
669
%                                                                             %
670
%                                                                             %
671
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672
%
673
%  RegisterWEBPImage() adds attributes for the WebP image format to
674
%  the list of supported formats.  The attributes include the image format
675
%  tag, a method to read and/or write the format, whether the format
676
%  supports the saving of more than one frame to the same file or blob,
677
%  whether the format supports native in-memory I/O, and a brief
678
%  description of the format.
679
%
680
%  The format of the RegisterWEBPImage method is:
681
%
682
%      size_t RegisterWEBPImage(void)
683
%
684
*/
685
ModuleExport size_t RegisterWEBPImage(void)
686
9
{
687
9
  char
688
9
    version[MagickPathExtent];
689
690
9
  MagickInfo
691
9
    *entry;
692
693
9
  *version='\0';
694
9
  entry=AcquireMagickInfo("WEBP","WEBP","WebP Image Format");
695
9
#if defined(MAGICKCORE_WEBP_DELEGATE)
696
9
  entry->decoder=(DecodeImageHandler *) ReadWEBPImage;
697
9
  entry->encoder=(EncodeImageHandler *) WriteWEBPImage;
698
9
  (void) FormatLocaleString(version,MagickPathExtent,"libwebp %d.%d.%d [%04X]",
699
9
    (WebPGetEncoderVersion() >> 16) & 0xff,
700
9
    (WebPGetEncoderVersion() >> 8) & 0xff,
701
9
    (WebPGetEncoderVersion() >> 0) & 0xff,WEBP_ENCODER_ABI_VERSION);
702
9
#endif
703
9
  entry->mime_type=ConstantString("image/webp");
704
9
  entry->flags|=CoderDecoderSeekableStreamFlag;
705
#if !defined(MAGICKCORE_WEBPMUX_DELEGATE)
706
  entry->flags^=CoderAdjoinFlag;
707
#endif
708
9
  entry->magick=(IsImageFormatHandler *) IsWEBP;
709
9
  if (*version != '\0')
710
9
    entry->version=ConstantString(version);
711
9
  (void) RegisterMagickInfo(entry);
712
9
  return(MagickImageCoderSignature);
713
9
}
714

715
/*
716
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
717
%                                                                             %
718
%                                                                             %
719
%                                                                             %
720
%   U n r e g i s t e r W E B P I m a g e                                     %
721
%                                                                             %
722
%                                                                             %
723
%                                                                             %
724
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725
%
726
%  UnregisterWEBPImage() removes format registrations made by the WebP module
727
%  from the list of supported formats.
728
%
729
%  The format of the UnregisterWEBPImage method is:
730
%
731
%      UnregisterWEBPImage(void)
732
%
733
*/
734
ModuleExport void UnregisterWEBPImage(void)
735
0
{
736
0
  (void) UnregisterMagickInfo("WEBP");
737
0
}
738
#if defined(MAGICKCORE_WEBP_DELEGATE)
739

740
/*
741
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
742
%                                                                             %
743
%                                                                             %
744
%                                                                             %
745
%   W r i t e W E B P I m a g e                                               %
746
%                                                                             %
747
%                                                                             %
748
%                                                                             %
749
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
750
%
751
%  WriteWEBPImage() writes an image in the WebP image format.
752
%
753
%  The format of the WriteWEBPImage method is:
754
%
755
%      MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
756
%        Image *image)
757
%
758
%  A description of each parameter follows.
759
%
760
%    o image_info: the image info.
761
%
762
%    o image:  The image.
763
%
764
*/
765
766
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
767
static int WebPEncodeProgress(int percent,const WebPPicture* picture)
768
0
{
769
0
#define EncodeImageTag  "Encode/Image"
770
771
0
  Image
772
0
    *image;
773
774
0
  MagickBooleanType
775
0
    status;
776
777
0
  image=(Image *) picture->user_data;
778
0
  status=SetImageProgress(image,EncodeImageTag,percent-1,100);
779
0
  return(status == MagickFalse ? 0 : 1);
780
0
}
781
#endif
782
783
static const char * WebPErrorCodeMessage(WebPEncodingError error_code)
784
0
{
785
0
  switch (error_code)
786
0
  {
787
0
    case VP8_ENC_OK:
788
0
      return "";
789
0
    case VP8_ENC_ERROR_OUT_OF_MEMORY:
790
0
      return "out of memory";
791
0
    case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
792
0
      return "bitstream out of memory";
793
0
    case VP8_ENC_ERROR_NULL_PARAMETER:
794
0
      return "NULL parameter";
795
0
    case VP8_ENC_ERROR_INVALID_CONFIGURATION:
796
0
      return "invalid configuration";
797
0
    case VP8_ENC_ERROR_BAD_DIMENSION:
798
0
      return "bad dimension";
799
0
    case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
800
0
      return "partition 0 overflow (> 512K)";
801
0
    case VP8_ENC_ERROR_PARTITION_OVERFLOW:
802
0
      return "partition overflow (> 16M)";
803
0
    case VP8_ENC_ERROR_BAD_WRITE:
804
0
      return "bad write";
805
0
    case VP8_ENC_ERROR_FILE_TOO_BIG:
806
0
      return "file too big (> 4GB)";
807
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
808
0
    case VP8_ENC_ERROR_USER_ABORT:
809
0
      return "user abort";
810
0
    case VP8_ENC_ERROR_LAST:
811
0
      return "error last";
812
0
#endif
813
0
  }
814
0
  return "unknown exception";
815
0
}
816
817
static MagickBooleanType WriteSingleWEBPPicture(const ImageInfo *image_info,
818
  Image *image,WebPPicture *picture,MemoryInfo **memory_info,
819
  ExceptionInfo *exception)
820
2.82k
{
821
2.82k
  MagickBooleanType
822
2.82k
    status;
823
824
2.82k
  ssize_t
825
2.82k
    y;
826
827
2.82k
  uint32_t
828
2.82k
    *magick_restrict q;
829
830
2.82k
#if WEBP_ENCODER_ABI_VERSION >= 0x0100
831
2.82k
  if (image->progress_monitor != (MagickProgressMonitor) NULL)
832
0
    {
833
0
      picture->progress_hook=WebPEncodeProgress;
834
0
      picture->user_data=(void *) image;
835
0
    }
836
2.82k
#endif
837
2.82k
  picture->width=(int) image->columns;
838
2.82k
  picture->height=(int) image->rows;
839
2.82k
  picture->argb_stride=(int) image->columns;
840
2.82k
  picture->use_argb=1;
841
  /*
842
    Allocate memory for pixels.
843
  */
844
2.82k
  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
845
0
    (void) TransformImageColorspace(image,sRGBColorspace,exception);
846
2.82k
  *memory_info=AcquireVirtualMemory(image->columns,image->rows*
847
2.82k
    sizeof(*(picture->argb)));
848
2.82k
  if (*memory_info == (MemoryInfo *) NULL)
849
2.82k
    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
850
2.82k
  picture->argb=(uint32_t *) GetVirtualMemoryBlob(*memory_info);
851
  /*
852
    Convert image to WebP raster pixels.
853
  */
854
2.82k
  status=MagickFalse;
855
2.82k
  q=picture->argb;
856
1.48M
  for (y=0; y < (ssize_t) image->rows; y++)
857
1.48M
  {
858
1.48M
    const Quantum
859
1.48M
      *magick_restrict p;
860
861
1.48M
    ssize_t
862
1.48M
      x;
863
864
1.48M
    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
865
1.48M
    if (p == (const Quantum *) NULL)
866
243
      break;
867
1.32G
    for (x=0; x < (ssize_t) image->columns; x++)
868
1.32G
    {
869
1.32G
      *q++=(uint32_t) (image->alpha_trait != UndefinedPixelTrait ? (uint32_t)
870
1.21G
        ScaleQuantumToChar(GetPixelAlpha(image,p)) << 24 : 0xff000000) |
871
1.32G
        ((uint32_t) ScaleQuantumToChar(GetPixelRed(image,p)) << 16) |
872
1.32G
        ((uint32_t) ScaleQuantumToChar(GetPixelGreen(image,p)) << 8) |
873
1.32G
        ((uint32_t) ScaleQuantumToChar(GetPixelBlue(image,p)));
874
1.32G
      p+=(ptrdiff_t) GetPixelChannels(image);
875
1.32G
    }
876
1.48M
    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
877
1.48M
      image->rows);
878
1.48M
    if (status == MagickFalse)
879
0
      break;
880
1.48M
  }
881
2.82k
  return(status);
882
2.82k
}
883
884
static MagickBooleanType WriteSingleWEBPImage(const ImageInfo *image_info,
885
  Image *image,WebPConfig *configure,WebPMemoryWriter *writer,
886
  ExceptionInfo *exception)
887
2.82k
{
888
2.82k
  MagickBooleanType
889
2.82k
    status;
890
891
2.82k
  MemoryInfo
892
2.82k
    *memory_info;
893
894
2.82k
  WebPPicture
895
2.82k
    picture;
896
897
2.82k
  if (WebPPictureInit(&picture) == 0)
898
2.82k
    ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
899
2.82k
  picture.writer=WebPMemoryWrite;
900
2.82k
  picture.custom_ptr=writer;
901
2.82k
  status=WriteSingleWEBPPicture(image_info,image,&picture,&memory_info,
902
2.82k
    exception);
903
2.82k
  if (status != MagickFalse)
904
2.58k
    {
905
2.58k
      status=(MagickBooleanType) WebPEncode(configure,&picture);
906
2.58k
      if (status == MagickFalse)
907
0
        (void) ThrowMagickException(exception,GetMagickModule(),
908
0
          CorruptImageError,WebPErrorCodeMessage(picture.error_code),"`%s'",
909
0
          image->filename);
910
2.58k
  }
911
2.82k
  if (memory_info != (MemoryInfo *) NULL)
912
2.82k
    memory_info=RelinquishVirtualMemory(memory_info);
913
2.82k
  WebPPictureFree(&picture);
914
915
2.82k
  return(status);
916
2.82k
}
917
918
#if defined(MAGICKCORE_WEBPMUX_DELEGATE)
919
static void *WebPDestroyMemoryInfo(void *memory_info)
920
0
{
921
0
  return((void *) RelinquishVirtualMemory((MemoryInfo *) memory_info));
922
0
}
923
924
static MagickBooleanType WriteAnimatedWEBPImage(const ImageInfo *image_info,
925
  Image *image,const WebPConfig *configure,WebPData *webp_data,
926
  ExceptionInfo *exception)
927
0
{
928
0
  Image
929
0
    *frame;
930
931
0
  LinkedListInfo
932
0
    *memory_info_list;
933
934
0
  MagickBooleanType
935
0
    status;
936
937
0
  MemoryInfo
938
0
    *memory_info;
939
940
0
  size_t
941
0
    effective_delta,
942
0
    frame_timestamp;
943
944
0
  WebPAnimEncoder
945
0
    *enc;
946
947
0
  WebPAnimEncoderOptions
948
0
    enc_options;
949
950
0
  WebPPicture
951
0
    picture;
952
953
0
  (void) WebPAnimEncoderOptionsInit(&enc_options);
954
0
  if (image_info->verbose != MagickFalse)
955
0
    enc_options.verbose=1;
956
  /*
957
    Appropriate default kmin, kmax values for lossy and lossless.
958
  */
959
0
  enc_options.kmin = configure->lossless ? 9 : 3;
960
0
  enc_options.kmax = configure->lossless ? 17 : 5;
961
0
  enc=WebPAnimEncoderNew((int) image->columns,(int) image->rows,&enc_options);
962
0
  if (enc == (WebPAnimEncoder*) NULL)
963
0
    return(MagickFalse);
964
0
  status=MagickTrue;
965
0
  effective_delta=0;
966
0
  frame_timestamp=0;
967
0
  memory_info_list=NewLinkedList(GetImageListLength(image));
968
0
  frame=image;
969
0
  while (frame != NULL)
970
0
  {
971
0
    status=(MagickBooleanType) WebPPictureInit(&picture);
972
0
    if (status == MagickFalse)
973
0
      {
974
0
        (void) ThrowMagickException(exception,GetMagickModule(),
975
0
          ResourceLimitError,"UnableToEncodeImageFile","`%s'",image->filename);
976
0
        break;
977
0
      }
978
0
    status=WriteSingleWEBPPicture(image_info,frame,&picture,
979
0
      &memory_info,exception);
980
0
    if (status != MagickFalse)
981
0
      {
982
0
        status=(MagickBooleanType) WebPAnimEncoderAdd(enc,&picture,
983
0
          (int) frame_timestamp,configure);
984
0
        if (status == MagickFalse)
985
0
          (void) ThrowMagickException(exception,GetMagickModule(),
986
0
            CorruptImageError,WebPErrorCodeMessage(picture.error_code),"`%s'",
987
0
            image->filename);
988
0
      }
989
0
    if (memory_info != (MemoryInfo *) NULL)
990
0
      (void) AppendValueToLinkedList(memory_info_list,memory_info);
991
0
    WebPPictureFree(&picture);
992
0
    effective_delta=(size_t) (frame->delay*1000*MagickSafeReciprocal(
993
0
      (double) frame->ticks_per_second));
994
0
    if (effective_delta < 10)
995
0
      effective_delta=100; /* Consistent with gif2webp */
996
0
    frame_timestamp+=effective_delta;
997
0
    frame=GetNextImageInList(frame);
998
0
  }
999
0
  if (status != MagickFalse)
1000
0
    {
1001
      /*
1002
        Add last null frame and assemble picture.
1003
      */
1004
0
      status=(MagickBooleanType) WebPAnimEncoderAdd(enc,(WebPPicture *) NULL,
1005
0
        (int) frame_timestamp,configure);
1006
0
      if (status != MagickFalse)
1007
0
        status=(MagickBooleanType) WebPAnimEncoderAssemble(enc,webp_data);
1008
0
      if (status == MagickFalse)
1009
0
        (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
1010
0
          WebPAnimEncoderGetError(enc),"`%s'",image->filename);
1011
0
    }
1012
0
  memory_info_list=DestroyLinkedList(memory_info_list,WebPDestroyMemoryInfo);
1013
0
  WebPAnimEncoderDelete(enc);
1014
0
  return(status);
1015
0
}
1016
1017
static MagickBooleanType WriteWEBPImageProfile(Image *image,
1018
  WebPData *webp_data,ExceptionInfo *exception)
1019
2.58k
{
1020
2.58k
  const StringInfo
1021
2.58k
    *icc_profile,
1022
2.58k
    *exif_profile,
1023
2.58k
    *xmp_profile;
1024
 
1025
2.58k
  WebPData
1026
2.58k
    chunk;
1027
 
1028
2.58k
  WebPMux
1029
2.58k
    *mux;
1030
 
1031
2.58k
  WebPMuxAnimParams
1032
2.58k
    new_params;
1033
 
1034
2.58k
  WebPMuxError
1035
2.58k
    mux_error;
1036
1037
2.58k
  icc_profile=GetImageProfile(image,"ICC");
1038
2.58k
  exif_profile=GetImageProfile(image,"EXIF");
1039
2.58k
  xmp_profile=GetImageProfile(image,"XMP");
1040
2.58k
  if ((icc_profile == (StringInfo *) NULL) &&
1041
2.58k
      (exif_profile == (StringInfo *) NULL) &&
1042
2.52k
      (xmp_profile == (StringInfo *) NULL) &&
1043
2.52k
      (image->iterations == 0))
1044
2.48k
    return(MagickTrue);
1045
97
  mux=WebPMuxCreate(webp_data, 1);
1046
97
  WebPDataClear(webp_data);
1047
97
  if (mux == NULL)
1048
0
    (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1049
0
      "UnableToEncodeImageFile","`%s'",image->filename);
1050
  /*
1051
    Clean up returned data.
1052
  */
1053
97
  memset(webp_data, 0, sizeof(*webp_data));
1054
97
  mux_error=WEBP_MUX_OK;
1055
97
  if (image->iterations > 0)
1056
38
    {
1057
      /*
1058
        If there is only 1 frame, webp_data will be created by
1059
        WriteSingleWEBPImage and WebPMuxGetAnimationParams returns
1060
        WEBP_MUX_NOT_FOUND.
1061
      */
1062
38
      mux_error=WebPMuxGetAnimationParams(mux, &new_params);
1063
38
      if (mux_error == WEBP_MUX_NOT_FOUND)
1064
38
        mux_error=WEBP_MUX_OK;
1065
0
      else
1066
0
        if (mux_error == WEBP_MUX_OK)
1067
0
          {
1068
0
            new_params.loop_count=MagickMin((int) image->iterations,65535);
1069
0
            mux_error=WebPMuxSetAnimationParams(mux, &new_params);
1070
0
          }
1071
38
    }
1072
97
  if ((icc_profile != (StringInfo *) NULL) && (mux_error == WEBP_MUX_OK))
1073
0
    {
1074
0
      chunk.bytes=GetStringInfoDatum(icc_profile);
1075
0
      chunk.size=GetStringInfoLength(icc_profile);
1076
0
      mux_error=WebPMuxSetChunk(mux,"ICCP",&chunk,0);
1077
0
    }
1078
97
  if ((exif_profile != (StringInfo *) NULL) && (mux_error == WEBP_MUX_OK))
1079
59
    {
1080
59
      chunk.bytes=GetStringInfoDatum(exif_profile);
1081
59
      chunk.size=GetStringInfoLength(exif_profile);
1082
59
      if ((chunk.size >= 6) &&
1083
58
          (chunk.bytes[0] == 'E') && (chunk.bytes[1] == 'x') &&
1084
45
          (chunk.bytes[2] == 'i') && (chunk.bytes[3] == 'f') &&
1085
39
          (chunk.bytes[4] == '\0') && (chunk.bytes[5] == '\0'))
1086
33
        {
1087
33
          chunk.bytes=GetStringInfoDatum(exif_profile)+6;
1088
33
          chunk.size-=6;
1089
33
        }
1090
59
      mux_error=WebPMuxSetChunk(mux,"EXIF",&chunk,0);
1091
59
    }
1092
97
  if ((xmp_profile != (StringInfo *) NULL) && (mux_error == WEBP_MUX_OK))
1093
0
    {
1094
0
      chunk.bytes=GetStringInfoDatum(xmp_profile);
1095
0
      chunk.size=GetStringInfoLength(xmp_profile);
1096
0
      mux_error=WebPMuxSetChunk(mux,"XMP ",&chunk,0);
1097
0
    }
1098
97
  if (mux_error == WEBP_MUX_OK)
1099
97
    mux_error=WebPMuxAssemble(mux,webp_data);
1100
97
  WebPMuxDelete(mux);
1101
97
  if (mux_error != WEBP_MUX_OK)
1102
0
    (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
1103
0
      "UnableToEncodeImageFile","`%s'",image->filename);
1104
97
  return(MagickTrue);
1105
2.58k
}
1106
#endif
1107
1108
static inline void SetBooleanOption(const ImageInfo *image_info,
1109
  const char *option,int *setting)
1110
16.9k
{
1111
16.9k
  const char
1112
16.9k
    *value;
1113
1114
16.9k
  value=GetImageOption(image_info,option);
1115
16.9k
  if (value != (char *) NULL)
1116
0
    *setting=(int) ParseCommandOption(MagickBooleanOptions,MagickFalse,value);
1117
16.9k
}
1118
1119
static inline void SetIntegerOption(const ImageInfo *image_info,
1120
  const char *option,int *setting)
1121
45.1k
{
1122
45.1k
  const char
1123
45.1k
    *value;
1124
1125
45.1k
  value=GetImageOption(image_info,option);
1126
45.1k
  if (value != (const char *) NULL)
1127
0
    *setting=StringToInteger(value);
1128
45.1k
}
1129
1130
static MagickBooleanType WriteWEBPImage(const ImageInfo *image_info,
1131
  Image *image,ExceptionInfo * exception)
1132
3.11k
{
1133
3.11k
  const char
1134
3.11k
    *value;
1135
1136
3.11k
  MagickBooleanType
1137
3.11k
    status;
1138
1139
3.11k
  WebPConfig
1140
3.11k
    configure;
1141
1142
3.11k
  WebPMemoryWriter
1143
3.11k
    writer;
1144
1145
  /*
1146
    Open output image file.
1147
  */
1148
3.11k
  assert(image_info != (const ImageInfo *) NULL);
1149
3.11k
  assert(image_info->signature == MagickCoreSignature);
1150
3.11k
  assert(image != (Image *) NULL);
1151
3.11k
  assert(image->signature == MagickCoreSignature);
1152
3.11k
  if (IsEventLogging() != MagickFalse)
1153
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1154
3.11k
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1155
3.11k
  if (status == MagickFalse)
1156
0
    return(status);
1157
3.11k
  if ((image->columns > 16383UL) || (image->rows > 16383UL))
1158
2.82k
    ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
1159
2.82k
  if (WebPConfigInit(&configure) == 0)
1160
2.82k
    ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
1161
2.82k
  if (image->quality != UndefinedCompressionQuality)
1162
920
    {
1163
920
      configure.quality=(float) image->quality;
1164
920
#if WEBP_ENCODER_ABI_VERSION >= 0x020e
1165
920
      configure.near_lossless=(int) image->quality;
1166
920
#endif
1167
920
    }
1168
2.82k
  if (image->quality >= 100)
1169
920
    configure.lossless=1;
1170
2.82k
  SetBooleanOption(image_info,"webp:lossless",&configure.lossless);
1171
2.82k
  value=GetImageOption(image_info,"webp:image-hint");
1172
2.82k
  if (value != (char *) NULL)
1173
0
    {
1174
0
      if (LocaleCompare(value,"default") == 0)
1175
0
        configure.image_hint=WEBP_HINT_DEFAULT;
1176
0
      if (LocaleCompare(value,"photo") == 0)
1177
0
        configure.image_hint=WEBP_HINT_PHOTO;
1178
0
      if (LocaleCompare(value,"picture") == 0)
1179
0
        configure.image_hint=WEBP_HINT_PICTURE;
1180
0
#if WEBP_ENCODER_ABI_VERSION >= 0x0200
1181
0
      if (LocaleCompare(value,"graph") == 0)
1182
0
        configure.image_hint=WEBP_HINT_GRAPH;
1183
0
#endif
1184
0
    }
1185
2.82k
  SetBooleanOption(image_info,"webp:auto-filter",&configure.autofilter);
1186
2.82k
  value=GetImageOption(image_info,"webp:target-psnr");
1187
2.82k
  if (value != (char *) NULL)
1188
0
    configure.target_PSNR=(float) StringToDouble(value,(char **) NULL);
1189
2.82k
  SetIntegerOption(image_info,"webp:alpha-compression",
1190
2.82k
    &configure.alpha_compression);
1191
2.82k
  SetIntegerOption(image_info,"webp:alpha-filtering",
1192
2.82k
    &configure.alpha_filtering);
1193
2.82k
  SetIntegerOption(image_info,"webp:alpha-quality",&configure.alpha_quality);
1194
2.82k
  SetIntegerOption(image_info,"webp:filter-strength",
1195
2.82k
    &configure.filter_strength);
1196
2.82k
  SetIntegerOption(image_info,"webp:filter-sharpness",
1197
2.82k
    &configure.filter_sharpness);
1198
2.82k
  SetIntegerOption(image_info,"webp:filter-type",&configure.filter_type);
1199
2.82k
  SetIntegerOption(image_info,"webp:method",&configure.method);
1200
2.82k
  SetIntegerOption(image_info,"webp:partitions",&configure.partitions);
1201
2.82k
  SetIntegerOption(image_info,"webp:partition-limit",
1202
2.82k
    &configure.partition_limit);
1203
2.82k
  SetIntegerOption(image_info,"webp:pass",&configure.pass);
1204
2.82k
  SetIntegerOption(image_info,"webp:preprocessing",&configure.preprocessing);
1205
2.82k
  SetIntegerOption(image_info,"webp:segments",&configure.segments);
1206
2.82k
  SetIntegerOption(image_info,"webp:show-compressed",
1207
2.82k
    &configure.show_compressed);
1208
2.82k
  SetIntegerOption(image_info,"webp:sns-strength",&configure.sns_strength);
1209
2.82k
  SetIntegerOption(image_info,"webp:target-size",&configure.target_size);
1210
2.82k
#if WEBP_ENCODER_ABI_VERSION >= 0x0201
1211
2.82k
  SetBooleanOption(image_info,"webp:emulate-jpeg-size",
1212
2.82k
    &configure.emulate_jpeg_size);
1213
2.82k
  SetBooleanOption(image_info,"webp:low-memory",&configure.low_memory);
1214
2.82k
  SetIntegerOption(image_info,"webp:thread-level",&configure.thread_level);
1215
2.82k
#endif
1216
2.82k
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
1217
2.82k
  SetBooleanOption(image_info,"webp:exact",&configure.exact);
1218
2.82k
#endif
1219
2.82k
#if WEBP_ENCODER_ABI_VERSION >= 0x020e
1220
2.82k
  SetBooleanOption(image_info,"webp:use-sharp-yuv",&configure.use_sharp_yuv);
1221
2.82k
#endif
1222
2.82k
  if (((configure.target_size > 0) || (configure.target_PSNR > 0)) &&
1223
0
      (configure.pass == 1))
1224
0
    configure.pass=6;
1225
2.82k
  if (WebPValidateConfig(&configure) == 0)
1226
2.82k
    ThrowWriterException(ResourceLimitError,"UnableToEncodeImageFile");
1227
2.82k
#if defined(MAGICKCORE_WEBPMUX_DELEGATE)
1228
2.82k
  {
1229
2.82k
    Image
1230
2.82k
      *next;
1231
1232
2.82k
    WebPData
1233
2.82k
      webp_data;
1234
1235
2.82k
    memset(&webp_data,0,sizeof(webp_data));
1236
2.82k
    next=GetNextImageInList(image);
1237
2.82k
    if ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse))
1238
0
      {
1239
0
        Image
1240
0
          *coalesce_image=(Image *) NULL;
1241
1242
0
        while(next != (Image *) NULL)
1243
0
        {
1244
0
          if ((next->rows != image->rows) || (next->columns != image->columns))
1245
0
            {
1246
0
              coalesce_image=CoalesceImages(image,exception);
1247
0
              break;
1248
0
            }
1249
0
          next=GetNextImageInList(next);
1250
0
        }
1251
0
        if (coalesce_image != (Image *) NULL)
1252
0
          {
1253
0
            status=WriteAnimatedWEBPImage(image_info,coalesce_image,&configure,
1254
0
              &webp_data,exception);
1255
0
            (void) DestroyImageList(coalesce_image);
1256
0
          }
1257
0
        else
1258
0
          status=WriteAnimatedWEBPImage(image_info,image,&configure,&webp_data,
1259
0
            exception);
1260
0
      }
1261
2.82k
    else
1262
2.82k
      {
1263
2.82k
        WebPMemoryWriterInit(&writer);
1264
2.82k
        status=WriteSingleWEBPImage(image_info,image,&configure,&writer,
1265
2.82k
          exception);
1266
2.82k
        if (status == MagickFalse)
1267
243
          WebPMemoryWriterClear(&writer);
1268
2.58k
        else
1269
2.58k
          {
1270
2.58k
            webp_data.bytes=writer.mem;
1271
2.58k
            webp_data.size=writer.size;
1272
2.58k
          }
1273
2.82k
      }
1274
2.82k
    if (status != MagickFalse)
1275
2.58k
      status=WriteWEBPImageProfile(image,&webp_data,exception);
1276
2.82k
    if (status != MagickFalse)
1277
2.58k
      (void) WriteBlob(image,webp_data.size,webp_data.bytes);
1278
2.82k
    WebPDataClear(&webp_data);
1279
2.82k
  }
1280
#else
1281
  WebPMemoryWriterInit(&writer);
1282
  status=WriteSingleWEBPImage(image_info,image,&configure,&writer,exception);
1283
  if (status != MagickFalse)
1284
    (void) WriteBlob(image,writer.size,writer.mem);
1285
  WebPMemoryWriterClear(&writer);
1286
#endif
1287
2.82k
  if (CloseBlob(image) == MagickFalse)
1288
0
    status=MagickFalse;
1289
2.82k
  return(status);
1290
2.82k
}
1291
#endif