fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed

Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
This commit is contained in:
2026-05-05 20:20:37 +01:00
parent a5f97b6632
commit f31522130f
81834 changed files with 11051982 additions and 108 deletions
@@ -0,0 +1,65 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## QuickShapes Module:
#####################################################################
qt_internal_add_qml_module(QuickShapes
URI "QtQuick.Shapes"
VERSION "${PROJECT_VERSION}"
PLUGIN_TARGET qmlshapesplugin
NO_PLUGIN_OPTIONAL
NO_GENERATE_PLUGIN_SOURCE
CLASS_NAME QmlShapesPlugin
DEPENDENCIES
QtQuick/auto
SOURCES
qquickshape.cpp qquickshape_p.h
qquickshape_p_p.h
qquickshapegenericrenderer.cpp qquickshapegenericrenderer_p.h
qquickshapesglobal.h qquickshapesglobal_p.h
qquickshapecurverenderer.cpp qquickshapecurverenderer_p.h qquickshapecurverenderer_p_p.h
qquickshapesoftwarerenderer.cpp qquickshapesoftwarerenderer_p.h
LIBRARIES
Qt::GuiPrivate
Qt::QuickPrivate
PUBLIC_LIBRARIES
Qt::Core
Qt::Qml
PRIVATE_MODULE_INTERFACE
Qt::QuickPrivate
)
# We need to do additional initialization, so we have to provide our own
# plugin class rather than using the generated one
qt_internal_extend_target(qmlshapesplugin
SOURCES qquickshapesplugin.cpp
LIBRARIES Qt::QuickShapesPrivate
)
qt_internal_add_shaders(QuickShapes "qtquickshapes_shaders"
SILENT
BATCHABLE
PRECOMPILE
OPTIMIZED
MULTIVIEW
PREFIX
"/qt-project.org/shapes"
FILES
"shaders_ng/lineargradient.vert"
"shaders_ng/lineargradient.frag"
"shaders_ng/radialgradient.vert"
"shaders_ng/radialgradient.frag"
"shaders_ng/conicalgradient.vert"
"shaders_ng/conicalgradient.frag"
"shaders_ng/texturefill.vert"
"shaders_ng/texturefill.frag"
"shaders_ng/wireframe.frag"
"shaders_ng/wireframe.vert"
)
if(QT_FEATURE_quickshapes_designhelpers)
add_subdirectory(designhelpers)
endif()
@@ -0,0 +1,14 @@
# Copyright (C) 2025 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#### Features
qt_feature("quickshapes-designhelpers" PRIVATE
SECTION "Qt Quick Shapes"
LABEL "Design Helpers"
PURPOSE "Provides helper types based on Qt Quick Shapes for use in design tools like Figma."
CONDITION QT_FEATURE_quick_path
)
qt_configure_add_summary_section(NAME "Qt Quick Shapes")
qt_configure_add_summary_entry(ARGS "quickshapes-designhelpers")
qt_configure_end_summary_section() # end of "Qt Quick Shapes" section
@@ -0,0 +1,34 @@
# Copyright (C) 2025 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_qml_module(QuickShapesDesignHelpersPrivate
URI "QtQuick.Shapes.DesignHelpers"
VERSION "${PROJECT_VERSION}"
PLUGIN_TARGET qtquickshapesdesignhelpersplugin
CLASS_NAME QtQuickShapesDesignHelpersPlugin
DEPENDENCIES
QtQuick/auto
INTERNAL_MODULE
SOURCES
qquickellipseshape.cpp
qquickellipseshape_p.h
qquickellipseshape_p_p.h
qquickrectangleshape.cpp
qquickrectangleshape_p.h
qquickrectangleshape_p_p.h
qquickregularpolygonshape.cpp
qquickregularpolygonshape_p.h
qquickregularpolygonshape_p_p.h
qquickshapesdesignhelpersglobal_p.h
qquickstarshape.cpp
qquickstarshape_p.h
qquickstarshape_p_p.h
NO_UNITY_BUILD_SOURCES
qquickstarshape.cpp
PUBLIC_LIBRARIES
Qt::Core
Qt::GuiPrivate
Qt::Qml
Qt::QuickPrivate
Qt::QuickShapesPrivate
)
@@ -0,0 +1,23 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Window {
visible: true
flags: Qt.FramelessWindowHint
width: 100
height: 100
//! [ellipseShape]
EllipseShape {
id: ellipseShape
anchors.fill: parent
width: 90
height: 90
startAngle: 0
sweepAngle: 270
}
//! [ellipseShape]
}
@@ -0,0 +1,27 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Window {
width: rectangleShape.width + 10
height: rectangleShape.height + 10
visible: true
flags: Qt.FramelessWindowHint
//! [rectangleShape]
RectangleShape {
id: rectangleShape
anchors.centerIn: parent
radius: 0
topLeftRadius: 30
bottomRightRadius: 30
bevel: true
joinStyle: ShapePath.MiterJoin
fillColor: "#3ad23c"
strokeColor: "transparent"
visible: false
}
//! [rectangleShape]
}
@@ -0,0 +1,22 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Shapes
Window {
width: rectangleShape.width + 10
height: rectangleShape.height + 10
visible: true
flags: Qt.FramelessWindowHint
//! [rectangleShape]
RectangleShape {
id: rectangleShape
anchors.centerIn: parent
topLeftRadius: 0
bottomRightBevel: true
joinStyle: ShapePath.MiterJoin
}
//! [rectangleShape]
}
@@ -0,0 +1,10 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\keyword Qt Quick Shapes Design Helpers
\qmlmodule QtQuick.Shapes.DesignHelpers \QtMajorVersion.\QtMinorVersion
\title Qt Quick Shapes Design Helpers
\ingroup qmlmodules
\brief Provides QML types for drawing shapes that work with design tools such as Figma.
*/
@@ -0,0 +1,993 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickellipseshape_p.h"
#include "qquickellipseshape_p_p.h"
#include <algorithm>
#include <QtMath>
QT_BEGIN_NAMESPACE
namespace {
inline bool is_equal(qreal a, qreal b, qreal epsilon = DBL_EPSILON)
{
return std::fabs(a - b) <= epsilon;
}
inline qreal arc_angle(qreal angle)
{
return angle - 90;
}
inline QVector2D arc_point(QVector2D center, QVector2D radius, qreal angle)
{
return QVector2D(center.x() + radius.x() * qCos(qDegreesToRadians(angle)),
center.y() + radius.y() * qSin(qDegreesToRadians(angle)));
}
// counter-clockwise
inline QVector2D tangent_ccw(QVector2D radius, qreal angle)
{
return QVector2D(-radius.x() * qSin(qDegreesToRadians(angle)),
radius.y() * qCos(qDegreesToRadians(angle)));
}
inline qreal cross(QVector2D a, QVector2D b)
{
return a.x() * b.y() - a.y() * b.x();
}
// cross product of two vectors defined by points A->B x C->D
inline qreal cross(QVector2D a, QVector2D b, QVector2D c, QVector2D d)
{
return cross(b - a, d - c);
}
qreal angle_between_vectors(QVector2D a, QVector2D b)
{
const QVector2D uA = a.normalized();
const QVector2D uB = b.normalized();
const qreal angle = qAtan2(cross(uA, uB), QVector2D::dotProduct(uA, uB));
if (std::fabs(angle) < FLT_EPSILON)
return 0.0f;
return angle;
}
// the intersection point can be calculated as C + T * (D - C) or A + S * (B - A)
bool lines_intersect(QVector2D a, QVector2D b, QVector2D c, QVector2D d, qreal *s, qreal *t)
{
// lines undefined
if ((a.x() == b.x() && a.y() == b.y()) || (c.x() == d.x() && c.y() == d.y()))
return false;
const qreal denom = cross(a, b, c, d);
// lines are parallel or overlap
if (denom == 0)
return false;
if (s != nullptr)
*s = cross(c, d, c, a) / denom;
if (t != nullptr)
*t = cross(a, b, c, a) / denom;
return true;
}
} // namespace
QQuickEllipseShapePrivate::QQuickEllipseShapePrivate() = default;
QQuickEllipseShapePrivate::~QQuickEllipseShapePrivate() = default;
void QQuickEllipseShapePrivate::addLine(QVector2D point)
{
auto line = new QQuickPathLine(path);
line->setX(point.x());
line->setY(point.y());
QQuickPathPrivate::get(path)->appendPathElement(line);
}
void QQuickEllipseShapePrivate::addArc(QVector2D point, QVector2D arcRadius,
QQuickPathArc::ArcDirection dir, bool largeArc)
{
auto arc = new QQuickPathArc(path);
arc->setX(point.x());
arc->setY(point.y());
arc->setRadiusX(arcRadius.x());
arc->setRadiusY(arcRadius.y());
arc->setDirection(dir);
arc->setUseLargeArc(largeArc);
QQuickPathPrivate::get(path)->appendPathElement(arc);
}
qreal QQuickEllipseShapePrivate::getBorderOffset() const
{
if (QQuickEllipseShape::BorderMode::Middle == borderMode)
return 0;
else if (QQuickEllipseShape::BorderMode::Outside == borderMode)
return -path->strokeWidth() * 0.5;
return path->strokeWidth() * 0.5f; // inside
}
void QQuickEllipseShapePrivate::roundCenter(QVector2D center, QVector2D ellipseRadius)
{
const qreal endAngle = arc_angle(startAngle + sweepAngle);
const QVector2D endPoint = arc_point(center, ellipseRadius, endAngle);
const qreal beginAngle = arc_angle(startAngle);
const QVector2D beginPoint = arc_point(center, ellipseRadius, beginAngle);
const QVector2D AB = endPoint - center;
const QVector2D AC = beginPoint - center;
const qreal a = angle_between_vectors(AB, AC);
const qreal halfAngle = std::fabs(a) * 0.5f;
const qreal maxCornerRadius = (std::min(AB.length(), AC.length()) * 0.5f) * qTan(halfAngle);
const qreal corner_radius = std::min(cornerRadius, maxCornerRadius);
// calculate B and C based on the corner radius
const qreal edgeOffset = corner_radius / qTan(halfAngle);
const QVector2D B = center + (AB.normalized() * edgeOffset);
const QVector2D C = center + (AC.normalized() * edgeOffset);
// update
auto &rc = roundedCorners[RoundedCornerIndex::Center];
rc.A = center;
rc.B = B;
rc.C = C;
rc.alpha = a;
rc.radius = corner_radius;
}
void QQuickEllipseShapePrivate::roundBeginEnd(QVector2D center, QVector2D ellipseRadius)
{
qreal deg = 0.0f;
const qreal endAngle = startAngle + sweepAngle;
bool e_outer = false, b_outer = false, e_inner = false, b_inner = false;
while (deg < 45.0f) {
deg += 1.0f;
if (e_outer && b_outer && e_inner && b_inner)
break;
if (!b_outer)
b_outer = roundOuter(center, ellipseRadius, deg, startAngle, endAngle,
RoundedCornerIndex::OuterBegin);
if (!e_outer)
e_outer = roundOuter(center, ellipseRadius, deg, endAngle, startAngle,
RoundedCornerIndex::OuterEnd);
if (!e_inner)
e_inner = roundInner(center, ellipseRadius, deg, endAngle, startAngle,
RoundedCornerIndex::InnerEnd);
if (!b_inner)
b_inner = roundInner(center, ellipseRadius, deg, startAngle, endAngle,
RoundedCornerIndex::InnerBegin);
}
}
bool QQuickEllipseShapePrivate::roundOuter(QVector2D center, QVector2D ellipseRadius, qreal deg,
qreal arcAngle1, qreal arcAngle2,
RoundedCornerIndex index)
{
bool done = false;
const qreal arcAngle = arc_angle(arcAngle1);
const QVector2D arcPoint = arc_point(center, ellipseRadius, arcAngle);
const qreal angle = arcAngle1 > arcAngle2 ? arcAngle - deg : arcAngle + deg;
const QVector2D B = arc_point(center, ellipseRadius, angle); // point on arc
// calculate tangent vector
const QVector2D uV = tangent_ccw(ellipseRadius, angle).normalized();
const QVector2D b1 = B + uV;
qreal s = 0, t = 0;
bool res = lines_intersect(center, arcPoint, B, b1, &s, &t);
if (res) {
const QVector2D A = center + s * (arcPoint - center);
const QVector2D AB = B - A;
const QVector2D AC = center - A;
const qreal a = angle_between_vectors(AB, AC);
const qreal halfAngle = std::fabs(a) * 0.5;
const qreal edgeOffset = AB.length();
const qreal corner_radius = edgeOffset * qTan(halfAngle);
// constrain by sweep
const qreal sweep = std::fabs(arcAngle2 - arcAngle1) * 0.5;
const qreal degMax = std::min(sweep, 45.0);
const QVector2D C = A + AC.normalized() * edgeOffset;
const qreal ptoc = (arcPoint - C).length();
qreal edge = 0;
if (innerArcRatio > 0) {
const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
innerArcRatio * ellipseRadius.y());
const QVector2D innerArcPoint =
arc_point(center, ellipseInnerRadius, arcAngle); // point on the inner arc
edge = (arcPoint - innerArcPoint).length();
} else {
edge = (arcPoint - center).length();
}
const qreal diff = std::fabs(corner_radius - cornerRadius); // closest to target radius
auto &rc = roundedCorners[index];
bool canUpdate = diff < rc.diff && !(corner_radius > cornerRadius) && !(deg > degMax)
&& !(rc.radius > corner_radius) && !(ptoc > edge * 0.5f);
// 1st loop or if constraints are met
if (rc.radius == 0 || canUpdate)
rc.update(diff, A, B, C, a, corner_radius);
done =
// corner radius is bigger or equal to the target radius
corner_radius > cornerRadius
|| is_equal(corner_radius, cornerRadius, 0.01f)
// angle used to define point B is bigger than available sweep
|| deg > degMax
|| is_equal(deg, degMax, 0.01f)
// the corner radius starts to decline (from previously calculated)
|| (rc.radius != 0 && rc.radius > corner_radius)
// point C is beyond the half of the ellipse edge
// (closer to the ellipse center or inner arc point)
|| (ptoc > edge * 0.5f);
}
return done;
}
bool QQuickEllipseShapePrivate::roundInner(QVector2D center, QVector2D ellipseRadius, qreal deg,
qreal arcAngle1, qreal arcAngle2,
RoundedCornerIndex index)
{
// make rounding corner bigger and produces smoother result
const qreal smoothFactor = 1.5;
deg *= smoothFactor;
bool done = false;
const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
innerArcRatio * ellipseRadius.y());
const qreal arcAngle = arc_angle(arcAngle1);
const QVector2D innerArcPoint =
arc_point(center, ellipseInnerRadius, arcAngle); // point on the inner arc
const qreal angle = arcAngle1 > arcAngle2 ? arcAngle - deg : arcAngle + deg;
const QVector2D B = arc_point(center, ellipseInnerRadius, angle); // point on arc
// calculate tangent vector
const QVector2D uV = tangent_ccw(ellipseInnerRadius, angle).normalized();
const QVector2D b1 = B + uV;
qreal s = 0, t = 0;
bool res = lines_intersect(center, innerArcPoint, B, b1, &s, &t);
if (res) {
// hit point
const QVector2D A = center + s * (innerArcPoint - center); // point on edge
const auto arcPoint = arc_point(center, ellipseRadius, arcAngle);
const QVector2D AB = B - A;
const QVector2D AC = A - innerArcPoint;
const qreal a = angle_between_vectors(AB, AC);
const qreal halfAngle = std::fabs(a) * 0.5;
const qreal edgeOffset = AB.length();
const qreal corner_radius = edgeOffset * qTan(halfAngle);
// constrain by sweep
const qreal sweep = std::fabs(arcAngle2 - arcAngle1) * 0.5;
const qreal degMax = std::min(sweep, 45.0);
const QVector2D C = A + AC.normalized() * edgeOffset;
const qreal ptoc = (innerArcPoint - C).length();
const qreal edge = (innerArcPoint - arcPoint).length();
const qreal diff =
std::fabs(corner_radius - cornerRadius * smoothFactor); // closest to target radius
auto &rc = roundedCorners[index];
bool canUpdate = diff < rc.diff && !(corner_radius > cornerRadius * smoothFactor)
&& !(deg > degMax) && !(rc.radius > corner_radius) && !(ptoc > edge * 0.5f);
// 1st loop or if constraints are met
if (rc.radius == 0 || canUpdate)
rc.update(diff, A, B, C, a, corner_radius);
done =
// corner radius is bigger or equal to the target radius
corner_radius > cornerRadius * smoothFactor
|| is_equal(corner_radius, cornerRadius * smoothFactor, 0.01f)
// angle used to define point B is bigger than available sweep
|| deg > degMax
|| is_equal(deg, degMax, 0.01f)
// the corner radius starts to decline (from previously calculated)
|| (rc.radius != 0 && rc.radius > corner_radius)
// point C is beyond the half of the ellipse edge
// (closer to the inner arc end point)
|| (ptoc > edge * 0.5f);
}
return done;
}
void QQuickEllipseShapePrivate::drawCenterCorner()
{
auto &rc = roundedCorners[RoundedCornerIndex::Center];
path->setStartX(rc.B.x());
path->setStartY(rc.B.y());
addArc(rc.C, QVector2D(rc.radius, rc.radius),
rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
}
void QQuickEllipseShapePrivate::drawInnerEndCorner()
{
auto &rc = roundedCorners[RoundedCornerIndex::InnerEnd];
addLine(rc.C);
addArc(rc.B, QVector2D(rc.radius, rc.radius),
rc.alpha > 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
}
void QQuickEllipseShapePrivate::drawInnerBeginCorner()
{
auto &rc = roundedCorners[RoundedCornerIndex::InnerBegin];
path->setStartX(rc.B.x());
path->setStartY(rc.B.y());
addArc(rc.C, QVector2D(rc.radius, rc.radius),
rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
}
void QQuickEllipseShapePrivate::drawOuterBeginCorner()
{
auto &rc = roundedCorners[RoundedCornerIndex::OuterBegin];
addLine(rc.C);
addArc(rc.B, QVector2D(rc.radius, rc.radius),
rc.alpha > 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
}
void QQuickEllipseShapePrivate::drawOuterArcRounded(QVector2D center, QVector2D ellipseRadius)
{
// split outer arc in two parts to avoid issues of the large arc
const qreal endAngle = startAngle + sweepAngle;
const qreal angle = startAngle > endAngle
? arc_angle(startAngle - std::fabs(sweepAngle * 0.5f))
: arc_angle(startAngle + std::fabs(sweepAngle * 0.5f));
const auto point = arc_point(center, ellipseRadius, angle); // mid point of the arc
// from begin to mid point
addArc(point, ellipseRadius,
sweepAngle > 0.0f ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
auto &rc = roundedCorners[RoundedCornerIndex::OuterEnd];
// from mid point to end rounded corner
addArc(rc.B, ellipseRadius,
sweepAngle > 0.0f ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
// rounded corner
addArc(rc.C, QVector2D(rc.radius, rc.radius),
rc.alpha < 0 ? QQuickPathArc::Clockwise : QQuickPathArc::Counterclockwise);
}
void QQuickEllipseShapePrivate::drawInnerArcRounded(QVector2D center, QVector2D ellipseRadius)
{
// split inner arc in two parts to avoid issues of the large arc
const qreal endAngle = startAngle + sweepAngle;
const QVector2D ellipseInnerRadius(innerArcRatio * ellipseRadius.x(),
innerArcRatio * ellipseRadius.y());
const qreal angle = endAngle > startAngle ? arc_angle(endAngle - std::fabs(sweepAngle * 0.5f))
: arc_angle(endAngle + std::fabs(sweepAngle * 0.5f));
const auto point = arc_point(center, ellipseInnerRadius, angle); // mid point of the arc
// from end to mid point
addArc(point, ellipseInnerRadius,
sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise : QQuickPathArc::Clockwise);
// from mid point to begin rounded corner
auto &rc = roundedCorners[RoundedCornerIndex::InnerBegin];
addArc(rc.B, ellipseInnerRadius,
sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise : QQuickPathArc::Clockwise);
}
void QQuickEllipseShapePrivate::drawOuterArc(QVector2D center, QVector2D ellipseRadius)
{
const qreal beginAngle = arc_angle(startAngle);
const qreal endAngle = arc_angle(startAngle + sweepAngle);
const qreal alpha = std::clamp(std::fabs(sweepAngle), 0.0, 359.9);
bool isFull = (alpha <= 0.0f || alpha >= 359.0f);
// QQuickPathArc has some weird behavior when it starts and ends at the same point
// leave some gap between the start and the end points in order to avoid it
const auto beginPoint = arc_point(center, ellipseRadius, isFull ? 0 : beginAngle);
const auto endPoint = arc_point(center, ellipseRadius, isFull ? 359.9 : endAngle);
path->setStartX(beginPoint.x());
path->setStartY(beginPoint.y());
addArc(endPoint, ellipseRadius,
isFull ? QQuickPathArc::Clockwise
: (sweepAngle > 0.0f ? QQuickPathArc::Clockwise
: QQuickPathArc::Counterclockwise),
isFull ? true : alpha > 180.0f);
// add reverse arc to hide fill color
if (qFuzzyCompare(innerArcRatio, 1)) {
addArc(beginPoint, ellipseRadius,
isFull ? QQuickPathArc::Counterclockwise
: (sweepAngle > 0.0f ? QQuickPathArc::Counterclockwise
: QQuickPathArc::Clockwise),
isFull ? true : alpha > 180.0f);
}
}
void QQuickEllipseShapePrivate::drawFullInnerArc(QVector2D center, QVector2D ellipseRadius)
{
const qreal beginAngle = arc_angle(startAngle);
auto arc = new QQuickPathAngleArc(path);
arc->setCenterX(center.x());
arc->setCenterY(center.y());
arc->setStartAngle(beginAngle);
arc->setRadiusX(innerArcRatio * ellipseRadius.x());
arc->setRadiusY(innerArcRatio * ellipseRadius.y());
arc->setSweepAngle(sweepAngle);
QQuickPathPrivate::get(path)->appendPathElement(arc);
}
void QQuickEllipseShapePrivate::drawWithInnerRadius(QVector2D center, QVector2D ellipseRadius)
{
drawInnerBeginCorner(); // path starts at the begin rounded corner on the inner arc
drawOuterBeginCorner(); // path continues to the begin rounded corner on the outer arc
// outer arc connecting begin and end rounded corners
drawOuterArcRounded(center, ellipseRadius);
// path continues to the end rounded corner on the inner arc
drawInnerEndCorner();
// inner arc connecting end and begin rounded corners
drawInnerArcRounded(center, ellipseRadius);
}
void QQuickEllipseShapePrivate::drawWithoutInnerRadius(QVector2D center, QVector2D ellipseRadius)
{
drawCenterCorner(); // path starts at rounded corner of ellipse center
drawOuterBeginCorner(); // path continues to the begin rounded corner on the outer arc
// outer arc connecting begin and end rounded corners
drawOuterArcRounded(center, ellipseRadius);
// path ends at the ellipse's center rounded corner
const auto &rc = roundedCorners[RoundedCornerIndex::Center];
addLine(rc.B);
}
void QQuickEllipseShapePrivate::updatePath()
{
const qreal borderOffset = getBorderOffset();
const QVector2D center =
QVector2D(width.valueBypassingBindings(), height.valueBypassingBindings()) * 0.5f;
const QVector2D ellipseRadius = center - QVector2D(borderOffset, borderOffset);
QQuickPathPrivate::get(path)->clearPathElements(QQuickPathPrivate::DeleteElementPolicy::Delete);
const qreal alpha = std::clamp(std::fabs(sweepAngle), 0.0, 359.9);
const bool isFull = alpha >= 359.0;
if (qFuzzyCompare(alpha, 0))
return;
// just an arc
if (qFuzzyCompare(innerArcRatio, 1)) {
drawOuterArc(center, ellipseRadius);
return;
}
roundedCorners.reset(); // cleanup old results
if (innerArcRatio != 0 && isFull) {
// this is a donut
drawOuterArc(center, ellipseRadius);
drawFullInnerArc(center, ellipseRadius);
} else if (innerArcRatio != 0 && !isFull) {
// this is an outlined arc
roundBeginEnd(center, ellipseRadius);
drawWithInnerRadius(center, ellipseRadius);
} else if (!isFull) {
// this is a pie
roundCenter(center, ellipseRadius);
roundBeginEnd(center, ellipseRadius);
drawWithoutInnerRadius(center, ellipseRadius);
} else {
drawOuterArc(center, ellipseRadius);
}
}
/*!
\qmltype EllipseShape
\inqmlmodule QtQuick.Shapes.DesignHelpers
\brief A shape component that can render an ellipse, an arc, or a pie slice.
\since QtQuick 6.10
The EllipseShape item paints an ellipse, which can be customized to appear
as a full ellipse, an arc, or a filled pie slice. Its appearance is
controlled by the \l startAngle and \l sweepAngle properties.
\section1 Basic Ellipse
By default, the item renders a full ellipse. The interior is filled with the
\l fillColor, and the outline is drawn according to the \l strokeColor, \l
strokeWidth, and \l strokeStyle properties.
\section1 Arc and Pie Slices
To create an arc or a pie slice, set the \l startAngle (0-360 degrees) and
\l sweepAngle (0-360 degrees) to define the segment of the ellipse to draw.
\b {Arc Mode}: To create a simple arc (just the outline), set the \l
fillColor to \c "transparent". The arc's line style can be customized with
\l dashPattern and \l dashOffset.
\b {Pie Mode}: To create a filled pie slice (a segment connected to the
center), simply set the \l fillColor. The outline of the slice will also be
stroked.
\b {Donut Mode}: To create a donut ring (a hollow ellipse), set the
\l innerArcRatio to a value between 0.0 and 1.0. This defines the ratio of
the inner ellipse's radius to the outer ellipse's radius.
The area inside the stroke is painted using either a solid fill color,
specified using the \l fillColor property, or a gradient, defined using one
of the \l ShapeGradient subtypes and set using the \l fillGradient
property. If both a color and a gradient are specified, the gradient is
used.
An optional border can be added to an ellipse with its own color and
thickness by setting the \l strokeColor and \l strokeWidth properties.
Setting the color to \c transparent creates a border without a fill color.
Ellipse can be drawn with rounded corners using the \l cornerRadius
property. The default value of the \l cornerRadius is 10 degrees.
EllipseShape's default value for \l {QtQuick.Shapes::Shape::preferredRendererType} is
\c Shape.CurveRenderer.
\section1 Example Usage
\snippet ellipseshape.qml ellipseShape
\image path-ellipseshape.png
*/
QQuickEllipseShape::QQuickEllipseShape(QQuickItem *parent)
: QQuickShape(*(new QQuickEllipseShapePrivate), parent)
{
Q_D(QQuickEllipseShape);
setPreferredRendererType(CurveRenderer);
setWidth(200);
setHeight(200);
d->path = new QQuickShapePath(this);
d->path->setParent(this);
d->path->setAsynchronous(true);
d->path->setStrokeWidth(1);
d->path->setStrokeColor(QColorConstants::Black);
d->path->setFillColor(QColorConstants::White);
d->sp.append(d->path);
d->path->setParent(this);
d->extra.value().resourcesList.append(d->path);
}
QQuickEllipseShape::~QQuickEllipseShape() = default;
/*!
\qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::sweepAngle
The angular extent in degrees to be drawn from the \l startAngle.
If set to positive value, the arc is drawn in clockwise direction.
If set to negative value, the arc is drawn in counter-clockwise direction.
The default value is \c 360.
*/
qreal QQuickEllipseShape::sweepAngle() const
{
Q_D(const QQuickEllipseShape);
return d->sweepAngle;
}
void QQuickEllipseShape::setSweepAngle(qreal sweepAngle)
{
Q_D(QQuickEllipseShape);
if (qFuzzyCompare(d->sweepAngle, sweepAngle))
return;
d->sweepAngle = sweepAngle;
d->updatePath();
emit sweepAngleChanged();
}
/*!
\qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::startAngle
The property defines the starting angle in degrees from which to begin
drawing the ellipse.
0 degrees points to the top. Angle increases in clockwise direction.
The default value is \c 0.
*/
qreal QQuickEllipseShape::startAngle() const
{
Q_D(const QQuickEllipseShape);
return d->startAngle;
}
void QQuickEllipseShape::setStartAngle(qreal startAngle)
{
Q_D(QQuickEllipseShape);
if (qFuzzyCompare(d->startAngle, startAngle))
return;
d->startAngle = startAngle;
d->updatePath();
emit startAngleChanged();
}
/*!
\include shapepath.qdocinc {dashOffset-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
*/
qreal QQuickEllipseShape::dashOffset() const
{
Q_D(const QQuickEllipseShape);
return d->path->dashOffset();
}
void QQuickEllipseShape::setDashOffset(qreal offset)
{
Q_D(QQuickEllipseShape);
if (qFuzzyCompare(d->path->dashOffset(), offset))
return;
d->path->setDashOffset(offset);
d->updatePath();
emit dashOffsetChanged();
}
/*!
\qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::cornerRadius
Controls the rounding of corners where the radial lines meet the elliptical
arcs. For pie segments, this rounds the connection to the outer arc. For
donut segments, this also rounds the connections to both inner and outer arcs.
The default value is \c 10.
*/
qreal QQuickEllipseShape::cornerRadius() const
{
Q_D(const QQuickEllipseShape);
return d->cornerRadius;
}
void QQuickEllipseShape::setCornerRadius(qreal cornerRadius)
{
Q_D(QQuickEllipseShape);
if (qFuzzyCompare(d->cornerRadius, cornerRadius))
return;
d->cornerRadius = cornerRadius;
d->updatePath();
emit cornerRadiusChanged();
}
/*!
\qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::innerArcRatio
This property defines the ratio between the inner and outer arcs.
Value range is between 0.0 and 1.0. Setting the value to 0.0 will cause
the inner arc to collapse toward the center, drawing a solid filled
ellipse. Setting the value to 1.0 makes the inner arc the same size as the
outer ellipse, resulting in just an arc. Values between 0.0 and 1.0 create
hollow elliptical rings.
The default value is \c 0.
*/
qreal QQuickEllipseShape::innerArcRatio() const
{
Q_D(const QQuickEllipseShape);
return d->innerArcRatio;
}
void QQuickEllipseShape::setInnerArcRatio(qreal innerArcRatio)
{
Q_D(QQuickEllipseShape);
if (qFuzzyCompare(d->innerArcRatio, innerArcRatio))
return;
d->innerArcRatio = innerArcRatio;
d->updatePath();
emit innerArcRatioChanged();
}
/*!
\qmlproperty real QtQuick.Shapes.DesignHelpers::EllipseShape::strokeWidth
This property holds the stroke width.
When set to a negative value, no stroking occurs.
The default value is \c 1.
*/
qreal QQuickEllipseShape::strokeWidth() const
{
Q_D(const QQuickEllipseShape);
return d->path->strokeWidth();
}
void QQuickEllipseShape::setStrokeWidth(qreal width)
{
Q_D(QQuickEllipseShape);
if (qFuzzyCompare(d->path->strokeWidth(), width))
return;
d->path->setStrokeWidth(width);
d->updatePath();
emit strokeWidthChanged();
}
/*!
\qmlproperty color QtQuick.Shapes.DesignHelpers::EllipseShape::fillColor
This property holds the fill color.
When set to \c transparent, no filling occurs.
The default value is \c "white".
\note If either \l fillGradient is set to something other than \c null, it
will be used instead of \c fillColor.
*/
QColor QQuickEllipseShape::fillColor() const
{
Q_D(const QQuickEllipseShape);
return d->path->fillColor();
}
void QQuickEllipseShape::setFillColor(const QColor &color)
{
Q_D(QQuickEllipseShape);
d->path->setFillColor(color);
d->updatePath();
emit fillColorChanged();
}
/*!
\qmlproperty color QtQuick.Shapes.DesignHelpers::EllipseShape::strokeColor
This property holds the stroking color.
When set to \c transparent, no stroking occurs.
The default value is \c "black".
*/
QColor QQuickEllipseShape::strokeColor() const
{
Q_D(const QQuickEllipseShape);
return d->path->strokeColor();
}
void QQuickEllipseShape::setStrokeColor(const QColor &color)
{
Q_D(QQuickEllipseShape);
d->path->setStrokeColor(color);
d->updatePath();
emit strokeColorChanged();
}
/*!
\include shapepath.qdocinc {capStyle-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
*/
QQuickShapePath::CapStyle QQuickEllipseShape::capStyle() const
{
Q_D(const QQuickEllipseShape);
return d->path->capStyle();
}
void QQuickEllipseShape::setCapStyle(QQuickShapePath::CapStyle style)
{
Q_D(QQuickEllipseShape);
if (d->path->capStyle() == style)
return;
d->path->setCapStyle(style);
d->updatePath();
emit capStyleChanged();
}
/*!
\include shapepath.qdocinc {joinStyle-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
*/
QQuickShapePath::JoinStyle QQuickEllipseShape::joinStyle() const
{
Q_D(const QQuickEllipseShape);
return d->path->joinStyle();
}
void QQuickEllipseShape::setJoinStyle(QQuickShapePath::JoinStyle style)
{
Q_D(QQuickEllipseShape);
if (d->path->joinStyle() == style)
return;
d->path->setJoinStyle(style);
d->updatePath();
emit joinStyleChanged();
}
/*!
\include shapepath.qdocinc {strokeStyle-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
*/
QQuickShapePath::StrokeStyle QQuickEllipseShape::strokeStyle() const
{
Q_D(const QQuickEllipseShape);
return d->path->strokeStyle();
}
void QQuickEllipseShape::setStrokeStyle(QQuickShapePath::StrokeStyle style)
{
Q_D(QQuickEllipseShape);
if (d->path->strokeStyle() == style)
return;
d->path->setStrokeStyle(style);
d->updatePath();
emit strokeStyleChanged();
}
/*!
\include shapepath.qdocinc {fillRule-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
*/
QQuickShapePath::FillRule QQuickEllipseShape::fillRule() const
{
Q_D(const QQuickEllipseShape);
return d->path->fillRule();
}
void QQuickEllipseShape::setFillRule(QQuickShapePath::FillRule fillRule)
{
Q_D(QQuickEllipseShape);
if (d->path->fillRule() == fillRule)
return;
d->path->setFillRule(fillRule);
d->updatePath();
emit fillRuleChanged();
}
/*!
\include shapepath.qdocinc {dashPattern-property} {QtQuick.Shapes.DesignHelpers::EllipseShape}
*/
QList<qreal> QQuickEllipseShape::dashPattern() const
{
Q_D(const QQuickEllipseShape);
return d->path->dashPattern();
}
void QQuickEllipseShape::setDashPattern(const QList<qreal> &array)
{
Q_D(QQuickEllipseShape);
d->path->setDashPattern(array);
d->updatePath();
emit dashPatternChanged();
}
/*!
\qmlproperty ShapeGradient QtQuick.Shapes.DesignHelpers::EllipseShape::fillGradient
The fillGradient of the ellipse fill color.
By default, no fillGradient is enabled and the value is null. In this case, the
fill uses a solid color based on the value of \l fillColor.
When set, \l fillColor is ignored and filling is done using one of the
\l ShapeGradient subtypes.
\note The \l Gradient type cannot be used here. Rather, prefer using one of
the advanced subtypes, like \l LinearGradient.
*/
QQuickShapeGradient *QQuickEllipseShape::fillGradient() const
{
Q_D(const QQuickEllipseShape);
return d->path->fillGradient();
}
void QQuickEllipseShape::setFillGradient(QQuickShapeGradient *fillGradient)
{
Q_D(QQuickEllipseShape);
d->path->setFillGradient(fillGradient);
d->updatePath();
emit gradientChanged();
}
void QQuickEllipseShape::resetFillGradient()
{
setFillGradient(nullptr);
}
/*!
\qmlproperty enumeration QtQuick.Shapes.DesignHelpers::EllipseShape::borderMode
The \l borderMode property determines where the border is drawn along the
edge of the ellipse.
\value EllipseShape.Inside
The border is drawn along the inside edge of the item and does not
affect the item width.
This is the default value.
\value EllipseShape.Middle
The border is drawn over the edge of the item and does not
affect the item width.
\value EllipseShape.Outside
The border is drawn along the outside edge of the item and increases
the item width by the value of \l strokeWidth.
\sa strokeWidth
*/
QQuickEllipseShape::BorderMode QQuickEllipseShape::borderMode() const
{
Q_D(const QQuickEllipseShape);
return d->borderMode;
}
void QQuickEllipseShape::setBorderMode(BorderMode borderMode)
{
Q_D(QQuickEllipseShape);
if (borderMode == d->borderMode)
return;
d->borderMode = borderMode;
d->updatePath();
emit borderModeChanged();
}
void QQuickEllipseShape::resetBorderMode()
{
setBorderMode(BorderMode::Inside);
}
void QQuickEllipseShape::itemChange(ItemChange change, const ItemChangeData &value)
{
Q_D(QQuickEllipseShape);
if (d->path)
d->updatePath();
QQuickItem::itemChange(change, value);
}
QT_END_NAMESPACE
#include "moc_qquickellipseshape_p.cpp"
@@ -0,0 +1,139 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKELLIPSESHAPE_P_H
#define QQUICKELLIPSESHAPE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtQuickShapes/private/qquickshape_p.h>
#include <QtQuickShapesDesignHelpers/qtquickshapesdesignhelpersexports.h>
QT_BEGIN_NAMESPACE
class QQuickEllipseShapePrivate;
class Q_QUICKSHAPESDESIGNHELPERS_EXPORT QQuickEllipseShape : public QQuickShape
{
public:
Q_OBJECT
Q_PROPERTY(qreal sweepAngle READ sweepAngle WRITE setSweepAngle NOTIFY sweepAngleChanged FINAL)
Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged FINAL)
Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged FINAL)
Q_PROPERTY(qreal innerArcRatio READ innerArcRatio WRITE setInnerArcRatio NOTIFY
innerArcRatioChanged FINAL)
Q_PROPERTY(qreal cornerRadius READ cornerRadius WRITE setCornerRadius NOTIFY cornerRadiusChanged
FINAL)
Q_PROPERTY(
qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged FINAL)
Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged FINAL)
Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged
FINAL)
Q_PROPERTY(QQuickShapePath::CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY
capStyleChanged FINAL)
Q_PROPERTY(QQuickShapePath::JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY
joinStyleChanged FINAL)
Q_PROPERTY(QQuickShapePath::StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY
strokeStyleChanged FINAL)
Q_PROPERTY(QQuickShapePath::FillRule fillRule READ fillRule WRITE setFillRule NOTIFY
fillRuleChanged FINAL)
Q_PROPERTY(QList<qreal> dashPattern READ dashPattern WRITE setDashPattern NOTIFY
dashPatternChanged FINAL)
Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient NOTIFY
gradientChanged RESET resetFillGradient FINAL)
Q_PROPERTY(BorderMode borderMode READ borderMode WRITE setBorderMode NOTIFY borderModeChanged
RESET resetBorderMode FINAL)
QML_NAMED_ELEMENT(EllipseShape)
QML_ADDED_IN_VERSION(6, 11)
public:
QQuickEllipseShape(QQuickItem *parent = nullptr);
~QQuickEllipseShape() override;
qreal sweepAngle() const;
void setSweepAngle(qreal sweepAngle);
qreal startAngle() const;
void setStartAngle(qreal startAngle);
qreal dashOffset() const;
void setDashOffset(qreal offset);
qreal innerArcRatio() const;
void setInnerArcRatio(qreal innerArcRatio);
qreal cornerRadius() const;
void setCornerRadius(qreal cornerRadius);
qreal strokeWidth() const;
void setStrokeWidth(qreal width);
QColor fillColor() const;
void setFillColor(const QColor &color);
QColor strokeColor() const;
void setStrokeColor(const QColor &color);
QQuickShapePath::CapStyle capStyle() const;
void setCapStyle(QQuickShapePath::CapStyle style);
QQuickShapePath::JoinStyle joinStyle() const;
void setJoinStyle(QQuickShapePath::JoinStyle style);
QQuickShapePath::StrokeStyle strokeStyle() const;
void setStrokeStyle(QQuickShapePath::StrokeStyle style);
QQuickShapePath::FillRule fillRule() const;
void setFillRule(QQuickShapePath::FillRule fillRule);
QList<qreal> dashPattern() const;
void setDashPattern(const QList<qreal> &array);
QQuickShapeGradient *fillGradient() const;
void setFillGradient(QQuickShapeGradient *fillGradient);
void resetFillGradient();
enum class BorderMode { Inside, Middle, Outside };
Q_ENUM(BorderMode)
BorderMode borderMode() const;
void setBorderMode(BorderMode borderMode);
void resetBorderMode();
Q_SIGNALS:
void innerArcRatioChanged();
void cornerRadiusChanged();
void startAngleChanged();
void sweepAngleChanged();
void strokeColorChanged();
void strokeWidthChanged();
void fillColorChanged();
void joinStyleChanged();
void capStyleChanged();
void fillRuleChanged();
void strokeStyleChanged();
void dashOffsetChanged();
void dashPatternChanged();
void gradientChanged();
void borderModeChanged();
protected:
void itemChange(ItemChange change, const ItemChangeData &value) override;
private:
Q_DISABLE_COPY(QQuickEllipseShape)
Q_DECLARE_PRIVATE(QQuickEllipseShape)
};
QT_END_NAMESPACE
#endif // QQUICKELLIPSE1SHAPE_P_H
@@ -0,0 +1,147 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKELLIPSESHAPE_P_P_H
#define QQUICKELLIPSESHAPE_P_P_H
#include "qquickellipseshape_p.h"
#include <QtQml/private/qqmlpropertyutils_p.h>
#include <QtQuickShapes/private/qquickshape_p_p.h>
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
QT_BEGIN_NAMESPACE
class Q_QUICKSHAPESDESIGNHELPERS_EXPORT QQuickEllipseShapePrivate : public QQuickShapePrivate
{
Q_DECLARE_PUBLIC(QQuickEllipseShape)
public:
QQuickEllipseShapePrivate();
~QQuickEllipseShapePrivate() override;
struct RoundedCorner
{
void update(qreal diff, QVector2D a, QVector2D b, QVector2D c, qreal alpha, qreal radius)
{
A = a;
B = b;
C = c;
this->alpha = alpha;
this->radius = radius;
this->diff = diff;
}
void reset()
{
A = QVector2D();
B = QVector2D();
C = QVector2D();
alpha = 0;
radius = 0;
diff = 0;
}
// A - a point, where the rounding corner is located
QVector2D A;
// B - tangent point of the rounded corner arc, which is located on the ellipse's arc
// for the rounded corner at the ellipse's center this is a point on the edge defined by the
// end angle
// for the rounded corner at the begin angle this is a point on the outer arc of the ellipse
QVector2D B;
// C - tangent point of the rounded corner arc, which is located on the ellipse's edge
// for the rounded corner at the ellipse's center this is a point on the edge defined by the
// begin angle
QVector2D C;
qreal alpha = 0; // angle between AB and AC
qreal radius = 0; // rounded corner radius
// not a rounded corner data, but a helper used to compare
// currently calculated radius to the previously calculated radius
qreal diff = 0;
};
enum class RoundedCornerIndex { Center, InnerEnd, OuterEnd, InnerBegin, OuterBegin };
// helper, to avoid typing static_cast<int>(RoundedCornerIndex) every time
class RoundedCornerArray
{
public:
RoundedCorner &operator[](RoundedCornerIndex index)
{
return array[static_cast<int>(index)];
}
void reset()
{
for (auto &rc : array)
rc.reset();
}
private:
RoundedCorner array[5];
} roundedCorners;
void addLine(QVector2D point);
void addArc(QVector2D point, QVector2D arcRadius, QQuickPathArc::ArcDirection dir,
bool largeArc = false);
qreal getBorderOffset() const;
// calculates rounded corner at the ellipse center
void roundCenter(QVector2D center, QVector2D ellipseRadius);
// runs loop where begin and end rounded corners are calculated
void roundBeginEnd(QVector2D center, QVector2D ellipseRadius);
// calculates rounded corners on the outer arc
bool roundOuter(QVector2D center, QVector2D ellipseRadius, qreal deg, qreal arcAngle1,
qreal arcAngle2, RoundedCornerIndex index);
// calculates rounded corners on the inner arc
bool roundInner(QVector2D center, QVector2D ellipseRadius, qreal deg, qreal arcAngle1,
qreal arcAngle2, RoundedCornerIndex index);
// starts path at the center rounded corner and draws center rounded corner
void drawCenterCorner();
// connects outer and inner arcs with line and draws end rounded corner
void drawInnerEndCorner();
// starts path at the begin rounded corner and draws begin rounded corner
void drawInnerBeginCorner();
// connects previous rounded corner (center or begin) with line and draws begin rounded corner
void drawOuterBeginCorner();
// draw outer arc path from begin rounded corner to the end rounded corner
void drawOuterArcRounded(QVector2D center, QVector2D ellipseRadius);
// draw inner arc path from end rounded corner to the begin rounded corner
void drawInnerArcRounded(QVector2D center, QVector2D ellipseRadius);
// draws outer arc when no rounded corners involved
void drawOuterArc(QVector2D center, QVector2D ellipseRadius);
// draws full inner arc (no rounded corners)
void drawFullInnerArc(QVector2D center, QVector2D ellipseRadius);
// draws an ellipse when inner radius is not zero
void drawWithInnerRadius(QVector2D center, QVector2D ellipseRadius);
// draws an ellipse when inner radius is greater than zero
void drawWithoutInnerRadius(QVector2D center, QVector2D ellipseRadius);
void updatePath();
QQuickShapePath *path = nullptr;
qreal startAngle = 0;
qreal sweepAngle = 360;
qreal innerArcRatio = 0;
qreal cornerRadius = 10;
QQuickEllipseShape::BorderMode borderMode = QQuickEllipseShape::BorderMode::Inside;
};
QT_END_NAMESPACE
#endif // QQUICKELLIPSE1SHAPE_P_P_H
@@ -0,0 +1,203 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKRECTANGLESHAPE_P_H
#define QQUICKRECTANGLESHAPE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtQuickShapes/private/qquickshape_p.h>
#include <QtQuickShapesDesignHelpers/qtquickshapesdesignhelpersexports.h>
QT_BEGIN_NAMESPACE
class QQuickRectangleShapePrivate;
class Q_QUICKSHAPESDESIGNHELPERS_EXPORT QQuickRectangleShape : public QQuickShape
{
Q_OBJECT
Q_PROPERTY(bool drawTop READ drawTop WRITE setDrawTop NOTIFY drawTopChanged RESET resetDrawTop FINAL REVISION(6, 11))
Q_PROPERTY(bool drawRight READ drawRight WRITE setDrawRight NOTIFY drawRightChanged RESET resetDrawRight FINAL REVISION(6, 11))
Q_PROPERTY(bool drawBottom READ drawBottom WRITE setDrawBottom NOTIFY drawBottomChanged RESET resetDrawBottom FINAL REVISION(6, 11))
Q_PROPERTY(bool drawLeft READ drawLeft WRITE setDrawLeft NOTIFY drawLeftChanged RESET resetDrawLeft FINAL REVISION(6, 11))
Q_PROPERTY(int radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL)
Q_PROPERTY(int topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged RESET resetTopLeftRadius FINAL)
Q_PROPERTY(int topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged RESET resetTopRightRadius FINAL)
Q_PROPERTY(int bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged RESET resetBottomLeftRadius FINAL)
Q_PROPERTY(int bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius NOTIFY bottomRightRadiusChanged RESET resetBottomRightRadius FINAL)
Q_PROPERTY(bool bevel READ hasBevel WRITE setBevel NOTIFY bevelChanged RESET resetBevel FINAL)
Q_PROPERTY(bool topLeftBevel READ hasTopLeftBevel WRITE setTopLeftBevel NOTIFY topLeftBevelChanged RESET resetTopLeftBevel FINAL)
Q_PROPERTY(bool topRightBevel READ hasTopRightBevel WRITE setTopRightBevel NOTIFY topRightBevelChanged RESET resetTopRightBevel FINAL)
Q_PROPERTY(bool bottomLeftBevel READ hasBottomLeftBevel WRITE setBottomLeftBevel NOTIFY bottomLeftBevelChanged RESET resetBottomLeftBevel FINAL)
Q_PROPERTY(bool bottomRightBevel READ hasBottomRightBevel WRITE setBottomRightBevel NOTIFY bottomRightBevelChanged RESET resetBottomRightBevel FINAL)
Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged FINAL)
Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged FINAL)
Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged FINAL)
Q_PROPERTY(QQuickShapePath::JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged FINAL)
Q_PROPERTY(QQuickShapePath::CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged FINAL)
Q_PROPERTY(QQuickShapePath::StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged FINAL)
Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged FINAL)
Q_PROPERTY(QList<qreal> dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged FINAL)
Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient NOTIFY fillGradientChanged RESET resetFillGradient FINAL)
Q_PROPERTY(BorderMode borderMode READ borderMode WRITE setBorderMode NOTIFY borderModeChanged RESET resetBorderMode FINAL)
QML_NAMED_ELEMENT(RectangleShape)
QML_ADDED_IN_VERSION(6, 10)
public:
QQuickRectangleShape(QQuickItem *parent = nullptr);
~QQuickRectangleShape();
bool drawTop() const;
void setDrawTop(bool drawTop);
void resetDrawTop();
bool drawRight() const;
void setDrawRight(bool drawRight);
void resetDrawRight();
bool drawBottom() const;
void setDrawBottom(bool drawBottom);
void resetDrawBottom();
bool drawLeft() const;
void setDrawLeft(bool drawLeft);
void resetDrawLeft();
int radius() const;
void setRadius(int radius);
void resetRadius();
int topLeftRadius() const;
void setTopLeftRadius(int topLeftRadius);
void resetTopLeftRadius();
int topRightRadius() const;
void setTopRightRadius(int topRightRadius);
void resetTopRightRadius();
int bottomLeftRadius() const;
void setBottomLeftRadius(int bottomLeftRadius);
void resetBottomLeftRadius();
int bottomRightRadius() const;
void setBottomRightRadius(int bottomRightRadius);
void resetBottomRightRadius();
bool hasBevel() const;
void setBevel(bool bevel);
void resetBevel();
bool hasTopLeftBevel() const;
void setTopLeftBevel(bool topLeftBevel);
void resetTopLeftBevel();
bool hasTopRightBevel() const;
void setTopRightBevel(bool topRightBevel);
void resetTopRightBevel();
bool hasBottomLeftBevel() const;
void setBottomLeftBevel(bool bottomLeftBevel);
void resetBottomLeftBevel();
bool hasBottomRightBevel() const;
void setBottomRightBevel(bool bottomRightBevel);
void resetBottomRightBevel();
QColor strokeColor() const;
void setStrokeColor(const QColor &color);
qreal strokeWidth() const;
void setStrokeWidth(qreal width);
QColor fillColor() const;
void setFillColor(const QColor &color);
QQuickShapePath::FillRule fillRule() const;
void setFillRule(QQuickShapePath::FillRule fillRule);
QQuickShapePath::JoinStyle joinStyle() const;
void setJoinStyle(QQuickShapePath::JoinStyle style);
int miterLimit() const;
void setMiterLimit(int limit);
QQuickShapePath::CapStyle capStyle() const;
void setCapStyle(QQuickShapePath::CapStyle style);
QQuickShapePath::StrokeStyle strokeStyle() const;
void setStrokeStyle(QQuickShapePath::StrokeStyle style);
qreal dashOffset() const;
void setDashOffset(qreal offset);
QList<qreal> dashPattern() const;
void setDashPattern(const QList<qreal> &array);
QQuickShapeGradient *fillGradient() const;
void setFillGradient(QQuickShapeGradient *fillGradient);
void resetFillGradient();
enum class BorderMode {
Inside,
Middle,
Outside
};
Q_ENUM(BorderMode)
BorderMode borderMode() const;
void setBorderMode(BorderMode borderMode);
void resetBorderMode();
Q_SIGNALS:
Q_REVISION(6, 11) void drawTopChanged();
Q_REVISION(6, 11) void drawRightChanged();
Q_REVISION(6, 11) void drawBottomChanged();
Q_REVISION(6, 11) void drawLeftChanged();
Q_REVISION(6, 11) void fillGradientChanged();
void radiusChanged();
void topLeftRadiusChanged();
void topRightRadiusChanged();
void bottomLeftRadiusChanged();
void bottomRightRadiusChanged();
void bevelChanged();
void topLeftBevelChanged();
void topRightBevelChanged();
void bottomLeftBevelChanged();
void bottomRightBevelChanged();
void shapePathChanged();
void strokeColorChanged();
void strokeWidthChanged();
void fillColorChanged();
void fillRuleChanged();
void joinStyleChanged();
void miterLimitChanged();
void capStyleChanged();
void strokeStyleChanged();
void dashOffsetChanged();
void dashPatternChanged();
void borderModeChanged();
protected:
void componentComplete() override;
void itemChange(ItemChange change, const ItemChangeData &value) override;
private:
void updatePolish() override;
Q_DISABLE_COPY(QQuickRectangleShape)
Q_DECLARE_PRIVATE(QQuickRectangleShape)
};
QT_END_NAMESPACE
#endif // QQUICKRECTANGLESHAPE_P_H
@@ -0,0 +1,111 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKRECTANGLESHAPE_P_P_H
#define QQUICKRECTANGLESHAPE_P_P_H
#include "qquickrectangleshape_p.h"
#include <QtQml/private/qqmlpropertyutils_p.h>
#include <QtQuickShapes/private/qquickshape_p_p.h>
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
QT_BEGIN_NAMESPACE
class Q_QUICKSHAPESDESIGNHELPERS_EXPORT QQuickRectangleShapePrivate : public QQuickShapePrivate
{
Q_DECLARE_PUBLIC(QQuickRectangleShape)
public:
QQuickRectangleShapePrivate() = default;
static QQuickRectangleShapePrivate *get(QQuickRectangleShape *p) { return p->d_func(); }
void setTopLeftRadius(int topLeftRadius, QQml::PropertyUtils::State propertyState);
void setTopRightRadius(int topRightRadius, QQml::PropertyUtils::State propertyState);
void setBottomLeftRadius(int bottomLeftRadius, QQml::PropertyUtils::State propertyState);
void setBottomRightRadius(int bottomRightRadius, QQml::PropertyUtils::State propertyState);
void setTopLeftBevel(bool topLeftBevel, QQml::PropertyUtils::State propertyState);
void setTopRightBevel(bool topRightBevel, QQml::PropertyUtils::State propertyState);
void setBottomLeftBevel(bool bottomLeftBevel, QQml::PropertyUtils::State propertyState);
void setBottomRightBevel(bool bottomRightBevel, QQml::PropertyUtils::State propertyState);
void maybeUpdateElements();
void calculateIndependentRadii();
enum class Edge {
Top,
Right,
Bottom,
Left,
NEdges
};
QQuickShapePath *shapePath = nullptr;
QQuickPathLine *topPathLine = nullptr;
QQuickPathMove *topPathMove = nullptr;
QQuickPathArc *topRightPathArc = nullptr;
QQuickPathLine *rightPathLine = nullptr;
QQuickPathMove *rightPathMove = nullptr;
QQuickPathArc *bottomRightPathArc = nullptr;
QQuickPathLine *bottomPathLine = nullptr;
QQuickPathMove *bottomPathMove = nullptr;
QQuickPathArc *bottomLeftPathArc = nullptr;
QQuickPathLine *leftPathLine = nullptr;
QQuickPathArc *topLeftPathArc = nullptr;
static const int defaultRadius = 10;
static const bool defaultDrawEdge = true;
static const bool defaultBevel = false;
int radius = defaultRadius;
int topLeftRadius = defaultRadius;
int topRightRadius = defaultRadius;
int bottomLeftRadius = defaultRadius;
int bottomRightRadius = defaultRadius;
int effectiveTopLeftRadius = 0;
int effectiveTopRightRadius = 0;
int effectiveBottomLeftRadius = 0;
int effectiveBottomRightRadius = 0;
// If not set, the individual radii properties are equal to radius.
bool explicitTopLeftRadius = false;
bool explicitTopRightRadius = false;
bool explicitBottomLeftRadius = false;
bool explicitBottomRightRadius = false;
bool drawTop = defaultDrawEdge;
bool drawRight = defaultDrawEdge;
bool drawBottom = defaultDrawEdge;
bool drawLeft = defaultDrawEdge;
bool bevel = defaultBevel;
bool topLeftBevel = defaultBevel;
bool topRightBevel = defaultBevel;
bool bottomLeftBevel = defaultBevel;
bool bottomRightBevel = defaultBevel;
// If not set, the individual bevel properties are equal to bevel.
bool explicitTopLeftBevel = false;
bool explicitTopRightBevel = false;
bool explicitBottomLeftBevel = false;
bool explicitBottomRightBevel = false;
qreal borderOffset = 0;
qreal borderRadiusAdjustment = 0;
Edge firstVisibleEdge = Edge::NEdges;
QQuickRectangleShape::BorderMode borderMode = QQuickRectangleShape::BorderMode::Inside;
};
QT_END_NAMESPACE
#endif // QQUICKRECTANGLESHAPE_P_P_H
@@ -0,0 +1,507 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickregularpolygonshape_p.h"
#include "qquickregularpolygonshape_p_p.h"
#include <algorithm>
#include <QtMath>
#include <cstddef>
QT_BEGIN_NAMESPACE
QQuickRegularPolygonShapePrivate::QQuickRegularPolygonShapePrivate() = default;
QQuickRegularPolygonShapePrivate::~QQuickRegularPolygonShapePrivate() = default;
namespace {
qreal intersect(QVector2D p, QVector2D dir1, QVector2D q, QVector2D dir2)
{
const auto r = dir1.normalized();
const auto s = dir2.normalized();
const QVector2D pq(q.x() - p.x(), q.y() - p.y());
const QVector2D snv(s.y(), -s.x());
return QVector2D::dotProduct(pq, snv) / QVector2D::dotProduct(r, snv);
}
} // namespace
void QQuickRegularPolygonShapePrivate::updatePoints()
{
points.clear();
const qreal sliceAngle = 360.0f / sideCount;
const qreal a = width.valueBypassingBindings() / 2.0f; // x-radius
const qreal b = height.valueBypassingBindings() / 2.0f; // y-radius
const QVector2D center(a, b);
for (int i = 0; i < sideCount; ++i) {
const qreal angleToCorner = qDegreesToRadians(i * sliceAngle);
// Start at top center
const QVector2D xy = center + QVector2D(a * qSin(angleToCorner), -b * qCos(angleToCorner));
points.push_back(std::move(xy));
}
}
void QQuickRegularPolygonShapePrivate::updateBisectors()
{
const auto size = points.size();
bisectors.clear();
// A list of vectors that are the bisectors of the inner angles of the polygon.
// This is used to calculate the intersection point of neighboring bisectors for a corner.
// The minimum length of the two neighboring corner bisectors intersection point is the
// maximum for the center of the circle that make up the corner radius.
for (size_t i = 0; i < size; ++i) {
const auto &a = points[i];
const auto &b = points[(i == 0 ? size : i) - 1];
const auto &c = points[(i + 1 == size) ? 0 : (i + 1)];
const QVector2D vAB(b.x() - a.x(), b.y() - a.y());
const QVector2D vAC(c.x() - a.x(), c.y() - a.y());
const auto bisector = (vAB + vAC).normalized();
bisectors.push_back(std::move(bisector));
}
}
void QQuickRegularPolygonShapePrivate::constructPolygonPath()
{
auto *ppath = QQuickShapePathPrivate::get(path);
// start path
path->setStartX(points[0].x());
path->setStartY(points[0].y());
for (const auto &p : points) {
auto line = new QQuickPathLine(path);
line->setX(p.x());
line->setY(p.y());
ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
}
// close path
auto line = new QQuickPathLine(path);
line->setX(points[0].x());
line->setY(points[0].y());
ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
path->processPath();
}
void QQuickRegularPolygonShapePrivate::constructRoundedPolygonPath()
{
auto *ppath = QQuickShapePathPrivate::get(path);
updateBisectors();
const auto size = points.size();
for (size_t i = 0; i < size; ++i) {
const size_t idxMinusOne = (i == 0 ? size : i) - 1;
const size_t idxPlusOne = (i + 1 == size) ? 0 : (i + 1);
const auto &a = points[i];
const auto &b = points[idxMinusOne];
const auto &c = points[idxPlusOne];
qreal r = cornerRadius;
const QVector2D vAB(b.x() - a.x(), b.y() - a.y());
const QVector2D vAC(c.x() - a.x(), c.y() - a.y());
// Calculate the intersection points of the two neighboring bisectors
const qreal tAB =
intersect(a, bisectors[i],
b, bisectors[idxMinusOne]);
const qreal tAC =
intersect(a, bisectors[i],
c, bisectors[idxPlusOne]);
const qreal tMax = std::min(tAB, tAC);
// Angle between the two vectors AB and AC as radians
const qreal alpha = qAcos(QVector2D::dotProduct(vAB, vAC) / (vAB.length() * vAC.length()));
// The maximum radius of the circle that can be drawn at the corner. This is another
// constraint that Figma uses to calculate the corner radius. The corner radius shouldn't
// be bigger than half of the distance between the two neighboring corners.
const qreal maxRadius = std::round(QVector2D(c.x() - b.x(), c.y() - b.y()).length() / 2);
r = std::min(r, maxRadius);
// The optimal length of the corner bisector to place the center of the circle.
const qreal cLength = r / (qSin(alpha / 2));
// Clamp c to the maximum value found from the intersection points of the bisectors.
const qreal realC = std::min(cLength, tMax);
if (realC < cLength)
r = realC * qSin(alpha / 2);
const qreal t = qSqrt(qPow(realC, 2) - qPow(r, 2));
const auto p1 = (vAB.normalized() * t) + QVector2D(a.x(), a.y());
const auto p2 = (vAC.normalized() * t) + QVector2D(a.x(), a.y());
if (i == 0) {
path->setStartX(p1.x());
path->setStartY(p1.y());
} else {
auto line = new QQuickPathLine(path);
line->setX(p1.x());
line->setY(p1.y());
ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
}
auto arc = new QQuickPathArc(path);
arc->setX(p2.x());
arc->setY(p2.y());
arc->setRadiusX(r);
arc->setRadiusY(r);
ppath->appendPathElement(arc, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
}
// Close the polygon
auto line = new QQuickPathLine(path);
line->setX(path->startX());
line->setY(path->startY());
ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
path->processPath();
}
void QQuickRegularPolygonShapePrivate::updatePath()
{
QQuickShapePathPrivate::get(path)->clearPathElements(
QQuickPathPrivate::DeleteElementPolicy::Delete);
updatePoints();
if (qFuzzyCompare(cornerRadius, 0.0))
constructPolygonPath();
else
constructRoundedPolygonPath();
}
/*!
\qmltype RegularPolygonShape
\inqmlmodule QtQuick.Shapes.DesignHelpers
\brief A filled regular polygon with an optional border.
\since QtQuick 6.10
A regular polygon can be just a 2D polygon shaped stroke, a filling, or a
stroke with filling. The \l strokeColor, \l strokeWidth, and \l strokeStyle
properties specify the appearance of the outline. The \l dashPattern and
\l dashOffset properties specify the appearance of dashed stroke.
The area inside the stroke is painted using either a solid fill color,
specified using the \l fillColor property, or a gradient, defined using
one of the \l ShapeGradient subtypes and set using the \l gradient
property. If both a color and a gradient are specified, the gradient is
used.
To create a polygon with a stroke, set the \sideCount property between 3 to
100 and the \l strokeWidth property greater than 0. The \l strokeWidth
property specifies the width of the polygon stroke. The default \l sideCount
value is 6 and the default \l strokeWidth value is 4. Setting the
\l strokeWidth value to a negetive value hides the border.
The \l cornerRadius property specifies whether the polygon corners are rounded.
*/
QQuickRegularPolygonShape::QQuickRegularPolygonShape(QQuickItem *parent)
: QQuickShape(*(new QQuickRegularPolygonShapePrivate), parent)
{
Q_D(QQuickRegularPolygonShape);
setPreferredRendererType(CurveRenderer);
setWidth(200);
setHeight(200);
d->path = new QQuickShapePath(this);
d->path->setAsynchronous(true);
d->path->setStrokeWidth(1);
d->path->setStrokeColor(QColorConstants::Black);
d->path->setFillColor(QColorConstants::White);
d->sp.append(d->path);
d->path->setParent(this);
d->extra.value().resourcesList.append(d->path);
}
QQuickRegularPolygonShape::~QQuickRegularPolygonShape() = default;
/*!
\include shapepath.qdocinc {dashOffset-property}
{QtQuick.Shapes.DesignHelpers::RegularPolygonShape}
*/
qreal QQuickRegularPolygonShape::dashOffset() const
{
Q_D(const QQuickRegularPolygonShape);
return d->path->dashOffset();
}
void QQuickRegularPolygonShape::setDashOffset(qreal offset)
{
Q_D(QQuickRegularPolygonShape);
if (qFuzzyCompare(d->path->dashOffset(), offset))
return;
d->path->setDashOffset(offset);
emit dashOffsetChanged();
}
/*!
\qmlproperty real QtQuick.Shapes.DesignHelpers::RegularPolygonShape::cornerRadius
The property property specifies whether the polygon corners are rounded.
The default value is \c 10.
*/
qreal QQuickRegularPolygonShape::cornerRadius() const
{
Q_D(const QQuickRegularPolygonShape);
return d->cornerRadius;
}
void QQuickRegularPolygonShape::setCornerRadius(qreal radius)
{
Q_D(QQuickRegularPolygonShape);
if (qFuzzyCompare(d->cornerRadius, radius))
return;
d->cornerRadius = radius;
d->updatePath();
emit cornerRadiusChanged();
}
/*!
\qmlproperty int QtQuick.Shapes.DesignHelpers::RegularPolygonShape::sideCount
The number of edges on the regular polygon. The minimum number of edges can
be 3.
The default value is \c 6.
*/
int QQuickRegularPolygonShape::sideCount() const
{
Q_D(const QQuickRegularPolygonShape);
return d->sideCount;
}
void QQuickRegularPolygonShape::setSideCount(int sideCount)
{
Q_D(QQuickRegularPolygonShape);
if (d->sideCount == sideCount)
return;
d->sideCount = sideCount;
d->updatePath();
emit sideCountChanged();
}
/*!
\qmlproperty real QtQuick.Shapes.DesignHelpers::RegularPolygonShape::strokeWidth
This property holds the stroke width.
When set to a negative value, no stroking occurs.
The default value is \c 1.
*/
qreal QQuickRegularPolygonShape::strokeWidth() const
{
Q_D(const QQuickRegularPolygonShape);
return d->path->strokeWidth();
}
void QQuickRegularPolygonShape::setStrokeWidth(qreal width)
{
Q_D(QQuickRegularPolygonShape);
if (qFuzzyCompare(d->path->strokeWidth(), width))
return;
d->path->setStrokeWidth(width);
emit strokeWidthChanged();
}
/*!
\qmlproperty color QtQuick.Shapes.DesignHelpers::RegularPolygonShape::fillColor
This property holds the fill color.
When set to \c transparent, no filling occurs.
The default value is \c "white".
\note If either \l fillGradient is set to something other than \c null, it
will be used instead of \c fillColor.
*/
QColor QQuickRegularPolygonShape::fillColor() const
{
Q_D(const QQuickRegularPolygonShape);
return d->path->fillColor();
}
void QQuickRegularPolygonShape::setFillColor(const QColor &color)
{
Q_D(QQuickRegularPolygonShape);
d->path->setFillColor(color);
d->updatePath();
emit fillColorChanged();
}
/*!
\qmlproperty color QtQuick.Shapes.DesignHelpers::RegularPolygonShape::strokeColor
This property holds the stroking color.
When set to \c transparent, no stroking occurs.
The default value is \c "black".
*/
QColor QQuickRegularPolygonShape::strokeColor() const
{
Q_D(const QQuickRegularPolygonShape);
return d->path->strokeColor();
}
void QQuickRegularPolygonShape::setStrokeColor(const QColor &color)
{
Q_D(QQuickRegularPolygonShape);
d->path->setStrokeColor(color);
emit strokeColorChanged();
}
/*!
\include shapepath.qdocinc {capStyle-property}
{QtQuick.Shapes.DesignHelpers::RegularPolygonShape}
Since a polygon is drawn, the path forms a loop with no line end points.
Therefore, capStyle is only needed when strokeStyle == ShapePath.DashLine
*/
QQuickShapePath::CapStyle QQuickRegularPolygonShape::capStyle() const
{
Q_D(const QQuickRegularPolygonShape);
return d->path->capStyle();
}
void QQuickRegularPolygonShape::setCapStyle(QQuickShapePath::CapStyle style)
{
Q_D(QQuickRegularPolygonShape);
if (d->path->capStyle() == style)
return;
d->path->setCapStyle(style);
emit capStyleChanged();
}
/*!
\include shapepath.qdocinc {joinStyle-property}
{QtQuick.Shapes.DesignHelpers::RegularPolygonShape}
The joinStyle is only meaningful if cornerRadius == 0.
*/
QQuickShapePath::JoinStyle QQuickRegularPolygonShape::joinStyle() const
{
Q_D(const QQuickRegularPolygonShape);
return d->path->joinStyle();
}
void QQuickRegularPolygonShape::setJoinStyle(QQuickShapePath::JoinStyle style)
{
Q_D(QQuickRegularPolygonShape);
if (d->path->joinStyle() == style)
return;
d->path->setJoinStyle(style);
emit joinStyleChanged();
}
/*!
\include shapepath.qdocinc {strokeStyle-property}
{QtQuick.Shapes.DesignHelpers::RegularPolygonShape}
*/
QQuickShapePath::StrokeStyle QQuickRegularPolygonShape::strokeStyle() const
{
Q_D(const QQuickRegularPolygonShape);
return d->path->strokeStyle();
}
void QQuickRegularPolygonShape::setStrokeStyle(QQuickShapePath::StrokeStyle style)
{
Q_D(QQuickRegularPolygonShape);
if (d->path->strokeStyle() == style)
return;
d->path->setStrokeStyle(style);
emit strokeStyleChanged();
}
/*!
\include shapepath.qdocinc {dashPattern-property}
{QtQuick.Shapes.DesignHelpers::RegularPolygonShape}
*/
QList<qreal> QQuickRegularPolygonShape::dashPattern() const
{
Q_D(const QQuickRegularPolygonShape);
return d->path->dashPattern();
}
void QQuickRegularPolygonShape::setDashPattern(const QList<qreal> &array)
{
Q_D(QQuickRegularPolygonShape);
d->path->setDashPattern(array);
emit dashPatternChanged();
}
/*!
\qmlproperty ShapeGradient QtQuick.Shapes.DesignHelpers::RegularPolygonShape::fillGradient
The fillGradient of the polygon fill color.
By default, no fillGradient is enabled and the value is null. In this case, the
fill uses a solid color based on the value of \l fillColor.
When set, \l fillColor is ignored and filling is done using one of the
\l ShapeGradient subtypes.
\note The \l Gradient type cannot be used here. Rather, prefer using one of
the advanced subtypes, like \l LinearGradient.
*/
QQuickShapeGradient *QQuickRegularPolygonShape::fillGradient() const
{
Q_D(const QQuickRegularPolygonShape);
return d->path->fillGradient();
}
void QQuickRegularPolygonShape::setFillGradient(QQuickShapeGradient *fillGradient)
{
Q_D(QQuickRegularPolygonShape);
d->path->setFillGradient(fillGradient);
emit gradientChanged();
}
void QQuickRegularPolygonShape::resetFillGradient()
{
setFillGradient(nullptr);
}
void QQuickRegularPolygonShape::itemChange(ItemChange change, const ItemChangeData &value)
{
Q_D(QQuickRegularPolygonShape);
if (d->path)
d->updatePath();
QQuickItem::itemChange(change, value);
}
QT_END_NAMESPACE
#include "moc_qquickregularpolygonshape_p.cpp"
@@ -0,0 +1,112 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKREGULARPOLYGONSHAPE_P_H
#define QQUICKREGULARPOLYGONSHAPE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtQuickShapes/private/qquickshape_p.h>
#include <QtQuickShapesDesignHelpers/qtquickshapesdesignhelpersexports.h>
QT_BEGIN_NAMESPACE
class QQuickRegularPolygonShapePrivate;
class Q_QUICKSHAPESDESIGNHELPERS_EXPORT QQuickRegularPolygonShape : public QQuickShape
{
public:
Q_OBJECT
Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged FINAL)
Q_PROPERTY(qreal cornerRadius READ cornerRadius WRITE setCornerRadius NOTIFY cornerRadiusChanged FINAL)
Q_PROPERTY(int sideCount READ sideCount WRITE setSideCount NOTIFY sideCountChanged FINAL)
Q_PROPERTY(
qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged FINAL)
Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged FINAL)
Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged
FINAL)
Q_PROPERTY(QQuickShapePath::CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY
capStyleChanged FINAL)
Q_PROPERTY(QQuickShapePath::JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY
joinStyleChanged FINAL)
Q_PROPERTY(QQuickShapePath::StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY
strokeStyleChanged FINAL)
Q_PROPERTY(QList<qreal> dashPattern READ dashPattern WRITE setDashPattern NOTIFY
dashPatternChanged FINAL)
Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient NOTIFY
gradientChanged RESET resetFillGradient FINAL)
QML_NAMED_ELEMENT(RegularPolygonShape)
QML_ADDED_IN_VERSION(6, 11)
public:
QQuickRegularPolygonShape(QQuickItem *parent = nullptr);
~QQuickRegularPolygonShape() override;
qreal dashOffset() const;
void setDashOffset(qreal offset);
qreal cornerRadius() const;
void setCornerRadius(qreal);
int sideCount() const;
void setSideCount(int);
qreal strokeWidth() const;
void setStrokeWidth(qreal width);
QColor fillColor() const;
void setFillColor(const QColor &color);
QColor strokeColor() const;
void setStrokeColor(const QColor &color);
QQuickShapePath::CapStyle capStyle() const;
void setCapStyle(QQuickShapePath::CapStyle style);
QQuickShapePath::JoinStyle joinStyle() const;
void setJoinStyle(QQuickShapePath::JoinStyle style);
QQuickShapePath::StrokeStyle strokeStyle() const;
void setStrokeStyle(QQuickShapePath::StrokeStyle style);
QList<qreal> dashPattern() const;
void setDashPattern(const QList<qreal> &array);
QQuickShapeGradient *fillGradient() const;
void setFillGradient(QQuickShapeGradient *fillGradient);
void resetFillGradient();
Q_SIGNALS:
void cornerRadiusChanged();
void sideCountChanged();
void strokeColorChanged();
void strokeWidthChanged();
void fillColorChanged();
void joinStyleChanged();
void capStyleChanged();
void strokeStyleChanged();
void dashOffsetChanged();
void dashPatternChanged();
void gradientChanged();
protected:
void itemChange(ItemChange change, const ItemChangeData &value) override;
private:
Q_DISABLE_COPY(QQuickRegularPolygonShape)
Q_DECLARE_PRIVATE(QQuickRegularPolygonShape)
};
QT_END_NAMESPACE
#endif // QQUICKREGULARPOLYGONSHAPE_P_H
@@ -0,0 +1,52 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKREGULARPOLYGONSHAPE_P_P_H
#define QQUICKREGULARPOLYGONSHAPE_P_P_H
#include "qquickregularpolygonshape_p.h"
#include <QtQml/private/qqmlpropertyutils_p.h>
#include <QtQuickShapes/private/qquickshape_p_p.h>
#include <qpoint.h>
#include <qvectornd.h>
#include <vector>
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
QT_BEGIN_NAMESPACE
class Q_QUICKSHAPESDESIGNHELPERS_EXPORT QQuickRegularPolygonShapePrivate : public QQuickShapePrivate
{
Q_DECLARE_PUBLIC(QQuickRegularPolygonShape)
public:
QQuickRegularPolygonShapePrivate();
~QQuickRegularPolygonShapePrivate() override;
void updatePath();
void updatePoints();
void updateBisectors();
void constructPolygonPath();
void constructRoundedPolygonPath();
qreal cornerRadius = 10;
int sideCount = 6;
std::vector<QVector2D> points;
std::vector<QVector2D> bisectors;
QQuickShapePath *path = nullptr;
};
QT_END_NAMESPACE
#endif // QQUICKREGULARPOLYGONSHAPE_P_P_H
@@ -0,0 +1,21 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSHAPESDESIGNHELPERSGLOBAL_P_H
#define QQUICKSHAPESDESIGNHELPERSGLOBAL_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qquickshapesdesignhelpersglobal.h"
#include <QtQuickShapesDesignHelpers/qtquickshapesdesignhelpersexports.h>
#endif // QQUICKSHAPESDESIGNHELPERSGLOBAL_P_H
@@ -0,0 +1,504 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickstarshape_p.h"
#include "qquickstarshape_p_p.h"
#include <algorithm>
#include <QtMath>
#include <cstddef>
QT_BEGIN_NAMESPACE
namespace {
inline qreal arc_angle(qreal angle)
{
return angle - 90;
}
inline QVector2D arc_point(QVector2D center, QVector2D radius, qreal angle)
{
return QVector2D(center.x() + radius.x() * qCos(qDegreesToRadians(angle)),
center.y() + radius.y() * qSin(qDegreesToRadians(angle)));
}
inline qreal cross(QVector2D a, QVector2D b)
{
return a.x() * b.y() - a.y() * b.x();
}
qreal angle_between_vectors(QVector2D a, QVector2D b)
{
const QVector2D uA = a.normalized();
const QVector2D uB = b.normalized();
const qreal angle = qAtan2(cross(uA, uB), QVector2D::dotProduct(uA, uB));
if (std::fabs(angle) < FLT_EPSILON)
return 0.0f;
return angle;
}
} // namespace
QQuickStarShapePrivate::QQuickStarShapePrivate() = default;
QQuickStarShapePrivate::~QQuickStarShapePrivate() = default;
void QQuickStarShapePrivate::updatePoints()
{
points.clear();
const qreal rectWidth = width.valueBypassingBindings();
const qreal rectHeight = height.valueBypassingBindings();
const QVector2D center(rectWidth * 0.5, rectHeight * 0.5);
const QVector2D radius(rectWidth * 0.5, rectHeight * 0.5);
const QVector2D inner_radius = radius * std::min(std::max(ratio, 0.001), 1.0);
const int numPoints = pointCount * 2;
const qreal sliceAngle = (360.0f / numPoints);
for (int i = 0; i < numPoints; ++i) {
const qreal angle = i * sliceAngle;
const auto p = arc_point(center, i % 2 == 0 ? radius : inner_radius, arc_angle(angle));
points.emplace_back(std::move(p));
}
}
void QQuickStarShapePrivate::constructPolygonPath()
{
auto *ppath = QQuickShapePathPrivate::get(path);
path->setStartX(points[0].x());
path->setStartY(points[0].y());
for (const auto &p : points) {
auto line = new QQuickPathLine(path);
line->setX(p.x());
line->setY(p.y());
ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
}
auto line = new QQuickPathLine(path);
line->setX(points[0].x());
line->setY(points[0].y());
ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
path->processPath();
}
void QQuickStarShapePrivate::constructRoundedPolygonPath()
{
const auto size = points.size();
auto *ppath = QQuickShapePathPrivate::get(path);
for (size_t i = 0; i < size; ++i) {
const auto &a = points[i];
const auto &b = points[(i == 0 ? size : i) - 1];
const auto &c = points[(i + 1 == size) ? 0 : (i + 1)];
const QVector2D ab = b - a;
const QVector2D ac = c - a;
const qreal alpha = angle_between_vectors(ab, ac);
const qreal halfAngle = std::fabs(alpha) * 0.5;
qreal corner_radius = cornerRadius;
auto edgeOffset = corner_radius / qTan(halfAngle);
const qreal edge = std::min(ab.length(), ac.length());
if (edgeOffset > edge * 0.5) {
edgeOffset = edge * 0.5;
corner_radius = edgeOffset * qTan(halfAngle);
}
const auto B = a + ab.normalized() * edgeOffset;
const auto C = a + ac.normalized() * edgeOffset;
if (i == 0) {
path->setStartX(B.x());
path->setStartY(B.y());
} else {
auto line = new QQuickPathLine(path);
line->setX(B.x());
line->setY(B.y());
ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
}
auto arc = new QQuickPathArc(path);
arc->setX(C.x());
arc->setY(C.y());
arc->setRadiusX(corner_radius);
arc->setRadiusY(corner_radius);
arc->setDirection(alpha > 0 ? QQuickPathArc::ArcDirection::Counterclockwise
: QQuickPathArc::ArcDirection::Clockwise);
ppath->appendPathElement(arc, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
}
auto line = new QQuickPathLine(path);
line->setX(path->startX());
line->setY(path->startY());
ppath->appendPathElement(line, QQuickPathPrivate::ProcessPathPolicy::DontProcess);
path->processPath();
}
void QQuickStarShapePrivate::updatePath()
{
QQuickShapePathPrivate::get(path)->clearPathElements(
QQuickPathPrivate::DeleteElementPolicy::Delete);
updatePoints();
if (qFuzzyCompare(cornerRadius, 0.0))
constructPolygonPath();
else
constructRoundedPolygonPath();
}
/*!
\qmltype StarShape
\inqmlmodule QtQuick.Shapes.DesignHelpers
\brief A filled star-shaped polygon with an optional border.
\since QtQuick 6.10
A star can be a star shaped stroke, a filling, or a stroke with filling.
The \l strokeColor, \l strokeWidth, and \l strokeStyle properties specify
the appearance of the outline. The \l dashPattern and \l dashOffset
properties specify the appearance of dashed stroke.
Set the \l pointCount property between 3 and 60 to specify the number of
points of the star. Set the \l ratio between 0.1 and 1 to specify the
distance of the inner points of the star from the center.
The area inside the stroke is painted using either a solid fill color, specified using the
\l fillColor property, or a gradient, defined using one of the \l ShapeGradient subtypes and set
using the \l gradient property. If both a color and a gradient are specified, the gradient is
used.
To create a star with a stroke, set the \l strokeWidth property to a value greater than 0. The
\l strokeWidth property specifies the width of the polygon stroke. The default \l pointCount
value is 6 and the default \l strokeWidth value is 4. Setting the \l strokeWidth value to a
negative value hides the border.
The \l cornerRadius property specifies whether the star corners are rounded.
*/
QQuickStarShape::QQuickStarShape(QQuickItem *parent)
: QQuickShape(*(new QQuickStarShapePrivate), parent)
{
Q_D(QQuickStarShape);
setPreferredRendererType(CurveRenderer);
setWidth(200);
setHeight(200);
d->path = new QQuickShapePath(this);
d->path->setAsynchronous(true);
d->path->setStrokeWidth(1);
d->path->setStrokeColor(QColorConstants::Black);
d->path->setFillColor(QColorConstants::White);
d->sp.append(d->path);
d->path->setParent(this);
d->extra.value().resourcesList.append(d->path);
}
QQuickStarShape::~QQuickStarShape() = default;
/*!
\include shapepath.qdocinc {dashOffset-property}
{QtQuick.Shapes.DesignHelpers::StarShape}
*/
qreal QQuickStarShape::dashOffset() const
{
Q_D(const QQuickStarShape);
return d->path->dashOffset();
}
void QQuickStarShape::setDashOffset(qreal offset)
{
Q_D(QQuickStarShape);
if (qFuzzyCompare(d->path->dashOffset(), offset))
return;
d->path->setDashOffset(offset);
emit dashOffsetChanged();
}
/*!
\qmlproperty real QtQuick.Shapes.DesignHelpers::StarShape::cornerRadius
The property controls the rounding of both the star's outer points and
inner points.
The default value is \c 10.
*/
qreal QQuickStarShape::cornerRadius() const
{
Q_D(const QQuickStarShape);
return d->cornerRadius;
}
void QQuickStarShape::setCornerRadius(qreal radius)
{
Q_D(QQuickStarShape);
if (qFuzzyCompare(d->cornerRadius, radius))
return;
d->cornerRadius = radius;
d->updatePath();
emit cornerRadiusChanged();
}
/*!
\qmlproperty real QtQuick.Shapes.DesignHelpers::StarShape::ratio
The property defines the distance of the inner points of the star from the
center.
The default value is \c 0.5.
*/
qreal QQuickStarShape::ratio() const
{
Q_D(const QQuickStarShape);
return d->ratio;
}
void QQuickStarShape::setRatio(qreal ratio)
{
Q_D(QQuickStarShape);
if (qFuzzyCompare(d->ratio, ratio))
return;
d->ratio = ratio;
d->updatePath();
emit ratioChanged();
}
/*!
\qmlproperty int QtQuick.Shapes.DesignHelpers::StarShape::pointCount
The property defines the total number of points the star has.
The default value is \c 6.
*/
int QQuickStarShape::pointCount() const
{
Q_D(const QQuickStarShape);
return d->pointCount;
}
void QQuickStarShape::setPointCount(int count)
{
Q_D(QQuickStarShape);
if (d->pointCount == count)
return;
d->pointCount = count;
d->updatePath();
emit pointCountChanged();
}
/*!
\qmlproperty real QtQuick.Shapes.DesignHelpers::StarShape::strokeWidth
This property holds the stroke width.
When set to a negative value, no stroking occurs.
The default value is \c 1.
*/
qreal QQuickStarShape::strokeWidth() const
{
Q_D(const QQuickStarShape);
return d->path->strokeWidth();
}
void QQuickStarShape::setStrokeWidth(qreal width)
{
Q_D(QQuickStarShape);
if (qFuzzyCompare(d->path->strokeWidth(), width))
return;
d->path->setStrokeWidth(width);
emit strokeWidthChanged();
}
/*!
\qmlproperty color QtQuick.Shapes.DesignHelpers::StarShape::fillColor
This property holds the fill color.
When set to \c transparent, no filling occurs.
The default value is \c "white".
\note If either \l fillGradient is set to something other than \c null, it
will be used instead of \c fillColor.
*/
QColor QQuickStarShape::fillColor() const
{
Q_D(const QQuickStarShape);
return d->path->fillColor();
}
void QQuickStarShape::setFillColor(const QColor &color)
{
Q_D(QQuickStarShape);
d->path->setFillColor(color);
d->updatePath();
emit fillColorChanged();
}
/*!
\qmlproperty color QtQuick.Shapes.DesignHelpers::StarShape::strokeColor
This property holds the stroking color.
When set to \c transparent, no stroking occurs.
The default value is \c "black".
*/
QColor QQuickStarShape::strokeColor() const
{
Q_D(const QQuickStarShape);
return d->path->strokeColor();
}
void QQuickStarShape::setStrokeColor(const QColor &color)
{
Q_D(QQuickStarShape);
d->path->setStrokeColor(color);
emit strokeColorChanged();
}
/*!
\include shapepath.qdocinc {capStyle-property}
{QtQuick.Shapes.DesignHelpers::StarShape}
Since a star is drawn, the path forms a loop with no line end points.
Therefore, capStyle is only needed when strokeStyle == ShapePath.DashLine
*/
QQuickShapePath::CapStyle QQuickStarShape::capStyle() const
{
Q_D(const QQuickStarShape);
return d->path->capStyle();
}
void QQuickStarShape::setCapStyle(QQuickShapePath::CapStyle style)
{
Q_D(QQuickStarShape);
if (d->path->capStyle() == style)
return;
d->path->setCapStyle(style);
emit capStyleChanged();
}
/*!
\include shapepath.qdocinc {joinStyle-property}
{QtQuick.Shapes.DesignHelpers::StarShape}
The joinStyle is only meaningful if cornerRadius == 0.
*/
QQuickShapePath::JoinStyle QQuickStarShape::joinStyle() const
{
Q_D(const QQuickStarShape);
return d->path->joinStyle();
}
void QQuickStarShape::setJoinStyle(QQuickShapePath::JoinStyle style)
{
Q_D(QQuickStarShape);
if (d->path->joinStyle() == style)
return;
d->path->setJoinStyle(style);
emit joinStyleChanged();
}
/*!
\include shapepath.qdocinc {strokeStyle-property}
{QtQuick.Shapes.DesignHelpers::StarShape}
*/
QQuickShapePath::StrokeStyle QQuickStarShape::strokeStyle() const
{
Q_D(const QQuickStarShape);
return d->path->strokeStyle();
}
void QQuickStarShape::setStrokeStyle(QQuickShapePath::StrokeStyle style)
{
Q_D(QQuickStarShape);
if (d->path->strokeStyle() == style)
return;
d->path->setStrokeStyle(style);
emit strokeStyleChanged();
}
/*!
\include shapepath.qdocinc {dashPattern-property}
{QtQuick.Shapes.DesignHelpers::StarShape}
*/
QList<qreal> QQuickStarShape::dashPattern() const
{
Q_D(const QQuickStarShape);
return d->path->dashPattern();
}
void QQuickStarShape::setDashPattern(const QList<qreal> &array)
{
Q_D(QQuickStarShape);
d->path->setDashPattern(array);
emit dashPatternChanged();
}
/*!
\qmlproperty ShapeGradient QtQuick.Shapes.DesignHelpers::StarShape::fillGradient
The fillGradient of the star fill color.
By default, no fillGradient is enabled and the value is null. In this case, the
fill uses a solid color based on the value of \l fillColor.
When set, \l fillColor is ignored and filling is done using one of the
\l ShapeGradient subtypes.
\note The \l Gradient type cannot be used here. Rather, prefer using one of
the advanced subtypes, like \l LinearGradient.
*/
QQuickShapeGradient *QQuickStarShape::fillGradient() const
{
Q_D(const QQuickStarShape);
return d->path->fillGradient();
}
void QQuickStarShape::setFillGradient(QQuickShapeGradient *fillGradient)
{
Q_D(QQuickStarShape);
d->path->setFillGradient(fillGradient);
emit gradientChanged();
}
void QQuickStarShape::resetFillGradient()
{
setFillGradient(nullptr);
}
void QQuickStarShape::itemChange(ItemChange change, const ItemChangeData &value)
{
Q_D(QQuickStarShape);
if (d->path)
d->updatePath();
QQuickItem::itemChange(change, value);
}
QT_END_NAMESPACE
#include "moc_qquickstarshape_p.cpp"
@@ -0,0 +1,118 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSTARSHAPE_P_H
#define QQUICKSTARSHAPE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtQuickShapes/private/qquickshape_p.h>
#include <QtQuickShapesDesignHelpers/qtquickshapesdesignhelpersexports.h>
QT_BEGIN_NAMESPACE
class QQuickStarShapePrivate;
class Q_QUICKSHAPESDESIGNHELPERS_EXPORT QQuickStarShape : public QQuickShape
{
public:
Q_OBJECT
Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged FINAL)
Q_PROPERTY(qreal cornerRadius READ cornerRadius WRITE setCornerRadius NOTIFY cornerRadiusChanged
FINAL)
Q_PROPERTY(int pointCount READ pointCount WRITE setPointCount NOTIFY pointCountChanged FINAL)
Q_PROPERTY(qreal ratio READ ratio WRITE setRatio NOTIFY ratioChanged FINAL)
Q_PROPERTY(
qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged FINAL)
Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged FINAL)
Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged
FINAL)
Q_PROPERTY(QQuickShapePath::CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY
capStyleChanged FINAL)
Q_PROPERTY(QQuickShapePath::JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY
joinStyleChanged FINAL)
Q_PROPERTY(QQuickShapePath::StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY
strokeStyleChanged FINAL)
Q_PROPERTY(QList<qreal> dashPattern READ dashPattern WRITE setDashPattern NOTIFY
dashPatternChanged FINAL)
Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient NOTIFY
gradientChanged RESET resetFillGradient FINAL)
QML_NAMED_ELEMENT(StarShape)
QML_ADDED_IN_VERSION(6, 11)
public:
QQuickStarShape(QQuickItem *parent = nullptr);
~QQuickStarShape() override;
qreal dashOffset() const;
void setDashOffset(qreal);
qreal cornerRadius() const;
void setCornerRadius(qreal);
qreal ratio() const;
void setRatio(qreal);
int pointCount() const;
void setPointCount(int);
qreal strokeWidth() const;
void setStrokeWidth(qreal);
QColor fillColor() const;
void setFillColor(const QColor &color);
QColor strokeColor() const;
void setStrokeColor(const QColor &color);
QQuickShapePath::CapStyle capStyle() const;
void setCapStyle(QQuickShapePath::CapStyle style);
QQuickShapePath::JoinStyle joinStyle() const;
void setJoinStyle(QQuickShapePath::JoinStyle style);
QQuickShapePath::StrokeStyle strokeStyle() const;
void setStrokeStyle(QQuickShapePath::StrokeStyle style);
QList<qreal> dashPattern() const;
void setDashPattern(const QList<qreal> &array);
QQuickShapeGradient *fillGradient() const;
void setFillGradient(QQuickShapeGradient *fillGradient);
void resetFillGradient();
Q_SIGNALS:
void cornerRadiusChanged();
void ratioChanged();
void pointCountChanged();
void strokeColorChanged();
void strokeWidthChanged();
void fillColorChanged();
void joinStyleChanged();
void capStyleChanged();
void strokeStyleChanged();
void dashOffsetChanged();
void dashPatternChanged();
void gradientChanged();
protected:
void itemChange(ItemChange change, const ItemChangeData &value) override;
private:
Q_DISABLE_COPY(QQuickStarShape)
Q_DECLARE_PRIVATE(QQuickStarShape)
};
QT_END_NAMESPACE
#endif // QQUICKSTARSHAPE_P_H
@@ -0,0 +1,51 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSTARSHAPE_P_P_H
#define QQUICKSTARSHAPE_P_P_H
#include "qquickstarshape_p.h"
#include <QtQml/private/qqmlpropertyutils_p.h>
#include <QtQuickShapes/private/qquickshape_p_p.h>
#include <qpoint.h>
#include <qvectornd.h>
#include <vector>
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
QT_BEGIN_NAMESPACE
class Q_QUICKSHAPESDESIGNHELPERS_EXPORT QQuickStarShapePrivate : public QQuickShapePrivate
{
Q_DECLARE_PUBLIC(QQuickStarShape)
public:
QQuickStarShapePrivate();
~QQuickStarShapePrivate() override;
void updatePath();
void updatePoints();
void constructPolygonPath();
void constructRoundedPolygonPath();
qreal cornerRadius = 10;
int pointCount = 6;
qreal ratio = 0.5;
std::vector<QVector2D> points;
QQuickShapePath *path = nullptr;
};
QT_END_NAMESPACE
#endif // QQUICKSTARSHAPE_P_P_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,482 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSHAPE_P_H
#define QQUICKSHAPE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QtQuick/qquickitem.h>
#include <private/qtquickglobal_p.h>
#include <private/qquickpath_p_p.h>
#include <private/qquickrectangle_p.h>
QT_BEGIN_NAMESPACE
class QQuickShapePathPrivate;
class QQuickShapePrivate;
void Q_QUICKSHAPES_EXPORT QQuickShapes_initializeModule();
class Q_QUICKSHAPES_EXPORT QQuickShapesModule
{
public:
static void defineModule();
};
class Q_QUICKSHAPES_EXPORT QQuickShapeGradient : public QQuickGradient
{
Q_OBJECT
Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged)
Q_CLASSINFO("DefaultProperty", "stops")
QML_NAMED_ELEMENT(ShapeGradient)
QML_ADDED_IN_VERSION(1, 0)
QML_UNCREATABLE("ShapeGradient is an abstract base class.");
public:
enum SpreadMode {
PadSpread,
ReflectSpread,
RepeatSpread
};
Q_ENUM(SpreadMode)
QQuickShapeGradient(QObject *parent = nullptr);
SpreadMode spread() const;
void setSpread(SpreadMode mode);
Q_SIGNALS:
void spreadChanged();
private:
SpreadMode m_spread;
};
class Q_QUICKSHAPES_EXPORT QQuickShapeLinearGradient : public QQuickShapeGradient
{
Q_OBJECT
Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed)
Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed)
Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed)
Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed)
Q_CLASSINFO("DefaultProperty", "stops")
QML_NAMED_ELEMENT(LinearGradient)
QML_ADDED_IN_VERSION(1, 0)
public:
QQuickShapeLinearGradient(QObject *parent = nullptr);
qreal x1() const;
void setX1(qreal v);
qreal y1() const;
void setY1(qreal v);
qreal x2() const;
void setX2(qreal v);
qreal y2() const;
void setY2(qreal v);
Q_SIGNALS:
void x1Changed();
void y1Changed();
void x2Changed();
void y2Changed();
private:
QPointF m_start;
QPointF m_end;
};
class Q_QUICKSHAPES_EXPORT QQuickShapeRadialGradient : public QQuickShapeGradient
{
Q_OBJECT
Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged)
Q_PROPERTY(qreal centerY READ centerY WRITE setCenterY NOTIFY centerYChanged)
Q_PROPERTY(qreal centerRadius READ centerRadius WRITE setCenterRadius NOTIFY centerRadiusChanged)
Q_PROPERTY(qreal focalX READ focalX WRITE setFocalX NOTIFY focalXChanged)
Q_PROPERTY(qreal focalY READ focalY WRITE setFocalY NOTIFY focalYChanged)
Q_PROPERTY(qreal focalRadius READ focalRadius WRITE setFocalRadius NOTIFY focalRadiusChanged)
Q_CLASSINFO("DefaultProperty", "stops")
QML_NAMED_ELEMENT(RadialGradient)
QML_ADDED_IN_VERSION(1, 0)
public:
QQuickShapeRadialGradient(QObject *parent = nullptr);
qreal centerX() const;
void setCenterX(qreal v);
qreal centerY() const;
void setCenterY(qreal v);
qreal centerRadius() const;
void setCenterRadius(qreal v);
qreal focalX() const;
void setFocalX(qreal v);
qreal focalY() const;
void setFocalY(qreal v);
qreal focalRadius() const;
void setFocalRadius(qreal v);
Q_SIGNALS:
void centerXChanged();
void centerYChanged();
void focalXChanged();
void focalYChanged();
void centerRadiusChanged();
void focalRadiusChanged();
private:
QPointF m_centerPoint;
QPointF m_focalPoint;
qreal m_centerRadius = 0;
qreal m_focalRadius = 0;
};
class Q_QUICKSHAPES_EXPORT QQuickShapeConicalGradient : public QQuickShapeGradient
{
Q_OBJECT
Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged)
Q_PROPERTY(qreal centerY READ centerY WRITE setCenterY NOTIFY centerYChanged)
Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged)
Q_CLASSINFO("DefaultProperty", "stops")
QML_NAMED_ELEMENT(ConicalGradient)
QML_ADDED_IN_VERSION(1, 0)
public:
QQuickShapeConicalGradient(QObject *parent = nullptr);
qreal centerX() const;
void setCenterX(qreal v);
qreal centerY() const;
void setCenterY(qreal v);
qreal angle() const;
void setAngle(qreal v);
Q_SIGNALS:
void centerXChanged();
void centerYChanged();
void angleChanged();
private:
QPointF m_centerPoint;
qreal m_angle = 0;
};
class Q_QUICKSHAPES_EXPORT QQuickShapeTrim : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal start READ start WRITE setStart NOTIFY startChanged FINAL)
Q_PROPERTY(qreal end READ end WRITE setEnd NOTIFY endChanged FINAL)
Q_PROPERTY(qreal offset READ offset WRITE setOffset NOTIFY offsetChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(6, 10)
public:
QQuickShapeTrim(QObject *parent = nullptr);
qreal start() const;
void setStart(qreal t);
qreal end() const;
void setEnd(qreal t);
qreal offset() const;
void setOffset(qreal t);
Q_SIGNALS:
void startChanged();
void endChanged();
void offsetChanged();
private:
qreal m_start = 0;
qreal m_end = 1;
qreal m_offset = 0;
};
class Q_QUICKSHAPES_EXPORT QQuickShapePath : public QQuickPath
{
Q_OBJECT
Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged)
Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged)
Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged)
Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged)
Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged)
Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged)
Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged)
Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged)
Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged)
Q_PROPERTY(QList<qreal> dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged)
Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient NOTIFY fillGradientChanged RESET resetFillGradient)
Q_PROPERTY(QSizeF scale READ scale WRITE setScale NOTIFY scaleChanged OVERRIDE REVISION(1, 14))
Q_PROPERTY(PathHints pathHints READ pathHints WRITE setPathHints NOTIFY pathHintsChanged REVISION(6, 7) FINAL)
Q_PROPERTY(QMatrix4x4 fillTransform READ fillTransform WRITE setFillTransform NOTIFY fillTransformChanged REVISION(6, 8) FINAL)
Q_PROPERTY(QQuickItem *fillItem READ fillItem WRITE setFillItem NOTIFY fillItemChanged REVISION(6, 8) FINAL)
Q_PROPERTY(QQuickShapeTrim *trim READ trim CONSTANT REVISION(6, 10) FINAL)
Q_PROPERTY(bool cosmeticStroke READ cosmeticStroke WRITE setCosmeticStroke NOTIFY cosmeticStrokeChanged REVISION(6, 11) FINAL)
QML_NAMED_ELEMENT(ShapePath)
QML_ADDED_IN_VERSION(1, 0)
public:
enum FillRule {
OddEvenFill = Qt::OddEvenFill,
WindingFill = Qt::WindingFill
};
Q_ENUM(FillRule)
enum JoinStyle {
MiterJoin = Qt::MiterJoin,
BevelJoin = Qt::BevelJoin,
RoundJoin = Qt::RoundJoin
};
Q_ENUM(JoinStyle)
enum CapStyle {
FlatCap = Qt::FlatCap,
SquareCap = Qt::SquareCap,
RoundCap = Qt::RoundCap
};
Q_ENUM(CapStyle)
enum StrokeStyle {
SolidLine = Qt::SolidLine,
DashLine = Qt::DashLine
};
Q_ENUM(StrokeStyle)
enum PathHint {
PathLinear = 0x1,
PathQuadratic = 0x2,
PathConvex = 0x4,
PathFillOnRight = 0x8,
PathSolid = 0x10,
PathNonIntersecting = 0x20,
PathNonOverlappingControlPointTriangles = 0x40
};
Q_DECLARE_FLAGS(PathHints, PathHint)
Q_FLAG(PathHints)
QQuickShapePath(QObject *parent = nullptr);
~QQuickShapePath();
QColor strokeColor() const;
void setStrokeColor(const QColor &color);
qreal strokeWidth() const;
void setStrokeWidth(qreal w);
QColor fillColor() const;
void setFillColor(const QColor &color);
FillRule fillRule() const;
void setFillRule(FillRule fillRule);
JoinStyle joinStyle() const;
void setJoinStyle(JoinStyle style);
int miterLimit() const;
void setMiterLimit(int limit);
CapStyle capStyle() const;
void setCapStyle(CapStyle style);
StrokeStyle strokeStyle() const;
void setStrokeStyle(StrokeStyle style);
qreal dashOffset() const;
void setDashOffset(qreal offset);
QList<qreal> dashPattern() const;
void setDashPattern(const QList<qreal> &array);
QQuickShapeGradient *fillGradient() const;
void setFillGradient(QQuickShapeGradient *gradient);
void resetFillGradient();
PathHints pathHints() const;
void setPathHints(PathHints newPathHints);
QMatrix4x4 fillTransform() const;
void setFillTransform(const QMatrix4x4 &matrix);
QQuickItem *fillItem() const;
void setFillItem(QQuickItem *newFillItem);
QQuickShapeTrim *trim();
bool hasTrim() const;
bool cosmeticStroke() const;
void setCosmeticStroke(bool c);
Q_SIGNALS:
void shapePathChanged();
void strokeColorChanged();
void strokeWidthChanged();
void fillColorChanged();
void fillRuleChanged();
void joinStyleChanged();
void miterLimitChanged();
void capStyleChanged();
void strokeStyleChanged();
void dashOffsetChanged();
void dashPatternChanged();
Q_REVISION(6, 11) void fillGradientChanged();
Q_REVISION(6, 7) void pathHintsChanged();
Q_REVISION(6, 8) void fillTransformChanged();
Q_REVISION(6, 8) void fillItemChanged();
Q_REVISION(6, 11) void cosmeticStrokeChanged();
private:
Q_DISABLE_COPY(QQuickShapePath)
Q_DECLARE_PRIVATE(QQuickShapePath)
Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged())
Q_PRIVATE_SLOT(d_func(), void _q_fillItemDestroyed())
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickShapePath::PathHints)
class Q_QUICKSHAPES_EXPORT QQuickShape : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(RendererType rendererType READ rendererType NOTIFY rendererChanged)
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
Q_PROPERTY(bool vendorExtensionsEnabled READ vendorExtensionsEnabled WRITE setVendorExtensionsEnabled NOTIFY vendorExtensionsEnabledChanged)
Q_PROPERTY(RendererType preferredRendererType READ preferredRendererType
WRITE setPreferredRendererType NOTIFY preferredRendererTypeChanged REVISION(6, 6) FINAL)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(ContainsMode containsMode READ containsMode WRITE setContainsMode NOTIFY containsModeChanged REVISION(1, 11))
Q_PROPERTY(QRectF boundingRect READ boundingRect NOTIFY boundingRectChanged REVISION(6, 6) FINAL)
Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged REVISION(6, 7) FINAL)
Q_PROPERTY(HAlignment horizontalAlignment READ horizontalAlignment WRITE setHorizontalAlignment NOTIFY horizontalAlignmentChanged REVISION(6, 7) FINAL)
Q_PROPERTY(VAlignment verticalAlignment READ verticalAlignment WRITE setVerticalAlignment NOTIFY verticalAlignmentChanged REVISION(6, 7) FINAL)
Q_PROPERTY(QQmlListProperty<QObject> data READ data OVERRIDE)
Q_CLASSINFO("DefaultProperty", "data")
QML_NAMED_ELEMENT(Shape)
QML_ADDED_IN_VERSION(1, 0)
public:
enum RendererType {
UnknownRenderer,
GeometryRenderer,
NvprRenderer,
SoftwareRenderer,
CurveRenderer
};
Q_ENUM(RendererType)
enum Status {
Null,
Ready,
Processing
};
Q_ENUM(Status)
enum ContainsMode {
BoundingRectContains,
FillContains
};
Q_ENUM(ContainsMode)
enum FillMode {
NoResize,
PreserveAspectFit,
PreserveAspectCrop,
Stretch
};
Q_ENUM(FillMode)
enum HAlignment { AlignLeft = Qt::AlignLeft,
AlignRight = Qt::AlignRight,
AlignHCenter = Qt::AlignHCenter };
Q_ENUM(HAlignment)
enum VAlignment { AlignTop = Qt::AlignTop,
AlignBottom = Qt::AlignBottom,
AlignVCenter = Qt::AlignVCenter };
Q_ENUM(VAlignment)
QQuickShape(QQuickItem *parent = nullptr);
~QQuickShape();
RendererType rendererType() const;
bool asynchronous() const;
void setAsynchronous(bool async);
Q_REVISION(6, 6) RendererType preferredRendererType() const;
Q_REVISION(6, 6) void setPreferredRendererType(RendererType preferredType);
Q_REVISION(6, 6) QRectF boundingRect() const override;
bool vendorExtensionsEnabled() const;
void setVendorExtensionsEnabled(bool enable);
Status status() const;
ContainsMode containsMode() const;
void setContainsMode(ContainsMode containsMode);
bool contains(const QPointF &point) const override;
QQmlListProperty<QObject> data();
Q_REVISION(6, 7) FillMode fillMode() const;
Q_REVISION(6, 7) void setFillMode(FillMode newFillMode);
Q_REVISION(6, 7) HAlignment horizontalAlignment() const;
Q_REVISION(6, 7) void setHorizontalAlignment(HAlignment newHorizontalAlignment);
Q_REVISION(6, 7) VAlignment verticalAlignment() const;
Q_REVISION(6, 7) void setVerticalAlignment(VAlignment newVerticalAlignment);
protected:
QQuickShape(QQuickShapePrivate &dd, QQuickItem *parent);
QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override;
void updatePolish() override;
void itemChange(ItemChange change, const ItemChangeData &data) override;
void componentComplete() override;
void classBegin() override;
Q_SIGNALS:
void rendererChanged();
void asynchronousChanged();
void vendorExtensionsEnabledChanged();
void statusChanged();
Q_REVISION(6, 6) void preferredRendererTypeChanged();
Q_REVISION(6, 6) void boundingRectChanged();
Q_REVISION(1, 11) void containsModeChanged();
Q_REVISION(6, 7) void fillModeChanged();
Q_REVISION(6, 7) void horizontalAlignmentChanged();
Q_REVISION(6, 7) void verticalAlignmentChanged();
private:
Q_DISABLE_COPY(QQuickShape)
Q_DECLARE_PRIVATE(QQuickShape)
Q_PRIVATE_SLOT(d_func(), void _q_shapePathChanged())
};
QT_END_NAMESPACE
#endif // QQUICKSHAPE_P_H
@@ -0,0 +1,196 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSHAPE_P_P_H
#define QQUICKSHAPE_P_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QtQuickShapes/private/qquickshape_p.h>
#include <private/qquickitem_p.h>
#include <private/qsgtransform_p.h>
#include <QPainterPath>
#include <QColor>
#include <QBrush>
#include <QElapsedTimer>
#if QT_CONFIG(opengl)
# include <private/qopenglcontext_p.h>
#endif
QT_BEGIN_NAMESPACE
class QSGPlainTexture;
class QRhi;
class QQuickAbstractPathRenderer
{
public:
enum Flag {
SupportsAsync = 0x01
};
Q_DECLARE_FLAGS(Flags, Flag)
enum FillGradientType { NoGradient = 0, LinearGradient, RadialGradient, ConicalGradient };
virtual ~QQuickAbstractPathRenderer() { }
// Gui thread
virtual void beginSync(int totalCount, bool *countChanged) = 0;
virtual void endSync(bool async) = 0;
virtual void setAsyncCallback(void (*)(void *), void *) { }
virtual Flags flags() const { return {}; }
virtual void setPath(int index, const QQuickPath *path);
virtual void setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints pathHints = {}) = 0;
virtual void setStrokeColor(int index, const QColor &color) = 0;
virtual void setStrokeWidth(int index, qreal w) = 0;
virtual void setCosmeticStroke(int index, bool c) = 0;
virtual void setFillColor(int index, const QColor &color) = 0;
virtual void setFillRule(int index, QQuickShapePath::FillRule fillRule) = 0;
virtual void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) = 0;
virtual void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) = 0;
virtual void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
qreal dashOffset, const QList<qreal> &dashPattern) = 0;
virtual void setFillGradient(int index, QQuickShapeGradient *gradient) = 0;
virtual void setFillTextureProvider(int index, QQuickItem *textureProviderItem) = 0;
virtual void setFillTransform(int index, const QSGTransform &transform) = 0;
virtual void setTriangulationScale(int, qreal) { }
virtual void handleSceneChange(QQuickWindow *window) = 0;
// Render thread, with gui blocked
virtual void updateNode() = 0;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags)
inline void QQuickAbstractPathRenderer::setPath(int index, const QQuickPath *path)
{
QQuickShapePath::PathHints hints;
QPainterPath newPath = path ? path->path() : QPainterPath();
if (const auto *shapePath = qobject_cast<const QQuickShapePath *>(path)) {
hints = shapePath->pathHints();
if (shapePath->hasTrim()) {
const QQuickShapeTrim *trim = const_cast<QQuickShapePath *>(shapePath)->trim();
newPath = path->path().trimmed(trim->start(), trim->end(), trim->offset());
}
}
setPath(index, newPath, hints);
}
struct QQuickShapeStrokeFillParams
{
QQuickShapeStrokeFillParams();
QColor strokeColor;
qreal strokeWidth;
QColor fillColor;
QQuickShapePath::FillRule fillRule;
QQuickShapePath::JoinStyle joinStyle;
int miterLimit;
QQuickShapePath::CapStyle capStyle;
QQuickShapePath::StrokeStyle strokeStyle;
bool cosmeticStroke = false;
qreal dashOffset;
QList<qreal> dashPattern;
QQuickShapeGradient *fillGradient;
QSGTransform fillTransform;
QQuickItem *fillItem;
QQuickShapeTrim *trim;
};
class Q_QUICKSHAPES_EXPORT QQuickShapePathPrivate : public QQuickPathPrivate
{
Q_DECLARE_PUBLIC(QQuickShapePath)
public:
enum Dirty {
DirtyPath = 0x01,
DirtyStrokeColor = 0x02,
DirtyStrokeWidth = 0x04,
DirtyFillColor = 0x08,
DirtyFillRule = 0x10,
DirtyStyle = 0x20,
DirtyDash = 0x40,
DirtyFillGradient = 0x80,
DirtyFillTransform = 0x100,
DirtyFillItem = 0x200,
DirtyTrim = 0x400,
DirtyAll = 0x7FF
};
QQuickShapePathPrivate();
void _q_pathChanged();
void _q_fillGradientChanged();
void _q_fillItemDestroyed();
void handleSceneChange();
void writeToDebugStream(QDebug &debug) const override;
static QQuickShapePathPrivate *get(QQuickShapePath *p) { return p->d_func(); }
int dirty;
QQuickShapeStrokeFillParams sfp;
QQuickShapePath::PathHints pathHints;
};
class Q_QUICKSHAPES_EXPORT QQuickShapePrivate : public QQuickItemPrivate
{
Q_DECLARE_PUBLIC(QQuickShape)
public:
QQuickShapePrivate();
~QQuickShapePrivate();
void init();
QQuickShape::RendererType selectRendererType();
void createRenderer();
QSGNode *createNode();
void sync();
void _q_shapePathChanged();
void setStatus(QQuickShape::Status newStatus);
void handleSceneChange(QQuickWindow *w);
static QQuickShapePrivate *get(QQuickShape *item) { return item->d_func(); }
static void asyncShapeReady(void *data);
qreal getImplicitWidth() const override;
qreal getImplicitHeight() const override;
int effectRefCount;
QList<QQuickShapePath *> sp;
QElapsedTimer syncTimer;
QQuickAbstractPathRenderer *renderer = nullptr;
int syncTimingTotalDirty = 0;
int syncTimeCounter = 0;
QQuickShape::Status status = QQuickShape::Null;
QQuickShape::RendererType rendererType = QQuickShape::UnknownRenderer;
QQuickShape::RendererType preferredType = QQuickShape::UnknownRenderer;
QQuickShape::ContainsMode containsMode = QQuickShape::BoundingRectContains;
QQuickShape::FillMode fillMode = QQuickShape::NoResize;
QQuickShape::HAlignment horizontalAlignment = QQuickShape::AlignLeft;
QQuickShape::VAlignment verticalAlignment = QQuickShape::AlignTop;
bool spChanged = false;
bool rendererChanged = false;
bool async = false;
bool enableVendorExts = false;
bool syncTimingActive = false;
qreal triangulationScale = 1.0;
};
QT_END_NAMESPACE
#endif
@@ -0,0 +1,936 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickshapecurverenderer_p.h"
#include "qquickshapecurverenderer_p_p.h"
#if QT_CONFIG(thread)
#include <QtCore/qthreadpool.h>
#endif
#include <QtGui/qvector2d.h>
#include <QtGui/qvector4d.h>
#include <QtGui/private/qtriangulator_p.h>
#include <QtGui/private/qtriangulatingstroker_p.h>
#include <QtGui/private/qrhi_p.h>
#include <QtQuick/private/qsgcurvefillnode_p.h>
#include <QtQuick/private/qsgcurvestrokenode_p.h>
#include <QtQuick/private/qquadpath_p.h>
#include <QtQuick/private/qsgcurveprocessor_p.h>
#include <QtQuick/qsgmaterial.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcShapeCurveRenderer, "qt.shape.curverenderer");
namespace {
/*! \internal
Choice of vertex shader to use for the wireframe node:
* \c SimpleWFT is for when vertices are already in logical coordinates
* \c StrokeWFT chooses the stroke shader, which moves vertices according to the stroke width uniform
*/
enum WireFrameType { SimpleWFT, StrokeWFT };
class QQuickShapeWireFrameMaterialShader : public QSGMaterialShader
{
public:
QQuickShapeWireFrameMaterialShader(WireFrameType wft, int viewCount) : m_wftype(wft)
{
setShaderFileName(VertexStage, wft == StrokeWFT ?
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shapestroke_wireframe.vert.qsb") :
QStringLiteral(":/qt-project.org/shapes/shaders_ng/wireframe.vert.qsb"), viewCount);
setShaderFileName(FragmentStage,
wft == StrokeWFT ?
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shapestroke_wireframe.frag.qsb") :
QStringLiteral(":/qt-project.org/shapes/shaders_ng/wireframe.frag.qsb"), viewCount);
}
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *) override;
WireFrameType m_wftype;
};
class QQuickShapeWireFrameMaterial : public QSGMaterial
{
public:
QQuickShapeWireFrameMaterial(WireFrameType wft) : m_wftype(wft)
{
setFlag(Blending, true);
}
int compare(const QSGMaterial *other) const override
{
return (type() - other->type());
}
void setCosmeticStroke(bool c)
{
m_cosmeticStroke = c;
}
void setStrokeWidth(float width)
{
m_strokeWidth = width;
}
float strokeWidth()
{
return (m_cosmeticStroke ? -1.0 : 1.0) * qAbs(m_strokeWidth);
}
protected:
QSGMaterialType *type() const override
{
static QSGMaterialType t;
return &t;
}
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode) const override
{
return new QQuickShapeWireFrameMaterialShader(m_wftype, viewCount());
}
WireFrameType m_wftype;
bool m_cosmeticStroke = false;
float m_strokeWidth = 1.0f;
};
bool QQuickShapeWireFrameMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *)
{
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 64);
const int matrixCount = qMin(state.projectionMatrixCount(), newMaterial->viewCount());
bool changed = false;
float localScale = /* newNode != nullptr ? newNode->localScale() : */ 1.0f;
for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
if (state.isMatrixDirty()) {
QMatrix4x4 m = state.combinedMatrix(viewIndex);
if (m_wftype == StrokeWFT)
m.scale(localScale);
memcpy(buf->data() + 64 * viewIndex, m.constData(), 64);
changed = true;
}
}
// determinant is xscale * yscale, as long as Item.transform does not include shearing or rotation
const float matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio() * localScale;
memcpy(buf->data() + matrixCount * 64, &matrixScale, 4);
const float dpr = state.devicePixelRatio();
memcpy(buf->data() + matrixCount * 64 + 8, &dpr, 4);
const float opacity = 1.0; // don't fade the wireframe
memcpy(buf->data() + matrixCount * 64 + 4, &opacity, 4);
const float strokeWidth = static_cast<QQuickShapeWireFrameMaterial *>(newMaterial)->strokeWidth();
memcpy(buf->data() + matrixCount * 64 + 12, &strokeWidth, 4);
changed = true;
// shapestroke_wireframe.vert doesn't use the strokeColor and debug uniforms, so we don't bother setting them
return changed;
}
template <WireFrameType wftype>
class QQuickShapeWireFrameNode : public QSGCurveAbstractNode
{
public:
struct WireFrameVertex
{
float x, y, u, v, w, nx, ny, sw;
};
QQuickShapeWireFrameNode()
{
isDebugNode = true;
setFlag(OwnsGeometry, true);
setGeometry(new QSGGeometry(attributes(), 0, 0));
activateMaterial();
}
void setColor(QColor col) override
{
Q_UNUSED(col);
}
void setUseStandardDerivatives(bool useStandardDerivatives) override
{
Q_UNUSED(useStandardDerivatives);
}
void setCosmeticStroke(bool c)
{
m_material->setCosmeticStroke(c);
}
void setStrokeWidth(float width)
{
m_material->setStrokeWidth(width);
}
void activateMaterial()
{
m_material.reset(new QQuickShapeWireFrameMaterial(wftype));
setMaterial(m_material.data());
}
static const QSGGeometry::AttributeSet &attributes()
{
static QSGGeometry::Attribute data[] = {
QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute),
QSGGeometry::Attribute::createWithAttributeType(1, 3, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute),
QSGGeometry::Attribute::createWithAttributeType(2, 3, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute),
};
static QSGGeometry::AttributeSet attrs = { 3, sizeof(WireFrameVertex), data };
return attrs;
}
void cookGeometry() override
{
// Intentionally empty
}
protected:
QScopedPointer<QQuickShapeWireFrameMaterial> m_material;
};
}
QQuickShapeCurveRenderer::~QQuickShapeCurveRenderer()
{
for (const PathData &pd : std::as_const(m_paths)) {
if (pd.currentRunner) {
pd.currentRunner->orphaned = true;
if (!pd.currentRunner->isAsync || pd.currentRunner->isDone)
delete pd.currentRunner;
}
}
}
void QQuickShapeCurveRenderer::beginSync(int totalCount, bool *countChanged)
{
if (countChanged != nullptr && totalCount != m_paths.size())
*countChanged = true;
for (int i = totalCount; i < m_paths.size(); i++) { // Handle removal of paths
setFillTextureProvider(i, nullptr); // deref window
m_removedPaths.append(m_paths.at(i));
}
m_paths.resize(totalCount);
}
void QQuickShapeCurveRenderer::setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints pathHints)
{
auto &pathData = m_paths[index];
pathData.originalPath = path;
pathData.pathHints = pathHints;
pathData.m_dirty |= PathDirty;
}
void QQuickShapeCurveRenderer::setStrokeColor(int index, const QColor &color)
{
auto &pathData = m_paths[index];
const bool wasVisible = pathData.isStrokeVisible();
pathData.pen.setColor(color);
if (pathData.isStrokeVisible() != wasVisible)
pathData.m_dirty |= StrokeDirty;
else
pathData.m_dirty |= UniformsDirty;
}
void QQuickShapeCurveRenderer::setStrokeWidth(int index, qreal w)
{
auto &pathData = m_paths[index];
if (w > 0) {
pathData.validPenWidth = true;
pathData.pen.setWidthF(w);
} else {
pathData.validPenWidth = false;
}
pathData.m_dirty |= StrokeDirty;
}
void QQuickShapeCurveRenderer::setCosmeticStroke(int index, bool c)
{
auto &pathData = m_paths[index];
pathData.pen.setCosmetic(c);
pathData.m_dirty |= StrokeDirty;
}
void QQuickShapeCurveRenderer::setFillColor(int index, const QColor &color)
{
auto &pathData = m_paths[index];
const bool wasVisible = pathData.isFillVisible();
pathData.fillColor = color;
if (pathData.isFillVisible() != wasVisible)
pathData.m_dirty |= FillDirty;
else
pathData.m_dirty |= UniformsDirty;
}
void QQuickShapeCurveRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule)
{
auto &pathData = m_paths[index];
pathData.fillRule = Qt::FillRule(fillRule);
pathData.m_dirty |= PathDirty;
}
void QQuickShapeCurveRenderer::setJoinStyle(int index,
QQuickShapePath::JoinStyle joinStyle,
int miterLimit)
{
auto &pathData = m_paths[index];
pathData.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
pathData.pen.setMiterLimit(miterLimit);
pathData.m_dirty |= StrokeDirty;
}
void QQuickShapeCurveRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle)
{
auto &pathData = m_paths[index];
pathData.pen.setCapStyle(Qt::PenCapStyle(capStyle));
pathData.m_dirty |= StrokeDirty;
}
void QQuickShapeCurveRenderer::setStrokeStyle(int index,
QQuickShapePath::StrokeStyle strokeStyle,
qreal dashOffset,
const QList<qreal> &dashPattern)
{
auto &pathData = m_paths[index];
pathData.pen.setStyle(Qt::PenStyle(strokeStyle));
if (strokeStyle == QQuickShapePath::DashLine) {
pathData.pen.setDashPattern(dashPattern);
pathData.pen.setDashOffset(dashOffset);
}
pathData.m_dirty |= StrokeDirty;
}
void QQuickShapeCurveRenderer::setFillGradient(int index, QQuickShapeGradient *gradient)
{
PathData &pd(m_paths[index]);
const bool wasVisible = pd.isFillVisible();
pd.gradientType = QGradient::NoGradient;
if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) {
pd.gradientType = QGradient::LinearGradient;
pd.gradient.stops = gradient->gradientStops();
pd.gradient.spread = QGradient::Spread(gradient->spread());
pd.gradient.a = QPointF(g->x1(), g->y1());
pd.gradient.b = QPointF(g->x2(), g->y2());
} else if (QQuickShapeRadialGradient *g = qobject_cast<QQuickShapeRadialGradient *>(gradient)) {
pd.gradientType = QGradient::RadialGradient;
pd.gradient.a = QPointF(g->centerX(), g->centerY());
pd.gradient.b = QPointF(g->focalX(), g->focalY());
pd.gradient.v0 = g->centerRadius();
pd.gradient.v1 = g->focalRadius();
} else if (QQuickShapeConicalGradient *g = qobject_cast<QQuickShapeConicalGradient *>(gradient)) {
pd.gradientType = QGradient::ConicalGradient;
pd.gradient.a = QPointF(g->centerX(), g->centerY());
pd.gradient.v0 = g->angle();
} else if (gradient != nullptr) {
static bool warned = false;
if (!warned) {
warned = true;
qCWarning(lcShapeCurveRenderer) << "Unsupported gradient fill";
}
}
if (pd.gradientType != QGradient::NoGradient) {
pd.gradient.stops = gradient->gradientStops();
pd.gradient.spread = QGradient::Spread(gradient->spread());
}
pd.m_dirty |= (pd.isFillVisible() != wasVisible) ? FillDirty : UniformsDirty;
}
void QQuickShapeCurveRenderer::setFillTransform(int index, const QSGTransform &transform)
{
auto &pathData = m_paths[index];
pathData.fillTransform = transform;
pathData.m_dirty |= UniformsDirty;
}
void QQuickShapeCurveRenderer::setFillTextureProvider(int index, QQuickItem *textureProviderItem)
{
auto &pathData = m_paths[index];
const bool wasVisible = pathData.isFillVisible();
if (pathData.fillTextureProviderItem != nullptr)
QQuickItemPrivate::get(pathData.fillTextureProviderItem)->derefWindow();
pathData.fillTextureProviderItem = textureProviderItem;
if (pathData.fillTextureProviderItem != nullptr)
QQuickItemPrivate::get(pathData.fillTextureProviderItem)->refWindow(m_item->window());
pathData.m_dirty |= (pathData.isFillVisible() != wasVisible) ? FillDirty : UniformsDirty;
}
void QQuickShapeCurveRenderer::handleSceneChange(QQuickWindow *window)
{
for (auto &pathData : m_paths) {
if (pathData.fillTextureProviderItem != nullptr) {
if (window == nullptr)
QQuickItemPrivate::get(pathData.fillTextureProviderItem)->derefWindow();
else
QQuickItemPrivate::get(pathData.fillTextureProviderItem)->refWindow(window);
}
}
}
void QQuickShapeCurveRenderer::setAsyncCallback(void (*callback)(void *), void *data)
{
m_asyncCallback = callback;
m_asyncCallbackData = data;
}
void QQuickShapeCurveRenderer::endSync(bool async)
{
bool asyncThreadsRunning = false;
for (PathData &pathData : m_paths) {
if (!pathData.m_dirty)
continue;
if (pathData.m_dirty == UniformsDirty) {
// Requires no curve node computation, gets handled directly in updateNode()
continue;
}
if (pathData.currentRunner) {
// We are in a new sync round before updateNode() has been called to commit the results
// of the previous sync and processing round
if (pathData.currentRunner->isAsync) {
// Already performing async processing. A new run of the runner will be started in
// updateNode() to take care of the new dirty flags
asyncThreadsRunning = true;
continue;
} else {
// Throw away outdated results and start a new processing
delete pathData.currentRunner;
pathData.currentRunner = nullptr;
}
}
pathData.currentRunner = new QQuickShapeCurveRunnable;
setUpRunner(&pathData);
#if QT_CONFIG(thread)
if (async) {
pathData.currentRunner->isAsync = true;
QThreadPool::globalInstance()->start(pathData.currentRunner);
asyncThreadsRunning = true;
} else
#endif
{
pathData.currentRunner->run();
}
}
if (async && !asyncThreadsRunning && m_asyncCallback)
m_asyncCallback(m_asyncCallbackData);
}
void QQuickShapeCurveRenderer::setUpRunner(PathData *pathData)
{
Q_ASSERT(pathData->currentRunner);
QQuickShapeCurveRunnable *runner = pathData->currentRunner;
runner->isDone = false;
runner->pathData = *pathData;
runner->pathData.fillNodes.clear();
runner->pathData.strokeNodes.clear();
runner->pathData.currentRunner = nullptr;
pathData->m_dirty = 0;
if (!runner->isInitialized) {
runner->isInitialized = true;
runner->setAutoDelete(false);
QObject::connect(runner, &QQuickShapeCurveRunnable::done, qApp,
[this](QQuickShapeCurveRunnable *r) {
r->isDone = true;
if (r->orphaned) {
delete r; // Renderer was destroyed
} else if (r->isAsync) {
maybeUpdateAsyncItem();
}
});
}
}
void QQuickShapeCurveRenderer::maybeUpdateAsyncItem()
{
for (const PathData &pd : std::as_const(m_paths)) {
if (pd.currentRunner && !pd.currentRunner->isDone)
return;
}
if (m_item)
m_item->update();
if (m_asyncCallback)
m_asyncCallback(m_asyncCallbackData);
}
QQuickShapeCurveRunnable::~QQuickShapeCurveRunnable()
{
qDeleteAll(pathData.fillNodes);
qDeleteAll(pathData.strokeNodes);
}
void QQuickShapeCurveRunnable::run()
{
QQuickShapeCurveRenderer::processPath(&pathData);
emit done(this);
}
static bool disableScreenSpaceDerivativeShader()
{
static bool d = qEnvironmentVariableIntValue("QT_QUICKSHAPES_DISABLE_STANDARD_DERIVATIVES") != 0;
return d;
}
void QQuickShapeCurveRenderer::updateNode()
{
if (!m_rootNode)
return;
auto updateUniforms = [](const PathData &pathData) {
for (auto &pathNode : std::as_const(pathData.fillNodes)) {
if (pathNode->isDebugNode)
continue;
QSGCurveFillNode *fillNode = static_cast<QSGCurveFillNode *>(pathNode);
fillNode->setColor(pathData.fillColor);
fillNode->setGradientType(pathData.gradientType);
fillNode->setFillGradient(pathData.gradient);
fillNode->setFillTransform(pathData.fillTransform);
fillNode->setFillTextureProvider(pathData.fillTextureProviderItem != nullptr
? pathData.fillTextureProviderItem->textureProvider()
: nullptr);
}
for (QSGCurveAbstractNode *pathNode : std::as_const(pathData.strokeNodes)) {
pathNode->setColor(pathData.pen.color());
if (pathNode->isDebugNode) {
auto *wfNode = static_cast<QQuickShapeWireFrameNode<StrokeWFT> *>(pathNode);
wfNode->setStrokeWidth(pathData.pen.widthF());
wfNode->setCosmeticStroke(pathData.pen.isCosmetic());
} else {
auto *strokeNode = static_cast<QSGCurveStrokeNode *>(pathNode);
strokeNode->setStrokeWidth(pathData.pen.widthF());
strokeNode->setCosmeticStroke(pathData.pen.isCosmetic());
}
}
};
NodeList toBeDeleted;
for (const PathData &pathData : std::as_const(m_removedPaths)) {
toBeDeleted += pathData.fillNodes;
toBeDeleted += pathData.strokeNodes;
}
m_removedPaths.clear();
const bool supportsDerivatives = m_item != nullptr
&& m_item->window() != nullptr
&& m_item->window()->rhi() != nullptr
&& !disableScreenSpaceDerivativeShader()
? m_item->window()->rhi()->isFeatureSupported(QRhi::ScreenSpaceDerivatives)
: false;
for (int i = 0; i < m_paths.size(); i++) {
PathData &pathData = m_paths[i];
if (pathData.currentRunner) {
if (!pathData.currentRunner->isDone)
continue;
// Find insertion point for new nodes. Default is the first stroke node of this path
QSGNode *nextNode = pathData.strokeNodes.value(0);
// If that is 0, use the first node (stroke or fill) of later paths, if any
for (int j = i + 1; !nextNode && j < m_paths.size(); j++) {
const PathData &pd = m_paths[j];
nextNode = pd.fillNodes.isEmpty() ? pd.strokeNodes.value(0) : pd.fillNodes.value(0);
}
PathData &newData = pathData.currentRunner->pathData;
if (newData.m_dirty & PathDirty)
pathData.path = newData.path;
if (newData.m_dirty & FillDirty) {
pathData.fillPath = newData.fillPath;
for (auto *node : std::as_const(newData.fillNodes)) {
node->setUseStandardDerivatives(supportsDerivatives);
if (nextNode)
m_rootNode->insertChildNodeBefore(node, nextNode);
else
m_rootNode->appendChildNode(node);
}
toBeDeleted += pathData.fillNodes;
pathData.fillNodes = newData.fillNodes;
}
if (newData.m_dirty & StrokeDirty) {
for (auto *node : std::as_const(newData.strokeNodes)) {
node->setUseStandardDerivatives(supportsDerivatives);
if (nextNode)
m_rootNode->insertChildNodeBefore(node, nextNode);
else
m_rootNode->appendChildNode(node);
}
toBeDeleted += pathData.strokeNodes;
pathData.strokeNodes = newData.strokeNodes;
}
if (newData.m_dirty & UniformsDirty)
updateUniforms(newData);
// Ownership of new nodes have been transferred to root node
newData.fillNodes.clear();
newData.strokeNodes.clear();
#if QT_CONFIG(thread)
if (pathData.currentRunner->isAsync && (pathData.m_dirty & ~UniformsDirty)) {
// New changes have arrived while runner was computing; restart it to handle them
setUpRunner(&pathData);
QThreadPool::globalInstance()->start(pathData.currentRunner);
} else
#endif
{
pathData.currentRunner->deleteLater();
pathData.currentRunner = nullptr;
}
}
if (pathData.m_dirty == UniformsDirty && !pathData.currentRunner) {
// Simple case so no runner was created in endSync(); handle it directly here
updateUniforms(pathData);
pathData.m_dirty = 0;
}
}
qDeleteAll(toBeDeleted); // also removes them from m_rootNode's child list
}
void QQuickShapeCurveRenderer::processPath(PathData *pathData)
{
static const bool doOverlapSolving = !qEnvironmentVariableIntValue("QT_QUICKSHAPES_DISABLE_OVERLAP_SOLVER");
static const bool doIntersetionSolving = !qEnvironmentVariableIntValue("QT_QUICKSHAPES_DISABLE_INTERSECTION_SOLVER");
static const bool useTriangulatingStroker = qEnvironmentVariableIntValue("QT_QUICKSHAPES_TRIANGULATING_STROKER");
static const bool simplifyPath = qEnvironmentVariableIntValue("QT_QUICKSHAPES_SIMPLIFY_PATHS");
int &dirtyFlags = pathData->m_dirty;
if (dirtyFlags & PathDirty) {
if (simplifyPath)
pathData->path = QQuadPath::fromPainterPath(pathData->originalPath.simplified(), QQuadPath::PathLinear | QQuadPath::PathNonIntersecting | QQuadPath::PathNonOverlappingControlPointTriangles);
else
pathData->path = QQuadPath::fromPainterPath(pathData->originalPath, QQuadPath::PathHints(int(pathData->pathHints)));
pathData->path.setFillRule(pathData->fillRule);
pathData->fillPath = {};
dirtyFlags |= (FillDirty | StrokeDirty);
}
if (dirtyFlags & FillDirty) {
if (pathData->isFillVisible()) {
if (pathData->fillPath.isEmpty()) {
pathData->fillPath = pathData->path.subPathsClosed();
if (doIntersetionSolving)
QSGCurveProcessor::solveIntersections(pathData->fillPath);
pathData->fillPath.addCurvatureData();
if (doOverlapSolving)
QSGCurveProcessor::solveOverlaps(pathData->fillPath);
}
pathData->fillNodes = addFillNodes(pathData->fillPath);
dirtyFlags |= (StrokeDirty | UniformsDirty);
}
}
if (dirtyFlags & StrokeDirty) {
if (pathData->isStrokeVisible()) {
const QPen &pen = pathData->pen;
const bool solid = (pen.style() == Qt::SolidLine);
const QQuadPath &strokePath = solid ? pathData->path
: pathData->path.dashed(pen.widthF(),
pen.dashPattern(),
pen.dashOffset());
if (useTriangulatingStroker)
pathData->strokeNodes = addTriangulatingStrokerNodes(strokePath, pen);
else
pathData->strokeNodes = addCurveStrokeNodes(strokePath, pen);
}
}
}
QQuickShapeCurveRenderer::NodeList QQuickShapeCurveRenderer::addFillNodes(const QQuadPath &path)
{
NodeList ret;
std::unique_ptr<QSGCurveFillNode> node(new QSGCurveFillNode);
std::unique_ptr<QQuickShapeWireFrameNode<SimpleWFT>> wfNode;
const qsizetype approxDataCount = 20 * path.elementCount();
node->reserve(approxDataCount);
const int debugFlags = debugVisualization();
const bool wireFrame = debugFlags & DebugWireframe;
if (Q_LIKELY(!wireFrame)) {
QSGCurveProcessor::processFill(path,
path.fillRule(),
[&node](const std::array<QVector2D, 3> &v,
const std::array<QVector2D, 3> &n,
QSGCurveProcessor::uvForPointCallback uvForPoint)
{
node->appendTriangle(v, n, uvForPoint);
});
} else {
QList<QQuickShapeWireFrameNode<SimpleWFT>::WireFrameVertex> wfVertices;
wfVertices.reserve(approxDataCount);
QSGCurveProcessor::processFill(path,
path.fillRule(),
[&wfVertices, &node](const std::array<QVector2D, 3> &v,
const std::array<QVector2D, 3> &n,
QSGCurveProcessor::uvForPointCallback uvForPoint)
{
node->appendTriangle(v, n, uvForPoint);
wfVertices.append({v.at(0).x(), v.at(0).y(), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }); // 0
wfVertices.append({v.at(1).x(), v.at(1).y(), 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f }); // 1
wfVertices.append({v.at(2).x(), v.at(2).y(), 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f }); // 2
});
wfNode.reset(new QQuickShapeWireFrameNode<SimpleWFT>);
const QList<quint32> indices = node->uncookedIndexes();
QSGGeometry *wfg = new QSGGeometry(QQuickShapeWireFrameNode<SimpleWFT>::attributes(),
wfVertices.size(),
indices.size(),
QSGGeometry::UnsignedIntType);
wfNode->setGeometry(wfg);
wfg->setDrawingMode(QSGGeometry::DrawTriangles);
memcpy(wfg->indexData(),
indices.data(),
indices.size() * wfg->sizeOfIndex());
memcpy(wfg->vertexData(),
wfVertices.data(),
wfg->vertexCount() * wfg->sizeOfVertex());
}
if (Q_UNLIKELY(debugFlags & DebugCurves))
node->setDebug(0.5f);
if (node->uncookedIndexes().size() > 0) {
node->cookGeometry();
ret.append(node.release());
if (wireFrame)
ret.append(wfNode.release());
}
return ret;
}
QQuickShapeCurveRenderer::NodeList QQuickShapeCurveRenderer::addTriangulatingStrokerNodes(const QQuadPath &path, const QPen &pen)
{
NodeList ret;
const QColor &color = pen.color();
QList<QQuickShapeWireFrameNode<StrokeWFT>::WireFrameVertex> wfVertices;
QTriangulatingStroker stroker;
const auto painterPath = path.toPainterPath();
const QVectorPath &vp = qtVectorPathForPath(painterPath);
stroker.process(vp, pen, {}, {});
auto *node = new QSGCurveFillNode;
auto uvForPoint = [](QVector2D v1, QVector2D v2, QVector2D p)
{
double divisor = v1.x() * v2.y() - v2.x() * v1.y();
float u = (p.x() * v2.y() - p.y() * v2.x()) / divisor;
float v = (p.y() * v1.x() - p.x() * v1.y()) / divisor;
return QVector2D(u, v);
};
// Find uv coordinates for the point p, for a quadratic curve from p0 to p2 with control point p1
// also works for a line from p0 to p2, where p1 is on the inside of the path relative to the line
auto curveUv = [uvForPoint](QVector2D p0, QVector2D p1, QVector2D p2, QVector2D p)
{
QVector2D v1 = 2 * (p1 - p0);
QVector2D v2 = p2 - v1 - p0;
return uvForPoint(v1, v2, p - p0);
};
auto findPointOtherSide = [](const QVector2D &startPoint, const QVector2D &endPoint, const QVector2D &referencePoint){
QVector2D baseLine = endPoint - startPoint;
QVector2D insideVector = referencePoint - startPoint;
QVector2D normal = QVector2D(-baseLine.y(), baseLine.x()); // TODO: limit size of triangle
bool swap = QVector2D::dotProduct(insideVector, normal) < 0;
return swap ? startPoint + normal : startPoint - normal;
};
static bool disableExtraTriangles = qEnvironmentVariableIntValue("QT_QUICKSHAPES_WIP_DISABLE_EXTRA_STROKE_TRIANGLES");
auto addStrokeTriangle = [&](const QVector2D &p1, const QVector2D &p2, const QVector2D &p3){
if (p1 == p2 || p2 == p3) {
return;
}
auto uvForPoint = [&p1, &p2, &p3, curveUv](QVector2D p) {
auto uv = curveUv(p1, p2, p3, p);
return QVector3D(uv.x(), uv.y(), 0.0f); // Line
};
node->appendTriangle(p1, p2, p3, uvForPoint);
wfVertices.append({p1.x(), p1.y(), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}); // 0
wfVertices.append({p2.x(), p2.y(), 0.0f, 0.1f, 0.0f, 0.0f, 0.0f, 1.0f}); // 1
wfVertices.append({p3.x(), p3.y(), 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f}); // 2
if (!disableExtraTriangles) {
// Add a triangle on the outer side of the line to get some more AA
// The new point replaces p2 (currentVertex+1)
QVector2D op = findPointOtherSide(p1, p3, p2);
node->appendTriangle(p1, op, p3, uvForPoint);
wfVertices.append({p1.x(), p1.y(), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f});
wfVertices.append({op.x(), op.y(), 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}); // replacing p2
wfVertices.append({p3.x(), p3.y(), 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f});
}
};
const int vertCount = stroker.vertexCount() / 2;
const float *verts = stroker.vertices();
for (int i = 0; i < vertCount - 2; ++i) {
QVector2D p[3];
for (int j = 0; j < 3; ++j) {
p[j] = QVector2D(verts[(i+j)*2], verts[(i+j)*2 + 1]);
}
addStrokeTriangle(p[0], p[1], p[2]);
}
QList<quint32> indices = node->uncookedIndexes();
if (indices.size() > 0) {
node->setColor(color);
node->cookGeometry();
ret.append(node);
}
const bool wireFrame = debugVisualization() & DebugWireframe;
if (wireFrame) {
QQuickShapeWireFrameNode<StrokeWFT> *wfNode = new QQuickShapeWireFrameNode<StrokeWFT>;
QSGGeometry *wfg = new QSGGeometry(QQuickShapeWireFrameNode<StrokeWFT>::attributes(),
wfVertices.size(),
indices.size(),
QSGGeometry::UnsignedIntType);
wfNode->setGeometry(wfg);
wfg->setDrawingMode(QSGGeometry::DrawTriangles);
memcpy(wfg->indexData(),
indices.data(),
indices.size() * wfg->sizeOfIndex());
memcpy(wfg->vertexData(),
wfVertices.data(),
wfg->vertexCount() * wfg->sizeOfVertex());
ret.append(wfNode);
}
return ret;
}
void QQuickShapeCurveRenderer::setRootNode(QSGNode *node)
{
clearNodeReferences();
m_rootNode = node;
}
void QQuickShapeCurveRenderer::clearNodeReferences()
{
for (PathData &pd : m_paths) {
pd.fillNodes.clear();
pd.strokeNodes.clear();
}
}
int QQuickShapeCurveRenderer::debugVisualizationFlags = QQuickShapeCurveRenderer::NoDebug;
int QQuickShapeCurveRenderer::debugVisualization()
{
static const int envFlags = qEnvironmentVariableIntValue("QT_QUICKSHAPES_DEBUG");
return debugVisualizationFlags | envFlags;
}
void QQuickShapeCurveRenderer::setDebugVisualization(int options)
{
if (debugVisualizationFlags == options)
return;
debugVisualizationFlags = options;
}
/*! \internal
Convert \a path to QSGCurveAbstractNodes with vertices ready to send to the GPU.
The given \a path is assumed to be a stroke centerline: it may be continuous or dashed.
Also create the wireframe node if enabled.
*/
QQuickShapeCurveRenderer::NodeList QQuickShapeCurveRenderer::addCurveStrokeNodes(const QQuadPath &path, const QPen &pen)
{
NodeList ret;
const bool debug = debugVisualization() & DebugCurves;
auto *node = new QSGCurveStrokeNode;
node->setDebug(0.2f * debug);
QList<QQuickShapeWireFrameNode<StrokeWFT>::WireFrameVertex> wfVertices;
const float penWidth = pen.widthF();
static const int subdivisions = qEnvironmentVariable("QT_QUICKSHAPES_STROKE_SUBDIVISIONS", QStringLiteral("3")).toInt();
const bool wireFrame = debugVisualization() & DebugWireframe;
QSGCurveProcessor::processStroke(path,
pen.miterLimit(),
penWidth, pen.isCosmetic(),
pen.joinStyle(),
pen.capStyle(),
// addStrokeTriangleCallback (see qsgcurveprocessor_p.h):
[&wfVertices, &node, &wireFrame](const std::array<QVector2D, 3> &vtx, // triangle corners
const std::array<QVector2D, 3> &ctl, // curve control points
const std::array<QVector2D, 3> &n, // normals
const std::array<float, 3> &ex, // extrusions
QSGCurveStrokeNode::TriangleFlags flags)
{
const QVector2D &v0 = vtx.at(0);
const QVector2D &v1 = vtx.at(1);
const QVector2D &v2 = vtx.at(2);
if (flags.testFlag(QSGCurveStrokeNode::TriangleFlag::Line))
node->appendTriangle(vtx, std::array<QVector2D, 2>{ctl.at(0), ctl.at(2)}, n, ex);
else
node->appendTriangle(vtx, ctl, n, ex);
if (Q_UNLIKELY(wireFrame)) {
wfVertices.append({v0.x(), v0.y(), 1.0f, 0.0f, 0.0f, n.at(0).x(), n.at(0).y(), ex.at(0)});
wfVertices.append({v1.x(), v1.y(), 0.0f, 1.0f, 0.0f, n.at(1).x(), n.at(1).y(), ex.at(1)});
wfVertices.append({v2.x(), v2.y(), 0.0f, 0.0f, 1.0f, n.at(2).x(), n.at(2).y(), ex.at(2)});
}
},
subdivisions);
auto indexCopy = node->uncookedIndexes(); // uncookedIndexes get deleted on cooking
node->setColor(pen.color());
node->setStrokeWidth(penWidth);
node->setCosmeticStroke(pen.isCosmetic());
node->cookGeometry();
ret.append(node);
if (Q_UNLIKELY(wireFrame)) {
QQuickShapeWireFrameNode<StrokeWFT> *wfNode = new QQuickShapeWireFrameNode<StrokeWFT>;
QSGGeometry *wfg = new QSGGeometry(QQuickShapeWireFrameNode<StrokeWFT>::attributes(),
wfVertices.size(),
indexCopy.size(),
QSGGeometry::UnsignedIntType);
wfNode->setGeometry(wfg);
wfNode->setCosmeticStroke(pen.isCosmetic());
wfNode->setStrokeWidth(penWidth);
wfg->setDrawingMode(QSGGeometry::DrawTriangles);
memcpy(wfg->indexData(),
indexCopy.data(),
indexCopy.size() * wfg->sizeOfIndex());
memcpy(wfg->vertexData(),
wfVertices.data(),
wfg->vertexCount() * wfg->sizeOfVertex());
ret.append(wfNode);
}
return ret;
}
QT_END_NAMESPACE
@@ -0,0 +1,166 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSHAPECURVERENDERER_P_H
#define QQUICKSHAPECURVERENDERER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of a number of Qt sources files. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
#include <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QtQuickShapes/private/qquickshape_p_p.h>
#include <QtQuick/private/qquadpath_p.h>
#include <QtQuick/private/qsgcurveabstractnode_p.h>
#include <QtQuick/private/qsggradientcache_p.h>
#include <qsgnode.h>
#include <qsggeometry.h>
#include <qsgmaterial.h>
#include <qsgrendererinterface.h>
#include <qsgtexture.h>
#include <QtCore/qrunnable.h>
#include <QRunnable>
#include <QtGui/private/qtriangulator_p.h>
#include <QtQuick/private/qsgcurvefillnode_p.h>
QT_BEGIN_NAMESPACE
class QQuickShapeCurveRunnable;
class Q_QUICKSHAPES_EXPORT QQuickShapeCurveRenderer : public QQuickAbstractPathRenderer
{
public:
QQuickShapeCurveRenderer(QQuickItem *item)
: m_item(item)
{ }
~QQuickShapeCurveRenderer() override;
void beginSync(int totalCount, bool *countChanged) override;
void setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints pathHints = {}) override;
void setStrokeColor(int index, const QColor &color) override;
void setStrokeWidth(int index, qreal w) override;
void setCosmeticStroke(int index, bool c) override;
void setFillColor(int index, const QColor &color) override;
void setFillRule(int index, QQuickShapePath::FillRule fillRule) override;
void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override;
void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override;
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
qreal dashOffset, const QList<qreal> &dashPattern) override;
void setFillGradient(int index, QQuickShapeGradient *gradient) override;
void setFillTextureProvider(int index, QQuickItem *textureProviderItem) override;
void setFillTransform(int index, const QSGTransform &transform) override;
void endSync(bool async) override;
void setAsyncCallback(void (*)(void *), void *) override;
Flags flags() const override { return SupportsAsync; }
void handleSceneChange(QQuickWindow *window) override;
void updateNode() override;
void setRootNode(QSGNode *node);
void clearNodeReferences();
using NodeList = QList<QSGCurveAbstractNode *>;
enum DirtyFlag
{
PathDirty = 0x01,
FillDirty = 0x02,
StrokeDirty = 0x04,
UniformsDirty = 0x08
};
enum DebugVisualizationOption {
NoDebug = 0,
DebugCurves = 0x01,
DebugWireframe = 0x02
};
static int debugVisualization();
static void setDebugVisualization(int options);
private:
struct PathData {
bool isFillVisible() const
{
return gradientType != QGradient::NoGradient
|| fillTextureProviderItem != nullptr
|| fillColor.alpha() > 0;
}
bool isStrokeVisible() const
{
return validPenWidth && pen.color().alpha() > 0 && pen.style() != Qt::NoPen;
}
QGradient::Type gradientType = QGradient::NoGradient;
QSGGradientCache::GradientDesc gradient;
QSGTransform fillTransform;
QColor fillColor;
Qt::FillRule fillRule = Qt::OddEvenFill;
QPen pen;
bool validPenWidth = true;
int m_dirty = 0;
QQuickShapePath::PathHints pathHints;
QPainterPath originalPath;
QQuadPath path;
QQuadPath fillPath;
NodeList fillNodes;
NodeList strokeNodes;
QQuickShapeCurveRunnable *currentRunner = nullptr;
QQuickItem *fillTextureProviderItem = nullptr;
};
void setUpRunner(PathData *pathData);
void maybeUpdateAsyncItem();
static void processPath(PathData *pathData);
static NodeList addFillNodes(const QQuadPath &path);
static NodeList addTriangulatingStrokerNodes(const QQuadPath &path, const QPen &pen);
static NodeList addCurveStrokeNodes(const QQuadPath &path, const QPen &pen);
QQuickItem *m_item;
QSGNode *m_rootNode = nullptr;
QList<PathData> m_paths;
QList<PathData> m_removedPaths;
void (*m_asyncCallback)(void *) = nullptr;
void *m_asyncCallbackData = nullptr;
static int debugVisualizationFlags;
friend class QQuickShapeCurveRunnable;
};
class QQuickShapeCurveRunnable : public QObject, public QRunnable
{
Q_OBJECT
public:
~QQuickShapeCurveRunnable() override;
void run() override;
bool isInitialized = false;
bool isAsync = false;
bool isDone = false;
bool orphaned = false;
// input / output
QQuickShapeCurveRenderer::PathData pathData;
Q_SIGNALS:
void done(QQuickShapeCurveRunnable *self);
};
QT_END_NAMESPACE
#endif // QQUICKSHAPECURVERENDERER_P_H
@@ -0,0 +1,28 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSHAPECURVERENDERER_P_P_H
#define QQUICKSHAPECURVERENDERER_P_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of a number of Qt sources files. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
#include <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QLoggingCategory>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcShapeCurveRenderer);
QT_END_NAMESPACE
#endif //QQUICKSHAPECURVERENDERER_P_P_H
@@ -0,0 +1,397 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSHAPEGENERICRENDERER_P_H
#define QQUICKSHAPEGENERICRENDERER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of a number of Qt sources files. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
#include <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QtQuickShapes/private/qquickshape_p_p.h>
#include <QtQuick/private/qsggradientcache_p.h>
#include <qsgnode.h>
#include <qsggeometry.h>
#include <qsgmaterial.h>
#include <qsgrendererinterface.h>
#include <qsgtexture.h>
#include <QtCore/qrunnable.h>
QT_BEGIN_NAMESPACE
class QQuickShapeGenericNode;
class QQuickShapeGenericStrokeFillNode;
class QQuickShapeFillRunnable;
class QQuickShapeStrokeRunnable;
class QQuickShapeGenericRenderer : public QQuickAbstractPathRenderer
{
public:
enum Dirty {
DirtyFillGeom = 0x01,
DirtyStrokeGeom = 0x02,
DirtyColor = 0x04,
DirtyFillGradient = 0x08,
DirtyFillTransform = 0x10,
DirtyFillTexture = 0x20,
DirtyList = 0x40 // only for accDirty
};
QQuickShapeGenericRenderer(QQuickItem *item)
: m_item(item),
m_api(QSGRendererInterface::Unknown),
m_rootNode(nullptr),
m_accDirty(0),
m_asyncCallback(nullptr),
m_asyncCallbackData(nullptr)
{ }
~QQuickShapeGenericRenderer();
void beginSync(int totalCount, bool *countChanged) override;
void setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints pathHints = {}) override;
void setStrokeColor(int index, const QColor &color) override;
void setStrokeWidth(int index, qreal w) override;
void setCosmeticStroke(int index, bool c) override;
void setFillColor(int index, const QColor &color) override;
void setFillRule(int index, QQuickShapePath::FillRule fillRule) override;
void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override;
void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override;
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
qreal dashOffset, const QList<qreal> &dashPattern) override;
void setFillGradient(int index, QQuickShapeGradient *gradient) override;
void setFillTextureProvider(int index, QQuickItem *textureProviderItem) override;
void setFillTransform(int index, const QSGTransform &transform) override;
void setTriangulationScale(int index, qreal scale) override;
void endSync(bool async) override;
void setAsyncCallback(void (*)(void *), void *) override;
Flags flags() const override { return SupportsAsync; }
void handleSceneChange(QQuickWindow *window) override;
void updateNode() override;
void setRootNode(QQuickShapeGenericNode *node);
struct Color4ub { unsigned char r, g, b, a; };
typedef QList<QSGGeometry::ColoredPoint2D> VertexContainerType;
typedef QList<QSGGeometry::TexturedPoint2D> TexturedVertexContainerType;
typedef QList<quint32> IndexContainerType;
static void triangulateFill(const QPainterPath &path,
const Color4ub &fillColor,
VertexContainerType *fillVertices,
IndexContainerType *fillIndices,
QSGGeometry::Type *indexType,
bool supportsElementIndexUint,
qreal triangulationScale);
static void triangulateStroke(const QPainterPath &path,
const QPen &pen,
const Color4ub &strokeColor,
VertexContainerType *strokeVertices,
const QSize &clipSize,
qreal triangulationScale);
private:
void maybeUpdateAsyncItem();
struct ShapePathData {
float strokeWidth;
float triangulationScale;
QPen pen;
Color4ub strokeColor = { uchar(0), uchar(0), uchar(0), uchar(0) };
Color4ub fillColor = { uchar(0), uchar(0), uchar(0), uchar(0) };
Qt::FillRule fillRule;
QPainterPath path;
FillGradientType fillGradientActive;
QSGGradientCache::GradientDesc fillGradient;
QQuickItem *fillTextureProviderItem = nullptr;
QSGTransform fillTransform;
VertexContainerType fillVertices;
IndexContainerType fillIndices;
QSGGeometry::Type indexType;
VertexContainerType strokeVertices;
int syncDirty;
int effectiveDirty = 0;
QQuickShapeFillRunnable *pendingFill = nullptr;
QQuickShapeStrokeRunnable *pendingStroke = nullptr;
};
void updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n);
void updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node);
void updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node);
QQuickItem *m_item;
QSGRendererInterface::GraphicsApi m_api;
QQuickShapeGenericNode *m_rootNode;
QList<ShapePathData> m_sp;
int m_accDirty;
void (*m_asyncCallback)(void *);
void *m_asyncCallbackData;
};
class QQuickShapeFillRunnable : public QObject, public QRunnable
{
Q_OBJECT
public:
void run() override;
bool orphaned = false;
// input
QPainterPath path;
QQuickShapeGenericRenderer::Color4ub fillColor;
bool supportsElementIndexUint;
qreal triangulationScale;
// output
QQuickShapeGenericRenderer::VertexContainerType fillVertices;
QQuickShapeGenericRenderer::IndexContainerType fillIndices;
QSGGeometry::Type indexType;
Q_SIGNALS:
void done(QQuickShapeFillRunnable *self);
};
class QQuickShapeStrokeRunnable : public QObject, public QRunnable
{
Q_OBJECT
public:
void run() override;
bool orphaned = false;
// input
QPainterPath path;
QPen pen;
QQuickShapeGenericRenderer::Color4ub strokeColor;
QSize clipSize;
qreal triangulationScale;
// output
QQuickShapeGenericRenderer::VertexContainerType strokeVertices;
Q_SIGNALS:
void done(QQuickShapeStrokeRunnable *self);
};
class QQuickShapeGenericStrokeFillNode : public QObject, public QSGGeometryNode
{
Q_OBJECT
public:
QQuickShapeGenericStrokeFillNode(QQuickWindow *window);
enum Material {
MatSolidColor,
MatLinearGradient,
MatRadialGradient,
MatConicalGradient,
MatTextureFill
};
void activateMaterial(QQuickWindow *window, Material m);
// shadow data for custom materials
QSGGradientCache::GradientDesc m_fillGradient;
QSGTextureProvider *m_fillTextureProvider = nullptr;
QSGTransform m_fillTransform;
void preprocess() override;
private Q_SLOTS:
void handleTextureChanged();
void handleTextureProviderDestroyed();
private:
QScopedPointer<QSGMaterial> m_material;
friend class QQuickShapeGenericRenderer;
};
class QQuickShapeGenericNode : public QSGNode
{
public:
QQuickShapeGenericStrokeFillNode *m_fillNode = nullptr;
QQuickShapeGenericStrokeFillNode *m_strokeNode = nullptr;
QQuickShapeGenericNode *m_next = nullptr;
};
class QQuickShapeGenericMaterialFactory
{
public:
static QSGMaterial *createVertexColor(QQuickWindow *window);
static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node);
static QSGMaterial *createRadialGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node);
static QSGMaterial *createConicalGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node);
static QSGMaterial *createTextureFill(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node);
};
class QQuickShapeLinearGradientRhiShader : public QSGMaterialShader
{
public:
QQuickShapeLinearGradientRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
QSGMaterial *oldMaterial) override;
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
private:
QSGTransform m_fillTransform;
QVector2D m_gradA;
QVector2D m_gradB;
};
class QQuickShapeLinearGradientMaterial : public QSGMaterial
{
public:
QQuickShapeLinearGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
: m_node(node)
{
// Passing RequiresFullMatrix is essential in order to prevent the
// batch renderer from baking in simple, translate-only transforms into
// the vertex data. The shader will rely on the fact that
// vertexCoord.xy is the Shape-space coordinate and so no modifications
// are welcome.
setFlag(Blending | RequiresFullMatrix);
}
QSGMaterialType *type() const override;
int compare(const QSGMaterial *other) const override;
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override;
QQuickShapeGenericStrokeFillNode *node() const { return m_node; }
private:
QQuickShapeGenericStrokeFillNode *m_node;
};
class QQuickShapeRadialGradientRhiShader : public QSGMaterialShader
{
public:
QQuickShapeRadialGradientRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
QSGMaterial *oldMaterial) override;
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
private:
QSGTransform m_fillTransform;
QVector2D m_focalPoint;
QVector2D m_focalToCenter;
float m_centerRadius;
float m_focalRadius;
};
class QQuickShapeRadialGradientMaterial : public QSGMaterial
{
public:
QQuickShapeRadialGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
: m_node(node)
{
setFlag(Blending | RequiresFullMatrix);
}
QSGMaterialType *type() const override;
int compare(const QSGMaterial *other) const override;
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override;
QQuickShapeGenericStrokeFillNode *node() const { return m_node; }
private:
QQuickShapeGenericStrokeFillNode *m_node;
};
class QQuickShapeConicalGradientRhiShader : public QSGMaterialShader
{
public:
QQuickShapeConicalGradientRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
QSGMaterial *oldMaterial) override;
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
private:
QSGTransform m_fillTransform;
QVector2D m_centerPoint;
float m_angle;
};
class QQuickShapeConicalGradientMaterial : public QSGMaterial
{
public:
QQuickShapeConicalGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
: m_node(node)
{
setFlag(Blending | RequiresFullMatrix);
}
QSGMaterialType *type() const override;
int compare(const QSGMaterial *other) const override;
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override;
QQuickShapeGenericStrokeFillNode *node() const { return m_node; }
private:
QQuickShapeGenericStrokeFillNode *m_node;
};
class QQuickShapeTextureFillRhiShader : public QSGMaterialShader
{
public:
QQuickShapeTextureFillRhiShader(int viewCount);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial,
QSGMaterial *oldMaterial) override;
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
private:
QSGTransform m_fillTransform;
QVector2D m_boundsOffset;
QVector2D m_boundsSize;
};
class QQuickShapeTextureFillMaterial : public QSGMaterial
{
public:
QQuickShapeTextureFillMaterial(QQuickShapeGenericStrokeFillNode *node)
: m_node(node)
{
setFlag(Blending | RequiresFullMatrix);
}
~QQuickShapeTextureFillMaterial() override;
QSGMaterialType *type() const override;
int compare(const QSGMaterial *other) const override;
QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) const override;
QQuickShapeGenericStrokeFillNode *node() const { return m_node; }
QSGPlainTexture *dummyTexture() const
{
return m_dummyTexture;
}
void setDummyTexture(QSGPlainTexture *texture)
{
m_dummyTexture = texture;
}
private:
QQuickShapeGenericStrokeFillNode *m_node;
QSGPlainTexture *m_dummyTexture = nullptr;
};
QT_END_NAMESPACE
#endif // QQUICKSHAPEGENERICRENDERER_P_H
@@ -0,0 +1,10 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSHAPESGLOBAL_H
#define QQUICKSHAPESGLOBAL_H
#include <QtCore/qglobal.h>
#include <QtQuickShapes/qtquickshapesexports.h>
#endif // QQUICKSHAPESGLOBAL_H
@@ -0,0 +1,27 @@
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSHAPESGLOBAL_P_H
#define QQUICKSHAPESGLOBAL_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qquickshapesglobal.h"
#include <QtQuickShapes/qtquickshapesexports.h>
QT_BEGIN_NAMESPACE
void Q_QUICKSHAPES_EXPORT qml_register_types_QtQuick_Shapes();
QT_END_NAMESPACE
#endif // QQUICKSHAPESGLOBAL_P_H
@@ -0,0 +1,287 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickshapesoftwarerenderer_p.h"
#include <private/qquickpath_p_p.h>
QT_BEGIN_NAMESPACE
void QQuickShapeSoftwareRenderer::beginSync(int totalCount, bool *countChanged)
{
if (m_sp.size() != totalCount) {
m_sp.resize(totalCount);
m_accDirty |= DirtyList;
*countChanged = true;
} else {
*countChanged = false;
}
}
void QQuickShapeSoftwareRenderer::setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints)
{
ShapePathGuiData &d(m_sp[index]);
d.path = path;
d.dirty |= DirtyPath;
m_accDirty |= DirtyPath;
}
void QQuickShapeSoftwareRenderer::setStrokeColor(int index, const QColor &color)
{
ShapePathGuiData &d(m_sp[index]);
d.pen.setColor(color);
d.dirty |= DirtyPen;
m_accDirty |= DirtyPen;
}
void QQuickShapeSoftwareRenderer::setStrokeWidth(int index, qreal w)
{
ShapePathGuiData &d(m_sp[index]);
d.strokeWidth = w;
if (w > 0.0f)
d.pen.setWidthF(w);
d.dirty |= DirtyPen;
m_accDirty |= DirtyPen;
}
void QQuickShapeSoftwareRenderer::setCosmeticStroke(int index, bool c)
{
ShapePathGuiData &d(m_sp[index]);
d.pen.setCosmetic(c);
d.dirty |= DirtyPen;
m_accDirty |= DirtyPen;
}
void QQuickShapeSoftwareRenderer::setFillColor(int index, const QColor &color)
{
ShapePathGuiData &d(m_sp[index]);
d.fillColor = color;
d.brush.setColor(color);
d.dirty |= DirtyBrush;
m_accDirty |= DirtyBrush;
}
void QQuickShapeSoftwareRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule)
{
ShapePathGuiData &d(m_sp[index]);
d.fillRule = Qt::FillRule(fillRule);
d.dirty |= DirtyFillRule;
m_accDirty |= DirtyFillRule;
}
void QQuickShapeSoftwareRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit)
{
ShapePathGuiData &d(m_sp[index]);
d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
d.pen.setMiterLimit(miterLimit);
d.dirty |= DirtyPen;
m_accDirty |= DirtyPen;
}
void QQuickShapeSoftwareRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle)
{
ShapePathGuiData &d(m_sp[index]);
d.pen.setCapStyle(Qt::PenCapStyle(capStyle));
d.dirty |= DirtyPen;
m_accDirty |= DirtyPen;
}
void QQuickShapeSoftwareRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
qreal dashOffset, const QList<qreal> &dashPattern)
{
ShapePathGuiData &d(m_sp[index]);
switch (strokeStyle) {
case QQuickShapePath::SolidLine:
d.pen.setStyle(Qt::SolidLine);
break;
case QQuickShapePath::DashLine:
d.pen.setStyle(Qt::CustomDashLine);
d.pen.setDashPattern(dashPattern);
d.pen.setDashOffset(dashOffset);
break;
default:
break;
}
d.dirty |= DirtyPen;
m_accDirty |= DirtyPen;
}
static inline void setupPainterGradient(QGradient *painterGradient, const QQuickShapeGradient &g)
{
painterGradient->setStops(g.gradientStops()); // sorted
switch (g.spread()) {
case QQuickShapeGradient::PadSpread:
painterGradient->setSpread(QGradient::PadSpread);
break;
case QQuickShapeGradient::RepeatSpread:
painterGradient->setSpread(QGradient::RepeatSpread);
break;
case QQuickShapeGradient::ReflectSpread:
painterGradient->setSpread(QGradient::ReflectSpread);
break;
default:
break;
}
}
void QQuickShapeSoftwareRenderer::setFillGradient(int index, QQuickShapeGradient *gradient)
{
ShapePathGuiData &d(m_sp[index]);
if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) {
QLinearGradient painterGradient(g->x1(), g->y1(), g->x2(), g->y2());
setupPainterGradient(&painterGradient, *g);
d.brush = QBrush(painterGradient);
} else if (QQuickShapeRadialGradient *g = qobject_cast<QQuickShapeRadialGradient *>(gradient)) {
QRadialGradient painterGradient(g->centerX(), g->centerY(), g->centerRadius(),
g->focalX(), g->focalY(), g->focalRadius());
setupPainterGradient(&painterGradient, *g);
d.brush = QBrush(painterGradient);
} else if (QQuickShapeConicalGradient *g = qobject_cast<QQuickShapeConicalGradient *>(gradient)) {
QConicalGradient painterGradient(g->centerX(), g->centerY(), g->angle());
setupPainterGradient(&painterGradient, *g);
d.brush = QBrush(painterGradient);
} else {
d.brush = QBrush(d.fillColor);
}
d.dirty |= DirtyBrush;
m_accDirty |= DirtyBrush;
}
void QQuickShapeSoftwareRenderer::setFillTextureProvider(int index, QQuickItem *textureProviderItem)
{
Q_UNUSED(index);
Q_UNUSED(textureProviderItem);
}
void QQuickShapeSoftwareRenderer::handleSceneChange(QQuickWindow *window)
{
Q_UNUSED(window);
// No action needed
}
void QQuickShapeSoftwareRenderer::setFillTransform(int index, const QSGTransform &transform)
{
ShapePathGuiData &d(m_sp[index]);
if (!(transform.isIdentity() && d.brush.transform().isIdentity())) // No need to copy if both==I
d.brush.setTransform(transform.matrix().toTransform());
d.dirty |= DirtyBrush;
m_accDirty |= DirtyBrush;
}
void QQuickShapeSoftwareRenderer::setTriangulationScale(int index, qreal scale)
{
ShapePathGuiData &d(m_sp[index]);
d.triangulationScale = scale;
d.dirty |= DirtyPen;
m_accDirty |= DirtyPen;
}
void QQuickShapeSoftwareRenderer::endSync(bool)
{
}
void QQuickShapeSoftwareRenderer::setNode(QQuickShapeSoftwareRenderNode *node)
{
m_node = node;
m_accDirty |= DirtyList;
}
void QQuickShapeSoftwareRenderer::updateNode()
{
if (!m_accDirty)
return;
const int count = m_sp.size();
const bool listChanged = m_accDirty & DirtyList;
if (listChanged)
m_node->m_sp.resize(count);
m_node->m_boundingRect = QRectF();
for (int i = 0; i < count; ++i) {
ShapePathGuiData &src(m_sp[i]);
QQuickShapeSoftwareRenderNode::ShapePathRenderData &dst(m_node->m_sp[i]);
if (listChanged || (src.dirty & DirtyPath)) {
dst.path = src.path;
dst.path.setFillRule(src.fillRule);
}
if (listChanged || (src.dirty & DirtyFillRule))
dst.path.setFillRule(src.fillRule);
if (listChanged || (src.dirty & DirtyPen)) {
dst.pen = src.pen;
dst.strokeWidth = src.strokeWidth;
dst.triangulationScale = src.triangulationScale;
}
if (listChanged || (src.dirty & DirtyBrush))
dst.brush = src.brush;
src.dirty = 0;
QRectF br = dst.path.boundingRect();
float sw = qMax(1.0f, dst.strokeWidth);
if (dst.pen.isCosmetic())
sw *= 2.0f / dst.triangulationScale;
br.adjust(-sw, -sw, sw, sw);
m_node->m_boundingRect |= br;
}
m_node->markDirty(QSGNode::DirtyMaterial);
m_accDirty = 0;
}
QQuickShapeSoftwareRenderNode::QQuickShapeSoftwareRenderNode(QQuickShape *item)
: m_item(item)
{
}
QQuickShapeSoftwareRenderNode::~QQuickShapeSoftwareRenderNode()
{
releaseResources();
}
void QQuickShapeSoftwareRenderNode::releaseResources()
{
}
void QQuickShapeSoftwareRenderNode::render(const RenderState *state)
{
if (m_sp.isEmpty())
return;
QSGRendererInterface *rif = m_item->window()->rendererInterface();
QPainter *p = static_cast<QPainter *>(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource));
Q_ASSERT(p);
const QRegion *clipRegion = state->clipRegion();
if (clipRegion && !clipRegion->isEmpty())
p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform
p->setTransform(matrix()->toTransform());
p->setOpacity(inheritedOpacity());
for (const ShapePathRenderData &d : std::as_const(m_sp)) {
p->setPen(d.strokeWidth > 0.0f && d.pen.color() != Qt::transparent ? d.pen : Qt::NoPen);
p->setBrush(d.brush.color() != Qt::transparent ? d.brush : Qt::NoBrush);
p->drawPath(d.path);
}
}
QSGRenderNode::StateFlags QQuickShapeSoftwareRenderNode::changedStates() const
{
return {};
}
QSGRenderNode::RenderingFlags QQuickShapeSoftwareRenderNode::flags() const
{
return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect()
}
QRectF QQuickShapeSoftwareRenderNode::rect() const
{
return m_boundingRect;
}
QT_END_NAMESPACE
@@ -0,0 +1,107 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QQUICKSHAPESOFTWARERENDERER_P_H
#define QQUICKSHAPESOFTWARERENDERER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of a number of Qt sources files. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
#include <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <QtQuickShapes/private/qquickshape_p_p.h>
#include <qsgrendernode.h>
#include <QPen>
#include <QBrush>
QT_BEGIN_NAMESPACE
class QQuickShapeSoftwareRenderNode;
class QQuickShapeSoftwareRenderer : public QQuickAbstractPathRenderer
{
public:
enum Dirty {
DirtyPath = 0x01,
DirtyPen = 0x02,
DirtyFillRule = 0x04,
DirtyBrush = 0x08,
DirtyList = 0x10
};
void beginSync(int totalCount, bool *countChanged) override;
void setPath(int index, const QPainterPath &path, QQuickShapePath::PathHints pathHints = {}) override;
void setStrokeColor(int index, const QColor &color) override;
void setStrokeWidth(int index, qreal w) override;
void setCosmeticStroke(int index, bool c) override;
void setFillColor(int index, const QColor &color) override;
void setFillRule(int index, QQuickShapePath::FillRule fillRule) override;
void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override;
void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override;
void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
qreal dashOffset, const QList<qreal> &dashPattern) override;
void setFillGradient(int index, QQuickShapeGradient *gradient) override;
void setFillTextureProvider(int index, QQuickItem *textureProviderItem) override;
void setFillTransform(int index, const QSGTransform &transform) override;
void setTriangulationScale(int index, qreal scale) override;
void endSync(bool async) override;
void handleSceneChange(QQuickWindow *window) override;
void updateNode() override;
void setNode(QQuickShapeSoftwareRenderNode *node);
private:
QQuickShapeSoftwareRenderNode *m_node = nullptr;
int m_accDirty = 0;
struct ShapePathGuiData {
int dirty = 0;
QPainterPath path;
QPen pen;
float strokeWidth;
float triangulationScale;
QColor fillColor;
QBrush brush;
Qt::FillRule fillRule;
};
QList<ShapePathGuiData> m_sp;
};
class QQuickShapeSoftwareRenderNode : public QSGRenderNode
{
public:
QQuickShapeSoftwareRenderNode(QQuickShape *item);
~QQuickShapeSoftwareRenderNode();
void render(const RenderState *state) override;
void releaseResources() override;
StateFlags changedStates() const override;
RenderingFlags flags() const override;
QRectF rect() const override;
private:
QQuickShape *m_item;
struct ShapePathRenderData {
QPainterPath path;
QPen pen;
float strokeWidth;
float triangulationScale;
QBrush brush;
};
QList<ShapePathRenderData> m_sp;
QRectF m_boundingRect;
friend class QQuickShapeSoftwareRenderer;
};
QT_END_NAMESPACE
#endif // QQUICKSHAPESOFTWARERENDERER_P_H
@@ -0,0 +1,30 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
#include <QtQuickShapes/private/qquickshape_p.h>
QT_BEGIN_NAMESPACE
Q_GHS_KEEP_REFERENCE(qml_register_types_QtQuick_Shapes);
Q_GHS_KEEP_REFERENCE(QQuickShapes_initializeModule);
class QmlShapesPlugin : public QQmlEngineExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
public:
QmlShapesPlugin(QObject *parent = nullptr) : QQmlEngineExtensionPlugin(parent)
{
volatile auto registration = &qml_register_types_QtQuick_Shapes;
volatile auto initialize = &QQuickShapes_initializeModule;
Q_UNUSED(registration);
Q_UNUSED(initialize);
}
};
QT_END_NAMESPACE
#include "qquickshapesplugin.moc"
@@ -0,0 +1,33 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#version 440
layout(location = 0) in vec2 coord;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D gradTabTexture;
layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 matrix[QSHADER_VIEW_COUNT];
#else
mat4 matrix;
#endif
mat4 gradientMatrix;
vec2 translationPoint;
float angle;
float opacity;
} ubuf;
#define INVERSE_2PI 0.1591549430918953358
void main()
{
float t;
if (abs(coord.y) == abs(coord.x))
t = (atan(-coord.y + 0.002, coord.x) + ubuf.angle) * INVERSE_2PI;
else
t = (atan(-coord.y, coord.x) + ubuf.angle) * INVERSE_2PI;
fragColor = texture(gradTabTexture, vec2(t - floor(t), 0.5)) * ubuf.opacity;
}
@@ -0,0 +1,29 @@
#version 440
layout(location = 0) in vec4 vertexCoord;
layout(location = 1) in vec4 vertexColor;
layout(location = 0) out vec2 coord;
layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 matrix[QSHADER_VIEW_COUNT];
#else
mat4 matrix;
#endif
mat4 gradientMatrix;
vec2 translationPoint;
float angle;
float opacity;
} ubuf;
void main()
{
vec2 gradVertexCoord = (ubuf.gradientMatrix * vertexCoord).xy;
coord = gradVertexCoord - ubuf.translationPoint;
#if QSHADER_VIEW_COUNT >= 2
gl_Position = ubuf.matrix[gl_ViewIndex] * vertexCoord;
#else
gl_Position = ubuf.matrix * vertexCoord;
#endif
}
@@ -0,0 +1,26 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#version 440
layout(location = 0) in float gradTabIndex;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D gradTabTexture;
layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 matrix[QSHADER_VIEW_COUNT];
#else
mat4 matrix;
#endif
mat4 gradientMatrix;
vec2 gradStart;
vec2 gradEnd;
float opacity;
} ubuf;
void main()
{
fragColor = texture(gradTabTexture, vec2(gradTabIndex, 0.5)) * ubuf.opacity;
}
@@ -0,0 +1,30 @@
#version 440
layout(location = 0) in vec4 vertexCoord;
layout(location = 1) in vec4 vertexColor;
layout(location = 0) out float gradTabIndex;
layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 matrix[QSHADER_VIEW_COUNT];
#else
mat4 matrix;
#endif
mat4 gradientMatrix;
vec2 gradStart;
vec2 gradEnd;
float opacity;
} ubuf;
void main()
{
vec2 gradVertexCoord = (ubuf.gradientMatrix * vertexCoord).xy;
vec2 gradVec = ubuf.gradEnd - ubuf.gradStart;
gradTabIndex = dot(gradVec, gradVertexCoord - ubuf.gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);
#if QSHADER_VIEW_COUNT >= 2
gl_Position = ubuf.matrix[gl_ViewIndex] * vertexCoord;
#else
gl_Position = ubuf.matrix * vertexCoord;
#endif
}
@@ -0,0 +1,40 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#version 440
layout(location = 0) in vec2 coord;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D gradTabTexture;
layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 matrix[QSHADER_VIEW_COUNT];
#else
mat4 matrix;
#endif
mat4 gradientMatrix;
vec2 translationPoint;
vec2 focalToCenter;
float centerRadius;
float focalRadius;
float opacity;
} ubuf;
void main()
{
float rd = ubuf.centerRadius - ubuf.focalRadius;
float b = 2.0 * (rd * ubuf.focalRadius + dot(coord, ubuf.focalToCenter));
float fmp2_m_radius2 = -ubuf.focalToCenter.x * ubuf.focalToCenter.x - ubuf.focalToCenter.y * ubuf.focalToCenter.y + rd * rd;
float inverse_2_fmp2_m_radius2 = 1.0 / (2.0 * fmp2_m_radius2);
float det = b * b - 4.0 * fmp2_m_radius2 * ((ubuf.focalRadius * ubuf.focalRadius) - dot(coord, coord));
vec4 result = vec4(0.0);
if (det >= 0.0) {
float detSqrt = sqrt(det);
float w = max((-b - detSqrt) * inverse_2_fmp2_m_radius2, (-b + detSqrt) * inverse_2_fmp2_m_radius2);
if (ubuf.focalRadius + w * (ubuf.centerRadius - ubuf.focalRadius) >= 0.0)
result = texture(gradTabTexture, vec2(w, 0.5)) * ubuf.opacity;
}
fragColor = result;
}
@@ -0,0 +1,31 @@
#version 440
layout(location = 0) in vec4 vertexCoord;
layout(location = 1) in vec4 vertexColor;
layout(location = 0) out vec2 coord;
layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 matrix[QSHADER_VIEW_COUNT];
#else
mat4 matrix;
#endif
mat4 gradientMatrix;
vec2 translationPoint;
vec2 focalToCenter;
float centerRadius;
float focalRadius;
float opacity;
} ubuf;
void main()
{
vec2 gradVertexCoord = (ubuf.gradientMatrix * vertexCoord).xy;
coord = gradVertexCoord - ubuf.translationPoint;
#if QSHADER_VIEW_COUNT >= 2
gl_Position = ubuf.matrix[gl_ViewIndex] * vertexCoord;
#else
gl_Position = ubuf.matrix * vertexCoord;
#endif
}
@@ -0,0 +1,25 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#version 440
layout(location = 0) in vec2 textureCoord;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D sourceTexture;
layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 qt_Matrix[QSHADER_VIEW_COUNT];
#else
mat4 qt_Matrix;
#endif
mat4 fillMatrix;
vec2 boundsSize;
float qt_Opacity;
} ubuf;
void main()
{
fragColor = texture(sourceTexture, textureCoord) * ubuf.qt_Opacity;
}
@@ -0,0 +1,31 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#version 440
layout(location = 0) in vec4 vertexCoord;
layout(location = 1) in vec4 vertexColor;
layout(location = 0) out vec2 textureCoord;
layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 qt_Matrix[QSHADER_VIEW_COUNT];
#else
mat4 qt_Matrix;
#endif
mat4 fillMatrix;
vec2 boundsSize;
float qt_Opacity;
} ubuf;
void main()
{
vec2 xformed = (ubuf.fillMatrix * vertexCoord).xy;
textureCoord = vec2(xformed.x / ubuf.boundsSize.x, xformed.y / ubuf.boundsSize.y);
#if QSHADER_VIEW_COUNT >= 2
gl_Position = ubuf.qt_Matrix[gl_ViewIndex] * vertexCoord;
#else
gl_Position = ubuf.qt_Matrix * vertexCoord;
#endif
}
@@ -0,0 +1,25 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#version 440
layout(location = 0) out vec4 fragColor;
layout(location = 0) in vec3 barycentric;
layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 qt_Matrix[QSHADER_VIEW_COUNT];
#else
mat4 qt_Matrix;
#endif
} ubuf;
void main()
{
float f = min(barycentric.x, min(barycentric.y, barycentric.z));
float d = fwidth(f * 1.5);
float alpha = smoothstep(0.0, d, f);
//alpha = 1.0 - step(0.5, barycentric.x);
fragColor = vec4(1.0, 0.2, 1.0, 1.0) * (1.0 - alpha);
}
@@ -0,0 +1,24 @@
#version 440
layout(location = 0) in vec4 vertexCoord;
layout(location = 1) in vec3 vertexBarycentric;
layout(location = 2) in vec3 normalExt; // x and y: normal vector; z: strokeWidth multiplier (default 1)
layout(location = 0) out vec3 barycentric;
layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
mat4 qt_Matrix[QSHADER_VIEW_COUNT];
#else
mat4 qt_Matrix;
#endif
} ubuf;
void main()
{
barycentric = vertexBarycentric;
#if QSHADER_VIEW_COUNT >= 2
gl_Position = ubuf.qt_Matrix[gl_ViewIndex] * vertexCoord;
#else
gl_Position = ubuf.qt_Matrix * vertexCoord;
#endif
}