From 925bbe7a5e11e772f06e203382203e94b8715b3a Mon Sep 17 00:00:00 2001 From: ItsLemmy Date: Sun, 30 Nov 2025 11:46:18 -0500 Subject: [PATCH] NImageRounded: back to using a custom shader as it looks much better than ClippingRectangle. It seems ClippingRectangle has issues with fractional pixes. --- .../ControlCenter/Cards/ProfileCard.qml | 2 +- .../Panels/SetupWizard/SetupWallpaperStep.qml | 103 +++--------------- Shaders/frag/rounded_image.frag | 93 ++++++++++++---- Shaders/qsb/rounded_image.frag.qsb | Bin 2750 -> 3682 bytes Widgets/NImageRounded.qml | 39 +++++-- 5 files changed, 116 insertions(+), 121 deletions(-) diff --git a/Modules/Panels/ControlCenter/Cards/ProfileCard.qml b/Modules/Panels/ControlCenter/Cards/ProfileCard.qml index f827b506..62ca83e1 100644 --- a/Modules/Panels/ControlCenter/Cards/ProfileCard.qml +++ b/Modules/Panels/ControlCenter/Cards/ProfileCard.qml @@ -32,7 +32,7 @@ NBox { imagePath: Settings.preprocessPath(Settings.data.general.avatarImage) fallbackIcon: "person" borderColor: Color.mPrimary - borderWidth: Style.borderM + borderWidth: Style.borderS * 1.5 } ColumnLayout { diff --git a/Modules/Panels/SetupWizard/SetupWallpaperStep.qml b/Modules/Panels/SetupWizard/SetupWallpaperStep.qml index e453bc7d..c5938b6c 100644 --- a/Modules/Panels/SetupWizard/SetupWallpaperStep.qml +++ b/Modules/Panels/SetupWizard/SetupWallpaperStep.qml @@ -63,101 +63,28 @@ ColumnLayout { } } - // Large preview with rounded corners and shadow effect + // Large preview area Rectangle { Layout.fillWidth: true Layout.fillHeight: true Layout.minimumHeight: 180 color: Color.mSurfaceVariant radius: Style.radiusL - border.color: selectedWallpaper !== "" ? Color.mPrimary : Color.mOutline - border.width: selectedWallpaper !== "" ? 2 : 1 - clip: true - // Mirror WallpaperPanel approach with rounded shader mask - NImageCached { - id: previewCached + // Image with rounded corners + NImageRounded { anchors.fill: parent - anchors.margins: 4 - cacheFolder: Settings.cacheDirImagesWallpapers + visible: selectedWallpaper !== "" imagePath: selectedWallpaper !== "" ? "file://" + selectedWallpaper : "" - visible: false // used as texture source for the shader - } - - ShaderEffect { - anchors.fill: parent - anchors.margins: 4 - property var source: ShaderEffectSource { - sourceItem: previewCached - hideSource: true - live: true - recursive: false - format: ShaderEffectSource.RGBA - } - property real itemWidth: width - property real itemHeight: height - property real cornerRadius: Style.radiusL - property real imageOpacity: 1.0 - fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/rounded_image.frag.qsb") - supportsAtlasTextures: false - blending: true - } - - // Loading placeholder - Rectangle { - anchors.fill: parent - color: Color.mSurfaceVariant radius: Style.radiusL - visible: (previewCached.status === Image.Loading || previewCached.status === Image.Null) && selectedWallpaper !== "" - - NIcon { - icon: "image" - pointSize: Style.fontSizeXXL - color: Color.mOnSurfaceVariant - anchors.centerIn: parent - } - } - - // Error placeholder - Rectangle { - anchors.fill: parent - color: Color.mError - opacity: 0.1 - radius: Style.radiusL - visible: previewCached.status === Image.Error && selectedWallpaper !== "" - - ColumnLayout { - anchors.centerIn: parent - spacing: Style.marginS - - NIcon { - icon: "alert-circle" - pointSize: Style.fontSizeXXL - color: Color.mError - Layout.alignment: Qt.AlignHCenter - } - - NText { - text: I18n.tr("setup.wallpaper.preview-error") - pointSize: Style.fontSizeS - color: Color.mError - Layout.alignment: Qt.AlignHCenter - } - } - } - - NBusyIndicator { - anchors.centerIn: parent - visible: (previewCached.status === Image.Loading || previewCached.status === Image.Null) && selectedWallpaper !== "" - running: visible - size: 28 + borderColor: selectedWallpaper !== "" ? Color.mPrimary : Color.mOutline + borderWidth: selectedWallpaper !== "" ? 2 : 1 } ColumnLayout { anchors.centerIn: parent spacing: Style.marginL visible: selectedWallpaper === "" - opacity: 0.6 Rectangle { Layout.alignment: Qt.AlignHCenter @@ -165,12 +92,11 @@ ColumnLayout { height: 64 radius: width / 2 color: Color.mPrimary - opacity: 0.15 NIcon { icon: "sparkles" pointSize: Style.fontSizeXXL - color: Color.mPrimary + color: Color.mOnPrimary anchors.centerIn: parent } } @@ -232,19 +158,22 @@ ColumnLayout { Repeater { model: filteredWallpapers - delegate: Rectangle { + delegate: Item { Layout.preferredWidth: 120 Layout.preferredHeight: 80 - color: Color.mSurface - border.color: selectedWallpaper === modelData ? Color.mPrimary : Color.mOutline - border.width: selectedWallpaper === modelData ? 2 : 1 - clip: true + + Rectangle { + anchors.fill: parent + color: Color.transparent + border.color: selectedWallpaper === modelData ? Color.mPrimary : Color.mOutline + border.width: selectedWallpaper === modelData ? 2 : 1 + } // Cached thumbnail NImageCached { id: thumbCached anchors.fill: parent - anchors.margins: 3 + anchors.margins: selectedWallpaper === modelData ? 2 : 1 source: "file://" + modelData } diff --git a/Shaders/frag/rounded_image.frag b/Shaders/frag/rounded_image.frag index 9d493b21..e38c0a00 100644 --- a/Shaders/frag/rounded_image.frag +++ b/Shaders/frag/rounded_image.frag @@ -11,8 +11,11 @@ layout(std140, binding = 0) uniform buf { // Custom properties with non-conflicting names float itemWidth; float itemHeight; + float sourceWidth; + float sourceHeight; float cornerRadius; float imageOpacity; + int fillMode; } ubuf; // Function to calculate the signed distance from a point to a rounded box @@ -24,33 +27,75 @@ float roundedBoxSDF(vec2 centerPos, vec2 boxSize, float radius) { void main() { // Get size from uniforms vec2 itemSize = vec2(ubuf.itemWidth, ubuf.itemHeight); + vec2 sourceSize = vec2(ubuf.sourceWidth, ubuf.sourceHeight); float cornerRadius = ubuf.cornerRadius; float itemOpacity = ubuf.imageOpacity; - - // Normalize coordinates to [-0.5, 0.5] range - vec2 uv = qt_TexCoord0 - 0.5; - - // Scale by aspect ratio to maintain uniform rounding - vec2 aspectRatio = itemSize / max(itemSize.x, itemSize.y); - uv *= aspectRatio; - - // Calculate half size in normalized space - vec2 halfSize = 0.5 * aspectRatio; - - // Normalize the corner radius - float normalizedRadius = cornerRadius / max(itemSize.x, itemSize.y); - - // Calculate distance to rounded rectangle - float distance = roundedBoxSDF(uv, halfSize, normalizedRadius); - - // Create smooth alpha mask - float smoothedAlpha = 1.0 - smoothstep(0.0, fwidth(distance), distance); - + int fillMode = ubuf.fillMode; + + // Work in pixel space for accurate rounded rectangle calculation + vec2 pixelPos = qt_TexCoord0 * itemSize; + + // Calculate distance to rounded rectangle edge (in pixels) + vec2 centerOffset = pixelPos - itemSize * 0.5; + float distance = roundedBoxSDF(centerOffset, itemSize * 0.5, cornerRadius); + + // Create smooth alpha mask for edge with anti-aliasing + float alpha = 1.0 - smoothstep(-0.5, 0.5, distance); + + // Calculate UV coordinates based on fill mode + vec2 imageUV = qt_TexCoord0; + + // fillMode constants from Qt: + // Image.Stretch = 0 + // Image.PreserveAspectFit = 1 + // Image.PreserveAspectCrop = 2 + // Image.Tile = 3 + // Image.TileVertically = 4 + // Image.TileHorizontally = 5 + // Image.Pad = 6 + + if (fillMode == 2) { // PreserveAspectCrop + // Calculate aspect ratios + float itemAspect = itemSize.x / itemSize.y; + float sourceAspect = sourceSize.x / sourceSize.y; + + // Calculate the scale needed to cover the item area + vec2 scale; + if (sourceAspect > itemAspect) { + // Image is wider - fit height, crop sides + scale.y = 1.0; + scale.x = sourceAspect / itemAspect; + } else { + // Image is taller - fit width, crop top/bottom + scale.x = 1.0; + scale.y = itemAspect / sourceAspect; + } + + // Apply scale and center + imageUV = (qt_TexCoord0 - 0.5) / scale + 0.5; + } else if (fillMode == 1) { // PreserveAspectFit + float itemAspect = itemSize.x / itemSize.y; + float sourceAspect = sourceSize.x / sourceSize.y; + + vec2 scale; + if (sourceAspect > itemAspect) { + // Image is wider - fit width, letterbox top/bottom + scale.x = 1.0; + scale.y = itemAspect / sourceAspect; + } else { + // Image is taller - fit height, letterbox sides + scale.y = 1.0; + scale.x = sourceAspect / itemAspect; + } + + imageUV = (qt_TexCoord0 - 0.5) * scale + 0.5; + } + // For Stretch (0) or other modes, use qt_TexCoord0 as-is + // Sample the texture - vec4 color = texture(source, qt_TexCoord0); - + vec4 color = texture(source, imageUV); + // Apply the rounded mask and opacity - // Make sure areas outside the rounded rect are completely transparent - float finalAlpha = color.a * smoothedAlpha * itemOpacity * ubuf.qt_Opacity; + float finalAlpha = color.a * alpha * itemOpacity * ubuf.qt_Opacity; fragColor = vec4(color.rgb * finalAlpha, finalAlpha); } \ No newline at end of file diff --git a/Shaders/qsb/rounded_image.frag.qsb b/Shaders/qsb/rounded_image.frag.qsb index 666f6434973c9fd0eb50651c6d43250023c55b53..a82f6550991a22735a301d015f8f899e4f0ff9a9 100644 GIT binary patch literal 3682 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 AH2?qr literal 2750 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 El;wG(X8-^I diff --git a/Widgets/NImageRounded.qml b/Widgets/NImageRounded.qml index f93ea5a3..a403c1d6 100644 --- a/Widgets/NImageRounded.qml +++ b/Widgets/NImageRounded.qml @@ -19,17 +19,19 @@ Item { signal statusChanged(int status) - ClippingRectangle { + Rectangle { anchors.fill: parent - color: Color.transparent radius: root.radius - border.color: root.borderColor + color: Color.transparent border.width: root.borderWidth + border.color: root.borderColor Image { + id: imageSource anchors.fill: parent - visible: !showFallback - source: imagePath + anchors.margins: root.borderWidth + visible: false + source: root.imagePath mipmap: true smooth: true asynchronous: true @@ -38,11 +40,30 @@ Item { onStatusChanged: root.statusChanged(status) } + ShaderEffect { + anchors.fill: parent + anchors.margins: root.borderWidth + visible: !root.showFallback + property variant source: imageSource + property real itemWidth: width + property real itemHeight: height + property real sourceWidth: imageSource.sourceSize.width + property real sourceHeight: imageSource.sourceSize.height + property real cornerRadius: Math.max(0, root.radius - root.borderWidth) + property real imageOpacity: 1.0 + property int fillMode: root.imageFillMode + + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/rounded_image.frag.qsb") + supportsAtlasTextures: false + blending: true + } + NIcon { - anchors.centerIn: parent - visible: showFallback - icon: fallbackIcon - pointSize: fallbackIconSize + anchors.fill: parent + anchors.margins: root.borderWidth + visible: root.showFallback + icon: root.fallbackIcon + pointSize: root.fallbackIconSize } } }