forked from Mirrorlandia_minetest/irrlicht
76d013d9d6
Based somewhat on a patch in Minetest from sfan5 594de99153
There might be more problems which may be the reason they checked for other values in Minetest, but don't have more info for now and so far this works.
Forum: https://irrlicht.sourceforge.io/forum/viewtopic.php?f=2&t=52819&p=306518
git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6385 dfc29bdd-3216-0410-991c-e03cc46cb475
315 lines
7.6 KiB
C++
315 lines
7.6 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"
|
|
|
|
#ifdef _IRR_COMPILE_WITH_JPG_LOADER_
|
|
|
|
#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" );
|
|
}
|
|
|
|
|
|
#ifdef _IRR_COMPILE_WITH_LIBJPEG_
|
|
|
|
// 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);
|
|
}
|
|
#endif // _IRR_COMPILE_WITH_LIBJPEG_
|
|
|
|
//! returns true if the file maybe is able to be loaded by this class
|
|
bool CImageLoaderJPG::isALoadableFileFormat(io::IReadFile* file) const
|
|
{
|
|
#ifndef _IRR_COMPILE_WITH_LIBJPEG_
|
|
return false;
|
|
#else
|
|
|
|
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);
|
|
#endif
|
|
}
|
|
|
|
//! creates a surface from the file
|
|
IImage* CImageLoaderJPG::loadImage(io::IReadFile* file) const
|
|
{
|
|
#ifndef _IRR_COMPILE_WITH_LIBJPEG_
|
|
os::Printer::log("Can't load as not compiled with _IRR_COMPILE_WITH_LIBJPEG_:", file->getFileName(), ELL_DEBUG);
|
|
return 0;
|
|
#else
|
|
|
|
if (!file)
|
|
return 0;
|
|
|
|
core::stringc filename = file->getFileName();
|
|
long fileSize = file->getSize();
|
|
if ( fileSize < 3 )
|
|
return 0;
|
|
|
|
u8 **rowPtr=0;
|
|
u8* input = new u8[fileSize];
|
|
file->read(input, fileSize);
|
|
|
|
// 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 = fileSize;
|
|
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;
|
|
|
|
// 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;
|
|
|
|
if ( width > JPEG_MAX_DIMENSION || height > JPEG_MAX_DIMENSION )
|
|
{
|
|
os::Printer::log("Image dimensions too large for JPG in file", filename, ELL_WARNING);
|
|
longjmp(jerr.setjmp_buffer, 1);
|
|
}
|
|
|
|
|
|
// 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( size_t 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;
|
|
rowPtr = 0;
|
|
// 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;
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//! creates a loader which is able to load jpeg images
|
|
IImageLoader* createImageLoaderJPG()
|
|
{
|
|
return new CImageLoaderJPG();
|
|
}
|
|
|
|
} // end namespace video
|
|
} // end namespace irr
|
|
|
|
#endif
|
|
|