mirror of
https://github.com/KrisKennaway/ii-pix.git
synced 2024-11-19 08:30:48 +00:00
Pack output in Apple II screen format and save as binary file.
This commit is contained in:
parent
38d621a097
commit
2458bf98f7
98
dither.py
98
dither.py
@ -4,8 +4,8 @@ from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
# TODO:
|
||||
# - output binary files that can be viewed on Apple II
|
||||
# - use perceptual colour difference model
|
||||
# - compare to bmp2dhr and a2bestpix
|
||||
# - look ahead N pixels and compute all 2^N bit patterns, then minimize
|
||||
# average error
|
||||
# - optimize Dither.apply() critical path
|
||||
@ -95,40 +95,104 @@ class FloydSteinbergDither(Dither):
|
||||
ORIGIN = (0, 1)
|
||||
|
||||
|
||||
class KennawayDither(Dither):
|
||||
# 0 * 7 5 3 1
|
||||
# 3 5 3 1 1 0
|
||||
PATTERN = np.array(((0, 0, 7, 5, 3, 1), (3, 5, 3, 1, 1, 0)))
|
||||
class BuckelsDither(Dither):
|
||||
# 0 * 2 1
|
||||
# 1 2 1 0
|
||||
# 0 1 0 0
|
||||
PATTERN = np.array(((0, 0, 2, 1), (1, 2, 1, 0), (0, 1, 0, 0)))
|
||||
ORIGIN = (0, 1)
|
||||
|
||||
|
||||
def dither(filename):
|
||||
def open_image(filename: str) -> Image:
|
||||
im = Image.open(filename)
|
||||
if im.mode != "RGB":
|
||||
im = im.convert("RGB")
|
||||
im.resize((X_RES, Y_RES), resample=Image.LANCZOS)
|
||||
im.show()
|
||||
return im
|
||||
|
||||
# ditherer = FloydSteinbergDither()
|
||||
ditherer = KennawayDither()
|
||||
|
||||
def dither_image(image: Image, dither: Dither) -> Image:
|
||||
for y in range(Y_RES):
|
||||
print(y)
|
||||
newpixel = (0, 0, 0)
|
||||
new_pixel = (0, 0, 0)
|
||||
for x in range(X_RES):
|
||||
oldpixel = im.getpixel((x, y))
|
||||
newpixel = find_closest_color(oldpixel, newpixel, x)
|
||||
im.putpixel((x, y), tuple(newpixel))
|
||||
quant_error = oldpixel - newpixel
|
||||
ditherer.apply(im, x, y, quant_error)
|
||||
im.show()
|
||||
old_pixel = image.getpixel((x, y))
|
||||
new_pixel = find_closest_color(old_pixel, new_pixel, x)
|
||||
image.putpixel((x, y), tuple(new_pixel))
|
||||
quant_error = old_pixel - new_pixel
|
||||
dither.apply(image, x, y, quant_error)
|
||||
return image
|
||||
|
||||
|
||||
class Screen:
|
||||
def __init__(self, image: Image):
|
||||
self.bitmap = np.zeros((Y_RES, X_RES), dtype=np.bool)
|
||||
|
||||
self.main = np.zeros(8192, dtype=np.uint8)
|
||||
self.aux = np.zeros(8192, dtype=np.uint8)
|
||||
|
||||
for y in range(Y_RES):
|
||||
for x in range(X_RES):
|
||||
pixel = image.getpixel((x, y))
|
||||
dots = DOTS[pixel]
|
||||
phase = x % 4
|
||||
self.bitmap[y, x] = dots[phase]
|
||||
|
||||
@staticmethod
|
||||
def y_to_base_addr(y: int) -> int:
|
||||
"""Maps y coordinate to screen memory base address."""
|
||||
a = y // 64
|
||||
d = y - 64 * a
|
||||
b = d // 8
|
||||
c = d - 8 * b
|
||||
|
||||
return 1024 * c + 128 * b + 40 * a
|
||||
|
||||
def pack(self):
|
||||
# The DHGR display encodes 7 pixels across interleaved 4-byte sequences
|
||||
# of AUX and MAIN memory, as follows:
|
||||
# PBBBAAAA PDDCCCCB PFEEEEDD PGGGGFFF
|
||||
# Aux N Main N Aux N+1 Main N+1 (N even)
|
||||
main_col = np.zeros((Y_RES, X_RES // 14), dtype=np.uint8)
|
||||
aux_col = np.zeros((Y_RES, X_RES // 14), dtype=np.uint8)
|
||||
for byte_offset in range(80):
|
||||
column = np.zeros(Y_RES, dtype=np.uint8)
|
||||
for bit in range(7):
|
||||
column |= (self.bitmap[:, 7 * byte_offset + bit].astype(
|
||||
np.uint8) << bit)
|
||||
if byte_offset % 2 == 0:
|
||||
aux_col[:, byte_offset // 2] = column
|
||||
else:
|
||||
main_col[:, (byte_offset - 1) // 2] = column
|
||||
|
||||
for y in range(Y_RES):
|
||||
addr = self.y_to_base_addr(y)
|
||||
self.aux[addr:addr + 40] = aux_col[y, :]
|
||||
self.main[addr:addr + 40] = main_col[y, :]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("input", type=str, help="Input file to process")
|
||||
parser.add_argument("output", type=str, help="Output file for ")
|
||||
|
||||
args = parser.parse_args()
|
||||
dither(args.input)
|
||||
image = open_image(args.input)
|
||||
# image.show()
|
||||
|
||||
dither = FloydSteinbergDither()
|
||||
# dither = BuckelsDither()
|
||||
|
||||
output = dither_image(image, dither)
|
||||
output.show()
|
||||
screen = Screen(output)
|
||||
bitmap = Image.fromarray(screen.bitmap.astype('uint8') * 255)
|
||||
# bitmap.show()
|
||||
screen.pack()
|
||||
|
||||
with open("output.bin", "wb") as f:
|
||||
f.write(screen.main)
|
||||
f.write(screen.aux)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Loading…
Reference in New Issue
Block a user