Kreslení napřímo
C modul picogame je i obyčejná kreslicí knihovna. Canvas kreslí tvary, bitmapy a text do
libovolného RGB565 bufferu a Bitmap obalí libovolný buffer pixelů, aniž by ho kopírovala.
Sprity a Scene jsou postavené nad tím a používat je nemusíš.
Dva druhy obrazovky
Sekce “Dva druhy obrazovky”Jak kreslit, záleží na tom, kde leží pixely obrazovky.
- Desky s framebufferem (Fruit Jam a další DVI desky): celá obrazovka je buffer v RAM.
Položíš na něj
Canvasa kreslíš, stejně jako v SDL nebo Pygame. Všechno pod dalším nadpisem platí pro tyhle desky. - SPI displeje (PicoPad a většina desek s malým displejem): pixely jsou v displeji a na kopii
není místo - snímek 320×240 v RGB565 má 150 KB a hra na RP2040 má volných 25-40 KB RAM.
picogame tam celý snímek nikdy nedrží; vykresluje po několika řádcích a posílá je do displeje.
Kreslit můžeš pořád napřímo, ale uvnitř callbacku
StripDraw, který se volá pro každý pruh.
Otevření obrazovky
Sekce “Otevření obrazovky”import boardimport displayioimport picodviimport picogame as pg
displayio.release_displays()framebuffer = picodvi.Framebuffer(320, 240, color_depth=16, clk_dp=board.CKP, clk_dn=board.CKN, red_dp=board.D0P, red_dn=board.D0N, green_dp=board.D1P, green_dn=board.D1N, blue_dp=board.D2P, blue_dn=board.D2N)screen = pg.Canvas(320, 240, buffer=framebuffer)
def color(r, g, b): """RGB565 v pořadí bajtů, které čte picodvi.""" return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)screen teď kreslí do paměti, kterou DVI výstup zobrazuje. Žádné volání refresh není potřeba:
co zapíšeš, je na obrazovce.
Pro všechny barvy na téhle obrazovce, včetně palet, používej color(). pg.rgb565() vrací
pořadí bajtů pro SPI displeje, takže na DVI obrazovce by se z červené stala modrá.
Tvary
Sekce “Tvary”screen.clear(color(0, 0, 40))screen.fill_rect(200, 10, 50, 30, color(255, 0, 0))screen.fill_circle(290, 25, 15, color(0, 255, 0))screen.line(200, 50, 310, 50, color(255, 255, 255))Canvas má dále pixel, rect, circle, ring, triangle, fill_triangle, ellipse,
fill_ellipse, fill_round_rect a frame3d, plus dávková volání pro mnoho trojúhelníků nebo
úseků najednou. Úplný seznam je v referenci.
Jednotlivé pixely můžeš zapisovat i přímo do bufferu: memoryview(framebuffer) má jednu položku
na pixel, takže pixels[y * 320 + x] = color(255, 255, 255).
Obrázky ze souboru
Sekce “Obrázky ze souboru”Soubor přečte adafruit_imageload. pg.Bitmap pak používá tytéž pixely, převádí se jen paleta:
import arrayimport adafruit_imageload
def load_image(path): """Načte 8bitový (indexovaný) BMP jako picogame Bitmap.""" bitmap, palette = adafruit_imageload.load( path, bitmap=lambda width, height, colors: displayio.Bitmap(width, height, 256), palette=displayio.Palette) colors = array.array("H", [0] * 256) for index in range(len(palette)): rgb = palette[index] colors[index] = color(rgb >> 16, (rgb >> 8) & 0xFF, rgb & 0xFF) pixels = memoryview(bitmap) return pg.Bitmap(pixels, bitmap.width, bitmap.height, format=pg.PAL8, palette=colors, stride=len(pixels) // bitmap.height)
picture = load_image("/picture.bmp")screen.blit(picture, 10, 10)Obrázek ulož jako 8bitový (256barevný) BMP. Umí to většina grafických editorů; v Pillow je to
Image.open("picture.png").convert("RGB").quantize(256).save("picture.bmp").
blit(bitmap, x, y, frame=0, flip_x=False, flip_y=False) vykreslí bitmapu na libovolné místo,
oříznutou podle canvasu. Chceš-li jednu barvu palety průhlednou, předej její index jako
transparent= při vytváření pg.Bitmap.
Text
Sekce “Text”Vestavěný font
Sekce “Vestavěný font”import terminalio
screen.text(200, 60, "Hello, DVI", color(255, 255, 255), terminalio.FONT)screen.text(200, 76, "on a box", color(0, 0, 0), terminalio.FONT, color(255, 200, 0))Argumenty jsou x, y, text, barva, font a volitelně barva pozadí jako šestý argument. x, y je
levý horní roh. terminalio.FONT má 6×12 pixelů na znak. Tahle C cesta bere jen vestavěný font.
Vlastní font (BDF nebo PCF)
Sekce “Vlastní font (BDF nebo PCF)”Pro jakýkoli jiný font načte glyfy adafruit_bitmap_font a z každého se stane malá Bitmap,
kterou vykreslíš přes blit:
from adafruit_bitmap_font import bitmap_font
font = bitmap_font.load_font("/fonts/myfont.pcf")glyph_cache = {}
def glyph_bitmap(glyph, text_color): """Převede jeden glyf fontu na picogame Bitmap s průhledným pozadím.""" width, height = glyph.width, glyph.height pixels = bytearray(width * height) for y in range(height): for x in range(width): pixels[y * width + x] = glyph.bitmap[x, y] palette = array.array("H", (0, text_color)) return pg.Bitmap(pixels, width, height, format=pg.PAL8, palette=palette, transparent=0)
def draw_text(canvas, x, y, text, text_color): """Vykreslí text s účařím na y.""" font.load_glyphs(text) for char in text: glyph = font.get_glyph(ord(char)) if glyph is None: continue if glyph.width and glyph.height: # mezera má prázdnou bitmapu key = (char, text_color) if key not in glyph_cache: glyph_cache[key] = glyph_bitmap(glyph, text_color) canvas.blit(glyph_cache[key], x + glyph.dx, y - glyph.height - glyph.dy) x += glyph.shift_x
draw_text(screen, 10, 170, "Proportional text", color(255, 220, 0))Tohle zvládá i proporcionální fonty: každý glyf se umístí podle vlastního posunu a šířky. Každý
znak se převede jednou pro každou barvu a pak se bere z glyph_cache.
Stejný kód čte BDF i PCF. U velkých fontů dej přednost PCF: je to binární formát, obvykle zhruba
poloviční oproti stejnému BDF, a adafruit_bitmap_font z něj čte jen glyfy, o které požádáš. BDF
na PCF převede bdftopcf (součást nástrojů X.Org).
Buffery mimo obrazovku
Sekce “Buffery mimo obrazovku”Canvas umí kreslit do libovolného bytearray a pg.Bitmap z něj udělá něco, co jde vykreslit
přes blit:
panel_pixels = bytearray(120 * 40 * 2) # šířka * výška * 2 bajty (RGB565)panel = pg.Canvas(120, 40, buffer=panel_pixels)panel.clear(color(70, 70, 90))panel.rect(0, 0, 120, 40, color(255, 255, 255))panel.text(8, 14, "off-screen", color(255, 255, 255), terminalio.FONT)
panel_bitmap = pg.Bitmap(panel_pixels, 120, 40)screen.blit(panel_bitmap, 190, 190)Takhle můžeš panel, sprite nebo celý snímek poskládat jednou a vykreslovat ho mnohokrát. Je to
obyčejný bytearray, takže s týmiž pixely může pracovat i tvůj vlastní kód - viper, nativní modul,
ulab.
Animace
Sekce “Animace”Žádná smyčka snímků, do které by ses musel zapojit, není: smaž, co se pohnulo, nakresli to na novém místě, opakuj.
import time
x = 0while True: screen.fill_rect(x, 200, 20, 20, color(0, 0, 40)) # smaž starý čtverec x = (x + 2) % 160 screen.fill_rect(x, 200, 20, 20, color(0, 200, 255)) # nakresli nový time.sleep(1 / 60)Kreslíš do obrazu, který je právě na obrazovce, takže velké překreslení může být vidět rozpracované. Velké změny poskládej v bufferu mimo obrazovku a výsledek vykresli jedním voláním.
Na SPI displeji: StripDraw
Sekce “Na SPI displeji: StripDraw”Na SPI displeji vykresluje obrazovku Scene po pruzích řádků a vrstva StripDraw volá tvou
funkci pro každý pruh. Uvnitř dostaneš Canvas pro daný pruh a používáš stejná volání jako výše:
import boardimport terminalioimport picogame as pg
display = board.DISPLAYWIDTH, HEIGHT = display.width, display.heightSTRIP_HEIGHT = 8scene = pg.Scene(display, bytearray(WIDTH * STRIP_HEIGHT * 2), bytearray(WIDTH * STRIP_HEIGHT * 2))ball_x = 0
def draw(view, strip_x, strip_y, strip_width, strip_height): # view pokrývá řádky obrazovky strip_y .. strip_y + strip_height - 1, # takže od každého y na obrazovce odečti strip_y view.clear(pg.rgb565(0, 0, 40)) view.fill_circle(ball_x, HEIGHT // 2 - strip_y, 20, pg.rgb565(0, 255, 0)) view.text(10, 10 - strip_y, "Hello", pg.rgb565(255, 255, 255), terminalio.FONT)
scene.add(pg.StripDraw(draw, 0, 0, WIDTH, HEIGHT))while True: ball_x = (ball_x + 2) % WIDTH scene.refresh()Barvy tady dává pg.rgb565(), protože tohle je pořadí bajtů SPI displejů. Co se do jednoho pruhu
nevejde - obrázek, delší řádek textu - se prostě vykreslí znovu, oříznuté, v dalším pruhu.
Tvoje funkce běží pro každý pruh, takže v ní jen kresli. Pozice a stav hry spočítej jednou za
snímek, před scene.refresh().
StripDraw se překresluje při každém refresh, což je správně pro obsah, který se hýbe. Obsah,
který se mění jen občas, vytvoř s always_dirty=False a po změně zavolej jeho invalidate();
jinak ho scéna přeskočí.
Kdy použít zbytek
Sekce “Kdy použít zbytek”Všechno výše je celý C modul viděný zespodu. Scene, Sprite, Tilemap a pomocné knihovny
picogame_* přidávají překreslování jen toho, co se změnilo, kolize, animace, vstup a textové
widgety. Jak do sebe zapadají, ukazují stránky Jak picogame funguje
a Kreslicí cesty.