diff --git a/packages/frontend/src/components/MkImageEffectorDialog.vue b/packages/frontend/src/components/MkImageEffectorDialog.vue index de2c92bbd6..5605baff51 100644 --- a/packages/frontend/src/components/MkImageEffectorDialog.vue +++ b/packages/frontend/src/components/MkImageEffectorDialog.vue @@ -20,6 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
+
{{ i18n.ts.preview }}
@@ -74,6 +75,8 @@ const emit = defineEmits<{ const dialog = useTemplateRef('dialog'); +const canvasLoading = ref(false); + async function cancel() { if (layers.length > 0) { const { canceled } = await os.confirm({ @@ -91,7 +94,9 @@ const layers = reactive([]); watch(layers, async () => { if (renderer != null) { - renderer.setLayers(layers); + canvasLoading.value = true; + await renderer.setLayers(layers); + canvasLoading.value = false; } }, { deep: true }); diff --git a/packages/frontend/src/components/MkWatermarkEditorDialog.vue b/packages/frontend/src/components/MkWatermarkEditorDialog.vue index db231f90d7..46d952ce90 100644 --- a/packages/frontend/src/components/MkWatermarkEditorDialog.vue +++ b/packages/frontend/src/components/MkWatermarkEditorDialog.vue @@ -20,6 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
+
{{ i18n.ts.preview }}
@@ -89,6 +90,8 @@ import { genId } from '@/utility/id.js'; const $i = ensureSignin(); +const canvasLoading = ref(true); + function createTextLayer(): WatermarkPreset['layers'][number] { return { id: genId(), @@ -232,6 +235,13 @@ let imageBitmap: ImageBitmap | null = null; async function initRenderer() { if (canvasEl.value == null) return; + canvasLoading.value = true; + + await Promise.all([ + sampleImage_3_2_loading, + sampleImage_2_3_loading, + ]); + if (sampleImageType.value === '3_2') { renderer = new WatermarkRenderer({ canvas: canvasEl.value, @@ -270,7 +280,9 @@ async function initRenderer() { await renderer!.setLayers(preset.layers); - renderer!.render(); + await renderer!.render(); + + canvasLoading.value = false; } onMounted(async () => { diff --git a/packages/frontend/src/utility/image-effector/ImageEffector.ts b/packages/frontend/src/utility/image-effector/ImageEffector.ts index 282f36778f..63f5d6fa4b 100644 --- a/packages/frontend/src/utility/image-effector/ImageEffector.ts +++ b/packages/frontend/src/utility/image-effector/ImageEffector.ts @@ -196,40 +196,35 @@ export class ImageEffector { + let shaderProgram = this.shaderCache.get(fx.id); + if (shaderProgram) return shaderProgram; + + let shaderSource: string; + if (typeof fx.shader === 'string') { + shaderSource = fx.shader; + } else { + shaderSource = await fx.shader(); + } + shaderProgram = this.initShaderProgram(`#version 300 es + in vec2 position; + out vec2 in_uv; + + void main() { + in_uv = (position + 1.0) / 2.0; + gl_Position = vec4(position, 0.0, 1.0); + } + `, shaderSource); + this.shaderCache.set(fx.id, shaderProgram); + return shaderProgram; + } + private async renderLayer(layer: ImageEffectorLayer, preTexture: WebGLTexture) { const gl = this.gl; - const fx = this.fxs.find(fx => fx.id === layer.fxId); if (fx == null) return; - const cachedShader = this.shaderCache.get(fx.id); - let shaderProgram: WebGLProgram; - - if (cachedShader != null) { - shaderProgram = cachedShader; - } else { - let shaderSource: string; - - if (typeof fx.shader === 'string') { - shaderSource = fx.shader; - } else { - shaderSource = await fx.shader(); - } - - shaderProgram = this.initShaderProgram(`#version 300 es - in vec2 position; - out vec2 in_uv; - - void main() { - in_uv = (position + 1.0) / 2.0; - gl_Position = vec4(position, 0.0, 1.0); - } - `, shaderSource); - } - - if (cachedShader == null) { - this.shaderCache.set(fx.id, shaderProgram); - } + const shaderProgram = await this.getOrLoadShaderProgram(fx); gl.useProgram(shaderProgram); @@ -268,6 +263,13 @@ export class ImageEffector { + const fx = this.fxs.find(fx => fx.id === layer.fxId); + if (!fx) return; + await this.getOrLoadShaderProgram(fx); + })); + { gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, this.originalImageTexture); @@ -359,7 +361,7 @@ export class ImageEffector