diff options
Diffstat (limited to 'src/quickvectorimage/generator/utils_p.h')
-rw-r--r-- | src/quickvectorimage/generator/utils_p.h | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/src/quickvectorimage/generator/utils_p.h b/src/quickvectorimage/generator/utils_p.h new file mode 100644 index 0000000000..bb65ee2b69 --- /dev/null +++ b/src/quickvectorimage/generator/utils_p.h @@ -0,0 +1,264 @@ +// Copyright (C) 2024 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 UTILS_P_H +#define UTILS_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/qquicktranslate_p.h> +#include <private/qquickitem_p.h> +#include <private/qsvgnode_p.h> + +#include <private/qquadpath_p.h> +#include <private/qsvgvisitor_p.h> + +QT_BEGIN_NAMESPACE + +namespace QQuickVectorImageGenerator::Utils +{ + +class ViewBoxItem : public QQuickItem +{ +public: + ViewBoxItem(const QRectF viewBox, QQuickItem *parent = nullptr) : QQuickItem(parent), m_viewBox(viewBox) { setXForm(); } + +protected: + void geometryChange(const QRectF &/*newGeometry*/, const QRectF &/*oldGeometry*/) override + { + setXForm(); + } + +private: + void setXForm() + { + auto xformProp = transform(); + xformProp.clear(&xformProp); + bool translate = !qFuzzyIsNull(m_viewBox.x()) || !qFuzzyIsNull(m_viewBox.y()); + if (translate) { + auto *tr = new QQuickTranslate(this); + tr->setX(-m_viewBox.x()); + tr->setY(-m_viewBox.y()); + xformProp.append(&xformProp, tr); + } + if (!m_viewBox.isEmpty() && width() && height()) { + auto *scale = new QQuickScale(this); + qreal sx = width() / m_viewBox.width(); + qreal sy = height() / m_viewBox.height(); + + scale->setXScale(sx); + scale->setYScale(sy); + xformProp.append(&xformProp, scale); + } + } + QRectF m_viewBox; +}; + +inline QPainterPath polygonToPath(const QPolygonF &poly, bool closed) +{ + QPainterPath path; + if (poly.isEmpty()) + return path; + bool first = true; + for (const auto &p : poly) { + if (first) + path.moveTo(p); + else + path.lineTo(p); + first = false; + } + if (closed) + path.closeSubpath(); + return path; +} + +inline QString pathHintString(const QQuadPath &qp) +{ + QString res; + QTextStream str(&res); + auto flags = qp.pathHints(); + if (!flags) + return res; + str << "pathHints:"; + bool first = true; + +#define CHECK_PATH_HINT(flagName) \ + if (flags.testFlag(QQuadPath::flagName)) { \ + if (!first) \ + str << " |"; \ + first = false; \ + str << " ShapePath." #flagName; \ + } + + CHECK_PATH_HINT(PathLinear) + CHECK_PATH_HINT(PathQuadratic) + CHECK_PATH_HINT(PathConvex) + CHECK_PATH_HINT(PathFillOnRight) + CHECK_PATH_HINT(PathSolid) + CHECK_PATH_HINT(PathNonIntersecting) + CHECK_PATH_HINT(PathNonOverlappingControlPointTriangles) + + return res; +} + +// Find the square that gives the same gradient in QGradient::LogicalMode as +// objModeRect does in QGradient::ObjectMode + +// When the object's bounding box is not square, the stripes that are conceptually +// perpendicular to the gradient vector within object bounding box space shall render +// non-perpendicular relative to the gradient vector in user space due to application +// of the non-uniform scaling transformation from bounding box space to user space. +inline QRectF mapToQtLogicalMode(const QRectF &objModeRect, const QRectF &boundingRect) +{ + + QRect pixelRect(objModeRect.x() * boundingRect.width() + boundingRect.left(), + objModeRect.y() * boundingRect.height() + boundingRect.top(), + objModeRect.width() * boundingRect.width(), + objModeRect.height() * boundingRect.height()); + + if (pixelRect.isEmpty()) // pure horizontal/vertical gradient + return pixelRect; + + double w = boundingRect.width(); + double h = boundingRect.height(); + double objModeSlope = objModeRect.height() / objModeRect.width(); + double a = objModeSlope * w / h; + + // do calculation with origin == pixelRect.topLeft + double x2 = pixelRect.width(); + double y2 = pixelRect.height(); + double x = (x2 + a * y2) / (1 + a * a); + double y = y2 - (x - x2)/a; + + return QRectF(pixelRect.topLeft(), QSizeF(x,y)); +} + +inline QString toSvgString(const QPainterPath &path) +{ + QString svgPathString; + QTextStream strm(&svgPathString); + + for (int i = 0; i < path.elementCount(); ++i) { + QPainterPath::Element element = path.elementAt(i); + if (element.isMoveTo()) { + strm << "M " << element.x << " " << element.y << " "; + } else if (element.isLineTo()) { + strm << "L " << element.x << " " << element.y << " "; + } else if (element.isCurveTo()) { + QPointF c1(element.x, element.y); + ++i; + element = path.elementAt(i); + + QPointF c2(element.x, element.y); + ++i; + element = path.elementAt(i); + QPointF ep(element.x, element.y); + + strm << "C " + << c1.x() << " " + << c1.y() << " " + << c2.x() << " " + << c2.y() << " " + << ep.x() << " " + << ep.y() << " "; + } + } + + return svgPathString; +} + +inline QString toSvgString(const QQuadPath &path) +{ + QString svgPathString; + QTextStream strm(&svgPathString); + path.iterateElements([&](const QQuadPath::Element &e, int) { + if (e.isSubpathStart()) + strm << "M " << e.startPoint().x() << " " << e.startPoint().y() << " "; + if (e.isLine()) + strm << "L " << e.endPoint().x() << " " << e.endPoint().y() << " "; + else + strm << "Q " << e.controlPoint().x() << " " << e.controlPoint().y() << " " + << e.endPoint().x() << " " << e.endPoint().y() << " "; + }); + + return svgPathString; +} + +inline QString strokeCapStyleString(Qt::PenCapStyle strokeCapStyle) +{ + QString capStyle; + switch (strokeCapStyle) { + case Qt::FlatCap: + capStyle = QStringLiteral("ShapePath.FlatCap"); + break; + case Qt::SquareCap: + capStyle = QStringLiteral("ShapePath.SquareCap"); + break; + case Qt::RoundCap: + capStyle = QStringLiteral("ShapePath.RoundCap"); + break; + default: + Q_UNREACHABLE(); + break; + } + + return capStyle; +} + +inline QString strokeJoinStyleString(Qt::PenJoinStyle strokeJoinStyle) +{ + QString joinStyle; + switch (strokeJoinStyle) { + case Qt::MiterJoin: + joinStyle = QStringLiteral("ShapePath.MiterJoin"); + break; + case Qt::BevelJoin: + joinStyle = QStringLiteral("ShapePath.BevelJoin"); + break; + case Qt::RoundJoin: + joinStyle = QStringLiteral("ShapePath.RoundJoin"); + break; + default: + //TODO: Add support for SvgMiter case + Q_UNREACHABLE(); + break; + } + + return joinStyle; +} + +template<typename T> +inline QString listString(QList<T> list) +{ + if (list.isEmpty()) + return QStringLiteral("[]"); + + QString listString; + QTextStream stream(&listString); + stream << "["; + + if (list.length() > 1) { + for (int i = 0; i < list.length() - 1; i++) { + T v = list[i]; + stream << v << ", "; + } + } + + stream << list.last() << "]"; + return listString; +} + +} + +QT_END_NAMESPACE + +#endif // UTILS_P_H |