diff options
author | Paul Olav Tvete <paul.tvete@qt.io> | 2023-10-05 08:43:55 +0200 |
---|---|---|
committer | Paul Olav Tvete <paul.tvete@qt.io> | 2023-10-30 10:36:06 +0200 |
commit | e9913b70717f55efb88e3022254299c848bcb940 (patch) | |
tree | 99672b7cfe9d10a5ae84f8610cb9cab546db747f /src | |
parent | 24e0d715a6e14f06c9da7604086789431960a41a (diff) |
Private API for iterating over QSvgTinyDocument
Expose more node information in the internal API. Also
use the new API to implement a debug operator for
QSvgTinyDocument. Includes a drive-by fix: use floating
point instead of int for rounded rect radius.
Task-number: QTBUG-116883
Change-Id: I238b7313c10b88abd7c2ca755b898ecb6f8c3e8b
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/svg/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/svg/qsvgdebug.cpp | 185 | ||||
-rw-r--r-- | src/svg/qsvggraphics.cpp | 2 | ||||
-rw-r--r-- | src/svg/qsvggraphics_p.h | 26 | ||||
-rw-r--r-- | src/svg/qsvghandler.cpp | 4 | ||||
-rw-r--r-- | src/svg/qsvgnode_p.h | 1 | ||||
-rw-r--r-- | src/svg/qsvgstyle_p.h | 7 | ||||
-rw-r--r-- | src/svg/qsvgtinydocument_p.h | 2 | ||||
-rw-r--r-- | src/svg/qsvgvisitor.cpp | 119 | ||||
-rw-r--r-- | src/svg/qsvgvisitor_p.h | 57 |
10 files changed, 397 insertions, 8 deletions
diff --git a/src/svg/CMakeLists.txt b/src/svg/CMakeLists.txt index bb01c53..0bea57c 100644 --- a/src/svg/CMakeLists.txt +++ b/src/svg/CMakeLists.txt @@ -23,6 +23,8 @@ qt_internal_add_module(Svg qsvgstructure.cpp qsvgstructure_p.h qsvgstyle.cpp qsvgstyle_p.h qsvgtinydocument.cpp qsvgtinydocument_p.h + qsvgdebug.cpp + qsvgvisitor.cpp qsvgvisitor_p.h qtsvgglobal.h qtsvgglobal_p.h DEFINES QT_NO_CONTEXTLESS_CONNECT diff --git a/src/svg/qsvgdebug.cpp b/src/svg/qsvgdebug.cpp new file mode 100644 index 0000000..a3cdf8d --- /dev/null +++ b/src/svg/qsvgdebug.cpp @@ -0,0 +1,185 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qsvgvisitor_p.h" +#include <QDebug> + +QT_BEGIN_NAMESPACE + +static const char *nodeTypeStrings[] = { + "DOC", + "G", + "DEFS", + "SWITCH", + "ANIMATION", + "ARC", + "CIRCLE", + "ELLIPSE", + "IMAGE", + "LINE", + "PATH", + "POLYGON", + "POLYLINE", + "RECT", + "TEXT", + "TEXTAREA", + "TSPAN", + "USE", + "VIDEO" +}; + +// TODO: something like this is needed in several places. Make a common version. +static const char *typeName(const QSvgNode *node) +{ + constexpr int typeNameCount = sizeof(nodeTypeStrings) / sizeof(const char *); + if (node->type() < typeNameCount) + return nodeTypeStrings[node->type()]; + return "UNKNOWN"; +} + +class SvgDebugVisitor : public QSvgVisitor +{ +public: + SvgDebugVisitor(QDebug &stream) : debug(stream) {} + void write(const QSvgTinyDocument *doc); + +protected: + void visitNode(const QSvgNode *) override; + bool visitStructureNodeStart(const QSvgStructureNode *node) override; + void visitStructureNodeEnd(const QSvgStructureNode *) override; + void visitAnimationNode(const QSvgAnimation *node) override; + void visitEllipseNode(const QSvgEllipse *node) override; + void visitImageNode(const QSvgImage *node) override; + void visitLineNode(const QSvgLine *node) override; + void visitPathNode(const QSvgPath *node) override; + void visitPolygonNode(const QSvgPolygon *node) override; + void visitPolylineNode(const QSvgPolyline *node) override; + void visitRectNode(const QSvgRect *node) override; + void visitTextNode(const QSvgText *node) override; + void visitUseNode(const QSvgUse *node) override; + void visitVideoNode(const QSvgVideo *node) override; + +private: + const char *indent() { m_indent.fill(' ', m_indentLevel * 2); return m_indent.constData();} + void handleBaseNode(const QSvgNode *node); + QDebug &debug; + int m_indentLevel = 0; + QByteArray m_indent; + int nodeCounter = 0; +}; + +void SvgDebugVisitor::handleBaseNode(const QSvgNode *node) +{ + debug << indent() << typeName(node) << "node, ID:" << node->nodeId(); + nodeCounter++; +} + +void SvgDebugVisitor::visitNode(const QSvgNode *node) +{ + handleBaseNode(node); + debug << Qt::endl; +} + +bool SvgDebugVisitor::visitStructureNodeStart(const QSvgStructureNode *node) +{ + debug << indent() << "START node" << node->nodeId() << "type" << typeName(node) << node->type() << Qt::endl; + m_indentLevel++; + return true; +} + +void SvgDebugVisitor::visitStructureNodeEnd(const QSvgStructureNode *node) +{ + m_indentLevel--; + debug << indent() << "END node" << node->nodeId() << Qt::endl; +} + +void SvgDebugVisitor::visitAnimationNode(const QSvgAnimation *node) +{ + handleBaseNode(node); + debug << Qt::endl; +} + +void SvgDebugVisitor::visitEllipseNode(const QSvgEllipse *node) +{ + handleBaseNode(node); + debug << "rect:" << node->rect() << Qt::endl; +} + +void SvgDebugVisitor::visitImageNode(const QSvgImage *node) +{ + handleBaseNode(node); + debug << "image:" << node->image() << Qt::endl; +} + +void SvgDebugVisitor::visitLineNode(const QSvgLine *node) +{ + handleBaseNode(node); + debug << "line:" << node->line() << Qt::endl; +} + +void SvgDebugVisitor::visitPathNode(const QSvgPath *node) +{ + handleBaseNode(node); + debug << "path:" << node->path().elementCount() << "elements." << Qt::endl; +} + +void SvgDebugVisitor::visitPolygonNode(const QSvgPolygon *node) +{ + handleBaseNode(node); + debug << "polygon:" << node->polygon().size() << "elements." << Qt::endl; +} + +void SvgDebugVisitor::visitPolylineNode(const QSvgPolyline *node) +{ + handleBaseNode(node); + debug << "polygon:" << node->polygon().size() << "elements." << Qt::endl; +} + +void SvgDebugVisitor::visitRectNode(const QSvgRect *node) +{ + handleBaseNode(node); + debug << "rect:" << node->rect() << "radius:" << node->radius() << Qt::endl; +} + +void SvgDebugVisitor::visitTextNode(const QSvgText *node) +{ + handleBaseNode(node); + QString text; + for (const auto *tspan : node->tspans()) { + if (!tspan) + text += QStringLiteral("\\n"); + else + text += tspan->text(); + } + debug << "text:" << text << Qt::endl; +} + +void SvgDebugVisitor::visitUseNode(const QSvgUse *node) +{ + handleBaseNode(node); + debug << "link ID:" << node->linkId() << Qt::endl; +} + +void SvgDebugVisitor::visitVideoNode(const QSvgVideo *node) +{ + handleBaseNode(node); + debug << Qt::endl; +} + +void SvgDebugVisitor::write(const QSvgTinyDocument *doc) +{ + debug << "SVG" << doc->size() << "viewBox" << doc->viewBox() << Qt::endl; + traverse(doc); + + debug << "END SVG" << nodeCounter << "nodes"; +} + +QDebug operator<<(QDebug debug, const QSvgTinyDocument &doc) +{ + SvgDebugVisitor visitor(debug); + visitor.write(&doc); + + return debug; +} + +QT_END_NAMESPACE diff --git a/src/svg/qsvggraphics.cpp b/src/svg/qsvggraphics.cpp index 5cb216b..1858d8e 100644 --- a/src/svg/qsvggraphics.cpp +++ b/src/svg/qsvggraphics.cpp @@ -250,7 +250,7 @@ void QSvgPolyline::draw(QPainter *p, QSvgExtraStates &states) QT_SVG_TIMING_EXIT("Polyline") } -QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, int rx, int ry) +QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, qreal rx, qreal ry) : QSvgNode(node), m_rect(rect), m_rx(rx), m_ry(ry) { diff --git a/src/svg/qsvggraphics_p.h b/src/svg/qsvggraphics_p.h index 9bb005b..42406e3 100644 --- a/src/svg/qsvggraphics_p.h +++ b/src/svg/qsvggraphics_p.h @@ -55,6 +55,7 @@ public: Type type() const override; QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override; QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; + QRectF rect() const { return m_bounds; } private: QRectF m_bounds; }; @@ -74,6 +75,9 @@ public: void draw(QPainter *p, QSvgExtraStates &states) override; Type type() const override; QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; + + QRectF rect() const { return m_bounds; } + const QImage &image() const { return m_image; } private: QImage m_image; QRectF m_bounds; @@ -87,6 +91,7 @@ public: Type type() const override; QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override; QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; + QLineF line() const { return m_line; } private: QLineF m_line; }; @@ -100,9 +105,10 @@ public: QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override; QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; - QPainterPath *qpath() { - return &m_path; + const QPainterPath &path() const { + return m_path; } + private: QPainterPath m_path; }; @@ -115,6 +121,9 @@ public: Type type() const override; QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override; QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; + const QPolygonF &polygon() const { + return m_poly; + } private: QPolygonF m_poly; }; @@ -127,6 +136,9 @@ public: Type type() const override; QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override; QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; + const QPolygonF &polygon() const { + return m_poly; + } private: QPolygonF m_poly; }; @@ -134,14 +146,16 @@ private: class Q_SVG_PRIVATE_EXPORT QSvgRect : public QSvgNode { public: - QSvgRect(QSvgNode *paren, const QRectF &rect, int rx=0, int ry=0); + QSvgRect(QSvgNode *paren, const QRectF &rect, qreal rx=0, qreal ry=0); Type type() const override; void draw(QPainter *p, QSvgExtraStates &states) override; QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override; QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; + QRectF rect() const { return m_rect; } + QPointF radius() const { return { m_rx, m_ry }; } private: QRectF m_rect; - int m_rx, m_ry; + qreal m_rx, m_ry; }; class QSvgTspan; @@ -163,6 +177,7 @@ public: Type type() const override; void addTspan(QSvgTspan *tspan) {m_tspans.append(tspan);} + const QList<QSvgTspan *> tspans() const { return m_tspans; } void addText(const QString &text); void addLineBreak() {m_tspans.append(LINEBREAK);} void setWhitespaceMode(WhitespaceMode mode) {m_mode = mode;} @@ -170,6 +185,9 @@ public: QRectF fastBounds(QPainter *p, QSvgExtraStates &states) const override; QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; + QPointF position() const { return m_coord; } + QSizeF size() const { return m_size; } + private: bool precheck(QPainter *p) const; void draw_helper(QPainter *p, QSvgExtraStates &states, QRectF *boundingRect = nullptr) const; diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp index c870081..08f6e71 100644 --- a/src/svg/qsvghandler.cpp +++ b/src/svg/qsvghandler.cpp @@ -3089,9 +3089,7 @@ static QSvgNode *createRectNode(QSvgNode *parent, nrx *= (100/(bounds.width()/2)); nry *= (100/(bounds.height()/2)); - QSvgNode *rect = new QSvgRect(parent, bounds, - int(nrx), - int(nry)); + QSvgNode *rect = new QSvgRect(parent, bounds, nrx, nry); return rect; } diff --git a/src/svg/qsvgnode_p.h b/src/svg/qsvgnode_p.h index df1341b..2c23b2f 100644 --- a/src/svg/qsvgnode_p.h +++ b/src/svg/qsvgnode_p.h @@ -133,6 +133,7 @@ public: void setXmlClass(const QString &str); bool shouldDrawNode(QPainter *p, QSvgExtraStates &states) const; + const QSvgStyle &style() const { return m_style; } protected: mutable QSvgStyle m_style; diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h index 20bd6d5..9581524 100644 --- a/src/svg/qsvgstyle_p.h +++ b/src/svg/qsvgstyle_p.h @@ -74,6 +74,8 @@ public: inline T *operator->() const { return t; } inline operator T*() const { return t; } + inline bool isDefault() const { return !t || t->isDefault(); } + private: T *t; }; @@ -138,6 +140,7 @@ public: virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) = 0; virtual void revert(QPainter *p, QSvgExtraStates &states) =0; virtual Type type() const=0; + bool isDefault() const { return false; } // [not virtual since called from templated class] }; class Q_SVG_PRIVATE_EXPORT QSvgFillStyleProperty : public QSvgStyleProperty @@ -197,6 +200,9 @@ public: void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) override; void revert(QPainter *p, QSvgExtraStates &states) override; Type type() const override; + qreal opacity() const { return m_opacity; } + bool isDefault() const { return qFuzzyCompare(m_opacity, 1.0); } + private: qreal m_opacity; qreal m_oldOpacity; @@ -597,6 +603,7 @@ public: { return m_transform; } + bool isDefault() const { return m_transform.isIdentity(); } private: //7.6 The transform attribute QTransform m_transform; diff --git a/src/svg/qsvgtinydocument_p.h b/src/svg/qsvgtinydocument_p.h index 7f0a9af..539db9c 100644 --- a/src/svg/qsvgtinydocument_p.h +++ b/src/svg/qsvgtinydocument_p.h @@ -111,6 +111,8 @@ private: const QSvg::FeatureSet m_featureSet; }; +Q_SVG_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSvgTinyDocument &doc); + inline QSize QSvgTinyDocument::size() const { if (m_size.isEmpty()) diff --git a/src/svg/qsvgvisitor.cpp b/src/svg/qsvgvisitor.cpp new file mode 100644 index 0000000..fe3ea72 --- /dev/null +++ b/src/svg/qsvgvisitor.cpp @@ -0,0 +1,119 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qsvgvisitor_p.h" + +QT_BEGIN_NAMESPACE + +void QSvgVisitor::traverse(const QSvgStructureNode *node) +{ + switch (node->type()) { + case QSvgNode::SWITCH: + if (!visitSwitchNodeStart(static_cast<const QSvgSwitch *>(node))) + return; + break; + case QSvgNode::DOC: + if (!visitDocumentNodeStart(static_cast<const QSvgTinyDocument *>(node))) + return; + break; + case QSvgNode::DEFS: + if (!visitDefsNodeStart(static_cast<const QSvgDefs *>(node))) + return; + break; + case QSvgNode::G: + if (!visitGroupNodeStart(static_cast<const QSvgG *>(node))) + return; + break; + default: + Q_UNREACHABLE(); + break; + } + + for (const auto *child : node->renderers()) { + switch (child->type()) { + case QSvgNode::SWITCH: + case QSvgNode::DOC: + case QSvgNode::DEFS: + case QSvgNode::G: + traverse(static_cast<const QSvgStructureNode *>(child)); + break; + case QSvgNode::ANIMATION: + visitAnimationNode(static_cast<const QSvgAnimation *>(child)); + break; + case QSvgNode::CIRCLE: + case QSvgNode::ELLIPSE: + visitEllipseNode(static_cast<const QSvgEllipse *>(child)); + break; + case QSvgNode::IMAGE: + visitImageNode(static_cast<const QSvgImage *>(child)); + break; + case QSvgNode::LINE: + visitLineNode(static_cast<const QSvgLine *>(child)); + break; + case QSvgNode::PATH: + visitPathNode(static_cast<const QSvgPath *>(child)); + break; + case QSvgNode::POLYGON: + visitPolygonNode(static_cast<const QSvgPolygon *>(child)); + break; + case QSvgNode::POLYLINE: + visitPolylineNode(static_cast<const QSvgPolyline *>(child)); + break; + case QSvgNode::RECT: + visitRectNode(static_cast<const QSvgRect *>(child)); + break; + case QSvgNode::TEXT: + case QSvgNode::TEXTAREA: + visitTextNode(static_cast<const QSvgText *>(child)); + break; + case QSvgNode::TSPAN: + visitTspanNode(static_cast<const QSvgTspan *>(child)); + break; + case QSvgNode::USE: + visitUseNode(static_cast<const QSvgUse *>(child)); + break; + case QSvgNode::VIDEO: + visitVideoNode(static_cast<const QSvgVideo *>(child)); + break; + + // Enum values that don't have any QSvgNode classes yet: + case QSvgNode::MASK: + case QSvgNode::SYMBOL: + case QSvgNode::MARKER: + case QSvgNode::PATTERN: + case QSvgNode::FILTER: + case QSvgNode::FEMERGE: + case QSvgNode::FEMERGENODE: + case QSvgNode::FECOLORMATRIX: + case QSvgNode::FEGAUSSIANBLUR: + case QSvgNode::FEOFFSET: + case QSvgNode::FECOMPOSITE: + case QSvgNode::FEFLOOD: + qDebug() << "Unhandled type in switch" << child->type(); + break; + + case QSvgNode::ARC: // Not used: to be removed + Q_UNREACHABLE(); + break; + } + } + switch (node->type()) { + case QSvgNode::SWITCH: + visitSwitchNodeEnd(static_cast<const QSvgSwitch *>(node)); + break; + case QSvgNode::DOC: + visitDocumentNodeEnd(static_cast<const QSvgTinyDocument *>(node)); + break; + case QSvgNode::DEFS: + visitDefsNodeEnd(static_cast<const QSvgDefs *>(node)); + break; + case QSvgNode::G: + visitGroupNodeEnd(static_cast<const QSvgG *>(node)); + break; + default: + Q_UNREACHABLE(); + break; + } +} + +QT_END_NAMESPACE diff --git a/src/svg/qsvgvisitor_p.h b/src/svg/qsvgvisitor_p.h new file mode 100644 index 0000000..dccff1a --- /dev/null +++ b/src/svg/qsvgvisitor_p.h @@ -0,0 +1,57 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QSVGVISITOR_P_H +#define QSVGVISITOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qsvgtinydocument_p.h> +#include <private/qsvghandler_p.h> +#include <private/qsvggraphics_p.h> +#include <private/qsvgstructure_p.h> + +QT_BEGIN_NAMESPACE + +class Q_SVG_PRIVATE_EXPORT QSvgVisitor { +public: + void traverse(const QSvgStructureNode *node); +protected: + virtual void visitNode(const QSvgNode *) {} + virtual bool visitStructureNodeStart(const QSvgStructureNode *node) { visitNode(node); return true; } + virtual void visitStructureNodeEnd(const QSvgStructureNode *) {} + virtual void visitAnimationNode(const QSvgAnimation *node) { visitNode(node); } + virtual void visitEllipseNode(const QSvgEllipse *node) { visitNode(node); } + virtual void visitImageNode(const QSvgImage *node) { visitNode(node); } + virtual void visitLineNode(const QSvgLine *node) { visitNode(node); } + virtual void visitPathNode(const QSvgPath *node) { visitNode(node); } + virtual void visitPolygonNode(const QSvgPolygon *node) { visitNode(node); } + virtual void visitPolylineNode(const QSvgPolyline *node) { visitNode(node); } + virtual void visitRectNode(const QSvgRect *node) { visitNode(node); } + virtual void visitTextNode(const QSvgText *node) { visitNode(node); } + virtual void visitTspanNode(const QSvgTspan *node) { visitNode(node); } + virtual void visitUseNode(const QSvgUse *node) { visitNode(node); } + virtual void visitVideoNode(const QSvgVideo *node) { visitNode(node); } + + virtual bool visitDocumentNodeStart(const QSvgTinyDocument *node) { return visitStructureNodeStart(node); } + virtual void visitDocumentNodeEnd(const QSvgTinyDocument *node) { visitStructureNodeEnd(node); } + virtual bool visitGroupNodeStart(const QSvgG *node) { return visitStructureNodeStart(node); } + virtual void visitGroupNodeEnd(const QSvgG *node) { visitStructureNodeEnd(node); } + virtual bool visitDefsNodeStart(const QSvgDefs *node) { return visitStructureNodeStart(node); } + virtual void visitDefsNodeEnd(const QSvgDefs *node) { visitStructureNodeEnd(node); }; + virtual bool visitSwitchNodeStart(const QSvgSwitch *node) { return visitStructureNodeStart(node); } + virtual void visitSwitchNodeEnd(const QSvgSwitch *node) { visitStructureNodeEnd(node); }; +}; + +QT_END_NAMESPACE + +#endif // QSVGVISITOR_P_H |