From 2232beacb7af14cf0f4575a9d05e1dcf315399c4 Mon Sep 17 00:00:00 2001 From: zefie Date: Sun, 26 Apr 2026 22:01:14 -0400 Subject: [PATCH] fix ALP generation and default to it --- zefie_wtvp_minisrv/app.js | 8 ++++- .../includes/classes/WTVImage.js | 30 ++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/zefie_wtvp_minisrv/app.js b/zefie_wtvp_minisrv/app.js index 94b69e38..832ff2ca 100644 --- a/zefie_wtvp_minisrv/app.js +++ b/zefie_wtvp_minisrv/app.js @@ -1314,9 +1314,15 @@ async function sendToClient(socket, headers_obj, data = null) { } + let imageArtemisType = 'ALP' // Add last modified if not a dynamic script if (socket_sessions[socket.id]) { if (socket_sessions[socket.id].request_headers) { + if (socket_sessions[socket.id].request_headers.query) { + if (socket_sessions[socket.id].request_headers.query.forceALF) { + imageArtemisType = 'ALF'; + } + } if (socket_sessions[socket.id].request_headers.service_file_path) { // Don't change Last-modified header if provided already if (!headers['Last-Modified'] && !headers['minisrv-no-last-modified']) { @@ -1354,7 +1360,7 @@ async function sendToClient(socket, headers_obj, data = null) { if (minisrv_config.config.image_decoder.image_formats && minisrv_config.config.image_decoder.image_formats.includes(headers_obj[contype_key].toLowerCase())) { const convertOpts = { jpegQuality: minisrv_config.config.image_decoder.jpg_quality, - type: 'ALF' + type: imageArtemisType }; if (minisrv_config.config.image_decoder.max_height > 0) convertOpts.maxHeight = minisrv_config.config.image_decoder.max_height; diff --git a/zefie_wtvp_minisrv/includes/classes/WTVImage.js b/zefie_wtvp_minisrv/includes/classes/WTVImage.js index 502dd168..06c83e51 100644 --- a/zefie_wtvp_minisrv/includes/classes/WTVImage.js +++ b/zefie_wtvp_minisrv/includes/classes/WTVImage.js @@ -17,7 +17,6 @@ * * ALP – the alpha table is prefixed with a black (0,0,0) phantom entry so * that palette index 0 is always fully-transparent black. - * ALP encoding does not yet work correctly. * ALF – the alpha table is suffixed with a black (0,0,0) phantom entry * (last palette slot is fully-transparent black). * @@ -367,6 +366,25 @@ class WTVImage { return { data: Buffer.concat(chunks), endOffset: offset }; } + /** + * Build the Artemis alpha lookup table payload for ALP/ALF. + * + * ALP stores its alpha table with a phantom transparent palette entry + * at index 0, so the payload omits that slot and encodes only the + * remaining indices. + * ALF stores its alpha table directly and may be left full-length. + */ + buildArtemisAlphaTable(type, alphaTable) { + if (type !== 'ALP') return Buffer.from(alphaTable); + + // Drop the phantom transparent index 0, then trim trailing opaque values + // since ALP app payloads are typically truncated like ALF. + const table = alphaTable.slice(1); + let trimEnd = table.length - 1; + while (trimEnd >= 0 && table[trimEnd] === 0xFF) trimEnd--; + return Buffer.from(table.slice(0, trimEnd + 1)); + } + // --------------------------------------------------------------------------- // Artemis alpha extension codec // --------------------------------------------------------------------------- @@ -461,7 +479,11 @@ class WTVImage { const rgba = Buffer.from(origData); for (let i = 0; i < pixelCount; i++) { const idx = indices[i]; - rgba[i * 4 + 3] = (idx < alphaTable.length) ? alphaTable[idx] : 0xFF; + if (type === 'ALP') { + rgba[i * 4 + 3] = (idx === 0) ? 0x00 : ((idx - 1 < alphaTable.length) ? alphaTable[idx - 1] : 0xFF); + } else { + rgba[i * 4 + 3] = (idx < alphaTable.length) ? alphaTable[idx] : 0xFF; + } } return { rgba, width, height, type }; @@ -697,7 +719,7 @@ class WTVImage { realPalette[transparentIdx * 3 + 2] = 0; } - const emitAlphaTable = fullAlpha; + const emitAlphaTable = this.buildArtemisAlphaTable(type, fullAlpha); const hasTransparent = bestZeroIdx >= 0; // Re-encode the LZW image stream from our (possibly swapped) indices. @@ -917,7 +939,7 @@ class WTVImage { // Emit full alphaTable (no truncation; WebTV may default missing // entries to 0x00 transparent rather than 0xFF opaque). - const emitAlphaTable = finalAlpha; + const emitAlphaTable = this.buildArtemisAlphaTable(type, finalAlpha); const minCodeSize = Math.max(2, Math.ceil(Math.log2(colors))); const appExtBlock = this.buildAppExtension('Artemis ', type, emitAlphaTable);