From 39e8eac8eda31efd35f2920adeb3e42586d053e2 Mon Sep 17 00:00:00 2001 From: kris Date: Sat, 21 Jan 2023 17:30:27 +0000 Subject: [PATCH] Add support for DHGR mono conversions --- README.md | 11 ++++++++--- convert.py | 32 ++++++++++++++++++++------------ convert_dhr.py | 46 +++++++++++++++++++++++++++++++++------------- convert_shr.py | 4 +++- image.py | 3 +++ screen.py | 8 ++++++-- 6 files changed, 73 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 199348b..5f761fb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ][-pix 2.0 +# ][-pix 2.1 ][-pix is an image conversion utility targeting Apple II graphics modes, currently Double Hi-Res (enhanced //e, //c, //gs) and Super Hi-Res (//gs). @@ -39,11 +39,11 @@ To convert an image, the basic command is: python convert.py [] ``` where -* `mode` is `dhr` for Double Hi-Res (560x192), or `shr` for Super Hi-Res (320x200) +* `mode` is `dhr` for Double Hi-Res Colour (560x192), `dhr_mono` for Double Hi-Res Mono (560x192), or `shr` for Super Hi-Res (320x200) * `input` is the source image file to convert (e.g. `my-image.jpg`) * `output` is the output filename to produce (e.g. `my-image.dhr`) -The following flags are supported in both `dhr` and `shr` modes: +The following flags are supported in all modes: * `--show-input` Whether to show the input image before conversion. (default: False) * `--show-output` Whether to show the output image after conversion. (default: True) @@ -158,6 +158,11 @@ python convert.py shr examples/shr/rabbit-kitten-original.png examples/shr/rabbi # Version history +## v2.1 (2023-01-21) + +* Added support for DHGR mono conversions +* Fixed compatibility with python 3.10 + ## v2.0 (2022-07-16) * Added support for Super Hi-Res 320x200 image conversions diff --git a/convert.py b/convert.py index d7b15a7..5ed13a2 100644 --- a/convert.py +++ b/convert.py @@ -79,6 +79,10 @@ def main(): "value of --palette)") dhr_parser.set_defaults(func=convert_dhr) + dhr_mono_parser = subparsers.add_parser("dhr_mono") + add_common_args(dhr_mono_parser) + dhr_mono_parser.set_defaults(func=convert_dhr_mono) + shr_parser = subparsers.add_parser("shr") add_common_args(shr_parser) shr_parser.add_argument( @@ -106,25 +110,29 @@ def prepare_image(image_filename: str, show_input: bool, screen, # Open and resize source image image = image_py.open(image_filename) if show_input: - image_py.resize(image, screen.X_RES, screen.Y_RES, - srgb_output=False).show() - rgb = np.array( - image_py.resize(image, screen.X_RES, screen.Y_RES, - gamma=gamma_correct)).astype(np.float32) / 255 - return rgb - + image_py.resize(image, screen.X_RES, screen.Y_RES * 2, + srgb_output=True).show() + return image_py.resize(image, screen.X_RES, screen.Y_RES, + gamma=gamma_correct) def convert_dhr(args): palette = palette_py.PALETTES[args.palette]() - screen = screen_py.DHGRScreen(palette) - rgb = prepare_image(args.input, args.show_input, screen, args.gamma_correct) - convert_dhr_py.convert(screen, rgb, args) + screen = screen_py.DHGRNTSCScreen(palette) + image = prepare_image(args.input, args.show_input, screen, + args.gamma_correct) + convert_dhr_py.convert(screen, image, args) + + +def convert_dhr_mono(args): + screen = screen_py.DHGRScreen() + image = prepare_image(args.input, args.show_input, screen, args.gamma_correct) + convert_dhr_py.convert_mono(screen, image, args) def convert_shr(args): screen = screen_py.SHR320Screen() - rgb = prepare_image(args.input, args.show_input, screen, args.gamma_correct) - convert_shr_py.convert(screen, rgb, args) + image = prepare_image(args.input, args.show_input, screen, args.gamma_correct) + convert_shr_py.convert(screen, image, args) if __name__ == "__main__": diff --git a/convert_dhr.py b/convert_dhr.py index 3ccf252..9ba7801 100644 --- a/convert_dhr.py +++ b/convert_dhr.py @@ -10,7 +10,27 @@ import screen as screen_py import image as image_py -def convert(screen: screen_py.DHGRScreen, rgb: np.ndarray, args): +def _output(out_image: Image, args): + if args.show_output: + out_image.show() + + if args.save_preview: + # Save Double hi-res image + outfile = os.path.join( + os.path.splitext(args.output)[0] + "-preview.png") + out_image.save(outfile, "PNG") + + +def _write(screen: screen_py.DHGRScreen, bitmap: np.ndarray, args): + screen.pack(bitmap) + with open(args.output, "wb") as f: + f.write(bytes(screen.aux)) + f.write(bytes(screen.main)) + + +def convert(screen: screen_py.DHGRNTSCScreen, image: Image, args): + rgb = np.array(image).astype(np.float32) / 255 + # Conversion matrix from RGB to CAM16UCS colour values. Indexed by # 24-bit RGB value base_dir = os.path.dirname(__file__) @@ -24,7 +44,7 @@ def convert(screen: screen_py.DHGRScreen, rgb: np.ndarray, 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.DHGRScreen(output_palette) + output_screen = screen_py.DHGRNTSCScreen(output_palette) if output_palette_name == "ntsc": output_srgb = output_screen.bitmap_to_image_ntsc(bitmap) else: @@ -34,15 +54,15 @@ def convert(screen: screen_py.DHGRScreen, rgb: np.ndarray, args): Image.fromarray(output_srgb), screen.X_RES, screen.Y_RES * 2, srgb_output=True) - if args.show_output: - out_image.show() + _output(out_image, args) + _write(screen, bitmap, args) - if args.save_preview: - # Save Double hi-res image - outfile = os.path.join( - os.path.splitext(args.output)[0] + "-preview.png") - out_image.save(outfile, "PNG") - screen.pack(bitmap) - with open(args.output, "wb") as f: - f.write(bytes(screen.aux)) - f.write(bytes(screen.main)) +def convert_mono(screen: screen_py.DHGRScreen, image: Image, args): + image = image.convert("1") + + out_image = Image.fromarray((np.array(image) * 255).astype(np.uint8)) + out_image = image_py.resize( + out_image, screen.X_RES, screen.Y_RES * 2, srgb_output=True) + + _output(out_image, args) + _write(screen, np.array(image).astype(bool), args) diff --git a/convert_shr.py b/convert_shr.py index 0a01f92..12c8bfc 100644 --- a/convert_shr.py +++ b/convert_shr.py @@ -362,7 +362,9 @@ class ClusterPalette: self._palette_lines[palette_idx] = [worst_line] -def convert(screen, rgb: np.ndarray, args): +def convert(screen, image: Image, args): + rgb = np.array(image).astype(np.float32) / 255 + # Conversion matrix from RGB to CAM16UCS colour values. Indexed by # 24-bit RGB value base_dir = os.path.dirname(__file__) diff --git a/image.py b/image.py index 41680ea..622b61b 100644 --- a/image.py +++ b/image.py @@ -45,3 +45,6 @@ def resize( np.uint8)) else: return res + +def resize_mono(image: Image, x_res, y_res) -> Image: + return image.resize((x_res, y_res), Image.LANCZOS) diff --git a/screen.py b/screen.py index 7c69142..681bbcc 100644 --- a/screen.py +++ b/screen.py @@ -71,10 +71,9 @@ class DHGRScreen: X_RES = 560 Y_RES = 192 - def __init__(self, palette: palette_py.Palette): + def __init__(self): self.main = np.zeros(8192, dtype=np.uint8) self.aux = np.zeros(8192, dtype=np.uint8) - self.palette = palette @staticmethod def y_to_base_addr(y: int) -> int: @@ -112,6 +111,11 @@ class DHGRScreen: self.main[addr:addr + 40] = main_col[y, :] return +class DHGRNTSCScreen(DHGRScreen): + def __init__(self, palette: palette_py.Palette): + self.palette = palette + super(DHGRNTSCScreen, self).__init__() + def bitmap_to_image_rgb(self, bitmap: np.ndarray) -> np.ndarray: """Convert our 2-bit bitmap image into a RGB image.