feat: Add background color setting and apply it to HTML and WebGL output.
This commit is contained in:
@@ -194,6 +194,19 @@ import { ChevronDown } from "@lucide/astro";
|
||||
<span class="label">TINT</span>
|
||||
</div>
|
||||
|
||||
<!-- Background Color Picker -->
|
||||
<div class="tui-color-btn" title="Background Color">
|
||||
<input
|
||||
type="color"
|
||||
id="input-bg-color"
|
||||
value="#000000"
|
||||
/>
|
||||
<span
|
||||
id="bg-color-swatch-display"
|
||||
class="color-swatch"></span>
|
||||
<span class="label">BG</span>
|
||||
</div>
|
||||
|
||||
<TuiToggle
|
||||
id="toggle-denoise"
|
||||
label="Denoise"
|
||||
|
||||
@@ -132,7 +132,8 @@ export class AsciiController {
|
||||
highlights: 0,
|
||||
scanlines: 0,
|
||||
vignette: 0,
|
||||
monoColor: '#ffffff'
|
||||
monoColor: '#ffffff',
|
||||
backgroundColor: '#000000'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -260,7 +261,8 @@ export class AsciiController {
|
||||
highlights: suggestions.highlights ?? this.settings.highlights,
|
||||
scanlines: suggestions.scanlines ?? this.settings.scanlines,
|
||||
vignette: suggestions.vignette ?? this.settings.vignette,
|
||||
monoColor: suggestions.monoColor ?? this.settings.monoColor
|
||||
monoColor: suggestions.monoColor ?? this.settings.monoColor,
|
||||
backgroundColor: suggestions.backgroundColor ?? this.settings.backgroundColor
|
||||
};
|
||||
|
||||
this.onSettingsChange?.();
|
||||
|
||||
@@ -80,7 +80,7 @@ export class AsciiExporter {
|
||||
heightRows: number
|
||||
): string {
|
||||
const { pixels } = this.getPixels(img, widthCols, heightRows);
|
||||
let output = `<pre style="font-family: monospace; line-height: 1; letter-spacing: 0; background-color: #000; color: #fff; font-size: 8px;">`;
|
||||
let output = `<pre style="font-family: monospace; line-height: 1; letter-spacing: 0; background-color: ${settings.backgroundColor}; color: #fff; font-size: 8px;">`;
|
||||
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 += `<span style="${colorStyle}">${safeChar}</span>`;
|
||||
}
|
||||
output += "<br>";
|
||||
@@ -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 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 =============
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user