From d1b44568459c796564b8081fdd59b15b71f18fb4 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 30 May 2025 20:06:18 +0900 Subject: [PATCH] wip --- .../src/components/MkImageEffectorDialog.vue | 2 +- .../src/components/MkUploaderDialog.vue | 46 +++++-------------- .../components/MkWatermarkEditorDialog.vue | 17 +++++-- packages/frontend/src/utility/watermark.ts | 2 +- 4 files changed, 27 insertions(+), 40 deletions(-) diff --git a/packages/frontend/src/components/MkImageEffectorDialog.vue b/packages/frontend/src/components/MkImageEffectorDialog.vue index 26a4a193eb..9c05e8237e 100644 --- a/packages/frontend/src/components/MkImageEffectorDialog.vue +++ b/packages/frontend/src/components/MkImageEffectorDialog.vue @@ -61,7 +61,7 @@ import { deepClone } from '@/utility/clone.js'; import { FXS } from '@/utility/image-effector/fxs.js'; const props = defineProps<{ - image: HTMLImageElement; + image: ImageBitmap; }>(); const emit = defineEmits<{ diff --git a/packages/frontend/src/components/MkUploaderDialog.vue b/packages/frontend/src/components/MkUploaderDialog.vue index 0254ccb911..0f25e255d5 100644 --- a/packages/frontend/src/components/MkUploaderDialog.vue +++ b/packages/frontend/src/components/MkUploaderDialog.vue @@ -297,9 +297,9 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) { icon: 'ti ti-sparkles', text: i18n.ts._imageEffector.title, action: async () => { - const imgElementCtx = await getImageElement(item.file); + const imageBitmap = await window.createImageBitmap(item.file); const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkImageEffectorDialog.vue')), { - image: imgElementCtx.img, + image: imageBitmap, }, { ok: (file) => { URL.revokeObjectURL(item.thumbnail); @@ -314,7 +314,7 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) { }); }, closed: () => { - imgElementCtx.dispose(); + imageBitmap.close(); dispose(); }, }); @@ -353,16 +353,16 @@ function showMenu(ev: MouseEvent, item: typeof items.value[0]) { icon: 'ti ti-plus', text: i18n.ts.add, action: async () => { - const imgElementCtx = await getImageElement(item.file); + const imageBitmap = await window.createImageBitmap(item.file); const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkWatermarkEditorDialog.vue')), { - image: imgElementCtx.img, + image: imageBitmap, }, { ok: (preset) => { prefer.commit('watermarkPresets', [...prefer.s.watermarkPresets, preset]); changeWatermarkPreset(preset.id); }, closed: () => { - imgElementCtx.dispose(); + imageBitmap.close(); dispose(); }, }); @@ -515,35 +515,11 @@ async function chooseFile(ev: MouseEvent) { } } -/** - * 使用後は dispose() でメモリの解放を忘れないように!!! - */ -function getImageElement(file: Blob | File): Promise<{ img: HTMLImageElement, dispose: () => void }> { - return new Promise((resolve, reject) => { - const img = new Image(); - - img.onload = () => { - resolve({ - img: markRaw(img), - dispose: () => { - URL.revokeObjectURL(img.src); - }, - }); - }; - - img.onerror = (error) => { - reject(error); - }; - - img.src = URL.createObjectURL(file); - }); -} - async function preprocess(item: (typeof items)['value'][number]): Promise { item.preprocessing = true; let file: Blob | File = item.file; - const imgElementCtx = await getImageElement(file); + const imageBitmap = await window.createImageBitmap(file); const needsWatermark = item.watermarkPresetId != null && WATERMARK_SUPPORTED_TYPES.includes(file.type); const preset = prefer.s.watermarkPresets.find(p => p.id === item.watermarkPresetId); @@ -551,9 +527,9 @@ async function preprocess(item: (typeof items)['value'][number]): Promise const canvas = window.document.createElement('canvas'); const renderer = new WatermarkRenderer({ canvas: canvas, - renderWidth: imgElementCtx.img.width, - renderHeight: imgElementCtx.img.height, - image: imgElementCtx.img, + renderWidth: imageBitmap.width, + renderHeight: imageBitmap.height, + image: imageBitmap, }); await renderer.setLayers(preset.layers); @@ -604,7 +580,7 @@ async function preprocess(item: (typeof items)['value'][number]): Promise item.preprocessedFile = markRaw(file); item.preprocessing = false; - imgElementCtx.dispose(); + imageBitmap.close(); } function initializeFile(file: File) { diff --git a/packages/frontend/src/components/MkWatermarkEditorDialog.vue b/packages/frontend/src/components/MkWatermarkEditorDialog.vue index afa8201b6e..81c0c72199 100644 --- a/packages/frontend/src/components/MkWatermarkEditorDialog.vue +++ b/packages/frontend/src/components/MkWatermarkEditorDialog.vue @@ -63,7 +63,7 @@ const $i = ensureSignin(); const props = defineProps<{ preset?: WatermarkPreset | null; - image?: HTMLImageElement | null; + image?: ImageBitmap | null; }>(); const preset = reactive(deepClone(props.preset) ?? { @@ -168,10 +168,21 @@ async function initRenderer() { image: sampleImage_2_3, }); } else if (props.image != null) { + const MAX_W = 1000; + const MAX_H = 1000; + let w = props.image.width; + let h = props.image.height; + + if (w > MAX_W || h > MAX_H) { + const scale = Math.min(MAX_W / w, MAX_H / h); + w *= scale; + h *= scale; + } + renderer = new WatermarkRenderer({ canvas: canvasEl.value, - renderWidth: props.image.width, - renderHeight: props.image.height, + renderWidth: w, + renderHeight: h, image: props.image, }); } diff --git a/packages/frontend/src/utility/watermark.ts b/packages/frontend/src/utility/watermark.ts index b9975fd4fc..4094f84977 100644 --- a/packages/frontend/src/utility/watermark.ts +++ b/packages/frontend/src/utility/watermark.ts @@ -39,7 +39,7 @@ export class WatermarkRenderer { canvas: HTMLCanvasElement, renderWidth: number, renderHeight: number, - image: HTMLImageElement, + image: HTMLImageElement | ImageBitmap, }) { this.effector = new ImageEffector({ canvas: options.canvas,