# 8 Bit Lenser Convert a modern image (PNG/JPG/GIF/BMP/WEBP) into a **Commodore 64 disk image** (`.d64` / `.d71` / `.d81`) containing a self-contained viewer that displays the picture on a real C64 or in an emulator. The converter works hard to preserve quality within the VIC-II's tight colour and resolution limits. The Commodore 64 is the reference target, but **25+ other retro machines** are supported too — Atari 8-bit, Apple II / IIgs, BBC Micro, ZX Spectrum, Amstrad CPC, TI-99/4A, NES, Sega Master System, Commodore 128 / 16 / Plus/4 / VIC-20 / PET, Amiga, and more — each writing a self-contained, emulator-verified cartridge or disk. There's also an **ANSI / CP437** text-art export for BBSes. Pick the target with `--platform` (CLI) or the **Platform** selector (GUI). See the sections below. ![8 Bit Lenser GUI](docs/gui.png) ## Highlights - **Five display modes** (auto-selectable): - **Hires** — 320×200, 2 colours per 8×8 cell. Best for sharp line art. - **Multicolor** — 160×200, 1 shared background + 3 colours per 4×8 cell (the classic "Koala" format). Best general-purpose photo mode. - **FLI** — re-points the video matrix every scanline for per-line (4×1) colour. - **Interlace** — two multicolor frames blended at 50 Hz for ~136 apparent colours. - **Mono** — highest-resolution path: 320×200 matched by *luminance* to a colour ramp, so detail is carried by intense dithering. Greyscale by default, or pick any palette colour as the base for a tinted monochrome (black → blue → light blue → white, etc.). - **Perceptual, dither-aware conversion.** All colour decisions are made in CIELAB. Each screen cell's colour set is chosen by an exhaustive, vectorised search; for error-diffusion dithering the search is **dither-aware** — it scores each colour pair by distance to the *segment between* the colours (not just the nearest colour), so the chosen colours bracket the cell and dithering blends to the true shade. The result is dramatically smoother, more accurate images (perceptual ΔE roughly halved). An *Intensive* mode additionally searches the global background. - **Selectable dithering** — **Atkinson** (default), Floyd–Steinberg, the larger Stucki / Jarvis / Sierra-3 / Burkes kernels, fast Sierra-Lite, tone-adaptive Ostromoukhov and Hilbert-curve Riemersma (all paired with dither-aware selection — best for photos), plus the ordered modes ordered Bayer, organic **blue-noise** (no grid) and **Yliluoma** (mixes >2 palette entries per cell — superb on the constrained flat-palette machines), and none — every one constrained so a cell never shows a colour it can't. - **Explore variations.** One click renders every Mode × Palette × Dither combination as a contact sheet (parallelised across CPU cores); pick the best, then fine-tune brightness/contrast/saturation/gamma on that choice. - **PAL / NTSC.** Choose the target video standard. Static and interlace viewers work on both (interlace flips per frame, so it flickers at the standard's field rate automatically); FLI ships a separately-timed viewer for each. - **Run in an emulator.** One click builds the image and boots it (every platform runs under **MAME**): for the C64 it attaches the disk, then `LOAD"*",8,1` + `RUN` to show the picture. Runs in warp mode except where a mode needs real-time (e.g. interlace flicker). - **On-disk info program.** When there's room, a colourful BASIC program is added that prints the original name, dimensions, format, colour depth, oldest EXIF date, file date, EXIF comment, when the C64 version was made, the host platform, and the Linux distribution/version. - **Self-contained viewers.** Each disk's first program embeds the picture and loads in a single pass, so `LOAD"*",8,1` then `RUN` just works — no second disk access, no emulator-config surprises. - **Standard interchange files.** Multicolor exports also drop a `PICTURE.KOA` (Koala) and hires a `PICTURE.ART` (OCP Art Studio) file for use in other C64 tools. - **GUI and CLI.** ## Atari 8-bit support A second target platform is built in (`--platform atari`, or the **Platform** selector in the GUI). It produces a **self-booting `.atr` disk** (written natively — no external tools) whose boot sectors load a viewer that shows the picture. ![8 Bit Lenser targeting the Atari 8-bit](docs/gui_atari.png) Both GR.15 modes pick their colours with the same **dither-aware** search as the C64 (for error-diffusion dithers): the 4 register colours are chosen so their *blends* span the image gamut, so floyd dithering reproduces a rich, smooth image instead of the muddy result of plain k-means centroids (perceptual ΔE ~17 → ~2.7 on a portrait). Defaults to Floyd–Steinberg. Atari display modes: - **GR.15** (ANTIC E) — 160×192, 4 colours chosen globally from 256 (no per-cell limit, so cleaner than C64 multicolor). - **GR.9** (GTIA) — 80×192, **16 real luminance shades of one hue** — superb greyscale (hue 0) or tinted monochrome (pick any hue). Choose the hue via the Mono/hue base control. - **GR.8** — 320×192 hi-res, two tones. - **GR.15+DLI** — a display-list interrupt rewrites the 4 colours every 2 scanlines (96 colour bands) for far more colour — the Atari analogue of C64 FLI. (Every *single* line is impossible: four register writes don't fit the inter-DLI window.) The Atari palette is generated (NTSC YIQ), or — if the `atari800` emulator's bundled palette file is installed (see *Requirements*) — read from that so the preview matches exactly. "Run in emulator" boots the `.atr` in **MAME** (`a800xl` / `a800xlp`). > Status: all four Atari modes are **boot-verified** in MAME (`a800xl`), in > addition to decode round-trips, previews and disk-structure checks. ## Apple II support A third platform (`--platform apple`, or the GUI **Platform** selector) targets the Apple II+ / //e. It writes a **self-booting `.dsk`** (DOS 3.3 sector order) with a native, no-DOS boot loader: the boot sector reads the 8K HGR bitmap across three tracks (stepping the drive head) and switches on graphics. - **HGR mono** — 280×192, 1-bit black & white, universal across II+ and //e. (On a colour monitor a finely-dithered HGR image shows NTSC artifact fringing; on a mono monitor it's clean B&W.) **Boot-verified in MAME.** - **HGR colour** (`hgr_color`) — 140×192 NTSC artifact colour (~6 colours), the iconic II+ colour mode; reuses the HGR loader. **Boot-verified in MAME** (real green/violet/blue/orange on the emulated Apple). - **DHGR** (`dhgr`) — //e Double Hi-Res, 140×192, **16 colours**, the best Apple photo mode. **Boot-verified in MAME** (`apple2ee`). The 16-colour palette is measured from MAME's own DHGR output so the on-screen colours match the preview. All three boot via a self-written multi-track loader (DOS-free): it uses the standard phase-overlap head seek and sets both the sector (`$3D`) and track (`$41`) the Disk II boot ROM verifies. "Run in emulator" boots the `.dsk` in **MAME** (`apple2p` for II+, `apple2ee` for //e). ## More platforms The same converter targets several other machines (each writes a self-contained, MAME-verified cartridge or disk; pick with `--platform` or the GUI selector). Every platform reports a consistent **perceptual** ΔE (measured after a light blur that models how the eye/CRT averages a dither), so quality is comparable across machines; and every machine with a constrained colour mode also offers a **monochrome** mode (luminance-matched, detail carried by dithering — the highest-detail path, and tintable via a base colour): - **TI-99/4A** (`ti99`) — TMS9918A Graphics Mode 2, 256×192 / 2 colours per 8×8 cell (like C64 hires); 8 KB `.rpk` cartridge. Each cell's colour pair is chosen by the same **dither-aware** segment search as the C64 (for error-diffusion dithers), roughly halving perceptual error on photos. Defaults to **Atkinson** dithering — its lighter diffusion bleeds less across the tight two-colour cell boundaries than Floyd–Steinberg. Also a **mono** mode (luminance-matched grey ramp — every cell neutral, so no colour clash and maximum detail; or tinted via a base colour). Verified in MAME (`ti99_4a`). - **TRS-80 Color Computer** (`coco`) — MC6847 PMODE 4 (256×192 mono) / PMODE 3 (128×192, 4 colours); `.ccc` Program Pak. - **BBC Micro Model B** (`bbc`) — MODE 0/1/2/5 (up to 8 colours); DFS `.ssd` disk. - **ColecoVision / Adam** (`coleco`) — TMS9918A GM2 (same chip as the TI-99/4A, so it reuses that encoder, including the **dither-aware** colour-pair search and the **mono** mode; defaults to **Atkinson**); `.col` cartridge. Verified in MAME (`coleco`). - **Atari 2600 / VCS** (`a2600`) — racing-the-beam 40×192 playfield kernel, **3 colours per scanline** (the background is rewritten mid-line so the left and right halves differ, plus a shared foreground); `.a26` cartridge. Defaults to Atkinson dithering (Bayer fares poorly on the 40px playfield). An optional **interlace** mode (`pf_il`) ships an 8K bank-switched cart that alternates two frames at 60Hz so each scanline shows ~4-6 *perceived* colours — much smoother, at the cost of flicker (best on emulators / LCDs). - **Mattel Intellivision** (`intv`) — STIC Foreground/Background mode, 160×96 = 20×12 cells of 8×8, **two colours per cell** (foreground from 8, background from all 16) like C64 hires, drawn from a 64-tile GRAM dictionary; a hand-written **CP1610** viewer on a clean cartridge (no copyrighted EXEC/game data). `.int` cartridge, verified in MAME (`intv`). The 64 tile shapes and the per-cell (tile, foreground, background) choices are optimised **jointly** by a block-truncation / vector-quantisation iteration (alternately re-assign each cell to its best tile, re-pick its two colours, and re-cut every tile's shape), which drives the picture to within ~1.5 % of the theoretical floor of the two-colour-per-cell model. Defaults to *no* dithering — within the 64-tile budget error-diffusion is counterproductive (the clustering destroys it). A **mono** mode (two greys, k-medoids tile codebook) gives a cleaner two-tone picture than the colour mode at this resolution. - **Commodore VIC-20** (`vic20`) — the VIC-20 has no bitmap mode, so images are drawn from a **programmable 256-character set** clustered (k-means) from the screen's 8×8 cells. Two modes: - **Multicolor** (default) — 88×184, **four colours per 4×8 cell**: three global registers (background and auxiliary from all 16, border from 0–7) plus one per-cell colour (0–7). Because the warm tones (orange/pink) live only in the global colours, this is the strong photo mode. The three globals are chosen by a dither-aware nearest-colour-ranked search; defaults to Floyd–Steinberg. - **Hires** — 176×184, one global background (any of 16) + one per-cell foreground (0–7); best for high-contrast line art. - **Mono** — 176×184 luminance-matched two-tone (black + white, or a tinted base); the 256-char dictionary is built by a k-medoids codebook so dithered detail survives. Self-programming 6502 viewer (the KERNAL leaves the VIC uninitialised for an autostart cart, so the viewer sets every register and copies the data to RAM). 8K `.a0` autostart cartridge, verified in MAME (`vic20`). - **Sinclair ZX Spectrum** (`spectrum`) — 256×192, **two colours per 8×8 cell** (ink + paper) like C64 hires, with the Spectrum's quirk that a cell's two colours must share the BRIGHT bit (so the pair is chosen from one brightness group). Uses the same **dither-aware** segment search; defaults to **Atkinson** (lighter diffusion suits the attribute cells, as on the TI-99). Output is a 48K `.sna` snapshot that bakes the picture into screen RAM plus a 3-byte idle loop, so it appears instantly and holds — plus the standard 6912-byte `.scr` screen file alongside. Also a **mono** mode (crisp black/white halftone at 256×192, free of attribute clash, or a tinted ramp). Verified in MAME (`spectrum`). - **Atari 5200** (`a5200`) — the 5200 is an Atari 8-bit (ANTIC + GTIA, 6502) in a console, so it **reuses the Atari converters** unchanged: **GR.15** (160×192, 4 colours, dither-aware), **GR.8** (320×192 two-tone) and **GR.9** (80×192, 16 real luminance shades = the greyscale / tinted-mono mode). Having no OS, the self-contained 6502 viewer programs ANTIC/GTIA hardware directly (GTIA is at $C000 on the 5200) and ANTIC DMAs the bitmap + display list straight from the cartridge ROM — nothing is copied to RAM. All display durations are supported (hold forever, until a controller button, or for *N* seconds — timed off ANTIC's VCOUNT, input read from the POKEY keypad / GTIA triggers). 32 KB `.a52` cartridge, verified in MAME (`a5200`). - **Atari 7800** (`a7800`) — the 7800's **MARIA** display processor is nothing like ANTIC/GTIA (display-list-list → per-zone display lists → objects, 8 palettes of 3 colours + a shared background = 25 colours on screen). Its 160A mode is 2bpp though, the same packing as GR.15, so the Atari encoder helpers are reused. **c160** (160×192, **25 colours**) splits each line into objects and clusters the image's segments into 8 palettes so each region gets a tuned 4-colour set; **mono** restricts those palettes to one hue's luminances for a smooth greyscale. A self-contained 6502 viewer loads MARIA's colour registers and points it at the display-list list; MARIA DMAs the bitmap + display lists straight from the cartridge ROM. 48 KB `.a78` cartridge, verified in MAME (`a7800`). - **Commodore 128** (`c128`) — drives the **VDC 8563** 80-column chip, with three display modes that trade resolution against colour: - **mono** — **640×200** greyscale, the **highest resolution** of any target here. MAME's VDC has no true linear bitmap (its bitmap path emits only one bit per 8-pixel cell), so — like `hicolor` — this draws through a per-image custom character set, restricted to the VDC's four greys, for smooth multi-level greyscale. `--mono-base` tints the grey ramp toward a colour. - **hicolor** — **640×200** in colour via a per-image **custom character set**. Each 8×8 cell draws a custom glyph (full per-pixel detail) in its own **ink** colour (attribute RAM) over one **global background**, a ZX-Spectrum-like colour model at double the Spectrum's horizontal resolution. The ~2000 cell glyphs are vector-quantised to a **512-glyph** set (two VDC charset banks, the second selected per cell by the attribute's alternate-charset bit). - **color** — a chunky **80×100** image in all **16 VDC colours**, one free solid colour per 8×2 cell (full colour, or smooth greyscale on the four greys), the coarsest but most colourful option. The 8502 viewer banks the data in over the BASIC ROM (`$FF00`), programs the VDC, and copies into the VDC's own RAM with an explicit per-byte update address (MAME's 8563 corrupts the auto-increment stream). Delivered as an autobooting **`.d64`** that loads and runs with BASIC 7.0 `RUN"PIC"`, verified in MAME (`c128`). - **Commodore 16** (`c16`) — drives the **TED** (7360/8360) chip, whose palette is **121 colours** (8 luminance levels × 16 hues) — far richer than the VIC-II. **hires** is the TED's **320×200** bitmap: two colours per 8×8 cell, but each may be any of the 121 colours (vs the VIC-II's 16), so photos come out noticeably more colourful than C64 hires. The two per-cell colours are stored across the TED's two colour matrices (a hue matrix + a luminance matrix). A tiny 7501 viewer programs the TED registers; the whole picture (matrices + bitmap) loads into the C16's 16 KB RAM. Also a **mono** mode (the TED's neutral grey ramp — 8 luminances — for smooth greyscale, tintable via `--mono-base`). Delivered as a **`.prg`** (MAME quickload, or `LOAD`+`RUN` on real hardware), verified in MAME (`c16`). - **Commodore Plus/4** (`plus4`) — the C16's bigger sibling: same **TED** chip and BASIC 3.5, just 64 KB RAM instead of 16 KB. The TED's bitmap hardware is identical, so the Plus/4 uses the C16 encoder unchanged (same **hires** and **mono** modes, same 121-colour palette) and the same **`.prg`** is binary compatible across both machines. Verified in MAME (`plus4`). - **Amstrad CPC** (`cpc`) — Z80 + Gate Array, with a fixed 27-colour palette and three true bitmap modes (no per-cell colour limit). **mode0** (160×200) is the flagship photo mode: a flat **16-colour** palette chosen from the 27 — the encoder greedily picks the best 16 for each image, then dithers, giving clean, colourful results. **mode1** (320×200, 4 colours) trades colour for resolution; **mono** (mode 2, 640×200, 2 colours) is the highest-resolution black & white. Like the ZX Spectrum, it's delivered as a **`.sna`** snapshot that bakes the screen, palette, mode and an idle CPU, so the picture appears instantly with no loader. Verified in MAME (`cpc6128`). - **Tandy CoCo 3** (`coco3`) — the CoCo 2's big upgrade: the **GIME** chip with a **64-colour** palette and true bitmap modes (no per-cell colour limit). **gr16** (160×192) is the flagship: a flat **16-colour** palette picked from the 64 per image, then dithered — clean, colourful results. **gr4** (320×192, 4 colours) trades colour for resolution; **mono** (640×192, 2 colours) is the highest-res black & white. A 6809 viewer in a 16 KB Program Pak copies the image to RAM and programs the GIME (palette, mode, geometry, video base), then idles. Delivered as a **`.ccc`** cartridge, verified in MAME (`coco3`; the app forces the RGB monitor, since MAME defaults to the composite artifact palette). - **Nintendo NES / Famicom** (`nes`) — the 2C02 PPU is **tile-based** (not a bitmap), so this is the most constrained target: a 256×240 background built from a **32×30 grid of 8×8 tiles** (≤256 unique in CHR), coloured by **4 sub-palettes** (a shared universal background + 3 colours each) with one chosen per **16×16** region via the attribute table, all from the NES's 54-colour master palette. The encoder picks the universal bg, clusters the image into 4 sub-palettes, assigns each region its best one, dithers, then vector-quantises the tile patterns to 256 CHR tiles. **bg** is the colour mode; **mono** uses the PPU's grey ramp. A 6502 viewer programs the PPU (palette, nametable, attributes) and enables the background. Delivered as an **iNES `.nes`** (NROM) cartridge, verified in MAME (`nes`). The blockiness is the NES's inherent per-16×16 attribute-clash and 256-tile limits, not the encoder. - **Apple IIGS** (`iigs`) — its **Super Hi-Res** mode is the highest-fidelity target here: **320×200** with **16 colours per scanline**, each line choosing one of **16 palettes** of 16 colours drawn from a **4096-colour** master — so up to ~256 colours on screen, and photos come out near-photographic. **shr** is the colour mode (the encoder clusters the 200 lines into 16 palettes, assigns each line its best, and dithers); **mono** is a single 16-level grey palette (the smoothest greyscale in lenser). The picture data is the 32 KB SHR block; a small loader (6502 + a 65C816 block move into bank `$E1`) fills the SHR screen and turns it on. Delivered as a bootable **5.25" `.dsk`** (boots via slot 6 like an Apple II), verified in MAME (`apple2gs`). Note: the 32 KB loads off the emulated floppy, so the picture takes ~30 s to appear. - **Commodore PET / CBM** (`pet2001`, `pet4032`, `pet8032`, `superpet`) — the PET has no bitmap or colour at all, just a fixed-character monochrome text screen. Images are rendered with the PETSCII **2×2 quadrant-block** graphics characters (16 patterns, derived from the character ROM), giving a one-bit pseudo-bitmap of **80×50** on the 40-column models (`pet2001` = PET 2001 / CBM 3000, `pet4032` = PET 40xx / CBM 40xx) and **160×50** on the 80-column ones (`pet8032` = PET 80xx / CBM 80xx, `superpet` = SuperPET SP9000 / MMF 9000). The `mono` mode dithers to one bit and maps each 2×2 block to its quadrant character; a 6502 loader pokes the screen codes into screen RAM (`$8000`). Delivered as a **`.prg`** (quickload / `LOAD`+`RUN`), verified in MAME. (MAME's `superpet` driver is incomplete, so that platform targets the identical-display `cbm8032`.) Necessarily very low resolution — it's a text terminal — but the dithered portrait is recognisable. - **Sega Master System** (`sms`) — tile-based like the NES but far less constrained: each 8×8 tile is **4bpp (16 colours)** and picks one of **two 16-colour palettes** from a 64-colour master, so up to **32 colours** on screen at 256×192. The encoder builds palette 0 for the whole image and palette 1 for the colours it serves worst, assigns each tile its better palette, dithers, and vector-quantises the patterns to the 448 tiles that fit VRAM. **bg** is the colour mode (near-photographic — much better than the NES); **mono** uses the VDP's 4 true greys. A Z80 viewer programs the VDP and uploads the tiles/name-table/palette. Delivered as an iNES-free **`.sms`** cartridge (with a valid "TMR SEGA" header), verified in MAME (`sms`). - **Commodore Amiga** (`amiga`) — the 68000 graphics machine. **lowres** is the flagship: 320×200 with **32 colours from 4096** (a true flat-palette bitmap, no per-cell limit) — clean, near-photographic. **mono** uses the Amiga's 16 real grey levels. A 68000 boot block loads the bitplanes into chip RAM via the boot trackdisk request and points the Copper at them. Delivered as a bootable **`.adf`** floppy (valid boot-block checksum), verified in MAME (`a500`). (The Amiga's famous **HAM** 4096-colour mode was also implemented and looks superb, but MAME's preliminary Amiga can't render 6-bitplane/HAM modes cleanly — black bands at the screen edges — so only the MAME-verified 32-colour and mono modes ship.) ## ANSI / BBS art (CP437) Not a machine at all: `--platform ansi` (or the GUI **Platform** selector) emits a **`.ANS` text file** — CP437 characters plus ANSI colour escapes — for display on a BBS or in any ANSI art viewer. There's **no disk, no emulator, and no slideshow**; it's a single text artefact, and it's fully **previewable** in the GUI. Every cell picks its colours from the **16 EGA/VGA colours**, using **iCE colours** (a bright background via the blink bit) so photos have a full palette. Two encoders, chosen by the **Intensive** toggle: - **Full glyph** (Intensive on, the default) — matches every **8×16** character cell to the best of the *whole* CP437 repertoire (letters, punctuation, box-drawing and block glyphs) together with an optimal foreground/background colour pair, by minimum CIELAB error. Gradients turn into shade characters, so the result is far richer than blocks alone. A blue-noise pre-dither kills banding. - **Half-block** (Intensive off, fast) — every cell is the upper-half block `▀` with an independent foreground (top pixel) and background (bottom pixel), giving two freely-coloured pixel rows per text row with no cell-colour clash. Modes set the canvas: **`80x25`** (one classic screen), **`80x50`** (taller, more detail) and **`mono`** (a greyscale ramp, tintable toward any hue via the mono base control). The output is verified byte-faithful — an independent CP437 re-render of the `.ANS` reproduces the preview exactly. ```sh python -m lenser.cli photo.jpg --platform ansi -m 80x50 -o photo.ans ``` ## Requirements **Required** - **Python 3.9+**, with **`numpy`** and **`Pillow`**. That's all the ANSI/CP437 export needs. **Optional external tools.** These are located by name on your **`PATH`** — the app runs `xa`, `c1541`, `mame` as bare commands (via `shutil.which`), with no config file or environment-variable override. If a tool isn't on `PATH`, the feature that needs it is disabled (the GUI greys it out; the CLI raises a clear error naming the missing command). Install them so the command is on `PATH`, or symlink the binary into a `PATH` directory. - **`PyQt5`** — only for the GUI (`import`ed at GUI start-up); the CLI works without it. Install into the same Python environment you run lenser with. - **[`xa`](https://www.floodgap.com/retrotech/xa/) (xa65)** — the 6502 assembler that builds the on-machine viewers. Required to export for any real machine (C64, Atari, Apple, …); looked up as the command **`xa`** on `PATH`. Not needed for the ANSI export or for image previews. - **[VICE](https://vice-emu.sourceforge.io/)'s `c1541`** — builds the Commodore disk images (`.d64` / `.d71` / `.d81`, and the C128 `.d64`). Looked up as the command **`c1541`** on `PATH` (it ships with VICE). Only needed when exporting those Commodore disk formats. - **[MAME](https://www.mamedev.org/)** — powers the **Run in emulator** button for **every** platform (the C64 and Atari included; there is no VICE/atari800 runner). Looked up as the command **`mame`** on `PATH`. MAME finds its own system ROMs via its normal `rompath`; lenser only launches it. Purely optional — it's never needed to *produce* a disk or cartridge, only to preview one. - **[atari800](https://atari800.github.io/)** — *optional palette source only* (it is **not** used to run anything). If its bundled NTSC palette file is present, lenser reads it so the Atari preview matches the emulator exactly; otherwise a generated YIQ palette is used. It is searched for at these fixed paths (not on `PATH`), first match wins: - `/usr/share/atari800/Palettes/Real.act` - `/usr/share/atari800/default.pal` - `/usr/local/share/atari800/default.pal` On Debian/Ubuntu, installing the packages puts every command on `PATH`: ```sh sudo apt install python3-numpy python3-pil python3-pyqt5 xa65 vice mame atari800 ``` ## Usage ### GUI ```sh python -m lenser.gui # or: 8bitlenser ``` Open an image (via **Open image…** or by **dragging an image file onto the window**), pick a mode / disk format / dithering, watch the live C64 preview, then **Export**. ### Slideshow (multiple images on one disk) Tune an image, click **Add to slideshow**, repeat for as many images as you like (each keeps its own mode / dithering / palette / adjustments), then open **Slideshow…** to arrange the queue and **Export** one drive image. The dialog shows a **live storage meter** and refuses to export once the queue exceeds the chosen disk format's capacity (or its directory limit), so you always know how many more images fit. Choose how the viewer advances: on a **keypress**, automatically after **N seconds**, or **both** (keys still work, but it auto-advances on the timer), and whether it **loops**. On the C64 the exported disk boots with `LOAD"*",8,1` then `RUN` and steps through the pictures. On the **C64**, hires, multicolor and mono may be **mixed freely** in one slideshow; **FLI** and **interlace** are supported too, but as *uniform* shows (all-FLI or all-interlace) since each needs its own raster engine. The **Commodore 128** (640×200 VDC hicolor/mono, `.d64` booted with `RUN"PIC"`), the **Atari** (GR.15 / GR.9 / GR.8 / GR.15+DLI, self-booting `.atr` that SIO-loads each slide), the **BBC Micro** (single screen mode, DFS `.ssd` that `*LOAD`s each slide), the **Apple II** (HGR, self-booting `.dsk`; up to 4 images loaded into RAM at once), the **Apple IIgs** (Super Hi-Res, self-booting `.dsk` with a two-stage 65816 loader that banks each 32 KB image; up to 4), and the **Commodore Amiga** (lo-res/HAM, bootable `.adf` whose 68000 boot block loads every image into chip RAM and cycles them) are also supported — **all seven disk platforms**. Headless equivalent — a JSON manifest fed to `8bitlenser-slideshow`: ```sh python -m lenser.slideshow_cli show.json -o show.d64 ``` where `show.json` lists the global options (`platform`, `format`, `advance`, `seconds`, `loop`) and an `items` array, each item an `image` path plus any per-image `mode` / `dither` / `palette` / `brightness` / `contrast` / `tint` / … overrides. ### Command line ```sh # Multicolor picture onto a .d64, plus a preview PNG of how it will look: python -m lenser.cli photo.jpg -m multicolor -o photo.d64 --preview photo.png # Let the tool pick the best standard mode, write a .d81: python -m lenser.cli photo.jpg -m auto -o photo.d81 # Best quality (slower) FLI with error-diffusion dithering: python -m lenser.cli photo.jpg -m fli -d floyd --intensive -o photo.d64 # Atari: 16-shade greyscale, and a 4-colour-per-line image: python -m lenser.cli photo.jpg --platform atari -m gr9 -d floyd -o photo.atr python -m lenser.cli photo.jpg --platform atari -m gr15dli -o photo.atr # High-res greyscale with smooth Stucki dithering: python -m lenser.cli photo.jpg -m mono -d stucki -o photo.d64 # ...or tinted monochrome in blue: python -m lenser.cli photo.jpg -m mono --mono-base blue -d jarvis -o photo.d64 ``` Options: `-m/--mode {auto,hires,multicolor,fli,interlace,mono}`, `-f/--format {d64,d71,d81}`, `-p/--palette {colodore,pepto}`, `-d/--dither {bayer,bluenoise,yliluoma,floyd,atkinson,stucki,jarvis,sierra,sierra_lite,burkes,riemersma,ostromoukhov,none}`, `--mono-base {grayscale,}`, `--video {pal,ntsc}`, `-a/--aspect {fit,fill,stretch}`, `--intensive`, `--brightness/--contrast/--saturation/--gamma`, `--preview`. ### On the C64 / in an emulator Attach the disk to drive 8 and: ``` LOAD"*",8,1 RUN ``` Press any key to return to BASIC (hires/multicolor). For FLI/interlace, reset to exit. ## Notes on the advanced modes - **FLI** is timing-critical: the viewer runs a cycle-stable raster loop. Expect a small settling artifact in the top rows (a well-known FLI characteristic) and the leftmost few pixels reserved by the hardware "FLI bug". - **Interlace** flickers at 25 Hz on a CRT; it looks best in an emulator or on an LCD. - Multicolor and Hires are the universally safe, flicker-free choices. ## How conversion works `lenser/convert/` holds one encoder per mode on top of a shared core (`convert/base.py` + `dither.py`). The pipeline: prepare & resize the image to the mode's pixel grid (`imageprep.py`), convert to CIELAB (`palette.py`), choose each cell's legal colour set by exhaustive search, dither within those sets, then pack the VIC-II bytes. `viewer/*.s` are the 6502 viewers (assembled by `xa`), combined with the picture data and written to a disk image by `diskimage.py` / `exporter.py`.