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