ii-vision/transcoder/screen_test.py
kris 5c550d8524 Separate the details of the bitmap packing from operations on the
packed representation (diff, apply etc).  This allows the (D)HGRBitmap
classes to focus on the bitmap packing and share common logic.

Numpy has unfortunate long-standing bugs to do with type coercion of
np.uint64, which leads to spurious "incompatible type" warnings when
e.g. operating on a np.uint64 and some other integer type.  To work
around this we cast explicitly to np.uint64 everywhere.

Get tests working again - for now HGR tests in screen_test.py are
disabled until I finish implementing new packing.

HGRBitmap is still incomplete although closer.
2019-07-04 15:21:20 +01:00

783 lines
26 KiB
Python

"""Tests for the screen module."""
import unittest
import numpy as np
from etaprogress.progress import ProgressBar
import colours
from palette import Palette, PALETTES
import screen
import sys
class TestDHGRBitmap(unittest.TestCase):
def setUp(self) -> None:
self.aux = screen.MemoryMap(screen_page=1)
self.main = screen.MemoryMap(screen_page=1)
def test_edit_distances(self):
for p in PALETTES:
ed = screen._edit_distances("DHGR", p)
print(p)
bar = ProgressBar((2 ** 13 * (2 ** 13 - 1)) / 2, max_width=80)
cnt = 0
for i in range(2 ** 13):
# Assert that self-distances are zero
self.assertEqual(0, ed[0][(i << 13) + i])
self.assertEqual(0, ed[1][(i << 13) + i])
self.assertEqual(0, ed[2][(i << 13) + i])
self.assertEqual(0, ed[3][(i << 13) + i])
# Assert that matrix is triangular
for j in range(i):
cnt += 1
if cnt % 10000 == 0:
bar.numerator = cnt
print(bar, end='\r')
sys.stdout.flush()
self.assertEqual(
ed[0][(i << 13) + j],
ed[0][(j << 13) + i],
)
self.assertEqual(
ed[1][(i << 13) + j],
ed[1][(j << 13) + i],
)
self.assertEqual(
ed[2][(i << 13) + j],
ed[2][(j << 13) + i],
)
self.assertEqual(
ed[3][(i << 13) + j],
ed[3][(j << 13) + i],
)
def test_make_header(self):
self.assertEqual(
0b100,
screen.DHGRBitmap._make_header(
np.uint64(0b0001000011111010110000111110101000))
)
def test_make_footer(self):
self.assertEqual(
0b1010000000000000000000000000000000,
screen.DHGRBitmap._make_footer(
np.uint64(0b0001000011111010110000111110101000))
)
def test_pixel_packing_offset_0(self):
# PBBBAAAA
self.aux.page_offset[0, 0] = 0b11110101
# PDDCCCCB
self.main.page_offset[0, 0] = 0b01000011
# PFEEEEDD
self.aux.page_offset[0, 1] = 0b11110101
# PGGGGFFF
self.main.page_offset[0, 1] = 0b01000011
dhgr = screen.DHGRBitmap(
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
self.assertEqual(
0b0001000011111010110000111110101000,
dhgr.packed[0, 0]
)
# Check header on neighbouring byte
self.assertEqual(
0b0000000000000000000000000000000100,
dhgr.packed[0, 1]
)
# No other entries should be set, in particular no footer since we
# are at packed offset 0
self.assertEqual(2, np.count_nonzero(dhgr.packed))
def test_pixel_packing_offset_1(self):
# PBBBAAAA
self.aux.page_offset[0, 2] = 0b11110101
# PDDCCCCB
self.main.page_offset[0, 2] = 0b01000011
# PFEEEEDD
self.aux.page_offset[0, 3] = 0b11110101
# PGGGGFFF
self.main.page_offset[0, 3] = 0b01000011
dhgr = screen.DHGRBitmap(
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
self.assertEqual(
0b0001000011111010110000111110101000,
dhgr.packed[0, 1]
)
# Check footer on neighbouring byte
self.assertEqual(
0b1010000000000000000000000000000000,
dhgr.packed[0, 0]
)
# Check header on neighbouring byte
self.assertEqual(
0b0000000000000000000000000000000100,
dhgr.packed[0, 2]
)
# No other entries should be set
self.assertEqual(3, np.count_nonzero(dhgr.packed))
def test_pixel_packing_offset_127(self):
# PBBBAAAA
self.aux.page_offset[0, 254] = 0b11110101
# PDDCCCCB
self.main.page_offset[0, 254] = 0b01000011
# PFEEEEDD
self.aux.page_offset[0, 255] = 0b11110101
# PGGGGFFF
self.main.page_offset[0, 255] = 0b01000011
dhgr = screen.DHGRBitmap(
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
self.assertEqual(
0b0001000011111010110000111110101000,
dhgr.packed[0, 127]
)
# Check footer on neighbouring byte
self.assertEqual(
0b1010000000000000000000000000000000,
dhgr.packed[0, 126]
)
# No other entries should be set, in particular header should not
# propagate to next row
self.assertEqual(2, np.count_nonzero(dhgr.packed))
def test_byte_offset(self):
self.assertEqual(0, screen.DHGRBitmap.byte_offset(0, is_aux=True))
self.assertEqual(1, screen.DHGRBitmap.byte_offset(0, is_aux=False))
self.assertEqual(2, screen.DHGRBitmap.byte_offset(1, is_aux=True))
self.assertEqual(3, screen.DHGRBitmap.byte_offset(1, is_aux=False))
def test_byte_offsets(self):
self.assertEqual((0, 2), screen.DHGRBitmap._byte_offsets(is_aux=True))
self.assertEqual((1, 3), screen.DHGRBitmap._byte_offsets(is_aux=False))
def test_mask_and_shift_data(self):
int13_max = np.uint64(2 ** 13 - 1)
int34_max = np.uint64(2 ** 34 - 1)
dhgr = screen.DHGRBitmap(
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
for o in range(3):
self.assertEqual(
int13_max,
dhgr.mask_and_shift_data(
screen.DHGRBitmap.BYTE_MASKS[o], o
)
)
# Now check complement, i.e. no bits taken from outside expected
# range
self.assertEqual(
0,
dhgr.mask_and_shift_data(
~screen.DHGRBitmap.BYTE_MASKS[o] & int34_max, o
)
)
def test_masked_update(self):
self.assertEqual(
0b0000000000000000000000001111111000,
screen.DHGRBitmap.masked_update(
0, np.uint64(0), np.uint8(0xff))
)
self.assertEqual(
0b0000000000000000011111110000000000,
screen.DHGRBitmap.masked_update(
1, np.uint64(0), np.uint8(0xff))
)
self.assertEqual(
0b0000000000111111100000000000000000,
screen.DHGRBitmap.masked_update(
2, np.uint64(0), np.uint8(0xff))
)
self.assertEqual(
0b0001111111000000000000000000000000,
screen.DHGRBitmap.masked_update(
3, np.uint64(0), np.uint8(0xff))
)
# Now test masking out existing values
int34_max = np.uint64(2 ** 34 - 1)
self.assertEqual(
0b1111111111111111111111110000000111,
screen.DHGRBitmap.masked_update(0, int34_max, np.uint8(0x00))
)
self.assertEqual(
0b1111111111111111100000001111111111,
screen.DHGRBitmap.masked_update(1, int34_max, np.uint8(0x00))
)
self.assertEqual(
0b1111111111000000011111111111111111,
screen.DHGRBitmap.masked_update(2, int34_max, np.uint8(0x00))
)
self.assertEqual(
0b1110000000111111111111111111111111,
screen.DHGRBitmap.masked_update(3, int34_max, np.uint8(0x00))
)
# Test that masked_update can broadcast to numpy arrays
ary = np.zeros((2, 2), dtype=np.uint64)
elt = np.uint64(0b1111111000)
self.assertTrue(np.array_equal(
np.array([[elt, elt], [elt, elt]], dtype=np.uint64),
screen.DHGRBitmap.masked_update(0, ary, np.uint8(0xff))
))
def test_apply(self):
dhgr = screen.DHGRBitmap(
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
dhgr.apply(page=0, offset=0, is_aux=True, value=np.uint8(0xff))
self.assertEqual(0b1111111000, dhgr.packed[0, 0])
dhgr.apply(page=12, offset=36, is_aux=True, value=np.uint8(0xff))
# Neighbouring header
self.assertEqual(
0,
dhgr.packed[12, 19])
# Body
self.assertEqual(
0b1111111000,
dhgr.packed[12, 18])
# Neighbouring footer
self.assertEqual(
0b1110000000000000000000000000000000,
dhgr.packed[12, 17])
# Now update the next aux offset in same uint64
dhgr.apply(page=12, offset=37, is_aux=True, value=np.uint8(0xff))
# Neighbouring header
self.assertEqual(
0,
dhgr.packed[12, 19])
# Body
self.assertEqual(
0b0000000111111100000001111111000,
dhgr.packed[12, 18]
)
# Neighbouring footer
self.assertEqual(
0b1110000000000000000000000000000000,
dhgr.packed[12, 17])
# Update offset 3, should propagate to next header
dhgr.apply(page=12, offset=37, is_aux=False, value=np.uint8(0b1010101))
self.assertEqual(
0b101,
dhgr.packed[12, 19])
self.assertEqual(
0b1010101111111100000001111111000,
dhgr.packed[12, 18]
)
self.assertEqual(
0b1110000000000000000000000000000000,
dhgr.packed[12, 17])
dhgr.apply(page=12, offset=36, is_aux=False, value=np.uint8(0b0001101))
self.assertEqual(
0b101,
dhgr.packed[12, 19])
self.assertEqual(
0b1010101111111100011011111111000,
dhgr.packed[12, 18]
)
self.assertEqual(
0b1110000000000000000000000000000000,
dhgr.packed[12, 17])
# Change offset 0, should propagate to neighbouring footer
dhgr.apply(page=12, offset=36, is_aux=True, value=np.uint8(0b0001101))
# Neighbouring header
self.assertEqual(
0b101,
dhgr.packed[12, 19])
self.assertEqual(
0b1010101111111100011010001101000,
dhgr.packed[12, 18]
)
# Neighbouring footer
self.assertEqual(
0b1010000000000000000000000000000000,
dhgr.packed[12, 17])
# Now propagate new header from neighbour onto (12, 18)
dhgr.apply(page=12, offset=35, is_aux=False, value=np.uint8(0b1010101))
self.assertEqual(
0b1010101111111100011010001101101,
dhgr.packed[12, 18]
)
# Neighbouring footer
self.assertEqual(
0b1011010101000000000000000000000000,
dhgr.packed[12, 17])
def test_fix_array_neighbours(self):
dhgr = screen.DHGRBitmap(
main_memory=self.main, aux_memory=self.aux, palette=Palette.NTSC)
packed = dhgr.masked_update(0, dhgr.packed, np.uint8(0x7f))
dhgr._fix_array_neighbours(packed, 0)
# Should propagate to all footers
self.assertEqual(
0, np.count_nonzero(
packed[packed != 0b1110000000000000000000001111111000]
)
)
# Should not change headers/footers
packed = dhgr.masked_update(1, packed, np.uint8(0b1010101))
dhgr._fix_array_neighbours(packed, 1)
self.assertEqual(
0, np.count_nonzero(
packed[packed != 0b1110000000000000010101011111111000]
)
)
# Should propagate to all headers
packed = dhgr.masked_update(3, packed, np.uint8(0b0110110))
dhgr._fix_array_neighbours(packed, 3)
self.assertEqual(
0, np.count_nonzero(
packed[packed != 0b1110110110000000010101011111111011]
)
)
def binary(a):
return np.vectorize("{:032b}".format)(a)
# class TestHGRBitmap: # unittest.TestCase):
# def setUp(self) -> None:
# self.main = screen.MemoryMap(screen_page=1)
#
# def test_pixel_packing_p0_p0(self):
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b01000011
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b01000011
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b1100000000111111000000001111
# got = hgr.packed[0, 0]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# def test_pixel_packing_p0_p1(self):
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b01000011
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b11000011
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b1000000001111111000000001111
# got = hgr.packed[0, 0]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# def test_pixel_packing_p1_p0(self):
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b11000011
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b01000011
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b1100000000111110000000011110
# got = hgr.packed[0, 0]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# def test_pixel_packing_p1_p1(self):
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b11000011
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b11000011
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b1000000001111110000000011110
# got = hgr.packed[0, 0]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# def test_pixel_packing_p1_promote_p0(self):
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b00000000
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b01000000
#
# # PDCCBBAA
# self.main.page_offset[0, 2] = 0b10000000
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b0000000000000000000000000001
# got = hgr.packed[0, 1]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# def test_pixel_packing_p1_promote_p1(self):
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b00000000
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b11000000
#
# # PDCCBBAA
# self.main.page_offset[0, 2] = 0b10000000
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b0000000000000000000000000001
# got = hgr.packed[0, 1]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# def test_nominal_colours(self):
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b01010101
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b00101010
# # PDCCBBAA
# self.main.page_offset[0, 2] = 0b01010101
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b000110011001100110011001100110011
# got = hgr.packed[0, 0]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# self.assertEqual(
# (
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# colours.HGRColours.VIOLET,
# ),
# colours.int34_to_nominal_colour_pixels(hgr.packed[0, 0],
# colours.HGRColours)
# )
#
# # See Figure 8.15 from Sather, "Understanding the Apple IIe"
#
# def test_nominal_colours_sather1(self):
# # Extend violet into light blue
#
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b01000000
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b10000000
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# self.assertEqual(
# (
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.LIGHT_BLUE,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# ),
# colours.int28_to_nominal_colour_pixels(hgr.packed[0, 0],
# colours.HGRColours)
# )
#
# def test_nominal_colours_sather2(self):
# # Cut off blue with black to produce dark blue
#
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b11000000
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b00000000
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# self.assertEqual(
# (
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.DARK_BLUE,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# ),
# colours.int28_to_nominal_colour_pixels(hgr.packed[0, 0],
# colours.HGRColours)
# )
#
# def test_nominal_colours_sather3(self):
# # Cut off blue with green to produce aqua
#
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b11000000
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b00000001
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# self.assertEqual(
# (
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.AQUA,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# ),
# colours.int28_to_nominal_colour_pixels(hgr.packed[0, 0],
# colours.HGRColours)
# )
#
# def test_nominal_colours_sather4(self):
# # Cut off white with black to produce pink
#
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b11100000
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b00000000
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b0000000000000011100000000000
# got = hgr.packed[0, 0]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# # TODO: BROWN(0001)/VIOLET(1100) should reframe to PINK (1011)
# self.assertEqual(
# (
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BROWN,
# colours.HGRColours.VIOLET,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# ),
# colours.int28_to_nominal_colour_pixels(hgr.packed[0, 0],
# colours.HGRColours)
# )
#
# def test_nominal_colours_sather5(self):
# # Extend green into light brown
#
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b01000000
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b10000000
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b0000000000000111000000000000
# got = hgr.packed[0, 0]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# # TODO: LIGHT_BLUE should reframe to PINK (1011)
# self.assertEqual(
# (
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.LIGHT_BLUE,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# ),
# colours.int28_to_nominal_colour_pixels(hgr.packed[0, 0],
# colours.HGRColours)
# )
#
# def test_nominal_colours_sather6(self):
# # Cut off orange with black to produce dark brown
#
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b11000000
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b00000000
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b00000000000000010000000000000
# got = hgr.packed[0, 0]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# # TODO: DARK_BLUE should reframe to DARK_BROWN
# self.assertEqual(
# (
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.DARK_BLUE,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# ),
# colours.int28_to_nominal_colour_pixels(hgr.packed[0, 0],
# colours.HGRColours)
# )
#
# def test_nominal_colours_sather7(self):
# # Cut off orange with violet to produce pink
#
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b11000000
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b00000001
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b00000000000001110000000000000
# got = hgr.packed[0, 0]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# # TODO: AQUA should reframe to PINK
# self.assertEqual(
# (
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.AQUA,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# ),
# colours.int28_to_nominal_colour_pixels(hgr.packed[0, 0],
# colours.HGRColours)
# )
#
# def test_nominal_colours_sather8(self):
# # Cut off white with black to produce aqua
#
# # PDCCBBAA
# self.main.page_offset[0, 0] = 0b11100000
# # PGGFFEED
# self.main.page_offset[0, 1] = 0b00000000
#
# hgr = screen.HGRBitmap(
# main_memory=self.main)
#
# want = 0b00000000000000011100000000000
# got = hgr.packed[0, 0]
#
# self.assertEqual(
# want, got, "\n%s\n%s" % (binary(want), binary(got))
# )
#
# # TODO: BROWN/VIOLET should reframe to AQUA
# self.assertEqual(
# (
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BROWN,
# colours.HGRColours.VIOLET,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# colours.HGRColours.BLACK,
# ),
# colours.int28_to_nominal_colour_pixels(
# hgr.packed[0, 0], colours.HGRColours)
# )
if __name__ == '__main__':
unittest.main()