479void QQuickShapePath::setStrokeGradient(QQuickShapeGradient *gradient)
481 Q_D(QQuickShapePath);
482 if (d->sfp.strokeGradient != gradient) {
483 if (d->sfp.strokeGradient)
484 qmlobject_disconnect(d->sfp.strokeGradient, QQuickShapeGradient, SIGNAL(updated()),
485 this, QQuickShapePath, SLOT(_q_strokeGradientChanged()));
486 d->sfp.strokeGradient = gradient;
487 if (d->sfp.strokeGradient)
488 qmlobject_connect(d->sfp.strokeGradient, QQuickShapeGradient, SIGNAL(updated()),
489 this, QQuickShapePath, SLOT(_q_strokeGradientChanged()));
490 emit strokeGradientChanged();
491 d->dirty |= QQuickShapePathPrivate::DirtyStrokeGradient;
492 emit shapePathChanged();
531void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient)
533 Q_D(QQuickShapePath);
534 if (d->sfp.fillGradient != gradient) {
535 if (d->sfp.fillGradient)
536 qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
537 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
538 d->sfp.fillGradient = gradient;
539 if (d->sfp.fillGradient)
540 qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
541 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
542 emit fillGradientChanged();
543 d->dirty |= QQuickShapePathPrivate::DirtyFillGradient;
544 emit shapePathChanged();
601void QQuickShapePath::setFillItem(QQuickItem *fillItem)
603 Q_D(QQuickShapePath);
604 if (d->sfp.fillItem != fillItem) {
605 if (d->sfp.fillItem !=
nullptr) {
606 qmlobject_disconnect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
607 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
609 d->sfp.fillItem = fillItem;
610 if (d->sfp.fillItem !=
nullptr) {
611 qmlobject_connect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
612 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
614 emit fillItemChanged();
616 d->dirty |= QQuickShapePathPrivate::DirtyFillItem;
617 emit shapePathChanged();
1331static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
1333 QQuickShape *item =
static_cast<QQuickShape *>(property->object);
1334 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1335 QQuickShapePath *path = qobject_cast<QQuickShapePath *>(obj);
1337 QQuickShapePathPrivate::get(path)->dirty = QQuickShapePathPrivate::DirtyAll;
1341 QQuickItemPrivate::data_append(property, obj);
1343 if (path && d->componentComplete) {
1344 QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
1345 d->_q_shapePathChanged();
1433void QQuickShape::itemChange(ItemChange change,
const ItemChangeData &data)
1438 if (change == ItemVisibleHasChanged && data.boolValue)
1439 d->_q_shapePathChanged();
1440 else if (change == QQuickItem::ItemSceneChange) {
1441 for (
int i = 0; i < d->sp.size(); ++i)
1442 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
1443 d->_q_shapePathChanged();
1444 d->handleSceneChange(data.window);
1445 }
else if (change == ItemTransformHasChanged && d->rendererType == QQuickShape::GeometryRenderer) {
1446 bool cosmeticStrokeFound =
false;
1447 for (
int i = 0; i < d->sp.size(); ++i) {
1448 if (d->sp[i]->cosmeticStroke()) {
1449 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyStrokeWidth;
1450 cosmeticStrokeFound =
true;
1453 if (cosmeticStrokeFound)
1454 d->_q_shapePathChanged();
1457 QQuickItem::itemChange(change, data);
1460QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1466 if (d->renderer || d->rendererChanged) {
1467 if (!node || d->rendererChanged) {
1468 d->rendererChanged =
false;
1470 node = d->createNode();
1473 d->renderer->updateNode();
1476 QMatrix4x4 fillModeTransform;
1480 if (d->fillMode != NoResize) {
1481 xScale = width() / implicitWidth();
1482 yScale = height() / implicitHeight();
1484 if (d->fillMode == PreserveAspectFit)
1485 xScale = yScale = qMin(xScale, yScale);
1486 else if (d->fillMode == PreserveAspectCrop)
1487 xScale = yScale = qMax(xScale, yScale);
1488 fillModeTransform.scale(xScale, yScale);
1490 if (d->horizontalAlignment != AlignLeft || d->verticalAlignment != AlignTop) {
1493 qreal w = xScale * implicitWidth();
1494 qreal h = yScale * implicitHeight();
1495 if (d->horizontalAlignment == AlignRight)
1497 else if (d->horizontalAlignment == AlignHCenter)
1498 tx = (width() - w) / 2;
1499 if (d->verticalAlignment == AlignBottom)
1501 else if (d->verticalAlignment == AlignVCenter)
1502 ty = (height() - h) / 2;
1503 fillModeTransform.translate(tx / xScale, ty / yScale);
1506 QSGTransformNode *transformNode =
static_cast<QSGTransformNode *>(node);
1507 if (fillModeTransform != transformNode->matrix())
1508 transformNode->setMatrix(fillModeTransform);
1513QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
1515 QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
1517 QSGRendererInterface *ri = q->window()->rendererInterface();
1521 static const bool environmentPreferCurve =
1522 qEnvironmentVariable(
"QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String(
"curverenderer");
1524 switch (ri->graphicsApi()) {
1525 case QSGRendererInterface::Software:
1526 res = QQuickShape::SoftwareRenderer;
1529 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1530 if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) {
1531 res = QQuickShape::CurveRenderer;
1533 res = QQuickShape::GeometryRenderer;
1536 qWarning(
"No path backend for this graphics API yet");
1545void QQuickShapePrivate::createRenderer()
1548 QQuickShape::RendererType selectedType = selectRendererType();
1549 if (selectedType == QQuickShape::UnknownRenderer)
1552 rendererType = selectedType;
1553 rendererChanged =
true;
1556 q->setFlag(QQuickItem::ItemObservesViewport, rendererType == QQuickShape::GeometryRenderer);
1558 switch (selectedType) {
1559 case QQuickShape::SoftwareRenderer:
1560 renderer =
new QQuickShapeSoftwareRenderer;
1562 case QQuickShape::GeometryRenderer:
1563 renderer =
new QQuickShapeGenericRenderer(q);
1565 case QQuickShape::CurveRenderer:
1566 renderer =
new QQuickShapeCurveRenderer(q);
1575QSGNode *QQuickShapePrivate::createNode()
1578 QSGNode *node =
nullptr;
1579 if (!q->window() || !renderer)
1581 QSGRendererInterface *ri = q->window()->rendererInterface();
1585 QSGNode *pathNode =
nullptr;
1586 switch (ri->graphicsApi()) {
1587 case QSGRendererInterface::Software:
1588 pathNode =
new QQuickShapeSoftwareRenderNode(q);
1589 static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
1590 static_cast<QQuickShapeSoftwareRenderNode *>(pathNode));
1593 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1594 if (rendererType == QQuickShape::CurveRenderer) {
1595 pathNode =
new QSGNode;
1596 static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(pathNode);
1598 pathNode =
new QQuickShapeGenericNode;
1599 static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
1600 static_cast<QQuickShapeGenericNode *>(pathNode));
1603 qWarning(
"No path backend for this graphics API yet");
1609 node =
new QSGTransformNode;
1610 node->appendChildNode(pathNode);
1624void QQuickShapePrivate::sync()
1627 syncTimingActive = QQSHAPE_LOG_TIME_DIRTY_SYNC().isDebugEnabled();
1628 if (syncTimingActive)
1631 const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync);
1633 setStatus(QQuickShape::Processing);
1634 renderer->setAsyncCallback(asyncShapeReady,
this);
1637 const int count = sp.size();
1638 bool countChanged =
false;
1639 const qreal det = windowToItemTransform().determinant();
1640 const qreal adjTriangulationScale = triangulationScale /
1641 (qIsNaN(det) || qIsNull(det) ? qreal(1) : qSqrt(qAbs(det)));
1642 renderer->beginSync(count, &countChanged);
1644 qCDebug(lcShapeSync) <<
"syncing" << count <<
"path(s)";
1645 for (
int i = 0; i < count; ++i) {
1646 QQuickShapePath *p = sp[i];
1647 qCDebug(lcShapeSync) <<
"- syncing path:" << p;
1648 int &dirty(QQuickShapePathPrivate::get(p)->dirty);
1649 totalDirty |= dirty;
1651 if (dirty & (QQuickShapePathPrivate::DirtyPath | QQuickShapePathPrivate::DirtyTrim)) {
1652 qCDebug(lcShapeSync) <<
" - DirtyPath";
1653 renderer->setPath(i, p);
1655 if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) {
1656 qCDebug(lcShapeSync) <<
" - DirtyStrokeColor:" << p->strokeColor();
1657 renderer->setStrokeColor(i, p->strokeColor());
1659 if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) {
1661 if (p->cosmeticStroke() || QSGCurveStrokeNode::expandingStrokeEnabled()) {
1662 renderer->setTriangulationScale(i, adjTriangulationScale);
1663 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1664 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale"
1665 << triangulationScale <<
"adjusted to" << adjTriangulationScale;
1667 renderer->setTriangulationScale(i, triangulationScale);
1668 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1669 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale" << triangulationScale;
1671 renderer->setStrokeWidth(i, p->strokeWidth());
1672 renderer->setCosmeticStroke(i, p->cosmeticStroke());
1674 if (dirty & QQuickShapePathPrivate::DirtyFillColor)
1675 renderer->setFillColor(i, p->fillColor());
1676 if (dirty & QQuickShapePathPrivate::DirtyFillRule)
1677 renderer->setFillRule(i, p->fillRule());
1678 if (dirty & QQuickShapePathPrivate::DirtyStyle) {
1679 renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit());
1680 renderer->setCapStyle(i, p->capStyle());
1682 if (dirty & QQuickShapePathPrivate::DirtyDash)
1683 renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern());
1684 if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
1685 renderer->setFillGradient(i, p->fillGradient());
1686 if (dirty & QQuickShapePathPrivate::DirtyStrokeGradient)
1687 renderer->setStrokeGradient(i, p->strokeGradient());
1688 if (dirty & QQuickShapePathPrivate::DirtyFillTransform)
1689 renderer->setFillTransform(i, QQuickShapePathPrivate::get(p)->sfp.fillTransform);
1690 if (dirty & QQuickShapePathPrivate::DirtyFillItem) {
1691 if (p->fillItem() ==
nullptr) {
1692 renderer->setFillTextureProvider(i,
nullptr);
1693 }
else if (p->fillItem()->isTextureProvider()) {
1694 renderer->setFillTextureProvider(i, p->fillItem());
1696 renderer->setFillTextureProvider(i,
nullptr);
1697 qWarning() <<
"QQuickShape: Fill item is not texture provider";
1704 syncTimingTotalDirty = totalDirty;
1705 if (syncTimingTotalDirty)
1708 syncTimingActive =
false;
1710 renderer->endSync(useAsync);
1713 setStatus(QQuickShape::Ready);
1714 if (syncTimingActive)
1715 qDebug(
"[Shape %p] [%d] [dirty=0x%x] update took %lld ms",
1716 q_func(), syncTimeCounter, syncTimingTotalDirty, syncTimer.elapsed());
1722 if (totalDirty || countChanged)