diff --git a/CHANGELOG.md b/CHANGELOG.md index df2bcf8..68ff93a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ยท versioned wi - **Filter the page grid by status.** Each legend entry is now a toggle button with the number of pages in that status; choose one or several to show only pages that have any of them. It combines with *Show only pages that need me*, the summary says how many pages are shown, *Show all pages* clears everything, a status no page has is greyed out, and the buttons announce their on/off state and work from the keyboard. ### Fixed +- **No divider lines on the character concept sheet.** The Konzeptbild composer drew light lines between the three poses and between the top and bottom rows, which cut through capes and swords. They are gone. Sheets already stored have the lines baked into the image file and need to be regenerated to lose them. - **Review opens at the top.** Opening a page in review no longer keeps the scroll position of the page grid, which could leave the sticky page bar covering the top of the page picture. diff --git a/routes/conversation.py b/routes/conversation.py index 24b5435..90acbd0 100644 --- a/routes/conversation.py +++ b/routes/conversation.py @@ -3617,7 +3617,6 @@ def _contain_image(raw: bytes, width: int, height: int, padding: int = 12) -> Im def _compose_character_reference(images: dict[str, bytes], prop_count: int) -> str: """Assemble panels without cropping or asking the model to draw a layout.""" canvas = Image.new("RGB", (1536, 1536), (246, 242, 234)) - draw = ImageDraw.Draw(canvas) with Image.open(io.BytesIO(images["turnaround"])) as source: turnaround = ImageOps.fit(source.convert("RGB"), (1536, 1024), Image.Resampling.LANCZOS) # The model receives an explicit equal-thirds triptych. Crop only those @@ -3646,10 +3645,7 @@ def _compose_character_reference(images: dict[str, bytes], prop_count: int) -> s panel = _contain_image(images[f"prop{index + 1}"], width, 512, 18) x = scene_width + tile_width * index canvas.paste(panel, (x, 1024)) - draw.line((x, 1024, x, 1536), fill=(205, 198, 186), width=3) - draw.line((0, 1024, 1536, 1024), fill=(205, 198, 186), width=4) - draw.line((512, 0, 512, 1024), fill=(218, 212, 202), width=2) - draw.line((1024, 0, 1024, 1024), fill=(218, 212, 202), width=2) + # No divider lines between panels: they cut through capes and swords at the panel edges. out = io.BytesIO() canvas.save(out, format="PNG", optimize=True) return "data:image/png;base64," + base64.b64encode(out.getvalue()).decode("ascii") diff --git a/tests/test_character_reference_layout.py b/tests/test_character_reference_layout.py new file mode 100644 index 0000000..ded0b43 --- /dev/null +++ b/tests/test_character_reference_layout.py @@ -0,0 +1,37 @@ +"""The character concept sheet is composed without divider lines between panels.""" +from __future__ import annotations + +import base64 +import io +import os +import tempfile +import unittest + +os.environ.setdefault("CONFIG_DIR", tempfile.mkdtemp(prefix="ttvc-refsheet-test-config-")) + +from PIL import Image + +import routes.conversation as conversation + +BG = (246, 242, 234) + + +def _png(color): + out = io.BytesIO() + Image.new("RGB", (1536, 1024), color).save(out, format="PNG") + return out.getvalue() + + +class ComposeReferenceNoDividers(unittest.TestCase): + def test_no_lines_between_panels(self): + # Every panel is the same colour as the sheet background, so any pixel that differs is a drawn line. + images = {k: _png(BG) for k in ("turnaround", "scene", "prop1", "prop2")} + for props in (0, 1, 2): + uri = conversation._compose_character_reference(images, props) + img = Image.open(io.BytesIO(base64.b64decode(uri.split(",", 1)[1]))).convert("RGB") + self.assertEqual(img.size, (1536, 1536)) + self.assertEqual(img.getcolors(maxcolors=16), [(1536 * 1536, BG)], f"divider drawn with {props} props") + + +if __name__ == "__main__": + unittest.main()