480void QQuickShapePath::setStrokeGradient(QQuickShapeGradient *gradient)
482 Q_D(QQuickShapePath);
483 if (d->sfp.strokeGradient != gradient) {
484 if (d->sfp.strokeGradient)
485 qmlobject_disconnect(d->sfp.strokeGradient, QQuickShapeGradient, SIGNAL(updated()),
486 this, QQuickShapePath, SLOT(_q_strokeGradientChanged()));
487 d->sfp.strokeGradient = gradient;
488 if (d->sfp.strokeGradient)
489 qmlobject_connect(d->sfp.strokeGradient, QQuickShapeGradient, SIGNAL(updated()),
490 this, QQuickShapePath, SLOT(_q_strokeGradientChanged()));
491 emit strokeGradientChanged();
492 d->dirty |= QQuickShapePathPrivate::DirtyStrokeGradient;
493 emit shapePathChanged();
532void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient)
534 Q_D(QQuickShapePath);
535 if (d->sfp.fillGradient != gradient) {
536 if (d->sfp.fillGradient)
537 qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
538 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
539 d->sfp.fillGradient = gradient;
540 if (d->sfp.fillGradient)
541 qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
542 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
543 emit fillGradientChanged();
544 d->dirty |= QQuickShapePathPrivate::DirtyFillGradient;
545 emit shapePathChanged();
602void QQuickShapePath::setFillItem(QQuickItem *fillItem)
604 Q_D(QQuickShapePath);
605 if (d->sfp.fillItem != fillItem) {
606 if (d->sfp.fillItem !=
nullptr) {
607 qmlobject_disconnect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
608 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
610 d->sfp.fillItem = fillItem;
611 if (d->sfp.fillItem !=
nullptr) {
612 qmlobject_connect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
613 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
615 emit fillItemChanged();
617 d->dirty |= QQuickShapePathPrivate::DirtyFillItem;
618 emit shapePathChanged();
1332static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
1334 QQuickShape *item =
static_cast<QQuickShape *>(property->object);
1335 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1336 QQuickShapePath *path = qobject_cast<QQuickShapePath *>(obj);
1338 QQuickShapePathPrivate::get(path)->dirty = QQuickShapePathPrivate::DirtyAll;
1342 QQuickItemPrivate::data_append(property, obj);
1344 if (path && d->componentComplete) {
1345 QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
1346 d->_q_shapePathChanged();
1434void QQuickShape::itemChange(ItemChange change,
const ItemChangeData &data)
1439 if (change == ItemVisibleHasChanged && data.boolValue)
1440 d->_q_shapePathChanged();
1441 else if (change == QQuickItem::ItemSceneChange) {
1442 for (
int i = 0; i < d->sp.size(); ++i)
1443 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
1444 d->_q_shapePathChanged();
1445 d->handleSceneChange(data.window);
1446 }
else if (change == ItemTransformHasChanged && d->rendererType == QQuickShape::GeometryRenderer) {
1447 bool cosmeticStrokeFound =
false;
1448 for (
int i = 0; i < d->sp.size(); ++i) {
1449 if (d->sp[i]->cosmeticStroke()) {
1450 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyStrokeWidth;
1451 cosmeticStrokeFound =
true;
1454 if (cosmeticStrokeFound)
1455 d->_q_shapePathChanged();
1458 QQuickItem::itemChange(change, data);
1461QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1467 if (d->renderer || d->rendererChanged) {
1468 if (!node || d->rendererChanged) {
1469 d->rendererChanged =
false;
1471 node = d->createNode();
1474 d->renderer->updateNode();
1477 QMatrix4x4 fillModeTransform;
1481 if (d->fillMode != NoResize) {
1482 xScale = width() / implicitWidth();
1483 yScale = height() / implicitHeight();
1485 if (d->fillMode == PreserveAspectFit)
1486 xScale = yScale = qMin(xScale, yScale);
1487 else if (d->fillMode == PreserveAspectCrop)
1488 xScale = yScale = qMax(xScale, yScale);
1489 fillModeTransform.scale(xScale, yScale);
1491 if (d->horizontalAlignment != AlignLeft || d->verticalAlignment != AlignTop) {
1494 qreal w = xScale * implicitWidth();
1495 qreal h = yScale * implicitHeight();
1496 if (d->horizontalAlignment == AlignRight)
1498 else if (d->horizontalAlignment == AlignHCenter)
1499 tx = (width() - w) / 2;
1500 if (d->verticalAlignment == AlignBottom)
1502 else if (d->verticalAlignment == AlignVCenter)
1503 ty = (height() - h) / 2;
1504 fillModeTransform.translate(tx / xScale, ty / yScale);
1507 QSGTransformNode *transformNode =
static_cast<QSGTransformNode *>(node);
1508 if (fillModeTransform != transformNode->matrix())
1509 transformNode->setMatrix(fillModeTransform);
1514QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
1516 QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
1518 QSGRendererInterface *ri = q->window()->rendererInterface();
1522 static const bool environmentPreferCurve =
1523 qEnvironmentVariable(
"QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String(
"curverenderer");
1525 switch (ri->graphicsApi()) {
1526 case QSGRendererInterface::Software:
1527 res = QQuickShape::SoftwareRenderer;
1530 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1531 if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) {
1532 res = QQuickShape::CurveRenderer;
1534 res = QQuickShape::GeometryRenderer;
1537 qWarning(
"No path backend for this graphics API yet");
1546void QQuickShapePrivate::createRenderer()
1549 QQuickShape::RendererType selectedType = selectRendererType();
1550 if (selectedType == QQuickShape::UnknownRenderer)
1553 rendererType = selectedType;
1554 rendererChanged =
true;
1557 q->setFlag(QQuickItem::ItemObservesViewport, rendererType == QQuickShape::GeometryRenderer);
1559 switch (selectedType) {
1560 case QQuickShape::SoftwareRenderer:
1561 renderer =
new QQuickShapeSoftwareRenderer;
1563 case QQuickShape::GeometryRenderer:
1564 renderer =
new QQuickShapeGenericRenderer(q);
1566 case QQuickShape::CurveRenderer:
1567 renderer =
new QQuickShapeCurveRenderer(q);
1576QSGNode *QQuickShapePrivate::createNode()
1579 QSGNode *node =
nullptr;
1580 if (!q->window() || !renderer)
1582 QSGRendererInterface *ri = q->window()->rendererInterface();
1586 QSGNode *pathNode =
nullptr;
1587 switch (ri->graphicsApi()) {
1588 case QSGRendererInterface::Software:
1589 pathNode =
new QQuickShapeSoftwareRenderNode(q);
1590 static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
1591 static_cast<QQuickShapeSoftwareRenderNode *>(pathNode));
1594 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1595 if (rendererType == QQuickShape::CurveRenderer) {
1596 pathNode =
new QSGNode;
1597 static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(pathNode);
1599 pathNode =
new QQuickShapeGenericNode;
1600 static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
1601 static_cast<QQuickShapeGenericNode *>(pathNode));
1604 qWarning(
"No path backend for this graphics API yet");
1610 node =
new QSGTransformNode;
1611 node->appendChildNode(pathNode);
1625void QQuickShapePrivate::sync()
1628 syncTimingActive = QQSHAPE_LOG_TIME_DIRTY_SYNC().isDebugEnabled();
1629 if (syncTimingActive)
1632 const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync);
1634 setStatus(QQuickShape::Processing);
1635 renderer->setAsyncCallback(asyncShapeReady,
this);
1638 const int count = sp.size();
1639 bool countChanged =
false;
1640 const qreal det = windowToItemTransform().determinant();
1641 const qreal adjTriangulationScale = triangulationScale /
1642 (qIsNaN(det) || qIsNull(det) ? qreal(1) : qSqrt(qAbs(det)));
1643 renderer->beginSync(count, &countChanged);
1645 qCDebug(lcShapeSync) <<
"syncing" << count <<
"path(s)";
1646 for (
int i = 0; i < count; ++i) {
1647 QQuickShapePath *p = sp[i];
1648 qCDebug(lcShapeSync) <<
"- syncing path:" << p;
1649 int &dirty(QQuickShapePathPrivate::get(p)->dirty);
1650 totalDirty |= dirty;
1652 if (dirty & (QQuickShapePathPrivate::DirtyPath | QQuickShapePathPrivate::DirtyTrim)) {
1653 qCDebug(lcShapeSync) <<
" - DirtyPath";
1654 renderer->setPath(i, p);
1656 if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) {
1657 qCDebug(lcShapeSync) <<
" - DirtyStrokeColor:" << p->strokeColor();
1658 renderer->setStrokeColor(i, p->strokeColor());
1660 if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) {
1662 if (p->cosmeticStroke() || QSGCurveStrokeNode::expandingStrokeEnabled()) {
1663 renderer->setTriangulationScale(i, adjTriangulationScale);
1664 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1665 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale"
1666 << triangulationScale <<
"adjusted to" << adjTriangulationScale;
1668 renderer->setTriangulationScale(i, triangulationScale);
1669 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1670 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale" << triangulationScale;
1672 renderer->setStrokeWidth(i, p->strokeWidth());
1673 renderer->setCosmeticStroke(i, p->cosmeticStroke());
1675 if (dirty & QQuickShapePathPrivate::DirtyFillColor)
1676 renderer->setFillColor(i, p->fillColor());
1677 if (dirty & QQuickShapePathPrivate::DirtyFillRule)
1678 renderer->setFillRule(i, p->fillRule());
1679 if (dirty & QQuickShapePathPrivate::DirtyStyle) {
1680 renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit());
1681 renderer->setCapStyle(i, p->capStyle());
1683 if (dirty & QQuickShapePathPrivate::DirtyDash)
1684 renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern());
1685 if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
1686 renderer->setFillGradient(i, p->fillGradient());
1687 if (dirty & QQuickShapePathPrivate::DirtyStrokeGradient)
1688 renderer->setStrokeGradient(i, p->strokeGradient());
1689 if (dirty & QQuickShapePathPrivate::DirtyFillTransform)
1690 renderer->setFillTransform(i, QQuickShapePathPrivate::get(p)->sfp.fillTransform);
1691 if (dirty & QQuickShapePathPrivate::DirtyFillItem) {
1692 if (p->fillItem() ==
nullptr) {
1693 renderer->setFillTextureProvider(i,
nullptr);
1694 }
else if (p->fillItem()->isTextureProvider()) {
1695 renderer->setFillTextureProvider(i, p->fillItem());
1697 renderer->setFillTextureProvider(i,
nullptr);
1698 qWarning() <<
"QQuickShape: Fill item is not texture provider";
1705 syncTimingTotalDirty = totalDirty;
1706 if (syncTimingTotalDirty)
1709 syncTimingActive =
false;
1711 renderer->endSync(useAsync);
1714 setStatus(QQuickShape::Ready);
1715 if (syncTimingActive)
1716 qDebug(
"[Shape %p] [%d] [dirty=0x%x] update took %lld ms",
1717 q_func(), syncTimeCounter, syncTimingTotalDirty, syncTimer.elapsed());
1723 if (totalDirty || countChanged)