Obsah
1. Propojení Pythonu s nativními knihovnami s využitím balíčku ctypes
3. Základní informace o knihovně Pygame
4. Vykreslení jednotlivých pixelů v rastrovém obrázku typu Image v Pythonu
6. Realizace nativní funkce, která vyplní obrázek jednoduchým testovacím vzorkem
7. Překlad nativní funkce do dynamicky linkované knihovny
8. Zavolání nativní funkce z Pythonu
9. Předání barvové palety do nativní funkce
10. Přednosti propojení Pythonu s jazykem C
11. Výpočet Mandelbrotovy a Juliovy množiny implementovaný v jazyce C
12. Zavolání nativních funkci pro výpočet Mandelbrotovy a Juliovy množiny z Pythonu
13. Mandelbrotova množina jako „mapa“ všech Juliových množin
14. Interaktivní určení konstanty c v Mandelbrotově množině
15. Výsledná podoba demonstračního příkladu
16. Buffer sdílený oběma rastrovými obrázky
17. Oprava předchozího postupu: použití dvou bufferů bez kopie pixelů
18. Přístup k pixelům obrázku po 32bitových slovech
19. Repositář s demonstračními příklady
1. Propojení Pythonu s nativními knihovnami s využitím balíčku ctypes
Na stránkách Roota, konkrétně v článcích [1] [2] a [3], jsme se již zabývali problematikou propojení vysokoúrovňového programovacího jazyka Python s nativními knihovnami naprogramovanými například v jazyku C (ovšem může se jednat i o Rust, C++ a pokud si dáme pozor, tak i Go). Poměrně jednoduchá je situace, kdy se pouze z Pythonu volají nativní funkce, kterým se předávají již naalokované objekty. Pro tento účel jsme v předchozích článcích využili především knihovnu cffi (viz též Overview). Dnes si naproti tomu ukážeme použití standardního balíčku ctypes (viz též dokumentaci).
Základní postup přitom zůstává stále stejný. Zdrojový kód napsaný v jazyku C (C++, Rustu, Go, …) je nejprve přeložen do nativní dynamické knihovny, tedy konkrétně do souboru s koncovkou „.so“ na Linuxu a „.dll“ v systému Microsoft Windows. Aplikace psaná v Pythonu tuto dynamickou knihovnu načte a přes balíček ctypes umožní volání funkcí naprogramovaných v C/C++ atd. Zpočátku se může zdát, že se jedná o bezproblémové řešení, ovšem v praxi musíme vyřešit především dva problémy: vlastnictví předávaných hodnot (tedy která strana alokuje paměť a která ji může dealokovat) a taktéž korektní předání hodnot různých typů. První problém musí vyřešit programátor, ovšem druhý problém může – i když pouze částečně – řešit i balíček realizující volání nativních funkcí z Pythonu. A právě zde nalezneme největší rozdíly mezi ctypes, cffi i dalšími balíčky určenými pro stejný účel. V tomto ohledu jsou možnosti ctypes dosti omezené, ovšem jedná se o standardní balíček, který navíc může pro některé účely plně vyhovovat.
Obrázek 1: Programovací jazyk C je prozatím ve své nice prakticky nenahraditelný, i když už vzniklo několik jeho nástupců (Zig, D, částečně Rust).
2. Řešený problém
V rámci navazujících kapitol se pokusíme o vytvoření jednoduché aplikace, která zobrazí Mandelbrotovu množinu a umožní uživateli interaktivní výběr souřadnice v této množině (což je komplexní číslo označované jako C nebo malé c), které bude ihned využito při vykreslení Juliovy množiny:
Obrázek 2: Výsledná aplikace. Uživatel může v Mandelbrotově množině (levá část) vybrat hodnotu komplexního čísla C. To se ihned projeví v pravé části na tvaru Juliovy množiny.
Obrázek 3: Juliova množina pro odlišnou hodnotu C (reálná část je nulová).
Obrázek 4: Juliova množina pro odlišnou hodnotu C.
Obrázek 5: Juliova množina pro odlišnou hodnotu C (imaginární část je nulová).
Samotné (minimalisticky pojaté) grafické uživatelské prostředí aplikace bude naprogramováno s využitím populární knihovny Pygame, výpočet Mandelbrotovy množiny a Juliovy množiny bude realizován v ANSI C a pro jednoduchost nebude algoritmus nijak optimalizován (výsledný nativní kód však je možné optimalizovat překladačem céčka, postačuje jen nastavit příslušné přepínače).
3. Základní informace o knihovně Pygame
Pygame je knihovna určená pro programovací jazyk Python, která interně volá funkce nativní knihovny SDL a několika dalších podpůrných knihoven. Myšlenka, na níž je Pygame postavena, je v mnoha ohledech podobná myšlence výše systému LÖVE (kde se ovšem používá jazyk Lua) – implementace nízkoúrovňových operací nechť je vytvořena odborníky v programovacích jazycích C a C++; pokud budou tyto operace implementovány dostatečně rychle, je již možné zbytek hry naprogramovat ve vysokoúrovňovém jazyku Python. A ukazuje se, že je tato myšlenka – a obecně systém rozdělení aplikace mezi dva programovací jazyky (kompilovaný a skriptovací) – poměrně úspěšná, neboť v Pygame již vzniklo mnoho zajímavých her i dalších aplikací.
Obrázek 6: Logo knihovny Pygame.
Pro naše účely využijeme jen několik typů objektů knihovny Pygame. Jedná se o:
- display: objekt představující plochu okna, do něhož se provádí vykreslování celé scény
- event: událost, která vznikne například při stisku tlačítka myši, pohybem myši, stiskem klávesy, zavřením okna atd.
- image: objekt s rastrovým obrázkem (s paletou či plnobarevným)
- clock: hodiny, které nám umožní například zajistit maximální frekvenci obnovování obsahu okna atd.
4. Vykreslení jednotlivých pixelů v rastrovém obrázku typu Image v Pythonu
S využitím knihovny Pygame nyní vytvoříme jednoduchou aplikaci, která po svém spuštění otevře okno se zadanými rozměry a vykreslí do něj dvojici rastrových obrázků s různými barvovými vzorky. První funkce otevře nové okno, vyplní ho zadanou barvou pozadí a vytvoří objekt typu „hodiny“ (ten použijeme v pozdějších verzích příkladu):
def initialize_ui(title, width, height): """Initialize Pygame display, drawing surface, and clocks.""" # set window title pygame.display.set_caption(title) # initialize window display = pygame.display.set_mode([width, height]) display.fill((0, 0, 0)) clock = pygame.time.Clock() return display, clock
Druhá funkce je již komplikovanější. Jedná se o realizaci programové smyčky reagující na události čtené z fronty. Pokud se uživatel pokusí zavřít okno aplikace, dojde k vytvoření události typu QUIT. A pokud stiskne klávesu Escape, bude se jednat o událost typu KEYDOWN, přičemž bude nastavený atribut key na kód klávesy Escape. Po zpracování všech událostí vykreslíme na displej dvojici rastrových obrázků operací display.blit, necháme překreslit displej (resp. obsah okna) a počkáme určitý časový interval:
def event_loop(display, image1, image2, clock): while True: for event in pygame.event.get(): if event.type == pygame.locals.QUIT: pygame.quit() sys.exit() if event.type == pygame.locals.KEYDOWN: if event.key == pygame.locals.K_ESCAPE: pygame.quit() sys.exit() # all events has been processed - update scene and redraw the screen display.blit(image1, (30, 20)) display.blit(image2, (60 + image1.get_width(), 20)) # and update the whole display pygame.display.update() clock.tick(25)
Dále je v programu realizována funkce, která do předaného obrázku (typu Image) vykreslí testovací vzorek. Povšimněte si, že musíme vypočítat hodnotu barvy ze všech tří barvových složek RGB (posunem složek o 8 a 16 bitů) a že se hodnota pixelu nastavuje metodou Image.set_at:
def render_test_rgb_image(image, green): width, height = image.get_size() for y in range(height): for x in range(width): color = (x<<16) + (green<<8) + y image.set_at((x, y), color)
A nakonec následuje inicializace okna, obrázků a vstup do těla smyčky pro zpracování událostí:
def main(): display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT) image1 = pygame.Surface([IMAGE_WIDTH, IMAGE_HEIGHT]) image2 = pygame.Surface([IMAGE_WIDTH, IMAGE_HEIGHT]) render_test_rgb_image(image1, 0) render_test_rgb_image(image2, 255) event_loop(display, image1, image2, clock) if __name__ == "__main__": main()
Úplný zdrojový kód bude vypadat následovně:
import sys import pygame import pygame.locals TITLE = "Raster image" SCREEN_WIDTH = 600 SCREEN_HEIGHT = 300 IMAGE_WIDTH = 256 IMAGE_HEIGHT = 256 def initialize_ui(title, width, height): """Initialize Pygame display, drawing surface, and clocks.""" # set window title pygame.display.set_caption(title) # initialize window display = pygame.display.set_mode([width, height]) display.fill((0, 0, 0)) clock = pygame.time.Clock() return display, clock def event_loop(display, image1, image2, clock): while True: for event in pygame.event.get(): if event.type == pygame.locals.QUIT: pygame.quit() sys.exit() if event.type == pygame.locals.KEYDOWN: if event.key == pygame.locals.K_ESCAPE: pygame.quit() sys.exit() # all events has been processed - update scene and redraw the screen display.blit(image1, (30, 20)) display.blit(image2, (60 + image1.get_width(), 20)) # and update the whole display pygame.display.update() clock.tick(25) def render_test_rgb_image(image, green): width, height = image.get_size() for y in range(height): for x in range(width): color = (x<<16) + (green<<8) + y image.set_at((x, y), color) def main(): display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT) image1 = pygame.Surface([IMAGE_WIDTH, IMAGE_HEIGHT]) image2 = pygame.Surface([IMAGE_WIDTH, IMAGE_HEIGHT]) render_test_rgb_image(image1, 0) render_test_rgb_image(image2, 255) event_loop(display, image1, image2, clock) if __name__ == "__main__": main() # finito
Po spuštění této funkce by se mělo na desktopu zobrazit nové okno s následujícím obsahem:
Obrázek 7: Okno s dvojicí testovacích rastrových obrázků.
5. Limity předchozího řešení
Dnešní první demonstrační příklad, který byl popsán v předchozí kapitole, je sice plně funkční, ovšem má jednu vadu – samotné vykreslování (resp. přesněji řečeno obarvování) pixelů přímo v Pythonu metodou set_at je velmi pomalé. Je tomu tak z toho důvodu, že je neustále nutné počítat adresu pixelu (i když obrázek vykreslujeme postupně po pixelech a potom po řádcích) a taktéž samotný výpočet barvy je pomalejší. A navíc je většinou (interně) nutné pole s rastrovými daty (tedy s jednotlivými pixely) zamykat. Pro naše relativně malé obrázky je sice vykreslení na moderním HW dostatečně rychlé, ale ukážeme si řešení založené na nativní funkci naprogramované v céčku. Toto řešení má výhodu v tom, že je velmi snadno rozšiřitelné i pro účely výpočtu Mandelbrotovy množiny a Juliovy množiny (množin).
6. Realizace nativní funkce, která vyplní obrázek jednoduchým testovacím vzorkem
Nyní se podívejme na to, jakým způsobem by mohla být vytvořena funkce naprogramovaná v jazyku C, které se předá ukazatel na data rastrového obrázku společně s rozměry tohoto obrázku. Funkce následně obrázek vyplní testovacím vzorkem. Budeme předpokládat, že každý pixel obrázku je uložen ve čtyřech po sobě jdoucích bajtech (viz předchozí kapitolu) a že mezi jednotlivými obrazovými řádky nejsou žádné vyplňovací bajty (což by souviselo s hodnotou stride). Za těchto podmínek je vyplnění rastrového obrázku RGB vzorkem velmi jednoduché, což ostatně můžeme vidět i z realizace této funkce. Povšimněte si především toho, že pixel je skutečně uložen ve čtyřech bajtech, jeden bajt však není použit (je při zápisu zcela přeskočen):
void render_test_rgb_image(unsigned int width, unsigned int height, unsigned char *pixels, unsigned char green) { unsigned int i, j; unsigned char *p = pixels; for (j = 0; j < height; j++) { for (i = 0; i < width; i++) { *p++ = i; *p++ = green; *p++ = j; p++; } } }
7. Překlad nativní funkce do dynamicky linkované knihovny
Zdrojový kód s touto funkcí musíme přeložit tak, aby vznikla dynamicky linkovaná knihovna (.so, .dll). Pro operační systém Linux bude příslušný Makefile soubor realizující překlad vypadat v té nejjednodušší podobě následovně:
.PHONY: all clean CC=gcc CFLAGS=-Wall -pedantic -ansi all: renderer.so clean: rm -f renderer.so renderer.so: renderer.c $(CC) $(CFLAGS) -shared -Wl,-soname,renderer -o $@ -fPIC $<
Překlad s následným vytvořením dynamicky linkované knihovny provedeme příkazem:
$ make
Překladem by měl vzniknout soubor renderer.so, v němž mj. najdeme i naši funkci render_test_rgb_image:
$ nm renderer.so 0000000000004008 b completed.0 w __cxa_finalize@GLIBC_2.2.5 0000000000001040 t deregister_tm_clones 00000000000010b0 t __do_global_dtors_aux 0000000000003de8 d __do_global_dtors_aux_fini_array_entry 0000000000003df0 d __dso_handle 0000000000003df8 d _DYNAMIC 000000000000117c t _fini 00000000000010f0 t frame_dummy 0000000000003de0 d __frame_dummy_init_array_entry 0000000000002080 r __FRAME_END__ 0000000000003fe8 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 0000000000002000 r __GNU_EH_FRAME_HDR 0000000000001000 t _init w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000001070 t register_tm_clones 00000000000010f9 T render_test_rgb_image 0000000000004008 d __TMC_END__
8. Zavolání nativní funkce z Pythonu
Otestujme si nyní, jak lze nativní funkci z předchozí kapitoly zavolat z Pythonu. Nejdříve musíme načíst dynamicky linkovanou knihovnu, což zajistí tyto řádky:
from ctypes import CDLL renderer = CDLL("./renderer.so")
Následně si necháme (přímo v Pythonu) naalokovat buffer o takové velikosti, aby do něj bylo možné uložit všechny pixely obrázku. Každý pixel je uložen ve čtyřech bajtech, takže velikost bude rovna 4 * IMAGE_WIDTH * IMAGE_HEIGHT. Pro alokaci bufferu použijeme funkci create_string_buffer, která však (i přes své jméno) akceptuje velikost v bajtech:
from ctypes import create_string_buffer buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT)
Nyní již můžeme naši nativní funkci zavolat a předat jí rozměry obrázku i právě naalokovaný buffer:
from ctypes import c_int renderer.render_test_rgb_image(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), buffer, 0)
Nakonec musíme hodnoty pixelů z bufferu převést na obrázek, k čemuž slouží funkce pygame.image.frombytes. Ta kromě bufferu s hodnotami pixelů potřebuje znát i velikost obrázku (předává se formou dvojice) a jeho formát (pro naše účely RGBX):
def image_from_buffer(buffer, width, height, fmt): return pygame.image.frombytes(bytes(buffer), (width, height), fmt) image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
Úplný zdrojový kód takto upraveného demonstračního příkladu vypadá následovně:
import sys from ctypes import CDLL, c_int, create_string_buffer import pygame import pygame.locals TITLE = "Renderer" SCREEN_WIDTH = 600 SCREEN_HEIGHT = 300 IMAGE_WIDTH = 256 IMAGE_HEIGHT = 256 def initialize_ui(title, width, height): """Initialize Pygame display, drawing surface, and clocks.""" # set window title pygame.display.set_caption(title) # initialize window display = pygame.display.set_mode([width, height]) display.fill((0, 0, 0)) clock = pygame.time.Clock() return display, clock def event_loop(display, image1, image2, clock): while True: for event in pygame.event.get(): if event.type == pygame.locals.QUIT: pygame.quit() sys.exit() if event.type == pygame.locals.KEYDOWN: if event.key == pygame.locals.K_ESCAPE: pygame.quit() sys.exit() # all events has been processed - update scene and redraw the screen display.blit(image1, (30, 20)) display.blit(image2, (60 + image1.get_width(), 20)) # and update the whole display pygame.display.update() clock.tick(25) def image_from_buffer(buffer, width, height, fmt): return pygame.image.frombytes(bytes(buffer), (width, height), fmt) def main(): display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT) # try to load dynamically linked library renderer = CDLL("./renderer.so") # create buffer for raster image buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT) renderer.render_test_rgb_image(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), buffer, 0) image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX") renderer.render_test_rgb_image(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), buffer, 255) image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX") event_loop(display, image1, image2, clock) if __name__ == "__main__": main() # finito
9. Předání barvové palety do nativní funkce
V dalším kroku se pokusíme o předání barvové palety do nativní funkce. Barvová paleta bude obsahovat 256 barev, přičemž každá barva je reprezentována třemi bajty – každý bajt totiž obsahuje hodnotu jedné barvové složky red, green nebo blue. Celkem je tedy nutné do nativní funkce předat 256×3 = 768 bajtů. Ovšem vzhledem k tomu, že barvová paleta nebude ve funkci měněna, můžeme ji předat ukazatelem, což znamená, že namísto 768 bajtů se předají čtyři bajty nebo osm bajtů (podle šířky ukazatele).
Připomeňme si, že původní nativní funkce určená pro vykreslení testovacího vzorku do rastrového obrázku vypadala následovně:
void render_test_rgb_image(unsigned int width, unsigned int height, unsigned char *pixels, unsigned char green) { unsigned int i, j; unsigned char *p = pixels; for (j = 0; j < height; j++) { for (i = 0; i < width; i++) { *p++ = i; *p++ = green; *p++ = j; p++; } } }
Tato funkce nepoužívala barvovou paletu pro přepočet nějaké celočíselné hodnoty na barvu z palety. Provedeme tedy následující úpravy:
- Funkce bude nově akceptovat ukazatel na první barvu v barvové paletě
- Namísto přímého výpočtu složek R, G, B použijeme barvovou paletu ve funkci LUT (look-up table)
Pro jednoduchost odvodíme index barvy pixelu pouze z jeho x-ové složky. Výsledná funkce bude vypadat následovně:
void render_test_palette_image(unsigned int width, unsigned int height, const unsigned char *palette, unsigned char *pixels) { unsigned int i, j; unsigned char *p = pixels; for (j = 0; j < height; j++) { for (i = 0; i < width; i++) { int color = i * 3; *p++ = palette[color]; *p++ = palette[color + 1]; *p++ = palette[color + 2]; p++; } } }
Výsledkem by měl obrázek z pravé části screenshotu:
Obrázek 8: Pravý obrázek je vykreslen s využitím barvové palety.
Upravená Pythonní část, která bude volat výše uvedenou funkci, bude vypadat následovně:
import sys from ctypes import CDLL, c_int, create_string_buffer from palette_mandmap import palette import pygame import pygame.locals TITLE = "Renderer" SCREEN_WIDTH = 600 SCREEN_HEIGHT = 300 IMAGE_WIDTH = 256 IMAGE_HEIGHT = 256 def initialize_ui(title, width, height): """Initialize Pygame display, drawing surface, and clocks.""" # set window title pygame.display.set_caption(title) # initialize window display = pygame.display.set_mode([width, height]) display.fill((0, 0, 0)) clock = pygame.time.Clock() return display, clock def event_loop(display, image1, image2, clock): while True: for event in pygame.event.get(): if event.type == pygame.locals.QUIT: pygame.quit() sys.exit() if event.type == pygame.locals.KEYDOWN: if event.key == pygame.locals.K_ESCAPE: pygame.quit() sys.exit() # all events has been processed - update scene and redraw the screen display.blit(image1, (30, 20)) display.blit(image2, (60 + image1.get_width(), 20)) # and update the whole display pygame.display.update() clock.tick(25) def palette_to_buffer(p): s = create_string_buffer(len(p) * 3) i = 0 for color in p: s[i] = color[0] s[i + 1] = color[1] s[i + 2] = color[2] i += 3 return s def image_from_buffer(buffer, width, height, fmt): return pygame.image.frombytes(bytes(buffer), (width, height), fmt) def main(): pal = palette_to_buffer(palette) display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT) # try to load dynamically linked library renderer = CDLL("./renderer.so") # create buffer for raster image buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT) renderer.render_test_rgb_image(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), buffer, 0) image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX") renderer.render_test_palette_image( c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer ) image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX") event_loop(display, image1, image2, clock) if __name__ == "__main__": main() # finito
10. Přednosti propojení Pythonu s jazykem C
Propojení programovacího jazyka Python s jazykem C (popř. s Rustem nebo jazykem Zig) přináší zajímavé možnosti. V první řadě to umožňuje relativně snadné volání funkcí ze systémových knihoven, ale i dalších funkcí dostupných formou dynamicky sdílených knihoven. Díky tomu lze spojit snadnost tvorby aplikací v Pythonu (vysokoúrovňový jazyk s relativně velkou mírou abstrakce) s optimalizovaným nativním kódem. Dobrým příkladem takového propojení je projekt Numpy, v němž se výpočetně náročné části realizují nativními funkcemi. A příkladem propojení Pythonu s Rustem může být projekt Polars, se kterým jsme se na stránkách Roota taktéž již setkali v článcích Knihovna Polars: výkonnější alternativa ke knihovně Pandas a Knihovna Polars: výkonnější alternativa ke knihovně Pandas (datové rámce).
Již v několika předchozích článcích o Pythonu [1] [2] [3] jsme pro některé benchmarky použili výpočet Mandelbrotovy množiny. Dnes si tento výpočet, doplněný navíc o výpočet Juliovy množiny, převedeme do céčka, čímž bychom měli dosáhnout menšího času výpočtu v porovnání s řešením v Pythonu (za předpokladu, že se nevyužije více jader mikroprocesoru).
11. Výpočet Mandelbrotovy a Juliovy množiny implementovaný v jazyce C
V rámci této kapitoly si přepíšeme funkce pro výpočet Mandelbrotovy množiny a Juliovy množiny z Pythonu do programovacího jazyka C (pro větší zajímavost do ANSI C, takže nepoužijeme typ komplexní číslo). V Pythonu lze oba výpočty realizovat velmi snadno:
def mandelbrot(cx, cy, maxiter): """Calculate number of iterations for given complex number to escape from set.""" c = complex(cx, cy) z = 0 for i in range(0, maxiter): if abs(z) < 2: return i z = z * z + c return 0 def recalc_mandelbrot(image, palette, xmin, ymin, xmax, ymax, maxiter=100): """Recalculate the whole fractal and render the set into given image.""" width, height = image.get_size() # rozmery obrazku stepx = (xmax - xmin) / width stepy = (ymax - ymin) / height y1 = ymin for y in range(0, height): x1 = xmin for x in range(0, width): i = mandelbrot(x1, y1, maxiter) i = 3 * i % 256 color = palette[i][2] + (palette[i][1] << 8) + (palette[i][0] << 16) image.set_at((x, y), color) x1 += stepx y1 += stepy def julia(zx0, zy0, cx, cy, maxiter): """Calculate number of iterations for given complex numbers Z and C to escape from set.""" c = complex(cx, cy) z = complex(zx0, zy0) for i in range(0, maxiter): if abs(z) > 2: return i z = z * z + c return 0 def recalc_julia(image, palette, xmin, ymin, xmax, ymax, cx, cy, maxiter=1000): """Recalculate the whole fractal and render the set into given image.""" width, height = image.get_size() # rozmery obrazku stepx = (xmax - xmin) / width stepy = (ymax - ymin) / height y1 = ymin for y in range(0, height): x1 = xmin for x in range(0, width): i = julia(x1, y1, cx, cy, maxiter) i = 3 * i % 256 color = palette[i][2] + (palette[i][1] << 8) + (palette[i][0] << 16) image.set_at((x, y), color) x1 += stepx y1 += stepy
Výše uvedené výpočty budou velmi pomalé, zejména při použití standardního CPythonu. Příslušné funkce přepsané do programovacího jazyka C současně provádí i vykreslení s využitím předané barvové palety, tedy podobně, jako tomu bylo v Pythonu:
void render_mandelbrot(unsigned int width, unsigned int height, const unsigned char *palette, unsigned char *pixels) { int x, y; double cx, cy; double xmin = -2.0, ymin = -1.5, xmax = 1.0, ymax = 1.5; unsigned char *p = pixels; cy = ymin; for (y = 0; y < height; y++) { cx = xmin; for (x = 0; x < width; x++) { double zx = 0.0; double zy = 0.0; unsigned int i = 0; while (i < 150) { double zx2 = zx * zx; double zy2 = zy * zy; if (zx2 + zy2 > 4.0) { break; } zy = 2.0 * zx * zy + cy; zx = zx2 - zy2 + cx; i++; } { unsigned char * pal = (unsigned char *)palette + (unsigned char)(i * 3); *p++ = *pal++; *p++ = *pal++; *p++ = *pal; p++; } cx += (xmax - xmin) / width; } cy += (ymax - ymin) / height; } } void render_julia(unsigned int width, unsigned int height, const unsigned char *palette, unsigned char *pixels, double cx, double cy) { int x, y; double zx0, zy0; double xmin = -1.5, ymin = -1.5, xmax = 1.5, ymax = 1.5; unsigned char *p = pixels; zy0 = ymin; for (y = 0; y < height; y++) { zx0 = xmin; for (x = 0; x < width; x++) { double zx = zx0; double zy = zy0; unsigned int i = 0; while (i < 150) { double zx2 = zx * zx; double zy2 = zy * zy; if (zx2 + zy2 > 4.0) { break; } zy = 2.0 * zx * zy + cy; zx = zx2 - zy2 + cx; i++; } { unsigned char * pal = (unsigned char *)palette + (unsigned char)(i * 3); *p++ = *pal++; *p++ = *pal++; *p++ = *pal; p++; } zx0 += (xmax - xmin) / width; } zy0 += (ymax - ymin) / height; } }
12. Zavolání nativních funkci pro výpočet Mandelbrotovy a Juliovy množiny z Pythonu
Volání funkcí pro výpočet Mandelbrotovy a Juliovy množiny, které byly vypsány v předchozí kapitole, lze v Pythonu realizovat poměrně snadno. Je zde ovšem novinka – předání komplexní konstanty c do funkce pro výpočet Juliovy množiny vyžaduje použití c_double, což je opět funkce definovaná v knihovně ctypes. Načtení dynamicky linkované knihovny, vytvoření pomocného bufferu a zavolání funkcí pro výpočet obou fraktálů může vypadat následovně:
# try to load dynamically linked library renderer = CDLL("./renderer.so") # create buffer for raster image buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT) renderer.render_mandelbrot(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer) image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX") renderer.render_julia( c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer, c_double(0.285), c_double(0.01), ) image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
Obrázek 9: Výsledek výpočtu Mandelbrotovy a Juliovy množiny.
A takto bude vypadat úplný zdrojový kód upraveného demonstračního příkladu (resp. přesněji řečeno jeho Pythonovská část):
import sys from ctypes import CDLL, c_double, c_int, create_string_buffer from palette_mandmap import palette import pygame import pygame.locals TITLE = "Renderer" SCREEN_WIDTH = 600 SCREEN_HEIGHT = 300 IMAGE_WIDTH = 256 IMAGE_HEIGHT = 256 def initialize_ui(title, width, height): """Initialize Pygame display, drawing surface, and clocks.""" # set window title pygame.display.set_caption(title) # initialize window display = pygame.display.set_mode([width, height]) display.fill((0, 0, 0)) clock = pygame.time.Clock() return display, clock def event_loop(display, image1, image2, clock): while True: for event in pygame.event.get(): if event.type == pygame.locals.QUIT: pygame.quit() sys.exit() if event.type == pygame.locals.KEYDOWN: if event.key == pygame.locals.K_ESCAPE: pygame.quit() sys.exit() # all events has been processed - update scene and redraw the screen display.blit(image1, (30, 20)) display.blit(image2, (60 + image1.get_width(), 20)) # and update the whole display pygame.display.update() clock.tick(25) def palette_to_buffer(p): s = create_string_buffer(len(p) * 3) i = 0 for color in p: s[i] = color[0] s[i + 1] = color[1] s[i + 2] = color[2] i += 3 return s def image_from_buffer(buffer, width, height, fmt): return pygame.image.frombytes(bytes(buffer), (width, height), fmt) def main(): pal = palette_to_buffer(palette) display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT) # try to load dynamically linked library renderer = CDLL("./renderer.so") # create buffer for raster image buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT) renderer.render_mandelbrot(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer) image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX") renderer.render_julia( c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer, c_double(0.285), c_double(0.01), ) image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX") event_loop(display, image1, image2, clock) if __name__ == "__main__": main() # finito
13. Mandelbrotova množina jako „mapa“ všech Juliových množin
Juliových množin existuje nekonečné množství. Liší se od sebe hodnotou c, která je použita při výpočtu bodů ležících uvnitř množiny (do nativní funkce jsme předávali reálnou a imaginární složku odděleně, a to v parametrech cx a cy). Juliovy množiny lze v závislosti na hodnotě c rozdělit na tři typy:
- Pro některé hodnoty c tvoří body ležící v Juliově množině spojitou oblast. To znamená, že každé dva body v takovéto Juliově množině je možné navzájem spojit určitou křivkou tak, že celá křivka leží uvnitř Juliovy množiny. Tato oblast je vždy jedna, neexistují tedy například navzájem izolované „ostrovy“.
- Pro jiné hodnoty c jsou jednotlivé body tvořící Juliovu množinu zcela izolovány, tj. v jejich okolí neexistuje další bod, který by ležel v Juliově množině. Taková „rozložená“ Juliova množina se nazývá Fatouův prach (Fatou dust) nebo také Cantorův prach (Cantor dust).
- Třetí možnost leží na hranici obou předchozích. Body ležící uvnitř Juliovy množiny sice nejsou vzájemně izolovány, ale současně netvoří žádnou plochu (body na úsečce také nejsou izolovány, ale úsečka má nulovou plochu).
Vztah mezi hodnotou komplexní konstanty c a typem Juliovy množiny je velmi složitý a při jeho detailním zkoumání byla nalezena „mapa“ všech Juliových množin – je jí již dnes několikrát zmíněná Mandelbrotova množina. Body ležící uvnitř Mandelbrotovy množiny představují komplexní hodnoty c, pro které je příslušná Juliova množina spojitá (a naopak). To tedy vlastně znamená, že můžeme interaktivně měnit bod uvnitř Mandelbrotovy množiny a vykreslit pro něj odpovídající Juliovu množinu. A právě tento interaktivní postup je uplatněn v dalším demonstračním příkladu.
Obrázek 10: Spojitá Juliova množina (pravý obrázek).
Obrázek 11: Nespojitá Juliova množina.
Obrázek 12: Hraniční případ.
14. Interaktivní určení konstanty c v Mandelbrotově množině
Jak ale vůbec zajistíme interaktivní nastavení a změnu konstanty c? Můžeme například v Mandelbrotově množině zobrazit „záměrný kříž“ představující hodnotu této konstanty (horizontální osa odpovídá reálné složce a vertikální osa pak složce imaginární) a s využitím kurzorových kláves (nebo myší) umožnit uživateli volbu konstanty.
Konstanty pro výchozí výpočet měřítka (přepočet ze souřadnic obrazovky na hodnotu v komplexní rovině) se získají takto:
scale_x = (XMAX - XMIN) / image1.get_width() scale_y = (YMAX - YMIN) / image1.get_height()
Výchozí hodnota konstanty c:
cx = cx_scr * scale_x + XMIN cy = cy_scr * scale_x + YMIN
Takto lze přepočítat pozici záměrného kříže na obrazovce v závislosti na hodnotách cx_scr_delta a cy_scr_delta, což jsou hodnoty –1, 0 či 1 (posun kříže):
cx_scr += cx_scr_delta cy_scr += cy_scr_delta
Samotný posun kříže je realizován kurzorovými klávesami. Při stisku některé kurzorové klávesy se nastaví příslušný posun do proměnných cx_scr_delta a cy_scr_delta tak, aby se při držení klávesy kříž stále posunoval (vlastně si v těchto konstantách zapamatujeme, které kurzorové klávesy jsou v daný okamžik stisknuty):
if event.type == pygame.locals.KEYDOWN: if event.key == pygame.locals.K_LEFT: cx_scr_delta = -1 if event.key == pygame.locals.K_RIGHT: cx_scr_delta = 1 if event.key == pygame.locals.K_UP: cy_scr_delta = -1 if event.key == pygame.locals.K_DOWN: cy_scr_delta = 1
Po puštění klávesy pochopitelně musíme pohyb zastavit. Opět vše naprogramujeme takovým způsobem, aby byl možný i diagonální pohyb a zastavení tohoto pohybu. Budeme tedy každou kurzorovou klávesu řešit samostatně:
if event.type == pygame.locals.KEYUP: if event.key == pygame.locals.K_LEFT: cx_scr_delta = 0 if event.key == pygame.locals.K_RIGHT: cx_scr_delta = 0 if event.key == pygame.locals.K_UP: cy_scr_delta = 0 if event.key == pygame.locals.K_DOWN: cy_scr_delta = 0
V posledním kroku do plochy prvního obrázku (což je Mandelbrotova množina) vykreslíme „záměrný kříž“, který ukazuje pozici hodnoty c v komplexní rovině:
pygame.draw.line( display, WHITE, (XSTART + cx_scr, YSTART), (XSTART + cx_scr, YSTART + image1.get_height() - 1), ) pygame.draw.line( display, WHITE, (XSTART, YSTART + cy_scr), (XSTART + image1.get_width() - 1, YSTART + cy_scr), )
Na závěr ve smyčce pro obsluhu událostí překreslíme okno a zajistíme, aby se kříž pohyboval maximální rychlostí 25 pixelů za sekundu:
pygame.display.update() clock.tick(25)
15. Výsledná podoba demonstračního příkladu
Demonstrační příklad upravený tak, že umožňuje interaktivní změnu konstanty c, vypadá následovně (resp. přesněji řečeno je uveden kód v Pythonu, zatímco výpočet obou množin v céčkové funkci zůstal zcela nezměněn):
import sys from ctypes import CDLL, c_double, c_int, create_string_buffer from palette_mandmap import palette import pygame import pygame.locals TITLE = "Renderer" SCREEN_WIDTH = 600 SCREEN_HEIGHT = 300 IMAGE_WIDTH = 256 IMAGE_HEIGHT = 256 # Mandelbrot fractal parameters XMIN = -2.0 XMAX = 1.0 YMIN = -1.5 YMAX = 1.5 MAXITER = 100 XSTART = 30 YSTART = 20 def initialize_ui(title, width, height): """Initialize Pygame display, drawing surface, and clocks.""" # set window title pygame.display.set_caption(title) # initialize window display = pygame.display.set_mode([width, height]) display.fill((0, 0, 0)) clock = pygame.time.Clock() return display, clock def event_loop(display, image1, image2, clock, pal, renderer, buffer): cx_scr = image1.get_width() / 2 - 1 + 32 cy_scr = image1.get_width() / 2 - 1 - 42 * 2 cx_scr_delta = 0 cy_scr_delta = 0 first_draw = True while True: for event in pygame.event.get(): if event.type == pygame.locals.QUIT: pygame.quit() sys.exit() if event.type == pygame.locals.KEYDOWN: if event.key == pygame.locals.K_ESCAPE: pygame.quit() sys.exit() if event.key == pygame.locals.K_RETURN: pygame.quit() sys.exit() if event.key == pygame.locals.K_LEFT: cx_scr_delta = -1 if event.key == pygame.locals.K_RIGHT: cx_scr_delta = 1 if event.key == pygame.locals.K_UP: cy_scr_delta = -1 if event.key == pygame.locals.K_DOWN: cy_scr_delta = 1 if event.type == pygame.locals.KEYUP: if event.key == pygame.locals.K_LEFT: cx_scr_delta = 0 if event.key == pygame.locals.K_RIGHT: cx_scr_delta = 0 if event.key == pygame.locals.K_UP: cy_scr_delta = 0 if event.key == pygame.locals.K_DOWN: cy_scr_delta = 0 # all events has been processed - update scene and redraw the screen # keep moving C cx_scr += cx_scr_delta cy_scr += cy_scr_delta # check for limits if cx_scr < 0: cx_scr = 0 if cx_scr > image1.get_width() - 1: cx_scr = image1.get_width() - 1 if cy_scr < 0: cy_scr = 0 if cy_scr > image1.get_height() - 1: cy_scr = image1.get_height() - 1 # recalculate Julia set if needed if cx_scr_delta != 0 or cy_scr_delta != 0 or first_draw: first_draw = False scale_x = (XMAX - XMIN) / image1.get_width() scale_y = (YMAX - YMIN) / image1.get_height() cx = cx_scr * scale_x + XMIN cy = cy_scr * scale_x + YMIN renderer.render_julia( c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer, c_double(cx), c_double(cy), ) image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX") # display Mandelbrot set and Julia se display.blit(image1, (XSTART, YSTART)) display.blit(image2, (60 + image1.get_width(), YSTART)) # display C coordinates WHITE = (255, 255, 255) pygame.draw.line( display, WHITE, (XSTART + cx_scr, YSTART), (XSTART + cx_scr, YSTART + image1.get_height() - 1), ) pygame.draw.line( display, WHITE, (XSTART, YSTART + cy_scr), (XSTART + image1.get_width() - 1, YSTART + cy_scr), ) # and update the whole display pygame.display.update() clock.tick(25) def palette_to_buffer(p): s = create_string_buffer(len(p) * 3) i = 0 for color in p: s[i] = color[0] s[i + 1] = color[1] s[i + 2] = color[2] i += 3 return s def image_from_buffer(buffer, width, height, fmt): return pygame.image.frombytes(bytes(buffer), (width, height), fmt) def main(): pal = palette_to_buffer(palette) display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT) renderer = CDLL("./renderer.so") # create buffer for raster image buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT) renderer.render_mandelbrot(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer) image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX") image2 = pygame.Surface([IMAGE_WIDTH, IMAGE_HEIGHT]) event_loop(display, image1, image2, clock, pal, renderer, buffer) if __name__ == "__main__": main() # finito
16. Buffer sdílený oběma rastrovými obrázky
V předchozích demonstračních příkladech jsme buffer s pixely vybarvenými nativní funkcí přenesli do rastrového obrázku s využitím této pomocné funkce:
def image_from_buffer(buffer, width, height, fmt): return pygame.image.frombytes(bytes(buffer), (width, height), fmt)
Tato funkce ve skutečnosti provedla kopii všech pixelů, což může být časově náročnější operace (u našich malých obrázků nikoli, ale u větších scén již ano). Proto se nabízí otázka, zda namísto toho nevyužít funkci pygame.image.frombytes, která kopii pixelů neprovádí. Upravme tedy zdrojový kód našeho příkladu do následující podoby (ukazuji jen změněné části):
import pygame import pygame.image as image import pygame.locals def event_loop(display, image1, image2, clock, pal, renderer, buffer): ... ... ... renderer.render_julia( c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer, c_double(cx), c_double(cy), ) image2 = image.frombuffer(buffer, (IMAGE_WIDTH, IMAGE_HEIGHT), "RGBX") ... ... ... def main(): pal = palette_to_buffer(palette) display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT) renderer = CDLL("./renderer.so") # create buffer for raster image buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT) renderer.render_mandelbrot(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer) image1 = image.frombuffer(buffer, (IMAGE_WIDTH, IMAGE_HEIGHT), "RGBX") image2 = pygame.Surface([IMAGE_WIDTH, IMAGE_HEIGHT]) event_loop(display, image1, image2, clock, pal, renderer, buffer) if __name__ == "__main__": main()
Po spuštění takto upraveného příkladu je patrné, že je buffer skutečně sdílen mezi oběma obrázky a že tedy původní Mandelbrotova množina byla již v prvním snímku překreslena Juliovou množinou:
Obrázek 13: Výsledek použití sdíleného bufferu pro oba rastrové obrázky.
Obrázek 14: Výsledek použití sdíleného bufferu pro oba rastrové obrázky.
Obrázek 15: Výsledek použití sdíleného bufferu pro oba rastrové obrázky.
17. Oprava předchozího postupu: použití dvou bufferů bez kopie pixelů
Ve skutečnosti pochopitelně stále můžeme využít rychlejší vykreslování bez opakované kopie pixelů. Musíme provést jen jedinou změnu – pro každý obrázek (tedy v našem případě pro obrázek Mandelbrotovy množiny a pro obrázek množiny Juliovy) je nutné alokovat a následně i použít vlastní buffer. Změn ve zdrojovém kódu je zapotřebí provést jen několik, což je ostatně patrné i z následujícího výpisu (změny jsou zde zvýrazněny):
def main(): pal = palette_to_buffer(palette) display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT) renderer = CDLL("./renderer.so") # create buffers for raster image buffer1 = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT) buffer2 = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT) renderer.render_mandelbrot(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer1) image1 = image.frombuffer(buffer1, (IMAGE_WIDTH, IMAGE_HEIGHT), "RGBX") image2 = pygame.Surface([IMAGE_WIDTH, IMAGE_HEIGHT]) event_loop(display, image1, image2, clock, pal, renderer, buffer2) if __name__ == "__main__": main()
Výsledky získané po spuštění takto upraveného příkladu:
Obrázek 16: Výsledek použití unikátního bufferu pro každý rastrový obrázek.
Obrázek 17: Výsledek použití unikátního bufferu pro každý rastrový obrázek.
Obrázek 18: Výsledek použití unikátního bufferu pro každý rastrový obrázek.
Výsledná podoba zdrojového kódu:
import sys from ctypes import CDLL, c_double, c_int, create_string_buffer from palette_mandmap import palette import pygame import pygame.image as image import pygame.locals TITLE = "Renderer" SCREEN_WIDTH = 600 SCREEN_HEIGHT = 300 IMAGE_WIDTH = 256 IMAGE_HEIGHT = 256 # Mandelbrot fractal parameters XMIN = -2.0 XMAX = 1.0 YMIN = -1.5 YMAX = 1.5 MAXITER = 100 XSTART = 30 YSTART = 20 def initialize_ui(title, width, height): """Initialize Pygame display, drawing surface, and clocks.""" # set window title pygame.display.set_caption(title) # initialize window display = pygame.display.set_mode([width, height]) display.fill((0, 0, 0)) clock = pygame.time.Clock() return display, clock def event_loop(display, image1, image2, clock, pal, renderer, buffer): cx_scr = image1.get_width() / 2 - 1 + 32 cy_scr = image1.get_width() / 2 - 1 - 42 * 2 cx_scr_delta = 0 cy_scr_delta = 0 first_draw = True while True: for event in pygame.event.get(): if event.type == pygame.locals.QUIT: pygame.quit() sys.exit() if event.type == pygame.locals.KEYDOWN: if event.key == pygame.locals.K_ESCAPE: pygame.quit() sys.exit() if event.key == pygame.locals.K_RETURN: pygame.quit() sys.exit() if event.key == pygame.locals.K_LEFT: cx_scr_delta = -1 if event.key == pygame.locals.K_RIGHT: cx_scr_delta = 1 if event.key == pygame.locals.K_UP: cy_scr_delta = -1 if event.key == pygame.locals.K_DOWN: cy_scr_delta = 1 if event.type == pygame.locals.KEYUP: if event.key == pygame.locals.K_LEFT: cx_scr_delta = 0 if event.key == pygame.locals.K_RIGHT: cx_scr_delta = 0 if event.key == pygame.locals.K_UP: cy_scr_delta = 0 if event.key == pygame.locals.K_DOWN: cy_scr_delta = 0 # all events has been processed - update scene and redraw the screen # keep moving C cx_scr += cx_scr_delta cy_scr += cy_scr_delta # check for limits if cx_scr < 0: cx_scr = 0 if cx_scr > image1.get_width() - 1: cx_scr = image1.get_width() - 1 if cy_scr < 0: cy_scr = 0 if cy_scr > image1.get_height() - 1: cy_scr = image1.get_height() - 1 # recalculate Julia set if needed if cx_scr_delta != 0 or cy_scr_delta != 0 or first_draw: first_draw = False scale_x = (XMAX - XMIN) / image1.get_width() scale_y = (YMAX - YMIN) / image1.get_height() cx = cx_scr * scale_x + XMIN cy = cy_scr * scale_x + YMIN renderer.render_julia( c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer, c_double(cx), c_double(cy), ) image2 = image.frombuffer(buffer, (IMAGE_WIDTH, IMAGE_HEIGHT), "RGBX") # display Mandelbrot set and Julia se display.blit(image1, (XSTART, YSTART)) display.blit(image2, (60 + image1.get_width(), YSTART)) # display C coordinates WHITE = (255, 255, 255) pygame.draw.line( display, WHITE, (XSTART + cx_scr, YSTART), (XSTART + cx_scr, YSTART + image1.get_height() - 1), ) pygame.draw.line( display, WHITE, (XSTART, YSTART + cy_scr), (XSTART + image1.get_width() - 1, YSTART + cy_scr), ) # and update the whole display pygame.display.update() clock.tick(25) def palette_to_buffer(p): s = create_string_buffer(len(p) * 3) i = 0 for color in p: s[i] = color[0] s[i + 1] = color[1] s[i + 2] = color[2] i += 3 return s def image_from_buffer(buffer, width, height, fmt): return pygame.image.frombytes(bytes(buffer), (width, height), fmt) def main(): pal = palette_to_buffer(palette) display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT) renderer = CDLL("./renderer.so") # create buffers for raster image buffer1 = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT) buffer2 = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT) renderer.render_mandelbrot(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer1) image1 = image.frombuffer(buffer1, (IMAGE_WIDTH, IMAGE_HEIGHT), "RGBX") image2 = pygame.Surface([IMAGE_WIDTH, IMAGE_HEIGHT]) event_loop(display, image1, image2, clock, pal, renderer, buffer2) if __name__ == "__main__": main() # finito
18. Přístup k pixelům obrázku po 32bitových slovech
Prozatím jsme ve všech nativních funkcích k barvám pixelů přistupovali po bajtech, což znamenalo, že se každá barvová složka měnila jedním zápisem (bajtu). Ovšem kromě toho pochopitelně můžeme k pixelům bez problémů přistupovat i po 32bitových slovech, přičemž horních osm bitů tohoto slova nebude využito (což zajišťuje formát „RGBX“). Z pohledu Pythonu k žádné změně nedojde, protože se do nativní funkce stále předává ukazatel na buffer, ovšem v nativní funkci bude tento ukazatel typu uint32_t * a nikoli unsigned char *. A zápis barvy se v tomto případě provede jedinou operací. Na druhou stranu musíme skládat barvy pixelů s využitím bitových posunů, takže reálné rychlosti zápisu se nebudou příliš odlišovat (jiné to bude ve chvíli, kdy budeme mít korektně připravenou barvovou paletu – to je relativně jednoduchá změna, kterou ponechám jako úkol pro vážené čtenáře):
#include <stdint.h> void render_test_rgb_image(unsigned int width, unsigned int height, uint32_t *pixels, unsigned char green) { unsigned int i, j; unsigned int *p = pixels; for (j = 0; j < height; j++) { for (i = 0; i < width; i++) { unsigned int color = (i << 16) + (green << 8) + j; *p++ = color; } } }
Výsledné obrázky by měly vypadat stejně, jako tomu bylo v dnešním prvním demonstračním příkladu:
Obrázek 19: Obrázky s testovacím vzorkem vyplněné zde popsanou nativní funkcí.
19. Repositář s demonstračními příklady
V dnešním článku popsané demonstrační příklady naleznete na GitHubu:
Navíc si pro úplnost uveďme demonstrační příklady použité v článcích o knihovně cffi. I v těchto článcích jsme se totiž o ctypes zmiňovali:
# | Příklad | Stručný popis | Adresa |
---|---|---|---|
1 | adder/adder.c | funkce psaná v C, která sečte své dva celočíselné parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/adder.c |
2 | adder/call_via_cffi1.py | zavolání céčkovské funkce přes cffi s korektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_cffi1.py |
3 | adder/call_via_cffi2.py | zavolání céčkovské funkce přes cffi s nekorektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_cffi2.py |
4 | adder/call_via_cffi3.py | zavolání céčkovské funkce přes cffi s nekorektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_cffi3.py |
5 | adder/call_via_cffi.sh | nastavení cest a spuštění všech tří předchozích Pythonovských skriptů | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_cffi.sh |
6 | adder/call_via_ctypes1.py | zavolání céčkovské funkce přes ctypes s korektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_ctypes1.py |
7 | adder/call_via_ctypes2.py | zavolání céčkovské funkce přes ctypes s nekorektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_ctypes2.py |
8 | adder/call_via_ctypes3.py | zavolání céčkovské funkce přes ctypes s nekorektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_ctypes3.py |
9 | adder/call_via_ctypes.sh | nastavení cest a spuštění všech tří předchozích Pythonovských skriptů | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_ctypes.sh |
10 | adder/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/make_library.sh |
11 | adder/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/clean.sh |
12 | greeter/greeter.c | funkce psaná v C, která na standardní výstup vytiskne řetězec | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/greeter.c |
13 | greeter/call_via_cffi1.py | zavolání céčkovské funkce přes cffi s nekorektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_cffi1.py |
14 | greeter/call_via_cffi2.py | zavolání céčkovské funkce přes cffi s korektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_cffi2.py |
15 | greeter/call_via_cffi3.py | zavolání céčkovské funkce přes cffi s korektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_cffi3.py |
16 | greeter/call_via_cffi.sh | nastavení cest a spuštění všech tří předchozích Pythonovských skriptů | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_cffi.sh |
17 | greeter/call_via_ctypes1.py | zavolání céčkovské funkce přes ctypes s nekorektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_ctypes1.py |
18 | greeter/call_via_ctypes2.py | zavolání céčkovské funkce přes ctypes s korektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_ctypes2.py |
19 | greeter/call_via_ctypes3.py | zavolání céčkovské funkce přes ctypes s korektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_ctypes3.py |
20 | greeter/call_via_ctypes.sh | nastavení cest a spuštění všech tří předchozích Pythonovských skriptů | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_ctypes.sh |
21 | greeter/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/make_library.sh |
22 | greeter/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/clean.sh |
23 | swapper/swapper.c | céčkovská funkce prohazující obsah svých dvou parametrů předávaných referencí | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/swapper.c |
24 | swapper/call_via_cffi1.py | zavolání céčkovské knihovny z jazyka Python (korektní varianta) | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/call_via_cffi1.py |
25 | swapper/call_via_cffi2.py | zavolání céčkovské knihovny z jazyka Python (nekorektní varianta) | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/call_via_cffi2.py |
26 | swapper/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/call_via_cffi.sh |
27 | swapper/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/make_library.sh |
28 | swapper/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/clean.sh |
29 | filler/filler.c | céčkovská funkce pro vyplnění části pole zadanou hodnotou | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/filler/filler.c |
30 | filler/call_via_cffi.py | zavolání céčkovské knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/filler/call_via_cffi.py |
31 | filler/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/filler/call_via_cffi.sh |
32 | filler/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/filler/make_library.sh |
32 | filler/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/filler/clean.sh |
33 | greeter_h/greeter.c | funkce psaná v C, která na standardní výstup vytiskne řetězec | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h/greeter.c |
34 | greeter_h/greeter.h | prototyp (předběžná deklarace) funkce greeter | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h/greeter.h |
35 | greeter_h/call_via_cffi4.py | zavolání céčkovské knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h/call_via_cffi4.py |
36 | greeter_h/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h/call_via_cffi.sh |
37 | greeter_h/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h/make_library.sh |
38 | greeter_h/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h/clean.sh |
39 | greeter_h2/greeter.c | funkce psaná v C, která na standardní výstup vytiskne řetězec | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h2/greeter.c |
40 | greeter_h2/greeter.h | prototyp (předběžná deklarace) funkce greeter obalená v testu na existenci symbolu/makra | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h2/greeter.h |
41 | greeter_h2/call_via_cffi5.py | zavolání céčkovské knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h2/call_via_cffi5.py |
42 | greeter_h2/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h2/call_via_cffi.sh |
43 | greeter_h2/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h2/make_library.sh |
44 | greeter_h2/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h2/clean.sh |
45 | greeter_h3/greeter.c | funkce psaná v C, která na standardní výstup vytiskne řetězec | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h3/greeter.c |
46 | greeter_h3/greeter.h | test na existenci symbolu/makra, pokud makro neexistuje, provede se vložení dalšího souboru | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h3/greeter.h |
47 | greeter_h3/_greeter.h | prototyp (předběžná deklarace) funkce greeter bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h3/_greeter.h |
48 | greeter_h3/call_via_cffi5.py | zavolání céčkovské funkce z knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h3/call_via_cffi5.py |
49 | greeter_h3/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h3/call_via_cffi.sh |
50 | greeter_h3/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h3/make_library.sh |
51 | greeter_h3/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_h3/clean.sh |
52 | greeter_build/greeter.c | funkce psaná v C, která na standardní výstup vytiskne řetězec | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_build/greeter.c |
53 | greeter_build/greeter.h | prototyp (předběžná deklarace) funkce greeter bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_build/greeter.h |
54 | greeter_build/call_via_cffi7.py | skript pro překlad céčkovské funkce, vytvoření dynamicky linkované knihovny a zavolání funkce z této knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_build/call_via_cffi7.py |
55 | greeter_build/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter_build/clean.sh |
56 | vector_printer/vector_printer.c | funkce psaná v C, která akceptuje jako svůj parametr strukturu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer/vector_printer.c |
57 | vector_printer/vector_printer.h | prototyp (předběžná deklarace) funkce print_vector bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer/vector_printer.h |
58 | vector_printer/call_via_cffi.sh | zavolání céčkovské funkce z knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer/call_via_cffi.sh |
59 | vector_printer/call_via_cffi.py | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer/call_via_cffi.py |
60 | vector_printer/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer/make_library.sh |
61 | vector_printer/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer/clean.sh |
62 | vector_printer2/vector_printer.c | funkce psaná v C, která akceptuje jako svůj parametr ukazatel na strukturu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer2/vector_printer.c |
63 | vector_printer2/vector_printer.h | prototyp (předběžná deklarace) funkce print_vector bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer2/vector_printer.h |
64 | vector_printer2/call_via_cffi.sh | zavolání céčkovské funkce z knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer2/call_via_cffi.sh |
65 | vector_printer2/call_via_cffi.py | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer2/call_via_cffi.py |
66 | vector_printer2/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer2/make_library.sh |
67 | vector_printer2/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vector_printer2/clean.sh |
68 | array_printer1/array_printer.c | funkce naprogramovaná v C, která akceptuje pole s prvky typu float | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer1/array_printer.c |
69 | array_printer1/array_printer.h | prototyp (předběžná deklarace) funkce print_array bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer1/array_printer.h |
70 | array_printer1/call_via_cffi.py | zavolání céčkovské funkce z knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer1/call_via_cffi.sh |
71 | array_printer1/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer1/call_via_cffi.py |
72 | array_printer1/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer1/make_library.sh |
73 | array_printer1/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer1/clean.sh |
74 | array_printer2/array_printer.c | funkce naprogramovaná v C, která akceptuje pole s prvky typu float | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer2/array_printer.c |
75 | array_printer2/array_printer.h | prototyp (předběžná deklarace) funkce print_array bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer2/array_printer.h |
76 | array_printer2/call_via_cffi.py | zavolání céčkovské funkce z knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer2/call_via_cffi.sh |
77 | array_printer2/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer2/call_via_cffi.py |
78 | array_printer2/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer2/make_library.sh |
79 | array_printer2/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer2/clean.sh |
80 | array_printer3/array_printer.c | funkce naprogramovaná v C, která akceptuje pole s prvky typu float | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer3/array_printer.c |
81 | array_printer3/array_printer.h | prototyp (předběžná deklarace) funkce print_array bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer3/array_printer.h |
82 | array_printer3/call_via_cffi.py | zavolání céčkovské funkce z knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer3/call_via_cffi.sh |
83 | array_printer3/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer3/call_via_cffi.py |
84 | array_printer3/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer3/make_library.sh |
85 | array_printer3/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer3/clean.sh |
86 | array_printer4/array_printer.c | funkce naprogramovaná v C, která akceptuje pole s prvky typu float | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer4/array_printer.c |
87 | array_printer4/array_printer.h | prototyp (předběžná deklarace) funkce print_array bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer4/array_printer.h |
88 | array_printer4/call_via_cffi.py | zavolání céčkovské funkce z knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer4/call_via_cffi.sh |
89 | array_printer4/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer4/call_via_cffi.py |
90 | array_printer4/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer4/make_library.sh |
91 | array_printer4/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer4/clean.sh |
92 | array_printer5/array_printer.c | funkce naprogramovaná v C, která akceptuje pole s prvky typu vector_t | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer5/array_printer.c |
93 | array_printer5/array_printer.h | prototyp (předběžná deklarace) funkce print_array bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer5/array_printer.h |
94 | array_printer5/call_via_cffi.py | zavolání céčkovské funkce z knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer5/call_via_cffi.sh |
95 | array_printer5/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer5/call_via_cffi.py |
96 | array_printer5/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer5/make_library.sh |
97 | array_printer5/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer5/clean.sh |
92 | array_printer6/array_printer.c | funkce naprogramovaná v C, která akceptuje dvourozměrné pole s prvky typu float | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer6/array_printer.c |
93 | array_printer6/array_printer.h | prototyp (předběžná deklarace) funkce print_array bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer6/array_printer.h |
94 | array_printer6/call_via_cffi.py | zavolání céčkovské funkce z knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer6/call_via_cffi.sh |
95 | array_printer6/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer6/call_via_cffi.py |
96 | array_printer6/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer6/make_library.sh |
97 | array_printer6/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer6/clean.sh |
98 | array_printer7/array_printer.c | funkce naprogramovaná v C, která akceptuje dvourozměrné pole s prvky typu float | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer7/array_printer.c |
99 | array_printer7/array_printer.h | prototyp (předběžná deklarace) funkce print_array bez dalších informací | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer7/array_printer.h |
100 | array_printer7/call_via_cffi.py | zavolání céčkovské funkce z knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer7/call_via_cffi.sh |
101 | array_printer7/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer7/call_via_cffi.py |
102 | array_printer7/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer7/make_library.sh |
103 | array_printer7/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/array_printer7/clean.sh |
20. Odkazy na Internetu
- ctypes – A foreign function library for Python
https://docs.python.org/3/library/ctypes.html - Pygame: display
https://www.pygame.org/docs/ref/display.html - Pygame: event
https://www.pygame.org/docs/ref/event.html - Pygame: image
https://www.pygame.org/docs/ref/image.html - Pygame: clock
https://www.pygame.org/docs/ref/time.html#pygame.time.Clock - Fraktály v počítačové grafice XII
https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xii/ - Fraktály v počítačové grafice XIII
https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xiii/ - Fraktály v počítačové grafice XIV
https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xiv/ - CFFI documentation
https://cffi.readthedocs.io/en/latest/ - cffi 1.15.1 na PyPi
https://pypi.org/project/cffi/ - Python Bindings: Calling C or C++ From Python
https://realpython.com/python-bindings-overview/ - Interfacing with C/C++ Libraries
https://docs.python-guide.org/scenarios/clibs/ - Cython, pybind11, cffi – which tool should you choose?
http://blog.behnel.de/posts/cython-pybind11-cffi-which-tool-to-choose.html - Python FFI with ctypes and cffi
https://eli.thegreenplace.net/2013/03/09/python-ffi-with-ctypes-and-cffi - Propojení Go s Pythonem s využitím cgo a ctypes
https://www.root.cz/clanky/propojeni-go-s-pythonem-s-vyuzitim-cgo-a-ctypes/ - Propojení Go s Pythonem s využitím cgo a ctypes (2. část)
https://www.root.cz/clanky/propojeni-go-s-pythonem-s-vyuzitim-cgo-a-ctypes-2-cast/ - Programovací jazyk Rust: použití FFI pro volání funkcí z nativních knihoven
https://www.root.cz/clanky/programovaci-jazyk-rust-pouziti-ffi-pro-volani-funkci-z-nativnich-knihoven/ - Programovací jazyk Rust: použití FFI při předávání struktur
https://www.root.cz/clanky/programovaci-jazyk-rust-pouziti-ffi-pri-predavani-struktur/ - Programovací jazyk Rust: použití FFI pro volání funkcí z nativních knihoven (2. část)
https://www.root.cz/clanky/programovaci-jazyk-rust-pouziti-ffi-pro-volani-funkci-z-nativnich-knihoven-2-cast/ - Dynamic-link library
https://en.wikipedia.org/wiki/Dynamic-link_library - Úvod do jazyka C: Deklarace funkcí
https://www.fi.muni.cz/usr/jkucera/pb071/sl5.htm - Using standard library headers with CFFI
https://stackoverflow.com/questions/57481873/using-standard-library-headers-with-cffi - Preparing and Distributing modules
https://cffi.readthedocs.io/en/latest/cdef.html - C Arrays
https://www.programiz.com/c-programming/c-arrays - C Arrays
https://www.w3schools.com/c/c_arrays.php - Array of Structures in C
https://overiq.com/c-programming-101/array-of-structures-in-c/#google_vignette