File indexing completed on 2024-04-28 04:05:04
0001 /* 0002 SPDX-FileCopyrightText: 2010 Stefan Majewsky <majewsky@gmx.net> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #ifndef KGAMERENDERER_P_H 0008 #define KGAMERENDERER_P_H 0009 0010 // KF 0011 #include <KImageCache> 0012 // Qt 0013 #include <QHash> 0014 #include <QMetaType> 0015 #include <QMutex> 0016 #include <QRunnable> 0017 #include <QSvgRenderer> 0018 #include <QThreadPool> 0019 0020 namespace KGRInternal 0021 { 0022 // Describes the state of a KGameRendererClient. 0023 struct ClientSpec { 0024 // The parentheses around QHash<QColor, QColor>() avoid compile 0025 // errors on platforms with older gcc versions, e.g. OS X 10.6. 0026 inline ClientSpec(const QString &spriteKey = QString(), 0027 int frame = -1, 0028 QSize size = QSize(), 0029 const QHash<QColor, QColor> &customColors = (QHash<QColor, QColor>())); 0030 QString spriteKey; 0031 int frame; 0032 QSize size; 0033 QHash<QColor, QColor> customColors; 0034 }; 0035 ClientSpec::ClientSpec(const QString &spriteKey_, int frame_, QSize size_, const QHash<QColor, QColor> &customColors_) 0036 : spriteKey(spriteKey_) 0037 , frame(frame_) 0038 , size(size_) 0039 , customColors(customColors_) 0040 { 0041 } 0042 0043 // Instantiates QSvgRenderer instances from one SVG file for multiple threads. 0044 class RendererPool 0045 { 0046 public: 0047 // The renderer pool needs the thread pool instance of 0048 // KGameRendererPrivate to terminate workers when a new SVG is loaded. 0049 // WARNING Call this only from the main thread. 0050 inline RendererPool(QThreadPool *threadPool); 0051 inline ~RendererPool(); 0052 0053 // The second argument can be used to pass an instance which has been 0054 // used earlier to check the validity of the SVG file. 0055 inline void setPath(const QString &graphicsPath, QSvgRenderer *renderer = nullptr); 0056 // This can be used to determine whether a call to allocRenderer() 0057 // would need to create a new renderer instance. 0058 inline bool hasAvailableRenderers() const; 0059 0060 // Returns a SVG renderer instance that can be used in the calling thread. 0061 inline QSvgRenderer *allocRenderer(); 0062 // Marks this renderer as available for allocation by other threads. 0063 inline void freeRenderer(QSvgRenderer *renderer); 0064 0065 private: 0066 QString m_path; // path to SVG file 0067 enum Validity { Checked_Invalid, Checked_Valid, Unchecked }; 0068 Validity m_valid; // holds whether m_path points to a valid file 0069 0070 mutable QMutex m_mutex; 0071 QThreadPool *m_threadPool; 0072 QHash<QSvgRenderer *, QThread *> m_hash; 0073 }; 0074 0075 // Describes a rendering job which is delegated to a worker thread. 0076 struct Job { 0077 KGRInternal::RendererPool *rendererPool; 0078 ClientSpec spec; 0079 QString cacheKey, elementKey; 0080 QImage result; 0081 }; 0082 0083 // Describes a worker thread. 0084 class Worker : public QRunnable 0085 { 0086 public: 0087 Worker(Job *job, bool isSynchronous, KGameRendererPrivate *parent); 0088 0089 void run() override; 0090 0091 private: 0092 Job *m_job; 0093 bool m_synchronous; 0094 KGameRendererPrivate *m_parent; 0095 }; 0096 } 0097 0098 Q_DECLARE_METATYPE(KGRInternal::Job *) 0099 0100 class KGameRendererPrivate : public QObject 0101 { 0102 Q_OBJECT 0103 0104 public: 0105 KGameRendererPrivate(KGameThemeProvider *provider, unsigned cacheSize, KGameRenderer *parent); 0106 0107 void _k_setTheme(const KGameTheme *theme); 0108 bool setTheme(const KGameTheme *theme); 0109 inline QString spriteFrameKey(const QString &key, int frame, bool normalizeFrameNo = false) const; 0110 void requestPixmap(const KGRInternal::ClientSpec &spec, KGameRendererClient *client, QPixmap *synchronousResult = nullptr); 0111 0112 private: 0113 inline void requestPixmap__propagateResult(const QPixmap &pixmap, KGameRendererClient *client, QPixmap *synchronousResult); 0114 public Q_SLOTS: 0115 void jobFinished(KGRInternal::Job *job, bool isSynchronous); // NOTE: This is invoked from KGRInternal::Worker::run. 0116 0117 public: 0118 KGameRenderer *const m_parent; 0119 0120 KGameThemeProvider *m_provider; 0121 const KGameTheme *m_currentTheme = nullptr; // will be loaded on first use 0122 0123 QString m_frameSuffix, m_sizePrefix, m_frameCountPrefix, m_boundsPrefix; 0124 unsigned m_cacheSize; 0125 KGameRenderer::Strategies m_strategies = KGameRenderer::UseDiskCache | KGameRenderer::UseRenderingThreads; 0126 int m_frameBaseIndex = 0; 0127 0128 QThreadPool m_workerPool; 0129 mutable KGRInternal::RendererPool m_rendererPool; 0130 0131 QHash<KGameRendererClient *, QString> m_clients; // maps client -> cache key of current pixmap 0132 QStringList m_pendingRequests; // cache keys of pixmaps which are currently being rendered 0133 0134 KImageCache *m_imageCache = nullptr; 0135 0136 // In multi-threaded scenarios, there are two possible ways to use KIC's 0137 // pixmap cache. 0138 // 1. The worker renders a QImage and stores it in the cache. The main 0139 // thread reads the QImage again and converts it into a QPixmap, 0140 // storing it in the pixmap cache for later re-use. 0141 // i.e. QImage -> diskcache -> QImage -> QPixmap -> pixmapcache -> serve 0142 // 2. The worker renders a QImage and sends it directly to the main 0143 // thread, which converts it to a QPixmap. The QPixmap is stored in 0144 // KIC's pixmap cache, and converted to QImage to be written to the 0145 // shared data cache. 0146 // i.e. QImage -> QPixmap -> pixmapcache -> serve 0147 // \-> QImage -> diskcache 0148 // We choose a third way: 0149 // 3. The worker renders a QImage which is converted to a QPixmap by the 0150 // main thread. The main thread caches the QPixmap itself, and stores 0151 // the QImage in the cache. 0152 // i.e. QImage -> QPixmap -> pixmapcache -> serve 0153 // \-> diskcache 0154 // As you see, implementing an own pixmap cache saves us one conversion. 0155 // We therefore disable KIC's pixmap cache because we do not need it. 0156 QHash<QString, QPixmap> m_pixmapCache; 0157 mutable QHash<QString, int> m_frameCountCache; 0158 mutable QHash<QString, QRectF> m_boundsCache; 0159 }; 0160 0161 class KGameRendererClientPrivate : public QObject 0162 { 0163 Q_OBJECT 0164 0165 public: 0166 KGameRendererClientPrivate(KGameRenderer *renderer, const QString &spriteKey, KGameRendererClient *parent); 0167 0168 public Q_SLOTS: 0169 void fetchPixmap(); 0170 0171 public: 0172 KGameRendererClient *const m_parent; 0173 0174 KGameRenderer *m_renderer; 0175 0176 KGRInternal::ClientSpec m_spec; 0177 }; 0178 0179 #endif // KGAMERENDERER_P_H