diff --git a/convert.py b/convert.py index f8069fe..37b3a15 100644 --- a/convert.py +++ b/convert.py @@ -140,7 +140,7 @@ def prepare_image(image_filename: str, show_input: bool, screen, def convert_hgr(args): palette = palette_py.PALETTES[args.palette]() - screen = screen_py.HGRScreen(palette) + screen = screen_py.HGRNTSCScreen(palette) image = prepare_image(args.input, args.show_input, screen, args.gamma_correct) convert_hgr_py.convert(screen, image, args) diff --git a/convert_hgr.py b/convert_hgr.py index 88a14e9..6594e7f 100644 --- a/convert_hgr.py +++ b/convert_hgr.py @@ -21,14 +21,14 @@ def _output(out_image: Image, args): out_image.save(outfile, "PNG") -def _write(screen: screen_py.HGRScreen, linear_bytemap: np.ndarray, args): +def _write(screen: screen_py.HGRNTSCScreen, linear_bytemap: np.ndarray, args): screen.pack_bytes(linear_bytemap) with open(args.output, "wb") as f: f.write(bytes(screen.main)) # TODO: unify with convert_dhr.convert() -def convert(screen: screen_py.HGRScreen, image: Image, args): +def convert(screen: screen_py.HGRNTSCScreen, image: Image, args): rgb = np.array(image).astype(np.float32) / 255 # Conversion matrix from RGB to CAM16UCS colour values. Indexed by @@ -45,7 +45,7 @@ def convert(screen: screen_py.HGRScreen, image: Image, args): # Show output image by rendering in target palette output_palette_name = args.show_palette or args.palette output_palette = palette_py.PALETTES[output_palette_name]() - output_screen = screen_py.HGRScreen(output_palette) + output_screen = screen_py.HGRNTSCScreen(output_palette) if output_palette_name == "ntsc": output_srgb = output_screen.bitmap_to_image_ntsc(bitmap) else: diff --git a/screen.py b/screen.py index 4254516..6ca55c2 100644 --- a/screen.py +++ b/screen.py @@ -222,6 +222,7 @@ class NTSCScreen: ), x % 4] return image_rgb + class DHGRNTSCScreen(DHGRScreen, NTSCScreen): def __init__(self, palette: palette_py.Palette): self.palette = palette @@ -230,7 +231,20 @@ class DHGRNTSCScreen(DHGRScreen, NTSCScreen): NTSC_PHASE_SHIFT = 0 -class HGRScreen(NTSCScreen): +class HGRNTSCScreen(NTSCScreen): + # Hi-Res really is 560 pixels horizontally, not 280 - but unlike DHGR + # you can only independently control about half of the pixels. + # + # In more detail, hi-res graphics works like this: + # - Each of the low 7 bits in a byte of screen memory results in + # enabling or disabling two sequential 560-resolution pixels. + # - pixel screen order is from LSB to MSB + # - if bit 8 (the "palette bit") is set then the 14-pixel sequence is + # shifted one position to the right, and the left-most pixel is filled + # in by duplicating the right-most pixel produced by the previous + # screen byte (i.e. bit 7) + # - thus each byte produces a 15 or 14 pixel sequence depending on + # whether or not the palette bit is set. X_RES = 560 Y_RES = 192 @@ -241,7 +255,7 @@ class HGRScreen(NTSCScreen): def __init__(self, palette: palette_py.Palette): self.main = np.zeros(8192, dtype=np.uint8) self.palette = palette - super(HGRScreen, self).__init__() + super(HGRNTSCScreen, self).__init__() def pack_bytes(self, linear_bytemap: np.ndarray): """Packs an image into memory format (8K main)."""