예제 #1
0
    def test_apply(self):
        """Test that header, body and footer are placed correctly."""
        hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        hgr.apply(0, 0, False, 0b11000011)
        hgr.apply(0, 1, False, 0b11000011)

        want = 0b1000011111000011000
        got = hgr.packed[0, 0]

        self.assertEqual(
            want, got, "\n%s\n%s" % (binary(want), binary(got))
        )

        # Now check with 4 consecutive bytes, i.e. even/odd pair plus the
        # neighbouring header/footer.
        hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        hgr.apply(1, 197, False, 128)
        hgr.apply(1, 198, False, 143)
        hgr.apply(1, 199, False, 192)
        hgr.apply(1, 200, False, 128)

        want = 0b0011000000110001111100
        got = hgr.packed[1, 199 // 2]

        self.assertEqual(
            want, got, "\n%s\n%s" % (binary(want), binary(got))
        )
예제 #2
0
    def __init__(self,
                 frame_grabber: FrameGrabber,
                 ticks_per_second: float,
                 mode: VideoMode = VideoMode.HGR,
                 palette: Palette = Palette.NTSC):
        self.mode = mode  # type: VideoMode
        self.frame_grabber = frame_grabber  # type: FrameGrabber
        self.ticks_per_second = ticks_per_second  # type: float
        self.ticks_per_frame = (self.ticks_per_second /
                                frame_grabber.input_frame_rate)  # type: float
        self.frame_number = 0  # type: int
        self.palette = palette  # type: Palette

        # Initialize empty screen
        self.memory_map = screen.MemoryMap(
            screen_page=1)  # type: screen.MemoryMap
        if self.mode == mode.DHGR:
            self.aux_memory_map = screen.MemoryMap(
                screen_page=1)  # type: screen.MemoryMap

            self.pixelmap = screen.DHGRBitmap(palette=palette,
                                              main_memory=self.memory_map,
                                              aux_memory=self.aux_memory_map)
        else:
            self.pixelmap = screen.HGRBitmap(
                palette=palette,
                main_memory=self.memory_map,
            )

        # Accumulates pending edit weights across frames
        self.update_priority = np.zeros((32, 256), dtype=np.int)
        if self.mode == mode.DHGR:
            self.aux_update_priority = np.zeros((32, 256), dtype=np.int)
예제 #3
0
    def test_pixel_packing_p1_p1(self):
        """Screen byte packing happens correctly with P=1, P=1 palette bits."""

        #                               PDCCBBAA
        self.main.page_offset[0, 0] = 0b11000011
        #                               PGGFFEED
        self.main.page_offset[0, 1] = 0b11000011

        hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        want = 0b1000011111000011000
        got = hgr.packed[0, 0]

        self.assertEqual(
            want, got, "\n%s\n%s" % (binary(want), binary(got))
        )
예제 #4
0
    def test_nominal_colours_sather_odd_5(self):
        """Cut off blue-black with violet to produce bright violet.

        "Bright" here is because the sequence of pixels has high intensity
        Blue-Blue-Light Blue-Light Blue-Violet-Violet.
        """

        #                               PDCCBBAA
        self.main.page_offset[0, 1] = 0b10100000
        #                               PGGFFEED
        self.main.page_offset[0, 2] = 0b00000001

        self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        masked = int(screen.HGRBitmap.mask_and_shift_data(
            self.hgr.packed[0, 0], byte_offset=1))
        dots = screen.HGRBitmap.to_dots(masked, byte_offset=1)

        self.assertEqual(
            (
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.DARK_BLUE,
                colours.HGRColours.MED_BLUE,
                colours.HGRColours.MED_BLUE,
                colours.HGRColours.LIGHT_BLUE,
                colours.HGRColours.LIGHT_BLUE,
                colours.HGRColours.VIOLET,
                colours.HGRColours.VIOLET
            ),
            colours.dots_to_nominal_colour_pixels(
                18, dots, colours.HGRColours,
                init_phase=screen.HGRBitmap.PHASES[1])
        )
예제 #5
0
    def test_nominal_colours_sather_even_5(self):
        """Cut off orange-black with green to produce bright green.

        "Bright" here is because the sequence of pixels has high intensity
        Orange-Orange-Yellow-Yellow-Green-Green."""

        #                               PDCCBBAA
        self.main.page_offset[0, 0] = 0b10100000
        #                               PGGFFEED
        self.main.page_offset[0, 1] = 0b00000001

        self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        masked = int(screen.HGRBitmap.mask_and_shift_data(
            self.hgr.packed[0, 0], byte_offset=0))
        dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)

        self.assertEqual(
            (
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BROWN,  # 0001
                colours.HGRColours.ORANGE,  # 1001
                colours.HGRColours.ORANGE,  # 1001
                colours.HGRColours.YELLOW,  # 1011
                colours.HGRColours.YELLOW,  # 1011
                colours.HGRColours.GREEN,  # 0011
                colours.HGRColours.GREEN,  # 0011
            ),
            colours.dots_to_nominal_colour_pixels(
                18, dots, colours.HGRColours,
                init_phase=screen.HGRBitmap.PHASES[0])
        )
예제 #6
0
    def test_nominal_colours_sather_even_1(self):
        """Extend violet into light blue."""

        #                               PDCCBBAA
        self.main.page_offset[0, 0] = 0b01000000
        #                               PGGFFEED
        self.main.page_offset[0, 1] = 0b10000000

        self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        masked = int(screen.HGRBitmap.mask_and_shift_data(
            self.hgr.packed[0, 0], byte_offset=0))
        dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)

        self.assertEqual(
            (
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.MAGENTA,  # 1000
                colours.HGRColours.VIOLET,  # 1100
                colours.HGRColours.LIGHT_BLUE,  # 1110
                colours.HGRColours.LIGHT_BLUE,  # 1110
                colours.HGRColours.MED_BLUE,  # 0110
                #  last repeated bit from byte 0
                colours.HGRColours.DARK_GREEN,  # 0010
            ),
            colours.dots_to_nominal_colour_pixels(
                18, dots, colours.HGRColours,
                init_phase=screen.HGRBitmap.PHASES[0])
        )
예제 #7
0
    def test_nominal_colours_sather_odd_2(self):
        """Cut off orange with black to produce dark brown."""

        #                               PDCCBBAA
        self.main.page_offset[0, 1] = 0b11000000
        #                               PGGFFEED
        self.main.page_offset[0, 2] = 0b00000000

        self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        masked = int(screen.HGRBitmap.mask_and_shift_data(
            self.hgr.packed[0, 0], byte_offset=1))
        dots = screen.HGRBitmap.to_dots(masked, byte_offset=1)

        self.assertEqual(
            (
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BROWN,
                colours.HGRColours.BROWN,
                colours.HGRColours.BROWN,
                colours.HGRColours.BROWN,
                colours.HGRColours.BLACK,
            ),
            colours.dots_to_nominal_colour_pixels(
                18, dots, colours.HGRColours,
                init_phase=screen.HGRBitmap.PHASES[1])
        )
예제 #8
0
    def test_nominal_colours_sather_odd_1(self):
        """Extend green into light brown."""

        #                               PDCCBBAA
        self.main.page_offset[0, 1] = 0b01000000
        #                               PGGFFEED
        self.main.page_offset[0, 2] = 0b10000000

        self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        masked = int(screen.HGRBitmap.mask_and_shift_data(
            self.hgr.packed[0, 0], byte_offset=1))
        dots = screen.HGRBitmap.to_dots(masked, byte_offset=1)

        self.assertEqual(
            (
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.DARK_GREEN,
                colours.HGRColours.GREEN,
                colours.HGRColours.YELLOW,
                colours.HGRColours.YELLOW,
                colours.HGRColours.ORANGE,
                colours.HGRColours.MAGENTA,
            ),
            colours.dots_to_nominal_colour_pixels(
                18, dots, colours.HGRColours,
                init_phase=screen.HGRBitmap.PHASES[1])
        )
예제 #9
0
    def test_nominal_colours_sather_even_4(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

        self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        masked = int(screen.HGRBitmap.mask_and_shift_data(
            self.hgr.packed[0, 0], byte_offset=0))
        dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)

        self.assertEqual(
            (
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BROWN,
                colours.HGRColours.ORANGE,
                colours.HGRColours.PINK,
                colours.HGRColours.PINK,
                colours.HGRColours.VIOLET,
                colours.HGRColours.DARK_BLUE,
                colours.HGRColours.BLACK,
            ),
            colours.dots_to_nominal_colour_pixels(
                18, dots, colours.HGRColours,
                init_phase=screen.HGRBitmap.PHASES[0])
        )
예제 #10
0
    def test_nominal_colours_sather_even_3(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

        self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        masked = int(screen.HGRBitmap.mask_and_shift_data(
            self.hgr.packed[0, 0], byte_offset=0))
        dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)

        self.assertEqual(
            (
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.BLACK,
                colours.HGRColours.DARK_BLUE,
                colours.HGRColours.MED_BLUE,
                colours.HGRColours.AQUA,
                colours.HGRColours.AQUA,
                colours.HGRColours.GREEN,
            ),
            colours.dots_to_nominal_colour_pixels(
                18, dots, colours.HGRColours,
                init_phase=screen.HGRBitmap.PHASES[0])
        )
예제 #11
0
    def _index_changes(self, source: screen.MemoryMap,
                       target: screen.MemoryMap, update_priority: np.array,
                       is_aux: True) -> Iterator[Tuple[int, int, List[int]]]:
        """Transform encoded screen to sequence of change tuples."""

        if self.mode == VideoMode.DHGR:
            if is_aux:
                target_pixelmap = screen.DHGRBitmap(
                    main_memory=self.memory_map,
                    aux_memory=target,
                    palette=self.palette)
            else:
                target_pixelmap = screen.DHGRBitmap(
                    main_memory=target,
                    aux_memory=self.aux_memory_map,
                    palette=self.palette)
        else:
            target_pixelmap = screen.HGRBitmap(main_memory=target,
                                               palette=self.palette)

        diff_weights = target_pixelmap.diff_weights(self.pixelmap, is_aux)
        # Don't bother storing into screen holes
        diff_weights[screen.SCREEN_HOLES] = 0

        # Clear any update priority entries that have resolved themselves
        # with new frame
        update_priority[diff_weights == 0] = 0
        update_priority += diff_weights

        priorities = self._heapify_priorities(update_priority)

        content_deltas = {}

        while priorities:
            pri, _, page, offset = heapq.heappop(priorities)

            assert not screen.SCREEN_HOLES[page, offset], (
                "Attempted to store into screen hole at (%d, %d)" %
                (page, offset))

            # Check whether we've already cleared this diff while processing
            # an earlier opcode
            if update_priority[page, offset] == 0:
                continue

            offsets = [offset]
            content = target.page_offset[page, offset]
            if self.mode == VideoMode.DHGR:
                # DHGR palette bit not expected to be set
                assert content < 0x80

            # Clear priority for the offset we're emitting
            update_priority[page, offset] = 0
            diff_weights[page, offset] = 0

            # Update memory maps
            source.page_offset[page, offset] = content
            self.pixelmap.apply(page, offset, is_aux, content)

            # Make sure we don't emit this offset as a side-effect of some
            # other offset later.
            for cd in content_deltas.values():
                cd[page, offset] = 0
                # TODO: what if we add another content_deltas entry later?
                #  We might clobber it again

            # Need to find 3 more offsets to fill this opcode
            for err, o in self._compute_error(page, content, target_pixelmap,
                                              diff_weights, content_deltas,
                                              is_aux):
                assert o != offset
                assert not screen.SCREEN_HOLES[page, o], (
                    "Attempted to store into screen hole at (%d, %d)" %
                    (page, o))

                if update_priority[page, o] == 0:
                    # Someone already resolved this diff.
                    continue

                # Make sure we don't end up considering this (page, offset)
                # again until the next image frame.  Even if a better match
                # comes along, it's probably better to fix up some other byte.
                # TODO: or should we recompute it with new error?
                for cd in content_deltas.values():
                    cd[page, o] = 0

                byte_offset = target_pixelmap.byte_offset(o, is_aux)
                old_packed = target_pixelmap.packed[page, o // 2]

                p = target_pixelmap.byte_pair_difference(
                    byte_offset, old_packed, content)

                # Update priority for the offset we're emitting
                update_priority[page, o] = p

                source.page_offset[page, o] = content
                self.pixelmap.apply(page, o, is_aux, content)

                if p:
                    # This content byte introduced an error, so put back on the
                    # heap in case we can get back to fixing it exactly
                    # during this frame.  Otherwise we'll get to it later.
                    heapq.heappush(priorities,
                                   (-p, random.getrandbits(8), page, o))

                offsets.append(o)
                if len(offsets) == 3:
                    break

            # Pad to 4 if we didn't find enough
            for _ in range(len(offsets), 4):
                offsets.append(offsets[0])
            yield (page + 32, content, offsets)

        # # TODO: there is still a bug causing residual diffs when we have
        # # apparently run out of work to do
        if not np.array_equal(source.page_offset, target.page_offset):
            diffs = np.nonzero(source.page_offset != target.page_offset)
            for i in range(len(diffs[0])):
                diff_p = diffs[0][i]
                diff_o = diffs[1][i]

                # For HGR, 0x00 or 0x7f may be visually equivalent to the same
                # bytes with high bit set (depending on neighbours), so skip
                # them
                if (source.page_offset[diff_p, diff_o] & 0x7f) == 0 and \
                        (target.page_offset[diff_p, diff_o] & 0x7f) == 0:
                    continue

                if (source.page_offset[diff_p, diff_o] & 0x7f) == 0x7f and \
                        (target.page_offset[diff_p, diff_o] & 0x7f) == 0x7f:
                    continue

                print("Diff at (%d, %d): %d != %d" %
                      (diff_p, diff_o, source.page_offset[diff_p, diff_o],
                       target.page_offset[diff_p, diff_o]))
                # assert False

        # If we run out of things to do, pad forever
        content = target.page_offset[0, 0]
        while True:
            yield (32, content, [0, 0, 0, 0])
예제 #12
0
    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

        self.hgr = screen.HGRBitmap(main_memory=self.main, palette=Palette.NTSC)

        want = 0b0100101010001010101000
        got = self.hgr.packed[0, 0]

        self.assertEqual(
            want, got, "\n%s\n%s" % (binary(want), binary(got))
        )

        masked = int(screen.HGRBitmap.mask_and_shift_data(
            self.hgr.packed[0, 0], byte_offset=0))
        dots = screen.HGRBitmap.to_dots(masked, byte_offset=0)
        self.assertEqual(
            (
                colours.HGRColours.MAGENTA,
                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.dots_to_nominal_colour_pixels(
                18, dots, colours.HGRColours,
                init_phase=screen.HGRBitmap.PHASES[0])
        )

        # Now check byte offset 1

        masked = int(screen.HGRBitmap.mask_and_shift_data(
            self.hgr.packed[0, 0], byte_offset=1))
        dots = screen.HGRBitmap.to_dots(masked, byte_offset=1)
        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.dots_to_nominal_colour_pixels(
                18, dots, colours.HGRColours,
                init_phase=screen.HGRBitmap.PHASES[1])
        )