Skip to content

About

Browser-based image-to-PXC converter for CrossPoint e-reader wallpapers. Supports Floyd-Steinberg, Atkinson, Jarvis, Stucki, Burkes, Bayer and Zhou-Fang dithering. No upload — runs entirely in your browser.

Topics

Resources

Stars

17 stars

Watchers

0 watching

Forks

Repository files navigation

Crosspoint Wallpaper Creator

crosspoint-pxc-converter.pages.dev

A browser-based creator that turns images and Game Boy 2BPP captures into sleep-screen wallpapers for the CrossPoint e-reader firmware running on XTeink devices. Inputs go in as PNG, JPG, WebP, BMP, GIF, .2bpp, .bin, .gb, or GB-Printer .txt logs. The current user-facing export is a native-palette .bmp (4-bit indexed); the PXC encoder remains internal. Every step runs locally in the browser — no upload, no server.


Devices

Device Resolution
X4 480×800
X3 528×792

The selected device controls the output canvas size for both export formats.


PXC format

PXC is CrossPoint's native wallpaper format. It stores 2 bits per pixel — four grey levels mapped one-to-one to the e-ink display's physical states.

Value Display level sRGB
0 Black 0
1 Dark grey 85
2 Light grey 170
3 White 255

File layout

Bytes 0–1   Width  (uint16 LE)
Bytes 2–3   Height (uint16 LE)
Bytes 4+    Pixel data — 2 bits per pixel, MSB first, row-major.
            Each byte holds 4 pixels: [p0 p1 p2 p3] in bits [7:6 5:4 3:2 1:0].
            Each row is ceil(width / 4) bytes; rows are not packed across
            boundaries.
Device File size
X4 96,004 bytes
X3 104,548 bytes

BMP format

The BMP export uses BMP3 (BITMAPINFOHEADER), 4-bit indexed colour, with a 4-entry palette.

  • Image mode — greyscale palette #000000 #555555 #AAAAAA #FFFFFF, matching the four e-ink levels and ImageMagick's -colorspace Gray -dither FloydSteinberg -remap palette.png -define bmp:format=bmp3 -type Palette pipeline.
  • Game Boy mode — palette pulled from the selected colour scheme (DMG, Pocket, B&W, or SGB) so the BMP previews correctly in any colour-aware viewer.

Where to put the file on the device

Path on SD card Effect
/sleep.pxc Single static sleep screen (PXC)
/sleep.bmp Single static sleep screen (BMP)
/.sleep/name.pxc Member of the rotating wallpaper pool
/.sleep/name.bmp Member of the rotating wallpaper pool

CrossPoint picks at random from /.sleep/ when multiple files are present. PXC and BMP files mix freely in the same pool.


Features

Input

  • Drag-and-drop, click-to-browse, or paste from the clipboard (Ctrl+V / Cmd+V).
  • The loader inspects the file extension and routes automatically:
    • .png, .jpg, .jpeg, .webp, .bmp, .gif → image mode.
    • .2bpp, .bin, .gb → Game Boy binary mode.
    • .txt (or pasted text) → Game Boy Printer log parser.
  • Bad inputs (decode failure, malformed GB byte stream, unrecognised text payload) surface as an inline status banner.

Source editor

  • Crop / Fit modes — crop selects a viewport that fills the device exactly; fit letterboxes the whole source. The crop window snaps when its centre aligns with the source centre.
  • Fit alignment — 3×3 grid (top-left through bottom-right) controls placement of the letterboxed image. The unused area fills with the chosen background colour (white or black).
  • Rotation — 90° clockwise / counter-clockwise steps.
  • Mirror — independent horizontal and vertical flips of the current view (H always flips left-right on screen, V always flips top-bottom, even when the image is rotated 90°/270°). The crop selection is preserved across rotation and mirror operations.
  • Zoom — continuous slider from 1× to a per-image maximum (capped so the source never upscales beyond its native pixels), plus mouse-wheel zoom anchored at the cursor.
  • Scrollable viewport — the source panel pans when the zoomed image exceeds its frame; scroll position is preserved during zoom changes.
  • Source label — displays the loaded asset's natural pixel dimensions, e.g. Source · 1920×1080 — drag or click to reposition.

Scaling

Downscaling runs Lanczos3 through pica. For sources more than 2× the device target, the pipeline halves the image with high-quality bilinear smoothing until it lands within 2× of the target, then performs a single Lanczos3 pass to land exactly on the target dimensions. The same scaling path drives the editor preview and the export, so what shows on screen reflects the final output.

Tone pipeline

Controls run in this processing order: black/white-point map → gamma → contrast → invert → dither.

Control Description
Black / White points 0–255 sliders that linearly remap the input range. Each clamps against the other so they cannot cross.
Gamma 0.30–5.00, default 1.00. Applies Vout = Vin^(1/γ) after the black/white remap. γ > 1 lifts shadows (brighter, more shadow detail); γ < 1 compresses shadows (darker, more highlight detail).
Contrast ±100 linear adjustment, pivoting at midpoint (128).
Invert Inverts luminance after tone mapping, before dithering.
Auto Sets black and white points from histogram percentile clipping; resets gamma to 1.00. Once enabled, re-runs automatically (debounced) when the crop region changes — rotation, mirror, zoom, or drag — so cropping into a darker or lighter area doesn't strand the levels at stale values. Moving any tone slider manually clears the auto-levels lock.
Reset all Restores black, white, and gamma to defaults.

Dithering

Every error-diffusion algorithm uses BT.601 luminance in sRGB space. The default PR1614 profile uses evenly spaced thresholds 43 / 128 / 213 and the native display levels [0, 85, 170, 255]. The alternate CrossPoint calibration remains available through the advanced preset toggle (Ctrl/Cmd + Shift + Q): dither thresholds 30 / 55 / 150, reconstruction levels [15, 35, 90, 210], and dither-off thresholds 45 / 70 / 140, matching the legacy branch in CrossPoint 1.6.5. These profiles control browser-side shade selection; both export the same native grayscale BMP palette.

Algorithm Notes
Zhou-Fang Default. JJN kernel with serpentine scanning and intensity-dependent coefficient + threshold modulation (Zhou & Fang, SIGGRAPH 2003). Eliminates the "worm" artifacts of single-direction JJN. Strong choice for photographic content on e-ink.
Floyd-Steinberg Classic 4-neighbour kernel (7/5/3/1 ÷ 16). Fast and balanced; the textbook reference for error diffusion.
Atkinson Distributes only 6/8 of the error across 6 neighbours. Preserves highlights, produces lighter results — popular for manga.
Jarvis (JJN) 3-row, 12-neighbour kernel (÷ 48). Wider error spread, less banding, softer edges.
Stucki JJN-family weights (÷ 42). Slightly sharper than Jarvis.
Burkes 2-row Stucki (÷ 32). Faster than the 3-row variants while staying sharp.
Bayer 4×4 ordered (threshold) dithering. No error bleeding; produces a regular crosshatch suited to flat illustration with hard edges.
Blue Noise 64×64 pre-computed blue-noise threshold matrix. Ordered like Bayer but with high-frequency noise distribution; clean look on photographic content without the directional bias of error diffusion.

A toggle disables dithering altogether for hard-quantised output.

Histogram

A live tone-distribution panel renders alongside the preview:

  • 256-bin luminance histogram of the post-tone pre-dither buffer.
  • Four coloured zones corresponding to the four output levels, each labelled with the percentage of pixels that fall into it.
  • A solid palette colour strip below for unambiguous level identification.
  • Threshold markers follow the active quantization profile. The default PR1614 markers are 43, 128, and 213.

Game Boy mode

Game Boy binaries (.2bpp, .bin, .gb) and GB-Printer text logs (.txt files or pasted text) decode through a 2-bits-per-pixel tile reader at 20 tiles wide.

Control Description
Output scale Integer pixel-perfect multiplier (1×, 2×, 3×, …) up to the maximum that still fits inside the device target. The scaled tile art centres on the canvas.
Background White or black fill behind the centred tile art.
Source preview palette Source-only colour scheme: DMG (green), Pocket (sepia/grey), B&W (greyscale), SGB (purple/orange). The output preview and BMP export always use native greyscale, regardless of this selection.
Invert Flips GB colour indices (0↔3, 1↔2) before conversion.
Rotation 90° clockwise / counter-clockwise.

The GB-Printer text-log parser reads hex byte lines and extracts the PRNT palette register, applying the captured grey levels automatically. The GB Printer palette readout reports the register value and the per-shade mapping in the file-info panel.

GB index → native grayscale level

GB index Meaning Output level
0 Lightest 3 (white)
1 Light 2 (light grey)
2 Dark 1 (dark grey)
3 Darkest 0 (black)

Preview

  • The output preview canvas updates live on every control change.
  • A 3.6× zoom loupe follows the cursor over the preview for pixel-level inspection.

Export

  • Download native-palette BMP — 4-bit indexed BMP3, using one shared encoder for both image and Game Boy modes. The four palette entries are exactly [0, 85, 170, 255], so CrossPoint recognizes the native shades and bypasses on-device dithering. This is the only user-facing export for now.
  • On the reader, use Sleep Screen Cover Filter → No Filter to retain all four grayscale shades. Select the matching X3/X4 size before downloading.
  • The CrossPoint-native PXC encoder remains internal; there is no PXC download control or event binding in the UI.

Filenames are tagged with the dither method (image mode) and target device, so multiple variants of the same source don't collide:

{baseName}[-{ditherCode}]-{DEVICE}.bmp
  • baseName — the source filename without its extension (defaults to sleep before any file is loaded).
  • ditherCode — image-mode short code: fs, atk, jjn, stk, bks, bay, zf (Zhou-Fang, default), bn (Blue Noise). Omitted when dither is off or the source is a GB capture.
  • DEVICE — X3 or X4, matching the device pill.

Examples: earth-zf-X4.bmp, earth-atk-X3.bmp, mario-X4.bmp (GB), earth-X4.bmp (image, dither off).


Development

npm install
npm run dev      # local dev server
npm run build    # type-check and produce static dist/
npm run test     # vitest run

The build output in dist/ is plain static files — HTML, JS chunks, CSS, and a worker bundle.

The std-env dependency is pinned to compatible version 4.2.0 because the registry advertised 4.3.0 but its archive returned HTTP 404 during the September 2026 hardening update. Remove the override once the archive is available and the clean install, tests, and build pass.

The production build uses relative asset URLs, so dist/ can be mounted directly under a CrossPoint Reader tools route (for example /tools/wallpaper/) without recompiling for that pathname. The page uses the same Inter/Lora typography, brand-green and stone palette, controls, cards, and site navigation treatment as crosspointreader.com.

Hosting requirements

The dither pipeline runs in a Web Worker. It uses SharedArrayBuffer when available and automatically falls back to a transferable ArrayBuffer on ordinary hosts. These headers enable the shared-memory path:

  • Cross-Origin-Opener-Policy: same-origin
  • Cross-Origin-Embedder-Policy: require-corp

public/_headers ships the correct values for hosts that read that file format. On other hosts, configure equivalent response-header rules if you want shared memory; conversion also works without them. Serve the build over HTTP(S), rather than opening index.html as a local file, so browser modules and workers can load.


Architecture

The codebase is plain DOM plus a hand-rolled flux-style store, organised into a strict one-directional layer chain (each layer may only import from layers further left):

domain  ←  infra  ←  app  ←  features  ←  ui
  • src/domain/ — pure logic with no DOM: geometry, tone, dithering, histogram, format encoders, GB decoders + helpers.
  • src/infra/ — browser, canvas, worker, and file-IO adapters.
  • src/app/ — store, reducer, runtime containers, top-level orchestration, loader routing, shared cleanup helpers.
  • src/features/{image,gb}/ — feature controllers and helpers wired through deps; image and GB never import each other.
  • src/ui/ — DOM lookup, store-driven render, event bindings, crop interaction, preview loupe.

The dither / tone pipeline runs in a Web Worker with shared or transferable buffers (see Hosting requirements). Session and processing counters reject obsolete file loads and conversions. Changes made during resizing remain pending until processed, and downloads stay disabled until the latest output is ready.

code-map.md carries the full architectural contract: layer rules, the single-source-of-truth registry that prevents parallel implementations, fluidity hot paths that must stay sub-frame, and runtime-object documentation. Read it before non-trivial changes.


Related


Author

Built by zgredex · Discord Megabit_Omega

About

Browser-based image-to-PXC converter for CrossPoint e-reader wallpapers. Supports Floyd-Steinberg, Atkinson, Jarvis, Stucki, Burkes, Bayer and Zhou-Fang dithering. No upload — runs entirely in your browser.

Topics

Resources

Stars

17 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages