515void QQuickShapePath::setStrokeGradient(QQuickShapeGradient *gradient)
517 Q_D(QQuickShapePath);
518 if (d->sfp.strokeGradient != gradient) {
519 if (d->sfp.strokeGradient)
520 qmlobject_disconnect(d->sfp.strokeGradient, QQuickShapeGradient, SIGNAL(updated()),
521 this, QQuickShapePath, SLOT(_q_strokeGradientChanged()));
522 d->sfp.strokeGradient = gradient;
523 if (d->sfp.strokeGradient)
524 qmlobject_connect(d->sfp.strokeGradient, QQuickShapeGradient, SIGNAL(updated()),
525 this, QQuickShapePath, SLOT(_q_strokeGradientChanged()));
526 emit strokeGradientChanged();
527 d->dirty |= QQuickShapePathPrivate::DirtyStrokeGradient;
528 emit shapePathChanged();
567void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient)
569 Q_D(QQuickShapePath);
570 if (d->sfp.fillGradient != gradient) {
571 if (d->sfp.fillGradient)
572 qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
573 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
574 d->sfp.fillGradient = gradient;
575 if (d->sfp.fillGradient)
576 qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
577 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
578 emit fillGradientChanged();
579 d->dirty |= QQuickShapePathPrivate::DirtyFillGradient;
580 emit shapePathChanged();
637void QQuickShapePath::setFillItem(QQuickItem *fillItem)
639 Q_D(QQuickShapePath);
640 if (d->sfp.fillItem != fillItem) {
641 if (d->sfp.fillItem !=
nullptr) {
642 qmlobject_disconnect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
643 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
645 d->sfp.fillItem = fillItem;
646 if (d->sfp.fillItem !=
nullptr) {
647 qmlobject_connect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
648 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
650 emit fillItemChanged();
652 d->dirty |= QQuickShapePathPrivate::DirtyFillItem;
653 emit shapePathChanged();
1372static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
1374 QQuickShape *item =
static_cast<QQuickShape *>(property->object);
1375 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1376 QQuickShapePath *path = qobject_cast<QQuickShapePath *>(obj);
1378 QQuickShapePathPrivate::get(path)->dirty = QQuickShapePathPrivate::DirtyAll;
1382 QQuickItemPrivate::data_append(property, obj);
1384 if (path && d->componentComplete) {
1385 QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
1386 d->_q_shapePathChanged();
1474void QQuickShape::itemChange(ItemChange change,
const ItemChangeData &data)
1479 if (change == ItemVisibleHasChanged && data.boolValue)
1480 d->_q_shapePathChanged();
1481 else if (change == QQuickItem::ItemSceneChange) {
1482 for (
int i = 0; i < d->sp.size(); ++i)
1483 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
1484 d->_q_shapePathChanged();
1485 d->handleSceneChange(data.window);
1486 }
else if (change == ItemTransformHasChanged && d->rendererType == QQuickShape::GeometryRenderer) {
1487 bool cosmeticStrokeFound =
false;
1488 for (
int i = 0; i < d->sp.size(); ++i) {
1489 if (d->sp[i]->cosmeticStroke()) {
1490 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyStrokeWidth;
1491 cosmeticStrokeFound =
true;
1494 if (cosmeticStrokeFound)
1495 d->_q_shapePathChanged();
1498 QQuickItem::itemChange(change, data);
1501QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1507 if (d->renderer || d->rendererChanged) {
1508 if (!node || d->rendererChanged) {
1509 d->rendererChanged =
false;
1511 node = d->createNode();
1514 d->renderer->updateNode();
1517 QMatrix4x4 fillModeTransform;
1521 if (d->fillMode != NoResize) {
1522 xScale = width() / implicitWidth();
1523 yScale = height() / implicitHeight();
1525 if (d->fillMode == PreserveAspectFit)
1526 xScale = yScale = qMin(xScale, yScale);
1527 else if (d->fillMode == PreserveAspectCrop)
1528 xScale = yScale = qMax(xScale, yScale);
1529 fillModeTransform.scale(xScale, yScale);
1531 if (d->horizontalAlignment != AlignLeft || d->verticalAlignment != AlignTop) {
1534 qreal w = xScale * implicitWidth();
1535 qreal h = yScale * implicitHeight();
1536 if (d->horizontalAlignment == AlignRight)
1538 else if (d->horizontalAlignment == AlignHCenter)
1539 tx = (width() - w) / 2;
1540 if (d->verticalAlignment == AlignBottom)
1542 else if (d->verticalAlignment == AlignVCenter)
1543 ty = (height() - h) / 2;
1544 fillModeTransform.translate(tx / xScale, ty / yScale);
1547 QSGTransformNode *transformNode =
static_cast<QSGTransformNode *>(node);
1548 if (fillModeTransform != transformNode->matrix())
1549 transformNode->setMatrix(fillModeTransform);
1554QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
1556 QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
1558 QSGRendererInterface *ri = q->window()->rendererInterface();
1562 static const bool environmentPreferCurve =
1563 qEnvironmentVariable(
"QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String(
"curverenderer");
1565 switch (ri->graphicsApi()) {
1566 case QSGRendererInterface::Software:
1567 res = QQuickShape::SoftwareRenderer;
1570 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1571 if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) {
1572 res = QQuickShape::CurveRenderer;
1574 res = QQuickShape::GeometryRenderer;
1577 qWarning(
"No path backend for this graphics API yet");
1586void QQuickShapePrivate::createRenderer()
1589 QQuickShape::RendererType selectedType = selectRendererType();
1590 if (selectedType == QQuickShape::UnknownRenderer)
1593 rendererType = selectedType;
1594 rendererChanged =
true;
1597 q->setFlag(QQuickItem::ItemObservesViewport, rendererType == QQuickShape::GeometryRenderer);
1599 switch (selectedType) {
1600 case QQuickShape::SoftwareRenderer:
1601 renderer =
new QQuickShapeSoftwareRenderer;
1603 case QQuickShape::GeometryRenderer:
1604 renderer =
new QQuickShapeGenericRenderer(q);
1606 case QQuickShape::CurveRenderer:
1607 renderer =
new QQuickShapeCurveRenderer(q);
1616QSGNode *QQuickShapePrivate::createNode()
1619 QSGNode *node =
nullptr;
1620 if (!q->window() || !renderer)
1622 QSGRendererInterface *ri = q->window()->rendererInterface();
1626 QSGNode *pathNode =
nullptr;
1627 switch (ri->graphicsApi()) {
1628 case QSGRendererInterface::Software:
1629 pathNode =
new QQuickShapeSoftwareRenderNode(q);
1630 static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
1631 static_cast<QQuickShapeSoftwareRenderNode *>(pathNode));
1634 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1635 if (rendererType == QQuickShape::CurveRenderer) {
1636 pathNode =
new QSGNode;
1637 static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(pathNode);
1639 pathNode =
new QQuickShapeGenericNode;
1640 static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
1641 static_cast<QQuickShapeGenericNode *>(pathNode));
1644 qWarning(
"No path backend for this graphics API yet");
1650 node =
new QSGTransformNode;
1651 node->appendChildNode(pathNode);
1665void QQuickShapePrivate::sync()
1668 syncTimingActive = QQSHAPE_LOG_TIME_DIRTY_SYNC().isDebugEnabled();
1669 if (syncTimingActive)
1672 const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync);
1674 setStatus(QQuickShape::Processing);
1675 renderer->setAsyncCallback(asyncShapeReady,
this);
1678 const int count = sp.size();
1679 bool countChanged =
false;
1680 const qreal det = windowToItemTransform().determinant();
1681 const qreal adjTriangulationScale = triangulationScale /
1682 (qIsNaN(det) || qIsNull(det) ? qreal(1) : qSqrt(qAbs(det)));
1683 renderer->beginSync(count, &countChanged);
1685 qCDebug(lcShapeSync) <<
"syncing" << count <<
"path(s)";
1686 for (
int i = 0; i < count; ++i) {
1687 QQuickShapePath *p = sp[i];
1688 qCDebug(lcShapeSync) <<
"- syncing path:" << p;
1689 int &dirty(QQuickShapePathPrivate::get(p)->dirty);
1690 totalDirty |= dirty;
1692 if (dirty & (QQuickShapePathPrivate::DirtyPath | QQuickShapePathPrivate::DirtyTrim)) {
1693 qCDebug(lcShapeSync) <<
" - DirtyPath";
1694 renderer->setPath(i, p);
1696 if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) {
1697 qCDebug(lcShapeSync) <<
" - DirtyStrokeColor:" << p->strokeColor();
1698 renderer->setStrokeColor(i, p->strokeColor());
1700 if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) {
1702 if (p->cosmeticStroke() || QSGCurveStrokeNode::expandingStrokeEnabled()) {
1703 renderer->setTriangulationScale(i, adjTriangulationScale);
1704 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1705 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale"
1706 << triangulationScale <<
"adjusted to" << adjTriangulationScale;
1708 renderer->setTriangulationScale(i, triangulationScale);
1709 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1710 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale" << triangulationScale;
1712 renderer->setStrokeWidth(i, p->strokeWidth());
1713 renderer->setCosmeticStroke(i, p->cosmeticStroke());
1715 if (dirty & QQuickShapePathPrivate::DirtyFillColor)
1716 renderer->setFillColor(i, p->fillColor());
1717 if (dirty & QQuickShapePathPrivate::DirtyFillRule)
1718 renderer->setFillRule(i, p->fillRule());
1719 if (dirty & QQuickShapePathPrivate::DirtyStyle) {
1720 renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit());
1721 renderer->setCapStyle(i, p->capStyle());
1723 if (dirty & QQuickShapePathPrivate::DirtyDash)
1724 renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern());
1725 if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
1726 renderer->setFillGradient(i, p->fillGradient());
1727 if (dirty & QQuickShapePathPrivate::DirtyStrokeGradient)
1728 renderer->setStrokeGradient(i, p->strokeGradient());
1729 if (dirty & QQuickShapePathPrivate::DirtyFillTransform)
1730 renderer->setFillTransform(i, QQuickShapePathPrivate::get(p)->sfp.fillTransform);
1731 if (dirty & QQuickShapePathPrivate::DirtyVisible)
1732 renderer->setVisible(i, p->isVisible());
1733 if (dirty & QQuickShapePathPrivate::DirtyFillItem) {
1734 if (p->fillItem() ==
nullptr) {
1735 renderer->setFillTextureProvider(i,
nullptr);
1736 }
else if (p->fillItem()->isTextureProvider()) {
1737 renderer->setFillTextureProvider(i, p->fillItem());
1739 renderer->setFillTextureProvider(i,
nullptr);
1740 qWarning() <<
"QQuickShape: Fill item is not texture provider";
1747 syncTimingTotalDirty = totalDirty;
1748 if (syncTimingTotalDirty)
1751 syncTimingActive =
false;
1753 renderer->endSync(useAsync);
1756 setStatus(QQuickShape::Ready);
1757 if (syncTimingActive)
1758 qDebug(
"[Shape %p] [%d] [dirty=0x%x] update took %lld ms",
1759 q_func(), syncTimeCounter, syncTimingTotalDirty, syncTimer.elapsed());
1765 if (totalDirty || countChanged)