1/***************************************************************************
2 * Copyright 2010 Stefan Majewsky <majewsky@gmx.net> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU Library General Public License *
6 * version 2 as published by the Free Software Foundation *
7 * *
8 * This program is distributed in the hope that it will be useful, *
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
11 * GNU Library General Public License for more details. *
12 * *
13 * You should have received a copy of the GNU Library General Public *
14 * License along with this program; if not, write to the *
15 * Free Software Foundation, Inc., *
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
17 ***************************************************************************/
18
19#ifndef KGAMERENDERER_P_H
20#define KGAMERENDERER_P_H
21
22#include <QtCore/QHash>
23#include <QtCore/QMetaType>
24#include <QtCore/QMutex>
25#include <QtCore/QRunnable>
26#include <QtCore/QThreadPool>
27#include <QtSvg/QSvgRenderer>
28#include <KImageCache>
29
30namespace KGRInternal
31{
32 //Describes the state of a KGameRendererClient.
33 struct ClientSpec
34 {
35 // The parentheses around QHash<QColor, QColor>() avoid compile
36 // errors on platforms with older gcc versions, e.g. OS X 10.6.
37 inline ClientSpec(const QString& spriteKey = QString(), int frame = -1, const QSize& size = QSize(), const QHash<QColor, QColor>& customColors = (QHash<QColor, QColor>()));
38 QString spriteKey;
39 int frame;
40 QSize size;
41 QHash<QColor, QColor> customColors;
42 };
43 ClientSpec::ClientSpec(const QString& spriteKey_, int frame_, const QSize& size_, const QHash<QColor, QColor>& customColors_)
44 : spriteKey(spriteKey_)
45 , frame(frame_)
46 , size(size_)
47 , customColors(customColors_)
48 {
49 }
50
51 //Instantiates QSvgRenderer instances from one SVG file for multiple threads.
52 class RendererPool
53 {
54 public:
55 //The renderer pool needs the thread pool instance of
56 //KGameRendererPrivate to terminate workers when a new SVG is loaded.
57 //WARNING Call this only from the main thread.
58 inline RendererPool(QThreadPool* threadPool);
59 inline ~RendererPool();
60
61 //The second argument can be used to pass an instance which has been
62 //used earlier to check the validity of the SVG file.
63 inline void setPath(const QString& graphicsPath, QSvgRenderer* renderer = 0);
64 //This can be used to determine whether a call to allocRenderer()
65 //would need to create a new renderer instance.
66 inline bool hasAvailableRenderers() const;
67
68 //Returns a SVG renderer instance that can be used in the calling thread.
69 inline QSvgRenderer* allocRenderer();
70 //Marks this renderer as available for allocation by other threads.
71 inline void freeRenderer(QSvgRenderer* renderer);
72 private:
73 QString m_path; //path to SVG file
74 enum Validity { Checked_Invalid, Checked_Valid, Unchecked };
75 Validity m_valid; //holds whether m_path points to a valid file
76
77 mutable QMutex m_mutex;
78 QThreadPool* m_threadPool;
79 QHash<QSvgRenderer*, QThread*> m_hash;
80 };
81
82 //Describes a rendering job which is delegated to a worker thread.
83 struct Job
84 {
85 KGRInternal::RendererPool* rendererPool;
86 ClientSpec spec;
87 QString cacheKey, elementKey;
88 QImage result;
89 };
90
91 //Describes a worker thread.
92 class Worker : public QRunnable
93 {
94 public:
95 Worker(Job* job, bool isSynchronous, KGameRendererPrivate* parent);
96
97 virtual void run();
98 private:
99 Job* m_job;
100 bool m_synchronous;
101 KGameRendererPrivate* m_parent;
102 };
103}
104
105Q_DECLARE_METATYPE(KGRInternal::Job*)
106
107class KGameRendererPrivate : public QObject
108{
109 Q_OBJECT
110 public:
111 KGameRendererPrivate(KgThemeProvider* provider, unsigned cacheSize, KGameRenderer* parent);
112 void _k_setTheme(const KgTheme* theme);
113 bool setTheme(const KgTheme* theme);
114 inline QString spriteFrameKey(const QString& key, int frame, bool normalizeFrameNo = false) const;
115 void requestPixmap(const KGRInternal::ClientSpec& spec, KGameRendererClient* client, QPixmap* synchronousResult = 0);
116 private:
117 inline void requestPixmap__propagateResult(const QPixmap& pixmap, KGameRendererClient* client, QPixmap* synchronousResult);
118 public Q_SLOTS:
119 void jobFinished(KGRInternal::Job* job, bool isSynchronous); //NOTE: This is invoked from KGRInternal::Worker::run.
120 public:
121 KGameRenderer* m_parent;
122
123 KgThemeProvider* m_provider;
124 const KgTheme* m_currentTheme;
125 QString m_frameSuffix, m_sizePrefix, m_frameCountPrefix, m_boundsPrefix;
126 unsigned m_cacheSize;
127 KGameRenderer::Strategies m_strategies;
128 int m_frameBaseIndex;
129 QGraphicsView* m_defaultPrimaryView;
130
131 QThreadPool m_workerPool;
132 KGRInternal::RendererPool m_rendererPool;
133
134 QHash<KGameRendererClient*, QString> m_clients; //maps client -> cache key of current pixmap
135 QStringList m_pendingRequests; //cache keys of pixmaps which are currently being rendered
136
137 KImageCache* m_imageCache;
138 //In multi-threaded scenarios, there are two possible ways to use KIC's
139 //pixmap cache.
140 //1. The worker renders a QImage and stores it in the cache. The main
141 // thread reads the QImage again and converts it into a QPixmap,
142 // storing it inthe pixmap cache for later re-use.
143 //i.e. QImage -> diskcache -> QImage -> QPixmap -> pixmapcache -> serve
144 //2. The worker renders a QImage and sends it directly to the main
145 // thread, which converts it to a QPixmap. The QPixmap is stored in
146 // KIC's pixmap cache, and converted to QImage to be written to the
147 // shared data cache.
148 //i.e. QImage -> QPixmap -> pixmapcache -> serve
149 // \-> QImage -> diskcache
150 //We choose a third way:
151 //3. The worker renders a QImage which is converted to a QPixmap by the
152 // main thread. The main thread caches the QPixmap itself, and stores
153 // the QImage in the cache.
154 //i.e. QImage -> QPixmap -> pixmapcache -> serve
155 // \-> diskcache
156 //As you see, implementing an own pixmap cache saves us one conversion.
157 //We therefore disable KIC's pixmap cache because we do not need it.
158 QHash<QString, QPixmap> m_pixmapCache;
159 QHash<QString, int> m_frameCountCache;
160 QHash<QString, QRectF> m_boundsCache;
161};
162
163class KGameRendererClientPrivate : public QObject
164{
165 Q_OBJECT
166 public:
167 KGameRendererClientPrivate(KGameRenderer* renderer, const QString& spriteKey, KGameRendererClient* parent);
168 public Q_SLOTS:
169 void fetchPixmap();
170 public:
171 KGameRendererClient* m_parent;
172 KGameRenderer* m_renderer;
173
174 KGRInternal::ClientSpec m_spec;
175};
176
177#endif // KGAMERENDERER_P_H
178