/**************************************************************************** ** ** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved. ** ** This file is part of the QtSVG module of the Qt Toolkit. ** ** This file may be used under the terms of the GNU General Public ** License version 2.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 GNU ** General Public Licensing requirements will be met: ** http://trolltech.com/products/qt/licenses/licensing/opensource/ ** ** If you are unsure which license is appropriate for your use, please ** review the following information: ** http://trolltech.com/products/qt/licenses/licensing/licensingoverview ** or contact the sales department at sales@trolltech.com. ** ** In addition, as a special exception, Trolltech gives you certain ** additional rights. These rights are described in the Trolltech GPL ** Exception version 1.0, which can be found at ** http://www.trolltech.com/products/qt/gplexception/ and in the file ** GPL_EXCEPTION.txt in this package. ** ** In addition, as a special exception, Trolltech, as the sole copyright ** holder for Qt Designer, grants users of the Qt/Eclipse Integration ** plug-in the right for the Qt/Eclipse Integration to link to ** functionality provided by Qt Designer and its related libraries. ** ** Trolltech reserves all rights not expressly granted herein. ** ** Trolltech ASA (c) 2007 ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ #include "qsvgstyle_p.h" #include "qsvgfont_p.h" #include "qsvggraphics_p.h" #include "qsvgnode_p.h" #include "qsvgtinydocument_p.h" #include "qpainter.h" #include "qpair.h" #include "qcolor.h" #include "qdebug.h" #include QSvgStyleProperty::~QSvgStyleProperty() { } QSvgQualityStyle::QSvgQualityStyle(int color) : m_colorRendering(color) { } void QSvgQualityStyle::apply(QPainter *, const QRectF &, QSvgNode *) { } void QSvgQualityStyle::revert(QPainter *) { } QSvgFillStyle::QSvgFillStyle(const QBrush &brush, bool fromColor) : m_fill(brush), m_fromColor(fromColor), m_fillRuleSet(false) { } void QSvgFillStyle::setFillRule(Qt::FillRule f) { m_fillRuleSet = true; m_fillRule = f; } static void recursivelySetFill(QSvgNode *node, Qt::FillRule f) { if (node->type() == QSvgNode::PATH) { QSvgPath *path = static_cast(node); path->qpath()->setFillRule(f); } else if (node->type() == QSvgNode::G) { QList renderers = static_cast(node)->renderers(); foreach(QSvgNode *n, renderers) { recursivelySetFill(n, f); } } } void QSvgFillStyle::apply(QPainter *p, const QRectF &, QSvgNode *node) { m_oldFill = p->brush(); if (m_fillRuleSet) { recursivelySetFill(node, m_fillRule); m_fillRuleSet = false;//set it only on the first run } p->setBrush(m_fill); } void QSvgFillStyle::revert(QPainter *p) { p->setBrush(m_oldFill); } QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush) : m_viewportFill(brush) { } void QSvgViewportFillStyle::apply(QPainter *p, const QRectF &, QSvgNode *) { m_oldFill = p->brush(); p->setBrush(m_viewportFill); } void QSvgViewportFillStyle::revert(QPainter *p) { p->setBrush(m_oldFill); } QSvgFontStyle::QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc) : m_font(font), m_pointSize(24), m_doc(doc) { } QSvgFontStyle::QSvgFontStyle(const QFont &font, QSvgTinyDocument *doc) : m_font(0), m_pointSize(24), m_doc(doc), m_qfont(font) { } void QSvgFontStyle::setPointSize(qreal size) { m_pointSize = size; } qreal QSvgFontStyle::pointSize() const { return m_pointSize; } void QSvgFontStyle::apply(QPainter *p, const QRectF &, QSvgNode *) { if (!m_font) { m_oldFont = p->font(); p->setFont(m_qfont); } } void QSvgFontStyle::revert(QPainter *p) { if (!m_font) { p->setFont(m_oldFont); } } QSvgStrokeStyle::QSvgStrokeStyle(const QPen &pen) : m_stroke(pen) { } void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *) { m_oldStroke = p->pen(); p->setPen(m_stroke); } void QSvgStrokeStyle::revert(QPainter *p) { p->setPen(m_oldStroke); } QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color) : m_solidColor(color) { } void QSvgSolidColorStyle::apply(QPainter *p, const QRectF &, QSvgNode *) { m_oldFill = p->brush(); m_oldStroke = p->pen(); QBrush b = m_oldFill; b.setColor(m_solidColor); p->setBrush(b); QPen pen = m_oldStroke; pen.setColor(m_solidColor); p->setPen(pen); } void QSvgSolidColorStyle::revert(QPainter *p) { p->setBrush(m_oldFill); p->setPen(m_oldStroke); } QSvgGradientStyle::QSvgGradientStyle(QGradient *grad, bool resolveBounds) : m_gradient(grad), m_resolveBounds(resolveBounds) { } void QSvgGradientStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *) { if (!m_link.isEmpty()) { resolveStops(); } m_oldFill = p->brush(); //resolving stop colors if (!m_resolvePoints.isEmpty()) { QColor color = p->brush().color(); if (!color.isValid()) color = p->pen().color(); QList::const_iterator itr = m_resolvePoints.constBegin(); for (; itr != m_resolvePoints.constEnd(); ++itr) { //qDebug()<<"resolving "<<(*itr)<<" to "<setColorAt(*itr, color); } } QBrush brush; //we need to resolve boundaries //the code is funky i'll have to verify it //(testcases right now are bugs/resolve_radial.svg // and bugs/resolve_linear.svg) if (m_resolveBounds) { if (m_gradient->type() == QGradient::LinearGradient) { QLinearGradient *grad = (QLinearGradient*)(m_gradient); qreal xs, ys, xf, yf; xs = rect.x() + rect.width() * grad->start().x(); ys = rect.y() + rect.height() * grad->start().y(); xf = rect.x() + rect.width() * grad->finalStop().x(); yf = rect.y() + rect.height() * grad->finalStop().y(); QLinearGradient gradient(xs, ys, xf, yf); gradient.setStops(m_gradient->stops()); gradient.setSpread(m_gradient->spread()); brush = QBrush(gradient); } else { QRadialGradient *grad = (QRadialGradient*)m_gradient; qreal cx, cy, r, fx, fy; cx = rect.width() * grad->center().x(); cy = rect.height() * grad->center().y(); //### the radius is wrong. it has to be transformed // so that the horizontal on is rect.width() * grad->radius(); // and vertical rect.height() * grad->radius(). it's a simple // transformation but we don't support exclusive fill // transformations at the moment r = rect.width() * grad->radius(); fx = rect.width() * grad->focalPoint().x(); fy = rect.width() * grad->focalPoint().y(); //qDebug()<stops()); gradient.setSpread(m_gradient->spread()); brush = QBrush(gradient); } } else { brush = QBrush(*m_gradient); } if (!m_matrix.isIdentity()) brush.setMatrix(m_matrix); p->setBrush(brush); } void QSvgGradientStyle::revert(QPainter *p) { p->setBrush(m_oldFill); } void QSvgGradientStyle::setMatrix(const QMatrix &mat) { m_matrix = mat; } void QSvgGradientStyle::addResolve(qreal offset) { m_resolvePoints.append(offset); } QSvgTransformStyle::QSvgTransformStyle(const QMatrix &trans) : m_transform(trans) { } void QSvgTransformStyle::apply(QPainter *p, const QRectF &, QSvgNode *) { m_oldWorldMatrix = p->matrix(); p->setMatrix(m_transform, true); } void QSvgTransformStyle::revert(QPainter *p) { p->setMatrix(m_oldWorldMatrix, false);//don't combine } QSvgStyleProperty::Type QSvgQualityStyle::type() const { return QUALITY; } QSvgStyleProperty::Type QSvgFillStyle::type() const { return FILL; } QSvgStyleProperty::Type QSvgViewportFillStyle::type() const { return VIEWPORT_FILL; } QSvgStyleProperty::Type QSvgFontStyle::type() const { return FONT; } QSvgStyleProperty::Type QSvgStrokeStyle::type() const { return STROKE; } QSvgStyleProperty::Type QSvgSolidColorStyle::type() const { return SOLID_COLOR; } QSvgStyleProperty::Type QSvgGradientStyle::type() const { return GRADIENT; } QSvgStyleProperty::Type QSvgTransformStyle::type() const { return TRANSFORM; } QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode) : m_mode(mode) { } void QSvgCompOpStyle::apply(QPainter *p, const QRectF &, QSvgNode *) { m_oldMode = p->compositionMode(); p->setCompositionMode(m_mode); } void QSvgCompOpStyle::revert(QPainter *p) { p->setCompositionMode(m_oldMode); } QSvgStyleProperty::Type QSvgCompOpStyle::type() const { return COMP_OP; } QSvgStyle::~QSvgStyle() { } void QSvgStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node) { if (quality) { quality->apply(p, rect, node); } if (fill) { fill->apply(p, rect, node); } if (viewportFill) { viewportFill->apply(p, rect, node); } if (font) { font->apply(p, rect, node); } if (stroke) { stroke->apply(p, rect, node); } if (solidColor) { solidColor->apply(p, rect, node); } if (gradient) { gradient->apply(p, rect, node); } if (transform) { transform->apply(p, rect, node); } if (animateColor) { animateColor->apply(p, rect, node); } //animated transforms have to be applied //_after_ the original object transformations if (!animateTransforms.isEmpty()) { QList >::const_iterator itr; for (itr = animateTransforms.constBegin(); itr != animateTransforms.constEnd(); ++itr) { (*itr)->apply(p, rect, node); } } if (opacity) { opacity->apply(p, rect, node); } if (compop) { compop->apply(p, rect, node); } } void QSvgStyle::revert(QPainter *p) { if (quality) { quality->revert(p); } if (fill) { fill->revert(p); } if (viewportFill) { viewportFill->revert(p); } if (font) { font->revert(p); } if (stroke) { stroke->revert(p); } if (solidColor) { solidColor->revert(p); } if (gradient) { gradient->revert(p); } //animated transforms need to be reverted _before_ //the native transforms if (!animateTransforms.isEmpty()) { QList >::const_iterator itr; itr = animateTransforms.constBegin(); //only need to rever the first one because that //one has the original world matrix for the primitve if (itr != animateTransforms.constEnd()) { (*itr)->revert(p); } } if (transform) { transform->revert(p); } if (animateColor) { animateColor->revert(p); } if (opacity) { opacity->revert(p); } if (compop) { compop->revert(p); } } QSvgAnimateTransform::QSvgAnimateTransform(int startMs, int endMs, int byMs ) : QSvgStyleProperty(), m_from(startMs), m_to(endMs), m_by(byMs), m_type(Empty), m_count(0), m_finished(false) { m_totalRunningTime = m_to - m_from; } void QSvgAnimateTransform::setArgs(TransformType type, const QVector &args) { m_type = type; m_args = args; Q_ASSERT(!(args.count()%3)); m_count = args.count() / 3; } void QSvgAnimateTransform::apply(QPainter *p, const QRectF &, QSvgNode *node) { m_oldWorldMatrix = p->matrix(); resolveMatrix(node); if (!m_finished || m_freeze) p->setMatrix(m_transform, true); } void QSvgAnimateTransform::revert(QPainter *p) { if (!m_finished || m_freeze) { p->setMatrix(m_oldWorldMatrix, false);//don't combine } } void QSvgAnimateTransform::resolveMatrix(QSvgNode *node) { static const qreal deg2rad = qreal(0.017453292519943295769); qreal elapsed = node->document()->currentElapsed(); qreal percent = (elapsed - m_from) / m_to; if (elapsed < m_from || m_finished) return; if (percent > 1) { percent -= ((int)percent); } qreal currentPosition = percent * (m_count-1); //array offset int startElem = static_cast(floor(currentPosition)); int endElem = static_cast(ceil(currentPosition)); switch(m_type) { case Translate: { startElem *= 3; endElem *= 3; qreal from1, from2, from3; qreal to1, to2, to3; from1 = m_args[startElem++]; from2 = m_args[startElem++]; from3 = m_args[startElem++]; to1 = m_args[endElem++]; to2 = m_args[endElem++]; to3 = m_args[endElem++]; qreal transXDiff = (to1-from1) * percent; qreal transX = from1 + transXDiff; qreal transYDiff = (to2-from2) * percent; qreal transY = from2 + transYDiff; m_transform = QMatrix(); m_transform.translate(transX, transY); break; } case Scale: { startElem *= 3; endElem *= 3; qreal from1, from2, from3; qreal to1, to2, to3; from1 = m_args[startElem++]; from2 = m_args[startElem++]; from3 = m_args[startElem++]; to1 = m_args[endElem++]; to2 = m_args[endElem++]; to3 = m_args[endElem++]; qreal transXDiff = (to1-from1) * percent; qreal transX = from1 + transXDiff; qreal transYDiff = (to2-from2) * percent; qreal transY = from2 + transYDiff; if (transY == 0) transY = transX; m_transform = QMatrix(); m_transform.scale(transX, transY); break; } case Rotate: { startElem *= 3; endElem *= 3; qreal from1, from2, from3; qreal to1, to2, to3; from1 = m_args[startElem++]; from2 = m_args[startElem++]; from3 = m_args[startElem++]; to1 = m_args[endElem++]; to2 = m_args[endElem++]; to3 = m_args[endElem++]; qreal rotationDiff = (to1-from1) * percent; //qreal rotation = from1 + rotationDiff; qreal transXDiff = (to2-from2) * percent; qreal transX = from2 + transXDiff; qreal transYDiff = (to3-from3) * percent; qreal transY = from3 + transYDiff; m_transform = QMatrix(); m_transform.translate(transX, transY); m_transform.rotate(rotationDiff); m_transform.translate(-transX, -transY); break; } case SkewX: { startElem *= 3; endElem *= 3; qreal from1, from2, from3; qreal to1, to2, to3; from1 = m_args[startElem++]; from2 = m_args[startElem++]; from3 = m_args[startElem++]; to1 = m_args[endElem++]; to2 = m_args[endElem++]; to3 = m_args[endElem++]; qreal transXDiff = (to1-from1) * percent; qreal transX = from1 + transXDiff; m_transform = QMatrix(); m_transform.shear(tan(transX*deg2rad), 0); break; } case SkewY: { startElem *= 3; endElem *= 3; qreal from1, from2, from3; qreal to1, to2, to3; from1 = m_args[startElem++]; from2 = m_args[startElem++]; from3 = m_args[startElem++]; to1 = m_args[endElem++]; to2 = m_args[endElem++]; to3 = m_args[endElem++]; qreal transYDiff = (to1-from1) * percent; qreal transY = from1 + transYDiff; m_transform = QMatrix(); m_transform.shear(0, tan(transY*deg2rad)); break; } default: break; } if (m_repeatCount < 0) return; if (elapsed > m_to) { if (m_repeatCount > 1) { --m_repeatCount; } else if (m_repeatCount > 0 && m_repeatCount < 1) { if (m_repeatCount <= percent) { m_finished = true; } } } else if (m_repeatCount > 0 && m_repeatCount < 1) { //this happens if m_repeatCount < 1 from the start if (m_repeatCount <= percent) { m_finished = true; } } } QSvgStyleProperty::Type QSvgAnimateTransform::type() const { return ANIMATE_TRANSFORM; } void QSvgAnimateTransform::setFreeze(bool freeze) { m_freeze = freeze; } void QSvgAnimateTransform::setRepeatCount(qreal repeatCount) { m_repeatCount = repeatCount; } QSvgAnimateColor::QSvgAnimateColor(int startMs, int endMs, int byMs) : QSvgStyleProperty(), m_from(startMs), m_to(endMs), m_by(byMs), m_finished(false) { m_totalRunningTime = m_to - m_from; } void QSvgAnimateColor::setArgs(bool fill, const QList &colors) { m_fill = fill; m_colors = colors; } void QSvgAnimateColor::setFreeze(bool freeze) { m_freeze = freeze; } void QSvgAnimateColor::setRepeatCount(qreal repeatCount) { m_repeatCount = repeatCount; } void QSvgAnimateColor::apply(QPainter *p, const QRectF &, QSvgNode *node) { qreal elapsed = node->document()->currentElapsed(); qreal percent = (elapsed - m_from) / m_to; if (elapsed < m_from || m_finished) return; if (percent > 1) { percent -= ((int)percent); } qreal currentPosition = percent * (m_colors.count()-1); //array offset percent *= (m_colors.count() - 1); if (percent > 1) { percent -= ((int)percent); } int startElem = static_cast(floor(currentPosition)); int endElem = static_cast(ceil(currentPosition)); QColor start = m_colors[startElem]; QColor end = m_colors[endElem]; qreal aDiff = (end.alpha() - start.alpha()) * percent; qreal rDiff = (end.red() - start.red()) * percent; qreal gDiff = (end.green() - start.green()) * percent; qreal bDiff = (end.blue() - start.blue()) * percent; int alpha = int(start.alpha() + aDiff); int red = int(start.red() + rDiff); int green = int(start.green() + gDiff); int blue = int(start.blue() + bDiff); QColor color(red, green, blue, alpha); if (m_fill) { QBrush b = p->brush(); m_oldBrush = b; b.setColor(color); p->setBrush(b); } else { QPen pen = p->pen(); m_oldPen = pen; pen.setColor(color); p->setPen(pen); } if (m_repeatCount < 0) return; if (elapsed > m_to) { if (m_repeatCount > 1) { --m_repeatCount; } else if (m_repeatCount > 0 && m_repeatCount < 1) { if (m_repeatCount <= percent) { m_finished = true; } } } else if (m_repeatCount > 0 && m_repeatCount < 1) { //this happens if m_repeatCount < 1 from the start if (m_repeatCount <= percent) { m_finished = true; } } } void QSvgAnimateColor::revert(QPainter *p) { if (m_fill) { p->setBrush(m_oldBrush); } else { p->setPen(m_oldPen); } } QSvgStyleProperty::Type QSvgAnimateColor::type() const { return ANIMATE_COLOR; } QString QSvgFontStyle::textAnchor() const { return m_textAnchor; } void QSvgFontStyle::setTextAnchor(const QString &anchor) { m_textAnchor = anchor; } QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity) : m_opacity(opacity) { } void QSvgOpacityStyle::apply(QPainter *p, const QRectF &, QSvgNode *) { m_oldOpacity = p->opacity(); p->setOpacity(m_opacity); } void QSvgOpacityStyle::revert(QPainter *p) { p->setOpacity(m_oldOpacity); } QSvgStyleProperty::Type QSvgOpacityStyle::type() const { return OPACITY; } void QSvgGradientStyle::setStopLink(const QString &link, QSvgTinyDocument *doc) { m_link = link; m_doc = doc; } void QSvgGradientStyle::resolveStops() { if (!m_link.isEmpty() && m_doc) { QSvgStyleProperty *prop = m_doc->scopeStyle(m_link); if (prop) { if (prop->type() == QSvgStyleProperty::GRADIENT) { QSvgGradientStyle *st = static_cast(prop); st->resolveStops(); m_gradient->setStops(st->qgradient()->stops()); } } m_link = QString(); } }