/[projet1]/public/pc/shared_libraries/freeimage/v3.12.0/Source/FreeImage/PluginPNG.cpp
Defence Force logotype

Contents of /public/pc/shared_libraries/freeimage/v3.12.0/Source/FreeImage/PluginPNG.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 126 - (show annotations)
Mon Jul 13 12:20:10 2009 UTC (10 years, 7 months ago) by dbug
File size: 27349 byte(s)
Added some shared libraries (UnitTest++, and FreeImage) to avoid having every single project brings its own libraries.
Ideally people should add them in a way we can upgrade versions without breaking things:
-> public/pc/shared_libraries/library_name/library_version/actuall_content
1 // ==========================================================
2 // PNG Loader and Writer
3 //
4 // Design and implementation by
5 // - Floris van den Berg (flvdberg@wxs.nl)
6 // - Herve Drolon (drolon@infonie.fr)
7 // - Detlev Vendt (detlev.vendt@brillit.de)
8 // - Aaron Shumate (trek@startreker.com)
9 //
10 // This file is part of FreeImage 3
11 //
12 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
13 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
14 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
15 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
16 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
17 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
18 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
19 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
20 // THIS DISCLAIMER.
21 //
22 // Use at your own risk!
23 // ==========================================================
24
25 #ifdef _MSC_VER
26 #pragma warning (disable : 4786) // identifier was truncated to 'number' characters
27 #endif
28
29 #include "FreeImage.h"
30 #include "Utilities.h"
31
32 #include "../Metadata/FreeImageTag.h"
33
34 // ----------------------------------------------------------
35
36 #define PNG_BYTES_TO_CHECK 8
37
38 // ----------------------------------------------------------
39
40 #include "../LibPNG/png.h"
41
42 // ----------------------------------------------------------
43
44 typedef struct {
45 FreeImageIO *s_io;
46 fi_handle s_handle;
47 } fi_ioStructure, *pfi_ioStructure;
48
49 /////////////////////////////////////////////////////////////////////////////
50 // libpng interface
51 //
52
53 static void
54 _ReadProc(png_structp png_ptr, unsigned char *data, png_size_t size) {
55 pfi_ioStructure pfio = (pfi_ioStructure)png_get_io_ptr(png_ptr);
56 unsigned n = pfio->s_io->read_proc(data, (unsigned int)size, 1, pfio->s_handle);
57 if(size && (n == 0)) {
58 throw "Read error: invalid or corrupted PNG file";
59 }
60 }
61
62 static void
63 _WriteProc(png_structp png_ptr, unsigned char *data, png_size_t size) {
64 pfi_ioStructure pfio = (pfi_ioStructure)png_get_io_ptr(png_ptr);
65 pfio->s_io->write_proc(data, (unsigned int)size, 1, pfio->s_handle);
66 }
67
68 static void
69 _FlushProc(png_structp png_ptr) {
70 // empty flush implementation
71 }
72
73 static void
74 error_handler(png_structp png_ptr, const char *error) {
75 throw error;
76 }
77
78 // in FreeImage warnings disabled
79
80 static void
81 warning_handler(png_structp png_ptr, const char *warning) {
82 }
83
84 // ==========================================================
85 // Metadata routines
86 // ==========================================================
87
88 static BOOL
89 ReadMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) {
90 // XMP keyword
91 char *g_png_xmp_keyword = "XML:com.adobe.xmp";
92
93 FITAG *tag = NULL;
94 png_textp text_ptr = NULL;
95 int num_text = 0;
96
97 // iTXt/tEXt/zTXt chuncks
98 if(png_get_text(png_ptr, info_ptr, &text_ptr, &num_text) > 0) {
99 for(int i = 0; i < num_text; i++) {
100 // create a tag
101 tag = FreeImage_CreateTag();
102 if(!tag) return FALSE;
103
104 DWORD tag_length = (DWORD) MAX(text_ptr[i].text_length, text_ptr[i].itxt_length);
105
106 FreeImage_SetTagLength(tag, tag_length);
107 FreeImage_SetTagCount(tag, tag_length);
108 FreeImage_SetTagType(tag, FIDT_ASCII);
109 FreeImage_SetTagValue(tag, text_ptr[i].text);
110
111 if(strcmp(text_ptr[i].key, g_png_xmp_keyword) == 0) {
112 // store the tag as XMP
113 FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
114 FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
115 } else {
116 // store the tag as a comment
117 FreeImage_SetTagKey(tag, text_ptr[i].key);
118 FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
119 }
120
121 // destroy the tag
122 FreeImage_DeleteTag(tag);
123 }
124 }
125
126 return TRUE;
127 }
128
129 static BOOL
130 WriteMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) {
131 // XMP keyword
132 char *g_png_xmp_keyword = "XML:com.adobe.xmp";
133
134 FITAG *tag = NULL;
135 FIMETADATA *mdhandle = NULL;
136 BOOL bResult = TRUE;
137
138 png_text text_metadata;
139
140 // set the 'Comments' metadata as iTXt chuncks
141
142 mdhandle = FreeImage_FindFirstMetadata(FIMD_COMMENTS, dib, &tag);
143
144 if(mdhandle) {
145 do {
146 memset(&text_metadata, 0, sizeof(png_text));
147 text_metadata.compression = 1; // iTXt, none
148 text_metadata.key = (char*)FreeImage_GetTagKey(tag); // keyword, 1-79 character description of "text"
149 text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "")
150 text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string
151 text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string
152 text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer
153 text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer
154
155 // set the tag
156 png_set_text(png_ptr, info_ptr, &text_metadata, 1);
157
158 } while(FreeImage_FindNextMetadata(mdhandle, &tag));
159
160 FreeImage_FindCloseMetadata(mdhandle);
161 bResult &= TRUE;
162 }
163
164 // set the 'XMP' metadata as iTXt chuncks
165 tag = NULL;
166 FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag);
167 if(tag && FreeImage_GetTagLength(tag)) {
168 memset(&text_metadata, 0, sizeof(png_text));
169 text_metadata.compression = 1; // iTXt, none
170 text_metadata.key = g_png_xmp_keyword; // keyword, 1-79 character description of "text"
171 text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "")
172 text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string
173 text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string
174 text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer
175 text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer
176
177 // set the tag
178 png_set_text(png_ptr, info_ptr, &text_metadata, 1);
179 bResult &= TRUE;
180 }
181
182 return bResult;
183 }
184
185 // ==========================================================
186 // Plugin Interface
187 // ==========================================================
188
189 static int s_format_id;
190
191 // ==========================================================
192 // Plugin Implementation
193 // ==========================================================
194
195 static const char * DLL_CALLCONV
196 Format() {
197 return "PNG";
198 }
199
200 static const char * DLL_CALLCONV
201 Description() {
202 return "Portable Network Graphics";
203 }
204
205 static const char * DLL_CALLCONV
206 Extension() {
207 return "png";
208 }
209
210 static const char * DLL_CALLCONV
211 RegExpr() {
212 return "^.PNG\r";
213 }
214
215 static const char * DLL_CALLCONV
216 MimeType() {
217 return "image/png";
218 }
219
220 static BOOL DLL_CALLCONV
221 Validate(FreeImageIO *io, fi_handle handle) {
222 BYTE png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
223 BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
224
225 io->read_proc(&signature, 1, 8, handle);
226
227 return (memcmp(png_signature, signature, 8) == 0);
228 }
229
230 static BOOL DLL_CALLCONV
231 SupportsExportDepth(int depth) {
232 return (
233 (depth == 1) ||
234 (depth == 4) ||
235 (depth == 8) ||
236 (depth == 24) ||
237 (depth == 32)
238 );
239 }
240
241 static BOOL DLL_CALLCONV
242 SupportsExportType(FREE_IMAGE_TYPE type) {
243 return (
244 (type == FIT_BITMAP) ||
245 (type == FIT_UINT16) ||
246 (type == FIT_RGB16) ||
247 (type == FIT_RGBA16)
248 );
249 }
250
251 static BOOL DLL_CALLCONV
252 SupportsICCProfiles() {
253 return TRUE;
254 }
255
256 // ----------------------------------------------------------
257
258 static FIBITMAP * DLL_CALLCONV
259 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
260 png_structp png_ptr = NULL;
261 png_infop info_ptr;
262 png_uint_32 width, height;
263 png_colorp png_palette = NULL;
264 int color_type, palette_entries = 0;
265 int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels
266
267 FIBITMAP *dib = NULL;
268 RGBQUAD *palette = NULL; // pointer to dib palette
269 png_bytepp row_pointers = NULL;
270 int i;
271
272 fi_ioStructure fio;
273 fio.s_handle = handle;
274 fio.s_io = io;
275
276 if (handle) {
277 try {
278 // check to see if the file is in fact a PNG file
279
280 BYTE png_check[PNG_BYTES_TO_CHECK];
281
282 io->read_proc(png_check, PNG_BYTES_TO_CHECK, 1, handle);
283
284 if (png_sig_cmp(png_check, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0)
285 return NULL; // Bad signature
286
287 // create the chunk manage structure
288
289 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler);
290
291 if (!png_ptr)
292 return NULL;
293
294 // create the info structure
295
296 info_ptr = png_create_info_struct(png_ptr);
297
298 if (!info_ptr) {
299 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
300 return NULL;
301 }
302
303 // init the IO
304
305 png_set_read_fn(png_ptr, &fio, _ReadProc);
306
307 if (setjmp(png_jmpbuf(png_ptr))) {
308 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
309 return NULL;
310 }
311
312 // because we have already read the signature...
313
314 png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);
315
316 // read the IHDR chunk
317
318 png_read_info(png_ptr, info_ptr);
319 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
320 pixel_depth = info_ptr->pixel_depth;
321
322 // get image data type (assume standard image type)
323
324 FREE_IMAGE_TYPE image_type = FIT_BITMAP;
325 if (bit_depth == 16) {
326 if ((pixel_depth == 16) && (color_type == PNG_COLOR_TYPE_GRAY)) {
327 image_type = FIT_UINT16;
328 }
329 else if ((pixel_depth == 48) && (color_type == PNG_COLOR_TYPE_RGB)) {
330 image_type = FIT_RGB16;
331 }
332 else if ((pixel_depth == 64) && (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) {
333 image_type = FIT_RGBA16;
334 } else {
335 // tell libpng to strip 16 bit/color files down to 8 bits/color
336 png_set_strip_16(png_ptr);
337 bit_depth = 8;
338 }
339 }
340
341 #ifndef FREEIMAGE_BIGENDIAN
342 if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) {
343 // turn on 16 bit byte swapping
344 png_set_swap(png_ptr);
345 }
346 #endif
347
348 // set some additional flags
349
350 switch(color_type) {
351 case PNG_COLOR_TYPE_RGB:
352 case PNG_COLOR_TYPE_RGB_ALPHA:
353 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
354 // flip the RGB pixels to BGR (or RGBA to BGRA)
355
356 if(image_type == FIT_BITMAP)
357 png_set_bgr(png_ptr);
358 #endif
359 break;
360
361 case PNG_COLOR_TYPE_PALETTE:
362 // expand palette images to the full 8 bits from 2 bits/pixel
363
364 if (pixel_depth == 2) {
365 png_set_packing(png_ptr);
366 pixel_depth = 8;
367 }
368
369 break;
370
371 case PNG_COLOR_TYPE_GRAY:
372 // expand grayscale images to the full 8 bits from 2 bits/pixel
373
374 if (pixel_depth == 2) {
375 png_set_expand(png_ptr);
376 pixel_depth = 8;
377 }
378
379 break;
380
381 case PNG_COLOR_TYPE_GRAY_ALPHA:
382 // expand 8-bit greyscale + 8-bit alpha to 32-bit
383
384 png_set_gray_to_rgb(png_ptr);
385 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
386 // flip the RGBA pixels to BGRA
387
388 png_set_bgr(png_ptr);
389 #endif
390 pixel_depth = 32;
391
392 break;
393
394 default:
395 throw "PNG format not supported";
396 }
397
398 // Get the background color to draw transparent and alpha images over.
399 // Note that even if the PNG file supplies a background, you are not required to
400 // use it - you should use the (solid) application background if it has one.
401
402 png_color_16p image_background = NULL;
403 RGBQUAD rgbBkColor;
404
405 if (png_get_bKGD(png_ptr, info_ptr, &image_background)) {
406 rgbBkColor.rgbRed = (BYTE)image_background->red;
407 rgbBkColor.rgbGreen = (BYTE)image_background->green;
408 rgbBkColor.rgbBlue = (BYTE)image_background->blue;
409 rgbBkColor.rgbReserved = 0;
410 }
411
412 // unlike the example in the libpng documentation, we have *no* idea where
413 // this file may have come from--so if it doesn't have a file gamma, don't
414 // do any correction ("do no harm")
415
416 double gamma = 0;
417 double screen_gamma = 2.2;
418
419 if (png_get_gAMA(png_ptr, info_ptr, &gamma) && ( flags & PNG_IGNOREGAMMA ) != PNG_IGNOREGAMMA)
420 png_set_gamma(png_ptr, screen_gamma, gamma);
421
422 // all transformations have been registered; now update info_ptr data
423
424 png_read_update_info(png_ptr, info_ptr);
425
426 // color type may have changed, due to our transformations
427
428 color_type = png_get_color_type(png_ptr,info_ptr);
429
430 // create a DIB and write the bitmap header
431 // set up the DIB palette, if needed
432
433 switch (color_type) {
434 case PNG_COLOR_TYPE_RGB:
435 png_set_invert_alpha(png_ptr);
436
437 if(image_type == FIT_BITMAP) {
438 dib = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
439 } else {
440 dib = FreeImage_AllocateT(image_type, width, height, pixel_depth);
441 }
442 break;
443
444 case PNG_COLOR_TYPE_RGB_ALPHA:
445 if(image_type == FIT_BITMAP) {
446 dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
447 } else {
448 dib = FreeImage_AllocateT(image_type, width, height, pixel_depth);
449 }
450 break;
451
452 case PNG_COLOR_TYPE_PALETTE:
453 dib = FreeImage_Allocate(width, height, pixel_depth);
454
455 png_get_PLTE(png_ptr,info_ptr, &png_palette,&palette_entries);
456
457 palette = FreeImage_GetPalette(dib);
458
459 // store the palette
460
461 for (i = 0; i < palette_entries; i++) {
462 palette[i].rgbRed = png_palette[i].red;
463 palette[i].rgbGreen = png_palette[i].green;
464 palette[i].rgbBlue = png_palette[i].blue;
465 }
466
467 // store the transparency table
468
469 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
470 int num_trans = 0;
471 png_bytep trans = NULL;
472 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
473 FreeImage_SetTransparencyTable(dib, (BYTE *)trans, num_trans);
474 }
475
476 break;
477
478 case PNG_COLOR_TYPE_GRAY:
479 dib = FreeImage_AllocateT(image_type, width, height, pixel_depth);
480
481 if(pixel_depth <= 8) {
482 palette = FreeImage_GetPalette(dib);
483 palette_entries = 1 << pixel_depth;
484
485 for (i = 0; i < palette_entries; i++) {
486 palette[i].rgbRed =
487 palette[i].rgbGreen =
488 palette[i].rgbBlue = (BYTE)((i * 255) / (palette_entries - 1));
489 }
490 }
491
492 // store the transparency table
493
494 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
495 png_color_16p trans_values = NULL;
496 png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_values);
497 if(trans_values) {
498 if (trans_values->gray < palette_entries) {
499 BYTE table[256];
500 memset(table, 0xFF, palette_entries);
501 table[trans_values->gray] = 0;
502 FreeImage_SetTransparencyTable(dib, table, palette_entries);
503 }
504 }
505 }
506
507 break;
508
509 default:
510 throw "PNG format not supported";
511 }
512
513 // store the background color
514 if(image_background) {
515 FreeImage_SetBackgroundColor(dib, &rgbBkColor);
516 }
517
518 // get physical resolution
519
520 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) {
521 png_uint_32 res_x, res_y;
522
523 // we'll overload this var and use 0 to mean no phys data,
524 // since if it's not in meters we can't use it anyway
525
526 int res_unit_type = 0;
527
528 png_get_pHYs(png_ptr,info_ptr,&res_x,&res_y,&res_unit_type);
529
530 if (res_unit_type == 1) {
531 FreeImage_SetDotsPerMeterX(dib, res_x);
532 FreeImage_SetDotsPerMeterY(dib, res_y);
533 }
534 }
535
536 // get possible ICC profile
537
538 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
539 png_charp profile_name = NULL;
540 png_charp profile_data = NULL;
541 png_uint_32 profile_length = 0;
542 int compression_type;
543
544 png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_length);
545
546 // copy ICC profile data (must be done after FreeImage_Allocate)
547
548 FreeImage_CreateICCProfile(dib, profile_data, profile_length);
549 }
550
551
552 // set the individual row_pointers to point at the correct offsets
553
554 row_pointers = (png_bytepp)malloc(height * sizeof(png_bytep));
555
556 if (!row_pointers) {
557 if (palette)
558 png_free(png_ptr, palette);
559
560 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
561
562 FreeImage_Unload(dib);
563 return NULL;
564 }
565
566 // read in the bitmap bits via the pointer table
567
568 for (png_uint_32 k = 0; k < height; k++)
569 row_pointers[height - 1 - k] = FreeImage_GetScanLine(dib, k);
570
571 png_read_image(png_ptr, row_pointers);
572
573 // check if the bitmap contains transparency, if so enable it in the header
574
575 if (FreeImage_GetBPP(dib) == 32)
576 if (FreeImage_GetColorType(dib) == FIC_RGBALPHA)
577 FreeImage_SetTransparent(dib, TRUE);
578 else
579 FreeImage_SetTransparent(dib, FALSE);
580
581 // cleanup
582
583 if (row_pointers) {
584 free(row_pointers);
585 row_pointers = NULL;
586 }
587
588 // read the rest of the file, getting any additional chunks in info_ptr
589
590 png_read_end(png_ptr, info_ptr);
591
592 // get possible metadata (it can be located both before and after the image data)
593
594 ReadMetadata(png_ptr, info_ptr, dib);
595
596 if (png_ptr) {
597 // clean up after the read, and free any memory allocated - REQUIRED
598 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
599 }
600
601 return dib;
602 } catch (const char *text) {
603 if (png_ptr)
604 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
605
606 if (row_pointers)
607 free(row_pointers);
608
609 if (dib)
610 FreeImage_Unload(dib);
611
612 FreeImage_OutputMessageProc(s_format_id, text);
613
614 return NULL;
615 }
616 }
617
618 return NULL;
619 }
620
621 static BOOL DLL_CALLCONV
622 Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
623 png_structp png_ptr;
624 png_infop info_ptr;
625 png_colorp palette = NULL;
626 png_uint_32 width, height;
627 BOOL has_alpha_channel = FALSE;
628
629 RGBQUAD *pal; // pointer to dib palette
630 int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels
631 int palette_entries;
632 int interlace_type;
633
634 fi_ioStructure fio;
635 fio.s_handle = handle;
636 fio.s_io = io;
637
638 if ((dib) && (handle)) {
639 try {
640 // create the chunk manage structure
641
642 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler);
643
644 if (!png_ptr) {
645 return FALSE;
646 }
647
648 // allocate/initialize the image information data.
649
650 info_ptr = png_create_info_struct(png_ptr);
651
652 if (!info_ptr) {
653 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
654 return FALSE;
655 }
656
657 // Set error handling. REQUIRED if you aren't supplying your own
658 // error handling functions in the png_create_write_struct() call.
659
660 if (setjmp(png_jmpbuf(png_ptr))) {
661 // if we get here, we had a problem reading the file
662
663 png_destroy_write_struct(&png_ptr, &info_ptr);
664
665 return FALSE;
666 }
667
668 // init the IO
669
670 png_set_write_fn(png_ptr, &fio, _WriteProc, _FlushProc);
671
672 // set physical resolution
673
674 png_uint_32 res_x = (png_uint_32)FreeImage_GetDotsPerMeterX(dib);
675 png_uint_32 res_y = (png_uint_32)FreeImage_GetDotsPerMeterY(dib);
676
677 if ((res_x > 0) && (res_y > 0)) {
678 png_set_pHYs(png_ptr, info_ptr, res_x, res_y, 1);
679 }
680
681 // Set the image information here. Width and height are up to 2^31,
682 // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
683 // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
684 // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
685 // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
686 // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
687 // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
688
689 width = FreeImage_GetWidth(dib);
690 height = FreeImage_GetHeight(dib);
691 pixel_depth = FreeImage_GetBPP(dib);
692
693 BOOL bInterlaced = FALSE;
694 if( (flags & PNG_INTERLACED) == PNG_INTERLACED) {
695 interlace_type = PNG_INTERLACE_ADAM7;
696 bInterlaced = TRUE;
697 } else {
698 interlace_type = PNG_INTERLACE_NONE;
699 }
700
701 // set the ZLIB compression level or default to PNG default compression level (ZLIB level = 6)
702 int zlib_level = flags & 0x0F;
703 if((zlib_level >= 1) && (zlib_level <= 9)) {
704 png_set_compression_level(png_ptr, zlib_level);
705 } else if((flags & PNG_Z_NO_COMPRESSION) == PNG_Z_NO_COMPRESSION) {
706 png_set_compression_level(png_ptr, Z_NO_COMPRESSION);
707 }
708
709 // filtered strategy works better for high color images
710 if(pixel_depth >= 16){
711 png_set_compression_strategy(png_ptr, Z_FILTERED);
712 png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH);
713 } else {
714 png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
715 }
716
717 FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
718 if(image_type == FIT_BITMAP) {
719 // standard image type
720 bit_depth = (pixel_depth > 8) ? 8 : pixel_depth;
721 } else {
722 // 16-bit greyscale or 16-bit RGB(A)
723 bit_depth = 16;
724 }
725
726 switch (FreeImage_GetColorType(dib)) {
727 case FIC_MINISWHITE:
728 // Invert monochrome files to have 0 as black and 1 as white (no break here)
729 png_set_invert_mono(png_ptr);
730
731 case FIC_MINISBLACK:
732 png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
733 PNG_COLOR_TYPE_GRAY, interlace_type,
734 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
735
736 break;
737
738 case FIC_PALETTE:
739 {
740 png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
741 PNG_COLOR_TYPE_PALETTE, interlace_type,
742 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
743
744 // set the palette
745
746 palette_entries = 1 << bit_depth;
747 palette = (png_colorp)png_malloc(png_ptr, palette_entries * sizeof (png_color));
748 pal = FreeImage_GetPalette(dib);
749
750 for (int i = 0; i < palette_entries; i++) {
751 palette[i].red = pal[i].rgbRed;
752 palette[i].green = pal[i].rgbGreen;
753 palette[i].blue = pal[i].rgbBlue;
754 }
755
756 png_set_PLTE(png_ptr, info_ptr, palette, palette_entries);
757
758 // You must not free palette here, because png_set_PLTE only makes a link to
759 // the palette that you malloced. Wait until you are about to destroy
760 // the png structure.
761
762 break;
763 }
764
765 case FIC_RGBALPHA :
766 has_alpha_channel = TRUE;
767
768 png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
769 PNG_COLOR_TYPE_RGBA, interlace_type,
770 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
771
772 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
773 // flip BGR pixels to RGB
774 if(image_type == FIT_BITMAP)
775 png_set_bgr(png_ptr);
776 #endif
777 break;
778
779 case FIC_RGB:
780 png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
781 PNG_COLOR_TYPE_RGB, interlace_type,
782 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
783
784 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
785 // flip BGR pixels to RGB
786 if(image_type == FIT_BITMAP)
787 png_set_bgr(png_ptr);
788 #endif
789 break;
790
791 case FIC_CMYK:
792 break;
793 }
794
795 // write possible ICC profile
796
797 FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib);
798 if (iccProfile->size && iccProfile->data) {
799 png_set_iCCP(png_ptr, info_ptr, "Embedded Profile", 0, (png_charp)iccProfile->data, iccProfile->size);
800 }
801
802 // write metadata
803
804 WriteMetadata(png_ptr, info_ptr, dib);
805
806 // Optional gamma chunk is strongly suggested if you have any guess
807 // as to the correct gamma of the image.
808 // png_set_gAMA(png_ptr, info_ptr, gamma);
809
810 // set the transparency table
811
812 if ((pixel_depth == 8) && (FreeImage_IsTransparent(dib)) && (FreeImage_GetTransparencyCount(dib) > 0)) {
813 png_set_tRNS(png_ptr, info_ptr, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib), NULL);
814 }
815
816 // set the background color
817
818 if(FreeImage_HasBackgroundColor(dib)) {
819 png_color_16 image_background;
820 RGBQUAD rgbBkColor;
821
822 FreeImage_GetBackgroundColor(dib, &rgbBkColor);
823 memset(&image_background, 0, sizeof(png_color_16));
824 image_background.blue = rgbBkColor.rgbBlue;
825 image_background.green = rgbBkColor.rgbGreen;
826 image_background.red = rgbBkColor.rgbRed;
827 image_background.index = rgbBkColor.rgbReserved;
828
829 png_set_bKGD(png_ptr, info_ptr, &image_background);
830 }
831
832 // Write the file header information.
833
834 png_write_info(png_ptr, info_ptr);
835
836 // write out the image data
837
838 #ifndef FREEIMAGE_BIGENDIAN
839 if (bit_depth == 16) {
840 // turn on 16 bit byte swapping
841 png_set_swap(png_ptr);
842 }
843 #endif
844
845 int number_passes = 1;
846 if (bInterlaced) {
847 number_passes = png_set_interlace_handling(png_ptr);
848 }
849
850 if ((pixel_depth == 32) && (!has_alpha_channel)) {
851 BYTE *buffer = (BYTE *)malloc(width * 3);
852
853 // transparent conversion to 24-bit
854 // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images
855 for (int pass = 0; pass < number_passes; pass++) {
856 for (png_uint_32 k = 0; k < height; k++) {
857 FreeImage_ConvertLine32To24(buffer, FreeImage_GetScanLine(dib, height - k - 1), width);
858 png_write_row(png_ptr, buffer);
859 }
860 }
861 free(buffer);
862 } else {
863 // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images
864 for (int pass = 0; pass < number_passes; pass++) {
865 for (png_uint_32 k = 0; k < height; k++) {
866 png_write_row(png_ptr, FreeImage_GetScanLine(dib, height - k - 1));
867 }
868 }
869 }
870
871 // It is REQUIRED to call this to finish writing the rest of the file
872 // Bug with png_flush
873
874 png_write_end(png_ptr, info_ptr);
875
876 // clean up after the write, and free any memory allocated
877 if (palette) {
878 png_free(png_ptr, palette);
879 }
880
881 png_destroy_write_struct(&png_ptr, &info_ptr);
882
883 return TRUE;
884 } catch (const char *text) {
885 FreeImage_OutputMessageProc(s_format_id, text);
886 }
887 }
888
889 return FALSE;
890 }
891
892 // ==========================================================
893 // Init
894 // ==========================================================
895
896 void DLL_CALLCONV
897 InitPNG(Plugin *plugin, int format_id) {
898 s_format_id = format_id;
899
900 plugin->format_proc = Format;
901 plugin->description_proc = Description;
902 plugin->extension_proc = Extension;
903 plugin->regexpr_proc = RegExpr;
904 plugin->open_proc = NULL;
905 plugin->close_proc = NULL;
906 plugin->pagecount_proc = NULL;
907 plugin->pagecapability_proc = NULL;
908 plugin->load_proc = Load;
909 plugin->save_proc = Save;
910 plugin->validate_proc = Validate;
911 plugin->mime_proc = MimeType;
912 plugin->supports_export_bpp_proc = SupportsExportDepth;
913 plugin->supports_export_type_proc = SupportsExportType;
914 plugin->supports_icc_profiles_proc = SupportsICCProfiles;
915 }

  ViewVC Help
Powered by ViewVC 1.1.26