source: trunk/src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp@ 203

Last change on this file since 203 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 10.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41#include "qsvgiconengine.h"
42
43#ifndef QT_NO_SVGRENDERER
44
45#include "qpainter.h"
46#include "qpixmap.h"
47#include "qsvgrenderer.h"
48#include "qpixmapcache.h"
49#include "qstyle.h"
50#include "qapplication.h"
51#include "qstyleoption.h"
52#include "qfileinfo.h"
53#include <QAtomicInt>
54#include "qdebug.h"
55
56QT_BEGIN_NAMESPACE
57
58class QSvgIconEnginePrivate : public QSharedData
59{
60public:
61 QSvgIconEnginePrivate()
62 : svgBuffers(0), addedPixmaps(0)
63 { stepSerialNum(); }
64
65 ~QSvgIconEnginePrivate()
66 { delete addedPixmaps; delete svgBuffers; }
67
68 static int hashKey(QIcon::Mode mode, QIcon::State state)
69 { return (((mode)<<4)|state); }
70
71 QString pmcKey(const QSize &size, QIcon::Mode mode, QIcon::State state)
72 { return QLatin1String("$qt_svgicon_")
73 + QString::number(serialNum, 16).append(QLatin1Char('_'))
74 + QString::number((((((size.width()<<11)|size.height())<<11)|mode)<<4)|state, 16); }
75
76 void stepSerialNum()
77 { serialNum = lastSerialNum.fetchAndAddRelaxed(1); };
78
79 void loadDataForModeAndState(QSvgRenderer *renderer, QIcon::Mode mode, QIcon::State state);
80
81 QHash<int, QString> svgFiles;
82 QHash<int, QByteArray> *svgBuffers;
83 QHash<int, QPixmap> *addedPixmaps;
84 int serialNum;
85 static QAtomicInt lastSerialNum;
86};
87
88QAtomicInt QSvgIconEnginePrivate::lastSerialNum;
89
90static inline int pmKey(const QSize &size, QIcon::Mode mode, QIcon::State state)
91{
92 return ((((((size.width()<<11)|size.height())<<11)|mode)<<4)|state);
93}
94
95QSvgIconEngine::QSvgIconEngine()
96 : d(new QSvgIconEnginePrivate)
97{
98}
99
100QSvgIconEngine::QSvgIconEngine(const QSvgIconEngine &other)
101 : QIconEngineV2(other), d(new QSvgIconEnginePrivate)
102{
103 d->svgFiles = other.d->svgFiles;
104 if (other.d->svgBuffers)
105 d->svgBuffers = new QHash<int, QByteArray>(*other.d->svgBuffers);
106 if (other.d->addedPixmaps)
107 d->addedPixmaps = new QHash<int, QPixmap>(*other.d->addedPixmaps);
108}
109
110
111QSvgIconEngine::~QSvgIconEngine()
112{
113}
114
115
116QSize QSvgIconEngine::actualSize(const QSize &size, QIcon::Mode mode,
117 QIcon::State state)
118{
119 if (d->addedPixmaps) {
120 QPixmap pm = d->addedPixmaps->value(d->hashKey(mode, state));
121 if (!pm.isNull() && pm.size() == size)
122 return size;
123 }
124
125 QSvgRenderer renderer;
126 d->loadDataForModeAndState(&renderer, mode, state);
127 if (renderer.isValid()) {
128 QSize defaultSize = renderer.defaultSize();
129 if (!defaultSize.isNull())
130 defaultSize.scale(size, Qt::KeepAspectRatio);
131 return defaultSize;
132 } else {
133 return QSize();
134 }
135}
136
137void QSvgIconEnginePrivate::loadDataForModeAndState(QSvgRenderer *renderer, QIcon::Mode mode, QIcon::State state)
138{
139 QByteArray buf;
140 if (svgBuffers) {
141 buf = svgBuffers->value(hashKey(mode, state));
142 if (buf.isEmpty())
143 buf = svgBuffers->value(hashKey(QIcon::Normal, QIcon::Off));
144 }
145 if (!buf.isEmpty()) {
146#ifndef QT_NO_COMPRESS
147 buf = qUncompress(buf);
148#endif
149 renderer->load(buf);
150 } else {
151 QString svgFile = svgFiles.value(hashKey(mode, state));
152 if (svgFile.isEmpty())
153 svgFile = svgFiles.value(hashKey(QIcon::Normal, QIcon::Off));
154 if (!svgFile.isEmpty())
155 renderer->load(svgFile);
156 }
157}
158
159QPixmap QSvgIconEngine::pixmap(const QSize &size, QIcon::Mode mode,
160 QIcon::State state)
161{
162 QPixmap pm;
163
164 if (d->addedPixmaps) {
165 pm = d->addedPixmaps->value(d->hashKey(mode, state));
166 if (!pm.isNull() && pm.size() == size)
167 return pm;
168 }
169
170 QSvgRenderer renderer;
171 d->loadDataForModeAndState(&renderer, mode, state);
172 if (!renderer.isValid())
173 return pm;
174
175 QSize actualSize = renderer.defaultSize();
176 if (!actualSize.isNull())
177 actualSize.scale(size, Qt::KeepAspectRatio);
178
179 QString pmckey(d->pmcKey(actualSize, mode, state));
180 if (QPixmapCache::find(pmckey, pm))
181 return pm;
182
183 QImage img(actualSize, QImage::Format_ARGB32_Premultiplied);
184 img.fill(0x00000000);
185 QPainter p(&img);
186 renderer.render(&p);
187 p.end();
188 pm = QPixmap::fromImage(img);
189 QStyleOption opt(0);
190 opt.palette = QApplication::palette();
191 QPixmap generated = QApplication::style()->generatedIconPixmap(mode, pm, &opt);
192 if (!generated.isNull())
193 pm = generated;
194
195 if (!pm.isNull())
196 QPixmapCache::insert(pmckey, pm);
197
198 return pm;
199}
200
201
202void QSvgIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode,
203 QIcon::State state)
204{
205 if (!d->addedPixmaps)
206 d->addedPixmaps = new QHash<int, QPixmap>;
207 d->stepSerialNum();
208 d->addedPixmaps->insert(d->hashKey(mode, state), pixmap);
209}
210
211
212void QSvgIconEngine::addFile(const QString &fileName, const QSize &,
213 QIcon::Mode mode, QIcon::State state)
214{
215 if (!fileName.isEmpty()) {
216 QString abs = fileName;
217 if (fileName.at(0) != QLatin1Char(':'))
218 abs = QFileInfo(fileName).absoluteFilePath();
219 if (abs.endsWith(QLatin1String(".svg"), Qt::CaseInsensitive)
220#ifndef QT_NO_COMPRESS
221 || abs.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)
222 || abs.endsWith(QLatin1String(".svg.gz"), Qt::CaseInsensitive))
223#endif
224 {
225 QSvgRenderer renderer(abs);
226 if (renderer.isValid()) {
227 d->stepSerialNum();
228 d->svgFiles.insert(d->hashKey(mode, state), abs);
229 }
230 } else {
231 QPixmap pm(abs);
232 if (!pm.isNull())
233 addPixmap(pm, mode, state);
234 }
235 }
236}
237
238void QSvgIconEngine::paint(QPainter *painter, const QRect &rect,
239 QIcon::Mode mode, QIcon::State state)
240{
241 painter->drawPixmap(rect, pixmap(rect.size(), mode, state));
242}
243
244QString QSvgIconEngine::key() const
245{
246 return QLatin1String("svg");
247}
248
249QIconEngineV2 *QSvgIconEngine::clone() const
250{
251 return new QSvgIconEngine(*this);
252}
253
254
255bool QSvgIconEngine::read(QDataStream &in)
256{
257 d = new QSvgIconEnginePrivate;
258 d->svgBuffers = new QHash<int, QByteArray>;
259
260 if (in.version() >= QDataStream::Qt_4_4) {
261 int isCompressed;
262 QHash<int, QString> fileNames; // For memoryoptimization later
263 in >> fileNames >> isCompressed >> *d->svgBuffers;
264#ifndef QT_NO_COMPRESS
265 if (!isCompressed) {
266 foreach(int key, d->svgBuffers->keys())
267 d->svgBuffers->insert(key, qCompress(d->svgBuffers->value(key)));
268 }
269#else
270 if (isCompressed) {
271 qWarning("QSvgIconEngine: Can not decompress SVG data");
272 d->svgBuffers->clear();
273 }
274#endif
275 int hasAddedPixmaps;
276 in >> hasAddedPixmaps;
277 if (hasAddedPixmaps) {
278 d->addedPixmaps = new QHash<int, QPixmap>;
279 in >> *d->addedPixmaps;
280 }
281 }
282 else {
283 QPixmap pixmap;
284 QByteArray data;
285 uint mode;
286 uint state;
287 int num_entries;
288
289 in >> data;
290 if (!data.isEmpty()) {
291#ifndef QT_NO_COMPRESS
292 data = qUncompress(data);
293#endif
294 if (!data.isEmpty())
295 d->svgBuffers->insert(d->hashKey(QIcon::Normal, QIcon::Off), data);
296 }
297 in >> num_entries;
298 for (int i=0; i<num_entries; ++i) {
299 if (in.atEnd())
300 return false;
301 in >> pixmap;
302 in >> mode;
303 in >> state;
304 // The pm list written by 4.3 is buggy and/or useless, so ignore.
305 //addPixmap(pixmap, QIcon::Mode(mode), QIcon::State(state));
306 }
307 }
308
309 return true;
310}
311
312
313bool QSvgIconEngine::write(QDataStream &out) const
314{
315 if (out.version() >= QDataStream::Qt_4_4) {
316 int isCompressed = 0;
317#ifndef QT_NO_COMPRESS
318 isCompressed = 1;
319#endif
320 QHash<int, QByteArray> svgBuffers;
321 if (d->svgBuffers)
322 svgBuffers = *d->svgBuffers;
323 foreach(int key, d->svgFiles.keys()) {
324 QByteArray buf;
325 QFile f(d->svgFiles.value(key));
326 if (f.open(QIODevice::ReadOnly))
327 buf = f.readAll();
328#ifndef QT_NO_COMPRESS
329 buf = qCompress(buf);
330#endif
331 svgBuffers.insert(key, buf);
332 }
333 out << d->svgFiles << isCompressed << svgBuffers;
334 if (d->addedPixmaps)
335 out << (int)1 << *d->addedPixmaps;
336 else
337 out << (int)0;
338 }
339 else {
340 QByteArray buf;
341 if (d->svgBuffers)
342 buf = d->svgBuffers->value(d->hashKey(QIcon::Normal, QIcon::Off));
343 if (buf.isEmpty()) {
344 QString svgFile = d->svgFiles.value(d->hashKey(QIcon::Normal, QIcon::Off));
345 if (!svgFile.isEmpty()) {
346 QFile f(svgFile);
347 if (f.open(QIODevice::ReadOnly))
348 buf = f.readAll();
349 }
350 }
351#ifndef QT_NO_COMPRESS
352 buf = qCompress(buf);
353#endif
354 out << buf;
355 // 4.3 has buggy handling of added pixmaps, so don't write any
356 out << (int)0;
357 }
358 return true;
359}
360
361QT_END_NAMESPACE
362
363#endif // QT_NO_SVGRENDERER
Note: See TracBrowser for help on using the repository browser.