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