479void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient)
481 Q_D(QQuickShapePath);
482 if (d->sfp.fillGradient != gradient) {
483 if (d->sfp.fillGradient)
484 qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
485 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
486 d->sfp.fillGradient = gradient;
487 if (d->sfp.fillGradient)
488 qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
489 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
490 emit fillGradientChanged();
491 d->dirty |= QQuickShapePathPrivate::DirtyFillGradient;
492 emit shapePathChanged();
549void QQuickShapePath::setFillItem(QQuickItem *fillItem)
551 Q_D(QQuickShapePath);
552 if (d->sfp.fillItem != fillItem) {
553 if (d->sfp.fillItem !=
nullptr) {
554 qmlobject_disconnect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
555 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
557 d->sfp.fillItem = fillItem;
558 if (d->sfp.fillItem !=
nullptr) {
559 qmlobject_connect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
560 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
562 emit fillItemChanged();
564 d->dirty |= QQuickShapePathPrivate::DirtyFillItem;
565 emit shapePathChanged();
1278static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
1280 QQuickShape *item =
static_cast<QQuickShape *>(property->object);
1281 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1282 QQuickShapePath *path = qobject_cast<QQuickShapePath *>(obj);
1284 QQuickShapePathPrivate::get(path)->dirty = QQuickShapePathPrivate::DirtyAll;
1288 QQuickItemPrivate::data_append(property, obj);
1290 if (path && d->componentComplete) {
1291 QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
1292 d->_q_shapePathChanged();
1380void QQuickShape::itemChange(ItemChange change,
const ItemChangeData &data)
1385 if (change == ItemVisibleHasChanged && data.boolValue)
1386 d->_q_shapePathChanged();
1387 else if (change == QQuickItem::ItemSceneChange) {
1388 for (
int i = 0; i < d->sp.size(); ++i)
1389 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
1390 d->_q_shapePathChanged();
1391 d->handleSceneChange(data.window);
1392 }
else if (change == ItemTransformHasChanged && d->rendererType == QQuickShape::GeometryRenderer) {
1393 bool cosmeticStrokeFound =
false;
1394 for (
int i = 0; i < d->sp.size(); ++i) {
1395 if (d->sp[i]->cosmeticStroke()) {
1396 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyStrokeWidth;
1397 cosmeticStrokeFound =
true;
1400 if (cosmeticStrokeFound)
1401 d->_q_shapePathChanged();
1404 QQuickItem::itemChange(change, data);
1407QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1413 if (d->renderer || d->rendererChanged) {
1414 if (!node || d->rendererChanged) {
1415 d->rendererChanged =
false;
1417 node = d->createNode();
1420 d->renderer->updateNode();
1423 QMatrix4x4 fillModeTransform;
1427 if (d->fillMode != NoResize) {
1428 xScale = width() / implicitWidth();
1429 yScale = height() / implicitHeight();
1431 if (d->fillMode == PreserveAspectFit)
1432 xScale = yScale = qMin(xScale, yScale);
1433 else if (d->fillMode == PreserveAspectCrop)
1434 xScale = yScale = qMax(xScale, yScale);
1435 fillModeTransform.scale(xScale, yScale);
1437 if (d->horizontalAlignment != AlignLeft || d->verticalAlignment != AlignTop) {
1440 qreal w = xScale * implicitWidth();
1441 qreal h = yScale * implicitHeight();
1442 if (d->horizontalAlignment == AlignRight)
1444 else if (d->horizontalAlignment == AlignHCenter)
1445 tx = (width() - w) / 2;
1446 if (d->verticalAlignment == AlignBottom)
1448 else if (d->verticalAlignment == AlignVCenter)
1449 ty = (height() - h) / 2;
1450 fillModeTransform.translate(tx / xScale, ty / yScale);
1453 QSGTransformNode *transformNode =
static_cast<QSGTransformNode *>(node);
1454 if (fillModeTransform != transformNode->matrix())
1455 transformNode->setMatrix(fillModeTransform);
1460QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
1462 QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
1464 QSGRendererInterface *ri = q->window()->rendererInterface();
1468 static const bool environmentPreferCurve =
1469 qEnvironmentVariable(
"QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String(
"curverenderer");
1471 switch (ri->graphicsApi()) {
1472 case QSGRendererInterface::Software:
1473 res = QQuickShape::SoftwareRenderer;
1476 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1477 if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) {
1478 res = QQuickShape::CurveRenderer;
1480 res = QQuickShape::GeometryRenderer;
1483 qWarning(
"No path backend for this graphics API yet");
1492void QQuickShapePrivate::createRenderer()
1495 QQuickShape::RendererType selectedType = selectRendererType();
1496 if (selectedType == QQuickShape::UnknownRenderer)
1499 rendererType = selectedType;
1500 rendererChanged =
true;
1503 q->setFlag(QQuickItem::ItemObservesViewport, rendererType == QQuickShape::GeometryRenderer);
1505 switch (selectedType) {
1506 case QQuickShape::SoftwareRenderer:
1507 renderer =
new QQuickShapeSoftwareRenderer;
1509 case QQuickShape::GeometryRenderer:
1510 renderer =
new QQuickShapeGenericRenderer(q);
1512 case QQuickShape::CurveRenderer:
1513 renderer =
new QQuickShapeCurveRenderer(q);
1522QSGNode *QQuickShapePrivate::createNode()
1525 QSGNode *node =
nullptr;
1526 if (!q->window() || !renderer)
1528 QSGRendererInterface *ri = q->window()->rendererInterface();
1532 QSGNode *pathNode =
nullptr;
1533 switch (ri->graphicsApi()) {
1534 case QSGRendererInterface::Software:
1535 pathNode =
new QQuickShapeSoftwareRenderNode(q);
1536 static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
1537 static_cast<QQuickShapeSoftwareRenderNode *>(pathNode));
1540 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1541 if (rendererType == QQuickShape::CurveRenderer) {
1542 pathNode =
new QSGNode;
1543 static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(pathNode);
1545 pathNode =
new QQuickShapeGenericNode;
1546 static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
1547 static_cast<QQuickShapeGenericNode *>(pathNode));
1550 qWarning(
"No path backend for this graphics API yet");
1556 node =
new QSGTransformNode;
1557 node->appendChildNode(pathNode);
1571void QQuickShapePrivate::sync()
1574 syncTimingActive = QQSHAPE_LOG_TIME_DIRTY_SYNC().isDebugEnabled();
1575 if (syncTimingActive)
1578 const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync);
1580 setStatus(QQuickShape::Processing);
1581 renderer->setAsyncCallback(asyncShapeReady,
this);
1584 const int count = sp.size();
1585 bool countChanged =
false;
1586 const qreal det = windowToItemTransform().determinant();
1587 const qreal adjTriangulationScale = triangulationScale /
1588 (qIsNaN(det) || qIsNull(det) ? qreal(1) : qSqrt(qAbs(det)));
1589 renderer->beginSync(count, &countChanged);
1591 qCDebug(lcShapeSync) <<
"syncing" << count <<
"path(s)";
1592 for (
int i = 0; i < count; ++i) {
1593 QQuickShapePath *p = sp[i];
1594 qCDebug(lcShapeSync) <<
"- syncing path:" << p;
1595 int &dirty(QQuickShapePathPrivate::get(p)->dirty);
1596 totalDirty |= dirty;
1598 if (dirty & (QQuickShapePathPrivate::DirtyPath | QQuickShapePathPrivate::DirtyTrim)) {
1599 qCDebug(lcShapeSync) <<
" - DirtyPath";
1600 renderer->setPath(i, p);
1602 if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) {
1603 qCDebug(lcShapeSync) <<
" - DirtyStrokeColor:" << p->strokeColor();
1604 renderer->setStrokeColor(i, p->strokeColor());
1606 if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) {
1608 if (p->cosmeticStroke() || QSGCurveStrokeNode::expandingStrokeEnabled()) {
1609 renderer->setTriangulationScale(i, adjTriangulationScale);
1610 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1611 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale"
1612 << triangulationScale <<
"adjusted to" << adjTriangulationScale;
1614 renderer->setTriangulationScale(i, triangulationScale);
1615 qCDebug(lcShapeSync) <<
" - DirtyStrokeWidth:" << p->strokeWidth()
1616 <<
"cosmetic:" << p->cosmeticStroke() <<
"triangulationScale" << triangulationScale;
1618 renderer->setStrokeWidth(i, p->strokeWidth());
1619 renderer->setCosmeticStroke(i, p->cosmeticStroke());
1621 if (dirty & QQuickShapePathPrivate::DirtyFillColor)
1622 renderer->setFillColor(i, p->fillColor());
1623 if (dirty & QQuickShapePathPrivate::DirtyFillRule)
1624 renderer->setFillRule(i, p->fillRule());
1625 if (dirty & QQuickShapePathPrivate::DirtyStyle) {
1626 renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit());
1627 renderer->setCapStyle(i, p->capStyle());
1629 if (dirty & QQuickShapePathPrivate::DirtyDash)
1630 renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern());
1631 if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
1632 renderer->setFillGradient(i, p->fillGradient());
1633 if (dirty & QQuickShapePathPrivate::DirtyFillTransform)
1634 renderer->setFillTransform(i, QQuickShapePathPrivate::get(p)->sfp.fillTransform);
1635 if (dirty & QQuickShapePathPrivate::DirtyFillItem) {
1636 if (p->fillItem() ==
nullptr) {
1637 renderer->setFillTextureProvider(i,
nullptr);
1638 }
else if (p->fillItem()->isTextureProvider()) {
1639 renderer->setFillTextureProvider(i, p->fillItem());
1641 renderer->setFillTextureProvider(i,
nullptr);
1642 qWarning() <<
"QQuickShape: Fill item is not texture provider";
1649 syncTimingTotalDirty = totalDirty;
1650 if (syncTimingTotalDirty)
1653 syncTimingActive =
false;
1655 renderer->endSync(useAsync);
1658 setStatus(QQuickShape::Ready);
1659 if (syncTimingActive)
1660 qDebug(
"[Shape %p] [%d] [dirty=0x%x] update took %lld ms",
1661 q_func(), syncTimeCounter, syncTimingTotalDirty, syncTimer.elapsed());
1667 if (totalDirty || countChanged)