added visual setting for border padding filter

This commit is contained in:
Arnaud_Cayrol 2026-02-15 19:15:09 +01:00
parent 105691815d
commit 1126b2525f
2 changed files with 150 additions and 4 deletions

View file

@ -5,6 +5,7 @@
import EyeIndicator from './visual/EyeIndicator.svelte';
import AlignmentIndicator from './visual/AlignmentIndicator.svelte';
import BrightnessIndicator from './visual/BrightnessIndicator.svelte';
import PaddingIndicator from './visual/PaddingIndicator.svelte';
let { disabled = false } = $props();
@ -216,13 +217,13 @@
bind:checked={config.processing.crop.enabled}
/>
</div>
<div class="section-hint">Faces too close to the edge of the photo are often distorted and require too much padding</div>
<div class="section-hint">Padding happens when a face is at the border of a photo. It is required to move the subject to the center of the frame.</div>
{#if config.processing.crop.enabled}
<div class="setting-row sub-setting">
<div class="setting-row sub-setting no-border">
<label for="max-padding">
<span class="setting-label">Max Padding</span>
<span class="setting-hint">Maximum allowed padding from edge replication</span>
<span class="setting-hint">Maximum allowed padding</span>
</label>
<div class="setting-control">
<input
@ -236,6 +237,9 @@
<span class="value">{config.processing.crop.max_padding_percent}%</span>
</div>
</div>
<div class="padding-indicator-container">
<PaddingIndicator maxPaddingPercent={config.processing.crop.max_padding_percent} />
</div>
{/if}
</div>
@ -738,7 +742,8 @@
border-bottom: 1px solid #252525;
}
.setting-row:last-child {
.setting-row:last-child,
.setting-row.no-border {
border-bottom: none;
}
@ -992,6 +997,16 @@
text-align: center;
}
.padding-indicator-container {
display: flex;
justify-content: center;
width: fit-content;
margin: 0 auto;
padding: 0.75rem 1rem;
background: #0f0f0f;
border-radius: 6px;
}
.brightness-indicator-container {
display: flex;
justify-content: center;
@ -1000,4 +1015,5 @@
border: 1px solid #333;
border-radius: 6px;
}
</style>

View file

@ -0,0 +1,130 @@
<script>
/**
* Visual indicator for the max padding threshold.
* Shows two nested squares: the full output frame (outer) and the
* minimum real-pixel region (inner). The border zone between them
* represents padding (duplicated edge pixels) and is tinted red.
*
* @param {number} maxPaddingPercent - Max allowed padding percent (5-80)
*/
let { maxPaddingPercent = 30 } = $props();
const frameSize = 100;
const svgSize = 116;
const frameOffset = (svgSize - frameSize) / 2;
// A padding fraction of P% means up to P% of the area can be outside the
// original photo. For a uniform border: area_fraction = 1 - inner_side².
// So inner_side_fraction = sqrt(1 - P/100).
const innerSideFraction = $derived(Math.sqrt(Math.max(0, 1 - maxPaddingPercent / 100)));
const innerSize = $derived(frameSize * innerSideFraction);
const border = $derived((frameSize - innerSize) / 2);
const innerX = $derived(frameOffset + border);
const innerY = $derived(frameOffset + border);
</script>
<div class="padding-indicator-wrap">
<svg viewBox="0 0 {svgSize} {svgSize}" class="padding-svg" role="img" aria-label="Padding threshold visualization">
<!-- Outer square: full output frame -->
<rect
x={frameOffset}
y={frameOffset}
width={frameSize}
height={frameSize}
fill="#2a2a2a"
stroke="#666"
stroke-width="1.5"
rx="2"
/>
<!-- Padding zone: red tint over the border area -->
<rect
x={frameOffset}
y={frameOffset}
width={frameSize}
height={frameSize}
fill="rgba(239, 68, 68, 0.30)"
rx="2"
/>
<!-- Inner square: real image pixels region -->
{#if innerSize > 2}
<rect
x={innerX}
y={innerY}
width={innerSize}
height={innerSize}
fill="#3a3a3a"
/>
<!-- Dashed border to distinguish inner from outer -->
<rect
x={innerX}
y={innerY}
width={innerSize}
height={innerSize}
fill="none"
stroke="rgba(255,255,255,0.35)"
stroke-width="1"
stroke-dasharray="3 2"
/>
{/if}
</svg>
<div class="legend">
<div class="legend-item">
<span class="swatch real"></span>
real image pixels
</div>
<div class="legend-item">
<span class="swatch padding"></span>
padding (duplicated edge pixels)
</div>
</div>
</div>
<style>
.padding-indicator-wrap {
display: flex;
flex-direction: row;
align-items: center;
gap: 1rem;
}
.padding-svg {
width: 116px;
height: 116px;
flex-shrink: 0;
}
.legend {
display: flex;
flex-direction: column;
gap: 0.4rem;
font-size: 0.75rem;
color: #aaa;
}
.legend-item {
display: flex;
align-items: center;
gap: 0.4rem;
}
.swatch {
display: inline-block;
width: 12px;
height: 12px;
border: 1px solid #666;
border-radius: 2px;
flex-shrink: 0;
}
.swatch.real {
background: #3a3a3a;
}
.swatch.padding {
background: color-mix(in srgb, rgb(239, 68, 68) 30%, #2a2a2a);
}
</style>