|
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 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
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. |
|
23 ** |
|
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. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
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::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::apply(QPainter *, const QRectF &, QSvgNode *, QSvgExtraStates &) |
|
77 { |
|
78 Q_ASSERT(!"This should not be called!"); |
|
79 } |
|
80 |
|
81 void QSvgFillStyleProperty::revert(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::apply(QPainter *, const QRectF &, QSvgNode *, QSvgExtraStates &) |
|
93 { |
|
94 |
|
95 } |
|
96 void QSvgQualityStyle::revert(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::apply(QPainter *p, const QRectF &, 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::revert(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::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &) |
|
173 { |
|
174 m_oldFill = p->brush(); |
|
175 p->setBrush(m_viewportFill); |
|
176 } |
|
177 |
|
178 void QSvgViewportFillStyle::revert(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::apply(QPainter *p, const QRectF &, 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.setPointSize(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::revert(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::apply(QPainter *p, const QRectF &, 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 if (setDashOffsetNeeded) { |
|
367 qreal currentWidth = pen.widthF(); |
|
368 if (currentWidth == 0) |
|
369 currentWidth = 1; |
|
370 pen.setDashOffset(states.strokeDashOffset / currentWidth); |
|
371 } |
|
372 |
|
373 pen.setCosmetic(states.vectorEffect); |
|
374 |
|
375 p->setPen(pen); |
|
376 } |
|
377 |
|
378 void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &states) |
|
379 { |
|
380 p->setPen(m_oldStroke); |
|
381 states.strokeOpacity = m_oldStrokeOpacity; |
|
382 states.strokeDashOffset = m_oldStrokeDashOffset; |
|
383 states.vectorEffect = m_oldVectorEffect; |
|
384 } |
|
385 |
|
386 void QSvgStrokeStyle::setDashArray(const QVector<qreal> &dashes) |
|
387 { |
|
388 if (m_strokeWidthSet) { |
|
389 QVector<qreal> d = dashes; |
|
390 qreal w = m_stroke.widthF(); |
|
391 if (w != 0 && w != 1) { |
|
392 for (int i = 0; i < d.size(); ++i) |
|
393 d[i] /= w; |
|
394 } |
|
395 m_stroke.setDashPattern(d); |
|
396 } else { |
|
397 m_stroke.setDashPattern(dashes); |
|
398 } |
|
399 m_strokeDashArraySet = 1; |
|
400 } |
|
401 |
|
402 QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color) |
|
403 : m_solidColor(color) |
|
404 { |
|
405 } |
|
406 |
|
407 QSvgGradientStyle::QSvgGradientStyle(QGradient *grad) |
|
408 : m_gradient(grad), m_gradientStopsSet(false) |
|
409 { |
|
410 } |
|
411 |
|
412 QBrush QSvgGradientStyle::brush(QPainter *, QSvgExtraStates &) |
|
413 { |
|
414 if (!m_link.isEmpty()) { |
|
415 resolveStops(); |
|
416 } |
|
417 |
|
418 // If the gradient is marked as empty, insert transparent black |
|
419 if (!m_gradientStopsSet) { |
|
420 m_gradient->setStops(QGradientStops() << QGradientStop(0.0, QColor(0, 0, 0, 0))); |
|
421 m_gradientStopsSet = true; |
|
422 } |
|
423 |
|
424 QBrush b(*m_gradient); |
|
425 |
|
426 if (!m_matrix.isIdentity()) |
|
427 b.setMatrix(m_matrix); |
|
428 |
|
429 return b; |
|
430 } |
|
431 |
|
432 |
|
433 void QSvgGradientStyle::setMatrix(const QMatrix &mat) |
|
434 { |
|
435 m_matrix = mat; |
|
436 } |
|
437 |
|
438 QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans) |
|
439 : m_transform(trans) |
|
440 { |
|
441 } |
|
442 |
|
443 void QSvgTransformStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &) |
|
444 { |
|
445 m_oldWorldTransform = p->worldTransform(); |
|
446 p->setWorldTransform(m_transform, true); |
|
447 } |
|
448 |
|
449 void QSvgTransformStyle::revert(QPainter *p, QSvgExtraStates &) |
|
450 { |
|
451 p->setWorldTransform(m_oldWorldTransform, false /* don't combine */); |
|
452 } |
|
453 |
|
454 QSvgStyleProperty::Type QSvgQualityStyle::type() const |
|
455 { |
|
456 return QUALITY; |
|
457 } |
|
458 |
|
459 QSvgStyleProperty::Type QSvgFillStyle::type() const |
|
460 { |
|
461 return FILL; |
|
462 } |
|
463 |
|
464 QSvgStyleProperty::Type QSvgViewportFillStyle::type() const |
|
465 { |
|
466 return VIEWPORT_FILL; |
|
467 } |
|
468 |
|
469 QSvgStyleProperty::Type QSvgFontStyle::type() const |
|
470 { |
|
471 return FONT; |
|
472 } |
|
473 |
|
474 QSvgStyleProperty::Type QSvgStrokeStyle::type() const |
|
475 { |
|
476 return STROKE; |
|
477 } |
|
478 |
|
479 QSvgStyleProperty::Type QSvgSolidColorStyle::type() const |
|
480 { |
|
481 return SOLID_COLOR; |
|
482 } |
|
483 |
|
484 QSvgStyleProperty::Type QSvgGradientStyle::type() const |
|
485 { |
|
486 return GRADIENT; |
|
487 } |
|
488 |
|
489 QSvgStyleProperty::Type QSvgTransformStyle::type() const |
|
490 { |
|
491 return TRANSFORM; |
|
492 } |
|
493 |
|
494 |
|
495 QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode) |
|
496 : m_mode(mode) |
|
497 { |
|
498 |
|
499 } |
|
500 |
|
501 void QSvgCompOpStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &) |
|
502 { |
|
503 m_oldMode = p->compositionMode(); |
|
504 p->setCompositionMode(m_mode); |
|
505 } |
|
506 |
|
507 void QSvgCompOpStyle::revert(QPainter *p, QSvgExtraStates &) |
|
508 { |
|
509 p->setCompositionMode(m_oldMode); |
|
510 } |
|
511 |
|
512 QSvgStyleProperty::Type QSvgCompOpStyle::type() const |
|
513 { |
|
514 return COMP_OP; |
|
515 } |
|
516 |
|
517 QSvgStyle::~QSvgStyle() |
|
518 { |
|
519 } |
|
520 |
|
521 void QSvgStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtraStates &states) |
|
522 { |
|
523 if (quality) { |
|
524 quality->apply(p, rect, node, states); |
|
525 } |
|
526 |
|
527 if (fill) { |
|
528 fill->apply(p, rect, node, states); |
|
529 } |
|
530 |
|
531 if (viewportFill) { |
|
532 viewportFill->apply(p, rect, node, states); |
|
533 } |
|
534 |
|
535 if (font) { |
|
536 font->apply(p, rect, node, states); |
|
537 } |
|
538 |
|
539 if (stroke) { |
|
540 stroke->apply(p, rect, node, states); |
|
541 } |
|
542 |
|
543 if (transform) { |
|
544 transform->apply(p, rect, node, states); |
|
545 } |
|
546 |
|
547 if (animateColor) { |
|
548 animateColor->apply(p, rect, node, states); |
|
549 } |
|
550 |
|
551 //animated transforms have to be applied |
|
552 //_after_ the original object transformations |
|
553 if (!animateTransforms.isEmpty()) { |
|
554 qreal totalTimeElapsed = node->document()->currentElapsed(); |
|
555 // Find the last animateTransform with additive="replace", since this will override all |
|
556 // previous animateTransforms. |
|
557 QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constEnd(); |
|
558 do { |
|
559 --itr; |
|
560 if ((*itr)->animActive(totalTimeElapsed) |
|
561 && (*itr)->additiveType() == QSvgAnimateTransform::Replace) { |
|
562 // An animateTransform with additive="replace" will replace the transform attribute. |
|
563 if (transform) |
|
564 transform->revert(p, states); |
|
565 break; |
|
566 } |
|
567 } while (itr != animateTransforms.constBegin()); |
|
568 |
|
569 // Apply the animateTransforms after and including the last one with additive="replace". |
|
570 for (; itr != animateTransforms.constEnd(); ++itr) { |
|
571 if ((*itr)->animActive(totalTimeElapsed)) |
|
572 (*itr)->apply(p, rect, node, states); |
|
573 } |
|
574 } |
|
575 |
|
576 if (opacity) { |
|
577 opacity->apply(p, rect, node, states); |
|
578 } |
|
579 |
|
580 if (compop) { |
|
581 compop->apply(p, rect, node, states); |
|
582 } |
|
583 } |
|
584 |
|
585 void QSvgStyle::revert(QPainter *p, QSvgExtraStates &states) |
|
586 { |
|
587 if (quality) { |
|
588 quality->revert(p, states); |
|
589 } |
|
590 |
|
591 if (fill) { |
|
592 fill->revert(p, states); |
|
593 } |
|
594 |
|
595 if (viewportFill) { |
|
596 viewportFill->revert(p, states); |
|
597 } |
|
598 |
|
599 if (font) { |
|
600 font->revert(p, states); |
|
601 } |
|
602 |
|
603 if (stroke) { |
|
604 stroke->revert(p, states); |
|
605 } |
|
606 |
|
607 //animated transforms need to be reverted _before_ |
|
608 //the native transforms |
|
609 if (!animateTransforms.isEmpty()) { |
|
610 QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constBegin(); |
|
611 for (; itr != animateTransforms.constEnd(); ++itr) { |
|
612 if ((*itr)->transformApplied()) { |
|
613 (*itr)->revert(p, states); |
|
614 break; |
|
615 } |
|
616 } |
|
617 for (; itr != animateTransforms.constEnd(); ++itr) |
|
618 (*itr)->clearTransformApplied(); |
|
619 } |
|
620 |
|
621 if (transform) { |
|
622 transform->revert(p, states); |
|
623 } |
|
624 |
|
625 if (animateColor) { |
|
626 animateColor->revert(p, states); |
|
627 } |
|
628 |
|
629 if (opacity) { |
|
630 opacity->revert(p, states); |
|
631 } |
|
632 |
|
633 if (compop) { |
|
634 compop->revert(p, states); |
|
635 } |
|
636 } |
|
637 |
|
638 QSvgAnimateTransform::QSvgAnimateTransform(int startMs, int endMs, int byMs ) |
|
639 : QSvgStyleProperty(), |
|
640 m_from(startMs), m_to(endMs), m_by(byMs), |
|
641 m_type(Empty), m_additive(Replace), m_count(0), m_finished(false), m_transformApplied(false) |
|
642 { |
|
643 m_totalRunningTime = m_to - m_from; |
|
644 } |
|
645 |
|
646 void QSvgAnimateTransform::setArgs(TransformType type, Additive additive, const QVector<qreal> &args) |
|
647 { |
|
648 m_type = type; |
|
649 m_args = args; |
|
650 m_additive = additive; |
|
651 Q_ASSERT(!(args.count()%3)); |
|
652 m_count = args.count() / 3; |
|
653 } |
|
654 |
|
655 void QSvgAnimateTransform::apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &) |
|
656 { |
|
657 m_oldWorldTransform = p->worldTransform(); |
|
658 resolveMatrix(node); |
|
659 p->setWorldTransform(m_transform, true); |
|
660 m_transformApplied = true; |
|
661 } |
|
662 |
|
663 void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &) |
|
664 { |
|
665 p->setWorldTransform(m_oldWorldTransform, false /* don't combine */); |
|
666 m_transformApplied = false; |
|
667 } |
|
668 |
|
669 void QSvgAnimateTransform::resolveMatrix(QSvgNode *node) |
|
670 { |
|
671 static const qreal deg2rad = qreal(0.017453292519943295769); |
|
672 qreal totalTimeElapsed = node->document()->currentElapsed(); |
|
673 if (totalTimeElapsed < m_from || m_finished) |
|
674 return; |
|
675 |
|
676 qreal animationFrame = 0; |
|
677 if (m_totalRunningTime != 0) { |
|
678 animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime; |
|
679 |
|
680 if (m_repeatCount >= 0 && m_repeatCount < animationFrame) { |
|
681 m_finished = true; |
|
682 animationFrame = m_repeatCount; |
|
683 } |
|
684 } |
|
685 |
|
686 qreal percentOfAnimation = animationFrame; |
|
687 if (percentOfAnimation > 1) { |
|
688 percentOfAnimation -= ((int)percentOfAnimation); |
|
689 } |
|
690 |
|
691 qreal currentPosition = percentOfAnimation * (m_count - 1); |
|
692 int startElem = qFloor(currentPosition); |
|
693 int endElem = qCeil(currentPosition); |
|
694 |
|
695 switch(m_type) |
|
696 { |
|
697 case Translate: { |
|
698 startElem *= 3; |
|
699 endElem *= 3; |
|
700 qreal from1, from2; |
|
701 qreal to1, to2; |
|
702 from1 = m_args[startElem++]; |
|
703 from2 = m_args[startElem++]; |
|
704 to1 = m_args[endElem++]; |
|
705 to2 = m_args[endElem++]; |
|
706 |
|
707 qreal transXDiff = (to1-from1) * percentOfAnimation; |
|
708 qreal transX = from1 + transXDiff; |
|
709 qreal transYDiff = (to2-from2) * percentOfAnimation; |
|
710 qreal transY = from2 + transYDiff; |
|
711 m_transform = QTransform(); |
|
712 m_transform.translate(transX, transY); |
|
713 break; |
|
714 } |
|
715 case Scale: { |
|
716 startElem *= 3; |
|
717 endElem *= 3; |
|
718 qreal from1, from2; |
|
719 qreal to1, to2; |
|
720 from1 = m_args[startElem++]; |
|
721 from2 = m_args[startElem++]; |
|
722 to1 = m_args[endElem++]; |
|
723 to2 = m_args[endElem++]; |
|
724 |
|
725 qreal transXDiff = (to1-from1) * percentOfAnimation; |
|
726 qreal transX = from1 + transXDiff; |
|
727 qreal transYDiff = (to2-from2) * percentOfAnimation; |
|
728 qreal transY = from2 + transYDiff; |
|
729 if (transY == 0) |
|
730 transY = transX; |
|
731 m_transform = QTransform(); |
|
732 m_transform.scale(transX, transY); |
|
733 break; |
|
734 } |
|
735 case Rotate: { |
|
736 startElem *= 3; |
|
737 endElem *= 3; |
|
738 qreal from1, from2, from3; |
|
739 qreal to1, to2, to3; |
|
740 from1 = m_args[startElem++]; |
|
741 from2 = m_args[startElem++]; |
|
742 from3 = m_args[startElem++]; |
|
743 to1 = m_args[endElem++]; |
|
744 to2 = m_args[endElem++]; |
|
745 to3 = m_args[endElem++]; |
|
746 |
|
747 qreal rotationDiff = (to1 - from1) * percentOfAnimation; |
|
748 //qreal rotation = from1 + rotationDiff; |
|
749 |
|
750 qreal transXDiff = (to2-from2) * percentOfAnimation; |
|
751 qreal transX = from2 + transXDiff; |
|
752 qreal transYDiff = (to3-from3) * percentOfAnimation; |
|
753 qreal transY = from3 + transYDiff; |
|
754 m_transform = QTransform(); |
|
755 m_transform.translate(transX, transY); |
|
756 m_transform.rotate(rotationDiff); |
|
757 m_transform.translate(-transX, -transY); |
|
758 break; |
|
759 } |
|
760 case SkewX: { |
|
761 startElem *= 3; |
|
762 endElem *= 3; |
|
763 qreal from1; |
|
764 qreal to1; |
|
765 from1 = m_args[startElem++]; |
|
766 to1 = m_args[endElem++]; |
|
767 |
|
768 qreal transXDiff = (to1-from1) * percentOfAnimation; |
|
769 qreal transX = from1 + transXDiff; |
|
770 m_transform = QTransform(); |
|
771 m_transform.shear(tan(transX * deg2rad), 0); |
|
772 break; |
|
773 } |
|
774 case SkewY: { |
|
775 startElem *= 3; |
|
776 endElem *= 3; |
|
777 qreal from1; |
|
778 qreal to1; |
|
779 from1 = m_args[startElem++]; |
|
780 to1 = m_args[endElem++]; |
|
781 |
|
782 |
|
783 qreal transYDiff = (to1 - from1) * percentOfAnimation; |
|
784 qreal transY = from1 + transYDiff; |
|
785 m_transform = QTransform(); |
|
786 m_transform.shear(0, tan(transY * deg2rad)); |
|
787 break; |
|
788 } |
|
789 default: |
|
790 break; |
|
791 } |
|
792 } |
|
793 |
|
794 QSvgStyleProperty::Type QSvgAnimateTransform::type() const |
|
795 { |
|
796 return ANIMATE_TRANSFORM; |
|
797 } |
|
798 |
|
799 void QSvgAnimateTransform::setFreeze(bool freeze) |
|
800 { |
|
801 m_freeze = freeze; |
|
802 } |
|
803 |
|
804 void QSvgAnimateTransform::setRepeatCount(qreal repeatCount) |
|
805 { |
|
806 m_repeatCount = repeatCount; |
|
807 } |
|
808 |
|
809 QSvgAnimateColor::QSvgAnimateColor(int startMs, int endMs, int byMs) |
|
810 : QSvgStyleProperty(), |
|
811 m_from(startMs), m_to(endMs), m_by(byMs), |
|
812 m_finished(false) |
|
813 { |
|
814 m_totalRunningTime = m_to - m_from; |
|
815 } |
|
816 |
|
817 void QSvgAnimateColor::setArgs(bool fill, |
|
818 const QList<QColor> &colors) |
|
819 { |
|
820 m_fill = fill; |
|
821 m_colors = colors; |
|
822 } |
|
823 |
|
824 void QSvgAnimateColor::setFreeze(bool freeze) |
|
825 { |
|
826 m_freeze = freeze; |
|
827 } |
|
828 |
|
829 void QSvgAnimateColor::setRepeatCount(qreal repeatCount) |
|
830 { |
|
831 m_repeatCount = repeatCount; |
|
832 } |
|
833 |
|
834 void QSvgAnimateColor::apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &) |
|
835 { |
|
836 qreal totalTimeElapsed = node->document()->currentElapsed(); |
|
837 if (totalTimeElapsed < m_from || m_finished) |
|
838 return; |
|
839 |
|
840 qreal animationFrame = 0; |
|
841 if (m_totalRunningTime != 0) |
|
842 animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime; |
|
843 |
|
844 if (m_repeatCount >= 0 && m_repeatCount < animationFrame) { |
|
845 m_finished = true; |
|
846 animationFrame = m_repeatCount; |
|
847 } |
|
848 |
|
849 qreal percentOfAnimation = animationFrame; |
|
850 if (percentOfAnimation > 1) { |
|
851 percentOfAnimation -= ((int)percentOfAnimation); |
|
852 } |
|
853 |
|
854 qreal currentPosition = percentOfAnimation * (m_colors.count() - 1); |
|
855 |
|
856 int startElem = qFloor(currentPosition); |
|
857 int endElem = qCeil(currentPosition); |
|
858 QColor start = m_colors[startElem]; |
|
859 QColor end = m_colors[endElem]; |
|
860 |
|
861 qreal percentOfColorMorph = currentPosition; |
|
862 if (percentOfColorMorph > 1) { |
|
863 percentOfColorMorph -= ((int)percentOfColorMorph); |
|
864 } |
|
865 |
|
866 // Interpolate between the two fixed colors start and end |
|
867 qreal aDiff = (end.alpha() - start.alpha()) * percentOfColorMorph; |
|
868 qreal rDiff = (end.red() - start.red()) * percentOfColorMorph; |
|
869 qreal gDiff = (end.green() - start.green()) * percentOfColorMorph; |
|
870 qreal bDiff = (end.blue() - start.blue()) * percentOfColorMorph; |
|
871 |
|
872 int alpha = int(start.alpha() + aDiff); |
|
873 int red = int(start.red() + rDiff); |
|
874 int green = int(start.green() + gDiff); |
|
875 int blue = int(start.blue() + bDiff); |
|
876 |
|
877 QColor color(red, green, blue, alpha); |
|
878 |
|
879 if (m_fill) { |
|
880 QBrush b = p->brush(); |
|
881 m_oldBrush = b; |
|
882 b.setColor(color); |
|
883 p->setBrush(b); |
|
884 } else { |
|
885 QPen pen = p->pen(); |
|
886 m_oldPen = pen; |
|
887 pen.setColor(color); |
|
888 p->setPen(pen); |
|
889 } |
|
890 } |
|
891 |
|
892 void QSvgAnimateColor::revert(QPainter *p, QSvgExtraStates &) |
|
893 { |
|
894 if (m_fill) { |
|
895 p->setBrush(m_oldBrush); |
|
896 } else { |
|
897 p->setPen(m_oldPen); |
|
898 } |
|
899 } |
|
900 |
|
901 QSvgStyleProperty::Type QSvgAnimateColor::type() const |
|
902 { |
|
903 return ANIMATE_COLOR; |
|
904 } |
|
905 |
|
906 QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity) |
|
907 : m_opacity(opacity), m_oldOpacity(0) |
|
908 { |
|
909 |
|
910 } |
|
911 |
|
912 void QSvgOpacityStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &) |
|
913 { |
|
914 m_oldOpacity = p->opacity(); |
|
915 p->setOpacity(m_opacity * m_oldOpacity); |
|
916 } |
|
917 |
|
918 void QSvgOpacityStyle::revert(QPainter *p, QSvgExtraStates &) |
|
919 { |
|
920 p->setOpacity(m_oldOpacity); |
|
921 } |
|
922 |
|
923 QSvgStyleProperty::Type QSvgOpacityStyle::type() const |
|
924 { |
|
925 return OPACITY; |
|
926 } |
|
927 |
|
928 void QSvgGradientStyle::setStopLink(const QString &link, QSvgTinyDocument *doc) |
|
929 { |
|
930 m_link = link; |
|
931 m_doc = doc; |
|
932 } |
|
933 |
|
934 void QSvgGradientStyle::resolveStops() |
|
935 { |
|
936 if (!m_link.isEmpty() && m_doc) { |
|
937 QSvgStyleProperty *prop = m_doc->styleProperty(m_link); |
|
938 if (prop) { |
|
939 if (prop->type() == QSvgStyleProperty::GRADIENT) { |
|
940 QSvgGradientStyle *st = |
|
941 static_cast<QSvgGradientStyle*>(prop); |
|
942 st->resolveStops(); |
|
943 m_gradient->setStops(st->qgradient()->stops()); |
|
944 m_gradientStopsSet = st->gradientStopsSet(); |
|
945 } |
|
946 } else { |
|
947 qWarning("Could not resolve property : %s", qPrintable(m_link)); |
|
948 } |
|
949 m_link = QString(); |
|
950 } |
|
951 } |
|
952 |
|
953 QT_END_NAMESPACE |
|
954 |
|
955 #endif // QT_NO_SVG |