This commit is contained in:
kakkokari-gtyih 2024-12-16 10:16:22 +09:00
parent 4fc3916219
commit 32ee4d8c7a
3 changed files with 116 additions and 19 deletions

View file

@ -9,8 +9,9 @@ SPDX-License-Identifier: AGPL-3.0-only
:width="1000"
:height="600"
:scroll="false"
:withOkButton="false"
:withOkButton="true"
@close="cancel()"
@ok="save()"
@closed="emit('closed')"
>
<template #header><i class="ti ti-ripple"></i> {{ i18n.ts.watermark }}</template>
@ -56,13 +57,15 @@ function cancel() {
dialogEl.value?.close();
}
function close() {
function save() {
emit('ok');
dialogEl.value?.close();
}
//#endregion
//#region
const useWatermark = computed(defaultStore.makeGetterSetter('useWatermark'));
const watermarkConfig = ref(defaultStore.state.watermarkConfig);
//#endregion
//#region Canvas

View file

@ -0,0 +1,109 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { getProxiedImageUrl } from "@/scripts/media-proxy.js";
export type WatermarkConfig = {
fileId: string | null;
fileUrl: string | null;
width: number | null;
height: number | null;
enlargement: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad';
gravity: 'auto' | 'left' | 'right' | 'top' | 'bottom';
opacity: number;
repeat: true | false | 'x' | 'y';
anchor: 'center' | 'top' | 'left' | 'bottom' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
offsetTop: number | null;
offsetLeft: number | null;
offsetBottom: number | null;
offsetRight: number | null;
backgroundColor: string | null;
rotate: number | null;
};
/**
*
*
* @param img stringは画像URL****
* @param el
* @param config
*/
export function applyWatermark(img: string | Blob, el: HTMLCanvasElement, config: WatermarkConfig) {
const canvas = el;
const ctx = canvas.getContext('2d')!;
const imgEl = new Image();
imgEl.onload = () => {
canvas.width = imgEl.width;
canvas.height = imgEl.height;
ctx.drawImage(imgEl, 0, 0);
if (config.fileUrl) {
const watermark = new Image();
watermark.onload = () => {
const width = config.width || watermark.width;
const height = config.height || watermark.height;
const x = (() => {
switch (config.anchor) {
case 'center':
case 'top':
case 'bottom':
return (canvas.width - width) / 2;
case 'left':
case 'top-left':
case 'bottom-left':
return 0;
case 'right':
case 'top-right':
case 'bottom-right':
return canvas.width - width;
}
})();
const y = (() => {
switch (config.anchor) {
case 'center':
case 'left':
case 'right':
return (canvas.height - height) / 2;
case 'top':
case 'top-left':
case 'top-right':
return 0;
case 'bottom':
case 'bottom-left':
case 'bottom-right':
return canvas.height - height;
}
})();
ctx.globalAlpha = config.opacity;
ctx.drawImage(watermark, x, y, width, height);
};
watermark.src = config.fileUrl;
}
};
if (typeof img === 'string') {
imgEl.src = getProxiedImageUrl(img, undefined, true);
} else {
const reader = new FileReader();
reader.onload = () => {
imgEl.src = reader.result as string;
};
reader.readAsDataURL(img);
}
}
/**
* Blobとして取得する
*
* @param img
* @param config
* @returns Blob
*/
export function getWatermarkAppliedImage(img: Blob, config: WatermarkConfig): Promise<Blob> {
const canvas = document.createElement('canvas');
applyWatermark(img, canvas, config);
return new Promise<Blob>(resolve => {
canvas.toBlob(blob => {
resolve(blob!);
});
});
}

View file

@ -9,6 +9,7 @@ import { hemisphere } from '@@/js/intl-const.js';
import lightTheme from '@@/themes/l-light.json5';
import darkTheme from '@@/themes/d-green-lime.json5';
import type { SoundType } from '@/scripts/sound.js';
import type { WatermarkConfig } from './scripts/watermark.js';
import { DEFAULT_DEVICE_KIND, type DeviceKind } from '@/scripts/device-kind.js';
import { miLocalStorage } from '@/local-storage.js';
import { Storage } from '@/pizzax.js';
@ -480,23 +481,7 @@ export const defaultStore = markRaw(new Storage('base', {
},
watermarkConfig: {
where: 'account',
default: null as {
fileId: string | null;
fileUrl: string | null;
width: number | null;
height: number | null;
enlargement: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad';
gravity: 'auto' | 'left' | 'right' | 'top' | 'bottom';
opacity: number;
repeat: true | false | 'x' | 'y';
anchor: 'center' | 'top' | 'left' | 'bottom' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
offsetTop: number | null;
offsetLeft: number | null;
offsetBottom: number | null;
offsetRight: number | null;
backgroundColor: string | null;
rotate: number | null;
} | null,
default: null as WatermarkConfig | null,
},
sound_masterVolume: {