]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CImageLoaderJPG.cpp
Drop obsolete configuration macros
[irrlicht.git] / source / Irrlicht / CImageLoaderJPG.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 "CImageLoaderJPG.h"\r
6 \r
7 #include "IReadFile.h"\r
8 #include "CImage.h"\r
9 #include "os.h"\r
10 #include "irrString.h"\r
11 \r
12 namespace irr\r
13 {\r
14 namespace video\r
15 {\r
16 \r
17 //! constructor\r
18 CImageLoaderJPG::CImageLoaderJPG()\r
19 {\r
20         #ifdef _DEBUG\r
21         setDebugName("CImageLoaderJPG");\r
22         #endif\r
23 }\r
24 \r
25 \r
26 \r
27 //! destructor\r
28 CImageLoaderJPG::~CImageLoaderJPG()\r
29 {\r
30 }\r
31 \r
32 \r
33 \r
34 //! returns true if the file maybe is able to be loaded by this class\r
35 //! based on the file extension (e.g. ".tga")\r
36 bool CImageLoaderJPG::isALoadableFileExtension(const io::path& filename) const\r
37 {\r
38         return core::hasFileExtension ( filename, "jpg", "jpeg" );\r
39 }\r
40 \r
41 // struct for handling jpeg errors\r
42 struct irr_jpeg_error_mgr\r
43 {\r
44         // public jpeg error fields\r
45         struct jpeg_error_mgr pub;\r
46 \r
47         // for longjmp, to return to caller on a fatal error\r
48         jmp_buf setjmp_buffer;\r
49 \r
50         // for having access to the filename when printing the error messages\r
51         core::stringc* filename;\r
52 };\r
53 \r
54 void CImageLoaderJPG::init_source (j_decompress_ptr cinfo)\r
55 {\r
56         // DO NOTHING\r
57 }\r
58 \r
59 \r
60 \r
61 boolean CImageLoaderJPG::fill_input_buffer (j_decompress_ptr cinfo)\r
62 {\r
63         // DO NOTHING\r
64         return TRUE;\r
65 }\r
66 \r
67 \r
68 \r
69 void CImageLoaderJPG::skip_input_data (j_decompress_ptr cinfo, long count)\r
70 {\r
71         jpeg_source_mgr * src = cinfo->src;\r
72         if(count > 0)\r
73         {\r
74                 src->bytes_in_buffer -= count;\r
75                 src->next_input_byte += count;\r
76         }\r
77 }\r
78 \r
79 \r
80 \r
81 void CImageLoaderJPG::term_source (j_decompress_ptr cinfo)\r
82 {\r
83         // DO NOTHING\r
84 }\r
85 \r
86 \r
87 void CImageLoaderJPG::error_exit (j_common_ptr cinfo)\r
88 {\r
89         // unfortunately we need to use a goto rather than throwing an exception\r
90         // as gcc crashes under linux crashes when using throw from within\r
91         // extern c code\r
92 \r
93         // Always display the message\r
94         (*cinfo->err->output_message) (cinfo);\r
95 \r
96         // cinfo->err really points to a irr_error_mgr struct\r
97         irr_jpeg_error_mgr *myerr = (irr_jpeg_error_mgr*) cinfo->err;\r
98 \r
99         longjmp(myerr->setjmp_buffer, 1);\r
100 }\r
101 \r
102 \r
103 void CImageLoaderJPG::output_message(j_common_ptr cinfo)\r
104 {\r
105         // display the error message.\r
106         c8 temp1[JMSG_LENGTH_MAX];\r
107         (*cinfo->err->format_message)(cinfo, temp1);\r
108         core::stringc errMsg("JPEG FATAL ERROR in ");\r
109 \r
110         irr_jpeg_error_mgr* myerr = (irr_jpeg_error_mgr*)cinfo->err;\r
111         errMsg += *myerr->filename;\r
112         os::Printer::log(errMsg.c_str(),temp1, ELL_ERROR);\r
113 }\r
114 \r
115 //! returns true if the file maybe is able to be loaded by this class\r
116 bool CImageLoaderJPG::isALoadableFileFormat(io::IReadFile* file) const\r
117 {\r
118         if (!(file && file->seek(0)))\r
119                 return false;\r
120         unsigned char header[3];\r
121         size_t headerLen = file->read(header, sizeof(header));\r
122         return headerLen >= 3 && !memcmp(header, "\xFF\xD8\xFF", 3);\r
123 }\r
124 \r
125 //! creates a surface from the file\r
126 IImage* CImageLoaderJPG::loadImage(io::IReadFile* file) const\r
127 {\r
128         if (!file)\r
129                 return 0;\r
130 \r
131         core::stringc filename = file->getFileName();\r
132 \r
133         u8 **rowPtr=0;\r
134         u8* input = new u8[file->getSize()];\r
135         file->read(input, file->getSize());\r
136 \r
137         // allocate and initialize JPEG decompression object\r
138         struct jpeg_decompress_struct cinfo;\r
139         struct irr_jpeg_error_mgr jerr;\r
140 \r
141         //We have to set up the error handler first, in case the initialization\r
142         //step fails.  (Unlikely, but it could happen if you are out of memory.)\r
143         //This routine fills in the contents of struct jerr, and returns jerr's\r
144         //address which we place into the link field in cinfo.\r
145 \r
146         cinfo.err = jpeg_std_error(&jerr.pub);\r
147         cinfo.err->error_exit = error_exit;\r
148         cinfo.err->output_message = output_message;\r
149         jerr.filename = &filename;\r
150 \r
151         // compatibility fudge:\r
152         // we need to use setjmp/longjmp for error handling as gcc-linux\r
153         // crashes when throwing within external c code\r
154         if (setjmp(jerr.setjmp_buffer))\r
155         {\r
156                 // If we get here, the JPEG code has signaled an error.\r
157                 // We need to clean up the JPEG object and return.\r
158 \r
159                 jpeg_destroy_decompress(&cinfo);\r
160 \r
161                 delete [] input;\r
162                 delete [] rowPtr;\r
163 \r
164                 // return null pointer\r
165                 return 0;\r
166         }\r
167 \r
168         // Now we can initialize the JPEG decompression object.\r
169         jpeg_create_decompress(&cinfo);\r
170 \r
171         // specify data source\r
172         jpeg_source_mgr jsrc;\r
173 \r
174         // Set up data pointer\r
175         jsrc.bytes_in_buffer = file->getSize();\r
176         jsrc.next_input_byte = (JOCTET*)input;\r
177         cinfo.src = &jsrc;\r
178 \r
179         jsrc.init_source = init_source;\r
180         jsrc.fill_input_buffer = fill_input_buffer;\r
181         jsrc.skip_input_data = skip_input_data;\r
182         jsrc.resync_to_restart = jpeg_resync_to_restart;\r
183         jsrc.term_source = term_source;\r
184 \r
185         // Decodes JPG input from whatever source\r
186         // Does everything AFTER jpeg_create_decompress\r
187         // and BEFORE jpeg_destroy_decompress\r
188         // Caller is responsible for arranging these + setting up cinfo\r
189 \r
190         // read file parameters with jpeg_read_header()\r
191         jpeg_read_header(&cinfo, TRUE);\r
192 \r
193         bool useCMYK=false;\r
194         if (cinfo.jpeg_color_space==JCS_CMYK)\r
195         {\r
196                 cinfo.out_color_space=JCS_CMYK;\r
197                 cinfo.out_color_components=4;\r
198                 useCMYK=true;\r
199         }\r
200         else\r
201         {\r
202                 cinfo.out_color_space=JCS_RGB;\r
203                 cinfo.out_color_components=3;\r
204         }\r
205         cinfo.output_gamma=2.2;\r
206         cinfo.do_fancy_upsampling=FALSE;\r
207 \r
208         // reject unreasonable sizes\r
209         if (!checkImageDimensions(cinfo.image_width, cinfo.image_height))\r
210                 longjmp(jerr.setjmp_buffer, 1);\r
211 \r
212         // Start decompressor\r
213         jpeg_start_decompress(&cinfo);\r
214 \r
215         // Get image data\r
216         u32 rowspan = cinfo.image_width * cinfo.out_color_components;\r
217         u32 width = cinfo.image_width;\r
218         u32 height = cinfo.image_height;\r
219 \r
220         // Allocate memory for buffer\r
221         u8* output = new u8[rowspan * height];\r
222 \r
223         // Here we use the library's state variable cinfo.output_scanline as the\r
224         // loop counter, so that we don't have to keep track ourselves.\r
225         // Create array of row pointers for lib\r
226         rowPtr = new u8* [height];\r
227 \r
228         for( u32 i = 0; i < height; i++ )\r
229                 rowPtr[i] = &output[ i * rowspan ];\r
230 \r
231         u32 rowsRead = 0;\r
232 \r
233         while( cinfo.output_scanline < cinfo.output_height )\r
234                 rowsRead += jpeg_read_scanlines( &cinfo, &rowPtr[rowsRead], cinfo.output_height - rowsRead );\r
235 \r
236         delete [] rowPtr;\r
237         // Finish decompression\r
238 \r
239         jpeg_finish_decompress(&cinfo);\r
240 \r
241         // Release JPEG decompression object\r
242         // This is an important step since it will release a good deal of memory.\r
243         jpeg_destroy_decompress(&cinfo);\r
244 \r
245         // convert image\r
246         IImage* image = 0;\r
247         if (useCMYK)\r
248         {\r
249                 image = new CImage(ECF_R8G8B8,\r
250                                 core::dimension2d<u32>(width, height));\r
251                 const u32 size = 3*width*height;\r
252                 u8* data = (u8*)image->getData();\r
253                 if (data)\r
254                 {\r
255                         for (u32 i=0,j=0; i<size; i+=3, j+=4)\r
256                         {\r
257                                 // Also works without K, but has more contrast with K multiplied in\r
258 //                              data[i+0] = output[j+2];\r
259 //                              data[i+1] = output[j+1];\r
260 //                              data[i+2] = output[j+0];\r
261                                 data[i+0] = (char)(output[j+2]*(output[j+3]/255.f));\r
262                                 data[i+1] = (char)(output[j+1]*(output[j+3]/255.f));\r
263                                 data[i+2] = (char)(output[j+0]*(output[j+3]/255.f));\r
264                         }\r
265                 }\r
266                 delete [] output;\r
267         }\r
268         else\r
269                 image = new CImage(ECF_R8G8B8,\r
270                                 core::dimension2d<u32>(width, height), output);\r
271 \r
272         delete [] input;\r
273 \r
274         return image;\r
275 }\r
276 \r
277 \r
278 \r
279 //! creates a loader which is able to load jpeg images\r
280 IImageLoader* createImageLoaderJPG()\r
281 {\r
282         return new CImageLoaderJPG();\r
283 }\r
284 \r
285 } // end namespace video\r
286 } // end namespace irr\r