diff options
author | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2023-07-13 11:45:55 +0200 |
---|---|---|
committer | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2023-07-14 08:40:47 +0200 |
commit | 73324c379f8c638106203d679549e7785d16af38 (patch) | |
tree | 87bb90f877290aba3f4023ea23dc2f73dcb0d3e1 /tests/manual/painterpathquickshape | |
parent | cb644288976b6abd2c9bda4caaf7e447660336a9 (diff) |
Improve Qt Quick Shapes curve renderer strokes
This adds a specialized stroke shader which replaces the
curve flattening triangulating stroker as the default. The
triangulating stroker is still available via an environment
variable for testing. Dashed strokes are now done by cutting
the path into segments ahead of time and applying the normal
solid stroking algorithm to the corresponding path.
In addition, the fill shader no longer depends on
the derivatives extension except when used in 3D scenes to
support perspective transforms.
The preprocessing of the shape has also gotten a few bug
fixes and improvements and there are extremely few artifacts
left.
Done-with: Paul Olav Tvete <paul.tvete@qt.io>
Done-with: Eirik Aavitsland <eirik.aavitsland@qt.io>
Done-with: Matthias Rauter <matthias.rauter@qt.io>
Task-number: QTBUG-104122
Change-Id: Ib49b41019956be3e3cd509d1f3cef6842658aceb
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Matthias Rauter <matthias.rauter@qt.io>
Diffstat (limited to 'tests/manual/painterpathquickshape')
-rw-r--r-- | tests/manual/painterpathquickshape/CMakeLists.txt | 78 | ||||
-rw-r--r-- | tests/manual/painterpathquickshape/ControlPanel.qml | 41 | ||||
-rw-r--r-- | tests/manual/painterpathquickshape/ControlledShape.qml | 8 | ||||
-rw-r--r-- | tests/manual/painterpathquickshape/DashedStroke.qml | 24 | ||||
-rw-r--r-- | tests/manual/painterpathquickshape/SimpleShape.qml | 9 | ||||
-rw-r--r-- | tests/manual/painterpathquickshape/SvgShape.qml | 9 | ||||
-rw-r--r-- | tests/manual/painterpathquickshape/main.cpp | 1 | ||||
-rw-r--r-- | tests/manual/painterpathquickshape/main.qml | 83 | ||||
-rw-r--r-- | tests/manual/painterpathquickshape/zoombox.frag | 22 |
9 files changed, 229 insertions, 46 deletions
diff --git a/tests/manual/painterpathquickshape/CMakeLists.txt b/tests/manual/painterpathquickshape/CMakeLists.txt index 67f5b142ef..1f92afaba5 100644 --- a/tests/manual/painterpathquickshape/CMakeLists.txt +++ b/tests/manual/painterpathquickshape/CMakeLists.txt @@ -1,6 +1,15 @@ # Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +cmake_minimum_required(VERSION 3.16) + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + project(painterPathQuickShape LANGUAGES C CXX ASM) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +find_package(Qt6 COMPONENTS ShaderTools) + qt_internal_add_manual_test(painterPathQuickShape GUI SOURCES @@ -20,35 +29,36 @@ qt_internal_add_manual_test(painterPathQuickShape set(qml_resource_files "1535737773.svg" "main.qml" - "SvgShape.qml" - "ControlPanel.qml" - "ControlPoint.qml" - "TextShape.qml" - "SimpleShape.qml" - "SmallPolygon.qml" - "Squircle.qml" - "ControlledShape.qml" - "Mussel.qml" - "Graziano.ttf" - "CubicShape.qml" - "hand-print.svg" - "background.png" - "arcDirection.qml" - "arcRotation.qml" - "capStyles.qml" - "cubicCurve.qml" - "dashPattern.qml" - "ellipticalArcs.qml" - "fillRules.qml" - "gradientSpreadModes.qml" - "joinStyles.qml" - "largeOrSmallArc.qml" - "linearGradient.qml" - "quadraticCurve.qml" - "radialGradient.qml" - "strokeOrFill.qml" - "text.qml" - "tiger.qml" + "SvgShape.qml" + "ControlPanel.qml" + "ControlPoint.qml" + "TextShape.qml" + "SimpleShape.qml" + "SmallPolygon.qml" + "Squircle.qml" + "ControlledShape.qml" + "Mussel.qml" + "Graziano.ttf" + "CubicShape.qml" + "DashedStroke.qml" + "hand-print.svg" + "background.png" + "arcDirection.qml" + "arcRotation.qml" + "capStyles.qml" + "cubicCurve.qml" + "dashPattern.qml" + "ellipticalArcs.qml" + "fillRules.qml" + "gradientSpreadModes.qml" + "joinStyles.qml" + "largeOrSmallArc.qml" + "linearGradient.qml" + "quadraticCurve.qml" + "radialGradient.qml" + "strokeOrFill.qml" + "text.qml" + "tiger.qml" ) qt_internal_add_resource(painterPathQuickShape "qml" @@ -58,6 +68,16 @@ qt_internal_add_resource(painterPathQuickShape "qml" ${qml_resource_files} ) +qt_add_shaders(painterPathQuickShape "shaders" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + "/" + FILES + "zoombox.frag" +) + qt_add_qml_module(painterPathQuickShape VERSION 1.0 URI InstancingTest diff --git a/tests/manual/painterpathquickshape/ControlPanel.qml b/tests/manual/painterpathquickshape/ControlPanel.qml index 1b34bb6eab..bb26e658f3 100644 --- a/tests/manual/painterpathquickshape/ControlPanel.qml +++ b/tests/manual/painterpathquickshape/ControlPanel.qml @@ -6,6 +6,7 @@ import QtQuick.Layouts import QtQuick.Controls import QtQuick.Shapes import QtQuick.Dialogs +import QtCore Item { property real scale: +scaleSlider.value.toFixed(4) @@ -15,7 +16,7 @@ Item { property color fillColor: Qt.rgba(fillColor.color.r, fillColor.color.g, fillColor.color.b, pathAlpha) property alias pathAlpha: alphaSlider.value property alias outlineAlpha: outlineAlphaSlider.value - property real outlineWidth: cosmeticPen.checked ? outlineWidth.value / scale : outlineWidth.value ** 2 + property real outlineWidth: cosmeticPen.checked ? outlineWidthEdit.text / scale : outlineWidthEdit.text property alias outlineStyle: outlineStyle.currentValue property alias capStyle: capStyle.currentValue property alias joinStyle: joinStyle.currentValue @@ -34,6 +35,23 @@ Item { property real pathMargin: marginEdit.text + Settings { + property alias enableOutline: enableOutline.checked + property alias outlineColor: outlineColor.color + property alias outlineWidth: outlineWidthEdit.text + property alias outlineAlpha: outlineAlphaSlider.value + property alias outlineStyle: outlineStyle.currentIndex + property alias joinStyle: joinStyle.currentIndex + property alias capStyle: capStyle.currentIndex + property alias cosmeticPen: cosmeticPen.checked + + property alias pathAlpha: alphaSlider.value + property alias fillColor: fillColor.color + + property alias setBackground: setBackground.checked + property alias backgroundColor: bgColor.color + } + function setScale(x) { scaleSlider.value = x } @@ -200,7 +218,7 @@ Item { model: [ "NoGradient", "LinearGradient", "RadialGradient", "ConicalGradient" ] } Label { - text: "Path alpha:" + text: "Fill alpha(" + Math.round(alphaSlider.value*100)/100 + "):" color: "white" } Slider { @@ -290,11 +308,24 @@ Item { } } Label { - text: "Outline width" + text: "Outline width:" color: "white" } + TextField { + id: outlineWidthEdit + text: (cosmeticPen.checked ? outlineWidthSlider.value: outlineWidthSlider.value ** 2).toFixed(2) + onEditingFinished: { + let val = +text + if (val > 0) { + if (cosmeticPen.checked) + outlineWidth.value = val * scale + else + outlineWidth.value = Math.sqrt(val) + } + } + } Slider { - id: outlineWidth + id: outlineWidthSlider Layout.fillWidth: true from: 0.0 to: 10.0 @@ -306,7 +337,7 @@ Item { palette.windowText: "white" } Label { - text: "Outline alpha" + text: "Outline alpha (" + Math.round(outlineAlphaSlider.value*100)/100 + "):" color: "white" } Slider { diff --git a/tests/manual/painterpathquickshape/ControlledShape.qml b/tests/manual/painterpathquickshape/ControlledShape.qml index cf9cbaa5d8..b0f4492eb2 100644 --- a/tests/manual/painterpathquickshape/ControlledShape.qml +++ b/tests/manual/painterpathquickshape/ControlledShape.qml @@ -7,12 +7,14 @@ import io.qt Item { id: topLevel - property alias fillColor: shapePath.fillColor - property alias strokeStyle: shapePath.strokeStyle property alias capStyle: shapePath.capStyle + property alias dashOffset: shapePath.dashOffset + property alias dashPattern: shapePath.dashPattern + property alias fillColor: shapePath.fillColor + property alias fillRule: shapePath.fillRule property alias strokeColor: shapePath.strokeColor + property alias strokeStyle: shapePath.strokeStyle property alias strokeWidth: shapePath.strokeWidth - property alias fillRule: shapePath.fillRule property alias shapeTransform: shape.transform property alias startX: shapePath.startX diff --git a/tests/manual/painterpathquickshape/DashedStroke.qml b/tests/manual/painterpathquickshape/DashedStroke.qml new file mode 100644 index 0000000000..60952ec599 --- /dev/null +++ b/tests/manual/painterpathquickshape/DashedStroke.qml @@ -0,0 +1,24 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Shapes + +ControlledShape { + fillColor: "transparent" + strokeStyle: ShapePath.DashLine + dashOffset: 6.5 + dashPattern: [ 4, 2, 1, 2, 1, 2 ] + delegate: [ + PathMove { x: 200; y: 200 }, + PathLine { x: 1000; y: 200 }, + PathMove { x: 200; y: 300 }, + PathLine { x: 1000; y: 300 }, + PathMove { x: 200; y: 400 }, + PathQuad { x: 1000; y: 400; controlX: 450; controlY: 600 }, + PathLine { x: 1600; y: 500 }, + PathMove { x: 200; y: 500 }, + PathQuad { x: 1000; y: 500; controlX: 450; controlY: 700 }, + PathLine { x: 1600; y: 600 } + ] +} diff --git a/tests/manual/painterpathquickshape/SimpleShape.qml b/tests/manual/painterpathquickshape/SimpleShape.qml index 3458e26f4a..cb6261c39a 100644 --- a/tests/manual/painterpathquickshape/SimpleShape.qml +++ b/tests/manual/painterpathquickshape/SimpleShape.qml @@ -11,12 +11,11 @@ ControlledShape { PathPolyline { id: ppl path: [ Qt.point(150.0, 100.0), - Qt.point(1250.0, 150.0), - Qt.point(100.0, 1000.0), - Qt.point(150, 100) - ] + Qt.point(1250.0, 150.0), + Qt.point(100.0, 1000.0), + Qt.point(150, 100) + ] }, - // A very narrow shape with one convex and one concave curve PathMove { x: 600; y: 1200}, PathQuad { x: 800; y: 1200; controlX: 700; controlY: 600 }, diff --git a/tests/manual/painterpathquickshape/SvgShape.qml b/tests/manual/painterpathquickshape/SvgShape.qml index 697ede2524..429ca507e6 100644 --- a/tests/manual/painterpathquickshape/SvgShape.qml +++ b/tests/manual/painterpathquickshape/SvgShape.qml @@ -63,12 +63,13 @@ Item { strokeWidth = "1.0" // default value defined by SVG standard strokeText = "strokeColor: \"" + strokeColor + "\"; strokeWidth: " + strokeWidth + ";" } - if (!fillColor) { - fillColor = "#00000000" - } + + let fillText = ""; + if (fillColor) + fillText = "fillColor: \"" + fillColor + "\";\n"; var obj = Qt.createQmlObject("import QtQuick\nimport QtQuick.Shapes\n ControlledShape { \n" - + "fillColor: \"" + fillColor + "\";\n" + + fillText + "shapeTransform: Matrix4x4 { matrix: Qt.matrix4x4(" + transform + "); }\n" + strokeText + "\n" + "fillRule: ShapePath.WindingFill; delegate: [ PathSvg { path: \"" + s + "\"; } ] }\n", diff --git a/tests/manual/painterpathquickshape/main.cpp b/tests/manual/painterpathquickshape/main.cpp index 5b780b9439..c1fa790bd4 100644 --- a/tests/manual/painterpathquickshape/main.cpp +++ b/tests/manual/painterpathquickshape/main.cpp @@ -15,6 +15,7 @@ int main(int argc, char *argv[]) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif QGuiApplication app(argc, argv); + app.setOrganizationName("QtProject"); qmlRegisterType<DebugPaintItem>("io.qt", 1, 0, "DebugPaintItem"); qmlRegisterType<SvgPathLoader>("io.qt", 1, 0, "SvgPathLoader"); diff --git a/tests/manual/painterpathquickshape/main.qml b/tests/manual/painterpathquickshape/main.qml index 9ad5d1f1a4..c7c1f60579 100644 --- a/tests/manual/painterpathquickshape/main.qml +++ b/tests/manual/painterpathquickshape/main.qml @@ -6,8 +6,10 @@ import QtQuick.Window import QtQuick.Shapes import QtQuick.Controls import QtQuick.Layouts +import QtCore Window { + id: theWindow width: 1024 height: 768 visible: true @@ -42,6 +44,10 @@ Window { source: "CubicShape.qml" } ListElement { + text: "DashedStroke" + source: "DashedStroke.qml" + } + ListElement { text: "Mussel" source: "Mussel.qml" } @@ -189,8 +195,80 @@ Window { y: controlPanel.pathMargin id: loader } + MouseArea { + acceptedButtons: Qt.NoButton + anchors.fill: parent + onMouseXChanged: { + let p = Qt.point(Math.round(mouseX), Math.round(mouseY)) + p = mapToItem(loader.item, p) + zoomTarget.sourceRect.x = p.x - zoomTarget.sourceRect.width/2 + } + onMouseYChanged: { + let p = Qt.point(Math.round(mouseX), Math.round(mouseY)) + p = mapToItem(loader.item, p) + zoomTarget.sourceRect.y = p.y - zoomTarget.sourceRect.height/2 + } + hoverEnabled: true + } + ShaderEffectSource { + id: zoomTarget + sourceItem: loader.item + sourceRect.width: 16 + sourceRect.height: 16 + } + } + + + Rectangle { + anchors.top: flickable.top + anchors.right: flickable.right + anchors.margins: 5 + width: 256 + height: 256 + border.color: Qt.black + ShaderEffect { + anchors.fill: parent + anchors.margins: 1 + property variant src: zoomTarget + property real textureWidth: zoomTarget.sourceRect.width + property real textureHeight: zoomTarget.sourceRect.height + fragmentShader: "zoombox.frag.qsb" + } + Button { + id: plusButton + text: "+" + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: 2 + width: 20 + height: 20 + onPressed: { + zoomTarget.sourceRect.width = Math.max(zoomTarget.sourceRect.width / 2, 4) + zoomTarget.sourceRect.height = Math.max(zoomTarget.sourceRect.height / 2, 4) + } + } + Button { + id: minusButton + text: "-" + anchors.top: plusButton.bottom + anchors.right: parent.right + anchors.margins: 2 + width: 20 + height: 20 + onPressed: { + zoomTarget.sourceRect.width = Math.max(zoomTarget.sourceRect.width * 2, 4) + zoomTarget.sourceRect.height = Math.max(zoomTarget.sourceRect.height * 2, 4) + } + } + Text { + text: "x"+parent.width / zoomTarget.sourceRect.width + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.margins: 2 + } } + ControlPanel { id: controlPanel anchors.bottom: parent.bottom @@ -198,4 +276,9 @@ Window { anchors.right: parent.right height: parent.height / 4 } + Settings { + property alias currentTab: comboBox.currentIndex + property alias windowWidth: theWindow.width + property alias windowHeight: theWindow.height + } } diff --git a/tests/manual/painterpathquickshape/zoombox.frag b/tests/manual/painterpathquickshape/zoombox.frag new file mode 100644 index 0000000000..e7754cb75c --- /dev/null +++ b/tests/manual/painterpathquickshape/zoombox.frag @@ -0,0 +1,22 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + float textureWidth; + float textureHeight; +}; +layout(binding = 1) uniform sampler2D src; + + +void main(void) +{ + + float xPixelIndex = (round((textureWidth - 1.) * qt_TexCoord0.x) + 0.5) / textureWidth; + float yPixelIndex = (round((textureHeight - 1.) * qt_TexCoord0.y) + 0.5) / textureHeight; + + fragColor = texture(src, vec2(xPixelIndex, yPixelIndex)) * qt_Opacity; +} |