This commit is contained in:
syuilo 2025-09-19 20:55:24 +09:00
parent dc7d50f583
commit 7a17f021d1
6 changed files with 35 additions and 22 deletions

8
locales/index.d.ts vendored
View File

@ -12236,9 +12236,9 @@ export interface Locale extends ILocale {
*/
"text": string;
/**
* ()
*
*/
"accountQr": string;
"qr": string;
/**
*
*/
@ -12303,6 +12303,10 @@ export interface Locale extends ILocale {
*
*/
"polkadotSubDotDivisions": string;
/**
* URLになります
*/
"leaveBlankToAccountUrl": string;
};
"_imageEffector": {
/**

View File

@ -3275,7 +3275,7 @@ _watermarkEditor:
opacity: "不透明度"
scale: "サイズ"
text: "テキスト"
accountQr: "二次元コード (アカウント)"
qr: "二次元コード"
position: "位置"
margin: "マージン"
type: "タイプ"
@ -3292,6 +3292,7 @@ _watermarkEditor:
polkadotSubDotOpacity: "サブドットの不透明度"
polkadotSubDotRadius: "サブドットの大きさ"
polkadotSubDotDivisions: "サブドットの数"
leaveBlankToAccountUrl: "空欄にするとアカウントのURLになります"
_imageEffector:
title: "エフェクト"

View File

@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
@update:modelValue="(v) => (layer as Extract<WatermarkPreset['layers'][number], { type: 'account-qr' }>).align.margin = v"
@update:modelValue="(v) => (layer as Extract<WatermarkPreset['layers'][number], { type: 'text' }>).align.margin = v"
>
<template #label>{{ i18n.ts._watermarkEditor.margin }}</template>
</MkRange>
@ -85,7 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
@update:modelValue="(v) => (layer as Extract<WatermarkPreset['layers'][number], { type: 'account-qr' }>).align.margin = v"
@update:modelValue="(v) => (layer as Extract<WatermarkPreset['layers'][number], { type: 'image' }>).align.margin = v"
>
<template #label>{{ i18n.ts._watermarkEditor.margin }}</template>
</MkRange>
@ -131,7 +131,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
</template>
<template v-else-if="layer.type === 'account-qr'">
<template v-else-if="layer.type === 'qr'">
<MkInput v-model="layer.data" debounce>
<template #label>{{ i18n.ts._watermarkEditor.text }}</template>
<template #caption>{{ i18n.ts._watermarkEditor.leaveBlankToAccountUrl }}</template>
</MkInput>
<FormSlot>
<template #label>{{ i18n.ts._watermarkEditor.position }}</template>
<MkPositionSelector
@ -147,7 +152,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:step="0.01"
:textConverter="(v) => (v * 100).toFixed(1) + '%'"
continuousUpdate
@update:modelValue="(v) => (layer as Extract<WatermarkPreset['layers'][number], { type: 'account-qr' }>).align.margin = v"
@update:modelValue="(v) => (layer as Extract<WatermarkPreset['layers'][number], { type: 'qr' }>).align.margin = v"
>
<template #label>{{ i18n.ts._watermarkEditor.margin }}</template>
</MkRange>

View File

@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>
<div v-if="layer.type === 'text'">{{ i18n.ts._watermarkEditor.text }}</div>
<div v-if="layer.type === 'image'">{{ i18n.ts._watermarkEditor.image }}</div>
<div v-if="layer.type === 'account-qr'">{{ i18n.ts._watermarkEditor.accountQr }}</div>
<div v-if="layer.type === 'qr'">{{ i18n.ts._watermarkEditor.qr }}</div>
<div v-if="layer.type === 'stripe'">{{ i18n.ts._watermarkEditor.stripe }}</div>
<div v-if="layer.type === 'polkadot'">{{ i18n.ts._watermarkEditor.polkadot }}</div>
<div v-if="layer.type === 'checker'">{{ i18n.ts._watermarkEditor.checker }}</div>
@ -108,10 +108,11 @@ function createImageLayer(): WatermarkPreset['layers'][number] {
};
}
function createAccountQrLayer(): WatermarkPreset['layers'][number] {
function createQrLayer(): WatermarkPreset['layers'][number] {
return {
id: genId(),
type: 'account-qr',
type: 'qr',
data: '',
align: { x: 'right', y: 'bottom', margin: 0 },
scale: 0.3,
opacity: 1,
@ -317,9 +318,9 @@ function addLayer(ev: MouseEvent) {
preset.layers.push(createImageLayer());
},
}, {
text: i18n.ts._watermarkEditor.accountQr,
text: i18n.ts._watermarkEditor.qr,
action: () => {
preset.layers.push(createAccountQrLayer());
preset.layers.push(createQrLayer());
},
}, {
text: i18n.ts._watermarkEditor.stripe,

View File

@ -67,7 +67,7 @@ interface TextureParamDef extends CommonParamDef {
} | {
type: 'url'; url: string | null;
} | {
type: 'account-qr';
type: 'qr'; data: string | null;
} | null;
};
@ -337,7 +337,7 @@ export class ImageEffector<IEX extends ReadonlyArray<ImageEffectorFx<any, any, a
const texture =
v.type === 'text' ? await createTextureFromText(this.gl, v.text) :
v.type === 'url' ? await createTextureFromUrl(this.gl, v.url) :
v.type === 'account-qr' ? await createTextureFromAccountQr(this.gl) :
v.type === 'qr' ? await createTextureFromQr(this.gl, { data: v.data }) :
null;
if (texture == null) continue;
@ -369,7 +369,7 @@ export class ImageEffector<IEX extends ReadonlyArray<ImageEffectorFx<any, any, a
return (
v.type === 'text' ? `text:${v.text}` :
v.type === 'url' ? `url:${v.url}` :
v.type === 'account-qr' ? 'account-qr' :
v.type === 'qr' ? `qr:${v.data}` :
''
);
}
@ -487,7 +487,7 @@ async function createTextureFromText(gl: WebGL2RenderingContext, text: string |
return info;
}
async function createTextureFromAccountQr(gl: WebGL2RenderingContext, resolution = 512): Promise<{ texture: WebGLTexture, width: number, height: number } | null> {
async function createTextureFromQr(gl: WebGL2RenderingContext, options: { data: string | null }, resolution = 512): Promise<{ texture: WebGLTexture, width: number, height: number } | null> {
const $i = ensureSignin();
const qrCodeInstance = new QRCodeStyling({
@ -495,7 +495,7 @@ async function createTextureFromAccountQr(gl: WebGL2RenderingContext, resolution
height: resolution,
margin: 42,
type: 'canvas',
data: `${url}/users/${$i.id}`,
data: options.data == null || options.data === '' ? `${url}/users/${$i.id}` : options.data,
image: $i.avatarUrl,
qrOptions: {
typeNumber: 0,
@ -522,12 +522,12 @@ async function createTextureFromAccountQr(gl: WebGL2RenderingContext, resolution
const blob = await qrCodeInstance.getRawData('png') as Blob | null;
if (blob == null) return null;
const data = await window.createImageBitmap(blob);
const image = await window.createImageBitmap(blob);
const texture = createTexture(gl);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, resolution, resolution, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, resolution, resolution, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.bindTexture(gl.TEXTURE_2D, null);
return {

View File

@ -44,7 +44,8 @@ export type WatermarkPreset = {
opacity: number;
} | {
id: string;
type: 'account-qr';
type: 'qr';
data: string;
scale: number;
align: Align;
opacity: number;
@ -133,7 +134,7 @@ export class WatermarkRenderer {
},
},
};
} else if (layer.type === 'account-qr') {
} else if (layer.type === 'qr') {
return {
fxId: 'watermarkPlacement',
id: layer.id,
@ -145,7 +146,8 @@ export class WatermarkRenderer {
opacity: layer.opacity,
cover: false,
watermark: {
type: 'account-qr',
type: 'qr',
data: layer.data,
},
},
};