From a30874d8f1ea57bb5783c106af85c478dfd66786 Mon Sep 17 00:00:00 2001 From: javl Date: Sat, 15 Aug 2026 11:16:24 +0200 Subject: [PATCH] Fix issue with size of first image getting applied to all images. Fixes issue #61 --- js/script.js | 46 ++++++++++++++++++++++----------------- tests/test_conversions.py | 34 +++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/js/script.js b/js/script.js index 1cc2c67..d5de5b5 100644 --- a/js/script.js +++ b/js/script.js @@ -171,7 +171,11 @@ const CONVERSION_BYTES_PER_VALUE = new Map([ // An images collection with helper methods function Images() { const collection = []; - this.push = (img, canvas, glyph) => { collection.push({ img, canvas, glyph }); }; + this.push = (img, canvas, glyph, width, height) => { + collection.push({ + img, canvas, glyph, width, height, + }); + }; this.remove = (image) => { const i = collection.indexOf(image); if (i !== -1) collection.splice(i, 1); @@ -217,9 +221,11 @@ function placeImage(_image) { const { canvas } = _image; const ctx = canvas.getContext('2d'); - // reset canvas size - canvas.width = Number.isFinite(settings.screenWidth) && settings.screenWidth > 0 ? settings.screenWidth : 1; - canvas.height = Number.isFinite(settings.screenHeight) && settings.screenHeight > 0 ? settings.screenHeight : 1; + // reset canvas size (falls back to the canvas' current size for images, + // like a re-imported byte array, that aren't tracked in the image list + // with their own width/height) + canvas.width = Number.isFinite(_image.width) && _image.width > 0 ? _image.width : canvas.width; + canvas.height = Number.isFinite(_image.height) && _image.height > 0 ? _image.height : canvas.height; // eslint-disable-next-line no-param-reassign _image.ctx = ctx; ctx.save(); @@ -358,8 +364,8 @@ function placeImage(_image) { ctx.fillRect(0, 0, canvas.width, canvas.height); if (settings.rotation === 90) { - canvas.width = settings.screenHeight; - canvas.height = settings.screenWidth; + canvas.width = clone.height; + canvas.height = clone.width; ctx.setTransform(1, 0, 0, 1, canvas.width, 0); ctx.rotate(Math.PI / 2); ctx.drawImage(clone, 0, 0); @@ -368,8 +374,8 @@ function placeImage(_image) { ctx.rotate(Math.PI); ctx.drawImage(clone, 0, 0); } else if (settings.rotation === 270) { - canvas.width = settings.screenHeight; - canvas.height = settings.screenWidth; + canvas.width = clone.height; + canvas.height = clone.width; ctx.setTransform(1, 0, 0, 1, 0, canvas.height); ctx.rotate(Math.PI * 1.5); ctx.drawImage(clone, 0, 0); @@ -399,6 +405,7 @@ function updateAllImages() { } // Easy way to update settings controlled by a textfield +// eslint-disable-next-line no-unused-vars function updateInteger(fieldName) { settings[fieldName] = parseInt(document.getElementById(fieldName).value); updateAllImages(); @@ -730,11 +737,10 @@ function handleImageSelection(evt) { w.min = 1; w.className = 'size-input'; w.value = img.width; - settings.screenWidth = img.width; w.oninput = () => { - canvas.width = w.value; - updateAllImages(); - updateInteger('screenWidth'); + const image = images.get(img); + image.width = parseInt(w.value, 10); + placeImage(image); }; const h = document.createElement('input'); @@ -744,11 +750,10 @@ function handleImageSelection(evt) { h.min = 1; h.className = 'size-input'; h.value = img.height; - settings.screenHeight = img.height; h.oninput = () => { - canvas.height = h.value; - updateAllImages(); - updateInteger('screenHeight'); + const image = images.get(img); + image.height = parseInt(h.value, 10); + placeImage(image); }; const gil = document.createElement('span'); @@ -759,7 +764,8 @@ function handleImageSelection(evt) { gi.type = 'text'; gi.name = 'glyph'; gi.className = 'glyph-input'; - gi.value = file.name.split('.')[0]; + const [fileName] = file.name.split('.'); + gi.value = fileName; gi.onchange = () => { const image = images.get(img); image.glyph = gi.value; @@ -820,7 +826,7 @@ function handleImageSelection(evt) { canvas.height = img.height; canvasContainer.appendChild(canvas); - images.push(img, canvas, file.name.split('.')[0]); + images.push(img, canvas, file.name.split('.')[0], img.width, img.height); if (images.length() > 1) { document.getElementById('all-same-size').style.display = 'block'; } @@ -895,8 +901,8 @@ function generateOutputString() { const storageNote = settings.esp32Format ? '' : ' in PROGMEM'; outputString += `// Array of all bitmaps for convenience. (Total bytes used to store images${storageNote} = ${bytesUsed})\n`; } - outputString += `const int ${getIdentifier()}_allArray_LEN = ${varQuickArray.length};\n`; - outputString += `const ${getImageType()}* ${getIdentifier()}_allArray[${varQuickArray.length}] = {\n\t${varQuickArray.join(',\n\t')}\n};\n`; + outputString += `const int ${getIdentifier()}allArray_LEN = ${varQuickArray.length};\n`; + outputString += `const ${getImageType()}* ${getIdentifier()}allArray[${varQuickArray.length}] = {\n\t${varQuickArray.join(',\n\t')}\n};\n`; break; } diff --git a/tests/test_conversions.py b/tests/test_conversions.py index 2a4cffe..a26d268 100644 --- a/tests/test_conversions.py +++ b/tests/test_conversions.py @@ -272,6 +272,32 @@ def run_roundtrip(page, output, draw_mode, width, height): return page.input_value("#code-output") +def run_multi_image_independent_sizes(page): + """Each image in a multi-image upload keeps its own canvas size; setting + one image's width/height must not resize any other image's canvas. + Regression test for a bug where every canvas was forced to the size of + the first uploaded image.""" + page.goto(INDEX_HTML.as_uri()) + page.set_input_files("#file-input", [str(TEST_IMAGE), str(TEST_IMAGE)]) + page.wait_for_function("document.querySelectorAll('#image-size-settings li').length === 2") + + page.fill("#image-size-settings li:nth-child(1) input[name='width']", "50") + page.fill("#image-size-settings li:nth-child(1) input[name='height']", "60") + page.fill("#image-size-settings li:nth-child(2) input[name='width']", "20") + page.fill("#image-size-settings li:nth-child(2) input[name='height']", "10") + + sizes = page.evaluate( + "[images.getByIndex(0).canvas.width, images.getByIndex(0).canvas.height," + " images.getByIndex(1).canvas.width, images.getByIndex(1).canvas.height]" + ) + expected = [50, 60, 20, 10] + if sizes != expected: + print("FAIL multi_image_independent_sizes") + return [f"multi_image_independent_sizes: expected canvas sizes {expected}, got {sizes}"] + print("ok multi_image_independent_sizes") + return [] + + def main(): parser = argparse.ArgumentParser() parser.add_argument( @@ -364,19 +390,23 @@ def main(): page.set_input_files("#file-input", str(TEST_IMAGE)) page.wait_for_selector("#image-size-settings li") + if not args.update: + failures.extend(run_multi_image_independent_sizes(page)) + browser.close() if args.update: print(f"\nWrote {len(SCENARIOS)} golden files to {GOLDEN_DIR}") return + total = len(SCENARIOS) + 1 if failures: - print(f"\n{len(failures)} of {len(SCENARIOS)} scenarios failed:") + print(f"\n{len(failures)} of {total} scenarios failed:") for f in failures: print(f" - {f}") sys.exit(1) - print(f"\nAll {len(SCENARIOS)} scenarios match golden output.") + print(f"\nAll {total} scenarios match golden output.") if __name__ == "__main__":