Coverage Report

Created: 2026-02-14 07:11

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