/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 | | |