summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2025-06-05 20:29:02 +0900
committerGitHub <noreply@github.com>2025-06-05 20:29:02 +0900
commita603a4970ed0dcba994e8718025b1e01c619fd0f (patch)
tree23d335981e653db2a895a144b991120262c8c636
parentchore: fix failure to publish misskey-js to npm registry (#16169) (diff)
downloadmisskey-a603a4970ed0dcba994e8718025b1e01c619fd0f.tar.gz
misskey-a603a4970ed0dcba994e8718025b1e01c619fd0f.tar.bz2
misskey-a603a4970ed0dcba994e8718025b1e01c619fd0f.zip
enhance(frontend): 画像エフェクト「色調補正」を追加 (#16170)
-rw-r--r--locales/index.d.ts4
-rw-r--r--locales/ja-JP.yml1
-rw-r--r--packages/frontend/src/utility/image-effector/fxs.ts2
-rw-r--r--packages/frontend/src/utility/image-effector/fxs/colorAdjust.ts136
4 files changed, 143 insertions, 0 deletions
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 17cdc7ca64..a4671aa812 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -12177,6 +12177,10 @@ export interface Locale extends ILocale {
*/
"grayscale": string;
/**
+ * 色調補正
+ */
+ "colorAdjust": string;
+ /**
* 色の圧縮
*/
"colorClamp": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index ed1f5354df..4988bfc259 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -3262,6 +3262,7 @@ _imageEffector:
mirror: "ミラー"
invert: "色の反転"
grayscale: "白黒"
+ colorAdjust: "色調補正"
colorClamp: "色の圧縮"
colorClampAdvanced: "色の圧縮(高度)"
distort: "歪み"
diff --git a/packages/frontend/src/utility/image-effector/fxs.ts b/packages/frontend/src/utility/image-effector/fxs.ts
index 5887a68c43..a5c8e2ff80 100644
--- a/packages/frontend/src/utility/image-effector/fxs.ts
+++ b/packages/frontend/src/utility/image-effector/fxs.ts
@@ -5,6 +5,7 @@
import { FX_checker } from './fxs/checker.js';
import { FX_chromaticAberration } from './fxs/chromaticAberration.js';
+import { FX_colorAdjust } from './fxs/colorAdjust.js';
import { FX_colorClamp } from './fxs/colorClamp.js';
import { FX_colorClampAdvanced } from './fxs/colorClampAdvanced.js';
import { FX_distort } from './fxs/distort.js';
@@ -26,6 +27,7 @@ export const FXS = [
FX_mirror,
FX_invert,
FX_grayscale,
+ FX_colorAdjust,
FX_colorClamp,
FX_colorClampAdvanced,
FX_distort,
diff --git a/packages/frontend/src/utility/image-effector/fxs/colorAdjust.ts b/packages/frontend/src/utility/image-effector/fxs/colorAdjust.ts
new file mode 100644
index 0000000000..cbb874852d
--- /dev/null
+++ b/packages/frontend/src/utility/image-effector/fxs/colorAdjust.ts
@@ -0,0 +1,136 @@
+/*
+ * 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_brightness;
+uniform float u_contrast;
+uniform float u_hue;
+uniform float u_lightness;
+uniform float u_saturation;
+out vec4 out_color;
+
+// RGB to HSL
+vec3 rgb2hsl(vec3 c) {
+ float maxc = max(max(c.r, c.g), c.b);
+ float minc = min(min(c.r, c.g), c.b);
+ float l = (maxc + minc) * 0.5;
+ float s = 0.0;
+ float h = 0.0;
+ if (maxc != minc) {
+ float d = maxc - minc;
+ s = l > 0.5 ? d / (2.0 - maxc - minc) : d / (maxc + minc);
+ if (maxc == c.r) {
+ h = (c.g - c.b) / d + (c.g < c.b ? 6.0 : 0.0);
+ } else if (maxc == c.g) {
+ h = (c.b - c.r) / d + 2.0;
+ } else {
+ h = (c.r - c.g) / d + 4.0;
+ }
+ h /= 6.0;
+ }
+ return vec3(h, s, l);
+}
+
+// HSL to RGB
+float hue2rgb(float p, float q, float t) {
+ if (t < 0.0) t += 1.0;
+ if (t > 1.0) t -= 1.0;
+ if (t < 1.0/6.0) return p + (q - p) * 6.0 * t;
+ if (t < 1.0/2.0) return q;
+ if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0;
+ return p;
+}
+vec3 hsl2rgb(vec3 hsl) {
+ float r, g, b;
+ float h = hsl.x;
+ float s = hsl.y;
+ float l = hsl.z;
+ if (s == 0.0) {
+ r = g = b = l;
+ } else {
+ float q = l < 0.5 ? l * (1.0 + s) : l + s - l * s;
+ float p = 2.0 * l - q;
+ r = hue2rgb(p, q, h + 1.0/3.0);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1.0/3.0);
+ }
+ return vec3(r, g, b);
+}
+
+void main() {
+ vec4 in_color = texture(in_texture, in_uv);
+ vec3 color = in_color.rgb;
+
+ color = color * u_brightness;
+ color += vec3(clamp(u_lightness, 0.0, 2.0) - 1.0);
+ color = (color - 0.5) * u_contrast + 0.5;
+
+ vec3 hsl = rgb2hsl(color);
+ hsl.x = mod(hsl.x + u_hue, 1.0);
+ hsl.y = clamp(hsl.y * u_saturation, 0.0, 1.0);
+
+ color = hsl2rgb(hsl);
+ out_color = vec4(color, in_color.a);
+}
+`;
+
+export const FX_colorAdjust = defineImageEffectorFx({
+ id: 'colorAdjust' as const,
+ name: i18n.ts._imageEffector._fxs.colorAdjust,
+ shader,
+ uniforms: ['lightness', 'contrast', 'hue', 'brightness', 'saturation'] as const,
+ params: {
+ lightness: {
+ type: 'number' as const,
+ default: 100,
+ min: 0,
+ max: 200,
+ step: 1,
+ },
+ contrast: {
+ type: 'number' as const,
+ default: 100,
+ min: 0,
+ max: 200,
+ step: 1,
+ },
+ hue: {
+ type: 'number' as const,
+ default: 0,
+ min: -360,
+ max: 360,
+ step: 1,
+ },
+ brightness: {
+ type: 'number' as const,
+ default: 100,
+ min: 0,
+ max: 200,
+ step: 1,
+ },
+ saturation: {
+ type: 'number' as const,
+ default: 100,
+ min: 0,
+ max: 200,
+ step: 1,
+ },
+ },
+ main: ({ gl, u, params }) => {
+ gl.uniform1f(u.brightness, params.brightness / 100);
+ gl.uniform1f(u.contrast, params.contrast / 100);
+ gl.uniform1f(u.hue, params.hue / 360);
+ gl.uniform1f(u.lightness, params.lightness / 100);
+ gl.uniform1f(u.saturation, params.saturation / 100);
+ },
+});