wip
This commit is contained in:
parent
9fb0f7357a
commit
65e0ab810e
|
@ -12071,6 +12071,20 @@ export interface Locale extends ILocale {
|
|||
* エフェクト
|
||||
*/
|
||||
"title": string;
|
||||
"_fxs": {
|
||||
/**
|
||||
* 色収差
|
||||
*/
|
||||
"chromaticAberration": string;
|
||||
/**
|
||||
* グリッチ
|
||||
*/
|
||||
"glitch": string;
|
||||
/**
|
||||
* ミラー
|
||||
*/
|
||||
"mirror": string;
|
||||
};
|
||||
};
|
||||
}
|
||||
declare const locales: {
|
||||
|
|
|
@ -3234,3 +3234,8 @@ _watermarkEditor:
|
|||
|
||||
_imageEffector:
|
||||
title: "エフェクト"
|
||||
|
||||
_fxs:
|
||||
chromaticAberration: "色収差"
|
||||
glitch: "グリッチ"
|
||||
mirror: "ミラー"
|
||||
|
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<template>
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #label>{{ fx.id }}</template>
|
||||
<template #label>{{ fx.name }}</template>
|
||||
<template #footer>
|
||||
<MkButton @click="emit('del')">{{ i18n.ts.remove }}</MkButton>
|
||||
</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">
|
||||
<template #label>{{ k }}</template>
|
||||
</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'">
|
||||
<MkRange v-model="layer.params[k]" continuousUpdate type="number" :min="0" :max="10000" :step="1">
|
||||
<template #label>{{ k }}</template>
|
||||
|
@ -37,6 +41,7 @@ import { FXS, ImageEffector } from '@/utility/image-effector/ImageEffector.js';
|
|||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkRadios from '@/components/MkRadios.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkRange from '@/components/MkRange.vue';
|
||||
import FormSlot from '@/components/form/slot.vue';
|
||||
|
|
|
@ -61,7 +61,7 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'ok', preset: WatermarkPreset): void;
|
||||
(ev: 'ok', image: File): void;
|
||||
(ev: 'cancel'): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
@ -83,7 +83,7 @@ watch(layers, async () => {
|
|||
|
||||
function addEffect(ev: MouseEvent) {
|
||||
os.popupMenu(FXS.filter(fx => fx.id !== 'watermarkPlacement').map((fx) => ({
|
||||
text: fx.id,
|
||||
text: fx.name,
|
||||
action: () => {
|
||||
layers.push({
|
||||
id: uuid(),
|
||||
|
@ -125,6 +125,14 @@ onUnmounted(() => {
|
|||
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>
|
||||
|
||||
<style module>
|
||||
|
|
|
@ -280,10 +280,14 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) {
|
|||
action: async () => {
|
||||
const cropped = await os.cropImageFile(item.file, { aspectRatio: null });
|
||||
URL.revokeObjectURL(item.thumbnail);
|
||||
items.value.splice(items.value.indexOf(item), 1, {
|
||||
const newItem = {
|
||||
...item,
|
||||
file: markRaw(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')), {
|
||||
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 { FX_chromaticAberration } from './fxs/chromaticAberration.js';
|
||||
import { FX_glitch } from './fxs/glitch.js';
|
||||
import { FX_mirror } from './fxs/mirror.js';
|
||||
import { FX_watermarkPlacement } from './fxs/watermarkPlacement.js';
|
||||
|
||||
type ParamTypeToPrimitive = {
|
||||
'number': number;
|
||||
'number:enum': number;
|
||||
'boolean': boolean;
|
||||
'align': { x: 'left' | 'center' | 'right'; y: 'top' | 'center' | 'bottom'; };
|
||||
'seed': number;
|
||||
|
@ -26,6 +28,7 @@ export function defineImageEffectorFx<ID extends string, P extends ImageEffector
|
|||
|
||||
export type ImageEffectorFx<ID extends string, P extends ImageEffectorFxParamDefs> = {
|
||||
id: ID;
|
||||
name: string;
|
||||
shader: string;
|
||||
params: P,
|
||||
main: (ctx: {
|
||||
|
@ -49,6 +52,7 @@ export const FXS = [
|
|||
FX_watermarkPlacement,
|
||||
FX_chromaticAberration,
|
||||
FX_glitch,
|
||||
FX_mirror,
|
||||
] as const satisfies ImageEffectorFx<string, any>[];
|
||||
|
||||
export type ImageEffectorLayerOf<
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
import { defineImageEffectorFx } from '../ImageEffector.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
const shader = `#version 300 es
|
||||
precision highp float;
|
||||
|
@ -52,6 +53,7 @@ void main() {
|
|||
|
||||
export const FX_chromaticAberration = defineImageEffectorFx({
|
||||
id: 'chromaticAberration' as const,
|
||||
name: i18n.ts._imageEffector._fxs.chromaticAberration,
|
||||
shader,
|
||||
params: {
|
||||
normalize: {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import seedrandom from 'seedrandom';
|
||||
import { defineImageEffectorFx } from '../ImageEffector.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
const shader = `#version 300 es
|
||||
precision highp float;
|
||||
|
@ -38,6 +39,7 @@ void main() {
|
|||
|
||||
export const FX_glitch = defineImageEffectorFx({
|
||||
id: 'glitch' as const,
|
||||
name: i18n.ts._imageEffector._fxs.glitch,
|
||||
shader,
|
||||
params: {
|
||||
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