1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "CImageLoaderPNG.h"
6 
7 #ifdef _IRR_COMPILE_WITH_PNG_LOADER_
8 
9 #ifdef _IRR_COMPILE_WITH_LIBPNG_
10 	#ifndef _IRR_USE_NON_SYSTEM_LIB_PNG_
11 	#include <png.h> // use system lib png
12 	#else // _IRR_USE_NON_SYSTEM_LIB_PNG_
13 	#include "libpng/png.h" // use irrlicht included lib png
14 	#endif // _IRR_USE_NON_SYSTEM_LIB_PNG_
15 #endif // _IRR_COMPILE_WITH_LIBPNG_
16 
17 #include "CImage.h"
18 #include "CReadFile.h"
19 #include "os.h"
20 
21 namespace irr
22 {
23 namespace video
24 {
25 
26 #ifdef _IRR_COMPILE_WITH_LIBPNG_
27 // PNG function for error handling
png_cpexcept_error(png_structp png_ptr,png_const_charp msg)28 static void png_cpexcept_error(png_structp png_ptr, png_const_charp msg)
29 {
30 	os::Printer::log("PNG fatal error", msg, ELL_ERROR);
31 	longjmp(png_jmpbuf(png_ptr), 1);
32 }
33 
34 // PNG function for warning handling
png_cpexcept_warn(png_structp png_ptr,png_const_charp msg)35 static void png_cpexcept_warn(png_structp png_ptr, png_const_charp msg)
36 {
37 	os::Printer::log("PNG warning", msg, ELL_WARNING);
38 }
39 
40 // PNG function for file reading
user_read_data_fcn(png_structp png_ptr,png_bytep data,png_size_t length)41 void PNGAPI user_read_data_fcn(png_structp png_ptr, png_bytep data, png_size_t length)
42 {
43 	png_size_t check;
44 
45 	// changed by zola {
46 	io::IReadFile* file=(io::IReadFile*)png_get_io_ptr(png_ptr);
47 	check=(png_size_t) file->read((void*)data,(u32)length);
48 	// }
49 
50 	if (check != length)
51 		png_error(png_ptr, "Read Error");
52 }
53 #endif // _IRR_COMPILE_WITH_LIBPNG_
54 
55 
56 //! returns true if the file maybe is able to be loaded by this class
57 //! based on the file extension (e.g. ".tga")
isALoadableFileExtension(const io::path & filename) const58 bool CImageLoaderPng::isALoadableFileExtension(const io::path& filename) const
59 {
60 #ifdef _IRR_COMPILE_WITH_LIBPNG_
61 	return core::hasFileExtension ( filename, "png" );
62 #else
63 	return false;
64 #endif // _IRR_COMPILE_WITH_LIBPNG_
65 }
66 
67 
68 //! returns true if the file maybe is able to be loaded by this class
isALoadableFileFormat(io::IReadFile * file) const69 bool CImageLoaderPng::isALoadableFileFormat(io::IReadFile* file) const
70 {
71 #ifdef _IRR_COMPILE_WITH_LIBPNG_
72 	if (!file)
73 		return false;
74 
75 	png_byte buffer[8];
76 	// Read the first few bytes of the PNG file
77 	if (file->read(buffer, 8) != 8)
78 		return false;
79 
80 	// Check if it really is a PNG file
81 	return !png_sig_cmp(buffer, 0, 8);
82 #else
83 	return false;
84 #endif // _IRR_COMPILE_WITH_LIBPNG_
85 }
86 
87 
88 // load in the image data
loadImage(io::IReadFile * file) const89 IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
90 {
91 #ifdef _IRR_COMPILE_WITH_LIBPNG_
92 	if (!file)
93 		return 0;
94 
95 	video::IImage* image = 0;
96 	//Used to point to image rows
97 	u8** RowPointers = 0;
98 
99 	png_byte buffer[8];
100 	// Read the first few bytes of the PNG file
101 	if( file->read(buffer, 8) != 8 )
102 	{
103 		os::Printer::log("LOAD PNG: can't read file\n", file->getFileName(), ELL_ERROR);
104 		return 0;
105 	}
106 
107 	// Check if it really is a PNG file
108 	if( png_sig_cmp(buffer, 0, 8) )
109 	{
110 		os::Printer::log("LOAD PNG: not really a png\n", file->getFileName(), ELL_ERROR);
111 		return 0;
112 	}
113 
114 	// Allocate the png read struct
115 	png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
116 		NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warn);
117 	if (!png_ptr)
118 	{
119 		os::Printer::log("LOAD PNG: Internal PNG create read struct failure\n", file->getFileName(), ELL_ERROR);
120 		return 0;
121 	}
122 
123 	// Allocate the png info struct
124 	png_infop info_ptr = png_create_info_struct(png_ptr);
125 	if (!info_ptr)
126 	{
127 		os::Printer::log("LOAD PNG: Internal PNG create info struct failure\n", file->getFileName(), ELL_ERROR);
128 		png_destroy_read_struct(&png_ptr, NULL, NULL);
129 		return 0;
130 	}
131 
132 	// for proper error handling
133 	if (setjmp(png_jmpbuf(png_ptr)))
134 	{
135 		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
136 		if (RowPointers)
137 			delete [] RowPointers;
138 		return 0;
139 	}
140 
141 	// changed by zola so we don't need to have public FILE pointers
142 	png_set_read_fn(png_ptr, file, user_read_data_fcn);
143 
144 	png_set_sig_bytes(png_ptr, 8); // Tell png that we read the signature
145 
146 	png_read_info(png_ptr, info_ptr); // Read the info section of the png file
147 
148 	u32 Width;
149 	u32 Height;
150 	s32 BitDepth;
151 	s32 ColorType;
152 	{
153 		// Use temporary variables to avoid passing casted pointers
154 		png_uint_32 w,h;
155 		// Extract info
156 		png_get_IHDR(png_ptr, info_ptr,
157 			&w, &h,
158 			&BitDepth, &ColorType, NULL, NULL, NULL);
159 		Width=w;
160 		Height=h;
161 	}
162 
163 	// Convert palette color to true color
164 	if (ColorType==PNG_COLOR_TYPE_PALETTE)
165 		png_set_palette_to_rgb(png_ptr);
166 
167 	// Convert low bit colors to 8 bit colors
168 	if (BitDepth < 8)
169 	{
170 		if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA)
171 			png_set_expand_gray_1_2_4_to_8(png_ptr);
172 		else
173 			png_set_packing(png_ptr);
174 	}
175 
176 	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
177 		png_set_tRNS_to_alpha(png_ptr);
178 
179 	// Convert high bit colors to 8 bit colors
180 	if (BitDepth == 16)
181 		png_set_strip_16(png_ptr);
182 
183 	// Convert gray color to true color
184 	if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA)
185 		png_set_gray_to_rgb(png_ptr);
186 
187 	int intent;
188 	const double screen_gamma = 2.2;
189 
190 	if (png_get_sRGB(png_ptr, info_ptr, &intent))
191 		png_set_gamma(png_ptr, screen_gamma, 0.45455);
192 	else
193 	{
194 		double image_gamma;
195 		if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
196 			png_set_gamma(png_ptr, screen_gamma, image_gamma);
197 		else
198 			png_set_gamma(png_ptr, screen_gamma, 0.45455);
199 	}
200 
201 	// Update the changes in between, as we need to get the new color type
202 	// for proper processing of the RGBA type
203 	png_read_update_info(png_ptr, info_ptr);
204 	{
205 		// Use temporary variables to avoid passing casted pointers
206 		png_uint_32 w,h;
207 		// Extract info
208 		png_get_IHDR(png_ptr, info_ptr,
209 			&w, &h,
210 			&BitDepth, &ColorType, NULL, NULL, NULL);
211 		Width=w;
212 		Height=h;
213 	}
214 
215 	// Convert RGBA to BGRA
216 	if (ColorType==PNG_COLOR_TYPE_RGB_ALPHA)
217 	{
218 #ifdef __BIG_ENDIAN__
219 		png_set_swap_alpha(png_ptr);
220 #else
221 		png_set_bgr(png_ptr);
222 #endif
223 	}
224 
225 	// Create the image structure to be filled by png data
226 	if (ColorType==PNG_COLOR_TYPE_RGB_ALPHA)
227 		image = new CImage(ECF_A8R8G8B8, core::dimension2d<u32>(Width, Height));
228 	else
229 		image = new CImage(ECF_R8G8B8, core::dimension2d<u32>(Width, Height));
230 	if (!image)
231 	{
232 		os::Printer::log("LOAD PNG: Internal PNG create image struct failure\n", file->getFileName(), ELL_ERROR);
233 		png_destroy_read_struct(&png_ptr, NULL, NULL);
234 		return 0;
235 	}
236 
237 	// Create array of pointers to rows in image data
238 	RowPointers = new png_bytep[Height];
239 	if (!RowPointers)
240 	{
241 		os::Printer::log("LOAD PNG: Internal PNG create row pointers failure\n", file->getFileName(), ELL_ERROR);
242 		png_destroy_read_struct(&png_ptr, NULL, NULL);
243 		delete image;
244 		return 0;
245 	}
246 
247 	// Fill array of pointers to rows in image data
248 	unsigned char* data = (unsigned char*)image->lock();
249 	for (u32 i=0; i<Height; ++i)
250 	{
251 		RowPointers[i]=data;
252 		data += image->getPitch();
253 	}
254 
255 	// for proper error handling
256 	if (setjmp(png_jmpbuf(png_ptr)))
257 	{
258 		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
259 		delete [] RowPointers;
260 		image->unlock();
261 		delete image;
262 		return 0;
263 	}
264 
265 	// Read data using the library function that handles all transformations including interlacing
266 	png_read_image(png_ptr, RowPointers);
267 
268 	png_read_end(png_ptr, NULL);
269 	delete [] RowPointers;
270 	image->unlock();
271 	png_destroy_read_struct(&png_ptr,&info_ptr, 0); // Clean up memory
272 
273 	return image;
274 #else
275 	return 0;
276 #endif // _IRR_COMPILE_WITH_LIBPNG_
277 }
278 
279 
createImageLoaderPNG()280 IImageLoader* createImageLoaderPNG()
281 {
282 	return new CImageLoaderPng();
283 }
284 
285 
286 }// end namespace irr
287 }//end namespace video
288 
289 #endif
290 
291