1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtSvg module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include "qsvgstyle_p.h" |
43 | |
44 | #ifndef QT_NO_SVG |
45 | |
46 | #include "qsvgfont_p.h" |
47 | #include "qsvggraphics_p.h" |
48 | #include "qsvgnode_p.h" |
49 | #include "qsvgtinydocument_p.h" |
50 | |
51 | #include "qpainter.h" |
52 | #include "qpair.h" |
53 | #include "qcolor.h" |
54 | #include "qdebug.h" |
55 | #include "qmath.h" |
56 | #include "qnumeric.h" |
57 | |
58 | QT_BEGIN_NAMESPACE |
59 | |
60 | QSvgExtraStates::() |
61 | : fillOpacity(1.0) |
62 | , strokeOpacity(1.0) |
63 | , svgFont(0) |
64 | , textAnchor(Qt::AlignLeft) |
65 | , fontWeight(400) |
66 | , fillRule(Qt::WindingFill) |
67 | , strokeDashOffset(0) |
68 | , vectorEffect(false) |
69 | { |
70 | } |
71 | |
72 | QSvgStyleProperty::~QSvgStyleProperty() |
73 | { |
74 | } |
75 | |
76 | void QSvgFillStyleProperty::(QPainter *, const QSvgNode *, QSvgExtraStates &) |
77 | { |
78 | Q_ASSERT(!"This should not be called!" ); |
79 | } |
80 | |
81 | void QSvgFillStyleProperty::(QPainter *, QSvgExtraStates &) |
82 | { |
83 | Q_ASSERT(!"This should not be called!" ); |
84 | } |
85 | |
86 | |
87 | QSvgQualityStyle::QSvgQualityStyle(int color) |
88 | : m_colorRendering(color) |
89 | { |
90 | |
91 | } |
92 | void QSvgQualityStyle::(QPainter *, const QSvgNode *, QSvgExtraStates &) |
93 | { |
94 | |
95 | } |
96 | void QSvgQualityStyle::(QPainter *, QSvgExtraStates &) |
97 | { |
98 | |
99 | } |
100 | |
101 | QSvgFillStyle::QSvgFillStyle() |
102 | : m_style(0) |
103 | , m_fillRule(Qt::WindingFill) |
104 | , m_oldFillRule(Qt::WindingFill) |
105 | , m_fillOpacity(1.0) |
106 | , m_oldFillOpacity(0) |
107 | , m_gradientResolved(1) |
108 | , m_fillRuleSet(0) |
109 | , m_fillOpacitySet(0) |
110 | , m_fillSet(0) |
111 | { |
112 | } |
113 | |
114 | void QSvgFillStyle::setFillRule(Qt::FillRule f) |
115 | { |
116 | m_fillRuleSet = 1; |
117 | m_fillRule = f; |
118 | } |
119 | |
120 | void QSvgFillStyle::setFillOpacity(qreal opacity) |
121 | { |
122 | m_fillOpacitySet = 1; |
123 | m_fillOpacity = opacity; |
124 | } |
125 | |
126 | void QSvgFillStyle::setFillStyle(QSvgFillStyleProperty* style) |
127 | { |
128 | m_style = style; |
129 | m_fillSet = 1; |
130 | } |
131 | |
132 | void QSvgFillStyle::setBrush(QBrush brush) |
133 | { |
134 | m_fill = brush; |
135 | m_style = 0; |
136 | m_fillSet = 1; |
137 | } |
138 | |
139 | void QSvgFillStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &states) |
140 | { |
141 | m_oldFill = p->brush(); |
142 | m_oldFillRule = states.fillRule; |
143 | m_oldFillOpacity = states.fillOpacity; |
144 | |
145 | if (m_fillRuleSet) |
146 | states.fillRule = m_fillRule; |
147 | if (m_fillSet) { |
148 | if (m_style) |
149 | p->setBrush(m_style->brush(p, states)); |
150 | else |
151 | p->setBrush(m_fill); |
152 | } |
153 | if (m_fillOpacitySet) |
154 | states.fillOpacity = m_fillOpacity; |
155 | } |
156 | |
157 | void QSvgFillStyle::(QPainter *p, QSvgExtraStates &states) |
158 | { |
159 | if (m_fillOpacitySet) |
160 | states.fillOpacity = m_oldFillOpacity; |
161 | if (m_fillSet) |
162 | p->setBrush(m_oldFill); |
163 | if (m_fillRuleSet) |
164 | states.fillRule = m_oldFillRule; |
165 | } |
166 | |
167 | QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush) |
168 | : m_viewportFill(brush) |
169 | { |
170 | } |
171 | |
172 | void QSvgViewportFillStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
173 | { |
174 | m_oldFill = p->brush(); |
175 | p->setBrush(m_viewportFill); |
176 | } |
177 | |
178 | void QSvgViewportFillStyle::(QPainter *p, QSvgExtraStates &) |
179 | { |
180 | p->setBrush(m_oldFill); |
181 | } |
182 | |
183 | QSvgFontStyle::QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc) |
184 | : m_svgFont(font) |
185 | , m_doc(doc) |
186 | , m_familySet(0) |
187 | , m_sizeSet(0) |
188 | , m_styleSet(0) |
189 | , m_variantSet(0) |
190 | , m_weightSet(0) |
191 | , m_textAnchorSet(0) |
192 | { |
193 | } |
194 | |
195 | QSvgFontStyle::QSvgFontStyle() |
196 | : m_svgFont(0) |
197 | , m_doc(0) |
198 | , m_familySet(0) |
199 | , m_sizeSet(0) |
200 | , m_styleSet(0) |
201 | , m_variantSet(0) |
202 | , m_weightSet(0) |
203 | , m_textAnchorSet(0) |
204 | { |
205 | } |
206 | |
207 | int QSvgFontStyle::SVGToQtWeight(int weight) { |
208 | switch (weight) { |
209 | case 100: |
210 | case 200: |
211 | return QFont::Light; |
212 | case 300: |
213 | case 400: |
214 | return QFont::Normal; |
215 | case 500: |
216 | case 600: |
217 | return QFont::DemiBold; |
218 | case 700: |
219 | case 800: |
220 | return QFont::Bold; |
221 | case 900: |
222 | return QFont::Black; |
223 | } |
224 | return QFont::Normal; |
225 | } |
226 | |
227 | void QSvgFontStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &states) |
228 | { |
229 | m_oldQFont = p->font(); |
230 | m_oldSvgFont = states.svgFont; |
231 | m_oldTextAnchor = states.textAnchor; |
232 | m_oldWeight = states.fontWeight; |
233 | |
234 | if (m_textAnchorSet) |
235 | states.textAnchor = m_textAnchor; |
236 | |
237 | QFont font = m_oldQFont; |
238 | if (m_familySet) { |
239 | states.svgFont = m_svgFont; |
240 | font.setFamily(m_qfont.family()); |
241 | } |
242 | |
243 | if (m_sizeSet) |
244 | font.setPointSizeF(m_qfont.pointSizeF()); |
245 | |
246 | if (m_styleSet) |
247 | font.setStyle(m_qfont.style()); |
248 | |
249 | if (m_variantSet) |
250 | font.setCapitalization(m_qfont.capitalization()); |
251 | |
252 | if (m_weightSet) { |
253 | if (m_weight == BOLDER) { |
254 | states.fontWeight = qMin(states.fontWeight + 100, 900); |
255 | } else if (m_weight == LIGHTER) { |
256 | states.fontWeight = qMax(states.fontWeight - 100, 100); |
257 | } else { |
258 | states.fontWeight = m_weight; |
259 | } |
260 | font.setWeight(SVGToQtWeight(states.fontWeight)); |
261 | } |
262 | |
263 | p->setFont(font); |
264 | } |
265 | |
266 | void QSvgFontStyle::(QPainter *p, QSvgExtraStates &states) |
267 | { |
268 | p->setFont(m_oldQFont); |
269 | states.svgFont = m_oldSvgFont; |
270 | states.textAnchor = m_oldTextAnchor; |
271 | states.fontWeight = m_oldWeight; |
272 | } |
273 | |
274 | QSvgStrokeStyle::QSvgStrokeStyle() |
275 | : m_strokeOpacity(1.0) |
276 | , m_oldStrokeOpacity(0.0) |
277 | , m_strokeDashOffset(0) |
278 | , m_oldStrokeDashOffset(0) |
279 | , m_style(0) |
280 | , m_gradientResolved(1) |
281 | , m_vectorEffect(0) |
282 | , m_oldVectorEffect(0) |
283 | , m_strokeSet(0) |
284 | , m_strokeDashArraySet(0) |
285 | , m_strokeDashOffsetSet(0) |
286 | , m_strokeLineCapSet(0) |
287 | , m_strokeLineJoinSet(0) |
288 | , m_strokeMiterLimitSet(0) |
289 | , m_strokeOpacitySet(0) |
290 | , m_strokeWidthSet(0) |
291 | , m_vectorEffectSet(0) |
292 | { |
293 | } |
294 | |
295 | void QSvgStrokeStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &states) |
296 | { |
297 | m_oldStroke = p->pen(); |
298 | m_oldStrokeOpacity = states.strokeOpacity; |
299 | m_oldStrokeDashOffset = states.strokeDashOffset; |
300 | m_oldVectorEffect = states.vectorEffect; |
301 | |
302 | QPen pen = p->pen(); |
303 | |
304 | qreal oldWidth = pen.widthF(); |
305 | qreal width = m_stroke.widthF(); |
306 | if (oldWidth == 0) |
307 | oldWidth = 1; |
308 | if (width == 0) |
309 | width = 1; |
310 | qreal scale = oldWidth / width; |
311 | |
312 | if (m_strokeOpacitySet) |
313 | states.strokeOpacity = m_strokeOpacity; |
314 | |
315 | if (m_vectorEffectSet) |
316 | states.vectorEffect = m_vectorEffect; |
317 | |
318 | if (m_strokeSet) { |
319 | if (m_style) |
320 | pen.setBrush(m_style->brush(p, states)); |
321 | else |
322 | pen.setBrush(m_stroke.brush()); |
323 | } |
324 | |
325 | if (m_strokeWidthSet) |
326 | pen.setWidthF(m_stroke.widthF()); |
327 | |
328 | bool setDashOffsetNeeded = false; |
329 | |
330 | if (m_strokeDashOffsetSet) { |
331 | states.strokeDashOffset = m_strokeDashOffset; |
332 | setDashOffsetNeeded = true; |
333 | } |
334 | |
335 | if (m_strokeDashArraySet) { |
336 | if (m_stroke.style() == Qt::SolidLine) { |
337 | pen.setStyle(Qt::SolidLine); |
338 | } else if (m_strokeWidthSet || oldWidth == 1) { |
339 | // If both width and dash array was set, the dash array is already scaled correctly. |
340 | pen.setDashPattern(m_stroke.dashPattern()); |
341 | setDashOffsetNeeded = true; |
342 | } else { |
343 | // If dash array was set, but not the width, the dash array has to be scaled with respect to the old width. |
344 | QVector<qreal> dashes = m_stroke.dashPattern(); |
345 | for (int i = 0; i < dashes.size(); ++i) |
346 | dashes[i] /= oldWidth; |
347 | pen.setDashPattern(dashes); |
348 | setDashOffsetNeeded = true; |
349 | } |
350 | } else if (m_strokeWidthSet && pen.style() != Qt::SolidLine && scale != 1) { |
351 | // If the width was set, but not the dash array, the old dash array must be scaled with respect to the new width. |
352 | QVector<qreal> dashes = pen.dashPattern(); |
353 | for (int i = 0; i < dashes.size(); ++i) |
354 | dashes[i] *= scale; |
355 | pen.setDashPattern(dashes); |
356 | setDashOffsetNeeded = true; |
357 | } |
358 | |
359 | if (m_strokeLineCapSet) |
360 | pen.setCapStyle(m_stroke.capStyle()); |
361 | if (m_strokeLineJoinSet) |
362 | pen.setJoinStyle(m_stroke.joinStyle()); |
363 | if (m_strokeMiterLimitSet) |
364 | pen.setMiterLimit(m_stroke.miterLimit()); |
365 | |
366 | // You can have dash offset on solid strokes in SVG files, but not in Qt. |
367 | // QPen::setDashOffset() will set the pen style to Qt::CustomDashLine, |
368 | // so don't call the method if the pen is solid. |
369 | if (setDashOffsetNeeded && pen.style() != Qt::SolidLine) { |
370 | qreal currentWidth = pen.widthF(); |
371 | if (currentWidth == 0) |
372 | currentWidth = 1; |
373 | pen.setDashOffset(states.strokeDashOffset / currentWidth); |
374 | } |
375 | |
376 | pen.setCosmetic(states.vectorEffect); |
377 | |
378 | p->setPen(pen); |
379 | } |
380 | |
381 | void QSvgStrokeStyle::(QPainter *p, QSvgExtraStates &states) |
382 | { |
383 | p->setPen(m_oldStroke); |
384 | states.strokeOpacity = m_oldStrokeOpacity; |
385 | states.strokeDashOffset = m_oldStrokeDashOffset; |
386 | states.vectorEffect = m_oldVectorEffect; |
387 | } |
388 | |
389 | void QSvgStrokeStyle::setDashArray(const QVector<qreal> &dashes) |
390 | { |
391 | if (m_strokeWidthSet) { |
392 | QVector<qreal> d = dashes; |
393 | qreal w = m_stroke.widthF(); |
394 | if (w != 0 && w != 1) { |
395 | for (int i = 0; i < d.size(); ++i) |
396 | d[i] /= w; |
397 | } |
398 | m_stroke.setDashPattern(d); |
399 | } else { |
400 | m_stroke.setDashPattern(dashes); |
401 | } |
402 | m_strokeDashArraySet = 1; |
403 | } |
404 | |
405 | QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color) |
406 | : m_solidColor(color) |
407 | { |
408 | } |
409 | |
410 | QSvgGradientStyle::QSvgGradientStyle(QGradient *grad) |
411 | : m_gradient(grad), m_gradientStopsSet(false) |
412 | { |
413 | } |
414 | |
415 | QBrush QSvgGradientStyle::(QPainter *, QSvgExtraStates &) |
416 | { |
417 | if (!m_link.isEmpty()) { |
418 | resolveStops(); |
419 | } |
420 | |
421 | // If the gradient is marked as empty, insert transparent black |
422 | if (!m_gradientStopsSet) { |
423 | m_gradient->setStops(QGradientStops() << QGradientStop(0.0, QColor(0, 0, 0, 0))); |
424 | m_gradientStopsSet = true; |
425 | } |
426 | |
427 | QBrush b(*m_gradient); |
428 | |
429 | if (!m_matrix.isIdentity()) |
430 | b.setMatrix(m_matrix); |
431 | |
432 | return b; |
433 | } |
434 | |
435 | |
436 | void QSvgGradientStyle::setMatrix(const QMatrix &mat) |
437 | { |
438 | m_matrix = mat; |
439 | } |
440 | |
441 | QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans) |
442 | : m_transform(trans) |
443 | { |
444 | } |
445 | |
446 | void QSvgTransformStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
447 | { |
448 | m_oldWorldTransform = p->worldTransform(); |
449 | p->setWorldTransform(m_transform, true); |
450 | } |
451 | |
452 | void QSvgTransformStyle::(QPainter *p, QSvgExtraStates &) |
453 | { |
454 | p->setWorldTransform(m_oldWorldTransform, false /* don't combine */); |
455 | } |
456 | |
457 | QSvgStyleProperty::Type QSvgQualityStyle::type() const |
458 | { |
459 | return QUALITY; |
460 | } |
461 | |
462 | QSvgStyleProperty::Type QSvgFillStyle::type() const |
463 | { |
464 | return FILL; |
465 | } |
466 | |
467 | QSvgStyleProperty::Type QSvgViewportFillStyle::type() const |
468 | { |
469 | return VIEWPORT_FILL; |
470 | } |
471 | |
472 | QSvgStyleProperty::Type QSvgFontStyle::type() const |
473 | { |
474 | return FONT; |
475 | } |
476 | |
477 | QSvgStyleProperty::Type QSvgStrokeStyle::type() const |
478 | { |
479 | return STROKE; |
480 | } |
481 | |
482 | QSvgStyleProperty::Type QSvgSolidColorStyle::type() const |
483 | { |
484 | return SOLID_COLOR; |
485 | } |
486 | |
487 | QSvgStyleProperty::Type QSvgGradientStyle::type() const |
488 | { |
489 | return GRADIENT; |
490 | } |
491 | |
492 | QSvgStyleProperty::Type QSvgTransformStyle::type() const |
493 | { |
494 | return TRANSFORM; |
495 | } |
496 | |
497 | |
498 | QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode) |
499 | : m_mode(mode) |
500 | { |
501 | |
502 | } |
503 | |
504 | void QSvgCompOpStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
505 | { |
506 | m_oldMode = p->compositionMode(); |
507 | p->setCompositionMode(m_mode); |
508 | } |
509 | |
510 | void QSvgCompOpStyle::(QPainter *p, QSvgExtraStates &) |
511 | { |
512 | p->setCompositionMode(m_oldMode); |
513 | } |
514 | |
515 | QSvgStyleProperty::Type QSvgCompOpStyle::type() const |
516 | { |
517 | return COMP_OP; |
518 | } |
519 | |
520 | QSvgStyle::~QSvgStyle() |
521 | { |
522 | } |
523 | |
524 | void QSvgStyle::(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) |
525 | { |
526 | if (quality) { |
527 | quality->apply(p, node, states); |
528 | } |
529 | |
530 | if (fill) { |
531 | fill->apply(p, node, states); |
532 | } |
533 | |
534 | if (viewportFill) { |
535 | viewportFill->apply(p, node, states); |
536 | } |
537 | |
538 | if (font) { |
539 | font->apply(p, node, states); |
540 | } |
541 | |
542 | if (stroke) { |
543 | stroke->apply(p, node, states); |
544 | } |
545 | |
546 | if (transform) { |
547 | transform->apply(p, node, states); |
548 | } |
549 | |
550 | if (animateColor) { |
551 | animateColor->apply(p, node, states); |
552 | } |
553 | |
554 | //animated transforms have to be applied |
555 | //_after_ the original object transformations |
556 | if (!animateTransforms.isEmpty()) { |
557 | qreal totalTimeElapsed = node->document()->currentElapsed(); |
558 | // Find the last animateTransform with additive="replace", since this will override all |
559 | // previous animateTransforms. |
560 | QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constEnd(); |
561 | do { |
562 | --itr; |
563 | if ((*itr)->animActive(totalTimeElapsed) |
564 | && (*itr)->additiveType() == QSvgAnimateTransform::Replace) { |
565 | // An animateTransform with additive="replace" will replace the transform attribute. |
566 | if (transform) |
567 | transform->revert(p, states); |
568 | break; |
569 | } |
570 | } while (itr != animateTransforms.constBegin()); |
571 | |
572 | // Apply the animateTransforms after and including the last one with additive="replace". |
573 | for (; itr != animateTransforms.constEnd(); ++itr) { |
574 | if ((*itr)->animActive(totalTimeElapsed)) |
575 | (*itr)->apply(p, node, states); |
576 | } |
577 | } |
578 | |
579 | if (opacity) { |
580 | opacity->apply(p, node, states); |
581 | } |
582 | |
583 | if (compop) { |
584 | compop->apply(p, node, states); |
585 | } |
586 | } |
587 | |
588 | void QSvgStyle::(QPainter *p, QSvgExtraStates &states) |
589 | { |
590 | if (quality) { |
591 | quality->revert(p, states); |
592 | } |
593 | |
594 | if (fill) { |
595 | fill->revert(p, states); |
596 | } |
597 | |
598 | if (viewportFill) { |
599 | viewportFill->revert(p, states); |
600 | } |
601 | |
602 | if (font) { |
603 | font->revert(p, states); |
604 | } |
605 | |
606 | if (stroke) { |
607 | stroke->revert(p, states); |
608 | } |
609 | |
610 | //animated transforms need to be reverted _before_ |
611 | //the native transforms |
612 | if (!animateTransforms.isEmpty()) { |
613 | QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constBegin(); |
614 | for (; itr != animateTransforms.constEnd(); ++itr) { |
615 | if ((*itr)->transformApplied()) { |
616 | (*itr)->revert(p, states); |
617 | break; |
618 | } |
619 | } |
620 | for (; itr != animateTransforms.constEnd(); ++itr) |
621 | (*itr)->clearTransformApplied(); |
622 | } |
623 | |
624 | if (transform) { |
625 | transform->revert(p, states); |
626 | } |
627 | |
628 | if (animateColor) { |
629 | animateColor->revert(p, states); |
630 | } |
631 | |
632 | if (opacity) { |
633 | opacity->revert(p, states); |
634 | } |
635 | |
636 | if (compop) { |
637 | compop->revert(p, states); |
638 | } |
639 | } |
640 | |
641 | QSvgAnimateTransform::QSvgAnimateTransform(int startMs, int endMs, int byMs ) |
642 | : QSvgStyleProperty(), |
643 | m_from(startMs), m_to(endMs), m_by(byMs), |
644 | m_type(Empty), m_additive(Replace), m_count(0), m_finished(false), m_transformApplied(false) |
645 | { |
646 | m_totalRunningTime = m_to - m_from; |
647 | } |
648 | |
649 | void QSvgAnimateTransform::setArgs(TransformType type, Additive additive, const QVector<qreal> &args) |
650 | { |
651 | m_type = type; |
652 | m_args = args; |
653 | m_additive = additive; |
654 | Q_ASSERT(!(args.count()%3)); |
655 | m_count = args.count() / 3; |
656 | } |
657 | |
658 | void QSvgAnimateTransform::(QPainter *p, const QSvgNode *node, QSvgExtraStates &) |
659 | { |
660 | m_oldWorldTransform = p->worldTransform(); |
661 | resolveMatrix(node); |
662 | p->setWorldTransform(m_transform, true); |
663 | m_transformApplied = true; |
664 | } |
665 | |
666 | void QSvgAnimateTransform::(QPainter *p, QSvgExtraStates &) |
667 | { |
668 | p->setWorldTransform(m_oldWorldTransform, false /* don't combine */); |
669 | m_transformApplied = false; |
670 | } |
671 | |
672 | void QSvgAnimateTransform::resolveMatrix(const QSvgNode *node) |
673 | { |
674 | static const qreal deg2rad = qreal(0.017453292519943295769); |
675 | qreal totalTimeElapsed = node->document()->currentElapsed(); |
676 | if (totalTimeElapsed < m_from || m_finished) |
677 | return; |
678 | |
679 | qreal animationFrame = 0; |
680 | if (m_totalRunningTime != 0) { |
681 | animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime; |
682 | |
683 | if (m_repeatCount >= 0 && m_repeatCount < animationFrame) { |
684 | m_finished = true; |
685 | animationFrame = m_repeatCount; |
686 | } |
687 | } |
688 | |
689 | qreal percentOfAnimation = animationFrame; |
690 | if (percentOfAnimation > 1) { |
691 | percentOfAnimation -= ((int)percentOfAnimation); |
692 | } |
693 | |
694 | qreal currentPosition = percentOfAnimation * (m_count - 1); |
695 | int startElem = qFloor(currentPosition); |
696 | int endElem = qCeil(currentPosition); |
697 | |
698 | switch(m_type) |
699 | { |
700 | case Translate: { |
701 | startElem *= 3; |
702 | endElem *= 3; |
703 | qreal from1, from2; |
704 | qreal to1, to2; |
705 | from1 = m_args[startElem++]; |
706 | from2 = m_args[startElem++]; |
707 | to1 = m_args[endElem++]; |
708 | to2 = m_args[endElem++]; |
709 | |
710 | qreal transXDiff = (to1-from1) * percentOfAnimation; |
711 | qreal transX = from1 + transXDiff; |
712 | qreal transYDiff = (to2-from2) * percentOfAnimation; |
713 | qreal transY = from2 + transYDiff; |
714 | m_transform = QTransform(); |
715 | m_transform.translate(transX, transY); |
716 | break; |
717 | } |
718 | case Scale: { |
719 | startElem *= 3; |
720 | endElem *= 3; |
721 | qreal from1, from2; |
722 | qreal to1, to2; |
723 | from1 = m_args[startElem++]; |
724 | from2 = m_args[startElem++]; |
725 | to1 = m_args[endElem++]; |
726 | to2 = m_args[endElem++]; |
727 | |
728 | qreal transXDiff = (to1-from1) * percentOfAnimation; |
729 | qreal transX = from1 + transXDiff; |
730 | qreal transYDiff = (to2-from2) * percentOfAnimation; |
731 | qreal transY = from2 + transYDiff; |
732 | if (transY == 0) |
733 | transY = transX; |
734 | m_transform = QTransform(); |
735 | m_transform.scale(transX, transY); |
736 | break; |
737 | } |
738 | case Rotate: { |
739 | startElem *= 3; |
740 | endElem *= 3; |
741 | qreal from1, from2, from3; |
742 | qreal to1, to2, to3; |
743 | from1 = m_args[startElem++]; |
744 | from2 = m_args[startElem++]; |
745 | from3 = m_args[startElem++]; |
746 | to1 = m_args[endElem++]; |
747 | to2 = m_args[endElem++]; |
748 | to3 = m_args[endElem++]; |
749 | |
750 | qreal rotationDiff = (to1 - from1) * percentOfAnimation; |
751 | //qreal rotation = from1 + rotationDiff; |
752 | |
753 | qreal transXDiff = (to2-from2) * percentOfAnimation; |
754 | qreal transX = from2 + transXDiff; |
755 | qreal transYDiff = (to3-from3) * percentOfAnimation; |
756 | qreal transY = from3 + transYDiff; |
757 | m_transform = QTransform(); |
758 | m_transform.translate(transX, transY); |
759 | m_transform.rotate(rotationDiff); |
760 | m_transform.translate(-transX, -transY); |
761 | break; |
762 | } |
763 | case SkewX: { |
764 | startElem *= 3; |
765 | endElem *= 3; |
766 | qreal from1; |
767 | qreal to1; |
768 | from1 = m_args[startElem++]; |
769 | to1 = m_args[endElem++]; |
770 | |
771 | qreal transXDiff = (to1-from1) * percentOfAnimation; |
772 | qreal transX = from1 + transXDiff; |
773 | m_transform = QTransform(); |
774 | m_transform.shear(qTan(transX * deg2rad), 0); |
775 | break; |
776 | } |
777 | case SkewY: { |
778 | startElem *= 3; |
779 | endElem *= 3; |
780 | qreal from1; |
781 | qreal to1; |
782 | from1 = m_args[startElem++]; |
783 | to1 = m_args[endElem++]; |
784 | |
785 | |
786 | qreal transYDiff = (to1 - from1) * percentOfAnimation; |
787 | qreal transY = from1 + transYDiff; |
788 | m_transform = QTransform(); |
789 | m_transform.shear(0, qTan(transY * deg2rad)); |
790 | break; |
791 | } |
792 | default: |
793 | break; |
794 | } |
795 | } |
796 | |
797 | QSvgStyleProperty::Type QSvgAnimateTransform::type() const |
798 | { |
799 | return ANIMATE_TRANSFORM; |
800 | } |
801 | |
802 | void QSvgAnimateTransform::setFreeze(bool freeze) |
803 | { |
804 | m_freeze = freeze; |
805 | } |
806 | |
807 | void QSvgAnimateTransform::setRepeatCount(qreal repeatCount) |
808 | { |
809 | m_repeatCount = repeatCount; |
810 | } |
811 | |
812 | QSvgAnimateColor::QSvgAnimateColor(int startMs, int endMs, int byMs) |
813 | : QSvgStyleProperty(), |
814 | m_from(startMs), m_to(endMs), m_by(byMs), |
815 | m_finished(false) |
816 | { |
817 | m_totalRunningTime = m_to - m_from; |
818 | } |
819 | |
820 | void QSvgAnimateColor::setArgs(bool fill, |
821 | const QList<QColor> &colors) |
822 | { |
823 | m_fill = fill; |
824 | m_colors = colors; |
825 | } |
826 | |
827 | void QSvgAnimateColor::setFreeze(bool freeze) |
828 | { |
829 | m_freeze = freeze; |
830 | } |
831 | |
832 | void QSvgAnimateColor::setRepeatCount(qreal repeatCount) |
833 | { |
834 | m_repeatCount = repeatCount; |
835 | } |
836 | |
837 | void QSvgAnimateColor::(QPainter *p, const QSvgNode *node, QSvgExtraStates &) |
838 | { |
839 | qreal totalTimeElapsed = node->document()->currentElapsed(); |
840 | if (totalTimeElapsed < m_from || m_finished) |
841 | return; |
842 | |
843 | qreal animationFrame = 0; |
844 | if (m_totalRunningTime != 0) |
845 | animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime; |
846 | |
847 | if (m_repeatCount >= 0 && m_repeatCount < animationFrame) { |
848 | m_finished = true; |
849 | animationFrame = m_repeatCount; |
850 | } |
851 | |
852 | qreal percentOfAnimation = animationFrame; |
853 | if (percentOfAnimation > 1) { |
854 | percentOfAnimation -= ((int)percentOfAnimation); |
855 | } |
856 | |
857 | qreal currentPosition = percentOfAnimation * (m_colors.count() - 1); |
858 | |
859 | int startElem = qFloor(currentPosition); |
860 | int endElem = qCeil(currentPosition); |
861 | QColor start = m_colors[startElem]; |
862 | QColor end = m_colors[endElem]; |
863 | |
864 | qreal percentOfColorMorph = currentPosition; |
865 | if (percentOfColorMorph > 1) { |
866 | percentOfColorMorph -= ((int)percentOfColorMorph); |
867 | } |
868 | |
869 | // Interpolate between the two fixed colors start and end |
870 | qreal aDiff = (end.alpha() - start.alpha()) * percentOfColorMorph; |
871 | qreal rDiff = (end.red() - start.red()) * percentOfColorMorph; |
872 | qreal gDiff = (end.green() - start.green()) * percentOfColorMorph; |
873 | qreal bDiff = (end.blue() - start.blue()) * percentOfColorMorph; |
874 | |
875 | int alpha = int(start.alpha() + aDiff); |
876 | int red = int(start.red() + rDiff); |
877 | int green = int(start.green() + gDiff); |
878 | int blue = int(start.blue() + bDiff); |
879 | |
880 | QColor color(red, green, blue, alpha); |
881 | |
882 | if (m_fill) { |
883 | QBrush b = p->brush(); |
884 | m_oldBrush = b; |
885 | b.setColor(color); |
886 | p->setBrush(b); |
887 | } else { |
888 | QPen pen = p->pen(); |
889 | m_oldPen = pen; |
890 | pen.setColor(color); |
891 | p->setPen(pen); |
892 | } |
893 | } |
894 | |
895 | void QSvgAnimateColor::(QPainter *p, QSvgExtraStates &) |
896 | { |
897 | if (m_fill) { |
898 | p->setBrush(m_oldBrush); |
899 | } else { |
900 | p->setPen(m_oldPen); |
901 | } |
902 | } |
903 | |
904 | QSvgStyleProperty::Type QSvgAnimateColor::type() const |
905 | { |
906 | return ANIMATE_COLOR; |
907 | } |
908 | |
909 | QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity) |
910 | : m_opacity(opacity), m_oldOpacity(0) |
911 | { |
912 | |
913 | } |
914 | |
915 | void QSvgOpacityStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
916 | { |
917 | m_oldOpacity = p->opacity(); |
918 | p->setOpacity(m_opacity * m_oldOpacity); |
919 | } |
920 | |
921 | void QSvgOpacityStyle::(QPainter *p, QSvgExtraStates &) |
922 | { |
923 | p->setOpacity(m_oldOpacity); |
924 | } |
925 | |
926 | QSvgStyleProperty::Type QSvgOpacityStyle::type() const |
927 | { |
928 | return OPACITY; |
929 | } |
930 | |
931 | void QSvgGradientStyle::setStopLink(const QString &link, QSvgTinyDocument *doc) |
932 | { |
933 | m_link = link; |
934 | m_doc = doc; |
935 | } |
936 | |
937 | void QSvgGradientStyle::resolveStops() |
938 | { |
939 | if (!m_link.isEmpty() && m_doc) { |
940 | QSvgStyleProperty *prop = m_doc->styleProperty(m_link); |
941 | if (prop) { |
942 | if (prop->type() == QSvgStyleProperty::GRADIENT) { |
943 | QSvgGradientStyle *st = |
944 | static_cast<QSvgGradientStyle*>(prop); |
945 | st->resolveStops(); |
946 | m_gradient->setStops(st->qgradient()->stops()); |
947 | m_gradientStopsSet = st->gradientStopsSet(); |
948 | } |
949 | } else { |
950 | qWarning("Could not resolve property : %s" , qPrintable(m_link)); |
951 | } |
952 | m_link = QString(); |
953 | } |
954 | } |
955 | |
956 | QT_END_NAMESPACE |
957 | |
958 | #endif // QT_NO_SVG |
959 | |