From cabf963e9429771e9eb69ea2d2183bc4bcb26f37 Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Tue, 10 Feb 2026 21:08:38 +0100 Subject: [PATCH] feat: Add background color setting and apply it to HTML and WebGL output. --- src/components/ControlPanel.astro | 13 +++++++++++++ src/scripts/ascii-controller.ts | 6 ++++-- src/scripts/ascii-exporter.ts | 15 ++++++++++----- src/scripts/ascii-shared.ts | 2 ++ src/scripts/ui-bindings.ts | 25 +++++++++++++++++++++++++ src/scripts/webgl-ascii.ts | 12 ++++++++++-- 6 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/components/ControlPanel.astro b/src/components/ControlPanel.astro index 6a8424d..355b7f2 100644 --- a/src/components/ControlPanel.astro +++ b/src/components/ControlPanel.astro @@ -194,6 +194,19 @@ import { ChevronDown } from "@lucide/astro"; TINT + +
+ + + BG +
+ `; + let output = `
`;
         const charSet = CHAR_SETS[settings.charSet] || CHAR_SETS.standard;
         const charCount = charSet.length;
 
@@ -101,13 +101,11 @@ export class AsciiExporter {
 
                 // Adjust color for display
                 const adjColor = this.adjustColor(r, g, b, settings);
+                const finalColor = settings.color ? adjColor : this.hexToRgb(settings.monoColor);
 
                 // Recalculate luma from adjusted color for char selection
                 let finalLuma = (0.2126 * adjColor.r + 0.7152 * adjColor.g + 0.0722 * adjColor.b) / 255;
 
-                // Adjust luma curve for char selection (gamma/contrast/exposure) explicitly?
-                // Actually `adjustColor` does that.
-
                 if (settings.invert) finalLuma = 1.0 - finalLuma;
 
                 const charIndex = Math.floor(finalLuma * (charCount - 1) + 0.5);
@@ -116,7 +114,7 @@ export class AsciiExporter {
                 // Escape HTML
                 const safeChar = char === '<' ? '<' : char === '>' ? '>' : char === '&' ? '&' : char;
 
-                const colorStyle = `color: rgb(${Math.round(adjColor.r)}, ${Math.round(adjColor.g)}, ${Math.round(adjColor.b)})`;
+                const colorStyle = `color: rgb(${Math.round(finalColor.r)}, ${Math.round(finalColor.g)}, ${Math.round(finalColor.b)})`;
                 output += `${safeChar}`;
             }
             output += "
"; @@ -177,4 +175,11 @@ export class AsciiExporter { b: Math.max(0, Math.min(255, bNorm * 255)) }; } + + private static hexToRgb(hex: string): { r: number, g: number, b: number } { + const r = parseInt(hex.slice(1, 3), 16); + const g = parseInt(hex.slice(3, 5), 16); + const b = parseInt(hex.slice(5, 7), 16); + return { r, g, b }; + } } diff --git a/src/scripts/ascii-shared.ts b/src/scripts/ascii-shared.ts index c8451b8..ab77e8e 100644 --- a/src/scripts/ascii-shared.ts +++ b/src/scripts/ascii-shared.ts @@ -30,6 +30,7 @@ export interface AsciiOptions { scanlines?: number; vignette?: number; monoColor?: string; + backgroundColor?: string; } export interface AsciiResult { @@ -69,6 +70,7 @@ export interface AsciiSettings { scanlines: number; vignette: number; monoColor: string; + backgroundColor: string; } // ============= Constants ============= diff --git a/src/scripts/ui-bindings.ts b/src/scripts/ui-bindings.ts index 04ebd39..6bd5d2d 100644 --- a/src/scripts/ui-bindings.ts +++ b/src/scripts/ui-bindings.ts @@ -243,6 +243,19 @@ export class UIBindings { } }); } + + const bgColorInput = document.getElementById('input-bg-color') as HTMLInputElement; + const bgColorSwatch = document.getElementById('bg-color-swatch-display'); + + if (bgColorInput) { + bgColorInput.addEventListener('input', () => { + if (this.isUpdatingUI) return; + this.controller.setSetting('backgroundColor', bgColorInput.value); + if (bgColorSwatch) { + bgColorSwatch.style.backgroundColor = bgColorInput.value; + } + }); + } } // ============= Segments ============= @@ -552,6 +565,18 @@ export class UIBindings { } } + const bgColorInput = document.getElementById('input-bg-color') as HTMLInputElement; + const bgColorSwatch = document.getElementById('bg-color-swatch-display'); + + if (bgColorInput && settings.backgroundColor) { + if (bgColorInput.value !== settings.backgroundColor) { + bgColorInput.value = settings.backgroundColor; + } + if (bgColorSwatch) { + bgColorSwatch.style.backgroundColor = settings.backgroundColor; + } + } + this.updateQueueDisplay(); this.isUpdatingUI = false; diff --git a/src/scripts/webgl-ascii.ts b/src/scripts/webgl-ascii.ts index 8702820..afd91f3 100644 --- a/src/scripts/webgl-ascii.ts +++ b/src/scripts/webgl-ascii.ts @@ -21,6 +21,7 @@ export interface RenderOptions { scanlines?: number; vignette?: number; monoColor?: string; + backgroundColor?: string; zoom?: number; zoomCenter?: { x: number; y: number }; mousePos?: { x: number; y: number }; @@ -120,6 +121,7 @@ export class WebGLAsciiRenderer { uniform float u_scanlines; uniform float u_vignette; uniform vec3 u_monoColor; + uniform vec3 u_backgroundColor; uniform float u_zoom; uniform vec2 u_zoomCenter; @@ -323,7 +325,7 @@ export class WebGLAsciiRenderer { finalColor = u_monoColor; } - gl_FragColor = vec4(finalColor * charAlpha, charAlpha); + gl_FragColor = vec4(mix(u_backgroundColor, finalColor, charAlpha), 1.0); } `; @@ -358,7 +360,7 @@ export class WebGLAsciiRenderer { 'u_invert', 'u_color', 'u_overlayStrength', 'u_edgeMode', 'u_dither', 'u_denoise', 'u_sharpen', 'u_edgeThreshold', 'u_shadows', 'u_highlights', - 'u_scanlines', 'u_vignette', 'u_monoColor', + 'u_scanlines', 'u_vignette', 'u_monoColor', 'u_backgroundColor', 'u_zoom', 'u_zoomCenter', 'u_mousePos', 'u_magnifierRadius', 'u_magnifierZoom', 'u_showMagnifier', 'u_aspect' ]; @@ -515,6 +517,12 @@ export class WebGLAsciiRenderer { const b = parseInt(hex.slice(5, 7), 16) / 255.0; gl.uniform3f(u['u_monoColor'], r, g, b); + const bgHex = options.backgroundColor || '#000000'; + const br = parseInt(bgHex.slice(1, 3), 16) / 255.0; + const bg = parseInt(bgHex.slice(3, 5), 16) / 255.0; + const bb = parseInt(bgHex.slice(5, 7), 16) / 255.0; + gl.uniform3f(u['u_backgroundColor'], br, bg, bb); + gl.uniform1f(u['u_zoom'], options.zoom || 1.0); gl.uniform2f(u['u_zoomCenter'], options.zoomCenter?.x ?? 0.5, options.zoomCenter?.y ?? 0.5); gl.uniform2f(u['u_mousePos'], options.mousePos?.x ?? -1.0, options.mousePos?.y ?? -1.0);