Add support for DHGR mono conversions

This commit is contained in:
kris 2023-01-21 17:30:27 +00:00
parent 629104b933
commit 39e8eac8ed
6 changed files with 73 additions and 31 deletions

View File

@ -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 <mode> [<flags>] <input> <output>
```
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

View File

@ -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__":

View File

@ -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)

View File

@ -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__)

View File

@ -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)

View File

@ -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.