Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsvgstyle.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
6#include "qsvgstyle_p.h"
7
8#include "qsvgnode_p.h"
9#include "private/qsvganimate_p.h"
10#include "qsvgdocument_p.h"
11
12#include "qpainter.h"
13#include "qcolor.h"
14
15#include <limits>
16
18
19QSvgExtraStates::QSvgExtraStates()
20 : imageRendering(QSvgQualityStyle::ImageRenderingAuto)
21{
22}
23
24QSvgExtraStates::QSvgExtraStates(const QSvgDocument *doc) : QSvgExtraStates()
25{
26 Q_ASSERT(doc);
27 m_doc = doc;
28}
29
30QSvgStyleProperty::~QSvgStyleProperty()
31 = default;
32
33QSvgQualityStyle::QSvgQualityStyle(int color)
34 : m_imageRendering(QSvgQualityStyle::ImageRenderingAuto)
35 , m_oldImageRendering(QSvgQualityStyle::ImageRenderingAuto)
36 , m_imageRenderingSet(0)
37{
38 Q_UNUSED(color);
39}
40
41QSvgQualityStyle::~QSvgQualityStyle()
42 = default;
43
44void QSvgQualityStyle::setImageRendering(ImageRendering hint) {
45 m_imageRendering = hint;
46 m_imageRenderingSet = 1;
47}
48
49void QSvgQualityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states)
50{
51 m_oldImageRendering = states.imageRendering;
52 if (m_imageRenderingSet) {
53 states.imageRendering = m_imageRendering;
54 }
55 if (m_imageRenderingSet) {
56 bool smooth = false;
57 if (m_imageRendering == ImageRenderingAuto)
58 // auto (the spec says to prefer quality)
59 smooth = true;
60 else
61 smooth = (m_imageRendering == ImageRenderingOptimizeQuality);
62 p->setRenderHint(QPainter::SmoothPixmapTransform, smooth);
63 }
64}
65
66void QSvgQualityStyle::revert(QPainter *p, QSvgExtraStates &states)
67{
68 if (m_imageRenderingSet) {
69 states.imageRendering = m_oldImageRendering;
70 bool smooth = false;
71 if (m_oldImageRendering == ImageRenderingAuto)
72 smooth = true;
73 else
74 smooth = (m_oldImageRendering == ImageRenderingOptimizeQuality);
75 p->setRenderHint(QPainter::SmoothPixmapTransform, smooth);
76 }
77}
78
79QSvgFillStyle::QSvgFillStyle()
80 : m_fillRuleSet(0)
81 , m_fillOpacitySet(0)
82 , m_fillSet(0)
83{
84}
85
86QSvgFillStyle::~QSvgFillStyle()
87 = default;
88
89void QSvgFillStyle::setFillRule(Qt::FillRule f)
90{
91 m_fillRuleSet = 1;
92 m_fillRule = f;
93}
94
95void QSvgFillStyle::setFillOpacity(qreal opacity)
96{
97 m_fillOpacitySet = 1;
98 m_fillOpacity = opacity;
99}
100
101void QSvgFillStyle::setPaintServer(QSvgPaintServerSharedPtr paintServer)
102{
103 m_paintServer = std::move(paintServer);
104 m_fillSet = 1;
105}
106
107void QSvgFillStyle::setBrush(QBrush brush)
108{
109 m_fill = std::move(brush);
110 m_paintServer.reset();
111 m_fillSet = 1;
112}
113
114void QSvgFillStyle::apply(QPainter *p, const QSvgNode *n, QSvgExtraStates &states)
115{
116 m_oldFill = p->brush();
117 m_oldFillRule = states.fillRule;
118 m_oldFillOpacity = states.fillOpacity;
119
120 if (m_fillRuleSet)
121 states.fillRule = m_fillRule;
122 if (m_fillSet) {
123 if (m_paintServer)
124 p->setBrush(m_paintServer->brush(p, n, states));
125 else
126 p->setBrush(m_fill);
127 }
128 if (m_fillOpacitySet)
129 states.fillOpacity = m_fillOpacity;
130}
131
132void QSvgFillStyle::revert(QPainter *p, QSvgExtraStates &states)
133{
134 if (m_fillOpacitySet)
135 states.fillOpacity = m_oldFillOpacity;
136 if (m_fillSet)
137 p->setBrush(m_oldFill);
138 if (m_fillRuleSet)
139 states.fillRule = m_oldFillRule;
140}
141
142QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush)
143 : m_viewportFill(brush)
144{
145}
146
147QSvgViewportFillStyle::~QSvgViewportFillStyle()
148 = default;
149
150void QSvgViewportFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
151{
152 m_oldFill = p->brush();
153 p->setBrush(m_viewportFill);
154}
155
156void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &)
157{
158 p->setBrush(m_oldFill);
159}
160
161const int QSvgFontStyle::LIGHTER;
162const int QSvgFontStyle::BOLDER;
163
164QSvgFontStyle::QSvgFontStyle()
165 : m_familySet(0)
166 , m_sizeSet(0)
167 , m_styleSet(0)
168 , m_variantSet(0)
169 , m_weightSet(0)
170 , m_textAnchorSet(0)
171{
172}
173
174QSvgFontStyle::~QSvgFontStyle()
175 = default;
176
177/*
178 The SVG font size is a qreal in user units, but it travels in QFont::pointSizeF()
179 as a _pixel_ size because QFont has no setPixelSizeF() (see setSize()). Converting
180 it back has to go through the int-only QFont::setPixelSize(), so round rather than
181 truncate: truncation biases every fractional size downwards, and the relative
182 error grows without bound as the size shrinks.
183
184 Clamp to 1 as well. QFont::setPixelSize() rejects a non-positive size with a
185 warning and leaves the font untouched, so rounding a sub-half-pixel size down to 0
186 would silently render the text at the inherited size instead of a small one.
187*/
188int QSvgFontStyle::pixelSize(qreal size)
189{
190 return qRound(qBound(qreal(1), size, qreal(std::numeric_limits<int>::max())));
191}
192
193void QSvgFontStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states)
194{
195 m_oldQFont = p->font();
196 m_oldSvgFont = states.svgFont;
197 m_oldTextAnchor = states.textAnchor;
198 m_oldWeight = states.fontWeight;
199
200 if (m_textAnchorSet)
201 states.textAnchor = m_textAnchor;
202
203 QFont font = m_oldQFont;
204 if (m_familySet) {
205 font.setFamilies(m_qfont.families());
206 states.svgFont = states.doc()->svgFont(m_qfont.family());
207 }
208
209 if (m_sizeSet)
210 font.setPointSizeF(m_qfont.pointSizeF());
211
212 if (m_styleSet)
213 font.setStyle(m_qfont.style());
214
215 if (m_variantSet)
216 font.setCapitalization(m_qfont.capitalization());
217
218 if (m_weightSet) {
219 if (m_weight == BOLDER) {
220 states.fontWeight = qMin(states.fontWeight, QFont::Weight::Black - 100) + 100;
221 } else if (m_weight == LIGHTER) {
222 states.fontWeight = qMax(states.fontWeight, QFont::Weight::Thin + 100) - 100;
223 } else {
224 states.fontWeight = m_weight;
225 }
226 font.setWeight(QFont::Weight(qBound(static_cast<int>(QFont::Weight::Thin),
227 states.fontWeight,
228 static_cast<int>(QFont::Weight::Black))));
229 }
230
231 p->setFont(font);
232}
233
234void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &states)
235{
236 p->setFont(m_oldQFont);
237 states.svgFont = m_oldSvgFont;
238 states.textAnchor = m_oldTextAnchor;
239 states.fontWeight = m_oldWeight;
240}
241
242QSvgStrokeStyle::QSvgStrokeStyle()
243 : m_vectorEffect(0)
244 , m_oldVectorEffect(0)
245 , m_strokeSet(0)
246 , m_strokeDashArraySet(0)
247 , m_strokeDashOffsetSet(0)
248 , m_strokeLineCapSet(0)
249 , m_strokeLineJoinSet(0)
250 , m_strokeMiterLimitSet(0)
251 , m_strokeOpacitySet(0)
252 , m_strokeWidthSet(0)
253 , m_vectorEffectSet(0)
254{
255}
256
257QSvgStrokeStyle::~QSvgStrokeStyle()
258 = default;
259
260void QSvgStrokeStyle::apply(QPainter *p, const QSvgNode *n, QSvgExtraStates &states)
261{
262 m_oldStroke = p->pen();
263 m_oldStrokeOpacity = states.strokeOpacity;
264 m_oldStrokeDashOffset = states.strokeDashOffset;
265 m_oldVectorEffect = states.vectorEffect;
266
267 QPen pen = p->pen();
268
269 qreal oldWidth = pen.widthF();
270 qreal width = m_stroke.widthF();
271 if (oldWidth == 0)
272 oldWidth = 1;
273 if (width == 0)
274 width = 1;
275 qreal scale = oldWidth / width;
276
277 if (m_strokeOpacitySet)
278 states.strokeOpacity = m_strokeOpacity;
279
280 if (m_vectorEffectSet)
281 states.vectorEffect = m_vectorEffect;
282
283 if (m_strokeSet) {
284 if (m_paintServer)
285 pen.setBrush(m_paintServer->brush(p, n, states));
286 else
287 pen.setBrush(m_stroke.brush());
288 }
289
290 if (m_strokeWidthSet)
291 pen.setWidthF(m_stroke.widthF());
292
293 bool setDashOffsetNeeded = false;
294
295 if (m_strokeDashOffsetSet) {
296 states.strokeDashOffset = m_strokeDashOffset;
297 setDashOffsetNeeded = true;
298 }
299
300 if (m_strokeDashArraySet) {
301 if (m_stroke.style() == Qt::SolidLine) {
302 pen.setStyle(Qt::SolidLine);
303 } else if (m_strokeWidthSet || oldWidth == 1) {
304 // If both width and dash array was set, the dash array is already scaled correctly.
305 pen.setDashPattern(m_stroke.dashPattern());
306 setDashOffsetNeeded = true;
307 } else {
308 // If dash array was set, but not the width, the dash array has to be scaled with respect to the old width.
309 QList<qreal> dashes = m_stroke.dashPattern();
310 for (int i = 0; i < dashes.size(); ++i)
311 dashes[i] /= oldWidth;
312 pen.setDashPattern(dashes);
313 setDashOffsetNeeded = true;
314 }
315 } else if (m_strokeWidthSet && pen.style() != Qt::SolidLine && scale != 1) {
316 // If the width was set, but not the dash array, the old dash array must be scaled with respect to the new width.
317 QList<qreal> dashes = pen.dashPattern();
318 for (int i = 0; i < dashes.size(); ++i)
319 dashes[i] *= scale;
320 pen.setDashPattern(dashes);
321 setDashOffsetNeeded = true;
322 }
323
324 if (m_strokeLineCapSet)
325 pen.setCapStyle(m_stroke.capStyle());
326 if (m_strokeLineJoinSet)
327 pen.setJoinStyle(m_stroke.joinStyle());
328 if (m_strokeMiterLimitSet)
329 pen.setMiterLimit(m_stroke.miterLimit());
330
331 // You can have dash offset on solid strokes in SVG files, but not in Qt.
332 // QPen::setDashOffset() will set the pen style to Qt::CustomDashLine,
333 // so don't call the method if the pen is solid.
334 if (setDashOffsetNeeded && pen.style() != Qt::SolidLine) {
335 qreal currentWidth = pen.widthF();
336 if (currentWidth == 0)
337 currentWidth = 1;
338 pen.setDashOffset(states.strokeDashOffset / currentWidth);
339 }
340
341 pen.setCosmetic(states.vectorEffect);
342
343 p->setPen(pen);
344}
345
346void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &states)
347{
348 p->setPen(m_oldStroke);
349 states.strokeOpacity = m_oldStrokeOpacity;
350 states.strokeDashOffset = m_oldStrokeDashOffset;
351 states.vectorEffect = m_oldVectorEffect;
352}
353
354void QSvgStrokeStyle::setDashArray(const QList<qreal> &dashes)
355{
356 if (m_strokeWidthSet) {
357 QList<qreal> d = dashes;
358 qreal w = m_stroke.widthF();
359 if (w != 0 && w != 1) {
360 for (int i = 0; i < d.size(); ++i)
361 d[i] /= w;
362 }
363 m_stroke.setDashPattern(d);
364 } else {
365 m_stroke.setDashPattern(dashes);
366 }
367 m_strokeDashArraySet = 1;
368}
369
370QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans)
371 : m_transform(trans)
372{
373}
374
375QSvgTransformStyle::~QSvgTransformStyle()
376 = default;
377
378void QSvgTransformStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
379{
380 m_oldWorldTransform.push(p->worldTransform());
381 p->setWorldTransform(m_transform, true);
382}
383
384void QSvgTransformStyle::revert(QPainter *p, QSvgExtraStates &)
385{
386 p->setWorldTransform(m_oldWorldTransform.pop(), false /* don't combine */);
387}
388
389QSvgStyleProperty::Type QSvgQualityStyle::type() const
390{
391 return Quality;
392}
393
394QSvgStyleProperty::Type QSvgFillStyle::type() const
395{
396 return Fill;
397}
398
399QSvgStyleProperty::Type QSvgViewportFillStyle::type() const
400{
401 return ViewportFill;
402}
403
404QSvgStyleProperty::Type QSvgFontStyle::type() const
405{
406 return Font;
407}
408
409QSvgStyleProperty::Type QSvgStrokeStyle::type() const
410{
411 return Stroke;
412}
413
414QSvgStyleProperty::Type QSvgTransformStyle::type() const
415{
416 return Transform;
417}
418
419
420QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode)
421 : m_mode(mode)
422{
423
424}
425
426QSvgCompOpStyle::~QSvgCompOpStyle()
427 = default;
428
429void QSvgCompOpStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
430{
431 m_oldMode = p->compositionMode();
432 p->setCompositionMode(m_mode);
433}
434
435void QSvgCompOpStyle::revert(QPainter *p, QSvgExtraStates &)
436{
437 p->setCompositionMode(m_oldMode);
438}
439
440QSvgStyleProperty::Type QSvgCompOpStyle::type() const
441{
442 return CompOp;
443}
444
445QSvgOffsetStyle::~QSvgOffsetStyle()
446 = default;
447
448void QSvgOffsetStyle::apply(QPainter *, const QSvgNode *, QSvgExtraStates &)
449{
450}
451
452void QSvgOffsetStyle::revert(QPainter *, QSvgExtraStates &)
453{
454}
455
456QSvgStyleProperty::Type QSvgOffsetStyle::type() const
457{
458 return Offset;
459}
460
461QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity)
462 : m_opacity(opacity)
463{
464
465}
466
467QSvgOpacityStyle::~QSvgOpacityStyle()
468 = default;
469
470void QSvgOpacityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
471{
472 m_oldOpacity = p->opacity();
473 p->setOpacity(m_opacity * m_oldOpacity);
474}
475
476void QSvgOpacityStyle::revert(QPainter *p, QSvgExtraStates &)
477{
478 p->setOpacity(m_oldOpacity);
479}
480
481QSvgStyleProperty::Type QSvgOpacityStyle::type() const
482{
483 return Opacity;
484}
485
486QSvgStaticStyle::QSvgStaticStyle()
487 = default;
488QSvgStaticStyle::~QSvgStaticStyle()
489 = default;
490
491void QSvgStaticStyle::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states)
492{
493 for (auto &prop : m_properties) {
494 if (prop)
495 prop->apply(p, node, states);
496 }
497}
498
499void QSvgStaticStyle::revert(QPainter *p, QSvgExtraStates &states)
500{
501 for (auto &prop : m_properties) {
502 if (prop)
503 prop->revert(p, states);
504 }
505}
506
507namespace {
508
509QColor sumValue(const QColor &c1, const QColor &c2)
510{
511 QRgb rgb1 = c1.rgba();
512 QRgb rgb2 = c2.rgba();
513 int sumRed = qRed(rgb1) + qRed(rgb2);
514 int sumGreen = qGreen(rgb1) + qGreen(rgb2);
515 int sumBlue = qBlue(rgb1) + qBlue(rgb2);
516
517 QRgb sumRgb = qRgba(qBound(0, sumRed, 255),
518 qBound(0, sumGreen, 255),
519 qBound(0, sumBlue, 255),
520 255);
521
522 return QColor(sumRgb);
523}
524
525qreal sumValue(qreal value1, qreal value2)
526{
527 qreal sumValue = value1 + value2;
528 return qBound(0.0, sumValue, 1.0);
529}
530
531}
532
533QSvgAnimatedStyle::QSvgAnimatedStyle()
534{
535}
536
537QSvgAnimatedStyle::~QSvgAnimatedStyle()
538{
539}
540
541void QSvgAnimatedStyle::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states)
542{
543 QSharedPointer<QSvgAbstractAnimator> animator = states.doc()->animator();
544 QList<QSvgAbstractAnimation *> nodeAnims = animator->animationsForNode(node);
545
546 savePaintingState(p, node, states);
547 if (nodeAnims.isEmpty())
548 return;
549
550 QSvgStyleState currentStyle = m_static;
551 for (const QSvgAbstractAnimation *anim : std::as_const(nodeAnims)) {
552 if (!anim->isActive())
553 continue;
554
555 fetchStyleState(anim, currentStyle);
556 }
557
558 applyStyle(p, states, currentStyle);
559}
560
561void QSvgAnimatedStyle::revert(QPainter *p, QSvgExtraStates &states)
562{
563 p->setWorldTransform(m_worldTransform);
564 p->setBrush(m_static.fill);
565 p->setPen(m_static.stroke);
566 p->setOpacity(m_static.opacity);
567 states.fillOpacity = m_static.fillOpacity;
568 states.strokeOpacity = m_static.strokeOpacity;
569}
570
571void QSvgAnimatedStyle::savePaintingState(const QPainter *p, const QSvgNode *node, QSvgExtraStates &states)
572{
573 m_worldTransform = p->worldTransform();
574 if (auto prop = node->style().property(QSvgStyleProperty::Transform))
575 m_static.transform = (static_cast<QSvgTransformStyle*>(prop))->qtransform();
576
577 if (auto prop = node->style().property(QSvgStyleProperty::Offset)) {
578 QSvgOffsetStyle *offset = static_cast<QSvgOffsetStyle*>(prop);
579 m_static.offsetPath = offset->path();
580 m_static.offsetRotateType = offset->rotateType();
581 m_static.offsetRotate = offset->rotateAngle();
582 }
583
584 m_static.fill = p->brush();
585 m_static.stroke = p->pen();
586 m_static.fillOpacity = states.fillOpacity;
587 m_static.strokeOpacity = states.strokeOpacity;
588 m_static.opacity = p->opacity();
589 m_transformToNode = m_static.transform.inverted() * m_worldTransform;
590}
591
592void QSvgAnimatedStyle::fetchStyleState(const QSvgAbstractAnimation *animation, QSvgStyleState &currentStyle)
593{
594 bool replace = animation->animationType() == QSvgAbstractAnimation::CSS ? true :
595 (static_cast<const QSvgAnimateNode *>(animation))->additiveType() == QSvgAnimateNode::Replace;
596
597 QList<QSvgAbstractAnimatedProperty *> properties = animation->properties();
598 for (const QSvgAbstractAnimatedProperty *property : std::as_const(properties)) {
599 if (property->propertyName() == QStringLiteral("fill")) {
600 QBrush brush = currentStyle.fill;
601 QColor brushColor = brush.color();
602 QColor animatedColor = property->interpolatedValue().value<QColor>();
603 QColor sumOrReplaceColor = replace ? animatedColor : sumValue(brushColor, animatedColor);
604 brush.setColor(sumOrReplaceColor);
605 currentStyle.fill = brush;
606 } else if (property->propertyName() == QStringLiteral("stroke")) {
607 QPen &pen = currentStyle.stroke;
608 QBrush penBrush = pen.brush();
609 QColor penColor = penBrush.color();
610 QColor animatedColor = property->interpolatedValue().value<QColor>();
611 QColor sumOrReplaceColor = replace ? animatedColor : sumValue(penColor, animatedColor);
612 penBrush.setColor(sumOrReplaceColor);
613 penBrush.setStyle(Qt::SolidPattern);
614 pen.setBrush(penBrush);
615 } else if (property->propertyName() == QStringLiteral("transform")) {
616 QTransform animatedTransform = property->interpolatedValue().value<QTransform>();
617 QTransform sumOrReplaceTransform = replace ? animatedTransform : animatedTransform * currentStyle.transform;
618 currentStyle.transform = sumOrReplaceTransform;
619 } else if (property->propertyName() == QStringLiteral("fill-opacity")) {
620 qreal animatedFillOpacity = property->interpolatedValue().value<qreal>();
621 qreal sumOrReplaceOpacity = replace ? animatedFillOpacity : sumValue(currentStyle.fillOpacity, animatedFillOpacity);
622 currentStyle.fillOpacity = sumOrReplaceOpacity;
623 } else if (property->propertyName() == QStringLiteral("stroke-opacity")) {
624 qreal animatedStrokeOpacity = property->interpolatedValue().value<qreal>();
625 qreal sumOrReplaceOpacity = replace ? animatedStrokeOpacity : sumValue(currentStyle.strokeOpacity, animatedStrokeOpacity);
626 currentStyle.strokeOpacity = sumOrReplaceOpacity;
627 } else if (property->propertyName() == QStringLiteral("opacity")) {
628 qreal animatedOpacity = property->interpolatedValue().value<qreal>();
629 qreal sumOrReplaceOpacity = replace ? animatedOpacity : sumValue(currentStyle.opacity, animatedOpacity);
630 currentStyle.opacity = sumOrReplaceOpacity;
631 } else if (property->propertyName() == QStringLiteral("offset-distance")) {
632 qreal offsetDistance = property->interpolatedValue().value<qreal>();
633 currentStyle.offsetDistance = offsetDistance;
634 }
635 }
636}
637
638void QSvgAnimatedStyle::applyStyle(QPainter *p, QSvgExtraStates &states, const QSvgStyleState &currentStyle)
639{
640 p->setBrush(currentStyle.fill);
641 states.fillOpacity = currentStyle.fillOpacity;
642 p->setPen(currentStyle.stroke);
643 states.strokeOpacity = currentStyle.strokeOpacity;
644 p->setOpacity(currentStyle.opacity);
645
646 QTransform transform = currentStyle.transform;
647 QTransform offset;
648 if (m_static.offsetPath) {
649 qreal offsetDistance = currentStyle.offsetDistance;
650 qreal angle = m_static.offsetPath.value().angleAtPercent(offsetDistance);
651 QPointF position = m_static.offsetPath.value().pointAtPercent(offsetDistance);
652 offset.translate(position.x(), position.y());
653
654 switch (m_static.offsetRotateType) {
655 case QtSvg::OffsetRotateType::Auto:
656 offset.rotate(-angle);
657 break;
658 case QtSvg::OffsetRotateType::Angle:
659 offset.rotate(m_static.offsetRotate);
660 break;
661 case QtSvg::OffsetRotateType::AutoAngle:
662 offset.rotate(m_static.offsetRotate - angle);
663 break;
664 case QtSvg::OffsetRotateType::Reverse:
665 offset.rotate(180 - angle);
666 break;
667 case QtSvg::OffsetRotateType::ReverseAngle:
668 offset.rotate(180 + m_static.offsetRotate - angle);
669 break;
670 }
671 }
672
673 QTransform combinedTransform = transform * offset * m_transformToNode;
674 p->setWorldTransform(combinedTransform);
675}
676
677QT_END_NAMESPACE
Combined button and popup list for selecting options.
#define QStringLiteral(str)
Definition qstring.h:1865