"""Render every Mode x Palette x Dither variation of an image, for the GUI's "Explore variations" contact sheet. Kept import-light enough to run in ProcessPoolExecutor worker processes; platform-aware (C64 or Atari). """ from __future__ import annotations from . import imageprep from .convert import render_preview DITHERS = ["bayer", "floyd", "atkinson", "none"] def combos(platform: str): """(mode, palette, dither) triples for a platform.""" if platform == "apple": from .apple.convert import MODES as PMODES return [(m, "mono", d) for m in PMODES for d in DITHERS] if platform == "atari": from .atari.convert import MODES as AMODES # floyd first: gr15/gr15dli use dither-aware palette selection adith = ["floyd", "atkinson", "bayer", "none"] return [(m, "ntsc", d) for m in AMODES for d in adith] if platform == "ti99": from .ti99.convert import MODES as TMODES # atkinson first: gm2 selection is dither-aware, and atkinson's lighter # diffusion bleeds less across the tight 8x8 two-colour cells than floyd tdith = ["atkinson", "floyd", "bayer", "none"] return [(m, "tms9918", d) for m in TMODES for d in tdith] if platform == "coco": from .coco.convert import MODES as COMODES return [(m, "mc6847", d) for m in COMODES for d in DITHERS] if platform == "bbc": from .bbc.convert import MODES as BMODES return [(m, "bbc", d) for m in BMODES for d in DITHERS] if platform == "coleco": from .coleco.convert import MODES as CVMODES # same TMS9918A GM2 as the TI-99 -> dither-aware, atkinson first cvdith = ["atkinson", "floyd", "bayer", "none"] return [(m, "tms9918", d) for m in CVMODES for d in cvdith] if platform == "a2600": from .a2600.convert import MODES as VMODES # atkinson/none first: bayer fares poorly on the 40px playfield adith = ["atkinson", "none", "floyd", "bayer"] return [(m, "tia", d) for m in VMODES for d in adith] if platform == "intv": from .intv.convert import MODES as IMODES # none first: the 64-tile GRAM limit makes error-diffusion counterproductive idith = ["none", "atkinson", "bayer", "floyd"] return [(m, "stic", d) for m in IMODES for d in idith] if platform == "vic20": from .vic20.convert import MODES as VMODES # floyd first: the colour selection is dither-aware (segment metric) vdith = ["floyd", "atkinson", "bayer", "none"] return [(m, "vic", d) for m in VMODES for d in vdith] if platform == "spectrum": from .spectrum.convert import MODES as ZMODES # atkinson first: 2-colour-per-cell (attribute clash), lighter diffusion # bleeds less across cells (as on the TI-99) zdith = ["atkinson", "floyd", "bayer", "none"] return [(m, "spectrum", d) for m in ZMODES for d in zdith] if platform == "a5200": from .a5200.convert import MODES as XMODES # floyd first: reuses the dither-aware Atari GTIA encoders xdith = ["floyd", "atkinson", "bayer", "none"] return [(m, "ntsc", d) for m in XMODES for d in xdith] if platform == "a7800": from .a7800.convert import MODES as SMODES sdith = ["floyd", "atkinson", "bayer", "none"] return [(m, "ntsc", d) for m in SMODES for d in sdith] if platform == "c128": from .c128.convert import MODES as C128_MODES # floyd first: the 640x200 two-tone is pure luminance dither, so error # diffusion yields the smoothest tonal gradients at this high resolution. c128dith = ["floyd", "atkinson", "bayer", "none"] return [(m, "vdc", d) for m in C128_MODES for d in c128dith] if platform in ("c16", "plus4"): from .c16.convert import MODES as C16_MODES # floyd first: TED hires picks 2 of 128 colours per cell, so error # diffusion brackets each cell and blends to the true shade. c16dith = ["floyd", "atkinson", "bayer", "none"] return [(m, "ted", d) for m in C16_MODES for d in c16dith] if platform == "cpc": from .cpc.convert import MODES as CPC_MODES # floyd first: a flat N-colour palette dithered -> error diffusion gives # the smoothest gradients. cpcdith = ["floyd", "atkinson", "bayer", "none"] return [(m, "cpc", d) for m in CPC_MODES for d in cpcdith] if platform == "coco3": from .coco3.convert import MODES as COCO3_MODES # floyd first: flat palette chosen from 64 colours, dithered. c3dith = ["floyd", "atkinson", "bayer", "none"] return [(m, "gime", d) for m in COCO3_MODES for d in c3dith] if platform == "nes": from .nes.convert import MODES as NES_MODES # atkinson first: lighter diffusion bleeds less across the NES's tight # 16x16 attribute-palette regions (like the TI/Spectrum cell platforms). nesdith = ["atkinson", "floyd", "bayer", "none"] return [(m, "nes", d) for m in NES_MODES for d in nesdith] if platform == "iigs": from .iigs.convert import MODES as IIGS_MODES # floyd first: per-line 16-of-4096 palette, dithered -> smoothest result. gsdith = ["floyd", "atkinson", "bayer", "none"] return [(m, "iigs", d) for m in IIGS_MODES for d in gsdith] if platform in ("pet2001", "pet4032", "pet8032", "superpet"): # one-bit quadrant-block mono; floyd carries the most tone at low res. return [("mono", "pet", d) for d in ("floyd", "atkinson", "bayer", "none")] if platform == "sms": from .sms.convert import MODES as SMS_MODES # floyd first: 16 colours/tile from 2 palettes -> error diffusion blends # to the smoothest result. smsdith = ["floyd", "atkinson", "bayer", "none"] return [(m, "sms", d) for m in SMS_MODES for d in smsdith] if platform == "amiga": from .amiga.convert import MODES as AMIGA_MODES # floyd first: flat 32-of-4096 palette dithered -> smoothest gradients. amdith = ["floyd", "atkinson", "bayer", "none"] return [(m, "amiga", d) for m in AMIGA_MODES for d in amdith] if platform == "ansi": from .ansi.convert import MODES as ANSI_MODES # floyd first: a free 16-colour quantise, so error diffusion blends best. ansidith = ["floyd", "atkinson", "bayer", "none"] return [(m, "vga", d) for m in ANSI_MODES for d in ansidith] from .convert import MODES as CMODES # floyd first: C64 colour selection is dither-aware, so error diffusion gives # the smoothest, most accurate result. cdith = ["floyd", "atkinson", "bayer", "none"] return [(m, p, d) for m in CMODES for p in ["colodore", "pepto"] for d in cdith] def render_variation(args): """Worker entry. args = (path, platform, mode, palette, dither, prep_kwargs, base_name). Returns (mode, palette, dither, error, rgb).""" path, platform, mode, palette, dither, prep_kwargs, base_name = args from . import platforms prep = imageprep.PrepOptions(**prep_kwargs) conv = platforms.convert(platform, path, mode, palette, dither, False, prep, base_name) rgb = render_preview(conv, palette, scale=1) return (mode, palette, dither, conv.error, rgb)