1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00

wallpaper: support different fill modes

This commit is contained in:
bbedward
2025-10-19 12:49:57 -04:00
parent e1c180a13f
commit 64960e4dcd
17 changed files with 368 additions and 207 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -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)

View File

@@ -16,62 +16,64 @@ 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 (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;

View File

@@ -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,51 +23,54 @@ 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 (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;

View File

@@ -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() {

View File

@@ -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) {

View File

@@ -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() {

View File

@@ -16,62 +16,64 @@ 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 (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;

View File

@@ -14,62 +14,64 @@ 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 (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;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.