diff --git a/kirigami.qrc b/kirigami.qrc
--- a/kirigami.qrc
+++ b/kirigami.qrc
@@ -79,6 +79,7 @@
src/controls/AbstractApplicationHeader.qml
src/controls/FormLayout.qml
src/controls/ListItemDragHandle.qml
+ src/controls/ShadowedImage.qml
src/styles/Material/AbstractListItem.qml
src/styles/Material/Theme.qml
src/styles/Material/SwipeListItem.qml
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -25,15 +25,19 @@
mnemonicattached.cpp
wheelhandler.cpp
shadowedrectangle.cpp
+ shadowedtexture.cpp
scenegraph/shadowedrectanglenode.cpp
scenegraph/shadowedrectanglematerial.cpp
scenegraph/shadowedborderrectanglematerial.cpp
scenegraph/paintedrectangleitem.cpp
+ scenegraph/shadowedtexturenode.cpp
+ scenegraph/shadowedtexturematerial.cpp
+ scenegraph/shadowedbordertexturematerial.cpp
${kirigami_QM_LOADER}
${KIRIGAMI_STATIC_FILES}
)
-qt5_add_resources(SHADERS scenegraph/shaders.qrc)
+qt5_add_resources(SHADERS scenegraph/shaders/shaders.qrc)
add_subdirectory(libkirigami)
diff --git a/src/controls/ShadowedImage.qml b/src/controls/ShadowedImage.qml
new file mode 100644
--- /dev/null
+++ b/src/controls/ShadowedImage.qml
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+import QtQuick 2.1
+import org.kde.kirigami 2.12
+
+Item {
+ property alias color: shadowRectangle.color
+ property alias radius: shadowRectangle.radius
+ property alias shadow: shadowRectangle.shadow
+ property alias border: shadowRectangle.border
+ property alias source: image.source
+
+ ShadowedTexture {
+ id: shadowRectangle
+ anchors.fill: parent
+
+ source: Image {
+ id: image
+ visible: false
+ }
+ }
+}
diff --git a/src/kirigamiplugin.cpp b/src/kirigamiplugin.cpp
--- a/src/kirigamiplugin.cpp
+++ b/src/kirigamiplugin.cpp
@@ -18,6 +18,7 @@
#include "scenepositionattached.h"
#include "wheelhandler.h"
#include "shadowedrectangle.h"
+#include "shadowedtexture.h"
#include
#include
@@ -238,6 +239,9 @@
// 2.12
qmlRegisterType(uri, 2, 12, "ShadowedRectangle");
+ qmlRegisterType(uri, 2, 12, "ShadowedTexture");
+ qmlRegisterType(componentUrl(QStringLiteral("ShadowedImage.qml")), uri, 2, 12, "ShadowedImage");
+
qmlRegisterUncreatableType(uri, 2, 12, "BorderGroup", QStringLiteral("Used as grouped property"));
qmlRegisterUncreatableType(uri, 2, 12, "ShadowGroup", QStringLiteral("Used as grouped property"));
diff --git a/src/scenegraph/shaders.qrc b/src/scenegraph/shaders.qrc
deleted file mode 100644
--- a/src/scenegraph/shaders.qrc
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- header_es.glsl
- header_desktop.glsl
- header_desktop_core.glsl
- shadowedrectangle.vert
- shadowedrectangle_core.vert
- shadowedrectangle.frag
- shadowedrectangle_core.frag
- shadowedborderrectangle.frag
- shadowedborderrectangle_core.frag
-
-
-
diff --git a/src/scenegraph/header_desktop.glsl b/src/scenegraph/shaders/header_desktop.glsl
rename from src/scenegraph/header_desktop.glsl
rename to src/scenegraph/shaders/header_desktop.glsl
diff --git a/src/scenegraph/header_desktop_core.glsl b/src/scenegraph/shaders/header_desktop_core.glsl
rename from src/scenegraph/header_desktop_core.glsl
rename to src/scenegraph/shaders/header_desktop_core.glsl
--- a/src/scenegraph/header_desktop_core.glsl
+++ b/src/scenegraph/shaders/header_desktop_core.glsl
@@ -12,3 +12,5 @@
// This file is intended for desktop OpenGL version 4.5 or greater.
#version 450
+
+#define CORE_PROFILE
diff --git a/src/scenegraph/header_es.glsl b/src/scenegraph/shaders/header_es.glsl
rename from src/scenegraph/header_es.glsl
rename to src/scenegraph/shaders/header_es.glsl
diff --git a/src/scenegraph/shaders/sdf.glsl b/src/scenegraph/shaders/sdf.glsl
new file mode 100644
--- /dev/null
+++ b/src/scenegraph/shaders/sdf.glsl
@@ -0,0 +1,276 @@
+// SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+// SPDX-FileCopyrightText: 2017 Inigo Quilez
+//
+// SPDX-License-Identifier: MIT
+//
+// This file is based on
+// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
+
+//if not GLES
+// include "desktop_header.glsl"
+//else
+// include "es_header.glsl"
+
+// A maximum point count to be used for sdf_polygon input arrays.
+// Unfortunately even function inputs require a fixed size at declaration time
+// for arrays, unless we were to use OpenGL 4.5.
+// Since the polygon is most likely to be defined in a uniform, this should be
+// at least less than MAX_FRAGMENT_UNIFORM_COMPONENTS / 2 (since we need vec2).
+#define SDF_POLYGON_MAX_POINT_COUNT 400
+
+/*********************************
+ Shapes
+*********************************/
+
+// Distance field for a circle.
+//
+// \param point A point on the distance field.
+// \param radius The radius of the circle.
+//
+// \return The signed distance from point to the circle. If negative, point is
+// inside the circle.
+lowp float sdf_circle(in lowp vec2 point, in lowp float radius)
+{
+ return length(point) - radius;
+}
+
+// Distance field for a triangle.
+//
+// \param point A point on the distance field.
+// \param p0 The first vertex of the triangle.
+// \param p0 The second vertex of the triangle.
+// \param p0 The third vertex of the triangle.
+//
+// \note The ordering of the three vertices does not matter.
+//
+// \return The signed distance from point to triangle. If negative, point is
+// inside the triangle.
+lowp float sdf_triangle(in lowp vec2 point, in lowp vec2 p0, in lowp vec2 p1, in lowp vec2 p2)
+{
+ lowp vec2 e0 = p1 - p0;
+ lowp vec2 e1 = p2 - p1;
+ lowp vec2 e2 = p0 - p2;
+
+ lowp vec2 v0 = point - p0;
+ lowp vec2 v1 = point - p1;
+ lowp vec2 v2 = point - p2;
+
+ lowp vec2 pq0 = v0 - e0 * clamp( dot(v0, e0) / dot(e0, e0), 0.0, 1.0 );
+ lowp vec2 pq1 = v1 - e1 * clamp( dot(v1, e1) / dot(e1, e1), 0.0, 1.0 );
+ lowp vec2 pq2 = v2 - e2 * clamp( dot(v2, e2) / dot(e2, e2), 0.0, 1.0 );
+
+ lowp float s = sign( e0.x*e2.y - e0.y*e2.x );
+ lowp vec2 d = min(min(vec2(dot(pq0,pq0), s*(v0.x*e0.y-v0.y*e0.x)),
+ vec2(dot(pq1,pq1), s*(v1.x*e1.y-v1.y*e1.x))),
+ vec2(dot(pq2,pq2), s*(v2.x*e2.y-v2.y*e2.x)));
+
+ return -sqrt(d.x)*sign(d.y);
+}
+
+// Distance field for an arbitrary polygon.
+//
+// \param point A point on the distance field.
+// \param vertices An array of points that make up the polygon.
+// \param count The amount of points to use for the polygon.
+//
+// \note points should be an array of vec2 of size SDF_POLYGON_MAX_POINT_COUNT.
+// Use count to indicate how many items of that array should be used.
+//
+// \return The signed distance from point to triangle. If negative, point is
+// inside the triangle.
+
+// Strictly speaking, GLES 2.0 doesn't support function array arguments (apparently), so our validation fails here.
+// But at least Mesa GLES 2.0 accepts it, so skip validation here instead.
+#if !defined(GL_ES) || !defined(VALIDATING)
+lowp float sdf_polygon(in lowp vec2 point, in lowp vec2[SDF_POLYGON_MAX_POINT_COUNT] vertices, in lowp int count)
+{
+ lowp float d = dot(point - vertices[0], point - vertices[0]);
+ lowp float s = 1.0;
+ for (int i = 0, j = count - 1; i < count && i < SDF_POLYGON_MAX_POINT_COUNT; j = i, i++)
+ {
+ lowp vec2 e = vertices[j] - vertices[i];
+ lowp vec2 w = point - vertices[i];
+ lowp float h = clamp( dot(w, e) / dot(e, e), 0.0, 1.0 );
+ lowp vec2 b = w - e * h;
+ d = min(d, dot(b, b));
+
+ bvec3 c = bvec3(point.y >= vertices[i].y, point.y < vertices[j].y, e.x * w.y > e.y * w.x);
+ if(all(c) || all(not(c))) s *= -1.0;
+ }
+ return s * sqrt(d);
+}
+#endif
+
+// Distance field for a rectangle.
+//
+// \param point A point on the distance field.
+// \param rect A vec2 with the size of the rectangle.
+//
+// \return The signed distance from point to rectangle. If negative, point is
+// inside the rectangle.
+lowp float sdf_rectangle(in lowp vec2 point, in lowp vec2 rect)
+{
+ lowp vec2 d = abs(point) - rect;
+ return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
+}
+
+// Distance field for a rectangle with rounded corners.
+//
+// \param point The point to calculate the distance of.
+// \param rect The rectangle to calculate the distance of.
+// \param radius A vec4 with the radius of each corner. Order is top right, bottom right, top left, bottom left.
+//
+// \return The signed distance from point to rectangle. If negative, point is
+// inside the rectangle.
+lowp float sdf_rounded_rectangle(in lowp vec2 point, in lowp vec2 rect, in lowp vec4 radius)
+{
+ radius.xy = (point.x > 0.0) ? radius.xy : radius.zw;
+ radius.x = (point.y > 0.0) ? radius.x : radius.y;
+ lowp vec2 d = abs(point) - rect + radius.x;
+ return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius.x;
+}
+
+/*********************
+ Operators
+*********************/
+
+// Convert a distance field to an annular (hollow) distance field.
+//
+// \param sdf The result of an sdf shape to convert.
+// \param thickness The thickness of the resulting shape.
+//
+// \return The value of sdf modified to an annular shape.
+lowp float sdf_annular(in lowp float sdf, in lowp float thickness)
+{
+ return abs(sdf) - thickness;
+}
+
+// Union two sdf shapes together.
+//
+// \param sdf1 The first sdf shape.
+// \param sdf2 The second sdf shape.
+//
+// \return The union of sdf1 and sdf2, that is, the distance to both sdf1 and
+// sdf2.
+lowp float sdf_union(in lowp float sdf1, in lowp float sdf2)
+{
+ return min(sdf1, sdf2);
+}
+
+// Subtract two sdf shapes.
+//
+// \param sdf1 The first sdf shape.
+// \param sdf2 The second sdf shape.
+//
+// \return sdf1 with sdf2 subtracted from it.
+lowp float sdf_subtract(in lowp float sdf1, in lowp float sdf2)
+{
+ return max(sdf1, -sdf2);
+}
+
+// Intersect two sdf shapes.
+//
+// \param sdf1 The first sdf shape.
+// \param sdf2 The second sdf shape.
+//
+// \return The intersection between sdf1 and sdf2, that is, the area where both
+// sdf1 and sdf2 provide the same distance value.
+lowp float sdf_intersect(in lowp float sdf1, in lowp float sdf2)
+{
+ return max(sdf1, sdf2);
+}
+
+// Smoothly intersect two sdf shapes.
+//
+// \param sdf1 The first sdf shape.
+// \param sdf2 The second sdf shape.
+// \param smoothing The amount of smoothing to apply.
+//
+// \return A smoothed version of the intersect operation.
+lowp float sdf_intersect_smooth(in lowp float sdf1, in lowp float sdf2, in lowp float smoothing)
+{
+ lowp float h = clamp(0.5 - 0.5 * (sdf1 - sdf2) / smoothing, 0.0, 1.0);
+ return mix(sdf1, sdf2, h) + smoothing * h * (1.0 - h);
+}
+
+// Round an sdf shape.
+//
+// \param sdf The sdf shape to round.
+// \param amount The amount of rounding to apply.
+//
+// \return The rounded shape of sdf.
+// Note that rounding happens by basically selecting an isoline of sdf,
+// therefore, the resulting shape may be larger than the input shape.
+lowp float sdf_round(in lowp float sdf, in lowp float amount)
+{
+ return sdf - amount;
+}
+
+// Convert an sdf shape to an outline of its shape.
+//
+// \param sdf The sdf shape to turn into an outline.
+//
+// \return The outline of sdf.
+lowp float sdf_outline(in lowp float sdf)
+{
+ return abs(sdf);
+}
+
+/********************
+ Convenience
+********************/
+
+// A constant to represent a "null" value of an sdf.
+//
+// Since 0 is a point exactly on the outline of an sdf shape, and negative
+// values are inside the shape, this uses a very large positive constant to
+// indicate a value that is really far away from the actual sdf shape.
+const lowp float sdf_null = 99999.0;
+
+// A constant for a default level of smoothing when rendering an sdf.
+//
+// This
+const lowp float sdf_default_smoothing = 0.001;
+
+// Render an sdf shape.
+//
+// This will render the sdf shape on top of whatever source color is input,
+// making sure to apply smoothing if desired.
+//
+// \param sdf The sdf shape to render.
+// \param sourceColor The source color to render on top of.
+// \param sdfColor The color to use for rendering the sdf shape.
+//
+// \return sourceColor with the sdf shape rendered on top.
+lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor)
+{
+ lowp float g = fwidth(sdf);
+ return mix(sourceColor, sdfColor, 1.0 - smoothstep(sdf_default_smoothing - g, sdf_default_smoothing + g, sdf));
+}
+
+// Render an sdf shape.
+//
+// This is an overload of sdf_render(float, vec4, vec4) that allows specifying a
+// smoothing amount.
+//
+// \param smoothing The amount of smoothing to apply to the sdf.
+//
+lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float smoothing)
+{
+ lowp float g = fwidth(sdf);
+ return mix(sourceColor, sdfColor, 1.0 - smoothstep(smoothing - g, smoothing + g, sdf));
+}
+
+// Render an sdf shape alpha-blended onto an existing color.
+//
+// This is an overload of sdf_render(float, vec4, vec4) that allows specifying a
+// blending amount and a smoothing amount.
+//
+// \param alpha The alpha to use for blending.
+// \param smoothing The amount of smoothing to apply to the sdf.
+//
+lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float alpha, in lowp float smoothing)
+{
+ lowp float g = fwidth(sdf);
+ return mix(sourceColor, sdfColor, alpha * (1.0 - smoothstep(smoothing - g, smoothing + g, sdf)));
+}
diff --git a/src/scenegraph/shaders/shaders.qrc b/src/scenegraph/shaders/shaders.qrc
new file mode 100644
--- /dev/null
+++ b/src/scenegraph/shaders/shaders.qrc
@@ -0,0 +1,21 @@
+
+
+
+ header_es.glsl
+ header_desktop.glsl
+ header_desktop_core.glsl
+ sdf.glsl
+ sdf.glsl
+ shadowedrectangle.vert
+ shadowedrectangle.vert
+ shadowedrectangle.frag
+ shadowedrectangle.frag
+ shadowedborderrectangle.frag
+ shadowedborderrectangle.frag
+ shadowedtexture.frag
+ shadowedborderrectangle.frag
+ shadowedbordertexture.frag
+ shadowedborderrectangle.frag
+
+
+
diff --git a/src/scenegraph/shadowedborderrectangle.frag b/src/scenegraph/shaders/shadowedborderrectangle.frag
rename from src/scenegraph/shadowedborderrectangle.frag
rename to src/scenegraph/shaders/shadowedborderrectangle.frag
--- a/src/scenegraph/shadowedborderrectangle.frag
+++ b/src/scenegraph/shaders/shadowedborderrectangle.frag
@@ -4,10 +4,7 @@
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
-#line 7
-
-// This is based on the 2D SDF functions provided by Inigo Quilez:
-// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
+// See sdf.glsl for the SDF related functions.
// This shader renders a rectangle with rounded corners and a shadow below it.
// In addition it renders a border around it.
@@ -22,30 +19,14 @@
uniform lowp float borderWidth;
uniform lowp vec4 borderColor;
+#ifdef CORE_PROFILE
+in lowp vec2 uv;
+out lowp vec4 out_color;
+#else
varying lowp vec2 uv;
+#endif
const lowp float minimum_shadow_radius = 0.05;
-const lowp float smoothing = 0.001;
-
-// Calculate the distance to a rectangle with rounded corners.
-// \param point The point to calculate the distance of.
-// \param rect The rectangle to calculate the distance of.
-// \param translation The amount of translation to apply to the rectangle.
-// \param radius A vec4 with the radius of each corner. Order is top right, bottom right, top left, bottom left.
-lowp float sdf_rounded_rectangle(in lowp vec2 point, in lowp vec2 rect, in lowp vec2 translation, in lowp vec4 radius)
-{
- radius.xy = (point.x > 0.0) ? radius.xy : radius.zw;
- radius.x = (point.y > 0.0) ? radius.x : radius.y;
- lowp vec2 d = abs(point - translation) - rect + radius.x;
- return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius.x;
-}
-
-// Render an sdf value into a color.
-lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float sdfAlpha)
-{
- lowp float g = fwidth(sdf);
- return mix(sourceColor, sdfColor, sdfAlpha * (1.0 - smoothstep(smoothing - g, smoothing + g, sdf)));
-}
void main()
{
@@ -56,39 +37,42 @@
// We want to account for size in regards to shadow radius, so that a larger shadow is
// more rounded, but only if we are not already rounding the corners due to corner radius.
lowp float size_factor = 0.5 * (minimum_shadow_radius / max(radius, minimum_shadow_radius));
-
lowp float shadow_radius = radius + size * size_factor;
lowp vec4 col = vec4(0.0);
// Calculate the shadow's distance field.
- lowp float shadow = sdf_rounded_rectangle(uv, aspect * inverse_scale, offset * 2.0 * inverse_scale, vec4(shadow_radius * inverse_scale));
+ lowp float shadow = sdf_rounded_rectangle(uv - offset * 2.0 * inverse_scale, aspect * inverse_scale, vec4(shadow_radius * inverse_scale));
// Render it, interpolating the color over the distance.
col = mix(col, shadowColor * sign(size), 1.0 - smoothstep(-size * 0.5, size * 0.5, shadow));
// Scale corrected corner radius
lowp vec4 corner_radius = vec4(radius * inverse_scale);
// Calculate the outer rectangle distance field.
- lowp float outer_rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, vec2(0.0), corner_radius);
+ lowp float outer_rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, corner_radius);
// First, remove anything that was rendered by the shadow if it is inside the rectangle.
// This allows us to use colors with alpha without rendering artifacts.
- col = sdf_render(outer_rect, col, vec4(0.0), 1.0);
+ col = sdf_render(outer_rect, col, vec4(0.0));
// Then, render it again but this time with the proper color and properly alpha blended.
- col = sdf_render(outer_rect, col, borderColor, 1.0);
+ col = sdf_render(outer_rect, col, borderColor);
// Calculate the inner rectangle distance field.
// This uses a reduced corner radius because the inner corners need to be smaller than the outer corners.
lowp vec4 inner_radius = vec4((radius - borderWidth * 2.0) * inverse_scale);
- lowp float inner_rect = sdf_rounded_rectangle(uv, (aspect - borderWidth * 2.0) * inverse_scale, vec2(0.0), inner_radius);
+ lowp float inner_rect = sdf_rounded_rectangle(uv, (aspect - borderWidth * 2.0) * inverse_scale, inner_radius);
// Like above, but this time cut out the inner rectangle.
- col = sdf_render(inner_rect, col, vec4(0.0), 1.0);
+ col = sdf_render(inner_rect, col, vec4(0.0));
// Finally, render the inner rectangle.
- col = sdf_render(inner_rect, col, color, 1.0);
+ col = sdf_render(inner_rect, col, color);
+#ifdef CORE_PROFILE
+ out_color = col * opacity;
+#else
gl_FragColor = col * opacity;
+#endif
}
diff --git a/src/scenegraph/shadowedborderrectangle_core.frag b/src/scenegraph/shaders/shadowedbordertexture.frag
rename from src/scenegraph/shadowedborderrectangle_core.frag
rename to src/scenegraph/shaders/shadowedbordertexture.frag
--- a/src/scenegraph/shadowedborderrectangle_core.frag
+++ b/src/scenegraph/shaders/shadowedbordertexture.frag
@@ -4,10 +4,7 @@
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
-#line 7
-
-// This is based on the 2D SDF functions provided by Inigo Quilez:
-// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
+// See sdf.glsl for the SDF related functions.
// This shader renders a rectangle with rounded corners and a shadow below it.
// In addition it renders a border around it.
@@ -21,32 +18,25 @@
uniform lowp vec2 aspect;
uniform lowp float borderWidth;
uniform lowp vec4 borderColor;
+uniform sampler2D textureSource;
+#ifdef CORE_PROFILE
in lowp vec2 uv;
-
out lowp vec4 out_color;
+#else
+varying lowp vec2 uv;
+#endif
const lowp float minimum_shadow_radius = 0.05;
-const lowp float smoothing = 0.001;
-
-// Calculate the distance to a rectangle with rounded corners.
-// \param point The point to calculate the distance of.
-// \param rect The rectangle to calculate the distance of.
-// \param translation The amount of translation to apply to the rectangle.
-// \param radius A vec4 with the radius of each corner. Order is top right, bottom right, top left, bottom left.
-lowp float sdf_rounded_rectangle(in lowp vec2 point, in lowp vec2 rect, in lowp vec2 translation, in lowp vec4 radius)
-{
- radius.xy = (point.x > 0.0) ? radius.xy : radius.zw;
- radius.x = (point.y > 0.0) ? radius.x : radius.y;
- lowp vec2 d = abs(point - translation) - rect + radius.x;
- return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius.x;
-}
-// Render an sdf value into a color.
-lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float sdfAlpha)
+// Tiny abstraction around texture() to deal with Core profile differences.
+lowp vec4 sample_texture(in sampler2D texture_source, in lowp vec2 uv)
{
- lowp float g = fwidth(sdf);
- return mix(sourceColor, sdfColor, sdfAlpha * (1.0 - smoothstep(smoothing - g, smoothing + g, sdf)));
+#ifdef CORE_PROFILE
+ return texture(texture_source, uv);
+#else
+ return texture2D(texture_source, uv);
+#endif
}
void main()
@@ -58,39 +48,49 @@
// We want to account for size in regards to shadow radius, so that a larger shadow is
// more rounded, but only if we are not already rounding the corners due to corner radius.
lowp float size_factor = 0.5 * (minimum_shadow_radius / max(radius, minimum_shadow_radius));
-
lowp float shadow_radius = radius + size * size_factor;
lowp vec4 col = vec4(0.0);
// Calculate the shadow's distance field.
- lowp float shadow = sdf_rounded_rectangle(uv, aspect * inverse_scale, offset * 2.0 * inverse_scale, vec4(shadow_radius * inverse_scale));
+ lowp float shadow = sdf_rounded_rectangle(uv - offset * 2.0 * inverse_scale, aspect * inverse_scale, vec4(shadow_radius * inverse_scale));
// Render it, interpolating the color over the distance.
col = mix(col, shadowColor * sign(size), 1.0 - smoothstep(-size * 0.5, size * 0.5, shadow));
// Scale corrected corner radius
lowp vec4 corner_radius = vec4(radius * inverse_scale);
// Calculate the outer rectangle distance field.
- lowp float outer_rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, vec2(0.0), corner_radius);
+ lowp float outer_rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, corner_radius);
// First, remove anything that was rendered by the shadow if it is inside the rectangle.
// This allows us to use colors with alpha without rendering artifacts.
- col = sdf_render(outer_rect, col, vec4(0.0), 1.0);
+ col = sdf_render(outer_rect, col, vec4(0.0));
// Then, render it again but this time with the proper color and properly alpha blended.
- col = sdf_render(outer_rect, col, borderColor, 1.0);
+ col = sdf_render(outer_rect, col, borderColor);
// Calculate the inner rectangle distance field.
// This uses a reduced corner radius because the inner corners need to be smaller than the outer corners.
lowp vec4 inner_radius = vec4((radius - borderWidth * 2.0) * inverse_scale);
- lowp float inner_rect = sdf_rounded_rectangle(uv, (aspect - borderWidth * 2.0) * inverse_scale, vec2(0.0), inner_radius);
+ lowp float inner_rect = sdf_rounded_rectangle(uv, (aspect - borderWidth * 2.0) * inverse_scale, inner_radius);
// Like above, but this time cut out the inner rectangle.
- col = sdf_render(inner_rect, col, vec4(0.0), 1.0);
+ col = sdf_render(inner_rect, col, vec4(0.0));
// Finally, render the inner rectangle.
- col = sdf_render(inner_rect, col, color, 1.0);
+ col = sdf_render(inner_rect, col, color);
+
+ inner_rect = sdf_rounded_rectangle(uv, (aspect - borderWidth * 2.0 + 0.005) * inverse_scale, inner_radius);
+
+ // Sample the texture, then blend it on top of the background color.
+ lowp vec2 texture_uv = ((uv / aspect) + (1.0 * inverse_scale)) / (2.0 * inverse_scale);
+ lowp vec4 texture_color = sample_texture(textureSource, texture_uv);
+ col = sdf_render(inner_rect, col, texture_color, texture_color.a, sdf_default_smoothing / 2.0);
+#ifdef CORE_PROFILE
out_color = col * opacity;
+#else
+ gl_FragColor = col * opacity;
+#endif
}
diff --git a/src/scenegraph/shadowedrectangle_core.frag b/src/scenegraph/shaders/shadowedrectangle.frag
rename from src/scenegraph/shadowedrectangle_core.frag
rename to src/scenegraph/shaders/shadowedrectangle.frag
--- a/src/scenegraph/shadowedrectangle_core.frag
+++ b/src/scenegraph/shaders/shadowedrectangle.frag
@@ -4,8 +4,7 @@
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
-// This is based on the 2D SDF functions provided by Inigo Quilez:
-// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
+// See sdf.glsl for the SDF related functions.
// This shader renders a rectangle with rounded corners and a shadow below it.
@@ -17,25 +16,15 @@
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
+#ifdef CORE_PROFILE
in lowp vec2 uv;
-
out lowp vec4 out_color;
+#else
+varying lowp vec2 uv;
+#endif
const lowp float minimum_shadow_radius = 0.05;
-// Calculate the distance to a rectangle with rounded corners.
-// \param point The point to calculate the distance of.
-// \param rect The rectangle to calculate the distance of.
-// \param translation The amount of translation to apply to the rectangle.
-// \param radius A vec4 with the radius of each corner. Order is top right, bottom right, top left, bottom left.
-lowp float sdf_rounded_rectangle(in lowp vec2 point, in lowp vec2 rect, in lowp vec2 translation, in lowp vec4 radius)
-{
- radius.xy = (point.x > 0.0) ? radius.xy : radius.zw;
- radius.x = (point.y > 0.0) ? radius.x : radius.y;
- lowp vec2 d = abs(point - translation) - rect + radius.x;
- return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius.x;
-}
-
void main()
{
// Scaling factor that is the inverse of the amount of scaling applied to the geometry.
@@ -45,27 +34,28 @@
// We want to account for size in regards to shadow radius, so that a larger shadow is
// more rounded, but only if we are not already rounding the corners due to corner radius.
lowp float size_factor = 0.5 * (minimum_shadow_radius / max(radius, minimum_shadow_radius));
-
- lowp float shadowRadius = radius + size * size_factor;
+ lowp float shadow_radius = radius + size * size_factor;
lowp vec4 col = vec4(0.0);
// Calculate the shadow's distance field.
- lowp float shadow = sdf_rounded_rectangle(uv, aspect * inverse_scale, offset * 2.0 * inverse_scale, vec4(shadowRadius * inverse_scale));
+ lowp float shadow = sdf_rounded_rectangle(uv - offset * 2.0 * inverse_scale, aspect * inverse_scale, vec4(shadow_radius * inverse_scale));
// Render it, interpolating the color over the distance.
col = mix(col, shadowColor * sign(size), 1.0 - smoothstep(-size * 0.5, size * 0.5, shadow));
// Calculate the main rectangle distance field.
- lowp float rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, vec2(0.0), vec4(radius * inverse_scale));
-
- lowp float g = fwidth(rect);
+ lowp float rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, vec4(radius * inverse_scale));
// First, remove anything that was rendered by the shadow if it is inside the rectangle.
// This allows us to use colors with alpha without rendering artifacts.
- col = mix(col, vec4(0.0), 1.0 - smoothstep(0.001 - g, 0.001 + g, rect));
+ col = sdf_render(rect, col, vec4(0.0));
// Then, render it again but this time with the proper color and properly alpha blended.
- col = mix(col, color, 1.0 - smoothstep(0.001 - g, 0.001 + g, rect));
+ col = sdf_render(rect, col, color);
+#ifdef CORE_PROFILE
out_color = col * opacity;
+#else
+ gl_FragColor = col * opacity;
+#endif
}
diff --git a/src/scenegraph/shadowedrectangle.vert b/src/scenegraph/shaders/shadowedrectangle.vert
rename from src/scenegraph/shadowedrectangle.vert
rename to src/scenegraph/shaders/shadowedrectangle.vert
--- a/src/scenegraph/shadowedrectangle.vert
+++ b/src/scenegraph/shaders/shadowedrectangle.vert
@@ -6,12 +6,16 @@
uniform highp mat4 matrix;
uniform lowp vec2 aspect;
-uniform lowp vec2 offset;
+#ifdef CORE_PROFILE
+in highp vec4 in_vertex;
+in mediump vec2 in_uv;
+out mediump vec2 uv;
+#else
attribute highp vec4 in_vertex;
attribute mediump vec2 in_uv;
-
varying mediump vec2 uv;
+#endif
void main() {
uv = (-1.0 + 2.0 * in_uv) * aspect;
diff --git a/src/scenegraph/shadowedrectangle.frag b/src/scenegraph/shaders/shadowedtexture.frag
rename from src/scenegraph/shadowedrectangle.frag
rename to src/scenegraph/shaders/shadowedtexture.frag
--- a/src/scenegraph/shadowedrectangle.frag
+++ b/src/scenegraph/shaders/shadowedtexture.frag
@@ -4,34 +4,37 @@
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
-// This is based on the 2D SDF functions provided by Inigo Quilez:
-// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
+// See sdf.glsl for the SDF related functions.
-// This shader renders a rectangle with rounded corners and a shadow below it.
+// This shader renders a texture on top of a rectangle with rounded corners and
+// a shadow below it.
uniform lowp float opacity;
uniform lowp float size;
uniform lowp float radius;
uniform lowp vec4 color;
uniform lowp vec4 shadowColor;
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
+uniform sampler2D textureSource;
+#ifdef CORE_PROFILE
+in lowp vec2 uv;
+out lowp vec4 out_color;
+#else
varying lowp vec2 uv;
+#endif
const lowp float minimum_shadow_radius = 0.05;
-// Calculate the distance to a rectangle with rounded corners.
-// \param point The point to calculate the distance of.
-// \param rect The rectangle to calculate the distance of.
-// \param translation The amount of translation to apply to the rectangle.
-// \param radius A vec4 with the radius of each corner. Order is top right, bottom right, top left, bottom left.
-lowp float sdf_rounded_rectangle(in lowp vec2 point, in lowp vec2 rect, in lowp vec2 translation, in lowp vec4 radius)
+// Tiny abstraction around texture() to deal with Core profile differences.
+lowp vec4 sample_texture(in sampler2D texture_source, in lowp vec2 uv)
{
- radius.xy = (point.x > 0.0) ? radius.xy : radius.zw;
- radius.x = (point.y > 0.0) ? radius.x : radius.y;
- lowp vec2 d = abs(point - translation) - rect + radius.x;
- return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius.x;
+#ifdef CORE_PROFILE
+ return texture(texture_source, uv);
+#else
+ return texture2D(texture_source, uv);
+#endif
}
void main()
@@ -43,27 +46,34 @@
// We want to account for size in regards to shadow radius, so that a larger shadow is
// more rounded, but only if we are not already rounding the corners due to corner radius.
lowp float size_factor = 0.5 * (minimum_shadow_radius / max(radius, minimum_shadow_radius));
-
- lowp float shadowRadius = radius + size * size_factor;
+ lowp float shadow_radius = radius + size * size_factor;
lowp vec4 col = vec4(0.0);
// Calculate the shadow's distance field.
- lowp float shadow = sdf_rounded_rectangle(uv, aspect * inverse_scale, offset * 2.0 * inverse_scale, vec4(shadowRadius * inverse_scale));
+ lowp float shadow = sdf_rounded_rectangle(uv - offset * 2.0 * inverse_scale, aspect * inverse_scale, vec4(shadow_radius * inverse_scale));
// Render it, interpolating the color over the distance.
col = mix(col, shadowColor * sign(size), 1.0 - smoothstep(-size * 0.5, size * 0.5, shadow));
// Calculate the main rectangle distance field.
- lowp float rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, vec2(0.0), vec4(radius * inverse_scale));
-
- lowp float g = fwidth(rect);
+ lowp float rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, vec4(radius * inverse_scale));
// First, remove anything that was rendered by the shadow if it is inside the rectangle.
// This allows us to use colors with alpha without rendering artifacts.
- col = mix(col, vec4(0.0), 1.0 - smoothstep(0.001 - g, 0.001 + g, rect));
+ col = sdf_render(rect, col, vec4(0.0));
+
+ // Then, render it again but this time with the proper color.
+ col = sdf_render(rect, col, color);
- // Then, render it again but this time with the proper color and properly alpha blended.
- col = mix(col, color, 1.0 - smoothstep(0.001 - g, 0.001 + g, rect));
+ // Sample the texture, then blend it on top of the background color.
+ lowp vec2 texture_uv = ((uv / aspect) + (1.0 * inverse_scale)) / (2.0 * inverse_scale);
+ lowp vec4 texture_color = sample_texture(textureSource, texture_uv);
+ col = sdf_render(rect, col, texture_color, texture_color.a, sdf_default_smoothing);
+// col = sdf_render(rect, col, vec4(texture_uv / inverse_scale, sign(texture_uv).x, 1.0));
+#ifdef CORE_PROFILE
+ out_color = col * opacity;
+#else
gl_FragColor = col * opacity;
+#endif
}
diff --git a/src/scenegraph/shadowedborderrectanglematerial.cpp b/src/scenegraph/shadowedborderrectanglematerial.cpp
--- a/src/scenegraph/shadowedborderrectanglematerial.cpp
+++ b/src/scenegraph/shadowedborderrectanglematerial.cpp
@@ -48,6 +48,7 @@
setShaderSourceFiles(QOpenGLShader::Fragment, {
shaderRoot + header,
+ shaderRoot + QStringLiteral("sdf.glsl"),
shaderRoot + QStringLiteral("shadowedborderrectangle.frag")
});
}
diff --git a/src/scenegraph/shadowedbordertexturematerial.h b/src/scenegraph/shadowedbordertexturematerial.h
new file mode 100644
--- /dev/null
+++ b/src/scenegraph/shadowedbordertexturematerial.h
@@ -0,0 +1,34 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#pragma once
+
+#include
+
+#include "shadowedborderrectanglematerial.h"
+
+class ShadowedBorderTextureMaterial : public ShadowedBorderRectangleMaterial
+{
+public:
+ ShadowedBorderTextureMaterial();
+
+ QSGMaterialShader* createShader() const override;
+ QSGMaterialType* type() const override;
+ int compare(const QSGMaterial* other) const override;
+
+ QSGTexture *textureSource = nullptr;
+
+ static QSGMaterialType staticType;
+};
+
+class ShadowedBorderTextureShader : public ShadowedBorderRectangleShader
+{
+public:
+ ShadowedBorderTextureShader();
+
+ void initialize() override;
+ void updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
diff --git a/src/scenegraph/shadowedbordertexturematerial.cpp b/src/scenegraph/shadowedbordertexturematerial.cpp
new file mode 100644
--- /dev/null
+++ b/src/scenegraph/shadowedbordertexturematerial.cpp
@@ -0,0 +1,74 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#include "shadowedbordertexturematerial.h"
+
+#include
+
+QSGMaterialType ShadowedBorderTextureMaterial::staticType;
+
+ShadowedBorderTextureMaterial::ShadowedBorderTextureMaterial()
+ : ShadowedBorderRectangleMaterial()
+{
+ setFlag(QSGMaterial::Blending, true);
+}
+
+QSGMaterialShader* ShadowedBorderTextureMaterial::createShader() const
+{
+ return new ShadowedBorderTextureShader{};
+}
+
+QSGMaterialType* ShadowedBorderTextureMaterial::type() const
+{
+ return &staticType;
+}
+
+int ShadowedBorderTextureMaterial::compare(const QSGMaterial *other) const
+{
+ auto material = static_cast(other);
+
+ auto result = ShadowedBorderRectangleMaterial::compare(other);
+ if (result == 0
+ && material->textureSource == textureSource) {
+ return 0;
+ }
+
+ return result;
+}
+
+ShadowedBorderTextureShader::ShadowedBorderTextureShader()
+{
+ auto header = QOpenGLContext::currentContext()->isOpenGLES() ? QStringLiteral("header_es.glsl") : QStringLiteral("header_desktop.glsl");
+
+ auto shaderRoot = QStringLiteral(":/org/kde/kirigami/shaders/");
+
+ setShaderSourceFiles(QOpenGLShader::Vertex, {
+ shaderRoot + header,
+ shaderRoot + QStringLiteral("shadowedrectangle.vert")
+ });
+
+ setShaderSourceFiles(QOpenGLShader::Fragment, {
+ shaderRoot + header,
+ shaderRoot + QStringLiteral("sdf.glsl"),
+ shaderRoot + QStringLiteral("shadowedbordertexture.frag")
+ });
+}
+
+void ShadowedBorderTextureShader::initialize()
+{
+ ShadowedBorderRectangleShader::initialize();
+ program()->setUniformValue("textureSource", 0);
+}
+
+void ShadowedBorderTextureShader::updateState(const QSGMaterialShader::RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial)
+{
+ ShadowedBorderRectangleShader::updateState(state, newMaterial, oldMaterial);
+
+ auto texture = static_cast(newMaterial)->textureSource;
+ if (texture) {
+ texture->bind();
+ }
+}
diff --git a/src/scenegraph/shadowedrectangle_core.vert b/src/scenegraph/shadowedrectangle_core.vert
deleted file mode 100644
--- a/src/scenegraph/shadowedrectangle_core.vert
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
- *
- * SPDX-License-Identifier: LGPL-2.0-or-later
- */
-
-uniform mat4 matrix;
-uniform vec2 aspect;
-uniform vec2 offset;
-
-in vec4 in_vertex;
-in vec2 in_uv;
-
-out vec2 uv;
-
-void main() {
- uv = (-1.0 + 2.0 * in_uv) * aspect;
- gl_Position = matrix * in_vertex;
-}
diff --git a/src/scenegraph/shadowedrectanglematerial.cpp b/src/scenegraph/shadowedrectanglematerial.cpp
--- a/src/scenegraph/shadowedrectanglematerial.cpp
+++ b/src/scenegraph/shadowedrectanglematerial.cpp
@@ -54,6 +54,7 @@
setShaderSourceFiles(QOpenGLShader::Fragment, {
shaderRoot + header,
+ shaderRoot + QStringLiteral("sdf.glsl"),
shaderRoot + QStringLiteral("shadowedrectangle.frag")
});
}
diff --git a/src/scenegraph/shadowedrectanglenode.h b/src/scenegraph/shadowedrectanglenode.h
--- a/src/scenegraph/shadowedrectanglenode.h
+++ b/src/scenegraph/shadowedrectanglenode.h
@@ -10,7 +10,9 @@
#include
#include
+class QSGMaterialType;
class ShadowedRectangleMaterial;
+class ShadowedBorderRectangleMaterial;
/**
* Scene graph node for a shadowed rectangle.
@@ -53,10 +55,16 @@
*/
void updateGeometry();
-private:
+protected:
+ virtual ShadowedRectangleMaterial *createBorderlessMaterial();
+ virtual ShadowedBorderRectangleMaterial *createBorderMaterial();
+ virtual QSGMaterialType* borderMaterialType();
+ virtual QSGMaterialType* borderlessMaterialType();
+
QSGGeometry *m_geometry;
- ShadowedRectangleMaterial *m_material;
+ ShadowedRectangleMaterial *m_material = nullptr;
+private:
QRectF m_rect;
qreal m_size = 0.0;
qreal m_radius = 0.0;
diff --git a/src/scenegraph/shadowedrectanglenode.cpp b/src/scenegraph/shadowedrectanglenode.cpp
--- a/src/scenegraph/shadowedrectanglenode.cpp
+++ b/src/scenegraph/shadowedrectanglenode.cpp
@@ -23,9 +23,6 @@
m_geometry = new QSGGeometry{QSGGeometry::defaultAttributes_TexturedPoint2D(), 4};
setGeometry(m_geometry);
- m_material = new ShadowedRectangleMaterial{};
- setMaterial(m_material);
-
setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
}
@@ -37,19 +34,18 @@
// switch to the with-border material. Otherwise use the no-border version.
if (enabled) {
- if (m_material->type() == &ShadowedRectangleMaterial::staticType) {
- auto newMaterial = new ShadowedBorderRectangleMaterial();
+ if (!m_material || m_material->type() == borderlessMaterialType()) {
+ auto newMaterial = createBorderMaterial();
setMaterial(newMaterial);
m_material = newMaterial;
m_rect = QRectF{};
markDirty(QSGNode::DirtyMaterial);
}
} else {
- if (m_material->type() == &ShadowedBorderRectangleMaterial::staticType) {
- auto newMaterial = new ShadowedRectangleMaterial();
+ if (!m_material || m_material->type() == borderMaterialType()) {
+ auto newMaterial = createBorderlessMaterial();
setMaterial(newMaterial);
m_material = newMaterial;
- m_material->aspect = m_aspect;
m_rect = QRectF{};
markDirty(QSGNode::DirtyMaterial);
}
@@ -134,7 +130,7 @@
void ShadowedRectangleNode::setBorderWidth(qreal width)
{
- if (m_material->type() != &ShadowedBorderRectangleMaterial::staticType) {
+ if (m_material->type() != borderMaterialType()) {
return;
}
@@ -151,7 +147,7 @@
void ShadowedRectangleNode::setBorderColor(const QColor& color)
{
- if (m_material->type() != &ShadowedBorderRectangleMaterial::staticType) {
+ if (m_material->type() != borderMaterialType()) {
return;
}
@@ -176,3 +172,23 @@
QSGGeometry::updateTexturedRectGeometry(m_geometry, rect, QRectF{0.0, 0.0, 1.0, 1.0});
markDirty(QSGNode::DirtyGeometry);
}
+
+ShadowedRectangleMaterial *ShadowedRectangleNode::createBorderlessMaterial()
+{
+ return new ShadowedRectangleMaterial{};
+}
+
+ShadowedBorderRectangleMaterial *ShadowedRectangleNode::createBorderMaterial()
+{
+ return new ShadowedBorderRectangleMaterial{};
+}
+
+QSGMaterialType *ShadowedRectangleNode::borderlessMaterialType()
+{
+ return &ShadowedRectangleMaterial::staticType;
+}
+
+QSGMaterialType *ShadowedRectangleNode::borderMaterialType()
+{
+ return &ShadowedBorderRectangleMaterial::staticType;
+}
diff --git a/src/scenegraph/shadowedtexturematerial.h b/src/scenegraph/shadowedtexturematerial.h
new file mode 100644
--- /dev/null
+++ b/src/scenegraph/shadowedtexturematerial.h
@@ -0,0 +1,40 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#pragma once
+
+#include
+
+#include "shadowedrectanglematerial.h"
+
+/**
+ * A material rendering a rectangle with a shadow.
+ *
+ * This material uses a distance field shader to render a rectangle with a
+ * shadow below it, optionally with rounded corners.
+ */
+class ShadowedTextureMaterial : public ShadowedRectangleMaterial
+{
+public:
+ ShadowedTextureMaterial();
+
+ QSGMaterialShader* createShader() const override;
+ QSGMaterialType* type() const override;
+ int compare(const QSGMaterial* other) const override;
+
+ QSGTexture *textureSource = nullptr;
+
+ static QSGMaterialType staticType;
+};
+
+class ShadowedTextureShader : public ShadowedRectangleShader
+{
+public:
+ ShadowedTextureShader();
+
+ void initialize() override;
+ void updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
+};
diff --git a/src/scenegraph/shadowedtexturematerial.cpp b/src/scenegraph/shadowedtexturematerial.cpp
new file mode 100644
--- /dev/null
+++ b/src/scenegraph/shadowedtexturematerial.cpp
@@ -0,0 +1,74 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#include "shadowedtexturematerial.h"
+
+#include
+
+QSGMaterialType ShadowedTextureMaterial::staticType;
+
+ShadowedTextureMaterial::ShadowedTextureMaterial()
+ : ShadowedRectangleMaterial()
+{
+ setFlag(QSGMaterial::Blending, true);
+}
+
+QSGMaterialShader* ShadowedTextureMaterial::createShader() const
+{
+ return new ShadowedTextureShader{};
+}
+
+QSGMaterialType* ShadowedTextureMaterial::type() const
+{
+ return &staticType;
+}
+
+int ShadowedTextureMaterial::compare(const QSGMaterial *other) const
+{
+ auto material = static_cast(other);
+
+ auto result = ShadowedRectangleMaterial::compare(other);
+ if (result == 0
+ && material->textureSource == textureSource) {
+ return 0;
+ }
+
+ return result;
+}
+
+ShadowedTextureShader::ShadowedTextureShader()
+{
+ auto header = QOpenGLContext::currentContext()->isOpenGLES() ? QStringLiteral("header_es.glsl") : QStringLiteral("header_desktop.glsl");
+
+ auto shaderRoot = QStringLiteral(":/org/kde/kirigami/shaders/");
+
+ setShaderSourceFiles(QOpenGLShader::Vertex, {
+ shaderRoot + header,
+ shaderRoot + QStringLiteral("shadowedrectangle.vert")
+ });
+
+ setShaderSourceFiles(QOpenGLShader::Fragment, {
+ shaderRoot + header,
+ shaderRoot + QStringLiteral("sdf.glsl"),
+ shaderRoot + QStringLiteral("shadowedtexture.frag")
+ });
+}
+
+void ShadowedTextureShader::initialize()
+{
+ ShadowedRectangleShader::initialize();
+ program()->setUniformValue("textureSource", 0);
+}
+
+void ShadowedTextureShader::updateState(const QSGMaterialShader::RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial)
+{
+ ShadowedRectangleShader::updateState(state, newMaterial, oldMaterial);
+
+ auto texture = static_cast(newMaterial)->textureSource;
+ if (texture) {
+ texture->bind();
+ }
+}
diff --git a/src/scenegraph/shadowedtexturenode.h b/src/scenegraph/shadowedtexturenode.h
new file mode 100644
--- /dev/null
+++ b/src/scenegraph/shadowedtexturenode.h
@@ -0,0 +1,42 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#pragma once
+
+#include
+#include
+
+#include "shadowedrectanglenode.h"
+#include "shadowedtexturematerial.h"
+
+/**
+ * Scene graph node for a shadowed texture source.
+ *
+ * This node will set up the geometry and materials for a shadowed rectangle,
+ * optionally with rounded corners, using a supplied texture source as the color
+ * for the rectangle.
+ *
+ * \note You must call updateGeometry() after setting properties of this node,
+ * otherwise the node's state will not correctly reflect all the properties.
+ *
+ * \sa ShadowedTexture
+ */
+class ShadowedTextureNode : public ShadowedRectangleNode
+{
+public:
+ ShadowedTextureNode();
+
+ void setTextureSource(QSGTextureProvider *source);
+ void preprocess() override;
+
+private:
+ ShadowedRectangleMaterial *createBorderlessMaterial() override;
+ ShadowedBorderRectangleMaterial *createBorderMaterial() override;
+ QSGMaterialType *borderlessMaterialType() override;
+ QSGMaterialType *borderMaterialType() override;
+
+ QPointer m_textureSource;
+};
diff --git a/src/scenegraph/shadowedtexturenode.cpp b/src/scenegraph/shadowedtexturenode.cpp
new file mode 100644
--- /dev/null
+++ b/src/scenegraph/shadowedtexturenode.cpp
@@ -0,0 +1,81 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#include "shadowedtexturenode.h"
+
+#include
+#include "shadowedtexturematerial.h"
+#include "shadowedbordertexturematerial.h"
+
+template
+inline void preprocessTexture(QSGMaterial *material, QSGTextureProvider *provider)
+{
+ auto m = static_cast(material);
+ // Since we handle texture coordinates differently in the shader, we
+ // need to remove the texture from the atlas for now.
+ if (provider->texture()->isAtlasTexture()) {
+ // Blegh, I have no idea why "removedFromAtlas" doesn't just return
+ // the texture when it's not an atlas.
+ m->textureSource = provider->texture()->removedFromAtlas();
+ } else {
+ m->textureSource = provider->texture();
+ }
+ if (QSGDynamicTexture *dynamic_texture = qobject_cast(m->textureSource)) {
+ dynamic_texture->updateTexture();
+ }
+}
+
+ShadowedTextureNode::ShadowedTextureNode()
+ : ShadowedRectangleNode()
+{
+ setFlag(QSGNode::UsePreprocess);
+}
+
+void ShadowedTextureNode::setTextureSource(QSGTextureProvider *source)
+{
+ if (m_textureSource == source) {
+ return;
+ }
+
+ if (m_textureSource) {
+ m_textureSource->disconnect();
+ }
+
+ m_textureSource = source;
+ QObject::connect(m_textureSource.data(), &QSGTextureProvider::textureChanged, [this]() { markDirty(QSGNode::DirtyMaterial); });
+ markDirty(QSGNode::DirtyMaterial);
+}
+
+void ShadowedTextureNode::preprocess()
+{
+ if (m_textureSource && m_material) {
+ if (m_material->type() == borderlessMaterialType()) {
+ preprocessTexture(m_material, m_textureSource);
+ } else {
+ preprocessTexture(m_material, m_textureSource);
+ }
+ }
+}
+
+ShadowedRectangleMaterial *ShadowedTextureNode::createBorderlessMaterial()
+{
+ return new ShadowedTextureMaterial{};
+}
+
+ShadowedBorderRectangleMaterial *ShadowedTextureNode::createBorderMaterial()
+{
+ return new ShadowedBorderTextureMaterial{};
+}
+
+QSGMaterialType *ShadowedTextureNode::borderlessMaterialType()
+{
+ return &ShadowedTextureMaterial::staticType;
+}
+
+QSGMaterialType *ShadowedTextureNode::borderMaterialType()
+{
+ return &ShadowedBorderTextureMaterial::staticType;
+}
diff --git a/src/shadowedrectangle.h b/src/shadowedrectangle.h
--- a/src/shadowedrectangle.h
+++ b/src/shadowedrectangle.h
@@ -41,6 +41,11 @@
Q_SIGNAL void changed();
+ inline bool isEnabled() const
+ {
+ return !qFuzzyIsNull(m_width);
+ }
+
private:
qreal m_width = 0.0;
QColor m_color = Qt::black;
@@ -110,6 +115,8 @@
* using distance fields, which provide greatly improved performance. The shadow is
* rendered outside of the item's bounds, so the item's width and height are the
* rectangle's width and height.
+ *
+ * @since 5.69 / 2.12
*/
class ShadowedRectangle : public QQuickItem
{
@@ -158,7 +165,7 @@
void componentComplete() override;
protected:
- void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value);
+ void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override;
QSGNode *updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data) override;
private:
diff --git a/src/shadowedrectangle.cpp b/src/shadowedrectangle.cpp
--- a/src/shadowedrectangle.cpp
+++ b/src/shadowedrectangle.cpp
@@ -175,6 +175,35 @@
checkSoftwareItem();
}
+void ShadowedRectangle::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+{
+ if (change == QQuickItem::ItemSceneChange && value.window) {
+ checkSoftwareItem();
+ }
+}
+
+QSGNode *ShadowedRectangle::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data)
+{
+ Q_UNUSED(data);
+
+ if (!node) {
+ node = new ShadowedRectangleNode{};
+ }
+
+ auto shadowNode = static_cast(node);
+ shadowNode->setBorderEnabled(m_border->isEnabled());
+ shadowNode->setRect(boundingRect());
+ shadowNode->setSize(m_shadow->size());
+ shadowNode->setRadius(m_radius);
+ shadowNode->setOffset(QVector2D{float(m_shadow->xOffset()), float(m_shadow->yOffset())});
+ shadowNode->setColor(m_color);
+ shadowNode->setShadowColor(m_shadow->color());
+ shadowNode->setBorderWidth(m_border->width());
+ shadowNode->setBorderColor(m_border->color());
+ shadowNode->updateGeometry();
+ return shadowNode;
+}
+
void ShadowedRectangle::checkSoftwareItem()
{
if (!m_softwareItem && window() && window()->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
@@ -202,33 +231,3 @@
setFlag(QQuickItem::ItemHasContents, false);
}
}
-
-void ShadowedRectangle::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
-{
- if (change == QQuickItem::ItemSceneChange && value.window) {
- checkSoftwareItem();
- }
-}
-
-QSGNode *ShadowedRectangle::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data)
-{
- Q_UNUSED(data);
-
- if (!node) {
- node = new ShadowedRectangleNode;
- }
-
- auto elevatedNode = static_cast(node);
- elevatedNode->setBorderEnabled(!qFuzzyIsNull(m_border->width()));
- elevatedNode->setRect(boundingRect());
- elevatedNode->setSize(m_shadow->size());
- elevatedNode->setRadius(m_radius);
- elevatedNode->setOffset(QVector2D{float(m_shadow->xOffset()), float(m_shadow->yOffset())});
- elevatedNode->setColor(m_color);
- elevatedNode->setShadowColor(m_shadow->color());
- elevatedNode->setBorderWidth(m_border->width());
- elevatedNode->setBorderColor(m_border->color());
- elevatedNode->updateGeometry();
-
- return elevatedNode;
-}
diff --git a/src/shadowedtexture.h b/src/shadowedtexture.h
new file mode 100644
--- /dev/null
+++ b/src/shadowedtexture.h
@@ -0,0 +1,43 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#pragma once
+
+#include "shadowedrectangle.h"
+
+/**
+ * A rectangle with a shadow, using a QQuickItem as texture.
+ *
+ * This item will render a rectangle, with a shadow below it. The rendering is done
+ * using distance fields, which provide greatly improved performance. The shadow is
+ * rendered outside of the item's bounds, so the item's width and height are the
+ * rectangle's width and height.
+ *
+ * @since 5.69 / 2.12
+ */
+class ShadowedTexture : public ShadowedRectangle
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QQuickItem *source READ source WRITE setSource NOTIFY sourceChanged)
+
+public:
+ ShadowedTexture(QQuickItem *parent = nullptr);
+ ~ShadowedTexture() override;
+
+ QQuickItem *source() const;
+ void setSource(QQuickItem *newSource);
+ Q_SIGNAL void sourceChanged();
+
+// void componentComplete() override;
+
+protected:
+// void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override;
+ QSGNode *updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data) override;
+
+private:
+ QQuickItem *m_source = nullptr;
+};
diff --git a/src/shadowedtexture.cpp b/src/shadowedtexture.cpp
new file mode 100644
--- /dev/null
+++ b/src/shadowedtexture.cpp
@@ -0,0 +1,111 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#include "shadowedtexture.h"
+
+#include
+#include
+#include
+
+#include "scenegraph/shadowedtexturenode.h"
+
+ShadowedTexture::ShadowedTexture(QQuickItem *parentItem)
+ : ShadowedRectangle(parentItem)
+{
+}
+
+ShadowedTexture::~ShadowedTexture()
+{
+}
+
+QQuickItem *ShadowedTexture::source() const
+{
+ return m_source;
+}
+
+void ShadowedTexture::setSource(QQuickItem *newSource)
+{
+ if (newSource == m_source) {
+ return;
+ }
+
+ m_source = newSource;
+ if (!m_source->parentItem()) {
+ m_source->setParentItem(this);
+ }
+
+ update();
+ Q_EMIT sourceChanged();
+}
+
+// void ShadowedRectangle::componentComplete()
+// {
+// QQuickItem::componentComplete();
+//
+// checkSoftwareItem();
+// }
+//
+// void ShadowedRectangle::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+// {
+// if (change == QQuickItem::ItemSceneChange && value.window) {
+// checkSoftwareItem();
+// }
+// }
+
+QSGNode *ShadowedTexture::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data)
+{
+ Q_UNUSED(data);
+
+ if (!node) {
+ node = new ShadowedTextureNode{};
+ }
+
+ auto shadowNode = static_cast(node);
+ shadowNode->setBorderEnabled(border()->isEnabled());
+ shadowNode->setRect(boundingRect());
+ shadowNode->setSize(shadow()->size());
+ shadowNode->setRadius(radius());
+ shadowNode->setOffset(QVector2D{float(shadow()->xOffset()), float(shadow()->yOffset())});
+ shadowNode->setColor(color());
+ shadowNode->setShadowColor(shadow()->color());
+ shadowNode->setBorderWidth(border()->width());
+ shadowNode->setBorderColor(border()->color());
+
+ if (m_source) {
+ shadowNode->setTextureSource(m_source->textureProvider());
+ }
+
+ shadowNode->updateGeometry();
+ return shadowNode;
+}
+
+// void ShadowedTexture::checkSoftwareItem()
+// {
+// if (!m_softwareItem && window() && window()->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
+// m_softwareItem = new PaintedRectangleItem{this};
+//
+// auto updateItem = [this]() {
+// auto borderWidth = m_border->width();
+// auto rect = boundingRect().adjusted(-borderWidth / 2, -borderWidth / 2, borderWidth / 2, borderWidth / 2);
+// m_softwareItem->setX(-borderWidth / 2);
+// m_softwareItem->setY(-borderWidth / 2);
+// m_softwareItem->setSize(rect.size());
+// m_softwareItem->setColor(m_color);
+// m_softwareItem->setRadius(m_radius);
+// m_softwareItem->setBorderWidth(borderWidth);
+// m_softwareItem->setBorderColor(m_border->color());
+// };
+//
+// updateItem();
+//
+// connect(this, &ShadowedTexture::widthChanged, m_softwareItem, updateItem);
+// connect(this, &ShadowedTexture::heightChanged, m_softwareItem, updateItem);
+// connect(this, &ShadowedRectangle::colorChanged, m_softwareItem, updateItem);
+// connect(this, &ShadowedRectangle::radiusChanged, m_softwareItem, updateItem);
+// connect(m_border.get(), &BorderGroup::changed, m_softwareItem, updateItem);
+// setFlag(QQuickItem::ItemHasContents, false);
+// }
+// }
diff --git a/tests/ShadowedImageTest.qml b/tests/ShadowedImageTest.qml
new file mode 100644
--- /dev/null
+++ b/tests/ShadowedImageTest.qml
@@ -0,0 +1,83 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Arjen Hiemstra
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+import org.kde.kirigami 2.12 as Kirigami
+
+Kirigami.ApplicationWindow {
+ id: window
+
+ width: 500
+ height: 500
+
+ pageStack.initialPage: Kirigami.Page {
+ leftPadding: 0
+ rightPadding: 0
+ topPadding: 0
+ bottomPadding: 0
+
+ Column {
+ anchors.centerIn: parent
+
+ Kirigami.ShadowedImage {
+ width: 400
+ height: 300
+
+ color: Kirigami.Theme.highlightColor
+
+ radius: radiusSlider.value
+
+ shadow.size: sizeSlider.value
+ shadow.xOffset: xOffsetSlider.value
+ shadow.yOffset: yOffsetSlider.value
+
+ border.width: borderWidthSlider.value
+ border.color: Kirigami.Theme.textColor
+
+ source: "/usr/share/wallpapers/Next/contents/images/1024x768.jpg"
+ }
+
+ Item { width: 1; height: Kirigami.Units.gridUnit }
+
+ Slider {
+ id: sizeSlider
+
+ from: 0
+ to: 100
+ }
+
+ Slider {
+ id: radiusSlider
+
+ from: 0
+ to: 200
+ }
+
+ Slider {
+ id: xOffsetSlider
+
+ from: -100
+ to: 100
+ }
+
+ Slider {
+ id: yOffsetSlider
+
+ from: -100
+ to: 100
+ }
+
+ Slider {
+ id: borderWidthSlider
+
+ from: 0
+ to: 50
+ }
+ }
+ }
+}