wip
This commit is contained in:
parent
9fb0f7357a
commit
65e0ab810e
|
@ -12071,6 +12071,20 @@ export interface Locale extends ILocale {
|
||||||
* エフェクト
|
* エフェクト
|
||||||
*/
|
*/
|
||||||
"title": string;
|
"title": string;
|
||||||
|
"_fxs": {
|
||||||
|
/**
|
||||||
|
* 色収差
|
||||||
|
*/
|
||||||
|
"chromaticAberration": string;
|
||||||
|
/**
|
||||||
|
* グリッチ
|
||||||
|
*/
|
||||||
|
"glitch": string;
|
||||||
|
/**
|
||||||
|
* ミラー
|
||||||
|
*/
|
||||||
|
"mirror": string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
declare const locales: {
|
declare const locales: {
|
||||||
|
|
|
@ -3234,3 +3234,8 @@ _watermarkEditor:
|
||||||
|
|
||||||
_imageEffector:
|
_imageEffector:
|
||||||
title: "エフェクト"
|
title: "エフェクト"
|
||||||
|
|
||||||
|
_fxs:
|
||||||
|
chromaticAberration: "色収差"
|
||||||
|
glitch: "グリッチ"
|
||||||
|
mirror: "ミラー"
|
||||||
|
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkFolder :defaultOpen="true">
|
<MkFolder :defaultOpen="true">
|
||||||
<template #label>{{ fx.id }}</template>
|
<template #label>{{ fx.name }}</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<MkButton @click="emit('del')">{{ i18n.ts.remove }}</MkButton>
|
<MkButton @click="emit('del')">{{ i18n.ts.remove }}</MkButton>
|
||||||
</template>
|
</template>
|
||||||
|
@ -18,6 +18,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkRange v-else-if="v.type === 'number'" v-model="layer.params[k]" continuousUpdate :min="v.min" :max="v.max" :step="v.step">
|
<MkRange v-else-if="v.type === 'number'" v-model="layer.params[k]" continuousUpdate :min="v.min" :max="v.max" :step="v.step">
|
||||||
<template #label>{{ k }}</template>
|
<template #label>{{ k }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
|
<MkRadios v-else-if="v.type === 'number:enum'" v-model="layer.params[k]">
|
||||||
|
<template #label>{{ k }}</template>
|
||||||
|
<option v-for="item in v.enum" :value="item.value">{{ item.label }}</option>
|
||||||
|
</MkRadios>
|
||||||
<div v-else-if="v.type === 'seed'">
|
<div v-else-if="v.type === 'seed'">
|
||||||
<MkRange v-model="layer.params[k]" continuousUpdate type="number" :min="0" :max="10000" :step="1">
|
<MkRange v-model="layer.params[k]" continuousUpdate type="number" :min="0" :max="10000" :step="1">
|
||||||
<template #label>{{ k }}</template>
|
<template #label>{{ k }}</template>
|
||||||
|
@ -37,6 +41,7 @@ import { FXS, ImageEffector } from '@/utility/image-effector/ImageEffector.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
|
import MkRadios from '@/components/MkRadios.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkRange from '@/components/MkRange.vue';
|
import MkRange from '@/components/MkRange.vue';
|
||||||
import FormSlot from '@/components/form/slot.vue';
|
import FormSlot from '@/components/form/slot.vue';
|
||||||
|
|
|
@ -61,7 +61,7 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'ok', preset: WatermarkPreset): void;
|
(ev: 'ok', image: File): void;
|
||||||
(ev: 'cancel'): void;
|
(ev: 'cancel'): void;
|
||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
@ -83,7 +83,7 @@ watch(layers, async () => {
|
||||||
|
|
||||||
function addEffect(ev: MouseEvent) {
|
function addEffect(ev: MouseEvent) {
|
||||||
os.popupMenu(FXS.filter(fx => fx.id !== 'watermarkPlacement').map((fx) => ({
|
os.popupMenu(FXS.filter(fx => fx.id !== 'watermarkPlacement').map((fx) => ({
|
||||||
text: fx.id,
|
text: fx.name,
|
||||||
action: () => {
|
action: () => {
|
||||||
layers.push({
|
layers.push({
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
|
@ -125,6 +125,14 @@ onUnmounted(() => {
|
||||||
renderer = null;
|
renderer = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
renderer!.render(); // toBlobの直前にレンダリングしないと何故か壊れる
|
||||||
|
canvasEl.value!.toBlob((blob) => {
|
||||||
|
emit('ok', new File([blob!], `image-${Date.now()}.png`, { type: 'image/png' }));
|
||||||
|
dialog.value?.close();
|
||||||
|
}, 'image/png');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style module>
|
<style module>
|
||||||
|
|
|
@ -280,10 +280,14 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) {
|
||||||
action: async () => {
|
action: async () => {
|
||||||
const cropped = await os.cropImageFile(item.file, { aspectRatio: null });
|
const cropped = await os.cropImageFile(item.file, { aspectRatio: null });
|
||||||
URL.revokeObjectURL(item.thumbnail);
|
URL.revokeObjectURL(item.thumbnail);
|
||||||
items.value.splice(items.value.indexOf(item), 1, {
|
const newItem = {
|
||||||
...item,
|
...item,
|
||||||
file: markRaw(cropped),
|
file: markRaw(cropped),
|
||||||
thumbnail: window.URL.createObjectURL(cropped),
|
thumbnail: window.URL.createObjectURL(cropped),
|
||||||
|
};
|
||||||
|
items.value.splice(items.value.indexOf(item), 1, newItem);
|
||||||
|
preprocess(newItem).then(() => {
|
||||||
|
triggerRef(items);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -298,9 +302,22 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) {
|
||||||
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkImageEffectorDialog.vue')), {
|
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkImageEffectorDialog.vue')), {
|
||||||
image: img,
|
image: img,
|
||||||
}, {
|
}, {
|
||||||
ok: () => {
|
ok: (file) => {
|
||||||
|
URL.revokeObjectURL(item.thumbnail);
|
||||||
|
const newItem = {
|
||||||
|
...item,
|
||||||
|
file: markRaw(file),
|
||||||
|
thumbnail: window.URL.createObjectURL(file),
|
||||||
|
};
|
||||||
|
items.value.splice(items.value.indexOf(item), 1, newItem);
|
||||||
|
preprocess(newItem).then(() => {
|
||||||
|
triggerRef(items);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
closed: () => {
|
||||||
|
URL.revokeObjectURL(img.src);
|
||||||
|
dispose();
|
||||||
},
|
},
|
||||||
closed: () => dispose(),
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,10 +6,12 @@
|
||||||
import { getProxiedImageUrl } from '../media-proxy.js';
|
import { getProxiedImageUrl } from '../media-proxy.js';
|
||||||
import { FX_chromaticAberration } from './fxs/chromaticAberration.js';
|
import { FX_chromaticAberration } from './fxs/chromaticAberration.js';
|
||||||
import { FX_glitch } from './fxs/glitch.js';
|
import { FX_glitch } from './fxs/glitch.js';
|
||||||
|
import { FX_mirror } from './fxs/mirror.js';
|
||||||
import { FX_watermarkPlacement } from './fxs/watermarkPlacement.js';
|
import { FX_watermarkPlacement } from './fxs/watermarkPlacement.js';
|
||||||
|
|
||||||
type ParamTypeToPrimitive = {
|
type ParamTypeToPrimitive = {
|
||||||
'number': number;
|
'number': number;
|
||||||
|
'number:enum': number;
|
||||||
'boolean': boolean;
|
'boolean': boolean;
|
||||||
'align': { x: 'left' | 'center' | 'right'; y: 'top' | 'center' | 'bottom'; };
|
'align': { x: 'left' | 'center' | 'right'; y: 'top' | 'center' | 'bottom'; };
|
||||||
'seed': number;
|
'seed': number;
|
||||||
|
@ -26,6 +28,7 @@ export function defineImageEffectorFx<ID extends string, P extends ImageEffector
|
||||||
|
|
||||||
export type ImageEffectorFx<ID extends string, P extends ImageEffectorFxParamDefs> = {
|
export type ImageEffectorFx<ID extends string, P extends ImageEffectorFxParamDefs> = {
|
||||||
id: ID;
|
id: ID;
|
||||||
|
name: string;
|
||||||
shader: string;
|
shader: string;
|
||||||
params: P,
|
params: P,
|
||||||
main: (ctx: {
|
main: (ctx: {
|
||||||
|
@ -49,6 +52,7 @@ export const FXS = [
|
||||||
FX_watermarkPlacement,
|
FX_watermarkPlacement,
|
||||||
FX_chromaticAberration,
|
FX_chromaticAberration,
|
||||||
FX_glitch,
|
FX_glitch,
|
||||||
|
FX_mirror,
|
||||||
] as const satisfies ImageEffectorFx<string, any>[];
|
] as const satisfies ImageEffectorFx<string, any>[];
|
||||||
|
|
||||||
export type ImageEffectorLayerOf<
|
export type ImageEffectorLayerOf<
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { defineImageEffectorFx } from '../ImageEffector.js';
|
import { defineImageEffectorFx } from '../ImageEffector.js';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const shader = `#version 300 es
|
const shader = `#version 300 es
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
@ -52,6 +53,7 @@ void main() {
|
||||||
|
|
||||||
export const FX_chromaticAberration = defineImageEffectorFx({
|
export const FX_chromaticAberration = defineImageEffectorFx({
|
||||||
id: 'chromaticAberration' as const,
|
id: 'chromaticAberration' as const,
|
||||||
|
name: i18n.ts._imageEffector._fxs.chromaticAberration,
|
||||||
shader,
|
shader,
|
||||||
params: {
|
params: {
|
||||||
normalize: {
|
normalize: {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import seedrandom from 'seedrandom';
|
import seedrandom from 'seedrandom';
|
||||||
import { defineImageEffectorFx } from '../ImageEffector.js';
|
import { defineImageEffectorFx } from '../ImageEffector.js';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const shader = `#version 300 es
|
const shader = `#version 300 es
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
@ -38,6 +39,7 @@ void main() {
|
||||||
|
|
||||||
export const FX_glitch = defineImageEffectorFx({
|
export const FX_glitch = defineImageEffectorFx({
|
||||||
id: 'glitch' as const,
|
id: 'glitch' as const,
|
||||||
|
name: i18n.ts._imageEffector._fxs.glitch,
|
||||||
shader,
|
shader,
|
||||||
params: {
|
params: {
|
||||||
amount: {
|
amount: {
|
||||||
|
|
|
@ -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 highp float;
|
||||||
|
|
||||||
|
in vec2 in_uv;
|
||||||
|
uniform sampler2D u_texture;
|
||||||
|
uniform vec2 u_resolution;
|
||||||
|
uniform int u_h;
|
||||||
|
uniform int u_v;
|
||||||
|
out vec4 out_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 pixel = texture(u_texture, in_uv);
|
||||||
|
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(u_texture, uv);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FX_mirror = defineImageEffectorFx({
|
||||||
|
id: 'mirror' as const,
|
||||||
|
name: i18n.ts._imageEffector._fxs.mirror,
|
||||||
|
shader,
|
||||||
|
params: {
|
||||||
|
h: {
|
||||||
|
type: 'number:enum' as const,
|
||||||
|
enum: [{ value: -1, label: '<-' }, { value: 0, label: '|' }, { value: 1, label: '->' }],
|
||||||
|
default: -1,
|
||||||
|
},
|
||||||
|
v: {
|
||||||
|
type: 'number:enum' as const,
|
||||||
|
enum: [{ value: -1, label: '^' }, { value: 0, label: '-' }, { value: 1, label: 'v' }],
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
main: ({ gl, program, params, preTexture }) => {
|
||||||
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, preTexture);
|
||||||
|
const u_texture = gl.getUniformLocation(program, 'u_texture');
|
||||||
|
gl.uniform1i(u_texture, 0);
|
||||||
|
|
||||||
|
const u_h = gl.getUniformLocation(program, 'u_h');
|
||||||
|
gl.uniform1i(u_h, params.h);
|
||||||
|
|
||||||
|
const u_v = gl.getUniformLocation(program, 'u_v');
|
||||||
|
gl.uniform1i(u_v, params.v);
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue