1/**********************************************************************************
2 This file is part of the game 'KTron'
3
4 Copyright (C) 1998-2000 by Matthias Kiefer <matthias.kiefer@gmx.de>
5 Copyright (C) 2005 Benjamin C. Meyer <ben at meyerhome dot net>
6 Copyright (C) 2008-2009 Stas Verberkt <legolas at legolasweb dot nl>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
22 *******************************************************************************/
23
24#include "renderer.h"
25#include "settings.h"
26#include "object.h"
27
28#include <QPainter>
29#include <QPixmap>
30#include <QSize>
31
32#include <KPixmapCache>
33#include <QSvgRenderer>
34#include <KDebug>
35#include <kfontutils.h>
36
37#define USE_UNSTABLE_LIBKDEGAMESPRIVATE_API
38#include <libkdegamesprivate/kgametheme.h>
39
40class RendererPrivate
41{
42 public:
43 RendererPrivate();
44 ~RendererPrivate();
45
46 QSize m_sceneSize;
47 QSize m_partSize;
48
49 QSvgRenderer m_renderer;
50 KPixmapCache m_cache;
51
52 QPixmap *m_playField;
53
54 QString m_currentTheme;
55};
56
57const QString sizeSuffix(QLatin1String( "_%1-%2" ));
58const QString frameSuffix(QLatin1String( "-%1" ));
59
60RendererPrivate::RendererPrivate()
61 : m_renderer()
62 , m_cache(QLatin1String( "ktron-cache" ))
63{
64 m_cache.setCacheLimit(3 * 1024);
65 m_cache.discard();
66 m_playField = 0;
67}
68
69RendererPrivate::~RendererPrivate()
70{
71 delete m_playField;
72}
73
74Renderer::Renderer()
75 : p(new RendererPrivate)
76{
77 loadTheme(Settings::theme());
78}
79
80Renderer::Renderer(const Renderer &)
81{
82}
83
84Renderer::~Renderer()
85{
86 delete p;
87}
88
89Renderer *Renderer::self()
90{
91 static Renderer r;
92 return &r;
93}
94
95bool Renderer::loadTheme(const QString &name)
96{
97 bool discardCache = !p->m_currentTheme.isEmpty();
98 if (!p->m_currentTheme.isEmpty() && p->m_currentTheme == name)
99 return true; //requested to load the theme that is already loaded
100 KGameTheme theme;
101 //try to load theme
102 if (!theme.load(name))
103 {
104 if (!theme.loadDefault())
105 return false;
106 }
107 p->m_currentTheme = name;
108
109 //load graphics
110 if (!p->m_renderer.load(theme.graphics()))
111 return false;
112 //flush cache
113 if (discardCache)
114 p->m_cache.discard();
115 return true;
116}
117
118QPixmap Renderer::getPart(const QString &frameSvgName)
119{
120 return getPartOfSize(frameSvgName, p->m_partSize);
121}
122
123QPixmap Renderer::getPartOfSize(const QString &frameSvgName, const QSize &partSize)
124{
125 QString framePixName = frameSvgName + sizeSuffix.arg(partSize.width()).arg(partSize.height());
126 QPixmap pix;
127 if (!p->m_cache.find(framePixName, pix))
128 {
129 pix = QPixmap(partSize);
130 pix.fill(Qt::transparent);
131 QPainter painter(&pix);
132 p->m_renderer.render(&painter, frameSvgName);
133 painter.end();
134 p->m_cache.insert(framePixName, pix);
135 }
136
137 //return the static pixmap
138 return pixmapFromCache(p, frameSvgName, partSize);
139}
140
141QPixmap Renderer::pixmapFromCache(RendererPrivate *p, const QString &svgName, const QSize &size)
142{
143 if (size.isEmpty())
144 return QPixmap();
145 QPixmap pix;
146 QString pixName = svgName + sizeSuffix.arg(size.width()).arg(size.height());
147
148 if (!p->m_cache.find(pixName, pix))
149 {
150 pix = QPixmap(size);
151 pix.fill(Qt::transparent);
152 QPainter painter(&pix);
153 p->m_renderer.render(&painter, svgName);
154 painter.end();
155 p->m_cache.insert(pixName, pix);
156 }
157
158 return pix;
159}
160
161QPixmap Renderer::background()
162{
163 QPixmap pix;
164 QString pixName = QLatin1String( "bgtile" ) + sizeSuffix.arg(p->m_sceneSize.width()).arg(p->m_sceneSize.height());
165 if (!p->m_cache.find(pixName, pix))
166 {
167 pix = QPixmap(p->m_sceneSize);
168 pix.fill(Qt::white);
169 QPainter painter(&pix);
170
171 QPixmap bgPix = getPart(QLatin1String( "bgtile" ));
172 if (!bgPix.isNull())
173 {
174 pix.fill(Qt::white);
175 int pw = bgPix.width();
176 int ph = bgPix.height();
177 for (int x = 0; x <= p->m_sceneSize.width(); x += pw) {
178 for (int y = 0; y <= p->m_sceneSize.height(); y += ph) {
179 painter.drawPixmap(x, y, bgPix);
180 }
181 }
182 }
183 else
184 {
185 pix.fill(Qt::green);
186 }
187
188 painter.end();
189 p->m_cache.insert(pixName, pix);
190 }
191
192 // Tiled background
193 return pix;
194}
195
196void Renderer::boardResized(int width, int height, int partWidth, int partHeight)
197{
198 //new metrics
199 p->m_sceneSize = QSize(width, height);
200 p->m_partSize = QSize(partWidth, partHeight);
201}
202
203void Renderer::resetPlayField()
204{
205 delete p->m_playField;
206 p->m_playField = new QPixmap(p->m_sceneSize);
207 //p->m_playField->fill(Qt::green);
208}
209
210void Renderer::updatePlayField(PlayField &playfield)
211{
212 int i, j;
213
214 if (!p->m_playField)
215 {
216 resetPlayField();
217 }
218
219 QPainter painter;
220 painter.begin(p->m_playField);
221
222 QPixmap bgPix = background();
223 painter.drawPixmap(0, 0, bgPix);
224
225 // Draw border
226 for (i = 0; i < playfield.getWidth() + 2; ++i)
227 {
228 for (j = 0; j < playfield.getHeight() + 2; ++j)
229 {
230 if (i == 0 || i == playfield.getWidth() + 1 || j == 0 || j == playfield.getHeight() + 1)
231 {
232 QPixmap part = Renderer::self()->getPart(QLatin1String( "border" ));
233 painter.drawPixmap(calculateOffsetX(i), calculateOffsetY(j), part);
234 }
235 }
236 }
237
238 // Examine all pixels and draw
239 for(i = 0; i < playfield.getWidth(); ++i)
240 {
241 for(j = 0; j < playfield.getHeight(); ++j)
242 {
243 if (playfield.getObjectAt(i, j)->getObjectType() != ObjectType::Object)
244 {
245 drawPart(painter, i, j, playfield.getObjectAt(i, j)->getSVGName());
246 }
247 }
248 }
249
250 painter.end();
251}
252
253int Renderer::calculateOffsetX(int x)
254{
255 return (x * p->m_partSize.width()) + (p->m_sceneSize.width() - (TRON_PLAYFIELD_WIDTH + 2) * p->m_partSize.width()) / 2;
256}
257
258int Renderer::calculateOffsetY(int y)
259{
260 return (y * p->m_partSize.height()) + (p->m_sceneSize.height() - (TRON_PLAYFIELD_HEIGHT + 2) * p->m_partSize.height()) / 2;
261}
262
263void Renderer::drawPart(QPainter & painter, int x, int y, QString svgName)
264{
265 //kDebug() << "Drawing part: " << svgName;
266
267 int xOffset = calculateOffsetX(x + 1);
268 int yOffset = calculateOffsetY(y + 1);
269
270 //int type = playfield[x][y];
271
272 QPixmap snakePart = Renderer::self()->getPart(svgName);
273
274 painter.drawPixmap(xOffset, yOffset, snakePart);
275}
276
277QPixmap *Renderer::getPlayField()
278{
279 return p->m_playField;
280}
281
282QPixmap Renderer::messageBox(const QString &message) {
283 int w = p->m_sceneSize.width() / 2;
284 int h = p->m_sceneSize.height() / 3;
285
286 QSize size(w, h);
287 QPixmap pixmap = getPartOfSize(QLatin1String( "display" ), size);
288
289 QPainter painter(&pixmap);
290
291 const int fontSize = KFontUtils::adaptFontSize(painter, message, w * 0.9, h, 28, 1, KFontUtils::DoNotAllowWordWrap);
292
293 painter.setPen(QColor(255, 255, 255, 220));
294 painter.setFont(QFont(QLatin1String( "Helvetica" ), fontSize, QFont::Bold));
295 painter.drawText(QRectF(0, 0, w, h), Qt::AlignCenter, message);
296
297 painter.end();
298
299 return pixmap;
300}
301