1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 03/07/2009
6 //
7 // Filename: src-IL/src/il_png.c
8 //
9 // Description: Portable network graphics file (.png) functions
10 //
11 // 20040223 XIX : now may spit out pngs with a transparent index, this is mostly a hack
12 // but the proper way of doing it would be to change the pal stuff to think in argb rather than rgb
13 // which is something of a bigger job.
14 //
15 //-----------------------------------------------------------------------------
16
17 // Most of the comments are left in this file from libpng's excellent example.c
18
19 #include "il_internal.h"
20 #ifndef IL_NO_PNG
21 #include <png.h>
22 #include "il_manip.h"
23 #include <stdlib.h>
24 #if PNG_LIBPNG_VER < 10200
25 #warning DevIL was designed with libpng 1.2.0 or higher in mind. Consider upgrading at www.libpng.org.
26 #endif
27
28 #if (defined(_WIN32) || defined(_WIN64)) && defined(IL_USE_PRAGMA_LIBS)
29 #if defined(_MSC_VER) || defined(__BORLANDC__)
30 #ifndef _DEBUG
31 #pragma comment(lib, "libpng.lib")
32 #pragma comment(lib, "zlib.lib")
33 #else
34 #pragma comment(lib, "libpng-d.lib")
35 #pragma comment(lib, "zlib-d.lib")
36 #endif
37 #endif
38 #endif
39
40
41 ILboolean iIsValidPng(void);
42 ILboolean iLoadPngInternal(void);
43 ILboolean iSavePngInternal(void);
44
45 ILint readpng_init(void);
46 ILboolean readpng_get_image(ILdouble display_exponent);
47 void readpng_cleanup(void);
48
49 png_structp png_ptr = NULL;
50 png_infop info_ptr = NULL;
51 ILint png_color_type;
52
53 #define GAMMA_CORRECTION 1.0 // Doesn't seem to be doing anything...
54
55
ilIsValidPng(ILconst_string FileName)56 ILboolean ilIsValidPng(ILconst_string FileName)
57 {
58 ILHANDLE PngFile;
59 ILboolean bPng = IL_FALSE;
60
61 if (!iCheckExtension(FileName, IL_TEXT("png"))) {
62 ilSetError(IL_INVALID_EXTENSION);
63 return bPng;
64 }
65
66 PngFile = iopenr(FileName);
67 if (PngFile == NULL) {
68 ilSetError(IL_COULD_NOT_OPEN_FILE);
69 return bPng;
70 }
71
72 bPng = ilIsValidPngF(PngFile);
73 icloser(PngFile);
74
75 return bPng;
76 }
77
78
ilIsValidPngF(ILHANDLE File)79 ILboolean ilIsValidPngF(ILHANDLE File)
80 {
81 ILuint FirstPos;
82 ILboolean bRet;
83
84 iSetInputFile(File);
85 FirstPos = itell();
86 bRet = iIsValidPng();
87 iseek(FirstPos, IL_SEEK_SET);
88
89 return bRet;
90 }
91
92
ilIsValidPngL(const void * Lump,ILuint Size)93 ILboolean ilIsValidPngL(const void *Lump, ILuint Size)
94 {
95 iSetInputLump(Lump, Size);
96 return iIsValidPng();
97 }
98
99
iIsValidPng()100 ILboolean iIsValidPng()
101 {
102 ILubyte Signature[8];
103 ILint Read;
104
105 Read = iread(Signature, 1, 8);
106 iseek(-Read, IL_SEEK_CUR);
107
108 return !png_sig_cmp(Signature, 0, 8);
109 }
110
111
112 // Reads a file
ilLoadPng(ILconst_string FileName)113 ILboolean ilLoadPng(ILconst_string FileName)
114 {
115 ILHANDLE PngFile;
116 ILboolean bPng = IL_FALSE;
117
118 PngFile = iopenr(FileName);
119 if (PngFile == NULL) {
120 ilSetError(IL_COULD_NOT_OPEN_FILE);
121 return bPng;
122 }
123
124 bPng = ilLoadPngF(PngFile);
125 icloser(PngFile);
126
127 return bPng;
128 }
129
130
131 // Reads an already-opened file
ilLoadPngF(ILHANDLE File)132 ILboolean ilLoadPngF(ILHANDLE File)
133 {
134 ILuint FirstPos;
135 ILboolean bRet;
136
137 iSetInputFile(File);
138 FirstPos = itell();
139 bRet = iLoadPngInternal();
140 iseek(FirstPos, IL_SEEK_SET);
141
142 return bRet;
143 }
144
145
146 // Reads from a memory "lump"
ilLoadPngL(const void * Lump,ILuint Size)147 ILboolean ilLoadPngL(const void *Lump, ILuint Size)
148 {
149 iSetInputLump(Lump, Size);
150 return iLoadPngInternal();
151 }
152
153
iLoadPngInternal()154 ILboolean iLoadPngInternal()
155 {
156 png_ptr = NULL;
157 info_ptr = NULL;
158
159 if (iCurImage == NULL) {
160 ilSetError(IL_ILLEGAL_OPERATION);
161 return IL_FALSE;
162 }
163 if (!iIsValidPng()) {
164 ilSetError(IL_INVALID_VALUE);
165 return IL_FALSE;
166 }
167
168 if (readpng_init())
169 return IL_FALSE;
170 if (!readpng_get_image(GAMMA_CORRECTION))
171 return IL_FALSE;
172
173 readpng_cleanup();
174
175 return ilFixImage();
176 }
177
178
png_read(png_structp png_ptr,png_bytep data,png_size_t length)179 static void png_read(png_structp png_ptr, png_bytep data, png_size_t length)
180 {
181 (void)png_ptr;
182 iread(data, 1, (ILuint)length);
183 return;
184 }
185
186
png_error_func(png_structp png_ptr,png_const_charp message)187 static void png_error_func(png_structp png_ptr, png_const_charp message)
188 {
189 ilSetError(IL_LIB_PNG_ERROR);
190
191 /*
192 changed 20040224
193 From the libpng docs:
194 "Errors handled through png_error() are fatal, meaning that png_error()
195 should never return to its caller. Currently, this is handled via
196 setjmp() and longjmp()"
197 */
198 //return;
199 longjmp(png_jmpbuf(png_ptr), 1);
200 }
201
png_warn_func(png_structp png_ptr,png_const_charp message)202 static void png_warn_func(png_structp png_ptr, png_const_charp message)
203 {
204 return;
205 }
206
207
readpng_init()208 ILint readpng_init()
209 {
210 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_func, png_warn_func);
211 if (!png_ptr)
212 return 4; /* out of memory */
213
214 info_ptr = png_create_info_struct(png_ptr);
215 if (!info_ptr) {
216 png_destroy_read_struct(&png_ptr, NULL, NULL);
217 return 4; /* out of memory */
218 }
219
220
221 /* we could create a second info struct here (end_info), but it's only
222 * useful if we want to keep pre- and post-IDAT chunk info separated
223 * (mainly for PNG-aware image editors and converters) */
224
225
226 /* setjmp() must be called in every function that calls a PNG-reading
227 * libpng function */
228
229 if (setjmp(png_jmpbuf(png_ptr))) {
230 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
231 return 2;
232 }
233
234
235 png_set_read_fn(png_ptr, NULL, png_read);
236 png_set_error_fn(png_ptr, NULL, png_error_func, png_warn_func);
237
238 // png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */
239
240 png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */
241
242
243 /* alternatively, could make separate calls to png_get_image_width(),
244 * etc., but want bit_depth and png_color_type for later [don't care about
245 * compression_type and filter_type => NULLs] */
246
247 /* OK, that's all we need for now; return happy */
248
249 return 0;
250 }
251
252
253 /* display_exponent == LUT_exponent * CRT_exponent */
254
readpng_get_image(ILdouble display_exponent)255 ILboolean readpng_get_image(ILdouble display_exponent)
256 {
257 png_bytepp row_pointers = NULL;
258 png_uint_32 width, height; // Changed the type to fix AMD64 bit problems, thanks to Eric Werness
259 ILdouble screen_gamma = 1.0;
260 ILuint i, channels;
261 ILenum format;
262 png_colorp palette;
263 ILint num_palette, j, bit_depth;
264 #if _WIN32 || DJGPP
265 ILdouble image_gamma;
266 #endif
267
268 /* setjmp() must be called in every function that calls a PNG-reading
269 * libpng function */
270
271 if (setjmp(png_jmpbuf(png_ptr))) {
272 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
273 return IL_FALSE;
274 }
275
276 png_get_IHDR(png_ptr, info_ptr, (png_uint_32*)&width, (png_uint_32*)&height,
277 &bit_depth, &png_color_type, NULL, NULL, NULL);
278
279 // Expand low-bit-depth grayscale images to 8 bits
280 if (png_color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
281 png_set_expand_gray_1_2_4_to_8(png_ptr);
282 }
283
284 // Expand RGB images with transparency to full alpha channels
285 // so the data will be available as RGBA quartets.
286 // But don't expand paletted images, since we want alpha palettes!
287 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) && !(png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)))
288 png_set_tRNS_to_alpha(png_ptr);
289
290 //refresh information (added 20040224)
291 png_get_IHDR(png_ptr, info_ptr, (png_uint_32*)&width, (png_uint_32*)&height,
292 &bit_depth, &png_color_type, NULL, NULL, NULL);
293
294 if (bit_depth < 8) { // Expanded earlier for grayscale, now take care of palette and rgb
295 bit_depth = 8;
296 png_set_packing(png_ptr);
297 }
298
299 // Perform gamma correction.
300 // @TODO: Determine if we should call png_set_gamma if image_gamma is 1.0.
301 #if _WIN32 || DJGPP
302 screen_gamma = 2.2;
303 if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
304 png_set_gamma(png_ptr, screen_gamma, image_gamma);
305 #else
306 screen_gamma = screen_gamma;
307 #endif
308
309 //fix endianess
310 #if BYTE_ORDER == LITTLE_ENDIAN
311 if (bit_depth == 16)
312 png_set_swap(png_ptr);
313 #endif
314
315
316 png_read_update_info(png_ptr, info_ptr);
317 channels = (ILint)png_get_channels(png_ptr, info_ptr);
318 //added 20040224: update png_color_type so that it has the correct value
319 //in iLoadPngInternal (globals rule...)
320 png_color_type = png_get_color_type(png_ptr, info_ptr);
321
322 //determine internal format
323 switch(png_color_type)
324 {
325 case PNG_COLOR_TYPE_PALETTE:
326 format = IL_COLOUR_INDEX;
327 break;
328 case PNG_COLOR_TYPE_GRAY:
329 format = IL_LUMINANCE;
330 break;
331 case PNG_COLOR_TYPE_GRAY_ALPHA:
332 format = IL_LUMINANCE_ALPHA;
333 break;
334 case PNG_COLOR_TYPE_RGB:
335 format = IL_RGB;
336 break;
337 case PNG_COLOR_TYPE_RGB_ALPHA:
338 format = IL_RGBA;
339 break;
340 default:
341 ilSetError(IL_ILLEGAL_FILE_VALUE);
342 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
343 return IL_FALSE;
344 }
345
346 if (!ilTexImage(width, height, 1, (ILubyte)channels, format, ilGetTypeBpc((ILubyte)(bit_depth >> 3)), NULL)) {
347 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
348 return IL_FALSE;
349 }
350 iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
351
352 //copy palette
353 if (format == IL_COLOUR_INDEX) {
354 int chans;
355 png_bytep trans = NULL;
356 int num_trans = -1;
357 if (!png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) {
358 ilSetError(IL_ILLEGAL_FILE_VALUE);
359 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
360 return IL_FALSE;
361 }
362
363 chans = 3;
364 iCurImage->Pal.PalType = IL_PAL_RGB24;
365
366 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
367 png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
368 iCurImage->Pal.PalType = IL_PAL_RGBA32;
369 chans = 4;
370 }
371
372 iCurImage->Pal.PalSize = num_palette * chans;
373
374 iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
375
376 for (j = 0; j < num_palette; ++j) {
377 iCurImage->Pal.Palette[chans*j + 0] = palette[j].red;
378 iCurImage->Pal.Palette[chans*j + 1] = palette[j].green;
379 iCurImage->Pal.Palette[chans*j + 2] = palette[j].blue;
380 if (trans!=NULL) {
381 if (j<num_trans)
382 iCurImage->Pal.Palette[chans*j + 3] = trans[j];
383 else
384 iCurImage->Pal.Palette[chans*j + 3] = 255;
385 }
386 }
387 }
388
389 //allocate row pointers
390 if ((row_pointers = (png_bytepp)ialloc(height * sizeof(png_bytep))) == NULL) {
391 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
392 return IL_FALSE;
393 }
394
395
396 // Set the individual row_pointers to point at the correct offsets */
397 for (i = 0; i < height; i++)
398 row_pointers[i] = iCurImage->Data + i * iCurImage->Bps;
399
400
401 // Now we can go ahead and just read the whole image
402 png_read_image(png_ptr, row_pointers);
403
404
405 /* and we're done! (png_read_end() can be omitted if no processing of
406 * post-IDAT text/time/etc. is desired) */
407 //png_read_end(png_ptr, NULL);
408 ifree(row_pointers);
409
410 return IL_TRUE;
411 }
412
413
readpng_cleanup()414 void readpng_cleanup()
415 {
416 if (png_ptr && info_ptr) {
417 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
418 png_ptr = NULL;
419 info_ptr = NULL;
420 }
421 }
422
423
424 //! Writes a Png file
ilSavePng(const ILstring FileName)425 ILboolean ilSavePng(const ILstring FileName)
426 {
427 ILHANDLE PngFile;
428 ILuint PngSize;
429
430 if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
431 if (iFileExists(FileName)) {
432 ilSetError(IL_FILE_ALREADY_EXISTS);
433 return IL_FALSE;
434 }
435 }
436
437 PngFile = iopenw(FileName);
438 if (PngFile == NULL) {
439 ilSetError(IL_COULD_NOT_OPEN_FILE);
440 return IL_FALSE;
441 }
442
443 PngSize = ilSavePngF(PngFile);
444 iclosew(PngFile);
445
446 if (PngSize == 0)
447 return IL_FALSE;
448 return IL_TRUE;
449 }
450
451
452 //! Writes a Png to an already-opened file
ilSavePngF(ILHANDLE File)453 ILuint ilSavePngF(ILHANDLE File)
454 {
455 ILuint Pos;
456 iSetOutputFile(File);
457 Pos = itellw();
458 if (iSavePngInternal() == IL_FALSE)
459 return 0; // Error occurred
460 return itellw() - Pos; // Return the number of bytes written.
461 }
462
463
464 //! Writes a Png to a memory "lump"
ilSavePngL(void * Lump,ILuint Size)465 ILuint ilSavePngL(void *Lump, ILuint Size)
466 {
467 ILuint Pos;
468 iSetOutputLump(Lump, Size);
469 Pos = itellw();
470 if (iSavePngInternal() == IL_FALSE)
471 return 0; // Error occurred
472 return itellw() - Pos; // Return the number of bytes written.
473 }
474
475
png_write(png_structp png_ptr,png_bytep data,png_size_t length)476 void png_write(png_structp png_ptr, png_bytep data, png_size_t length)
477 {
478 (void)png_ptr;
479 iwrite(data, 1, (ILuint)length);
480 return;
481 }
482
flush_data(png_structp png_ptr)483 void flush_data(png_structp png_ptr)
484 {
485 return;
486 }
487
488
489 // Internal function used to save the Png.
iSavePngInternal()490 ILboolean iSavePngInternal()
491 {
492 png_structp png_ptr;
493 png_infop info_ptr;
494 png_text text[4];
495 ILenum PngType;
496 ILuint BitDepth, i, j;
497 ILubyte **RowPtr = NULL;
498 ILimage *Temp = NULL;
499 ILpal *TempPal = NULL;
500
501 //XIX alpha
502 ILubyte transpart[1];
503 ILint trans;
504
505 if (iCurImage == NULL) {
506 ilSetError(IL_ILLEGAL_OPERATION);
507 return IL_FALSE;
508 }
509
510 /* Create and initialize the png_struct with the desired error handler
511 * functions. If you want to use the default stderr and longjump method,
512 * you can supply NULL for the last three parameters. We also check that
513 * the library version is compatible with the one used at compile time,
514 * in case we are using dynamically linked libraries. REQUIRED.
515 */
516 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_func, png_warn_func);
517 if (png_ptr == NULL) {
518 ilSetError(IL_LIB_PNG_ERROR);
519 return IL_FALSE;
520 }
521
522 // Allocate/initialize the image information data. REQUIRED
523 info_ptr = png_create_info_struct(png_ptr);
524 if (info_ptr == NULL) {
525 ilSetError(IL_LIB_PNG_ERROR);
526 goto error_label;
527 }
528
529 /*// Set error handling. REQUIRED if you aren't supplying your own
530 // error handling functions in the png_create_write_struct() call.
531 if (setjmp(png_jmpbuf(png_ptr))) {
532 // If we get here, we had a problem reading the file
533 png_destroy_write_struct(&png_ptr, &info_ptr);
534 ilSetError(IL_LIB_PNG_ERROR);
535 return IL_FALSE;
536 }*/
537
538 // png_init_io(png_ptr, PngFile);
539 png_set_write_fn(png_ptr, NULL, png_write, flush_data);
540
541 png_set_compression_level(png_ptr, iGetInt(IL_PNG_COMPRESSION));
542
543 switch (iCurImage->Type)
544 {
545 case IL_BYTE:
546 case IL_UNSIGNED_BYTE:
547 Temp = iCurImage;
548 BitDepth = 8;
549 break;
550 case IL_SHORT:
551 case IL_UNSIGNED_SHORT:
552 Temp = iCurImage;
553 BitDepth = 16;
554 break;
555 case IL_INT:
556 case IL_UNSIGNED_INT:
557 Temp = iConvertImage(iCurImage, iCurImage->Format, IL_UNSIGNED_SHORT);
558 if (Temp == NULL) {
559 png_destroy_write_struct(&png_ptr, &info_ptr);
560 return IL_FALSE;
561 }
562 BitDepth = 16;
563 break;
564 default:
565 ilSetError(IL_INTERNAL_ERROR);
566 goto error_label;
567 }
568
569 switch (iCurImage->Format)
570 {
571 case IL_COLOUR_INDEX:
572 PngType = PNG_COLOR_TYPE_PALETTE;
573 break;
574 case IL_LUMINANCE:
575 PngType = PNG_COLOR_TYPE_GRAY;
576 break;
577 case IL_LUMINANCE_ALPHA: //added 20050328
578 PngType = PNG_COLOR_TYPE_GRAY_ALPHA;
579 break;
580 case IL_RGB:
581 case IL_BGR:
582 PngType = PNG_COLOR_TYPE_RGB;
583 break;
584 case IL_RGBA:
585 case IL_BGRA:
586 PngType = PNG_COLOR_TYPE_RGB_ALPHA;
587 break;
588 default:
589 ilSetError(IL_INTERNAL_ERROR);
590 goto error_label;
591 }
592
593 // Set the image information here. Width and height are up to 2^31,
594 // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
595 // the png_color_type selected. png_color_type is one of PNG_COLOR_TYPE_GRAY,
596 // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
597 // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
598 // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
599 // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
600 if (iGetInt(IL_PNG_INTERLACE) == IL_TRUE) {
601 png_set_IHDR(png_ptr, info_ptr, iCurImage->Width, iCurImage->Height, BitDepth, PngType,
602 PNG_INTERLACE_ADAM7, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
603 }
604 else {
605 png_set_IHDR(png_ptr, info_ptr, iCurImage->Width, iCurImage->Height, BitDepth, PngType,
606 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
607 }
608
609 if (iCurImage->Format == IL_COLOUR_INDEX) {
610 // set the palette if there is one. REQUIRED for indexed-color images.
611 TempPal = iConvertPal(&iCurImage->Pal, IL_PAL_RGB24);
612 png_set_PLTE(png_ptr, info_ptr, (png_colorp)TempPal->Palette,
613 ilGetInteger(IL_PALETTE_NUM_COLS));
614
615 //XIX alpha
616 trans=iGetInt(IL_PNG_ALPHA_INDEX);
617 if ( trans>=0)
618 {
619 transpart[0]=(ILubyte)trans;
620 png_set_tRNS(png_ptr, info_ptr, transpart, 1, 0);
621 }
622 }
623
624 /*
625 // optional significant bit chunk
626 // if we are dealing with a grayscale image then
627 sig_bit.gray = true_bit_depth;
628 // otherwise, if we are dealing with a color image then
629 sig_bit.red = true_red_bit_depth;
630 sig_bit.green = true_green_bit_depth;
631 sig_bit.blue = true_blue_bit_depth;
632 // if the image has an alpha channel then
633 sig_bit.alpha = true_alpha_bit_depth;
634 png_set_sBIT(png_ptr, info_ptr, sig_bit);*/
635
636
637 /* Optional gamma chunk is strongly suggested if you have any guess
638 * as to the correct gamma of the image.
639 */
640 //png_set_gAMA(png_ptr, info_ptr, gamma);
641
642 // Optionally write comments into the image.
643 imemclear(text, sizeof(png_text) * 4);
644 text[0].key = "Generated by";
645 text[0].text = "Generated by the Developer's Image Library (DevIL)";
646 text[0].compression = PNG_TEXT_COMPRESSION_NONE;
647 text[1].key = "Author";
648 text[1].text = (char*)iGetString(IL_PNG_AUTHNAME_STRING); // Will not actually be modified!
649 text[1].compression = PNG_TEXT_COMPRESSION_NONE;
650 text[2].key = "Description";
651 text[2].text = iGetString(IL_PNG_DESCRIPTION_STRING);
652 text[2].compression = PNG_TEXT_COMPRESSION_NONE;
653 text[3].key = "Title";
654 text[3].text = iGetString(IL_PNG_TITLE_STRING);
655 text[3].compression = PNG_TEXT_COMPRESSION_NONE;
656 png_set_text(png_ptr, info_ptr, text, 3);
657
658 // Write the file header information. REQUIRED.
659 png_write_info(png_ptr, info_ptr);
660
661 // Free up our user-defined text.
662 if (text[1].text)
663 ifree(text[1].text);
664 if (text[2].text)
665 ifree(text[2].text);
666 if (text[3].text)
667 ifree(text[3].text);
668
669 /* Shift the pixels up to a legal bit depth and fill in
670 * as appropriate to correctly scale the image.
671 */
672 //png_set_shift(png_ptr, &sig_bit);
673
674 /* pack pixels into bytes */
675 //png_set_packing(png_ptr);
676
677 // swap location of alpha bytes from ARGB to RGBA
678 //png_set_swap_alpha(png_ptr);
679
680 // flip BGR pixels to RGB
681 if (iCurImage->Format == IL_BGR || iCurImage->Format == IL_BGRA)
682 png_set_bgr(png_ptr);
683
684 // swap bytes of 16-bit files to most significant byte first
685 #ifdef __LITTLE_ENDIAN__
686 png_set_swap(png_ptr);
687 #endif//__LITTLE_ENDIAN__
688
689 RowPtr = (ILubyte**)ialloc(iCurImage->Height * sizeof(ILubyte*));
690 if (RowPtr == NULL)
691 goto error_label;
692 if (iCurImage->Origin == IL_ORIGIN_UPPER_LEFT) {
693 for (i = 0; i < iCurImage->Height; i++) {
694 RowPtr[i] = Temp->Data + i * Temp->Bps;
695 }
696 }
697 else {
698 j = iCurImage->Height - 1;
699 for (i = 0; i < iCurImage->Height; i++, j--) {
700 RowPtr[i] = Temp->Data + j * Temp->Bps;
701 }
702 }
703
704 // Writes the image.
705 png_write_image(png_ptr, RowPtr);
706
707 // It is REQUIRED to call this to finish writing the rest of the file
708 png_write_end(png_ptr, info_ptr);
709
710 // clean up after the write, and ifree any memory allocated
711 png_destroy_write_struct(&png_ptr, &info_ptr);
712
713 ifree(RowPtr);
714
715 if (Temp != iCurImage)
716 ilCloseImage(Temp);
717 ilClosePal(TempPal);
718
719 return IL_TRUE;
720
721 error_label:
722 png_destroy_write_struct(&png_ptr, &info_ptr);
723 ifree(RowPtr);
724 if (Temp != iCurImage)
725 ilCloseImage(Temp);
726 ilClosePal(TempPal);
727 return IL_FALSE;
728 }
729
730
731 #endif//IL_NO_PNG
732