StelTextureMgr.cpp   StelTextureMgr.cpp 
skipping to change at line 23 skipping to change at line 23
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, U SA. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, U SA.
*/ */
#include "StelTextureMgr.hpp" #include "StelTextureMgr.hpp"
#include "StelFileMgr.hpp" #include "StelFileMgr.hpp"
#include "StelApp.hpp" #include "StelApp.hpp"
#include "StelMainGraphicsView.hpp"
extern "C" {
#include <jpeglib.h>
}
#include <png.h>
#include "StelUtils.hpp" #include "StelUtils.hpp"
#include "fixx11h.h"
#include <QHttp> #include <QHttp>
#include <QFileInfo> #include <QFileInfo>
#include <QFile> #include <QFile>
#include <QDebug> #include <QDebug>
#include <QThread> #include <QThread>
#include <QSettings> #include <QSettings>
#include <QGLFormat>
#include <cstdlib>
// Initialize statics
static PngLoader pngLoader;
static JpgLoader jpgLoader;
/*************************************************************************
Constructor for the StelTextureMgr class
*************************************************************************/
StelTextureMgr::StelTextureMgr() StelTextureMgr::StelTextureMgr()
{ {
// Init default values
setDefaultParams();
// Add default loaders
registerImageLoader("png", &pngLoader);
registerImageLoader("PNG", &pngLoader);
registerImageLoader("jpeg", &jpgLoader);
registerImageLoader("JPEG", &jpgLoader);
registerImageLoader("jpg", &jpgLoader);
registerImageLoader("JPG", &jpgLoader);
} }
/*************************************************************************
Destructor for the StelTextureMgr class
*************************************************************************/
StelTextureMgr::~StelTextureMgr() StelTextureMgr::~StelTextureMgr()
{ {
// Release the textures memory pool
TexMalloc::clear();
} }
/*************************************************************************
Initialize some variable from the openGL context.
Must be called after the creation of the openGL context.
*************************************************************************/
void StelTextureMgr::init() void StelTextureMgr::init()
{ {
// Get whether non-power-of-2 and non square 2D textures are support StelApp::getInstance().makeMainGLContextCurrent();
ed on this video card isNoPowerOfTwoAllowed = QGLFormat::openGLVersionFlags().testFlag(QGL
isNoPowerOfTwoAllowed = GLEE_ARB_texture_non_power_of_two || GLEE_VE Format::OpenGL_Version_2_0) || QGLFormat::openGLVersionFlags().testFlag(QGL
RSION_2_0; Format::OpenGL_ES_Version_2_0);
// Check vendor and renderer
QString glRenderer((char*)glGetString(GL_RENDERER));
QString glVendor((char*)glGetString(GL_VENDOR));
QString glVersion((char*)glGetString(GL_VERSION));
// Get Maximum Texture Size Supported by the video card
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
/*************************************************************************
Set default parameters for Mipmap mode, wrap mode, min and mag filters
*************************************************************************/
void StelTextureMgr::setDefaultParams()
{
setMipmapsMode();
setWrapMode();
setMinFilter();
setMagFilter();
setDynamicRangeMode();
}
/*************************************************************************
Internal
*************************************************************************/
StelTextureSP StelTextureMgr::initTex()
{
StelTextureSP tex(new StelTexture());
// Set parameters than can be set for this texture
tex->minFilter = ((mipmapsMode==true) ? GL_LINEAR_MIPMAP_NEAREST : m
inFilter);
tex->magFilter = magFilter;
tex->wrapMode = wrapMode;
tex->mipmapsMode = mipmapsMode;
tex->dynamicRangeMode = dynamicRangeMode;
return tex;
} }
/************************************************************************* StelTextureSP StelTextureMgr::createTexture(const QString& afilename, const
Load an image from a file and create a new texture from it. StelTexture::StelTextureParams& params)
*************************************************************************/
StelTextureSP StelTextureMgr::createTexture(const QString& afilename)
{ {
if (afilename.isEmpty()) if (afilename.isEmpty())
return StelTextureSP(); return StelTextureSP();
StelTextureSP tex = initTex(); StelTextureSP tex = StelTextureSP(new StelTexture());
try try
{ {
tex->fullPath = StelApp::getInstance().getFileMgr().findFile (afilename); tex->fullPath = StelFileMgr::findFile(afilename);
} }
catch (std::runtime_error e) catch (std::runtime_error e)
{ {
try try
{ {
tex->fullPath = StelApp::getInstance().getFileMgr(). findFile("textures/" + afilename); tex->fullPath = StelFileMgr::findFile("textures/" + afilename);
} }
catch (std::runtime_error er) catch (std::runtime_error er)
{ {
qWarning() << "WARNING : Can't find texture file " < < afilename << ": " << er.what() << endl; qWarning() << "WARNING : Can't find texture file " < < afilename << ": " << er.what() << endl;
tex->errorOccured = true; tex->errorOccured = true;
return StelTextureSP(); return StelTextureSP();
} }
} }
tex->qImage = QImage(tex->fullPath);
if (tex->qImage.isNull())
return StelTextureSP();
tex->loadParams = params;
tex->downloaded = true; tex->downloaded = true;
if (tex->fullPath.startsWith(":/")) if (tex->glLoad())
{
QFile tmpF(tex->fullPath);
tmpF.open(QIODevice::ReadOnly);
tex->downloadedData = tmpF.readAll();
}
// Simply load everything
if (tex->imageLoad() && tex->glLoad())
return tex; return tex;
else else
return StelTextureSP(); return StelTextureSP();
} }
/************************************************************************* StelTextureSP StelTextureMgr::createTextureThread(const QString& url, const
Load an image from a file and create a new texture from it in a new thread StelTexture::StelTextureParams& params, const QString& fileExtension, bool
. lazyLoading)
*************************************************************************/
StelTextureSP StelTextureMgr::createTextureThread(const QString& url, const
QString& fileExtension, bool lazyLoading)
{ {
if (url.isEmpty()) if (url.isEmpty())
return StelTextureSP(); return StelTextureSP();
StelTextureSP tex = initTex(); StelTextureSP tex = StelTextureSP(new StelTexture());
tex->loadParams = params;
if (!url.startsWith("http://")) if (!url.startsWith("http://"))
{ {
// Assume a local file // Assume a local file
try try
{ {
tex->fullPath = StelApp::getInstance().getFileMgr(). findFile(url); tex->fullPath = StelFileMgr::findFile(url);
} }
catch (std::runtime_error e) catch (std::runtime_error e)
{ {
try try
{ {
tex->fullPath = StelApp::getInstance().getFi leMgr().findFile("textures/" + url); tex->fullPath = StelFileMgr::findFile("textu res/" + url);
} }
catch (std::runtime_error er) catch (std::runtime_error er)
{ {
qWarning() << "WARNING : Can't find texture file " << url << ": " << er.what() << endl; qWarning() << "WARNING : Can't find texture file " << url << ": " << er.what() << endl;
tex->errorOccured = true; tex->errorOccured = true;
return StelTextureSP(); return StelTextureSP();
} }
} }
tex->downloaded = true; tex->downloaded = true;
} }
skipping to change at line 204 skipping to change at line 130
if (idx!=-1) if (idx!=-1)
tex->fileExtension = url.right(url.size()-id x-1); tex->fileExtension = url.right(url.size()-id x-1);
} }
} }
if (!fileExtension.isEmpty()) if (!fileExtension.isEmpty())
tex->fileExtension = fileExtension; tex->fileExtension = fileExtension;
if (!lazyLoading) if (!lazyLoading)
tex->bind(); tex->bind();
return tex; return tex;
} }
/*************************************************************************
Adapt the scaling for the texture. Return true if there was no errors
This method is re-entrant
*************************************************************************/
bool StelTextureMgr::reScale(StelTexture* tex)
{
const unsigned int nbPix = tex->width*tex->height;
const int bitpix = tex->internalFormat*8;
// Scale input image according to the dynamic range parameters
if (tex->format==GL_LUMINANCE)
{
switch (tex->dynamicRangeMode)
{
case (StelTextureTypes::Linear):
{
if (tex->internalFormat==1)
{
// Assumes already GLubyte = unsigne
d char on 8 bits
return true;
}
if (tex->internalFormat==2)
{
return true;
}
if (tex->internalFormat==4)
{
return true;
}
if (tex->internalFormat==-4)
{
return true;
}
// Unsupported format..
qWarning() << "Internal format: " << tex->in
ternalFormat << " is not supported for LUMINANCE texture " << tex->fullPath
;
return false;
}
case (StelTextureTypes::MinmaxQuantile):
{
// Compute the image histogram
int* histo = (int*)calloc(sizeof(int), 1<<bi
tpix);
if (tex->internalFormat==1)
{
// We assume unsigned char = GLubyte
GLubyte* data = (GLubyte*)tex->texel
s;
for (unsigned int i = 0; i <nbPix ;
++i)
++(histo[(int)data[i]]);
}
else if (tex->internalFormat==2)
{
// We assume short unsigned int = GL
ushort
GLushort* data = (GLushort*)tex->tex
els;
for (unsigned int i = 0; i <nbPix; +
+i)
++(histo[(int)data[i]]);
}
else
{
// Unsupported format..
qWarning() << "Internal format: " <<
tex->internalFormat << " is not supported for LUMINANCE texture " << tex->
fullPath;
return false;
}
// From the histogram, compute the Quantile
cut at values minQuantile and maxQuantile
const double minQuantile = 0.01;
const double maxQuantile = 0.99;
int minCut=0, maxCut=0;
int thresh = (int)(minQuantile*nbPix);
int minI = 0;
// Start from 1 to ignore zeroed region in t
he image
for (int idd=1;idd<1<<bitpix;++idd)
{
minI+=histo[idd];
if (minI>=thresh)
{
minCut = idd;
break;
}
}
thresh = (int)((1.-maxQuantile)*nbPix);
int maxI = 0;
// Finish at 1 to ignore zeroed region in th
e image
for (int idd=1<<bitpix;idd>=1;--idd)
{
maxI+=histo[idd];
if (maxI>=thresh)
{
maxCut = idd;
break;
}
}
free(histo);
qDebug() << "minCut=" << minCut << " maxCut=
" << maxCut;
if (tex->internalFormat==1)
{
double scaling = 255./(maxCut-minCut
);
GLubyte* data = (GLubyte*)tex->texel
s;
for (unsigned int i=0;i<nbPix;++i)
{
data[i] = (GLubyte)qMin((qMa
x((data[i]-minCut), 0)*scaling), 255.);
}
}
else if (tex->internalFormat==2 && tex->type
==GL_UNSIGNED_SHORT)
{
double scaling = 65535./(maxCut-minC
ut);
GLushort* data = (GLushort*)tex->tex
els;
for (unsigned int i=0;i<nbPix;++i)
data[i] = (GLushort)qMin((qM
ax(data[i]-minCut, 0)*scaling), 65535.);
}
else if (tex->internalFormat==2 && tex->type
==GL_SHORT)
{
double scaling = 65535./(maxCut-minC
ut);
GLshort* data = (GLshort*)tex->texel
s;
for (unsigned int i=0;i<nbPix;++i)
data[i] = (GLshort)qMin((qMa
x(data[i]-minCut, -32767)*scaling), 32767.);
}
else
{
// Unsupported format..
qWarning() << "Internal format: " <<
tex->internalFormat << " is not supported for LUMINANCE texture " << tex->
fullPath;
return false;
}
}
break;
default:
// Unsupported dynamic range mode..
qWarning() << "Dynamic range mode: " << (int
)tex->dynamicRangeMode << " is not supported by the texture manager, textur
e " << tex->fullPath << " will not be loaded.";
return false;
}
return true;
}
else
{
// TODO, no scaling is currently done for color images
return true;
}
}
/*************************************************************************
Load the image in memory by calling the associated ImageLoader
*************************************************************************/
bool StelTextureMgr::loadImage(StelTexture* tex)
{
if (tex->fileExtension.isEmpty())
tex->fileExtension = QFileInfo(tex->fullPath).suffix();
QMap<QString, ImageLoader*>::iterator loadFuncIter = imageLoaders.fi
nd(tex->fileExtension);
if (loadFuncIter==imageLoaders.end())
{
qWarning() << "Unsupported image file extension: " << tex->f
ileExtension << " for file: " << tex->fullPath;
tex->texels = NULL;
return false;
}
// Load the image
// Create a new texInfo with the proper parameters
ImageLoader::TexInfo texInfo;
texInfo.format = tex->format;
texInfo.width = tex->width;
texInfo.height = tex->height;
texInfo.type = tex->type;
texInfo.internalFormat = tex->internalFormat;
texInfo.texels = tex->texels;
texInfo.fullPath = tex->fullPath;
if (!loadFuncIter.value()->loadImage(tex->fullPath, texInfo))
{
qWarning() << "Image loading failed for file: " << tex->full
Path;
tex->texels = NULL;
return false;
}
// Update texture parameters from TexInfo
tex->format = texInfo.format;
tex->width = texInfo.width;
tex->height = texInfo.height;
tex->type = texInfo.type;
tex->internalFormat = texInfo.internalFormat;
tex->texels = texInfo.texels;
if (!tex->texels)
{
qWarning() << "Image loading returned empty texels for file:
" << tex->fullPath;
return false;
}
// Apply scaling for the image
if (reScale(tex)==false)
{
TexMalloc::free(tex->texels);
tex->texels = NULL;
return false;
}
// Repair texture size if non power of 2 is not allowed by the video
driver
if (!isNoPowerOfTwoAllowed && (!StelUtils::isPowerOfTwo(tex->height)
|| !StelUtils::isPowerOfTwo(tex->width)))
{
//qDebug() << "Can't load natively non power of 2 textures f
or texture: " << tex->fullPath;
int w = StelUtils::getBiggerPowerOfTwo(tex->width);
int h = StelUtils::getBiggerPowerOfTwo(tex->height);
//qDebug() << "Resize to " << w << "x" << h;
GLubyte* texels2 = (GLubyte *)calloc (sizeof (GLubyte) * tex
->internalFormat, w*h);
if (texels2==NULL)
{
qWarning() << "Insufficient memory for image data al
location: need to allocate array of "
<< w << "x" << h << " with " << sizeof (G
Lubyte) * tex->internalFormat
<< " bytes per pixels.";
TexMalloc::free(tex->texels);
tex->texels = NULL;
return false;
}
// Copy data into the power of two buffer
for (int j=0;j<tex->height;++j)
{
memcpy(&(texels2[j*w*tex->internalFormat]), &(tex->t
exels[j*tex->width*tex->internalFormat]), tex->width*tex->internalFormat);
}
// Copy the last line to all extra each lines to prevent pro
blems with interpolation on textures sides
for (int j=tex->height;j<h;++j)
{
memcpy(&(texels2[j*w*tex->internalFormat]), &(texels
2[(tex->height-1)*w*tex->internalFormat]), tex->width*tex->internalFormat);
}
if (w>tex->width)
{
for (int j=0;j<h;++j)
{
if (tex->internalFormat==1)
memset(&texels2[(j*w+tex->width)], t
exels2[(j*w+tex->width-1)], (w-tex->width));
else
{
for (int l=0;l<w-tex->width;++l)
memcpy(&texels2[(j*w+tex->wi
dth+l)*tex->internalFormat], &texels2[(j*w+tex->width-1)*tex->internalForma
t], tex->internalFormat);
}
}
}
// Update the texture coordinates because the new texture do
es not occupy the whole buffer
tex->texCoordinates[0].set((double)tex->width/w, 0.);
tex->texCoordinates[1].set(0., 0.);
tex->texCoordinates[2].set((double)tex->width/w, (double)tex
->height/h);
tex->texCoordinates[3].set(0., (double)tex->height/h);
tex->width = w;
tex->height = h;
TexMalloc::free(tex->texels);
tex->texels = texels2;
}
// Check that the image size is compatible with the hardware
if (tex->width>maxTextureSize)
{
qWarning() << "Warning: texture " << tex->fullPath << " is l
arger than " << maxTextureSize << " pixels and might be not supported.";
}
// The glLoading will be done later in the main thread
return true;
}
/*************************************************************************
Load a PNG image from a file.
Code borrowed from David HENRY with the following copyright notice:
* png.c -- png texture loader
* last modification: feb. 5, 2006
*
* Copyright (c) 2005-2006 David HENRY
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*************************************************************************/
bool PngLoader::loadImage(const QString& filename, TexInfo& texinfo)
{
png_byte magic[8];
png_structp png_ptr;
png_infop info_ptr;
int bit_depth, color_type;
FILE *fp = NULL;
png_bytep *row_pointers = NULL;
int i;
/* open image file */
fp = fopen (QFile::encodeName(filename).constData(), "rb");
if (!fp)
{
qWarning() << "error: couldn't open \""<< filename << "\"!";
return false;
}
/* read magic number */
fread (magic, 1, sizeof (magic), fp);
/* check for valid magic number */
if (!png_check_sig (magic, sizeof (magic)))
{
qWarning() << "error: \"" << filename << "\" is not a valid
PNG image!";
fclose (fp);
return false;
}
/* create a png read struct */
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL,
NULL);
if (!png_ptr)
{
fclose (fp);
return false;
}
/* create a png info struct */
info_ptr = png_create_info_struct (png_ptr);
if (!info_ptr)
{
fclose (fp);
png_destroy_read_struct (&png_ptr, NULL, NULL);
return false;
}
/* initialize the setjmp for returning properly after a libpng
error occured */
if (setjmp (png_jmpbuf (png_ptr)))
{
fclose (fp);
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
if (row_pointers)
free (row_pointers);
if (texinfo.texels)
TexMalloc::free (texinfo.texels);
texinfo.texels = NULL;
qWarning() << "There was an error while loading PNG image: "
<< texinfo.fullPath;
return false;
}
/* setup libpng for using standard C fread() function
with our FILE pointer */
png_init_io (png_ptr, fp);
/* tell libpng that we have already read the magic number */
png_set_sig_bytes (png_ptr, sizeof (magic));
/* read png info */
png_read_info (png_ptr, info_ptr);
/* get some usefull information from header */
bit_depth = png_get_bit_depth (png_ptr, info_ptr);
color_type = png_get_color_type (png_ptr, info_ptr);
/* convert index color images to RGB images */
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb (png_ptr);
/* convert 1-2-4 bits grayscale images to 8 bits
grayscale. */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_gray_1_2_4_to_8 (png_ptr);
if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha (png_ptr);
if (bit_depth == 16)
png_set_strip_16 (png_ptr);
else if (bit_depth < 8)
png_set_packing (png_ptr);
/* update info structure to apply transformations */
png_read_update_info (png_ptr, info_ptr);
/* retrieve updated information */
// johannes: never make pointer casts between pointers to int and lo
ng,
// because long is 64 bit on AMD64:
// (png_uint_32*)(&texinfo.width)
// is evel, and does not work for AMD64
png_uint_32 width,height;
png_get_IHDR (png_ptr, info_ptr,
&width,
&height,
&bit_depth, &color_type,
NULL, NULL, NULL);
texinfo.width = width;
texinfo.height = height;
/* get image format and components per pixel */
switch (color_type)
{
case PNG_COLOR_TYPE_GRAY:
texinfo.format = GL_LUMINANCE;
texinfo.internalFormat = 1;
texinfo.type = GL_UNSIGNED_BYTE;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
texinfo.format = GL_LUMINANCE_ALPHA;
texinfo.internalFormat = 2;
texinfo.type = GL_UNSIGNED_BYTE;
break;
case PNG_COLOR_TYPE_RGB:
texinfo.format = GL_RGB;
texinfo.internalFormat = 3;
texinfo.type = GL_UNSIGNED_BYTE;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
texinfo.format = GL_RGBA;
texinfo.internalFormat = 4;
texinfo.type = GL_UNSIGNED_BYTE;
break;
default:
// Badness
Q_ASSERT(0);
}
/* we can now allocate memory for storing pixel data */
texinfo.texels = (GLubyte *)TexMalloc::malloc (sizeof (GLubyte) * te
xinfo.width * texinfo.height * texinfo.internalFormat);
if (!texinfo.texels)
{
qWarning() << "Not enough memory to allocate the PNG image t
exture " << texinfo.fullPath;
fclose (fp);
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return false;
}
/* setup a pointer array. Each one points at the begining of a row.
*/
row_pointers = (png_bytep *)malloc (sizeof (png_bytep) * texinfo.hei
ght);
if (!row_pointers)
{
qWarning() << "Not enough memory to create the PNG image tex
ture " << texinfo.fullPath;
fclose (fp);
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return false;
}
for (i = 0; i < texinfo.height; ++i)
{
row_pointers[i] = (png_bytep)(texinfo.texels +
((texinfo.height - (i + 1)) *
texinfo.width * texinfo.internalFormat));
}
/* read pixel data using row pointers */
png_read_image (png_ptr, row_pointers);
/* finish decompression and release memory */
png_read_end (png_ptr, NULL);
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
/* we don't need row pointers anymore */
free (row_pointers);
fclose (fp);
return true;
}
/*************************************************************************
Load a JPG image from a file.
Code borrowed from David HENRY with the following copyright notice:
* jpeg.c -- jpeg texture loader
* last modification: feb. 9, 2006
*
* Copyright (c) 2005-2006 David HENRY
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* jpeg error manager */
struct my_error_mgr
{
/* "public" fields */
struct jpeg_error_mgr pub;
/* for return to caller */
jmp_buf setjmp_buffer;
};
void err_exit(j_common_ptr cinfo)
{
/* get error manager */
my_error_mgr* jerr = (my_error_mgr*)(cinfo->err);
/* display error message */
(*cinfo->err->output_message) (cinfo);
/* return control to the setjmp point */
longjmp (jerr->setjmp_buffer, 1);
}
bool JpgLoader::loadImage(const QString& filename, TexInfo& texinfo)
{
FILE *fp = NULL;
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
/* open image file */
fp = fopen(QFile::encodeName(filename).constData(), "rb");
if (!fp)
{
qWarning() << "Error: couldn't open \"" << filename << "\"!\
n";
return false;
}
/* create and configure decompressor */
jpeg_create_decompress (&cinfo);
cinfo.err = jpeg_std_error (&jerr.pub);
jpeg_stdio_src (&cinfo, fp);
/* configure error manager */
jerr.pub.error_exit = err_exit;
texinfo.texels = NULL;
if (setjmp(jerr.setjmp_buffer))
{
qWarning() << "Error: couldn't read jpeg file \"" << filenam
e << "\"!";
jpeg_destroy_decompress(&cinfo);
if (texinfo.texels)
{
free(texinfo.texels);
texinfo.texels = NULL;
}
fclose(fp);
return false;
}
/*
* NOTE: this is the simplest "readJpegFile" function. There
* is no advanced error handling. It would be a good idea to
* setup an error manager with a setjmp/longjmp mechanism.
* In this function, if an error occurs during reading the JPEG
* file, the libjpeg aborts the program.
* See jpeg_mem.c (or RTFM) for an advanced error handling which
* prevent this kind of behavior (http://tfc.duke.free.fr)
*/
/* read header and prepare for decompression */
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress (&cinfo);
/* initialize image's member variables */
texinfo.width = cinfo.image_width;
texinfo.height = cinfo.image_height;
texinfo.internalFormat = cinfo.num_components;
texinfo.type = GL_UNSIGNED_BYTE;
if (cinfo.num_components == 1)
texinfo.format = GL_LUMINANCE;
else
texinfo.format = GL_RGB;
texinfo.texels = (GLubyte *)malloc (sizeof (GLubyte) * texinfo.width
* texinfo.height * texinfo.inter
nalFormat);
/* extract each scanline of the image */
for (int i = 0; i < texinfo.height; ++i)
{
JSAMPROW j = (texinfo.texels +
((texinfo.height - (i + 1)) * texinfo.width * texinfo.i
nternalFormat));
jpeg_read_scanlines (&cinfo, &j, 1);
}
/* finish decompression and release memory */
jpeg_finish_decompress (&cinfo);
jpeg_destroy_decompress (&cinfo);
fclose(fp);
return true;
}
/*************************************************************************
Load a JPG image from a buffer.
Code borrowed from David HENRY with the following copyright notice:
* jpeg.c -- jpeg texture loader
* last modification: feb. 9, 2006
*
* Copyright (c) 2005-2006 David HENRY
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
void mem_init_source(j_decompress_ptr cinfo)
{
/* Nothing to do */
}
boolean mem_fill_input_buffer(j_decompress_ptr cinfo)
{
JOCTET eoi_buffer[2] = { 0xFF, JPEG_EOI };
struct jpeg_source_mgr *jsrc = cinfo->src;
/* Create a fake EOI marker */
jsrc->next_input_byte = eoi_buffer;
jsrc->bytes_in_buffer = 2;
return TRUE;
}
void mem_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
{
struct jpeg_source_mgr *jsrc = cinfo->src;
if (num_bytes > 0)
{
while (num_bytes > (long)jsrc->bytes_in_buffer)
{
num_bytes -= (long)jsrc->bytes_in_buffer;
mem_fill_input_buffer (cinfo);
}
jsrc->next_input_byte += num_bytes;
jsrc->bytes_in_buffer -= num_bytes;
}
}
void mem_term_source (j_decompress_ptr cinfo)
{
/* Nothing to do */
}
bool JpgLoader::loadFromMemory(const QByteArray& data, TexInfo& texinfo)
{
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
struct jpeg_source_mgr jsrc;
JSAMPROW j;
int i;
/* Create and configure decompress object */
jpeg_create_decompress (&cinfo);
cinfo.err = jpeg_std_error (&jerr.pub);
cinfo.src = &jsrc;
/* Configure error manager */
jerr.pub.error_exit = err_exit;
if (setjmp (jerr.setjmp_buffer))
{
qWarning() << "Error: couldn't read jpeg data!";
jpeg_destroy_decompress(&cinfo);
if (texinfo.texels)
{
TexMalloc::free(texinfo.texels);
texinfo.texels = NULL;
}
return false;
}
/* Configure source manager */
jsrc.next_input_byte = (const JOCTET*)data.constData();
jsrc.bytes_in_buffer = data.length();
jsrc.init_source = mem_init_source;
jsrc.fill_input_buffer = mem_fill_input_buffer;
jsrc.skip_input_data = mem_skip_input_data;
jsrc.resync_to_restart = jpeg_resync_to_restart;
jsrc.term_source = mem_term_source;
/* Read file's header and prepare for decompression */
jpeg_read_header (&cinfo, TRUE);
jpeg_start_decompress (&cinfo);
/* Initialize image's member variables and allocate memory for pixel
s */
texinfo.width = cinfo.image_width;
texinfo.height = cinfo.image_height;
texinfo.internalFormat = cinfo.num_components;
texinfo.format = (cinfo.num_components == 1) ? GL_LUMINANCE : GL_RGB
;
texinfo.texels = (GLubyte *)TexMalloc::malloc (sizeof (GLubyte) * te
xinfo.width * texinfo.height * texinfo.internalFormat);
// qWarning() << texinfo.width << texinfo.height << texinfo.internal
Format << texinfo.format;
/* Read scanlines */
for (i = 0; i < texinfo.height; ++i)
{
j = texinfo.texels + ((texinfo.height - (i + 1)) * texinfo.w
idth * texinfo.internalFormat);
jpeg_read_scanlines (&cinfo, &j, 1);
}
/* Finish decompression and release memory */
jpeg_finish_decompress (&cinfo);
jpeg_destroy_decompress (&cinfo);
return true;
}
// Multithread memory pool for textures loading
QMultiMap<size_t, void*> TexMalloc::cache;
QMap<void*, size_t> TexMalloc::newInsert;
QMutex TexMalloc::mutex;
void* TexMalloc::malloc(size_t size)
{
QMutexLocker lock(&mutex);
// static int tot = 0;
QMultiMap<size_t, void*>::iterator i = cache.find(size);
if (i==cache.end())
{
// tot += size;
//qWarning() << "Added " << size << "/" << tot;
void* buf = std::malloc(size);
newInsert.insert(buf, size);
return buf;
}
else
{
//qWarning() << "Reused " << size << "/" << tot;
void* buf = i.value();
cache.erase(i);
return buf;
}
}
// If a block with the same size already is in the pool, just free it else,
store it for a later re-use
void TexMalloc::free(void *ptr)
{
QMutexLocker lock(&mutex);
QMap<void*, size_t>::iterator i = newInsert.find(ptr);
if (i==newInsert.end())
{
std::free(ptr);
return;
}
else
{
cache.insert(i.value(), ptr);
}
}
// Clear cache and delete all blocks
void TexMalloc::clear()
{
QMutexLocker lock(&mutex);
for (QMap<void*, size_t>::iterator i = newInsert.begin();i!=newInser
t.end();++i)
std::free(i.key());
newInsert.clear();
}
 End of changes. 20 change blocks. 
98 lines changed or deleted 24 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/