aboutsummaryrefslogtreecommitdiffstats
path: root/src/quickvectorimage/generator/qquickitemgenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quickvectorimage/generator/qquickitemgenerator.cpp')
-rw-r--r--src/quickvectorimage/generator/qquickitemgenerator.cpp393
1 files changed, 393 insertions, 0 deletions
diff --git a/src/quickvectorimage/generator/qquickitemgenerator.cpp b/src/quickvectorimage/generator/qquickitemgenerator.cpp
new file mode 100644
index 0000000000..171cb6889f
--- /dev/null
+++ b/src/quickvectorimage/generator/qquickitemgenerator.cpp
@@ -0,0 +1,393 @@
+// 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
+
+#include "qquickitemgenerator_p.h"
+#include "utils_p.h"
+#include "qquicknodeinfo_p.h"
+
+#include <private/qsgcurveprocessor_p.h>
+#include <private/qquickshape_p.h>
+#include <private/qquadpath_p.h>
+#include <private/qquickitem_p.h>
+#include <private/qquickimagebase_p_p.h>
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcQuickVectorImage)
+
+QQuickItemGenerator::QQuickItemGenerator(const QString fileName, QQuickVectorImageGenerator::GeneratorFlags flags, QQuickItem *parentItem)
+ :QQuickGenerator(fileName, flags)
+{
+ Q_ASSERT(parentItem);
+ m_items.push(parentItem);
+ m_parentItem = parentItem;
+}
+
+QQuickItemGenerator::~QQuickItemGenerator()
+{
+}
+
+void QQuickItemGenerator::generateNodeBase(const NodeInfo &info)
+{
+ if (!info.isDefaultTransform) {
+ auto sx = info.transform.m11();
+ auto sy = info.transform.m22();
+ auto x = info.transform.m31();
+ auto y = info.transform.m32();
+
+ auto xformProp = currentItem()->transform();
+ if (info.transform.type() == QTransform::TxTranslate) {
+ auto *translate = new QQuickTranslate;
+ translate->setX(x);
+ translate->setY(y);
+ xformProp.append(&xformProp, translate);
+ } else if (info.transform.type() == QTransform::TxScale && !x && !y) {
+ auto scale = new QQuickScale;
+ scale->setParent(currentItem());
+ scale->setXScale(sx);
+ scale->setYScale(sy);
+ xformProp.append(&xformProp, scale);
+ } else {
+ const QMatrix4x4 m(info.transform);
+ auto xform = new QQuickMatrix4x4;
+ xform->setMatrix(m);
+ xformProp.append(&xformProp, xform);
+ }
+ }
+ if (!info.isDefaultOpacity) {
+ currentItem()->setOpacity(info.opacity);
+ }
+}
+
+bool QQuickItemGenerator::generateDefsNode(const NodeInfo &info)
+{
+ Q_UNUSED(info)
+
+ return false;
+}
+
+void QQuickItemGenerator::generateImageNode(const ImageNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ auto *imageItem = new QQuickImage;
+ auto *imagePriv = static_cast<QQuickImageBasePrivate*>(QQuickItemPrivate::get(imageItem));
+ imagePriv->pix.setImage(info.image);
+
+ imageItem->setX(info.rect.x());
+ imageItem->setY(info.rect.y());
+ imageItem->setWidth(info.rect.width());
+ imageItem->setHeight(info.rect.height());
+
+ generateNodeBase(info);
+
+ addCurrentItem(imageItem, info);
+ m_items.pop();
+}
+
+void QQuickItemGenerator::generatePath(const PathNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ if (m_inShapeItem) {
+ if (!info.isDefaultTransform)
+ qCWarning(lcQuickVectorImage) << "Skipped transform for node" << info.nodeId << "type" << info.typeName << "(this is not supposed to happen)";
+ optimizePaths(info);
+ } else {
+ auto *shapeItem = new QQuickShape;
+ if (m_flags.testFlag(QQuickVectorImageGenerator::GeneratorFlag::CurveRenderer))
+ shapeItem->setPreferredRendererType(QQuickShape::CurveRenderer);
+ shapeItem->setContainsMode(QQuickShape::ContainsMode::FillContains); // TODO: configurable?
+ addCurrentItem(shapeItem, info);
+ m_parentShapeItem = shapeItem;
+ m_inShapeItem = true;
+
+ generateNodeBase(info);
+
+ optimizePaths(info);
+ //qCDebug(lcQuickVectorGraphics) << *node->qpath();
+ m_items.pop();
+ m_inShapeItem = false;
+ m_parentShapeItem = nullptr;
+ }
+}
+
+void QQuickItemGenerator::outputShapePath(const PathNodeInfo &info, const QPainterPath *painterPath, const QQuadPath *quadPath, QQuickVectorImageGenerator::PathSelector pathSelector, const QRectF &boundingRect)
+{
+ Q_UNUSED(pathSelector)
+ Q_ASSERT(painterPath || quadPath);
+
+ const bool noPen = info.strokeStyle.color == QColorConstants::Transparent;
+ if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
+ return;
+
+ const bool noFill = info.grad.type() == QGradient::NoGradient && info.fillColor == QColorConstants::Transparent;
+
+ if (pathSelector == QQuickVectorImageGenerator::FillPath && noFill)
+ return;
+
+ QQuickShapePath::FillRule fillRule = QQuickShapePath::FillRule(painterPath ? painterPath->fillRule() : quadPath->fillRule());
+
+ QQuickShapePath *shapePath = new QQuickShapePath;
+ Q_ASSERT(shapePath);
+
+ if (!info.nodeId.isEmpty())
+ shapePath->setObjectName(QStringLiteral("svg_path:") + info.nodeId);
+
+ if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
+ shapePath->setStrokeColor(Qt::transparent);
+ } else {
+ shapePath->setStrokeColor(info.strokeStyle.color);
+ shapePath->setStrokeWidth(info.strokeStyle.width);
+ shapePath->setCapStyle(QQuickShapePath::CapStyle(info.strokeStyle.lineCapStyle));
+ shapePath->setJoinStyle(QQuickShapePath::JoinStyle(info.strokeStyle.lineJoinStyle));
+ shapePath->setMiterLimit(info.strokeStyle.miterLimit);
+ if (info.strokeStyle.dashArray.length() != 0) {
+ shapePath->setStrokeStyle(QQuickShapePath::DashLine);
+ shapePath->setDashPattern(info.strokeStyle.dashArray.toVector());
+ shapePath->setDashOffset(info.strokeStyle.dashOffset);
+ }
+ }
+
+ if (!(pathSelector & QQuickVectorImageGenerator::FillPath))
+ shapePath->setFillColor(Qt::transparent);
+ else if (info.grad.type() != QGradient::NoGradient)
+ generateGradient(&info.grad, shapePath, boundingRect);
+ else
+ shapePath->setFillColor(info.fillColor);
+
+ shapePath->setFillRule(fillRule);
+
+ QString svgPathString = painterPath ? QQuickVectorImageGenerator::Utils::toSvgString(*painterPath) : QQuickVectorImageGenerator::Utils::toSvgString(*quadPath);
+
+ auto *pathSvg = new QQuickPathSvg;
+ pathSvg->setPath(svgPathString);
+ pathSvg->setParent(shapePath);
+
+ auto pathElementProp = shapePath->pathElements();
+ pathElementProp.append(&pathElementProp, pathSvg);
+
+ shapePath->setParent(currentItem());
+ auto shapeDataProp = m_parentShapeItem->data();
+ shapeDataProp.append(&shapeDataProp, shapePath);
+}
+
+void QQuickItemGenerator::generateGradient(const QGradient *grad, QQuickShapePath *shapePath, const QRectF &boundingRect)
+{
+ if (!shapePath)
+ return;
+
+ auto setStops = [=](QQuickShapeGradient *quickGrad, const QGradientStops &stops) {
+ auto stopsProp = quickGrad->stops();
+ for (auto &stop : stops) {
+ auto *stopObj = new QQuickGradientStop(quickGrad);
+ stopObj->setPosition(stop.first);
+ stopObj->setColor(stop.second);
+ stopsProp.append(&stopsProp, stopObj);
+ }
+ };
+
+ if (grad->type() == QGradient::LinearGradient) {
+ auto *linGrad = static_cast<const QLinearGradient *>(grad);
+
+ QRectF gradRect(linGrad->start(), linGrad->finalStop());
+ QRectF logRect = linGrad->coordinateMode() == QGradient::LogicalMode ? gradRect : QQuickVectorImageGenerator::Utils::mapToQtLogicalMode(gradRect, boundingRect);
+
+ auto *quickGrad = new QQuickShapeLinearGradient(shapePath);
+ quickGrad->setX1(logRect.left());
+ quickGrad->setY1(logRect.top());
+ quickGrad->setX2(logRect.right());
+ quickGrad->setY2(logRect.bottom());
+ setStops(quickGrad, linGrad->stops());
+
+ shapePath->setFillGradient(quickGrad);
+ } else if (grad->type() == QGradient::RadialGradient) {
+ auto *radGrad = static_cast<const QRadialGradient*>(grad);
+ auto *quickGrad = new QQuickShapeRadialGradient(shapePath);
+ quickGrad->setCenterX(radGrad->center().x());
+ quickGrad->setCenterY(radGrad->center().y());
+ quickGrad->setCenterRadius(radGrad->radius());
+ quickGrad->setFocalX(radGrad->focalPoint().x());
+ quickGrad->setFocalY(radGrad->focalPoint().y());
+ setStops(quickGrad, radGrad->stops());
+
+ shapePath->setFillGradient(quickGrad);
+ }
+}
+
+void QQuickItemGenerator::generateNode(const NodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ qCWarning(lcQuickVectorImage) << "SVG NODE NOT IMPLEMENTED: "
+ << info.nodeId
+ << " type: " << info.typeName;
+}
+
+void QQuickItemGenerator::generateTextNode(const TextNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ QQuickItem *alignItem = nullptr;
+ QQuickText *textItem = nullptr;
+
+ QQuickItem *containerItem = new QQuickItem(currentItem());
+ addCurrentItem(containerItem, info);
+
+ generateNodeBase(info);
+
+ if (!info.isTextArea) {
+ alignItem = new QQuickItem(currentItem());
+ alignItem->setX(info.position.x());
+ alignItem->setY(info.position.y());
+ }
+
+ textItem = new QQuickText(containerItem);
+ addCurrentItem(textItem, info);
+
+ if (info.isTextArea) {
+ textItem->setX(info.position.x());
+ textItem->setY(info.position.y());
+ if (info.size.width() > 0)
+ textItem->setWidth(info.size.width());
+ if (info.size.height() > 0)
+ textItem->setHeight(info.size.height());
+ textItem->setWrapMode(QQuickText::Wrap);
+ textItem->setClip(true);
+ } else {
+ auto *anchors = QQuickItemPrivate::get(textItem)->anchors();
+ auto *alignPrivate = QQuickItemPrivate::get(alignItem);
+ anchors->setBaseline(alignPrivate->top());
+
+ switch (info.alignment) {
+ case Qt::AlignHCenter:
+ anchors->setHorizontalCenter(alignPrivate->left());
+ break;
+ case Qt::AlignRight:
+ anchors->setRight(alignPrivate->left());
+ break;
+ default:
+ qCDebug(lcQuickVectorImage) << "Unexpected text alignment" << info.alignment;
+ Q_FALLTHROUGH();
+ case Qt::AlignLeft:
+ anchors->setLeft(alignPrivate->left());
+ break;
+ }
+ }
+
+ textItem->setColor(info.fillColor);
+ textItem->setTextFormat(info.needsRichText ? QQuickText::RichText : QQuickText::StyledText);
+ textItem->setText(info.text);
+ textItem->setFont(info.font);
+
+ if (info.strokeColor != QColorConstants::Transparent) {
+ textItem->setStyleColor(info.strokeColor);
+ textItem->setStyle(QQuickText::Outline);
+ }
+
+ m_items.pop(); m_items.pop();
+}
+
+void QQuickItemGenerator::generateUseNode(const UseNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return;
+
+ if (info.stage == StructureNodeStage::Start) {
+ QQuickItem *item = new QQuickItem();
+ item->setPosition(info.startPos);
+ addCurrentItem(item, info);
+ generateNodeBase(info);
+ } else {
+ m_items.pop();
+ }
+
+}
+
+bool QQuickItemGenerator::generateStructureNode(const StructureNodeInfo &info)
+{
+ if (!isNodeVisible(info))
+ return false;
+
+ if (info.stage == StructureNodeStage::Start) {
+ if (!info.forceSeparatePaths && info.isPathContainer) {
+ m_inShapeItem = true;
+ auto *shapeItem = new QQuickShape;
+ if (m_flags.testFlag(QQuickVectorImageGenerator::GeneratorFlag::CurveRenderer))
+ shapeItem->setPreferredRendererType(QQuickShape::CurveRenderer);
+ m_parentShapeItem = shapeItem;
+ addCurrentItem(shapeItem, info);
+ } else {
+ QQuickItem *item = !info.viewBox.isEmpty() ? new QQuickVectorImageGenerator::Utils::ViewBoxItem(info.viewBox) : new QQuickItem;
+ addCurrentItem(item, info);
+ }
+
+ generateNodeBase(info);
+ } else {
+ m_inShapeItem = false;
+ m_parentShapeItem = nullptr;
+ m_items.pop();
+ }
+
+ return true;
+}
+
+bool QQuickItemGenerator::generateRootNode(const StructureNodeInfo &info)
+{
+ if (!isNodeVisible(info)) {
+ QQuickItem *item = new QQuickItem();
+ item->setParentItem(m_parentItem);
+
+ if (info.size.width() > 0)
+ m_parentItem->setImplicitWidth(info.size.width());
+
+ if (info.size.height() > 0)
+ m_parentItem->setImplicitHeight(info.size.height());
+
+ item->setWidth(m_parentItem->implicitWidth());
+ item->setHeight(m_parentItem->implicitHeight());
+
+ return false;
+ }
+
+ if (info.stage == StructureNodeStage::Start) {
+ QQuickItem *item = !info.viewBox.isEmpty() ? new QQuickVectorImageGenerator::Utils::ViewBoxItem(info.viewBox) : new QQuickItem;
+ addCurrentItem(item, info);
+ if (info.size.width() > 0)
+ m_parentItem->setImplicitWidth(info.size.width());
+
+ if (info.size.height() > 0)
+ m_parentItem->setImplicitHeight(info.size.height());
+
+ item->setWidth(m_parentItem->implicitWidth());
+ item->setHeight(m_parentItem->implicitHeight());
+ generateNodeBase(info);
+ } else {
+ m_inShapeItem = false;
+ m_parentShapeItem = nullptr;
+ m_items.pop();
+ }
+
+ return true;
+}
+
+QQuickItem *QQuickItemGenerator::currentItem()
+{
+ return m_items.top();
+}
+
+void QQuickItemGenerator::addCurrentItem(QQuickItem *item, const NodeInfo &info)
+{
+ item->setParentItem(currentItem());
+ m_items.push(item);
+ QStringView name = !info.nodeId.isEmpty() ? info.nodeId : info.typeName;
+ item->setObjectName(name);
+}
+
+QT_END_NAMESPACE