Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsvgstructure.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
4#include "qsvgstructure_p.h"
5#include "qsvggraphics_p.h"
6
7#include "qsvgstyle_p.h"
9#include "qsvggraphics_p.h"
10#include "qsvgstyle_p.h"
11#include "qsvgfilter_p.h"
12
13#include "qpainter.h"
14#include "qlocale.h"
15#include "qdebug.h"
16
17#include <QLoggingCategory>
19#include <QtGui/qimageiohandler.h>
20
22
24
26 : QSvgStructureNode(parent)
27{
28
29}
30
35
37{
39 while (itr != m_renderers.end()) {
40 QSvgNode *node = *itr;
41 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
42 node->draw(p, states);
43 ++itr;
44 }
45}
46
48{
49 return true;
50}
51
53{
54 return Group;
55}
56
62
64{
66 return doc ? doc->namedNode(id) : 0;
67}
68
70{
72
73 if (id.isEmpty())
74 return; //we can't add it to scope without id
75
77 if (doc)
78 doc->addNamedNode(id, child);
79}
80
82 : QSvgStructureNode(parent)
83{
84}
85
87{
88 return false;
89}
90
92{
93 return Defs;
94}
95
97 QSvgSymbolLike::PreserveAspectRatios pAspectRatios, QSvgSymbolLike::Overflow overflow)
98 : QSvgStructureNode(parent)
99 , m_rect(bounds)
100 , m_viewBox(viewBox)
101 , m_refP(refP)
102 , m_pAspectRatios(pAspectRatios)
103 , m_overflow(overflow)
104{
105
106}
107
109{
110 qreal scaleX = 1;
111 if (m_rect.width() > 0 && m_viewBox.width() > 0)
112 scaleX = m_rect.width()/m_viewBox.width();
113 qreal scaleY = 1;
114 if (m_rect.height() > 0 && m_viewBox.height() > 0)
115 scaleY = m_rect.height()/m_viewBox.height();
116
119 t.translate(- m_refP.x() * scaleX - m_rect.left() - m_viewBox.left() * scaleX,
120 - m_refP.y() * scaleY - m_rect.top() - m_viewBox.top() * scaleY);
121 t.scale(scaleX, scaleY);
122
123 if (m_viewBox.isValid())
124 p->setClipRect(t.mapRect(m_viewBox));
125 }
126
127 qreal offsetX = 0;
128 qreal offsetY = 0;
129
130 if (!qFuzzyCompare(scaleX, scaleY) &&
132
134 scaleX = scaleY = qMin(scaleX, scaleY);
135 else
136 scaleX = scaleY = qMax(scaleX, scaleY);
137
138 qreal xOverflow = scaleX * m_viewBox.width() - m_rect.width();
139 qreal yOverflow = scaleY * m_viewBox.height() - m_rect.height();
140
142 offsetX -= xOverflow / 2.;
144 offsetX -= xOverflow;
145
147 offsetY -= yOverflow / 2.;
149 offsetY -= yOverflow;
150 }
151
152 p->translate(offsetX - m_refP.x() * scaleX, offsetY - m_refP.y() * scaleY);
153 p->scale(scaleX, scaleY);
154}
155
156QSvgSymbol::QSvgSymbol(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
157 QSvgSymbol::PreserveAspectRatios pAspectRatios,
158 QSvgSymbol::Overflow overflow)
159 : QSvgSymbolLike(parent, bounds, viewBox, refP, pAspectRatios, overflow)
160{
161}
162
164{
165 if (!states.inUse) //Symbol is only drawn when within a use node.
166 return;
167
169
170 p->save();
172 while (itr != m_renderers.end()) {
173 QSvgNode *node = *itr;
174 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
175 node->draw(p, states);
176 ++itr;
177 }
178 p->restore();
179}
180
182{
183 return Symbol;
184}
185
186QSvgMarker::QSvgMarker(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
187 QSvgSymbol::PreserveAspectRatios pAspectRatios, QSvgSymbol::Overflow overflow,
188 Orientation orientation, qreal orientationAngle, MarkerUnits markerUnits)
189 : QSvgSymbolLike(parent, bounds, viewBox, refP, pAspectRatios, overflow)
190 , m_orientation(orientation)
191 , m_orientationAngle(orientationAngle)
192 , m_markerUnits(markerUnits)
193{
194 // apply the svg standard style
195 QSvgFillStyle *fillProp = new QSvgFillStyle();
196 fillProp->setBrush(Qt::black);
197 appendStyleProperty(fillProp, QStringLiteral(""));
198
199 QSvgStrokeStyle *strokeProp = new QSvgStrokeStyle();
200 strokeProp->setMiterLimit(4);
201 strokeProp->setWidth(1);
202 strokeProp->setLineCap(Qt::FlatCap);
203 strokeProp->setLineJoin(Qt::SvgMiterJoin);
204 strokeProp->setStroke(Qt::NoBrush);
205 appendStyleProperty(strokeProp, QStringLiteral(""));
206}
207
209 QtSvg::UnitTypes filterUnits, QtSvg::UnitTypes primitiveUnits)
210 : QSvgStructureNode(parent)
211 , m_rect(bounds)
212 , m_filterUnits(filterUnits)
213 , m_primitiveUnits(primitiveUnits)
214 , m_supported(true)
215{
216
217}
218
220{
221 return false;
222}
223
225{
226 if (!states.inUse) //Symbol is only drawn in combination with another node.
227 return;
228
230 return;
231 QScopedValueRollback<bool> recursingGuard(m_recursing, true);
232
234
235 p->save();
237
239
240 while (itr != m_renderers.end()) {
241 QSvgNode *node = *itr;
242 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
243 node->draw(p, states);
244 ++itr;
245 }
247 p->restore();
248}
249
251{
252 QScopedValueRollback<bool> inUseGuard(states.inUse, true);
253
254 auto getMeanAngle = [] (QPointF p0, QPointF p1, QPointF p2) {
255 QPointF t1 = p1 - p0;
256 QPointF t2 = p2 - p1;
257 qreal hyp1 = hypot(t1.x(), t1.y());
258 if (hyp1 > 0)
259 t1 /= hyp1;
260 else
261 return 0.;
262 qreal hyp2 = hypot(t2.x(), t2.y());
263 if (hyp2 > 0)
264 t2 /= hyp2;
265 else
266 return 0.;
267 QPointF tangent = t1 + t2;
268 return -atan2(tangent.y(), tangent.x()) / M_PI * 180.;
269 };
270
271 if (node->hasAnyMarker()) {
272 QList<PositionMarkerPair> marks;
273 if (node->type() == Line) {
274 QSvgLine *line = static_cast<QSvgLine*>(node);
275 if (!line)
276 return;
277 if (node->hasMarkerStart())
278 marks << PositionMarkerPair { line->line().p1().x(), line->line().p1().y(),
279 line->line().angle(), line->markerStartId(),
280 true};
281 if (node->hasMarkerEnd())
282 marks << PositionMarkerPair { line->line().p2().x(), line->line().p2().y(),
283 line->line().angle(), line->markerEndId() };
284 } else if (node->type() == Polyline || node->type() == Polygon) {
285 QSvgPolyline *polyline = static_cast<QSvgPolyline*>(node);
286 QSvgPolygon *polygon = static_cast<QSvgPolygon*>(node);
287
288 const QPolygonF &polyData = (node->type() == Polyline) ? polyline->polygon() : polygon->polygon();
289
290 if (node->hasMarkerStart() && polyData.size() > 1) {
291 QLineF line(polyData.at(0), polyData.at(1));
292 marks << PositionMarkerPair { line.p1().x(),
293 line.p1().y(),
294 line.angle(),
295 node->markerStartId(),
296 true };
297 }
298 if (node->hasMarkerMid()) {
299 for (int i = 1; i < polyData.size() - 1; i++) {
300 QPointF p0 = polyData.at(i-1);
301 QPointF p1 = polyData.at(i);
302 QPointF p2 = polyData.at(i+1);
303
304 marks << PositionMarkerPair { p1.x(),
305 p1.y(),
306 getMeanAngle(p0, p1, p2),
307 node->markerStartId() };
308 }
309 }
310 if (node->hasMarkerEnd() && polyData.size() > 1) {
311 QLineF line(polyData.at(polyData.size()-1), polyData.last());
312 marks << PositionMarkerPair { line.p2().x(),
313 line.p2().y(),
314 line.angle(),
315 node->markerEndId() };
316 }
317 } else if (node->type() == Path) {
318 QSvgPath *path = static_cast<QSvgPath*>(node);
319 if (!path)
320 return;
321 if (node->hasMarkerStart())
322 marks << PositionMarkerPair { path->path().pointAtPercent(0.).x(),
323 path->path().pointAtPercent(0.).y(),
324 path->path().angleAtPercent(0.),
325 path->markerStartId(),
326 true };
327 if (node->hasMarkerMid()) {
328 for (int i = 1; i < path->path().elementCount() - 1; i++) {
329 if (path->path().elementAt(i).type == QPainterPath::MoveToElement)
330 continue;
331 if (path->path().elementAt(i).type == QPainterPath::CurveToElement)
332 continue;
333 if (( path->path().elementAt(i).type == QPainterPath::CurveToDataElement &&
334 path->path().elementAt(i+1).type != QPainterPath::CurveToDataElement ) ||
335 path->path().elementAt(i).type == QPainterPath::LineToElement) {
336
337 QPointF p0(path->path().elementAt(i-1).x, path->path().elementAt(i-1).y);
338 QPointF p1(path->path().elementAt(i).x, path->path().elementAt(i).y);
339 QPointF p2(path->path().elementAt(i+1).x, path->path().elementAt(i+1).y);
340
341 marks << PositionMarkerPair { p1.x(),
342 p1.y(),
343 getMeanAngle(p0, p1, p2),
344 path->markerMidId() };
345 }
346 }
347 }
348 if (node->hasMarkerEnd())
349 marks << PositionMarkerPair { path->path().pointAtPercent(1.).x(),
350 path->path().pointAtPercent(1.).y(),
351 path->path().angleAtPercent(1.),
352 path->markerEndId() };
353 }
354 for (auto &i : marks) {
355 QSvgMarker *markNode = static_cast<QSvgMarker*>(node->document()->namedNode(i.markerId));
356 if (!markNode) {
357 continue;
358 }
359 p->save();
360 p->translate(i.x, i.y);
361 if (markNode->orientation() == QSvgMarker::Orientation::Value)
362 p->rotate(markNode->orientationAngle());
363 else {
364 p->rotate(-i.angle);
365 if (i.isStartNode && markNode->orientation() == QSvgMarker::Orientation::AutoStartReverse)
366 p->rotate(-180);
367 }
368 QRectF oldRect = markNode->m_rect;
369 if (markNode->markerUnits() == QSvgMarker::MarkerUnits::StrokeWidth) {
370 markNode->m_rect.setWidth(markNode->m_rect.width() * p->pen().widthF());
371 markNode->m_rect.setHeight(markNode->m_rect.height() * p->pen().widthF());
372 }
373 markNode->draw(p, states);
374 markNode->m_rect = oldRect;
375 p->restore();
376 }
377 }
378}
379
381{
382 return Marker;
383}
384
386{
387 QRectF filterBounds = m_rect.combinedWithLocalRect(bounds, document()->viewBox(), m_filterUnits);
388 QRect filterBoundsGlob = p->transform().mapRect(filterBounds).toRect();
389 QRect filterBoundsGlobRel = filterBoundsGlob.translated(-buffer.offset());
390
391 if (filterBoundsGlobRel.isEmpty())
392 return buffer;
393
395 if (!QImageIOHandler::allocateImage(filterBoundsGlobRel.size(), buffer.format(), &proxy)) {
396 qCWarning(lcSvgDraw) << "The requested filter is too big, ignoring";
397 return buffer;
398 }
399 proxy = buffer.copy(filterBoundsGlobRel);
400 proxy.setOffset(filterBoundsGlob.topLeft());
401 if (proxy.isNull())
402 return buffer;
403
404 QMap<QString, QImage> buffers;
406 buffers[QStringLiteral("SourceGraphic")] = proxy;
407
408 bool requiresSourceAlpha = false;
409
410 const QList<QSvgNode *> children = renderers();
411 for (const QSvgNode *renderer : children) {
413 if (filter && filter->requiresSourceAlpha()) {
414 requiresSourceAlpha = true;
415 break;
416 }
417 }
418
419 if (requiresSourceAlpha) {
420 QImage proxyAlpha = proxy.convertedTo(QImage::Format_Alpha8).convertedTo(proxy.format());
421 proxyAlpha.setOffset(proxy.offset());
422 if (proxyAlpha.isNull())
423 return buffer;
424 buffers[QStringLiteral("SourceAlpha")] = proxyAlpha;
425 }
426
428 for (const QSvgNode *renderer : children) {
430 if (filter) {
431 result = filter->apply(item, buffers, p, bounds, filterBounds, m_primitiveUnits, m_filterUnits);
432 if (!result.isNull()) {
434 buffers[filter->result()] = result;
435 }
436 }
437 }
438 return result;
439}
440
442{
443 m_supported = supported;
444}
445
447{
448 return m_supported;
449}
450
452{
453 return Filter;
454}
455
456
457inline static bool isSupportedSvgFeature(const QString &str)
458{
459 static const QStringList wordList = {
460 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#Text"),
461 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#Shape"),
462 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#SVG"),
463 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#Structure"),
464 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#SolidColor"),
465 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#Hyperlinking"),
466 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#CoreAttribute"),
467 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#XlinkAttribute"),
468 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#SVG-static"),
469 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#OpacityAttribute"),
470 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#Gradient"),
471 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#Font"),
472 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#Image"),
473 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#ConditionalProcessing"),
474 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#Extensibility"),
475 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#GraphicsAttribute"),
476 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#Prefetch"),
477 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#PaintAttribute"),
478 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#ConditionalProcessingAttribute"),
479 QStringLiteral("http://www.w3.org/Graphics/SVG/feature/1.2/#ExternalResourcesRequiredAttribute")
480 };
481
482 return wordList.contains(str);
483}
484
485static inline bool isSupportedSvgExtension(const QString &)
486{
487 return false;
488}
489
490
492 : QSvgStructureNode(parent)
493{
494 init();
495}
496
498{
500
501 while (itr != m_renderers.end()) {
502 QSvgNode *node = *itr;
503 if (node->isVisible() && (node->displayMode() != QSvgNode::NoneMode)) {
504 const QStringList &features = node->requiredFeatures();
505 const QStringList &extensions = node->requiredExtensions();
506 const QStringList &languages = node->requiredLanguages();
507 const QStringList &formats = node->requiredFormats();
508 const QStringList &fonts = node->requiredFonts();
509
510 bool okToRender = true;
511 if (!features.isEmpty()) {
512 QStringList::const_iterator sitr = features.constBegin();
513 for (; sitr != features.constEnd(); ++sitr) {
514 if (!isSupportedSvgFeature(*sitr)) {
515 okToRender = false;
516 break;
517 }
518 }
519 }
520
521 if (okToRender && !extensions.isEmpty()) {
522 QStringList::const_iterator sitr = extensions.constBegin();
523 for (; sitr != extensions.constEnd(); ++sitr) {
524 if (!isSupportedSvgExtension(*sitr)) {
525 okToRender = false;
526 break;
527 }
528 }
529 }
530
531 if (okToRender && !languages.isEmpty()) {
532 QStringList::const_iterator sitr = languages.constBegin();
533 okToRender = false;
534 for (; sitr != languages.constEnd(); ++sitr) {
535 if ((*sitr).startsWith(m_systemLanguagePrefix)) {
536 okToRender = true;
537 break;
538 }
539 }
540 }
541
542 if (okToRender && !formats.isEmpty()) {
543 okToRender = false;
544 }
545
546 if (okToRender && !fonts.isEmpty()) {
547 okToRender = false;
548 }
549
550 if (okToRender) {
551 node->draw(p, states);
552 break;
553 }
554 }
555 ++itr;
556 }
557}
558
560{
561 return Switch;
562}
563
564void QSvgSwitch::init()
565{
566 QLocale locale;
567 m_systemLanguage = locale.name().replace(QLatin1Char('_'), QLatin1Char('-'));
568 int idx = m_systemLanguage.indexOf(QLatin1Char('-'));
569 m_systemLanguagePrefix = m_systemLanguage.mid(0, idx);
570}
571
573{
575 if (!m_recursing) {
576 QScopedValueRollback<bool> guard(m_recursing, true);
577 for (QSvgNode *node : std::as_const(m_renderers))
578 bounds |= node->transformedBounds(p, states);
579 }
580 return bounds;
581}
582
584{
585 QSvgNode *prev = nullptr;
587 for (; itr != m_renderers.constEnd(); ++itr) {
588 QSvgNode *node = *itr;
589 if (node == n)
590 return prev;
591 prev = node;
592 }
593 return prev;
594}
595
597 QtSvg::UnitTypes contentUnits)
598 : QSvgStructureNode(parent)
599 , m_rect(bounds)
600 , m_contentUnits(contentUnits)
601{
602}
603
605{
606 return false;
607}
608
610{
611 QTransform t = p->transform();
612 p->resetTransform();
613 QRectF basicRect = targetNode->bounds(p, states);
614 *globalRect = t.mapRect(basicRect);
615 p->setTransform(t);
616 return createMask(p, states, basicRect, globalRect);
617}
618
619QImage QSvgMask::createMask(QPainter *p, QSvgExtraStates &states, const QRectF &localRect, QRectF *globalRect) const
620{
621 QRect imageBound = globalRect->toAlignedRect();
622 *globalRect = imageBound.toRectF();
623
624 QImage mask;
626 qCWarning(lcSvgDraw) << "The requested mask size is too big, ignoring";
627 return mask;
628 }
629
631 return mask;
632 QScopedValueRollback<bool> recursingGuard(m_recursing, true);
633
634 // Chrome seems to return the mask of the mask if a mask is set on the mask
635 if (this->hasMask()) {
636 QSvgMask *maskNode = static_cast<QSvgMask*>(document()->namedNode(this->maskId()));
637 if (maskNode) {
638 QRectF boundsRect;
639 return maskNode->createMask(p, states, localRect, &boundsRect);
640 }
641 }
642
643 // The mask is created with other elements during rendering.
644 // Black pixels are masked out, white pixels are not masked.
645 // The strategy is to draw the elements in a buffer (QImage) and to map
646 // the white-black image into a transparent-white image that can be used
647 // with QPainters composition mode to set the mask.
648
649 mask.fill(Qt::transparent);
652
653 QSvgExtraStates maskNodeStates;
654 applyStyleRecursive(&painter, maskNodeStates);
655
656 // The transformation of the mask node is not relevant. What matters are the contentUnits
657 // and the position/scale of the node that the mask is applied to.
659 painter.translate(-imageBound.topLeft());
660 painter.setTransform(p->transform(), true);
661
663 if (m_contentUnits == QtSvg::UnitTypes::objectBoundingBox){
664 painter.translate(localRect.topLeft());
665 painter.scale(localRect.width(), localRect.height());
666 }
667
668 // Draw all content items of the mask to generate the mask
670 while (itr != m_renderers.end()) {
671 QSvgNode *node = *itr;
672 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
673 node->draw(&painter, maskNodeStates);
674 ++itr;
675 }
676
677 for (int i=0; i < mask.height(); i++) {
678 QRgb *line = reinterpret_cast<QRgb *>(mask.scanLine(i));
679 for (int j=0; j < mask.width(); j++) {
680 const qreal rC = 0.2125, gC = 0.7154, bC = 0.0721; //luminanceToAlpha times alpha following SVG 1.1
681 int alpha = 255 - (qRed(line[j]) * rC + qGreen(line[j]) * gC + qBlue(line[j]) * bC) * qAlpha(line[j])/255.;
682 line[j] = qRgba(0, 0, 0, alpha);
683 }
684 }
685
686 // Make a path out of the clipRectangle and draw it inverted - black over all content items.
687 // This is required to apply a clip rectangle with transformations.
688 // painter.setClipRect(clipRect) sounds like the obvious thing to do but
689 // created artifacts due to antialiasing.
690 QRectF clipRect = m_rect.combinedWithLocalRect(localRect);
691 QPainterPath clipPath;
693 clipPath.addRect(mask.rect().adjusted(-10, -10, 20, 20));
694 clipPath.addPolygon(oldT.map(QPolygonF(clipRect)));
696 painter.fillPath(clipPath, Qt::black);
697
698 return mask;
699}
700
702{
703 return Mask;
704}
705
708 : QSvgStructureNode(parent),
709 m_rect(bounds),
710 m_viewBox(viewBox),
711 m_contentUnits(contentUnits),
712 m_transform(transform)
713
714{
715
716}
717
719{
720 return false;
721}
722
724{
725 static QImage checkerPattern;
726
727 if (checkerPattern.isNull()) {
728 checkerPattern = QImage(QSize(8, 8), QImage::Format_ARGB32);
729 QPainter p(&checkerPattern);
730 p.fillRect(QRect(0, 0, 4, 4), QColorConstants::Svg::white);
731 p.fillRect(QRect(4, 0, 4, 4), QColorConstants::Svg::black);
732 p.fillRect(QRect(0, 4, 4, 4), QColorConstants::Svg::black);
733 p.fillRect(QRect(4, 4, 4, 4), QColorConstants::Svg::white);
734 }
735
736 return checkerPattern;
737}
738
740{
741 // pe stands for Pattern Element
742 QRectF peBoundingBox;
743 QRectF peWorldBoundingBox;
744
745 QTransform t = p->transform();
746 p->resetTransform();
747 peBoundingBox = patternElement->bounds(p, states);
748 peWorldBoundingBox = t.mapRect(peBoundingBox);
749 p->setTransform(t);
750
751 // This function renders the pattern into an Image, so we need to apply the correct
752 // scaling values when we draw the pattern. The scaling is affected by two factors :
753 // - The "patternTransform" attribute which itself might contain a scaling
754 // - The scaling applied globally.
755 // The first is obtained from m11 and m22 matrix elements,
756 // while the second is calculated by dividing the patternElement global size
757 // by its local size.
758 qreal contentScaleFactorX = m_transform.m11();
759 qreal contentScaleFactorY = m_transform.m22();
760 if (m_contentUnits == QtSvg::UnitTypes::userSpaceOnUse) {
761 contentScaleFactorX *= t.m11();
762 contentScaleFactorY *= t.m22();
763 } else {
764 contentScaleFactorX *= peWorldBoundingBox.width();
765 contentScaleFactorY *= peWorldBoundingBox.height();
766 }
767
768 // Calculate the pattern bounding box depending on the used UnitTypes
769 QRectF patternBoundingBox = m_rect.combinedWithLocalRect(peBoundingBox);
770
772 imageSize.setWidth(qCeil(patternBoundingBox.width() * t.m11() * m_transform.m11()));
773 imageSize.setHeight(qCeil(patternBoundingBox.height() * t.m22() * m_transform.m22()));
774
775 calculateAppliedTransform(t, peBoundingBox, imageSize);
776 return renderPattern(imageSize, contentScaleFactorX, contentScaleFactorY);
777}
778
780{
781 return Pattern;
782}
783
784QImage QSvgPattern::renderPattern(QSize size, qreal contentScaleX, qreal contentScaleY)
785{
786 if (size.isEmpty() || !qIsFinite(contentScaleX) || !qIsFinite(contentScaleY))
787 return defaultPattern();
788
789 // Allocate a QImage to draw the pattern in with the calculated size.
792 qCWarning(lcSvgDraw) << "The requested pattern size is too big, ignoring";
793 return defaultPattern();
794 }
796
797 // Draw the pattern using our QPainter.
798 QPainter patternPainter(&pattern);
799 QSvgExtraStates patternStates;
800 initPainter(&patternPainter);
801 applyStyleRecursive(&patternPainter, patternStates);
802 patternPainter.resetTransform();
803
804 // According to the <pattern> definition, if viewBox exists then patternContentUnits
805 // is ignored
806 if (m_viewBox.isNull())
807 patternPainter.scale(contentScaleX, contentScaleY);
808 else
809 patternPainter.setWindow(m_viewBox.toRect());
810
811 // Draw all this Pattern children nodes with our QPainter,
812 // no need to use any Extra States
813 for (QSvgNode *node : m_renderers)
814 node->draw(&patternPainter, patternStates);
815
816 return pattern;
817}
818
819void QSvgPattern::calculateAppliedTransform(QTransform &worldTransform, QRectF peLocalBB, QSize imageSize)
820{
821 // Calculate the required transform to be applied to the QBrush used for correct
822 // pattern drawing with the object being rendered.
823 // Scale : Apply inverse the scale used above because QBrush uses the transform used
824 // by the QPainter and this function has already rendered the QImage with the
825 // correct size. Moreover, take into account the difference between the required
826 // ideal image size in float and the QSize given to image as an integer value.
827 //
828 // Translate : Apply translation depending on the calculated x and y values so that the
829 // drawn pattern can be shifted inside the object.
830 // Pattern Transform : Apply the transform in the "patternTransform" attribute. This
831 // transform contains everything except scaling, because it is
832 // already applied above on the QImage and the QPainter while
833 // drawing the pattern tile.
834 m_appliedTransform.reset();
835 qreal imageDownScaleFactorX = 1 / worldTransform.m11();
836 qreal imageDownScaleFactorY = 1 / worldTransform.m22();
837
838 m_appliedTransform.scale(qIsFinite(imageDownScaleFactorX) ? imageDownScaleFactorX : 1.0,
839 qIsFinite(imageDownScaleFactorY) ? imageDownScaleFactorY : 1.0);
840
841 QRectF p = m_rect.combinedWithLocalRect(peLocalBB);
842 m_appliedTransform.scale((p.width() * worldTransform.m11() * m_transform.m11()) / imageSize.width(),
843 (p.height() * worldTransform.m22() * m_transform.m22()) / imageSize.height());
844
845 QPointF translation = m_rect.translationRelativeToBoundingBox(peLocalBB);
846 m_appliedTransform.translate(translation.x() * worldTransform.m11(), translation.y() * worldTransform.m22());
847
848 QTransform scalelessTransform = m_transform;
849 scalelessTransform.scale(1 / m_transform.m11(), 1 / m_transform.m22());
850
851 m_appliedTransform = m_appliedTransform * scalelessTransform;
852}
853
static bool allocateImage(QSize size, QImage::Format format, QImage *image)
\inmodule QtGui
Definition qimage.h:37
@ Format_Alpha8
Definition qimage.h:65
@ Format_RGBA8888
Definition qimage.h:59
@ Format_ARGB32
Definition qimage.h:47
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
Definition qline.h:192
iterator end()
Definition qlist.h:626
const_iterator constBegin() const noexcept
Definition qlist.h:632
iterator begin()
Definition qlist.h:625
void append(parameter_type t)
Definition qlist.h:458
const_iterator constEnd() const noexcept
Definition qlist.h:633
\inmodule QtGui
void addRect(const QRectF &rect)
Adds the given rectangle to this path as a closed subpath.
void setFillRule(Qt::FillRule fillRule)
Sets the fill rule of the painter path to the given fillRule.
void addPolygon(const QPolygonF &polygon)
Adds the given polygon to the path as an (unclosed) subpath.
QPointF pointAtPercent(qreal t) const
Returns the point at at the percentage t of the current path.
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
void scale(qreal sx, qreal sy)
Scales the coordinate system by ({sx}, {sy}).
void fillPath(const QPainterPath &path, const QBrush &brush)
Fills the given path using the given brush.
const QTransform & transform() const
Alias for worldTransform().
void resetTransform()
Resets any transformations that were made using translate(), scale(), shear(), rotate(),...
void translate(const QPointF &offset)
Translates the coordinate system by the given offset; i.e.
void setTransform(const QTransform &transform, bool combine=false)
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
The QPolygonF class provides a list of points using floating point precision.
Definition qpolygon.h:96
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr qreal left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:497
constexpr void setWidth(qreal w) noexcept
Sets the width of the rectangle to the given finite width.
Definition qrect.h:818
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition qrect.h:658
constexpr QRect toRect() const noexcept
Returns a QRect based on the values of this rectangle.
Definition qrect.h:859
constexpr qreal top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:498
constexpr bool isValid() const noexcept
Returns true if the rectangle is valid, otherwise returns false.
Definition qrect.h:666
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr QRectF toRectF() const noexcept
Definition qrect.h:857
constexpr QRect translated(int dx, int dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
Definition qrect.h:261
\inmodule QtCore
Definition qsize.h:25
constexpr void setWidth(int w) noexcept
Sets the width to the given width.
Definition qsize.h:136
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
QSvgDefs(QSvgNode *parent)
Type type() const override
bool shouldDrawNode(QPainter *p, QSvgExtraStates &states) const override
static const QSvgFeFilterPrimitive * castToFilterPrimitive(const QSvgNode *node)
void setSupported(bool supported)
bool shouldDrawNode(QPainter *, QSvgExtraStates &) const override
Type type() const override
QSvgFilterContainer(QSvgNode *parent, const QSvgRectF &bounds, QtSvg::UnitTypes filterUnits, QtSvg::UnitTypes primitiveUnits)
QImage applyFilter(QSvgNode *referenceNode, const QImage &buffer, QPainter *p, QRectF bounds) const
bool shouldDrawNode(QPainter *p, QSvgExtraStates &states) const override
QSvgG(QSvgNode *parent)
void drawCommand(QPainter *, QSvgExtraStates &) override
Type type() const override
Type type() const override
QSvgMarker(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP, QSvgSymbolLike::PreserveAspectRatios pAspectRatios, QSvgSymbolLike::Overflow overflow, Orientation orientation, qreal orientationAngle, MarkerUnits markerUnits)
static void drawMarkersForNode(QSvgNode *node, QPainter *p, QSvgExtraStates &states)
void drawCommand(QPainter *p, QSvgExtraStates &states) override
QSvgMask(QSvgNode *parent, QSvgRectF bounds, QtSvg::UnitTypes contentsUnits)
bool shouldDrawNode(QPainter *, QSvgExtraStates &) const override
QImage createMask(QPainter *p, QSvgExtraStates &states, QSvgNode *targetNode, QRectF *globalRect) const
Type type() const override
bool hasAnyMarker() const
Definition qsvgnode.cpp:587
QString markerStartId() const
Definition qsvgnode.cpp:536
void revertStyle(QPainter *p, QSvgExtraStates &states) const
Definition qsvgnode.cpp:253
bool hasMarkerMid() const
Definition qsvgnode.cpp:563
bool hasMarkerEnd() const
Definition qsvgnode.cpp:580
virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const
Definition qsvgnode.cpp:338
const QStringList & requiredLanguages() const
Definition qsvgnode.cpp:447
void applyStyle(QPainter *p, QSvgExtraStates &states) const
Definition qsvgnode.cpp:232
bool hasMarkerStart() const
Definition qsvgnode.cpp:546
QString maskId() const
Definition qsvgnode.cpp:502
const QStringList & requiredFonts() const
Definition qsvgnode.cpp:467
const QStringList & requiredFormats() const
Definition qsvgnode.cpp:457
void applyStyleRecursive(QPainter *p, QSvgExtraStates &states) const
Definition qsvgnode.cpp:246
DisplayMode displayMode() const
Definition qsvgnode.cpp:599
void appendStyleProperty(QSvgStyleProperty *prop, const QString &id)
Definition qsvgnode.cpp:172
void draw(QPainter *p, QSvgExtraStates &states)
Definition qsvgnode.cpp:38
QString markerEndId() const
Definition qsvgnode.cpp:570
bool isVisible() const
Definition qsvgnode_p.h:207
static void initPainter(QPainter *p)
Definition qsvgnode.cpp:612
const QStringList & requiredFeatures() const
Definition qsvgnode.cpp:427
QSvgTinyDocument * document() const
Definition qsvgnode.cpp:372
virtual Type type() const =0
bool hasMask() const
Definition qsvgnode.cpp:512
const QStringList & requiredExtensions() const
Definition qsvgnode.cpp:437
const QPainterPath & path() const
bool shouldDrawNode(QPainter *, QSvgExtraStates &) const override
QImage patternImage(QPainter *p, QSvgExtraStates &states, const QSvgNode *patternElement)
Type type() const override
QSvgPattern(QSvgNode *parent, QSvgRectF bounds, QRectF viewBox, QtSvg::UnitTypes contentUnits, QTransform transform)
const QPolygonF & polygon() const
QRectF combinedWithLocalRect(const QRectF &localRect) const
QPointF translationRelativeToBoundingBox(const QRectF &boundingBox) const
QRectF bounds(QPainter *p, QSvgExtraStates &states) const override
QSvgNode * previousSiblingNode(QSvgNode *n) const
void addChild(QSvgNode *child, const QString &id)
QSvgStructureNode(QSvgNode *parent)
QSvgNode * scopeNode(const QString &id) const
QList< QSvgNode * > renderers() const
QList< QSvgNode * > m_renderers
void drawCommand(QPainter *p, QSvgExtraStates &states) override
QSvgSwitch(QSvgNode *parent)
Type type() const override
PreserveAspectRatios m_pAspectRatios
void setPainterToRectAndAdjustment(QPainter *p) const
QSvgSymbolLike(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP, QSvgSymbolLike::PreserveAspectRatios pAspectRatios, QSvgSymbolLike::Overflow overflow)
void drawCommand(QPainter *p, QSvgExtraStates &states) override
Type type() const override
QSvgSymbol(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP, QSvgSymbolLike::PreserveAspectRatios pAspectRatios, QSvgSymbolLike::Overflow overflow)
QSvgNode * namedNode(const QString &id) const
void addNamedNode(const QString &id, QSvgNode *node)
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QTransform & scale(qreal sx, qreal sy)
Scales the coordinate system by sx horizontally and sy vertically, and returns a reference to the mat...
qreal m11() const
Returns the horizontal scaling factor.
Definition qtransform.h:199
void reset()
Resets the matrix to an identity matrix, i.e.
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
qreal m22() const
Returns the vertical scaling factor.
Definition qtransform.h:215
QString str
[2]
QPixmap p2
QPixmap p1
[0]
qDeleteAll(list.begin(), list.end())
static const char defaultPattern[]
EGLint EGLint * formats
constexpr QColor white
Definition qcolor.h:457
constexpr QColor black
Definition qcolor.h:321
Combined button and popup list for selecting options.
@ transparent
Definition qnamespace.h:47
@ black
Definition qnamespace.h:30
@ SvgMiterJoin
@ NoBrush
@ OddEvenFill
@ FlatCap
#define Q_UNLIKELY(x)
bool qIsFinite(qfloat16 f) noexcept
Definition qfloat16.h:285
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
#define qCWarning(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
int qCeil(T v)
Definition qmath.h:36
#define M_PI
Definition qmath.h:209
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLuint const GLuint * buffers
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLenum GLuint buffer
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLfloat n
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei imageSize
GLuint GLenum GLenum transform
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
GLubyte * pattern
GLuint * states
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
constexpr int qRed(QRgb rgb)
Definition qrgb.h:18
constexpr int qGreen(QRgb rgb)
Definition qrgb.h:21
constexpr QRgb qRgba(int r, int g, int b, int a)
Definition qrgb.h:33
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:24
constexpr int qAlpha(QRgb rgb)
Definition qrgb.h:27
#define QStringLiteral(str)
static QImage & defaultPattern()
static bool isSupportedSvgExtension(const QString &)
static bool isSupportedSvgFeature(const QString &str)
#define t2
double qreal
Definition qtypes.h:187
QGraphicsItem * item
QLayoutItem * child
[0]
QPainter painter(this)
[7]
QStringList wordList
[0]
QNetworkProxy proxy
[0]
QSvgRenderer * renderer
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18