diff --git a/locales/en-US.yml b/locales/en-US.yml index 9c02e83021..faa7f5e59e 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,8 @@ _imageEffector: angle: "Angle" 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 4ae52990e5..16d1fe0ac6 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,8 @@ _imageEffector: angle: "角度" scale: "サイズ" size: "サイズ" + radius: "半径" + samples: "サンプル数" 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..1e78df16b8 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/blur.ts @@ -0,0 +1,77 @@ +/* + * 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; +uniform int u_samples; +out vec4 out_color; + +void main() { + vec2 texelSize = 1.0 / in_resolution; + vec4 result = vec4(0.0); + float totalSamples = 0.0; + + // 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; + + // 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) { + result += texture(in_texture, sampleUV); + totalSamples += 1.0; + } + } + } + + out_color = totalSamples > 0.0 ? result / totalSamples : texture(in_texture, in_uv); +} +`; + +export const FX_blur = defineImageEffectorFx({ + id: 'blur', + name: i18n.ts._imageEffector._fxs.blur, + shader, + uniforms: ['radius', 'samples'] as const, + params: { + radius: { + label: i18n.ts._imageEffector._fxProps.radius, + type: 'number', + default: 3.0, + min: 0.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