Coverage Report

Created: 2026-11-12 07:35

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