#!/usr/bin/env python3
+# this can be run on any shape file from the pipeline (mono, colour, rounded),
+# it auto detects which processing steps have been done and trims extra padding
+
+# resulting file is normalized for the Apple II: vertical pixels are doubled,
+# and if HGR mode, horizontal pixels are doubled and each line is hibit or not
+
+# it has been tested to restore the original file (undoes colour and rounding),
+# but does not restore the original file if it has been rounded with --enhance
+
import numpy
import sys
import PIL.Image
continue
shape = shape[:ys, :xs]
- # temporary, for shape_(d)hgr.png
- if (ys & 1) == 1:
- shape = shape[1:, :]
- ys -= 1
- if (xs & 3) == 0:
- assert False
- elif (xs & 3) == 1:
+ # normal file has ys even with pairs of identical lines
+ # rounded file has ys odd and we will only use the odd lines
+ if (ys & 1) == 0:
+ assert numpy.all(shape[::2, :] == shape[1::2, :])
+ shape = shape[1::2, :]
+ ys //= 2
+
+ # mono file has xs = a multiple of 4 + 1
+ # colour file has xs = a multiple of 4 + 3, trim the sides
+ if (xs & 3) == 1:
+ pass
+ elif (xs & 3) == 3:
shape = shape[:, 1:-1]
xs -= 2
- elif (xs & 3) == 2:
- shape = shape[:, 1:-2]
- xs -= 3
-
- # only look at even lines, they will be duplicated again at the end
- shape = shape[::2]
- ys >>= 1
-
- # remove 2 pixels that were added by shape_colour_to_mono.py decoder
- assert xs >= 2
- shape = shape[:, 1:-1]
- xs -= 2
- assert (xs & 3) == 1
+ else:
+ assert False
# take a single bit from each 4-bit colour value, according to x % 4
shape = (
--- /dev/null
+#!/usr/bin/env python3
+
+# hack to pad mono image with one black pixel to left and right
+# makes it easier to put mono images (parachute, derrick, etc) alongside color
+
+import numpy
+import sys
+import PIL.Image
+
+EXIT_SUCCESS = 0
+EXIT_FAILURE = 1
+
+PITCH_X = 256
+PITCH_Y = 64
+
+# see palette.py
+PALETTE = numpy.array(
+ [
+ [0x00, 0x00, 0x00],
+ [0xbc, 0x00, 0x89],
+ [0x00, 0x00, 0xbc],
+ [0xbc, 0x00, 0xe1],
+ [0x00, 0xbc, 0x89],
+ [0x80, 0x80, 0x80], #[0xbc, 0xbc, 0xbc],
+ [0x00, 0xbc, 0xe1],
+ [0xbc, 0xbc, 0xff],
+ [0xbc, 0xbc, 0x00],
+ [0xff, 0xbc, 0x89],
+ [0xc0, 0xc0, 0xc0], #[0xbc, 0xbc, 0xbc],
+ [0xff, 0xbc, 0xe1],
+ [0xbc, 0xff, 0x89],
+ [0xff, 0xff, 0xbc],
+ [0xbc, 0xff, 0xe1],
+ [0xff, 0xff, 0xff],
+ ],
+ numpy.uint8
+)
+
+if len(sys.argv) < 3:
+ print(f'usage: {sys.argv[0]:s} in.png out.png')
+ sys.exit(EXIT_FAILURE)
+in_png = sys.argv[1]
+out_png = sys.argv[2]
+
+image_in_pil = PIL.Image.open(in_png)
+assert image_in_pil.mode == 'P'
+image_in = numpy.frombuffer(
+ image_in_pil.tobytes(),
+ dtype = numpy.uint8
+).reshape((image_in_pil.size[1], image_in_pil.size[0]))
+
+image_out = numpy.zeros((PITCH_Y * 32, PITCH_X * 8), numpy.uint8)
+assert image_out.shape == image_in.shape
+
+for i in range(0x100):
+ j = i & 7
+ k = i >> 3
+ x = j * PITCH_X
+ y = k * PITCH_Y
+ bg = 0xa if (j ^ k) & 1 else 5
+ image_out[y:y + PITCH_Y, x:x + PITCH_X] = bg
+
+ shape = image_in[y:y + PITCH_Y, x:x + PITCH_X]
+ xs = shape.shape[1]
+ while xs:
+ if numpy.any(shape[:, xs - 1] != bg):
+ break
+ xs -= 1
+ else:
+ continue
+ ys = shape.shape[0]
+ while ys:
+ if numpy.any(shape[ys - 1, :] != bg):
+ break
+ ys -= 1
+ else:
+ continue
+ shape = shape[:ys, :xs]
+
+ shape1 = numpy.zeros((ys, xs + 2), numpy.uint8)
+ shape1[:, 1:-1] = shape
+ shape = shape1
+ xs += 2
+
+ image_out[y:y + ys, x:x + xs] = shape
+
+image_out_pil = PIL.Image.new(
+ 'P',
+ (image_out.shape[1], image_out.shape[0]),
+ None
+)
+image_out_pil.frombytes(image_out.tobytes())
+image_out_pil.putpalette(list(PALETTE.reshape((0x30,))))
+image_out_pil.save(out_png)