Files
pbio/README.md
irrlicht c5a4d0026b 16-Bit-Truecolor (RGB565) statt 8-Bit-Palette
Framebuffer ist jetzt &mut [u16], jeder Wert ein RGB565-Pixel. Der
Shader dekodiert die Bitfelder direkt aus einer R16Uint-Textur; die
Palette (Textur, API, Presets, Tooling) entfaellt. Neu: pbio::rgb565()
zum Packen von 8-Bit-Kanaelen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:04:05 +02:00

4.3 KiB
Raw Permalink Blame History

pbio: simple pixel buffer and I/O system — 16-Bit-Truecolor-Variante

Pixelbuffer-basiertes Fenster mit RGB565-Truecolor-Renderer (winit + wgpu) und Maus/Tastatureingaben.

pbio nimmt ein u16-Array, interpretiert jeden Wert als RGB565-Pixel (Bits RRRRRGGGGGGBBBBB) und rendert ihn auf ein Fenster. Optional mit Letterbox für ein festes Seitenverhältnis.

Dies ist der Branch truecolor16. Auf master liegt die 8-Bit-Variante mit Palette-Lookup — beide sind unabhängig voneinander nutzbar.

Integration

pbio ist nicht auf crates.io veröffentlicht. Das Crate kann direkt aus dem Git-Repository eingebunden werden:

[dependencies]
pbio = { git = "https://codeberg.org/irrlicht/rust-pbio.git", branch = "truecolor16" }

pbio braucht keine System-Libraries zum Bauen — winit lädt X11/Wayland zur Laufzeit per dlopen. Zur Laufzeit reichen aktuelle Grafiktreiber mit Vulkan-, Metal- oder DX12-Unterstützung für wgpu.

Verwendung

use pbio::{Event, Key, Platform, PlatformConfig};

fn main() {
    let mut p = Platform::new(PlatformConfig {
        title:            "Demo".into(),
        window_size:      (1024, 768),
        framebuffer_size: (320, 240),
        ..Default::default()
    });

    while !p.should_close() {
        p.poll_events(None);

        while let Some(ev) = p.poll_event() {
            if let Event::Key { key: Key::Escape, pressed: true, .. } = ev {
                p.request_close();
            }
        }

        let (w, _h) = p.framebuffer_size();
        let fb = p.framebuffer_mut();
        for (i, px) in fb.iter_mut().enumerate() {
            let v = ((i as u32 % w) ^ (i as u32 / w)) as u8;
            *px = pbio::rgb565(v, v, v);
        }

        p.present();
    }
}

API-Überblick

  • PlatformConfig Titel, Fenster-/Framebuffer-Größe, optionales aspect_ratio (Letterbox), VSync, mouse_visible, mouse_capture. Default = 800×600 Fenster, 320×240 Framebuffer, 4:3.
  • Platform::new(cfg) Fenster + GPU-Pipeline aufsetzen.
  • poll_events(timeout) winit-Events einlesen (None = blockierend).
  • poll_event() -> Option<Event> ein Event aus der Queue holen. Gibt &mut self nach jedem Call frei, sodass im Loop direkt auf die Platform reagiert werden kann (request_close, set_mouse_capture …). Empfohlen für die normale Event-Schleife.
  • drain_events() Iterator über Event (Key, Char, MouseMove, MouseBtn, Resize, CloseRequested). Borrowt &mut self bis der Iterator fertig ist — Reaktionen auf die Platform müssen dann nach dem Loop angewandt werden. Nützlich für reines Loggen/Filtern.
  • framebuffer_mut() &mut [u16] der Länge w*h, je Pixel ein RGB565-Wert.
  • rgb565(r, g, b) -> u16 packt 8-Bit-RGB in einen RGB565-Pixelwert (const fn).
  • present() Frame auf das Fenster bringen.
  • should_close() / request_close().
  • set_mouse_visible(bool) Cursor anzeigen oder verstecken (freie Bewegung).
  • set_mouse_capture(bool) Cursor einsperren + verstecken (winit CursorGrabMode::Locked, Fallback Confined), liefert unbegrenzte Bewegungsdeltas via mouse_delta(). Impliziert mouse_visible = false. Im Capture-Mode ist Event::MouseMove nicht sinnvoll (eingefrorene oder am Fensterrand klemmende Werte) — stattdessen mouse_delta() benutzen.
  • mouse_delta() -> (f32, f32) akkumulierte Mausbewegung seit dem letzten Aufruf in Framebuffer-Pixeln (gleiche Skala wie MouseMove), danach genullt. Nützlich für FPS-Kamerasteuerung.

Cursor-Modi

mouse_visible mouse_capture winit-Modus Einsatz
true false sichtbar, frei Standard-UI
false false versteckt, frei Eigener Cursor im Framebuffer
* true Locked / Confined, versteckt FPS-Kamera, unbegrenzte Deltas
// FPS-Kamera
p.set_mouse_capture(true);
let (dx, dy) = p.mouse_delta();
yaw   += dx * sensitivity;
pitch += dy * sensitivity;

Pixelformat

RGB565: 5 Bit Rot, 6 Bit Grün, 5 Bit Blau in einem u16 (Bits RRRRRGGGGGGBBBBB). Der Shader dekodiert die Bitfelder direkt — es gibt keine Palette. Zum Packen von 8-Bit-Kanälen dient pbio::rgb565:

let orange = pbio::rgb565(255, 160, 0);
p.framebuffer_mut()[0] = orange;