ii-vision/transcoder/colours.py
2019-07-07 21:13:28 +01:00

133 lines
3.2 KiB
Python

"""Apple II nominal display colours, represented by 4-bit dot sequences.
These are distinct from the effective colours that are actually displayed,
e.g. due to white/black coalescing and NTSC artifacting.
"""
from typing import Tuple, Type
import enum
import functools
def ror(int4: int, howmany: int) -> int:
"""Rotate-right an int4 some number of times."""
res = int4
for _ in range(howmany):
res = _ror(res)
return res
def _ror(int4: int) -> int:
return ((int4 & 0b1110) >> 1) ^ ((int4 & 0b0001) << 3)
def rol(int4: int, howmany: int) -> int:
"""Rotate-left an int4 some number of times."""
res = int4
for _ in range(howmany):
res = _rol(res)
return res
def _rol(int4: int) -> int:
return ((int4 & 0b0111) << 1) ^ ((int4 & 0b1000) >> 3)
class NominalColours(enum.Enum):
pass
class HGRColours(NominalColours):
# Value is memory bit order, which is opposite to screen order (bits
# ordered Left to Right on screen)
BLACK = 0b0000
MAGENTA = 0b0001
BROWN = 0b1000
ORANGE = 0b1001 # HGR colour
DARK_GREEN = 0b0100
GREY1 = 0b0101
GREEN = 0b1100 # HGR colour
YELLOW = 0b1101
DARK_BLUE = 0b0010
VIOLET = 0b0011 # HGR colour
GREY2 = 0b1010
PINK = 0b1011
MED_BLUE = 0b0110 # HGR colour
LIGHT_BLUE = 0b0111
AQUA = 0b1110
WHITE = 0b1111
class DHGRColours(NominalColours):
# DHGR 4-bit memory representation is right-rotated from the HGR video
# representation.
BLACK = 0b0000
MAGENTA = 0b1000
BROWN = 0b0100
ORANGE = 0b1100 # HGR colour
DARK_GREEN = 0b0010
GREY1 = 0b1010
GREEN = 0b0110 # HGR colour
YELLOW = 0b1110
DARK_BLUE = 0b0001
VIOLET = 0b1001 # HGR colour
GREY2 = 0b0101
PINK = 0b1101
MED_BLUE = 0b0011 # HGR colour
LIGHT_BLUE = 0b1011
AQUA = 0b0111
WHITE = 0b1111
@functools.lru_cache(None)
def dots_to_nominal_colour_pixels(
num_bits: int,
dots: int,
colours: Type[NominalColours],
init_phase: int = 1 # Such that phase = 0 at start of body
) -> Tuple[NominalColours]:
"""Sequence of num_bits nominal colour pixels via sliding 4-bit window.
Includes the 3-bit header that represents the trailing 3 bits of the
previous tuple body. i.e. storing a byte in aux even columns will also
influence the colours of the previous main odd column.
This naively models the NTSC colour artifacting.
TODO: Use a more careful analogue colour composition model to produce
effective pixel colours.
TODO: DHGR vs HGR colour differences can be modeled by changing init_phase
"""
res = []
shifted = dots
phase = init_phase
for i in range(num_bits):
colour = rol(shifted & 0b1111, phase)
res.append(colours(colour))
shifted >>= 1
phase += 1
if phase == 4:
phase = 0
return tuple(res)
@functools.lru_cache(None)
def dots_to_nominal_colour_pixel_values(
num_bits: int,
dots: int,
colours: Type[NominalColours],
init_phase: int = 1 # Such that phase = 0 at start of body
) -> Tuple[int]:
return tuple(p.value for p in dots_to_nominal_colour_pixels(
num_bits, dots, colours, init_phase
))