diff --git a/cc-pic.py b/cc-pic.py index 91bfc61..ced7495 100644 --- a/cc-pic.py +++ b/cc-pic.py @@ -1,11 +1,16 @@ #!/usr/bin/env python3 from typing import BinaryIO, TextIO -from PIL import Image +from PIL import Image, ImageColor from argparse import ArgumentParser, RawTextHelpFormatter from textwrap import dedent from functools import lru_cache +try: + PALETTE_ADAPTIVE = Image.Palette.ADAPTIVE +except Exception: + PALETTE_ADAPTIVE = Image.ADAPTIVE + class Converter: CC_COLORS = [ @@ -46,11 +51,30 @@ class Converter: 17, 17, 17 ] + DEFAULT_GRAYSCALE_PALETTE = [ + 0xf0, 0xf0, 0xf0, + 0x9d, 0x9d, 0x9d, + 0xbe, 0xbe, 0xbe, + 0xbf, 0xbf, 0xbf, + 0xb8, 0xb8, 0xb8, + 0x76, 0x76, 0x76, + 0xd0, 0xd0, 0xd0, + 0x4c, 0x4c, 0x4c, + 0x99, 0x99, 0x99, + 0x87, 0x87, 0x87, + 0xa9, 0xa9, 0xa9, + 0x77, 0x77, 0x77, + 0x65, 0x65, 0x65, + 0x6e, 0x6e, 0x6e, + 0x76, 0x76, 0x76, + 0x11, 0x11, 0x11 + ] + PIX_BITS = [[1, 2], [4, 8], [16, 0]] MAX_DIFF = 3 * 255 - def __init__(self, image: Image.Image, palette: list[int] | int = Image.ADAPTIVE): + def __init__(self, image: Image.Image, palette: list[int] | int = PALETTE_ADAPTIVE): if isinstance(palette, list): img_pal = Image.new("P", (1, 1)) img_pal.putpalette(palette) @@ -246,6 +270,25 @@ def main(): -p fill Stretch to fill""" ), ) + parser.add_argument( + "-P", + dest="palette", + default="auto", + help=dedent( + """\ + Palette to be used for that conversion. + Should be 16 colors or less + Valid options are: + -P auto Determine palette automatically + -P default Use default CC:Tweaked color palette + -P defaultgray Use default CC:Tweaked grayscale palette + -P "list:#RRGGBB,#RRGGBB,..." Use a set list of colors + -P "cpi:path" Load palette from a CCPI file + -P "gpl:path" Parse GIMP palette file and use first 16 colors + -P "txt:path" Load palette from a list of hex values + """ + ) + ) parser.add_argument("image_path") parser.add_argument("output_path") @@ -320,9 +363,34 @@ def main(): else: pass - palette = Image.Palette.ADAPTIVE + palette = PALETTE_ADAPTIVE if args.cpi_version == -2: + args.palette = "default" + + if args.palette == "auto": + palette = PALETTE_ADAPTIVE + elif args.palette == "default": palette = Converter.DEFAULT_PALETTE + elif args.palette == "defaultgray": + palette = Converter.DEFAULT_GRAYSCALE_PALETTE + elif args.palette.startswith("txt:"): + with open(args.palette[4:], "r") as fp: + palette = [] + for line in fp: + palette += ImageColor.getcolor(line.strip(), "RGB") # type: ignore + assert len(palette) <= 16 * 3 + elif args.palette.startswith("list:"): + palette = [] + for c in args.palette[5:].split(","): + palette += ImageColor.getcolor(c, "RGB") # type: ignore + assert len(palette) <= 16 * 3 + elif args.palette.startswith("cpi:"): + raise ValueError("not implemented yet") + elif args.palette.startswith("gpl:"): + raise ValueError("not implemented yet") + else: + raise ValueError(f"invalid palette identifier: {args.palette!r}") + converter = Converter(canv, palette) converter._img.save("/tmp/_ccpictmp.png") if args.textmode: