diff --git a/Common/SettingsData.qml b/Common/SettingsData.qml index ff9410f6..b5600f86 100644 --- a/Common/SettingsData.qml +++ b/Common/SettingsData.qml @@ -64,6 +64,7 @@ Singleton { property bool useFahrenheit: false property bool nightModeEnabled: false property int animationSpeed: SettingsData.AnimationSpeed.Short + property string wallpaperFillMode: "Fill" property bool showLauncherButton: true property bool showWorkspaceSwitcher: true @@ -486,6 +487,7 @@ Singleton { widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch" surfaceBase = settings.surfaceBase !== undefined ? settings.surfaceBase : "s" screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({}) + wallpaperFillMode = settings.wallpaperFillMode !== undefined ? settings.wallpaperFillMode : "Fill" animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : SettingsData.AnimationSpeed.Short acMonitorTimeout = settings.acMonitorTimeout !== undefined ? settings.acMonitorTimeout : 0 acLockTimeout = settings.acLockTimeout !== undefined ? settings.acLockTimeout : 0 @@ -648,6 +650,7 @@ Singleton { "hideBrightnessSlider": hideBrightnessSlider, "widgetBackgroundColor": widgetBackgroundColor, "surfaceBase": surfaceBase, + "wallpaperFillMode": wallpaperFillMode, "notificationTimeoutLow": notificationTimeoutLow, "notificationTimeoutNormal": notificationTimeoutNormal, "notificationTimeoutCritical": notificationTimeoutCritical, @@ -722,7 +725,7 @@ Singleton { "dankBarGothCornersEnabled", "dankBarBorderEnabled", "dankBarBorderColor", "dankBarBorderOpacity", "dankBarBorderThickness", "popupGapsAuto", "popupGapsManual", "dankBarPosition", "lockScreenShowPowerActions", "enableFprint", "maxFprintTries", - "hideBrightnessSlider", "widgetBackgroundColor", "surfaceBase", + "hideBrightnessSlider", "widgetBackgroundColor", "surfaceBase", "wallpaperFillMode", "notificationTimeoutLow", "notificationTimeoutNormal", "notificationTimeoutCritical", "notificationPopupPosition", "osdAlwaysShowValue", "powerActionConfirm", "customPowerActionLock", "customPowerActionLogout", "customPowerActionSuspend", @@ -1055,6 +1058,11 @@ Singleton { saveSettings() } + function setWallpaperFillMode(mode) { + wallpaperFillMode = mode + saveSettings() + } + function setShowLauncherButton(enabled) { showLauncherButton = enabled saveSettings() diff --git a/Modules/Settings/PersonalizationTab.qml b/Modules/Settings/PersonalizationTab.qml index d1ec6c70..069935e1 100644 --- a/Modules/Settings/PersonalizationTab.qml +++ b/Modules/Settings/PersonalizationTab.qml @@ -410,6 +410,56 @@ Item { } } + Item { + width: parent.width + height: fillModeGroup.height + visible: { + var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath + return currentWallpaper !== "" && !currentWallpaper.startsWith("#") + } + + DankButtonGroup { + id: fillModeGroup + anchors.horizontalCenter: parent.horizontalCenter + model: ["Stretch", "Fit", "Fill", "Tile", "Tile V", "Tile H", "Pad"] + selectionMode: "single" + buttonHeight: 28 + minButtonWidth: 48 + buttonPadding: Theme.spacingS + checkIconSize: 0 + textSize: Theme.fontSizeSmall + checkEnabled: false + currentIndex: { + const modes = ["Stretch", "Fit", "Fill", "Tile", "TileVertically", "TileHorizontally", "Pad"] + return modes.indexOf(SettingsData.wallpaperFillMode) + } + onSelectionChanged: (index, selected) => { + if (selected) { + const modes = ["Stretch", "Fit", "Fill", "Tile", "TileVertically", "TileHorizontally", "Pad"] + SettingsData.setWallpaperFillMode(modes[index]) + } + } + + Connections { + target: SettingsData + function onWallpaperFillModeChanged() { + const modes = ["Stretch", "Fit", "Fill", "Tile", "TileVertically", "TileHorizontally", "Pad"] + fillModeGroup.currentIndex = modes.indexOf(SettingsData.wallpaperFillMode) + } + } + + Connections { + target: personalizationTab + function onSelectedMonitorNameChanged() { + Qt.callLater(() => { + const modes = ["Stretch", "Fit", "Fill", "Tile", "TileVertically", "TileHorizontally", "Pad"] + fillModeGroup.currentIndex = modes.indexOf(SettingsData.wallpaperFillMode) + }) + } + } + } + } + // Per-Mode Wallpaper Section - Full Width Rectangle { width: parent.width diff --git a/Modules/WallpaperBackground.qml b/Modules/WallpaperBackground.qml index ffe4b11c..9c96f4de 100644 --- a/Modules/WallpaperBackground.qml +++ b/Modules/WallpaperBackground.qml @@ -40,6 +40,7 @@ Variants { property bool isColorSource: source.startsWith("#") property string transitionType: SessionData.wallpaperTransition property string actualTransitionType: transitionType + property bool isInitialized: false Connections { target: SessionData @@ -71,7 +72,7 @@ Variants { } } property real transitionProgress: 0 - property real fillMode: 1.0 + property real shaderFillMode: getFillMode(SettingsData.wallpaperFillMode) property vector4d fillColor: Qt.vector4d(0, 0, 0, 1) property real edgeSmoothness: 0.1 @@ -86,11 +87,34 @@ Variants { property bool hasCurrent: currentWallpaper.status === Image.Ready && !!currentWallpaper.source property bool booting: !hasCurrent && nextWallpaper.status === Image.Ready + function getFillMode(modeName) { + switch(modeName) { + case "Stretch": return Image.Stretch + case "Fit": + case "PreserveAspectFit": return Image.PreserveAspectFit + case "Fill": + case "PreserveAspectCrop": return Image.PreserveAspectCrop + case "Tile": return Image.Tile + case "TileVertically": return Image.TileVertically + case "TileHorizontally": return Image.TileHorizontally + case "Pad": return Image.Pad + default: return Image.PreserveAspectCrop + } + } + WallpaperEngineProc { id: weProc monitor: modelData.name } + Component.onCompleted: { + if (source) { + const formattedSource = source.startsWith("file://") ? source : "file://" + source + setWallpaperImmediate(formattedSource) + } + isInitialized = true + } + Component.onDestruction: { weProc.stop() } @@ -109,9 +133,9 @@ Variants { } else if (isColor) { setWallpaperImmediate("") } else { - // Always set immediately if there's no current wallpaper (startup) - if (!currentWallpaper.source) { + if (!isInitialized || !currentWallpaper.source) { setWallpaperImmediate(source.startsWith("file://") ? source : "file://" + source) + isInitialized = true } else { changeWallpaper(source.startsWith("file://") ? source : "file://" + source) } @@ -211,7 +235,7 @@ Variants { asynchronous: true smooth: true cache: true - fillMode: Image.PreserveAspectCrop + fillMode: root.getFillMode(SettingsData.wallpaperFillMode) } Image { @@ -223,7 +247,7 @@ Variants { asynchronous: true smooth: true cache: true - fillMode: Image.PreserveAspectCrop + fillMode: root.getFillMode(SettingsData.wallpaperFillMode) onStatusChanged: { if (status !== Image.Ready) @@ -275,7 +299,7 @@ Variants { property variant source1: root.hasCurrent ? currentWallpaper : transparentSource property variant source2: nextWallpaper property real progress: root.transitionProgress - property real fillMode: root.fillMode + property real fillMode: root.shaderFillMode property vector4d fillColor: root.fillColor property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : modelData.width) property real imageHeight1: Math.max(1, root.hasCurrent ? source1.sourceSize.height : modelData.height) @@ -296,7 +320,7 @@ Variants { property real progress: root.transitionProgress property real smoothness: root.edgeSmoothness property real direction: root.wipeDirection - property real fillMode: root.fillMode + property real fillMode: root.shaderFillMode property vector4d fillColor: root.fillColor property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : modelData.width) property real imageHeight1: Math.max(1, root.hasCurrent ? source1.sourceSize.height : modelData.height) @@ -319,7 +343,7 @@ Variants { property real aspectRatio: root.width / root.height property real centerX: root.discCenterX property real centerY: root.discCenterY - property real fillMode: root.fillMode + property real fillMode: root.shaderFillMode property vector4d fillColor: root.fillColor property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : modelData.width) property real imageHeight1: Math.max(1, root.hasCurrent ? source1.sourceSize.height : modelData.height) @@ -342,7 +366,7 @@ Variants { property real aspectRatio: root.width / root.height property real stripeCount: root.stripesCount property real angle: root.stripesAngle - property real fillMode: root.fillMode + property real fillMode: root.shaderFillMode property vector4d fillColor: root.fillColor property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : modelData.width) property real imageHeight1: Math.max(1, root.hasCurrent ? source1.sourceSize.height : modelData.height) @@ -365,7 +389,7 @@ Variants { property real centerX: 0.5 property real centerY: 0.5 property real aspectRatio: root.width / root.height - property real fillMode: root.fillMode + property real fillMode: root.shaderFillMode property vector4d fillColor: root.fillColor property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : modelData.width) property real imageHeight1: Math.max(1, root.hasCurrent ? source1.sourceSize.height : modelData.height) @@ -385,7 +409,7 @@ Variants { property variant source2: nextWallpaper property real progress: root.transitionProgress property real smoothness: root.edgeSmoothness - property real fillMode: root.fillMode + property real fillMode: root.shaderFillMode property vector4d fillColor: root.fillColor property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : modelData.width) property real imageHeight1: Math.max(1, root.hasCurrent ? source1.sourceSize.height : modelData.height) @@ -411,7 +435,7 @@ Variants { property real aspectRatio: root.width / root.height property real centerX: root.discCenterX property real centerY: root.discCenterY - property real fillMode: root.fillMode + property real fillMode: root.shaderFillMode property vector4d fillColor: root.fillColor property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : modelData.width) property real imageHeight1: Math.max(1, root.hasCurrent ? source1.sourceSize.height : modelData.height) diff --git a/Shaders/frag/wp_disc.frag b/Shaders/frag/wp_disc.frag index 0fc21229..f44fa1b4 100644 --- a/Shaders/frag/wp_disc.frag +++ b/Shaders/frag/wp_disc.frag @@ -16,67 +16,69 @@ layout(std140, binding = 0) uniform buf { float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) float aspectRatio; // Width / Height of the screen - // Fill mode parameters - float fillMode; // 0=no(center), 1=crop(fill), 2=fit(contain), 3=stretch - float imageWidth1; // Width of source1 image - float imageHeight1; // Height of source1 image - float imageWidth2; // Width of source2 image - float imageHeight2; // Height of source2 image - float screenWidth; // Screen width - float screenHeight; // Screen height - vec4 fillColor; // Fill color for empty areas (default: black) + float fillMode; // 0=stretch, 1=fit, 2=crop, 3=tile, 4=tileV, 5=tileH, 6=pad + float imageWidth1; + float imageHeight1; + float imageWidth2; + float imageHeight2; + float screenWidth; + float screenHeight; + vec4 fillColor; } ubuf; -// Calculate UV coordinates based on fill mode vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { - float imageAspect = imgWidth / imgHeight; - float screenAspect = ubuf.screenWidth / ubuf.screenHeight; vec2 transformedUV = uv; - + if (ubuf.fillMode < 0.5) { - // Mode 0: no (center) - No resize, center image at original size - // Convert UV to pixel coordinates, offset, then back to UV in image space - vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; - vec2 imagePixel = screenPixel - imageOffset; - transformedUV = imagePixel / vec2(imgWidth, imgHeight); - } + transformedUV = uv; + } else if (ubuf.fillMode < 1.5) { - // Mode 1: crop (fill/cover) - Fill screen, crop excess (default) + float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); + vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; + vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + vec2 imagePixel = (screenPixel - offset) / scale; + transformedUV = imagePixel / vec2(imgWidth, imgHeight); + } + else if (ubuf.fillMode < 2.5) { float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize; transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5; } - else if (ubuf.fillMode < 2.5) { - // Mode 2: fit (contain) - Fit inside screen, maintain aspect ratio - float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); - vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; - vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; - - // Convert screen UV to pixel coordinates + else if (ubuf.fillMode < 3.5) { + transformedUV = fract(uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight)); + } + else if (ubuf.fillMode < 4.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(uv.x, fract(tileUV.y)); + } + else if (ubuf.fillMode < 5.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(fract(tileUV.x), uv.y); + } + else { vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - // Adjust for offset and scale - vec2 imagePixel = (screenPixel - offset) / scale; - // Convert back to UV coordinates in image space + vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; + vec2 imagePixel = screenPixel - imageOffset; transformedUV = imagePixel / vec2(imgWidth, imgHeight); } - // Mode 3: stretch - Use original UV (stretches to fit) - // No transformation needed for stretch mode - + return transformedUV; } -// Sample texture with fill mode and handle out-of-bounds vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight) { vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); - - // Check if UV is out of bounds - if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || + + if (ubuf.fillMode >= 2.5 && ubuf.fillMode <= 5.5) { + return texture(tex, transformedUV); + } + + if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || transformedUV.y < 0.0 || transformedUV.y > 1.0) { return ubuf.fillColor; } - + return texture(tex, transformedUV); } diff --git a/Shaders/frag/wp_fade.frag b/Shaders/frag/wp_fade.frag index 47aa7c67..7b42139f 100644 --- a/Shaders/frag/wp_fade.frag +++ b/Shaders/frag/wp_fade.frag @@ -13,7 +13,7 @@ layout(std140, binding = 0) uniform buf { float progress; // Fill mode parameters - float fillMode; // 0=no(center), 1=crop(fill), 2=fit(contain), 3=stretch + float fillMode; // 0=stretch, 1=fit, 2=crop, 3=tile, 4=tileV, 5=tileH, 6=pad float imageWidth1; // Width of source1 image float imageHeight1; // Height of source1 image float imageWidth2; // Width of source2 image @@ -23,56 +23,59 @@ layout(std140, binding = 0) uniform buf { vec4 fillColor; // Fill color for empty areas (default: black) } ubuf; -// Calculate UV coordinates based on fill mode vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { - float imageAspect = imgWidth / imgHeight; - float screenAspect = ubuf.screenWidth / ubuf.screenHeight; vec2 transformedUV = uv; - + if (ubuf.fillMode < 0.5) { - // Mode 0: no (center) - No resize, center image at original size - // Convert UV to pixel coordinates, offset, then back to UV in image space - vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; - vec2 imagePixel = screenPixel - imageOffset; - transformedUV = imagePixel / vec2(imgWidth, imgHeight); - } + transformedUV = uv; + } else if (ubuf.fillMode < 1.5) { - // Mode 1: crop (fill/cover) - Fill screen, crop excess (default) + float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); + vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; + vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + vec2 imagePixel = (screenPixel - offset) / scale; + transformedUV = imagePixel / vec2(imgWidth, imgHeight); + } + else if (ubuf.fillMode < 2.5) { float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize; transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5; } - else if (ubuf.fillMode < 2.5) { - // Mode 2: fit (contain) - Fit inside screen, maintain aspect ratio - float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); - vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; - vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; - - // Convert screen UV to pixel coordinates + else if (ubuf.fillMode < 3.5) { + transformedUV = fract(uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight)); + } + else if (ubuf.fillMode < 4.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(uv.x, fract(tileUV.y)); + } + else if (ubuf.fillMode < 5.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(fract(tileUV.x), uv.y); + } + else { vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - // Adjust for offset and scale - vec2 imagePixel = (screenPixel - offset) / scale; - // Convert back to UV coordinates in image space + vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; + vec2 imagePixel = screenPixel - imageOffset; transformedUV = imagePixel / vec2(imgWidth, imgHeight); } - // Mode 3: stretch - Use original UV (stretches to fit) - // No transformation needed for stretch mode - + return transformedUV; } -// Sample texture with fill mode and handle out-of-bounds vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight) { vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); - - // Check if UV is out of bounds - if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || + + if (ubuf.fillMode >= 2.5 && ubuf.fillMode <= 5.5) { + return texture(tex, transformedUV); + } + + if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || transformedUV.y < 0.0 || transformedUV.y > 1.0) { return ubuf.fillColor; } - + return texture(tex, transformedUV); } diff --git a/Shaders/frag/wp_iris_bloom.frag b/Shaders/frag/wp_iris_bloom.frag index 56275b07..74cc42c4 100644 --- a/Shaders/frag/wp_iris_bloom.frag +++ b/Shaders/frag/wp_iris_bloom.frag @@ -16,7 +16,7 @@ layout(std140, binding = 0) uniform buf { float smoothness; float aspectRatio; - float fillMode; + float fillMode; // 0=stretch, 1=fit, 2=crop, 3=tile, 4=tileV, 5=tileH, 6=pad float imageWidth1; float imageHeight1; float imageWidth2; @@ -28,35 +28,58 @@ layout(std140, binding = 0) uniform buf { vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { vec2 transformedUV = uv; + if (ubuf.fillMode < 0.5) { - vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; - vec2 imagePixel = screenPixel - imageOffset; - transformedUV = imagePixel / vec2(imgWidth, imgHeight); + transformedUV = uv; } else if (ubuf.fillMode < 1.5) { + float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); + vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; + vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + vec2 imagePixel = (screenPixel - offset) / scale; + transformedUV = imagePixel / vec2(imgWidth, imgHeight); + } + else if (ubuf.fillMode < 2.5) { float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize; transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5; } - else if (ubuf.fillMode < 2.5) { - float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); - vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; - vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(scaledImageSize)) * 0.5; + else if (ubuf.fillMode < 3.5) { + transformedUV = fract(uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight)); + } + else if (ubuf.fillMode < 4.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(uv.x, fract(tileUV.y)); + } + else if (ubuf.fillMode < 5.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(fract(tileUV.x), uv.y); + } + else { vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - vec2 imagePixel = (screenPixel - offset) / scale; + vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; + vec2 imagePixel = screenPixel - imageOffset; transformedUV = imagePixel / vec2(imgWidth, imgHeight); } + return transformedUV; } vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight) { - vec2 tuv = calculateUV(uv, imgWidth, imgHeight); - if (tuv.x < 0.0 || tuv.x > 1.0 || tuv.y < 0.0 || tuv.y > 1.0) { + vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); + + if (ubuf.fillMode >= 2.5 && ubuf.fillMode <= 5.5) { + return texture(tex, transformedUV); + } + + if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || + transformedUV.y < 0.0 || transformedUV.y > 1.0) { return ubuf.fillColor; } - return texture(tex, tuv); + + return texture(tex, transformedUV); } void main() { diff --git a/Shaders/frag/wp_pixelate.frag b/Shaders/frag/wp_pixelate.frag index d5f5e030..2b6fb536 100644 --- a/Shaders/frag/wp_pixelate.frag +++ b/Shaders/frag/wp_pixelate.frag @@ -16,8 +16,7 @@ layout(std140, binding = 0) uniform buf { float smoothness; // controls starting block size (0..1) float aspectRatio; // (unused) - // Fill mode parameters - float fillMode; // 0=no(center), 1=crop, 2=fit, 3=stretch + float fillMode; // 0=stretch, 1=fit, 2=crop, 3=tile, 4=tileV, 5=tileH, 6=pad float imageWidth1; float imageHeight1; float imageWidth2; @@ -29,17 +28,11 @@ layout(std140, binding = 0) uniform buf { vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { vec2 transformedUV = uv; + if (ubuf.fillMode < 0.5) { - vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; - vec2 imagePixel = screenPixel - imageOffset; - transformedUV = imagePixel / vec2(imgWidth, imgHeight); - } else if (ubuf.fillMode < 1.5) { - float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); - vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; - vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize; - transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5; - } else if (ubuf.fillMode < 2.5) { + transformedUV = uv; + } + else if (ubuf.fillMode < 1.5) { float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; @@ -47,13 +40,46 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { vec2 imagePixel = (screenPixel - offset) / scale; transformedUV = imagePixel / vec2(imgWidth, imgHeight); } + else if (ubuf.fillMode < 2.5) { + float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); + vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; + vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize; + transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5; + } + else if (ubuf.fillMode < 3.5) { + transformedUV = fract(uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight)); + } + else if (ubuf.fillMode < 4.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(uv.x, fract(tileUV.y)); + } + else if (ubuf.fillMode < 5.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(fract(tileUV.x), uv.y); + } + else { + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; + vec2 imagePixel = screenPixel - imageOffset; + transformedUV = imagePixel / vec2(imgWidth, imgHeight); + } + return transformedUV; } -vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float w, float h) { - vec2 tuv = calculateUV(uv, w, h); - if (tuv.x < 0.0 || tuv.x > 1.0 || tuv.y < 0.0 || tuv.y > 1.0) return ubuf.fillColor; - return texture(tex, tuv); +vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight) { + vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); + + if (ubuf.fillMode >= 2.5 && ubuf.fillMode <= 5.5) { + return texture(tex, transformedUV); + } + + if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || + transformedUV.y < 0.0 || transformedUV.y > 1.0) { + return ubuf.fillColor; + } + + return texture(tex, transformedUV); } vec2 quantizeUV(vec2 uv, float cellPx) { diff --git a/Shaders/frag/wp_portal.frag b/Shaders/frag/wp_portal.frag index 0a2cc427..21e939c3 100644 --- a/Shaders/frag/wp_portal.frag +++ b/Shaders/frag/wp_portal.frag @@ -16,8 +16,7 @@ layout(std140, binding = 0) uniform buf { float smoothness; // 0..1 (edge softness) float aspectRatio; // width / height - // Fill mode parameters - float fillMode; // 0=no(center), 1=crop(fill), 2=fit(contain), 3=stretch + float fillMode; // 0=stretch, 1=fit, 2=crop, 3=tile, 4=tileV, 5=tileH, 6=pad float imageWidth1; float imageHeight1; float imageWidth2; @@ -31,18 +30,9 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { vec2 transformedUV = uv; if (ubuf.fillMode < 0.5) { - vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; - vec2 imagePixel = screenPixel - imageOffset; - transformedUV = imagePixel / vec2(imgWidth, imgHeight); + transformedUV = uv; } else if (ubuf.fillMode < 1.5) { - float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); - vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; - vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize; - transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5; - } - else if (ubuf.fillMode < 2.5) { float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; @@ -50,15 +40,46 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { vec2 imagePixel = (screenPixel - offset) / scale; transformedUV = imagePixel / vec2(imgWidth, imgHeight); } - // else: stretch + else if (ubuf.fillMode < 2.5) { + float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); + vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; + vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize; + transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5; + } + else if (ubuf.fillMode < 3.5) { + transformedUV = fract(uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight)); + } + else if (ubuf.fillMode < 4.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(uv.x, fract(tileUV.y)); + } + else if (ubuf.fillMode < 5.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(fract(tileUV.x), uv.y); + } + else { + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; + vec2 imagePixel = screenPixel - imageOffset; + transformedUV = imagePixel / vec2(imgWidth, imgHeight); + } return transformedUV; } -vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float w, float h) { - vec2 tuv = calculateUV(uv, w, h); - if (tuv.x < 0.0 || tuv.x > 1.0 || tuv.y < 0.0 || tuv.y > 1.0) return ubuf.fillColor; - return texture(tex, tuv); +vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight) { + vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); + + if (ubuf.fillMode >= 2.5 && ubuf.fillMode <= 5.5) { + return texture(tex, transformedUV); + } + + if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || + transformedUV.y < 0.0 || transformedUV.y > 1.0) { + return ubuf.fillColor; + } + + return texture(tex, transformedUV); } void main() { diff --git a/Shaders/frag/wp_stripes.frag b/Shaders/frag/wp_stripes.frag index c2684ce4..62da21e2 100644 --- a/Shaders/frag/wp_stripes.frag +++ b/Shaders/frag/wp_stripes.frag @@ -16,67 +16,69 @@ layout(std140, binding = 0) uniform buf { float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) float aspectRatio; // Width / Height of the screen - // Fill mode parameters - float fillMode; // 0=no(center), 1=crop(fill), 2=fit(contain), 3=stretch - float imageWidth1; // Width of source1 image - float imageHeight1; // Height of source1 image - float imageWidth2; // Width of source2 image - float imageHeight2; // Height of source2 image - float screenWidth; // Screen width - float screenHeight; // Screen height - vec4 fillColor; // Fill color for empty areas (default: black) + float fillMode; // 0=stretch, 1=fit, 2=crop, 3=tile, 4=tileV, 5=tileH, 6=pad + float imageWidth1; + float imageHeight1; + float imageWidth2; + float imageHeight2; + float screenWidth; + float screenHeight; + vec4 fillColor; } ubuf; -// Calculate UV coordinates based on fill mode vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { - float imageAspect = imgWidth / imgHeight; - float screenAspect = ubuf.screenWidth / ubuf.screenHeight; vec2 transformedUV = uv; - + if (ubuf.fillMode < 0.5) { - // Mode 0: no (center) - No resize, center image at original size - // Convert UV to pixel coordinates, offset, then back to UV in image space - vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; - vec2 imagePixel = screenPixel - imageOffset; - transformedUV = imagePixel / vec2(imgWidth, imgHeight); - } + transformedUV = uv; + } else if (ubuf.fillMode < 1.5) { - // Mode 1: crop (fill/cover) - Fill screen, crop excess (default) + float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); + vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; + vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + vec2 imagePixel = (screenPixel - offset) / scale; + transformedUV = imagePixel / vec2(imgWidth, imgHeight); + } + else if (ubuf.fillMode < 2.5) { float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize; transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5; } - else if (ubuf.fillMode < 2.5) { - // Mode 2: fit (contain) - Fit inside screen, maintain aspect ratio - float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); - vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; - vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; - - // Convert screen UV to pixel coordinates + else if (ubuf.fillMode < 3.5) { + transformedUV = fract(uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight)); + } + else if (ubuf.fillMode < 4.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(uv.x, fract(tileUV.y)); + } + else if (ubuf.fillMode < 5.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(fract(tileUV.x), uv.y); + } + else { vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - // Adjust for offset and scale - vec2 imagePixel = (screenPixel - offset) / scale; - // Convert back to UV coordinates in image space + vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; + vec2 imagePixel = screenPixel - imageOffset; transformedUV = imagePixel / vec2(imgWidth, imgHeight); } - // Mode 3: stretch - Use original UV (stretches to fit) - // No transformation needed for stretch mode - + return transformedUV; } -// Sample texture with fill mode and handle out-of-bounds vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight) { vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); - - // Check if UV is out of bounds - if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || + + if (ubuf.fillMode >= 2.5 && ubuf.fillMode <= 5.5) { + return texture(tex, transformedUV); + } + + if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || transformedUV.y < 0.0 || transformedUV.y > 1.0) { return ubuf.fillColor; } - + return texture(tex, transformedUV); } diff --git a/Shaders/frag/wp_wipe.frag b/Shaders/frag/wp_wipe.frag index 46b21e6f..4960ef8f 100644 --- a/Shaders/frag/wp_wipe.frag +++ b/Shaders/frag/wp_wipe.frag @@ -14,67 +14,69 @@ layout(std140, binding = 0) uniform buf { float direction; // 0=left, 1=right, 2=up, 3=down float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) - // Fill mode parameters - float fillMode; // 0=no(center), 1=crop(fill), 2=fit(contain), 3=stretch - float imageWidth1; // Width of source1 image - float imageHeight1; // Height of source1 image - float imageWidth2; // Width of source2 image - float imageHeight2; // Height of source2 image - float screenWidth; // Screen width - float screenHeight; // Screen height - vec4 fillColor; // Fill color for empty areas (default: black) + float fillMode; // 0=stretch, 1=fit, 2=crop, 3=tile, 4=tileV, 5=tileH, 6=pad + float imageWidth1; + float imageHeight1; + float imageWidth2; + float imageHeight2; + float screenWidth; + float screenHeight; + vec4 fillColor; } ubuf; -// Calculate UV coordinates based on fill mode vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { - float imageAspect = imgWidth / imgHeight; - float screenAspect = ubuf.screenWidth / ubuf.screenHeight; vec2 transformedUV = uv; - + if (ubuf.fillMode < 0.5) { - // Mode 0: no (center) - No resize, center image at original size - // Convert UV to pixel coordinates, offset, then back to UV in image space - vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; - vec2 imagePixel = screenPixel - imageOffset; - transformedUV = imagePixel / vec2(imgWidth, imgHeight); - } + transformedUV = uv; + } else if (ubuf.fillMode < 1.5) { - // Mode 1: crop (fill/cover) - Fill screen, crop excess (default) + float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); + vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; + vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + vec2 imagePixel = (screenPixel - offset) / scale; + transformedUV = imagePixel / vec2(imgWidth, imgHeight); + } + else if (ubuf.fillMode < 2.5) { float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize; transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5; } - else if (ubuf.fillMode < 2.5) { - // Mode 2: fit (contain) - Fit inside screen, maintain aspect ratio - float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); - vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale; - vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5; - - // Convert screen UV to pixel coordinates + else if (ubuf.fillMode < 3.5) { + transformedUV = fract(uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight)); + } + else if (ubuf.fillMode < 4.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(uv.x, fract(tileUV.y)); + } + else if (ubuf.fillMode < 5.5) { + vec2 tileUV = uv * vec2(ubuf.screenWidth, ubuf.screenHeight) / vec2(imgWidth, imgHeight); + transformedUV = vec2(fract(tileUV.x), uv.y); + } + else { vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); - // Adjust for offset and scale - vec2 imagePixel = (screenPixel - offset) / scale; - // Convert back to UV coordinates in image space + vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5; + vec2 imagePixel = screenPixel - imageOffset; transformedUV = imagePixel / vec2(imgWidth, imgHeight); } - // Mode 3: stretch - Use original UV (stretches to fit) - // No transformation needed for stretch mode - + return transformedUV; } -// Sample texture with fill mode and handle out-of-bounds vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight) { vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); - - // Check if UV is out of bounds - if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || + + if (ubuf.fillMode >= 2.5 && ubuf.fillMode <= 5.5) { + return texture(tex, transformedUV); + } + + if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || transformedUV.y < 0.0 || transformedUV.y > 1.0) { return ubuf.fillColor; } - + return texture(tex, transformedUV); } diff --git a/Shaders/qsb/wp_disc.frag.qsb b/Shaders/qsb/wp_disc.frag.qsb index 8e1705e7..fc6324f9 100644 Binary files a/Shaders/qsb/wp_disc.frag.qsb and b/Shaders/qsb/wp_disc.frag.qsb differ diff --git a/Shaders/qsb/wp_fade.frag.qsb b/Shaders/qsb/wp_fade.frag.qsb index 3081ea8e..a9146959 100644 Binary files a/Shaders/qsb/wp_fade.frag.qsb and b/Shaders/qsb/wp_fade.frag.qsb differ diff --git a/Shaders/qsb/wp_iris_bloom.frag.qsb b/Shaders/qsb/wp_iris_bloom.frag.qsb index 2244e2e7..565a7617 100644 Binary files a/Shaders/qsb/wp_iris_bloom.frag.qsb and b/Shaders/qsb/wp_iris_bloom.frag.qsb differ diff --git a/Shaders/qsb/wp_pixelate.frag.qsb b/Shaders/qsb/wp_pixelate.frag.qsb index c784cc52..85236a5d 100644 Binary files a/Shaders/qsb/wp_pixelate.frag.qsb and b/Shaders/qsb/wp_pixelate.frag.qsb differ diff --git a/Shaders/qsb/wp_portal.frag.qsb b/Shaders/qsb/wp_portal.frag.qsb index 731f1916..6ae2fece 100644 Binary files a/Shaders/qsb/wp_portal.frag.qsb and b/Shaders/qsb/wp_portal.frag.qsb differ diff --git a/Shaders/qsb/wp_stripes.frag.qsb b/Shaders/qsb/wp_stripes.frag.qsb index db496eaa..e3d28b8a 100644 Binary files a/Shaders/qsb/wp_stripes.frag.qsb and b/Shaders/qsb/wp_stripes.frag.qsb differ diff --git a/Shaders/qsb/wp_wipe.frag.qsb b/Shaders/qsb/wp_wipe.frag.qsb index a7dc6b14..83364542 100644 Binary files a/Shaders/qsb/wp_wipe.frag.qsb and b/Shaders/qsb/wp_wipe.frag.qsb differ