478void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient)
480 Q_D(QQuickShapePath);
481 if (d->sfp.fillGradient != gradient) {
482 if (d->sfp.fillGradient)
483 qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
484 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
485 d->sfp.fillGradient = gradient;
486 if (d->sfp.fillGradient)
487 qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
488 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
489 d->dirty |= QQuickShapePathPrivate::DirtyFillGradient;
490 emit shapePathChanged();
547void QQuickShapePath::setFillItem(QQuickItem *fillItem)
549 Q_D(QQuickShapePath);
550 if (d->sfp.fillItem != fillItem) {
551 if (d->sfp.fillItem !=
nullptr) {
552 qmlobject_disconnect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
553 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
555 d->sfp.fillItem = fillItem;
556 if (d->sfp.fillItem !=
nullptr) {
557 qmlobject_connect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
558 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
560 emit fillItemChanged();
562 d->dirty |= QQuickShapePathPrivate::DirtyFillItem;
563 emit shapePathChanged();
1276static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
1278 QQuickShape *item =
static_cast<QQuickShape *>(property->object);
1279 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1280 QQuickShapePath *path = qobject_cast<QQuickShapePath *>(obj);
1282 QQuickShapePathPrivate::get(path)->dirty = QQuickShapePathPrivate::DirtyAll;
1286 QQuickItemPrivate::data_append(property, obj);
1288 if (path && d->componentComplete) {
1289 QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
1290 d->_q_shapePathChanged();
1378void QQuickShape::itemChange(ItemChange change,
const ItemChangeData &data)
1383 if (change == ItemVisibleHasChanged && data.boolValue)
1384 d->_q_shapePathChanged();
1385 else if (change == QQuickItem::ItemSceneChange) {
1386 for (
int i = 0; i < d->sp.size(); ++i)
1387 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
1388 d->_q_shapePathChanged();
1389 d->handleSceneChange(data.window);
1390 }
else if (change == ItemTransformHasChanged && d->rendererType == QQuickShape::GeometryRenderer) {
1391 bool cosmeticStrokeFound =
false;
1392 for (
int i = 0; i < d->sp.size(); ++i) {
1393 if (d->sp[i]->cosmeticStroke()) {
1394 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyStrokeWidth;
1395 cosmeticStrokeFound =
true;
1398 if (cosmeticStrokeFound)
1399 d->_q_shapePathChanged();
1402 QQuickItem::itemChange(change, data);
1405QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1411 if (d->renderer || d->rendererChanged) {
1412 if (!node || d->rendererChanged) {
1413 d->rendererChanged =
false;
1415 node = d->createNode();
1418 d->renderer->updateNode();
1421 QMatrix4x4 fillModeTransform;
1425 if (d->fillMode != NoResize) {
1426 xScale = width() / implicitWidth();
1427 yScale = height() / implicitHeight();
1429 if (d->fillMode == PreserveAspectFit)
1430 xScale = yScale = qMin(xScale, yScale);
1431 else if (d->fillMode == PreserveAspectCrop)
1432 xScale = yScale = qMax(xScale, yScale);
1433 fillModeTransform.scale(xScale, yScale);
1435 if (d->horizontalAlignment != AlignLeft || d->verticalAlignment != AlignTop) {
1438 qreal w = xScale * implicitWidth();
1439 qreal h = yScale * implicitHeight();
1440 if (d->horizontalAlignment == AlignRight)
1442 else if (d->horizontalAlignment == AlignHCenter)
1443 tx = (width() - w) / 2;
1444 if (d->verticalAlignment == AlignBottom)
1446 else if (d->verticalAlignment == AlignVCenter)
1447 ty = (height() - h) / 2;
1448 fillModeTransform.translate(tx / xScale, ty / yScale);
1451 QSGTransformNode *transformNode =
static_cast<QSGTransformNode *>(node);
1452 if (fillModeTransform != transformNode->matrix())
1453 transformNode->setMatrix(fillModeTransform);
1458QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
1460 QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
1462 QSGRendererInterface *ri = q->window()->rendererInterface();
1466 static const bool environmentPreferCurve =
1467 qEnvironmentVariable(
"QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String(
"curverenderer");
1469 switch (ri->graphicsApi()) {
1470 case QSGRendererInterface::Software:
1471 res = QQuickShape::SoftwareRenderer;
1474 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1475 if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) {
1476 res = QQuickShape::CurveRenderer;
1478 res = QQuickShape::GeometryRenderer;
1481 qWarning(
"No path backend for this graphics API yet");
1490void QQuickShapePrivate::createRenderer()
1493 QQuickShape::RendererType selectedType = selectRendererType();
1494 if (selectedType == QQuickShape::UnknownRenderer)
1497 rendererType = selectedType;
1498 rendererChanged =
true;
1501 q->setFlag(QQuickItem::ItemObservesViewport, rendererType == QQuickShape::GeometryRenderer);
1503 switch (selectedType) {
1504 case QQuickShape::SoftwareRenderer:
1505 renderer =
new QQuickShapeSoftwareRenderer;
1507 case QQuickShape::GeometryRenderer:
1508 renderer =
new QQuickShapeGenericRenderer(q);
1510 case QQuickShape::CurveRenderer:
1511 renderer =
new QQuickShapeCurveRenderer(q);
1520QSGNode *QQuickShapePrivate::createNode()
1523 QSGNode *node =
nullptr;
1524 if (!q->window() || !renderer)
1526 QSGRendererInterface *ri = q->window()->rendererInterface();
1530 QSGNode *pathNode =
nullptr;
1531 switch (ri->graphicsApi()) {
1532 case QSGRendererInterface::Software:
1533 pathNode =
new QQuickShapeSoftwareRenderNode(q);
1534 static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
1535 static_cast<QQuickShapeSoftwareRenderNode *>(pathNode));
1538 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1539 if (rendererType == QQuickShape::CurveRenderer) {
1540 pathNode =
new QSGNode;
1541 static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(pathNode);
1543 pathNode =
new QQuickShapeGenericNode;
1544 static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
1545 static_cast<QQuickShapeGenericNode *>(pathNode));
1548 qWarning(
"No path backend for this graphics API yet");
1554 node =
new QSGTransformNode;
1555 node->appendChildNode(pathNode);
1569void QQuickShapePrivate::sync()
1572 syncTimingActive = QQSHAPE_LOG_TIME_DIRTY_SYNC().isDebugEnabled();
1573 if (syncTimingActive)
1576 const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync);
1578 setStatus(QQuickShape::Processing);
1579 renderer->setAsyncCallback(asyncShapeReady,
this);
1582 const int count = sp.size();
1583 bool countChanged =
false;
1584 const qreal det = windowToItemTransform().determinant();
1585 const qreal adjTriangulationScale = triangulationScale /
1586 (qIsNaN(det) || qIsNull(det) ? qreal(1) : qSqrt(qAbs(det)));
1587 renderer->beginSync(count, &countChanged);
1589 qCDebug(lcShapeSync) <<
"syncing" << count <<
"path(s)";
1590 for (
int i = 0; i < count; ++i) {
1591 QQuickShapePath *p = sp[i];
1592 qCDebug(lcShapeSync) <<
"- syncing path:" << p;
1593 int &dirty(QQuickShapePathPrivate::get(p)->dirty);
1594 totalDirty |= dirty;
1596 if (dirty & (QQuickShapePathPrivate::DirtyPath | QQuickShapePathPrivate::DirtyTrim)) {
1597 qCDebug(lcShapeSync) <<
" - DirtyPath";
1598 renderer->setPath(i, p);
1600 if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) {
1601 qCDebug(lcShapeSync) <<
" - DirtyStrokeColor:" << p->strokeColor();
1602 renderer->setStrokeColor(i, p->strokeColor());
1604 if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) {
1606 if (p->cosmeticStroke() || QSGCurveStrokeNode::expandingStrokeEnabled()) {
1607 renderer->setTriangulationScale(i, adjTriangulationScale);
1608 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1609 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale"
1610 << triangulationScale <<
"adjusted to" << adjTriangulationScale;
1612 renderer->setTriangulationScale(i, triangulationScale);
1613 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1614 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale" << triangulationScale;
1616 renderer->setStrokeWidth(i, p->strokeWidth());
1617 renderer->setCosmeticStroke(i, p->cosmeticStroke());
1619 if (dirty & QQuickShapePathPrivate::DirtyFillColor)
1620 renderer->setFillColor(i, p->fillColor());
1621 if (dirty & QQuickShapePathPrivate::DirtyFillRule)
1622 renderer->setFillRule(i, p->fillRule());
1623 if (dirty & QQuickShapePathPrivate::DirtyStyle) {
1624 renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit());
1625 renderer->setCapStyle(i, p->capStyle());
1627 if (dirty & QQuickShapePathPrivate::DirtyDash)
1628 renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern());
1629 if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
1630 renderer->setFillGradient(i, p->fillGradient());
1631 if (dirty & QQuickShapePathPrivate::DirtyFillTransform)
1632 renderer->setFillTransform(i, QQuickShapePathPrivate::get(p)->sfp.fillTransform);
1633 if (dirty & QQuickShapePathPrivate::DirtyFillItem) {
1634 if (p->fillItem() ==
nullptr) {
1635 renderer->setFillTextureProvider(i,
nullptr);
1636 }
else if (p->fillItem()->isTextureProvider()) {
1637 renderer->setFillTextureProvider(i, p->fillItem());
1639 renderer->setFillTextureProvider(i,
nullptr);
1640 qWarning() <<
"QQuickShape: Fill item is not texture provider";
1647 syncTimingTotalDirty = totalDirty;
1648 if (syncTimingTotalDirty)
1651 syncTimingActive =
false;
1653 renderer->endSync(useAsync);
1656 setStatus(QQuickShape::Ready);
1657 if (syncTimingActive)
1658 qDebug(
"[Shape %p] [%d] [dirty=0x%x] update took %lld ms",
1659 q_func(), syncTimeCounter, syncTimingTotalDirty, syncTimer.elapsed());
1665 if (totalDirty || countChanged)