From a1ab2fa38c2b7485c069f9cd089bc7de59416c9d Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Tue, 3 Jun 2025 22:54:25 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=E3=82=B7=E3=82=A7=E3=83=BC?= =?UTF-8?q?=E3=83=80=E3=83=BC=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92?= =?UTF-8?q?lazy-loading=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MkImageEffectorDialog.vue | 12 +-- .../src/components/MkUploaderDialog.vue | 2 +- .../pages/settings/drive.WatermarkItem.vue | 4 +- .../utility/image-effector/ImageEffector.ts | 41 +++++++--- .../src/utility/image-effector/fxs.ts | 4 +- .../utility/image-effector/fxs/checker.glsl | 43 ++++++++++ .../src/utility/image-effector/fxs/checker.ts | 42 +--------- .../fxs/chromaticAberration.glsl | 48 +++++++++++ .../image-effector/fxs/chromaticAberration.ts | 47 +---------- .../image-effector/fxs/colorClamp.glsl | 22 +++++ .../utility/image-effector/fxs/colorClamp.ts | 21 +---- .../fxs/colorClampAdvanced.glsl | 26 ++++++ .../image-effector/fxs/colorClampAdvanced.ts | 25 +----- .../utility/image-effector/fxs/distort.glsl | 26 ++++++ .../src/utility/image-effector/fxs/distort.ts | 25 +----- .../utility/image-effector/fxs/glitch.glsl | 33 ++++++++ .../src/utility/image-effector/fxs/glitch.ts | 32 +------- .../utility/image-effector/fxs/grayscale.glsl | 22 +++++ .../utility/image-effector/fxs/grayscale.ts | 21 +---- .../utility/image-effector/fxs/invert.glsl | 23 ++++++ .../src/utility/image-effector/fxs/invert.ts | 22 +---- .../utility/image-effector/fxs/mirror.glsl | 31 +++++++ .../src/utility/image-effector/fxs/mirror.ts | 30 +------ .../utility/image-effector/fxs/polkadot.glsl | 75 +++++++++++++++++ .../utility/image-effector/fxs/polkadot.ts | 74 +---------------- .../utility/image-effector/fxs/stripe.glsl | 45 +++++++++++ .../src/utility/image-effector/fxs/stripe.ts | 44 +--------- .../utility/image-effector/fxs/threshold.glsl | 23 ++++++ .../utility/image-effector/fxs/threshold.ts | 22 +---- .../fxs/watermarkPlacement.glsl | 80 +++++++++++++++++++ .../image-effector/fxs/watermarkPlacement.ts | 79 +----------------- .../utility/image-effector/fxs/zoomLines.glsl | 33 ++++++++ .../utility/image-effector/fxs/zoomLines.ts | 32 +------- packages/frontend/src/utility/watermark.ts | 6 +- 34 files changed, 586 insertions(+), 529 deletions(-) create mode 100644 packages/frontend/src/utility/image-effector/fxs/checker.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/chromaticAberration.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/colorClamp.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/distort.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/glitch.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/grayscale.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/invert.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/mirror.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/polkadot.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/stripe.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/threshold.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.glsl create mode 100644 packages/frontend/src/utility/image-effector/fxs/zoomLines.glsl diff --git a/packages/frontend/src/components/MkImageEffectorDialog.vue b/packages/frontend/src/components/MkImageEffectorDialog.vue index 42502ba449..de2c92bbd6 100644 --- a/packages/frontend/src/components/MkImageEffectorDialog.vue +++ b/packages/frontend/src/components/MkImageEffectorDialog.vue @@ -166,7 +166,7 @@ onMounted(async () => { await renderer.setLayers(layers); - renderer.render(); + await renderer.render(); closeWaiting(); }); @@ -193,7 +193,7 @@ async function save() { await nextTick(); // waitingがレンダリングされるまで待つ renderer.changeResolution(imageBitmap.width, imageBitmap.height); // 本番レンダリングのためオリジナル画質に戻す - renderer.render(); // toBlobの直前にレンダリングしないと何故か壊れる + await renderer.render(); // toBlobの直前にレンダリングしないと何故か壊れる canvasEl.value.toBlob((blob) => { emit('ok', new File([blob!], `image-${Date.now()}.png`, { type: 'image/png' })); dialog.value?.close(); @@ -202,14 +202,14 @@ async function save() { } const enabled = ref(true); -watch(enabled, () => { +watch(enabled, async () => { if (renderer != null) { if (enabled.value) { - renderer.setLayers(layers); + await renderer.setLayers(layers); } else { - renderer.setLayers([]); + await renderer.setLayers([]); } - renderer.render(); + await renderer.render(); } }); diff --git a/packages/frontend/src/components/MkUploaderDialog.vue b/packages/frontend/src/components/MkUploaderDialog.vue index 77ee36b2a2..6096cb858a 100644 --- a/packages/frontend/src/components/MkUploaderDialog.vue +++ b/packages/frontend/src/components/MkUploaderDialog.vue @@ -560,7 +560,7 @@ async function preprocess(item: (typeof items)['value'][number]): Promise await renderer.setLayers(preset.layers); - renderer.render(); + await renderer.render(); file = await new Promise((resolve) => { canvas.toBlob((blob) => { diff --git a/packages/frontend/src/pages/settings/drive.WatermarkItem.vue b/packages/frontend/src/pages/settings/drive.WatermarkItem.vue index b466f35fc5..7123b214f1 100644 --- a/packages/frontend/src/pages/settings/drive.WatermarkItem.vue +++ b/packages/frontend/src/pages/settings/drive.WatermarkItem.vue @@ -80,7 +80,7 @@ onMounted(() => { await renderer.setLayers(props.preset.layers); - renderer.render(); + await renderer.render(); }, { immediate: true }); }; }); @@ -95,7 +95,7 @@ onUnmounted(() => { watch(() => props.preset, async () => { if (renderer != null) { await renderer.setLayers(props.preset.layers); - renderer.render(); + await renderer.render(); } }, { deep: true }); diff --git a/packages/frontend/src/utility/image-effector/ImageEffector.ts b/packages/frontend/src/utility/image-effector/ImageEffector.ts index 472d21bb1b..16169d359a 100644 --- a/packages/frontend/src/utility/image-effector/ImageEffector.ts +++ b/packages/frontend/src/utility/image-effector/ImageEffector.ts @@ -27,7 +27,7 @@ export function defineImageEffectorFx = { id: ID; name: string; - shader: string; + shader: string | (() => Promise); uniforms: US; params: PS, main: (ctx: { @@ -196,22 +196,37 @@ export class ImageEffector fx.id === layer.fxId); if (fx == null) return; const cachedShader = this.shaderCache.get(fx.id); - const shaderProgram = cachedShader ?? this.initShaderProgram(`#version 300 es - in vec2 position; - out vec2 in_uv; + let shaderProgram: WebGLProgram; - void main() { - in_uv = (position + 1.0) / 2.0; - gl_Position = vec4(position, 0.0, 1.0); + if (cachedShader != null) { + shaderProgram = cachedShader; + } else { + let shaderSource: string; + + if (typeof fx.shader === 'string') { + shaderSource = fx.shader; + } else { + shaderSource = await fx.shader(); } - `, fx.shader); + + shaderProgram = this.initShaderProgram(`#version 300 es + in vec2 position; + out vec2 in_uv; + + void main() { + in_uv = (position + 1.0) / 2.0; + gl_Position = vec4(position, 0.0, 1.0); + } + `, shaderSource); + } + if (cachedShader == null) { this.shaderCache.set(fx.id, shaderProgram); } @@ -230,7 +245,7 @@ export class ImageEffector { + Object.entries(fx.params as ImageEffectorFxParamDefs).map(([key, param]) => { return [key, layer.params[key] ?? param.default]; }), ), @@ -238,7 +253,7 @@ export class ImageEffector { + Object.entries(fx.params as ImageEffectorFxParamDefs).map(([k, v]) => { if (v.type !== 'texture') return [k, null]; const param = getValue(layer.params, k); if (param == null) return [k, null]; @@ -250,7 +265,7 @@ export class ImageEffector[]; +] satisfies ReadonlyArray>; export const WATERMARK_FXS = [ FX_watermarkPlacement, FX_stripe, FX_polkadot, FX_checker, -] as const satisfies ImageEffectorFx[]; +] satisfies ReadonlyArray>; diff --git a/packages/frontend/src/utility/image-effector/fxs/checker.glsl b/packages/frontend/src/utility/image-effector/fxs/checker.glsl new file mode 100644 index 0000000000..b1625e6b17 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/checker.glsl @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +const float PI = 3.141592653589793; +const float TWO_PI = 6.283185307179586; +const float HALF_PI = 1.5707963267948966; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform float u_angle; +uniform float u_scale; +uniform vec3 u_color; +uniform float u_opacity; +out vec4 out_color; + +void main() { + vec4 in_color = texture(in_texture, in_uv); + float x_ratio = max(in_resolution.x / in_resolution.y, 1.0); + float y_ratio = max(in_resolution.y / in_resolution.x, 1.0); + + float angle = -(u_angle * PI); + vec2 centeredUv = (in_uv - vec2(0.5, 0.5)) * vec2(x_ratio, y_ratio); + vec2 rotatedUV = vec2( + centeredUv.x * cos(angle) - centeredUv.y * sin(angle), + centeredUv.x * sin(angle) + centeredUv.y * cos(angle) + ); + + float fmodResult = mod(floor(u_scale * rotatedUV.x) + floor(u_scale * rotatedUV.y), 2.0); + float fin = max(sign(fmodResult), 0.0); + + out_color = vec4( + mix(in_color.r, u_color.r, fin * u_opacity), + mix(in_color.g, u_color.g, fin * u_opacity), + mix(in_color.b, u_color.b, fin * u_opacity), + in_color.a + ); +} diff --git a/packages/frontend/src/utility/image-effector/fxs/checker.ts b/packages/frontend/src/utility/image-effector/fxs/checker.ts index b799bd0d13..9b1210fd56 100644 --- a/packages/frontend/src/utility/image-effector/fxs/checker.ts +++ b/packages/frontend/src/utility/image-effector/fxs/checker.ts @@ -6,50 +6,10 @@ import { defineImageEffectorFx } from '../ImageEffector.js'; import { i18n } from '@/i18n.js'; -const shader = `#version 300 es -precision mediump float; - -const float PI = 3.141592653589793; -const float TWO_PI = 6.283185307179586; -const float HALF_PI = 1.5707963267948966; - -in vec2 in_uv; -uniform sampler2D in_texture; -uniform vec2 in_resolution; -uniform float u_angle; -uniform float u_scale; -uniform vec3 u_color; -uniform float u_opacity; -out vec4 out_color; - -void main() { - vec4 in_color = texture(in_texture, in_uv); - float x_ratio = max(in_resolution.x / in_resolution.y, 1.0); - float y_ratio = max(in_resolution.y / in_resolution.x, 1.0); - - float angle = -(u_angle * PI); - vec2 centeredUv = (in_uv - vec2(0.5, 0.5)) * vec2(x_ratio, y_ratio); - vec2 rotatedUV = vec2( - centeredUv.x * cos(angle) - centeredUv.y * sin(angle), - centeredUv.x * sin(angle) + centeredUv.y * cos(angle) - ); - - float fmodResult = mod(floor(u_scale * rotatedUV.x) + floor(u_scale * rotatedUV.y), 2.0); - float fin = max(sign(fmodResult), 0.0); - - out_color = vec4( - mix(in_color.r, u_color.r, fin * u_opacity), - mix(in_color.g, u_color.g, fin * u_opacity), - mix(in_color.b, u_color.b, fin * u_opacity), - in_color.a - ); -} -`; - export const FX_checker = defineImageEffectorFx({ id: 'checker' as const, name: i18n.ts._imageEffector._fxs.checker, - shader, + shader: () => import('./checker.glsl?raw').then(m => m.default), uniforms: ['angle', 'scale', 'color', 'opacity'] as const, params: { angle: { diff --git a/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.glsl b/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.glsl new file mode 100644 index 0000000000..cc5d3ddaf3 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.glsl @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +out vec4 out_color; +uniform float u_amount; +uniform float u_start; +uniform bool u_normalize; + +void main() { + int samples = 64; + float r_strength = 1.0; + float g_strength = 1.5; + float b_strength = 2.0; + + vec2 size = vec2(in_resolution.x, in_resolution.y); + + vec4 accumulator = vec4(0.0); + float normalisedValue = length((in_uv - 0.5) * 2.0); + float strength = clamp((normalisedValue - u_start) * (1.0 / (1.0 - u_start)), 0.0, 1.0); + + vec2 vector = (u_normalize ? normalize(in_uv - vec2(0.5)) : in_uv - vec2(0.5)); + vec2 velocity = vector * strength * u_amount; + + vec2 rOffset = -vector * strength * (u_amount * r_strength); + vec2 gOffset = -vector * strength * (u_amount * g_strength); + vec2 bOffset = -vector * strength * (u_amount * b_strength); + + for (int i = 0; i < samples; i++) { + accumulator.r += texture(in_texture, in_uv + rOffset).r; + rOffset -= velocity / float(samples); + + accumulator.g += texture(in_texture, in_uv + gOffset).g; + gOffset -= velocity / float(samples); + + accumulator.b += texture(in_texture, in_uv + bOffset).b; + bOffset -= velocity / float(samples); + } + + out_color = vec4(vec3(accumulator / float(samples)), 1.0); +} diff --git a/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.ts b/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.ts index 82d7d883aa..905d82bd6d 100644 --- a/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.ts +++ b/packages/frontend/src/utility/image-effector/fxs/chromaticAberration.ts @@ -6,55 +6,10 @@ 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; -out vec4 out_color; -uniform float u_amount; -uniform float u_start; -uniform bool u_normalize; - -void main() { - int samples = 64; - float r_strength = 1.0; - float g_strength = 1.5; - float b_strength = 2.0; - - vec2 size = vec2(in_resolution.x, in_resolution.y); - - vec4 accumulator = vec4(0.0); - float normalisedValue = length((in_uv - 0.5) * 2.0); - float strength = clamp((normalisedValue - u_start) * (1.0 / (1.0 - u_start)), 0.0, 1.0); - - vec2 vector = (u_normalize ? normalize(in_uv - vec2(0.5)) : in_uv - vec2(0.5)); - vec2 velocity = vector * strength * u_amount; - - vec2 rOffset = -vector * strength * (u_amount * r_strength); - vec2 gOffset = -vector * strength * (u_amount * g_strength); - vec2 bOffset = -vector * strength * (u_amount * b_strength); - - for (int i = 0; i < samples; i++) { - accumulator.r += texture(in_texture, in_uv + rOffset).r; - rOffset -= velocity / float(samples); - - accumulator.g += texture(in_texture, in_uv + gOffset).g; - gOffset -= velocity / float(samples); - - accumulator.b += texture(in_texture, in_uv + bOffset).b; - bOffset -= velocity / float(samples); - } - - out_color = vec4(vec3(accumulator / float(samples)), 1.0); -} -`; - export const FX_chromaticAberration = defineImageEffectorFx({ id: 'chromaticAberration' as const, name: i18n.ts._imageEffector._fxs.chromaticAberration, - shader, + shader: () => import('./chromaticAberration.glsl?raw').then(m => m.default), uniforms: ['amount', 'start', 'normalize'] as const, params: { normalize: { diff --git a/packages/frontend/src/utility/image-effector/fxs/colorClamp.glsl b/packages/frontend/src/utility/image-effector/fxs/colorClamp.glsl new file mode 100644 index 0000000000..b069124177 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/colorClamp.glsl @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform float u_max; +uniform float u_min; +out vec4 out_color; + +void main() { + vec4 in_color = texture(in_texture, in_uv); + float r = min(max(in_color.r, u_min), u_max); + float g = min(max(in_color.g, u_min), u_max); + float b = min(max(in_color.b, u_min), u_max); + out_color = vec4(r, g, b, in_color.a); +} diff --git a/packages/frontend/src/utility/image-effector/fxs/colorClamp.ts b/packages/frontend/src/utility/image-effector/fxs/colorClamp.ts index 5393d73df0..b53def8a81 100644 --- a/packages/frontend/src/utility/image-effector/fxs/colorClamp.ts +++ b/packages/frontend/src/utility/image-effector/fxs/colorClamp.ts @@ -6,29 +6,10 @@ 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_max; -uniform float u_min; -out vec4 out_color; - -void main() { - vec4 in_color = texture(in_texture, in_uv); - float r = min(max(in_color.r, u_min), u_max); - float g = min(max(in_color.g, u_min), u_max); - float b = min(max(in_color.b, u_min), u_max); - out_color = vec4(r, g, b, in_color.a); -} -`; - export const FX_colorClamp = defineImageEffectorFx({ id: 'colorClamp' as const, name: i18n.ts._imageEffector._fxs.colorClamp, - shader, + shader: () => import('./colorClamp.glsl?raw').then(m => m.default), uniforms: ['max', 'min'] as const, params: { max: { diff --git a/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.glsl b/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.glsl new file mode 100644 index 0000000000..f9c0ed9d4d --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.glsl @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform float u_rMax; +uniform float u_rMin; +uniform float u_gMax; +uniform float u_gMin; +uniform float u_bMax; +uniform float u_bMin; +out vec4 out_color; + +void main() { + vec4 in_color = texture(in_texture, in_uv); + float r = min(max(in_color.r, u_rMin), u_rMax); + float g = min(max(in_color.g, u_gMin), u_gMax); + float b = min(max(in_color.b, u_bMin), u_bMax); + out_color = vec4(r, g, b, in_color.a); +} diff --git a/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.ts b/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.ts index c66d75a83f..ea318ab0e2 100644 --- a/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.ts +++ b/packages/frontend/src/utility/image-effector/fxs/colorClampAdvanced.ts @@ -6,33 +6,10 @@ 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_rMax; -uniform float u_rMin; -uniform float u_gMax; -uniform float u_gMin; -uniform float u_bMax; -uniform float u_bMin; -out vec4 out_color; - -void main() { - vec4 in_color = texture(in_texture, in_uv); - float r = min(max(in_color.r, u_rMin), u_rMax); - float g = min(max(in_color.g, u_gMin), u_gMax); - float b = min(max(in_color.b, u_bMin), u_bMax); - out_color = vec4(r, g, b, in_color.a); -} -`; - export const FX_colorClampAdvanced = defineImageEffectorFx({ id: 'colorClampAdvanced' as const, name: i18n.ts._imageEffector._fxs.colorClampAdvanced, - shader, + shader: () => import('./colorClampAdvanced.glsl?raw').then(m => m.default), uniforms: ['rMax', 'rMin', 'gMax', 'gMin', 'bMax', 'bMin'] as const, params: { rMax: { diff --git a/packages/frontend/src/utility/image-effector/fxs/distort.glsl b/packages/frontend/src/utility/image-effector/fxs/distort.glsl new file mode 100644 index 0000000000..9f27fda5c9 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/distort.glsl @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform float u_phase; +uniform float u_frequency; +uniform float u_strength; +uniform int u_direction; // 0: vertical, 1: horizontal +out vec4 out_color; + +void main() { + float v = u_direction == 0 ? + sin(u_phase + in_uv.y * u_frequency) * u_strength : + sin(u_phase + in_uv.x * u_frequency) * u_strength; + vec4 in_color = u_direction == 0 ? + texture(in_texture, vec2(in_uv.x + v, in_uv.y)) : + texture(in_texture, vec2(in_uv.x, in_uv.y + v)); + out_color = in_color; +} diff --git a/packages/frontend/src/utility/image-effector/fxs/distort.ts b/packages/frontend/src/utility/image-effector/fxs/distort.ts index f91287c038..21ec4e130a 100644 --- a/packages/frontend/src/utility/image-effector/fxs/distort.ts +++ b/packages/frontend/src/utility/image-effector/fxs/distort.ts @@ -6,33 +6,10 @@ 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_phase; -uniform float u_frequency; -uniform float u_strength; -uniform int u_direction; // 0: vertical, 1: horizontal -out vec4 out_color; - -void main() { - float v = u_direction == 0 ? - sin(u_phase + in_uv.y * u_frequency) * u_strength : - sin(u_phase + in_uv.x * u_frequency) * u_strength; - vec4 in_color = u_direction == 0 ? - texture(in_texture, vec2(in_uv.x + v, in_uv.y)) : - texture(in_texture, vec2(in_uv.x, in_uv.y + v)); - out_color = in_color; -} -`; - export const FX_distort = defineImageEffectorFx({ id: 'distort' as const, name: i18n.ts._imageEffector._fxs.distort, - shader, + shader: () => import('./distort.glsl?raw').then(m => m.default), uniforms: ['phase', 'frequency', 'strength', 'direction'] as const, params: { direction: { diff --git a/packages/frontend/src/utility/image-effector/fxs/glitch.glsl b/packages/frontend/src/utility/image-effector/fxs/glitch.glsl new file mode 100644 index 0000000000..1ce8c21d63 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/glitch.glsl @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform int u_amount; +uniform float u_shiftStrengths[128]; +uniform float u_shiftOrigins[128]; +uniform float u_shiftHeights[128]; +uniform float u_channelShift; +out vec4 out_color; + +void main() { + float v = 0.0; + + for (int i = 0; i < u_amount; i++) { + if (in_uv.y > (u_shiftOrigins[i] - u_shiftHeights[i]) && in_uv.y < (u_shiftOrigins[i] + u_shiftHeights[i])) { + v += u_shiftStrengths[i]; + } + } + + float r = texture(in_texture, vec2(in_uv.x + (v * (1.0 + u_channelShift)), in_uv.y)).r; + float g = texture(in_texture, vec2(in_uv.x + v, in_uv.y)).g; + float b = texture(in_texture, vec2(in_uv.x + (v * (1.0 + (u_channelShift / 2.0))), in_uv.y)).b; + float a = texture(in_texture, vec2(in_uv.x + v, in_uv.y)).a; + out_color = vec4(r, g, b, a); +} diff --git a/packages/frontend/src/utility/image-effector/fxs/glitch.ts b/packages/frontend/src/utility/image-effector/fxs/glitch.ts index e4939a4302..17b898b249 100644 --- a/packages/frontend/src/utility/image-effector/fxs/glitch.ts +++ b/packages/frontend/src/utility/image-effector/fxs/glitch.ts @@ -7,40 +7,10 @@ import seedrandom from 'seedrandom'; 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 int u_amount; -uniform float u_shiftStrengths[128]; -uniform float u_shiftOrigins[128]; -uniform float u_shiftHeights[128]; -uniform float u_channelShift; -out vec4 out_color; - -void main() { - float v = 0.0; - - for (int i = 0; i < u_amount; i++) { - if (in_uv.y > (u_shiftOrigins[i] - u_shiftHeights[i]) && in_uv.y < (u_shiftOrigins[i] + u_shiftHeights[i])) { - v += u_shiftStrengths[i]; - } - } - - float r = texture(in_texture, vec2(in_uv.x + (v * (1.0 + u_channelShift)), in_uv.y)).r; - float g = texture(in_texture, vec2(in_uv.x + v, in_uv.y)).g; - float b = texture(in_texture, vec2(in_uv.x + (v * (1.0 + (u_channelShift / 2.0))), in_uv.y)).b; - float a = texture(in_texture, vec2(in_uv.x + v, in_uv.y)).a; - out_color = vec4(r, g, b, a); -} -`; - export const FX_glitch = defineImageEffectorFx({ id: 'glitch' as const, name: i18n.ts._imageEffector._fxs.glitch, - shader, + shader: () => import('./glitch.glsl?raw').then(m => m.default), uniforms: ['amount', 'channelShift'] as const, params: { amount: { diff --git a/packages/frontend/src/utility/image-effector/fxs/grayscale.glsl b/packages/frontend/src/utility/image-effector/fxs/grayscale.glsl new file mode 100644 index 0000000000..4d37e33961 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/grayscale.glsl @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +out vec4 out_color; + +float getBrightness(vec4 color) { + return (color.r + color.g + color.b) / 3.0; +} + +void main() { + vec4 in_color = texture(in_texture, in_uv); + float brightness = getBrightness(in_color); + out_color = vec4(brightness, brightness, brightness, in_color.a); +} diff --git a/packages/frontend/src/utility/image-effector/fxs/grayscale.ts b/packages/frontend/src/utility/image-effector/fxs/grayscale.ts index 8f33706ae7..38c2230a27 100644 --- a/packages/frontend/src/utility/image-effector/fxs/grayscale.ts +++ b/packages/frontend/src/utility/image-effector/fxs/grayscale.ts @@ -6,29 +6,10 @@ 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; -out vec4 out_color; - -float getBrightness(vec4 color) { - return (color.r + color.g + color.b) / 3.0; -} - -void main() { - vec4 in_color = texture(in_texture, in_uv); - float brightness = getBrightness(in_color); - out_color = vec4(brightness, brightness, brightness, in_color.a); -} -`; - export const FX_grayscale = defineImageEffectorFx({ id: 'grayscale' as const, name: i18n.ts._imageEffector._fxs.grayscale, - shader, + shader: () => import('./grayscale.glsl?raw').then(m => m.default), uniforms: [] as const, params: { }, diff --git a/packages/frontend/src/utility/image-effector/fxs/invert.glsl b/packages/frontend/src/utility/image-effector/fxs/invert.glsl new file mode 100644 index 0000000000..38726d3c87 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/invert.glsl @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform bool u_r; +uniform bool u_g; +uniform bool u_b; +out vec4 out_color; + +void main() { + vec4 in_color = texture(in_texture, in_uv); + out_color.r = u_r ? 1.0 - in_color.r : in_color.r; + out_color.g = u_g ? 1.0 - in_color.g : in_color.g; + out_color.b = u_b ? 1.0 - in_color.b : in_color.b; + out_color.a = in_color.a; +} diff --git a/packages/frontend/src/utility/image-effector/fxs/invert.ts b/packages/frontend/src/utility/image-effector/fxs/invert.ts index 220a2dea30..65e6e8d4c7 100644 --- a/packages/frontend/src/utility/image-effector/fxs/invert.ts +++ b/packages/frontend/src/utility/image-effector/fxs/invert.ts @@ -6,30 +6,10 @@ 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 bool u_r; -uniform bool u_g; -uniform bool u_b; -out vec4 out_color; - -void main() { - vec4 in_color = texture(in_texture, in_uv); - out_color.r = u_r ? 1.0 - in_color.r : in_color.r; - out_color.g = u_g ? 1.0 - in_color.g : in_color.g; - out_color.b = u_b ? 1.0 - in_color.b : in_color.b; - out_color.a = in_color.a; -} -`; - export const FX_invert = defineImageEffectorFx({ id: 'invert' as const, name: i18n.ts._imageEffector._fxs.invert, - shader, + shader: () => import('./invert.glsl?raw').then(m => m.default), uniforms: ['r', 'g', 'b'] as const, params: { r: { diff --git a/packages/frontend/src/utility/image-effector/fxs/mirror.glsl b/packages/frontend/src/utility/image-effector/fxs/mirror.glsl new file mode 100644 index 0000000000..4ef807aaba --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/mirror.glsl @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform int u_h; +uniform int u_v; +out vec4 out_color; + +void main() { + vec2 uv = in_uv; + if (u_h == -1 && in_uv.x > 0.5) { + uv.x = 1.0 - uv.x; + } + if (u_h == 1 && in_uv.x < 0.5) { + uv.x = 1.0 - uv.x; + } + if (u_v == -1 && in_uv.y > 0.5) { + uv.y = 1.0 - uv.y; + } + if (u_v == 1 && in_uv.y < 0.5) { + uv.y = 1.0 - uv.y; + } + out_color = texture(in_texture, uv); +} diff --git a/packages/frontend/src/utility/image-effector/fxs/mirror.ts b/packages/frontend/src/utility/image-effector/fxs/mirror.ts index 5946a2e0dc..4220a6ac62 100644 --- a/packages/frontend/src/utility/image-effector/fxs/mirror.ts +++ b/packages/frontend/src/utility/image-effector/fxs/mirror.ts @@ -6,38 +6,10 @@ 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 int u_h; -uniform int u_v; -out vec4 out_color; - -void main() { - vec2 uv = in_uv; - if (u_h == -1 && in_uv.x > 0.5) { - uv.x = 1.0 - uv.x; - } - if (u_h == 1 && in_uv.x < 0.5) { - uv.x = 1.0 - uv.x; - } - if (u_v == -1 && in_uv.y > 0.5) { - uv.y = 1.0 - uv.y; - } - if (u_v == 1 && in_uv.y < 0.5) { - uv.y = 1.0 - uv.y; - } - out_color = texture(in_texture, uv); -} -`; - export const FX_mirror = defineImageEffectorFx({ id: 'mirror' as const, name: i18n.ts._imageEffector._fxs.mirror, - shader, + shader: () => import('./mirror.glsl?raw').then(m => m.default), uniforms: ['h', 'v'] as const, params: { h: { diff --git a/packages/frontend/src/utility/image-effector/fxs/polkadot.glsl b/packages/frontend/src/utility/image-effector/fxs/polkadot.glsl new file mode 100644 index 0000000000..a7c7940d70 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/polkadot.glsl @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +const float PI = 3.141592653589793; +const float TWO_PI = 6.283185307179586; +const float HALF_PI = 1.5707963267948966; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform float u_angle; +uniform float u_scale; +uniform float u_major_radius; +uniform float u_major_opacity; +uniform float u_minor_divisions; +uniform float u_minor_radius; +uniform float u_minor_opacity; +uniform vec3 u_color; +out vec4 out_color; + +void main() { + vec4 in_color = texture(in_texture, in_uv); + float x_ratio = max(in_resolution.x / in_resolution.y, 1.0); + float y_ratio = max(in_resolution.y / in_resolution.x, 1.0); + + float angle = -(u_angle * PI); + vec2 centeredUv = (in_uv - vec2(0.5, 0.5)) * vec2(x_ratio, y_ratio); + vec2 rotatedUV = vec2( + centeredUv.x * cos(angle) - centeredUv.y * sin(angle), + centeredUv.x * sin(angle) + centeredUv.y * cos(angle) + ); + + float major_modX = mod(rotatedUV.x, (1.0 / u_scale)); + float major_modY = mod(rotatedUV.y, (1.0 / u_scale)); + float major_threshold = ((u_major_radius / 2.0) / u_scale); + if ( + length(vec2(major_modX, major_modY)) < major_threshold || + length(vec2((1.0 / u_scale) - major_modX, major_modY)) < major_threshold || + length(vec2(major_modX, (1.0 / u_scale) - major_modY)) < major_threshold || + length(vec2((1.0 / u_scale) - major_modX, (1.0 / u_scale) - major_modY)) < major_threshold + ) { + out_color = vec4( + mix(in_color.r, u_color.r, u_major_opacity), + mix(in_color.g, u_color.g, u_major_opacity), + mix(in_color.b, u_color.b, u_major_opacity), + in_color.a + ); + return; + } + + float minor_modX = mod(rotatedUV.x, (1.0 / u_scale / u_minor_divisions)); + float minor_modY = mod(rotatedUV.y, (1.0 / u_scale / u_minor_divisions)); + float minor_threshold = ((u_minor_radius / 2.0) / (u_minor_divisions * u_scale)); + if ( + length(vec2(minor_modX, minor_modY)) < minor_threshold || + length(vec2((1.0 / u_scale / u_minor_divisions) - minor_modX, minor_modY)) < minor_threshold || + length(vec2(minor_modX, (1.0 / u_scale / u_minor_divisions) - minor_modY)) < minor_threshold || + length(vec2((1.0 / u_scale / u_minor_divisions) - minor_modX, (1.0 / u_scale / u_minor_divisions) - minor_modY)) < minor_threshold + ) { + out_color = vec4( + mix(in_color.r, u_color.r, u_minor_opacity), + mix(in_color.g, u_color.g, u_minor_opacity), + mix(in_color.b, u_color.b, u_minor_opacity), + in_color.a + ); + return; + } + + out_color = in_color; +} diff --git a/packages/frontend/src/utility/image-effector/fxs/polkadot.ts b/packages/frontend/src/utility/image-effector/fxs/polkadot.ts index 198dd9bad0..c6a4cff5cd 100644 --- a/packages/frontend/src/utility/image-effector/fxs/polkadot.ts +++ b/packages/frontend/src/utility/image-effector/fxs/polkadot.ts @@ -6,82 +6,10 @@ import { defineImageEffectorFx } from '../ImageEffector.js'; import { i18n } from '@/i18n.js'; -const shader = `#version 300 es -precision mediump float; - -const float PI = 3.141592653589793; -const float TWO_PI = 6.283185307179586; -const float HALF_PI = 1.5707963267948966; - -in vec2 in_uv; -uniform sampler2D in_texture; -uniform vec2 in_resolution; -uniform float u_angle; -uniform float u_scale; -uniform float u_major_radius; -uniform float u_major_opacity; -uniform float u_minor_divisions; -uniform float u_minor_radius; -uniform float u_minor_opacity; -uniform vec3 u_color; -out vec4 out_color; - -void main() { - vec4 in_color = texture(in_texture, in_uv); - float x_ratio = max(in_resolution.x / in_resolution.y, 1.0); - float y_ratio = max(in_resolution.y / in_resolution.x, 1.0); - - float angle = -(u_angle * PI); - vec2 centeredUv = (in_uv - vec2(0.5, 0.5)) * vec2(x_ratio, y_ratio); - vec2 rotatedUV = vec2( - centeredUv.x * cos(angle) - centeredUv.y * sin(angle), - centeredUv.x * sin(angle) + centeredUv.y * cos(angle) - ); - - float major_modX = mod(rotatedUV.x, (1.0 / u_scale)); - float major_modY = mod(rotatedUV.y, (1.0 / u_scale)); - float major_threshold = ((u_major_radius / 2.0) / u_scale); - if ( - length(vec2(major_modX, major_modY)) < major_threshold || - length(vec2((1.0 / u_scale) - major_modX, major_modY)) < major_threshold || - length(vec2(major_modX, (1.0 / u_scale) - major_modY)) < major_threshold || - length(vec2((1.0 / u_scale) - major_modX, (1.0 / u_scale) - major_modY)) < major_threshold - ) { - out_color = vec4( - mix(in_color.r, u_color.r, u_major_opacity), - mix(in_color.g, u_color.g, u_major_opacity), - mix(in_color.b, u_color.b, u_major_opacity), - in_color.a - ); - return; - } - - float minor_modX = mod(rotatedUV.x, (1.0 / u_scale / u_minor_divisions)); - float minor_modY = mod(rotatedUV.y, (1.0 / u_scale / u_minor_divisions)); - float minor_threshold = ((u_minor_radius / 2.0) / (u_minor_divisions * u_scale)); - if ( - length(vec2(minor_modX, minor_modY)) < minor_threshold || - length(vec2((1.0 / u_scale / u_minor_divisions) - minor_modX, minor_modY)) < minor_threshold || - length(vec2(minor_modX, (1.0 / u_scale / u_minor_divisions) - minor_modY)) < minor_threshold || - length(vec2((1.0 / u_scale / u_minor_divisions) - minor_modX, (1.0 / u_scale / u_minor_divisions) - minor_modY)) < minor_threshold - ) { - out_color = vec4( - mix(in_color.r, u_color.r, u_minor_opacity), - mix(in_color.g, u_color.g, u_minor_opacity), - mix(in_color.b, u_color.b, u_minor_opacity), - in_color.a - ); - return; - } - - out_color = in_color; -} -`; - export const FX_polkadot = defineImageEffectorFx({ id: 'polkadot' as const, name: i18n.ts._imageEffector._fxs.polkadot, - shader, + shader: () => import('./polkadot.glsl?raw').then(m => m.default), uniforms: ['angle', 'scale', 'major_radius', 'major_opacity', 'minor_divisions', 'minor_radius', 'minor_opacity', 'color'] as const, params: { angle: { diff --git a/packages/frontend/src/utility/image-effector/fxs/stripe.glsl b/packages/frontend/src/utility/image-effector/fxs/stripe.glsl new file mode 100644 index 0000000000..d54827658c --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/stripe.glsl @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +const float PI = 3.141592653589793; +const float TWO_PI = 6.283185307179586; +const float HALF_PI = 1.5707963267948966; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform float u_angle; +uniform float u_frequency; +uniform float u_phase; +uniform float u_threshold; +uniform vec3 u_color; +uniform float u_opacity; +out vec4 out_color; + +void main() { + vec4 in_color = texture(in_texture, in_uv); + float x_ratio = max(in_resolution.x / in_resolution.y, 1.0); + float y_ratio = max(in_resolution.y / in_resolution.x, 1.0); + + float angle = -(u_angle * PI); + vec2 centeredUv = (in_uv - vec2(0.5, 0.5)) * vec2(x_ratio, y_ratio); + vec2 rotatedUV = vec2( + centeredUv.x * cos(angle) - centeredUv.y * sin(angle), + centeredUv.x * sin(angle) + centeredUv.y * cos(angle) + ); + + float phase = u_phase * TWO_PI; + float value = (1.0 + sin((rotatedUV.x * u_frequency - HALF_PI) + phase)) / 2.0; + value = value < u_threshold ? 1.0 : 0.0; + out_color = vec4( + mix(in_color.r, u_color.r, value * u_opacity), + mix(in_color.g, u_color.g, value * u_opacity), + mix(in_color.b, u_color.b, value * u_opacity), + in_color.a + ); +} diff --git a/packages/frontend/src/utility/image-effector/fxs/stripe.ts b/packages/frontend/src/utility/image-effector/fxs/stripe.ts index 37766e185d..46fb98db5e 100644 --- a/packages/frontend/src/utility/image-effector/fxs/stripe.ts +++ b/packages/frontend/src/utility/image-effector/fxs/stripe.ts @@ -6,52 +6,10 @@ import { defineImageEffectorFx } from '../ImageEffector.js'; import { i18n } from '@/i18n.js'; -const shader = `#version 300 es -precision mediump float; - -const float PI = 3.141592653589793; -const float TWO_PI = 6.283185307179586; -const float HALF_PI = 1.5707963267948966; - -in vec2 in_uv; -uniform sampler2D in_texture; -uniform vec2 in_resolution; -uniform float u_angle; -uniform float u_frequency; -uniform float u_phase; -uniform float u_threshold; -uniform vec3 u_color; -uniform float u_opacity; -out vec4 out_color; - -void main() { - vec4 in_color = texture(in_texture, in_uv); - float x_ratio = max(in_resolution.x / in_resolution.y, 1.0); - float y_ratio = max(in_resolution.y / in_resolution.x, 1.0); - - float angle = -(u_angle * PI); - vec2 centeredUv = (in_uv - vec2(0.5, 0.5)) * vec2(x_ratio, y_ratio); - vec2 rotatedUV = vec2( - centeredUv.x * cos(angle) - centeredUv.y * sin(angle), - centeredUv.x * sin(angle) + centeredUv.y * cos(angle) - ); - - float phase = u_phase * TWO_PI; - float value = (1.0 + sin((rotatedUV.x * u_frequency - HALF_PI) + phase)) / 2.0; - value = value < u_threshold ? 1.0 : 0.0; - out_color = vec4( - mix(in_color.r, u_color.r, value * u_opacity), - mix(in_color.g, u_color.g, value * u_opacity), - mix(in_color.b, u_color.b, value * u_opacity), - in_color.a - ); -} -`; - export const FX_stripe = defineImageEffectorFx({ id: 'stripe' as const, name: i18n.ts._imageEffector._fxs.stripe, - shader, + shader: () => import('./stripe.glsl?raw').then(m => m.default), uniforms: ['angle', 'frequency', 'phase', 'threshold', 'color', 'opacity'] as const, params: { angle: { diff --git a/packages/frontend/src/utility/image-effector/fxs/threshold.glsl b/packages/frontend/src/utility/image-effector/fxs/threshold.glsl new file mode 100644 index 0000000000..f938b2d520 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/threshold.glsl @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform float u_r; +uniform float u_g; +uniform float u_b; +out vec4 out_color; + +void main() { + vec4 in_color = texture(in_texture, in_uv); + float r = in_color.r < u_r ? 0.0 : 1.0; + float g = in_color.g < u_g ? 0.0 : 1.0; + float b = in_color.b < u_b ? 0.0 : 1.0; + out_color = vec4(r, g, b, in_color.a); +} diff --git a/packages/frontend/src/utility/image-effector/fxs/threshold.ts b/packages/frontend/src/utility/image-effector/fxs/threshold.ts index f2b8b107fd..fc6d73dcb0 100644 --- a/packages/frontend/src/utility/image-effector/fxs/threshold.ts +++ b/packages/frontend/src/utility/image-effector/fxs/threshold.ts @@ -6,30 +6,10 @@ 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_r; -uniform float u_g; -uniform float u_b; -out vec4 out_color; - -void main() { - vec4 in_color = texture(in_texture, in_uv); - float r = in_color.r < u_r ? 0.0 : 1.0; - float g = in_color.g < u_g ? 0.0 : 1.0; - float b = in_color.b < u_b ? 0.0 : 1.0; - out_color = vec4(r, g, b, in_color.a); -} -`; - export const FX_threshold = defineImageEffectorFx({ id: 'threshold' as const, name: i18n.ts._imageEffector._fxs.threshold, - shader, + shader: () => import('./threshold.glsl?raw').then(m => m.default), uniforms: ['r', 'g', 'b'] as const, params: { r: { diff --git a/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.glsl b/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.glsl new file mode 100644 index 0000000000..1bcda303c5 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.glsl @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +const float PI = 3.141592653589793; +const float TWO_PI = 6.283185307179586; +const float HALF_PI = 1.5707963267948966; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform sampler2D u_texture_watermark; +uniform vec2 u_resolution_watermark; +uniform float u_scale; +uniform float u_angle; +uniform float u_opacity; +uniform bool u_repeat; +uniform int u_alignX; // 0: left, 1: center, 2: right +uniform int u_alignY; // 0: top, 1: center, 2: bottom +uniform int u_fitMode; // 0: contain, 1: cover +out vec4 out_color; + +void main() { + vec4 in_color = texture(in_texture, in_uv); + float in_x_ratio = max(in_resolution.x / in_resolution.y, 1.0); + float in_y_ratio = max(in_resolution.y / in_resolution.x, 1.0); + + bool contain = u_fitMode == 0; + + float x_ratio = u_resolution_watermark.x / in_resolution.x; + float y_ratio = u_resolution_watermark.y / in_resolution.y; + + float aspect_ratio = contain ? + (min(x_ratio, y_ratio) / max(x_ratio, y_ratio)) : + (max(x_ratio, y_ratio) / min(x_ratio, y_ratio)); + + float x_scale = contain ? + (x_ratio > y_ratio ? 1.0 * u_scale : aspect_ratio * u_scale) : + (x_ratio > y_ratio ? aspect_ratio * u_scale : 1.0 * u_scale); + + float y_scale = contain ? + (y_ratio > x_ratio ? 1.0 * u_scale : aspect_ratio * u_scale) : + (y_ratio > x_ratio ? aspect_ratio * u_scale : 1.0 * u_scale); + + float x_offset = u_alignX == 0 ? x_scale / 2.0 : u_alignX == 2 ? 1.0 - (x_scale / 2.0) : 0.5; + float y_offset = u_alignY == 0 ? y_scale / 2.0 : u_alignY == 2 ? 1.0 - (y_scale / 2.0) : 0.5; + + float angle = -(u_angle * PI); + vec2 center = vec2(x_offset, y_offset); + //vec2 centeredUv = (in_uv - center) * vec2(in_x_ratio, in_y_ratio); + vec2 centeredUv = (in_uv - center); + vec2 rotatedUV = vec2( + centeredUv.x * cos(angle) - centeredUv.y * sin(angle), + centeredUv.x * sin(angle) + centeredUv.y * cos(angle) + ) + center; + + // trim + if (!u_repeat) { + bool isInside = rotatedUV.x > x_offset - (x_scale / 2.0) && rotatedUV.x < x_offset + (x_scale / 2.0) && + rotatedUV.y > y_offset - (y_scale / 2.0) && rotatedUV.y < y_offset + (y_scale / 2.0); + if (!isInside) { + out_color = in_color; + return; + } + } + + vec4 watermark_color = texture(u_texture_watermark, vec2( + (rotatedUV.x - (x_offset - (x_scale / 2.0))) / x_scale, + (rotatedUV.y - (y_offset - (y_scale / 2.0))) / y_scale + )); + + out_color.r = mix(in_color.r, watermark_color.r, u_opacity * watermark_color.a); + out_color.g = mix(in_color.g, watermark_color.g, u_opacity * watermark_color.a); + out_color.b = mix(in_color.b, watermark_color.b, u_opacity * watermark_color.a); + out_color.a = in_color.a * (1.0 - u_opacity * watermark_color.a) + watermark_color.a * u_opacity; +} diff --git a/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.ts b/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.ts index 1c1c95b0c5..d0c901110d 100644 --- a/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.ts +++ b/packages/frontend/src/utility/image-effector/fxs/watermarkPlacement.ts @@ -5,87 +5,10 @@ import { defineImageEffectorFx } from '../ImageEffector.js'; -const shader = `#version 300 es -precision mediump float; - -const float PI = 3.141592653589793; -const float TWO_PI = 6.283185307179586; -const float HALF_PI = 1.5707963267948966; - -in vec2 in_uv; -uniform sampler2D in_texture; -uniform vec2 in_resolution; -uniform sampler2D u_texture_watermark; -uniform vec2 u_resolution_watermark; -uniform float u_scale; -uniform float u_angle; -uniform float u_opacity; -uniform bool u_repeat; -uniform int u_alignX; // 0: left, 1: center, 2: right -uniform int u_alignY; // 0: top, 1: center, 2: bottom -uniform int u_fitMode; // 0: contain, 1: cover -out vec4 out_color; - -void main() { - vec4 in_color = texture(in_texture, in_uv); - float in_x_ratio = max(in_resolution.x / in_resolution.y, 1.0); - float in_y_ratio = max(in_resolution.y / in_resolution.x, 1.0); - - bool contain = u_fitMode == 0; - - float x_ratio = u_resolution_watermark.x / in_resolution.x; - float y_ratio = u_resolution_watermark.y / in_resolution.y; - - float aspect_ratio = contain ? - (min(x_ratio, y_ratio) / max(x_ratio, y_ratio)) : - (max(x_ratio, y_ratio) / min(x_ratio, y_ratio)); - - float x_scale = contain ? - (x_ratio > y_ratio ? 1.0 * u_scale : aspect_ratio * u_scale) : - (x_ratio > y_ratio ? aspect_ratio * u_scale : 1.0 * u_scale); - - float y_scale = contain ? - (y_ratio > x_ratio ? 1.0 * u_scale : aspect_ratio * u_scale) : - (y_ratio > x_ratio ? aspect_ratio * u_scale : 1.0 * u_scale); - - float x_offset = u_alignX == 0 ? x_scale / 2.0 : u_alignX == 2 ? 1.0 - (x_scale / 2.0) : 0.5; - float y_offset = u_alignY == 0 ? y_scale / 2.0 : u_alignY == 2 ? 1.0 - (y_scale / 2.0) : 0.5; - - float angle = -(u_angle * PI); - vec2 center = vec2(x_offset, y_offset); - //vec2 centeredUv = (in_uv - center) * vec2(in_x_ratio, in_y_ratio); - vec2 centeredUv = (in_uv - center); - vec2 rotatedUV = vec2( - centeredUv.x * cos(angle) - centeredUv.y * sin(angle), - centeredUv.x * sin(angle) + centeredUv.y * cos(angle) - ) + center; - - // trim - if (!u_repeat) { - bool isInside = rotatedUV.x > x_offset - (x_scale / 2.0) && rotatedUV.x < x_offset + (x_scale / 2.0) && - rotatedUV.y > y_offset - (y_scale / 2.0) && rotatedUV.y < y_offset + (y_scale / 2.0); - if (!isInside) { - out_color = in_color; - return; - } - } - - vec4 watermark_color = texture(u_texture_watermark, vec2( - (rotatedUV.x - (x_offset - (x_scale / 2.0))) / x_scale, - (rotatedUV.y - (y_offset - (y_scale / 2.0))) / y_scale - )); - - out_color.r = mix(in_color.r, watermark_color.r, u_opacity * watermark_color.a); - out_color.g = mix(in_color.g, watermark_color.g, u_opacity * watermark_color.a); - out_color.b = mix(in_color.b, watermark_color.b, u_opacity * watermark_color.a); - out_color.a = in_color.a * (1.0 - u_opacity * watermark_color.a) + watermark_color.a * u_opacity; -} -`; - export const FX_watermarkPlacement = defineImageEffectorFx({ id: 'watermarkPlacement' as const, name: '(internal)', - shader, + shader: () => import('./watermarkPlacement.glsl?raw').then(m => m.default), uniforms: ['texture_watermark', 'resolution_watermark', 'scale', 'angle', 'opacity', 'repeat', 'alignX', 'alignY', 'fitMode'] as const, params: { cover: { diff --git a/packages/frontend/src/utility/image-effector/fxs/zoomLines.glsl b/packages/frontend/src/utility/image-effector/fxs/zoomLines.glsl new file mode 100644 index 0000000000..a5faf92bf4 --- /dev/null +++ b/packages/frontend/src/utility/image-effector/fxs/zoomLines.glsl @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#version 300 es +precision mediump float; + +in vec2 in_uv; +uniform sampler2D in_texture; +uniform vec2 in_resolution; +uniform vec2 u_pos; +uniform float u_frequency; +uniform bool u_thresholdEnabled; +uniform float u_threshold; +uniform float u_maskSize; +uniform bool u_black; +out vec4 out_color; + +void main() { + vec4 in_color = texture(in_texture, in_uv); + float angle = atan(-u_pos.y + (in_uv.y), -u_pos.x + (in_uv.x)); + float t = (1.0 + sin(angle * u_frequency)) / 2.0; + if (u_thresholdEnabled) t = t < u_threshold ? 1.0 : 0.0; + float d = distance(in_uv * vec2(2.0, 2.0), u_pos * vec2(2.0, 2.0)); + float mask = d < u_maskSize ? 0.0 : ((d - u_maskSize) * (1.0 + (u_maskSize * 2.0))); + out_color = vec4( + mix(in_color.r, u_black ? 0.0 : 1.0, t * mask), + mix(in_color.g, u_black ? 0.0 : 1.0, t * mask), + mix(in_color.b, u_black ? 0.0 : 1.0, t * mask), + in_color.a + ); +} diff --git a/packages/frontend/src/utility/image-effector/fxs/zoomLines.ts b/packages/frontend/src/utility/image-effector/fxs/zoomLines.ts index 2613362a71..b52b947055 100644 --- a/packages/frontend/src/utility/image-effector/fxs/zoomLines.ts +++ b/packages/frontend/src/utility/image-effector/fxs/zoomLines.ts @@ -6,40 +6,10 @@ 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 vec2 u_pos; -uniform float u_frequency; -uniform bool u_thresholdEnabled; -uniform float u_threshold; -uniform float u_maskSize; -uniform bool u_black; -out vec4 out_color; - -void main() { - vec4 in_color = texture(in_texture, in_uv); - float angle = atan(-u_pos.y + (in_uv.y), -u_pos.x + (in_uv.x)); - float t = (1.0 + sin(angle * u_frequency)) / 2.0; - if (u_thresholdEnabled) t = t < u_threshold ? 1.0 : 0.0; - float d = distance(in_uv * vec2(2.0, 2.0), u_pos * vec2(2.0, 2.0)); - float mask = d < u_maskSize ? 0.0 : ((d - u_maskSize) * (1.0 + (u_maskSize * 2.0))); - out_color = vec4( - mix(in_color.r, u_black ? 0.0 : 1.0, t * mask), - mix(in_color.g, u_black ? 0.0 : 1.0, t * mask), - mix(in_color.b, u_black ? 0.0 : 1.0, t * mask), - in_color.a - ); -} -`; - export const FX_zoomLines = defineImageEffectorFx({ id: 'zoomLines' as const, name: i18n.ts._imageEffector._fxs.zoomLines, - shader, + shader: () => import('./zoomLines.glsl?raw').then(m => m.default), uniforms: ['pos', 'frequency', 'thresholdEnabled', 'threshold', 'maskSize', 'black'] as const, params: { x: { diff --git a/packages/frontend/src/utility/watermark.ts b/packages/frontend/src/utility/watermark.ts index a70e14d95d..eb96244d37 100644 --- a/packages/frontend/src/utility/watermark.ts +++ b/packages/frontend/src/utility/watermark.ts @@ -163,11 +163,11 @@ export class WatermarkRenderer { public async setLayers(layers: WatermarkPreset['layers']) { this.layers = layers; await this.effector.setLayers(this.makeImageEffectorLayers()); - this.render(); + await this.render(); } - public render(): void { - this.effector.render(); + public async render(): Promise { + await this.effector.render(); } /*