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