47 lines
1.7 KiB
Python
47 lines
1.7 KiB
Python
"""ZX Spectrum monochrome / tinted-mono mode.
|
|
|
|
256x192 matched by luminance. The Spectrum has no grey, so greyscale is a 2-level
|
|
black/white halftone (bright black + bright white) -- which at 256x192 with
|
|
dithering is a crisp, high-detail image free of attribute clash. A base colour
|
|
gives a 3-level tinted ramp (black -> colour -> white), all within one brightness
|
|
group so the per-cell BRIGHT constraint is satisfied. Reuses the hires packing.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
|
|
from ...convert import base
|
|
from .. import palette as spal
|
|
from . import hires
|
|
|
|
|
|
def _ramp(base_color):
|
|
"""Luminance ramp kept inside ONE brightness group (shared BRIGHT bit)."""
|
|
if base_color is None:
|
|
return [8, 15] # bright black + bright white
|
|
c = base_color & 7 # base hue 0-7
|
|
if c in (0, 7):
|
|
return [8, 15]
|
|
return [8, 8 | c, 15] # bright black, bright hue, bright white
|
|
|
|
|
|
def convert(img_rgb, palette_name="spectrum", dither_mode="atkinson",
|
|
intensive=False, base_color=None):
|
|
plab = spal.palette_lab()
|
|
prgb = spal.get_palette().astype(np.uint8)
|
|
ramp = _ramp(base_color)
|
|
|
|
idx, sets, rows, cols, err = base.mono_render(
|
|
img_rgb, plab, ramp, hires.WIDTH, hires.HEIGHT,
|
|
hires.CELL_W, hires.CELL_H, dither_mode, n_free=2)
|
|
|
|
scr = hires._encode(idx, sets, rows, cols)
|
|
|
|
return base.Conversion(
|
|
mode="mono", width=hires.WIDTH, height=hires.HEIGHT,
|
|
pixel_aspect=hires.PIXEL_ASPECT, index_image=idx.astype(np.uint16),
|
|
data=bytes(scr), data_addr=0x4000, viewer="spectrum", preview_rgb=prgb[idx],
|
|
error=err,
|
|
meta={"palette": "spectrum", "dither": dither_mode, "base_color": base_color},
|
|
)
|