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
5 #include "CImageLoaderJPG.h"
\r
7 #include "IReadFile.h"
\r
10 #include "irrString.h"
\r
18 CImageLoaderJPG::CImageLoaderJPG()
\r
21 setDebugName("CImageLoaderJPG");
\r
28 CImageLoaderJPG::~CImageLoaderJPG()
\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
38 return core::hasFileExtension ( filename, "jpg", "jpeg" );
\r
41 // struct for handling jpeg errors
\r
42 struct irr_jpeg_error_mgr
\r
44 // public jpeg error fields
\r
45 struct jpeg_error_mgr pub;
\r
47 // for longjmp, to return to caller on a fatal error
\r
48 jmp_buf setjmp_buffer;
\r
50 // for having access to the filename when printing the error messages
\r
51 core::stringc* filename;
\r
54 void CImageLoaderJPG::init_source (j_decompress_ptr cinfo)
\r
61 boolean CImageLoaderJPG::fill_input_buffer (j_decompress_ptr cinfo)
\r
69 void CImageLoaderJPG::skip_input_data (j_decompress_ptr cinfo, long count)
\r
71 jpeg_source_mgr * src = cinfo->src;
\r
74 src->bytes_in_buffer -= count;
\r
75 src->next_input_byte += count;
\r
81 void CImageLoaderJPG::term_source (j_decompress_ptr cinfo)
\r
87 void CImageLoaderJPG::error_exit (j_common_ptr cinfo)
\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
93 // Always display the message
\r
94 (*cinfo->err->output_message) (cinfo);
\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
99 longjmp(myerr->setjmp_buffer, 1);
\r
103 void CImageLoaderJPG::output_message(j_common_ptr cinfo)
\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
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
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
118 if (!(file && file->seek(0)))
\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
125 //! creates a surface from the file
\r
126 IImage* CImageLoaderJPG::loadImage(io::IReadFile* file) const
\r
131 core::stringc filename = file->getFileName();
\r
134 u8* input = new u8[file->getSize()];
\r
135 file->read(input, file->getSize());
\r
137 // allocate and initialize JPEG decompression object
\r
138 struct jpeg_decompress_struct cinfo;
\r
139 struct irr_jpeg_error_mgr jerr;
\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
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
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
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
159 jpeg_destroy_decompress(&cinfo);
\r
164 // return null pointer
\r
168 // Now we can initialize the JPEG decompression object.
\r
169 jpeg_create_decompress(&cinfo);
\r
171 // specify data source
\r
172 jpeg_source_mgr jsrc;
\r
174 // Set up data pointer
\r
175 jsrc.bytes_in_buffer = file->getSize();
\r
176 jsrc.next_input_byte = (JOCTET*)input;
\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
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
190 // read file parameters with jpeg_read_header()
\r
191 jpeg_read_header(&cinfo, TRUE);
\r
193 bool useCMYK=false;
\r
194 if (cinfo.jpeg_color_space==JCS_CMYK)
\r
196 cinfo.out_color_space=JCS_CMYK;
\r
197 cinfo.out_color_components=4;
\r
202 cinfo.out_color_space=JCS_RGB;
\r
203 cinfo.out_color_components=3;
\r
205 cinfo.output_gamma=2.2;
\r
206 cinfo.do_fancy_upsampling=FALSE;
\r
208 // reject unreasonable sizes
\r
209 if (!checkImageDimensions(cinfo.image_width, cinfo.image_height))
\r
210 longjmp(jerr.setjmp_buffer, 1);
\r
212 // Start decompressor
\r
213 jpeg_start_decompress(&cinfo);
\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
220 // Allocate memory for buffer
\r
221 u8* output = new u8[rowspan * height];
\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
228 for( u32 i = 0; i < height; i++ )
\r
229 rowPtr[i] = &output[ i * rowspan ];
\r
233 while( cinfo.output_scanline < cinfo.output_height )
\r
234 rowsRead += jpeg_read_scanlines( &cinfo, &rowPtr[rowsRead], cinfo.output_height - rowsRead );
\r
237 // Finish decompression
\r
239 jpeg_finish_decompress(&cinfo);
\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
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
255 for (u32 i=0,j=0; i<size; i+=3, j+=4)
\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
269 image = new CImage(ECF_R8G8B8,
\r
270 core::dimension2d<u32>(width, height), output);
\r
279 //! creates a loader which is able to load jpeg images
\r
280 IImageLoader* createImageLoaderJPG()
\r
282 return new CImageLoaderJPG();
\r
285 } // end namespace video
\r
286 } // end namespace irr
\r