475void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient)
477 Q_D(QQuickShapePath);
478 if (d->sfp.fillGradient != gradient) {
479 if (d->sfp.fillGradient)
480 qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
481 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
482 d->sfp.fillGradient = gradient;
483 if (d->sfp.fillGradient)
484 qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
485 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
486 d->dirty |= QQuickShapePathPrivate::DirtyFillGradient;
487 emit shapePathChanged();
544void QQuickShapePath::setFillItem(QQuickItem *fillItem)
546 Q_D(QQuickShapePath);
547 if (d->sfp.fillItem != fillItem) {
548 if (d->sfp.fillItem !=
nullptr) {
549 qmlobject_disconnect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
550 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
552 d->sfp.fillItem = fillItem;
553 if (d->sfp.fillItem !=
nullptr) {
554 qmlobject_connect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
555 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
557 emit fillItemChanged();
559 d->dirty |= QQuickShapePathPrivate::DirtyFillItem;
560 emit shapePathChanged();
1273static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
1275 QQuickShape *item =
static_cast<QQuickShape *>(property->object);
1276 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1277 QQuickShapePath *path = qobject_cast<QQuickShapePath *>(obj);
1279 QQuickShapePathPrivate::get(path)->dirty = QQuickShapePathPrivate::DirtyAll;
1283 QQuickItemPrivate::data_append(property, obj);
1285 if (path && d->componentComplete) {
1286 QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
1287 d->_q_shapePathChanged();
1293 QQuickShape *item =
static_cast<QQuickShape *>(property->object);
1294 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1296 for (QQuickShapePath *p : d->sp)
1297 QObject::disconnect(p, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
1301 QQuickItemPrivate::data_clear(property);
1303 if (d->componentComplete)
1304 d->_q_shapePathChanged();
1375void QQuickShape::itemChange(ItemChange change,
const ItemChangeData &data)
1380 if (change == ItemVisibleHasChanged && data.boolValue)
1381 d->_q_shapePathChanged();
1382 else if (change == QQuickItem::ItemSceneChange) {
1383 for (
int i = 0; i < d->sp.size(); ++i)
1384 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
1385 d->_q_shapePathChanged();
1386 d->handleSceneChange(data.window);
1387 }
else if (change == ItemTransformHasChanged && d->rendererType == QQuickShape::GeometryRenderer) {
1388 bool cosmeticStrokeFound =
false;
1389 for (
int i = 0; i < d->sp.size(); ++i) {
1390 if (d->sp[i]->cosmeticStroke()) {
1391 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyStrokeWidth;
1392 cosmeticStrokeFound =
true;
1395 if (cosmeticStrokeFound)
1396 d->_q_shapePathChanged();
1399 QQuickItem::itemChange(change, data);
1402QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1408 if (d->renderer || d->rendererChanged) {
1409 if (!node || d->rendererChanged) {
1410 d->rendererChanged =
false;
1412 node = d->createNode();
1415 d->renderer->updateNode();
1418 QMatrix4x4 fillModeTransform;
1422 if (d->fillMode != NoResize) {
1423 xScale = width() / implicitWidth();
1424 yScale = height() / implicitHeight();
1426 if (d->fillMode == PreserveAspectFit)
1427 xScale = yScale = qMin(xScale, yScale);
1428 else if (d->fillMode == PreserveAspectCrop)
1429 xScale = yScale = qMax(xScale, yScale);
1430 fillModeTransform.scale(xScale, yScale);
1432 if (d->horizontalAlignment != AlignLeft || d->verticalAlignment != AlignTop) {
1435 qreal w = xScale * implicitWidth();
1436 qreal h = yScale * implicitHeight();
1437 if (d->horizontalAlignment == AlignRight)
1439 else if (d->horizontalAlignment == AlignHCenter)
1440 tx = (width() - w) / 2;
1441 if (d->verticalAlignment == AlignBottom)
1443 else if (d->verticalAlignment == AlignVCenter)
1444 ty = (height() - h) / 2;
1445 fillModeTransform.translate(tx / xScale, ty / yScale);
1448 QSGTransformNode *transformNode =
static_cast<QSGTransformNode *>(node);
1449 if (fillModeTransform != transformNode->matrix())
1450 transformNode->setMatrix(fillModeTransform);
1455QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
1457 QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
1459 QSGRendererInterface *ri = q->window()->rendererInterface();
1463 static const bool environmentPreferCurve =
1464 qEnvironmentVariable(
"QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String(
"curverenderer");
1466 switch (ri->graphicsApi()) {
1467 case QSGRendererInterface::Software:
1468 res = QQuickShape::SoftwareRenderer;
1471 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1472 if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) {
1473 res = QQuickShape::CurveRenderer;
1475 res = QQuickShape::GeometryRenderer;
1478 qWarning(
"No path backend for this graphics API yet");
1487void QQuickShapePrivate::createRenderer()
1490 QQuickShape::RendererType selectedType = selectRendererType();
1491 if (selectedType == QQuickShape::UnknownRenderer)
1494 rendererType = selectedType;
1495 rendererChanged =
true;
1498 q->setFlag(QQuickItem::ItemObservesViewport, rendererType == QQuickShape::GeometryRenderer);
1500 switch (selectedType) {
1501 case QQuickShape::SoftwareRenderer:
1502 renderer =
new QQuickShapeSoftwareRenderer;
1504 case QQuickShape::GeometryRenderer:
1505 renderer =
new QQuickShapeGenericRenderer(q);
1507 case QQuickShape::CurveRenderer:
1508 renderer =
new QQuickShapeCurveRenderer(q);
1517QSGNode *QQuickShapePrivate::createNode()
1520 QSGNode *node =
nullptr;
1521 if (!q->window() || !renderer)
1523 QSGRendererInterface *ri = q->window()->rendererInterface();
1527 QSGNode *pathNode =
nullptr;
1528 switch (ri->graphicsApi()) {
1529 case QSGRendererInterface::Software:
1530 pathNode =
new QQuickShapeSoftwareRenderNode(q);
1531 static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
1532 static_cast<QQuickShapeSoftwareRenderNode *>(pathNode));
1535 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1536 if (rendererType == QQuickShape::CurveRenderer) {
1537 pathNode =
new QSGNode;
1538 static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(pathNode);
1540 pathNode =
new QQuickShapeGenericNode;
1541 static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
1542 static_cast<QQuickShapeGenericNode *>(pathNode));
1545 qWarning(
"No path backend for this graphics API yet");
1551 node =
new QSGTransformNode;
1552 node->appendChildNode(pathNode);
1566void QQuickShapePrivate::sync()
1569 syncTimingActive = QQSHAPE_LOG_TIME_DIRTY_SYNC().isDebugEnabled();
1570 if (syncTimingActive)
1573 const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync);
1575 setStatus(QQuickShape::Processing);
1576 renderer->setAsyncCallback(asyncShapeReady,
this);
1579 const int count = sp.size();
1580 bool countChanged =
false;
1581 const qreal det = windowToItemTransform().determinant();
1582 const qreal adjTriangulationScale = triangulationScale /
1583 (qIsNaN(det) || qIsNull(det) ? qreal(1) : qSqrt(qAbs(windowToItemTransform().determinant())));
1584 renderer->beginSync(count, &countChanged);
1586 qCDebug(lcShapeSync) <<
"syncing" << count <<
"path(s)";
1587 for (
int i = 0; i < count; ++i) {
1588 QQuickShapePath *p = sp[i];
1589 qCDebug(lcShapeSync) <<
"- syncing path:" << p;
1590 int &dirty(QQuickShapePathPrivate::get(p)->dirty);
1591 totalDirty |= dirty;
1593 if (dirty & (QQuickShapePathPrivate::DirtyPath | QQuickShapePathPrivate::DirtyTrim)) {
1594 qCDebug(lcShapeSync) <<
" - DirtyPath";
1595 renderer->setPath(i, p);
1597 if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) {
1598 qCDebug(lcShapeSync) <<
" - DirtyStrokeColor:" << p->strokeColor();
1599 renderer->setStrokeColor(i, p->strokeColor());
1601 if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) {
1603 if (p->cosmeticStroke() || QSGCurveStrokeNode::expandingStrokeEnabled()) {
1604 renderer->setTriangulationScale(adjTriangulationScale);
1605 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1606 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale"
1607 << triangulationScale <<
"adjusted to" << adjTriangulationScale;
1609 renderer->setTriangulationScale(triangulationScale);
1610 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1611 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale" << triangulationScale;
1613 renderer->setStrokeWidth(i, p->strokeWidth());
1614 renderer->setCosmeticStroke(i, p->cosmeticStroke());
1616 if (dirty & QQuickShapePathPrivate::DirtyFillColor)
1617 renderer->setFillColor(i, p->fillColor());
1618 if (dirty & QQuickShapePathPrivate::DirtyFillRule)
1619 renderer->setFillRule(i, p->fillRule());
1620 if (dirty & QQuickShapePathPrivate::DirtyStyle) {
1621 renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit());
1622 renderer->setCapStyle(i, p->capStyle());
1624 if (dirty & QQuickShapePathPrivate::DirtyDash)
1625 renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern());
1626 if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
1627 renderer->setFillGradient(i, p->fillGradient());
1628 if (dirty & QQuickShapePathPrivate::DirtyFillTransform)
1629 renderer->setFillTransform(i, QQuickShapePathPrivate::get(p)->sfp.fillTransform);
1630 if (dirty & QQuickShapePathPrivate::DirtyFillItem) {
1631 if (p->fillItem() ==
nullptr) {
1632 renderer->setFillTextureProvider(i,
nullptr);
1633 }
else if (p->fillItem()->isTextureProvider()) {
1634 renderer->setFillTextureProvider(i, p->fillItem());
1636 renderer->setFillTextureProvider(i,
nullptr);
1637 qWarning() <<
"QQuickShape: Fill item is not texture provider";
1644 syncTimingTotalDirty = totalDirty;
1645 if (syncTimingTotalDirty)
1648 syncTimingActive =
false;
1650 renderer->endSync(useAsync);
1653 setStatus(QQuickShape::Ready);
1654 if (syncTimingActive)
1655 qDebug(
"[Shape %p] [%d] [dirty=0x%x] update took %lld ms",
1656 q_func(), syncTimeCounter, syncTimingTotalDirty, syncTimer.elapsed());
1662 if (totalDirty || countChanged)