StelTexture.cpp   StelTexture.cpp 
skipping to change at line 37 skipping to change at line 37
#include <QImageReader> #include <QImageReader>
#include <QSize> #include <QSize>
#include <QDebug> #include <QDebug>
#include <QUrl> #include <QUrl>
#include <QImage> #include <QImage>
#include <QNetworkReply> #include <QNetworkReply>
#include <QtEndian> #include <QtEndian>
#include <QFuture> #include <QFuture>
#include <QtConcurrent> #include <QtConcurrent>
#include <cstdlib> StelTexture::StelTexture(StelTextureMgr *mgr) : textureMgr(mgr), gl(Q_NULLP
TR), networkReply(Q_NULLPTR), loader(Q_NULLPTR), errorOccured(false), alpha
StelTexture::StelTexture(StelTextureMgr *mgr) : textureMgr(mgr), networkRep Channel(false), id(0),
ly(NULL), loader(NULL), errorOccured(false), alphaChannel(false), id(0), av
gLuminance(-1.f),
width(-1), height(-1), glSize(0) width(-1), height(-1), glSize(0)
{ {
} }
StelTexture::~StelTexture() StelTexture::~StelTexture()
{ {
if (id != 0) if (id != 0)
{ {
//make sure the correct GL context is bound! //make sure the correct GL context is bound!
StelApp::getInstance().ensureGLContextCurrent(); StelApp::getInstance().ensureGLContextCurrent();
if (glIsTexture(id)==GL_FALSE) if (gl->glIsTexture(id)==GL_FALSE)
{ {
GLenum err = glGetError(); GLenum err = gl->glGetError();
qWarning() << "WARNING: in StelTexture::~StelTexture () tried to delete invalid texture with ID=" << id << "Current GL ERROR sta tus is" << err << "(" << StelOpenGL::getGLErrorText(err) << ")"; qWarning() << "WARNING: in StelTexture::~StelTexture () tried to delete invalid texture with ID=" << id << "Current GL ERROR sta tus is" << err << "(" << StelOpenGL::getGLErrorText(err) << ")";
} }
else else
{ {
glDeleteTextures(1, &id); gl->glDeleteTextures(1, &id);
textureMgr->glMemoryUsage -= glSize; textureMgr->glMemoryUsage -= glSize;
textureMgr->idMap.remove(id);
glSize = 0; glSize = 0;
} }
#ifndef NDEBUG #ifndef NDEBUG
qDebug()<<"Deleted StelTexture"<<id<<", total memory usage " <<textureMgr->glMemoryUsage / (1024.0 * 1024.0)<<"MB"; qDebug()<<"Deleted StelTexture"<<id<<", total memory usage " <<textureMgr->glMemoryUsage / (1024.0 * 1024.0)<<"MB";
#endif #endif
id = 0; id = 0;
} }
else if (id) else if (id)
{ {
qWarning()<<"Cannot delete texture"<<id<<", no GL context"; qWarning()<<"Cannot delete texture"<<id<<", no GL context";
} }
if (networkReply) if (networkReply)
{ {
networkReply->abort(); networkReply->abort();
//networkReply->deleteLater(); //networkReply->deleteLater();
delete networkReply; delete networkReply;
networkReply = NULL; networkReply = Q_NULLPTR;
} }
if (loader != NULL) { if (loader != Q_NULLPTR) {
delete loader; delete loader;
loader = NULL; loader = Q_NULLPTR;
}
}
void StelTexture::wrapGLTexture(GLuint texId)
{
gl = QOpenGLContext::currentContext()->functions();
bool valid = gl->glIsTexture(texId);
if(valid)
{
id = texId;
//Note: there is no way to retrieve texture width/height on
OpenGL ES
//so the members will be wrong
//also we can't estimate memory usage because of this
}
else
{
errorMessage="No valid OpenGL texture name";
errorOccured=true;
} }
} }
/************************************************************************* /*************************************************************************
This method should be called if the texture loading failed for any reasons This method should be called if the texture loading failed for any reasons
*************************************************************************/ *************************************************************************/
void StelTexture::reportError(const QString& aerrorMessage) void StelTexture::reportError(const QString& aerrorMessage)
{ {
errorOccured = true; errorOccured = true;
errorMessage = aerrorMessage; errorMessage = aerrorMessage;
skipping to change at line 111 skipping to change at line 128
ret.height = image.height(); ret.height = image.height();
ret.data = convertToGLFormat(image, &ret.format, &ret.type); ret.data = convertToGLFormat(image, &ret.format, &ret.type);
return ret; return ret;
} }
/************************************************************************* /*************************************************************************
Defined to be passed to QtConcurrent::run Defined to be passed to QtConcurrent::run
*************************************************************************/ *************************************************************************/
StelTexture::GLData StelTexture::loadFromPath(const QString &path) StelTexture::GLData StelTexture::loadFromPath(const QString &path)
{ {
return imageToGLData(QImage(path)); try
{
return imageToGLData(QImage(path));
}
catch(std::exception& ex) //this catches out-of-memory errors from f
ile conversion
{
qCritical()<<"Failed loading texture from"<<path<<"error:"<<
ex.what();
GLData ret;
ret.loaderError = ex.what();
return ret;
}
} }
StelTexture::GLData StelTexture::loadFromData(const QByteArray& data) StelTexture::GLData StelTexture::loadFromData(const QByteArray& data)
{ {
return imageToGLData(QImage::fromData(data)); try
{
return imageToGLData(QImage::fromData(data));
}
catch(std::exception& ex) //this catches out-of-memory errors from
file conversion
{
qCritical()<<"Failed loading texture"<<ex.what();
GLData ret;
ret.loaderError = ex.what();
return ret;
}
} }
/************************************************************************* /*************************************************************************
Bind the texture so that it can be used for openGL drawing (calls glBindTe xture) Bind the texture so that it can be used for openGL drawing (calls glBindTe xture)
*************************************************************************/ *************************************************************************/
bool StelTexture::bind(int slot) bool StelTexture::bind(int slot)
{ {
if (id != 0) if (id != 0)
{ {
// The texture is already fully loaded, just bind and return true; // The texture is already fully loaded, just bind and return true;
glActiveTexture(GL_TEXTURE0 + slot); gl->glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, id); gl->glBindTexture(GL_TEXTURE_2D, id);
return true; return true;
} }
if (errorOccured) if (errorOccured)
return false; return false;
if(load())
{
// Finally load the data in the main thread.
glLoad(loader->result());
delete loader;
loader = Q_NULLPTR;
if (id != 0)
{
// The texture is already fully loaded, just bind an
d return true;
gl->glActiveTexture(GL_TEXTURE0 + slot);
gl->glBindTexture(GL_TEXTURE_2D, id);
return true;
}
if (errorOccured)
return false;
}
return false;
}
void StelTexture::waitForLoaded()
{
if(networkReply)
{
qWarning()<<"StelTexture::waitForLoaded called for a network
-loaded texture"<<fullPath;
Q_ASSERT(0);
}
if(loader)
loader->waitForFinished();
}
template <typename T, typename Param, typename Arg>
void StelTexture::startAsyncLoader(T (*functionPointer)(Param), const Arg &
arg)
{
Q_ASSERT(loader==Q_NULLPTR);
#if (QT_VERSION >= QT_VERSION_CHECK(5,4,0))
//own thread pool only supported with Qt 5.4+
loader = new QFuture<GLData>(QtConcurrent::run(textureMgr->loaderThr
eadPool, functionPointer, arg));
#else
//this restores compatibility with Qt 5.3, with the drawback of pote
ntially using
//more memory while loading textures (because more than one can be l
oaded at a time)
loader = new QFuture<GLData>(QtConcurrent::run(functionPointer, arg)
);
#endif
}
bool StelTexture::load()
{
// If the file is remote, start a network connection. // If the file is remote, start a network connection.
if (loader == NULL && networkReply == NULL && fullPath.startsWith("h ttp://")) { if (loader == Q_NULLPTR && networkReply == Q_NULLPTR && fullPath.sta rtsWith("http://")) {
QNetworkRequest req = QNetworkRequest(QUrl(fullPath)); QNetworkRequest req = QNetworkRequest(QUrl(fullPath));
// Define that preference should be given to cached files (n o etag checks) // Define that preference should be given to cached files (n o etag checks)
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
req.setRawHeader("User-Agent", StelUtils::getUserAgentString ().toLatin1()); req.setRawHeader("User-Agent", StelUtils::getUserAgentString ().toLatin1());
networkReply = StelApp::getInstance().getNetworkAccessManage r()->get(req); networkReply = StelApp::getInstance().getNetworkAccessManage r()->get(req);
connect(networkReply, SIGNAL(finished()), this, SLOT(onNetwo rkReply())); connect(networkReply, SIGNAL(finished()), this, SLOT(onNetwo rkReply()));
return false; return false;
} }
// The network connection is still running. // The network connection is still running.
if (networkReply != NULL) if (networkReply != Q_NULLPTR)
return false; return false;
// Not a remote file, start a loader from local file. // Not a remote file, start a loader from local file.
if (loader == NULL) if (loader == Q_NULLPTR)
{ {
loader = new QFuture<GLData>(QtConcurrent::run(loadFromPath, fullPath)); startAsyncLoader(loadFromPath,fullPath);
return false; return false;
} }
// Wait until the loader finish. // Wait until the loader finish.
if (!loader->isFinished()) return loader->isFinished();
return false;
// Finally load the data in the main thread.
glLoad(loader->result());
delete loader;
loader = NULL;
return true;
} }
void StelTexture::onNetworkReply() void StelTexture::onNetworkReply()
{ {
Q_ASSERT(loader == NULL); Q_ASSERT(loader == Q_NULLPTR);
if (networkReply->error() != QNetworkReply::NoError) if (networkReply->error() != QNetworkReply::NoError)
{ {
reportError(networkReply->errorString()); reportError(networkReply->errorString());
} }
else else
{ {
QByteArray data = networkReply->readAll(); QByteArray data = networkReply->readAll();
if(data.isEmpty()) //prevent starting the loader when there is nothing to load if(data.isEmpty()) //prevent starting the loader when there is nothing to load
reportError(QString("Empty result received for URL: %1").arg(networkReply->url().toString())); reportError(QString("Empty result received for URL: %1").arg(networkReply->url().toString()));
else else
loader = new QFuture<GLData>(QtConcurrent::run(loadF romData, data)); startAsyncLoader(loadFromData, data);
} }
networkReply->deleteLater(); networkReply->deleteLater();
networkReply = NULL; networkReply = Q_NULLPTR;
} }
/************************************************************************* /*************************************************************************
Return the width and heigth of the texture in pixels Return the width and heigth of the texture in pixels
*************************************************************************/ *************************************************************************/
bool StelTexture::getDimensions(int &awidth, int &aheight) bool StelTexture::getDimensions(int &awidth, int &aheight)
{ {
if (width<0 || height<0) if (width<0 || height<0)
{ {
// Try to get the size from the file without loading data // Try to get the size from the file without loading data
skipping to change at line 222 skipping to change at line 299
*format = image.hasAlphaChannel() ? GL_LUMINANCE_ALPHA : GL_ LUMINANCE; *format = image.hasAlphaChannel() ? GL_LUMINANCE_ALPHA : GL_ LUMINANCE;
} }
else if (image.hasAlphaChannel()) else if (image.hasAlphaChannel())
{ {
*format = GL_RGBA; *format = GL_RGBA;
} }
else else
*format = GL_RGB; *format = GL_RGB;
*type = GL_UNSIGNED_BYTE; *type = GL_UNSIGNED_BYTE;
int bpp = *format == GL_LUMINANCE_ALPHA ? 2 : int bpp = *format == GL_LUMINANCE_ALPHA ? 2 :
*format == GL_LUMINANCE ? 1 : *format == GL_LUMINANCE ?
*format == GL_RGBA ? 4 : 1 :
3;
*format == GL_RGBA ? 4 :
3;
ret.reserve(width * height * bpp); ret.reserve(width * height * bpp);
QImage tmp = image.convertToFormat(QImage::Format_ARGB32); QImage tmp = image.convertToFormat(QImage::Format_ARGB32);
// flips bits over y // flips bits over y
int ipl = tmp.bytesPerLine() / 4; int ipl = tmp.bytesPerLine() / 4;
for (int y = 0; y < height / 2; ++y) for (int y = 0; y < height / 2; ++y)
{ {
int *a = (int *) tmp.scanLine(y); int *a = (int *) tmp.scanLine(y);
int *b = (int *) tmp.scanLine(height - y - 1); int *b = (int *) tmp.scanLine(height - y - 1);
skipping to change at line 250 skipping to change at line 327
// we always use a tightly packed format, with 1-4 bpp // we always use a tightly packed format, with 1-4 bpp
for (int i = 0; i < height; ++i) for (int i = 0; i < height; ++i)
{ {
uint *p = (uint *) tmp.scanLine(i); uint *p = (uint *) tmp.scanLine(i);
for (int x = 0; x < width; ++x) for (int x = 0; x < width; ++x)
{ {
uint c = qToBigEndian(p[x]); uint c = qToBigEndian(p[x]);
const char* ptr = (const char*)&c; const char* ptr = (const char*)&c;
switch (*format) switch (*format)
{ {
case GL_RGBA: case GL_RGBA:
ret.append(ptr + 1, 3); ret.append(ptr + 1, 3);
ret.append(ptr, 1); ret.append(ptr, 1);
break; break;
case GL_RGB: case GL_RGB:
ret.append(ptr + 1, 3); ret.append(ptr + 1, 3);
break; break;
case GL_LUMINANCE: case GL_LUMINANCE:
ret.append(ptr + 1, 1); ret.append(ptr + 1, 1);
break; break;
case GL_LUMINANCE_ALPHA: case GL_LUMINANCE_ALPHA:
ret.append(ptr + 1, 1); ret.append(ptr + 1, 1);
ret.append(ptr, 1); ret.append(ptr, 1);
break; break;
default: default:
Q_ASSERT(false); Q_ASSERT(false);
} }
} }
} }
return ret; return ret;
} }
bool StelTexture::glLoad(const GLData& data) bool StelTexture::glLoad(const GLData& data)
{ {
if (data.data.isEmpty()) if (data.data.isEmpty())
{ {
reportError("Unknown error"); reportError(data.loaderError.isEmpty()?"Unknown error":data. loaderError);
return false; return false;
} }
width = data.width; width = data.width;
height = data.height; height = data.height;
//make sure the correct GL context is bound! //make sure the correct GL context is bound!
StelApp::getInstance().ensureGLContextCurrent(); StelApp::getInstance().ensureGLContextCurrent();
initializeOpenGLFunctions(); gl = QOpenGLContext::currentContext()->functions();
//check minimum texture size //check minimum texture size
GLint maxSize; GLint maxSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSize); gl->glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSize);
if(maxSize < width || maxSize < height) if(maxSize < width || maxSize < height)
{ {
reportError(QString("Texture size (%1/%2) is larger than GL_ MAX_TEXTURE_SIZE (%3)!").arg(width).arg(height).arg(maxSize)); reportError(QString("Texture size (%1/%2) is larger than GL_ MAX_TEXTURE_SIZE (%3)!").arg(width).arg(height).arg(maxSize));
return false; return false;
} }
glActiveTexture(GL_TEXTURE0); gl->glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &id); gl->glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id); gl->glBindTexture(GL_TEXTURE_2D, id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, loadParams.fil gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, loadParams
tering); .filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, loadParams.fil gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, loadParams
tering); .filtering);
//the conversion from QImage may result in tightly packed scanlines that are no longer 4-byte aligned! //the conversion from QImage may result in tightly packed scanlines that are no longer 4-byte aligned!
//--> we have to set the GL_UNPACK_ALIGNMENT accordingly //--> we have to set the GL_UNPACK_ALIGNMENT accordingly
//remember current alignment //remember current alignment
GLint oldalignment; GLint oldalignment;
glGetIntegerv(GL_UNPACK_ALIGNMENT,&oldalignment); gl->glGetIntegerv(GL_UNPACK_ALIGNMENT,&oldalignment);
switch(data.format) switch(data.format)
{ {
case GL_RGBA: case GL_RGBA:
//RGBA pixels are always in 4 byte aligned rows //RGBA pixels are always in 4 byte aligned rows
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); gl->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
alphaChannel = true; alphaChannel = true;
break; break;
case GL_LUMINANCE_ALPHA: case GL_LUMINANCE_ALPHA:
//these ones are at least always in 2 byte aligned r ows, but may also be 4 aligned //these ones are at least always in 2 byte aligned r ows, but may also be 4 aligned
glPixelStorei(GL_UNPACK_ALIGNMENT, 2); gl->glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
alphaChannel = true; alphaChannel = true;
break; break;
default: default:
//for the other cases, they may be on any alignment (depending on image width) //for the other cases, they may be on any alignment (depending on image width)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); gl->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
alphaChannel = false; alphaChannel = false;
} }
//do pixel transfer //do pixel transfer
glTexImage2D(GL_TEXTURE_2D, 0, data.format, width, height, 0, data.f gl->glTexImage2D(GL_TEXTURE_2D, 0, data.format, width, height, 0, da
ormat, ta.format,
data.type, data.data.constData()); data.type, data.data.constData());
//for now, assume full sized 8 bit GL formats used internally //for now, assume full sized 8 bit GL formats used internally
glSize = data.data.size(); glSize = data.data.size();
textureMgr->glMemoryUsage += glSize;
#ifndef NDEBUG #ifndef NDEBUG
qDebug()<<"StelTexture"<<id<<"uploaded, total memory usage "<<textur eMgr->glMemoryUsage / (1024.0 * 1024.0)<<"MB"; qDebug()<<"StelTexture"<<id<<"uploaded, total memory usage "<<textur eMgr->glMemoryUsage / (1024.0 * 1024.0)<<"MB";
#endif #endif
//restore old value //restore old value
glPixelStorei(GL_UNPACK_ALIGNMENT, oldalignment); gl->glPixelStorei(GL_UNPACK_ALIGNMENT, oldalignment);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, loadParams.wrapMod gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, loadParams.wra
e); pMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, loadParams.wrapMod gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, loadParams.wra
e); pMode);
if (loadParams.generateMipmaps) if (loadParams.generateMipmaps)
{ {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, loadPa gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, lo
rams.filterMipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR_MIPMAP_NEAREST); adParams.filterMipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR_MIPMAP_NEAREST
glGenerateMipmap(GL_TEXTURE_2D); );
gl->glGenerateMipmap(GL_TEXTURE_2D);
glSize = glSize + glSize/3; //mipmaps require 1/3 more mem
} }
//register ID with textureMgr and increment size
textureMgr->glMemoryUsage += glSize;
textureMgr->idMap.insert(id,sharedFromThis());
// Report success of texture loading // Report success of texture loading
emit(loadingProcessFinished(false)); emit(loadingProcessFinished(false));
return true; return true;
} }
// Actually load the texture to openGL memory // Actually load the texture to openGL memory
bool StelTexture::glLoad(const QImage& image) bool StelTexture::glLoad(const QImage& image)
{ {
return glLoad(imageToGLData(image)); return glLoad(imageToGLData(image));
} }
 End of changes. 40 change blocks. 
78 lines changed or deleted 175 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/