287 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (C) 2002-2012 Nikolaus Gebhardt
 | |
| // This file is part of the "Irrlicht Engine".
 | |
| // For conditions of distribution and use, see copyright notice in irrlicht.h
 | |
| 
 | |
| #include "CImageLoaderJPG.h"
 | |
| 
 | |
| #include "IReadFile.h"
 | |
| #include "CImage.h"
 | |
| #include "os.h"
 | |
| #include "irrString.h"
 | |
| 
 | |
| namespace irr
 | |
| {
 | |
| namespace video
 | |
| {
 | |
| 
 | |
| //! constructor
 | |
| CImageLoaderJPG::CImageLoaderJPG()
 | |
| {
 | |
| 	#ifdef _DEBUG
 | |
| 	setDebugName("CImageLoaderJPG");
 | |
| 	#endif
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| //! destructor
 | |
| CImageLoaderJPG::~CImageLoaderJPG()
 | |
| {
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| //! returns true if the file maybe is able to be loaded by this class
 | |
| //! based on the file extension (e.g. ".tga")
 | |
| bool CImageLoaderJPG::isALoadableFileExtension(const io::path& filename) const
 | |
| {
 | |
| 	return core::hasFileExtension ( filename, "jpg", "jpeg" );
 | |
| }
 | |
| 
 | |
| // struct for handling jpeg errors
 | |
| struct irr_jpeg_error_mgr
 | |
| {
 | |
| 	// public jpeg error fields
 | |
| 	struct jpeg_error_mgr pub;
 | |
| 
 | |
| 	// for longjmp, to return to caller on a fatal error
 | |
| 	jmp_buf setjmp_buffer;
 | |
| 
 | |
| 	// for having access to the filename when printing the error messages
 | |
| 	core::stringc* filename;
 | |
| };
 | |
| 
 | |
| void CImageLoaderJPG::init_source (j_decompress_ptr cinfo)
 | |
| {
 | |
| 	// DO NOTHING
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| boolean CImageLoaderJPG::fill_input_buffer (j_decompress_ptr cinfo)
 | |
| {
 | |
| 	// DO NOTHING
 | |
| 	return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void CImageLoaderJPG::skip_input_data (j_decompress_ptr cinfo, long count)
 | |
| {
 | |
| 	jpeg_source_mgr * src = cinfo->src;
 | |
| 	if(count > 0)
 | |
| 	{
 | |
| 		src->bytes_in_buffer -= count;
 | |
| 		src->next_input_byte += count;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void CImageLoaderJPG::term_source (j_decompress_ptr cinfo)
 | |
| {
 | |
| 	// DO NOTHING
 | |
| }
 | |
| 
 | |
| 
 | |
| void CImageLoaderJPG::error_exit (j_common_ptr cinfo)
 | |
| {
 | |
| 	// unfortunately we need to use a goto rather than throwing an exception
 | |
| 	// as gcc crashes under linux crashes when using throw from within
 | |
| 	// extern c code
 | |
| 
 | |
| 	// Always display the message
 | |
| 	(*cinfo->err->output_message) (cinfo);
 | |
| 
 | |
| 	// cinfo->err really points to a irr_error_mgr struct
 | |
| 	irr_jpeg_error_mgr *myerr = (irr_jpeg_error_mgr*) cinfo->err;
 | |
| 
 | |
| 	longjmp(myerr->setjmp_buffer, 1);
 | |
| }
 | |
| 
 | |
| 
 | |
| void CImageLoaderJPG::output_message(j_common_ptr cinfo)
 | |
| {
 | |
| 	// display the error message.
 | |
| 	c8 temp1[JMSG_LENGTH_MAX];
 | |
| 	(*cinfo->err->format_message)(cinfo, temp1);
 | |
| 	core::stringc errMsg("JPEG FATAL ERROR in ");
 | |
| 
 | |
| 	irr_jpeg_error_mgr* myerr = (irr_jpeg_error_mgr*)cinfo->err;
 | |
| 	errMsg += *myerr->filename;
 | |
| 	os::Printer::log(errMsg.c_str(),temp1, ELL_ERROR);
 | |
| }
 | |
| 
 | |
| //! returns true if the file maybe is able to be loaded by this class
 | |
| bool CImageLoaderJPG::isALoadableFileFormat(io::IReadFile* file) const
 | |
| {
 | |
| 	if (!(file && file->seek(0)))
 | |
| 		return false;
 | |
| 	unsigned char header[3];
 | |
| 	size_t headerLen = file->read(header, sizeof(header));
 | |
| 	return headerLen >= 3 && !memcmp(header, "\xFF\xD8\xFF", 3);
 | |
| }
 | |
| 
 | |
| //! creates a surface from the file
 | |
| IImage* CImageLoaderJPG::loadImage(io::IReadFile* file) const
 | |
| {
 | |
| 	if (!file)
 | |
| 		return 0;
 | |
| 
 | |
| 	core::stringc filename = file->getFileName();
 | |
| 
 | |
| 	u8 **rowPtr=0;
 | |
| 	u8* input = new u8[file->getSize()];
 | |
| 	file->read(input, file->getSize());
 | |
| 
 | |
| 	// allocate and initialize JPEG decompression object
 | |
| 	struct jpeg_decompress_struct cinfo;
 | |
| 	struct irr_jpeg_error_mgr jerr;
 | |
| 
 | |
| 	//We have to set up the error handler first, in case the initialization
 | |
| 	//step fails.  (Unlikely, but it could happen if you are out of memory.)
 | |
| 	//This routine fills in the contents of struct jerr, and returns jerr's
 | |
| 	//address which we place into the link field in cinfo.
 | |
| 
 | |
| 	cinfo.err = jpeg_std_error(&jerr.pub);
 | |
| 	cinfo.err->error_exit = error_exit;
 | |
| 	cinfo.err->output_message = output_message;
 | |
| 	jerr.filename = &filename;
 | |
| 
 | |
| 	// compatibility fudge:
 | |
| 	// we need to use setjmp/longjmp for error handling as gcc-linux
 | |
| 	// crashes when throwing within external c code
 | |
| 	if (setjmp(jerr.setjmp_buffer))
 | |
| 	{
 | |
| 		// If we get here, the JPEG code has signaled an error.
 | |
| 		// We need to clean up the JPEG object and return.
 | |
| 
 | |
| 		jpeg_destroy_decompress(&cinfo);
 | |
| 
 | |
| 		delete [] input;
 | |
| 		delete [] rowPtr;
 | |
| 
 | |
| 		// return null pointer
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	// Now we can initialize the JPEG decompression object.
 | |
| 	jpeg_create_decompress(&cinfo);
 | |
| 
 | |
| 	// specify data source
 | |
| 	jpeg_source_mgr jsrc;
 | |
| 
 | |
| 	// Set up data pointer
 | |
| 	jsrc.bytes_in_buffer = file->getSize();
 | |
| 	jsrc.next_input_byte = (JOCTET*)input;
 | |
| 	cinfo.src = &jsrc;
 | |
| 
 | |
| 	jsrc.init_source = init_source;
 | |
| 	jsrc.fill_input_buffer = fill_input_buffer;
 | |
| 	jsrc.skip_input_data = skip_input_data;
 | |
| 	jsrc.resync_to_restart = jpeg_resync_to_restart;
 | |
| 	jsrc.term_source = term_source;
 | |
| 
 | |
| 	// Decodes JPG input from whatever source
 | |
| 	// Does everything AFTER jpeg_create_decompress
 | |
| 	// and BEFORE jpeg_destroy_decompress
 | |
| 	// Caller is responsible for arranging these + setting up cinfo
 | |
| 
 | |
| 	// read file parameters with jpeg_read_header()
 | |
| 	jpeg_read_header(&cinfo, TRUE);
 | |
| 
 | |
| 	bool useCMYK=false;
 | |
| 	if (cinfo.jpeg_color_space==JCS_CMYK)
 | |
| 	{
 | |
| 		cinfo.out_color_space=JCS_CMYK;
 | |
| 		cinfo.out_color_components=4;
 | |
| 		useCMYK=true;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		cinfo.out_color_space=JCS_RGB;
 | |
| 		cinfo.out_color_components=3;
 | |
| 	}
 | |
| 	cinfo.output_gamma=2.2;
 | |
| 	cinfo.do_fancy_upsampling=FALSE;
 | |
| 
 | |
| 	// reject unreasonable sizes
 | |
| 	if (!checkImageDimensions(cinfo.image_width, cinfo.image_height))
 | |
| 		longjmp(jerr.setjmp_buffer, 1);
 | |
| 
 | |
| 	// Start decompressor
 | |
| 	jpeg_start_decompress(&cinfo);
 | |
| 
 | |
| 	// Get image data
 | |
| 	u32 rowspan = cinfo.image_width * cinfo.out_color_components;
 | |
| 	u32 width = cinfo.image_width;
 | |
| 	u32 height = cinfo.image_height;
 | |
| 
 | |
| 	// Allocate memory for buffer
 | |
| 	u8* output = new u8[rowspan * height];
 | |
| 
 | |
| 	// Here we use the library's state variable cinfo.output_scanline as the
 | |
| 	// loop counter, so that we don't have to keep track ourselves.
 | |
| 	// Create array of row pointers for lib
 | |
| 	rowPtr = new u8* [height];
 | |
| 
 | |
| 	for( u32 i = 0; i < height; i++ )
 | |
| 		rowPtr[i] = &output[ i * rowspan ];
 | |
| 
 | |
| 	u32 rowsRead = 0;
 | |
| 
 | |
| 	while( cinfo.output_scanline < cinfo.output_height )
 | |
| 		rowsRead += jpeg_read_scanlines( &cinfo, &rowPtr[rowsRead], cinfo.output_height - rowsRead );
 | |
| 
 | |
| 	delete [] rowPtr;
 | |
| 	// Finish decompression
 | |
| 
 | |
| 	jpeg_finish_decompress(&cinfo);
 | |
| 
 | |
| 	// Release JPEG decompression object
 | |
| 	// This is an important step since it will release a good deal of memory.
 | |
| 	jpeg_destroy_decompress(&cinfo);
 | |
| 
 | |
| 	// convert image
 | |
| 	IImage* image = 0;
 | |
| 	if (useCMYK)
 | |
| 	{
 | |
| 		image = new CImage(ECF_R8G8B8,
 | |
| 				core::dimension2d<u32>(width, height));
 | |
| 		const u32 size = 3*width*height;
 | |
| 		u8* data = (u8*)image->getData();
 | |
| 		if (data)
 | |
| 		{
 | |
| 			for (u32 i=0,j=0; i<size; i+=3, j+=4)
 | |
| 			{
 | |
| 				// Also works without K, but has more contrast with K multiplied in
 | |
| //				data[i+0] = output[j+2];
 | |
| //				data[i+1] = output[j+1];
 | |
| //				data[i+2] = output[j+0];
 | |
| 				data[i+0] = (char)(output[j+2]*(output[j+3]/255.f));
 | |
| 				data[i+1] = (char)(output[j+1]*(output[j+3]/255.f));
 | |
| 				data[i+2] = (char)(output[j+0]*(output[j+3]/255.f));
 | |
| 			}
 | |
| 		}
 | |
| 		delete [] output;
 | |
| 	}
 | |
| 	else
 | |
| 		image = new CImage(ECF_R8G8B8,
 | |
| 				core::dimension2d<u32>(width, height), output);
 | |
| 
 | |
| 	delete [] input;
 | |
| 
 | |
| 	return image;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| //! creates a loader which is able to load jpeg images
 | |
| IImageLoader* createImageLoaderJPG()
 | |
| {
 | |
| 	return new CImageLoaderJPG();
 | |
| }
 | |
| 
 | |
| } // end namespace video
 | |
| } // end namespace irr
 |