File indexing completed on 2024-03-24 15:18:27

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