This commit is contained in:
syuilo 2025-11-02 18:57:01 +09:00
parent ec79b3ae37
commit 244f5a2793
6 changed files with 52 additions and 8 deletions

8
locales/index.d.ts vendored
View File

@ -5658,6 +5658,14 @@ export interface Locale extends ILocale {
*
*/
"withQrCode": string;
/**
*
*/
"backgroundColor": string;
/**
*
*/
"textColor": string;
};
"_compression": {
"_quality": {

View File

@ -1411,6 +1411,8 @@ _imageFrameEditor:
captionSub: "キャプション(小)"
availableVariables: "利用可能な変数"
withQrCode: "二次元コード"
backgroundColor: "背景色"
textColor: "文字色"
_compression:
_quality:

View File

@ -35,6 +35,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts._imageFrameEditor.borderThickness }}</template>
</MkRange>
<MkInput :modelValue="getHex(frame.bgColor)" type="color" @update:modelValue="v => { const c = getRgb(v); if (c != null) frame.bgColor = c; }">
<template #label>{{ i18n.ts._imageFrameEditor.backgroundColor }}</template>
</MkInput>
<MkInput :modelValue="getHex(frame.fgColor)" type="color" @update:modelValue="v => { const c = getRgb(v); if (c != null) frame.fgColor = c; }">
<template #label>{{ i18n.ts._imageFrameEditor.textColor }}</template>
</MkInput>
<MkFolder :defaultOpen="true">
<template #label>{{ i18n.ts._imageFrameEditor.header }}</template>
@ -180,7 +188,7 @@ const frame = reactive<ImageFrameParams>(deepClone(props.frame) ?? {
centered: false,
withQrCode: true,
},
bgColor: [255, 255, 255],
bgColor: [1, 1, 1],
fgColor: [0, 0, 0],
});
@ -321,6 +329,24 @@ async function save() {
emit('ok', preset);
}
function getHex(c: [number, number, number]) {
return `#${c.map(x => (x * 255).toString(16).padStart(2, '0')).join('')}`;
}
function getRgb(hex: string | number): [number, number, number] | null {
if (
typeof hex === 'number' ||
typeof hex !== 'string' ||
!/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(hex)
) {
return null;
}
const m = hex.slice(1).match(/[0-9a-fA-F]{2}/g);
if (m == null) return [0, 0, 0];
return m.map(x => parseInt(x, 16) / 255) as [number, number, number];
}
</script>
<style module>

View File

@ -18,14 +18,19 @@ uniform float u_paddingTop;
uniform float u_paddingBottom;
uniform float u_paddingLeft;
uniform float u_paddingRight;
uniform vec3 u_bg;
out vec4 out_color;
float remap(float value, float inputMin, float inputMax, float outputMin, float outputMax) {
return outputMin + (outputMax - outputMin) * ((value - inputMin) / (inputMax - inputMin));
}
vec3 blendAlpha(vec3 bg, vec4 fg) {
return fg.a * fg.rgb + (1.0 - fg.a) * bg;
}
void main() {
vec4 bg = vec4(1.0, 1.0, 1.0, 1.0);
vec4 bg = vec4(u_bg, 1.0);
vec4 image_color = texture(u_image, vec2(
remap(in_uv.x, u_paddingLeft, 1.0 - u_paddingRight, 0.0, 1.0),
@ -43,9 +48,9 @@ void main() {
)) : bg;
if (in_uv.y < u_paddingTop) {
out_color = topLabel_color;
out_color = vec4(blendAlpha(bg.rgb, topLabel_color), 1.0);
} else if (in_uv.y > (1.0 - u_paddingBottom)) {
out_color = bottomLabel_color;
out_color = vec4(blendAlpha(bg.rgb, bottomLabel_color), 1.0);
} else {
if (in_uv.y > u_paddingTop && in_uv.x > u_paddingLeft && in_uv.x < (1.0 - u_paddingRight)) {
out_color = image_color;

View File

@ -10,7 +10,7 @@ export const FX_frame = defineImageEffectorFx({
id: 'frame',
name: '(internal)',
shader,
uniforms: ['image', 'topLabel', 'bottomLabel', 'topLabelEnabled', 'bottomLabelEnabled', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'] as const,
uniforms: ['image', 'topLabel', 'bottomLabel', 'topLabelEnabled', 'bottomLabelEnabled', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight', 'bg'] as const,
params: {
image: {
type: 'textureRef',
@ -56,6 +56,10 @@ export const FX_frame = defineImageEffectorFx({
max: 1,
min: 0,
},
bg: {
type: 'color',
default: [1, 1, 1],
},
},
main: ({ gl, u, params, textures }) => {
const image = textures.image;
@ -71,6 +75,7 @@ export const FX_frame = defineImageEffectorFx({
gl.uniform1f(u.paddingBottom, params.paddingBottom);
gl.uniform1f(u.paddingLeft, params.paddingLeft);
gl.uniform1f(u.paddingRight, params.paddingRight);
gl.uniform3f(u.bg, params.bg[0], params.bg[1], params.bg[2]);
if (params.topLabelEnabled) {
const topLabel = textures.topLabel;

View File

@ -112,9 +112,6 @@ export class ImageFrameRenderer {
const qrSize = scaleBase * 0.1;
const qrMarginRight = Math.max((labelCanvasCtx.canvas.height - qrSize) / 2, paddingRight);
labelCanvasCtx.fillStyle = '#ffffff';
labelCanvasCtx.fillRect(0, 0, labelCanvasCtx.canvas.width, labelCanvasCtx.canvas.height);
labelCanvasCtx.fillStyle = '#000000';
labelCanvasCtx.font = `bold ${fontSize}px sans-serif`;
labelCanvasCtx.textBaseline = 'middle';
@ -241,6 +238,7 @@ export class ImageFrameRenderer {
paddingRight: paddingRight / renderWidth,
paddingTop: paddingTop / renderHeight,
paddingBottom: paddingBottom / renderHeight,
bg: params.bgColor,
},
}]);
}