import sys import tempfile import unittest from pathlib import Path from PIL import Image SCRIPTS = Path(__file__).resolve().parents[1] / "scripts" sys.path.insert(0, str(SCRIPTS)) from build_scroll_skins import build_asset, remove_chroma_background, sanitize_output_edges, stretch_safe_center class BuildScrollSkinsTests(unittest.TestCase): def setUp(self): self.directory = tempfile.TemporaryDirectory() self.addCleanup(self.directory.cleanup) self.root = Path(self.directory.name) def test_chroma_background_becomes_transparent_black(self): image = Image.new("RGB", (4, 2), (0, 255, 0)) image.putpixel((1, 0), (180, 30, 20)) cleaned = remove_chroma_background(image, (0, 255, 0), tolerance=80) self.assertEqual((0, 0, 0, 0), cleaned.getpixel((0, 0))) self.assertEqual((180, 30, 20, 255), cleaned.getpixel((1, 0))) def test_safe_center_stretch_preserves_caps_and_exact_size(self): image = Image.new("RGBA", (30, 10), (150, 20, 10, 255)) for y in range(10): for x in range(5): image.putpixel((x, y), (220, 170, 40, 255)) image.putpixel((29 - x, y), (220, 170, 40, 255)) output = stretch_safe_center( image, output_size=(60, 20), cap_width=5, padding=2, ) self.assertEqual((60, 20), output.size) self.assertEqual((0, 0, 0, 0), output.getpixel((0, 0))) self.assertEqual((220, 170, 40, 255), output.getpixel((3, 10))) self.assertEqual((150, 20, 10, 255), output.getpixel((30, 10))) self.assertEqual((220, 170, 40, 255), output.getpixel((56, 10))) def test_sanitizes_only_forbidden_edge_artifacts(self): image = Image.new("RGBA", (3, 1), (248, 240, 220, 255)) image.putpixel((0, 0), (0, 255, 0, 128)) image.putpixel((1, 0), (250, 250, 250, 96)) cleaned = sanitize_output_edges(image) self.assertEqual((0, 0, 0, 0), cleaned.getpixel((0, 0))) self.assertEqual((0, 0, 0, 0), cleaned.getpixel((1, 0))) self.assertEqual((248, 240, 220, 255), cleaned.getpixel((2, 0))) def test_build_asset_honors_declared_palette_size(self): source = Image.new("RGB", (32, 16), (0, 255, 0)) for y in range(1, 15): for x in range(1, 31): source.putpixel((x, y), (120 + (x % 30) * 4, 20 + y, 10)) source_path = self.root / "source.png" output_path = self.root / "output.png" source.save(source_path) asset = { "id": "palette-test", "source": "source.png", "output": "output.png", "outputPixels": {"width": 60, "height": 30}, "alpha": {"transparentOuterPadding": 1}, "processing": { "capWidth": 4, "keyColor": "#00FF00", "keyTolerance": 80, "paletteColors": 8, }, } build_asset(asset, self.root) with Image.open(output_path) as output: visible_colors = { pixel[:3] for pixel in output.convert("RGBA").get_flattened_data() if pixel[3] > 0 } self.assertLessEqual(len(visible_colors), 8) if __name__ == "__main__": unittest.main()