summaryrefslogtreecommitdiff
path: root/src/svg/qsvgtinydocument.cpp
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit983a4b61f1da719dac647a7190533d1edf2b2159 (patch)
tree3b52101d36c2b432540c20e0e188f62197c42c59 /src/svg/qsvgtinydocument.cpp
downloadqtsvg-983a4b61f1da719dac647a7190533d1edf2b2159.tar.gz
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/svg/qsvgtinydocument.cpp')
-rw-r--r--src/svg/qsvgtinydocument.cpp492
1 files changed, 492 insertions, 0 deletions
diff --git a/src/svg/qsvgtinydocument.cpp b/src/svg/qsvgtinydocument.cpp
new file mode 100644
index 0000000..7bd8004
--- /dev/null
+++ b/src/svg/qsvgtinydocument.cpp
@@ -0,0 +1,492 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgtinydocument_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qsvghandler_p.h"
+#include "qsvgfont_p.h"
+
+#include "qpainter.h"
+#include "qfile.h"
+#include "qbuffer.h"
+#include "qbytearray.h"
+#include "qqueue.h"
+#include "qstack.h"
+#include "qdebug.h"
+
+#ifndef QT_NO_COMPRESS
+#include <zlib.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QSvgTinyDocument::QSvgTinyDocument()
+ : QSvgStructureNode(0)
+ , m_widthPercent(false)
+ , m_heightPercent(false)
+ , m_animated(false)
+ , m_animationDuration(0)
+ , m_fps(30)
+{
+}
+
+QSvgTinyDocument::~QSvgTinyDocument()
+{
+}
+
+#ifndef QT_NO_COMPRESS
+# ifdef QT_BUILD_INTERNAL
+Q_AUTOTEST_EXPORT QByteArray qt_inflateGZipDataFrom(QIODevice *device);
+# else
+static QByteArray qt_inflateGZipDataFrom(QIODevice *device);
+# endif
+
+QByteArray qt_inflateGZipDataFrom(QIODevice *device)
+{
+ if (!device)
+ return QByteArray();
+
+ if (!device->isOpen())
+ device->open(QIODevice::ReadOnly);
+
+ Q_ASSERT(device->isOpen() && device->isReadable());
+
+ static const int CHUNK_SIZE = 4096;
+ int zlibResult = Z_OK;
+
+ QByteArray source;
+ QByteArray destination;
+
+ // Initialize zlib stream struct
+ z_stream zlibStream;
+ zlibStream.next_in = Z_NULL;
+ zlibStream.avail_in = 0;
+ zlibStream.avail_out = 0;
+ zlibStream.zalloc = Z_NULL;
+ zlibStream.zfree = Z_NULL;
+ zlibStream.opaque = Z_NULL;
+
+ // Adding 16 to the window size gives us gzip decoding
+ if (inflateInit2(&zlibStream, MAX_WBITS + 16) != Z_OK) {
+ qWarning("Cannot initialize zlib, because: %s",
+ (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
+ return QByteArray();
+ }
+
+ bool stillMoreWorkToDo = true;
+ while (stillMoreWorkToDo) {
+
+ if (!zlibStream.avail_in) {
+ source = device->read(CHUNK_SIZE);
+
+ if (source.isEmpty())
+ break;
+
+ zlibStream.avail_in = source.size();
+ zlibStream.next_in = reinterpret_cast<Bytef*>(source.data());
+ }
+
+ do {
+ // Prepare the destination buffer
+ int oldSize = destination.size();
+ destination.resize(oldSize + CHUNK_SIZE);
+ zlibStream.next_out = reinterpret_cast<Bytef*>(
+ destination.data() + oldSize - zlibStream.avail_out);
+ zlibStream.avail_out += CHUNK_SIZE;
+
+ zlibResult = inflate(&zlibStream, Z_NO_FLUSH);
+ switch (zlibResult) {
+ case Z_NEED_DICT:
+ case Z_DATA_ERROR:
+ case Z_STREAM_ERROR:
+ case Z_MEM_ERROR: {
+ inflateEnd(&zlibStream);
+ qWarning("Error while inflating gzip file: %s",
+ (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
+ destination.chop(zlibStream.avail_out);
+ return destination;
+ }
+ }
+
+ // If the output buffer still has more room after calling inflate
+ // it means we have to provide more data, so exit the loop here
+ } while (!zlibStream.avail_out);
+
+ if (zlibResult == Z_STREAM_END) {
+ // Make sure there are no more members to process before exiting
+ if (!(zlibStream.avail_in && inflateReset(&zlibStream) == Z_OK))
+ stillMoreWorkToDo = false;
+ }
+ }
+
+ // Chop off trailing space in the buffer
+ destination.chop(zlibStream.avail_out);
+
+ inflateEnd(&zlibStream);
+ return destination;
+}
+#endif
+
+QSvgTinyDocument * QSvgTinyDocument::load(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly)) {
+ qWarning("Cannot open file '%s', because: %s",
+ qPrintable(fileName), qPrintable(file.errorString()));
+ return 0;
+ }
+
+#ifndef QT_NO_COMPRESS
+ if (fileName.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)
+ || fileName.endsWith(QLatin1String(".svg.gz"), Qt::CaseInsensitive)) {
+ return load(qt_inflateGZipDataFrom(&file));
+ }
+#endif
+
+ QSvgTinyDocument *doc = 0;
+ QSvgHandler handler(&file);
+ if (handler.ok()) {
+ doc = handler.document();
+ doc->m_animationDuration = handler.animationDuration();
+ } else {
+ qWarning("Cannot read file '%s', because: %s (line %d)",
+ qPrintable(fileName), qPrintable(handler.errorString()), handler.lineNumber());
+ }
+ return doc;
+}
+
+QSvgTinyDocument * QSvgTinyDocument::load(const QByteArray &contents)
+{
+#ifndef QT_NO_COMPRESS
+ // Check for gzip magic number and inflate if appropriate
+ if (contents.startsWith("\x1f\x8b")) {
+ QBuffer buffer(const_cast<QByteArray *>(&contents));
+ return load(qt_inflateGZipDataFrom(&buffer));
+ }
+#endif
+
+ QSvgHandler handler(contents);
+
+ QSvgTinyDocument *doc = 0;
+ if (handler.ok()) {
+ doc = handler.document();
+ doc->m_animationDuration = handler.animationDuration();
+ }
+ return doc;
+}
+
+QSvgTinyDocument * QSvgTinyDocument::load(QXmlStreamReader *contents)
+{
+ QSvgHandler handler(contents);
+
+ QSvgTinyDocument *doc = 0;
+ if (handler.ok()) {
+ doc = handler.document();
+ doc->m_animationDuration = handler.animationDuration();
+ }
+ return doc;
+}
+
+void QSvgTinyDocument::draw(QPainter *p, const QRectF &bounds)
+{
+ if (m_time.isNull()) {
+ m_time.start();
+ }
+
+ if (displayMode() == QSvgNode::NoneMode)
+ return;
+
+ p->save();
+ //sets default style on the painter
+ //### not the most optimal way
+ mapSourceToTarget(p, bounds);
+ QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
+ pen.setMiterLimit(4);
+ p->setPen(pen);
+ p->setBrush(Qt::black);
+ p->setRenderHint(QPainter::Antialiasing);
+ p->setRenderHint(QPainter::SmoothPixmapTransform);
+ QList<QSvgNode*>::iterator itr = m_renderers.begin();
+ applyStyle(p, m_states);
+ while (itr != m_renderers.end()) {
+ QSvgNode *node = *itr;
+ if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
+ node->draw(p, m_states);
+ ++itr;
+ }
+ revertStyle(p, m_states);
+ p->restore();
+}
+
+
+void QSvgTinyDocument::draw(QPainter *p, const QString &id,
+ const QRectF &bounds)
+{
+ QSvgNode *node = scopeNode(id);
+
+ if (!node) {
+ qDebug("Couldn't find node %s. Skipping rendering.", qPrintable(id));
+ return;
+ }
+ if (m_time.isNull()) {
+ m_time.start();
+ }
+
+ if (node->displayMode() == QSvgNode::NoneMode)
+ return;
+
+ p->save();
+
+ const QRectF elementBounds = node->transformedBounds();
+
+ mapSourceToTarget(p, bounds, elementBounds);
+ QTransform originalTransform = p->worldTransform();
+
+ //XXX set default style on the painter
+ QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
+ pen.setMiterLimit(4);
+ p->setPen(pen);
+ p->setBrush(Qt::black);
+ p->setRenderHint(QPainter::Antialiasing);
+ p->setRenderHint(QPainter::SmoothPixmapTransform);
+
+ QStack<QSvgNode*> parentApplyStack;
+ QSvgNode *parent = node->parent();
+ while (parent) {
+ parentApplyStack.push(parent);
+ parent = parent->parent();
+ }
+
+ for (int i = parentApplyStack.size() - 1; i >= 0; --i)
+ parentApplyStack[i]->applyStyle(p, m_states);
+
+ // Reset the world transform so that our parents don't affect
+ // the position
+ QTransform currentTransform = p->worldTransform();
+ p->setWorldTransform(originalTransform);
+
+ node->draw(p, m_states);
+
+ p->setWorldTransform(currentTransform);
+
+ for (int i = 0; i < parentApplyStack.size(); ++i)
+ parentApplyStack[i]->revertStyle(p, m_states);
+
+ //p->fillRect(bounds.adjusted(-5, -5, 5, 5), QColor(0, 0, 255, 100));
+
+ p->restore();
+}
+
+
+QSvgNode::Type QSvgTinyDocument::type() const
+{
+ return DOC;
+}
+
+void QSvgTinyDocument::setWidth(int len, bool percent)
+{
+ m_size.setWidth(len);
+ m_widthPercent = percent;
+}
+
+void QSvgTinyDocument::setHeight(int len, bool percent)
+{
+ m_size.setHeight(len);
+ m_heightPercent = percent;
+}
+
+void QSvgTinyDocument::setViewBox(const QRectF &rect)
+{
+ m_viewBox = rect;
+}
+
+void QSvgTinyDocument::addSvgFont(QSvgFont *font)
+{
+ m_fonts.insert(font->familyName(), font);
+}
+
+QSvgFont * QSvgTinyDocument::svgFont(const QString &family) const
+{
+ return m_fonts[family];
+}
+
+void QSvgTinyDocument::addNamedNode(const QString &id, QSvgNode *node)
+{
+ m_namedNodes.insert(id, node);
+}
+
+QSvgNode *QSvgTinyDocument::namedNode(const QString &id) const
+{
+ return m_namedNodes.value(id);
+}
+
+void QSvgTinyDocument::addNamedStyle(const QString &id, QSvgFillStyleProperty *style)
+{
+ m_namedStyles.insert(id, style);
+}
+
+QSvgFillStyleProperty *QSvgTinyDocument::namedStyle(const QString &id) const
+{
+ return m_namedStyles.value(id);
+}
+
+void QSvgTinyDocument::restartAnimation()
+{
+ m_time.restart();
+}
+
+bool QSvgTinyDocument::animated() const
+{
+ return m_animated;
+}
+
+void QSvgTinyDocument::setAnimated(bool a)
+{
+ m_animated = a;
+}
+
+void QSvgTinyDocument::draw(QPainter *p)
+{
+ draw(p, QRectF());
+}
+
+void QSvgTinyDocument::draw(QPainter *p, QSvgExtraStates &)
+{
+ draw(p);
+}
+
+void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect)
+{
+ QRectF target = targetRect;
+ if (target.isNull()) {
+ QPaintDevice *dev = p->device();
+ QRectF deviceRect(0, 0, dev->width(), dev->height());
+ if (deviceRect.isNull()) {
+ if (sourceRect.isNull())
+ target = QRectF(QPointF(0, 0), size());
+ else
+ target = QRectF(QPointF(0, 0), sourceRect.size());
+ } else {
+ target = deviceRect;
+ }
+ }
+
+ QRectF source = sourceRect;
+ if (source.isNull())
+ source = viewBox();
+
+ if (source != target && !source.isNull()) {
+ QTransform transform;
+ transform.scale(target.width() / source.width(),
+ target.height() / source.height());
+ QRectF c2 = transform.mapRect(source);
+ p->translate(target.x() - c2.x(),
+ target.y() - c2.y());
+ p->scale(target.width() / source.width(),
+ target.height() / source.height());
+ }
+}
+
+QRectF QSvgTinyDocument::boundsOnElement(const QString &id) const
+{
+ const QSvgNode *node = scopeNode(id);
+ if (!node)
+ node = this;
+ return node->transformedBounds();
+}
+
+bool QSvgTinyDocument::elementExists(const QString &id) const
+{
+ QSvgNode *node = scopeNode(id);
+
+ return (node!=0);
+}
+
+QMatrix QSvgTinyDocument::matrixForElement(const QString &id) const
+{
+ QSvgNode *node = scopeNode(id);
+
+ if (!node) {
+ qDebug("Couldn't find node %s. Skipping rendering.", qPrintable(id));
+ return QMatrix();
+ }
+
+ QTransform t;
+
+ node = node->parent();
+ while (node) {
+ if (node->m_style.transform)
+ t *= node->m_style.transform->qtransform();
+ node = node->parent();
+ }
+
+ return t.toAffine();
+}
+
+int QSvgTinyDocument::currentFrame() const
+{
+ double runningPercentage = qMin(m_time.elapsed()/double(m_animationDuration), 1.);
+
+ int totalFrames = m_fps * m_animationDuration;
+
+ return int(runningPercentage * totalFrames);
+}
+
+void QSvgTinyDocument::setCurrentFrame(int frame)
+{
+ int totalFrames = m_fps * m_animationDuration;
+ double framePercentage = frame/double(totalFrames);
+ double timeForFrame = m_animationDuration * framePercentage; //in S
+ timeForFrame *= 1000; //in ms
+ int timeToAdd = int(timeForFrame - m_time.elapsed());
+ m_time = m_time.addMSecs(timeToAdd);
+}
+
+void QSvgTinyDocument::setFramesPerSecond(int num)
+{
+ m_fps = num;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG