File indexing completed on 2022-12-06 18:58:56

0001 /*
0002     SPDX-FileCopyrightText: 2010 Henry de Valence <hdevalence@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "texturemanager.h"
0008 
0009 #include "kspaths.h"
0010 #include "auxiliary/kspaths.h"
0011 
0012 #ifdef KSTARS_LITE
0013 #include <QStandardPaths>
0014 #include <QImage>
0015 #else
0016 #include "skymap.h"
0017 #include "kstars.h"
0018 #endif
0019 
0020 #ifdef HAVE_OPENGL
0021 #include <QGLWidget>
0022 #endif
0023 
0024 // We returning reference to image. We refer to this image when search
0025 // for image fails
0026 const static QImage emptyImage;
0027 
0028 TextureManager *TextureManager::m_p = nullptr;
0029 
0030 TextureManager *TextureManager::Create()
0031 {
0032     if (!m_p)
0033     {
0034         m_p = new TextureManager();
0035         discoverTextureDirs();
0036     }
0037     return m_p;
0038 }
0039 
0040 void TextureManager::Release()
0041 {
0042     delete m_p;
0043     m_p = nullptr;
0044 }
0045 
0046 const QImage &TextureManager::getImage(const QString &name)
0047 {
0048     Create();
0049     if (name.isEmpty())
0050         return emptyImage;
0051     CacheIter it = findTexture(name);
0052     if (it != m_p->m_textures.constEnd())
0053     {
0054         return *it;
0055     }
0056     else
0057     {
0058         return emptyImage;
0059     }
0060 }
0061 
0062 TextureManager::CacheIter TextureManager::findTexture(const QString &name)
0063 {
0064     Create();
0065     // Lookup in cache first
0066     CacheIter it = m_p->m_textures.constFind(name);
0067     if (it != m_p->m_textures.constEnd())
0068     {
0069         return it;
0070     }
0071 
0072     for (const auto &dir : m_p->m_texture_directories)
0073     {
0074         const auto &filename = QString("%1/%2.png").arg(dir).arg(name);
0075         QFile file{ filename };
0076         if (file.exists())
0077             return (TextureManager::CacheIter)m_p->m_textures.insert(
0078                 name, QImage(filename, "PNG"));
0079     }
0080 
0081     //Try to load from the file in 'skycultures/western' subdirectory for western constellation art
0082     QString filename = KSPaths::locate(QStandardPaths::AppLocalDataLocation,
0083                                        QString("skycultures/western/%1.png").arg(name));
0084     if (!filename.isNull())
0085     {
0086         return (TextureManager::CacheIter)m_p->m_textures.insert(name,
0087                                                                  QImage(filename, "PNG"));
0088     }
0089 
0090     //Try to load from the file in 'skycultures/inuit' subdirectory for Inuit constellation art
0091     filename = KSPaths::locate(QStandardPaths::AppLocalDataLocation,
0092                                QString("skycultures/inuit/%1.png").arg(name));
0093     if (!filename.isNull())
0094     {
0095         return (TextureManager::CacheIter)m_p->m_textures.insert(name,
0096                                                                  QImage(filename, "PNG"));
0097     }
0098 
0099     // Try to load from file in main data directory
0100     filename = KSPaths::locate(QStandardPaths::AppLocalDataLocation,
0101                                QString("textures/%1.png").arg(name));
0102 
0103     if (!filename.isNull())
0104     {
0105         return (TextureManager::CacheIter)m_p->m_textures.insert(name,
0106                                                                  QImage(filename, "PNG"));
0107     }
0108 
0109     return (TextureManager::CacheIter)m_p->m_textures.insert(name, QImage());
0110 }
0111 
0112 #ifdef HAVE_OPENGL
0113 static void bindImage(const QImage &img, QGLWidget *cxt)
0114 {
0115     GLuint tid = cxt->bindTexture(img, GL_TEXTURE_2D, GL_RGBA, QGLContext::DefaultBindOption);
0116     glBindTexture(GL_TEXTURE_2D, tid);
0117 }
0118 
0119 // FIXME: should we check that image have appropriate size as bindFromImage do?
0120 void TextureManager::bindTexture(const QString &name, QGLWidget *cxt)
0121 {
0122     Create();
0123     Q_ASSERT("Must be called only with valid GL context" && cxt);
0124 
0125     CacheIter it = findTexture(name);
0126     if (it != m_p->m_textures.constEnd())
0127         bindImage(*it, cxt);
0128 }
0129 
0130 void TextureManager::bindFromImage(const QImage &image, QGLWidget *cxt)
0131 {
0132     Create();
0133     Q_ASSERT("Must be called only with valid GL context" && cxt);
0134 
0135     if (image.width() != image.height() || (image.width() & (image.width() - 1)))
0136     {
0137         // Compute texture size
0138         int longest  = qMax(image.width(), image.height());
0139         int tex_size = 2;
0140         while (tex_size < longest)
0141         {
0142             tex_size *= 2;
0143         }
0144         // FIXME: Check if Qt does this for us already. [Note that it does scale to the nearest power of two]
0145         bindImage(image.scaled(tex_size, tex_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), cxt);
0146     }
0147     else
0148     {
0149         bindImage(image, cxt);
0150     }
0151 }
0152 #endif
0153 
0154 TextureManager::TextureManager(QObject *parent) : QObject(parent) {}
0155 
0156 void TextureManager::discoverTextureDirs()
0157 {
0158     // clear the cache
0159     m_p->m_textures = {};
0160 
0161     const auto &base = KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
0162     QDirIterator search(base, QStringList() << "textures_*", QDir::Dirs);
0163 
0164     auto &dirs = m_p->m_texture_directories;
0165     while (search.hasNext())
0166     {
0167         dirs.push_back(search.next());
0168     }
0169 
0170     dirs.push_back(base);
0171 };