17#include <QLoggingCategory>
18#include <qscopedvaluerollback.h>
19#include <QtGui/qimageiohandler.h>
23QSvgG::QSvgG(QSvgNode *parent)
24 : QSvgStructureNode(parent)
29QSvgStructureNode::~QSvgStructureNode()
31 qDeleteAll(m_renderers);
34void QSvgG::drawCommand(QPainter *p, QSvgExtraStates &states)
36 QList<QSvgNode*>::iterator itr = m_renderers.begin();
37 while (itr != m_renderers.end()) {
38 QSvgNode *node = *itr;
39 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
40 node->draw(p, states);
45bool QSvgG::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
50QSvgNode::Type QSvgG::type()
const
55bool QSvgG::requiresGroupRendering()
const
57 return m_renderers.count() > 1;
60QSvgStructureNode::QSvgStructureNode(QSvgNode *parent)
66QSvgNode * QSvgStructureNode::scopeNode(
const QString &id)
const
68 QSvgTinyDocument *doc = document();
69 return doc ? doc->namedNode(id) : 0;
72void QSvgStructureNode::addChild(QSvgNode *child,
const QString &id)
74 m_renderers.append(child);
79 QSvgTinyDocument *doc = document();
81 doc->addNamedNode(id, child);
84QSvgDefs::QSvgDefs(QSvgNode *parent)
85 : QSvgStructureNode(parent)
89bool QSvgDefs::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
94QSvgNode::Type QSvgDefs::type()
const
99QSvgSymbolLike::QSvgSymbolLike(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
100 QSvgSymbolLike::PreserveAspectRatios pAspectRatios, QSvgSymbolLike::Overflow overflow)
101 : QSvgStructureNode(parent)
105 , m_pAspectRatios(pAspectRatios)
106 , m_overflow(overflow)
111QRectF QSvgSymbolLike::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states)
const
114 setPainterToRectAndAdjustment(p);
115 QRectF rect = internalBounds(p, states);
120bool QSvgSymbolLike::requiresGroupRendering()
const
122 return m_renderers.count() > 1;
125QRectF QSvgSymbolLike::clipRect()
const
127 if (m_overflow != Overflow::Hidden || !m_viewBox.isValid())
131 if (m_rect.width() > 0 && m_viewBox.width() > 0)
132 scaleX = m_rect.width() / m_viewBox.width();
134 if (m_rect.height() > 0 && m_viewBox.height() > 0)
135 scaleY = m_rect.height() / m_viewBox.height();
138 t.translate(- m_refP.x() * scaleX - m_rect.left() - m_viewBox.left() * scaleX,
139 - m_refP.y() * scaleY - m_rect.top() - m_viewBox.top() * scaleY);
140 t.scale(scaleX, scaleY);
142 return t.mapRect(m_viewBox);
145QTransform QSvgSymbolLike::aspectRatioTransform()
const
153 if (m_rect.width() > 0 && m_viewBox.width() > 0)
154 scaleX = m_rect.width() / m_viewBox.width();
156 if (m_rect.height() > 0 && m_viewBox.height() > 0)
157 scaleY = m_rect.height() / m_viewBox.height();
159 if (!qFuzzyCompare(scaleX, scaleY) &&
160 m_pAspectRatios.testAnyFlag(PreserveAspectRatio::xyMask)) {
162 if (m_pAspectRatios.testAnyFlag(PreserveAspectRatio::meet))
163 scaleX = scaleY = qMin(scaleX, scaleY);
165 scaleX = scaleY = qMax(scaleX, scaleY);
167 qreal xOverflow = scaleX * m_viewBox.width() - m_rect.width();
168 qreal yOverflow = scaleY * m_viewBox.height() - m_rect.height();
170 if ((m_pAspectRatios & PreserveAspectRatio::xMask) == PreserveAspectRatio::xMid)
171 offsetX -= xOverflow / 2.;
172 else if ((m_pAspectRatios & PreserveAspectRatio::xMask) == PreserveAspectRatio::xMax)
173 offsetX -= xOverflow;
175 if ((m_pAspectRatios & PreserveAspectRatio::yMask) == PreserveAspectRatio::yMid)
176 offsetY -= yOverflow / 2.;
177 else if ((m_pAspectRatios & PreserveAspectRatio::yMask) == PreserveAspectRatio::yMax)
178 offsetY -= yOverflow;
181 xform.translate(offsetX - m_refP.x() * scaleX, offsetY - m_refP.y() * scaleY);
182 xform.scale(scaleX, scaleY);
187void QSvgSymbolLike::setPainterToRectAndAdjustment(QPainter *p)
const
189 QRectF clip = clipRect();
191 p->setClipRect(clip);
193 p->setTransform(aspectRatioTransform(),
true);
196QSvgSymbol::QSvgSymbol(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
197 QSvgSymbol::PreserveAspectRatios pAspectRatios,
198 QSvgSymbol::Overflow overflow)
199 : QSvgSymbolLike(parent, bounds, viewBox, refP, pAspectRatios, overflow)
203void QSvgSymbol::drawCommand(QPainter *p, QSvgExtraStates &states)
208 QList<QSvgNode*>::iterator itr = m_renderers.begin();
211 setPainterToRectAndAdjustment(p);
212 while (itr != m_renderers.end()) {
213 QSvgNode *node = *itr;
214 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
215 node->draw(p, states);
221QSvgNode::Type QSvgSymbol::type()
const
226QSvgMarker::QSvgMarker(QSvgNode *parent, QRectF bounds, QRectF viewBox, QPointF refP,
227 QSvgSymbol::PreserveAspectRatios pAspectRatios, QSvgSymbol::Overflow overflow,
228 Orientation orientation, qreal orientationAngle, MarkerUnits markerUnits)
229 : QSvgSymbolLike(parent, bounds, viewBox, refP, pAspectRatios, overflow)
230 , m_orientation(orientation)
231 , m_orientationAngle(orientationAngle)
232 , m_markerUnits(markerUnits)
235 QSvgFillStyle *fillProp =
new QSvgFillStyle();
236 fillProp->setBrush(Qt::black);
239 QSvgStrokeStyle *strokeProp =
new QSvgStrokeStyle();
240 strokeProp->setMiterLimit(4);
241 strokeProp->setWidth(1);
242 strokeProp->setLineCap(Qt::FlatCap);
243 strokeProp->setLineJoin(Qt::SvgMiterJoin);
244 strokeProp->setStroke(Qt::NoBrush);
248QSvgFilterContainer::QSvgFilterContainer(QSvgNode *parent,
const QSvgRectF &bounds,
249 QtSvg::UnitTypes filterUnits, QtSvg::UnitTypes primitiveUnits)
250 : QSvgStructureNode(parent)
252 , m_filterUnits(filterUnits)
253 , m_primitiveUnits(primitiveUnits)
259bool QSvgFilterContainer::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
264void QSvgMarker::drawCommand(QPainter *p, QSvgExtraStates &states)
269 if (Q_UNLIKELY(m_recursing))
271 QScopedValueRollback<
bool> recursingGuard(m_recursing,
true);
273 QList<QSvgNode*>::iterator itr = m_renderers.begin();
276 setPainterToRectAndAdjustment(p);
278 while (itr != m_renderers.end()) {
279 QSvgNode *node = *itr;
280 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
281 node->draw(p, states);
289struct PositionMarkerPair {
294 bool isStartNode =
false;
297QList<PositionMarkerPair> markersForNode(
const QSvgNode *node)
299 if (!node->hasAnyMarker())
302 auto getMeanAngle = [](QPointF p0, QPointF p1, QPointF p2) -> qreal {
303 QPointF t1 = p1 - p0;
304 QPointF t2 = p2 - p1;
305 qreal hyp1 = hypot(t1.x(), t1.y());
310 qreal hyp2 = hypot(t2.x(), t2.y());
315 QPointF tangent = t1 + t2;
316 return -atan2(tangent.y(), tangent.x()) / M_PI * 180.;
319 QList<PositionMarkerPair> markers;
321 switch (node->type()) {
322 case QSvgNode::Line: {
323 const QSvgLine *line =
static_cast<
const QSvgLine*>(node);
324 if (node->hasMarkerStart())
325 markers << PositionMarkerPair { line->line().p1().x(), line->line().p1().y(),
326 line->line().angle(), line->markerStartId(),
328 if (node->hasMarkerEnd())
329 markers << PositionMarkerPair { line->line().p2().x(), line->line().p2().y(),
330 line->line().angle(), line->markerEndId() };
333 case QSvgNode::Polyline:
334 case QSvgNode::Polygon: {
335 const QPolygonF &polyData = (node->type() == QSvgNode::Polyline)
336 ?
static_cast<
const QSvgPolyline*>(node)->polygon()
337 :
static_cast<
const QSvgPolygon*>(node)->polygon();
339 if (node->hasMarkerStart() && polyData.size() > 1) {
340 QLineF line(polyData.at(0), polyData.at(1));
341 markers << PositionMarkerPair { line.p1().x(),
344 node->markerStartId(),
347 if (node->hasMarkerMid()) {
348 for (
int i = 1; i < polyData.size() - 1; i++) {
349 QPointF p0 = polyData.at(i - 1);
350 QPointF p1 = polyData.at(i);
351 QPointF p2 = polyData.at(i + 1);
353 markers << PositionMarkerPair { p1.x(),
355 getMeanAngle(p0, p1, p2),
356 node->markerStartId() };
359 if (node->hasMarkerEnd() && polyData.size() > 1) {
360 QLineF line(polyData.at(polyData.size() - 1), polyData.last());
361 markers << PositionMarkerPair { line.p2().x(),
364 node->markerEndId() };
368 case QSvgNode::Path: {
369 const QSvgPath *path =
static_cast<
const QSvgPath*>(node);
370 if (node->hasMarkerStart())
371 markers << PositionMarkerPair { path->path().pointAtPercent(0.).x(),
372 path->path().pointAtPercent(0.).y(),
373 path->path().angleAtPercent(0.),
374 path->markerStartId(),
376 if (node->hasMarkerMid()) {
377 for (
int i = 1; i < path->path().elementCount() - 1; i++) {
378 if (path->path().elementAt(i).type == QPainterPath::MoveToElement)
380 if (path->path().elementAt(i).type == QPainterPath::CurveToElement)
382 if (( path->path().elementAt(i).type == QPainterPath::CurveToDataElement &&
383 path->path().elementAt(i + 1).type != QPainterPath::CurveToDataElement ) ||
384 path->path().elementAt(i).type == QPainterPath::LineToElement) {
386 QPointF p0(path->path().elementAt(i - 1).x, path->path().elementAt(i - 1).y);
387 QPointF p1(path->path().elementAt(i).x, path->path().elementAt(i).y);
388 QPointF p2(path->path().elementAt(i + 1).x, path->path().elementAt(i + 1).y);
390 markers << PositionMarkerPair { p1.x(),
392 getMeanAngle(p0, p1, p2),
393 path->markerMidId() };
397 if (node->hasMarkerEnd())
398 markers << PositionMarkerPair { path->path().pointAtPercent(1.).x(),
399 path->path().pointAtPercent(1.).y(),
400 path->path().angleAtPercent(1.),
401 path->markerEndId() };
415void QSvgMarker::drawMarkersForNode(QSvgNode *node, QPainter *p, QSvgExtraStates &states)
417 drawHelper(node, p, states);
420QRectF QSvgMarker::markersBoundsForNode(
const QSvgNode *node, QPainter *p, QSvgExtraStates &states)
423 drawHelper(node, p, states, &bounds);
427QSvgNode::Type QSvgMarker::type()
const
432void QSvgMarker::drawHelper(
const QSvgNode *node, QPainter *p,
433 QSvgExtraStates &states, QRectF *boundingRect)
435 QScopedValueRollback<
bool> inUseGuard(states.inUse,
true);
437 const bool isPainting = (boundingRect ==
nullptr);
438 const auto markers = markersForNode(node);
439 for (
auto &i : markers) {
440 QSvgMarker *markNode =
static_cast<QSvgMarker*>(node->document()->namedNode(i.markerId));
445 p->translate(i.x, i.y);
446 if (markNode->orientation() == QSvgMarker::Orientation::Value) {
447 p->rotate(markNode->orientationAngle());
450 if (i.isStartNode && markNode->orientation()
451 == QSvgMarker::Orientation::AutoStartReverse) {
455 QRectF oldRect = markNode->m_rect;
456 if (markNode->markerUnits() == QSvgMarker::MarkerUnits::StrokeWidth) {
457 markNode->m_rect.setWidth(markNode->m_rect.width() * p->pen().widthF());
458 markNode->m_rect.setHeight(markNode->m_rect.height() * p->pen().widthF());
461 markNode->draw(p, states);
464 QTransform xf = p->transform();
466 *boundingRect |= xf.mapRect(markNode->decoratedInternalBounds(p, states));
469 markNode->m_rect = oldRect;
474QImage QSvgFilterContainer::applyFilter(
const QImage &buffer, QPainter *p,
const QRectF &bounds)
const
476 QRectF localFilterRegion = m_rect.resolveRelativeLengths(bounds, m_filterUnits);
477 QRect globalFilterRegion = p->transform().mapRect(localFilterRegion).toRect();
478 QRect globalFilterRegionRel = globalFilterRegion.translated(-buffer.offset());
480 if (globalFilterRegionRel.isEmpty())
484 if (!QImageIOHandler::allocateImage(globalFilterRegionRel.size(), buffer.format(), &proxy)) {
485 qCWarning(lcSvgDraw) <<
"The requested filter is too big, ignoring";
488 proxy = buffer.copy(globalFilterRegionRel);
489 proxy.setOffset(globalFilterRegion.topLeft());
493 QMap<QString, QImage> buffers;
497 bool requiresSourceAlpha =
false;
499 const QList<QSvgNode *> children = renderers();
500 for (
const QSvgNode *renderer : children) {
501 const QSvgFeFilterPrimitive *filter = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer);
502 if (filter && filter->requiresSourceAlpha()) {
503 requiresSourceAlpha =
true;
508 if (requiresSourceAlpha) {
509 QImage proxyAlpha = proxy.convertedTo(QImage::Format_Alpha8).convertedTo(proxy.format());
510 proxyAlpha.setOffset(proxy.offset());
511 if (proxyAlpha.isNull())
517 for (
const QSvgNode *renderer : children) {
518 const QSvgFeFilterPrimitive *filter = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer);
520 result = filter->apply(buffers, p, bounds, localFilterRegion, m_primitiveUnits, m_filterUnits);
521 if (!result.isNull()) {
523 buffers[filter->result()] = result;
530void QSvgFilterContainer::setSupported(
bool supported)
532 m_supported = supported;
535bool QSvgFilterContainer::supported()
const
540QRectF QSvgFilterContainer::filterRegion(
const QRectF &itemBounds)
const
542 return m_rect.resolveRelativeLengths(itemBounds, m_filterUnits);
545QSvgNode::Type QSvgFilterContainer::type()
const
553 static const QStringList wordList = {
554 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Text"),
555 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Shape"),
556 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#SVG"),
557 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Structure"),
558 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#SolidColor"),
559 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Hyperlinking"),
560 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#CoreAttribute"),
561 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#XlinkAttribute"),
562 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#SVG-static"),
563 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#OpacityAttribute"),
564 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Gradient"),
565 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Font"),
566 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Image"),
567 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#ConditionalProcessing"),
568 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Extensibility"),
569 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#GraphicsAttribute"),
570 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#Prefetch"),
571 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#PaintAttribute"),
572 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#ConditionalProcessingAttribute"),
573 QStringLiteral(
"http://www.w3.org/Graphics/SVG/feature/1.2/#ExternalResourcesRequiredAttribute")
576 return wordList.contains(str);
585QSvgSwitch::QSvgSwitch(QSvgNode *parent)
586 : QSvgStructureNode(parent)
591QSvgNode *QSvgSwitch::childToRender()
const
593 auto itr = m_renderers.begin();
595 while (itr != m_renderers.end()) {
596 QSvgNode *node = *itr;
597 if (node->isVisible() && (node->displayMode() != QSvgNode::NoneMode)) {
598 const QStringList &features = node->requiredFeatures();
599 const QStringList &extensions = node->requiredExtensions();
600 const QStringList &languages = node->requiredLanguages();
601 const QStringList &formats = node->requiredFormats();
602 const QStringList &fonts = node->requiredFonts();
604 bool okToRender =
true;
605 if (!features.isEmpty()) {
606 QStringList::const_iterator sitr = features.constBegin();
607 for (; sitr != features.constEnd(); ++sitr) {
608 if (!isSupportedSvgFeature(*sitr)) {
615 if (okToRender && !extensions.isEmpty()) {
616 QStringList::const_iterator sitr = extensions.constBegin();
617 for (; sitr != extensions.constEnd(); ++sitr) {
618 if (!isSupportedSvgExtension(*sitr)) {
625 if (okToRender && !languages.isEmpty()) {
626 QStringList::const_iterator sitr = languages.constBegin();
628 for (; sitr != languages.constEnd(); ++sitr) {
629 if ((*sitr).startsWith(m_systemLanguagePrefix)) {
636 if (okToRender && !formats.isEmpty())
639 if (okToRender && !fonts.isEmpty())
652void QSvgSwitch::drawCommand(QPainter *p, QSvgExtraStates &states)
654 QSvgNode *node = childToRender();
656 node->draw(p, states);
659QSvgNode::Type QSvgSwitch::type()
const
664void QSvgSwitch::init()
667 m_systemLanguage = locale.name().replace(QLatin1Char(
'_'), QLatin1Char(
'-'));
668 int idx = m_systemLanguage.indexOf(QLatin1Char(
'-'));
669 m_systemLanguagePrefix = m_systemLanguage.mid(0, idx);
672QRectF QSvgStructureNode::internalBounds(QPainter *p, QSvgExtraStates &states)
const
676 QScopedValueRollback<
bool> guard(m_recursing,
true);
677 for (QSvgNode *node : std::as_const(m_renderers))
678 bounds |= node->bounds(p, states);
683QRectF QSvgStructureNode::decoratedInternalBounds(QPainter *p, QSvgExtraStates &states)
const
687 QScopedValueRollback<
bool> guard(m_recursing,
true);
688 for (QSvgNode *node : std::as_const(m_renderers))
689 bounds |= node->decoratedBounds(p, states);
694QSvgNode* QSvgStructureNode::previousSiblingNode(QSvgNode *n)
const
696 QSvgNode *prev =
nullptr;
697 QList<QSvgNode*>::const_iterator itr = m_renderers.constBegin();
698 for (; itr != m_renderers.constEnd(); ++itr) {
699 QSvgNode *node = *itr;
707QSvgMask::QSvgMask(QSvgNode *parent, QSvgRectF bounds,
708 QtSvg::UnitTypes contentUnits)
709 : QSvgStructureNode(parent)
711 , m_contentUnits(contentUnits)
715bool QSvgMask::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
720QImage QSvgMask::createMask(QPainter *p, QSvgExtraStates &states, QSvgNode *targetNode, QRectF *globalRect)
const
722 QTransform t = p->transform();
724 QRectF basicRect = targetNode->internalBounds(p, states);
725 *globalRect = t.mapRect(basicRect);
727 return createMask(p, states, basicRect, globalRect);
730QImage QSvgMask::createMask(QPainter *p, QSvgExtraStates &states,
const QRectF &localRect, QRectF *globalRect)
const
732 QRect imageBound = globalRect->toAlignedRect();
733 *globalRect = imageBound.toRectF();
736 if (!QImageIOHandler::allocateImage(imageBound.size(), QImage::Format_RGBA8888, &mask)) {
737 qCWarning(lcSvgDraw) <<
"The requested mask size is too big, ignoring";
741 if (Q_UNLIKELY(m_recursing))
743 QScopedValueRollback<
bool> recursingGuard(m_recursing,
true);
746 if (
this->hasMask()) {
747 QSvgMask *maskNode =
static_cast<QSvgMask*>(document()->namedNode(
this->maskId()));
750 return maskNode->createMask(p, states, localRect, &boundsRect);
760 mask.fill(Qt::transparent);
761 QPainter painter(&mask);
762 initPainter(&painter);
764 QSvgExtraStates maskNodeStates;
765 applyStyleRecursive(&painter, maskNodeStates);
769 painter.resetTransform();
770 painter.translate(-imageBound.topLeft());
771 painter.setTransform(p->transform(),
true);
773 QTransform oldT = painter.transform();
774 if (m_contentUnits == QtSvg::UnitTypes::objectBoundingBox){
775 painter.translate(localRect.topLeft());
776 painter.scale(localRect.width(), localRect.height());
780 QList<QSvgNode*>::const_iterator itr = m_renderers.begin();
781 while (itr != m_renderers.end()) {
782 QSvgNode *node = *itr;
783 if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
784 node->draw(&painter, maskNodeStates);
788 for (
int i=0; i < mask.height(); i++) {
789 QRgb *line =
reinterpret_cast<QRgb *>(mask.scanLine(i));
790 for (
int j=0; j < mask.width(); j++) {
791 const qreal rC = 0.2125, gC = 0.7154, bC = 0.0721;
792 int alpha = 255 - (qRed(line[j]) * rC + qGreen(line[j]) * gC + qBlue(line[j]) * bC) * qAlpha(line[j])/255.;
793 line[j] = qRgba(0, 0, 0, alpha);
801 QRectF clipRect = m_rect.resolveRelativeLengths(localRect);
802 QPainterPath clipPath;
803 clipPath.setFillRule(Qt::OddEvenFill);
804 clipPath.addRect(mask.rect().adjusted(-10, -10, 20, 20));
805 clipPath.addPolygon(oldT.map(QPolygonF(clipRect)));
806 painter.resetTransform();
807 painter.fillPath(clipPath, Qt::black);
808 revertStyleRecursive(&painter, maskNodeStates);
812QSvgNode::Type QSvgMask::type()
const
817QSvgPattern::QSvgPattern(QSvgNode *parent, QSvgRectF bounds, QRectF viewBox,
818 QtSvg::UnitTypes contentUnits, QTransform transform)
819 : QSvgStructureNode(parent),
822 m_contentUnits(contentUnits),
823 m_isRendering(
false),
824 m_transform(transform)
830bool QSvgPattern::shouldDrawNode(QPainter *, QSvgExtraStates &)
const
837 static QImage checkerPattern;
839 if (checkerPattern.isNull()) {
840 checkerPattern = QImage(QSize(8, 8), QImage::Format_ARGB32);
842 p.fillRect(QRect(0, 0, 4, 4), QColorConstants::Svg::white);
843 p.fillRect(QRect(4, 0, 4, 4), QColorConstants::Svg::black);
844 p.fillRect(QRect(0, 4, 4, 4), QColorConstants::Svg::black);
845 p.fillRect(QRect(4, 4, 4, 4), QColorConstants::Svg::white);
848 return checkerPattern;
851QImage QSvgPattern::patternImage(QPainter *p, QSvgExtraStates &states,
const QSvgNode *patternElement)
854 QRectF peBoundingBox;
855 QRectF peWorldBoundingBox;
857 QTransform t = p->transform();
859 peBoundingBox = patternElement->internalBounds(p, states);
860 peWorldBoundingBox = t.mapRect(peBoundingBox);
870 qreal contentScaleFactorX = m_transform.m11();
871 qreal contentScaleFactorY = m_transform.m22();
872 if (m_contentUnits == QtSvg::UnitTypes::userSpaceOnUse) {
873 contentScaleFactorX *= t.m11();
874 contentScaleFactorY *= t.m22();
876 contentScaleFactorX *= peWorldBoundingBox.width();
877 contentScaleFactorY *= peWorldBoundingBox.height();
881 QRectF patternBoundingBox = m_rect.resolveRelativeLengths(peBoundingBox);
884 imageSize.setWidth(qCeil(patternBoundingBox.width() * t.m11() * m_transform.m11()));
885 imageSize.setHeight(qCeil(patternBoundingBox.height() * t.m22() * m_transform.m22()));
886 if (imageSize.isEmpty())
889 calculateAppliedTransform(t, peBoundingBox, imageSize);
890 if (document()->isCalculatingImplicitViewBox())
891 return QImage(imageSize, QImage::Format_ARGB32);
893 return renderPattern(imageSize, contentScaleFactorX, contentScaleFactorY);
896QSvgNode::Type QSvgPattern::type()
const
901QImage QSvgPattern::renderPattern(QSize size, qreal contentScaleX, qreal contentScaleY)
903 if (size.isEmpty() || !qIsFinite(contentScaleX) || !qIsFinite(contentScaleY))
904 return defaultPattern();
908 if (!QImageIOHandler::allocateImage(size, QImage::Format_ARGB32, &pattern)) {
909 qCWarning(lcSvgDraw) <<
"The requested pattern size is too big, ignoring";
910 return defaultPattern();
912 pattern.fill(Qt::transparent);
915 qCWarning(lcSvgDraw) <<
"The pattern is trying to render itself recursively. "
916 "Returning a transparent QImage of the right size.";
919 QScopedValueRollback<
bool> guard(m_isRendering,
true);
922 QPainter patternPainter(&pattern);
923 QSvgExtraStates patternStates;
924 initPainter(&patternPainter);
925 applyStyleRecursive(&patternPainter, patternStates);
926 patternPainter.resetTransform();
930 if (m_viewBox.isNull())
931 patternPainter.scale(contentScaleX, contentScaleY);
933 patternPainter.setWindow(m_viewBox.toRect());
937 for (QSvgNode *node : m_renderers)
938 node->draw(&patternPainter, patternStates);
940 revertStyleRecursive(&patternPainter, patternStates);
944void QSvgPattern::calculateAppliedTransform(QTransform &worldTransform, QRectF peLocalBB, QSize imageSize)
959 m_appliedTransform.reset();
960 qreal imageDownScaleFactorX = 1 / worldTransform.m11();
961 qreal imageDownScaleFactorY = 1 / worldTransform.m22();
963 m_appliedTransform.scale(qIsFinite(imageDownScaleFactorX) ? imageDownScaleFactorX : 1.0,
964 qIsFinite(imageDownScaleFactorY) ? imageDownScaleFactorY : 1.0);
966 QRectF p = m_rect.resolveRelativeLengths(peLocalBB);
967 m_appliedTransform.scale((p.width() * worldTransform.m11() * m_transform.m11()) / imageSize.width(),
968 (p.height() * worldTransform.m22() * m_transform.m22()) / imageSize.height());
970 QPointF translation = m_rect.translationRelativeToBoundingBox(peLocalBB);
971 m_appliedTransform.translate(translation.x() * worldTransform.m11(), translation.y() * worldTransform.m22());
973 QTransform scalelessTransform = m_transform;
974 scalelessTransform.scale(1 / m_transform.m11(), 1 / m_transform.m22());
976 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)