wip
This commit is contained in:
parent
604a1d1c9f
commit
ed02cf6677
|
@ -12346,6 +12346,10 @@ export interface Locale extends ILocale {
|
||||||
* 白黒
|
* 白黒
|
||||||
*/
|
*/
|
||||||
"grayscale": string;
|
"grayscale": string;
|
||||||
|
/**
|
||||||
|
* ぼかし
|
||||||
|
*/
|
||||||
|
"blur": string;
|
||||||
/**
|
/**
|
||||||
* 色調補正
|
* 色調補正
|
||||||
*/
|
*/
|
||||||
|
@ -12391,9 +12395,9 @@ export interface Locale extends ILocale {
|
||||||
*/
|
*/
|
||||||
"tearing": string;
|
"tearing": string;
|
||||||
/**
|
/**
|
||||||
* 塗りつぶし(四角)
|
* 塗りつぶし
|
||||||
*/
|
*/
|
||||||
"fillSquare": string;
|
"fill": string;
|
||||||
};
|
};
|
||||||
"_fxProps": {
|
"_fxProps": {
|
||||||
/**
|
/**
|
||||||
|
@ -12408,6 +12412,14 @@ export interface Locale extends ILocale {
|
||||||
* サイズ
|
* サイズ
|
||||||
*/
|
*/
|
||||||
"size": string;
|
"size": string;
|
||||||
|
/**
|
||||||
|
* 半径
|
||||||
|
*/
|
||||||
|
"radius": string;
|
||||||
|
/**
|
||||||
|
* サンプル数
|
||||||
|
*/
|
||||||
|
"samples": string;
|
||||||
/**
|
/**
|
||||||
* 位置
|
* 位置
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3318,7 +3318,7 @@ _imageEffector:
|
||||||
checker: "チェッカー"
|
checker: "チェッカー"
|
||||||
blockNoise: "ブロックノイズ"
|
blockNoise: "ブロックノイズ"
|
||||||
tearing: "ティアリング"
|
tearing: "ティアリング"
|
||||||
fillSquare: "塗りつぶし(四角)"
|
fill: "塗りつぶし"
|
||||||
|
|
||||||
_fxProps:
|
_fxProps:
|
||||||
angle: "角度"
|
angle: "角度"
|
||||||
|
|
|
@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div :class="$style.previewContainer">
|
<div :class="$style.previewContainer">
|
||||||
<div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
|
<div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
|
||||||
<div class="_acrylic" :class="$style.editControls">
|
<div class="_acrylic" :class="$style.editControls">
|
||||||
<button class="_button" :class="[$style.previewControlsButton, fillSquare ? $style.active : null]" @click="fillSquare = true"><i class="ti ti-pencil"></i></button>
|
<button class="_button" :class="[$style.previewControlsButton, penMode != null ? $style.active : null]" @click="showPenMenu"><i class="ti ti-pencil"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="_acrylic" :class="$style.previewControls">
|
<div class="_acrylic" :class="$style.previewControls">
|
||||||
<button class="_button" :class="[$style.previewControlsButton, !enabled ? $style.active : null]" @click="enabled = false">Before</button>
|
<button class="_button" :class="[$style.previewControlsButton, !enabled ? $style.active : null]" @click="enabled = false">Before</button>
|
||||||
|
@ -216,10 +216,24 @@ watch(enabled, () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const fillSquare = ref(false);
|
const penMode = ref<'fill' | 'blur' | null>(null);
|
||||||
|
|
||||||
|
function showPenMenu(ev: MouseEvent) {
|
||||||
|
os.popupMenu([{
|
||||||
|
text: i18n.ts._imageEffector._fxs.fill,
|
||||||
|
action: () => {
|
||||||
|
penMode.value = 'fill';
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
text: i18n.ts._imageEffector._fxs.blur,
|
||||||
|
action: () => {
|
||||||
|
penMode.value = 'blur';
|
||||||
|
},
|
||||||
|
}], ev.currentTarget ?? ev.target);
|
||||||
|
}
|
||||||
|
|
||||||
function onImagePointerdown(ev: PointerEvent) {
|
function onImagePointerdown(ev: PointerEvent) {
|
||||||
if (canvasEl.value == null || imageBitmap == null || !fillSquare.value) return;
|
if (canvasEl.value == null || imageBitmap == null || penMode.value == null) return;
|
||||||
|
|
||||||
const AW = canvasEl.value.clientWidth;
|
const AW = canvasEl.value.clientWidth;
|
||||||
const AH = canvasEl.value.clientHeight;
|
const AH = canvasEl.value.clientHeight;
|
||||||
|
@ -250,19 +264,34 @@ function onImagePointerdown(ev: PointerEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = genId();
|
const id = genId();
|
||||||
layers.push({
|
if (penMode.value === 'fill') {
|
||||||
id,
|
layers.push({
|
||||||
fxId: 'fillSquare',
|
id,
|
||||||
params: {
|
fxId: 'fill',
|
||||||
offsetX: 0,
|
params: {
|
||||||
offsetY: 0,
|
offsetX: 0,
|
||||||
scaleX: 0.1,
|
offsetY: 0,
|
||||||
scaleY: 0.1,
|
scaleX: 0.1,
|
||||||
angle: 0,
|
scaleY: 0.1,
|
||||||
opacity: 1,
|
angle: 0,
|
||||||
color: [1, 1, 1],
|
opacity: 1,
|
||||||
},
|
color: [1, 1, 1],
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
} else if (penMode.value === 'blur') {
|
||||||
|
layers.push({
|
||||||
|
id,
|
||||||
|
fxId: 'blur',
|
||||||
|
params: {
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: 0,
|
||||||
|
scaleX: 0.1,
|
||||||
|
scaleY: 0.1,
|
||||||
|
angle: 0,
|
||||||
|
radius: 3,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_move(ev.offsetX, ev.offsetY);
|
_move(ev.offsetX, ev.offsetY);
|
||||||
|
|
||||||
|
@ -302,7 +331,7 @@ function onImagePointerdown(ev: PointerEvent) {
|
||||||
canvasEl.value?.removeEventListener('pointercancel', up);
|
canvasEl.value?.removeEventListener('pointercancel', up);
|
||||||
canvasEl.value?.releasePointerCapture(ev.pointerId);
|
canvasEl.value?.releasePointerCapture(ev.pointerId);
|
||||||
|
|
||||||
fillSquare.value = false;
|
penMode.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvasEl.value.addEventListener('pointermove', move);
|
canvasEl.value.addEventListener('pointermove', move);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FX_blur } from './fxs/blur.js';
|
|
||||||
import { FX_checker } from './fxs/checker.js';
|
import { FX_checker } from './fxs/checker.js';
|
||||||
import { FX_chromaticAberration } from './fxs/chromaticAberration.js';
|
import { FX_chromaticAberration } from './fxs/chromaticAberration.js';
|
||||||
import { FX_colorAdjust } from './fxs/colorAdjust.js';
|
import { FX_colorAdjust } from './fxs/colorAdjust.js';
|
||||||
|
@ -19,14 +18,14 @@ import { FX_stripe } from './fxs/stripe.js';
|
||||||
import { FX_threshold } from './fxs/threshold.js';
|
import { FX_threshold } from './fxs/threshold.js';
|
||||||
import { FX_zoomLines } from './fxs/zoomLines.js';
|
import { FX_zoomLines } from './fxs/zoomLines.js';
|
||||||
import { FX_blockNoise } from './fxs/blockNoise.js';
|
import { FX_blockNoise } from './fxs/blockNoise.js';
|
||||||
import { FX_fillSquare } from './fxs/fillSquare.js';
|
import { FX_fill } from './fxs/fill.js';
|
||||||
|
import { FX_blur } from './fxs/blur.js';
|
||||||
import type { ImageEffectorFx } from './ImageEffector.js';
|
import type { ImageEffectorFx } from './ImageEffector.js';
|
||||||
|
|
||||||
export const FXS = [
|
export const FXS = [
|
||||||
FX_mirror,
|
FX_mirror,
|
||||||
FX_invert,
|
FX_invert,
|
||||||
FX_grayscale,
|
FX_grayscale,
|
||||||
FX_blur,
|
|
||||||
FX_colorAdjust,
|
FX_colorAdjust,
|
||||||
FX_colorClamp,
|
FX_colorClamp,
|
||||||
FX_colorClampAdvanced,
|
FX_colorClampAdvanced,
|
||||||
|
@ -39,5 +38,6 @@ export const FXS = [
|
||||||
FX_chromaticAberration,
|
FX_chromaticAberration,
|
||||||
FX_tearing,
|
FX_tearing,
|
||||||
FX_blockNoise,
|
FX_blockNoise,
|
||||||
FX_fillSquare,
|
FX_fill,
|
||||||
|
FX_blur,
|
||||||
] as const satisfies ImageEffectorFx<string, any>[];
|
] as const satisfies ImageEffectorFx<string, any>[];
|
||||||
|
|
|
@ -9,14 +9,34 @@ import { i18n } from '@/i18n.js';
|
||||||
const shader = `#version 300 es
|
const shader = `#version 300 es
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
|
const float PI = 3.141592653589793;
|
||||||
|
const float TWO_PI = 6.283185307179586;
|
||||||
|
const float HALF_PI = 1.5707963267948966;
|
||||||
|
|
||||||
in vec2 in_uv;
|
in vec2 in_uv;
|
||||||
uniform sampler2D in_texture;
|
uniform sampler2D in_texture;
|
||||||
uniform vec2 in_resolution;
|
uniform vec2 in_resolution;
|
||||||
|
uniform vec2 u_offset;
|
||||||
|
uniform vec2 u_scale;
|
||||||
|
uniform float u_angle;
|
||||||
uniform float u_radius;
|
uniform float u_radius;
|
||||||
uniform int u_samples;
|
uniform int u_samples;
|
||||||
out vec4 out_color;
|
out vec4 out_color;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
float angle = -(u_angle * PI);
|
||||||
|
vec2 centeredUv = in_uv - vec2(0.5, 0.5) - u_offset;
|
||||||
|
vec2 rotatedUV = vec2(
|
||||||
|
centeredUv.x * cos(angle) - centeredUv.y * sin(angle),
|
||||||
|
centeredUv.x * sin(angle) + centeredUv.y * cos(angle)
|
||||||
|
) + u_offset;
|
||||||
|
|
||||||
|
bool isInside = rotatedUV.x > u_offset.x - u_scale.x && rotatedUV.x < u_offset.x + u_scale.x && rotatedUV.y > u_offset.y - u_scale.y && rotatedUV.y < u_offset.y + u_scale.y;
|
||||||
|
if (!isInside) {
|
||||||
|
out_color = texture(in_texture, in_uv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vec4 result = vec4(0.0);
|
vec4 result = vec4(0.0);
|
||||||
float totalSamples = 0.0;
|
float totalSamples = 0.0;
|
||||||
|
|
||||||
|
@ -57,18 +77,66 @@ export const FX_blur = defineImageEffectorFx({
|
||||||
id: 'blur',
|
id: 'blur',
|
||||||
name: i18n.ts._imageEffector._fxs.blur,
|
name: i18n.ts._imageEffector._fxs.blur,
|
||||||
shader,
|
shader,
|
||||||
uniforms: ['radius', 'samples'] as const,
|
uniforms: ['offset', 'scale', 'angle', 'radius', 'samples'] as const,
|
||||||
params: {
|
params: {
|
||||||
|
offsetX: {
|
||||||
|
label: i18n.ts._imageEffector._fxProps.offset + ' X',
|
||||||
|
type: 'number',
|
||||||
|
default: 0.0,
|
||||||
|
min: -1.0,
|
||||||
|
max: 1.0,
|
||||||
|
step: 0.01,
|
||||||
|
toViewValue: v => Math.round(v * 100) + '%',
|
||||||
|
},
|
||||||
|
offsetY: {
|
||||||
|
label: i18n.ts._imageEffector._fxProps.offset + ' Y',
|
||||||
|
type: 'number',
|
||||||
|
default: 0.0,
|
||||||
|
min: -1.0,
|
||||||
|
max: 1.0,
|
||||||
|
step: 0.01,
|
||||||
|
toViewValue: v => Math.round(v * 100) + '%',
|
||||||
|
},
|
||||||
|
scaleX: {
|
||||||
|
label: i18n.ts._imageEffector._fxProps.scale + ' X',
|
||||||
|
type: 'number',
|
||||||
|
default: 0.5,
|
||||||
|
min: 0.0,
|
||||||
|
max: 1.0,
|
||||||
|
step: 0.01,
|
||||||
|
toViewValue: v => Math.round(v * 100) + '%',
|
||||||
|
},
|
||||||
|
scaleY: {
|
||||||
|
label: i18n.ts._imageEffector._fxProps.scale + ' Y',
|
||||||
|
type: 'number',
|
||||||
|
default: 0.5,
|
||||||
|
min: 0.0,
|
||||||
|
max: 1.0,
|
||||||
|
step: 0.01,
|
||||||
|
toViewValue: v => Math.round(v * 100) + '%',
|
||||||
|
},
|
||||||
|
angle: {
|
||||||
|
label: i18n.ts._imageEffector._fxProps.angle,
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
min: -1.0,
|
||||||
|
max: 1.0,
|
||||||
|
step: 0.01,
|
||||||
|
toViewValue: v => Math.round(v * 90) + '°',
|
||||||
|
},
|
||||||
radius: {
|
radius: {
|
||||||
label: i18n.ts._imageEffector._fxProps.strength,
|
label: i18n.ts._imageEffector._fxProps.strength,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
default: 3.0,
|
default: 3.0,
|
||||||
min: 0.0,
|
min: 0.0,
|
||||||
max: 20.0,
|
max: 10.0,
|
||||||
step: 0.5,
|
step: 0.5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
main: ({ gl, u, params }) => {
|
main: ({ gl, u, params }) => {
|
||||||
|
gl.uniform2f(u.offset, params.offsetX / 2, params.offsetY / 2);
|
||||||
|
gl.uniform2f(u.scale, params.scaleX / 2, params.scaleY / 2);
|
||||||
|
gl.uniform1f(u.angle, params.angle / 2);
|
||||||
gl.uniform1f(u.radius, params.radius);
|
gl.uniform1f(u.radius, params.radius);
|
||||||
gl.uniform1i(u.samples, 256);
|
gl.uniform1i(u.samples, 256);
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,9 +46,9 @@ void main() {
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const FX_fillSquare = defineImageEffectorFx({
|
export const FX_fill = defineImageEffectorFx({
|
||||||
id: 'fillSquare',
|
id: 'fill',
|
||||||
name: i18n.ts._imageEffector._fxs.fillSquare,
|
name: i18n.ts._imageEffector._fxs.fill,
|
||||||
shader,
|
shader,
|
||||||
uniforms: ['offset', 'scale', 'angle', 'color', 'opacity'] as const,
|
uniforms: ['offset', 'scale', 'angle', 'color', 'opacity'] as const,
|
||||||
params: {
|
params: {
|
Loading…
Reference in New Issue