Fixes a crash in QDoubleSpinBox
[qt-netbsd.git] / src / svg / qsvgstyle.cpp
blob802181fe3fc08fd8196adc836a63e99a843e9ca2
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtSvg module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial Usage
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
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.
36 ** If you have questions regarding the use of this file, please contact
37 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
42 #include "qsvgstyle_p.h"
44 #ifndef QT_NO_SVG
46 #include "qsvgfont_p.h"
47 #include "qsvggraphics_p.h"
48 #include "qsvgnode_p.h"
49 #include "qsvgtinydocument_p.h"
51 #include "qpainter.h"
52 #include "qpair.h"
53 #include "qcolor.h"
54 #include "qdebug.h"
55 #include "qmath.h"
56 #include "qnumeric.h"
58 QT_BEGIN_NAMESPACE
60 QSvgExtraStates::QSvgExtraStates()
61 : fillOpacity(1.0)
65 QSvgStyleProperty::~QSvgStyleProperty()
69 QSvgQualityStyle::QSvgQualityStyle(int color)
70 : m_colorRendering(color)
74 void QSvgQualityStyle::apply(QPainter *, const QRectF &, QSvgNode *, QSvgExtraStates &)
78 void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &)
83 QSvgFillStyle::QSvgFillStyle(const QBrush &brush)
84 : m_fill(brush), m_style(0), m_fillRuleSet(false), m_fillRule(Qt::OddEvenFill),
85 m_fillOpacitySet(false), m_fillOpacity(0),m_oldOpacity(0)
89 QSvgFillStyle::QSvgFillStyle(QSvgStyleProperty *style)
90 : m_style(style), m_fillRuleSet(false), m_fillRule(Qt::OddEvenFill),
91 m_fillOpacitySet(false), m_fillOpacity(0),m_oldOpacity(0)
95 void QSvgFillStyle::setFillRule(Qt::FillRule f)
97 m_fillRuleSet = true;
98 m_fillRule = f;
101 void QSvgFillStyle::setFillOpacity(qreal opacity)
103 m_fillOpacitySet = true;
104 m_fillOpacity = opacity;
107 static void recursivelySetFill(QSvgNode *node, Qt::FillRule f)
109 if (node->type() == QSvgNode::PATH) {
110 QSvgPath *path = static_cast<QSvgPath*>(node);
111 path->qpath()->setFillRule(f);
112 } else if (node->type() == QSvgNode::G) {
113 QList<QSvgNode*> renderers = static_cast<QSvgG*>(node)->renderers();
114 foreach(QSvgNode *n, renderers) {
115 recursivelySetFill(n, f);
119 void QSvgFillStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtraStates &states)
121 m_oldFill = p->brush();
122 m_oldOpacity = states.fillOpacity;
124 if (m_fillRuleSet) {
125 recursivelySetFill(node, m_fillRule);
126 m_fillRuleSet = false;//set it only on the first run
128 p->setBrush(m_fill);
129 if (m_fillOpacitySet)
130 states.fillOpacity = m_fillOpacity;
131 if (m_style)
132 m_style->apply(p, rect, node, states);
135 void QSvgFillStyle::revert(QPainter *p, QSvgExtraStates &states)
137 if (m_style)
138 m_style->revert(p, states);
139 p->setBrush(m_oldFill);
140 if (m_fillOpacitySet)
141 states.fillOpacity = m_oldOpacity;
144 QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush)
145 : m_viewportFill(brush)
149 void QSvgViewportFillStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
151 m_oldFill = p->brush();
152 p->setBrush(m_viewportFill);
155 void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &)
157 p->setBrush(m_oldFill);
160 QSvgFontStyle::QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc)
161 : m_font(font), m_pointSize(24), m_doc(doc)
165 QSvgFontStyle::QSvgFontStyle(const QFont &font, QSvgTinyDocument *doc)
166 : m_font(0), m_pointSize(24), m_doc(doc), m_qfont(font)
171 void QSvgFontStyle::setPointSize(qreal size)
173 m_pointSize = size;
176 qreal QSvgFontStyle::pointSize() const
178 return m_pointSize;
181 void QSvgFontStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
183 if (!m_font) {
184 m_oldFont = p->font();
185 p->setFont(m_qfont);
189 void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &)
191 if (!m_font) {
192 p->setFont(m_oldFont);
196 QSvgStrokeStyle::QSvgStrokeStyle(const QPen &pen)
197 : m_stroke(pen)
201 void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
203 m_oldStroke = p->pen();
204 p->setPen(m_stroke);
207 void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &)
209 p->setPen(m_oldStroke);
212 QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
213 : m_solidColor(color)
217 void QSvgSolidColorStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
219 m_oldFill = p->brush();
220 m_oldStroke = p->pen();
221 QBrush b = m_oldFill;
222 b.setColor(m_solidColor);
223 p->setBrush(b);
224 QPen pen = m_oldStroke;
225 pen.setColor(m_solidColor);
226 p->setPen(pen);
229 void QSvgSolidColorStyle::revert(QPainter *p, QSvgExtraStates &)
231 p->setBrush(m_oldFill);
232 p->setPen(m_oldStroke);
235 QSvgGradientStyle::QSvgGradientStyle(QGradient *grad)
236 : m_gradient(grad), m_gradientStopsSet(false)
240 void QSvgGradientStyle::apply(QPainter *p, const QRectF &/*rect*/, QSvgNode *, QSvgExtraStates &)
242 if (!m_link.isEmpty()) {
243 resolveStops();
246 m_oldFill = p->brush();
248 //resolving stop colors
249 if (!m_resolvePoints.isEmpty()) {
250 QColor color = p->brush().color();
251 if (!color.isValid())
252 color = p->pen().color();
253 QList<qreal>::const_iterator itr = m_resolvePoints.constBegin();
254 for (; itr != m_resolvePoints.constEnd(); ++itr) {
255 //qDebug()<<"resolving "<<(*itr)<<" to "<<color;
256 m_gradient->setColorAt(*itr, color);
260 // If the gradient is marked as empty, insert transparent black
261 if (!m_gradientStopsSet) {
262 m_gradient->setStops(QGradientStops() << QGradientStop(0.0, QColor(0, 0, 0, 0)));
263 m_gradientStopsSet = true;
266 QBrush brush;
267 brush = QBrush(*m_gradient);
269 if (!m_matrix.isIdentity())
270 brush.setMatrix(m_matrix);
272 p->setBrush(brush);
275 void QSvgGradientStyle::revert(QPainter *p, QSvgExtraStates &)
277 p->setBrush(m_oldFill);
281 void QSvgGradientStyle::setMatrix(const QMatrix &mat)
283 m_matrix = mat;
286 void QSvgGradientStyle::addResolve(qreal offset)
288 m_resolvePoints.append(offset);
291 QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans)
292 : m_transform(trans)
296 void QSvgTransformStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
298 m_oldWorldTransform = p->worldTransform();
299 p->setWorldTransform(m_transform, true);
302 void QSvgTransformStyle::revert(QPainter *p, QSvgExtraStates &)
304 p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
307 QSvgStyleProperty::Type QSvgQualityStyle::type() const
309 return QUALITY;
312 QSvgStyleProperty::Type QSvgFillStyle::type() const
314 return FILL;
317 QSvgStyleProperty::Type QSvgViewportFillStyle::type() const
319 return VIEWPORT_FILL;
322 QSvgStyleProperty::Type QSvgFontStyle::type() const
324 return FONT;
327 QSvgStyleProperty::Type QSvgStrokeStyle::type() const
329 return STROKE;
332 QSvgStyleProperty::Type QSvgSolidColorStyle::type() const
334 return SOLID_COLOR;
337 QSvgStyleProperty::Type QSvgGradientStyle::type() const
339 return GRADIENT;
342 QSvgStyleProperty::Type QSvgTransformStyle::type() const
344 return TRANSFORM;
348 QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode)
349 : m_mode(mode)
354 void QSvgCompOpStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
356 m_oldMode = p->compositionMode();
357 p->setCompositionMode(m_mode);
360 void QSvgCompOpStyle::revert(QPainter *p, QSvgExtraStates &)
362 p->setCompositionMode(m_oldMode);
365 QSvgStyleProperty::Type QSvgCompOpStyle::type() const
367 return COMP_OP;
370 QSvgStyle::~QSvgStyle()
374 void QSvgStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtraStates &states)
376 if (quality) {
377 quality->apply(p, rect, node, states);
380 if (fill) {
381 fill->apply(p, rect, node, states);
384 if (viewportFill) {
385 viewportFill->apply(p, rect, node, states);
388 if (font) {
389 font->apply(p, rect, node, states);
392 if (stroke) {
393 stroke->apply(p, rect, node, states);
396 if (solidColor) {
397 solidColor->apply(p, rect, node, states);
400 if (gradient) {
401 gradient->apply(p, rect, node, states);
404 if (transform) {
405 transform->apply(p, rect, node, states);
408 if (animateColor) {
409 animateColor->apply(p, rect, node, states);
412 //animated transforms have to be applied
413 //_after_ the original object transformations
414 if (!animateTransforms.isEmpty()) {
415 QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr;
416 for (itr = animateTransforms.constBegin(); itr != animateTransforms.constEnd();
417 ++itr) {
418 (*itr)->apply(p, rect, node, states);
422 if (opacity) {
423 opacity->apply(p, rect, node, states);
426 if (compop) {
427 compop->apply(p, rect, node, states);
431 void QSvgStyle::revert(QPainter *p, QSvgExtraStates &states)
433 if (quality) {
434 quality->revert(p, states);
437 if (fill) {
438 fill->revert(p, states);
441 if (viewportFill) {
442 viewportFill->revert(p, states);
445 if (font) {
446 font->revert(p, states);
449 if (stroke) {
450 stroke->revert(p, states);
453 if (solidColor) {
454 solidColor->revert(p, states);
457 if (gradient) {
458 gradient->revert(p, states);
461 //animated transforms need to be reverted _before_
462 //the native transforms
463 if (!animateTransforms.isEmpty()) {
464 QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr;
465 itr = animateTransforms.constBegin();
466 //only need to rever the first one because that
467 //one has the original world matrix for the primitve
468 if (itr != animateTransforms.constEnd()) {
469 (*itr)->revert(p, states);
473 if (transform) {
474 transform->revert(p, states);
477 if (animateColor) {
478 animateColor->revert(p, states);
481 if (opacity) {
482 opacity->revert(p, states);
485 if (compop) {
486 compop->revert(p, states);
490 QSvgAnimateTransform::QSvgAnimateTransform(int startMs, int endMs, int byMs )
491 : QSvgStyleProperty(),
492 m_from(startMs), m_to(endMs), m_by(byMs),
493 m_type(Empty), m_count(0), m_finished(false)
495 m_totalRunningTime = m_to - m_from;
498 void QSvgAnimateTransform::setArgs(TransformType type, const QVector<qreal> &args)
500 m_type = type;
501 m_args = args;
502 Q_ASSERT(!(args.count()%3));
503 m_count = args.count() / 3;
506 void QSvgAnimateTransform::apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &)
508 m_oldWorldTransform = p->worldTransform();
509 resolveMatrix(node);
510 if (!m_finished || m_freeze)
511 p->setWorldTransform(m_transform, true);
514 void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &)
516 if (!m_finished || m_freeze) {
517 p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
521 void QSvgAnimateTransform::resolveMatrix(QSvgNode *node)
523 static const qreal deg2rad = qreal(0.017453292519943295769);
524 qreal totalTimeElapsed = node->document()->currentElapsed();
525 if (totalTimeElapsed < m_from || m_finished)
526 return;
528 qreal animationFrame = 0;
529 if (m_totalRunningTime != 0) {
530 animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
532 if (m_repeatCount >= 0 && m_repeatCount < animationFrame) {
533 m_finished = true;
534 animationFrame = m_repeatCount;
538 qreal percentOfAnimation = animationFrame;
539 if (percentOfAnimation > 1) {
540 percentOfAnimation -= ((int)percentOfAnimation);
543 qreal currentPosition = percentOfAnimation * (m_count - 1);
544 int startElem = qFloor(currentPosition);
545 int endElem = qCeil(currentPosition);
547 switch(m_type)
549 case Translate: {
550 startElem *= 3;
551 endElem *= 3;
552 qreal from1, from2, from3;
553 qreal to1, to2, to3;
554 from1 = m_args[startElem++];
555 from2 = m_args[startElem++];
556 from3 = m_args[startElem++];
557 to1 = m_args[endElem++];
558 to2 = m_args[endElem++];
559 to3 = m_args[endElem++];
561 qreal transXDiff = (to1-from1) * percentOfAnimation;
562 qreal transX = from1 + transXDiff;
563 qreal transYDiff = (to2-from2) * percentOfAnimation;
564 qreal transY = from2 + transYDiff;
565 m_transform = QTransform();
566 m_transform.translate(transX, transY);
567 break;
569 case Scale: {
570 startElem *= 3;
571 endElem *= 3;
572 qreal from1, from2, from3;
573 qreal to1, to2, to3;
574 from1 = m_args[startElem++];
575 from2 = m_args[startElem++];
576 from3 = m_args[startElem++];
577 to1 = m_args[endElem++];
578 to2 = m_args[endElem++];
579 to3 = m_args[endElem++];
581 qreal transXDiff = (to1-from1) * percentOfAnimation;
582 qreal transX = from1 + transXDiff;
583 qreal transYDiff = (to2-from2) * percentOfAnimation;
584 qreal transY = from2 + transYDiff;
585 if (transY == 0)
586 transY = transX;
587 m_transform = QTransform();
588 m_transform.scale(transX, transY);
589 break;
591 case Rotate: {
592 startElem *= 3;
593 endElem *= 3;
594 qreal from1, from2, from3;
595 qreal to1, to2, to3;
596 from1 = m_args[startElem++];
597 from2 = m_args[startElem++];
598 from3 = m_args[startElem++];
599 to1 = m_args[endElem++];
600 to2 = m_args[endElem++];
601 to3 = m_args[endElem++];
603 qreal rotationDiff = (to1 - from1) * percentOfAnimation;
604 //qreal rotation = from1 + rotationDiff;
606 qreal transXDiff = (to2-from2) * percentOfAnimation;
607 qreal transX = from2 + transXDiff;
608 qreal transYDiff = (to3-from3) * percentOfAnimation;
609 qreal transY = from3 + transYDiff;
610 m_transform = QTransform();
611 m_transform.translate(transX, transY);
612 m_transform.rotate(rotationDiff);
613 m_transform.translate(-transX, -transY);
614 break;
616 case SkewX: {
617 startElem *= 3;
618 endElem *= 3;
619 qreal from1, from2, from3;
620 qreal to1, to2, to3;
621 from1 = m_args[startElem++];
622 from2 = m_args[startElem++];
623 from3 = m_args[startElem++];
624 to1 = m_args[endElem++];
625 to2 = m_args[endElem++];
626 to3 = m_args[endElem++];
628 qreal transXDiff = (to1-from1) * percentOfAnimation;
629 qreal transX = from1 + transXDiff;
630 m_transform = QTransform();
631 m_transform.shear(tan(transX * deg2rad), 0);
632 break;
634 case SkewY: {
635 startElem *= 3;
636 endElem *= 3;
637 qreal from1, from2, from3;
638 qreal to1, to2, to3;
639 from1 = m_args[startElem++];
640 from2 = m_args[startElem++];
641 from3 = m_args[startElem++];
642 to1 = m_args[endElem++];
643 to2 = m_args[endElem++];
644 to3 = m_args[endElem++];
647 qreal transYDiff = (to1 - from1) * percentOfAnimation;
648 qreal transY = from1 + transYDiff;
649 m_transform = QTransform();
650 m_transform.shear(0, tan(transY * deg2rad));
651 break;
653 default:
654 break;
658 QSvgStyleProperty::Type QSvgAnimateTransform::type() const
660 return ANIMATE_TRANSFORM;
663 void QSvgAnimateTransform::setFreeze(bool freeze)
665 m_freeze = freeze;
668 void QSvgAnimateTransform::setRepeatCount(qreal repeatCount)
670 m_repeatCount = repeatCount;
673 QSvgAnimateColor::QSvgAnimateColor(int startMs, int endMs, int byMs)
674 : QSvgStyleProperty(),
675 m_from(startMs), m_to(endMs), m_by(byMs),
676 m_finished(false)
678 m_totalRunningTime = m_to - m_from;
681 void QSvgAnimateColor::setArgs(bool fill,
682 const QList<QColor> &colors)
684 m_fill = fill;
685 m_colors = colors;
688 void QSvgAnimateColor::setFreeze(bool freeze)
690 m_freeze = freeze;
693 void QSvgAnimateColor::setRepeatCount(qreal repeatCount)
695 m_repeatCount = repeatCount;
698 void QSvgAnimateColor::apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &)
700 qreal totalTimeElapsed = node->document()->currentElapsed();
701 if (totalTimeElapsed < m_from || m_finished)
702 return;
704 qreal animationFrame = (totalTimeElapsed - m_from) / m_to;
706 if (m_repeatCount >= 0 && m_repeatCount < animationFrame) {
707 m_finished = true;
708 animationFrame = m_repeatCount;
711 qreal percentOfAnimation = animationFrame;
712 if (percentOfAnimation > 1) {
713 percentOfAnimation -= ((int)percentOfAnimation);
716 qreal currentPosition = percentOfAnimation * (m_colors.count() - 1);
718 int startElem = qFloor(currentPosition);
719 int endElem = qCeil(currentPosition);
720 QColor start = m_colors[startElem];
721 QColor end = m_colors[endElem];
723 qreal percentOfColorMorph = currentPosition;
724 if (percentOfColorMorph > 1) {
725 percentOfColorMorph -= ((int)percentOfColorMorph);
728 // Interpolate between the two fixed colors start and end
729 qreal aDiff = (end.alpha() - start.alpha()) * percentOfColorMorph;
730 qreal rDiff = (end.red() - start.red()) * percentOfColorMorph;
731 qreal gDiff = (end.green() - start.green()) * percentOfColorMorph;
732 qreal bDiff = (end.blue() - start.blue()) * percentOfColorMorph;
734 int alpha = int(start.alpha() + aDiff);
735 int red = int(start.red() + rDiff);
736 int green = int(start.green() + gDiff);
737 int blue = int(start.blue() + bDiff);
739 QColor color(red, green, blue, alpha);
741 if (m_fill) {
742 QBrush b = p->brush();
743 m_oldBrush = b;
744 b.setColor(color);
745 p->setBrush(b);
746 } else {
747 QPen pen = p->pen();
748 m_oldPen = pen;
749 pen.setColor(color);
750 p->setPen(pen);
754 void QSvgAnimateColor::revert(QPainter *p, QSvgExtraStates &)
756 if (m_fill) {
757 p->setBrush(m_oldBrush);
758 } else {
759 p->setPen(m_oldPen);
763 QSvgStyleProperty::Type QSvgAnimateColor::type() const
765 return ANIMATE_COLOR;
768 QString QSvgFontStyle::textAnchor() const
770 return m_textAnchor;
773 void QSvgFontStyle::setTextAnchor(const QString &anchor)
775 m_textAnchor = anchor;
778 QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity)
779 : m_opacity(opacity)
784 void QSvgOpacityStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
786 m_oldOpacity = p->opacity();
787 p->setOpacity(m_opacity * m_oldOpacity);
790 void QSvgOpacityStyle::revert(QPainter *p, QSvgExtraStates &)
792 p->setOpacity(m_oldOpacity);
795 QSvgStyleProperty::Type QSvgOpacityStyle::type() const
797 return OPACITY;
800 void QSvgGradientStyle::setStopLink(const QString &link, QSvgTinyDocument *doc)
802 m_link = link;
803 m_doc = doc;
806 void QSvgGradientStyle::resolveStops()
808 if (!m_link.isEmpty() && m_doc) {
809 QSvgStyleProperty *prop = m_doc->scopeStyle(m_link);
810 if (prop) {
811 if (prop->type() == QSvgStyleProperty::GRADIENT) {
812 QSvgGradientStyle *st =
813 static_cast<QSvgGradientStyle*>(prop);
814 st->resolveStops();
815 m_gradient->setStops(st->qgradient()->stops());
816 m_gradientStopsSet = st->gradientStopsSet();
819 m_link = QString();
823 QT_END_NAMESPACE
825 #endif // QT_NO_SVG