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 | |
30 | namespace 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 | |
105 | Q_DECLARE_METATYPE(KGRInternal::Job*) |
106 | |
107 | class 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 | |
163 | class 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 | |