source: trunk/src/svg/qsvgtinydocument.cpp@ 437

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

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

File size: 12.5 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 QtSvg module 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
42#include "qsvgtinydocument_p.h"
43
44#ifndef QT_NO_SVG
45
46#include "qsvghandler_p.h"
47#include "qsvgfont_p.h"
48
49#include "qpainter.h"
50#include "qfile.h"
51#include "qbuffer.h"
52#include "qbytearray.h"
53#include "qqueue.h"
54#include "qstack.h"
55#include "qdebug.h"
56
57#ifndef QT_NO_COMPRESS
58#include <zlib.h>
59#endif
60
61QT_BEGIN_NAMESPACE
62
63QSvgTinyDocument::QSvgTinyDocument()
64 : QSvgStructureNode(0),
65 m_animated(false),
66 m_animationDuration(0),
67 m_fps(30)
68{
69}
70
71QSvgTinyDocument::~QSvgTinyDocument()
72{
73}
74
75#ifndef QT_NO_COMPRESS
76# ifdef QT_BUILD_INTERNAL
77Q_AUTOTEST_EXPORT QByteArray qt_inflateGZipDataFrom(QIODevice *device);
78# else
79static QByteArray qt_inflateGZipDataFrom(QIODevice *device);
80# endif
81
82QByteArray qt_inflateGZipDataFrom(QIODevice *device)
83{
84 if (!device)
85 return QByteArray();
86
87 if (!device->isOpen())
88 device->open(QIODevice::ReadOnly);
89
90 Q_ASSERT(device->isOpen() && device->isReadable());
91
92 static const int CHUNK_SIZE = 4096;
93 int zlibResult = Z_OK;
94
95 QByteArray source;
96 QByteArray destination;
97
98 // Initialize zlib stream struct
99 z_stream zlibStream;
100 zlibStream.next_in = Z_NULL;
101 zlibStream.avail_in = 0;
102 zlibStream.avail_out = 0;
103 zlibStream.zalloc = Z_NULL;
104 zlibStream.zfree = Z_NULL;
105 zlibStream.opaque = Z_NULL;
106
107 // Adding 16 to the window size gives us gzip decoding
108 if (inflateInit2(&zlibStream, MAX_WBITS + 16) != Z_OK) {
109 qWarning("Cannot initialize zlib, because: %s",
110 (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
111 return QByteArray();
112 }
113
114 bool stillMoreWorkToDo = true;
115 while (stillMoreWorkToDo) {
116
117 if (!zlibStream.avail_in) {
118 source = device->read(CHUNK_SIZE);
119
120 if (source.isEmpty())
121 break;
122
123 zlibStream.avail_in = source.size();
124 zlibStream.next_in = reinterpret_cast<Bytef*>(source.data());
125 }
126
127 do {
128 // Prepare the destination buffer
129 int oldSize = destination.size();
130 destination.resize(oldSize + CHUNK_SIZE);
131 zlibStream.next_out = reinterpret_cast<Bytef*>(
132 destination.data() + oldSize - zlibStream.avail_out);
133 zlibStream.avail_out += CHUNK_SIZE;
134
135 zlibResult = inflate(&zlibStream, Z_NO_FLUSH);
136 switch (zlibResult) {
137 case Z_NEED_DICT:
138 case Z_DATA_ERROR:
139 case Z_STREAM_ERROR:
140 case Z_MEM_ERROR: {
141 inflateEnd(&zlibStream);
142 qWarning("Error while inflating gzip file: %s",
143 (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
144 destination.chop(zlibStream.avail_out);
145 return destination;
146 }
147 }
148
149 // If the output buffer still has more room after calling inflate
150 // it means we have to provide more data, so exit the loop here
151 } while (!zlibStream.avail_out);
152
153 if (zlibResult == Z_STREAM_END) {
154 // Make sure there are no more members to process before exiting
155 if (!(zlibStream.avail_in && inflateReset(&zlibStream) == Z_OK))
156 stillMoreWorkToDo = false;
157 }
158 }
159
160 // Chop off trailing space in the buffer
161 destination.chop(zlibStream.avail_out);
162
163 inflateEnd(&zlibStream);
164 return destination;
165}
166#endif
167
168QSvgTinyDocument * QSvgTinyDocument::load(const QString &fileName)
169{
170 QFile file(fileName);
171 if (!file.open(QFile::ReadOnly)) {
172 qWarning("Cannot open file '%s', because: %s",
173 qPrintable(fileName), qPrintable(file.errorString()));
174 return 0;
175 }
176
177#ifndef QT_NO_COMPRESS
178 if (fileName.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)
179 || fileName.endsWith(QLatin1String(".svg.gz"), Qt::CaseInsensitive)) {
180 return load(qt_inflateGZipDataFrom(&file));
181 }
182#endif
183
184 QSvgTinyDocument *doc = 0;
185 QSvgHandler handler(&file);
186 if (handler.ok()) {
187 doc = handler.document();
188 doc->m_animationDuration = handler.animationDuration();
189 } else {
190 qWarning("Cannot read file '%s', because: %s (line %d)",
191 qPrintable(fileName), qPrintable(handler.errorString()), handler.lineNumber());
192 }
193 return doc;
194}
195
196QSvgTinyDocument * QSvgTinyDocument::load(const QByteArray &contents)
197{
198#ifndef QT_NO_COMPRESS
199 // Check for gzip magic number and inflate if appropriate
200 if (contents.startsWith("\x1f\x8b")) {
201 QBuffer buffer(const_cast<QByteArray *>(&contents));
202 return load(qt_inflateGZipDataFrom(&buffer));
203 }
204#endif
205
206 QSvgHandler handler(contents);
207
208 QSvgTinyDocument *doc = 0;
209 if (handler.ok()) {
210 doc = handler.document();
211 doc->m_animationDuration = handler.animationDuration();
212 }
213 return doc;
214}
215
216QSvgTinyDocument * QSvgTinyDocument::load(QXmlStreamReader *contents)
217{
218 QSvgHandler handler(contents);
219
220 QSvgTinyDocument *doc = 0;
221 if (handler.ok()) {
222 doc = handler.document();
223 doc->m_animationDuration = handler.animationDuration();
224 }
225 return doc;
226}
227
228void QSvgTinyDocument::draw(QPainter *p, const QRectF &bounds)
229{
230 if (m_time.isNull()) {
231 m_time.start();
232 }
233
234 p->save();
235
236 //sets default style on the painter
237 //### not the most optimal way
238 mapSourceToTarget(p, bounds);
239 p->setPen(Qt::NoPen);
240 p->setBrush(Qt::black);
241 p->setRenderHint(QPainter::Antialiasing);
242 p->setRenderHint(QPainter::SmoothPixmapTransform);
243 QList<QSvgNode*>::iterator itr = m_renderers.begin();
244 applyStyle(p, m_states);
245 while (itr != m_renderers.end()) {
246 QSvgNode *node = *itr;
247 if (node->isVisible())
248 node->draw(p, m_states);
249 ++itr;
250 }
251 revertStyle(p, m_states);
252 p->restore();
253}
254
255
256void QSvgTinyDocument::draw(QPainter *p, const QString &id,
257 const QRectF &bounds)
258{
259 QSvgNode *node = scopeNode(id);
260
261 if (!node) {
262 qDebug("Couldn't find node %s. Skipping rendering.", qPrintable(id));
263 return;
264 }
265
266 p->save();
267
268 const QRectF elementBounds = node->transformedBounds(QTransform());
269
270 mapSourceToTarget(p, bounds, elementBounds);
271 QTransform originalTransform = p->worldTransform();
272
273 //XXX set default style on the painter
274 p->setPen(Qt::NoPen);
275 p->setBrush(Qt::black);
276 p->setRenderHint(QPainter::Antialiasing);
277 p->setRenderHint(QPainter::SmoothPixmapTransform);
278
279 QStack<QSvgNode*> parentApplyStack;
280 QSvgNode *parent = node->parent();
281 while (parent) {
282 parentApplyStack.push(parent);
283 parent = parent->parent();
284 }
285
286 for (int i = parentApplyStack.size() - 1; i >= 0; --i)
287 parentApplyStack[i]->applyStyle(p, m_states);
288
289 // Reset the world transform so that our parents don't affect
290 // the position
291 QTransform currentTransform = p->worldTransform();
292 p->setWorldTransform(originalTransform);
293
294 node->draw(p, m_states);
295
296 p->setWorldTransform(currentTransform);
297
298 for (int i = 0; i < parentApplyStack.size(); ++i)
299 parentApplyStack[i]->revertStyle(p, m_states);
300
301 //p->fillRect(bounds.adjusted(-5, -5, 5, 5), QColor(0, 0, 255, 100));
302
303 p->restore();
304}
305
306
307QSvgNode::Type QSvgTinyDocument::type() const
308{
309 return DOC;
310}
311
312void QSvgTinyDocument::setWidth(int len, bool percent)
313{
314 m_size.setWidth(len);
315 m_widthPercent = percent;
316}
317
318void QSvgTinyDocument::setHeight(int len, bool percent)
319{
320 m_size.setHeight(len);
321 m_heightPercent = percent;
322}
323
324void QSvgTinyDocument::setViewBox(const QRectF &rect)
325{
326 m_viewBox = rect;
327}
328
329void QSvgTinyDocument::addSvgFont(QSvgFont *font)
330{
331 m_fonts.insert(font->familyName(), font);
332}
333
334QSvgFont * QSvgTinyDocument::svgFont(const QString &family) const
335{
336 return m_fonts[family];
337}
338
339void QSvgTinyDocument::restartAnimation()
340{
341 m_time.restart();
342}
343
344bool QSvgTinyDocument::animated() const
345{
346 return m_animated;
347}
348
349void QSvgTinyDocument::setAnimated(bool a)
350{
351 m_animated = a;
352}
353
354void QSvgTinyDocument::draw(QPainter *p)
355{
356 draw(p, QRectF());
357}
358
359void QSvgTinyDocument::draw(QPainter *p, QSvgExtraStates &)
360{
361 draw(p);
362}
363
364void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect)
365{
366 QRectF target = targetRect;
367 if (target.isNull()) {
368 QPaintDevice *dev = p->device();
369 QRectF deviceRect(0, 0, dev->width(), dev->height());
370 if (deviceRect.isNull()) {
371 if (sourceRect.isNull())
372 target = QRectF(QPointF(0, 0), size());
373 else
374 target = QRectF(QPointF(0, 0), sourceRect.size());
375 } else {
376 target = deviceRect;
377 }
378 }
379
380 QRectF source = sourceRect;
381 if (source.isNull())
382 source = viewBox();
383
384 if (source != target && !source.isNull()) {
385 QTransform transform;
386 transform.scale(target.width() / source.width(),
387 target.height() / source.height());
388 QRectF c2 = transform.mapRect(source);
389 p->translate(target.x() - c2.x(),
390 target.y() - c2.y());
391 p->scale(target.width() / source.width(),
392 target.height() / source.height());
393 }
394}
395
396QRectF QSvgTinyDocument::boundsOnElement(const QString &id) const
397{
398 const QSvgNode *node = scopeNode(id);
399 if (!node)
400 node = this;
401
402 return node->transformedBounds(QTransform());
403}
404
405bool QSvgTinyDocument::elementExists(const QString &id) const
406{
407 QSvgNode *node = scopeNode(id);
408
409 return (node!=0);
410}
411
412QMatrix QSvgTinyDocument::matrixForElement(const QString &id) const
413{
414 QSvgNode *node = scopeNode(id);
415
416 if (!node) {
417 qDebug("Couldn't find node %s. Skipping rendering.", qPrintable(id));
418 return QMatrix();
419 }
420
421 QTransform t;
422
423 node = node->parent();
424 while (node) {
425 if (node->m_style.transform)
426 t *= node->m_style.transform->qtransform();
427 node = node->parent();
428 }
429
430 return t.toAffine();
431}
432
433int QSvgTinyDocument::currentFrame() const
434{
435 double runningPercentage = qMin(m_time.elapsed()/double(m_animationDuration), 1.);
436
437 int totalFrames = m_fps * m_animationDuration;
438
439 return int(runningPercentage * totalFrames);
440}
441
442void QSvgTinyDocument::setCurrentFrame(int frame)
443{
444 int totalFrames = m_fps * m_animationDuration;
445 double framePercentage = frame/double(totalFrames);
446 double timeForFrame = m_animationDuration * framePercentage; //in S
447 timeForFrame *= 1000; //in ms
448 int timeToAdd = int(timeForFrame - m_time.elapsed());
449 m_time = m_time.addMSecs(timeToAdd);
450}
451
452void QSvgTinyDocument::setFramesPerSecond(int num)
453{
454 m_fps = num;
455}
456
457QT_END_NAMESPACE
458
459#endif // QT_NO_SVG
Note: See TracBrowser for help on using the repository browser.