diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/svg/qsvgtinydocument.cpp | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/svg/qsvgtinydocument.cpp')
-rw-r--r-- | src/svg/qsvgtinydocument.cpp | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/src/svg/qsvgtinydocument.cpp b/src/svg/qsvgtinydocument.cpp new file mode 100644 index 0000000..b6b8526 --- /dev/null +++ b/src/svg/qsvgtinydocument.cpp @@ -0,0 +1,459 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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_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(); + } + + p->save(); + + //sets default style on the painter + //### not the most optimal way + mapSourceToTarget(p, bounds); + p->setPen(Qt::NoPen); + 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->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; + } + + p->save(); + + const QRectF elementBounds = node->transformedBounds(QTransform()); + + mapSourceToTarget(p, bounds, elementBounds); + QTransform originalTransform = p->worldTransform(); + + //XXX set default style on the painter + p->setPen(Qt::NoPen); + 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::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(QTransform()); +} + +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 |