wip
This commit is contained in:
parent
0d52145b2b
commit
c9b5e66cdb
|
|
@ -31,6 +31,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.controls">
|
<div :class="$style.controls">
|
||||||
<div class="_spacer _gaps">
|
<div class="_spacer _gaps">
|
||||||
|
<MkButton inline rounded primary @click="chooseFile">{{ i18n.ts.selectFile }}</MkButton>
|
||||||
|
|
||||||
<MkRange v-model="params.borderThickness" :min="0" :max="0.2" :step="0.01" :continuousUpdate="true">
|
<MkRange v-model="params.borderThickness" :min="0" :max="0.2" :step="0.01" :continuousUpdate="true">
|
||||||
<template #label>{{ i18n.ts._imageFrameEditor.borderThickness }}</template>
|
<template #label>{{ i18n.ts._imageFrameEditor.borderThickness }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
|
|
@ -175,6 +177,7 @@ import { ensureSignin } from '@/i.js';
|
||||||
import { genId } from '@/utility/id.js';
|
import { genId } from '@/utility/id.js';
|
||||||
import { useMkSelect } from '@/composables/use-mkselect.js';
|
import { useMkSelect } from '@/composables/use-mkselect.js';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
|
import { selectFile } from '@/utility/drive.js';
|
||||||
|
|
||||||
const $i = ensureSignin();
|
const $i = ensureSignin();
|
||||||
|
|
||||||
|
|
@ -216,6 +219,7 @@ const params = reactive<ImageFrameParams>(deepClone(props.params) ?? {
|
||||||
bgColor: [1, 1, 1],
|
bgColor: [1, 1, 1],
|
||||||
fgColor: [0, 0, 0],
|
fgColor: [0, 0, 0],
|
||||||
font: 'sans-serif',
|
font: 'sans-serif',
|
||||||
|
imageUrl: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
@ -409,6 +413,28 @@ function getRgb(hex: string | number): [number, number, number] | null {
|
||||||
if (m == null) return [0, 0, 0];
|
if (m == null) return [0, 0, 0];
|
||||||
return m.map(x => parseInt(x, 16) / 255) as [number, number, number];
|
return m.map(x => parseInt(x, 16) / 255) as [number, number, number];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function chooseFile(ev: MouseEvent) {
|
||||||
|
selectFile({
|
||||||
|
anchorElement: ev.currentTarget ?? ev.target,
|
||||||
|
multiple: false,
|
||||||
|
label: i18n.ts.selectFile,
|
||||||
|
features: {
|
||||||
|
watermark: false,
|
||||||
|
},
|
||||||
|
}).then((file) => {
|
||||||
|
if (!file.type.startsWith('image')) {
|
||||||
|
os.alert({
|
||||||
|
type: 'warning',
|
||||||
|
title: i18n.ts._watermarkEditor.driveFileTypeWarn,
|
||||||
|
text: i18n.ts._watermarkEditor.driveFileTypeWarnDescription,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
params.imageUrl = file.url;
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style module>
|
<style module>
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ export type ImageFrameParams = {
|
||||||
fgColor: [r: number, g: number, b: number];
|
fgColor: [r: number, g: number, b: number];
|
||||||
font: 'serif' | 'sans-serif';
|
font: 'serif' | 'sans-serif';
|
||||||
borderRadius: number; // TODO
|
borderRadius: number; // TODO
|
||||||
|
imageUrl: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ImageFramePreset = {
|
export type ImageFramePreset = {
|
||||||
|
|
@ -241,6 +242,17 @@ export class ImageFrameRenderer {
|
||||||
this.compositor.registerTexture('bottomLabel', bottomLabelImage);
|
this.compositor.registerTexture('bottomLabel', bottomLabelImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params.imageUrl != null) {
|
||||||
|
const frameImage = new Image();
|
||||||
|
frameImage.crossOrigin = 'anonymous';
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
frameImage.onload = () => resolve();
|
||||||
|
frameImage.onerror = () => reject(new Error('Failed to load frame image'));
|
||||||
|
frameImage.src = params.imageUrl!;
|
||||||
|
});
|
||||||
|
this.compositor.registerTexture('frameImage', frameImage);
|
||||||
|
}
|
||||||
|
|
||||||
this.compositor.changeResolution(renderWidth, renderHeight);
|
this.compositor.changeResolution(renderWidth, renderHeight);
|
||||||
|
|
||||||
this.compositor.render([{
|
this.compositor.render([{
|
||||||
|
|
@ -248,6 +260,7 @@ export class ImageFrameRenderer {
|
||||||
id: 'a',
|
id: 'a',
|
||||||
params: {
|
params: {
|
||||||
image: 'image',
|
image: 'image',
|
||||||
|
frameImage: 'frameImage',
|
||||||
topLabel: 'topLabel',
|
topLabel: 'topLabel',
|
||||||
bottomLabel: 'bottomLabel',
|
bottomLabel: 'bottomLabel',
|
||||||
topLabelEnabled: params.labelTop.enabled,
|
topLabelEnabled: params.labelTop.enabled,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ in vec2 in_uv;
|
||||||
uniform sampler2D in_texture;
|
uniform sampler2D in_texture;
|
||||||
uniform vec2 in_resolution;
|
uniform vec2 in_resolution;
|
||||||
uniform sampler2D u_image;
|
uniform sampler2D u_image;
|
||||||
|
uniform sampler2D u_frameImage;
|
||||||
uniform sampler2D u_topLabel;
|
uniform sampler2D u_topLabel;
|
||||||
uniform sampler2D u_bottomLabel;
|
uniform sampler2D u_bottomLabel;
|
||||||
uniform bool u_topLabelEnabled;
|
uniform bool u_topLabelEnabled;
|
||||||
|
|
@ -37,6 +38,9 @@ void main() {
|
||||||
remap(in_uv.y, u_paddingTop, 1.0 - u_paddingBottom, 0.0, 1.0)
|
remap(in_uv.y, u_paddingTop, 1.0 - u_paddingBottom, 0.0, 1.0)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
vec4 imageFrame_color = texture(u_frameImage, in_uv);
|
||||||
|
image_color = vec4(blendAlpha(image_color.rgb, imageFrame_color), 1.0);
|
||||||
|
|
||||||
vec4 topLabel_color = u_topLabelEnabled ? texture(u_topLabel, vec2(
|
vec4 topLabel_color = u_topLabelEnabled ? texture(u_topLabel, vec2(
|
||||||
in_uv.x,
|
in_uv.x,
|
||||||
remap(in_uv.y, 0.0, u_paddingTop, 0.0, 1.0)
|
remap(in_uv.y, 0.0, u_paddingTop, 0.0, 1.0)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { defineImageCompositorFunction } from '@/lib/ImageCompositor.js';
|
||||||
|
|
||||||
export const FN_frame = defineImageCompositorFunction<{
|
export const FN_frame = defineImageCompositorFunction<{
|
||||||
image: string | null;
|
image: string | null;
|
||||||
|
frameImage: string | null;
|
||||||
topLabel: string | null;
|
topLabel: string | null;
|
||||||
bottomLabel: string | null;
|
bottomLabel: string | null;
|
||||||
topLabelEnabled: boolean;
|
topLabelEnabled: boolean;
|
||||||
|
|
@ -36,21 +37,30 @@ export const FN_frame = defineImageCompositorFunction<{
|
||||||
gl.uniform1f(u.paddingRight, params.paddingRight);
|
gl.uniform1f(u.paddingRight, params.paddingRight);
|
||||||
gl.uniform3f(u.bg, params.bg[0], params.bg[1], params.bg[2]);
|
gl.uniform3f(u.bg, params.bg[0], params.bg[1], params.bg[2]);
|
||||||
|
|
||||||
|
if (params.frameImage != null) {
|
||||||
|
const frameImage = textures.get(params.frameImage);
|
||||||
|
if (frameImage) {
|
||||||
|
gl.activeTexture(gl.TEXTURE2);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, frameImage.texture);
|
||||||
|
gl.uniform1i(u.frameImage, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (params.topLabelEnabled && params.topLabel != null) {
|
if (params.topLabelEnabled && params.topLabel != null) {
|
||||||
const topLabel = textures.get(params.topLabel);
|
const topLabel = textures.get(params.topLabel);
|
||||||
if (topLabel) {
|
if (topLabel) {
|
||||||
gl.activeTexture(gl.TEXTURE2);
|
gl.activeTexture(gl.TEXTURE3);
|
||||||
gl.bindTexture(gl.TEXTURE_2D, topLabel.texture);
|
gl.bindTexture(gl.TEXTURE_2D, topLabel.texture);
|
||||||
gl.uniform1i(u.topLabel, 2);
|
gl.uniform1i(u.topLabel, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.bottomLabelEnabled && params.bottomLabel != null) {
|
if (params.bottomLabelEnabled && params.bottomLabel != null) {
|
||||||
const bottomLabel = textures.get(params.bottomLabel);
|
const bottomLabel = textures.get(params.bottomLabel);
|
||||||
if (bottomLabel) {
|
if (bottomLabel) {
|
||||||
gl.activeTexture(gl.TEXTURE3);
|
gl.activeTexture(gl.TEXTURE4);
|
||||||
gl.bindTexture(gl.TEXTURE_2D, bottomLabel.texture);
|
gl.bindTexture(gl.TEXTURE_2D, bottomLabel.texture);
|
||||||
gl.uniform1i(u.bottomLabel, 3);
|
gl.uniform1i(u.bottomLabel, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue