fix(characters): remove divider lines from the concept sheet
The sheet composer drew light lines between the three poses and between the top and bottom rows, cutting through capes and swords. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b50855ea2e
commit
6d1c6c1b88
@ -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.
|
- **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
|
### 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.
|
- **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.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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:
|
def _compose_character_reference(images: dict[str, bytes], prop_count: int) -> str:
|
||||||
"""Assemble panels without cropping or asking the model to draw a layout."""
|
"""Assemble panels without cropping or asking the model to draw a layout."""
|
||||||
canvas = Image.new("RGB", (1536, 1536), (246, 242, 234))
|
canvas = Image.new("RGB", (1536, 1536), (246, 242, 234))
|
||||||
draw = ImageDraw.Draw(canvas)
|
|
||||||
with Image.open(io.BytesIO(images["turnaround"])) as source:
|
with Image.open(io.BytesIO(images["turnaround"])) as source:
|
||||||
turnaround = ImageOps.fit(source.convert("RGB"), (1536, 1024), Image.Resampling.LANCZOS)
|
turnaround = ImageOps.fit(source.convert("RGB"), (1536, 1024), Image.Resampling.LANCZOS)
|
||||||
# The model receives an explicit equal-thirds triptych. Crop only those
|
# 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)
|
panel = _contain_image(images[f"prop{index + 1}"], width, 512, 18)
|
||||||
x = scene_width + tile_width * index
|
x = scene_width + tile_width * index
|
||||||
canvas.paste(panel, (x, 1024))
|
canvas.paste(panel, (x, 1024))
|
||||||
draw.line((x, 1024, x, 1536), fill=(205, 198, 186), width=3)
|
# No divider lines between panels: they cut through capes and swords at the panel edges.
|
||||||
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)
|
|
||||||
out = io.BytesIO()
|
out = io.BytesIO()
|
||||||
canvas.save(out, format="PNG", optimize=True)
|
canvas.save(out, format="PNG", optimize=True)
|
||||||
return "data:image/png;base64," + base64.b64encode(out.getvalue()).decode("ascii")
|
return "data:image/png;base64," + base64.b64encode(out.getvalue()).decode("ascii")
|
||||||
|
|||||||
37
tests/test_character_reference_layout.py
Normal file
37
tests/test_character_reference_layout.py
Normal file
@ -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()
|
||||||
Loading…
Reference in New Issue
Block a user