Coverage Report

Created: 2026-09-03 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginWebP.cpp
Line
Count
Source
1
// ==========================================================
2
// Google WebP Loader & Writer
3
//
4
// Design and implementation by
5
// - Herve Drolon (drolon@infonie.fr)
6
//
7
// This file is part of FreeImage 3
8
//
9
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
10
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
11
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
12
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
13
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
14
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
15
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
16
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
17
// THIS DISCLAIMER.
18
//
19
// Use at your own risk!
20
// ==========================================================
21
22
#include "FreeImage.h"
23
#include "Utilities.h"
24
25
#include "../Metadata/FreeImageTag.h"
26
27
#include "../LibWebP/src/webp/decode.h"
28
#include "../LibWebP/src/webp/encode.h"
29
#include "../LibWebP/src/webp/mux.h"
30
31
// ==========================================================
32
// Plugin Interface
33
// ==========================================================
34
35
static int s_format_id;
36
37
// ----------------------------------------------------------
38
//   Helpers for the load function
39
// ----------------------------------------------------------
40
41
/**
42
Read the whole file into memory
43
*/
44
static BOOL
45
0
ReadFileToWebPData(FreeImageIO *io, fi_handle handle, WebPData * const bitstream) {
46
0
  uint8_t *raw_data = NULL;
47
48
0
  try {
49
    // Read the input file and put it in memory
50
0
    long start_pos = io->tell_proc(handle);
51
0
    io->seek_proc(handle, 0, SEEK_END);
52
0
    size_t file_length = (size_t)(io->tell_proc(handle) - start_pos);
53
0
    io->seek_proc(handle, start_pos, SEEK_SET);
54
0
    raw_data = (uint8_t*)malloc(file_length * sizeof(uint8_t));
55
0
    if(!raw_data) {
56
0
      throw FI_MSG_ERROR_MEMORY;
57
0
    }
58
0
    if(io->read_proc(raw_data, 1, (unsigned)file_length, handle) != file_length) {
59
0
      throw "Error while reading input stream";
60
0
    }
61
    
62
    // copy pointers (must be released later using free)
63
0
    bitstream->bytes = raw_data;
64
0
    bitstream->size = file_length;
65
66
0
    return TRUE;
67
68
0
  } catch(const char *text) {
69
0
    if(raw_data) {
70
0
      free(raw_data);
71
0
    }
72
0
    memset(bitstream, 0, sizeof(WebPData));
73
0
    if(NULL != text) {
74
0
      FreeImage_OutputMessageProc(s_format_id, text);
75
0
    }
76
0
    return FALSE;
77
0
  }
78
0
}
79
80
// ----------------------------------------------------------
81
//   Helpers for the save function
82
// ----------------------------------------------------------
83
84
/**
85
Output function. Should return 1 if writing was successful.
86
data/data_size is the segment of data to write, and 'picture' is for
87
reference (and so one can make use of picture->custom_ptr).
88
*/
89
static int 
90
0
WebP_MemoryWriter(const BYTE *data, size_t data_size, const WebPPicture* const picture) {
91
0
  FIMEMORY *hmem = (FIMEMORY*)picture->custom_ptr;
92
0
  return data_size ? (FreeImage_WriteMemory(data, 1, (unsigned)data_size, hmem) == data_size) : 0;
93
0
}
94
95
// ==========================================================
96
// Plugin Implementation
97
// ==========================================================
98
99
static const char * DLL_CALLCONV
100
2
Format() {
101
2
  return "WebP";
102
2
}
103
104
static const char * DLL_CALLCONV
105
0
Description() {
106
0
  return "Google WebP image format";
107
0
}
108
109
static const char * DLL_CALLCONV
110
0
Extension() {
111
0
  return "webp";
112
0
}
113
114
static const char * DLL_CALLCONV
115
0
RegExpr() {
116
0
  return NULL;
117
0
}
118
119
static const char * DLL_CALLCONV
120
0
MimeType() {
121
0
  return "image/webp";
122
0
}
123
124
static BOOL DLL_CALLCONV
125
20.6k
Validate(FreeImageIO *io, fi_handle handle) {
126
20.6k
  BYTE riff_signature[4] = { 0x52, 0x49, 0x46, 0x46 };
127
20.6k
  BYTE webp_signature[4] = { 0x57, 0x45, 0x42, 0x50 };
128
20.6k
  BYTE signature[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
129
130
20.6k
  io->read_proc(signature, 1, 12, handle);
131
132
20.6k
  if(memcmp(riff_signature, signature, 4) == 0) {
133
31
    if(memcmp(webp_signature, signature + 8, 4) == 0) {
134
1
      return TRUE;
135
1
    }
136
31
  }
137
138
20.6k
  return FALSE;
139
20.6k
}
140
141
static BOOL DLL_CALLCONV
142
0
SupportsExportDepth(int depth) {
143
0
  return (
144
0
    (depth == 24) || 
145
0
    (depth == 32)
146
0
    );
147
0
}
148
149
static BOOL DLL_CALLCONV 
150
0
SupportsExportType(FREE_IMAGE_TYPE type) {
151
0
  return (type == FIT_BITMAP) ? TRUE : FALSE;
152
0
}
153
154
static BOOL DLL_CALLCONV
155
0
SupportsICCProfiles() {
156
0
  return TRUE;
157
0
}
158
159
static BOOL DLL_CALLCONV
160
0
SupportsNoPixels() {
161
0
  return TRUE;
162
0
}
163
164
// ----------------------------------------------------------
165
166
static void * DLL_CALLCONV
167
0
Open(FreeImageIO *io, fi_handle handle, BOOL read) {
168
0
  WebPMux *mux = NULL;
169
0
  int copy_data = 1;  // 1 : copy data into the mux, 0 : keep a link to local data
170
171
0
  if(read) {
172
    // create the MUX object from the input stream
173
0
    WebPData bitstream;
174
    // read the input file and put it in memory
175
0
    if(!ReadFileToWebPData(io, handle, &bitstream)) {
176
0
      return NULL;
177
0
    }
178
    // create the MUX object
179
0
    mux = WebPMuxCreate(&bitstream, copy_data);
180
    // no longer needed since copy_data == 1
181
0
    free((void*)bitstream.bytes);
182
0
    if(mux == NULL) {
183
0
      FreeImage_OutputMessageProc(s_format_id, "Failed to create mux object from file");
184
0
      return NULL;
185
0
    }
186
0
  } else {
187
    // creates an empty mux object
188
0
    mux = WebPMuxNew();
189
0
    if(mux == NULL) {
190
0
      FreeImage_OutputMessageProc(s_format_id, "Failed to create empty mux object");
191
0
      return NULL;
192
0
    }
193
0
  }
194
  
195
0
  return mux;
196
0
}
197
198
static void DLL_CALLCONV
199
0
Close(FreeImageIO *io, fi_handle handle, void *data) {
200
0
  WebPMux *mux = (WebPMux*)data;
201
0
  if(mux != NULL) {
202
    // free the MUX object
203
0
    WebPMuxDelete(mux);
204
0
  }
205
0
}
206
207
// ----------------------------------------------------------
208
209
/**
210
Decode a WebP image and returns a FIBITMAP image
211
@param webp_image Raw WebP image
212
@param flags FreeImage load flags
213
@return Returns a dib if successfull, returns NULL otherwise
214
*/
215
static FIBITMAP *
216
0
DecodeImage(WebPData *webp_image, int flags) {
217
0
  FIBITMAP *dib = NULL;
218
219
0
  const uint8_t* data = webp_image->bytes;  // raw image data
220
0
  const size_t data_size = webp_image->size;  // raw image size
221
222
0
    VP8StatusCode webp_status = VP8_STATUS_OK;
223
224
0
  BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
225
226
  // Main object storing the configuration for advanced decoding
227
0
  WebPDecoderConfig decoder_config;
228
  // Output buffer
229
0
  WebPDecBuffer* const output_buffer = &decoder_config.output;
230
  // Features gathered from the bitstream
231
0
  WebPBitstreamFeatures* const bitstream = &decoder_config.input;
232
233
0
  try {
234
    // Initialize the configuration as empty
235
    // This function must always be called first, unless WebPGetFeatures() is to be called
236
0
    if(!WebPInitDecoderConfig(&decoder_config)) {
237
0
      throw "Library version mismatch";
238
0
    }
239
240
    // Retrieve features from the bitstream
241
    // The bitstream structure is filled with information gathered from the bitstream
242
0
    webp_status = WebPGetFeatures(data, data_size, bitstream);
243
0
    if(webp_status != VP8_STATUS_OK) {
244
0
      throw FI_MSG_ERROR_PARSING;
245
0
    }
246
247
    // Allocate output dib
248
249
0
    unsigned bpp = bitstream->has_alpha ? 32 : 24; 
250
0
    unsigned width = (unsigned)bitstream->width;
251
0
    unsigned height = (unsigned)bitstream->height;
252
253
0
    dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
254
0
    if(!dib) {
255
0
      throw FI_MSG_ERROR_DIB_MEMORY;
256
0
    }
257
258
0
    if(header_only) {
259
0
      WebPFreeDecBuffer(output_buffer);
260
0
      return dib;
261
0
    }
262
263
    // --- Set decoding options ---
264
265
    // use multi-threaded decoding
266
0
    decoder_config.options.use_threads = 1;
267
    // set output color space
268
0
    output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
269
270
    // ---
271
272
    // decode the input stream, taking 'config' into account. 
273
    
274
0
    webp_status = WebPDecode(data, data_size, &decoder_config);
275
0
    if(webp_status != VP8_STATUS_OK) {
276
0
      throw FI_MSG_ERROR_PARSING;
277
0
    }
278
279
    // fill the dib with the decoded data
280
281
0
    const BYTE *src_bitmap = output_buffer->u.RGBA.rgba;
282
0
    const unsigned src_pitch = (unsigned)output_buffer->u.RGBA.stride;
283
284
0
    switch(bpp) {
285
0
      case 24:
286
0
        for(unsigned y = 0; y < height; y++) {
287
0
          const BYTE *src_bits = src_bitmap + y * src_pitch;            
288
0
          BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, height-1-y);
289
0
          for(unsigned x = 0; x < width; x++) {
290
0
            dst_bits[FI_RGBA_BLUE] = src_bits[0];  // B
291
0
            dst_bits[FI_RGBA_GREEN] = src_bits[1];  // G
292
0
            dst_bits[FI_RGBA_RED] = src_bits[2];  // R
293
0
            src_bits += 3;
294
0
            dst_bits += 3;
295
0
          }
296
0
        }
297
0
        break;
298
0
      case 32:
299
0
        for(unsigned y = 0; y < height; y++) {
300
0
          const BYTE *src_bits = src_bitmap + y * src_pitch;            
301
0
          BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, height-1-y);
302
0
          for(unsigned x = 0; x < width; x++) {
303
0
            dst_bits[FI_RGBA_BLUE] = src_bits[0];  // B
304
0
            dst_bits[FI_RGBA_GREEN] = src_bits[1];  // G
305
0
            dst_bits[FI_RGBA_RED] = src_bits[2];  // R
306
0
            dst_bits[FI_RGBA_ALPHA] = src_bits[3];  // A
307
0
            src_bits += 4;
308
0
            dst_bits += 4;
309
0
          }
310
0
        }
311
0
        break;
312
0
    }
313
314
    // Free the decoder
315
0
    WebPFreeDecBuffer(output_buffer);
316
317
0
    return dib;
318
319
0
  } catch (const char *text) {
320
0
    if(dib) {
321
0
      FreeImage_Unload(dib);
322
0
    }
323
0
    WebPFreeDecBuffer(output_buffer);
324
325
0
    if(NULL != text) {
326
0
      FreeImage_OutputMessageProc(s_format_id, text);
327
0
    }
328
329
0
    return NULL;
330
0
  }
331
0
}
332
333
static FIBITMAP * DLL_CALLCONV
334
0
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
335
0
  WebPMux *mux = NULL;
336
0
  WebPMuxFrameInfo webp_frame = { 0 };  // raw image
337
0
  WebPData color_profile; // ICC raw data
338
0
  WebPData xmp_metadata;  // XMP raw data
339
0
  WebPData exif_metadata; // EXIF raw data
340
0
  FIBITMAP *dib = NULL;
341
0
  WebPMuxError error_status;
342
343
0
  if(!handle) {
344
0
    return NULL;
345
0
  }
346
347
0
  try {
348
    // get the MUX object
349
0
    mux = (WebPMux*)data;
350
0
    if(!mux) {
351
0
      throw (1);
352
0
    }
353
    
354
    // gets the feature flags from the mux object
355
0
    uint32_t webp_flags = 0;
356
0
    error_status = WebPMuxGetFeatures(mux, &webp_flags);
357
0
    if(error_status != WEBP_MUX_OK) {
358
0
      throw (1);
359
0
    }
360
361
    // get image data
362
0
    error_status = WebPMuxGetFrame(mux, 1, &webp_frame);
363
364
0
    if(error_status == WEBP_MUX_OK) {
365
      // decode the data (can be limited to the header if flags uses FIF_LOAD_NOPIXELS)
366
0
      dib = DecodeImage(&webp_frame.bitstream, flags);
367
0
      if(!dib) {
368
0
        throw (1);
369
0
      }
370
      
371
      // get ICC profile
372
0
      if(webp_flags & ICCP_FLAG) {
373
0
        error_status = WebPMuxGetChunk(mux, "ICCP", &color_profile);
374
0
        if(error_status == WEBP_MUX_OK) {
375
0
          FreeImage_CreateICCProfile(dib, (void*)color_profile.bytes, (long)color_profile.size);
376
0
        }
377
0
      }
378
379
      // get XMP metadata
380
0
      if(webp_flags & XMP_FLAG) {
381
0
        error_status = WebPMuxGetChunk(mux, "XMP ", &xmp_metadata);
382
0
        if(error_status == WEBP_MUX_OK) {
383
          // create a tag
384
0
          FITAG *tag = FreeImage_CreateTag();
385
0
          if(tag) {
386
0
            FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
387
0
            FreeImage_SetTagLength(tag, (DWORD)xmp_metadata.size);
388
0
            FreeImage_SetTagCount(tag, (DWORD)xmp_metadata.size);
389
0
            FreeImage_SetTagType(tag, FIDT_ASCII);
390
0
            FreeImage_SetTagValue(tag, xmp_metadata.bytes);
391
            
392
            // store the tag
393
0
            FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
394
395
            // destroy the tag
396
0
            FreeImage_DeleteTag(tag);
397
0
          }
398
0
        }
399
0
      }
400
401
      // get Exif metadata
402
0
      if(webp_flags & EXIF_FLAG) {
403
0
        error_status = WebPMuxGetChunk(mux, "EXIF", &exif_metadata);
404
0
        if(error_status == WEBP_MUX_OK) {
405
          // read the Exif raw data as a blob
406
0
          jpeg_read_exif_profile_raw(dib, exif_metadata.bytes, (unsigned)exif_metadata.size);
407
          // read and decode the Exif data
408
0
          jpeg_read_exif_profile(dib, exif_metadata.bytes, (unsigned)exif_metadata.size);
409
0
        }
410
0
      }
411
0
    }
412
413
0
    WebPDataClear(&webp_frame.bitstream);
414
415
0
    return dib;
416
417
0
  } catch(int) {
418
0
    WebPDataClear(&webp_frame.bitstream);
419
0
    return NULL;
420
0
  }
421
0
}
422
423
// --------------------------------------------------------------------------
424
425
/**
426
Encode a FIBITMAP to a WebP image
427
@param hmem Memory output stream, containing on return the encoded image
428
@param dib The FIBITMAP to encode
429
@param flags FreeImage save flags
430
@return Returns TRUE if successfull, returns FALSE otherwise
431
*/
432
static BOOL
433
0
EncodeImage(FIMEMORY *hmem, FIBITMAP *dib, int flags) {
434
0
  WebPPicture picture;  // Input buffer
435
0
  WebPConfig config;    // Coding parameters
436
437
0
  BOOL bIsFlipped = FALSE;
438
439
0
  try {
440
0
    const unsigned width = FreeImage_GetWidth(dib);
441
0
    const unsigned height = FreeImage_GetHeight(dib);
442
0
    const unsigned bpp = FreeImage_GetBPP(dib);
443
0
    const unsigned pitch = FreeImage_GetPitch(dib);
444
445
    // check image type
446
0
    FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
447
448
0
    if( !((image_type == FIT_BITMAP) && ((bpp == 24) || (bpp == 32))) )  {
449
0
      throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
450
0
    }
451
452
    // check format limits
453
0
    if(MAX(width, height) > WEBP_MAX_DIMENSION) {
454
0
      FreeImage_OutputMessageProc(s_format_id, "Unsupported image size: width x height = %d x %d", width, height);
455
0
      return FALSE;
456
0
    }
457
458
    // Initialize output I/O
459
0
    if(WebPPictureInit(&picture) == 1) {
460
0
      picture.writer = WebP_MemoryWriter;
461
0
      picture.custom_ptr = hmem;
462
0
      picture.width = (int)width;
463
0
      picture.height = (int)height;
464
0
    } else {
465
0
      throw "Couldn't initialize WebPPicture";
466
0
    }
467
468
    // --- Set encoding parameters ---
469
470
    // Initialize encoding parameters to default values
471
0
    WebPConfigInit(&config);
472
473
    // quality/speed trade-off (0=fast, 6=slower-better)
474
0
    config.method = 6;
475
476
0
    if((flags & WEBP_LOSSLESS) == WEBP_LOSSLESS) {
477
      // lossless encoding
478
0
      config.lossless = 1;
479
0
      picture.use_argb = 1;
480
0
    } else if((flags & 0x7F) > 0) {
481
      // lossy encoding
482
0
      config.lossless = 0;
483
      // quality is between 1 (smallest file) and 100 (biggest) - default to 75
484
0
      config.quality = (float)(flags & 0x7F);
485
0
      if(config.quality > 100) {
486
0
        config.quality = 100;
487
0
      }
488
0
    }
489
490
    // validate encoding parameters
491
0
    if(WebPValidateConfig(&config) == 0) {
492
0
      throw "Failed to initialize encoder";
493
0
    }
494
495
    // --- Perform encoding ---
496
    
497
    // Invert dib scanlines
498
0
    bIsFlipped = FreeImage_FlipVertical(dib);
499
500
501
    // convert dib buffer to output stream
502
503
0
    const BYTE *bits = FreeImage_GetBits(dib);
504
505
0
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
506
0
    switch(bpp) {
507
0
      case 24:
508
0
        WebPPictureImportBGR(&picture, bits, pitch);
509
0
        break;
510
0
      case 32:
511
0
        WebPPictureImportBGRA(&picture, bits, pitch);
512
0
        break;
513
0
    }
514
#else
515
    switch(bpp) {
516
      case 24:
517
        WebPPictureImportRGB(&picture, bits, pitch);
518
        break;
519
      case 32:
520
        WebPPictureImportRGBA(&picture, bits, pitch);
521
        break;
522
    }
523
524
#endif // FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
525
526
0
    if(!WebPEncode(&config, &picture)) {
527
0
      throw "Failed to encode image";
528
0
    }
529
530
0
    WebPPictureFree(&picture);
531
532
0
    if(bIsFlipped) {
533
      // invert dib scanlines
534
0
      FreeImage_FlipVertical(dib);
535
0
    }
536
537
0
    return TRUE;
538
539
0
  } catch (const char* text) {
540
541
0
    WebPPictureFree(&picture);
542
543
0
    if(bIsFlipped) {
544
      // invert dib scanlines
545
0
      FreeImage_FlipVertical(dib);
546
0
    }
547
548
0
    if(NULL != text) {
549
0
      FreeImage_OutputMessageProc(s_format_id, text);
550
0
    }
551
0
  }
552
553
0
  return FALSE;
554
0
}
555
556
static BOOL DLL_CALLCONV
557
0
Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
558
0
  WebPMux *mux = NULL;
559
0
  FIMEMORY *hmem = NULL;
560
0
  WebPData webp_image;
561
0
  WebPData output_data = { 0 };
562
0
  WebPMuxError error_status;
563
564
0
  int copy_data = 1;  // 1 : copy data into the mux, 0 : keep a link to local data
565
566
0
  if(!dib || !handle || !data) {
567
0
    return FALSE;
568
0
  }
569
570
0
  try {
571
572
    // get the MUX object
573
0
    mux = (WebPMux*)data;
574
0
    if(!mux) {
575
0
      return FALSE;
576
0
    }
577
578
    // --- prepare image data ---
579
580
    // encode image as a WebP blob
581
0
    hmem = FreeImage_OpenMemory();
582
0
    if(!hmem || !EncodeImage(hmem, dib, flags)) {
583
0
      throw (1);
584
0
    }
585
    // store the blob into the mux
586
0
    BYTE *data = NULL;
587
0
    DWORD data_size = 0;
588
0
    FreeImage_AcquireMemory(hmem, &data, &data_size);
589
0
    webp_image.bytes = data;
590
0
    webp_image.size = data_size;
591
0
    error_status = WebPMuxSetImage(mux, &webp_image, copy_data);
592
    // no longer needed since copy_data == 1
593
0
    FreeImage_CloseMemory(hmem);
594
0
    hmem = NULL;
595
0
    if(error_status != WEBP_MUX_OK) {
596
0
      throw (1);
597
0
    }
598
599
    // --- set metadata ---
600
    
601
    // set ICC color profile
602
0
    {
603
0
      FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib);
604
0
      if (iccProfile->size && iccProfile->data) {
605
0
        WebPData icc_profile;
606
0
        icc_profile.bytes = (uint8_t*)iccProfile->data;
607
0
        icc_profile.size = (size_t)iccProfile->size;
608
0
        error_status = WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
609
0
        if(error_status != WEBP_MUX_OK) {
610
0
          throw (1);
611
0
        }
612
0
      }
613
0
    }
614
615
    // set XMP metadata
616
0
    {
617
0
      FITAG *tag = NULL;
618
0
      if(FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag)) {
619
0
        WebPData xmp_profile;
620
0
        xmp_profile.bytes = (uint8_t*)FreeImage_GetTagValue(tag);
621
0
        xmp_profile.size = (size_t)FreeImage_GetTagLength(tag);
622
0
        error_status = WebPMuxSetChunk(mux, "XMP ", &xmp_profile, copy_data);
623
0
        if(error_status != WEBP_MUX_OK) {
624
0
          throw (1);
625
0
        }
626
0
      }
627
0
    }
628
629
    // set Exif metadata
630
0
    {
631
0
      FITAG *tag = NULL;
632
0
      if(FreeImage_GetMetadata(FIMD_EXIF_RAW, dib, g_TagLib_ExifRawFieldName, &tag)) {
633
0
        WebPData exif_profile;
634
0
        exif_profile.bytes = (uint8_t*)FreeImage_GetTagValue(tag);
635
0
        exif_profile.size = (size_t)FreeImage_GetTagLength(tag);
636
0
        error_status = WebPMuxSetChunk(mux, "EXIF", &exif_profile, copy_data);
637
0
        if(error_status != WEBP_MUX_OK) {
638
0
          throw (1);
639
0
        }
640
0
      }
641
0
    }
642
    
643
    // get data from mux in WebP RIFF format
644
0
    error_status = WebPMuxAssemble(mux, &output_data);
645
0
    if(error_status != WEBP_MUX_OK) {
646
0
      FreeImage_OutputMessageProc(s_format_id, "Failed to create webp output file");
647
0
      throw (1);
648
0
    }
649
650
    // write the file to the output stream
651
0
    if(io->write_proc((void*)output_data.bytes, 1, (unsigned)output_data.size, handle) != output_data.size) {
652
0
      FreeImage_OutputMessageProc(s_format_id, "Failed to write webp output file");
653
0
      throw (1);
654
0
    }
655
656
    // free WebP output file
657
0
    WebPDataClear(&output_data);
658
659
0
    return TRUE;
660
661
0
  } catch(int) {
662
0
    if(hmem) {
663
0
      FreeImage_CloseMemory(hmem);
664
0
    }
665
    
666
0
    WebPDataClear(&output_data);
667
668
0
    return FALSE;
669
0
  }
670
0
}
671
672
// ==========================================================
673
//   Init
674
// ==========================================================
675
676
void DLL_CALLCONV
677
2
InitWEBP(Plugin *plugin, int format_id) {
678
2
  s_format_id = format_id;
679
680
2
  plugin->format_proc = Format;
681
2
  plugin->description_proc = Description;
682
2
  plugin->extension_proc = Extension;
683
2
  plugin->regexpr_proc = RegExpr;
684
2
  plugin->open_proc = Open;
685
2
  plugin->close_proc = Close;
686
2
  plugin->pagecount_proc = NULL;
687
  plugin->pagecapability_proc = NULL;
688
2
  plugin->load_proc = Load;
689
2
  plugin->save_proc = Save;
690
2
  plugin->validate_proc = Validate;
691
2
  plugin->mime_proc = MimeType;
692
2
  plugin->supports_export_bpp_proc = SupportsExportDepth;
693
2
  plugin->supports_export_type_proc = SupportsExportType;
694
2
  plugin->supports_icc_profiles_proc = SupportsICCProfiles;
695
2
  plugin->supports_no_pixels_proc = SupportsNoPixels;
696
2
}
697