diff --git a/src/lib/components/ImagePreview.svelte b/src/lib/components/ImagePreview.svelte index 6e18725..3d89505 100644 --- a/src/lib/components/ImagePreview.svelte +++ b/src/lib/components/ImagePreview.svelte @@ -18,16 +18,86 @@ let previewDataUrl = $state(null); let isProcessing = $state(false); let debounceTimer: ReturnType | null = null; + let initialEffectiveConfig = $state(null); const isManualCrop = $derived(config.crop.enabled && config.crop.mode === 'manual'); const targetAspect = $derived(device.width / device.height); + // Build a string representing only config values that affect output + // Properties at neutral values are equivalent to being disabled + function getEffectiveConfig(cfg: PipelineConfig): string { + const effective: Record = {}; + + // Crop: only matters if enabled + if (cfg.crop.enabled) { + effective.crop = { mode: cfg.crop.mode, region: cfg.crop.region }; + } + + // Resize: only matters if enabled + if (cfg.resize.enabled) { + effective.resize = true; + } + + // Brightness: 0 is neutral + if (cfg.brightness.enabled && cfg.brightness.value !== 0) { + effective.brightness = cfg.brightness.value; + } + + // Contrast: 0 is neutral + if (cfg.contrast.enabled && cfg.contrast.value !== 0) { + effective.contrast = cfg.contrast.value; + } + + // Gamma: 1.0 is neutral + if (cfg.gamma.enabled && cfg.gamma.value !== 1.0) { + effective.gamma = cfg.gamma.value; + } + + // Greyscale: only matters if enabled + if (cfg.greyscale.enabled) { + effective.greyscale = true; + } + + // Sharpen: 0 amount is neutral + if (cfg.sharpen.enabled && cfg.sharpen.amount > 0) { + effective.sharpen = cfg.sharpen.amount; + } + + // Auto-levels: only matters if enabled + if (cfg.autoLevels.enabled) { + effective.autoLevels = cfg.autoLevels.clipPercent; + } + + // Dither: only matters if enabled + if (cfg.dither.enabled) { + effective.dither = cfg.dither.algorithm; + } + + // Quantize: only matters if enabled + if (cfg.quantize.enabled) { + effective.quantize = cfg.quantize.levels; + } + + return JSON.stringify(effective); + } + + // Capture initial effective config on mount + $effect(() => { + if (initialEffectiveConfig === null) { + initialEffectiveConfig = getEffectiveConfig(config); + } + }); + // Track config changes and trigger debounced processing $effect(() => { - // Access config to create dependency (but skip processing in manual crop mode without region) - const configStr = JSON.stringify(config); + const effectiveConfig = getEffectiveConfig(config); - // Don't process if in manual crop mode - wait for region to be set + // Skip if effective config hasn't changed (prevents unnecessary reprocessing) + if (effectiveConfig === initialEffectiveConfig) { + return; + } + + // Don't process if in manual crop mode without region if (isManualCrop && !config.crop.region) { return; }