19#include <QLoggingCategory>
20#include <qscopedvaluerollback.h>
21#include <QtGui/qimageiohandler.h>
25QSvgG::QSvgG(QSvgNode *parent)
26 : QSvgStructureNode(parent)
31QSvgStructureNode::~QSvgStructureNode()
35void QSvgG::drawCommand(QPainter *p, QSvgExtraStates &states)
37 for (
const auto &node : renderers()) {
38 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
39 node->draw(p, states);
43bool QSvgG::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
48QSvgNode::Type QSvgG::type()
const
53bool QSvgG::requiresGroupRendering()
const
55 return m_renderers.size() > 1;
58QSvgStructureNode::QSvgStructureNode(QSvgNode *parent)
64QSvgNode * QSvgStructureNode::scopeNode(
const QString &id)
const
66 QSvgDocument *doc = document();
67 return doc ? doc->namedNode(id) : 0;
70void QSvgStructureNode::addChild(std::unique_ptr<QSvgNode> child,
const QString &id)
73 QSvgDocument *doc = document();
75 doc->addNamedNode(id, child.get());
78 m_renderers.push_back(std::move(child));
81QSvgDefs::QSvgDefs(QSvgNode *parent)
82 : QSvgStructureNode(parent)
86bool QSvgDefs::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
91QSvgNode::Type QSvgDefs::type()
const
96QSvgSymbolLike::QSvgSymbolLike(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
97 QSvgSymbolLike::PreserveAspectRatios pAspectRatios, QSvgSymbolLike::Overflow overflow)
98 : QSvgStructureNode(parent)
102 , m_pAspectRatios(pAspectRatios)
103 , m_overflow(overflow)
108QRectF QSvgSymbolLike::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states)
const
111 setPainterToRectAndAdjustment(p);
112 QRectF rect = internalBounds(p, states);
117bool QSvgSymbolLike::requiresGroupRendering()
const
119 return m_renderers.size() > 1;
122QRectF QSvgSymbolLike::clipRect()
const
124 if (m_overflow != Overflow::Hidden || !m_viewBox.isValid())
128 if (m_rect.width() > 0 && m_viewBox.width() > 0)
129 scaleX = m_rect.width() / m_viewBox.width();
131 if (m_rect.height() > 0 && m_viewBox.height() > 0)
132 scaleY = m_rect.height() / m_viewBox.height();
135 t.translate(- m_refP.x() * scaleX - m_rect.left() - m_viewBox.left() * scaleX,
136 - m_refP.y() * scaleY - m_rect.top() - m_viewBox.top() * scaleY);
137 t.scale(scaleX, scaleY);
139 return t.mapRect(m_viewBox);
142QTransform QSvgSymbolLike::aspectRatioTransform()
const
150 if (m_rect.width() > 0 && m_viewBox.width() > 0)
151 scaleX = m_rect.width() / m_viewBox.width();
153 if (m_rect.height() > 0 && m_viewBox.height() > 0)
154 scaleY = m_rect.height() / m_viewBox.height();
156 if (!qFuzzyCompare(scaleX, scaleY) &&
157 m_pAspectRatios.testAnyFlag(PreserveAspectRatio::xyMask)) {
159 if (m_pAspectRatios.testAnyFlag(PreserveAspectRatio::meet))
160 scaleX = scaleY = qMin(scaleX, scaleY);
162 scaleX = scaleY = qMax(scaleX, scaleY);
164 qreal xOverflow = scaleX * m_viewBox.width() - m_rect.width();
165 qreal yOverflow = scaleY * m_viewBox.height() - m_rect.height();
167 if ((m_pAspectRatios & PreserveAspectRatio::xMask) == PreserveAspectRatio::xMid)
168 offsetX -= xOverflow / 2.;
169 else if ((m_pAspectRatios & PreserveAspectRatio::xMask) == PreserveAspectRatio::xMax)
170 offsetX -= xOverflow;
172 if ((m_pAspectRatios & PreserveAspectRatio::yMask) == PreserveAspectRatio::yMid)
173 offsetY -= yOverflow / 2.;
174 else if ((m_pAspectRatios & PreserveAspectRatio::yMask) == PreserveAspectRatio::yMax)
175 offsetY -= yOverflow;
178 xform.translate(offsetX - m_refP.x() * scaleX, offsetY - m_refP.y() * scaleY);
179 xform.scale(scaleX, scaleY);
184void QSvgSymbolLike::setPainterToRectAndAdjustment(QPainter *p)
const
186 QRectF clip = clipRect();
188 p->setClipRect(clip);
190 p->setTransform(aspectRatioTransform(),
true);
193QSvgSymbol::QSvgSymbol(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
194 QSvgSymbol::PreserveAspectRatios pAspectRatios,
195 QSvgSymbol::Overflow overflow)
196 : QSvgSymbolLike(parent, bounds, viewBox, refP, pAspectRatios, overflow)
200void QSvgSymbol::drawCommand(QPainter *p, QSvgExtraStates &states)
206 setPainterToRectAndAdjustment(p);
207 for (
const auto &node : renderers()) {
208 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
209 node->draw(p, states);
214QSvgNode::Type QSvgSymbol::type()
const
219QSvgMarker::QSvgMarker(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
220 QSvgSymbol::PreserveAspectRatios pAspectRatios, QSvgSymbol::Overflow overflow,
221 Orientation orientation, qreal orientationAngle, MarkerUnits markerUnits)
222 : QSvgSymbolLike(parent, bounds, viewBox, refP, pAspectRatios, overflow)
223 , m_orientation(orientation)
224 , m_orientationAngle(orientationAngle)
225 , m_markerUnits(markerUnits)
228 QSvgFillStyle *fillProp =
new QSvgFillStyle();
229 fillProp->setBrush(Qt::black);
232 QSvgStrokeStyle *strokeProp =
new QSvgStrokeStyle();
233 strokeProp->setMiterLimit(4);
234 strokeProp->setWidth(1);
235 strokeProp->setLineCap(Qt::FlatCap);
236 strokeProp->setLineJoin(Qt::SvgMiterJoin);
237 strokeProp->setStroke(Qt::NoBrush);
241QSvgFilterContainer::QSvgFilterContainer(QSvgNode *parent,
const QSvgRectF &bounds,
242 QtSvg::UnitTypes filterUnits, QtSvg::UnitTypes primitiveUnits)
243 : QSvgStructureNode(parent)
245 , m_filterUnits(filterUnits)
246 , m_primitiveUnits(primitiveUnits)
252bool QSvgFilterContainer::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
257void QSvgMarker::drawCommand(QPainter *p, QSvgExtraStates &states)
262 if (Q_UNLIKELY(m_recursing))
264 QScopedValueRollback<
bool> recursingGuard(m_recursing,
true);
267 setPainterToRectAndAdjustment(p);
269 for (
const auto &node : renderers()) {
270 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
271 node->draw(p, states);
278struct PositionMarkerPair {
283 bool isStartNode =
false;
286QList<PositionMarkerPair> markersForNode(
const QSvgNode *node)
288 if (!node->hasAnyMarker())
291 auto getMeanAngle = [](QPointF p0, QPointF p1, QPointF p2) -> qreal {
292 QPointF t1 = p1 - p0;
293 QPointF t2 = p2 - p1;
294 qreal hyp1 = hypot(t1.x(), t1.y());
299 qreal hyp2 = hypot(t2.x(), t2.y());
304 QPointF tangent = t1 + t2;
305 return -atan2(tangent.y(), tangent.x()) / M_PI * 180.;
308 QList<PositionMarkerPair> markers;
310 switch (node->type()) {
311 case QSvgNode::Line: {
312 const QSvgLine *line =
static_cast<
const QSvgLine*>(node);
313 if (node->hasMarkerStart())
314 markers << PositionMarkerPair { line->line().p1().x(), line->line().p1().y(),
315 line->line().angle(), line->markerStartId(),
317 if (node->hasMarkerEnd())
318 markers << PositionMarkerPair { line->line().p2().x(), line->line().p2().y(),
319 line->line().angle(), line->markerEndId() };
322 case QSvgNode::Polyline:
323 case QSvgNode::Polygon: {
324 const QPolygonF &polyData = (node->type() == QSvgNode::Polyline)
325 ?
static_cast<
const QSvgPolyline*>(node)->polygon()
326 :
static_cast<
const QSvgPolygon*>(node)->polygon();
328 if (node->hasMarkerStart() && polyData.size() > 1) {
329 QLineF line(polyData.at(0), polyData.at(1));
330 markers << PositionMarkerPair { line.p1().x(),
333 node->markerStartId(),
336 if (node->hasMarkerMid()) {
337 for (
int i = 1; i < polyData.size() - 1; i++) {
338 QPointF p0 = polyData.at(i - 1);
339 QPointF p1 = polyData.at(i);
340 QPointF p2 = polyData.at(i + 1);
342 markers << PositionMarkerPair { p1.x(),
344 getMeanAngle(p0, p1, p2),
345 node->markerStartId() };
348 if (node->hasMarkerEnd() && polyData.size() > 1) {
349 QLineF line(polyData.at(polyData.size() - 1), polyData.last());
350 markers << PositionMarkerPair { line.p2().x(),
353 node->markerEndId() };
357 case QSvgNode::Path: {
358 const QSvgPath *path =
static_cast<
const QSvgPath*>(node);
359 if (node->hasMarkerStart())
360 markers << PositionMarkerPair { path->path().pointAtPercent(0.).x(),
361 path->path().pointAtPercent(0.).y(),
362 path->path().angleAtPercent(0.),
363 path->markerStartId(),
365 if (node->hasMarkerMid()) {
366 for (
int i = 1; i < path->path().elementCount() - 1; i++) {
367 if (path->path().elementAt(i).type == QPainterPath::MoveToElement)
369 if (path->path().elementAt(i).type == QPainterPath::CurveToElement)
371 if (( path->path().elementAt(i).type == QPainterPath::CurveToDataElement &&
372 path->path().elementAt(i + 1).type != QPainterPath::CurveToDataElement ) ||
373 path->path().elementAt(i).type == QPainterPath::LineToElement) {
375 QPointF p0(path->path().elementAt(i - 1).x, path->path().elementAt(i - 1).y);
376 QPointF p1(path->path().elementAt(i).x, path->path().elementAt(i).y);
377 QPointF p2(path->path().elementAt(i + 1).x, path->path().elementAt(i + 1).y);
379 markers << PositionMarkerPair { p1.x(),
381 getMeanAngle(p0, p1, p2),
382 path->markerMidId() };
386 if (node->hasMarkerEnd())
387 markers << PositionMarkerPair { path->path().pointAtPercent(1.).x(),
388 path->path().pointAtPercent(1.).y(),
389 path->path().angleAtPercent(1.),
390 path->markerEndId() };
404void QSvgMarker::drawMarkersForNode(QSvgNode *node, QPainter *p, QSvgExtraStates &states)
406 drawHelper(node, p, states);
409QRectF QSvgMarker::markersBoundsForNode(
const QSvgNode *node, QPainter *p, QSvgExtraStates &states)
412 drawHelper(node, p, states, &bounds);
416QSvgNode::Type QSvgMarker::type()
const
421void QSvgMarker::drawHelper(
const QSvgNode *node, QPainter *p,
422 QSvgExtraStates &states, QRectF *boundingRect)
424 QScopedValueRollback<
bool> inUseGuard(states.inUse,
true);
426 const bool isPainting = (boundingRect ==
nullptr);
427 const auto markers = markersForNode(node);
428 for (
auto &i : markers) {
429 QSvgNode *referencedNode = node->document()->namedNode(i.markerId);
430 if (!referencedNode || referencedNode->type() != QSvgNode::Marker)
432 QSvgMarker *markNode =
static_cast<QSvgMarker *>(referencedNode);
435 p->translate(i.x, i.y);
436 if (markNode->orientation() == QSvgMarker::Orientation::Value) {
437 p->rotate(markNode->orientationAngle());
440 if (i.isStartNode && markNode->orientation()
441 == QSvgMarker::Orientation::AutoStartReverse) {
445 QRectF oldRect = markNode->m_rect;
446 if (markNode->markerUnits() == QSvgMarker::MarkerUnits::StrokeWidth) {
447 markNode->m_rect.setWidth(markNode->m_rect.width() * p->pen().widthF());
448 markNode->m_rect.setHeight(markNode->m_rect.height() * p->pen().widthF());
451 markNode->draw(p, states);
454 QTransform xf = p->transform();
456 *boundingRect |= xf.mapRect(markNode->decoratedInternalBounds(p, states));
459 markNode->m_rect = oldRect;
464QImage QSvgFilterContainer::applyFilter(
const QImage &buffer, QPainter *p,
const QRectF &bounds)
const
466 QRectF localFilterRegion = m_rect.resolveRelativeLengths(bounds, m_filterUnits);
467 QRect globalFilterRegion = p->transform().mapRect(localFilterRegion).toRect();
468 QRect globalFilterRegionRel = globalFilterRegion.translated(-buffer.offset());
470 if (globalFilterRegionRel.isEmpty())
474 if (!QImageIOHandler::allocateImage(globalFilterRegionRel.size(), buffer.format(), &proxy)) {
475 qCWarning(lcSvgDraw) <<
"The requested filter is too big, ignoring";
478 proxy = buffer.copy(globalFilterRegionRel);
479 proxy.setOffset(globalFilterRegion.topLeft());
483 QMap<QString, QImage> buffers;
487 bool requiresSourceAlpha =
false;
489 for (
const auto &node : renderers()) {
490 const QSvgFeFilterPrimitive *filter = QSvgFeFilterPrimitive::castToFilterPrimitive(node.get());
491 if (filter && filter->requiresSourceAlpha()) {
492 requiresSourceAlpha =
true;
497 if (requiresSourceAlpha) {
498 QImage proxyAlpha = proxy.convertedTo(QImage::Format_Alpha8).convertedTo(proxy.format());
499 proxyAlpha.setOffset(proxy.offset());
500 if (proxyAlpha.isNull())
506 for (
const auto &node : renderers()) {
507 const QSvgFeFilterPrimitive *filter = QSvgFeFilterPrimitive::castToFilterPrimitive(node.get());
509 result = filter->apply(buffers, p, bounds, localFilterRegion, m_primitiveUnits, m_filterUnits);
510 if (!result.isNull()) {
512 buffers[filter->result()] = result;
519void QSvgFilterContainer::setSupported(
bool supported)
521 m_supported = supported;
524bool QSvgFilterContainer::supported()
const
529QRectF QSvgFilterContainer::filterRegion(
const QRectF &itemBounds)
const
531 return m_rect.resolveRelativeLengths(itemBounds, m_filterUnits);
534QSvgNode::Type QSvgFilterContainer::type()
const
542 static const QStringList wordList = {
543 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Text"),
544 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Shape"),
545 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#SVG"),
546 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Structure"),
547 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#SolidColor"),
548 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Hyperlinking"),
549 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#CoreAttribute"),
550 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#XlinkAttribute"),
551 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#SVG-static"),
552 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#OpacityAttribute"),
553 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Gradient"),
554 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Font"),
555 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Image"),
556 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#ConditionalProcessing"),
557 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Extensibility"),
558 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#GraphicsAttribute"),
559 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Prefetch"),
560 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#PaintAttribute"),
561 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#ConditionalProcessingAttribute"),
562 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#ExternalResourcesRequiredAttribute")
565 return wordList.contains(str);
574QSvgSwitch::QSvgSwitch(QSvgNode *parent)
575 : QSvgStructureNode(parent)
580QSvgNode *QSvgSwitch::childToRender()
const
582 auto itr = m_renderers.begin();
584 for (
const auto &node : renderers()) {
585 if (node->isVisible() && (node->displayMode() != QSvgNode::NoneMode)) {
586 const QStringList &features = node->requiredFeatures();
587 const QStringList &extensions = node->requiredExtensions();
588 const QStringList &languages = node->requiredLanguages();
589 const QStringList &formats = node->requiredFormats();
590 const QStringList &fonts = node->requiredFonts();
592 bool okToRender =
true;
593 if (!features.isEmpty()) {
594 QStringList::const_iterator sitr = features.constBegin();
595 for (; sitr != features.constEnd(); ++sitr) {
596 if (!isSupportedSvgFeature(*sitr)) {
603 if (okToRender && !extensions.isEmpty()) {
604 QStringList::const_iterator sitr = extensions.constBegin();
605 for (; sitr != extensions.constEnd(); ++sitr) {
606 if (!isSupportedSvgExtension(*sitr)) {
613 if (okToRender && !languages.isEmpty()) {
614 QStringList::const_iterator sitr = languages.constBegin();
616 for (; sitr != languages.constEnd(); ++sitr) {
617 if ((*sitr).startsWith(m_systemLanguagePrefix)) {
624 if (okToRender && !formats.isEmpty())
627 if (okToRender && !fonts.isEmpty())
640void QSvgSwitch::drawCommand(QPainter *p, QSvgExtraStates &states)
642 QSvgNode *node = childToRender();
644 node->draw(p, states);
647QSvgNode::Type QSvgSwitch::type()
const
652void QSvgSwitch::init()
655 m_systemLanguage = locale.name().replace(QLatin1Char(
'_'), QLatin1Char(
'-'));
656 int idx = m_systemLanguage.indexOf(QLatin1Char(
'-'));
657 m_systemLanguagePrefix = m_systemLanguage.mid(0, idx);
660QRectF QSvgStructureNode::internalBounds(QPainter *p, QSvgExtraStates &states)
const
664 QScopedValueRollback<
bool> guard(m_recursing,
true);
665 for (
const auto &node : renderers())
666 bounds |= node->bounds(p, states);
671QRectF QSvgStructureNode::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states)
const
675 QScopedValueRollback<
bool> guard(m_recursing,
true);
676 for (
const auto &node : renderers())
677 bounds |= node->decoratedBounds(p, states);
682QSvgNode* QSvgStructureNode::previousSiblingNode(QSvgNode *n)
const
684 QSvgNode *prev =
nullptr;
685 for (
const auto &node : renderers()) {
693QSvgMask::QSvgMask(QSvgNode *parent, QSvgRectF bounds,
694 QtSvg::UnitTypes contentUnits)
695 : QSvgStructureNode(parent)
697 , m_contentUnits(contentUnits)
701bool QSvgMask::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
706QImage QSvgMask::createMask(QPainter *p, QSvgExtraStates &states, QSvgNode *targetNode, QRectF *globalRect)
const
708 QTransform t = p->transform();
710 QRectF basicRect = targetNode->internalBounds(p, states);
711 *globalRect = t.mapRect(basicRect);
713 return createMask(p, states, basicRect, globalRect);
716QImage QSvgMask::createMask(QPainter *p, QSvgExtraStates &states,
const QRectF &localRect, QRectF *globalRect)
const
718 QRect imageBound = globalRect->toAlignedRect();
719 *globalRect = imageBound.toRectF();
722 if (!QImageIOHandler::allocateImage(imageBound.size(), QImage::Format_RGBA8888, &mask)) {
723 qCWarning(lcSvgDraw) <<
"The requested mask size is too big, ignoring";
727 if (Q_UNLIKELY(m_recursing))
729 QScopedValueRollback<
bool> recursingGuard(m_recursing,
true);
732 if (
this->hasMask()) {
733 QSvgNode *referencedNode = document()->namedNode(
this->maskId());
734 if (referencedNode && referencedNode->type() == QSvgNode::Mask) {
735 QSvgMask *maskNode =
static_cast<QSvgMask *>(referencedNode);
737 return maskNode->createMask(p, states, localRect, &boundsRect);
747 mask.fill(Qt::transparent);
748 QPainter painter(&mask);
749 initPainter(&painter);
751 QSvgExtraStates maskNodeStates;
752 applyStyleRecursive(&painter, maskNodeStates);
756 painter.resetTransform();
757 painter.translate(-imageBound.topLeft());
758 painter.setTransform(p->transform(),
true);
760 QTransform oldT = painter.transform();
761 if (m_contentUnits == QtSvg::UnitTypes::objectBoundingBox){
762 painter.translate(localRect.topLeft());
763 painter.scale(localRect.width(), localRect.height());
767 for (
const auto &node : renderers())
769 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
770 node->draw(&painter, maskNodeStates);
773 for (
int i=0; i < mask.height(); i++) {
774 QRgb *line =
reinterpret_cast<QRgb *>(mask.scanLine(i));
775 for (
int j=0; j < mask.width(); j++) {
776 const qreal rC = 0.2125, gC = 0.7154, bC = 0.0721;
777 int alpha = 255 - (qRed(line[j]) * rC + qGreen(line[j]) * gC + qBlue(line[j]) * bC) * qAlpha(line[j])/255.;
778 line[j] = qRgba(0, 0, 0, alpha);
786 QRectF clipRect = m_rect.resolveRelativeLengths(localRect);
787 QPainterPath clipPath;
788 clipPath.setFillRule(Qt::OddEvenFill);
789 clipPath.addRect(mask.rect().adjusted(-10, -10, 20, 20));
790 clipPath.addPolygon(oldT.map(QPolygonF(clipRect)));
791 painter.resetTransform();
792 painter.fillPath(clipPath, Qt::black);
793 revertStyleRecursive(&painter, maskNodeStates);
797QSvgNode::Type QSvgMask::type()
const
802QSvgPattern::QSvgPattern(QSvgNode *parent, QSvgRectF bounds, QRectF viewBox,
803 QtSvg::UnitTypes contentUnits, QTransform transform)
804 : QSvgStructureNode(parent),
807 m_contentUnits(contentUnits),
808 m_isRendering(
false),
809 m_transform(transform)
815bool QSvgPattern::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
822 static QImage checkerPattern;
824 if (checkerPattern.isNull()) {
825 checkerPattern = QImage(QSize(8, 8), QImage::Format_ARGB32);
827 p.fillRect(QRect(0, 0, 4, 4), QColorConstants::Svg::white);
828 p.fillRect(QRect(4, 0, 4, 4), QColorConstants::Svg::black);
829 p.fillRect(QRect(0, 4, 4, 4), QColorConstants::Svg::black);
830 p.fillRect(QRect(4, 4, 4, 4), QColorConstants::Svg::white);
833 return checkerPattern;
836QImage QSvgPattern::patternImage(QPainter *p, QSvgExtraStates &states,
const QSvgNode *patternElement)
839 QRectF peBoundingBox;
840 QRectF peWorldBoundingBox;
842 QTransform t = p->transform();
844 peBoundingBox = patternElement->internalBounds(p, states);
845 peWorldBoundingBox = t.mapRect(peBoundingBox);
855 qreal contentScaleFactorX = m_transform.m11();
856 qreal contentScaleFactorY = m_transform.m22();
857 if (m_contentUnits == QtSvg::UnitTypes::userSpaceOnUse) {
858 contentScaleFactorX *= t.m11();
859 contentScaleFactorY *= t.m22();
861 contentScaleFactorX *= peWorldBoundingBox.width();
862 contentScaleFactorY *= peWorldBoundingBox.height();
866 QRectF patternBoundingBox = m_rect.resolveRelativeLengths(peBoundingBox);
869 imageSize.setWidth(qCeil(patternBoundingBox.width() * t.m11() * m_transform.m11()));
870 imageSize.setHeight(qCeil(patternBoundingBox.height() * t.m22() * m_transform.m22()));
871 if (imageSize.isEmpty())
874 calculateAppliedTransform(t, peBoundingBox, imageSize);
875 if (document()->isCalculatingImplicitViewBox())
876 return QImage(imageSize, QImage::Format_ARGB32);
878 return renderPattern(imageSize, contentScaleFactorX, contentScaleFactorY);
881QSvgNode::Type QSvgPattern::type()
const
886QImage QSvgPattern::renderPattern(QSize size, qreal contentScaleX, qreal contentScaleY)
888 if (size.isEmpty() || !qIsFinite(contentScaleX) || !qIsFinite(contentScaleY))
889 return defaultPattern();
893 if (!QImageIOHandler::allocateImage(size, QImage::Format_ARGB32, &pattern)) {
894 qCWarning(lcSvgDraw) <<
"The requested pattern size is too big, ignoring";
895 return defaultPattern();
897 pattern.fill(Qt::transparent);
900 qCWarning(lcSvgDraw) <<
"The pattern is trying to render itself recursively. "
901 "Returning a transparent QImage of the right size.";
904 QScopedValueRollback<
bool> guard(m_isRendering,
true);
907 QPainter patternPainter(&pattern);
908 QSvgExtraStates patternStates;
909 initPainter(&patternPainter);
910 applyStyleRecursive(&patternPainter, patternStates);
911 patternPainter.resetTransform();
915 if (m_viewBox.isNull())
916 patternPainter.scale(contentScaleX, contentScaleY);
918 patternPainter.setWindow(m_viewBox.toRect());
922 for (
const auto &node : renderers())
923 node->draw(&patternPainter, patternStates);
925 revertStyleRecursive(&patternPainter, patternStates);
929void QSvgPattern::calculateAppliedTransform(QTransform &worldTransform, QRectF peLocalBB, QSize imageSize)
944 m_appliedTransform.reset();
945 qreal imageDownScaleFactorX = 1 / worldTransform.m11();
946 qreal imageDownScaleFactorY = 1 / worldTransform.m22();
948 m_appliedTransform.scale(qIsFinite(imageDownScaleFactorX) ? imageDownScaleFactorX : 1.0,
949 qIsFinite(imageDownScaleFactorY) ? imageDownScaleFactorY : 1.0);
951 QRectF p = m_rect.resolveRelativeLengths(peLocalBB);
952 m_appliedTransform.scale((p.width() * worldTransform.m11() * m_transform.m11()) / imageSize.width(),
953 (p.height() * worldTransform.m22() * m_transform.m22()) / imageSize.height());
955 QPointF translation = m_rect.translationRelativeToBoundingBox(peLocalBB);
956 m_appliedTransform.translate(translation.x() * worldTransform.m11(), translation.y() * worldTransform.m22());
958 QTransform scalelessTransform = m_transform;
959 scalelessTransform.scale(1 / m_transform.m11(), 1 / m_transform.m22());
961 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(const QString &str)