diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index a260abe79..de616d3d3 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Zuschneiden (Füllen)", "fit": "Einpassen (Beibehalten)", + "repeat": "Wiederholen (Kachel)", "stretch": "Strecken" }, "no-match": "Keine Übereinstimmung gefunden.", diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 170e19428..3ec109b0d 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Crop (Fill)", "fit": "Fit (Contain)", + "repeat": "Repeat (Tile)", "stretch": "Stretch" }, "no-match": "No match found.", diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 94b1c34f2..02bdad46e 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Recortar (Rellenar)", "fit": "Ajustar (Contener)", + "repeat": "Repetir (Mosaico)", "stretch": "Estirar" }, "no-match": "No se encontraron coincidencias.", diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 9a0f76480..841748e77 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Recadrer (Remplir)", "fit": "Ajuster (Contenir)", + "repeat": "Répéter (Mosaïque)", "stretch": "Étirer" }, "no-match": "Aucun résultat trouvé.", diff --git a/Assets/Translations/hu.json b/Assets/Translations/hu.json index 1dbac0890..a42ceb5fc 100644 --- a/Assets/Translations/hu.json +++ b/Assets/Translations/hu.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Vágás (Kitöltés)", "fit": "Illesztés (Tartalmazás)", + "repeat": "Ismétlés (Mozaik)", "stretch": "Nyújtás" }, "no-match": "Nincs találat.", diff --git a/Assets/Translations/ja.json b/Assets/Translations/ja.json index f595904dd..a91792eb8 100644 --- a/Assets/Translations/ja.json +++ b/Assets/Translations/ja.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "拡大する", "fit": "画面に合わせる", + "repeat": "繰り返し(タイル)", "stretch": "引き伸ばして表示" }, "no-match": "一致する項目がありません。", diff --git a/Assets/Translations/ku.json b/Assets/Translations/ku.json index ae006c519..035405296 100644 --- a/Assets/Translations/ku.json +++ b/Assets/Translations/ku.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Birîn (Dagirtin)", "fit": "Guncav (Tê de)", + "repeat": "Dubarekirin (Tile)", "stretch": "Dirêjkirin" }, "no-match": "Lihevhatin nehat dîtin.", diff --git a/Assets/Translations/nl.json b/Assets/Translations/nl.json index 9257fa917..699e9e52f 100644 --- a/Assets/Translations/nl.json +++ b/Assets/Translations/nl.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Bijsnijden (vullend)", "fit": "Passend (volledig zichtbaar)", + "repeat": "Herhalen (Tegel)", "stretch": "Uitrekken" }, "no-match": "Geen resultaten gevonden.", diff --git a/Assets/Translations/pl.json b/Assets/Translations/pl.json index bc1b67f34..a4f8b3f58 100644 --- a/Assets/Translations/pl.json +++ b/Assets/Translations/pl.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Przytnij (Wypełnij)", "fit": "Dopasuj (Zmieść)", + "repeat": "Powtórz (Kafel)", "stretch": "Rozciągnij" }, "no-match": "Nie znaleziono dopasowania.", diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index c1df4629c..fc85e5621 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Cortar (Preencher)", "fit": "Ajustar (Conter)", + "repeat": "Repetir (Mosaico)", "stretch": "Esticar" }, "no-match": "Nenhuma correspondência encontrada.", diff --git a/Assets/Translations/ru.json b/Assets/Translations/ru.json index e791733cf..7c1b54d9a 100644 --- a/Assets/Translations/ru.json +++ b/Assets/Translations/ru.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Обрезать (Заполнить)", "fit": "Подогнать (Вместить)", + "repeat": "Повторить (Замостить)", "stretch": "Растянуть" }, "no-match": "Совпадений не найдено.", diff --git a/Assets/Translations/tr.json b/Assets/Translations/tr.json index c281ec3ca..8a91b75bb 100644 --- a/Assets/Translations/tr.json +++ b/Assets/Translations/tr.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Kırp (Doldur)", "fit": "Sığ (İçer)", + "repeat": "Tekrarla (Döşe)", "stretch": "Ger" }, "no-match": "Eşleşme bulunamadı.", diff --git a/Assets/Translations/uk-UA.json b/Assets/Translations/uk-UA.json index 3d93f55de..5c37c32be 100644 --- a/Assets/Translations/uk-UA.json +++ b/Assets/Translations/uk-UA.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "Обрізати (Заповнити)", "fit": "Вмістити (Вписати)", + "repeat": "Повторити (Плитка)", "stretch": "Розтягнути" }, "no-match": "Збігів не знайдено.", diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 4f0d51860..348b6ac9a 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -1594,6 +1594,7 @@ "fill-modes": { "crop": "裁剪(填充)", "fit": "适应(包含)", + "repeat": "重复(平铺)", "stretch": "拉伸" }, "no-match": "未找到匹配项。", diff --git a/Assets/Translations/zh-TW.json b/Assets/Translations/zh-TW.json index c2df41a2f..41a1b04e7 100644 --- a/Assets/Translations/zh-TW.json +++ b/Assets/Translations/zh-TW.json @@ -1501,6 +1501,7 @@ "fill-modes": { "crop": "裁切 (填滿)", "fit": "調整 (收容)", + "repeat": "重複 (平鋪)", "stretch": "延展" }, "no-match": "沒有相關結果", diff --git a/Services/UI/WallpaperService.qml b/Services/UI/WallpaperService.qml index 5c05cbc21..1d25eec81 100644 --- a/Services/UI/WallpaperService.qml +++ b/Services/UI/WallpaperService.qml @@ -165,6 +165,11 @@ Singleton { "name": I18n.tr("wallpaper.fill-modes.stretch"), "uniform": 3.0 }); + fillModeModel.append({ + "key": "repeat", + "name": I18n.tr("wallpaper.fill-modes.repeat"), + "uniform": 4.0 + }); // Populate transitionsModel with translated names transitionsModel.append({ diff --git a/Shaders/frag/wp_disc.frag b/Shaders/frag/wp_disc.frag index 9bdf0a70c..3add2f138 100644 --- a/Shaders/frag/wp_disc.frag +++ b/Shaders/frag/wp_disc.frag @@ -17,7 +17,7 @@ layout(std140, binding = 0) uniform buf { float aspectRatio; // Width / Height of the screen // Fill mode parameters - float fillMode; // 0=no(center), 1=crop(fill), 2=fit(contain), 3=stretch + float fillMode; // 0=center, 1=crop, 2=fit, 3=stretch, 4=repeat float imageWidth1; // Width of source1 image float imageHeight1; // Height of source1 image float imageWidth2; // Width of source2 image @@ -38,7 +38,7 @@ 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 @@ -46,7 +46,7 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { 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) { // Mode 1: crop (fill/cover) - Fill screen, crop excess (default) float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); @@ -59,7 +59,7 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { 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 vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); // Adjust for offset and scale @@ -67,9 +67,16 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { // Convert back to UV coordinates in image space transformedUV = imagePixel / vec2(imgWidth, imgHeight); } - // Mode 3: stretch - Use original UV (stretches to fit) - // No transformation needed for stretch mode - + else if (ubuf.fillMode < 3.5) { + // Mode 3: stretch - Use original UV (stretches to fit) + // No transformation needed for stretch mode + } + else { + // Mode 4: repeat (tile) - Tile image at original size + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + transformedUV = screenPixel / vec2(imgWidth, imgHeight); + } + return transformedUV; } @@ -83,6 +90,11 @@ vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight, vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); + // Mode 4 (repeat): use fract() to tile the image + if (ubuf.fillMode > 3.5) { + return texture(tex, fract(transformedUV)); + } + // Check if UV is out of bounds if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || transformedUV.y < 0.0 || transformedUV.y > 1.0) { diff --git a/Shaders/frag/wp_fade.frag b/Shaders/frag/wp_fade.frag index 805798a47..6fbc9a1e1 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=center, 1=crop, 2=fit, 3=stretch, 4=repeat float imageWidth1; // Width of source1 image float imageHeight1; // Height of source1 image float imageWidth2; // Width of source2 image @@ -34,7 +34,7 @@ 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 @@ -42,7 +42,7 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { 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) { // Mode 1: crop (fill/cover) - Fill screen, crop excess (default) float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); @@ -55,7 +55,7 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { 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 vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); // Adjust for offset and scale @@ -63,9 +63,16 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { // Convert back to UV coordinates in image space transformedUV = imagePixel / vec2(imgWidth, imgHeight); } - // Mode 3: stretch - Use original UV (stretches to fit) - // No transformation needed for stretch mode - + else if (ubuf.fillMode < 3.5) { + // Mode 3: stretch - Use original UV (stretches to fit) + // No transformation needed for stretch mode + } + else { + // Mode 4: repeat (tile) - Tile image at original size + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + transformedUV = screenPixel / vec2(imgWidth, imgHeight); + } + return transformedUV; } @@ -79,6 +86,11 @@ vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight, vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); + // Mode 4 (repeat): use fract() to tile the image + if (ubuf.fillMode > 3.5) { + return texture(tex, fract(transformedUV)); + } + // Check if UV is out of bounds if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || transformedUV.y < 0.0 || transformedUV.y > 1.0) { diff --git a/Shaders/frag/wp_stripes.frag b/Shaders/frag/wp_stripes.frag index 50a33146e..72e83f56c 100644 --- a/Shaders/frag/wp_stripes.frag +++ b/Shaders/frag/wp_stripes.frag @@ -17,7 +17,7 @@ layout(std140, binding = 0) uniform buf { float aspectRatio; // Width / Height of the screen // Fill mode parameters - float fillMode; // 0=no(center), 1=crop(fill), 2=fit(contain), 3=stretch + float fillMode; // 0=center, 1=crop, 2=fit, 3=stretch, 4=repeat float imageWidth1; // Width of source1 image float imageHeight1; // Height of source1 image float imageWidth2; // Width of source2 image @@ -38,7 +38,7 @@ 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 @@ -46,7 +46,7 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { 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) { // Mode 1: crop (fill/cover) - Fill screen, crop excess (default) float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); @@ -59,7 +59,7 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { 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 vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); // Adjust for offset and scale @@ -67,9 +67,16 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { // Convert back to UV coordinates in image space transformedUV = imagePixel / vec2(imgWidth, imgHeight); } - // Mode 3: stretch - Use original UV (stretches to fit) - // No transformation needed for stretch mode - + else if (ubuf.fillMode < 3.5) { + // Mode 3: stretch - Use original UV (stretches to fit) + // No transformation needed for stretch mode + } + else { + // Mode 4: repeat (tile) - Tile image at original size + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + transformedUV = screenPixel / vec2(imgWidth, imgHeight); + } + return transformedUV; } @@ -83,6 +90,11 @@ vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight, vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); + // Mode 4 (repeat): use fract() to tile the image + if (ubuf.fillMode > 3.5) { + return texture(tex, fract(transformedUV)); + } + // Check if UV is out of bounds if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || transformedUV.y < 0.0 || transformedUV.y > 1.0) { diff --git a/Shaders/frag/wp_wipe.frag b/Shaders/frag/wp_wipe.frag index 15cd01e90..19ab477a8 100644 --- a/Shaders/frag/wp_wipe.frag +++ b/Shaders/frag/wp_wipe.frag @@ -15,7 +15,7 @@ layout(std140, binding = 0) uniform buf { 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 fillMode; // 0=center, 1=crop, 2=fit, 3=stretch, 4=repeat float imageWidth1; // Width of source1 image float imageHeight1; // Height of source1 image float imageWidth2; // Width of source2 image @@ -36,7 +36,7 @@ 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 @@ -44,7 +44,7 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { 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) { // Mode 1: crop (fill/cover) - Fill screen, crop excess (default) float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight); @@ -57,7 +57,7 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { 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 vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); // Adjust for offset and scale @@ -65,9 +65,16 @@ vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) { // Convert back to UV coordinates in image space transformedUV = imagePixel / vec2(imgWidth, imgHeight); } - // Mode 3: stretch - Use original UV (stretches to fit) - // No transformation needed for stretch mode - + else if (ubuf.fillMode < 3.5) { + // Mode 3: stretch - Use original UV (stretches to fit) + // No transformation needed for stretch mode + } + else { + // Mode 4: repeat (tile) - Tile image at original size + vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight); + transformedUV = screenPixel / vec2(imgWidth, imgHeight); + } + return transformedUV; } @@ -81,6 +88,11 @@ vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight, vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight); + // Mode 4 (repeat): use fract() to tile the image + if (ubuf.fillMode > 3.5) { + return texture(tex, fract(transformedUV)); + } + // Check if UV is out of bounds if (transformedUV.x < 0.0 || transformedUV.x > 1.0 || transformedUV.y < 0.0 || transformedUV.y > 1.0) { diff --git a/Shaders/qsb/wp_disc.frag.qsb b/Shaders/qsb/wp_disc.frag.qsb index 24f759416..d324e006b 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 578adbd5b..677862618 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_stripes.frag.qsb b/Shaders/qsb/wp_stripes.frag.qsb index 8b8cfdcc4..3265b2b5d 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 cfda11679..f878f3598 100644 Binary files a/Shaders/qsb/wp_wipe.frag.qsb and b/Shaders/qsb/wp_wipe.frag.qsb differ