First public commit.
This commit is contained in:
parent
2a48f52979
commit
4bac9d83ed
288 changed files with 18417 additions and 1076 deletions
16
lenser/coco/convert/__init__.py
Normal file
16
lenser/coco/convert/__init__.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
"""TRS-80 Color Computer conversion dispatch."""
|
||||
from __future__ import annotations
|
||||
from ... import imageprep
|
||||
from . import pmode3, pmode4, mono
|
||||
|
||||
_MODULES = {"pmode4": pmode4, "pmode3": pmode3, "mono": mono}
|
||||
MODES = list(_MODULES.keys())
|
||||
|
||||
|
||||
def convert_image(path_or_img, mode="pmode4", palette_name="mc6847",
|
||||
dither_mode="floyd", intensive=False, prep_opt=None, base_color=None):
|
||||
prep_opt = prep_opt or imageprep.PrepOptions()
|
||||
module = _MODULES[mode]
|
||||
img_rgb = imageprep.prepare(path_or_img, module.WIDTH, module.HEIGHT,
|
||||
module.PIXEL_ASPECT, prep_opt, border_rgb=(0, 0, 0))
|
||||
return module.convert(img_rgb, palette_name, dither_mode, intensive, base_color=base_color)
|
||||
15
lenser/coco/convert/mono.py
Normal file
15
lenser/coco/convert/mono.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
"""CoCo monochrome -- PMODE 4's 256x192 black & white, exposed as the standard
|
||||
``mono`` mode for cross-platform parity (tone carried by dithering)."""
|
||||
from __future__ import annotations
|
||||
|
||||
from . import pmode4
|
||||
|
||||
WIDTH, HEIGHT, PIXEL_ASPECT = pmode4.WIDTH, pmode4.HEIGHT, pmode4.PIXEL_ASPECT
|
||||
|
||||
|
||||
def convert(img_rgb, palette_name="mc6847", dither_mode="floyd",
|
||||
intensive=False, base_color=None):
|
||||
conv = pmode4.convert(img_rgb, palette_name, dither_mode, intensive,
|
||||
base_color=base_color)
|
||||
conv.mode = "mono"
|
||||
return conv
|
||||
44
lenser/coco/convert/pmode3.py
Normal file
44
lenser/coco/convert/pmode3.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
"""CoCo PMODE 3: 128x192, 4 colours from one MC6847 colour set.
|
||||
|
||||
128 wide on a 4:3 screen -> 2:1 pixels (like C64 multicolor). The 4 colours are
|
||||
fixed per CSS set (green/yellow/blue/red or buff/cyan/magenta/orange); we dither
|
||||
to whichever set reproduces the image with lower error and tell the viewer which.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ... import dither
|
||||
from ...convert.base import Conversion, perceptual_error
|
||||
from ...palette import srgb_to_lab
|
||||
from .. import palette as cpal
|
||||
|
||||
WIDTH, HEIGHT = 128, 192
|
||||
PIXEL_ASPECT = 2.0
|
||||
|
||||
|
||||
def convert(img_rgb, palette_name="mc6847", dither_mode="floyd",
|
||||
intensive=False, base_color=None):
|
||||
img_lab = srgb_to_lab(img_rgb)
|
||||
allowed = np.tile(np.array([0, 1, 2, 3]), (HEIGHT, WIDTH, 1))
|
||||
|
||||
best = None
|
||||
for vdg, rgb in cpal.PMODE3_SETS.items():
|
||||
plab = srgb_to_lab(rgb)
|
||||
idx = dither.quantize(img_lab, allowed, plab, dither_mode).astype(np.uint8)
|
||||
err = perceptual_error(idx, img_lab, plab)
|
||||
if best is None or err < best[0]:
|
||||
best = (err, vdg, rgb, plab, idx)
|
||||
|
||||
err, vdg, rgb, plab, idx = best
|
||||
data = cpal.pack_pmode3(idx) # 6144-byte video buffer
|
||||
preview = rgb.astype(np.uint8)[idx]
|
||||
|
||||
return Conversion(
|
||||
mode="pmode3", width=WIDTH, height=HEIGHT, pixel_aspect=PIXEL_ASPECT,
|
||||
index_image=idx.astype(np.uint16), data=data, data_addr=0,
|
||||
viewer="pmode3", preview_rgb=preview,
|
||||
error=err,
|
||||
meta={"palette": "mc6847", "dither": dither_mode, "vdg": vdg},
|
||||
)
|
||||
41
lenser/coco/convert/pmode4.py
Normal file
41
lenser/coco/convert/pmode4.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
"""CoCo PMODE 4: 256x192, 1 bit/pixel, black & white (buff on black).
|
||||
|
||||
The CoCo's high-resolution monochrome mode -- 256x192 is exactly 4:3, so square
|
||||
pixels. Tone is carried by dithering, like Apple HGR mono.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ... import dither
|
||||
from ...convert.base import Conversion, perceptual_error
|
||||
from .. import palette as cpal
|
||||
|
||||
WIDTH, HEIGHT = 256, 192
|
||||
PIXEL_ASPECT = 1.0
|
||||
|
||||
|
||||
def convert(img_rgb, palette_name="mc6847", dither_mode="floyd",
|
||||
intensive=False, base_color=None):
|
||||
from ...palette import srgb_to_lab
|
||||
plab = cpal.mono_lab()
|
||||
L = srgb_to_lab(img_rgb)[..., 0]
|
||||
img_mono = np.zeros((HEIGHT, WIDTH, 3))
|
||||
img_mono[..., 0] = L
|
||||
plab_mono = np.zeros((2, 3))
|
||||
plab_mono[:, 0] = plab[:, 0]
|
||||
|
||||
allowed = np.tile(np.array([0, 1]), (HEIGHT, WIDTH, 1))
|
||||
idx = dither.quantize(img_mono, allowed, plab_mono, dither_mode).astype(np.uint8)
|
||||
|
||||
data = cpal.pack_pmode4(idx) # 6144-byte video buffer
|
||||
preview = cpal.MONO.astype(np.uint8)[idx]
|
||||
|
||||
return Conversion(
|
||||
mode="pmode4", width=WIDTH, height=HEIGHT, pixel_aspect=PIXEL_ASPECT,
|
||||
index_image=idx.astype(np.uint16), data=data, data_addr=0,
|
||||
viewer="pmode4", preview_rgb=preview,
|
||||
error=perceptual_error(idx, img_mono, plab_mono),
|
||||
meta={"palette": "mc6847", "dither": dither_mode, "vdg": 0xF8},
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue