diff --git a/cc-pic.py b/cc-pic.py index 1cfe32b..91bfc61 100644 --- a/cc-pic.py +++ b/cc-pic.py @@ -27,12 +27,37 @@ class Converter: ("f", "colors.black"), ] + DEFAULT_PALETTE = [ + 240, 240, 240, + 242, 178, 51, + 229, 127, 216, + 153, 178, 242, + 222, 222, 108, + 127, 204, 25, + 242, 178, 204, + 76, 76, 76, + 153, 153, 153, + 76, 153, 178, + 178, 102, 229, + 51, 102, 204, + 127, 102, 76, + 87, 166, 78, + 204, 76, 76, + 17, 17, 17 + ] + PIX_BITS = [[1, 2], [4, 8], [16, 0]] MAX_DIFF = 3 * 255 - def __init__(self, image: Image.Image): - self._img = image.convert("P", palette=Image.ADAPTIVE, colors=16) + def __init__(self, image: Image.Image, palette: list[int] | int = Image.ADAPTIVE): + if isinstance(palette, list): + img_pal = Image.new("P", (1, 1)) + img_pal.putpalette(palette) + self._img = image.quantize(16, palette=img_pal) + else: + self._img = image.convert("P", palette=palette, colors=16) + self._imgdata = self._img.load() self._palette: list[int] = self._img.getpalette() or [] if len(self._palette) < 16 * 3: @@ -57,6 +82,7 @@ class Converter: for oy, line in enumerate(self.PIX_BITS): for ox in range(len(line)): pix = self._imgdata[x + ox, y + oy] + assert pix < 16, f"{pix} is too big at {x+ox}:{y+oy}" brightness = self._brightness(pix) if brightness > brightest_l: brightest_l, brightest_i = brightness, pix @@ -70,6 +96,8 @@ class Converter: def _get_block(self, x: int, y: int) -> tuple[int, int, int]: dark_i, bri_i = self._get_colors(x, y) + assert dark_i < 16, f"{dark_i} is too big" + assert bri_i < 16, f"{bri_i} is too big" out: int = 0 for oy, line in enumerate(self.PIX_BITS): for ox, bit in enumerate(line): @@ -95,6 +123,14 @@ class Converter: value >>= 7 def export_binary(self, io: BinaryIO, version: int = -1): + if version == -2: + for y in range(0, self._img.height - 2, 3): + line: bytearray = bytearray() + for x in range(0, self._img.width - 1, 2): + ch, bg, fg = self._get_block(x, y) + line.extend([(ch + 0x80) & 0xFF, fg << 4 | bg]) + io.write(line) + return if version == -1: if self._img.width <= 255 * 2 and self._img.height < 255 * 3: version = 0 @@ -180,12 +216,14 @@ def main(): dest="cpi_version", type=int, default=-1, - choices=(-1, 0, 1), + choices=(-2, -1, 0, 1), help=dedent( """\ Force specific CPI version to be used. Only applies to binary format. Valid versions: + -V -2 Uses raw format. No headers, default palette. + Used for OBCB-CC project. -V -1 Choose any fitting one For images smaller than 255x255, uses CPIv0 -V 0 OG CPI, 255x255 maximum, uncompressed @@ -282,7 +320,10 @@ def main(): else: pass - converter = Converter(canv) + palette = Image.Palette.ADAPTIVE + if args.cpi_version == -2: + palette = Converter.DEFAULT_PALETTE + converter = Converter(canv, palette) converter._img.save("/tmp/_ccpictmp.png") if args.textmode: with open(args.output_path, "w") as fp: