mirror of
https://github.com/KrisKennaway/ii-vision.git
synced 2024-12-21 20:29:21 +00:00
105 lines
2.9 KiB
Python
105 lines
2.9 KiB
Python
"""Screen module represents Apple II video display."""
|
|
|
|
# from ortools.constraint_solver import pywrapcp
|
|
# from ortools.constraint_solver import routing_enums_pb2
|
|
|
|
import numpy as np
|
|
|
|
|
|
def bitmap_similarity(a1: np.array, a2: np.array) -> float:
|
|
"""Measure bitwise % similarity between two bitmap arrays"""
|
|
bits_different = np.asscalar(np.sum(np.logical_xor(a1, a2)))
|
|
|
|
return 1 - (bits_different / (np.shape(a1)[0] * np.shape(a1)[1]))
|
|
|
|
|
|
class Bytemap:
|
|
"""Bitmap array with horizontal pixels packed into bytes."""
|
|
|
|
def __init__(self, bitmap: np.array):
|
|
self.ymax = bitmap.shape[0] # type: int
|
|
self.xmax = bitmap.shape[1] # type: int
|
|
if self.xmax % 7 != 0:
|
|
raise ValueError(
|
|
"Bitmap x dimension not divisible by 7: %d" % self.xmax)
|
|
|
|
self._unpacked_bitmap = bitmap
|
|
|
|
self.bytemap = self._pack() # type: np.array
|
|
|
|
def _pack(self) -> np.array:
|
|
pixels = self._unpacked_bitmap
|
|
|
|
# Insert zero column after every 7
|
|
for i in range(pixels.shape[1] // 7 - 1, -1, -1):
|
|
pixels = np.insert(pixels, (i + 1) * 7, False, axis=1)
|
|
|
|
# packbits is big-endian so we flip the array before and after to
|
|
# invert this
|
|
return np.flip(np.packbits(np.flip(pixels, axis=1), axis=1), axis=1)
|
|
|
|
def unpack(self) -> np.array:
|
|
"""Convert packed screen representation to bitmap."""
|
|
bm = np.unpackbits(self.bytemap, axis=1)
|
|
bm = np.delete(bm, np.arange(0, bm.shape[1], 8), axis=1)
|
|
|
|
# Need to flip each 7-bit sequence
|
|
reorder_cols = []
|
|
for i in range(bm.shape[1] // 7):
|
|
for j in range((i + 1) * 7 - 1, i * 7 - 1, -1):
|
|
reorder_cols.append(j)
|
|
bm = bm[:, reorder_cols]
|
|
|
|
return np.array(bm, dtype=np.bool)
|
|
|
|
|
|
class Bitmap:
|
|
XMAX = None # type: int
|
|
YMAX = None # type: int
|
|
|
|
def __init__(self, bitmap: np.array = None):
|
|
if bitmap is None:
|
|
self.bitmap = np.zeros((self.YMAX, self.XMAX), dtype=bool)
|
|
else:
|
|
self.bitmap = bitmap
|
|
|
|
def randomize(self) -> None:
|
|
self.bitmap = np.random.randint(
|
|
2, size=(self.YMAX, self.XMAX), dtype=bool)
|
|
|
|
def pack(self):
|
|
return Bytemap(self.bitmap)
|
|
|
|
@classmethod
|
|
def from_bytemap(cls, bytemap: Bytemap):
|
|
return cls(bytemap.unpack())
|
|
|
|
|
|
class HGR140Bitmap(Bitmap):
|
|
XMAX = 140 # double-wide pixels to not worry about colour effects
|
|
YMAX = 192
|
|
|
|
def pack(self):
|
|
# Double each pixel horizontally
|
|
return Bytemap(np.repeat(self.bitmap, 2, axis=1))
|
|
|
|
@classmethod
|
|
def from_bytemap(cls, bytemap: Bytemap):
|
|
# Undouble pixels
|
|
bm = bytemap.unpack()
|
|
return cls(
|
|
np.array(
|
|
np.delete(bm, np.arange(0, bm.shape[1], 2), axis=1),
|
|
dtype=np.bool
|
|
))
|
|
|
|
|
|
class HGRBitmap(Bitmap):
|
|
XMAX = 280
|
|
YMAX = 192
|
|
|
|
|
|
class DHGRBitmap(Bitmap):
|
|
XMAX = 560
|
|
YMAX = 192
|