19#include <QLoggingCategory>
20#include <qscopedvaluerollback.h>
21#include <QtGui/qimageiohandler.h>
23#include <QtCore/qlatin1stringview.h>
24#include <QtCore/qstringview.h>
25#include <QtCore/private/qoffsetstringarray_p.h>
29using namespace Qt::StringLiterals;
31QSvgG::QSvgG(QSvgNode *parent)
32 : QSvgStructureNode(parent)
40void QSvgG::drawCommand(QPainter *p, QSvgExtraStates &states)
42 for (
const auto &node : renderers()) {
43 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
44 node->draw(p, states);
48bool QSvgG::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
53QSvgNode::Type QSvgG::type()
const
58bool QSvgG::requiresGroupRendering()
const
60 return m_renderers.size() > 1;
63QSvgStructureNode::QSvgStructureNode(QSvgNode *parent)
69QSvgStructureNode::~QSvgStructureNode()
72QSvgNode * QSvgStructureNode::scopeNode(
const QString &id)
const
74 QSvgDocument *doc = document();
75 return doc ? doc->namedNode(id) : 0;
78void QSvgStructureNode::addChild(std::unique_ptr<QSvgNode> child,
const QString &id)
81 QSvgDocument *doc = document();
83 doc->addNamedNode(id, child.get());
86 m_renderers.push_back(std::move(child));
89QSvgDefs::QSvgDefs(QSvgNode *parent)
90 : QSvgStructureNode(parent)
97bool QSvgDefs::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
102QSvgNode::Type QSvgDefs::type()
const
107QSvgSymbolLike::QSvgSymbolLike(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
108 QSvgSymbolLike::PreserveAspectRatios pAspectRatios, QSvgSymbolLike::Overflow overflow)
109 : QSvgStructureNode(parent)
113 , m_pAspectRatios(pAspectRatios)
114 , m_overflow(overflow)
119QSvgSymbolLike::~QSvgSymbolLike()
122QRectF QSvgSymbolLike::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states)
const
125 setPainterToRectAndAdjustment(p);
126 QRectF rect = internalBounds(p, states);
131bool QSvgSymbolLike::requiresGroupRendering()
const
133 return m_renderers.size() > 1;
136QRectF QSvgSymbolLike::clipRect()
const
138 if (m_overflow != Overflow::Hidden || !m_viewBox.isValid())
142 if (m_rect.width() > 0 && m_viewBox.width() > 0)
143 scaleX = m_rect.width() / m_viewBox.width();
145 if (m_rect.height() > 0 && m_viewBox.height() > 0)
146 scaleY = m_rect.height() / m_viewBox.height();
149 t.translate(- m_refP.x() * scaleX - m_rect.left() - m_viewBox.left() * scaleX,
150 - m_refP.y() * scaleY - m_rect.top() - m_viewBox.top() * scaleY);
151 t.scale(scaleX, scaleY);
153 return t.mapRect(m_viewBox);
156QTransform QSvgSymbolLike::aspectRatioTransform()
const
164 if (m_rect.width() > 0 && m_viewBox.width() > 0)
165 scaleX = m_rect.width() / m_viewBox.width();
167 if (m_rect.height() > 0 && m_viewBox.height() > 0)
168 scaleY = m_rect.height() / m_viewBox.height();
170 if (!qFuzzyCompare(scaleX, scaleY) &&
171 m_pAspectRatios.testAnyFlag(PreserveAspectRatio::xyMask)) {
173 if (m_pAspectRatios.testAnyFlag(PreserveAspectRatio::meet))
174 scaleX = scaleY = qMin(scaleX, scaleY);
176 scaleX = scaleY = qMax(scaleX, scaleY);
178 qreal xOverflow = scaleX * m_viewBox.width() - m_rect.width();
179 qreal yOverflow = scaleY * m_viewBox.height() - m_rect.height();
181 if ((m_pAspectRatios & PreserveAspectRatio::xMask) == PreserveAspectRatio::xMid)
182 offsetX -= xOverflow / 2.;
183 else if ((m_pAspectRatios & PreserveAspectRatio::xMask) == PreserveAspectRatio::xMax)
184 offsetX -= xOverflow;
186 if ((m_pAspectRatios & PreserveAspectRatio::yMask) == PreserveAspectRatio::yMid)
187 offsetY -= yOverflow / 2.;
188 else if ((m_pAspectRatios & PreserveAspectRatio::yMask) == PreserveAspectRatio::yMax)
189 offsetY -= yOverflow;
192 xform.translate(offsetX - m_refP.x() * scaleX, offsetY - m_refP.y() * scaleY);
193 xform.scale(scaleX, scaleY);
198void QSvgSymbolLike::setPainterToRectAndAdjustment(QPainter *p)
const
200 QRectF clip = clipRect();
202 p->setClipRect(clip);
204 p->setTransform(aspectRatioTransform(),
true);
207QSvgSymbol::QSvgSymbol(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
208 QSvgSymbol::PreserveAspectRatios pAspectRatios,
209 QSvgSymbol::Overflow overflow)
210 : QSvgSymbolLike(parent, bounds, viewBox, refP, pAspectRatios, overflow)
214QSvgSymbol::~QSvgSymbol()
217void QSvgSymbol::drawCommand(QPainter *p, QSvgExtraStates &states)
223 setPainterToRectAndAdjustment(p);
224 for (
const auto &node : renderers()) {
225 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
226 node->draw(p, states);
231QSvgNode::Type QSvgSymbol::type()
const
236QSvgMarker::QSvgMarker(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
237 QSvgSymbol::PreserveAspectRatios pAspectRatios, QSvgSymbol::Overflow overflow,
238 Orientation orientation, qreal orientationAngle, MarkerUnits markerUnits)
239 : QSvgSymbolLike(parent, bounds, viewBox, refP, pAspectRatios, overflow)
240 , m_orientation(orientation)
241 , m_orientationAngle(orientationAngle)
242 , m_markerUnits(markerUnits)
245 QSvgFillStyle *fillProp =
new QSvgFillStyle();
246 fillProp->setBrush(Qt::black);
247 appendStyleProperty(fillProp);
249 QSvgStrokeStyle *strokeProp =
new QSvgStrokeStyle();
250 strokeProp->setMiterLimit(4);
251 strokeProp->setWidth(1);
252 strokeProp->setLineCap(Qt::FlatCap);
253 strokeProp->setLineJoin(Qt::SvgMiterJoin);
254 strokeProp->setStroke(Qt::NoBrush);
255 appendStyleProperty(strokeProp);
258QSvgMarker::~QSvgMarker()
261QSvgFilterContainer::QSvgFilterContainer(QSvgNode *parent,
const QSvgRectF &bounds,
262 QtSvg::UnitTypes filterUnits, QtSvg::UnitTypes primitiveUnits)
263 : QSvgStructureNode(parent)
265 , m_filterUnits(filterUnits)
266 , m_primitiveUnits(primitiveUnits)
272QSvgFilterContainer::~QSvgFilterContainer()
275bool QSvgFilterContainer::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
280void QSvgMarker::drawCommand(QPainter *p, QSvgExtraStates &states)
285 if (Q_UNLIKELY(m_recursing))
287 QScopedValueRollback<
bool> recursingGuard(m_recursing,
true);
290 setPainterToRectAndAdjustment(p);
292 for (
const auto &node : renderers()) {
293 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
294 node->draw(p, states);
301struct PositionMarkerPair {
306 bool isStartNode =
false;
309QList<PositionMarkerPair> markersForNode(
const QSvgNode *node)
311 if (!node->hasAnyMarker())
314 auto getMeanAngle = [](QPointF p0, QPointF p1, QPointF p2) -> qreal {
315 QPointF t1 = p1 - p0;
316 QPointF t2 = p2 - p1;
317 qreal hyp1 = hypot(t1.x(), t1.y());
322 qreal hyp2 = hypot(t2.x(), t2.y());
327 QPointF tangent = t1 + t2;
328 return -atan2(tangent.y(), tangent.x()) / M_PI * 180.;
331 QList<PositionMarkerPair> markers;
333 switch (node->type()) {
334 case QSvgNode::Line: {
335 const QSvgLine *line =
static_cast<
const QSvgLine*>(node);
336 if (node->hasMarkerStart())
337 markers << PositionMarkerPair { line->line().p1().x(), line->line().p1().y(),
338 line->line().angle(), line->markerStartId(),
340 if (node->hasMarkerEnd())
341 markers << PositionMarkerPair { line->line().p2().x(), line->line().p2().y(),
342 line->line().angle(), line->markerEndId() };
345 case QSvgNode::Polyline:
346 case QSvgNode::Polygon: {
347 const QPolygonF &polyData = (node->type() == QSvgNode::Polyline)
348 ?
static_cast<
const QSvgPolyline*>(node)->polygon()
349 :
static_cast<
const QSvgPolygon*>(node)->polygon();
351 if (node->hasMarkerStart() && polyData.size() > 1) {
352 QLineF line(polyData.at(0), polyData.at(1));
353 markers << PositionMarkerPair { line.p1().x(),
356 node->markerStartId(),
359 if (node->hasMarkerMid()) {
360 for (
int i = 1; i < polyData.size() - 1; i++) {
361 QPointF p0 = polyData.at(i - 1);
362 QPointF p1 = polyData.at(i);
363 QPointF p2 = polyData.at(i + 1);
365 markers << PositionMarkerPair { p1.x(),
367 getMeanAngle(p0, p1, p2),
368 node->markerStartId() };
371 if (node->hasMarkerEnd() && polyData.size() > 1) {
372 QLineF line(polyData.at(polyData.size() - 1), polyData.last());
373 markers << PositionMarkerPair { line.p2().x(),
376 node->markerEndId() };
380 case QSvgNode::Path: {
381 const QSvgPath *path =
static_cast<
const QSvgPath*>(node);
382 if (node->hasMarkerStart())
383 markers << PositionMarkerPair { path->path().pointAtPercent(0.).x(),
384 path->path().pointAtPercent(0.).y(),
385 path->path().angleAtPercent(0.),
386 path->markerStartId(),
388 if (node->hasMarkerMid()) {
389 for (
int i = 1; i < path->path().elementCount() - 1; i++) {
390 if (path->path().elementAt(i).type == QPainterPath::MoveToElement)
392 if (path->path().elementAt(i).type == QPainterPath::CurveToElement)
394 if (( path->path().elementAt(i).type == QPainterPath::CurveToDataElement &&
395 path->path().elementAt(i + 1).type != QPainterPath::CurveToDataElement ) ||
396 path->path().elementAt(i).type == QPainterPath::LineToElement) {
398 QPointF p0(path->path().elementAt(i - 1).x, path->path().elementAt(i - 1).y);
399 QPointF p1(path->path().elementAt(i).x, path->path().elementAt(i).y);
400 QPointF p2(path->path().elementAt(i + 1).x, path->path().elementAt(i + 1).y);
402 markers << PositionMarkerPair { p1.x(),
404 getMeanAngle(p0, p1, p2),
405 path->markerMidId() };
409 if (node->hasMarkerEnd())
410 markers << PositionMarkerPair { path->path().pointAtPercent(1.).x(),
411 path->path().pointAtPercent(1.).y(),
412 path->path().angleAtPercent(1.),
413 path->markerEndId() };
427void QSvgMarker::drawMarkersForNode(QSvgNode *node, QPainter *p, QSvgExtraStates &states)
429 drawHelper(node, p, states);
432QRectF QSvgMarker::markersBoundsForNode(
const QSvgNode *node, QPainter *p, QSvgExtraStates &states)
435 drawHelper(node, p, states, &bounds);
439QSvgNode::Type QSvgMarker::type()
const
444void QSvgMarker::drawHelper(
const QSvgNode *node, QPainter *p,
445 QSvgExtraStates &states, QRectF *boundingRect)
447 QScopedValueRollback<
bool> inUseGuard(states.inUse,
true);
449 const bool isPainting = (boundingRect ==
nullptr);
450 const auto markers = markersForNode(node);
451 for (
auto &i : markers) {
452 QSvgNode *referencedNode = node->document()->namedNode(i.markerId);
453 if (!referencedNode || referencedNode->type() != QSvgNode::Marker)
455 QSvgMarker *markNode =
static_cast<QSvgMarker *>(referencedNode);
458 p->translate(i.x, i.y);
459 if (markNode->orientation() == QSvgMarker::Orientation::Value) {
460 p->rotate(markNode->orientationAngle());
463 if (i.isStartNode && markNode->orientation()
464 == QSvgMarker::Orientation::AutoStartReverse) {
468 QRectF oldRect = markNode->m_rect;
469 if (markNode->markerUnits() == QSvgMarker::MarkerUnits::StrokeWidth) {
470 markNode->m_rect.setWidth(markNode->m_rect.width() * p->pen().widthF());
471 markNode->m_rect.setHeight(markNode->m_rect.height() * p->pen().widthF());
474 markNode->draw(p, states);
477 QTransform xf = p->transform();
479 *boundingRect |= xf.mapRect(markNode->decoratedInternalBounds(p, states));
482 markNode->m_rect = oldRect;
487QImage QSvgFilterContainer::applyFilter(
const QImage &buffer, QPainter *p,
const QRectF &bounds)
const
489 QRectF localFilterRegion = m_rect.resolveRelativeLengths(bounds, m_filterUnits);
490 QRect globalFilterRegion = p->transform().mapRect(localFilterRegion).toRect();
491 QRect globalFilterRegionRel = globalFilterRegion.translated(-buffer.offset());
493 if (globalFilterRegionRel.isEmpty())
497 if (!QImageIOHandler::allocateImage(globalFilterRegionRel.size(), buffer.format(), &proxy)) {
498 qCWarning(lcSvgDraw) <<
"The requested filter is too big, ignoring";
501 proxy = buffer.copy(globalFilterRegionRel);
502 proxy.setOffset(globalFilterRegion.topLeft());
506 QMap<QString, QImage> buffers;
510 bool requiresSourceAlpha =
false;
512 for (
const auto &node : renderers()) {
513 const QSvgFeFilterPrimitive *filter = QSvgFeFilterPrimitive::castToFilterPrimitive(node.get());
514 if (filter && filter->requiresSourceAlpha()) {
515 requiresSourceAlpha =
true;
520 if (requiresSourceAlpha) {
521 QImage proxyAlpha = proxy.convertedTo(QImage::Format_Alpha8).convertedTo(proxy.format());
522 proxyAlpha.setOffset(proxy.offset());
523 if (proxyAlpha.isNull())
529 for (
const auto &node : renderers()) {
530 const QSvgFeFilterPrimitive *filter = QSvgFeFilterPrimitive::castToFilterPrimitive(node.get());
532 result = filter->apply(buffers, p, bounds, localFilterRegion, m_primitiveUnits, m_filterUnits);
533 if (!result.isNull()) {
535 buffers[filter->result()] = result;
542void QSvgFilterContainer::setSupported(
bool supported)
544 m_supported = supported;
547bool QSvgFilterContainer::supported()
const
552QRectF QSvgFilterContainer::filterRegion(
const QRectF &itemBounds)
const
554 return m_rect.resolveRelativeLengths(itemBounds, m_filterUnits);
557QSvgNode::Type QSvgFilterContainer::type()
const
565 constexpr auto prefix_1_2 =
"http://www.w3.org/Graphics/SVG/feature/1.2/#"_L1;
566 if (str.startsWith(prefix_1_2)) {
567 const auto suffix = str.sliced(prefix_1_2.size());
569 constexpr auto features = qOffsetStringArray(
583 "ConditionalProcessing",
588 "ConditionalProcessingAttribute",
589 "ExternalResourcesRequiredAttribute"
595 for (
int i = 0; i < features.count(); ++i)
596 if (suffix == QLatin1StringView{features.at(i)})
609QSvgSwitch::QSvgSwitch(QSvgNode *parent)
610 : QSvgStructureNode(parent)
615QSvgSwitch::~QSvgSwitch()
618QSvgNode *QSvgSwitch::childToRender()
const
620 for (
const auto &node : renderers()) {
621 if (node->isVisible() && (node->displayMode() != QSvgNode::NoneMode)) {
622 const QStringList &features = node->requiredFeatures();
623 const QStringList &extensions = node->requiredExtensions();
624 const QStringList &languages = node->requiredLanguages();
625 const QStringList &formats = node->requiredFormats();
626 const QStringList &fonts = node->requiredFonts();
628 bool okToRender =
true;
629 if (!features.isEmpty()) {
630 QStringList::const_iterator sitr = features.constBegin();
631 for (; sitr != features.constEnd(); ++sitr) {
632 if (!isSupportedSvgFeature(*sitr)) {
639 if (okToRender && !extensions.isEmpty()) {
640 QStringList::const_iterator sitr = extensions.constBegin();
641 for (; sitr != extensions.constEnd(); ++sitr) {
642 if (!isSupportedSvgExtension(*sitr)) {
649 if (okToRender && !languages.isEmpty()) {
650 QStringList::const_iterator sitr = languages.constBegin();
652 for (; sitr != languages.constEnd(); ++sitr) {
653 if ((*sitr).startsWith(m_systemLanguagePrefix)) {
660 if (okToRender && !formats.isEmpty())
663 if (okToRender && !fonts.isEmpty())
674void QSvgSwitch::drawCommand(QPainter *p, QSvgExtraStates &states)
676 QSvgNode *node = childToRender();
678 node->draw(p, states);
681QSvgNode::Type QSvgSwitch::type()
const
686void QSvgSwitch::init()
689 m_systemLanguage = locale.name().replace(QLatin1Char(
'_'), QLatin1Char(
'-'));
690 int idx = m_systemLanguage.indexOf(QLatin1Char(
'-'));
691 m_systemLanguagePrefix = m_systemLanguage.mid(0, idx);
694QRectF QSvgStructureNode::internalBounds(QPainter *p, QSvgExtraStates &states)
const
698 QScopedValueRollback<
bool> guard(m_recursing,
true);
699 for (
const auto &node : renderers())
700 bounds |= node->bounds(p, states);
705QRectF QSvgStructureNode::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states)
const
709 QScopedValueRollback<
bool> guard(m_recursing,
true);
710 for (
const auto &node : renderers())
711 bounds |= node->decoratedBounds(p, states);
716QSvgNode* QSvgStructureNode::previousSiblingNode(QSvgNode *n)
const
718 QSvgNode *prev =
nullptr;
719 for (
const auto &node : renderers()) {
727void QSvgStructureNode::releaseDescendants()
734 while (!m_renderers.empty()) {
735 auto nodes = &m_renderers;
736 bool isSubtree =
true;
738 switch (nodes->front()->type()) {
741 case QSvgNode::Group:
743 case QSvgNode::Pattern:
744 case QSvgNode::Symbol:
745 case QSvgNode::Switch:
746 case QSvgNode::Filter:
748 QSvgStructureNode *subtree =
static_cast<QSvgStructureNode *>(nodes->front().get());
749 isSubtree = !subtree->m_renderers.empty();
751 nodes = &subtree->m_renderers;
763QSvgMask::QSvgMask(QSvgNode *parent, QSvgRectF bounds,
764 QtSvg::UnitTypes contentUnits)
765 : QSvgStructureNode(parent)
767 , m_contentUnits(contentUnits)
774bool QSvgMask::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
779QImage QSvgMask::createMask(QPainter *p, QSvgExtraStates &states, QSvgNode *targetNode, QRectF *globalRect)
const
781 QTransform t = p->transform();
783 QRectF basicRect = targetNode->internalBounds(p, states);
784 *globalRect = t.mapRect(basicRect);
786 return createMask(p, states, basicRect, globalRect);
789QImage QSvgMask::createMask(QPainter *p, QSvgExtraStates &states,
const QRectF &localRect, QRectF *globalRect)
const
791 QRect imageBound = globalRect->toAlignedRect();
792 *globalRect = imageBound.toRectF();
795 if (!QImageIOHandler::allocateImage(imageBound.size(), QImage::Format_RGBA8888, &mask)) {
796 qCWarning(lcSvgDraw) <<
"The requested mask size is too big, ignoring";
800 if (Q_UNLIKELY(m_recursing))
802 QScopedValueRollback<
bool> recursingGuard(m_recursing,
true);
805 if (
this->hasMask()) {
806 QSvgNode *referencedNode = document()->namedNode(
this->maskId());
807 if (referencedNode && referencedNode->type() == QSvgNode::Mask) {
808 QSvgMask *maskNode =
static_cast<QSvgMask *>(referencedNode);
810 return maskNode->createMask(p, states, localRect, &boundsRect);
820 mask.fill(Qt::transparent);
821 QPainter painter(&mask);
822 initPainter(&painter);
824 QSvgExtraStates maskNodeStates;
825 applyStyleRecursive(&painter, maskNodeStates);
829 painter.resetTransform();
830 painter.translate(-imageBound.topLeft());
831 painter.setTransform(p->transform(),
true);
833 QTransform oldT = painter.transform();
834 if (m_contentUnits == QtSvg::UnitTypes::objectBoundingBox){
835 painter.translate(localRect.topLeft());
836 painter.scale(localRect.width(), localRect.height());
840 for (
const auto &node : renderers())
842 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
843 node->draw(&painter, maskNodeStates);
846 for (
int i=0; i < mask.height(); i++) {
847 QRgb *line =
reinterpret_cast<QRgb *>(mask.scanLine(i));
848 for (
int j=0; j < mask.width(); j++) {
849 const qreal rC = 0.2125, gC = 0.7154, bC = 0.0721;
850 int alpha = 255 - (qRed(line[j]) * rC + qGreen(line[j]) * gC + qBlue(line[j]) * bC) * qAlpha(line[j])/255.;
851 line[j] = qRgba(0, 0, 0, alpha);
859 QRectF clipRect = m_rect.resolveRelativeLengths(localRect);
860 QPainterPath clipPath;
861 clipPath.setFillRule(Qt::OddEvenFill);
862 clipPath.addRect(mask.rect().adjusted(-10, -10, 20, 20));
863 clipPath.addPolygon(oldT.map(QPolygonF(clipRect)));
864 painter.resetTransform();
865 painter.fillPath(clipPath, Qt::black);
866 revertStyleRecursive(&painter, maskNodeStates);
870QSvgNode::Type QSvgMask::type()
const
875QSvgPattern::QSvgPattern(QSvgNode *parent, QSvgRectF bounds, QRectF viewBox,
876 QtSvg::UnitTypes contentUnits, QTransform transform)
877 : QSvgStructureNode(parent),
880 m_contentUnits(contentUnits),
881 m_isRendering(
false),
882 m_transform(transform)
888QSvgPattern::~QSvgPattern()
891bool QSvgPattern::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
898 static QImage checkerPattern;
900 if (checkerPattern.isNull()) {
901 checkerPattern = QImage(QSize(8, 8), QImage::Format_ARGB32);
903 p.fillRect(QRect(0, 0, 4, 4), QColorConstants::Svg::white);
904 p.fillRect(QRect(4, 0, 4, 4), QColorConstants::Svg::black);
905 p.fillRect(QRect(0, 4, 4, 4), QColorConstants::Svg::black);
906 p.fillRect(QRect(4, 4, 4, 4), QColorConstants::Svg::white);
909 return checkerPattern;
912QImage QSvgPattern::patternImage(QPainter *p, QSvgExtraStates &states,
const QSvgNode *patternElement)
915 QRectF peBoundingBox;
916 QRectF peWorldBoundingBox;
918 QTransform t = p->transform();
920 peBoundingBox = patternElement->internalBounds(p, states);
921 peWorldBoundingBox = t.mapRect(peBoundingBox);
931 qreal contentScaleFactorX = m_transform.m11();
932 qreal contentScaleFactorY = m_transform.m22();
933 if (m_contentUnits == QtSvg::UnitTypes::userSpaceOnUse) {
934 contentScaleFactorX *= t.m11();
935 contentScaleFactorY *= t.m22();
937 contentScaleFactorX *= peWorldBoundingBox.width();
938 contentScaleFactorY *= peWorldBoundingBox.height();
942 QRectF patternBoundingBox = m_rect.resolveRelativeLengths(peBoundingBox);
945 imageSize.setWidth(qCeil(patternBoundingBox.width() * t.m11() * m_transform.m11()));
946 imageSize.setHeight(qCeil(patternBoundingBox.height() * t.m22() * m_transform.m22()));
947 if (imageSize.isEmpty())
950 calculateAppliedTransform(t, peBoundingBox, imageSize);
951 if (document()->isCalculatingImplicitViewBox())
952 return QImage(imageSize, QImage::Format_ARGB32);
954 return renderPattern(imageSize, contentScaleFactorX, contentScaleFactorY);
957QSvgNode::Type QSvgPattern::type()
const
962QImage QSvgPattern::renderPattern(QSize size, qreal contentScaleX, qreal contentScaleY)
964 if (size.isEmpty() || !qIsFinite(contentScaleX) || !qIsFinite(contentScaleY))
965 return defaultPattern();
969 if (!QImageIOHandler::allocateImage(size, QImage::Format_ARGB32, &pattern)) {
970 qCWarning(lcSvgDraw) <<
"The requested pattern size is too big, ignoring";
971 return defaultPattern();
973 pattern.fill(Qt::transparent);
976 qCWarning(lcSvgDraw) <<
"The pattern is trying to render itself recursively. "
977 "Returning a transparent QImage of the right size.";
980 QScopedValueRollback<
bool> guard(m_isRendering,
true);
983 QPainter patternPainter(&pattern);
984 QSvgExtraStates patternStates;
985 initPainter(&patternPainter);
986 applyStyleRecursive(&patternPainter, patternStates);
987 patternPainter.resetTransform();
991 if (m_viewBox.isNull())
992 patternPainter.scale(contentScaleX, contentScaleY);
994 patternPainter.setWindow(m_viewBox.toRect());
998 for (
const auto &node : renderers())
999 node->draw(&patternPainter, patternStates);
1001 revertStyleRecursive(&patternPainter, patternStates);
1005void QSvgPattern::calculateAppliedTransform(QTransform &worldTransform, QRectF peLocalBB, QSize imageSize)
1020 m_appliedTransform.reset();
1021 qreal imageDownScaleFactorX = 1 / worldTransform.m11();
1022 qreal imageDownScaleFactorY = 1 / worldTransform.m22();
1024 m_appliedTransform.scale(qIsFinite(imageDownScaleFactorX) ? imageDownScaleFactorX : 1.0,
1025 qIsFinite(imageDownScaleFactorY) ? imageDownScaleFactorY : 1.0);
1027 QRectF p = m_rect.resolveRelativeLengths(peLocalBB);
1028 m_appliedTransform.scale((p.width() * worldTransform.m11() * m_transform.m11()) / imageSize.width(),
1029 (p.height() * worldTransform.m22() * m_transform.m22()) / imageSize.height());
1031 QPointF translation = m_rect.translationRelativeToBoundingBox(peLocalBB);
1032 m_appliedTransform.translate(translation.x() * worldTransform.m11(), translation.y() * worldTransform.m22());
1034 QTransform scalelessTransform = m_transform;
1035 scalelessTransform.scale(1 / m_transform.m11(), 1 / m_transform.m22());
1037 m_appliedTransform = m_appliedTransform * scalelessTransform;
The QPolygonF class provides a list of points using floating point precision.
Combined button and popup list for selecting options.
#define QStringLiteral(str)
static QImage & defaultPattern()
static bool isSupportedSvgExtension(const QString &)
static bool isSupportedSvgFeature(QStringView str)