The book is currently only available in German.
Standard-Bibliothek — `bild`
Verfügbar ab Kiste 0.9.4. Vollständige Spec in
docs/bild-modul.md.
Das bild-Modul arbeitet mit Rasterbildern: ein Bild im Speicher erzeugen, Pixel oder ganze Blöcke einfärben, bestehende Bilder laden und in den gängigen Formaten speichern. Ein bild ist ein Handle (eine ganz-Zahl, die auf das Bild im Speicher verweist) — genau wie ein Widget im gui-Modul (Kapitel 34).
bild liest und schreibt Dateien, braucht also ein Dateisystem: es läuft in der IDE (F5), über kiste run und kiste build — aber nicht im Browser-Playground.
35.1 Ein Bild erzeugen und speichern
nutze bild
nimm b = bild.neu(16, 16) // neues Bild, 16x16 Pixel (zunächst transparent)
bild.setze_pixel(b, 0, 0, 255, 0, 0) // oben links: rot
bild.setze_pixel(b, 1, 1, 0, 200, 0) // grün
bild.schreibe_png(b, "mein_bild.png") // als PNG-Datei speichern
Eine Farbe gibst du als drei Zahlen Rot, Grün, Blau an — jeweils von 0 bis 255. setze_pixel malt einen deckenden Pixel.
35.2 Ganze Blöcke füllen
Statt Pixel für Pixel kannst du ein ganzes Rechteck einfärben — praktisch, um ein kleines Raster zu einem größeren Bild aufzuziehen:
nimm b = bild.neu(64, 64)
// x, y, Breite, Höhe, dann r, g, b:
bild.setze_block(b, 0, 0, 64, 64, 240, 240, 240) // heller Hintergrund
bild.setze_block(b, 16, 16, 32, 32, 59, 130, 246) // blaues Quadrat in der Mitte
bild.schreibe_png(b, "block.png")
Ein ganzes Bild in einem Rutsch: Wenn du ein großes Bild pixelweise rechnest, ist ein Aufruf pro Pixel langsam. bild.setze_puffer schreibt ein komplettes Bild in einem Aufruf. Du gibst eine Liste mit einer Zahl pro Pixel an — die drei Farbkanäle in EINER Zahl gepackt: r*65536 + g*256 + b. Die Liste ist zeilenweise geordnet (erst die oberste Zeile von links nach rechts, dann die nächste) und muss genau Breite·Höhe Einträge haben:
nutze liste
nimm b = bild.neu(3, 2) // 3 breit, 2 hoch = 6 Pixel
nimm puf = liste.gefüllt(6, 0)
puf[0] = 255 * 65536 + 0 * 256 + 0 // (0,0) rot
puf[1] = 0 * 65536 + 255 * 256 + 0 // (1,0) grün
puf[2] = 0 * 65536 + 0 * 256 + 255 // (2,0) blau
// puf[3..5] = zweite Zeile …
bild.setze_puffer(b, puf) // alle 6 Pixel auf einmal
Bei großen Bildern ist das um ein Vielfaches schneller als setze_block pro Pixel — der Pixel-Editor KistePix zeichnet damit auch riesige Leinwände flüssig.
35.3 Bilder laden und Pixel lesen
bild.lade erkennt das Format automatisch an der Datei (PNG, JPEG, GIF, BMP, TIFF, WEBP) und gibt ein Bild zurück:
nimm foto = bild.lade("foto.jpg")
sag bild.breite(foto) // Breite in Pixeln
sag bild.höhe(foto) // Höhe in Pixeln
nimm p = bild.hole_pixel(foto, 0, 0) // Farbe an (0,0) als Liste [r, g, b, a]
sag p[0] // Rot-Anteil (0-255)
sag p[3] // Alpha: 255 = deckend, 0 = durchsichtig
hole_pixel gibt vier Werte zurück: Rot, Grün, Blau und Alpha (Deckkraft).
Ein ganzes Bild in einem Rutsch lesen: So wie bild.setze_puffer schreibt, liest
bild.hole_puffer ALLE Pixel in einem Aufruf — viel schneller als Breite·Höhe
einzelne hole_pixel-Aufrufe, wenn du ein großes Bild durchgehst. Du bekommst eine Liste mit
einer Zahl pro Pixel (zeilenweise), in der die vier Kanäle gepackt sind:
r*16777216 + g*65536 + b*256 + a. Entpacken geht mit div und %:
nimm foto = bild.lade("bild.png")
nimm puf = bild.hole_puffer(foto) // Breite·Höhe Einträge
nimm p = puf // in eine lokale Variable → schnelle Indexierung
nimm v = p[0] // erstes Pixel (oben links), gepackt
nimm r = (v div 16777216) % 256 // Rot
nimm g = (v div 65536) % 256 // Grün
nimm b = (v div 256) % 256 // Blau
nimm a = v % 256 // Alpha
Tipp: Bind die Puffer-Liste erst an eine lokale Variable (
nimm p = puf) und lies dannp[i]— das ist deutlich schneller, als in einer engen Schleifebild.hole_pixelpro Pixel aufzurufen.
35.4 Speicher-Formate
| Funktion | Format | Gut für |
|---|---|---|
bild.schreibe_png(b, pfad) |
PNG | verlustfrei — scharfe Kanten, Pixel-Kunst, Transparenz |
bild.schreibe_jpeg(b, pfad, güte) |
JPEG | Fotos; güte 1–100 (verlustbehaftet, keine Transparenz) |
bild.schreibe_gif(b, pfad) |
GIF | wenige Farben; auch animiert (siehe unten) |
bild.schreibe_bmp(b, pfad) |
BMP | einfaches, unkomprimiertes Format |
bild.schreibe_tiff(b, pfad) |
TIFF | verlustfrei, im Druck/Archiv verbreitet |
bild.schreibe_jpeg(b, "foto.jpg", 90) // Güte 90 von 100
Faustregel: Für Pixel-Kunst und alles mit klaren Farbflächen ist PNG die richtige Wahl (verlustfrei, kann durchsichtig). JPEG lohnt sich nur für Fotos.
35.5 Transparenz
Ein frisches Bild von bild.neu ist komplett durchsichtig. Was du nicht bemalst, bleibt transparent — so bekommst du Sprites ohne Hintergrund:
nimm sprite = bild.neu(8, 8)
bild.setze_pixel(sprite, 4, 4, 255, 200, 0) // nur ein Punkt
bild.schreibe_png(sprite, "sprite.png") // Rest der 8x8 bleibt durchsichtig
Willst du einen deckenden Hintergrund, füll ihn einfach vorher mit setze_block.
Ein Bild auf ein anderes zeichnen — bild.zeichne_auf(ziel, quelle, x, y) kopiert quelle bei (x,y) aufs ziel und erhält dabei die Transparenz. So setzt man mehrere Sprites zu einem Bild zusammen, z. B. zu einem Sprite-Sheet:
nimm sprite_a = bild.neu(16, 16)
bild.setze_block(sprite_a, 0, 0, 16, 16, 255, 200, 0) // gelb
nimm sprite_b = bild.neu(16, 16)
bild.setze_block(sprite_b, 0, 0, 16, 16, 59, 130, 246) // blau
nimm sheet = bild.neu(32, 16) // transparente Fläche für zwei 16×16-Sprites
bild.zeichne_auf(sheet, sprite_a, 0, 0) // linke Zelle
bild.zeichne_auf(sheet, sprite_b, 16, 0) // rechte Zelle
bild.schreibe_png(sheet, "sheet.png")
Ragt die Quelle über den Rand des Ziels hinaus, wird sie sauber abgeschnitten.
35.6 Animiertes GIF
Aus einer Liste von Bildern wird ein animiertes GIF. Die Verzögerung gilt je Bild und zählt in Hundertstelsekunden (10 = 0,1 s):
nimm a = bild.neu(16, 16)
bild.setze_block(a, 0, 0, 16, 16, 239, 68, 68) // rotes Bild
nimm b = bild.neu(16, 16)
bild.setze_block(b, 0, 0, 16, 16, 59, 130, 246) // blaues Bild
bild.schreibe_gif_animiert("blinken.gif", [a, b], 50) // wechselt alle 0,5 s
Soll jedes Bild seine eigene Dauer haben (unterschiedlich lange Frames), gibst du statt einer Zahl eine zweite Liste mit — eine Dauer je Bild, ebenfalls in Hundertstelsekunden:
bild.schreibe_gif_animiert_dauern("lauf.gif", [a, b], [10, 50]) // a: 0,1 s, b: 0,5 s
Die beiden Listen müssen gleich viele Einträge haben. So exportiert man z. B. eine Zeitleiste mit unterschiedlich langen Frames.
Zwei Dinge nehmen dir beide Funktionen ab: das GIF läuft endlos in der Schleife, und vor jedem Bild wird die Fläche wieder transparent geräumt — Bilder mit durchsichtigen Rändern überlagern sich also nicht, und nichts geistert vom vorigen Bild nach. Die Bilderliste muss übrigens kein festes [a, b] sein: du darfst sie auch Stück für Stück aufbauen und dann übergeben — praktisch, wenn du erst zur Laufzeit weißt, welche Bilder hineingehören:
nimm bilder = []
liste.hänge_an(bilder, a)
liste.hänge_an(bilder, b)
bild.schreibe_gif_animiert("blinken.gif", bilder, 50)
35.7 Praxis: vom Modell zur Bilddatei
Oft liegt ein Bild nicht als Pixel vor, sondern als Modell — zum Beispiel eine flache Liste von Paletten-Indizes plus eine Palette aus Hex-Farben (so macht es der Pixel-Editor KistePix). Der Weg zur Datei: für jede Zelle die Hex-Farbe in drei Zahlen verwandeln (mit dem bytes-Modul, Kapitel 30) und als Block malen.
nutze bild
nutze bytes
nutze text
nimm PALETTE = ["#ffffff", "#ef4444", "#3b82f6"]
nimm zellen = [0, 1, 0, 1, 2, 1, 0, 1, 0] // 3x3 "Bild" als Indizes
nimm RASTER = 3
nimm ZOOM = 16 // jede Zelle wird 16x16 Pixel
funktion nach_rgb(hex) {
nimm ohne = text.ersetzt(hex, "#", "") // "#ef4444" -> "ef4444"
gib bytes.als_ganze(bytes.aus_hex(ohne)) // -> [239, 68, 68]
}
nimm img = bild.neu(RASTER * ZOOM, RASTER * ZOOM)
für y = 0 bis RASTER - 1 {
für x = 0 bis RASTER - 1 {
nimm rgb = nach_rgb(PALETTE[zellen[y * RASTER + x]])
bild.setze_block(img, x * ZOOM, y * ZOOM, ZOOM, ZOOM, rgb[0], rgb[1], rgb[2])
}
}
bild.schreibe_png(img, "muster.png")
Genau diesen Weg baust du im KistePix-Buch Schritt für Schritt als Export-Funktion.
35.8 Speichern-Dialog
Wenn der Nutzer den Dateinamen selbst wählen soll, kombinierst du bild mit dem gui-Modul (Kapitel 34): gui.dialog_speichern öffnet einen „Speichern unter"-Dialog (unter Windows der echte Betriebssystem-Dialog), und gui.gewählter_pfad liefert den gewählten Pfad, den du an bild.schreibe_png gibst.
35.9 Was bild (noch) nicht kann
- WEBP schreiben — WEBP lässt sich laden, aber (noch) nicht speichern (das bräuchte eine externe C-Bibliothek). Für verlustfreies Speichern nimm PNG.
- SVG — ist ein Vektorformat (Linien und Flächen statt Pixeln) und gehört nicht in ein Rasterbild-Modul.
- Zeichnen von Linien/Kreisen ins Bild — dafür ist die
gui-Leinwand da;bildsetzt Pixel und Blöcke.