From 2fe422c10b25b32b04929d805231691bc0873a43 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 01:57:44 +0000 Subject: [PATCH 1/4] Initial plan From fa503d3f9069042991dc09d9df5caadfb5fb7687 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:04:00 +0000 Subject: [PATCH 2/4] Implement blur effect for image effector system Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- locales/en-US.yml | 2 + locales/ja-JP.yml | 2 + .../src/utility/image-effector/fxs.ts | 2 + .../src/utility/image-effector/fxs/blur.ts | 66 +++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 packages/frontend/src/utility/image-effector/fxs/blur.ts diff --git a/locales/en-US.yml b/locales/en-US.yml index 9c02e83021..e54060c267 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -3194,6 +3194,7 @@ _imageEffector: mirror: "Mirror" invert: "Invert Colors" grayscale: "Grayscale" + blur: "Blur" colorAdjust: "Color Correction" colorClamp: "Color Compression" colorClampAdvanced: "Color Compression (Advanced)" @@ -3209,6 +3210,7 @@ _imageEffector: angle: "Angle" scale: "Size" size: "Size" + radius: "Radius" color: "Color" opacity: "Opacity" normalize: "Normalize" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 4ae52990e5..dcb1701a06 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -3306,6 +3306,7 @@ _imageEffector: mirror: "ミラー" invert: "色の反転" grayscale: "白黒" + blur: "ぼかし" colorAdjust: "色調補正" colorClamp: "色の圧縮" colorClampAdvanced: "色の圧縮(高度)" @@ -3323,6 +3324,7 @@ _imageEffector: angle: "角度" scale: "サイズ" size: "サイズ" + radius: "半径" offset: "位置" color: "色" opacity: "不透明度" diff --git a/packages/frontend/src/utility/image-effector/fxs.ts b/packages/frontend/src/utility/image-effector/fxs.ts index 43e10a22fc..b954bd370d 100644 --- a/packages/frontend/src/utility/image-effector/fxs.ts +++ b/packages/frontend/src/utility/image-effector/fxs.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { FX_blur } from './fxs/blur.js'; import { FX_checker } from './fxs/checker.js'; import { FX_chromaticAberration } from './fxs/chromaticAberration.js'; import { FX_colorAdjust } from './fxs/colorAdjust.js'; @@ -25,6 +26,7 @@ export const FXS = [ FX_mirror, FX_invert, FX_grayscale, + FX_blur, FX_colorAdjust, FX_colorClamp, FX_colorClampAdvanced, diff --git a/packages/frontend/src/utility/image-effector/fxs/blur.ts b/packages/frontend/src/utility/image-effector/fxs/blur.ts new file mode 100644 index 0000000000..31324e08d3 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/blur.ts @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineImageEffectorFx } from '../ImageEffector.js'; +import { i18n } from '@/i18n.js'; + +const shader = `#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform float u_radius; +out vec4 out_color; + +void main() { + vec2 texelSize = 1.0 / in_resolution; + vec4 result = vec4(0.0); + + // Simple box blur with fixed kernel size for better performance + // This avoids dynamic loops which can be slow on some GPUs + float radius = min(u_radius, 8.0); // Clamp to reasonable maximum + + // Sample in a cross pattern for efficiency + result += texture(in_texture, in_uv); // Center + + // Horizontal samples + result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, 0.0)); + result += texture(in_texture, in_uv + vec2(radius * texelSize.x, 0.0)); + + // Vertical samples + result += texture(in_texture, in_uv + vec2(0.0, -radius * texelSize.y)); + result += texture(in_texture, in_uv + vec2(0.0, radius * texelSize.y)); + + // Diagonal samples for better quality + result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, -radius * texelSize.y)); + result += texture(in_texture, in_uv + vec2(radius * texelSize.x, -radius * texelSize.y)); + result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, radius * texelSize.y)); + result += texture(in_texture, in_uv + vec2(radius * texelSize.x, radius * texelSize.y)); + + // Average the samples + out_color = result / 9.0; +} +`; + +export const FX_blur = defineImageEffectorFx({ + id: 'blur', + name: i18n.ts._imageEffector._fxs.blur, + shader, + uniforms: ['radius'] as const, + params: { + radius: { + label: i18n.ts._imageEffector._fxProps.radius, + type: 'number', + default: 3.0, + min: 0.0, + max: 10.0, + step: 0.5, + }, + }, + main: ({ gl, u, params }) => { + gl.uniform1f(u.radius, params.radius); + }, +}); \ No newline at end of file From bb2ce2552bf37a4e92c4639c37baa86cdd4a057f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:17:17 +0000 Subject: [PATCH 3/4] Improve blur quality with configurable sample count Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- locales/en-US.yml | 1 + locales/ja-JP.yml | 1 + .../src/utility/image-effector/fxs/blur.ts | 67 ++++++++++++------- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index e54060c267..faa7f5e59e 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -3211,6 +3211,7 @@ _imageEffector: scale: "Size" size: "Size" radius: "Radius" + samples: "Samples" color: "Color" opacity: "Opacity" normalize: "Normalize" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index dcb1701a06..16d1fe0ac6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -3325,6 +3325,7 @@ _imageEffector: scale: "サイズ" size: "サイズ" radius: "半径" + samples: "サンプル数" offset: "位置" color: "色" opacity: "不透明度" diff --git a/packages/frontend/src/utility/image-effector/fxs/blur.ts b/packages/frontend/src/utility/image-effector/fxs/blur.ts index 31324e08d3..3a6fd333fe 100644 --- a/packages/frontend/src/utility/image-effector/fxs/blur.ts +++ b/packages/frontend/src/utility/image-effector/fxs/blur.ts @@ -13,35 +13,47 @@ in vec2 in_uv; uniform sampler2D in_texture; uniform vec2 in_resolution; uniform float u_radius; +uniform int u_samples; out vec4 out_color; void main() { vec2 texelSize = 1.0 / in_resolution; vec4 result = vec4(0.0); + float totalWeight = 0.0; - // Simple box blur with fixed kernel size for better performance - // This avoids dynamic loops which can be slow on some GPUs - float radius = min(u_radius, 8.0); // Clamp to reasonable maximum + // High-quality blur with configurable sample count + int samples = min(u_samples, 128); // Clamp to reasonable maximum + int radius = int(u_radius); - // Sample in a cross pattern for efficiency - result += texture(in_texture, in_uv); // Center + // Use a more sophisticated sampling pattern for better quality + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + // Calculate distance from center for weighting + float distance = length(vec2(float(x), float(y))); + + // Skip samples beyond the circular radius + if (distance > u_radius) continue; + + // Calculate sample position + vec2 offset = vec2(float(x), float(y)) * texelSize; + vec2 sampleUV = in_uv + offset; + + // Only sample if within texture bounds + if (sampleUV.x >= 0.0 && sampleUV.x <= 1.0 && sampleUV.y >= 0.0 && sampleUV.y <= 1.0) { + // Use Gaussian-like weighting for better quality + float weight = exp(-(distance * distance) / (2.0 * (u_radius * 0.5) * (u_radius * 0.5))); + result += texture(in_texture, sampleUV) * weight; + totalWeight += weight; + + // Limit actual samples processed for performance + samples--; + if (samples <= 0) break; + } + } + if (samples <= 0) break; + } - // Horizontal samples - result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, 0.0)); - result += texture(in_texture, in_uv + vec2(radius * texelSize.x, 0.0)); - - // Vertical samples - result += texture(in_texture, in_uv + vec2(0.0, -radius * texelSize.y)); - result += texture(in_texture, in_uv + vec2(0.0, radius * texelSize.y)); - - // Diagonal samples for better quality - result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, -radius * texelSize.y)); - result += texture(in_texture, in_uv + vec2(radius * texelSize.x, -radius * texelSize.y)); - result += texture(in_texture, in_uv + vec2(-radius * texelSize.x, radius * texelSize.y)); - result += texture(in_texture, in_uv + vec2(radius * texelSize.x, radius * texelSize.y)); - - // Average the samples - out_color = result / 9.0; + out_color = totalWeight > 0.0 ? result / totalWeight : texture(in_texture, in_uv); } `; @@ -49,18 +61,27 @@ export const FX_blur = defineImageEffectorFx({ id: 'blur', name: i18n.ts._imageEffector._fxs.blur, shader, - uniforms: ['radius'] as const, + uniforms: ['radius', 'samples'] as const, params: { radius: { label: i18n.ts._imageEffector._fxProps.radius, type: 'number', default: 3.0, min: 0.0, - max: 10.0, + max: 15.0, step: 0.5, }, + samples: { + label: i18n.ts._imageEffector._fxProps.samples, + type: 'number', + default: 64, + min: 9, + max: 128, + step: 1, + }, }, main: ({ gl, u, params }) => { gl.uniform1f(u.radius, params.radius); + gl.uniform1i(u.samples, params.samples); }, }); \ No newline at end of file From c33d34939096b2fc4e7d4c4f387a3f19f7d4d1cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:36:45 +0000 Subject: [PATCH 4/4] Revert to simpler blur implementation with configurable sample count Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- .../src/utility/image-effector/fxs/blur.ts | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/packages/frontend/src/utility/image-effector/fxs/blur.ts b/packages/frontend/src/utility/image-effector/fxs/blur.ts index 3a6fd333fe..1e78df16b8 100644 --- a/packages/frontend/src/utility/image-effector/fxs/blur.ts +++ b/packages/frontend/src/utility/image-effector/fxs/blur.ts @@ -19,41 +19,31 @@ out vec4 out_color; void main() { vec2 texelSize = 1.0 / in_resolution; vec4 result = vec4(0.0); - float totalWeight = 0.0; + float totalSamples = 0.0; - // High-quality blur with configurable sample count - int samples = min(u_samples, 128); // Clamp to reasonable maximum - int radius = int(u_radius); + // Simple box blur with configurable sample count + // Based on the original cross pattern but with more samples + int samples = min(u_samples, 128); + float radius = u_radius; - // Use a more sophisticated sampling pattern for better quality - for (int x = -radius; x <= radius; x++) { - for (int y = -radius; y <= radius; y++) { - // Calculate distance from center for weighting - float distance = length(vec2(float(x), float(y))); - - // Skip samples beyond the circular radius - if (distance > u_radius) continue; - - // Calculate sample position - vec2 offset = vec2(float(x), float(y)) * texelSize; + // Calculate how many samples to take in each direction + int sampleRadius = int(sqrt(float(samples)) / 2.0); + + // Sample in a grid pattern around the center + for (int x = -sampleRadius; x <= sampleRadius; x++) { + for (int y = -sampleRadius; y <= sampleRadius; y++) { + vec2 offset = vec2(float(x), float(y)) * texelSize * radius; vec2 sampleUV = in_uv + offset; // Only sample if within texture bounds if (sampleUV.x >= 0.0 && sampleUV.x <= 1.0 && sampleUV.y >= 0.0 && sampleUV.y <= 1.0) { - // Use Gaussian-like weighting for better quality - float weight = exp(-(distance * distance) / (2.0 * (u_radius * 0.5) * (u_radius * 0.5))); - result += texture(in_texture, sampleUV) * weight; - totalWeight += weight; - - // Limit actual samples processed for performance - samples--; - if (samples <= 0) break; + result += texture(in_texture, sampleUV); + totalSamples += 1.0; } } - if (samples <= 0) break; } - out_color = totalWeight > 0.0 ? result / totalWeight : texture(in_texture, in_uv); + out_color = totalSamples > 0.0 ? result / totalSamples : texture(in_texture, in_uv); } `;