• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Stellarium / stellarium / 3996069357

pending completion
3996069357

push

github

Ruslan Kabatsayev
Shorten some lines

5 of 5 new or added lines in 1 file covered. (100.0%)

14663 of 124076 relevant lines covered (11.82%)

22035.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/core/StelTexture.cpp
1
/*
2
 * Stellarium
3
 * Copyright (C) 2006 Fabien Chereau
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
18
 */
19

20
#include "StelTexture.hpp"
21
#include "StelTextureMgr.hpp"
22
#include "StelApp.hpp"
23
#include "StelUtils.hpp"
24
#include "StelMainView.hpp"
25
#include "StelOpenGL.hpp"
26

27

28
#include <QImageReader>
29
#include <QSize>
30
#include <QDebug>
31
#include <QUrl>
32
#include <QImage>
33
#include <QNetworkReply>
34
#include <QtEndian>
35
#include <QFuture>
36
#include <QtConcurrent>
37

38
#ifndef GL_TEXTURE_MAX_ANISOTROPY
39
# define GL_TEXTURE_MAX_ANISOTROPY 0x84FE
40
#endif
41

42
StelTexture::StelTexture(StelTextureMgr *mgr) : textureMgr(mgr), gl(Q_NULLPTR), networkReply(Q_NULLPTR), loader(Q_NULLPTR), errorOccured(false), alphaChannel(false), id(0),
×
43
        width(-1), height(-1), glSize(0)
×
44
{
45
}
×
46

47
StelTexture::~StelTexture()
×
48
{
49
        if (id != 0)
×
50
        {
51
                /// FS: make sure the correct GL context is bound!
52
                /// Causes #595 flicker in Night Mode with DSS/HiPS. Tentatively remove this.
53
                //StelApp::getInstance().ensureGLContextCurrent();
54

55
                if (gl->glIsTexture(id)==GL_FALSE)
×
56
                {
57
                        GLenum err = gl->glGetError();
×
58
                        qWarning() << "WARNING: in StelTexture::~StelTexture() tried to delete invalid texture with ID=" << id << "Current GL ERROR status is" << err << "(" << StelOpenGL::getGLErrorText(err) << ")";
×
59
                }
60
                else
61
                {
62
                        gl->glDeleteTextures(1, &id);
×
63
                        textureMgr->glMemoryUsage -= glSize;
×
64
                        textureMgr->idMap.remove(id);
×
65
                        glSize = 0;
×
66
                }
67
#ifndef NDEBUG
68
                if (qApp->property("verbose") == true)
×
69
                        qDebug()<<"Deleted StelTexture"<<id<<", total memory usage "<<textureMgr->glMemoryUsage / (1024.0 * 1024.0)<<"MB";
×
70
#endif
71
                id = 0;
×
72
        }
73
        else if (id)
×
74
        {
75
                qWarning()<<"Cannot delete texture"<<id<<", no GL context";
×
76
        }
77
        if (networkReply)
×
78
        {
79
                networkReply->abort();
×
80
                //networkReply->deleteLater();
81
                delete networkReply;
×
82
                networkReply = Q_NULLPTR;
×
83
        }
84
        if (loader != Q_NULLPTR) {
×
85
                delete loader;
×
86
                loader = Q_NULLPTR;
×
87
        }
88
}
×
89

90
void StelTexture::wrapGLTexture(GLuint texId)
×
91
{
92
        gl = QOpenGLContext::currentContext()->functions();
×
93
        bool valid = gl->glIsTexture(texId);
×
94
        if(valid)
×
95
        {
96
                id = texId;
×
97
                //Note: there is no way to retrieve texture width/height on OpenGL ES
98
                //so the members will be wrong
99
                //also we can't estimate memory usage because of this
100
        }
101
        else
102
        {
103
                errorMessage="No valid OpenGL texture name";
×
104
                errorOccured=true;
×
105
        }
106
}
×
107

108
/*************************************************************************
109
 This method should be called if the texture loading failed for any reasons
110
 *************************************************************************/
111
void StelTexture::reportError(const QString& aerrorMessage)
×
112
{
113
        errorOccured = true;
×
114
        errorMessage = aerrorMessage;
×
115
        // Report failure of texture loading
116
        emit loadingProcessFinished(true);
×
117
}
×
118

119
StelTexture::GLData StelTexture::imageToGLData(const QImage &image, const int decimateBy)
×
120
{
121
        GLData ret = GLData();
×
122
        if (image.isNull())
×
123
                return ret;
×
124
        ret.data = convertToGLFormat(image, ret.format, ret.type, decimateBy, ret.width, ret.height);
×
125
        return ret;
×
126
}
×
127

128
/*************************************************************************
129
 Defined to be passed to QtConcurrent::run
130
 *************************************************************************/
131
StelTexture::GLData StelTexture::loadFromPath(const QString &path, const int decimateBy)
×
132
{
133
        try
134
        {
135
                return imageToGLData(QImage(path), decimateBy);
×
136
        }
137
        catch(std::exception& ex) //this catches out-of-memory errors from file conversion
×
138
        {
139
                qCritical()<<"Failed loading texture from"<<path<<"error:"<<ex.what();
×
140
                GLData ret;
×
141
                ret.loaderError = ex.what();
×
142
                return ret;
×
143
        }
×
144
}
145

146
StelTexture::GLData StelTexture::loadFromData(const QByteArray& data, const int decimateBy)
×
147
{
148
        try
149
        {
150
                return imageToGLData(QImage::fromData(data), decimateBy);
×
151
        }
152
        catch(std::exception& ex)  //this catches out-of-memory errors from file conversion
×
153
        {
154
                qCritical()<<"Failed loading texture"<<ex.what();
×
155
                GLData ret;
×
156
                ret.loaderError = ex.what();
×
157
                return ret;
×
158
        }
×
159
}
160

161
/*************************************************************************
162
 Bind the texture so that it can be used for openGL drawing (calls glBindTexture)
163
 *************************************************************************/
164

165
bool StelTexture::bind(uint slot)
×
166
{
167
        if (id != 0)
×
168
        {
169
                // The texture is already fully loaded, just bind and return true;
170
                gl->glActiveTexture(GL_TEXTURE0 + slot);
×
171
                gl->glBindTexture(GL_TEXTURE_2D, id);
×
172
                return true;
×
173
        }
174
        if (errorOccured)
×
175
                return false;
×
176

177
        if(load())
×
178
        {
179
                // Finally load the data in the main thread.
180
                glLoad(loader->result());
×
181
                delete loader;
×
182
                loader = Q_NULLPTR;
×
183
                if (id != 0)
×
184
                {
185
                        // The texture is already fully loaded, just bind and return true;
186
                        gl->glActiveTexture(GL_TEXTURE0 + slot);
×
187
                        gl->glBindTexture(GL_TEXTURE_2D, id);
×
188
                        return true;
×
189
                }
190
                if (errorOccured)
×
191
                        return false;
×
192
        }
193
        return false;
×
194
}
195

196
void StelTexture::waitForLoaded()
×
197
{
198
        if(networkReply)
×
199
        {
200
                qWarning()<<"StelTexture::waitForLoaded called for a network-loaded texture"<<fullPath;
×
201
                Q_ASSERT(0);
×
202
        }
203
        if(loader)
×
204
                loader->waitForFinished();
×
205
}
×
206

207
template <typename T, typename...Params, typename...Args>
208
void StelTexture::startAsyncLoader(T (*functionPointer)(Params...), Args&&...args)
×
209
{
210
        Q_ASSERT(loader==Q_NULLPTR);
×
211
        //own thread pool supported with Qt 5.4+
212
        loader = new QFuture<GLData>(QtConcurrent::run(textureMgr->loaderThreadPool, functionPointer, std::forward<Args>(args)...));
×
213
}
×
214

215
bool StelTexture::load()
×
216
{
217
        // If the file is remote, start a network connection.
218
        if (loader == Q_NULLPTR && networkReply == Q_NULLPTR &&
×
219
                        (fullPath.startsWith("http", Qt::CaseInsensitive) || fullPath.startsWith("file://", Qt::CaseInsensitive)))
×
220
        {
221
                QNetworkRequest req = QNetworkRequest(QUrl(fullPath));
×
222
                // Define that preference should be given to cached files (no etag checks)
223
                req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
×
224
                req.setRawHeader("User-Agent", StelUtils::getUserAgentString().toLatin1());
×
225
                networkReply = StelApp::getInstance().getNetworkAccessManager()->get(req);
×
226
                connect(networkReply, SIGNAL(finished()), this, SLOT(onNetworkReply()));
×
227
                return false;
×
228
        }
×
229
        // The network connection is still running.
230
        if (networkReply != Q_NULLPTR)
×
231
                return false;
×
232
        // Not a remote file, start a loader from local file.
233
        if (loader == Q_NULLPTR)
×
234
        {
235
                startAsyncLoader(static_cast<GLData(*)(const QString&, const int)>(loadFromPath), fullPath, loadParams.decimation);
×
236
                return false;
×
237
        }
238
        // Wait until the loader finish.
239
        return loader->isFinished();
×
240
}
241

242
void StelTexture::onNetworkReply()
×
243
{
244
        Q_ASSERT(loader == Q_NULLPTR);
×
245
        if (networkReply->error() == QNetworkReply::NoError && networkReply->bytesAvailable()>0)
×
246
        {
247
                QByteArray data = networkReply->readAll();
×
248
                if(data.isEmpty()) //prevent starting the loader when there is nothing to load
×
249
                        reportError(QString("Empty result received for URL: %1").arg(networkReply->url().toString()));
×
250
                else
251
                        startAsyncLoader(static_cast<GLData(*)(const QByteArray&, const int)>(loadFromData), data, loadParams.decimation);
×
252
        }
×
253
        else
254
                reportError(networkReply->errorString());
×
255

256
        networkReply->deleteLater();
×
257
        networkReply = Q_NULLPTR;
×
258
}
×
259

260
/*************************************************************************
261
 Return the width and height of the texture in pixels
262
*************************************************************************/
263
bool StelTexture::getDimensions(int &awidth, int &aheight)
×
264
{
265
        if (width<0 || height<0)
×
266
        {
267
                // Try to get the size from the file without loading data
268
                QImageReader im(fullPath);
×
269
                if (!im.canRead())
×
270
                {
271
                        return false;
×
272
                }
273
                QSize size = im.size();
×
274
                width = size.width();
×
275
                height = size.height();
×
276
        }
×
277
        awidth = width;
×
278
        aheight = height;
×
279
        return true;
×
280
}
281

282
QByteArray StelTexture::convertToGLFormat(QImage image, GLint& format, GLint& type, int decimate, int& width, int& height)
×
283
{
284
        QByteArray ret;
×
285
        const auto glInfo = StelMainView::getInstance().getGLInformation();
×
286
        width = std::min(image.width()/decimate, glInfo.maxTextureSize);
×
287
        height = std::min(image.height()/decimate, glInfo.maxTextureSize);
×
288

289
#ifndef NDEBUG
290
        if (decimate>1)
×
291
                qDebug() << "decimated texture width: " << image.width() << "/" << decimate << "->" << width;
×
292
#endif
293
        if(width != image.width() || height != image.height())
×
294
        {
295
                if ((decimate==1) || (image.width()/decimate>glInfo.maxTextureSize) || (image.height()/decimate>glInfo.maxTextureSize))
×
296
                        qWarning().nospace() << "Got a texture with too large dimensions: "
×
297
                                                         << image.width() << "x" << image.height()
×
298
                                                         << ", while maximum size is " << glInfo.maxTextureSize
×
299
                                                         << ". Shrinking to fit in the limit.";
×
300
                image = image.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
×
301
        }
302

303
        if (glInfo.supportsLuminanceTextures && image.isGrayscale())
×
304
        {
305
                format = image.hasAlphaChannel() ? GL_LUMINANCE_ALPHA : GL_LUMINANCE;
×
306
        }
307
        else if (image.hasAlphaChannel())
×
308
        {
309
                format = GL_RGBA;
×
310
        }
311
        else
312
                format = GL_RGB;
×
313
        type = GL_UNSIGNED_BYTE;
×
314
        int bpp = format == GL_LUMINANCE_ALPHA ? 2 :
×
315
                                                  format == GL_LUMINANCE ? 1 :
×
316
                                                                            format == GL_RGBA ? 4 :
×
317
                                                                                                 3;
318

319
        ret.reserve(width * height * bpp);
×
320
        QImage tmp = image.convertToFormat(QImage::Format_ARGB32);
×
321

322
        // convert data
323
        // we always use a tightly packed format, with 1-4 bpp
324
        // the image should be flipped over y, so read it backwards from the end
325
        for (int i = height - 1; i >= 0; --i)
×
326
        {
327
                uint *p = reinterpret_cast<uint *>( tmp.scanLine(i));
×
328
                for (int x = 0; x < width; ++x)
×
329
                {
330
                        uint c = qToBigEndian(p[x]);
×
331
                        const char* ptr = reinterpret_cast<const char*>(&c);
×
332
                        switch (format)
×
333
                        {
334
                                case GL_RGBA:
×
335
                                        ret.append(ptr + 1, 3);
×
336
                                        ret.append(ptr, 1);
×
337
                                        break;
×
338
                                case GL_RGB:
×
339
                                        ret.append(ptr + 1, 3);
×
340
                                        break;
×
341
                                case GL_LUMINANCE:
×
342
                                        ret.append(ptr + 1, 1);
×
343
                                        break;
×
344
                                case GL_LUMINANCE_ALPHA:
×
345
                                        ret.append(ptr + 1, 1);
×
346
                                        ret.append(ptr, 1);
×
347
                                        break;
×
348
                                default:
×
349
                                        Q_ASSERT(false);
×
350
                        }
351
                }
352
        }
353
        return ret;
×
354
}
×
355

356
bool StelTexture::glLoad(const GLData& data)
×
357
{
358
        if (data.data.isEmpty())
×
359
        {
360
                reportError(data.loaderError.isEmpty()?"Unknown error":data.loaderError);
×
361
                return false;
×
362
        }
363

364
        width = data.width;
×
365
        height = data.height;
×
366

367
        /// FS: make sure the correct GL context is bound!
368
        /// Causes #595 flicker in Night Mode with DSS/HiPS. Tentatively remove this.
369
        //StelApp::getInstance().ensureGLContextCurrent();
370
        gl = QOpenGLContext::currentContext()->functions();
×
371

372
        //check minimum texture size
373
        GLint maxSize;
374
        gl->glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSize);
×
375
        if(maxSize < width || maxSize < height)
×
376
        {
377
                reportError(QString("Texture size (%1/%2) is larger than GL_MAX_TEXTURE_SIZE (%3)!").arg(width).arg(height).arg(maxSize));
×
378
                return false;
×
379
        }
380

381
        gl->glActiveTexture(GL_TEXTURE0);
×
382
        gl->glGenTextures(1, &id);
×
383
        gl->glBindTexture(GL_TEXTURE_2D, id);
×
384
        gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, loadParams.filtering);
×
385
        gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, loadParams.filtering);
×
386

387
        //the conversion from QImage may result in tightly packed scanlines that are no longer 4-byte aligned!
388
        //--> we have to set the GL_UNPACK_ALIGNMENT accordingly
389

390
        //remember current alignment
391
        GLint oldalignment;
392
        gl->glGetIntegerv(GL_UNPACK_ALIGNMENT,&oldalignment);
×
393

394
        switch(data.format)
×
395
        {
396
                case GL_RGBA:
×
397
                        //RGBA pixels are always in 4 byte aligned rows
398
                        gl->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
×
399
                        alphaChannel = true;
×
400
                        break;
×
401
                case GL_LUMINANCE_ALPHA:
×
402
                        //these ones are at least always in 2 byte aligned rows, but may also be 4 aligned
403
                        gl->glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
×
404
                        alphaChannel = true;
×
405
                        break;
×
406
                default:
×
407
                        //for the other cases, they may be on any alignment (depending on image width)
408
                        gl->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
×
409
                        alphaChannel = false;
×
410
        }
411

412
        //do pixel transfer
413
        gl->glTexImage2D(GL_TEXTURE_2D, 0, data.format, width, height, 0, static_cast<GLenum>(data.format),
×
414
                         static_cast<GLenum>(data.type), data.data.constData());
×
415

416
        //for now, assume full sized 8 bit GL formats used internally
417
        glSize = static_cast<uint>(data.data.size());
×
418

419
#ifndef NDEBUG
420
        if (qApp->property("verbose") == true)
×
421
                qDebug()<<"StelTexture"<<id<<"uploaded, total memory usage "<<textureMgr->glMemoryUsage / (1024.0 * 1024.0)<<"MB";
×
422
#endif
423

424
        //restore old value
425
        gl->glPixelStorei(GL_UNPACK_ALIGNMENT, oldalignment);
×
426

427
        gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, loadParams.wrapMode);
×
428
        gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, loadParams.wrapMode);
×
429
        if (loadParams.generateMipmaps)
×
430
        {
431
                gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, loadParams.filterMipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR_MIPMAP_NEAREST);
×
432

433
                const auto conf = StelApp::getInstance().getSettings();
×
434
                const GLint desiredAnisotropyLevel = conf->value("video/anisotropic_filtering", 16).toUInt();
×
435
                const auto maxAnisotropy = StelMainView::getInstance().getGLInformation().maxAnisotropy;
×
436
                if(maxAnisotropy > 0 && desiredAnisotropyLevel > 0)
×
437
                {
438
                        const int anisotropy = std::min(maxAnisotropy, desiredAnisotropyLevel);
×
439
                        gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
×
440

441
                        // Assuming maximum possible anisotropy, all mipmaps will require
442
                        // 4× the size of base mip level. Generally anisotropy may be
443
                        // lower, but we prefer to overestimate VRAM consumption than to
444
                        // underestimate it.
445
                        glSize *= 4;
×
446
                }
×
447
                else
448
                {
449
                        glSize = glSize + glSize/3; //mipmaps require 1/3 more mem
×
450
                }
451
                gl->glGenerateMipmap(GL_TEXTURE_2D);
×
452
        }
453

454
        //register ID with textureMgr and increment size
455
        textureMgr->glMemoryUsage += glSize;
×
456
        textureMgr->idMap.insert(id,sharedFromThis());
×
457

458

459
        // Report success of texture loading
460
        emit loadingProcessFinished(false);
×
461
        return true;
×
462
}
463

464
// Actually load the texture to openGL memory
465
bool StelTexture::glLoad(const QImage& image)
×
466
{
467
        return glLoad(imageToGLData(image, loadParams.decimation));
×
468
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc