From 1da33021c74a2c72eb98babf88c28a194d0c3552 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 26 Jul 2026 10:45:24 +0000 Subject: [PATCH 1/6] Reorganize C sources into src/, include/, and tests/. Match the MicroPython-style usermod layout used by sibling usdl2: build glue stays at the repo root for USER_C_MODULES discovery, headers live under include/, sources under src/, and smoke/parity scripts under tests/. --- AGENTS.md | 35 ++++++++++++++++ README.md | 42 ++++++++++--------- REFACTOR_PLAN.md | 2 + circuitpython.mk | 26 ++++++------ docs/scripts/generate_reference.py | 16 +++++-- font_8x14.h => include/font_8x14.h | 0 font_8x16.h => include/font_8x16.h | 0 font_8x8.h => include/font_8x8.h | 0 gfx_area_mp.h => include/gfx_area_mp.h | 0 .../gfx_bindings_mp.h | 0 gfx_bmp565.h => include/gfx_bmp565.h | 0 gfx_canvas_mp.h => include/gfx_canvas_mp.h | 0 .../gfx_capabilities.h | 0 gfx_core.h => include/gfx_core.h | 0 gfx_draw.h => include/gfx_draw.h | 0 gfx_files.h => include/gfx_files.h | 0 gfx_font.h => include/gfx_font.h | 0 .../gfx_framebuffer.h | 0 gfx_shapes.h => include/gfx_shapes.h | 0 .../graphics_qstrdefs.h | 0 micropython.cmake | 24 +++++------ micropython.mk | 24 +++++------ scripts/sync_fonts.py | 5 ++- setup.py | 21 ++++++---- gfx_area_mp.c => src/gfx_area_mp.c | 0 gfx_bindings_mp.c => src/gfx_bindings_mp.c | 0 gfx_bmp565.c => src/gfx_bmp565.c | 0 gfx_canvas_mp.c => src/gfx_canvas_mp.c | 0 gfx_capabilities.c => src/gfx_capabilities.c | 0 gfx_draw.c => src/gfx_draw.c | 0 gfx_files.c => src/gfx_files.c | 0 gfx_font.c => src/gfx_font.c | 0 gfx_framebuffer.c => src/gfx_framebuffer.c | 0 gfx_module_cpy.c => src/gfx_module_cpy.c | 0 gfx_module_mp.c => src/gfx_module_mp.c | 0 gfx_shapes.c => src/gfx_shapes.c | 0 test_area.py => tests/test_area.py | 0 test_graphics.py => tests/test_graphics.py | 0 test_parity.py => tests/test_parity.py | 0 .../test_parity_kwargs.py | 0 test_subclass.py => tests/test_subclass.py | 0 web/index.html | 10 ++--- 42 files changed, 129 insertions(+), 76 deletions(-) create mode 100644 AGENTS.md rename font_8x14.h => include/font_8x14.h (100%) rename font_8x16.h => include/font_8x16.h (100%) rename font_8x8.h => include/font_8x8.h (100%) rename gfx_area_mp.h => include/gfx_area_mp.h (100%) rename gfx_bindings_mp.h => include/gfx_bindings_mp.h (100%) rename gfx_bmp565.h => include/gfx_bmp565.h (100%) rename gfx_canvas_mp.h => include/gfx_canvas_mp.h (100%) rename gfx_capabilities.h => include/gfx_capabilities.h (100%) rename gfx_core.h => include/gfx_core.h (100%) rename gfx_draw.h => include/gfx_draw.h (100%) rename gfx_files.h => include/gfx_files.h (100%) rename gfx_font.h => include/gfx_font.h (100%) rename gfx_framebuffer.h => include/gfx_framebuffer.h (100%) rename gfx_shapes.h => include/gfx_shapes.h (100%) rename graphics_qstrdefs.h => include/graphics_qstrdefs.h (100%) rename gfx_area_mp.c => src/gfx_area_mp.c (100%) rename gfx_bindings_mp.c => src/gfx_bindings_mp.c (100%) rename gfx_bmp565.c => src/gfx_bmp565.c (100%) rename gfx_canvas_mp.c => src/gfx_canvas_mp.c (100%) rename gfx_capabilities.c => src/gfx_capabilities.c (100%) rename gfx_draw.c => src/gfx_draw.c (100%) rename gfx_files.c => src/gfx_files.c (100%) rename gfx_font.c => src/gfx_font.c (100%) rename gfx_framebuffer.c => src/gfx_framebuffer.c (100%) rename gfx_module_cpy.c => src/gfx_module_cpy.c (100%) rename gfx_module_mp.c => src/gfx_module_mp.c (100%) rename gfx_shapes.c => src/gfx_shapes.c (100%) rename test_area.py => tests/test_area.py (100%) rename test_graphics.py => tests/test_graphics.py (100%) rename test_parity.py => tests/test_parity.py (100%) rename test_parity_kwargs.py => tests/test_parity_kwargs.py (100%) rename test_subclass.py => tests/test_subclass.py (100%) diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2bf9ce1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,35 @@ +# AGENTS.md — graphics + +Native all-C **graphics** module for MicroPython, CircuitPython, and CPython +(`import graphics`). Prefer this wheel on desktop/Android when available; the +pure-Python alternative is pydisplay's `src/lib/graphics`. + +## Layout + +MicroPython-style usermod layout (same pattern as sibling `usdl2`): + +- Root: `micropython.mk`, `micropython.cmake`, `circuitpython.mk`, `setup.py`, + patch scripts — build glue stays here for `USER_C_MODULES` discovery +- `src/` — all `.c` sources (shared core + MP/CP bindings + CPython extension) +- `include/` — shared headers (`gfx_*.h`, `font_8x*.h`, `graphics_qstrdefs.h`) +- `tests/` — smoke and parity scripts +- No `.c` / `.h` at repo root + +## Smoke + +```bash +python3 -m venv .venv +.venv/bin/pip install -e . +.venv/bin/python tests/test_area.py +.venv/bin/python tests/test_graphics.py +.venv/bin/python tests/test_subclass.py +``` + +MicroPython / CircuitPython: build via cmods `./build_mp.sh` / +`lv_circuitpython_mod/build_cp.sh`, then run the same scripts under `tests/`. + +## After C changes + +Refresh pydisplay’s committed runtimes (desktop bins + PyScript vendor wasm) +with `../build_pydisplay_runtimes.sh` from the cmods workspace root when the +usermod is linked into those interpreters. diff --git a/README.md b/README.md index 4297eed..5073405 100644 --- a/README.md +++ b/README.md @@ -58,30 +58,32 @@ MIT (framebuf algorithms derived from MicroPython `extmod/modframebuf.c`, Damien ``` graphics/ - gfx_core.h # types, format IDs, Area geometry - gfx_framebuffer.c/h # format pixel ops - gfx_shapes.c/h # drawing algorithms via gfx_canvas_t - gfx_draw.c/h # Draw clip stack (C core) - gfx_font.c/h # text8 - gfx_capabilities.c/h # capabilities reporting - gfx_area_mp.c # MicroPython Area bindings - gfx_module_mp.c # MicroPython module registration - gfx_module_cpy.c # CPython module registration - micropython.mk - circuitpython.mk - test_area.py - test_graphics.py - test_subclass.py + micropython.mk / micropython.cmake / circuitpython.mk / setup.py + include/ # shared headers (gfx_*.h, font_8x*.h, qstrs) + src/ # C sources (core + MP/CP/CPython bindings) + tests/ # smoke / parity tests + docs/ scripts/ web/ ``` +Core vs bindings (under `include/` + `src/`): + +- `gfx_core.h` — types, format IDs, Area geometry +- `gfx_framebuffer` — format pixel ops +- `gfx_shapes` — drawing algorithms via `gfx_canvas_t` +- `gfx_draw` — Draw clip stack (C core) +- `gfx_font` — text8/14/16 +- `gfx_capabilities` — capabilities reporting +- `gfx_area_mp` / `gfx_bindings_mp` / `gfx_module_mp` — MicroPython / CircuitPython +- `gfx_module_cpy` — CPython module registration + ### CPython (editable) ```bash python3 -m venv .venv .venv/bin/pip install -e . -.venv/bin/python test_area.py -.venv/bin/python test_graphics.py -.venv/bin/python test_subclass.py +.venv/bin/python tests/test_area.py +.venv/bin/python tests/test_graphics.py +.venv/bin/python tests/test_subclass.py ``` ### MicroPython @@ -101,9 +103,9 @@ cd micropython/ports/unix make submodules make USER_C_MODULES=../../.. cd ../../.. -./micropython/ports/unix/build-standard/micropython graphics/test_area.py -./micropython/ports/unix/build-standard/micropython graphics/test_graphics.py -./micropython/ports/unix/build-standard/micropython graphics/test_subclass.py +./micropython/ports/unix/build-standard/micropython graphics/tests/test_area.py +./micropython/ports/unix/build-standard/micropython graphics/tests/test_graphics.py +./micropython/ports/unix/build-standard/micropython graphics/tests/test_subclass.py ``` ([cmods](https://github.com/PyDevices/cmods) is an optional convenience workspace with `./build_mp.sh`; it is not required.) diff --git a/REFACTOR_PLAN.md b/REFACTOR_PLAN.md index 4ffc2e6..b79316e 100644 --- a/REFACTOR_PLAN.md +++ b/REFACTOR_PLAN.md @@ -6,6 +6,8 @@ **Do not modify:** upstream `micropython/` / `circuitpython/` clones (leave uncommitted). +**On-disk layout:** `include/` (headers) + `src/` (`.c`) + `tests/` — see README / `AGENTS.md`. Layer names below are logical units, not repo-root paths. + --- ## Architecture principles diff --git a/circuitpython.mk b/circuitpython.mk index a5f15a2..e86c93a 100644 --- a/circuitpython.mk +++ b/circuitpython.mk @@ -1,21 +1,21 @@ # CircuitPython build glue for graphics (unix coverage). GRAPHICS_MOD_DIR ?= $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) -CFLAGS += -I$(GRAPHICS_MOD_DIR) -DCIRCUITPY_GRAPHICS=1 -QSTR_DEFS += $(GRAPHICS_MOD_DIR)/graphics_qstrdefs.h +CFLAGS += -I$(GRAPHICS_MOD_DIR)/include -DCIRCUITPY_GRAPHICS=1 +QSTR_DEFS += $(GRAPHICS_MOD_DIR)/include/graphics_qstrdefs.h GRAPHICS_SOURCES := \ - $(GRAPHICS_MOD_DIR)/gfx_module_mp.c \ - $(GRAPHICS_MOD_DIR)/gfx_bindings_mp.c \ - $(GRAPHICS_MOD_DIR)/gfx_canvas_mp.c \ - $(GRAPHICS_MOD_DIR)/gfx_framebuffer.c \ - $(GRAPHICS_MOD_DIR)/gfx_shapes.c \ - $(GRAPHICS_MOD_DIR)/gfx_draw.c \ - $(GRAPHICS_MOD_DIR)/gfx_font.c \ - $(GRAPHICS_MOD_DIR)/gfx_bmp565.c \ - $(GRAPHICS_MOD_DIR)/gfx_files.c \ - $(GRAPHICS_MOD_DIR)/gfx_capabilities.c \ - $(GRAPHICS_MOD_DIR)/gfx_area_mp.c + $(GRAPHICS_MOD_DIR)/src/gfx_module_mp.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_bindings_mp.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_canvas_mp.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_framebuffer.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_shapes.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_draw.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_font.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_bmp565.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_files.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_capabilities.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_area_mp.c GRAPHICS_SUPPRESS_CFLAGS := -Wno-unused-function -Wno-sign-compare $(foreach _gfx,$(GRAPHICS_SOURCES),$(eval $(BUILD)/$(_gfx:.c=.o): CFLAGS += $(GRAPHICS_SUPPRESS_CFLAGS))) diff --git a/docs/scripts/generate_reference.py b/docs/scripts/generate_reference.py index 7876e1b..43320b6 100644 --- a/docs/scripts/generate_reference.py +++ b/docs/scripts/generate_reference.py @@ -17,7 +17,7 @@ REPOSITORY_URL = "https://github.com/PyDevices/graphics" PROJECT_DOCS_URL = "https://pydevices.github.io/graphics/api" SOURCEY_VERSION = "3.6.5" -CPYTHON_BINDING = "gfx_module_cpy.c" +CPYTHON_BINDING = "src/gfx_module_cpy.c" CLASS_SPECS: Dict[str, Dict[str, object]] = { @@ -219,13 +219,23 @@ "native-drawing", "Native drawing and framebuffer API", "Public C declarations for geometry, framebuffer operations, shapes, and clipping.", - ("gfx_core.h", "gfx_framebuffer.h", "gfx_shapes.h", "gfx_draw.h"), + ( + "include/gfx_core.h", + "include/gfx_framebuffer.h", + "include/gfx_shapes.h", + "include/gfx_draw.h", + ), ), ( "native-assets-runtime", "Native fonts, images, and runtime API", "Public C declarations for fonts, BMP565 data, image conversion, and capability reporting.", - ("gfx_font.h", "gfx_bmp565.h", "gfx_files.h", "gfx_capabilities.h"), + ( + "include/gfx_font.h", + "include/gfx_bmp565.h", + "include/gfx_files.h", + "include/gfx_capabilities.h", + ), ), ) diff --git a/font_8x14.h b/include/font_8x14.h similarity index 100% rename from font_8x14.h rename to include/font_8x14.h diff --git a/font_8x16.h b/include/font_8x16.h similarity index 100% rename from font_8x16.h rename to include/font_8x16.h diff --git a/font_8x8.h b/include/font_8x8.h similarity index 100% rename from font_8x8.h rename to include/font_8x8.h diff --git a/gfx_area_mp.h b/include/gfx_area_mp.h similarity index 100% rename from gfx_area_mp.h rename to include/gfx_area_mp.h diff --git a/gfx_bindings_mp.h b/include/gfx_bindings_mp.h similarity index 100% rename from gfx_bindings_mp.h rename to include/gfx_bindings_mp.h diff --git a/gfx_bmp565.h b/include/gfx_bmp565.h similarity index 100% rename from gfx_bmp565.h rename to include/gfx_bmp565.h diff --git a/gfx_canvas_mp.h b/include/gfx_canvas_mp.h similarity index 100% rename from gfx_canvas_mp.h rename to include/gfx_canvas_mp.h diff --git a/gfx_capabilities.h b/include/gfx_capabilities.h similarity index 100% rename from gfx_capabilities.h rename to include/gfx_capabilities.h diff --git a/gfx_core.h b/include/gfx_core.h similarity index 100% rename from gfx_core.h rename to include/gfx_core.h diff --git a/gfx_draw.h b/include/gfx_draw.h similarity index 100% rename from gfx_draw.h rename to include/gfx_draw.h diff --git a/gfx_files.h b/include/gfx_files.h similarity index 100% rename from gfx_files.h rename to include/gfx_files.h diff --git a/gfx_font.h b/include/gfx_font.h similarity index 100% rename from gfx_font.h rename to include/gfx_font.h diff --git a/gfx_framebuffer.h b/include/gfx_framebuffer.h similarity index 100% rename from gfx_framebuffer.h rename to include/gfx_framebuffer.h diff --git a/gfx_shapes.h b/include/gfx_shapes.h similarity index 100% rename from gfx_shapes.h rename to include/gfx_shapes.h diff --git a/graphics_qstrdefs.h b/include/graphics_qstrdefs.h similarity index 100% rename from graphics_qstrdefs.h rename to include/graphics_qstrdefs.h diff --git a/micropython.cmake b/micropython.cmake index cbdb472..3dd6b2e 100644 --- a/micropython.cmake +++ b/micropython.cmake @@ -14,20 +14,20 @@ set(GRAPHICS_MOD_DIR ${CMAKE_CURRENT_LIST_DIR}) add_library(graphics INTERFACE) target_sources(graphics INTERFACE - ${GRAPHICS_MOD_DIR}/gfx_module_mp.c - ${GRAPHICS_MOD_DIR}/gfx_bindings_mp.c - ${GRAPHICS_MOD_DIR}/gfx_canvas_mp.c - ${GRAPHICS_MOD_DIR}/gfx_framebuffer.c - ${GRAPHICS_MOD_DIR}/gfx_shapes.c - ${GRAPHICS_MOD_DIR}/gfx_draw.c - ${GRAPHICS_MOD_DIR}/gfx_font.c - ${GRAPHICS_MOD_DIR}/gfx_bmp565.c - ${GRAPHICS_MOD_DIR}/gfx_files.c - ${GRAPHICS_MOD_DIR}/gfx_capabilities.c - ${GRAPHICS_MOD_DIR}/gfx_area_mp.c + ${GRAPHICS_MOD_DIR}/src/gfx_module_mp.c + ${GRAPHICS_MOD_DIR}/src/gfx_bindings_mp.c + ${GRAPHICS_MOD_DIR}/src/gfx_canvas_mp.c + ${GRAPHICS_MOD_DIR}/src/gfx_framebuffer.c + ${GRAPHICS_MOD_DIR}/src/gfx_shapes.c + ${GRAPHICS_MOD_DIR}/src/gfx_draw.c + ${GRAPHICS_MOD_DIR}/src/gfx_font.c + ${GRAPHICS_MOD_DIR}/src/gfx_bmp565.c + ${GRAPHICS_MOD_DIR}/src/gfx_files.c + ${GRAPHICS_MOD_DIR}/src/gfx_capabilities.c + ${GRAPHICS_MOD_DIR}/src/gfx_area_mp.c ) -target_include_directories(graphics INTERFACE ${GRAPHICS_MOD_DIR}) +target_include_directories(graphics INTERFACE ${GRAPHICS_MOD_DIR}/include) target_compile_options(graphics INTERFACE -Wno-unused-function -Wno-sign-compare diff --git a/micropython.mk b/micropython.mk index 3f71d5d..7c0b611 100644 --- a/micropython.mk +++ b/micropython.mk @@ -5,7 +5,7 @@ GRAPHICS_MOD_DIR := $(USERMOD_DIR) -CFLAGS_USERMOD += -I$(GRAPHICS_MOD_DIR) -Wno-unused-function -Wno-sign-compare -Wno-unused-const-variable +CFLAGS_USERMOD += -I$(GRAPHICS_MOD_DIR)/include -Wno-unused-function -Wno-sign-compare -Wno-unused-const-variable # math: cosf/sinf etc. # - mimxrt/samd: bare `ld` — absolute libm.a in LDFLAGS # - stm32: CROSS_COMPILE is set *after* py.mk, so $(CC) here is the host @@ -20,14 +20,14 @@ LDFLAGS_USERMOD += -lm endif SRC_USERMOD_C += \ - $(GRAPHICS_MOD_DIR)/gfx_module_mp.c \ - $(GRAPHICS_MOD_DIR)/gfx_bindings_mp.c \ - $(GRAPHICS_MOD_DIR)/gfx_canvas_mp.c \ - $(GRAPHICS_MOD_DIR)/gfx_framebuffer.c \ - $(GRAPHICS_MOD_DIR)/gfx_shapes.c \ - $(GRAPHICS_MOD_DIR)/gfx_draw.c \ - $(GRAPHICS_MOD_DIR)/gfx_font.c \ - $(GRAPHICS_MOD_DIR)/gfx_bmp565.c \ - $(GRAPHICS_MOD_DIR)/gfx_files.c \ - $(GRAPHICS_MOD_DIR)/gfx_capabilities.c \ - $(GRAPHICS_MOD_DIR)/gfx_area_mp.c + $(GRAPHICS_MOD_DIR)/src/gfx_module_mp.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_bindings_mp.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_canvas_mp.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_framebuffer.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_shapes.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_draw.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_font.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_bmp565.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_files.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_capabilities.c \ + $(GRAPHICS_MOD_DIR)/src/gfx_area_mp.c diff --git a/scripts/sync_fonts.py b/scripts/sync_fonts.py index 7991f95..33fc65c 100644 --- a/scripts/sync_fonts.py +++ b/scripts/sync_fonts.py @@ -3,7 +3,7 @@ """Generate C font headers from pydisplay ``lib/graphics/_font_8x*.py`` romfont data. Canonical font bytes live in pydisplay; this script writes ``font_8x8.h``, -``font_8x14.h``, and ``font_8x16.h`` next to ``gfx_font.c``. +``font_8x14.h``, and ``font_8x16.h`` under ``include/``. Usage (from repo root):: @@ -21,6 +21,7 @@ import sys _GRAPHICS_DIR = Path(__file__).resolve().parents[1] +_INCLUDE_DIR = _GRAPHICS_DIR / "include" _DEFAULT_PYDISPLAY = _GRAPHICS_DIR.parent.parent / "pydisplay" @@ -80,7 +81,7 @@ def sync_fonts(*, pydisplay: Path, check: bool = False) -> None: expected = 256 * height if len(data) != expected: raise SystemExit(f"{py_path.name}: expected {expected} bytes, got {len(data)}") - out_path = _GRAPHICS_DIR / f"{array_name}.h" + out_path = _INCLUDE_DIR / f"{array_name}.h" content = _render_header(c_name, array_name, data, height) if check: if not out_path.is_file(): diff --git a/setup.py b/setup.py index 5ccf3a8..7da2216 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,13 @@ # SPDX-License-Identifier: MIT """Build native graphics C extension.""" +import os import sys from setuptools import Extension, setup +ROOT = os.path.dirname(os.path.abspath(__file__)) + if sys.platform == "win32": extra_compile_args = ["/wd4996"] extra_link_args = [] @@ -16,14 +19,14 @@ extra_link_args = ["-lm"] GFX_SOURCES = [ - "gfx_module_cpy.c", - "gfx_framebuffer.c", - "gfx_shapes.c", - "gfx_draw.c", - "gfx_font.c", - "gfx_bmp565.c", - "gfx_files.c", - "gfx_capabilities.c", + os.path.join("src", "gfx_module_cpy.c"), + os.path.join("src", "gfx_framebuffer.c"), + os.path.join("src", "gfx_shapes.c"), + os.path.join("src", "gfx_draw.c"), + os.path.join("src", "gfx_font.c"), + os.path.join("src", "gfx_bmp565.c"), + os.path.join("src", "gfx_files.c"), + os.path.join("src", "gfx_capabilities.c"), ] setup( @@ -34,7 +37,7 @@ Extension( "graphics", sources=GFX_SOURCES, - include_dirs=["."], + include_dirs=[os.path.join(ROOT, "include")], extra_compile_args=extra_compile_args, extra_link_args=extra_link_args, ), diff --git a/gfx_area_mp.c b/src/gfx_area_mp.c similarity index 100% rename from gfx_area_mp.c rename to src/gfx_area_mp.c diff --git a/gfx_bindings_mp.c b/src/gfx_bindings_mp.c similarity index 100% rename from gfx_bindings_mp.c rename to src/gfx_bindings_mp.c diff --git a/gfx_bmp565.c b/src/gfx_bmp565.c similarity index 100% rename from gfx_bmp565.c rename to src/gfx_bmp565.c diff --git a/gfx_canvas_mp.c b/src/gfx_canvas_mp.c similarity index 100% rename from gfx_canvas_mp.c rename to src/gfx_canvas_mp.c diff --git a/gfx_capabilities.c b/src/gfx_capabilities.c similarity index 100% rename from gfx_capabilities.c rename to src/gfx_capabilities.c diff --git a/gfx_draw.c b/src/gfx_draw.c similarity index 100% rename from gfx_draw.c rename to src/gfx_draw.c diff --git a/gfx_files.c b/src/gfx_files.c similarity index 100% rename from gfx_files.c rename to src/gfx_files.c diff --git a/gfx_font.c b/src/gfx_font.c similarity index 100% rename from gfx_font.c rename to src/gfx_font.c diff --git a/gfx_framebuffer.c b/src/gfx_framebuffer.c similarity index 100% rename from gfx_framebuffer.c rename to src/gfx_framebuffer.c diff --git a/gfx_module_cpy.c b/src/gfx_module_cpy.c similarity index 100% rename from gfx_module_cpy.c rename to src/gfx_module_cpy.c diff --git a/gfx_module_mp.c b/src/gfx_module_mp.c similarity index 100% rename from gfx_module_mp.c rename to src/gfx_module_mp.c diff --git a/gfx_shapes.c b/src/gfx_shapes.c similarity index 100% rename from gfx_shapes.c rename to src/gfx_shapes.c diff --git a/test_area.py b/tests/test_area.py similarity index 100% rename from test_area.py rename to tests/test_area.py diff --git a/test_graphics.py b/tests/test_graphics.py similarity index 100% rename from test_graphics.py rename to tests/test_graphics.py diff --git a/test_parity.py b/tests/test_parity.py similarity index 100% rename from test_parity.py rename to tests/test_parity.py diff --git a/test_parity_kwargs.py b/tests/test_parity_kwargs.py similarity index 100% rename from test_parity_kwargs.py rename to tests/test_parity_kwargs.py diff --git a/test_subclass.py b/tests/test_subclass.py similarity index 100% rename from test_subclass.py rename to tests/test_subclass.py diff --git a/web/index.html b/web/index.html index f6847d1..2cf6baa 100644 --- a/web/index.html +++ b/web/index.html @@ -80,9 +80,9 @@

CPython

Editable install, then run the test scripts.

python3 -m venv .venv
 .venv/bin/pip install -e .
-.venv/bin/python test_area.py
-.venv/bin/python test_graphics.py
-.venv/bin/python test_subclass.py
+.venv/bin/python tests/test_area.py +.venv/bin/python tests/test_graphics.py +.venv/bin/python tests/test_subclass.py

MicroPython

@@ -92,9 +92,9 @@

MicroPython

make USER_C_MODULES=../../.. ./micropython/ports/unix/build-standard/micropython \ - graphics/test_area.py + graphics/tests/test_area.py ./micropython/ports/unix/build-standard/micropython \ - graphics/test_graphics.py + graphics/tests/test_graphics.py
From bcaf65f4b2f43b4b0316ce2525e89abed7f7c331 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 26 Jul 2026 10:45:25 +0000 Subject: [PATCH 2/6] Regenerate API reference for new source paths. Update pinned source links after the include/src move so Pages and verify-site.mjs resolve existing files at the documented commit. --- docs/generated/area-clipping.md | 54 +- docs/generated/drawing.md | 94 +-- docs/generated/fonts-images.md | 46 +- docs/generated/framebuffer.md | 64 +- docs/generated/introduction.md | 4 +- docs/generated/manifest.json | 828 ++++++++++++------------ docs/generated/module-formats.md | 24 +- docs/generated/native-assets-runtime.md | 56 +- docs/generated/native-drawing.md | 70 +- 9 files changed, 620 insertions(+), 620 deletions(-) diff --git a/docs/generated/area-clipping.md b/docs/generated/area-clipping.md index fd29f06..d50a847 100644 --- a/docs/generated/area-clipping.md +++ b/docs/generated/area-clipping.md @@ -3,7 +3,7 @@ title: Area and clipping description: Rectangle geometry, scoped clip contexts, and clipped canvas operations. --- -Source snapshot: [`aa6041e9226bdf5172a656eef722b852ec02f270`](https://github.com/PyDevices/graphics/tree/aa6041e9226bdf5172a656eef722b852ec02f270). +Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). Rectangle geometry, scoped clip contexts, and clipped canvas operations. @@ -17,7 +17,7 @@ class Area(x=0, y=0, w=0, h=0) Immutable rectangle geometry used for bounds, clipping, and draw results. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L395) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L395) ## `graphics.Area.contains` @@ -27,7 +27,7 @@ Area.contains(point_or_x, y=None) Test whether the area contains a point. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L377) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L377) ## `graphics.Area.contains_area` @@ -37,7 +37,7 @@ Area.contains_area(other) Test whether another Area is fully contained. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L378) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L378) ## `graphics.Area.intersects` @@ -47,7 +47,7 @@ Area.intersects(other) Test whether two areas overlap. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L379) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L379) ## `graphics.Area.touches_or_intersects` @@ -57,7 +57,7 @@ Area.touches_or_intersects(other) Test whether two areas overlap or share an edge. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L380) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L380) ## `graphics.Area.shift` @@ -67,7 +67,7 @@ Area.shift(dx=0, dy=0) Return a copy translated by the requested offset. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L381) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L381) ## `graphics.Area.clip` @@ -77,7 +77,7 @@ Area.clip(other) Return the intersection with another Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L382) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L382) ## `graphics.Area.offset` @@ -87,7 +87,7 @@ Area.offset(left, top=None, right=None, bottom=None) Return an Area expanded independently on each edge. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L383) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L383) ## `graphics.Area.inset` @@ -97,7 +97,7 @@ Area.inset(left, top=None, right=None, bottom=None) Return an Area reduced independently on each edge. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L384) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L384) ## `graphics.Area.x` @@ -107,7 +107,7 @@ Area.x Left coordinate. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L229) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L229) ## `graphics.Area.y` @@ -117,7 +117,7 @@ Area.y Top coordinate. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L230) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L230) ## `graphics.Area.w` @@ -127,7 +127,7 @@ Area.w Width in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L231) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L231) ## `graphics.Area.h` @@ -137,7 +137,7 @@ Area.h Height in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L232) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L232) ## `graphics.ClipContext` @@ -147,7 +147,7 @@ class ClipContext(draw, area) Context manager returned by Draw.clip() for scoped clipping. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2147) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2147) ## `graphics.ClipContext.__enter__` @@ -157,7 +157,7 @@ ClipContext.__enter__() Push the requested clipping area and return the effective clip. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2140) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2140) ## `graphics.ClipContext.__exit__` @@ -167,7 +167,7 @@ ClipContext.__exit__(exc_type, exc_value, traceback) Pop the clipping area when leaving the context. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2141) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2141) ## `graphics.ClippedCanvas` @@ -177,7 +177,7 @@ class ClippedCanvas(canvas, clip) Canvas proxy that intersects writes with a fixed Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2414) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2414) ## `graphics.ClippedCanvas.pixel` @@ -187,7 +187,7 @@ ClippedCanvas.pixel(x, y, color=None) Read or write one pixel, depending on whether a color is supplied. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2402) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2402) ## `graphics.ClippedCanvas.fill` @@ -197,7 +197,7 @@ ClippedCanvas.fill(color) Fill the target canvas with one color and return the affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2403) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2403) ## `graphics.ClippedCanvas.fill_rect` @@ -207,7 +207,7 @@ ClippedCanvas.fill_rect(x, y, width, height, color) Fill a rectangular region and return the clipped affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2404) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2404) ## `graphics.ClippedCanvas.hline` @@ -217,7 +217,7 @@ ClippedCanvas.hline(x, y, width, color) Draw a horizontal line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2405) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2405) ## `graphics.ClippedCanvas.vline` @@ -227,7 +227,7 @@ ClippedCanvas.vline(x, y, height, color) Draw a vertical line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2406) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2406) ## `graphics.ClippedCanvas.blit_rect` @@ -237,7 +237,7 @@ ClippedCanvas.blit_rect(buffer, x, y, width, height) Copy a packed RGB565 rectangle onto the target. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2407) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2407) ## `graphics.ClippedCanvas.blit_transparent` @@ -247,7 +247,7 @@ ClippedCanvas.blit_transparent(buffer, x, y, width, height, key) Copy a packed RGB565 rectangle while skipping the transparent key. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2408) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2408) ## `graphics.ClippedCanvas.width` @@ -257,7 +257,7 @@ ClippedCanvas.width Width in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2223) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2223) ## `graphics.ClippedCanvas.height` @@ -267,4 +267,4 @@ ClippedCanvas.height Height in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2224) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2224) diff --git a/docs/generated/drawing.md b/docs/generated/drawing.md index a3b7eb8..e3fa62c 100644 --- a/docs/generated/drawing.md +++ b/docs/generated/drawing.md @@ -3,7 +3,7 @@ title: Drawing operations description: Object-oriented and module-level shape, text, polygon, and blit operations. --- -Source snapshot: [`aa6041e9226bdf5172a656eef722b852ec02f270`](https://github.com/PyDevices/graphics/tree/aa6041e9226bdf5172a656eef722b852ec02f270). +Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). Object-oriented and module-level shape, text, polygon, and blit operations. @@ -17,7 +17,7 @@ class Draw(canvas) Drawing facade that adds a bounded clip stack to any compatible canvas. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2085) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2085) ## `graphics.Draw.fill` @@ -27,7 +27,7 @@ Draw.fill(color) Fill the target canvas with one color and return the affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2057) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2057) ## `graphics.Draw.fill_rect` @@ -37,7 +37,7 @@ Draw.fill_rect(x, y, width, height, color) Fill a rectangular region and return the clipped affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2058) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2058) ## `graphics.Draw.pixel` @@ -47,7 +47,7 @@ Draw.pixel(x, y, color=None) Read or write one pixel, depending on whether a color is supplied. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2059) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2059) ## `graphics.Draw.hline` @@ -57,7 +57,7 @@ Draw.hline(x, y, width, color) Draw a horizontal line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2060) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2060) ## `graphics.Draw.vline` @@ -67,7 +67,7 @@ Draw.vline(x, y, height, color) Draw a vertical line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2061) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2061) ## `graphics.Draw.line` @@ -77,7 +77,7 @@ Draw.line(x1, y1, x2, y2, color) Draw a line between two points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2062) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2062) ## `graphics.Draw.rect` @@ -87,7 +87,7 @@ Draw.rect(x, y, width, height, color, fill=False) Draw an outlined or filled rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2063) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2063) ## `graphics.Draw.round_rect` @@ -97,7 +97,7 @@ Draw.round_rect(x, y, width, height, radius, color, fill=False) Draw an outlined or filled rounded rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2064) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2064) ## `graphics.Draw.circle` @@ -107,7 +107,7 @@ Draw.circle(x, y, radius, color, fill=False) Draw an outlined or filled circle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2065) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2065) ## `graphics.Draw.ellipse` @@ -117,7 +117,7 @@ Draw.ellipse(x, y, x_radius, y_radius, color, fill=False, mask=0x0F) Draw selected quadrants of an outlined or filled ellipse. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2066) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2066) ## `graphics.Draw.arc` @@ -127,7 +127,7 @@ Draw.arc(x, y, radius, start_angle, end_angle, color) Draw a circular arc between two angles. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2067) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2067) ## `graphics.Draw.triangle` @@ -137,7 +137,7 @@ Draw.triangle(x0, y0, x1, y1, x2, y2, color, fill=False) Draw an outlined or filled triangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2068) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2068) ## `graphics.Draw.gradient_rect` @@ -147,7 +147,7 @@ Draw.gradient_rect(x, y, width, height, color1, color2=None, vertical=True) Fill a rectangle with a horizontal or vertical color gradient. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2069) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2069) ## `graphics.Draw.poly` @@ -157,7 +157,7 @@ Draw.poly(x, y, coordinates, color, fill=False) Draw a polygon from a packed coordinate buffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2070) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2070) ## `graphics.Draw.polygon` @@ -167,7 +167,7 @@ Draw.polygon(points, x, y, color, angle=0, center_x=0, center_y=0) Draw a translated and optionally rotated sequence of points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2071) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2071) ## `graphics.Draw.blit` @@ -177,7 +177,7 @@ Draw.blit(source, x, y, key=-1, palette=None) Copy another framebuffer onto the target with optional keying and palette lookup. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2072) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2072) ## `graphics.Draw.blit_rect` @@ -187,7 +187,7 @@ Draw.blit_rect(buffer, x, y, width, height) Copy a packed RGB565 rectangle onto the target. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2073) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2073) ## `graphics.Draw.blit_transparent` @@ -197,7 +197,7 @@ Draw.blit_transparent(buffer, x, y, width, height, key) Copy a packed RGB565 rectangle while skipping the transparent key. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2074) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2074) ## `graphics.Draw.text` @@ -207,7 +207,7 @@ Draw.text(text, x, y, color=1, scale=1, inverted=False, font_data=None, height=8 Render bitmap text with selectable height, scale, and inversion. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2075) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2075) ## `graphics.Draw.text8` @@ -217,7 +217,7 @@ Draw.text8(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 8-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2076) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2076) ## `graphics.Draw.text14` @@ -227,7 +227,7 @@ Draw.text14(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 14-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2077) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2077) ## `graphics.Draw.text16` @@ -237,7 +237,7 @@ Draw.text16(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 16-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2078) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2078) ## `graphics.Draw.clip` @@ -247,7 +247,7 @@ Draw.clip(area_or_x, y=None, width=None, height=None) Return the intersection with another Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2079) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2079) ## `graphics.fill` @@ -257,7 +257,7 @@ graphics.fill(canvas, color) Fill the target canvas with one color and return the affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2953) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2953) ## `graphics.fill_rect` @@ -267,7 +267,7 @@ graphics.fill_rect(canvas, x, y, width, height, color) Fill a rectangular region and return the clipped affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2954) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2954) ## `graphics.pixel` @@ -277,7 +277,7 @@ graphics.pixel(canvas, x, y, color=None) Read or write one pixel, depending on whether a color is supplied. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2955) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2955) ## `graphics.hline` @@ -287,7 +287,7 @@ graphics.hline(canvas, x, y, width, color) Draw a horizontal line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2956) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2956) ## `graphics.vline` @@ -297,7 +297,7 @@ graphics.vline(canvas, x, y, height, color) Draw a vertical line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2957) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2957) ## `graphics.line` @@ -307,7 +307,7 @@ graphics.line(canvas, x1, y1, x2, y2, color) Draw a line between two points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2958) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2958) ## `graphics.rect` @@ -317,7 +317,7 @@ graphics.rect(canvas, x, y, width, height, color, fill=False) Draw an outlined or filled rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2959) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2959) ## `graphics.round_rect` @@ -327,7 +327,7 @@ graphics.round_rect(canvas, x, y, width, height, radius, color, fill=False) Draw an outlined or filled rounded rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2960) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2960) ## `graphics.circle` @@ -337,7 +337,7 @@ graphics.circle(canvas, x, y, radius, color, fill=False) Draw an outlined or filled circle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2961) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2961) ## `graphics.ellipse` @@ -347,7 +347,7 @@ graphics.ellipse(canvas, x, y, x_radius, y_radius, color, fill=False, mask=0x0F) Draw selected quadrants of an outlined or filled ellipse. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2962) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2962) ## `graphics.arc` @@ -357,7 +357,7 @@ graphics.arc(canvas, x, y, radius, start_angle, end_angle, color) Draw a circular arc between two angles. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2963) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2963) ## `graphics.triangle` @@ -367,7 +367,7 @@ graphics.triangle(canvas, x0, y0, x1, y1, x2, y2, color, fill=False) Draw an outlined or filled triangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2964) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2964) ## `graphics.gradient_rect` @@ -377,7 +377,7 @@ graphics.gradient_rect(canvas, x, y, width, height, color1, color2=None, vertica Fill a rectangle with a horizontal or vertical color gradient. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2965) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2965) ## `graphics.poly` @@ -387,7 +387,7 @@ graphics.poly(canvas, x, y, coordinates, color, fill=False) Draw a polygon from a packed coordinate buffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2966) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2966) ## `graphics.blit` @@ -397,7 +397,7 @@ graphics.blit(canvas, source, x, y, key=-1, palette=None) Copy another framebuffer onto the target with optional keying and palette lookup. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2967) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2967) ## `graphics.blit_rect` @@ -407,7 +407,7 @@ graphics.blit_rect(canvas, buffer, x, y, width, height) Copy a packed RGB565 rectangle onto the target. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2968) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2968) ## `graphics.blit_transparent` @@ -417,7 +417,7 @@ graphics.blit_transparent(canvas, buffer, x, y, width, height, key) Copy a packed RGB565 rectangle while skipping the transparent key. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2969) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2969) ## `graphics.polygon` @@ -427,7 +427,7 @@ graphics.polygon(canvas, points, x, y, color, angle=0, center_x=0, center_y=0) Draw a translated and optionally rotated sequence of points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2970) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2970) ## `graphics.text` @@ -437,7 +437,7 @@ graphics.text(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=No Render bitmap text with selectable height, scale, and inversion. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2976) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2976) ## `graphics.text8` @@ -447,7 +447,7 @@ graphics.text8(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=N Render text with the built-in 8-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2977) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2977) ## `graphics.text14` @@ -457,7 +457,7 @@ graphics.text14(canvas, text, x, y, color=1, scale=1, inverted=False, font_data= Render text with the built-in 14-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2978) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2978) ## `graphics.text16` @@ -467,4 +467,4 @@ graphics.text16(canvas, text, x, y, color=1, scale=1, inverted=False, font_data= Render text with the built-in 16-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2979) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2979) diff --git a/docs/generated/fonts-images.md b/docs/generated/fonts-images.md index ec90ac1..87972af 100644 --- a/docs/generated/fonts-images.md +++ b/docs/generated/fonts-images.md @@ -3,7 +3,7 @@ title: Fonts and images description: Bitmap font rendering, RGB565 bitmap access, and image conversion helpers. --- -Source snapshot: [`aa6041e9226bdf5172a656eef722b852ec02f270`](https://github.com/PyDevices/graphics/tree/aa6041e9226bdf5172a656eef722b852ec02f270). +Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). Bitmap font rendering, RGB565 bitmap access, and image conversion helpers. @@ -17,7 +17,7 @@ class Font(font_data=None, height=0, cached=True) Bitmap font loader and renderer for built-in, file, or buffer-backed glyph data. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2643) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2643) ## `graphics.Font.text` @@ -27,7 +27,7 @@ Font.text(canvas, text, x, y, color, scale=1, inverted=False) Render bitmap text with selectable height, scale, and inversion. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2633) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2633) ## `graphics.Font.draw_char` @@ -37,7 +37,7 @@ Font.draw_char(char, x, y, canvas, color, scale=1, inverted=False) Render one glyph through this Font onto a canvas. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2634) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2634) ## `graphics.Font.text_width` @@ -47,7 +47,7 @@ Font.text_width(text, scale=1) Measure rendered text width at the selected scale. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2635) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2635) ## `graphics.Font.deinit` @@ -57,7 +57,7 @@ Font.deinit() Release resources owned by the object. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2636) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2636) ## `graphics.Font.export` @@ -67,7 +67,7 @@ Font.export(filename) Write cached font bytes to a file. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2637) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2637) ## `graphics.Font.width` @@ -77,7 +77,7 @@ Font.width Width in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2626) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2626) ## `graphics.Font.height` @@ -87,7 +87,7 @@ Font.height Height in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2627) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2627) ## `graphics.Font.font_name` @@ -97,7 +97,7 @@ Font.font_name Font source name or the in-memory/default identifier. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2628) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2628) ## `graphics.BMP565` @@ -107,7 +107,7 @@ class BMP565(filename=None, source=None, streamed=False, mirrored=False, width=N RGB565 bitmap reader supporting buffered and streamed access. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2934) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2934) ## `graphics.BMP565.save` @@ -117,7 +117,7 @@ BMP565.save(filename=None) Write the current image or bitmap to a versioned output path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2923) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2923) ## `graphics.BMP565.deinit` @@ -127,7 +127,7 @@ BMP565.deinit() Release resources owned by the object. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2924) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2924) ## `graphics.BMP565.width` @@ -137,7 +137,7 @@ BMP565.width Width in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2914) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2914) ## `graphics.BMP565.height` @@ -147,7 +147,7 @@ BMP565.height Height in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2915) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2915) ## `graphics.BMP565.buffer` @@ -157,7 +157,7 @@ BMP565.buffer Backing pixel buffer when the object owns or exposes one. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2916) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2916) ## `graphics.BMP565.bpp` @@ -167,7 +167,7 @@ BMP565.bpp Bitmap color depth in bits per pixel. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2917) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2917) ## `graphics.BMP565.BPP` @@ -177,7 +177,7 @@ BMP565.BPP Bitmap storage width in bytes per pixel. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2918) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2918) ## `graphics.load_image` @@ -187,7 +187,7 @@ graphics.load_image(path) Load a supported image file as a FrameBuffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2971) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2971) ## `graphics.save_image` @@ -197,7 +197,7 @@ graphics.save_image(framebuffer, path='screenshot') Save a FrameBuffer to a versioned image path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2972) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2972) ## `graphics.bmp_to_framebuffer` @@ -207,7 +207,7 @@ graphics.bmp_to_framebuffer(path) Decode a BMP file into a FrameBuffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2973) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2973) ## `graphics.pbm_to_framebuffer` @@ -217,7 +217,7 @@ graphics.pbm_to_framebuffer(path) Decode a PBM file into a FrameBuffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2974) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2974) ## `graphics.pgm_to_framebuffer` @@ -227,4 +227,4 @@ graphics.pgm_to_framebuffer(path) Decode a PGM file into a FrameBuffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2975) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2975) diff --git a/docs/generated/framebuffer.md b/docs/generated/framebuffer.md index 7300a1a..055dede 100644 --- a/docs/generated/framebuffer.md +++ b/docs/generated/framebuffer.md @@ -3,7 +3,7 @@ title: FrameBuffer description: Native framebuffer construction, drawing, text, blitting, scrolling, and file output. --- -Source snapshot: [`aa6041e9226bdf5172a656eef722b852ec02f270`](https://github.com/PyDevices/graphics/tree/aa6041e9226bdf5172a656eef722b852ec02f270). +Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). Native framebuffer construction, drawing, text, blitting, scrolling, and file output. @@ -17,7 +17,7 @@ class FrameBuffer(buffer, width, height, format, stride=None) A framebuf-compatible native drawing surface backed by a writable buffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1112) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1112) ## `graphics.FrameBuffer.from_file` @@ -27,7 +27,7 @@ FrameBuffer.from_file(path) Load a supported image file into a new FrameBuffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1082) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1082) ## `graphics.FrameBuffer.fill` @@ -37,7 +37,7 @@ FrameBuffer.fill(color) Fill the target canvas with one color and return the affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1083) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1083) ## `graphics.FrameBuffer.fill_rect` @@ -47,7 +47,7 @@ FrameBuffer.fill_rect(x, y, width, height, color) Fill a rectangular region and return the clipped affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1084) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1084) ## `graphics.FrameBuffer.pixel` @@ -57,7 +57,7 @@ FrameBuffer.pixel(x, y, color=None) Read or write one pixel, depending on whether a color is supplied. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1085) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1085) ## `graphics.FrameBuffer.hline` @@ -67,7 +67,7 @@ FrameBuffer.hline(x, y, width, color) Draw a horizontal line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1086) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1086) ## `graphics.FrameBuffer.vline` @@ -77,7 +77,7 @@ FrameBuffer.vline(x, y, height, color) Draw a vertical line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1087) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1087) ## `graphics.FrameBuffer.rect` @@ -87,7 +87,7 @@ FrameBuffer.rect(x, y, width, height, color, fill=False) Draw an outlined or filled rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1088) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1088) ## `graphics.FrameBuffer.round_rect` @@ -97,7 +97,7 @@ FrameBuffer.round_rect(x, y, width, height, radius, color, fill=False) Draw an outlined or filled rounded rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1089) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1089) ## `graphics.FrameBuffer.circle` @@ -107,7 +107,7 @@ FrameBuffer.circle(x, y, radius, color, fill=False) Draw an outlined or filled circle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1090) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1090) ## `graphics.FrameBuffer.line` @@ -117,7 +117,7 @@ FrameBuffer.line(x1, y1, x2, y2, color) Draw a line between two points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1091) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1091) ## `graphics.FrameBuffer.ellipse` @@ -127,7 +127,7 @@ FrameBuffer.ellipse(x, y, x_radius, y_radius, color, fill=False, mask=0x0F) Draw selected quadrants of an outlined or filled ellipse. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1092) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1092) ## `graphics.FrameBuffer.poly` @@ -137,7 +137,7 @@ FrameBuffer.poly(x, y, coordinates, color, fill=False) Draw a polygon from a packed coordinate buffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1093) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1093) ## `graphics.FrameBuffer.arc` @@ -147,7 +147,7 @@ FrameBuffer.arc(x, y, radius, start_angle, end_angle, color) Draw a circular arc between two angles. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1094) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1094) ## `graphics.FrameBuffer.triangle` @@ -157,7 +157,7 @@ FrameBuffer.triangle(x0, y0, x1, y1, x2, y2, color, fill=False) Draw an outlined or filled triangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1095) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1095) ## `graphics.FrameBuffer.gradient_rect` @@ -167,7 +167,7 @@ FrameBuffer.gradient_rect(x, y, width, height, color1, color2=None, vertical=Tru Fill a rectangle with a horizontal or vertical color gradient. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1096) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1096) ## `graphics.FrameBuffer.polygon` @@ -177,7 +177,7 @@ FrameBuffer.polygon(points, x, y, color, angle=0, center_x=0, center_y=0) Draw a translated and optionally rotated sequence of points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1097) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1097) ## `graphics.FrameBuffer.blit` @@ -187,7 +187,7 @@ FrameBuffer.blit(source, x, y, key=-1, palette=None) Copy another framebuffer onto the target with optional keying and palette lookup. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1098) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1098) ## `graphics.FrameBuffer.blit_rect` @@ -197,7 +197,7 @@ FrameBuffer.blit_rect(buffer, x, y, width, height) Copy a packed RGB565 rectangle onto the target. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1099) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1099) ## `graphics.FrameBuffer.blit_transparent` @@ -207,7 +207,7 @@ FrameBuffer.blit_transparent(buffer, x, y, width, height, key) Copy a packed RGB565 rectangle while skipping the transparent key. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1100) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1100) ## `graphics.FrameBuffer.text` @@ -217,7 +217,7 @@ FrameBuffer.text(text, x, y, color=1, scale=1, inverted=False, font_data=None, h Render bitmap text with selectable height, scale, and inversion. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1101) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1101) ## `graphics.FrameBuffer.text8` @@ -227,7 +227,7 @@ FrameBuffer.text8(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 8-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1102) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1102) ## `graphics.FrameBuffer.text14` @@ -237,7 +237,7 @@ FrameBuffer.text14(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 14-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1103) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1103) ## `graphics.FrameBuffer.text16` @@ -247,7 +247,7 @@ FrameBuffer.text16(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 16-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1104) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1104) ## `graphics.FrameBuffer.scroll` @@ -257,7 +257,7 @@ FrameBuffer.scroll(x_step, y_step) Move framebuffer contents by the requested x and y offsets. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1105) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1105) ## `graphics.FrameBuffer.save` @@ -267,7 +267,7 @@ FrameBuffer.save(path='screenshot') Write the current image or bitmap to a versioned output path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1106) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1106) ## `graphics.FrameBuffer.width` @@ -277,7 +277,7 @@ FrameBuffer.width Width in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1073) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1073) ## `graphics.FrameBuffer.height` @@ -287,7 +287,7 @@ FrameBuffer.height Height in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1074) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1074) ## `graphics.FrameBuffer.buffer` @@ -297,7 +297,7 @@ FrameBuffer.buffer Backing pixel buffer when the object owns or exposes one. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1075) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1075) ## `graphics.FrameBuffer.format` @@ -307,7 +307,7 @@ FrameBuffer.format Framebuffer pixel-format identifier. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1076) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1076) ## `graphics.FrameBuffer.color_depth` @@ -317,4 +317,4 @@ FrameBuffer.color_depth Bits per pixel for the framebuffer format. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1077) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1077) diff --git a/docs/generated/introduction.md b/docs/generated/introduction.md index a13ab6d..c0918d7 100644 --- a/docs/generated/introduction.md +++ b/docs/generated/introduction.md @@ -18,8 +18,8 @@ assert changed == Area(10, 10, 40, 24) ## Reference snapshot -- Release line: **v0.0.8** -- Source commit: [`aa6041e9226bdf5172a656eef722b852ec02f270`](https://github.com/PyDevices/graphics/tree/aa6041e9226bdf5172a656eef722b852ec02f270) +- Release line: **unreleased** +- Source commit: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552) - License: **MIT** - Adapter: **CPython binding tables plus public C headers** - Python API entries indexed: **136** diff --git a/docs/generated/manifest.json b/docs/generated/manifest.json index 08ef1d7..ab7d360 100644 --- a/docs/generated/manifest.json +++ b/docs/generated/manifest.json @@ -1,6 +1,6 @@ { "adapter": "cpython-binding-tables+public-c-headers", - "commit": "aa6041e9226bdf5172a656eef722b852ec02f270", + "commit": "1da33021c74a2c72eb98babf88c28a194d0c3552", "generated_with": "sourcey@3.6.5", "license": "MIT", "native_c_symbol_count": 61, @@ -8,7 +8,7 @@ { "slug": "area-clipping", "source_files": [ - "gfx_module_cpy.c" + "src/gfx_module_cpy.c" ], "symbol_count": 26, "title": "Area and clipping" @@ -16,7 +16,7 @@ { "slug": "framebuffer", "source_files": [ - "gfx_module_cpy.c" + "src/gfx_module_cpy.c" ], "symbol_count": 31, "title": "FrameBuffer" @@ -24,7 +24,7 @@ { "slug": "drawing", "source_files": [ - "gfx_module_cpy.c" + "src/gfx_module_cpy.c" ], "symbol_count": 46, "title": "Drawing operations" @@ -32,7 +32,7 @@ { "slug": "fonts-images", "source_files": [ - "gfx_module_cpy.c" + "src/gfx_module_cpy.c" ], "symbol_count": 22, "title": "Fonts and images" @@ -40,7 +40,7 @@ { "slug": "module-formats", "source_files": [ - "gfx_module_cpy.c" + "src/gfx_module_cpy.c" ], "symbol_count": 11, "title": "Runtime and pixel formats" @@ -48,9 +48,9 @@ { "slug": "native-drawing", "source_files": [ - "gfx_draw.h", - "gfx_framebuffer.h", - "gfx_shapes.h" + "include/gfx_draw.h", + "include/gfx_framebuffer.h", + "include/gfx_shapes.h" ], "symbol_count": 34, "title": "Native drawing and framebuffer API" @@ -58,28 +58,28 @@ { "slug": "native-assets-runtime", "source_files": [ - "gfx_bmp565.h", - "gfx_files.h", - "gfx_font.h" + "include/gfx_bmp565.h", + "include/gfx_files.h", + "include/gfx_font.h" ], "symbol_count": 27, "title": "Native fonts, images, and runtime API" } ], "python_symbol_count": 136, - "release": "v0.0.8", + "release": "unreleased", "repository": "https://github.com/PyDevices/graphics", "repository_native_source_file_count": 31, "schema": "pydevices-graphics-sourcey-reference.v1", "source_file_count": 7, "source_files": [ - "gfx_bmp565.h", - "gfx_draw.h", - "gfx_files.h", - "gfx_font.h", - "gfx_framebuffer.h", - "gfx_module_cpy.c", - "gfx_shapes.h" + "include/gfx_bmp565.h", + "include/gfx_draw.h", + "include/gfx_files.h", + "include/gfx_font.h", + "include/gfx_framebuffer.h", + "include/gfx_shapes.h", + "src/gfx_module_cpy.c" ], "symbol_count": 197, "symbols": [ @@ -87,10 +87,10 @@ "kind": "class", "line": 395, "name": "Area", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area", "signature": "class Area(x=0, y=0, w=0, h=0)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L395", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L395", "summary": "Immutable rectangle geometry used for bounds, clipping, and draw results." }, { @@ -98,10 +98,10 @@ "kind": "method", "line": 377, "name": "contains", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.contains", "signature": "Area.contains(point_or_x, y=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L377", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L377", "summary": "Test whether the area contains a point." }, { @@ -109,10 +109,10 @@ "kind": "method", "line": 378, "name": "contains_area", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.contains_area", "signature": "Area.contains_area(other)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L378", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L378", "summary": "Test whether another Area is fully contained." }, { @@ -120,10 +120,10 @@ "kind": "method", "line": 379, "name": "intersects", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.intersects", "signature": "Area.intersects(other)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L379", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L379", "summary": "Test whether two areas overlap." }, { @@ -131,10 +131,10 @@ "kind": "method", "line": 380, "name": "touches_or_intersects", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.touches_or_intersects", "signature": "Area.touches_or_intersects(other)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L380", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L380", "summary": "Test whether two areas overlap or share an edge." }, { @@ -142,10 +142,10 @@ "kind": "method", "line": 381, "name": "shift", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.shift", "signature": "Area.shift(dx=0, dy=0)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L381", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L381", "summary": "Return a copy translated by the requested offset." }, { @@ -153,10 +153,10 @@ "kind": "method", "line": 382, "name": "clip", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.clip", "signature": "Area.clip(other)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L382", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L382", "summary": "Return the intersection with another Area." }, { @@ -164,10 +164,10 @@ "kind": "method", "line": 383, "name": "offset", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.offset", "signature": "Area.offset(left, top=None, right=None, bottom=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L383", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L383", "summary": "Return an Area expanded independently on each edge." }, { @@ -175,10 +175,10 @@ "kind": "method", "line": 384, "name": "inset", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.inset", "signature": "Area.inset(left, top=None, right=None, bottom=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L384", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L384", "summary": "Return an Area reduced independently on each edge." }, { @@ -186,10 +186,10 @@ "kind": "property", "line": 229, "name": "x", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.x", "signature": "Area.x", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L229", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L229", "summary": "Left coordinate." }, { @@ -197,10 +197,10 @@ "kind": "property", "line": 230, "name": "y", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.y", "signature": "Area.y", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L230", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L230", "summary": "Top coordinate." }, { @@ -208,10 +208,10 @@ "kind": "property", "line": 231, "name": "w", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.w", "signature": "Area.w", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L231", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L231", "summary": "Width in pixels." }, { @@ -219,20 +219,20 @@ "kind": "property", "line": 232, "name": "h", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.h", "signature": "Area.h", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L232", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L232", "summary": "Height in pixels." }, { "kind": "class", "line": 2147, "name": "ClipContext", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClipContext", "signature": "class ClipContext(draw, area)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2147", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2147", "summary": "Context manager returned by Draw.clip() for scoped clipping." }, { @@ -240,10 +240,10 @@ "kind": "method", "line": 2140, "name": "__enter__", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClipContext.__enter__", "signature": "ClipContext.__enter__()", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2140", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2140", "summary": "Push the requested clipping area and return the effective clip." }, { @@ -251,20 +251,20 @@ "kind": "method", "line": 2141, "name": "__exit__", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClipContext.__exit__", "signature": "ClipContext.__exit__(exc_type, exc_value, traceback)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2141", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2141", "summary": "Pop the clipping area when leaving the context." }, { "kind": "class", "line": 2414, "name": "ClippedCanvas", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas", "signature": "class ClippedCanvas(canvas, clip)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2414", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2414", "summary": "Canvas proxy that intersects writes with a fixed Area." }, { @@ -272,10 +272,10 @@ "kind": "method", "line": 2402, "name": "pixel", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.pixel", "signature": "ClippedCanvas.pixel(x, y, color=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2402", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2402", "summary": "Read or write one pixel, depending on whether a color is supplied." }, { @@ -283,10 +283,10 @@ "kind": "method", "line": 2403, "name": "fill", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.fill", "signature": "ClippedCanvas.fill(color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2403", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2403", "summary": "Fill the target canvas with one color and return the affected Area." }, { @@ -294,10 +294,10 @@ "kind": "method", "line": 2404, "name": "fill_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.fill_rect", "signature": "ClippedCanvas.fill_rect(x, y, width, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2404", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2404", "summary": "Fill a rectangular region and return the clipped affected Area." }, { @@ -305,10 +305,10 @@ "kind": "method", "line": 2405, "name": "hline", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.hline", "signature": "ClippedCanvas.hline(x, y, width, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2405", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2405", "summary": "Draw a horizontal line." }, { @@ -316,10 +316,10 @@ "kind": "method", "line": 2406, "name": "vline", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.vline", "signature": "ClippedCanvas.vline(x, y, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2406", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2406", "summary": "Draw a vertical line." }, { @@ -327,10 +327,10 @@ "kind": "method", "line": 2407, "name": "blit_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.blit_rect", "signature": "ClippedCanvas.blit_rect(buffer, x, y, width, height)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2407", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2407", "summary": "Copy a packed RGB565 rectangle onto the target." }, { @@ -338,10 +338,10 @@ "kind": "method", "line": 2408, "name": "blit_transparent", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.blit_transparent", "signature": "ClippedCanvas.blit_transparent(buffer, x, y, width, height, key)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2408", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2408", "summary": "Copy a packed RGB565 rectangle while skipping the transparent key." }, { @@ -349,10 +349,10 @@ "kind": "property", "line": 2223, "name": "width", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.width", "signature": "ClippedCanvas.width", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2223", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2223", "summary": "Width in pixels." }, { @@ -360,20 +360,20 @@ "kind": "property", "line": 2224, "name": "height", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.height", "signature": "ClippedCanvas.height", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2224", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2224", "summary": "Height in pixels." }, { "kind": "class", "line": 1112, "name": "FrameBuffer", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer", "signature": "class FrameBuffer(buffer, width, height, format, stride=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1112", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1112", "summary": "A framebuf-compatible native drawing surface backed by a writable buffer." }, { @@ -381,10 +381,10 @@ "kind": "method", "line": 1082, "name": "from_file", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.from_file", "signature": "FrameBuffer.from_file(path)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1082", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1082", "summary": "Load a supported image file into a new FrameBuffer." }, { @@ -392,10 +392,10 @@ "kind": "method", "line": 1083, "name": "fill", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.fill", "signature": "FrameBuffer.fill(color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1083", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1083", "summary": "Fill the target canvas with one color and return the affected Area." }, { @@ -403,10 +403,10 @@ "kind": "method", "line": 1084, "name": "fill_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.fill_rect", "signature": "FrameBuffer.fill_rect(x, y, width, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1084", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1084", "summary": "Fill a rectangular region and return the clipped affected Area." }, { @@ -414,10 +414,10 @@ "kind": "method", "line": 1085, "name": "pixel", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.pixel", "signature": "FrameBuffer.pixel(x, y, color=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1085", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1085", "summary": "Read or write one pixel, depending on whether a color is supplied." }, { @@ -425,10 +425,10 @@ "kind": "method", "line": 1086, "name": "hline", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.hline", "signature": "FrameBuffer.hline(x, y, width, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1086", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1086", "summary": "Draw a horizontal line." }, { @@ -436,10 +436,10 @@ "kind": "method", "line": 1087, "name": "vline", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.vline", "signature": "FrameBuffer.vline(x, y, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1087", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1087", "summary": "Draw a vertical line." }, { @@ -447,10 +447,10 @@ "kind": "method", "line": 1088, "name": "rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.rect", "signature": "FrameBuffer.rect(x, y, width, height, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1088", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1088", "summary": "Draw an outlined or filled rectangle." }, { @@ -458,10 +458,10 @@ "kind": "method", "line": 1089, "name": "round_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.round_rect", "signature": "FrameBuffer.round_rect(x, y, width, height, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1089", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1089", "summary": "Draw an outlined or filled rounded rectangle." }, { @@ -469,10 +469,10 @@ "kind": "method", "line": 1090, "name": "circle", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.circle", "signature": "FrameBuffer.circle(x, y, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1090", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1090", "summary": "Draw an outlined or filled circle." }, { @@ -480,10 +480,10 @@ "kind": "method", "line": 1091, "name": "line", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.line", "signature": "FrameBuffer.line(x1, y1, x2, y2, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1091", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1091", "summary": "Draw a line between two points." }, { @@ -491,10 +491,10 @@ "kind": "method", "line": 1092, "name": "ellipse", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.ellipse", "signature": "FrameBuffer.ellipse(x, y, x_radius, y_radius, color, fill=False, mask=0x0F)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1092", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1092", "summary": "Draw selected quadrants of an outlined or filled ellipse." }, { @@ -502,10 +502,10 @@ "kind": "method", "line": 1093, "name": "poly", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.poly", "signature": "FrameBuffer.poly(x, y, coordinates, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1093", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1093", "summary": "Draw a polygon from a packed coordinate buffer." }, { @@ -513,10 +513,10 @@ "kind": "method", "line": 1094, "name": "arc", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.arc", "signature": "FrameBuffer.arc(x, y, radius, start_angle, end_angle, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1094", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1094", "summary": "Draw a circular arc between two angles." }, { @@ -524,10 +524,10 @@ "kind": "method", "line": 1095, "name": "triangle", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.triangle", "signature": "FrameBuffer.triangle(x0, y0, x1, y1, x2, y2, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1095", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1095", "summary": "Draw an outlined or filled triangle." }, { @@ -535,10 +535,10 @@ "kind": "method", "line": 1096, "name": "gradient_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.gradient_rect", "signature": "FrameBuffer.gradient_rect(x, y, width, height, color1, color2=None, vertical=True)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1096", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1096", "summary": "Fill a rectangle with a horizontal or vertical color gradient." }, { @@ -546,10 +546,10 @@ "kind": "method", "line": 1097, "name": "polygon", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.polygon", "signature": "FrameBuffer.polygon(points, x, y, color, angle=0, center_x=0, center_y=0)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1097", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1097", "summary": "Draw a translated and optionally rotated sequence of points." }, { @@ -557,10 +557,10 @@ "kind": "method", "line": 1098, "name": "blit", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.blit", "signature": "FrameBuffer.blit(source, x, y, key=-1, palette=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1098", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1098", "summary": "Copy another framebuffer onto the target with optional keying and palette lookup." }, { @@ -568,10 +568,10 @@ "kind": "method", "line": 1099, "name": "blit_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.blit_rect", "signature": "FrameBuffer.blit_rect(buffer, x, y, width, height)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1099", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1099", "summary": "Copy a packed RGB565 rectangle onto the target." }, { @@ -579,10 +579,10 @@ "kind": "method", "line": 1100, "name": "blit_transparent", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.blit_transparent", "signature": "FrameBuffer.blit_transparent(buffer, x, y, width, height, key)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1100", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1100", "summary": "Copy a packed RGB565 rectangle while skipping the transparent key." }, { @@ -590,10 +590,10 @@ "kind": "method", "line": 1101, "name": "text", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.text", "signature": "FrameBuffer.text(text, x, y, color=1, scale=1, inverted=False, font_data=None, height=8)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1101", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1101", "summary": "Render bitmap text with selectable height, scale, and inversion." }, { @@ -601,10 +601,10 @@ "kind": "method", "line": 1102, "name": "text8", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.text8", "signature": "FrameBuffer.text8(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1102", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1102", "summary": "Render text with the built-in 8-pixel-high font path." }, { @@ -612,10 +612,10 @@ "kind": "method", "line": 1103, "name": "text14", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.text14", "signature": "FrameBuffer.text14(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1103", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1103", "summary": "Render text with the built-in 14-pixel-high font path." }, { @@ -623,10 +623,10 @@ "kind": "method", "line": 1104, "name": "text16", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.text16", "signature": "FrameBuffer.text16(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1104", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1104", "summary": "Render text with the built-in 16-pixel-high font path." }, { @@ -634,10 +634,10 @@ "kind": "method", "line": 1105, "name": "scroll", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.scroll", "signature": "FrameBuffer.scroll(x_step, y_step)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1105", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1105", "summary": "Move framebuffer contents by the requested x and y offsets." }, { @@ -645,10 +645,10 @@ "kind": "method", "line": 1106, "name": "save", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.save", "signature": "FrameBuffer.save(path='screenshot')", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1106", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1106", "summary": "Write the current image or bitmap to a versioned output path." }, { @@ -656,10 +656,10 @@ "kind": "property", "line": 1073, "name": "width", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.width", "signature": "FrameBuffer.width", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1073", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1073", "summary": "Width in pixels." }, { @@ -667,10 +667,10 @@ "kind": "property", "line": 1074, "name": "height", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.height", "signature": "FrameBuffer.height", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1074", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1074", "summary": "Height in pixels." }, { @@ -678,10 +678,10 @@ "kind": "property", "line": 1075, "name": "buffer", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.buffer", "signature": "FrameBuffer.buffer", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1075", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1075", "summary": "Backing pixel buffer when the object owns or exposes one." }, { @@ -689,10 +689,10 @@ "kind": "property", "line": 1076, "name": "format", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.format", "signature": "FrameBuffer.format", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1076", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1076", "summary": "Framebuffer pixel-format identifier." }, { @@ -700,20 +700,20 @@ "kind": "property", "line": 1077, "name": "color_depth", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.color_depth", "signature": "FrameBuffer.color_depth", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L1077", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1077", "summary": "Bits per pixel for the framebuffer format." }, { "kind": "class", "line": 2085, "name": "Draw", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw", "signature": "class Draw(canvas)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2085", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2085", "summary": "Drawing facade that adds a bounded clip stack to any compatible canvas." }, { @@ -721,10 +721,10 @@ "kind": "method", "line": 2057, "name": "fill", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.fill", "signature": "Draw.fill(color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2057", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2057", "summary": "Fill the target canvas with one color and return the affected Area." }, { @@ -732,10 +732,10 @@ "kind": "method", "line": 2058, "name": "fill_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.fill_rect", "signature": "Draw.fill_rect(x, y, width, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2058", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2058", "summary": "Fill a rectangular region and return the clipped affected Area." }, { @@ -743,10 +743,10 @@ "kind": "method", "line": 2059, "name": "pixel", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.pixel", "signature": "Draw.pixel(x, y, color=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2059", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2059", "summary": "Read or write one pixel, depending on whether a color is supplied." }, { @@ -754,10 +754,10 @@ "kind": "method", "line": 2060, "name": "hline", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.hline", "signature": "Draw.hline(x, y, width, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2060", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2060", "summary": "Draw a horizontal line." }, { @@ -765,10 +765,10 @@ "kind": "method", "line": 2061, "name": "vline", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.vline", "signature": "Draw.vline(x, y, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2061", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2061", "summary": "Draw a vertical line." }, { @@ -776,10 +776,10 @@ "kind": "method", "line": 2062, "name": "line", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.line", "signature": "Draw.line(x1, y1, x2, y2, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2062", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2062", "summary": "Draw a line between two points." }, { @@ -787,10 +787,10 @@ "kind": "method", "line": 2063, "name": "rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.rect", "signature": "Draw.rect(x, y, width, height, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2063", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2063", "summary": "Draw an outlined or filled rectangle." }, { @@ -798,10 +798,10 @@ "kind": "method", "line": 2064, "name": "round_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.round_rect", "signature": "Draw.round_rect(x, y, width, height, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2064", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2064", "summary": "Draw an outlined or filled rounded rectangle." }, { @@ -809,10 +809,10 @@ "kind": "method", "line": 2065, "name": "circle", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.circle", "signature": "Draw.circle(x, y, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2065", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2065", "summary": "Draw an outlined or filled circle." }, { @@ -820,10 +820,10 @@ "kind": "method", "line": 2066, "name": "ellipse", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.ellipse", "signature": "Draw.ellipse(x, y, x_radius, y_radius, color, fill=False, mask=0x0F)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2066", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2066", "summary": "Draw selected quadrants of an outlined or filled ellipse." }, { @@ -831,10 +831,10 @@ "kind": "method", "line": 2067, "name": "arc", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.arc", "signature": "Draw.arc(x, y, radius, start_angle, end_angle, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2067", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2067", "summary": "Draw a circular arc between two angles." }, { @@ -842,10 +842,10 @@ "kind": "method", "line": 2068, "name": "triangle", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.triangle", "signature": "Draw.triangle(x0, y0, x1, y1, x2, y2, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2068", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2068", "summary": "Draw an outlined or filled triangle." }, { @@ -853,10 +853,10 @@ "kind": "method", "line": 2069, "name": "gradient_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.gradient_rect", "signature": "Draw.gradient_rect(x, y, width, height, color1, color2=None, vertical=True)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2069", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2069", "summary": "Fill a rectangle with a horizontal or vertical color gradient." }, { @@ -864,10 +864,10 @@ "kind": "method", "line": 2070, "name": "poly", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.poly", "signature": "Draw.poly(x, y, coordinates, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2070", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2070", "summary": "Draw a polygon from a packed coordinate buffer." }, { @@ -875,10 +875,10 @@ "kind": "method", "line": 2071, "name": "polygon", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.polygon", "signature": "Draw.polygon(points, x, y, color, angle=0, center_x=0, center_y=0)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2071", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2071", "summary": "Draw a translated and optionally rotated sequence of points." }, { @@ -886,10 +886,10 @@ "kind": "method", "line": 2072, "name": "blit", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.blit", "signature": "Draw.blit(source, x, y, key=-1, palette=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2072", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2072", "summary": "Copy another framebuffer onto the target with optional keying and palette lookup." }, { @@ -897,10 +897,10 @@ "kind": "method", "line": 2073, "name": "blit_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.blit_rect", "signature": "Draw.blit_rect(buffer, x, y, width, height)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2073", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2073", "summary": "Copy a packed RGB565 rectangle onto the target." }, { @@ -908,10 +908,10 @@ "kind": "method", "line": 2074, "name": "blit_transparent", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.blit_transparent", "signature": "Draw.blit_transparent(buffer, x, y, width, height, key)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2074", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2074", "summary": "Copy a packed RGB565 rectangle while skipping the transparent key." }, { @@ -919,10 +919,10 @@ "kind": "method", "line": 2075, "name": "text", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.text", "signature": "Draw.text(text, x, y, color=1, scale=1, inverted=False, font_data=None, height=8)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2075", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2075", "summary": "Render bitmap text with selectable height, scale, and inversion." }, { @@ -930,10 +930,10 @@ "kind": "method", "line": 2076, "name": "text8", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.text8", "signature": "Draw.text8(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2076", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2076", "summary": "Render text with the built-in 8-pixel-high font path." }, { @@ -941,10 +941,10 @@ "kind": "method", "line": 2077, "name": "text14", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.text14", "signature": "Draw.text14(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2077", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2077", "summary": "Render text with the built-in 14-pixel-high font path." }, { @@ -952,10 +952,10 @@ "kind": "method", "line": 2078, "name": "text16", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.text16", "signature": "Draw.text16(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2078", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2078", "summary": "Render text with the built-in 16-pixel-high font path." }, { @@ -963,10 +963,10 @@ "kind": "method", "line": 2079, "name": "clip", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.clip", "signature": "Draw.clip(area_or_x, y=None, width=None, height=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2079", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2079", "summary": "Return the intersection with another Area." }, { @@ -974,10 +974,10 @@ "kind": "function", "line": 2953, "name": "fill", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.fill", "signature": "graphics.fill(canvas, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2953", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2953", "summary": "Fill the target canvas with one color and return the affected Area." }, { @@ -985,10 +985,10 @@ "kind": "function", "line": 2954, "name": "fill_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.fill_rect", "signature": "graphics.fill_rect(canvas, x, y, width, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2954", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2954", "summary": "Fill a rectangular region and return the clipped affected Area." }, { @@ -996,10 +996,10 @@ "kind": "function", "line": 2955, "name": "pixel", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.pixel", "signature": "graphics.pixel(canvas, x, y, color=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2955", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2955", "summary": "Read or write one pixel, depending on whether a color is supplied." }, { @@ -1007,10 +1007,10 @@ "kind": "function", "line": 2956, "name": "hline", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.hline", "signature": "graphics.hline(canvas, x, y, width, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2956", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2956", "summary": "Draw a horizontal line." }, { @@ -1018,10 +1018,10 @@ "kind": "function", "line": 2957, "name": "vline", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.vline", "signature": "graphics.vline(canvas, x, y, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2957", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2957", "summary": "Draw a vertical line." }, { @@ -1029,10 +1029,10 @@ "kind": "function", "line": 2958, "name": "line", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.line", "signature": "graphics.line(canvas, x1, y1, x2, y2, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2958", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2958", "summary": "Draw a line between two points." }, { @@ -1040,10 +1040,10 @@ "kind": "function", "line": 2959, "name": "rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.rect", "signature": "graphics.rect(canvas, x, y, width, height, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2959", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2959", "summary": "Draw an outlined or filled rectangle." }, { @@ -1051,10 +1051,10 @@ "kind": "function", "line": 2960, "name": "round_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.round_rect", "signature": "graphics.round_rect(canvas, x, y, width, height, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2960", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2960", "summary": "Draw an outlined or filled rounded rectangle." }, { @@ -1062,10 +1062,10 @@ "kind": "function", "line": 2961, "name": "circle", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.circle", "signature": "graphics.circle(canvas, x, y, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2961", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2961", "summary": "Draw an outlined or filled circle." }, { @@ -1073,10 +1073,10 @@ "kind": "function", "line": 2962, "name": "ellipse", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ellipse", "signature": "graphics.ellipse(canvas, x, y, x_radius, y_radius, color, fill=False, mask=0x0F)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2962", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2962", "summary": "Draw selected quadrants of an outlined or filled ellipse." }, { @@ -1084,10 +1084,10 @@ "kind": "function", "line": 2963, "name": "arc", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.arc", "signature": "graphics.arc(canvas, x, y, radius, start_angle, end_angle, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2963", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2963", "summary": "Draw a circular arc between two angles." }, { @@ -1095,10 +1095,10 @@ "kind": "function", "line": 2964, "name": "triangle", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.triangle", "signature": "graphics.triangle(canvas, x0, y0, x1, y1, x2, y2, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2964", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2964", "summary": "Draw an outlined or filled triangle." }, { @@ -1106,10 +1106,10 @@ "kind": "function", "line": 2965, "name": "gradient_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.gradient_rect", "signature": "graphics.gradient_rect(canvas, x, y, width, height, color1, color2=None, vertical=True)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2965", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2965", "summary": "Fill a rectangle with a horizontal or vertical color gradient." }, { @@ -1117,10 +1117,10 @@ "kind": "function", "line": 2966, "name": "poly", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.poly", "signature": "graphics.poly(canvas, x, y, coordinates, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2966", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2966", "summary": "Draw a polygon from a packed coordinate buffer." }, { @@ -1128,10 +1128,10 @@ "kind": "function", "line": 2967, "name": "blit", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.blit", "signature": "graphics.blit(canvas, source, x, y, key=-1, palette=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2967", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2967", "summary": "Copy another framebuffer onto the target with optional keying and palette lookup." }, { @@ -1139,10 +1139,10 @@ "kind": "function", "line": 2968, "name": "blit_rect", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.blit_rect", "signature": "graphics.blit_rect(canvas, buffer, x, y, width, height)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2968", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2968", "summary": "Copy a packed RGB565 rectangle onto the target." }, { @@ -1150,10 +1150,10 @@ "kind": "function", "line": 2969, "name": "blit_transparent", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.blit_transparent", "signature": "graphics.blit_transparent(canvas, buffer, x, y, width, height, key)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2969", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2969", "summary": "Copy a packed RGB565 rectangle while skipping the transparent key." }, { @@ -1161,10 +1161,10 @@ "kind": "function", "line": 2970, "name": "polygon", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.polygon", "signature": "graphics.polygon(canvas, points, x, y, color, angle=0, center_x=0, center_y=0)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2970", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2970", "summary": "Draw a translated and optionally rotated sequence of points." }, { @@ -1172,10 +1172,10 @@ "kind": "function", "line": 2976, "name": "text", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.text", "signature": "graphics.text(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=None, height=8)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2976", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2976", "summary": "Render bitmap text with selectable height, scale, and inversion." }, { @@ -1183,10 +1183,10 @@ "kind": "function", "line": 2977, "name": "text8", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.text8", "signature": "graphics.text8(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2977", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2977", "summary": "Render text with the built-in 8-pixel-high font path." }, { @@ -1194,10 +1194,10 @@ "kind": "function", "line": 2978, "name": "text14", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.text14", "signature": "graphics.text14(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2978", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2978", "summary": "Render text with the built-in 14-pixel-high font path." }, { @@ -1205,20 +1205,20 @@ "kind": "function", "line": 2979, "name": "text16", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.text16", "signature": "graphics.text16(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2979", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2979", "summary": "Render text with the built-in 16-pixel-high font path." }, { "kind": "class", "line": 2643, "name": "Font", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font", "signature": "class Font(font_data=None, height=0, cached=True)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2643", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2643", "summary": "Bitmap font loader and renderer for built-in, file, or buffer-backed glyph data." }, { @@ -1226,10 +1226,10 @@ "kind": "method", "line": 2633, "name": "text", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.text", "signature": "Font.text(canvas, text, x, y, color, scale=1, inverted=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2633", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2633", "summary": "Render bitmap text with selectable height, scale, and inversion." }, { @@ -1237,10 +1237,10 @@ "kind": "method", "line": 2634, "name": "draw_char", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.draw_char", "signature": "Font.draw_char(char, x, y, canvas, color, scale=1, inverted=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2634", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2634", "summary": "Render one glyph through this Font onto a canvas." }, { @@ -1248,10 +1248,10 @@ "kind": "method", "line": 2635, "name": "text_width", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.text_width", "signature": "Font.text_width(text, scale=1)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2635", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2635", "summary": "Measure rendered text width at the selected scale." }, { @@ -1259,10 +1259,10 @@ "kind": "method", "line": 2636, "name": "deinit", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.deinit", "signature": "Font.deinit()", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2636", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2636", "summary": "Release resources owned by the object." }, { @@ -1270,10 +1270,10 @@ "kind": "method", "line": 2637, "name": "export", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.export", "signature": "Font.export(filename)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2637", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2637", "summary": "Write cached font bytes to a file." }, { @@ -1281,10 +1281,10 @@ "kind": "property", "line": 2626, "name": "width", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.width", "signature": "Font.width", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2626", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2626", "summary": "Width in pixels." }, { @@ -1292,10 +1292,10 @@ "kind": "property", "line": 2627, "name": "height", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.height", "signature": "Font.height", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2627", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2627", "summary": "Height in pixels." }, { @@ -1303,20 +1303,20 @@ "kind": "property", "line": 2628, "name": "font_name", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.font_name", "signature": "Font.font_name", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2628", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2628", "summary": "Font source name or the in-memory/default identifier." }, { "kind": "class", "line": 2934, "name": "BMP565", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565", "signature": "class BMP565(filename=None, source=None, streamed=False, mirrored=False, width=None, height=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2934", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2934", "summary": "RGB565 bitmap reader supporting buffered and streamed access." }, { @@ -1324,10 +1324,10 @@ "kind": "method", "line": 2923, "name": "save", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.save", "signature": "BMP565.save(filename=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2923", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2923", "summary": "Write the current image or bitmap to a versioned output path." }, { @@ -1335,10 +1335,10 @@ "kind": "method", "line": 2924, "name": "deinit", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.deinit", "signature": "BMP565.deinit()", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2924", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2924", "summary": "Release resources owned by the object." }, { @@ -1346,10 +1346,10 @@ "kind": "property", "line": 2914, "name": "width", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.width", "signature": "BMP565.width", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2914", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2914", "summary": "Width in pixels." }, { @@ -1357,10 +1357,10 @@ "kind": "property", "line": 2915, "name": "height", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.height", "signature": "BMP565.height", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2915", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2915", "summary": "Height in pixels." }, { @@ -1368,10 +1368,10 @@ "kind": "property", "line": 2916, "name": "buffer", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.buffer", "signature": "BMP565.buffer", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2916", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2916", "summary": "Backing pixel buffer when the object owns or exposes one." }, { @@ -1379,10 +1379,10 @@ "kind": "property", "line": 2917, "name": "bpp", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.bpp", "signature": "BMP565.bpp", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2917", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2917", "summary": "Bitmap color depth in bits per pixel." }, { @@ -1390,10 +1390,10 @@ "kind": "property", "line": 2918, "name": "BPP", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.BPP", "signature": "BMP565.BPP", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2918", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2918", "summary": "Bitmap storage width in bytes per pixel." }, { @@ -1401,10 +1401,10 @@ "kind": "function", "line": 2971, "name": "load_image", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.load_image", "signature": "graphics.load_image(path)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2971", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2971", "summary": "Load a supported image file as a FrameBuffer." }, { @@ -1412,10 +1412,10 @@ "kind": "function", "line": 2972, "name": "save_image", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.save_image", "signature": "graphics.save_image(framebuffer, path='screenshot')", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2972", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2972", "summary": "Save a FrameBuffer to a versioned image path." }, { @@ -1423,10 +1423,10 @@ "kind": "function", "line": 2973, "name": "bmp_to_framebuffer", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.bmp_to_framebuffer", "signature": "graphics.bmp_to_framebuffer(path)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2973", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2973", "summary": "Decode a BMP file into a FrameBuffer." }, { @@ -1434,10 +1434,10 @@ "kind": "function", "line": 2974, "name": "pbm_to_framebuffer", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.pbm_to_framebuffer", "signature": "graphics.pbm_to_framebuffer(path)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2974", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2974", "summary": "Decode a PBM file into a FrameBuffer." }, { @@ -1445,10 +1445,10 @@ "kind": "function", "line": 2975, "name": "pgm_to_framebuffer", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.pgm_to_framebuffer", "signature": "graphics.pgm_to_framebuffer(path)", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2975", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2975", "summary": "Decode a PGM file into a FrameBuffer." }, { @@ -1456,10 +1456,10 @@ "kind": "function", "line": 2950, "name": "framebuf_backend", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.framebuf_backend", "signature": "graphics.framebuf_backend()", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2950", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2950", "summary": "Return the active framebuffer backend identifier." }, { @@ -1467,10 +1467,10 @@ "kind": "function", "line": 2951, "name": "implementation", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.implementation", "signature": "graphics.implementation()", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2951", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2951", "summary": "Return the implementation identifier for this graphics module." }, { @@ -1478,10 +1478,10 @@ "kind": "function", "line": 2952, "name": "capabilities", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.capabilities", "signature": "graphics.capabilities()", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2952", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2952", "summary": "Return structured runtime feature and pixel-format capabilities." }, { @@ -1489,10 +1489,10 @@ "kind": "constant", "line": 3027, "name": "MONO_VLSB", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.MONO_VLSB", "signature": "graphics.MONO_VLSB", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3027", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3027", "summary": "Monochrome vertical least-significant-bit layout." }, { @@ -1500,10 +1500,10 @@ "kind": "constant", "line": 3028, "name": "MONO_HLSB", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.MONO_HLSB", "signature": "graphics.MONO_HLSB", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3028", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3028", "summary": "Monochrome horizontal least-significant-bit layout." }, { @@ -1511,10 +1511,10 @@ "kind": "constant", "line": 3029, "name": "MONO_HMSB", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.MONO_HMSB", "signature": "graphics.MONO_HMSB", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3029", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3029", "summary": "Monochrome horizontal most-significant-bit layout." }, { @@ -1522,10 +1522,10 @@ "kind": "constant", "line": 3030, "name": "RGB565", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.RGB565", "signature": "graphics.RGB565", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3030", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3030", "summary": "16-bit RGB565 pixel layout." }, { @@ -1533,10 +1533,10 @@ "kind": "constant", "line": 3031, "name": "GS2_HMSB", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.GS2_HMSB", "signature": "graphics.GS2_HMSB", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3031", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3031", "summary": "2-bit grayscale horizontal most-significant-bit layout." }, { @@ -1544,10 +1544,10 @@ "kind": "constant", "line": 3032, "name": "GS4_HMSB", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.GS4_HMSB", "signature": "graphics.GS4_HMSB", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3032", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3032", "summary": "4-bit grayscale horizontal most-significant-bit layout." }, { @@ -1555,10 +1555,10 @@ "kind": "constant", "line": 3033, "name": "GS8", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.GS8", "signature": "graphics.GS8", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3033", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3033", "summary": "8-bit grayscale layout." }, { @@ -1566,620 +1566,620 @@ "kind": "constant", "line": 3034, "name": "RGB888", - "path": "gfx_module_cpy.c", + "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.RGB888", "signature": "graphics.RGB888", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3034", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3034", "summary": "24-bit RGB888 pixel layout." }, { "kind": "c_function", "line": 21, "name": "gfx_fb_validate_buffer", - "path": "gfx_framebuffer.h", + "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_validate_buffer", "signature": "int gfx_fb_validate_buffer(size_t buf_len, int width, int height, int format, int stride);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L21", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L21", "summary": "Native fb entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 22, "name": "gfx_fb_color_depth", - "path": "gfx_framebuffer.h", + "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_color_depth", "signature": "int gfx_fb_color_depth(int format);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L22", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L22", "summary": "Native fb entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 24, "name": "gfx_fb_setpixel", - "path": "gfx_framebuffer.h", + "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_setpixel", "signature": "void gfx_fb_setpixel(const gfx_fb_t *fb, unsigned int x, unsigned int y, uint32_t col);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L24", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L24", "summary": "Native fb entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 25, "name": "gfx_fb_getpixel", - "path": "gfx_framebuffer.h", + "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_getpixel", "signature": "uint32_t gfx_fb_getpixel(const gfx_fb_t *fb, unsigned int x, unsigned int y);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L25", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L25", "summary": "Native fb entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 26, "name": "gfx_fb_setpixel_checked", - "path": "gfx_framebuffer.h", + "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_setpixel_checked", "signature": "void gfx_fb_setpixel_checked(const gfx_fb_t *fb, int x, int y, int col, int mask);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L26", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L26", "summary": "Native fb entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 27, "name": "gfx_fb_fill_rect_raw", - "path": "gfx_framebuffer.h", + "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_fill_rect_raw", "signature": "void gfx_fb_fill_rect_raw(const gfx_fb_t *fb, int x, int y, int w, int h, uint32_t col);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L27", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L27", "summary": "Native fb entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 28, "name": "gfx_fb_fill_rect", - "path": "gfx_framebuffer.h", + "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_fill_rect", "signature": "gfx_area_t gfx_fb_fill_rect(const gfx_fb_t *fb, int x, int y, int w, int h, uint32_t col);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L28", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L28", "summary": "Native fb entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 29, "name": "gfx_fb_fill", - "path": "gfx_framebuffer.h", + "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_fill", "signature": "gfx_area_t gfx_fb_fill(const gfx_fb_t *fb, uint32_t col);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L29", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L29", "summary": "Native fb entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 31, "name": "gfx_fb_canvas_init", - "path": "gfx_framebuffer.h", + "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_canvas_init", "signature": "void gfx_fb_canvas_init(gfx_canvas_t *canvas, const gfx_fb_t *fb);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L31", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L31", "summary": "Native fb entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 32, "name": "gfx_fb_scroll", - "path": "gfx_framebuffer.h", + "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_scroll", "signature": "void gfx_fb_scroll(gfx_fb_t *fb, int xstep, int ystep);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L32", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L32", "summary": "Native fb entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 20, "name": "gfx_clipped_canvas_init", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_clipped_canvas_init", "signature": "void gfx_clipped_canvas_init(gfx_clipped_canvas_t *cc, const gfx_canvas_t *parent, const gfx_area_t *clip);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L20", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L20", "summary": "Native clipped entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 22, "name": "gfx_shapes_pixel", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_pixel", "signature": "gfx_area_t gfx_shapes_pixel(const gfx_canvas_t *canvas, int x, int y, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L22", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L22", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 23, "name": "gfx_shapes_hline", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_hline", "signature": "gfx_area_t gfx_shapes_hline(const gfx_canvas_t *canvas, int x, int y, int w, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L23", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L23", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 24, "name": "gfx_shapes_vline", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_vline", "signature": "gfx_area_t gfx_shapes_vline(const gfx_canvas_t *canvas, int x, int y, int h, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L24", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L24", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 25, "name": "gfx_shapes_fill_rect", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_fill_rect", "signature": "gfx_area_t gfx_shapes_fill_rect(const gfx_canvas_t *canvas, int x, int y, int w, int h, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L25", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L25", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 26, "name": "gfx_shapes_rect", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_rect", "signature": "gfx_area_t gfx_shapes_rect(const gfx_canvas_t *canvas, int x, int y, int w, int h, int c, int fill);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L26", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L26", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 27, "name": "gfx_shapes_line", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_line", "signature": "gfx_area_t gfx_shapes_line(const gfx_canvas_t *canvas, int x1, int y1, int x2, int y2, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L27", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L27", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 28, "name": "gfx_shapes_ellipse", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_ellipse", "signature": "gfx_area_t gfx_shapes_ellipse(const gfx_canvas_t *canvas, int cx, int cy, int xradius, int yradius, int col, int fill, int mask_part);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L28", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L28", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 29, "name": "gfx_shapes_fill", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_fill", "signature": "gfx_area_t gfx_shapes_fill(const gfx_canvas_t *canvas, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L29", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L29", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 31, "name": "gfx_shapes_poly_int_from_buffer", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_poly_int_from_buffer", "signature": "int gfx_shapes_poly_int_from_buffer(const void *buf, size_t len, size_t itemsize, const char *fmt, size_t index, int *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L31", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L31", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 32, "name": "gfx_shapes_poly", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_poly", "signature": "gfx_area_t gfx_shapes_poly(const gfx_canvas_t *canvas, int x, int y, const void *coords, size_t coords_len, size_t itemsize, const char *fmt, int col, int fill);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L32", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L32", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 33, "name": "gfx_shapes_blit", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_blit", "signature": "gfx_area_t gfx_shapes_blit(const gfx_canvas_t *canvas, const gfx_fb_t *source, int x, int y, int key, const gfx_fb_t *palette);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L33", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L33", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 34, "name": "gfx_shapes_circle", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_circle", "signature": "gfx_area_t gfx_shapes_circle(const gfx_canvas_t *canvas, int x0, int y0, int r, int c, int fill);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L34", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L34", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 35, "name": "gfx_shapes_round_rect", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_round_rect", "signature": "gfx_area_t gfx_shapes_round_rect(const gfx_canvas_t *canvas, int x0, int y0, int w, int h, int r, int c, int fill);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L35", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L35", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 36, "name": "gfx_shapes_triangle", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_triangle", "signature": "gfx_area_t gfx_shapes_triangle(const gfx_canvas_t *canvas, int x0, int y0, int x1, int y1, int x2, int y2, int c, int fill);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L36", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L36", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 37, "name": "gfx_shapes_arc", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_arc", "signature": "gfx_area_t gfx_shapes_arc(const gfx_canvas_t *canvas, int x, int y, int r, float a0, float a1, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L37", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L37", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 38, "name": "gfx_shapes_gradient_rect", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_gradient_rect", "signature": "gfx_area_t gfx_shapes_gradient_rect(const gfx_canvas_t *canvas, int x, int y, int w, int h, int c1, int c2, int vertical);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L38", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L38", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 39, "name": "gfx_shapes_blit_rect", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_blit_rect", "signature": "gfx_area_t gfx_shapes_blit_rect(const gfx_canvas_t *canvas, const void *buf, int x, int y, int w, int h, int bpp);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L39", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L39", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 40, "name": "gfx_shapes_blit_transparent", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_blit_transparent", "signature": "gfx_area_t gfx_shapes_blit_transparent(const gfx_canvas_t *canvas, const void *buf, int x, int y, int w, int h, int key, int bpp);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L40", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L40", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 41, "name": "gfx_shapes_polygon", - "path": "gfx_shapes.h", + "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_polygon", "signature": "gfx_area_t gfx_shapes_polygon(const gfx_canvas_t *canvas, const int *points, size_t n_points, int x, int y, int color, float angle, int center_x, int center_y);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L41", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L41", "summary": "Native shapes entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 21, "name": "gfx_draw_init", - "path": "gfx_draw.h", + "path": "include/gfx_draw.h", "qualified_name": "gfx_draw_init", "signature": "void gfx_draw_init(gfx_draw_t *draw, const gfx_canvas_t *canvas);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_draw.h#L21", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L21", "summary": "Native draw entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 23, "name": "gfx_draw_effective_clip", - "path": "gfx_draw.h", + "path": "include/gfx_draw.h", "qualified_name": "gfx_draw_effective_clip", "signature": "gfx_area_t gfx_draw_effective_clip(const gfx_draw_t *draw);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_draw.h#L23", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L23", "summary": "Native draw entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 24, "name": "gfx_draw_push_clip", - "path": "gfx_draw.h", + "path": "include/gfx_draw.h", "qualified_name": "gfx_draw_push_clip", "signature": "void gfx_draw_push_clip(gfx_draw_t *draw, const gfx_area_t *clip);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_draw.h#L24", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L24", "summary": "Native draw entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 25, "name": "gfx_draw_pop_clip", - "path": "gfx_draw.h", + "path": "include/gfx_draw.h", "qualified_name": "gfx_draw_pop_clip", "signature": "void gfx_draw_pop_clip(gfx_draw_t *draw);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_draw.h#L25", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L25", "summary": "Native draw entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 22, "name": "gfx_font_init_default", - "path": "gfx_font.h", + "path": "include/gfx_font.h", "qualified_name": "gfx_font_init_default", "signature": "void gfx_font_init_default(gfx_font_t *font, int height);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L22", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L22", "summary": "Native font entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 23, "name": "gfx_font_init_from_data", - "path": "gfx_font.h", + "path": "include/gfx_font.h", "qualified_name": "gfx_font_init_from_data", "signature": "void gfx_font_init_from_data(gfx_font_t *font, const uint8_t *data, size_t len, int height);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L23", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L23", "summary": "Native font entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 24, "name": "gfx_font_deinit", - "path": "gfx_font.h", + "path": "include/gfx_font.h", "qualified_name": "gfx_font_deinit", "signature": "void gfx_font_deinit(gfx_font_t *font);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L24", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L24", "summary": "Native font entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 26, "name": "gfx_font_export", - "path": "gfx_font.h", + "path": "include/gfx_font.h", "qualified_name": "gfx_font_export", "signature": "int gfx_font_export(const gfx_font_t *font, const char *filename);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L26", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L26", "summary": "Native font entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 29, "name": "gfx_font_text", - "path": "gfx_font.h", + "path": "include/gfx_font.h", "qualified_name": "gfx_font_text", "signature": "gfx_area_t gfx_font_text(const gfx_canvas_t *canvas, const gfx_font_t *font, const char *str, int x0, int y0, int col, int scale, int inverted);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L29", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L29", "summary": "Native font entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 30, "name": "gfx_font_draw_char", - "path": "gfx_font.h", + "path": "include/gfx_font.h", "qualified_name": "gfx_font_draw_char", "signature": "gfx_area_t gfx_font_draw_char(const gfx_canvas_t *canvas, const gfx_font_t *font, unsigned char ch, int x, int y, int color, int scale, int inverted);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L30", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L30", "summary": "Native font entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 31, "name": "gfx_font_text_width", - "path": "gfx_font.h", + "path": "include/gfx_font.h", "qualified_name": "gfx_font_text_width", "signature": "int gfx_font_text_width(const gfx_font_t *font, const char *str, int scale);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L31", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L31", "summary": "Native font entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 32, "name": "gfx_font_text8", - "path": "gfx_font.h", + "path": "include/gfx_font.h", "qualified_name": "gfx_font_text8", "signature": "gfx_area_t gfx_font_text8(const gfx_canvas_t *canvas, const char *str, int x0, int y0, int col);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L32", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L32", "summary": "Native font entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 33, "name": "gfx_font_text14", - "path": "gfx_font.h", + "path": "include/gfx_font.h", "qualified_name": "gfx_font_text14", "signature": "gfx_area_t gfx_font_text14(const gfx_canvas_t *canvas, const char *str, int x0, int y0, int col);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L33", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L33", "summary": "Native font entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 34, "name": "gfx_font_text16", - "path": "gfx_font.h", + "path": "include/gfx_font.h", "qualified_name": "gfx_font_text16", "signature": "gfx_area_t gfx_font_text16(const gfx_canvas_t *canvas, const char *str, int x0, int y0, int col);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L34", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L34", "summary": "Native font entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 27, "name": "gfx_bmp565_read_header_from_file", - "path": "gfx_bmp565.h", + "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_read_header_from_file", "signature": "int gfx_bmp565_read_header_from_file(const char *path, int *width, int *height, unsigned int *data_offset);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L27", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L27", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 28, "name": "gfx_bmp565_open_stream", - "path": "gfx_bmp565.h", + "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_open_stream", "signature": "int gfx_bmp565_open_stream(const char *path, gfx_bmp565_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L28", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L28", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 29, "name": "gfx_bmp565_load_from_file", - "path": "gfx_bmp565.h", + "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_load_from_file", "signature": "int gfx_bmp565_load_from_file(const char *path, gfx_bmp565_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L29", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L29", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 30, "name": "gfx_bmp565_init_from_buffer", - "path": "gfx_bmp565.h", + "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_init_from_buffer", "signature": "int gfx_bmp565_init_from_buffer(gfx_bmp565_t *out, const uint8_t *buf, size_t len, int width, int height);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L30", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L30", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 31, "name": "gfx_bmp565_deinit", - "path": "gfx_bmp565.h", + "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_deinit", "signature": "void gfx_bmp565_deinit(gfx_bmp565_t *bmp);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L31", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L31", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 32, "name": "gfx_bmp565_save", - "path": "gfx_bmp565.h", + "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_save", "signature": "int gfx_bmp565_save(const gfx_bmp565_t *bmp, const char *path);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L32", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L32", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 33, "name": "gfx_bmp565_save_versioned", - "path": "gfx_bmp565.h", + "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_save_versioned", "signature": "int gfx_bmp565_save_versioned(const gfx_bmp565_t *bmp, const char *path, char *out_path, size_t out_path_len);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L33", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L33", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 34, "name": "gfx_bmp565_read_bytes", - "path": "gfx_bmp565.h", + "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_read_bytes", "signature": "int gfx_bmp565_read_bytes(const gfx_bmp565_t *bmp, int start, int stop, uint8_t *out, size_t out_cap, size_t *out_len);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L34", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L34", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 35, "name": "gfx_bmp565_read_region", - "path": "gfx_bmp565.h", + "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_read_region", "signature": "int gfx_bmp565_read_region(const gfx_bmp565_t *bmp, int x0, int x1, int y0, int y1, uint8_t *out, size_t out_cap, size_t *out_len);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L35", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L35", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 43, "name": "gfx_image_probe", - "path": "gfx_files.h", + "path": "include/gfx_files.h", "qualified_name": "gfx_image_probe", "signature": "int gfx_image_probe(const uint8_t *data, size_t len, gfx_image_info_t *info);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L43", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L43", "summary": "Native image entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 47, "name": "gfx_image_decode", - "path": "gfx_files.h", + "path": "include/gfx_files.h", "qualified_name": "gfx_image_decode", "signature": "int gfx_image_decode(const uint8_t *data, size_t len, const gfx_image_info_t *info, uint8_t *dst, size_t dst_len);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L47", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L47", "summary": "Native image entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 64, "name": "gfx_image_fb_free", - "path": "gfx_files.h", + "path": "include/gfx_files.h", "qualified_name": "gfx_image_fb_free", "signature": "void gfx_image_fb_free(gfx_image_fb_t *img);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L64", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L64", "summary": "Native image entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 65, "name": "gfx_files_load_image", - "path": "gfx_files.h", + "path": "include/gfx_files.h", "qualified_name": "gfx_files_load_image", "signature": "int gfx_files_load_image(const char *path, gfx_image_fb_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L65", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L65", "summary": "Native files entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 66, "name": "gfx_files_save_image", - "path": "gfx_files.h", + "path": "include/gfx_files.h", "qualified_name": "gfx_files_save_image", "signature": "int gfx_files_save_image(const gfx_fb_t *fb, const char *path, char *out_path, size_t out_path_len);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L66", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L66", "summary": "Native files entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 67, "name": "gfx_files_pbm_to_framebuffer", - "path": "gfx_files.h", + "path": "include/gfx_files.h", "qualified_name": "gfx_files_pbm_to_framebuffer", "signature": "int gfx_files_pbm_to_framebuffer(const char *path, gfx_image_fb_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L67", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L67", "summary": "Native files entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 68, "name": "gfx_files_pgm_to_framebuffer", - "path": "gfx_files.h", + "path": "include/gfx_files.h", "qualified_name": "gfx_files_pgm_to_framebuffer", "signature": "int gfx_files_pgm_to_framebuffer(const char *path, gfx_image_fb_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L68", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L68", "summary": "Native files entry point declared for firmware and extension integration." }, { "kind": "c_function", "line": 69, "name": "gfx_files_bmp_to_framebuffer", - "path": "gfx_files.h", + "path": "include/gfx_files.h", "qualified_name": "gfx_files_bmp_to_framebuffer", "signature": "int gfx_files_bmp_to_framebuffer(const char *path, gfx_image_fb_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L69", + "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L69", "summary": "Native files entry point declared for firmware and extension integration." } ] diff --git a/docs/generated/module-formats.md b/docs/generated/module-formats.md index 9d4c644..57fb214 100644 --- a/docs/generated/module-formats.md +++ b/docs/generated/module-formats.md @@ -3,7 +3,7 @@ title: Runtime and pixel formats description: Runtime capability probes and the exported framebuffer format constants. --- -Source snapshot: [`aa6041e9226bdf5172a656eef722b852ec02f270`](https://github.com/PyDevices/graphics/tree/aa6041e9226bdf5172a656eef722b852ec02f270). +Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). Runtime capability probes and the exported framebuffer format constants. @@ -17,7 +17,7 @@ graphics.framebuf_backend() Return the active framebuffer backend identifier. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2950) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2950) ## `graphics.implementation` @@ -27,7 +27,7 @@ graphics.implementation() Return the implementation identifier for this graphics module. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2951) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2951) ## `graphics.capabilities` @@ -37,7 +37,7 @@ graphics.capabilities() Return structured runtime feature and pixel-format capabilities. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L2952) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2952) ## `graphics.MONO_VLSB` @@ -47,7 +47,7 @@ graphics.MONO_VLSB Monochrome vertical least-significant-bit layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3027) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3027) ## `graphics.MONO_HLSB` @@ -57,7 +57,7 @@ graphics.MONO_HLSB Monochrome horizontal least-significant-bit layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3028) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3028) ## `graphics.MONO_HMSB` @@ -67,7 +67,7 @@ graphics.MONO_HMSB Monochrome horizontal most-significant-bit layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3029) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3029) ## `graphics.RGB565` @@ -77,7 +77,7 @@ graphics.RGB565 16-bit RGB565 pixel layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3030) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3030) ## `graphics.GS2_HMSB` @@ -87,7 +87,7 @@ graphics.GS2_HMSB 2-bit grayscale horizontal most-significant-bit layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3031) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3031) ## `graphics.GS4_HMSB` @@ -97,7 +97,7 @@ graphics.GS4_HMSB 4-bit grayscale horizontal most-significant-bit layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3032) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3032) ## `graphics.GS8` @@ -107,7 +107,7 @@ graphics.GS8 8-bit grayscale layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3033) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3033) ## `graphics.RGB888` @@ -117,4 +117,4 @@ graphics.RGB888 24-bit RGB888 pixel layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_module_cpy.c#L3034) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3034) diff --git a/docs/generated/native-assets-runtime.md b/docs/generated/native-assets-runtime.md index 2c3d166..3a198fa 100644 --- a/docs/generated/native-assets-runtime.md +++ b/docs/generated/native-assets-runtime.md @@ -3,7 +3,7 @@ title: Native fonts, images, and runtime API description: Public C declarations for fonts, BMP565 data, image conversion, and capability reporting. --- -Source snapshot: [`aa6041e9226bdf5172a656eef722b852ec02f270`](https://github.com/PyDevices/graphics/tree/aa6041e9226bdf5172a656eef722b852ec02f270). +Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). Public C declarations for fonts, BMP565 data, image conversion, and capability reporting. @@ -17,7 +17,7 @@ void gfx_font_init_default(gfx_font_t *font, int height); Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L22) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L22) ## `gfx_font_init_from_data` @@ -27,7 +27,7 @@ void gfx_font_init_from_data(gfx_font_t *font, const uint8_t *data, size_t len, Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L23) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L23) ## `gfx_font_deinit` @@ -37,7 +37,7 @@ void gfx_font_deinit(gfx_font_t *font); Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L24) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L24) ## `gfx_font_export` @@ -47,7 +47,7 @@ int gfx_font_export(const gfx_font_t *font, const char *filename); Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L26) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L26) ## `gfx_font_text` @@ -57,7 +57,7 @@ gfx_area_t gfx_font_text(const gfx_canvas_t *canvas, const gfx_font_t *font, con Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L29) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L29) ## `gfx_font_draw_char` @@ -67,7 +67,7 @@ gfx_area_t gfx_font_draw_char(const gfx_canvas_t *canvas, const gfx_font_t *font Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L30) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L30) ## `gfx_font_text_width` @@ -77,7 +77,7 @@ int gfx_font_text_width(const gfx_font_t *font, const char *str, int scale); Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L31) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L31) ## `gfx_font_text8` @@ -87,7 +87,7 @@ gfx_area_t gfx_font_text8(const gfx_canvas_t *canvas, const char *str, int x0, i Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L32) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L32) ## `gfx_font_text14` @@ -97,7 +97,7 @@ gfx_area_t gfx_font_text14(const gfx_canvas_t *canvas, const char *str, int x0, Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L33) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L33) ## `gfx_font_text16` @@ -107,7 +107,7 @@ gfx_area_t gfx_font_text16(const gfx_canvas_t *canvas, const char *str, int x0, Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_font.h#L34) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L34) ## `gfx_bmp565_read_header_from_file` @@ -117,7 +117,7 @@ int gfx_bmp565_read_header_from_file(const char *path, int *width, int *height, Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L27) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L27) ## `gfx_bmp565_open_stream` @@ -127,7 +127,7 @@ int gfx_bmp565_open_stream(const char *path, gfx_bmp565_t *out); Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L28) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L28) ## `gfx_bmp565_load_from_file` @@ -137,7 +137,7 @@ int gfx_bmp565_load_from_file(const char *path, gfx_bmp565_t *out); Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L29) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L29) ## `gfx_bmp565_init_from_buffer` @@ -147,7 +147,7 @@ int gfx_bmp565_init_from_buffer(gfx_bmp565_t *out, const uint8_t *buf, size_t le Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L30) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L30) ## `gfx_bmp565_deinit` @@ -157,7 +157,7 @@ void gfx_bmp565_deinit(gfx_bmp565_t *bmp); Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L31) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L31) ## `gfx_bmp565_save` @@ -167,7 +167,7 @@ int gfx_bmp565_save(const gfx_bmp565_t *bmp, const char *path); Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L32) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L32) ## `gfx_bmp565_save_versioned` @@ -177,7 +177,7 @@ int gfx_bmp565_save_versioned(const gfx_bmp565_t *bmp, const char *path, char *o Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L33) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L33) ## `gfx_bmp565_read_bytes` @@ -187,7 +187,7 @@ int gfx_bmp565_read_bytes(const gfx_bmp565_t *bmp, int start, int stop, uint8_t Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L34) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L34) ## `gfx_bmp565_read_region` @@ -197,7 +197,7 @@ int gfx_bmp565_read_region(const gfx_bmp565_t *bmp, int x0, int x1, int y0, int Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_bmp565.h#L35) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L35) ## `gfx_image_probe` @@ -207,7 +207,7 @@ int gfx_image_probe(const uint8_t *data, size_t len, gfx_image_info_t *info); Native image entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L43) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L43) ## `gfx_image_decode` @@ -217,7 +217,7 @@ int gfx_image_decode(const uint8_t *data, size_t len, const gfx_image_info_t *in Native image entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L47) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L47) ## `gfx_image_fb_free` @@ -227,7 +227,7 @@ void gfx_image_fb_free(gfx_image_fb_t *img); Native image entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L64) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L64) ## `gfx_files_load_image` @@ -237,7 +237,7 @@ int gfx_files_load_image(const char *path, gfx_image_fb_t *out); Native files entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L65) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L65) ## `gfx_files_save_image` @@ -247,7 +247,7 @@ int gfx_files_save_image(const gfx_fb_t *fb, const char *path, char *out_path, s Native files entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L66) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L66) ## `gfx_files_pbm_to_framebuffer` @@ -257,7 +257,7 @@ int gfx_files_pbm_to_framebuffer(const char *path, gfx_image_fb_t *out); Native files entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L67) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L67) ## `gfx_files_pgm_to_framebuffer` @@ -267,7 +267,7 @@ int gfx_files_pgm_to_framebuffer(const char *path, gfx_image_fb_t *out); Native files entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L68) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L68) ## `gfx_files_bmp_to_framebuffer` @@ -277,4 +277,4 @@ int gfx_files_bmp_to_framebuffer(const char *path, gfx_image_fb_t *out); Native files entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_files.h#L69) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L69) diff --git a/docs/generated/native-drawing.md b/docs/generated/native-drawing.md index 1b04351..54f63fe 100644 --- a/docs/generated/native-drawing.md +++ b/docs/generated/native-drawing.md @@ -3,7 +3,7 @@ title: Native drawing and framebuffer API description: Public C declarations for geometry, framebuffer operations, shapes, and clipping. --- -Source snapshot: [`aa6041e9226bdf5172a656eef722b852ec02f270`](https://github.com/PyDevices/graphics/tree/aa6041e9226bdf5172a656eef722b852ec02f270). +Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). Public C declarations for geometry, framebuffer operations, shapes, and clipping. @@ -17,7 +17,7 @@ int gfx_fb_validate_buffer(size_t buf_len, int width, int height, int format, in Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L21) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L21) ## `gfx_fb_color_depth` @@ -27,7 +27,7 @@ int gfx_fb_color_depth(int format); Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L22) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L22) ## `gfx_fb_setpixel` @@ -37,7 +37,7 @@ void gfx_fb_setpixel(const gfx_fb_t *fb, unsigned int x, unsigned int y, uint32_ Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L24) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L24) ## `gfx_fb_getpixel` @@ -47,7 +47,7 @@ uint32_t gfx_fb_getpixel(const gfx_fb_t *fb, unsigned int x, unsigned int y); Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L25) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L25) ## `gfx_fb_setpixel_checked` @@ -57,7 +57,7 @@ void gfx_fb_setpixel_checked(const gfx_fb_t *fb, int x, int y, int col, int mask Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L26) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L26) ## `gfx_fb_fill_rect_raw` @@ -67,7 +67,7 @@ void gfx_fb_fill_rect_raw(const gfx_fb_t *fb, int x, int y, int w, int h, uint32 Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L27) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L27) ## `gfx_fb_fill_rect` @@ -77,7 +77,7 @@ gfx_area_t gfx_fb_fill_rect(const gfx_fb_t *fb, int x, int y, int w, int h, uint Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L28) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L28) ## `gfx_fb_fill` @@ -87,7 +87,7 @@ gfx_area_t gfx_fb_fill(const gfx_fb_t *fb, uint32_t col); Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L29) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L29) ## `gfx_fb_canvas_init` @@ -97,7 +97,7 @@ void gfx_fb_canvas_init(gfx_canvas_t *canvas, const gfx_fb_t *fb); Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L31) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L31) ## `gfx_fb_scroll` @@ -107,7 +107,7 @@ void gfx_fb_scroll(gfx_fb_t *fb, int xstep, int ystep); Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_framebuffer.h#L32) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L32) ## `gfx_clipped_canvas_init` @@ -117,7 +117,7 @@ void gfx_clipped_canvas_init(gfx_clipped_canvas_t *cc, const gfx_canvas_t *paren Native clipped entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L20) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L20) ## `gfx_shapes_pixel` @@ -127,7 +127,7 @@ gfx_area_t gfx_shapes_pixel(const gfx_canvas_t *canvas, int x, int y, int c); Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L22) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L22) ## `gfx_shapes_hline` @@ -137,7 +137,7 @@ gfx_area_t gfx_shapes_hline(const gfx_canvas_t *canvas, int x, int y, int w, int Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L23) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L23) ## `gfx_shapes_vline` @@ -147,7 +147,7 @@ gfx_area_t gfx_shapes_vline(const gfx_canvas_t *canvas, int x, int y, int h, int Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L24) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L24) ## `gfx_shapes_fill_rect` @@ -157,7 +157,7 @@ gfx_area_t gfx_shapes_fill_rect(const gfx_canvas_t *canvas, int x, int y, int w, Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L25) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L25) ## `gfx_shapes_rect` @@ -167,7 +167,7 @@ gfx_area_t gfx_shapes_rect(const gfx_canvas_t *canvas, int x, int y, int w, int Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L26) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L26) ## `gfx_shapes_line` @@ -177,7 +177,7 @@ gfx_area_t gfx_shapes_line(const gfx_canvas_t *canvas, int x1, int y1, int x2, i Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L27) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L27) ## `gfx_shapes_ellipse` @@ -187,7 +187,7 @@ gfx_area_t gfx_shapes_ellipse(const gfx_canvas_t *canvas, int cx, int cy, int xr Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L28) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L28) ## `gfx_shapes_fill` @@ -197,7 +197,7 @@ gfx_area_t gfx_shapes_fill(const gfx_canvas_t *canvas, int c); Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L29) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L29) ## `gfx_shapes_poly_int_from_buffer` @@ -207,7 +207,7 @@ int gfx_shapes_poly_int_from_buffer(const void *buf, size_t len, size_t itemsize Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L31) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L31) ## `gfx_shapes_poly` @@ -217,7 +217,7 @@ gfx_area_t gfx_shapes_poly(const gfx_canvas_t *canvas, int x, int y, const void Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L32) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L32) ## `gfx_shapes_blit` @@ -227,7 +227,7 @@ gfx_area_t gfx_shapes_blit(const gfx_canvas_t *canvas, const gfx_fb_t *source, i Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L33) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L33) ## `gfx_shapes_circle` @@ -237,7 +237,7 @@ gfx_area_t gfx_shapes_circle(const gfx_canvas_t *canvas, int x0, int y0, int r, Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L34) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L34) ## `gfx_shapes_round_rect` @@ -247,7 +247,7 @@ gfx_area_t gfx_shapes_round_rect(const gfx_canvas_t *canvas, int x0, int y0, int Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L35) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L35) ## `gfx_shapes_triangle` @@ -257,7 +257,7 @@ gfx_area_t gfx_shapes_triangle(const gfx_canvas_t *canvas, int x0, int y0, int x Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L36) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L36) ## `gfx_shapes_arc` @@ -267,7 +267,7 @@ gfx_area_t gfx_shapes_arc(const gfx_canvas_t *canvas, int x, int y, int r, float Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L37) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L37) ## `gfx_shapes_gradient_rect` @@ -277,7 +277,7 @@ gfx_area_t gfx_shapes_gradient_rect(const gfx_canvas_t *canvas, int x, int y, in Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L38) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L38) ## `gfx_shapes_blit_rect` @@ -287,7 +287,7 @@ gfx_area_t gfx_shapes_blit_rect(const gfx_canvas_t *canvas, const void *buf, int Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L39) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L39) ## `gfx_shapes_blit_transparent` @@ -297,7 +297,7 @@ gfx_area_t gfx_shapes_blit_transparent(const gfx_canvas_t *canvas, const void *b Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L40) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L40) ## `gfx_shapes_polygon` @@ -307,7 +307,7 @@ gfx_area_t gfx_shapes_polygon(const gfx_canvas_t *canvas, const int *points, siz Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_shapes.h#L41) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L41) ## `gfx_draw_init` @@ -317,7 +317,7 @@ void gfx_draw_init(gfx_draw_t *draw, const gfx_canvas_t *canvas); Native draw entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_draw.h#L21) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L21) ## `gfx_draw_effective_clip` @@ -327,7 +327,7 @@ gfx_area_t gfx_draw_effective_clip(const gfx_draw_t *draw); Native draw entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_draw.h#L23) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L23) ## `gfx_draw_push_clip` @@ -337,7 +337,7 @@ void gfx_draw_push_clip(gfx_draw_t *draw, const gfx_area_t *clip); Native draw entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_draw.h#L24) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L24) ## `gfx_draw_pop_clip` @@ -347,4 +347,4 @@ void gfx_draw_pop_clip(gfx_draw_t *draw); Native draw entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/aa6041e9226bdf5172a656eef722b852ec02f270/gfx_draw.h#L25) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L25) From 7d36786eb65368cce407df7f5e24c7fa519f0509 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 26 Jul 2026 10:46:19 +0000 Subject: [PATCH 3/6] Refresh generated docs commit pins after docs check. Re-run the Sourcey generator so immutable source links match HEAD after the layout move and prior reference regenerate. --- docs/generated/area-clipping.md | 54 ++-- docs/generated/drawing.md | 94 +++--- docs/generated/fonts-images.md | 46 +-- docs/generated/framebuffer.md | 64 ++-- docs/generated/introduction.md | 2 +- docs/generated/manifest.json | 396 ++++++++++++------------ docs/generated/module-formats.md | 24 +- docs/generated/native-assets-runtime.md | 56 ++-- docs/generated/native-drawing.md | 70 ++--- 9 files changed, 403 insertions(+), 403 deletions(-) diff --git a/docs/generated/area-clipping.md b/docs/generated/area-clipping.md index d50a847..ab7e9d1 100644 --- a/docs/generated/area-clipping.md +++ b/docs/generated/area-clipping.md @@ -3,7 +3,7 @@ title: Area and clipping description: Rectangle geometry, scoped clip contexts, and clipped canvas operations. --- -Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). +Source snapshot: [`bcaf65f4b2f43b4b0316ce2525e89abed7f7c331`](https://github.com/PyDevices/graphics/tree/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331). Rectangle geometry, scoped clip contexts, and clipped canvas operations. @@ -17,7 +17,7 @@ class Area(x=0, y=0, w=0, h=0) Immutable rectangle geometry used for bounds, clipping, and draw results. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L395) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L395) ## `graphics.Area.contains` @@ -27,7 +27,7 @@ Area.contains(point_or_x, y=None) Test whether the area contains a point. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L377) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L377) ## `graphics.Area.contains_area` @@ -37,7 +37,7 @@ Area.contains_area(other) Test whether another Area is fully contained. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L378) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L378) ## `graphics.Area.intersects` @@ -47,7 +47,7 @@ Area.intersects(other) Test whether two areas overlap. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L379) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L379) ## `graphics.Area.touches_or_intersects` @@ -57,7 +57,7 @@ Area.touches_or_intersects(other) Test whether two areas overlap or share an edge. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L380) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L380) ## `graphics.Area.shift` @@ -67,7 +67,7 @@ Area.shift(dx=0, dy=0) Return a copy translated by the requested offset. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L381) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L381) ## `graphics.Area.clip` @@ -77,7 +77,7 @@ Area.clip(other) Return the intersection with another Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L382) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L382) ## `graphics.Area.offset` @@ -87,7 +87,7 @@ Area.offset(left, top=None, right=None, bottom=None) Return an Area expanded independently on each edge. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L383) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L383) ## `graphics.Area.inset` @@ -97,7 +97,7 @@ Area.inset(left, top=None, right=None, bottom=None) Return an Area reduced independently on each edge. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L384) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L384) ## `graphics.Area.x` @@ -107,7 +107,7 @@ Area.x Left coordinate. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L229) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L229) ## `graphics.Area.y` @@ -117,7 +117,7 @@ Area.y Top coordinate. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L230) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L230) ## `graphics.Area.w` @@ -127,7 +127,7 @@ Area.w Width in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L231) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L231) ## `graphics.Area.h` @@ -137,7 +137,7 @@ Area.h Height in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L232) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L232) ## `graphics.ClipContext` @@ -147,7 +147,7 @@ class ClipContext(draw, area) Context manager returned by Draw.clip() for scoped clipping. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2147) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2147) ## `graphics.ClipContext.__enter__` @@ -157,7 +157,7 @@ ClipContext.__enter__() Push the requested clipping area and return the effective clip. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2140) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2140) ## `graphics.ClipContext.__exit__` @@ -167,7 +167,7 @@ ClipContext.__exit__(exc_type, exc_value, traceback) Pop the clipping area when leaving the context. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2141) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2141) ## `graphics.ClippedCanvas` @@ -177,7 +177,7 @@ class ClippedCanvas(canvas, clip) Canvas proxy that intersects writes with a fixed Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2414) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2414) ## `graphics.ClippedCanvas.pixel` @@ -187,7 +187,7 @@ ClippedCanvas.pixel(x, y, color=None) Read or write one pixel, depending on whether a color is supplied. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2402) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2402) ## `graphics.ClippedCanvas.fill` @@ -197,7 +197,7 @@ ClippedCanvas.fill(color) Fill the target canvas with one color and return the affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2403) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2403) ## `graphics.ClippedCanvas.fill_rect` @@ -207,7 +207,7 @@ ClippedCanvas.fill_rect(x, y, width, height, color) Fill a rectangular region and return the clipped affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2404) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2404) ## `graphics.ClippedCanvas.hline` @@ -217,7 +217,7 @@ ClippedCanvas.hline(x, y, width, color) Draw a horizontal line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2405) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2405) ## `graphics.ClippedCanvas.vline` @@ -227,7 +227,7 @@ ClippedCanvas.vline(x, y, height, color) Draw a vertical line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2406) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2406) ## `graphics.ClippedCanvas.blit_rect` @@ -237,7 +237,7 @@ ClippedCanvas.blit_rect(buffer, x, y, width, height) Copy a packed RGB565 rectangle onto the target. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2407) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2407) ## `graphics.ClippedCanvas.blit_transparent` @@ -247,7 +247,7 @@ ClippedCanvas.blit_transparent(buffer, x, y, width, height, key) Copy a packed RGB565 rectangle while skipping the transparent key. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2408) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2408) ## `graphics.ClippedCanvas.width` @@ -257,7 +257,7 @@ ClippedCanvas.width Width in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2223) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2223) ## `graphics.ClippedCanvas.height` @@ -267,4 +267,4 @@ ClippedCanvas.height Height in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2224) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2224) diff --git a/docs/generated/drawing.md b/docs/generated/drawing.md index e3fa62c..a8cf427 100644 --- a/docs/generated/drawing.md +++ b/docs/generated/drawing.md @@ -3,7 +3,7 @@ title: Drawing operations description: Object-oriented and module-level shape, text, polygon, and blit operations. --- -Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). +Source snapshot: [`bcaf65f4b2f43b4b0316ce2525e89abed7f7c331`](https://github.com/PyDevices/graphics/tree/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331). Object-oriented and module-level shape, text, polygon, and blit operations. @@ -17,7 +17,7 @@ class Draw(canvas) Drawing facade that adds a bounded clip stack to any compatible canvas. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2085) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2085) ## `graphics.Draw.fill` @@ -27,7 +27,7 @@ Draw.fill(color) Fill the target canvas with one color and return the affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2057) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2057) ## `graphics.Draw.fill_rect` @@ -37,7 +37,7 @@ Draw.fill_rect(x, y, width, height, color) Fill a rectangular region and return the clipped affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2058) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2058) ## `graphics.Draw.pixel` @@ -47,7 +47,7 @@ Draw.pixel(x, y, color=None) Read or write one pixel, depending on whether a color is supplied. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2059) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2059) ## `graphics.Draw.hline` @@ -57,7 +57,7 @@ Draw.hline(x, y, width, color) Draw a horizontal line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2060) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2060) ## `graphics.Draw.vline` @@ -67,7 +67,7 @@ Draw.vline(x, y, height, color) Draw a vertical line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2061) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2061) ## `graphics.Draw.line` @@ -77,7 +77,7 @@ Draw.line(x1, y1, x2, y2, color) Draw a line between two points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2062) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2062) ## `graphics.Draw.rect` @@ -87,7 +87,7 @@ Draw.rect(x, y, width, height, color, fill=False) Draw an outlined or filled rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2063) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2063) ## `graphics.Draw.round_rect` @@ -97,7 +97,7 @@ Draw.round_rect(x, y, width, height, radius, color, fill=False) Draw an outlined or filled rounded rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2064) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2064) ## `graphics.Draw.circle` @@ -107,7 +107,7 @@ Draw.circle(x, y, radius, color, fill=False) Draw an outlined or filled circle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2065) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2065) ## `graphics.Draw.ellipse` @@ -117,7 +117,7 @@ Draw.ellipse(x, y, x_radius, y_radius, color, fill=False, mask=0x0F) Draw selected quadrants of an outlined or filled ellipse. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2066) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2066) ## `graphics.Draw.arc` @@ -127,7 +127,7 @@ Draw.arc(x, y, radius, start_angle, end_angle, color) Draw a circular arc between two angles. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2067) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2067) ## `graphics.Draw.triangle` @@ -137,7 +137,7 @@ Draw.triangle(x0, y0, x1, y1, x2, y2, color, fill=False) Draw an outlined or filled triangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2068) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2068) ## `graphics.Draw.gradient_rect` @@ -147,7 +147,7 @@ Draw.gradient_rect(x, y, width, height, color1, color2=None, vertical=True) Fill a rectangle with a horizontal or vertical color gradient. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2069) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2069) ## `graphics.Draw.poly` @@ -157,7 +157,7 @@ Draw.poly(x, y, coordinates, color, fill=False) Draw a polygon from a packed coordinate buffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2070) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2070) ## `graphics.Draw.polygon` @@ -167,7 +167,7 @@ Draw.polygon(points, x, y, color, angle=0, center_x=0, center_y=0) Draw a translated and optionally rotated sequence of points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2071) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2071) ## `graphics.Draw.blit` @@ -177,7 +177,7 @@ Draw.blit(source, x, y, key=-1, palette=None) Copy another framebuffer onto the target with optional keying and palette lookup. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2072) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2072) ## `graphics.Draw.blit_rect` @@ -187,7 +187,7 @@ Draw.blit_rect(buffer, x, y, width, height) Copy a packed RGB565 rectangle onto the target. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2073) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2073) ## `graphics.Draw.blit_transparent` @@ -197,7 +197,7 @@ Draw.blit_transparent(buffer, x, y, width, height, key) Copy a packed RGB565 rectangle while skipping the transparent key. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2074) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2074) ## `graphics.Draw.text` @@ -207,7 +207,7 @@ Draw.text(text, x, y, color=1, scale=1, inverted=False, font_data=None, height=8 Render bitmap text with selectable height, scale, and inversion. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2075) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2075) ## `graphics.Draw.text8` @@ -217,7 +217,7 @@ Draw.text8(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 8-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2076) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2076) ## `graphics.Draw.text14` @@ -227,7 +227,7 @@ Draw.text14(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 14-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2077) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2077) ## `graphics.Draw.text16` @@ -237,7 +237,7 @@ Draw.text16(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 16-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2078) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2078) ## `graphics.Draw.clip` @@ -247,7 +247,7 @@ Draw.clip(area_or_x, y=None, width=None, height=None) Return the intersection with another Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2079) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2079) ## `graphics.fill` @@ -257,7 +257,7 @@ graphics.fill(canvas, color) Fill the target canvas with one color and return the affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2953) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2953) ## `graphics.fill_rect` @@ -267,7 +267,7 @@ graphics.fill_rect(canvas, x, y, width, height, color) Fill a rectangular region and return the clipped affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2954) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2954) ## `graphics.pixel` @@ -277,7 +277,7 @@ graphics.pixel(canvas, x, y, color=None) Read or write one pixel, depending on whether a color is supplied. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2955) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2955) ## `graphics.hline` @@ -287,7 +287,7 @@ graphics.hline(canvas, x, y, width, color) Draw a horizontal line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2956) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2956) ## `graphics.vline` @@ -297,7 +297,7 @@ graphics.vline(canvas, x, y, height, color) Draw a vertical line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2957) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2957) ## `graphics.line` @@ -307,7 +307,7 @@ graphics.line(canvas, x1, y1, x2, y2, color) Draw a line between two points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2958) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2958) ## `graphics.rect` @@ -317,7 +317,7 @@ graphics.rect(canvas, x, y, width, height, color, fill=False) Draw an outlined or filled rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2959) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2959) ## `graphics.round_rect` @@ -327,7 +327,7 @@ graphics.round_rect(canvas, x, y, width, height, radius, color, fill=False) Draw an outlined or filled rounded rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2960) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2960) ## `graphics.circle` @@ -337,7 +337,7 @@ graphics.circle(canvas, x, y, radius, color, fill=False) Draw an outlined or filled circle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2961) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2961) ## `graphics.ellipse` @@ -347,7 +347,7 @@ graphics.ellipse(canvas, x, y, x_radius, y_radius, color, fill=False, mask=0x0F) Draw selected quadrants of an outlined or filled ellipse. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2962) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2962) ## `graphics.arc` @@ -357,7 +357,7 @@ graphics.arc(canvas, x, y, radius, start_angle, end_angle, color) Draw a circular arc between two angles. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2963) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2963) ## `graphics.triangle` @@ -367,7 +367,7 @@ graphics.triangle(canvas, x0, y0, x1, y1, x2, y2, color, fill=False) Draw an outlined or filled triangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2964) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2964) ## `graphics.gradient_rect` @@ -377,7 +377,7 @@ graphics.gradient_rect(canvas, x, y, width, height, color1, color2=None, vertica Fill a rectangle with a horizontal or vertical color gradient. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2965) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2965) ## `graphics.poly` @@ -387,7 +387,7 @@ graphics.poly(canvas, x, y, coordinates, color, fill=False) Draw a polygon from a packed coordinate buffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2966) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2966) ## `graphics.blit` @@ -397,7 +397,7 @@ graphics.blit(canvas, source, x, y, key=-1, palette=None) Copy another framebuffer onto the target with optional keying and palette lookup. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2967) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2967) ## `graphics.blit_rect` @@ -407,7 +407,7 @@ graphics.blit_rect(canvas, buffer, x, y, width, height) Copy a packed RGB565 rectangle onto the target. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2968) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2968) ## `graphics.blit_transparent` @@ -417,7 +417,7 @@ graphics.blit_transparent(canvas, buffer, x, y, width, height, key) Copy a packed RGB565 rectangle while skipping the transparent key. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2969) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2969) ## `graphics.polygon` @@ -427,7 +427,7 @@ graphics.polygon(canvas, points, x, y, color, angle=0, center_x=0, center_y=0) Draw a translated and optionally rotated sequence of points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2970) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2970) ## `graphics.text` @@ -437,7 +437,7 @@ graphics.text(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=No Render bitmap text with selectable height, scale, and inversion. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2976) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2976) ## `graphics.text8` @@ -447,7 +447,7 @@ graphics.text8(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=N Render text with the built-in 8-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2977) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2977) ## `graphics.text14` @@ -457,7 +457,7 @@ graphics.text14(canvas, text, x, y, color=1, scale=1, inverted=False, font_data= Render text with the built-in 14-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2978) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2978) ## `graphics.text16` @@ -467,4 +467,4 @@ graphics.text16(canvas, text, x, y, color=1, scale=1, inverted=False, font_data= Render text with the built-in 16-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2979) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2979) diff --git a/docs/generated/fonts-images.md b/docs/generated/fonts-images.md index 87972af..79dcdf2 100644 --- a/docs/generated/fonts-images.md +++ b/docs/generated/fonts-images.md @@ -3,7 +3,7 @@ title: Fonts and images description: Bitmap font rendering, RGB565 bitmap access, and image conversion helpers. --- -Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). +Source snapshot: [`bcaf65f4b2f43b4b0316ce2525e89abed7f7c331`](https://github.com/PyDevices/graphics/tree/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331). Bitmap font rendering, RGB565 bitmap access, and image conversion helpers. @@ -17,7 +17,7 @@ class Font(font_data=None, height=0, cached=True) Bitmap font loader and renderer for built-in, file, or buffer-backed glyph data. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2643) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2643) ## `graphics.Font.text` @@ -27,7 +27,7 @@ Font.text(canvas, text, x, y, color, scale=1, inverted=False) Render bitmap text with selectable height, scale, and inversion. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2633) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2633) ## `graphics.Font.draw_char` @@ -37,7 +37,7 @@ Font.draw_char(char, x, y, canvas, color, scale=1, inverted=False) Render one glyph through this Font onto a canvas. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2634) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2634) ## `graphics.Font.text_width` @@ -47,7 +47,7 @@ Font.text_width(text, scale=1) Measure rendered text width at the selected scale. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2635) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2635) ## `graphics.Font.deinit` @@ -57,7 +57,7 @@ Font.deinit() Release resources owned by the object. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2636) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2636) ## `graphics.Font.export` @@ -67,7 +67,7 @@ Font.export(filename) Write cached font bytes to a file. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2637) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2637) ## `graphics.Font.width` @@ -77,7 +77,7 @@ Font.width Width in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2626) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2626) ## `graphics.Font.height` @@ -87,7 +87,7 @@ Font.height Height in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2627) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2627) ## `graphics.Font.font_name` @@ -97,7 +97,7 @@ Font.font_name Font source name or the in-memory/default identifier. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2628) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2628) ## `graphics.BMP565` @@ -107,7 +107,7 @@ class BMP565(filename=None, source=None, streamed=False, mirrored=False, width=N RGB565 bitmap reader supporting buffered and streamed access. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2934) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2934) ## `graphics.BMP565.save` @@ -117,7 +117,7 @@ BMP565.save(filename=None) Write the current image or bitmap to a versioned output path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2923) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2923) ## `graphics.BMP565.deinit` @@ -127,7 +127,7 @@ BMP565.deinit() Release resources owned by the object. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2924) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2924) ## `graphics.BMP565.width` @@ -137,7 +137,7 @@ BMP565.width Width in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2914) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2914) ## `graphics.BMP565.height` @@ -147,7 +147,7 @@ BMP565.height Height in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2915) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2915) ## `graphics.BMP565.buffer` @@ -157,7 +157,7 @@ BMP565.buffer Backing pixel buffer when the object owns or exposes one. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2916) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2916) ## `graphics.BMP565.bpp` @@ -167,7 +167,7 @@ BMP565.bpp Bitmap color depth in bits per pixel. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2917) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2917) ## `graphics.BMP565.BPP` @@ -177,7 +177,7 @@ BMP565.BPP Bitmap storage width in bytes per pixel. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2918) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2918) ## `graphics.load_image` @@ -187,7 +187,7 @@ graphics.load_image(path) Load a supported image file as a FrameBuffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2971) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2971) ## `graphics.save_image` @@ -197,7 +197,7 @@ graphics.save_image(framebuffer, path='screenshot') Save a FrameBuffer to a versioned image path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2972) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2972) ## `graphics.bmp_to_framebuffer` @@ -207,7 +207,7 @@ graphics.bmp_to_framebuffer(path) Decode a BMP file into a FrameBuffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2973) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2973) ## `graphics.pbm_to_framebuffer` @@ -217,7 +217,7 @@ graphics.pbm_to_framebuffer(path) Decode a PBM file into a FrameBuffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2974) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2974) ## `graphics.pgm_to_framebuffer` @@ -227,4 +227,4 @@ graphics.pgm_to_framebuffer(path) Decode a PGM file into a FrameBuffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2975) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2975) diff --git a/docs/generated/framebuffer.md b/docs/generated/framebuffer.md index 055dede..e76ef4c 100644 --- a/docs/generated/framebuffer.md +++ b/docs/generated/framebuffer.md @@ -3,7 +3,7 @@ title: FrameBuffer description: Native framebuffer construction, drawing, text, blitting, scrolling, and file output. --- -Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). +Source snapshot: [`bcaf65f4b2f43b4b0316ce2525e89abed7f7c331`](https://github.com/PyDevices/graphics/tree/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331). Native framebuffer construction, drawing, text, blitting, scrolling, and file output. @@ -17,7 +17,7 @@ class FrameBuffer(buffer, width, height, format, stride=None) A framebuf-compatible native drawing surface backed by a writable buffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1112) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1112) ## `graphics.FrameBuffer.from_file` @@ -27,7 +27,7 @@ FrameBuffer.from_file(path) Load a supported image file into a new FrameBuffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1082) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1082) ## `graphics.FrameBuffer.fill` @@ -37,7 +37,7 @@ FrameBuffer.fill(color) Fill the target canvas with one color and return the affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1083) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1083) ## `graphics.FrameBuffer.fill_rect` @@ -47,7 +47,7 @@ FrameBuffer.fill_rect(x, y, width, height, color) Fill a rectangular region and return the clipped affected Area. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1084) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1084) ## `graphics.FrameBuffer.pixel` @@ -57,7 +57,7 @@ FrameBuffer.pixel(x, y, color=None) Read or write one pixel, depending on whether a color is supplied. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1085) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1085) ## `graphics.FrameBuffer.hline` @@ -67,7 +67,7 @@ FrameBuffer.hline(x, y, width, color) Draw a horizontal line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1086) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1086) ## `graphics.FrameBuffer.vline` @@ -77,7 +77,7 @@ FrameBuffer.vline(x, y, height, color) Draw a vertical line. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1087) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1087) ## `graphics.FrameBuffer.rect` @@ -87,7 +87,7 @@ FrameBuffer.rect(x, y, width, height, color, fill=False) Draw an outlined or filled rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1088) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1088) ## `graphics.FrameBuffer.round_rect` @@ -97,7 +97,7 @@ FrameBuffer.round_rect(x, y, width, height, radius, color, fill=False) Draw an outlined or filled rounded rectangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1089) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1089) ## `graphics.FrameBuffer.circle` @@ -107,7 +107,7 @@ FrameBuffer.circle(x, y, radius, color, fill=False) Draw an outlined or filled circle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1090) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1090) ## `graphics.FrameBuffer.line` @@ -117,7 +117,7 @@ FrameBuffer.line(x1, y1, x2, y2, color) Draw a line between two points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1091) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1091) ## `graphics.FrameBuffer.ellipse` @@ -127,7 +127,7 @@ FrameBuffer.ellipse(x, y, x_radius, y_radius, color, fill=False, mask=0x0F) Draw selected quadrants of an outlined or filled ellipse. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1092) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1092) ## `graphics.FrameBuffer.poly` @@ -137,7 +137,7 @@ FrameBuffer.poly(x, y, coordinates, color, fill=False) Draw a polygon from a packed coordinate buffer. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1093) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1093) ## `graphics.FrameBuffer.arc` @@ -147,7 +147,7 @@ FrameBuffer.arc(x, y, radius, start_angle, end_angle, color) Draw a circular arc between two angles. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1094) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1094) ## `graphics.FrameBuffer.triangle` @@ -157,7 +157,7 @@ FrameBuffer.triangle(x0, y0, x1, y1, x2, y2, color, fill=False) Draw an outlined or filled triangle. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1095) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1095) ## `graphics.FrameBuffer.gradient_rect` @@ -167,7 +167,7 @@ FrameBuffer.gradient_rect(x, y, width, height, color1, color2=None, vertical=Tru Fill a rectangle with a horizontal or vertical color gradient. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1096) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1096) ## `graphics.FrameBuffer.polygon` @@ -177,7 +177,7 @@ FrameBuffer.polygon(points, x, y, color, angle=0, center_x=0, center_y=0) Draw a translated and optionally rotated sequence of points. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1097) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1097) ## `graphics.FrameBuffer.blit` @@ -187,7 +187,7 @@ FrameBuffer.blit(source, x, y, key=-1, palette=None) Copy another framebuffer onto the target with optional keying and palette lookup. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1098) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1098) ## `graphics.FrameBuffer.blit_rect` @@ -197,7 +197,7 @@ FrameBuffer.blit_rect(buffer, x, y, width, height) Copy a packed RGB565 rectangle onto the target. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1099) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1099) ## `graphics.FrameBuffer.blit_transparent` @@ -207,7 +207,7 @@ FrameBuffer.blit_transparent(buffer, x, y, width, height, key) Copy a packed RGB565 rectangle while skipping the transparent key. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1100) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1100) ## `graphics.FrameBuffer.text` @@ -217,7 +217,7 @@ FrameBuffer.text(text, x, y, color=1, scale=1, inverted=False, font_data=None, h Render bitmap text with selectable height, scale, and inversion. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1101) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1101) ## `graphics.FrameBuffer.text8` @@ -227,7 +227,7 @@ FrameBuffer.text8(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 8-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1102) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1102) ## `graphics.FrameBuffer.text14` @@ -237,7 +237,7 @@ FrameBuffer.text14(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 14-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1103) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1103) ## `graphics.FrameBuffer.text16` @@ -247,7 +247,7 @@ FrameBuffer.text16(text, x, y, color=1, scale=1, inverted=False, font_data=None) Render text with the built-in 16-pixel-high font path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1104) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1104) ## `graphics.FrameBuffer.scroll` @@ -257,7 +257,7 @@ FrameBuffer.scroll(x_step, y_step) Move framebuffer contents by the requested x and y offsets. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1105) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1105) ## `graphics.FrameBuffer.save` @@ -267,7 +267,7 @@ FrameBuffer.save(path='screenshot') Write the current image or bitmap to a versioned output path. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1106) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1106) ## `graphics.FrameBuffer.width` @@ -277,7 +277,7 @@ FrameBuffer.width Width in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1073) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1073) ## `graphics.FrameBuffer.height` @@ -287,7 +287,7 @@ FrameBuffer.height Height in pixels. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1074) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1074) ## `graphics.FrameBuffer.buffer` @@ -297,7 +297,7 @@ FrameBuffer.buffer Backing pixel buffer when the object owns or exposes one. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1075) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1075) ## `graphics.FrameBuffer.format` @@ -307,7 +307,7 @@ FrameBuffer.format Framebuffer pixel-format identifier. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1076) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1076) ## `graphics.FrameBuffer.color_depth` @@ -317,4 +317,4 @@ FrameBuffer.color_depth Bits per pixel for the framebuffer format. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1077) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1077) diff --git a/docs/generated/introduction.md b/docs/generated/introduction.md index c0918d7..3d434f3 100644 --- a/docs/generated/introduction.md +++ b/docs/generated/introduction.md @@ -19,7 +19,7 @@ assert changed == Area(10, 10, 40, 24) ## Reference snapshot - Release line: **unreleased** -- Source commit: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552) +- Source commit: [`bcaf65f4b2f43b4b0316ce2525e89abed7f7c331`](https://github.com/PyDevices/graphics/tree/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331) - License: **MIT** - Adapter: **CPython binding tables plus public C headers** - Python API entries indexed: **136** diff --git a/docs/generated/manifest.json b/docs/generated/manifest.json index ab7d360..710f5b5 100644 --- a/docs/generated/manifest.json +++ b/docs/generated/manifest.json @@ -1,6 +1,6 @@ { "adapter": "cpython-binding-tables+public-c-headers", - "commit": "1da33021c74a2c72eb98babf88c28a194d0c3552", + "commit": "bcaf65f4b2f43b4b0316ce2525e89abed7f7c331", "generated_with": "sourcey@3.6.5", "license": "MIT", "native_c_symbol_count": 61, @@ -90,7 +90,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area", "signature": "class Area(x=0, y=0, w=0, h=0)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L395", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L395", "summary": "Immutable rectangle geometry used for bounds, clipping, and draw results." }, { @@ -101,7 +101,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.contains", "signature": "Area.contains(point_or_x, y=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L377", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L377", "summary": "Test whether the area contains a point." }, { @@ -112,7 +112,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.contains_area", "signature": "Area.contains_area(other)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L378", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L378", "summary": "Test whether another Area is fully contained." }, { @@ -123,7 +123,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.intersects", "signature": "Area.intersects(other)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L379", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L379", "summary": "Test whether two areas overlap." }, { @@ -134,7 +134,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.touches_or_intersects", "signature": "Area.touches_or_intersects(other)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L380", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L380", "summary": "Test whether two areas overlap or share an edge." }, { @@ -145,7 +145,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.shift", "signature": "Area.shift(dx=0, dy=0)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L381", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L381", "summary": "Return a copy translated by the requested offset." }, { @@ -156,7 +156,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.clip", "signature": "Area.clip(other)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L382", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L382", "summary": "Return the intersection with another Area." }, { @@ -167,7 +167,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.offset", "signature": "Area.offset(left, top=None, right=None, bottom=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L383", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L383", "summary": "Return an Area expanded independently on each edge." }, { @@ -178,7 +178,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.inset", "signature": "Area.inset(left, top=None, right=None, bottom=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L384", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L384", "summary": "Return an Area reduced independently on each edge." }, { @@ -189,7 +189,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.x", "signature": "Area.x", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L229", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L229", "summary": "Left coordinate." }, { @@ -200,7 +200,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.y", "signature": "Area.y", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L230", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L230", "summary": "Top coordinate." }, { @@ -211,7 +211,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.w", "signature": "Area.w", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L231", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L231", "summary": "Width in pixels." }, { @@ -222,7 +222,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Area.h", "signature": "Area.h", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L232", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L232", "summary": "Height in pixels." }, { @@ -232,7 +232,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClipContext", "signature": "class ClipContext(draw, area)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2147", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2147", "summary": "Context manager returned by Draw.clip() for scoped clipping." }, { @@ -243,7 +243,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClipContext.__enter__", "signature": "ClipContext.__enter__()", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2140", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2140", "summary": "Push the requested clipping area and return the effective clip." }, { @@ -254,7 +254,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClipContext.__exit__", "signature": "ClipContext.__exit__(exc_type, exc_value, traceback)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2141", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2141", "summary": "Pop the clipping area when leaving the context." }, { @@ -264,7 +264,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas", "signature": "class ClippedCanvas(canvas, clip)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2414", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2414", "summary": "Canvas proxy that intersects writes with a fixed Area." }, { @@ -275,7 +275,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.pixel", "signature": "ClippedCanvas.pixel(x, y, color=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2402", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2402", "summary": "Read or write one pixel, depending on whether a color is supplied." }, { @@ -286,7 +286,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.fill", "signature": "ClippedCanvas.fill(color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2403", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2403", "summary": "Fill the target canvas with one color and return the affected Area." }, { @@ -297,7 +297,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.fill_rect", "signature": "ClippedCanvas.fill_rect(x, y, width, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2404", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2404", "summary": "Fill a rectangular region and return the clipped affected Area." }, { @@ -308,7 +308,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.hline", "signature": "ClippedCanvas.hline(x, y, width, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2405", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2405", "summary": "Draw a horizontal line." }, { @@ -319,7 +319,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.vline", "signature": "ClippedCanvas.vline(x, y, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2406", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2406", "summary": "Draw a vertical line." }, { @@ -330,7 +330,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.blit_rect", "signature": "ClippedCanvas.blit_rect(buffer, x, y, width, height)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2407", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2407", "summary": "Copy a packed RGB565 rectangle onto the target." }, { @@ -341,7 +341,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.blit_transparent", "signature": "ClippedCanvas.blit_transparent(buffer, x, y, width, height, key)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2408", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2408", "summary": "Copy a packed RGB565 rectangle while skipping the transparent key." }, { @@ -352,7 +352,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.width", "signature": "ClippedCanvas.width", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2223", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2223", "summary": "Width in pixels." }, { @@ -363,7 +363,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ClippedCanvas.height", "signature": "ClippedCanvas.height", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2224", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2224", "summary": "Height in pixels." }, { @@ -373,7 +373,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer", "signature": "class FrameBuffer(buffer, width, height, format, stride=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1112", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1112", "summary": "A framebuf-compatible native drawing surface backed by a writable buffer." }, { @@ -384,7 +384,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.from_file", "signature": "FrameBuffer.from_file(path)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1082", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1082", "summary": "Load a supported image file into a new FrameBuffer." }, { @@ -395,7 +395,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.fill", "signature": "FrameBuffer.fill(color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1083", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1083", "summary": "Fill the target canvas with one color and return the affected Area." }, { @@ -406,7 +406,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.fill_rect", "signature": "FrameBuffer.fill_rect(x, y, width, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1084", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1084", "summary": "Fill a rectangular region and return the clipped affected Area." }, { @@ -417,7 +417,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.pixel", "signature": "FrameBuffer.pixel(x, y, color=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1085", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1085", "summary": "Read or write one pixel, depending on whether a color is supplied." }, { @@ -428,7 +428,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.hline", "signature": "FrameBuffer.hline(x, y, width, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1086", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1086", "summary": "Draw a horizontal line." }, { @@ -439,7 +439,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.vline", "signature": "FrameBuffer.vline(x, y, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1087", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1087", "summary": "Draw a vertical line." }, { @@ -450,7 +450,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.rect", "signature": "FrameBuffer.rect(x, y, width, height, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1088", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1088", "summary": "Draw an outlined or filled rectangle." }, { @@ -461,7 +461,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.round_rect", "signature": "FrameBuffer.round_rect(x, y, width, height, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1089", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1089", "summary": "Draw an outlined or filled rounded rectangle." }, { @@ -472,7 +472,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.circle", "signature": "FrameBuffer.circle(x, y, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1090", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1090", "summary": "Draw an outlined or filled circle." }, { @@ -483,7 +483,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.line", "signature": "FrameBuffer.line(x1, y1, x2, y2, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1091", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1091", "summary": "Draw a line between two points." }, { @@ -494,7 +494,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.ellipse", "signature": "FrameBuffer.ellipse(x, y, x_radius, y_radius, color, fill=False, mask=0x0F)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1092", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1092", "summary": "Draw selected quadrants of an outlined or filled ellipse." }, { @@ -505,7 +505,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.poly", "signature": "FrameBuffer.poly(x, y, coordinates, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1093", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1093", "summary": "Draw a polygon from a packed coordinate buffer." }, { @@ -516,7 +516,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.arc", "signature": "FrameBuffer.arc(x, y, radius, start_angle, end_angle, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1094", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1094", "summary": "Draw a circular arc between two angles." }, { @@ -527,7 +527,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.triangle", "signature": "FrameBuffer.triangle(x0, y0, x1, y1, x2, y2, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1095", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1095", "summary": "Draw an outlined or filled triangle." }, { @@ -538,7 +538,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.gradient_rect", "signature": "FrameBuffer.gradient_rect(x, y, width, height, color1, color2=None, vertical=True)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1096", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1096", "summary": "Fill a rectangle with a horizontal or vertical color gradient." }, { @@ -549,7 +549,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.polygon", "signature": "FrameBuffer.polygon(points, x, y, color, angle=0, center_x=0, center_y=0)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1097", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1097", "summary": "Draw a translated and optionally rotated sequence of points." }, { @@ -560,7 +560,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.blit", "signature": "FrameBuffer.blit(source, x, y, key=-1, palette=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1098", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1098", "summary": "Copy another framebuffer onto the target with optional keying and palette lookup." }, { @@ -571,7 +571,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.blit_rect", "signature": "FrameBuffer.blit_rect(buffer, x, y, width, height)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1099", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1099", "summary": "Copy a packed RGB565 rectangle onto the target." }, { @@ -582,7 +582,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.blit_transparent", "signature": "FrameBuffer.blit_transparent(buffer, x, y, width, height, key)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1100", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1100", "summary": "Copy a packed RGB565 rectangle while skipping the transparent key." }, { @@ -593,7 +593,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.text", "signature": "FrameBuffer.text(text, x, y, color=1, scale=1, inverted=False, font_data=None, height=8)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1101", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1101", "summary": "Render bitmap text with selectable height, scale, and inversion." }, { @@ -604,7 +604,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.text8", "signature": "FrameBuffer.text8(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1102", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1102", "summary": "Render text with the built-in 8-pixel-high font path." }, { @@ -615,7 +615,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.text14", "signature": "FrameBuffer.text14(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1103", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1103", "summary": "Render text with the built-in 14-pixel-high font path." }, { @@ -626,7 +626,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.text16", "signature": "FrameBuffer.text16(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1104", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1104", "summary": "Render text with the built-in 16-pixel-high font path." }, { @@ -637,7 +637,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.scroll", "signature": "FrameBuffer.scroll(x_step, y_step)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1105", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1105", "summary": "Move framebuffer contents by the requested x and y offsets." }, { @@ -648,7 +648,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.save", "signature": "FrameBuffer.save(path='screenshot')", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1106", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1106", "summary": "Write the current image or bitmap to a versioned output path." }, { @@ -659,7 +659,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.width", "signature": "FrameBuffer.width", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1073", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1073", "summary": "Width in pixels." }, { @@ -670,7 +670,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.height", "signature": "FrameBuffer.height", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1074", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1074", "summary": "Height in pixels." }, { @@ -681,7 +681,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.buffer", "signature": "FrameBuffer.buffer", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1075", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1075", "summary": "Backing pixel buffer when the object owns or exposes one." }, { @@ -692,7 +692,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.format", "signature": "FrameBuffer.format", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1076", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1076", "summary": "Framebuffer pixel-format identifier." }, { @@ -703,7 +703,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.FrameBuffer.color_depth", "signature": "FrameBuffer.color_depth", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L1077", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L1077", "summary": "Bits per pixel for the framebuffer format." }, { @@ -713,7 +713,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw", "signature": "class Draw(canvas)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2085", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2085", "summary": "Drawing facade that adds a bounded clip stack to any compatible canvas." }, { @@ -724,7 +724,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.fill", "signature": "Draw.fill(color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2057", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2057", "summary": "Fill the target canvas with one color and return the affected Area." }, { @@ -735,7 +735,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.fill_rect", "signature": "Draw.fill_rect(x, y, width, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2058", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2058", "summary": "Fill a rectangular region and return the clipped affected Area." }, { @@ -746,7 +746,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.pixel", "signature": "Draw.pixel(x, y, color=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2059", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2059", "summary": "Read or write one pixel, depending on whether a color is supplied." }, { @@ -757,7 +757,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.hline", "signature": "Draw.hline(x, y, width, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2060", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2060", "summary": "Draw a horizontal line." }, { @@ -768,7 +768,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.vline", "signature": "Draw.vline(x, y, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2061", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2061", "summary": "Draw a vertical line." }, { @@ -779,7 +779,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.line", "signature": "Draw.line(x1, y1, x2, y2, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2062", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2062", "summary": "Draw a line between two points." }, { @@ -790,7 +790,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.rect", "signature": "Draw.rect(x, y, width, height, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2063", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2063", "summary": "Draw an outlined or filled rectangle." }, { @@ -801,7 +801,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.round_rect", "signature": "Draw.round_rect(x, y, width, height, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2064", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2064", "summary": "Draw an outlined or filled rounded rectangle." }, { @@ -812,7 +812,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.circle", "signature": "Draw.circle(x, y, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2065", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2065", "summary": "Draw an outlined or filled circle." }, { @@ -823,7 +823,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.ellipse", "signature": "Draw.ellipse(x, y, x_radius, y_radius, color, fill=False, mask=0x0F)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2066", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2066", "summary": "Draw selected quadrants of an outlined or filled ellipse." }, { @@ -834,7 +834,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.arc", "signature": "Draw.arc(x, y, radius, start_angle, end_angle, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2067", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2067", "summary": "Draw a circular arc between two angles." }, { @@ -845,7 +845,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.triangle", "signature": "Draw.triangle(x0, y0, x1, y1, x2, y2, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2068", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2068", "summary": "Draw an outlined or filled triangle." }, { @@ -856,7 +856,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.gradient_rect", "signature": "Draw.gradient_rect(x, y, width, height, color1, color2=None, vertical=True)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2069", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2069", "summary": "Fill a rectangle with a horizontal or vertical color gradient." }, { @@ -867,7 +867,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.poly", "signature": "Draw.poly(x, y, coordinates, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2070", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2070", "summary": "Draw a polygon from a packed coordinate buffer." }, { @@ -878,7 +878,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.polygon", "signature": "Draw.polygon(points, x, y, color, angle=0, center_x=0, center_y=0)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2071", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2071", "summary": "Draw a translated and optionally rotated sequence of points." }, { @@ -889,7 +889,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.blit", "signature": "Draw.blit(source, x, y, key=-1, palette=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2072", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2072", "summary": "Copy another framebuffer onto the target with optional keying and palette lookup." }, { @@ -900,7 +900,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.blit_rect", "signature": "Draw.blit_rect(buffer, x, y, width, height)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2073", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2073", "summary": "Copy a packed RGB565 rectangle onto the target." }, { @@ -911,7 +911,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.blit_transparent", "signature": "Draw.blit_transparent(buffer, x, y, width, height, key)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2074", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2074", "summary": "Copy a packed RGB565 rectangle while skipping the transparent key." }, { @@ -922,7 +922,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.text", "signature": "Draw.text(text, x, y, color=1, scale=1, inverted=False, font_data=None, height=8)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2075", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2075", "summary": "Render bitmap text with selectable height, scale, and inversion." }, { @@ -933,7 +933,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.text8", "signature": "Draw.text8(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2076", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2076", "summary": "Render text with the built-in 8-pixel-high font path." }, { @@ -944,7 +944,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.text14", "signature": "Draw.text14(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2077", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2077", "summary": "Render text with the built-in 14-pixel-high font path." }, { @@ -955,7 +955,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.text16", "signature": "Draw.text16(text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2078", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2078", "summary": "Render text with the built-in 16-pixel-high font path." }, { @@ -966,7 +966,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Draw.clip", "signature": "Draw.clip(area_or_x, y=None, width=None, height=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2079", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2079", "summary": "Return the intersection with another Area." }, { @@ -977,7 +977,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.fill", "signature": "graphics.fill(canvas, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2953", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2953", "summary": "Fill the target canvas with one color and return the affected Area." }, { @@ -988,7 +988,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.fill_rect", "signature": "graphics.fill_rect(canvas, x, y, width, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2954", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2954", "summary": "Fill a rectangular region and return the clipped affected Area." }, { @@ -999,7 +999,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.pixel", "signature": "graphics.pixel(canvas, x, y, color=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2955", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2955", "summary": "Read or write one pixel, depending on whether a color is supplied." }, { @@ -1010,7 +1010,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.hline", "signature": "graphics.hline(canvas, x, y, width, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2956", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2956", "summary": "Draw a horizontal line." }, { @@ -1021,7 +1021,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.vline", "signature": "graphics.vline(canvas, x, y, height, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2957", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2957", "summary": "Draw a vertical line." }, { @@ -1032,7 +1032,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.line", "signature": "graphics.line(canvas, x1, y1, x2, y2, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2958", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2958", "summary": "Draw a line between two points." }, { @@ -1043,7 +1043,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.rect", "signature": "graphics.rect(canvas, x, y, width, height, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2959", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2959", "summary": "Draw an outlined or filled rectangle." }, { @@ -1054,7 +1054,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.round_rect", "signature": "graphics.round_rect(canvas, x, y, width, height, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2960", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2960", "summary": "Draw an outlined or filled rounded rectangle." }, { @@ -1065,7 +1065,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.circle", "signature": "graphics.circle(canvas, x, y, radius, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2961", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2961", "summary": "Draw an outlined or filled circle." }, { @@ -1076,7 +1076,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.ellipse", "signature": "graphics.ellipse(canvas, x, y, x_radius, y_radius, color, fill=False, mask=0x0F)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2962", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2962", "summary": "Draw selected quadrants of an outlined or filled ellipse." }, { @@ -1087,7 +1087,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.arc", "signature": "graphics.arc(canvas, x, y, radius, start_angle, end_angle, color)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2963", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2963", "summary": "Draw a circular arc between two angles." }, { @@ -1098,7 +1098,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.triangle", "signature": "graphics.triangle(canvas, x0, y0, x1, y1, x2, y2, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2964", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2964", "summary": "Draw an outlined or filled triangle." }, { @@ -1109,7 +1109,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.gradient_rect", "signature": "graphics.gradient_rect(canvas, x, y, width, height, color1, color2=None, vertical=True)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2965", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2965", "summary": "Fill a rectangle with a horizontal or vertical color gradient." }, { @@ -1120,7 +1120,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.poly", "signature": "graphics.poly(canvas, x, y, coordinates, color, fill=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2966", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2966", "summary": "Draw a polygon from a packed coordinate buffer." }, { @@ -1131,7 +1131,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.blit", "signature": "graphics.blit(canvas, source, x, y, key=-1, palette=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2967", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2967", "summary": "Copy another framebuffer onto the target with optional keying and palette lookup." }, { @@ -1142,7 +1142,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.blit_rect", "signature": "graphics.blit_rect(canvas, buffer, x, y, width, height)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2968", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2968", "summary": "Copy a packed RGB565 rectangle onto the target." }, { @@ -1153,7 +1153,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.blit_transparent", "signature": "graphics.blit_transparent(canvas, buffer, x, y, width, height, key)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2969", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2969", "summary": "Copy a packed RGB565 rectangle while skipping the transparent key." }, { @@ -1164,7 +1164,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.polygon", "signature": "graphics.polygon(canvas, points, x, y, color, angle=0, center_x=0, center_y=0)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2970", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2970", "summary": "Draw a translated and optionally rotated sequence of points." }, { @@ -1175,7 +1175,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.text", "signature": "graphics.text(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=None, height=8)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2976", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2976", "summary": "Render bitmap text with selectable height, scale, and inversion." }, { @@ -1186,7 +1186,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.text8", "signature": "graphics.text8(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2977", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2977", "summary": "Render text with the built-in 8-pixel-high font path." }, { @@ -1197,7 +1197,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.text14", "signature": "graphics.text14(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2978", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2978", "summary": "Render text with the built-in 14-pixel-high font path." }, { @@ -1208,7 +1208,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.text16", "signature": "graphics.text16(canvas, text, x, y, color=1, scale=1, inverted=False, font_data=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2979", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2979", "summary": "Render text with the built-in 16-pixel-high font path." }, { @@ -1218,7 +1218,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font", "signature": "class Font(font_data=None, height=0, cached=True)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2643", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2643", "summary": "Bitmap font loader and renderer for built-in, file, or buffer-backed glyph data." }, { @@ -1229,7 +1229,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.text", "signature": "Font.text(canvas, text, x, y, color, scale=1, inverted=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2633", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2633", "summary": "Render bitmap text with selectable height, scale, and inversion." }, { @@ -1240,7 +1240,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.draw_char", "signature": "Font.draw_char(char, x, y, canvas, color, scale=1, inverted=False)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2634", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2634", "summary": "Render one glyph through this Font onto a canvas." }, { @@ -1251,7 +1251,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.text_width", "signature": "Font.text_width(text, scale=1)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2635", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2635", "summary": "Measure rendered text width at the selected scale." }, { @@ -1262,7 +1262,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.deinit", "signature": "Font.deinit()", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2636", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2636", "summary": "Release resources owned by the object." }, { @@ -1273,7 +1273,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.export", "signature": "Font.export(filename)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2637", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2637", "summary": "Write cached font bytes to a file." }, { @@ -1284,7 +1284,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.width", "signature": "Font.width", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2626", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2626", "summary": "Width in pixels." }, { @@ -1295,7 +1295,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.height", "signature": "Font.height", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2627", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2627", "summary": "Height in pixels." }, { @@ -1306,7 +1306,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.Font.font_name", "signature": "Font.font_name", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2628", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2628", "summary": "Font source name or the in-memory/default identifier." }, { @@ -1316,7 +1316,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565", "signature": "class BMP565(filename=None, source=None, streamed=False, mirrored=False, width=None, height=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2934", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2934", "summary": "RGB565 bitmap reader supporting buffered and streamed access." }, { @@ -1327,7 +1327,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.save", "signature": "BMP565.save(filename=None)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2923", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2923", "summary": "Write the current image or bitmap to a versioned output path." }, { @@ -1338,7 +1338,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.deinit", "signature": "BMP565.deinit()", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2924", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2924", "summary": "Release resources owned by the object." }, { @@ -1349,7 +1349,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.width", "signature": "BMP565.width", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2914", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2914", "summary": "Width in pixels." }, { @@ -1360,7 +1360,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.height", "signature": "BMP565.height", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2915", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2915", "summary": "Height in pixels." }, { @@ -1371,7 +1371,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.buffer", "signature": "BMP565.buffer", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2916", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2916", "summary": "Backing pixel buffer when the object owns or exposes one." }, { @@ -1382,7 +1382,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.bpp", "signature": "BMP565.bpp", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2917", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2917", "summary": "Bitmap color depth in bits per pixel." }, { @@ -1393,7 +1393,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.BMP565.BPP", "signature": "BMP565.BPP", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2918", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2918", "summary": "Bitmap storage width in bytes per pixel." }, { @@ -1404,7 +1404,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.load_image", "signature": "graphics.load_image(path)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2971", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2971", "summary": "Load a supported image file as a FrameBuffer." }, { @@ -1415,7 +1415,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.save_image", "signature": "graphics.save_image(framebuffer, path='screenshot')", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2972", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2972", "summary": "Save a FrameBuffer to a versioned image path." }, { @@ -1426,7 +1426,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.bmp_to_framebuffer", "signature": "graphics.bmp_to_framebuffer(path)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2973", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2973", "summary": "Decode a BMP file into a FrameBuffer." }, { @@ -1437,7 +1437,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.pbm_to_framebuffer", "signature": "graphics.pbm_to_framebuffer(path)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2974", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2974", "summary": "Decode a PBM file into a FrameBuffer." }, { @@ -1448,7 +1448,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.pgm_to_framebuffer", "signature": "graphics.pgm_to_framebuffer(path)", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2975", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2975", "summary": "Decode a PGM file into a FrameBuffer." }, { @@ -1459,7 +1459,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.framebuf_backend", "signature": "graphics.framebuf_backend()", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2950", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2950", "summary": "Return the active framebuffer backend identifier." }, { @@ -1470,7 +1470,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.implementation", "signature": "graphics.implementation()", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2951", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2951", "summary": "Return the implementation identifier for this graphics module." }, { @@ -1481,7 +1481,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.capabilities", "signature": "graphics.capabilities()", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2952", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2952", "summary": "Return structured runtime feature and pixel-format capabilities." }, { @@ -1492,7 +1492,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.MONO_VLSB", "signature": "graphics.MONO_VLSB", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3027", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3027", "summary": "Monochrome vertical least-significant-bit layout." }, { @@ -1503,7 +1503,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.MONO_HLSB", "signature": "graphics.MONO_HLSB", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3028", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3028", "summary": "Monochrome horizontal least-significant-bit layout." }, { @@ -1514,7 +1514,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.MONO_HMSB", "signature": "graphics.MONO_HMSB", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3029", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3029", "summary": "Monochrome horizontal most-significant-bit layout." }, { @@ -1525,7 +1525,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.RGB565", "signature": "graphics.RGB565", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3030", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3030", "summary": "16-bit RGB565 pixel layout." }, { @@ -1536,7 +1536,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.GS2_HMSB", "signature": "graphics.GS2_HMSB", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3031", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3031", "summary": "2-bit grayscale horizontal most-significant-bit layout." }, { @@ -1547,7 +1547,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.GS4_HMSB", "signature": "graphics.GS4_HMSB", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3032", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3032", "summary": "4-bit grayscale horizontal most-significant-bit layout." }, { @@ -1558,7 +1558,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.GS8", "signature": "graphics.GS8", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3033", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3033", "summary": "8-bit grayscale layout." }, { @@ -1569,7 +1569,7 @@ "path": "src/gfx_module_cpy.c", "qualified_name": "graphics.RGB888", "signature": "graphics.RGB888", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3034", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3034", "summary": "24-bit RGB888 pixel layout." }, { @@ -1579,7 +1579,7 @@ "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_validate_buffer", "signature": "int gfx_fb_validate_buffer(size_t buf_len, int width, int height, int format, int stride);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L21", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L21", "summary": "Native fb entry point declared for firmware and extension integration." }, { @@ -1589,7 +1589,7 @@ "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_color_depth", "signature": "int gfx_fb_color_depth(int format);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L22", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L22", "summary": "Native fb entry point declared for firmware and extension integration." }, { @@ -1599,7 +1599,7 @@ "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_setpixel", "signature": "void gfx_fb_setpixel(const gfx_fb_t *fb, unsigned int x, unsigned int y, uint32_t col);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L24", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L24", "summary": "Native fb entry point declared for firmware and extension integration." }, { @@ -1609,7 +1609,7 @@ "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_getpixel", "signature": "uint32_t gfx_fb_getpixel(const gfx_fb_t *fb, unsigned int x, unsigned int y);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L25", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L25", "summary": "Native fb entry point declared for firmware and extension integration." }, { @@ -1619,7 +1619,7 @@ "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_setpixel_checked", "signature": "void gfx_fb_setpixel_checked(const gfx_fb_t *fb, int x, int y, int col, int mask);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L26", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L26", "summary": "Native fb entry point declared for firmware and extension integration." }, { @@ -1629,7 +1629,7 @@ "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_fill_rect_raw", "signature": "void gfx_fb_fill_rect_raw(const gfx_fb_t *fb, int x, int y, int w, int h, uint32_t col);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L27", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L27", "summary": "Native fb entry point declared for firmware and extension integration." }, { @@ -1639,7 +1639,7 @@ "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_fill_rect", "signature": "gfx_area_t gfx_fb_fill_rect(const gfx_fb_t *fb, int x, int y, int w, int h, uint32_t col);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L28", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L28", "summary": "Native fb entry point declared for firmware and extension integration." }, { @@ -1649,7 +1649,7 @@ "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_fill", "signature": "gfx_area_t gfx_fb_fill(const gfx_fb_t *fb, uint32_t col);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L29", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L29", "summary": "Native fb entry point declared for firmware and extension integration." }, { @@ -1659,7 +1659,7 @@ "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_canvas_init", "signature": "void gfx_fb_canvas_init(gfx_canvas_t *canvas, const gfx_fb_t *fb);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L31", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L31", "summary": "Native fb entry point declared for firmware and extension integration." }, { @@ -1669,7 +1669,7 @@ "path": "include/gfx_framebuffer.h", "qualified_name": "gfx_fb_scroll", "signature": "void gfx_fb_scroll(gfx_fb_t *fb, int xstep, int ystep);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L32", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L32", "summary": "Native fb entry point declared for firmware and extension integration." }, { @@ -1679,7 +1679,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_clipped_canvas_init", "signature": "void gfx_clipped_canvas_init(gfx_clipped_canvas_t *cc, const gfx_canvas_t *parent, const gfx_area_t *clip);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L20", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L20", "summary": "Native clipped entry point declared for firmware and extension integration." }, { @@ -1689,7 +1689,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_pixel", "signature": "gfx_area_t gfx_shapes_pixel(const gfx_canvas_t *canvas, int x, int y, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L22", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L22", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1699,7 +1699,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_hline", "signature": "gfx_area_t gfx_shapes_hline(const gfx_canvas_t *canvas, int x, int y, int w, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L23", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L23", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1709,7 +1709,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_vline", "signature": "gfx_area_t gfx_shapes_vline(const gfx_canvas_t *canvas, int x, int y, int h, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L24", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L24", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1719,7 +1719,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_fill_rect", "signature": "gfx_area_t gfx_shapes_fill_rect(const gfx_canvas_t *canvas, int x, int y, int w, int h, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L25", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L25", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1729,7 +1729,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_rect", "signature": "gfx_area_t gfx_shapes_rect(const gfx_canvas_t *canvas, int x, int y, int w, int h, int c, int fill);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L26", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L26", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1739,7 +1739,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_line", "signature": "gfx_area_t gfx_shapes_line(const gfx_canvas_t *canvas, int x1, int y1, int x2, int y2, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L27", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L27", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1749,7 +1749,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_ellipse", "signature": "gfx_area_t gfx_shapes_ellipse(const gfx_canvas_t *canvas, int cx, int cy, int xradius, int yradius, int col, int fill, int mask_part);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L28", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L28", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1759,7 +1759,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_fill", "signature": "gfx_area_t gfx_shapes_fill(const gfx_canvas_t *canvas, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L29", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L29", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1769,7 +1769,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_poly_int_from_buffer", "signature": "int gfx_shapes_poly_int_from_buffer(const void *buf, size_t len, size_t itemsize, const char *fmt, size_t index, int *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L31", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L31", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1779,7 +1779,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_poly", "signature": "gfx_area_t gfx_shapes_poly(const gfx_canvas_t *canvas, int x, int y, const void *coords, size_t coords_len, size_t itemsize, const char *fmt, int col, int fill);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L32", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L32", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1789,7 +1789,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_blit", "signature": "gfx_area_t gfx_shapes_blit(const gfx_canvas_t *canvas, const gfx_fb_t *source, int x, int y, int key, const gfx_fb_t *palette);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L33", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L33", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1799,7 +1799,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_circle", "signature": "gfx_area_t gfx_shapes_circle(const gfx_canvas_t *canvas, int x0, int y0, int r, int c, int fill);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L34", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L34", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1809,7 +1809,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_round_rect", "signature": "gfx_area_t gfx_shapes_round_rect(const gfx_canvas_t *canvas, int x0, int y0, int w, int h, int r, int c, int fill);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L35", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L35", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1819,7 +1819,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_triangle", "signature": "gfx_area_t gfx_shapes_triangle(const gfx_canvas_t *canvas, int x0, int y0, int x1, int y1, int x2, int y2, int c, int fill);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L36", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L36", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1829,7 +1829,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_arc", "signature": "gfx_area_t gfx_shapes_arc(const gfx_canvas_t *canvas, int x, int y, int r, float a0, float a1, int c);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L37", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L37", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1839,7 +1839,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_gradient_rect", "signature": "gfx_area_t gfx_shapes_gradient_rect(const gfx_canvas_t *canvas, int x, int y, int w, int h, int c1, int c2, int vertical);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L38", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L38", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1849,7 +1849,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_blit_rect", "signature": "gfx_area_t gfx_shapes_blit_rect(const gfx_canvas_t *canvas, const void *buf, int x, int y, int w, int h, int bpp);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L39", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L39", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1859,7 +1859,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_blit_transparent", "signature": "gfx_area_t gfx_shapes_blit_transparent(const gfx_canvas_t *canvas, const void *buf, int x, int y, int w, int h, int key, int bpp);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L40", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L40", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1869,7 +1869,7 @@ "path": "include/gfx_shapes.h", "qualified_name": "gfx_shapes_polygon", "signature": "gfx_area_t gfx_shapes_polygon(const gfx_canvas_t *canvas, const int *points, size_t n_points, int x, int y, int color, float angle, int center_x, int center_y);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L41", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L41", "summary": "Native shapes entry point declared for firmware and extension integration." }, { @@ -1879,7 +1879,7 @@ "path": "include/gfx_draw.h", "qualified_name": "gfx_draw_init", "signature": "void gfx_draw_init(gfx_draw_t *draw, const gfx_canvas_t *canvas);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L21", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_draw.h#L21", "summary": "Native draw entry point declared for firmware and extension integration." }, { @@ -1889,7 +1889,7 @@ "path": "include/gfx_draw.h", "qualified_name": "gfx_draw_effective_clip", "signature": "gfx_area_t gfx_draw_effective_clip(const gfx_draw_t *draw);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L23", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_draw.h#L23", "summary": "Native draw entry point declared for firmware and extension integration." }, { @@ -1899,7 +1899,7 @@ "path": "include/gfx_draw.h", "qualified_name": "gfx_draw_push_clip", "signature": "void gfx_draw_push_clip(gfx_draw_t *draw, const gfx_area_t *clip);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L24", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_draw.h#L24", "summary": "Native draw entry point declared for firmware and extension integration." }, { @@ -1909,7 +1909,7 @@ "path": "include/gfx_draw.h", "qualified_name": "gfx_draw_pop_clip", "signature": "void gfx_draw_pop_clip(gfx_draw_t *draw);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L25", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_draw.h#L25", "summary": "Native draw entry point declared for firmware and extension integration." }, { @@ -1919,7 +1919,7 @@ "path": "include/gfx_font.h", "qualified_name": "gfx_font_init_default", "signature": "void gfx_font_init_default(gfx_font_t *font, int height);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L22", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L22", "summary": "Native font entry point declared for firmware and extension integration." }, { @@ -1929,7 +1929,7 @@ "path": "include/gfx_font.h", "qualified_name": "gfx_font_init_from_data", "signature": "void gfx_font_init_from_data(gfx_font_t *font, const uint8_t *data, size_t len, int height);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L23", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L23", "summary": "Native font entry point declared for firmware and extension integration." }, { @@ -1939,7 +1939,7 @@ "path": "include/gfx_font.h", "qualified_name": "gfx_font_deinit", "signature": "void gfx_font_deinit(gfx_font_t *font);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L24", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L24", "summary": "Native font entry point declared for firmware and extension integration." }, { @@ -1949,7 +1949,7 @@ "path": "include/gfx_font.h", "qualified_name": "gfx_font_export", "signature": "int gfx_font_export(const gfx_font_t *font, const char *filename);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L26", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L26", "summary": "Native font entry point declared for firmware and extension integration." }, { @@ -1959,7 +1959,7 @@ "path": "include/gfx_font.h", "qualified_name": "gfx_font_text", "signature": "gfx_area_t gfx_font_text(const gfx_canvas_t *canvas, const gfx_font_t *font, const char *str, int x0, int y0, int col, int scale, int inverted);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L29", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L29", "summary": "Native font entry point declared for firmware and extension integration." }, { @@ -1969,7 +1969,7 @@ "path": "include/gfx_font.h", "qualified_name": "gfx_font_draw_char", "signature": "gfx_area_t gfx_font_draw_char(const gfx_canvas_t *canvas, const gfx_font_t *font, unsigned char ch, int x, int y, int color, int scale, int inverted);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L30", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L30", "summary": "Native font entry point declared for firmware and extension integration." }, { @@ -1979,7 +1979,7 @@ "path": "include/gfx_font.h", "qualified_name": "gfx_font_text_width", "signature": "int gfx_font_text_width(const gfx_font_t *font, const char *str, int scale);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L31", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L31", "summary": "Native font entry point declared for firmware and extension integration." }, { @@ -1989,7 +1989,7 @@ "path": "include/gfx_font.h", "qualified_name": "gfx_font_text8", "signature": "gfx_area_t gfx_font_text8(const gfx_canvas_t *canvas, const char *str, int x0, int y0, int col);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L32", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L32", "summary": "Native font entry point declared for firmware and extension integration." }, { @@ -1999,7 +1999,7 @@ "path": "include/gfx_font.h", "qualified_name": "gfx_font_text14", "signature": "gfx_area_t gfx_font_text14(const gfx_canvas_t *canvas, const char *str, int x0, int y0, int col);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L33", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L33", "summary": "Native font entry point declared for firmware and extension integration." }, { @@ -2009,7 +2009,7 @@ "path": "include/gfx_font.h", "qualified_name": "gfx_font_text16", "signature": "gfx_area_t gfx_font_text16(const gfx_canvas_t *canvas, const char *str, int x0, int y0, int col);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L34", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L34", "summary": "Native font entry point declared for firmware and extension integration." }, { @@ -2019,7 +2019,7 @@ "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_read_header_from_file", "signature": "int gfx_bmp565_read_header_from_file(const char *path, int *width, int *height, unsigned int *data_offset);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L27", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L27", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { @@ -2029,7 +2029,7 @@ "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_open_stream", "signature": "int gfx_bmp565_open_stream(const char *path, gfx_bmp565_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L28", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L28", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { @@ -2039,7 +2039,7 @@ "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_load_from_file", "signature": "int gfx_bmp565_load_from_file(const char *path, gfx_bmp565_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L29", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L29", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { @@ -2049,7 +2049,7 @@ "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_init_from_buffer", "signature": "int gfx_bmp565_init_from_buffer(gfx_bmp565_t *out, const uint8_t *buf, size_t len, int width, int height);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L30", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L30", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { @@ -2059,7 +2059,7 @@ "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_deinit", "signature": "void gfx_bmp565_deinit(gfx_bmp565_t *bmp);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L31", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L31", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { @@ -2069,7 +2069,7 @@ "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_save", "signature": "int gfx_bmp565_save(const gfx_bmp565_t *bmp, const char *path);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L32", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L32", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { @@ -2079,7 +2079,7 @@ "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_save_versioned", "signature": "int gfx_bmp565_save_versioned(const gfx_bmp565_t *bmp, const char *path, char *out_path, size_t out_path_len);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L33", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L33", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { @@ -2089,7 +2089,7 @@ "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_read_bytes", "signature": "int gfx_bmp565_read_bytes(const gfx_bmp565_t *bmp, int start, int stop, uint8_t *out, size_t out_cap, size_t *out_len);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L34", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L34", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { @@ -2099,7 +2099,7 @@ "path": "include/gfx_bmp565.h", "qualified_name": "gfx_bmp565_read_region", "signature": "int gfx_bmp565_read_region(const gfx_bmp565_t *bmp, int x0, int x1, int y0, int y1, uint8_t *out, size_t out_cap, size_t *out_len);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L35", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L35", "summary": "Native bmp565 entry point declared for firmware and extension integration." }, { @@ -2109,7 +2109,7 @@ "path": "include/gfx_files.h", "qualified_name": "gfx_image_probe", "signature": "int gfx_image_probe(const uint8_t *data, size_t len, gfx_image_info_t *info);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L43", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L43", "summary": "Native image entry point declared for firmware and extension integration." }, { @@ -2119,7 +2119,7 @@ "path": "include/gfx_files.h", "qualified_name": "gfx_image_decode", "signature": "int gfx_image_decode(const uint8_t *data, size_t len, const gfx_image_info_t *info, uint8_t *dst, size_t dst_len);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L47", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L47", "summary": "Native image entry point declared for firmware and extension integration." }, { @@ -2129,7 +2129,7 @@ "path": "include/gfx_files.h", "qualified_name": "gfx_image_fb_free", "signature": "void gfx_image_fb_free(gfx_image_fb_t *img);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L64", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L64", "summary": "Native image entry point declared for firmware and extension integration." }, { @@ -2139,7 +2139,7 @@ "path": "include/gfx_files.h", "qualified_name": "gfx_files_load_image", "signature": "int gfx_files_load_image(const char *path, gfx_image_fb_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L65", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L65", "summary": "Native files entry point declared for firmware and extension integration." }, { @@ -2149,7 +2149,7 @@ "path": "include/gfx_files.h", "qualified_name": "gfx_files_save_image", "signature": "int gfx_files_save_image(const gfx_fb_t *fb, const char *path, char *out_path, size_t out_path_len);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L66", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L66", "summary": "Native files entry point declared for firmware and extension integration." }, { @@ -2159,7 +2159,7 @@ "path": "include/gfx_files.h", "qualified_name": "gfx_files_pbm_to_framebuffer", "signature": "int gfx_files_pbm_to_framebuffer(const char *path, gfx_image_fb_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L67", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L67", "summary": "Native files entry point declared for firmware and extension integration." }, { @@ -2169,7 +2169,7 @@ "path": "include/gfx_files.h", "qualified_name": "gfx_files_pgm_to_framebuffer", "signature": "int gfx_files_pgm_to_framebuffer(const char *path, gfx_image_fb_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L68", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L68", "summary": "Native files entry point declared for firmware and extension integration." }, { @@ -2179,7 +2179,7 @@ "path": "include/gfx_files.h", "qualified_name": "gfx_files_bmp_to_framebuffer", "signature": "int gfx_files_bmp_to_framebuffer(const char *path, gfx_image_fb_t *out);", - "source_url": "https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L69", + "source_url": "https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L69", "summary": "Native files entry point declared for firmware and extension integration." } ] diff --git a/docs/generated/module-formats.md b/docs/generated/module-formats.md index 57fb214..9d3b8f7 100644 --- a/docs/generated/module-formats.md +++ b/docs/generated/module-formats.md @@ -3,7 +3,7 @@ title: Runtime and pixel formats description: Runtime capability probes and the exported framebuffer format constants. --- -Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). +Source snapshot: [`bcaf65f4b2f43b4b0316ce2525e89abed7f7c331`](https://github.com/PyDevices/graphics/tree/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331). Runtime capability probes and the exported framebuffer format constants. @@ -17,7 +17,7 @@ graphics.framebuf_backend() Return the active framebuffer backend identifier. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2950) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2950) ## `graphics.implementation` @@ -27,7 +27,7 @@ graphics.implementation() Return the implementation identifier for this graphics module. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2951) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2951) ## `graphics.capabilities` @@ -37,7 +37,7 @@ graphics.capabilities() Return structured runtime feature and pixel-format capabilities. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L2952) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L2952) ## `graphics.MONO_VLSB` @@ -47,7 +47,7 @@ graphics.MONO_VLSB Monochrome vertical least-significant-bit layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3027) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3027) ## `graphics.MONO_HLSB` @@ -57,7 +57,7 @@ graphics.MONO_HLSB Monochrome horizontal least-significant-bit layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3028) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3028) ## `graphics.MONO_HMSB` @@ -67,7 +67,7 @@ graphics.MONO_HMSB Monochrome horizontal most-significant-bit layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3029) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3029) ## `graphics.RGB565` @@ -77,7 +77,7 @@ graphics.RGB565 16-bit RGB565 pixel layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3030) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3030) ## `graphics.GS2_HMSB` @@ -87,7 +87,7 @@ graphics.GS2_HMSB 2-bit grayscale horizontal most-significant-bit layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3031) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3031) ## `graphics.GS4_HMSB` @@ -97,7 +97,7 @@ graphics.GS4_HMSB 4-bit grayscale horizontal most-significant-bit layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3032) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3032) ## `graphics.GS8` @@ -107,7 +107,7 @@ graphics.GS8 8-bit grayscale layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3033) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3033) ## `graphics.RGB888` @@ -117,4 +117,4 @@ graphics.RGB888 24-bit RGB888 pixel layout. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/src/gfx_module_cpy.c#L3034) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/src/gfx_module_cpy.c#L3034) diff --git a/docs/generated/native-assets-runtime.md b/docs/generated/native-assets-runtime.md index 3a198fa..d857a95 100644 --- a/docs/generated/native-assets-runtime.md +++ b/docs/generated/native-assets-runtime.md @@ -3,7 +3,7 @@ title: Native fonts, images, and runtime API description: Public C declarations for fonts, BMP565 data, image conversion, and capability reporting. --- -Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). +Source snapshot: [`bcaf65f4b2f43b4b0316ce2525e89abed7f7c331`](https://github.com/PyDevices/graphics/tree/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331). Public C declarations for fonts, BMP565 data, image conversion, and capability reporting. @@ -17,7 +17,7 @@ void gfx_font_init_default(gfx_font_t *font, int height); Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L22) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L22) ## `gfx_font_init_from_data` @@ -27,7 +27,7 @@ void gfx_font_init_from_data(gfx_font_t *font, const uint8_t *data, size_t len, Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L23) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L23) ## `gfx_font_deinit` @@ -37,7 +37,7 @@ void gfx_font_deinit(gfx_font_t *font); Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L24) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L24) ## `gfx_font_export` @@ -47,7 +47,7 @@ int gfx_font_export(const gfx_font_t *font, const char *filename); Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L26) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L26) ## `gfx_font_text` @@ -57,7 +57,7 @@ gfx_area_t gfx_font_text(const gfx_canvas_t *canvas, const gfx_font_t *font, con Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L29) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L29) ## `gfx_font_draw_char` @@ -67,7 +67,7 @@ gfx_area_t gfx_font_draw_char(const gfx_canvas_t *canvas, const gfx_font_t *font Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L30) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L30) ## `gfx_font_text_width` @@ -77,7 +77,7 @@ int gfx_font_text_width(const gfx_font_t *font, const char *str, int scale); Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L31) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L31) ## `gfx_font_text8` @@ -87,7 +87,7 @@ gfx_area_t gfx_font_text8(const gfx_canvas_t *canvas, const char *str, int x0, i Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L32) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L32) ## `gfx_font_text14` @@ -97,7 +97,7 @@ gfx_area_t gfx_font_text14(const gfx_canvas_t *canvas, const char *str, int x0, Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L33) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L33) ## `gfx_font_text16` @@ -107,7 +107,7 @@ gfx_area_t gfx_font_text16(const gfx_canvas_t *canvas, const char *str, int x0, Native font entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_font.h#L34) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_font.h#L34) ## `gfx_bmp565_read_header_from_file` @@ -117,7 +117,7 @@ int gfx_bmp565_read_header_from_file(const char *path, int *width, int *height, Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L27) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L27) ## `gfx_bmp565_open_stream` @@ -127,7 +127,7 @@ int gfx_bmp565_open_stream(const char *path, gfx_bmp565_t *out); Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L28) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L28) ## `gfx_bmp565_load_from_file` @@ -137,7 +137,7 @@ int gfx_bmp565_load_from_file(const char *path, gfx_bmp565_t *out); Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L29) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L29) ## `gfx_bmp565_init_from_buffer` @@ -147,7 +147,7 @@ int gfx_bmp565_init_from_buffer(gfx_bmp565_t *out, const uint8_t *buf, size_t le Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L30) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L30) ## `gfx_bmp565_deinit` @@ -157,7 +157,7 @@ void gfx_bmp565_deinit(gfx_bmp565_t *bmp); Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L31) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L31) ## `gfx_bmp565_save` @@ -167,7 +167,7 @@ int gfx_bmp565_save(const gfx_bmp565_t *bmp, const char *path); Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L32) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L32) ## `gfx_bmp565_save_versioned` @@ -177,7 +177,7 @@ int gfx_bmp565_save_versioned(const gfx_bmp565_t *bmp, const char *path, char *o Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L33) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L33) ## `gfx_bmp565_read_bytes` @@ -187,7 +187,7 @@ int gfx_bmp565_read_bytes(const gfx_bmp565_t *bmp, int start, int stop, uint8_t Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L34) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L34) ## `gfx_bmp565_read_region` @@ -197,7 +197,7 @@ int gfx_bmp565_read_region(const gfx_bmp565_t *bmp, int x0, int x1, int y0, int Native bmp565 entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_bmp565.h#L35) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_bmp565.h#L35) ## `gfx_image_probe` @@ -207,7 +207,7 @@ int gfx_image_probe(const uint8_t *data, size_t len, gfx_image_info_t *info); Native image entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L43) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L43) ## `gfx_image_decode` @@ -217,7 +217,7 @@ int gfx_image_decode(const uint8_t *data, size_t len, const gfx_image_info_t *in Native image entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L47) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L47) ## `gfx_image_fb_free` @@ -227,7 +227,7 @@ void gfx_image_fb_free(gfx_image_fb_t *img); Native image entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L64) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L64) ## `gfx_files_load_image` @@ -237,7 +237,7 @@ int gfx_files_load_image(const char *path, gfx_image_fb_t *out); Native files entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L65) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L65) ## `gfx_files_save_image` @@ -247,7 +247,7 @@ int gfx_files_save_image(const gfx_fb_t *fb, const char *path, char *out_path, s Native files entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L66) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L66) ## `gfx_files_pbm_to_framebuffer` @@ -257,7 +257,7 @@ int gfx_files_pbm_to_framebuffer(const char *path, gfx_image_fb_t *out); Native files entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L67) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L67) ## `gfx_files_pgm_to_framebuffer` @@ -267,7 +267,7 @@ int gfx_files_pgm_to_framebuffer(const char *path, gfx_image_fb_t *out); Native files entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L68) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L68) ## `gfx_files_bmp_to_framebuffer` @@ -277,4 +277,4 @@ int gfx_files_bmp_to_framebuffer(const char *path, gfx_image_fb_t *out); Native files entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_files.h#L69) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_files.h#L69) diff --git a/docs/generated/native-drawing.md b/docs/generated/native-drawing.md index 54f63fe..b1984fe 100644 --- a/docs/generated/native-drawing.md +++ b/docs/generated/native-drawing.md @@ -3,7 +3,7 @@ title: Native drawing and framebuffer API description: Public C declarations for geometry, framebuffer operations, shapes, and clipping. --- -Source snapshot: [`1da33021c74a2c72eb98babf88c28a194d0c3552`](https://github.com/PyDevices/graphics/tree/1da33021c74a2c72eb98babf88c28a194d0c3552). +Source snapshot: [`bcaf65f4b2f43b4b0316ce2525e89abed7f7c331`](https://github.com/PyDevices/graphics/tree/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331). Public C declarations for geometry, framebuffer operations, shapes, and clipping. @@ -17,7 +17,7 @@ int gfx_fb_validate_buffer(size_t buf_len, int width, int height, int format, in Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L21) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L21) ## `gfx_fb_color_depth` @@ -27,7 +27,7 @@ int gfx_fb_color_depth(int format); Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L22) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L22) ## `gfx_fb_setpixel` @@ -37,7 +37,7 @@ void gfx_fb_setpixel(const gfx_fb_t *fb, unsigned int x, unsigned int y, uint32_ Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L24) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L24) ## `gfx_fb_getpixel` @@ -47,7 +47,7 @@ uint32_t gfx_fb_getpixel(const gfx_fb_t *fb, unsigned int x, unsigned int y); Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L25) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L25) ## `gfx_fb_setpixel_checked` @@ -57,7 +57,7 @@ void gfx_fb_setpixel_checked(const gfx_fb_t *fb, int x, int y, int col, int mask Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L26) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L26) ## `gfx_fb_fill_rect_raw` @@ -67,7 +67,7 @@ void gfx_fb_fill_rect_raw(const gfx_fb_t *fb, int x, int y, int w, int h, uint32 Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L27) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L27) ## `gfx_fb_fill_rect` @@ -77,7 +77,7 @@ gfx_area_t gfx_fb_fill_rect(const gfx_fb_t *fb, int x, int y, int w, int h, uint Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L28) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L28) ## `gfx_fb_fill` @@ -87,7 +87,7 @@ gfx_area_t gfx_fb_fill(const gfx_fb_t *fb, uint32_t col); Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L29) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L29) ## `gfx_fb_canvas_init` @@ -97,7 +97,7 @@ void gfx_fb_canvas_init(gfx_canvas_t *canvas, const gfx_fb_t *fb); Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L31) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L31) ## `gfx_fb_scroll` @@ -107,7 +107,7 @@ void gfx_fb_scroll(gfx_fb_t *fb, int xstep, int ystep); Native fb entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_framebuffer.h#L32) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_framebuffer.h#L32) ## `gfx_clipped_canvas_init` @@ -117,7 +117,7 @@ void gfx_clipped_canvas_init(gfx_clipped_canvas_t *cc, const gfx_canvas_t *paren Native clipped entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L20) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L20) ## `gfx_shapes_pixel` @@ -127,7 +127,7 @@ gfx_area_t gfx_shapes_pixel(const gfx_canvas_t *canvas, int x, int y, int c); Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L22) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L22) ## `gfx_shapes_hline` @@ -137,7 +137,7 @@ gfx_area_t gfx_shapes_hline(const gfx_canvas_t *canvas, int x, int y, int w, int Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L23) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L23) ## `gfx_shapes_vline` @@ -147,7 +147,7 @@ gfx_area_t gfx_shapes_vline(const gfx_canvas_t *canvas, int x, int y, int h, int Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L24) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L24) ## `gfx_shapes_fill_rect` @@ -157,7 +157,7 @@ gfx_area_t gfx_shapes_fill_rect(const gfx_canvas_t *canvas, int x, int y, int w, Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L25) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L25) ## `gfx_shapes_rect` @@ -167,7 +167,7 @@ gfx_area_t gfx_shapes_rect(const gfx_canvas_t *canvas, int x, int y, int w, int Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L26) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L26) ## `gfx_shapes_line` @@ -177,7 +177,7 @@ gfx_area_t gfx_shapes_line(const gfx_canvas_t *canvas, int x1, int y1, int x2, i Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L27) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L27) ## `gfx_shapes_ellipse` @@ -187,7 +187,7 @@ gfx_area_t gfx_shapes_ellipse(const gfx_canvas_t *canvas, int cx, int cy, int xr Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L28) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L28) ## `gfx_shapes_fill` @@ -197,7 +197,7 @@ gfx_area_t gfx_shapes_fill(const gfx_canvas_t *canvas, int c); Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L29) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L29) ## `gfx_shapes_poly_int_from_buffer` @@ -207,7 +207,7 @@ int gfx_shapes_poly_int_from_buffer(const void *buf, size_t len, size_t itemsize Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L31) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L31) ## `gfx_shapes_poly` @@ -217,7 +217,7 @@ gfx_area_t gfx_shapes_poly(const gfx_canvas_t *canvas, int x, int y, const void Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L32) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L32) ## `gfx_shapes_blit` @@ -227,7 +227,7 @@ gfx_area_t gfx_shapes_blit(const gfx_canvas_t *canvas, const gfx_fb_t *source, i Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L33) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L33) ## `gfx_shapes_circle` @@ -237,7 +237,7 @@ gfx_area_t gfx_shapes_circle(const gfx_canvas_t *canvas, int x0, int y0, int r, Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L34) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L34) ## `gfx_shapes_round_rect` @@ -247,7 +247,7 @@ gfx_area_t gfx_shapes_round_rect(const gfx_canvas_t *canvas, int x0, int y0, int Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L35) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L35) ## `gfx_shapes_triangle` @@ -257,7 +257,7 @@ gfx_area_t gfx_shapes_triangle(const gfx_canvas_t *canvas, int x0, int y0, int x Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L36) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L36) ## `gfx_shapes_arc` @@ -267,7 +267,7 @@ gfx_area_t gfx_shapes_arc(const gfx_canvas_t *canvas, int x, int y, int r, float Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L37) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L37) ## `gfx_shapes_gradient_rect` @@ -277,7 +277,7 @@ gfx_area_t gfx_shapes_gradient_rect(const gfx_canvas_t *canvas, int x, int y, in Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L38) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L38) ## `gfx_shapes_blit_rect` @@ -287,7 +287,7 @@ gfx_area_t gfx_shapes_blit_rect(const gfx_canvas_t *canvas, const void *buf, int Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L39) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L39) ## `gfx_shapes_blit_transparent` @@ -297,7 +297,7 @@ gfx_area_t gfx_shapes_blit_transparent(const gfx_canvas_t *canvas, const void *b Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L40) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L40) ## `gfx_shapes_polygon` @@ -307,7 +307,7 @@ gfx_area_t gfx_shapes_polygon(const gfx_canvas_t *canvas, const int *points, siz Native shapes entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_shapes.h#L41) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_shapes.h#L41) ## `gfx_draw_init` @@ -317,7 +317,7 @@ void gfx_draw_init(gfx_draw_t *draw, const gfx_canvas_t *canvas); Native draw entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L21) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_draw.h#L21) ## `gfx_draw_effective_clip` @@ -327,7 +327,7 @@ gfx_area_t gfx_draw_effective_clip(const gfx_draw_t *draw); Native draw entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L23) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_draw.h#L23) ## `gfx_draw_push_clip` @@ -337,7 +337,7 @@ void gfx_draw_push_clip(gfx_draw_t *draw, const gfx_area_t *clip); Native draw entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L24) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_draw.h#L24) ## `gfx_draw_pop_clip` @@ -347,4 +347,4 @@ void gfx_draw_pop_clip(gfx_draw_t *draw); Native draw entry point declared for firmware and extension integration. -[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/1da33021c74a2c72eb98babf88c28a194d0c3552/include/gfx_draw.h#L25) +[View the pinned source declaration](https://github.com/PyDevices/graphics/blob/bcaf65f4b2f43b4b0316ce2525e89abed7f7c331/include/gfx_draw.h#L25) From e63a2fc2bbb71c9175117176404a8cacb5825975 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 26 Jul 2026 10:55:24 +0000 Subject: [PATCH 4/6] Ignore setuptools/PyPI build leftovers more completely. Keep wheels, eggs, object files, and VERSION out of the tree; drop the redundant graphics_cmod.egg-info entry covered by *.egg-info/. --- .gitignore | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 54ee404..46ce334 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,18 @@ .venv/ __pycache__/ -*.egg-info/ +*.py[cod] +*.o +*.a *.so *.pyd +*.egg +*.egg-info/ +.eggs/ build/ dist/ wheelhouse/ VERSION -graphics_cmod.egg-info/ + docs/node_modules/ docs/dist/ From b3a1cc9906317d4539ff15847b98cd7562d7a734 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 26 Jul 2026 11:21:52 +0000 Subject: [PATCH 5/6] Add pure-Python graphics package and dual publish pipelines. Move the Python package to lib/graphics/ (including framebuf.py), document native + Python products, and publish graphics-py / MIP alongside graphics-cmod on the same vX.Y.Z tag. Font C headers sync from lib/graphics/_font_8x*.py. --- .github/workflows/publish-micropython-lib.yml | 157 ++++ .gitignore | 1 + AGENTS.md | 36 +- PUBLISHING.md | 152 +-- README.md | 69 +- lib/graphics/README.md | 52 + lib/graphics/__init__.py | 113 +++ lib/graphics/_area.py | 270 ++++++ lib/graphics/_blit_hooks.py | 131 +++ lib/graphics/_bmp565.py | 199 ++++ lib/graphics/_clip.py | 120 +++ lib/graphics/_draw.py | 156 +++ lib/graphics/_files.py | 214 +++++ lib/graphics/_font.py | 370 ++++++++ lib/graphics/_font_8x14.py | 259 +++++ lib/graphics/_font_8x16.py | 259 +++++ lib/graphics/_font_8x8.py | 259 +++++ lib/graphics/_framebuf_plus.py | 644 +++++++++++++ lib/graphics/_shapes.py | 888 ++++++++++++++++++ lib/graphics/framebuf.py | 786 ++++++++++++++++ scripts/build.py | 504 ++++++++++ scripts/manifestfile.py | 668 +++++++++++++ scripts/publish_make_pyproject.py | 258 +++++ scripts/publish_micropython_lib.sh | 199 ++++ scripts/publish_mip_ghpages.sh | 84 ++ scripts/sync_fonts.py | 28 +- 26 files changed, 6696 insertions(+), 180 deletions(-) create mode 100644 .github/workflows/publish-micropython-lib.yml create mode 100644 lib/graphics/README.md create mode 100644 lib/graphics/__init__.py create mode 100644 lib/graphics/_area.py create mode 100644 lib/graphics/_blit_hooks.py create mode 100644 lib/graphics/_bmp565.py create mode 100644 lib/graphics/_clip.py create mode 100644 lib/graphics/_draw.py create mode 100644 lib/graphics/_files.py create mode 100644 lib/graphics/_font.py create mode 100644 lib/graphics/_font_8x14.py create mode 100644 lib/graphics/_font_8x16.py create mode 100644 lib/graphics/_font_8x8.py create mode 100644 lib/graphics/_framebuf_plus.py create mode 100644 lib/graphics/_shapes.py create mode 100644 lib/graphics/framebuf.py create mode 100755 scripts/build.py create mode 100755 scripts/manifestfile.py create mode 100755 scripts/publish_make_pyproject.py create mode 100755 scripts/publish_micropython_lib.sh create mode 100755 scripts/publish_mip_ghpages.sh diff --git a/.github/workflows/publish-micropython-lib.yml b/.github/workflows/publish-micropython-lib.yml new file mode 100644 index 0000000..adacee5 --- /dev/null +++ b/.github/workflows/publish-micropython-lib.yml @@ -0,0 +1,157 @@ +# Publish pure-Python graphics to micropython-lib, MIP index, and TestPyPI (graphics-py). +# Native graphics-cmod wheels are published by publish-testpypi.yml on the same tag. +# +# Release trigger: push tag vX.Y.Z (see scripts/publish_release_tag.sh). +# +# Secrets: +# MICROPYTHON_LIB_DEPLOY_TOKEN — PAT with contents:write on PyDevices/micropython-lib +# TESTPYPI_API_TOKEN — TestPyPI API token + +name: Publish micropython-lib + +on: + push: + tags: + - 'v*.*.*' + workflow_dispatch: + inputs: + version: + description: Semver X.Y.Z + type: string + required: false + sync_sources: + description: Sync graphics into micropython-lib + type: boolean + default: true + upload_testpypi: + description: Upload graphics-py wheels to TestPyPI + type: boolean + default: false + publish_mip_index: + description: Rebuild mip/PyDevices and push gh-pages + type: boolean + default: true + commit_message: + description: micropython-lib commit message + type: string + required: false + +permissions: + contents: read + +# Sibling repos also push to micropython-lib; scripts rebase-retry on conflict. +concurrency: + group: publish-micropython-lib-${{ github.repository }} + cancel-in-progress: false + +jobs: + publish: + runs-on: ubuntu-latest + if: github.repository == 'PyDevices/graphics' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Resolve release version + id: version + env: + INPUT_VERSION: ${{ inputs.version }} + run: | + set -euo pipefail + normalize() { + local v="${1#v}" + v="$(echo "$v" | tr -d '[:space:]')" + if [[ ! "$v" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then + echo "Invalid semver: $1" >&2 + exit 1 + fi + echo "$v" + } + if [[ "${{ github.event_name }}" == "push" ]]; then + VERSION="$(normalize "${GITHUB_REF_NAME}")" + elif [[ -n "${INPUT_VERSION:-}" ]]; then + VERSION="$(normalize "$INPUT_VERSION")" + else + TAG="$(git describe --tags --exact-match 2>/dev/null || true)" + if [[ -z "$TAG" ]]; then + echo "Set version input or run from a vX.Y.Z tag." >&2 + exit 1 + fi + VERSION="$(normalize "$TAG")" + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Publishing graphics-py / MIP release $VERSION" + + - name: Verify publish secrets + env: + DEPLOY_TOKEN: ${{ secrets.MICROPYTHON_LIB_DEPLOY_TOKEN }} + TESTPYPI_TOKEN: ${{ secrets.TESTPYPI_API_TOKEN }} + run: | + set -euo pipefail + missing=() + if [[ -z "${DEPLOY_TOKEN}" ]]; then + missing+=("MICROPYTHON_LIB_DEPLOY_TOKEN") + fi + if [[ "${{ github.event_name }}" == "push" || "${{ inputs.upload_testpypi }}" == "true" ]]; then + if [[ -z "${TESTPYPI_TOKEN}" ]]; then + missing+=("TESTPYPI_API_TOKEN") + fi + fi + if [[ ${#missing[@]} -gt 0 ]]; then + echo "::error::GitHub did not supply secrets to PyDevices/graphics: ${missing[*]}" + echo "Org Settings → Secrets → Actions → each secret → Repository access → include graphics." + exit 1 + fi + + - uses: actions/checkout@v4 + with: + repository: PyDevices/micropython-lib + ref: PyDevices + path: micropython-lib + token: ${{ secrets.MICROPYTHON_LIB_DEPLOY_TOKEN }} + + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Install publish tools + if: github.event_name == 'push' || inputs.upload_testpypi == true + run: pip install hatch twine + + - name: Configure micropython-lib git + run: | + git -C micropython-lib config user.name 'github-actions[bot]' + git -C micropython-lib config user.email 'github-actions[bot]@users.noreply.github.com' + git -C micropython-lib remote set-url origin \ + "https://x-access-token:${{ secrets.MICROPYTHON_LIB_DEPLOY_TOKEN }}@github.com/PyDevices/micropython-lib.git" + + - name: Sync graphics into micropython-lib + if: github.event_name == 'push' || inputs.sync_sources == true + env: + MICROPYTHON_LIB_DIR: ${{ github.workspace }}/micropython-lib + GRAPHICS_VERSION: ${{ steps.version.outputs.version }} + TESTPYPI_API_TOKEN: ${{ secrets.TESTPYPI_API_TOKEN }} + run: | + VERSION="${{ steps.version.outputs.version }}" + MSG="${{ inputs.commit_message }}" + if [[ -z "$MSG" ]]; then + MSG="graphics: Release v${VERSION} (${GITHUB_SHA::7})." + fi + EXTRA=(--commit-message "$MSG" --push) + if [[ "${{ github.event_name }}" != "push" && "${{ inputs.upload_testpypi }}" != "true" ]]; then + EXTRA=(--skip-pypi --commit-message "$MSG" --push) + fi + chmod +x scripts/publish_micropython_lib.sh + ./scripts/publish_micropython_lib.sh "${EXTRA[@]}" + + - name: Publish MIP index to gh-pages + if: github.event_name == 'push' || inputs.publish_mip_index == true + env: + MICROPYTHON_LIB_DIR: ${{ github.workspace }}/micropython-lib + GRAPHICS_DIR: ${{ github.workspace }} + MICROPYTHON_DIR: /tmp/micropython + MIP_INDEX_OUTPUT: /tmp/mip-index + run: | + chmod +x scripts/publish_mip_ghpages.sh + ./scripts/publish_mip_ghpages.sh diff --git a/.gitignore b/.gitignore index 46ce334..ebc678e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ docs/dist/ # Cursor IDE throwaways (do not ignore the whole .cursor/ tree) .cursor/debug* .cursor/plans/ +wheels/ diff --git a/AGENTS.md b/AGENTS.md index 2bf9ce1..72394e1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,35 +1,47 @@ # AGENTS.md — graphics -Native all-C **graphics** module for MicroPython, CircuitPython, and CPython -(`import graphics`). Prefer this wheel on desktop/Android when available; the -pure-Python alternative is pydisplay's `src/lib/graphics`. +Native and pure-Python **graphics** (`import graphics`) for MicroPython, +CircuitPython, and CPython. Prefer `graphics-cmod` on desktop/Android when +available; otherwise `graphics-py` / MIP `graphics`. ## Layout -MicroPython-style usermod layout (same pattern as sibling `usdl2`): - - Root: `micropython.mk`, `micropython.cmake`, `circuitpython.mk`, `setup.py`, patch scripts — build glue stays here for `USER_C_MODULES` discovery - `src/` — all `.c` sources (shared core + MP/CP bindings + CPython extension) - `include/` — shared headers (`gfx_*.h`, `font_8x*.h`, `graphics_qstrdefs.h`) -- `tests/` — smoke and parity scripts +- `lib/graphics/` — pure-Python package (same public API as the cmod) +- `tests/` — native smoke and parity scripts - No `.c` / `.h` at repo root ## Smoke ```bash +# native python3 -m venv .venv +echo 0.0.0 > VERSION .venv/bin/pip install -e . .venv/bin/python tests/test_area.py .venv/bin/python tests/test_graphics.py -.venv/bin/python tests/test_subclass.py + +# pure Python +PYTHONPATH=lib .venv/bin/python -c "import graphics; assert graphics.implementation() == 'graphics_python'" ``` -MicroPython / CircuitPython: build via cmods `./build_mp.sh` / -`lv_circuitpython_mod/build_cp.sh`, then run the same scripts under `tests/`. +## Publishing + +One tag `vX.Y.Z` publishes: + +1. **graphics-cmod** — `publish-testpypi.yml` (cibuildwheel) +2. **graphics-py** + MIP — `publish-micropython-lib.yml` (micropython-lib + TestPyPI + gh-pages) + +See `PUBLISHING.md`. Next shared version continues the `graphics-cmod` line +(`v0.0.9` → `v0.0.10`). ## After C changes -Refresh pydisplay’s committed runtimes (desktop bins + PyScript vendor wasm) -with `../build_pydisplay_runtimes.sh` from the cmods workspace root when the -usermod is linked into those interpreters. +Refresh pydisplay’s committed runtimes with `../build_pydisplay_runtimes.sh` +from the cmods workspace root when the usermod is linked into those interpreters. + +Font C headers: `python3 scripts/sync_fonts.py` (source of truth is +`lib/graphics/_font_8x*.py`). diff --git a/PUBLISHING.md b/PUBLISHING.md index 202bba2..7022ee2 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -1,155 +1,65 @@ # Publishing and releases -How changes in this repo become versioned **`graphics-cmod`** wheels on [TestPyPI](https://test.pypi.org/project/graphics-cmod/), and how to install them. +One annotated tag `vX.Y.Z` publishes **both** products at that version: -The CPython package ships a native **`graphics_native`** extension plus the pure-Python **`graphics`** package. CI builds platform wheels with [cibuildwheel](https://cibuildwheel.pypa.io/): +| Product | Channel | Workflow | +|---------|---------|----------| +| **graphics-cmod** | TestPyPI (platform wheels) | `publish-testpypi.yml` | +| **graphics-py** | TestPyPI (pure Python) + micropython-lib / MIP | `publish-micropython-lib.yml` | -| Platform | Wheel tag | -|----------|-----------| -| Linux x86_64 | `manylinux_*` | -| Windows x64 | `win_amd64` | -| Android arm64 | `android_21_arm64_v8a` (`cp313`, `cp314`) | -| Android x86_64 (emulator) | `android_21_x86_64` (`cp313`, `cp314`) | - -## Pipeline overview +## Pipeline ```text -graphics (your machine) - commit → push main +graphics (commit on main) + ./scripts/publish_release_tag.sh --push # next patch after highest v* │ - ▼ - ./scripts/publish_release_tag.sh --push (or manual git tag vX.Y.Z) + ├─► publish-testpypi.yml + │ cibuildwheel → Linux + Windows + Android → graphics-cmod │ - ▼ -graphics: Publish TestPyPI (on tag push v*.*.* or workflow_dispatch) - cibuildwheel → Linux + Windows + Android wheels → twine upload + └─► publish-micropython-lib.yml + sync → micropython/graphics/ + hatch + twine → graphics-py + rebuild mip/PyDevices → gh-pages + remove legacy micropython/pydisplay/graphics/ ``` ## Version numbers -Format: **`X.Y.Z`** (semver) - -| Part | Source | -|------|--------| -| **First release** | `RELEASE_VERSION` in `setup.py` when no `v*.*.*` tags exist yet (**`0.0.1`**) | -| **Later releases** | highest existing tag + 1 patch (`v0.0.1` → `v0.0.2`, …) | - -Preview the next version: - -```bash -./scripts/next_release_version.sh --verbose -``` - -TestPyPI rejects re-uploading the same version — each release needs a new tag (handled automatically by `publish_release_tag.sh`). - -## One-time setup - -### TestPyPI token (organization secret) - -`TESTPYPI_API_TOKEN` can live at the **PyDevices organization** level: - -1. **PyDevices** org → **Settings** → **Secrets and variables** → **Actions** -2. Organization secret name: `TESTPYPI_API_TOKEN` -3. Grant access to **`graphics`** (and other publish repos as needed) - -Workflows reference the secret by name — no YAML changes required. If a repository also defines the same secret locally, the **repo secret overrides** the org secret. - -### Optional repository secret - -| Secret | Required | Purpose | -|--------|----------|---------| -| `RELEASE_WORKFLOW_TOKEN` | only for future CI that tags from `GITHUB_TOKEN` | PAT with **`actions:write`** on this repo — tag pushes from `GITHUB_TOKEN` do not trigger other workflows | - -For manual or local tag pushes, `TESTPYPI_API_TOKEN` alone is enough. - -## Release (local clone) - -```bash -git push origin main -./scripts/publish_release_tag.sh --push -``` - -Preview without tagging: +Continue the **graphics-cmod** line (`v0.0.9` → `v0.0.10`, …). Preview: ```bash ./scripts/next_release_version.sh --verbose ./scripts/publish_release_tag.sh --dry-run ``` -## Manual release (GitHub CLI, no clone) +## Secrets -```bash -gh workflow run publish-testpypi.yml --repo PyDevices/graphics -f version=0.0.1 -gh run list --repo PyDevices/graphics -gh run watch --repo PyDevices/graphics -``` - -## GitHub Actions workflows - -| Workflow | Trigger | What it does | -|----------|---------|--------------| -| [publish-testpypi.yml](.github/workflows/publish-testpypi.yml) | Tag push `v*.*.*`; workflow_dispatch | **cibuildwheel**: Linux manylinux + Windows amd64 + Android (`android_21_arm64_v8a`, `android_21_x86_64`) → TestPyPI upload | - -## Scripts - -| Script | Purpose | +| Secret | Purpose | |--------|---------| -| `scripts/next_release_version.sh` | Print next semver (`RELEASE_VERSION` or highest tag + 1 patch) | -| `scripts/publish_release_tag.sh` | Create annotated tag `vX.Y.Z` and optionally push (triggers publish) | +| `TESTPYPI_API_TOKEN` | TestPyPI upload (cmod + graphics-py) | +| `MICROPYTHON_LIB_DEPLOY_TOKEN` | PAT with `contents:write` on PyDevices/micropython-lib | -## Local wheel builds (cibuildwheel) +Grant both secrets to the **graphics** repository (org secret repository access). -Reproduce CI wheels locally: +## Install ```bash -pipx install cibuildwheel # one-time -echo "0.0.0.dev" > VERSION -pipx run cibuildwheel --platform linux # or --platform windows -ls wheelhouse/ -``` +# native +pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ graphics-cmod -**Linux requires Docker** for manylinux wheels. **Windows** needs MSVC Build Tools on a native Windows shell. - -**Android (PEP 738):** needs the Android SDK on Linux or macOS: +# pure Python +pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ graphics-py +``` -```bash -echo "0.0.0.dev" > VERSION -pipx run cibuildwheel --platform android -ls wheelhouse/*android*.whl +```python +mip.install("graphics", index="https://PyDevices.github.io/micropython-lib/mip/PyDevices") ``` -Dev-only smoke test without cibuildwheel: +## Local cmod wheel smoke ```bash echo "0.0.0.dev" > VERSION python3 -m venv .venv .venv/bin/pip install -e . -.venv/bin/python test_graphics.py -.venv/bin/python test_area.py +.venv/bin/python tests/test_graphics.py ``` - -## Install from TestPyPI - -Wheels are built for **CPython 3.10–3.14** (one wheel per minor × platform). Pip picks the tag matching your interpreter. - -```bash -pip install -i https://test.pypi.org/simple/ graphics-cmod -``` - -Import as usual: - -```python -import graphics -from graphics import FrameBuffer, RGB565, framebuf_backend -``` - -## Troubleshooting - -| Symptom | Likely cause | -|---------|----------------| -| Publish fails: 403 on TestPyPI | Bad or missing `TESTPYPI_API_TOKEN`; confirm org secret grants access to this repo | -| Publish fails: 400 duplicate version | Tag already uploaded; publish the next patch | -| Publish fails on Windows only | Check MSVC build logs in the `windows-latest` matrix job | -| pip: no matching distribution | No wheel for your CPython minor / platform — check files on TestPyPI | -| Local cibuildwheel: `FileNotFoundError: 'docker'` | Linux manylinux builds need Docker locally; CI has it | -| `publish_release_tag.sh`: uncommitted changes | Commit or stash before tagging | diff --git a/README.md b/README.md index 5073405..6f1b59b 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,18 @@ -# graphics-cmod +# graphics -Native all-C **graphics** module for MicroPython, CircuitPython, and CPython. Import as `graphics`. +Native and pure-Python **graphics** for MicroPython, CircuitPython, and CPython. +Import as `graphics`. -> **Pip name:** `graphics-cmod` · **Import:** `import graphics` +| Product | Pip / MIP | Role | +|---------|-----------|------| +| **graphics-cmod** | TestPyPI `graphics-cmod` | All-C extension (prefer on desktop/Android when available) | +| **graphics-py** | TestPyPI `graphics-py`, MIP `graphics` | Pure-Python package (same public API) | -Prefer this wheel on desktop/Android when available. The pure-Python alternative is [`pydisplay-graphics`](https://test.pypi.org/project/pydisplay-graphics/) (same import name). +One release tag `vX.Y.Z` publishes both products at that version. ## Install -### CPython (TestPyPI) +### Native (TestPyPI) ```bash pip install \ @@ -17,6 +21,22 @@ pip install \ graphics-cmod ``` +### Pure Python (TestPyPI) + +```bash +pip install \ + -i https://test.pypi.org/simple/ \ + --extra-index-url https://pypi.org/simple/ \ + graphics-py +``` + +### MicroPython (MIP) + +```python +import mip +mip.install("graphics", index="https://PyDevices.github.io/micropython-lib/mip/PyDevices") +``` + ### Quick start ```python @@ -26,8 +46,7 @@ from graphics import FrameBuffer, RGB565 fb = FrameBuffer(bytearray(160 * 128 * 2), 160, 128, RGB565) fb.fill(0) fb.fill_rect(10, 10, 40, 40, 0xF800) -assert graphics.framebuf_backend() == "native" -print(graphics.implementation()) +print(graphics.implementation()) # native_cmod or graphics_python ``` ## What you get @@ -37,14 +56,12 @@ print(graphics.implementation()) - Format constants: `MONO_VLSB`, `MONO_HLSB`, `MONO_HMSB`, `RGB565`, `GS2_HMSB`, `GS4_HMSB`, `GS8`, `RGB888` - `framebuf_backend()`, `capabilities()`, `implementation()` -Firmware / editable builds: see **Build from source** below. - ## Links - [Source-linked API reference](https://pydevices.github.io/graphics/api/) - [Source](https://github.com/PyDevices/graphics) - [Issues](https://github.com/PyDevices/graphics/issues) -- Related: [pydisplay-graphics](https://test.pypi.org/project/pydisplay-graphics/), [pydisplay](https://github.com/PyDevices/pydisplay) +- Related: [pydisplay](https://github.com/PyDevices/pydisplay) ## License @@ -59,24 +76,14 @@ MIT (framebuf algorithms derived from MicroPython `extmod/modframebuf.c`, Damien ``` graphics/ micropython.mk / micropython.cmake / circuitpython.mk / setup.py - include/ # shared headers (gfx_*.h, font_8x*.h, qstrs) + include/ # C headers (gfx_*.h, font_8x*.h, qstrs) src/ # C sources (core + MP/CP/CPython bindings) - tests/ # smoke / parity tests + lib/graphics/ # pure-Python package (import graphics) + tests/ # native smoke / parity tests docs/ scripts/ web/ ``` -Core vs bindings (under `include/` + `src/`): - -- `gfx_core.h` — types, format IDs, Area geometry -- `gfx_framebuffer` — format pixel ops -- `gfx_shapes` — drawing algorithms via `gfx_canvas_t` -- `gfx_draw` — Draw clip stack (C core) -- `gfx_font` — text8/14/16 -- `gfx_capabilities` — capabilities reporting -- `gfx_area_mp` / `gfx_bindings_mp` / `gfx_module_mp` — MicroPython / CircuitPython -- `gfx_module_cpy` — CPython module registration - -### CPython (editable) +### CPython native (editable) ```bash python3 -m venv .venv @@ -86,6 +93,12 @@ python3 -m venv .venv .venv/bin/python tests/test_subclass.py ``` +### Pure Python (no extension) + +```bash +PYTHONPATH=lib python3 -c "import graphics; print(graphics.implementation())" +``` + ### MicroPython Clone as a sibling of `micropython/`: @@ -96,20 +109,18 @@ workspace/ micropython/ ``` -There is no `graphics/manifest.py` — the C usermod is the only graphics path when linked in. - ```bash cd micropython/ports/unix make submodules make USER_C_MODULES=../../.. cd ../../.. ./micropython/ports/unix/build-standard/micropython graphics/tests/test_area.py -./micropython/ports/unix/build-standard/micropython graphics/tests/test_graphics.py -./micropython/ports/unix/build-standard/micropython graphics/tests/test_subclass.py ``` ([cmods](https://github.com/PyDevices/cmods) is an optional convenience workspace with `./build_mp.sh`; it is not required.) ### pydisplay integration -pydisplay's Python `src/lib/graphics` remains the pure-Python implementation. When this cmod is installed or linked, `graphics.framebuf_backend()` reports `native` and `graphics.implementation()` reports `native_cmod`. +When this cmod is installed or linked, `graphics.framebuf_backend()` reports +`native` and `graphics.implementation()` reports `native_cmod`. Otherwise the +pure-Python package reports `graphics_python`. diff --git a/lib/graphics/README.md b/lib/graphics/README.md new file mode 100644 index 0000000..74bce6f --- /dev/null +++ b/lib/graphics/README.md @@ -0,0 +1,52 @@ +# graphics-py + +Pure-Python `graphics` package — `FrameBuffer`, `Draw`, fonts, shapes, and image +loaders. Import as `graphics`. + +> **Pip name:** `graphics-py` · **Import:** `import graphics` + +On desktop/Android when a native wheel is available, prefer +[`graphics-cmod`](https://test.pypi.org/project/graphics-cmod/) (same import +name, C implementation). Both products are published from this repo and share +the same release tag / version. + +## Install + +### CPython (TestPyPI) + +```bash +pip install \ + -i https://test.pypi.org/simple/ \ + --extra-index-url https://pypi.org/simple/ \ + graphics-py +``` + +### MicroPython (MIP) + +```python +import mip +mip.install("graphics", index="https://PyDevices.github.io/micropython-lib/mip/PyDevices") +``` + +## Quick start + +```python +import graphics + +fb = graphics.FrameBuffer( + bytearray(160 * 128 * 2), 160, 128, graphics.RGB565 +) +fb.fill(0) +fb.fill_rect(10, 10, 40, 40, 0xF800) +print(graphics.implementation()) # graphics_python +``` + +## Links + +- [API reference (native)](https://pydevices.github.io/graphics/api/) +- [Source](https://github.com/PyDevices/graphics) +- Related: [graphics-cmod](https://test.pypi.org/project/graphics-cmod/), [pydisplay](https://github.com/PyDevices/pydisplay) + +## License + +MIT — see [LICENSE](https://github.com/PyDevices/graphics/blob/main/LICENSE). diff --git a/lib/graphics/__init__.py b/lib/graphics/__init__.py new file mode 100644 index 0000000..7de1c44 --- /dev/null +++ b/lib/graphics/__init__.py @@ -0,0 +1,113 @@ +""" +graphics — cross-platform 2D drawing for *Python. + +Extends MicroPython's ``framebuf`` with shape helpers, fonts, image loaders, and +``Area`` bounding boxes for partial updates. On CPython and CircuitPython the +built-in pure-Python ``framebuf`` fallback is used automatically. + +Quick start:: + + import graphics + + fb = graphics.FrameBuffer(bytearray(16 * 16 * 2), 16, 16, graphics.RGB565) + fb.fill(0) + area = fb.fill_rect(1, 1, 6, 6, 0xFFFF) + graphics.text8(fb, "Hi", 0, 0, 0xFFFF) +""" + +from ._area import Area +from ._bmp565 import BMP565 +from ._clip import ClipContext, ClippedCanvas +from ._draw import Draw +from ._files import ( + bmp_to_framebuffer, + export_framebuffer, + load_image, + pbm_to_framebuffer, + pgm_to_framebuffer, + save_image, +) +from ._font import Font, text, text8, text14, text16 +from ._framebuf_plus import ( + GS2_HMSB, + GS4_HMSB, + GS8, + MONO_HLSB, + MONO_HMSB, + MONO_VLSB, + RGB565, + RGB888, + FrameBuffer, +) +from ._shapes import ( + arc, + blit, + blit_rect, + blit_transparent, + circle, + ellipse, + fill, + fill_rect, + gradient_rect, + hline, + line, + pixel, + poly, + polygon, + rect, + round_rect, + triangle, + vline, +) + + +def implementation(): + """Return ``graphics_python`` (this package) vs ``native_cmod`` for the C module.""" + return "graphics_python" + + +__all__ = [ + "BMP565", + "GS2_HMSB", + "GS4_HMSB", + "GS8", + "MONO_HLSB", + "MONO_HMSB", + "MONO_VLSB", + "RGB565", + "Area", + "ClipContext", + "ClippedCanvas", + "Draw", + "Font", + "FrameBuffer", + "arc", + "blit", + "blit_rect", + "blit_transparent", + "bmp_to_framebuffer", + "circle", + "ellipse", + "export_framebuffer", + "fill", + "fill_rect", + "gradient_rect", + "hline", + "implementation", + "line", + "load_image", + "pbm_to_framebuffer", + "pgm_to_framebuffer", + "pixel", + "poly", + "polygon", + "rect", + "round_rect", + "save_image", + "text", + "text8", + "text14", + "text16", + "triangle", + "vline", +] diff --git a/lib/graphics/_area.py b/lib/graphics/_area.py new file mode 100644 index 0000000..e5a4ecd --- /dev/null +++ b/lib/graphics/_area.py @@ -0,0 +1,270 @@ +# SPDX-FileCopyrightText: 2024 Brad Barnett +# +# SPDX-License-Identifier: MIT +""" +`graphics._area` +==================================================== +Area class for defining rectangular areas. +""" + + +class Area: + """ + Represents a rectangular area defined by its position and dimensions. + + Attributes: + x (int | float): The x-coordinate of the top-left corner of the area. + y (int | float): The y-coordinate of the top-left corner of the area. + w (int | float): The width of the area. + h (int | float): The height of the area. + + Methods: + contains(x, y): Checks if the specified point is contained within the area. + contains_area(other): Checks if the specified area is contained within the area. + intersects(other): Checks if the current Area object intersects with another Area object. + touches_or_intersects(other): Checks if the current Area object touches or intersects with another Area object. + shift(dx=0, dy=0): Returns a new Area shifted by the specified amount in the x and y directions. + clip(other): Clips the current Area object to the specified Area object. + + Special Methods: + __eq__(other): Checks if the current Area object is equal to another Area object. + __ne__(other): Checks if the current Area object is not equal to another Area object. + __add__(other): Computes the union of the current Area object and another Area object. + __iter__(): Returns an iterator over the elements of the Area object. + __repr__(): Returns a string representation of the Area object. + __str__(): Returns a string representation of the Area object. + """ + + __hash__ = None + + def __init__(self, x, y=None, w=None, h=None): + """ + Initializes a new instance of the Area class. + + Args: + x (int | float | tuple): The x-coordinate of the top-left corner of the area or + a tuple containing the x, y, w, and h coordinates of the area. + y (int | float): The y-coordinate of the top-left corner of the area. + w (int | float): The width of the area. + h (int | float): The height of the area. + """ + if isinstance(x, tuple): + x, y, w, h = x + if y is None or w is None or h is None: + raise ValueError("Invalid arguments") + self.x = x + self.y = y + self.w = w + self.h = h + + def contains(self, x, y=None): + """ + Checks if the specified point is contained within the area. + + Args: + x (int | tuple): The x-coordinate of the point to check + or a tuple containing the x and y coordinates of the point. + y (int): The y-coordinate of the point to check. + + Returns: + (bool): True if the point is contained within the area, False otherwise. + """ + if isinstance(x, tuple): + x, y = x + return self.x <= x < self.x + self.w and self.y <= y < self.y + self.h + + def contains_area(self, other): + """ + Checks if the specified area is contained within the area. + + Args: + other (Area): The other area to check. + + Returns: + (bool): True if the other area is contained within the area, False otherwise. + """ + return ( + self.x <= other.x + and self.y <= other.y + and self.x + self.w >= other.x + other.w + and self.y + self.h >= other.y + other.h + ) + + def intersects(self, other): + """ + Checks if the current Area object intersects with another Area object. + + Args: + other (Area): The other Area object to check for overlap. + + Returns: + (bool): True if the two Area objects intersect, False otherwise. + """ + if self.x + self.w <= other.x or other.x + other.w <= self.x: + return False + return not (self.y + self.h <= other.y or other.y + other.h <= self.y) + + def touches_or_intersects(self, other): + """ + Checks if the current Area object touches or intersects with another Area object. + + Args: + other (Area): The other Area object to check for overlap or touch. + + Returns: + (bool): True if the two Area objects touch or intersect, False otherwise. + """ + if self.x + self.w < other.x or other.x + other.w < self.x: + return False + return not (self.y + self.h < other.y or other.y + other.h < self.y) + + def shift(self, dx=0, dy=0): + """ + Returns a new Area shifted by the specified amount in the x and y directions. + + Args: + dx (int | float): The amount to shift the area in the x direction. + dy (int | float): The amount to shift the area in the y direction. + + Returns: + (Area): A new Area object shift by the specified amount in the x and y directions. + """ + return Area(self.x + dx, self.y + dy, self.w, self.h) + + def clip(self, other): + """ + Clips the current Area object to the specified Area object. + + Args: + other (Area): The other Area object to clip to. + + Returns: + (Area): A new Area object representing the clipped area. + """ + x = max(self.x, other.x) + y = max(self.y, other.y) + w = min(self.x + self.w, other.x + other.w) - x + h = min(self.y + self.h, other.y + other.h) - y + return Area(x, y, w, h) + + def offset(self, d1, d2=None, d3=None, d4=None): + """ + Returns a new Area offset by the specified amount(s). + + If only one argument is provided, it is used as the offset in all 4 directions. + If two arguments are provided, the first is used as the offset in the x direction and the second as the offset in the y direction. + If three arguments are provided, they are used as the offsets in the left, top/bottom, and right directions, respectively. + If four arguments are provided, they are used as the offsets in the left, top, right, and bottom directions, respectively. + + Args: + d1 (int | float): The offset in the x direction or the offset in all 4 directions. + d2 (int | float): The offset in the y direction or the offset in the top/bottom direction. + d3 (int | float): The offset in the right direction. + d4 (int | float): The offset in the bottom direction. + + Returns: + (Area): A new Area object offset by the specified amount(s). + """ + if d2 is None: + d2 = d3 = d4 = d1 + elif d3 is None: + d3 = d1 + d4 = d2 + elif d4 is None: + d4 = d2 + return Area(self.x - d1, self.y - d2, self.w + d1 + d3, self.h + d2 + d4) + + def inset(self, d1, d2=None, d3=None, d4=None): + """ + Returns a new Area inset by the specified amount(s). + + If only one argument is provided, it is used as the inset in all 4 directions. + If two arguments are provided, the first is used as the inset in the x direction and the second as the inset in the y direction. + If three arguments are provided, they are used as the insets in the left, top/bottom, and right directions, respectively. + If four arguments are provided, they are used as the insets in the left, top, right, and bottom directions, respectively. + + Args: + d1 (int | float): The inset in the x direction or the inset in all 4 directions. + d2 (int | float): The inset in the y direction or the inset in the top/bottom direction. + d3 (int | float): The inset in the right direction. + d4 (int | float): The inset in the bottom direction. + + Returns: + (Area): A new Area object inset by the specified amount(s). + """ + if d2 is None: + d2 = d3 = d4 = d1 + elif d3 is None: + d3 = d1 + d4 = d2 + elif d4 is None: + d4 = d2 + return Area(self.x + d1, self.y + d2, self.w - d1 - d3, self.h - d2 - d4) + + def __eq__(self, other): + """ + Checks if the current Area object is equal to another Area object. + + Args: + other (Area): The other Area object to compare with. + + Returns: + (bool): True if the two Area objects are equal, False otherwise. + """ + return self.x == other.x and self.y == other.y and self.w == other.w and self.h == other.h + + def __ne__(self, other): + """ + Checks if the current Area object is not equal to another Area object. + + Args: + other (Area): The other Area object to compare with. + + Returns: + (bool): True if the two Area objects are not equal, False otherwise. + """ + return not self.__eq__(other) + + def __add__(self, other): + """ + Computes the union of the current Area object and another Area object. + + Args: + other (Area): The other Area object to compute the union with. + + Returns: + (Area): A new Area object representing the union of the two areas. + """ + return Area( + min(self.x, other.x), + min(self.y, other.y), + max(self.x + self.w, other.x + other.w) - min(self.x, other.x), + max(self.y + self.h, other.y + other.h) - min(self.y, other.y), + ) + + def __iter__(self): + """ + Returns an iterator over the elements of the Area object. + + Returns: + (iterator): An iterator over the elements of the Area object. + """ + return iter((self.x, self.y, self.w, self.h)) + + def __repr__(self): + """ + Returns a string representation of the Area object. + + Returns: + (str): A string representation of the Area object. + """ + return f"Area({self.x}, {self.y}, {self.w}, {self.h})" + + def __str__(self): + """ + Returns a string representation of the Area object. + + Returns: + (str): A string representation of the Area object. + """ + return f"Area({self.x}, {self.y}, {self.w}, {self.h})" diff --git a/lib/graphics/_blit_hooks.py b/lib/graphics/_blit_hooks.py new file mode 100644 index 0000000..6f38e45 --- /dev/null +++ b/lib/graphics/_blit_hooks.py @@ -0,0 +1,131 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""Blit helpers for display-driver fast paths and clipping.""" + +from ._area import Area + +_RGB565_BPP = 2 + + +def clip_blit_bounds(canvas, source, x, y): + """Return clipped destination/source origin as ``(x0, y0, w, h, src_x, src_y)``. + + Returns ``None`` when the blit is fully outside the canvas. + """ + if ( + (-x >= source.width) + or (-y >= source.height) + or (x >= canvas.width) + or (y >= canvas.height) + ): + return None + + x0 = max(0, x) + y0 = max(0, y) + src_x = max(0, -x) + src_y = max(0, -y) + w = min(canvas.width, x + source.width) - x0 + h = min(canvas.height, y + source.height) - y0 + return x0, y0, w, h, src_x, src_y + + +def canvas_accepts_blit_rect(canvas): + """True when ``canvas.blit_rect`` is a display-style fast path (not RAM copy).""" + if not callable(getattr(canvas, "blit_rect", None)): + return False + from ._framebuf_plus import FrameBuffer + + return not isinstance(canvas, FrameBuffer) + + +def canvas_accepts_blit_transparent(canvas): + """True when ``canvas.blit_transparent`` is implemented by the display driver.""" + if not callable(getattr(canvas, "blit_transparent", None)): + return False + from ._framebuf_plus import FrameBuffer + + return not isinstance(canvas, FrameBuffer) + + +def _source_rgb565_bytes_per_pixel(source): + fmt = getattr(source, "format", None) + if fmt is None: + return None + from ._framebuf_plus import RGB565 + + return _RGB565_BPP if fmt == RGB565 else None + + +def _framebuffer_base_blit(canvas, source, x, y, key=-1, palette=None): + """Call ``framebuf.FrameBuffer.blit`` without re-entering graphics overrides.""" + base = type(canvas).__mro__[1] + base.blit(canvas, source, x, y, key, palette) + + +def _extract_rgb565_rows(source, src_x, src_y, w, h): + bpp = _RGB565_BPP + row_bytes = w * bpp + out = bytearray(row_bytes * h) + for row in range(h): + src_start = ((src_y + row) * source.width + src_x) * bpp + src_end = src_start + row_bytes + dst_start = row * row_bytes + out[dst_start : dst_start + row_bytes] = source.buffer[src_start:src_end] + return out + + +def blit_rect_to_buffer(canvas, buf, x, y, w, h, *, bpp=_RGB565_BPP): + """Copy an RGB565 rectangle into ``canvas.buffer``.""" + if x < 0 or y < 0 or x + w > canvas.width or y + h > canvas.height: + raise ValueError("The provided x, y, w, h values are out of range") + + expected = w * h * bpp + if len(buf) != expected: + raise ValueError( + f"The source buffer is not the correct size (got {len(buf)} bytes, expected {expected})" + ) + + for row in range(h): + source_begin = row * w * bpp + source_end = source_begin + w * bpp + dest_begin = ((y + row) * canvas.width + x) * bpp + dest_end = dest_begin + w * bpp + canvas.buffer[dest_begin:dest_end] = buf[source_begin:source_end] + + +def blit_rect_dispatch(canvas, buf, x, y, w, h): + """Blit a raw RGB565 buffer, using a canvas hook when available.""" + if canvas_accepts_blit_rect(canvas): + canvas.blit_rect(buf, x, y, w, h) + else: + blit_rect_to_buffer(canvas, buf, x, y, w, h) + return Area(x, y, w, h) + + +def try_fast_framebuffer_blit(canvas, source, x, y, key=-1, palette=None): + """Use framebuffer or display fast paths when available. + + Returns an :class:`Area` on success, or ``None`` to use the pixel loop. + """ + clipped = clip_blit_bounds(canvas, source, x, y) + if clipped is None: + return None + + x0, y0, w, h, src_x, src_y = clipped + + from ._framebuf_plus import FrameBuffer + + if isinstance(canvas, FrameBuffer) and isinstance(source, FrameBuffer): + _framebuffer_base_blit(canvas, source, x, y, key, palette) + return Area(x0, y0, w, h) + + if key != -1 or palette is not None: + return None + + if _source_rgb565_bytes_per_pixel(source) is None or not hasattr(source, "buffer"): + return None + + data = _extract_rgb565_rows(source, src_x, src_y, w, h) + blit_rect_dispatch(canvas, data, x0, y0, w, h) + return Area(x0, y0, w, h) diff --git a/lib/graphics/_bmp565.py b/lib/graphics/_bmp565.py new file mode 100644 index 0000000..852feba --- /dev/null +++ b/lib/graphics/_bmp565.py @@ -0,0 +1,199 @@ +# SPDX-FileCopyrightText: 2024 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""RGB565 Windows BMP read/write helpers and sliceable BMP565 asset.""" + +import os +import struct + +BMP565_BPP = 16 +BMP565_BYTES_PER_PIXEL = 2 + + +def read_bmp565_header(f): + """Read an RGB565 BMP header from an open binary file. + + Returns: + tuple[int, int, int]: width, height, data_offset + """ + if f.read(2) != b"BM": + raise ValueError("Not a BMP file") + f.seek(10) + data_offset = struct.unpack("= canvas.width or \ + # y < -self.height or y >= canvas.height: + # return + # Go through each row of the character. + for char_y in range(self._font_height): + # Grab the byte for the current row of font data. + if not (line := self._read_line(char, char_y)): + continue # maybe character isnt there? go to next + # Go through each column in the row byte. + for char_x in range(self.width): + # Draw a pixel for each bit that's flipped on. + if (line >> (self.width - char_x - 1)) & 0x1: + canvas.fill_rect( + ( + x + char_x * scale + if not inverted + else x + (self._font_width - char_x - 1) * scale + ), + ( + y + char_y * scale + if not inverted + else y + (self._font_height - char_y - 1) * scale + ), + scale, + scale, + color, + ) + return Area(x, y, self._font_width * scale, self._font_height * scale) + + def text(self, canvas, string, x, y, color, scale=1, inverted=False): + """ + Draw text to the canvas. + + Args: + canvas (Canvas): The DisplayDriver, FrameBuffer, or other canvas-like object to draw on. + string (str): The text to draw. + x (int): The x position to start drawing the text. + y (int): The y position to start drawing the text. + color (int): The color to draw the text in. + scale (int): The scale factor to draw the text at. Default is 1. + inverted (bool): If True, draw the text inverted. Default is False. + + Returns: + (Area): The area that was drawn to. + """ + if inverted: + string = "".join(reversed(string)) + + char_y = y + largest_x = 0 # the last x position reached on the longest line + for chunk in string.split("\n"): + last_x = x # the last x position reached on the current line + for i, char in enumerate(chunk): + char_x = x + (i * self.width * scale) + if ( + (char_x < canvas.width if hasattr(canvas, "width") else True) + and (char_y < canvas.height if hasattr(canvas, "height") else True) + and char_x + (self.width * scale) > 0 + and char_y + (self.height * scale) > 0 + ): + self.draw_char( + char, + char_x, + char_y, + canvas, + color, + scale=scale, + inverted=inverted, + ) + last_x = char_x + (self.width * scale) + largest_x = max([largest_x, last_x]) # update the largest x position + char_y += self.height * scale + return Area(x, y, largest_x - x, char_y - y) + + def text_width(self, text, scale=1): + """ + Return the pixel width of the specified text message. + Takes into account the scale factor, but not any newlines. + + Args: + text (str): The text to measure. + scale (int): The scale factor to measure the text at. Default is 1. + """ + return len(text) * self._font_width * scale + + def _read_line(self, char, line): + """Read a line of font data for a character.""" + # ROM fonts cover 256 code points (0..255). Skip anything outside that + # range instead of indexing off the end of the cache / file. + code = ord(char) + if code > 255: + return None + offset = (code * self.height) + line + if self._cache: + if offset >= len(self._cache): + return None + return self._cache[offset] + + self._font.seek(offset) + try: + return struct.unpack("B", self._font.read(1))[0] + except RuntimeError: # maybe character isnt there? go to next + return None + + def export(self, filename): + """ + Export the font data in self._cache to a .py file that can be imported. + The format is a single bytes object named _FONT. There are 256 lines, one for each character. + The last line is `FONT = memoryview(_FONT)`. + + Args: + filename (str): The path to save the file to. + """ + if not self._cache: + raise RuntimeError("Font data not cached, cannot export") + mv = memoryview(self._cache) + with open(filename, "w") as f: + f.write("_FONT =\\\n") + for i in range(256): + f.write("b'") + for j in range(self.height): + f.write(f"\\x{mv[(i * self.height) + j]:02x}") + f.write("'\\\n") + f.write("\nFONT = memoryview(_FONT)\n") diff --git a/lib/graphics/_font_8x14.py b/lib/graphics/_font_8x14.py new file mode 100644 index 0000000..a4810bc --- /dev/null +++ b/lib/graphics/_font_8x14.py @@ -0,0 +1,259 @@ +_FONT = ( + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x7e\x81\xa5\x81\x81\xbd\x99\x81\x7e\x00\x00\x00" + b"\x00\x00\x7e\xff\xdb\xff\xff\xc3\xe7\xff\x7e\x00\x00\x00" + b"\x00\x00\x00\x6c\xfe\xfe\xfe\xfe\x7c\x38\x10\x00\x00\x00" + b"\x00\x00\x00\x10\x38\x7c\xfe\x7c\x38\x10\x00\x00\x00\x00" + b"\x00\x00\x18\x3c\x3c\xe7\xe7\xe7\x18\x18\x3c\x00\x00\x00" + b"\x00\x00\x18\x3c\x7e\xff\xff\x7e\x18\x18\x3c\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x18\x3c\x3c\x18\x00\x00\x00\x00\x00" + b"\xff\xff\xff\xff\xff\xe7\xc3\xc3\xe7\xff\xff\xff\xff\xff" + b"\x00\x00\x00\x00\x3c\x66\x42\x42\x66\x3c\x00\x00\x00\x00" + b"\xff\xff\xff\xff\xc3\x99\xbd\xbd\x99\xc3\xff\xff\xff\xff" + b"\x00\x00\x1e\x0e\x1a\x32\x78\xcc\xcc\xcc\x78\x00\x00\x00" + b"\x00\x00\x3c\x66\x66\x66\x3c\x18\x7e\x18\x18\x00\x00\x00" + b"\x00\x00\x3f\x33\x3f\x30\x30\x30\x70\xf0\xe0\x00\x00\x00" + b"\x00\x00\x7f\x63\x7f\x63\x63\x63\x67\xe7\xe6\xc0\x00\x00" + b"\x00\x00\x18\x18\xdb\x3c\xe7\x3c\xdb\x18\x18\x00\x00\x00" + b"\x00\x00\x80\xc0\xe0\xf8\xfe\xf8\xe0\xc0\x80\x00\x00\x00" + b"\x00\x00\x02\x06\x0e\x3e\xfe\x3e\x0e\x06\x02\x00\x00\x00" + b"\x00\x00\x18\x3c\x7e\x18\x18\x18\x7e\x3c\x18\x00\x00\x00" + b"\x00\x00\x66\x66\x66\x66\x66\x66\x00\x66\x66\x00\x00\x00" + b"\x00\x00\x7f\xdb\xdb\xdb\x7b\x1b\x1b\x1b\x1b\x00\x00\x00" + b"\x00\x7c\xc6\x60\x38\x6c\xc6\xc6\x6c\x38\x0c\xc6\x7c\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\xfe\x00\x00\x00" + b"\x00\x00\x18\x3c\x7e\x18\x18\x18\x7e\x3c\x18\x7e\x00\x00" + b"\x00\x00\x18\x3c\x7e\x18\x18\x18\x18\x18\x18\x00\x00\x00" + b"\x00\x00\x18\x18\x18\x18\x18\x18\x7e\x3c\x18\x00\x00\x00" + b"\x00\x00\x00\x00\x18\x0c\xfe\x0c\x18\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x30\x60\xfe\x60\x30\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xc0\xc0\xc0\xfe\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x28\x6c\xfe\x6c\x28\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x10\x38\x38\x7c\x7c\xfe\xfe\x00\x00\x00\x00" + b"\x00\x00\x00\xfe\xfe\x7c\x7c\x38\x38\x10\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x18\x3c\x3c\x3c\x18\x18\x00\x18\x18\x00\x00\x00" + b"\x00\x66\x66\x66\x24\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x6c\x6c\xfe\x6c\x6c\x6c\xfe\x6c\x6c\x00\x00\x00" + b"\x18\x18\x7c\xc6\xc2\xc0\x7c\x06\x86\xc6\x7c\x18\x18\x00" + b"\x00\x00\x00\x00\xc2\xc6\x0c\x18\x30\x66\xc6\x00\x00\x00" + b"\x00\x00\x38\x6c\x6c\x38\x76\xdc\xcc\xcc\x76\x00\x00\x00" + b"\x00\x30\x30\x30\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x0c\x18\x30\x30\x30\x30\x30\x18\x0c\x00\x00\x00" + b"\x00\x00\x30\x18\x0c\x0c\x0c\x0c\x0c\x18\x30\x00\x00\x00" + b"\x00\x00\x00\x00\x66\x3c\xff\x3c\x66\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x18\x18\x7e\x18\x18\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x18\x18\x18\x30\x00\x00" + b"\x00\x00\x00\x00\x00\x00\xfe\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x18\x00\x00\x00" + b"\x00\x00\x02\x06\x0c\x18\x30\x60\xc0\x80\x00\x00\x00\x00" + b"\x00\x00\x7c\xc6\xce\xde\xf6\xe6\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x00\x18\x38\x78\x18\x18\x18\x18\x18\x7e\x00\x00\x00" + b"\x00\x00\x7c\xc6\x06\x0c\x18\x30\x60\xc6\xfe\x00\x00\x00" + b"\x00\x00\x7c\xc6\x06\x06\x3c\x06\x06\xc6\x7c\x00\x00\x00" + b"\x00\x00\x0c\x1c\x3c\x6c\xcc\xfe\x0c\x0c\x1e\x00\x00\x00" + b"\x00\x00\xfe\xc0\xc0\xc0\xfc\x06\x06\xc6\x7c\x00\x00\x00" + b"\x00\x00\x38\x60\xc0\xc0\xfc\xc6\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x00\xfe\xc6\x06\x0c\x18\x30\x30\x30\x30\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\xc6\x7c\xc6\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\xc6\x7e\x06\x06\x0c\x78\x00\x00\x00" + b"\x00\x00\x00\x18\x18\x00\x00\x00\x18\x18\x00\x00\x00\x00" + b"\x00\x00\x00\x18\x18\x00\x00\x00\x18\x18\x30\x00\x00\x00" + b"\x00\x00\x06\x0c\x18\x30\x60\x30\x18\x0c\x06\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7e\x00\x00\x7e\x00\x00\x00\x00\x00" + b"\x00\x00\x60\x30\x18\x0c\x06\x0c\x18\x30\x60\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\x0c\x18\x18\x00\x18\x18\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\xde\xde\xde\xdc\xc0\x7c\x00\x00\x00" + b"\x00\x00\x10\x38\x6c\xc6\xc6\xfe\xc6\xc6\xc6\x00\x00\x00" + b"\x00\x00\xfc\x66\x66\x66\x7c\x66\x66\x66\xfc\x00\x00\x00" + b"\x00\x00\x3c\x66\xc2\xc0\xc0\xc0\xc2\x66\x3c\x00\x00\x00" + b"\x00\x00\xf8\x6c\x66\x66\x66\x66\x66\x6c\xf8\x00\x00\x00" + b"\x00\x00\xfe\x66\x62\x68\x78\x68\x62\x66\xfe\x00\x00\x00" + b"\x00\x00\xfe\x66\x62\x68\x78\x68\x60\x60\xf0\x00\x00\x00" + b"\x00\x00\x3c\x66\xc2\xc0\xc0\xde\xc6\x66\x3a\x00\x00\x00" + b"\x00\x00\xc6\xc6\xc6\xc6\xfe\xc6\xc6\xc6\xc6\x00\x00\x00" + b"\x00\x00\x3c\x18\x18\x18\x18\x18\x18\x18\x3c\x00\x00\x00" + b"\x00\x00\x1e\x0c\x0c\x0c\x0c\x0c\xcc\xcc\x78\x00\x00\x00" + b"\x00\x00\xe6\x66\x6c\x6c\x78\x6c\x6c\x66\xe6\x00\x00\x00" + b"\x00\x00\xf0\x60\x60\x60\x60\x60\x62\x66\xfe\x00\x00\x00" + b"\x00\x00\xc6\xee\xfe\xfe\xd6\xc6\xc6\xc6\xc6\x00\x00\x00" + b"\x00\x00\xc6\xe6\xf6\xfe\xde\xce\xc6\xc6\xc6\x00\x00\x00" + b"\x00\x00\x38\x6c\xc6\xc6\xc6\xc6\xc6\x6c\x38\x00\x00\x00" + b"\x00\x00\xfc\x66\x66\x66\x7c\x60\x60\x60\xf0\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\xc6\xc6\xd6\xde\x7c\x0c\x0e\x00\x00" + b"\x00\x00\xfc\x66\x66\x66\x7c\x6c\x66\x66\xe6\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\x60\x38\x0c\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x00\x7e\x7e\x5a\x18\x18\x18\x18\x18\x3c\x00\x00\x00" + b"\x00\x00\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x00\xc6\xc6\xc6\xc6\xc6\xc6\x6c\x38\x10\x00\x00\x00" + b"\x00\x00\xc6\xc6\xc6\xc6\xd6\xd6\xfe\x7c\x6c\x00\x00\x00" + b"\x00\x00\xc6\xc6\x6c\x38\x38\x38\x6c\xc6\xc6\x00\x00\x00" + b"\x00\x00\x66\x66\x66\x66\x3c\x18\x18\x18\x3c\x00\x00\x00" + b"\x00\x00\xfe\xc6\x8c\x18\x30\x60\xc2\xc6\xfe\x00\x00\x00" + b"\x00\x00\x3c\x30\x30\x30\x30\x30\x30\x30\x3c\x00\x00\x00" + b"\x00\x00\x80\xc0\xe0\x70\x38\x1c\x0e\x06\x02\x00\x00\x00" + b"\x00\x00\x3c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x3c\x00\x00\x00" + b"\x10\x38\x6c\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00" + b"\x30\x30\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00" + b"\x00\x00\xe0\x60\x60\x78\x6c\x66\x66\x66\x7c\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7c\xc6\xc0\xc0\xc6\x7c\x00\x00\x00" + b"\x00\x00\x1c\x0c\x0c\x3c\x6c\xcc\xcc\xcc\x76\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7c\xc6\xfe\xc0\xc6\x7c\x00\x00\x00" + b"\x00\x00\x38\x6c\x64\x60\xf0\x60\x60\x60\xf0\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x76\xcc\xcc\xcc\x7c\x0c\xcc\x78\x00" + b"\x00\x00\xe0\x60\x60\x6c\x76\x66\x66\x66\xe6\x00\x00\x00" + b"\x00\x00\x18\x18\x00\x38\x18\x18\x18\x18\x3c\x00\x00\x00" + b"\x00\x00\x06\x06\x00\x0e\x06\x06\x06\x06\x66\x66\x3c\x00" + b"\x00\x00\xe0\x60\x60\x66\x6c\x78\x6c\x66\xe6\x00\x00\x00" + b"\x00\x00\x38\x18\x18\x18\x18\x18\x18\x18\x3c\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xec\xfe\xd6\xd6\xd6\xc6\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xdc\x66\x66\x66\x66\x66\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xdc\x66\x66\x66\x7c\x60\x60\xf0\x00" + b"\x00\x00\x00\x00\x00\x76\xcc\xcc\xcc\x7c\x0c\x0c\x1e\x00" + b"\x00\x00\x00\x00\x00\xdc\x76\x66\x60\x60\xf0\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7c\xc6\x70\x1c\xc6\x7c\x00\x00\x00" + b"\x00\x00\x10\x30\x30\xfc\x30\x30\x30\x36\x1c\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x66\x66\x66\x66\x3c\x18\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xc6\xc6\xd6\xd6\xfe\x6c\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xc6\x6c\x38\x38\x6c\xc6\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xc6\xc6\xc6\xc6\x7e\x06\x0c\xf8\x00" + b"\x00\x00\x00\x00\x00\xfe\xcc\x18\x30\x66\xfe\x00\x00\x00" + b"\x00\x00\x0e\x18\x18\x18\x70\x18\x18\x18\x0e\x00\x00\x00" + b"\x00\x00\x18\x18\x18\x18\x00\x18\x18\x18\x18\x00\x00\x00" + b"\x00\x00\x70\x18\x18\x18\x0e\x18\x18\x18\x70\x00\x00\x00" + b"\x00\x00\x76\xdc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x10\x38\x6c\xc6\xc6\xfe\x00\x00\x00\x00" + b"\x00\x00\x3c\x66\xc2\xc0\xc0\xc2\x66\x3c\x0c\x06\x7c\x00" + b"\x00\x00\xcc\xcc\x00\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00" + b"\x00\x0c\x18\x30\x00\x7c\xc6\xfe\xc0\xc6\x7c\x00\x00\x00" + b"\x00\x10\x38\x6c\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00" + b"\x00\x00\xcc\xcc\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00" + b"\x00\x60\x30\x18\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00" + b"\x00\x38\x6c\x38\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00" + b"\x00\x00\x00\x00\x3c\x66\x60\x66\x3c\x0c\x06\x3c\x00\x00" + b"\x00\x10\x38\x6c\x00\x7c\xc6\xfe\xc0\xc6\x7c\x00\x00\x00" + b"\x00\x00\xcc\xcc\x00\x7c\xc6\xfe\xc0\xc6\x7c\x00\x00\x00" + b"\x00\x60\x30\x18\x00\x7c\xc6\xfe\xc0\xc6\x7c\x00\x00\x00" + b"\x00\x00\x66\x66\x00\x38\x18\x18\x18\x18\x3c\x00\x00\x00" + b"\x00\x18\x3c\x66\x00\x38\x18\x18\x18\x18\x3c\x00\x00\x00" + b"\x00\x60\x30\x18\x00\x38\x18\x18\x18\x18\x3c\x00\x00\x00" + b"\x00\xc6\xc6\x10\x38\x6c\xc6\xc6\xfe\xc6\xc6\x00\x00\x00" + b"\x38\x6c\x38\x00\x38\x6c\xc6\xc6\xfe\xc6\xc6\x00\x00\x00" + b"\x18\x30\x60\x00\xfe\x66\x60\x7c\x60\x66\xfe\x00\x00\x00" + b"\x00\x00\x00\x00\xcc\x76\x36\x7e\xd8\xd8\x6e\x00\x00\x00" + b"\x00\x00\x3e\x6c\xcc\xcc\xfe\xcc\xcc\xcc\xce\x00\x00\x00" + b"\x00\x10\x38\x6c\x00\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x00\xc6\xc6\x00\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x60\x30\x18\x00\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x30\x78\xcc\x00\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00" + b"\x00\x60\x30\x18\x00\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00" + b"\x00\x00\xc6\xc6\x00\xc6\xc6\xc6\xc6\x7e\x06\x0c\x78\x00" + b"\x00\xc6\xc6\x38\x6c\xc6\xc6\xc6\xc6\x6c\x38\x00\x00\x00" + b"\x00\xc6\xc6\x00\xc6\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x18\x18\x3c\x66\x60\x60\x66\x3c\x18\x18\x00\x00\x00" + b"\x00\x38\x6c\x64\x60\xf0\x60\x60\x60\xe6\xfc\x00\x00\x00" + b"\x00\x00\x66\x66\x3c\x18\x7e\x18\x7e\x18\x18\x00\x00\x00" + b"\x00\xf8\xcc\xcc\xf8\xc4\xcc\xde\xcc\xcc\xc6\x00\x00\x00" + b"\x00\x0e\x1b\x18\x18\x18\x7e\x18\x18\x18\x18\xd8\x70\x00" + b"\x00\x18\x30\x60\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00" + b"\x00\x0c\x18\x30\x00\x38\x18\x18\x18\x18\x3c\x00\x00\x00" + b"\x00\x18\x30\x60\x00\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x18\x30\x60\x00\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00" + b"\x00\x00\x76\xdc\x00\xdc\x66\x66\x66\x66\x66\x00\x00\x00" + b"\x76\xdc\x00\xc6\xe6\xf6\xfe\xde\xce\xc6\xc6\x00\x00\x00" + b"\x00\x3c\x6c\x6c\x3e\x00\x7e\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x38\x6c\x6c\x38\x00\x7c\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x30\x30\x00\x30\x30\x60\xc6\xc6\x7c\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\xfe\xc0\xc0\xc0\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\xfe\x06\x06\x06\x00\x00\x00\x00" + b"\x00\xc0\xc0\xc6\xcc\xd8\x30\x60\xdc\x86\x0c\x18\x3e\x00" + b"\x00\xc0\xc0\xc6\xcc\xd8\x30\x66\xce\x9e\x3e\x06\x06\x00" + b"\x00\x00\x18\x18\x00\x18\x18\x3c\x3c\x3c\x18\x00\x00\x00" + b"\x00\x00\x00\x00\x36\x6c\xd8\x6c\x36\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\xd8\x6c\x36\x6c\xd8\x00\x00\x00\x00\x00" + b"\x11\x44\x11\x44\x11\x44\x11\x44\x11\x44\x11\x44\x11\x44" + b"\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa" + b"\xdd\x77\xdd\x77\xdd\x77\xdd\x77\xdd\x77\xdd\x77\xdd\x77" + b"\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x18\x18\xf8\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\xf8\x18\xf8\x18\x18\x18\x18\x18\x18" + b"\x36\x36\x36\x36\x36\x36\x36\xf6\x36\x36\x36\x36\x36\x36" + b"\x00\x00\x00\x00\x00\x00\x00\xfe\x36\x36\x36\x36\x36\x36" + b"\x00\x00\x00\x00\x00\xf8\x18\xf8\x18\x18\x18\x18\x18\x18" + b"\x36\x36\x36\x36\x36\xf6\x06\xf6\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x00\x00\x00\x00\x00\xfe\x06\xf6\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\xf6\x06\xfe\x00\x00\x00\x00\x00\x00" + b"\x36\x36\x36\x36\x36\x36\x36\xfe\x00\x00\x00\x00\x00\x00" + b"\x18\x18\x18\x18\x18\xf8\x18\xf8\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\xf8\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x18\x18\x1f\x00\x00\x00\x00\x00\x00" + b"\x18\x18\x18\x18\x18\x18\x18\xff\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\xff\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x18\x18\x1f\x18\x18\x18\x18\x18\x18" + b"\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00" + b"\x18\x18\x18\x18\x18\x18\x18\xff\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x1f\x18\x1f\x18\x18\x18\x18\x18\x18" + b"\x36\x36\x36\x36\x36\x36\x36\x37\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x37\x30\x3f\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x3f\x30\x37\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\xf7\x00\xff\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xff\x00\xf7\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x37\x30\x37\x36\x36\x36\x36\x36\x36" + b"\x00\x00\x00\x00\x00\xff\x00\xff\x00\x00\x00\x00\x00\x00" + b"\x36\x36\x36\x36\x36\xf7\x00\xf7\x36\x36\x36\x36\x36\x36" + b"\x18\x18\x18\x18\x18\xff\x00\xff\x00\x00\x00\x00\x00\x00" + b"\x36\x36\x36\x36\x36\x36\x36\xff\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xff\x00\xff\x18\x18\x18\x18\x18\x18" + b"\x00\x00\x00\x00\x00\x00\x00\xff\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x36\x36\x3f\x00\x00\x00\x00\x00\x00" + b"\x18\x18\x18\x18\x18\x1f\x18\x1f\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x1f\x18\x1f\x18\x18\x18\x18\x18\x18" + b"\x00\x00\x00\x00\x00\x00\x00\x3f\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x36\x36\xff\x36\x36\x36\x36\x36\x36" + b"\x18\x18\x18\x18\x18\xff\x18\xff\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x18\x18\xf8\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x1f\x18\x18\x18\x18\x18\x18" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff" + b"\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0" + b"\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f" + b"\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x76\xdc\xd8\xd8\xdc\x76\x00\x00\x00" + b"\x00\x00\x00\x00\x7c\xc6\xfc\xc6\xc6\xfc\xc0\xc0\x40\x00" + b"\x00\x00\xfe\xc6\xc6\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00" + b"\x00\x00\x00\x00\xfe\x6c\x6c\x6c\x6c\x6c\x6c\x00\x00\x00" + b"\x00\x00\xfe\xc6\x60\x30\x18\x30\x60\xc6\xfe\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7e\xd8\xd8\xd8\xd8\x70\x00\x00\x00" + b"\x00\x00\x00\x00\x66\x66\x66\x66\x7c\x60\x60\xc0\x00\x00" + b"\x00\x00\x00\x00\x76\xdc\x18\x18\x18\x18\x18\x00\x00\x00" + b"\x00\x00\x7e\x18\x3c\x66\x66\x66\x3c\x18\x7e\x00\x00\x00" + b"\x00\x00\x38\x6c\xc6\xc6\xfe\xc6\xc6\x6c\x38\x00\x00\x00" + b"\x00\x00\x38\x6c\xc6\xc6\xc6\x6c\x6c\x6c\xee\x00\x00\x00" + b"\x00\x00\x1e\x30\x18\x0c\x3e\x66\x66\x66\x3c\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7e\xdb\xdb\x7e\x00\x00\x00\x00\x00" + b"\x00\x00\x03\x06\x7e\xdb\xdb\xf3\x7e\x60\xc0\x00\x00\x00" + b"\x00\x00\x1c\x30\x60\x60\x7c\x60\x60\x30\x1c\x00\x00\x00" + b"\x00\x00\x00\x7c\xc6\xc6\xc6\xc6\xc6\xc6\xc6\x00\x00\x00" + b"\x00\x00\x00\xfe\x00\x00\xfe\x00\x00\xfe\x00\x00\x00\x00" + b"\x00\x00\x00\x18\x18\x7e\x18\x18\x00\x00\xff\x00\x00\x00" + b"\x00\x00\x30\x18\x0c\x06\x0c\x18\x30\x00\x7e\x00\x00\x00" + b"\x00\x00\x0c\x18\x30\x60\x30\x18\x0c\x00\x7e\x00\x00\x00" + b"\x00\x00\x0e\x1b\x1b\x18\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x18\x18\x18\xd8\xd8\x70\x00\x00\x00" + b"\x00\x00\x00\x18\x18\x00\x7e\x00\x18\x18\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x76\xdc\x00\x76\xdc\x00\x00\x00\x00\x00" + b"\x00\x38\x6c\x6c\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x18\x18\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00" + b"\x00\x0f\x0c\x0c\x0c\x0c\x0c\xec\x6c\x3c\x1c\x00\x00\x00" + b"\x00\xd8\x6c\x6c\x6c\x6c\x6c\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x70\xd8\x30\x60\xc8\xf8\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x7c\x7c\x7c\x7c\x7c\x7c\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +) +FONT = memoryview(_FONT) diff --git a/lib/graphics/_font_8x16.py b/lib/graphics/_font_8x16.py new file mode 100644 index 0000000..6ad3c75 --- /dev/null +++ b/lib/graphics/_font_8x16.py @@ -0,0 +1,259 @@ +_FONT = ( + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x7e\x81\xa5\x81\x81\xbd\x99\x81\x81\x7e\x00\x00\x00\x00" + b"\x00\x00\x7e\xff\xdb\xff\xff\xc3\xe7\xff\xff\x7e\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x6c\xfe\xfe\xfe\xfe\x7c\x38\x10\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x10\x38\x7c\xfe\x7c\x38\x10\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x18\x3c\x3c\xe7\xe7\xe7\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\x00\x00\x18\x3c\x7e\xff\xff\x7e\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x18\x3c\x3c\x18\x00\x00\x00\x00\x00\x00" + b"\xff\xff\xff\xff\xff\xff\xe7\xc3\xc3\xe7\xff\xff\xff\xff\xff\xff" + b"\x00\x00\x00\x00\x00\x3c\x66\x42\x42\x66\x3c\x00\x00\x00\x00\x00" + b"\xff\xff\xff\xff\xff\xc3\x99\xbd\xbd\x99\xc3\xff\xff\xff\xff\xff" + b"\x00\x00\x1e\x0e\x1a\x32\x78\xcc\xcc\xcc\xcc\x78\x00\x00\x00\x00" + b"\x00\x00\x3c\x66\x66\x66\x66\x3c\x18\x7e\x18\x18\x00\x00\x00\x00" + b"\x00\x00\x3f\x33\x3f\x30\x30\x30\x30\x70\xf0\xe0\x00\x00\x00\x00" + b"\x00\x00\x7f\x63\x7f\x63\x63\x63\x63\x67\xe7\xe6\xc0\x00\x00\x00" + b"\x00\x00\x00\x18\x18\xdb\x3c\xe7\x3c\xdb\x18\x18\x00\x00\x00\x00" + b"\x00\x80\xc0\xe0\xf0\xf8\xfe\xf8\xf0\xe0\xc0\x80\x00\x00\x00\x00" + b"\x00\x02\x06\x0e\x1e\x3e\xfe\x3e\x1e\x0e\x06\x02\x00\x00\x00\x00" + b"\x00\x00\x18\x3c\x7e\x18\x18\x18\x7e\x3c\x18\x00\x00\x00\x00\x00" + b"\x00\x00\x66\x66\x66\x66\x66\x66\x66\x00\x66\x66\x00\x00\x00\x00" + b"\x00\x00\x7f\xdb\xdb\xdb\x7b\x1b\x1b\x1b\x1b\x1b\x00\x00\x00\x00" + b"\x00\x7c\xc6\x60\x38\x6c\xc6\xc6\x6c\x38\x0c\xc6\x7c\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\xfe\xfe\x00\x00\x00\x00" + b"\x00\x00\x18\x3c\x7e\x18\x18\x18\x7e\x3c\x18\x7e\x00\x00\x00\x00" + b"\x00\x00\x18\x3c\x7e\x18\x18\x18\x18\x18\x18\x18\x00\x00\x00\x00" + b"\x00\x00\x18\x18\x18\x18\x18\x18\x18\x7e\x3c\x18\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x18\x0c\xfe\x0c\x18\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x30\x60\xfe\x60\x30\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\xc0\xc0\xc0\xfe\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x28\x6c\xfe\x6c\x28\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x10\x38\x38\x7c\x7c\xfe\xfe\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\xfe\xfe\x7c\x7c\x38\x38\x10\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x18\x3c\x3c\x3c\x18\x18\x18\x00\x18\x18\x00\x00\x00\x00" + b"\x00\x66\x66\x66\x24\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x6c\x6c\xfe\x6c\x6c\x6c\xfe\x6c\x6c\x00\x00\x00\x00" + b"\x18\x18\x7c\xc6\xc2\xc0\x7c\x06\x06\x86\xc6\x7c\x18\x18\x00\x00" + b"\x00\x00\x00\x00\xc2\xc6\x0c\x18\x30\x60\xc6\x86\x00\x00\x00\x00" + b"\x00\x00\x38\x6c\x6c\x38\x76\xdc\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x30\x30\x30\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x0c\x18\x30\x30\x30\x30\x30\x30\x18\x0c\x00\x00\x00\x00" + b"\x00\x00\x30\x18\x0c\x0c\x0c\x0c\x0c\x0c\x18\x30\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x66\x3c\xff\x3c\x66\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x18\x18\x7e\x18\x18\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x18\x18\x30\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\xfe\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x18\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x02\x06\x0c\x18\x30\x60\xc0\x80\x00\x00\x00\x00" + b"\x00\x00\x38\x6c\xc6\xc6\xd6\xd6\xc6\xc6\x6c\x38\x00\x00\x00\x00" + b"\x00\x00\x18\x38\x78\x18\x18\x18\x18\x18\x18\x7e\x00\x00\x00\x00" + b"\x00\x00\x7c\xc6\x06\x0c\x18\x30\x60\xc0\xc6\xfe\x00\x00\x00\x00" + b"\x00\x00\x7c\xc6\x06\x06\x3c\x06\x06\x06\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\x0c\x1c\x3c\x6c\xcc\xfe\x0c\x0c\x0c\x1e\x00\x00\x00\x00" + b"\x00\x00\xfe\xc0\xc0\xc0\xfc\x06\x06\x06\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\x38\x60\xc0\xc0\xfc\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\xfe\xc6\x06\x06\x0c\x18\x30\x30\x30\x30\x00\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\xc6\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\xc6\x7e\x06\x06\x06\x0c\x78\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x18\x18\x00\x00\x00\x18\x18\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x18\x18\x00\x00\x00\x18\x18\x30\x00\x00\x00\x00" + b"\x00\x00\x00\x06\x0c\x18\x30\x60\x30\x18\x0c\x06\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7e\x00\x00\x7e\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x60\x30\x18\x0c\x06\x0c\x18\x30\x60\x00\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\x0c\x18\x18\x18\x00\x18\x18\x00\x00\x00\x00" + b"\x00\x00\x00\x7c\xc6\xc6\xde\xde\xde\xdc\xc0\x7c\x00\x00\x00\x00" + b"\x00\x00\x10\x38\x6c\xc6\xc6\xfe\xc6\xc6\xc6\xc6\x00\x00\x00\x00" + b"\x00\x00\xfc\x66\x66\x66\x7c\x66\x66\x66\x66\xfc\x00\x00\x00\x00" + b"\x00\x00\x3c\x66\xc2\xc0\xc0\xc0\xc0\xc2\x66\x3c\x00\x00\x00\x00" + b"\x00\x00\xf8\x6c\x66\x66\x66\x66\x66\x66\x6c\xf8\x00\x00\x00\x00" + b"\x00\x00\xfe\x66\x62\x68\x78\x68\x60\x62\x66\xfe\x00\x00\x00\x00" + b"\x00\x00\xfe\x66\x62\x68\x78\x68\x60\x60\x60\xf0\x00\x00\x00\x00" + b"\x00\x00\x3c\x66\xc2\xc0\xc0\xde\xc6\xc6\x66\x3a\x00\x00\x00\x00" + b"\x00\x00\xc6\xc6\xc6\xc6\xfe\xc6\xc6\xc6\xc6\xc6\x00\x00\x00\x00" + b"\x00\x00\x3c\x18\x18\x18\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\x00\x1e\x0c\x0c\x0c\x0c\x0c\xcc\xcc\xcc\x78\x00\x00\x00\x00" + b"\x00\x00\xe6\x66\x66\x6c\x78\x78\x6c\x66\x66\xe6\x00\x00\x00\x00" + b"\x00\x00\xf0\x60\x60\x60\x60\x60\x60\x62\x66\xfe\x00\x00\x00\x00" + b"\x00\x00\xc6\xee\xfe\xfe\xd6\xc6\xc6\xc6\xc6\xc6\x00\x00\x00\x00" + b"\x00\x00\xc6\xe6\xf6\xfe\xde\xce\xc6\xc6\xc6\xc6\x00\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\xfc\x66\x66\x66\x7c\x60\x60\x60\x60\xf0\x00\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\xc6\xc6\xc6\xc6\xd6\xde\x7c\x0c\x0e\x00\x00" + b"\x00\x00\xfc\x66\x66\x66\x7c\x6c\x66\x66\x66\xe6\x00\x00\x00\x00" + b"\x00\x00\x7c\xc6\xc6\x60\x38\x0c\x06\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\x7e\x7e\x5a\x18\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\x00\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\xc6\xc6\xc6\xc6\xc6\xc6\xc6\x6c\x38\x10\x00\x00\x00\x00" + b"\x00\x00\xc6\xc6\xc6\xc6\xd6\xd6\xd6\xfe\xee\x6c\x00\x00\x00\x00" + b"\x00\x00\xc6\xc6\x6c\x7c\x38\x38\x7c\x6c\xc6\xc6\x00\x00\x00\x00" + b"\x00\x00\x66\x66\x66\x66\x3c\x18\x18\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\x00\xfe\xc6\x86\x0c\x18\x30\x60\xc2\xc6\xfe\x00\x00\x00\x00" + b"\x00\x00\x3c\x30\x30\x30\x30\x30\x30\x30\x30\x3c\x00\x00\x00\x00" + b"\x00\x00\x00\x80\xc0\xe0\x70\x38\x1c\x0e\x06\x02\x00\x00\x00\x00" + b"\x00\x00\x3c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x3c\x00\x00\x00\x00" + b"\x10\x38\x6c\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00" + b"\x00\x30\x18\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x78\x0c\x7c\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x00\xe0\x60\x60\x78\x6c\x66\x66\x66\x66\x7c\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7c\xc6\xc0\xc0\xc0\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\x1c\x0c\x0c\x3c\x6c\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7c\xc6\xfe\xc0\xc0\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\x1c\x36\x32\x30\x78\x30\x30\x30\x30\x78\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x76\xcc\xcc\xcc\xcc\xcc\x7c\x0c\xcc\x78\x00" + b"\x00\x00\xe0\x60\x60\x6c\x76\x66\x66\x66\x66\xe6\x00\x00\x00\x00" + b"\x00\x00\x18\x18\x00\x38\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\x00\x06\x06\x00\x0e\x06\x06\x06\x06\x06\x06\x66\x66\x3c\x00" + b"\x00\x00\xe0\x60\x60\x66\x6c\x78\x78\x6c\x66\xe6\x00\x00\x00\x00" + b"\x00\x00\x38\x18\x18\x18\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xec\xfe\xd6\xd6\xd6\xd6\xc6\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xdc\x66\x66\x66\x66\x66\x66\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7c\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xdc\x66\x66\x66\x66\x66\x7c\x60\x60\xf0\x00" + b"\x00\x00\x00\x00\x00\x76\xcc\xcc\xcc\xcc\xcc\x7c\x0c\x0c\x1e\x00" + b"\x00\x00\x00\x00\x00\xdc\x76\x66\x60\x60\x60\xf0\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7c\xc6\x60\x38\x0c\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\x10\x30\x30\xfc\x30\x30\x30\x30\x36\x1c\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xcc\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xc6\xc6\xc6\xc6\xc6\x6c\x38\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xc6\xc6\xd6\xd6\xd6\xfe\x6c\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xc6\x6c\x38\x38\x38\x6c\xc6\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xc6\xc6\xc6\xc6\xc6\xc6\x7e\x06\x0c\xf8\x00" + b"\x00\x00\x00\x00\x00\xfe\xcc\x18\x30\x60\xc6\xfe\x00\x00\x00\x00" + b"\x00\x00\x0e\x18\x18\x18\x70\x18\x18\x18\x18\x0e\x00\x00\x00\x00" + b"\x00\x00\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x00\x00\x00\x00" + b"\x00\x00\x70\x18\x18\x18\x0e\x18\x18\x18\x18\x70\x00\x00\x00\x00" + b"\x00\x76\xdc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x10\x38\x6c\xc6\xc6\xc6\xfe\x00\x00\x00\x00\x00" + b"\x00\x00\x3c\x66\xc2\xc0\xc0\xc0\xc0\xc2\x66\x3c\x18\x70\x00\x00" + b"\x00\x00\xcc\x00\x00\xcc\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x0c\x18\x30\x00\x7c\xc6\xfe\xc0\xc0\xc6\x7c\x00\x00\x00\x00" + b"\x00\x10\x38\x6c\x00\x78\x0c\x7c\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x00\xcc\x00\x00\x78\x0c\x7c\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x60\x30\x18\x00\x78\x0c\x7c\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x38\x6c\x38\x00\x78\x0c\x7c\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7c\xc6\xc0\xc0\xc0\xc6\x7c\x18\x70\x00\x00" + b"\x00\x10\x38\x6c\x00\x7c\xc6\xfe\xc0\xc0\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\xc6\x00\x00\x7c\xc6\xfe\xc0\xc0\xc6\x7c\x00\x00\x00\x00" + b"\x00\x60\x30\x18\x00\x7c\xc6\xfe\xc0\xc0\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\x66\x00\x00\x38\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\x18\x3c\x66\x00\x38\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\x60\x30\x18\x00\x38\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\xc6\x00\x10\x38\x6c\xc6\xc6\xfe\xc6\xc6\xc6\x00\x00\x00\x00" + b"\x38\x6c\x38\x10\x38\x6c\xc6\xfe\xc6\xc6\xc6\xc6\x00\x00\x00\x00" + b"\x0c\x18\x00\xfe\x66\x62\x68\x78\x68\x62\x66\xfe\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xec\x36\x36\x7e\xd8\xd8\x6e\x00\x00\x00\x00" + b"\x00\x00\x3e\x6c\xcc\xcc\xfe\xcc\xcc\xcc\xcc\xce\x00\x00\x00\x00" + b"\x00\x10\x38\x6c\x00\x7c\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\xc6\x00\x00\x7c\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x60\x30\x18\x00\x7c\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x30\x78\xcc\x00\xcc\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x60\x30\x18\x00\xcc\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x00\xc6\x00\x00\xc6\xc6\xc6\xc6\xc6\xc6\x7e\x06\x0c\x78\x00" + b"\x00\xc6\x00\x7c\xc6\xc6\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\xc6\x00\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x18\x18\x7c\xc6\xc0\xc0\xc0\xc6\x7c\x18\x18\x00\x00\x00\x00" + b"\x00\x38\x6c\x64\x60\xf0\x60\x60\x60\x60\xe6\xfc\x00\x00\x00\x00" + b"\x00\x00\x66\x66\x3c\x18\x7e\x18\x7e\x18\x18\x18\x00\x00\x00\x00" + b"\x00\xf8\xcc\xcc\xf8\xc4\xcc\xde\xcc\xcc\xcc\xc6\x00\x00\x00\x00" + b"\x00\x0e\x1b\x18\x18\x18\x7e\x18\x18\x18\xd8\x70\x00\x00\x00\x00" + b"\x00\x18\x30\x60\x00\x78\x0c\x7c\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x0c\x18\x30\x00\x38\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00" + b"\x00\x18\x30\x60\x00\x7c\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x18\x30\x60\x00\xcc\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00" + b"\x00\x00\x76\xdc\x00\xdc\x66\x66\x66\x66\x66\x66\x00\x00\x00\x00" + b"\x76\xdc\x00\xc6\xe6\xf6\xfe\xde\xce\xc6\xc6\xc6\x00\x00\x00\x00" + b"\x00\x00\x3c\x6c\x6c\x3e\x00\x7e\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x38\x6c\x6c\x38\x00\x7c\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x30\x30\x00\x30\x30\x60\xc0\xc6\xc6\x7c\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\xfe\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\xfe\x06\x06\x06\x06\x00\x00\x00\x00\x00" + b"\x00\x60\xe0\x62\x66\x6c\x18\x30\x60\xdc\x86\x0c\x18\x3e\x00\x00" + b"\x00\x60\xe0\x62\x66\x6c\x18\x30\x66\xce\x9a\x3f\x06\x06\x00\x00" + b"\x00\x00\x18\x18\x00\x18\x18\x18\x3c\x3c\x3c\x18\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x36\x6c\xd8\x6c\x36\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xd8\x6c\x36\x6c\xd8\x00\x00\x00\x00\x00\x00" + b"\x11\x44\x11\x44\x11\x44\x11\x44\x11\x44\x11\x44\x11\x44\x11\x44" + b"\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa" + b"\xdd\x77\xdd\x77\xdd\x77\xdd\x77\xdd\x77\xdd\x77\xdd\x77\xdd\x77" + b"\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x18\x18\xf8\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\xf8\x18\xf8\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x36\x36\x36\x36\x36\x36\x36\xf6\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x00\x00\x00\x00\x00\x00\x00\xfe\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x00\x00\x00\x00\x00\xf8\x18\xf8\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x36\x36\x36\x36\x36\xf6\x06\xf6\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x00\x00\x00\x00\x00\xfe\x06\xf6\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\xf6\x06\xfe\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x36\x36\x36\x36\x36\x36\x36\xfe\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x18\x18\x18\x18\x18\xf8\x18\xf8\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\xf8\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x18\x18\x1f\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x18\x18\x18\x18\x18\x18\x18\xff\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\xff\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x18\x18\x1f\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x18\x18\x18\x18\x18\x18\x18\xff\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x1f\x18\x1f\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x36\x36\x36\x36\x36\x36\x36\x37\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x37\x30\x3f\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x3f\x30\x37\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\xf7\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xff\x00\xf7\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x37\x30\x37\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x00\x00\x00\x00\x00\xff\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x36\x36\x36\x36\x36\xf7\x00\xf7\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x18\x18\x18\x18\x18\xff\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x36\x36\x36\x36\x36\x36\x36\xff\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xff\x00\xff\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x00\x00\x00\x00\x00\x00\x00\xff\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x36\x36\x3f\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x18\x18\x18\x18\x18\x1f\x18\x1f\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x1f\x18\x1f\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x00\x00\x00\x00\x00\x00\x00\x3f\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x36\x36\xff\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x18\x18\x18\x18\x18\xff\x18\xff\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x18\x18\xf8\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x1f\x18\x18\x18\x18\x18\x18\x18\x18" + b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff" + b"\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0" + b"\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f" + b"\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x76\xdc\xd8\xd8\xd8\xdc\x76\x00\x00\x00\x00" + b"\x00\x00\x78\xcc\xcc\xcc\xd8\xcc\xc6\xc6\xc6\xcc\x00\x00\x00\x00" + b"\x00\x00\xfe\xc6\xc6\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\xfe\x6c\x6c\x6c\x6c\x6c\x6c\x00\x00\x00\x00" + b"\x00\x00\xfe\xc6\x60\x30\x18\x18\x30\x60\xc6\xfe\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7e\xd8\xd8\xd8\xd8\xd8\x70\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x66\x66\x66\x66\x66\x66\x7c\x60\x60\xc0\x00" + b"\x00\x00\x00\x00\x76\xdc\x18\x18\x18\x18\x18\x18\x00\x00\x00\x00" + b"\x00\x00\x7e\x18\x3c\x66\x66\x66\x66\x3c\x18\x7e\x00\x00\x00\x00" + b"\x00\x00\x38\x6c\xc6\xc6\xfe\xc6\xc6\xc6\x6c\x38\x00\x00\x00\x00" + b"\x00\x00\x38\x6c\xc6\xc6\xc6\x6c\x6c\x6c\x6c\xee\x00\x00\x00\x00" + b"\x00\x00\x1e\x30\x18\x0c\x3e\x66\x66\x66\x66\x3c\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x7e\xdb\xdb\xdb\x7e\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x03\x06\x7e\xdb\xdb\xf3\x7e\x60\xc0\x00\x00\x00\x00" + b"\x00\x00\x1c\x30\x60\x60\x7c\x60\x60\x60\x30\x1c\x00\x00\x00\x00" + b"\x00\x00\x00\x7c\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\x00\x00\x00\x00" + b"\x00\x00\x00\x00\xfe\x00\x00\xfe\x00\x00\xfe\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x18\x18\x7e\x18\x18\x00\x00\x7e\x00\x00\x00\x00" + b"\x00\x00\x00\x30\x18\x0c\x06\x0c\x18\x30\x00\x7e\x00\x00\x00\x00" + b"\x00\x00\x00\x0c\x18\x30\x60\x30\x18\x0c\x00\x7e\x00\x00\x00\x00" + b"\x00\x00\x0e\x1b\x1b\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\x18\x18\x18\x18\xd8\xd8\xd8\x70\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x18\x00\x7e\x00\x18\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x76\xdc\x00\x76\xdc\x00\x00\x00\x00\x00\x00" + b"\x00\x38\x6c\x6c\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x18\x18\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x0f\x0c\x0c\x0c\x0c\x0c\xec\x6c\x6c\x3c\x1c\x00\x00\x00\x00" + b"\x00\x6c\x36\x36\x36\x36\x36\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x3c\x66\x0c\x18\x32\x7e\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x7e\x7e\x7e\x7e\x7e\x7e\x7e\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +) +FONT = memoryview(_FONT) diff --git a/lib/graphics/_font_8x8.py b/lib/graphics/_font_8x8.py new file mode 100644 index 0000000..8b3bef2 --- /dev/null +++ b/lib/graphics/_font_8x8.py @@ -0,0 +1,259 @@ +_FONT = ( + b"\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x7e\x81\xa5\x81\xbd\x99\x81\x7e" + b"\x7e\xff\xdb\xff\xc3\xe7\xff\x7e" + b"\x6c\xfe\xfe\xfe\x7c\x38\x10\x00" + b"\x10\x38\x7c\xfe\x7c\x38\x10\x00" + b"\x38\x7c\x38\xfe\xfe\xd6\x10\x38" + b"\x10\x38\x7c\xfe\xfe\x7c\x10\x38" + b"\x00\x00\x18\x3c\x3c\x18\x00\x00" + b"\xff\xff\xe7\xc3\xc3\xe7\xff\xff" + b"\x00\x3c\x66\x42\x42\x66\x3c\x00" + b"\xff\xc3\x99\xbd\xbd\x99\xc3\xff" + b"\x0f\x07\x0f\x7d\xcc\xcc\xcc\x78" + b"\x3c\x66\x66\x66\x3c\x18\x7e\x18" + b"\x3f\x33\x3f\x30\x30\x70\xf0\xe0" + b"\x7f\x63\x7f\x63\x63\x67\xe6\xc0" + b"\x18\xdb\x3c\xe7\xe7\x3c\xdb\x18" + b"\x80\xe0\xf8\xfe\xf8\xe0\x80\x00" + b"\x02\x0e\x3e\xfe\x3e\x0e\x02\x00" + b"\x18\x3c\x7e\x18\x18\x7e\x3c\x18" + b"\x66\x66\x66\x66\x66\x00\x66\x00" + b"\x7f\xdb\xdb\x7b\x1b\x1b\x1b\x00" + b"\x3e\x61\x3c\x66\x66\x3c\x86\x7c" + b"\x00\x00\x00\x00\x7e\x7e\x7e\x00" + b"\x18\x3c\x7e\x18\x7e\x3c\x18\xff" + b"\x18\x3c\x7e\x18\x18\x18\x18\x00" + b"\x18\x18\x18\x18\x7e\x3c\x18\x00" + b"\x00\x18\x0c\xfe\x0c\x18\x00\x00" + b"\x00\x30\x60\xfe\x60\x30\x00\x00" + b"\x00\x00\xc0\xc0\xc0\xfe\x00\x00" + b"\x00\x24\x66\xff\x66\x24\x00\x00" + b"\x00\x18\x3c\x7e\xff\xff\x00\x00" + b"\x00\xff\xff\x7e\x3c\x18\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x18\x3c\x3c\x18\x18\x00\x18\x00" + b"\x66\x66\x24\x00\x00\x00\x00\x00" + b"\x6c\x6c\xfe\x6c\xfe\x6c\x6c\x00" + b"\x18\x3e\x60\x3c\x06\x7c\x18\x00" + b"\x00\xc6\xcc\x18\x30\x66\xc6\x00" + b"\x38\x6c\x38\x76\xdc\xcc\x76\x00" + b"\x18\x18\x30\x00\x00\x00\x00\x00" + b"\x0c\x18\x30\x30\x30\x18\x0c\x00" + b"\x30\x18\x0c\x0c\x0c\x18\x30\x00" + b"\x00\x66\x3c\xff\x3c\x66\x00\x00" + b"\x00\x18\x18\x7e\x18\x18\x00\x00" + b"\x00\x00\x00\x00\x00\x18\x18\x30" + b"\x00\x00\x00\x7e\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x18\x18\x00" + b"\x06\x0c\x18\x30\x60\xc0\x80\x00" + b"\x38\x6c\xc6\xd6\xc6\x6c\x38\x00" + b"\x18\x38\x18\x18\x18\x18\x7e\x00" + b"\x7c\xc6\x06\x1c\x30\x66\xfe\x00" + b"\x7c\xc6\x06\x3c\x06\xc6\x7c\x00" + b"\x1c\x3c\x6c\xcc\xfe\x0c\x1e\x00" + b"\xfe\xc0\xc0\xfc\x06\xc6\x7c\x00" + b"\x38\x60\xc0\xfc\xc6\xc6\x7c\x00" + b"\xfe\xc6\x0c\x18\x30\x30\x30\x00" + b"\x7c\xc6\xc6\x7c\xc6\xc6\x7c\x00" + b"\x7c\xc6\xc6\x7e\x06\x0c\x78\x00" + b"\x00\x18\x18\x00\x00\x18\x18\x00" + b"\x00\x18\x18\x00\x00\x18\x18\x30" + b"\x06\x0c\x18\x30\x18\x0c\x06\x00" + b"\x00\x00\x7e\x00\x00\x7e\x00\x00" + b"\x60\x30\x18\x0c\x18\x30\x60\x00" + b"\x7c\xc6\x0c\x18\x18\x00\x18\x00" + b"\x7c\xc6\xde\xde\xde\xc0\x78\x00" + b"\x38\x6c\xc6\xfe\xc6\xc6\xc6\x00" + b"\xfc\x66\x66\x7c\x66\x66\xfc\x00" + b"\x3c\x66\xc0\xc0\xc0\x66\x3c\x00" + b"\xf8\x6c\x66\x66\x66\x6c\xf8\x00" + b"\xfe\x62\x68\x78\x68\x62\xfe\x00" + b"\xfe\x62\x68\x78\x68\x60\xf0\x00" + b"\x3c\x66\xc0\xc0\xce\x66\x3a\x00" + b"\xc6\xc6\xc6\xfe\xc6\xc6\xc6\x00" + b"\x3c\x18\x18\x18\x18\x18\x3c\x00" + b"\x1e\x0c\x0c\x0c\xcc\xcc\x78\x00" + b"\xe6\x66\x6c\x78\x6c\x66\xe6\x00" + b"\xf0\x60\x60\x60\x62\x66\xfe\x00" + b"\xc6\xee\xfe\xfe\xd6\xc6\xc6\x00" + b"\xc6\xe6\xf6\xde\xce\xc6\xc6\x00" + b"\x7c\xc6\xc6\xc6\xc6\xc6\x7c\x00" + b"\xfc\x66\x66\x7c\x60\x60\xf0\x00" + b"\x7c\xc6\xc6\xc6\xc6\xce\x7c\x0e" + b"\xfc\x66\x66\x7c\x6c\x66\xe6\x00" + b"\x3c\x66\x30\x18\x0c\x66\x3c\x00" + b"\x7e\x7e\x5a\x18\x18\x18\x3c\x00" + b"\xc6\xc6\xc6\xc6\xc6\xc6\x7c\x00" + b"\xc6\xc6\xc6\xc6\xc6\x6c\x38\x00" + b"\xc6\xc6\xc6\xd6\xd6\xfe\x6c\x00" + b"\xc6\xc6\x6c\x38\x6c\xc6\xc6\x00" + b"\x66\x66\x66\x3c\x18\x18\x3c\x00" + b"\xfe\xc6\x8c\x18\x32\x66\xfe\x00" + b"\x3c\x30\x30\x30\x30\x30\x3c\x00" + b"\xc0\x60\x30\x18\x0c\x06\x02\x00" + b"\x3c\x0c\x0c\x0c\x0c\x0c\x3c\x00" + b"\x10\x38\x6c\xc6\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\xff" + b"\x30\x18\x0c\x00\x00\x00\x00\x00" + b"\x00\x00\x78\x0c\x7c\xcc\x76\x00" + b"\xe0\x60\x7c\x66\x66\x66\xdc\x00" + b"\x00\x00\x7c\xc6\xc0\xc6\x7c\x00" + b"\x1c\x0c\x7c\xcc\xcc\xcc\x76\x00" + b"\x00\x00\x7c\xc6\xfe\xc0\x7c\x00" + b"\x3c\x66\x60\xf8\x60\x60\xf0\x00" + b"\x00\x00\x76\xcc\xcc\x7c\x0c\xf8" + b"\xe0\x60\x6c\x76\x66\x66\xe6\x00" + b"\x18\x00\x38\x18\x18\x18\x3c\x00" + b"\x06\x00\x06\x06\x06\x66\x66\x3c" + b"\xe0\x60\x66\x6c\x78\x6c\xe6\x00" + b"\x38\x18\x18\x18\x18\x18\x3c\x00" + b"\x00\x00\xec\xfe\xd6\xd6\xd6\x00" + b"\x00\x00\xdc\x66\x66\x66\x66\x00" + b"\x00\x00\x7c\xc6\xc6\xc6\x7c\x00" + b"\x00\x00\xdc\x66\x66\x7c\x60\xf0" + b"\x00\x00\x76\xcc\xcc\x7c\x0c\x1e" + b"\x00\x00\xdc\x76\x60\x60\xf0\x00" + b"\x00\x00\x7e\xc0\x7c\x06\xfc\x00" + b"\x30\x30\xfc\x30\x30\x36\x1c\x00" + b"\x00\x00\xcc\xcc\xcc\xcc\x76\x00" + b"\x00\x00\xc6\xc6\xc6\x6c\x38\x00" + b"\x00\x00\xc6\xd6\xd6\xfe\x6c\x00" + b"\x00\x00\xc6\x6c\x38\x6c\xc6\x00" + b"\x00\x00\xc6\xc6\xc6\x7e\x06\xfc" + b"\x00\x00\x7e\x4c\x18\x32\x7e\x00" + b"\x0e\x18\x18\x70\x18\x18\x0e\x00" + b"\x18\x18\x18\x18\x18\x18\x18\x00" + b"\x70\x18\x18\x0e\x18\x18\x70\x00" + b"\x76\xdc\x00\x00\x00\x00\x00\x00" + b"\x00\x10\x38\x6c\xc6\xc6\xfe\x00" + b"\x7c\xc6\xc0\xc0\xc6\x7c\x0c\x78" + b"\xcc\x00\xcc\xcc\xcc\xcc\x76\x00" + b"\x0c\x18\x7c\xc6\xfe\xc0\x7c\x00" + b"\x7c\x82\x78\x0c\x7c\xcc\x76\x00" + b"\xc6\x00\x78\x0c\x7c\xcc\x76\x00" + b"\x30\x18\x78\x0c\x7c\xcc\x76\x00" + b"\x30\x30\x78\x0c\x7c\xcc\x76\x00" + b"\x00\x00\x7e\xc0\xc0\x7e\x0c\x38" + b"\x7c\x82\x7c\xc6\xfe\xc0\x7c\x00" + b"\xc6\x00\x7c\xc6\xfe\xc0\x7c\x00" + b"\x30\x18\x7c\xc6\xfe\xc0\x7c\x00" + b"\x66\x00\x38\x18\x18\x18\x3c\x00" + b"\x7c\x82\x38\x18\x18\x18\x3c\x00" + b"\x30\x18\x00\x38\x18\x18\x3c\x00" + b"\xc6\x38\x6c\xc6\xfe\xc6\xc6\x00" + b"\x38\x6c\x7c\xc6\xfe\xc6\xc6\x00" + b"\x18\x30\xfe\xc0\xf8\xc0\xfe\x00" + b"\x00\x00\x7e\x18\x7e\xd8\x7e\x00" + b"\x3e\x6c\xcc\xfe\xcc\xcc\xce\x00" + b"\x7c\x82\x7c\xc6\xc6\xc6\x7c\x00" + b"\xc6\x00\x7c\xc6\xc6\xc6\x7c\x00" + b"\x30\x18\x7c\xc6\xc6\xc6\x7c\x00" + b"\x78\x84\x00\xcc\xcc\xcc\x76\x00" + b"\x60\x30\xcc\xcc\xcc\xcc\x76\x00" + b"\xc6\x00\xc6\xc6\xc6\x7e\x06\xfc" + b"\xc6\x38\x6c\xc6\xc6\x6c\x38\x00" + b"\xc6\x00\xc6\xc6\xc6\xc6\x7c\x00" + b"\x18\x18\x7e\xc0\xc0\x7e\x18\x18" + b"\x38\x6c\x64\xf0\x60\x66\xfc\x00" + b"\x66\x66\x3c\x7e\x18\x7e\x18\x18" + b"\xf8\xcc\xcc\xfa\xc6\xcf\xc6\xc7" + b"\x0e\x1b\x18\x3c\x18\xd8\x70\x00" + b"\x18\x30\x78\x0c\x7c\xcc\x76\x00" + b"\x0c\x18\x00\x38\x18\x18\x3c\x00" + b"\x0c\x18\x7c\xc6\xc6\xc6\x7c\x00" + b"\x18\x30\xcc\xcc\xcc\xcc\x76\x00" + b"\x76\xdc\x00\xdc\x66\x66\x66\x00" + b"\x76\xdc\x00\xe6\xf6\xde\xce\x00" + b"\x3c\x6c\x6c\x3e\x00\x7e\x00\x00" + b"\x38\x6c\x6c\x38\x00\x7c\x00\x00" + b"\x18\x00\x18\x18\x30\x63\x3e\x00" + b"\x00\x00\x00\xfe\xc0\xc0\x00\x00" + b"\x00\x00\x00\xfe\x06\x06\x00\x00" + b"\x63\xe6\x6c\x7e\x33\x66\xcc\x0f" + b"\x63\xe6\x6c\x7a\x36\x6a\xdf\x06" + b"\x18\x00\x18\x18\x3c\x3c\x18\x00" + b"\x00\x33\x66\xcc\x66\x33\x00\x00" + b"\x00\xcc\x66\x33\x66\xcc\x00\x00" + b"\x22\x88\x22\x88\x22\x88\x22\x88" + b"\x55\xaa\x55\xaa\x55\xaa\x55\xaa" + b"\x77\xdd\x77\xdd\x77\xdd\x77\xdd" + b"\x18\x18\x18\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\xf8\x18\x18\x18" + b"\x18\x18\xf8\x18\xf8\x18\x18\x18" + b"\x36\x36\x36\x36\xf6\x36\x36\x36" + b"\x00\x00\x00\x00\xfe\x36\x36\x36" + b"\x00\x00\xf8\x18\xf8\x18\x18\x18" + b"\x36\x36\xf6\x06\xf6\x36\x36\x36" + b"\x36\x36\x36\x36\x36\x36\x36\x36" + b"\x00\x00\xfe\x06\xf6\x36\x36\x36" + b"\x36\x36\xf6\x06\xfe\x00\x00\x00" + b"\x36\x36\x36\x36\xfe\x00\x00\x00" + b"\x18\x18\xf8\x18\xf8\x00\x00\x00" + b"\x00\x00\x00\x00\xf8\x18\x18\x18" + b"\x18\x18\x18\x18\x1f\x00\x00\x00" + b"\x18\x18\x18\x18\xff\x00\x00\x00" + b"\x00\x00\x00\x00\xff\x18\x18\x18" + b"\x18\x18\x18\x18\x1f\x18\x18\x18" + b"\x00\x00\x00\x00\xff\x00\x00\x00" + b"\x18\x18\x18\x18\xff\x18\x18\x18" + b"\x18\x18\x1f\x18\x1f\x18\x18\x18" + b"\x36\x36\x36\x36\x37\x36\x36\x36" + b"\x36\x36\x37\x30\x3f\x00\x00\x00" + b"\x00\x00\x3f\x30\x37\x36\x36\x36" + b"\x36\x36\xf7\x00\xff\x00\x00\x00" + b"\x00\x00\xff\x00\xf7\x36\x36\x36" + b"\x36\x36\x37\x30\x37\x36\x36\x36" + b"\x00\x00\xff\x00\xff\x00\x00\x00" + b"\x36\x36\xf7\x00\xf7\x36\x36\x36" + b"\x18\x18\xff\x00\xff\x00\x00\x00" + b"\x36\x36\x36\x36\xff\x00\x00\x00" + b"\x00\x00\xff\x00\xff\x18\x18\x18" + b"\x00\x00\x00\x00\xff\x36\x36\x36" + b"\x36\x36\x36\x36\x3f\x00\x00\x00" + b"\x18\x18\x1f\x18\x1f\x00\x00\x00" + b"\x00\x00\x1f\x18\x1f\x18\x18\x18" + b"\x00\x00\x00\x00\x3f\x36\x36\x36" + b"\x36\x36\x36\x36\xff\x36\x36\x36" + b"\x18\x18\xff\x18\xff\x18\x18\x18" + b"\x18\x18\x18\x18\xf8\x00\x00\x00" + b"\x00\x00\x00\x00\x1f\x18\x18\x18" + b"\xff\xff\xff\xff\xff\xff\xff\xff" + b"\x00\x00\x00\x00\xff\xff\xff\xff" + b"\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0" + b"\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f" + b"\xff\xff\xff\xff\x00\x00\x00\x00" + b"\x00\x00\x76\xdc\xc8\xdc\x76\x00" + b"\x78\xcc\xcc\xd8\xcc\xc6\xcc\x00" + b"\xfe\xc6\xc0\xc0\xc0\xc0\xc0\x00" + b"\x00\x00\xfe\x6c\x6c\x6c\x6c\x00" + b"\xfe\xc6\x60\x30\x60\xc6\xfe\x00" + b"\x00\x00\x7e\xd8\xd8\xd8\x70\x00" + b"\x00\x00\x66\x66\x66\x66\x7c\xc0" + b"\x00\x76\xdc\x18\x18\x18\x18\x00" + b"\x7e\x18\x3c\x66\x66\x3c\x18\x7e" + b"\x38\x6c\xc6\xfe\xc6\x6c\x38\x00" + b"\x38\x6c\xc6\xc6\x6c\x6c\xee\x00" + b"\x0e\x18\x0c\x3e\x66\x66\x3c\x00" + b"\x00\x00\x7e\xdb\xdb\x7e\x00\x00" + b"\x06\x0c\x7e\xdb\xdb\x7e\x60\xc0" + b"\x1e\x30\x60\x7e\x60\x30\x1e\x00" + b"\x00\x7c\xc6\xc6\xc6\xc6\xc6\x00" + b"\x00\xfe\x00\xfe\x00\xfe\x00\x00" + b"\x18\x18\x7e\x18\x18\x00\x7e\x00" + b"\x30\x18\x0c\x18\x30\x00\x7e\x00" + b"\x0c\x18\x30\x18\x0c\x00\x7e\x00" + b"\x0e\x1b\x1b\x18\x18\x18\x18\x18" + b"\x18\x18\x18\x18\x18\xd8\xd8\x70" + b"\x00\x18\x00\x7e\x00\x18\x00\x00" + b"\x00\x76\xdc\x00\x76\xdc\x00\x00" + b"\x38\x6c\x6c\x38\x00\x00\x00\x00" + b"\x00\x00\x00\x18\x18\x00\x00\x00" + b"\x00\x00\x00\x18\x00\x00\x00\x00" + b"\x0f\x0c\x0c\x0c\xec\x6c\x3c\x1c" + b"\x6c\x36\x36\x36\x36\x00\x00\x00" + b"\x78\x0c\x18\x30\x7c\x00\x00\x00" + b"\x00\x00\x3c\x3c\x3c\x3c\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00" +) +FONT = memoryview(_FONT) diff --git a/lib/graphics/_framebuf_plus.py b/lib/graphics/_framebuf_plus.py new file mode 100644 index 0000000..7db6627 --- /dev/null +++ b/lib/graphics/_framebuf_plus.py @@ -0,0 +1,644 @@ +from . import _files, _font, _shapes +from ._area import Area +from ._blit_hooks import clip_blit_bounds + +try: + # Local submodule: present in TestPyPI wheels and after scripts/install_sync_framebuf.py runs. + from .framebuf import ( + GS2_HMSB, + GS4_HMSB, + GS8, + MONO_HLSB, + MONO_HMSB, + MONO_VLSB, + RGB565, + ) + from .framebuf import FrameBuffer as _FrameBuffer +except ImportError: + # graphics/framebuf.py is a generated packaging artifact (gitignored) not present + # in all deploy paths (PyScript, mip-install from GitHub, bare CP/MP without wheel). + # Fall back to the bare framebuf module: native C on MicroPython hardware, + # or add_ons/framebuf.py when add_ons/ is on sys.path (PyScript, CircuitPython, etc.). + from framebuf import ( # type: ignore[no-redef] + GS2_HMSB, + GS4_HMSB, + GS8, + MONO_HLSB, + MONO_HMSB, + MONO_VLSB, + RGB565, + ) + from framebuf import FrameBuffer as _FrameBuffer # type: ignore[no-redef] + +# pydisplay extension — not in MicroPython framebuf +RGB888 = 7 + + +class _RGB888Format: + depth = 24 + + @staticmethod + def set_pixel(framebuf, x, y, color): + index = (y * framebuf._stride + x) * 3 + framebuf._buffer[index : index + 3] = bytes( + ((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF) + ) + + @staticmethod + def get_pixel(framebuf, x, y): + index = (y * framebuf._stride + x) * 3 + r, g, b = framebuf._buffer[index : index + 3] + return (r << 16) | (g << 8) | b + + @staticmethod + def fill(framebuf, color): + rgb = bytes(((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF)) + for i in range(0, len(framebuf._buffer), 3): + framebuf._buffer[i : i + 3] = rgb + + @staticmethod + def fill_rect(framebuf, x, y, width, height, color): + rgb = bytes(((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF)) + stride = framebuf._stride + for _y in range(y, y + height): + row = _y * stride + for _x in range(x, x + width): + index = (row + _x) * 3 + framebuf._buffer[index : index + 3] = rgb + + +class FrameBuffer(_FrameBuffer): + """ + An extension of MicroPython's framebuf.FrameBuffer that adds some useful methods for drawing shapes and text. + Each method returns a bounding box (x, y, w, h) of the drawn shape to indicate + the area of the display that was modified. This can be used to update only the + modified area of the display. Exposes attributes not exposed in the base class, such + as color_depth, width, height, buffer, and format. Also adds a save method to save + the framebuffer to a file, and a from_file method to load a framebuffer from a file. + + Inherits from the bundled pure-Python ``.framebuf.FrameBuffer`` (never the + compiled native ``framebuf`` module, to guarantee identical behavior across + runtimes). Methods should return an Area object, but the MicroPython + framebuf module returns None, so the methods inherited from + framebuf.FrameBuffer are overridden to return an Area object. + + Args: + buffer (bytearray): Framebuffer buffer + width (int): Width in pixels + height (int): Height in pixels + format (int): Framebuffer format + + Attributes: + buffer (bytearray): Framebuffer buffer + width (int): Width in pixels + height (int): Height in pixels + format (int): Framebuffer format + color_depth (int): Color depth + """ + + def __init__(self, buffer, width, height, format, *args, **kwargs): + self._rgb888 = format == RGB888 + # RGB888 is a pydisplay extension; base framebuf never implements it. + # Initialize the C extmod base as RGB565 so MicroPython does not fall back + # to make_new with an invalid format. Pixel ops use _RGB888Format below. + if self._rgb888: + super().__init__(buffer, width, height, RGB565) + else: + super().__init__(buffer, width, height, format, *args, **kwargs) + self._width = width + self._height = height + self._fb_format = format + self._buffer = buffer + if self._rgb888: + stride = args[0] if args else kwargs.get("stride") + self._stride = stride if stride is not None else width + self._color_depth = 24 + elif format in (MONO_VLSB, MONO_HLSB, MONO_HMSB): + self._color_depth = 1 + elif format == RGB565: + self._color_depth = 16 + elif format == GS2_HMSB: + self._color_depth = 2 + elif format == GS4_HMSB: + self._color_depth = 4 + elif format == GS8: + self._color_depth = 8 + else: + raise ValueError("invalid format") + + @property + def color_depth(self): + return self._color_depth + + @property + def width(self): + return self._width + + @property + def height(self): + return self._height + + @property + def buffer(self): + return self._buffer + + @property + def format(self): + return self._fb_format + + def fill_rect(self, x, y, w, h, c): + """ + Fill the given rectangle with the given color. + + Args: + x (int): x coordinate + y (int): y coordinate + w (int): Width in pixels + h (int): Height in pixels + c (int): color + + Returns: + (Area): Bounding box of the filled rectangle + """ + if self._rgb888: + _RGB888Format.fill_rect(self, x, y, w, h, c) + return Area(x, y, w, h) + super().fill_rect(x, y, w, h, c) + return Area(x, y, w, h) + + def pixel(self, x, y, c=None): + """ + Draw a single pixel at the given location and color. + + Args: + x (int): x coordinate + y (int): y coordinate + c (int): color (default: None) + + Returns: + (Area): Bounding box of the pixel + """ + if self._rgb888: + if c is None: + return _RGB888Format.get_pixel(self, x, y) + _RGB888Format.set_pixel(self, x, y, c) + return Area(x, y, 1, 1) + if c is None: + return super().pixel(x, y) + super().pixel(x, y, c) + return Area(x, y, 1, 1) + + def fill(self, c): + """ + Fill the buffer with the given color. + + Args: + c (int): color + + Returns: + (Area): Bounding box of the filled buffer + """ + if self._rgb888: + _RGB888Format.fill(self, c) + return Area(0, 0, self.width, self.height) + super().fill(c) + return Area(0, 0, self.width, self.height) + + def ellipse(self, x, y, rx, ry, c, f=False, m=0b1111): + """ + Draw an ellipse at the given location, radii and color. + + Args: + x (int): Center x coordinate + y (int): Center y coordinate + rx (int): X radius + ry (int): Y radius + c (int): color + f (bool): Fill the ellipse (default: False) + m (int): Bitmask to determine which quadrants to draw (default: 0b1111) + + Returns: + (Area): Bounding box of the ellipse + """ + super().ellipse(x, y, rx, ry, c, f, m) + return Area(x - rx, y - ry, 2 * rx, 2 * ry) + + def hline(self, x, y, w, c): + """ + Draw a horizontal line at the given location, width and color. + + Args: + x (int): x coordinate + y (int): y coordinate + w (int): Width in pixels + c (int): color + + Returns: + (Area): Bounding box of the horizontal line + """ + super().hline(x, y, w, c) + return Area(x, y, w, 1) + + def line(self, x1, y1, x2, y2, c): + """ + Draw a line between the given start and end points and color. + + Args: + x1 (int): Start x coordinate + y1 (int): Start y coordinate + x2 (int): End x coordinate + y2 (int): End y coordinate + c (int): color + + Returns: + (Area): Bounding box of the line + """ + super().line(x1, y1, x2, y2, c) + return Area(min(x1, x2), min(y1, y2), abs(x2 - x1) + 1, abs(y2 - y1) + 1) + + def poly(self, x, y, coords, c, f=False): + """ + Draw a polygon at the given location, coordinates and color. + + Args: + x (int): x coordinate + y (int): y coordinate + coords (array): Array of x, y coordinate tuples + c (int): color + f (bool): Fill the polygon (default: False) + + Returns: + (Area): Bounding box of the polygon + """ + super().poly(x, y, coords, c, f) + # Calculate the bounding box of the polygon + # Convert the coords to a list of x, y tuples if it is not already + if isinstance(coords, list): + vertices = coords + elif isinstance(coords, tuple): + vertices = list(coords) + else: + # Check that the coords array has an even number of elements + if len(coords) % 2 != 0: + raise ValueError("coords must have an even number of elements") + vertices = [(coords[i], coords[i + 1]) for i in range(0, len(coords), 2)] + # Find the min and max x and y values + min_x = min([v[0] for v in vertices]) + min_y = min([v[1] for v in vertices]) + max_x = max([v[0] for v in vertices]) + max_y = max([v[1] for v in vertices]) + return Area(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1) + + def rect(self, x, y, w, h, c, f=False): + """ + Draw a rectangle at the given location, size and color. + + Args: + x (int): Top left corner x coordinate + y (int): Top left corner y coordinate + w (int): Width in pixels + h (int): Height in pixels + c (int): color + f (bool): Fill the rectangle (default: False) + + Returns: + (Area): Bounding box of the rectangle + """ + super().rect(x, y, w, h, c, f) + return Area(x, y, w, h) + + def vline(self, x, y, h, c): + """ + Draw a vertical line at the given location, height and color. + + Args: + x (int): x coordinate + y (int): y coordinate + h (int): Height in pixels + c (int): color + + Returns: + (Area): Bounding box of the vertical line + """ + super().vline(x, y, h, c) + return Area(x, y, 1, h) + + def text(self, s, x, y, c=1, scale=1, inverted=False, font_data=None, height=8): + """ + Draw text at the given location, using the given font and color. + + Args: + s (str): Text to draw + x (int): x coordinate + y (int): y coordinate + c (int): color + scale (int): Scale factor (default: 1) + inverted (bool): Invert the text (default: False) + font_data (str): Path to the font file (default: None) + height (int): Height of the font (default: 8) + + Returns: + (Area): Bounding box of the text + """ + return _font.text( + self, s, x, y, c, scale=scale, inverted=inverted, font_data=font_data, height=height + ) + + def blit(self, source, x, y, key=-1, palette=None): + """ + Blit the given buffer at the given location. + + Args: + source (FrameBuffer): FrameBuffer to blit + x (int): x coordinate + y (int): y coordinate + key (int): Color key (default: -1) + palette (FrameBuffer): Palette (default: None) + + Returns: + (Area): Bounding box of the blitted buffer + """ + super().blit(source, x, y, key, palette) + clipped = clip_blit_bounds(self, source, x, y) + if clipped is None: + return None + x0, y0, w, h, _, _ = clipped + return Area(x0, y0, w, h) + + ########### Additional methods + + def arc(self, x, y, r, a0, a1, c): + """ + Arc drawing function. Will draw a single pixel wide arc with a radius r + centered at x, y from a0 to a1. + + Args: + x (int): X-coordinate of the arc's center. + y (int): Y-coordinate of the arc's center. + r (int): Radius of the arc. + a0 (float): Starting angle in degrees. + a1 (float): Ending angle in degrees. + c (int): color. + + Returns: + (Area): The bounding box of the arc. + """ + return _shapes.arc(self, x, y, r, a0, a1, c) + + def blit_rect(self, buf, x, y, w, h): + """ + Blit a rectangular area from a buffer to the canvas. Uses the canvas's blit_rect method if available, + otherwise writes directly to the buffer. + + Args: + buf (memoryview): Buffer to blit. Must already be byte-swapped if necessary. + x (int): X-coordinate to blit to. + y (int): Y-coordinate to blit to. + w (int): Width of the area to blit. + h (int): Height of the area to blit. + + Returns: + (Area): The bounding box of the blitted area. + """ + return _shapes.blit_rect(self, buf, x, y, w, h) + + def blit_transparent(self, buf, x, y, w, h, key): + """ + Blit a buffer with transparency. + + Args: + buf (memoryview): Buffer to blit. + x (int): X-coordinate to blit to. + y (int): Y-coordinate to blit to. + w (int): Width of the area to blit. + h (int): Height of the area to blit. + key (int): Key value for transparency. + + Returns: + (Area): The bounding box of the blitted area. + """ + return _shapes.blit_transparent(self, buf, x, y, w, h, key) + + def circle(self, x0, y0, r, c, f=False): + """ + Circle drawing function. Will draw a single pixel wide circle + centered at x0, y0 and the specified r. + + Args: + x0 (int): Center x coordinate + y0 (int): Center y coordinate + r (int): Radius + c (int): Color + f (bool): Fill the circle (default: False) + + Returns: + (Area): The bounding box of the circle. + """ + return _shapes.circle(self, x0, y0, r, c, f) + + def gradient_rect(self, x, y, w, h, c1, c2=None, vertical=True): + """ + Fill a rectangle with a gradient. + + Args: + x (int): X-coordinate of the top-left corner of the rectangle. + y (int): Y-coordinate of the top-left corner of the rectangle. + w (int): Width of the rectangle. + h (int): Height of the rectangle. + c1 (int): 565 encoded color for the top or left edge. + c2 (int): 565 encoded color for the bottom or right edge. If None or the same as c1, + fill_rect will be called instead. + vertical (bool): If True, the gradient will be vertical. If False, the gradient will be horizontal. + + Returns: + (Area): The bounding box of the filled area. + """ + return _shapes.gradient_rect(self, x, y, w, h, c1, c2, vertical) + + def polygon(self, points, x, y, color, angle=0, center_x=0, center_y=0): + """ + Draw a polygon on the canvas. + + Args: + points (list): List of points to draw. + x (int): X-coordinate of the polygon's position. + y (int): Y-coordinate of the polygon's position. + color (int): color. + angle (float): Rotation angle in radians (default: 0). + center_x (int): X-coordinate of the rotation center (default: 0). + center_y (int): Y-coordinate of the rotation center (default: 0). + + Raises: + ValueError: If the polygon has less than 3 points. + + Returns: + (Area): The bounding box of the polygon. + """ + return _shapes.polygon(self, points, x, y, color, angle, center_x, center_y) + + def round_rect(self, x0, y0, w, h, r, c, f=False): + """ + Rounded rectangle drawing function. Will draw a single pixel wide rounded rectangle starting at + x0, y0 and extending w, h pixels with the specified radius. + + Args: + x0 (int): X-coordinate of the top-left corner of the rectangle. + y0 (int): Y-coordinate of the top-left corner of the rectangle. + w (int): Width of the rectangle. + h (int): Height of the rectangle. + r (int): Radius of the corners. + c (int): color. + f (bool): Fill the rectangle (default: False). + + Returns: + (Area): The bounding box of the rectangle. + """ + return _shapes.round_rect(self, x0, y0, w, h, r, c, f) + + def triangle(self, x0, y0, x1, y1, x2, y2, c, f=False): + """ + Triangle drawing function. Draws a single pixel wide triangle with vertices at + (x0, y0), (x1, y1), and (x2, y2). + + Args: + x0 (int): X-coordinate of the first vertex. + y0 (int): Y-coordinate of the first vertex. + x1 (int): X-coordinate of the second vertex. + y1 (int): Y-coordinate of the second vertex. + x2 (int): X-coordinate of the third vertex. + y2 (int): Y-coordinate of the third vertex. + c (int): color. + f (bool): Fill the triangle (default: False). + + Returns: + (Area): The bounding box of the triangle. + """ + return _shapes.triangle(self, x0, y0, x1, y1, x2, y2, c, f) + + def text8(self, s, x, y, c=1, scale=1, inverted=False, font_data=None): + """ + Place text on the canvas with an 8 pixel high font. + Breaks on \n to next line. Does not break on line going off canvas. + + Args: + s (str): The text to draw. + x (int): The x position to start drawing the text. + y (int): The y position to start drawing the text. + c (int): The color to draw the text in. Default is 1. + scale (int): The scale factor to draw the text at. Default is 1. + inverted (bool): If True, draw the text inverted. Default is False. + font_data (str): The path to the font file to use. Default is None. + + Returns: + Area: The area that was drawn to. + """ + return _font.text8(self, s, x, y, c, scale, inverted, font_data) + + def text14(self, s, x, y, c=1, scale=1, inverted=False, font_data=None): + """ + Place text on the canvas with a 14 pixel high font. + Breaks on \n to next line. Does not break on line going off canvas. + + Args: + s (str): The text to draw. + x (int): The x position to start drawing the text. + y (int): The y position to start drawing the text. + c (int): The color to draw the text in. Default is 1. + scale (int): The scale factor to draw the text at. Default is 1. + inverted (bool): If True, draw the text inverted. Default is False. + font_data (str): The path to the font file to use. Default is None. + + Returns: + Area: The area that was drawn to. + """ + return _font.text14(self, s, x, y, c, scale, inverted, font_data) + + def text16(self, s, x, y, c=1, scale=1, inverted=False, font_data=None): + """ + Place text on the canvas with a 16 pixel high font. + Breaks on \n to next line. Does not break on line going off canvas. + + Args: + s (str): The text to draw. + x (int): The x position to start drawing the text. + y (int): The y position to start drawing the text. + c (int): The color to draw the text in. Default is 1. + scale (int): The scale factor to draw the text at. Default is 1. + inverted (bool): If True, draw the text inverted. Default is False. + font_data (str): The path to the font file to use. Default is None. + + Returns: + Area: The area that was drawn to. + """ + return _font.text16(self, s, x, y, c, scale, inverted, font_data) + + def scroll(self, xstep, ystep): + """Scroll buffer contents. Returns the full buffer bounds.""" + super().scroll(xstep, ystep) + return Area(0, 0, self.width, self.height) + + def save(self, filename=None): + """ + Save the framebuffer to a file. The file extension must match the format, otherwise + the extension will be appended to the filename. + + Saves 1-bit formats as PBM, 2-bit formats as PGM with max value 3, 4-bit formats as PGM with max value 15, + 8-bit formats as PGM with max value 255, and 16-bit formats as BMP. + + Args: + filename (str): Filename to save to + """ + return _files.save_image(self, filename) + + def export(self, filename): + """Export this framebuffer as an importable ``.py`` bitmap module. + + See :func:`graphics._files.export_framebuffer`. Ships ``BITMAP`` as a + ``bytearray`` for zero-copy :meth:`from_bitmap` on MicroPython. + """ + return _files.export_framebuffer(self, filename) + + @staticmethod + def from_file(filename): + """ + Load a framebuffer from a file. + + Args: + filename (str): Filename to load from + """ + return _files.load_image(filename) + + @staticmethod + def from_bitmap(buffer, width, height, format): + """Build a ``FrameBuffer`` from a bitmap buffer (export-module style). + + If ``buffer`` is already a writable ``bytearray`` (or a writable + ``memoryview``), it is used without copying. Legacy ``bytes`` / + ``memoryview(bytes)`` modules get one ``bytearray`` copy so MicroPython + can own a writable pixel buffer. + """ + if isinstance(buffer, bytearray): + buf = buffer + elif isinstance(buffer, memoryview): + writable = False + try: + underlying = buffer.obj # CPython + writable = isinstance(underlying, bytearray) + except AttributeError: + # MicroPython: probe whether the view accepts assignment. + try: + if len(buffer): + tmp = buffer[0] + buffer[0] = tmp + writable = True + except (TypeError, AttributeError): + writable = False + buf = buffer if writable else bytearray(buffer) + else: + buf = bytearray(buffer) + return FrameBuffer(buf, width, height, format) + + @staticmethod + def from_module(mod): + """Load from a module with ``WIDTH``, ``HEIGHT``, ``FORMAT``, ``BITMAP``.""" + return FrameBuffer.from_bitmap(mod.BITMAP, mod.WIDTH, mod.HEIGHT, mod.FORMAT) diff --git a/lib/graphics/_shapes.py b/lib/graphics/_shapes.py new file mode 100644 index 0000000..a068803 --- /dev/null +++ b/lib/graphics/_shapes.py @@ -0,0 +1,888 @@ +# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries, 2024 Brad Barnett +# +# SPDX-License-Identifier: MIT +""" +`graphics._shapes` +==================================================== +Graphics primitives for drawing on a canvas. + +Heavily modified from gfx.py at: +https://github.com/adafruit/Adafruit_CircuitPython_GFX +* Author(s): Kattni Rembor, Tony DiCola, Jonah Yolles-Murphy, based on code by Phil Burgess + +Implementation Notes +-------------------- +.pixel(), .fill_rect(), .fill() and .blit_rect() will be called from the canvas object if the canvas +object has these methods. + +.pixel() and .blit_rect() assume 16-bit color depth. + +""" + +import math + +from ._area import Area +from ._blit_hooks import ( + blit_rect_dispatch, + canvas_accepts_blit_transparent, + try_fast_framebuffer_blit, +) + + +def arc(canvas, x, y, r, a0, a1, c): + """ + Arc drawing function. Will draw a single pixel wide arc with a radius r + centered at x, y from a0 to a1. + + Args: + x (int): X-coordinate of the arc's center. + y (int): Y-coordinate of the arc's center. + r (int): Radius of the arc. + a0 (float): Starting angle in degrees. + a1 (float): Ending angle in degrees. + c (int): color. + + Returns: + (Area): The bounding box of the arc. + """ + resolution = 60 + a0 = math.radians(a0) + a1 = math.radians(a1) + x0 = x + int(r * math.cos(a0)) + y0 = y + int(r * math.sin(a0)) + if a1 > a0: + arc_range = range(int(a0 * resolution), int(a1 * resolution)) + else: + arc_range = range(int(a0 * resolution), int(a1 * resolution), -1) + + x_min = x_max = x0 + y_min = y_max = y0 + for a in arc_range: + ar = a / resolution + x1 = x + int(r * math.cos(ar)) + y1 = y + int(r * math.sin(ar)) + line(canvas, x0, y0, x1, y1, c) + x_min = min(x0, x1, x_min) + x_max = max(x0, x1, x_max) + y_min = min(y0, y1, y_min) + y_max = max(y0, y1, y_max) + x0 = x1 + y0 = y1 + return Area(x_min, y_min, x_max - x_min, y_max - y_min) + + +def blit(canvas, source, x, y, key=-1, palette=None): + """ + Blit a source to the canvas at the specified x, y location. + + Args: + source (FrameBuffer): Source FrameBuffer object. + x (int): X-coordinate to blit to. + y (int): Y-coordinate to blit to. + key (int): Key value for transparency (default: -1). + palette (Palette): Palette object for color translation (default: None). + + Returns: + (Area): The bounding box of the blitted area. + """ + fast = try_fast_framebuffer_blit(canvas, source, x, y, key, palette) + if fast is not None: + return fast + + from ._blit_hooks import clip_blit_bounds + + clipped = clip_blit_bounds(canvas, source, x, y) + if clipped is None: + return None + + x0, y0, w, h, src_x, src_y = clipped + x0end = x0 + w + y0end = y0 + h + + for cy0 in range(y0, y0end): + cx1 = src_x + (cy0 - y0) + src_row = src_y + (cy0 - y0) + for cx0 in range(x0, x0end): + col = source.pixel(cx1, src_row) + if palette: + col = palette.pixel(col, 0) + if col != key: + pixel(canvas, cx0, cy0, col) + cx1 += 1 + return Area(x0, y0, w, h) + + +def blit_rect(canvas, buf, x, y, w, h): + """ + Blit a rectangular area from a buffer to the canvas. Uses the canvas's blit_rect method if available, + otherwise writes directly to the buffer. + + Args: + buf (memoryview): Buffer to blit. Must already be byte-swapped if necessary. + x (int): X-coordinate to blit to. + y (int): Y-coordinate to blit to. + w (int): Width of the area to blit. + h (int): Height of the area to blit. + + Returns: + (Area): The bounding box of the blitted area. + """ + return blit_rect_dispatch(canvas, buf, x, y, w, h) + + +def blit_transparent(canvas, buf, x, y, w, h, key): + """ + Blit a buffer with transparency. + + Args: + buf (memoryview): Buffer to blit. + x (int): X-coordinate to blit to. + y (int): Y-coordinate to blit to. + w (int): Width of the area to blit. + h (int): Height of the area to blit. + key (int): Key value for transparency. + + Returns: + (Area): The bounding box of the blitted area. + """ + if canvas_accepts_blit_transparent(canvas): + canvas.blit_transparent(buf, x, y, w, h, key) + return Area(x, y, w, h) + + BPP = canvas.color_depth // 8 + key_bytes = key.to_bytes(BPP, "little") + stride = w * BPP + for j in range(h): + rowstart = j * stride + colstart = 0 + # iterate over each pixel looking for the first non-key pixel + while colstart < stride: + startoffset = rowstart + colstart + if buf[startoffset : startoffset + BPP] != key_bytes: + # found a non-key pixel + # then iterate over each pixel looking for the next key pixel + colend = colstart + while colend < stride: + endoffset = rowstart + colend + if buf[endoffset : endoffset + BPP] == key_bytes: + break + colend += BPP + # blit the non-key pixels + blit_rect( + canvas, + buf[rowstart + colstart : rowstart + colend], + x + colstart // BPP, + y + j, + (colend - colstart) // BPP, + 1, + ) + colstart = colend + else: + colstart += BPP + return Area(x, y, w, h) + + +def circle(canvas, x0, y0, r, c, f=False): + """ + Circle drawing function. Will draw a single pixel wide circle + centered at x0, y0 and the specified r. + + Args: + x0 (int): Center x coordinate + y0 (int): Center y coordinate + r (int): Radius + c (int): Color + f (bool): Fill the circle (default: False) + + Returns: + (Area): The bounding box of the circle. + """ + if f: + return _fill_circle_helper(canvas, x0, y0, r, c, 0, 0) + + _circle_helper(canvas, x0, y0, r, c, 0, 0) + return Area(x0 - r, y0 - r, 2 * r, 2 * r) + + +def _circle_helper(canvas, x0, y0, r, c, x_offset, y_offset): + """ + Circle helper function. Draws the 4 quadrants of a circle with center at x0, y0 and the specified r + separated by the specified x_offset and y_offset. Draws a circle if offsets are 0. Draws the 4 corners of + a round_rect if an offset is greater than 0. + """ + f = 1 - r + ddF_x = 1 + ddF_y = -2 * r + x = 0 + y = r + while x < y: + if f >= 0: + y -= 1 + ddF_y += 2 + f += ddF_y + x += 1 + ddF_x += 2 + f += ddF_x + offset_x = x + x_offset + offset_y = y + y_offset + pixel(canvas, x0 + offset_x - 1, y0 - offset_y, c) # 90 to 45 + pixel(canvas, x0 - offset_x, y0 - offset_y, c) # 90 to 135 + pixel(canvas, x0 + offset_x - 1, y0 + offset_y - 1, c) # 270 to 315 + pixel(canvas, x0 - offset_x, y0 + offset_y - 1, c) # 270 to 225 + offset_x = y + x_offset + offset_y = x + y_offset + pixel(canvas, x0 + offset_x - 1, y0 + offset_y - 1, c) # 0 to 315 + pixel(canvas, x0 - offset_x, y0 + offset_y - 1, c) # 180 to 225 + pixel(canvas, x0 + offset_x - 1, y0 - offset_y, c) # 0 to 45 + pixel(canvas, x0 - offset_x, y0 - offset_y, c) # 180 to 135 + + +def _fill_circle_helper(canvas, x0, y0, r, c, x_offset, y_offset): + """ + Fill circle helper function. Draws the 4 quadrants of a filled circle with center at x0, y0 and the + specified r separated by the specified x_offset and y_offset. Fills a circle if offsets are 0. Fills the + 4 corners of a filled round_rect if an offset is greater than 0. + """ + # vline(canvas, x0, y0 - r, 2 * r + 1, c) + f = 1 - r + ddF_x = 1 + ddF_y = -2 * r + x = 0 + y = r + while x < y: + if f >= 0: + y -= 1 + ddF_y += 2 + f += ddF_y + x += 1 + ddF_x += 2 + f += ddF_x + offset_x = x + x_offset + offset_y = y + y_offset + vline(canvas, x0 - offset_x, y0 - offset_y, 2 * offset_y, c) + vline(canvas, x0 + offset_x - 1, y0 - offset_y, 2 * offset_y, c) + offset_x = y + x_offset + offset_y = x + y_offset + vline(canvas, x0 - offset_x, y0 - offset_y, 2 * offset_y, c) + vline(canvas, x0 + offset_x - 1, y0 - offset_y, 2 * offset_y, c) + + return Area(x0 - r, y0 - r, 2 * r, 2 * r) + + +def ellipse(canvas, x0, y0, r1, r2, c, f=False, m=0b1111, w=None, h=None): + """ + Midpoint ellipse algorithm + Draw an ellipse at the given location. Radii r1 and r2 define the geometry; equal values cause a + circle to be drawn. The c parameter defines the color. + + The optional f parameter can be set to True to fill the ellipse. Otherwise just a one pixel outline + is drawn. + + The optional m parameter enables drawing to be restricted to certain quadrants of the ellipse. + The LS four bits determine which quadrants are to be drawn, with bit 0 specifying Q1, b1 Q2, + b2 Q3 and b3 Q4. Quadrants are numbered counterclockwise with Q1 being top right. + + Args: + x0 (int): Center x coordinate + y0 (int): Center y coordinate + r1 (int): x radius + r2 (int): y radius + c (int): color + f (bool): Fill the ellipse (default: False) + m (int): Bitmask to determine which quadrants to draw (default: 0b1111) + w (int): Width of the ellipse (default: None) + h (int): Height of the ellipse (default: None) + + Returns: + (Area): The bounding box of the ellipse. + """ + if r1 < 1 or r2 < 1: + return + + x_side = w - 2 * r1 if w else 0 + y_side = h - 2 * r2 if h else 0 + x_offset = x_side // 2 if w else 0 + y_offset = y_side // 2 if h else 0 + + if f: + if y_offset > 0: + fill_rect(canvas, x0 - w // 2, y0 - y_offset, w, y_side, c) + if x_offset > 0: + fill_rect(canvas, x0 - x_offset, y0 - h // 2, x_side, r1, c) + fill_rect(canvas, x0 - x_offset, y0 + h // 2 - r1, x_side, r1, c) + + if x_offset > 0: + hline(canvas, x0 - x_offset, y0 - h // 2, x_side, c) + hline(canvas, x0 - x_offset, y0 + h // 2, x_side, c) + if y_offset > 0: + vline(canvas, x0 - w // 2, y0 - y_offset, y_side, c) + vline(canvas, x0 + w // 2, y0 - y_offset, y_side, c) + + a2 = r1 * r1 + b2 = r2 * r2 + fa2 = 4 * a2 + fb2 = 4 * b2 + + x1 = r1 + y1 = 0 + sigma = 2 * a2 + b2 * (1 - 2 * r1) + while a2 * y1 <= b2 * x1: + if f: + if m & 0x1: + hline(canvas, x0 + x_offset, y0 - y1 - y_offset, x1, c) + if m & 0x2: + hline(canvas, x0 - x1 - x_offset, y0 - y1 - y_offset, x1, c) + if m & 0x4: + hline(canvas, x0 - x1 - x_offset, y0 + y1 + y_offset, x1, c) + if m & 0x8: + hline(canvas, x0 + x_offset, y0 + y1 + y_offset, x1, c) + else: + if m & 0x1: + pixel(canvas, x0 + x1 + x_offset, y0 - y1 - y_offset, c) + if m & 0x2: + pixel(canvas, x0 - x1 - x_offset, y0 - y1 - y_offset, c) + if m & 0x4: + pixel(canvas, x0 - x1 - x_offset, y0 + y1 + y_offset, c) + if m & 0x8: + pixel(canvas, x0 + x1 + x_offset, y0 + y1 + y_offset, c) + if sigma >= 0: + sigma += fb2 * (1 - x1) + x1 -= 1 + sigma += a2 * ((4 * y1) + 6) + y1 += 1 + + x1 = 0 + y1 = r2 + sigma = 2 * b2 + a2 * (1 - 2 * r2) + while b2 * x1 <= a2 * y1: + if f: + if m & 0x1: + hline(canvas, x0 + x_offset, y0 - y1 - y_offset, x1, c) + if m & 0x2: + hline(canvas, x0 - x1 - x_offset, y0 - y1 - y_offset, x1, c) + if m & 0x4: + hline(canvas, x0 - x1 - x_offset, y0 + y1 + y_offset, x1, c) + if m & 0x8: + hline(canvas, x0 + x_offset, y0 + y1 + y_offset, x1, c) + else: + if m & 0x1: + pixel(canvas, x0 + x1 + x_offset, y0 - y1 - y_offset, c) + if m & 0x2: + pixel(canvas, x0 - x1 - x_offset, y0 - y1 - y_offset, c) + if m & 0x4: + pixel(canvas, x0 - x1 - x_offset, y0 + y1 + y_offset, c) + if m & 0x8: + pixel(canvas, x0 + x1 + x_offset, y0 + y1 + y_offset, c) + if sigma >= 0: + sigma += fa2 * (1 - y1) + y1 -= 1 + sigma += b2 * ((4 * x1) + 6) + x1 += 1 + return Area(x0 - r1 - x_offset, y0 - r2 - y_offset, 2 * (r1 + x_offset), 2 * (r2 + y_offset)) + + +def fill(canvas, c): + """ + Fill the entire canvas with a color. Uses the canvas's fill method if available, + otherwise calls the fill_rect function. + + Args: + c (int): color. + + Returns: + (Area): The bounding box of the filled area. + """ + if hasattr(canvas, "fill"): + canvas.fill(c) + return Area(0, 0, canvas.width, canvas.height) + else: + return fill_rect(canvas, 0, 0, canvas.width, canvas.height, c) + + +def fill_rect(canvas, x, y, w, h, c): + """ + Filled rectangle drawing function. Draws a filled rectangle starting at + x, y and extending w, h pixels. Uses the canvas's fill_rect method if available, + otherwise calls the pixel function for each pixel. + + Args: + x (int): X-coordinate of the top-left corner of the rectangle. + y (int): Y-coordinate of the top-left corner of the rectangle. + w (int): Width of the rectangle. + h (int): Height of the rectangle. + c (int): color + + Returns: + (Area): The bounding box of the filled area. + """ + if y < -h or y > canvas.height or x < -w or x > canvas.width: + return + if hasattr(canvas, "fill_rect"): + canvas.fill_rect(x, y, w, h, c) + else: + for j in range(y, y + h): + for i in range(x, x + w): + pixel(canvas, i, j, c) + return Area(x, y, w, h) + + +def gradient_rect(canvas, x, y, w, h, c1, c2=None, vertical=True): + """ + Fill a rectangle with a gradient. + + Args: + x (int): X-coordinate of the top-left corner of the rectangle. + y (int): Y-coordinate of the top-left corner of the rectangle. + w (int): Width of the rectangle. + h (int): Height of the rectangle. + c1 (int): 565 encoded color for the top or left edge. + c2 (int): 565 encoded color for the bottom or right edge. If None or the same as c1, + fill_rect will be called instead. + vertical (bool): If True, the gradient will be vertical. If False, the gradient will be horizontal. + + Returns: + (Area): The bounding box of the filled area. + """ + if c2 is None or c1 == c2: + return fill_rect(canvas, x, y, w, h, c1) + r1, g1, b1 = (c1 >> 8) & 0xF8, (c1 >> 3) & 0xFC, (c1 << 3) & 0xF8 + r2, g2, b2 = (c2 >> 8) & 0xF8, (c2 >> 3) & 0xFC, (c2 << 3) & 0xF8 + if vertical: + for j in range(h): + r = r1 + (r2 - r1) * j // h + g = g1 + (g2 - g1) * j // h + b = b1 + (b2 - b1) * j // h + c = (r & 0xF8) << 8 | (g & 0xFC) << 3 | (b & 0xF8) >> 3 + fill_rect(canvas, x, y + j, w, 1, c) + else: + for i in range(w): + r = r1 + (r2 - r1) * i // w + g = g1 + (g2 - g1) * i // w + b = b1 + (b2 - b1) * i // w + c = (r & 0xF8) << 8 | (g & 0xFC) << 3 | (b & 0xF8) >> 3 + fill_rect(canvas, x + i, y, 1, h, c) + return Area(x, y, w, h) + + +def hline(canvas, x0, y0, w, c): + """ + Horizontal line drawing function. Will draw a single pixel wide line. + + Args: + x0 (int): X-coordinate of the start of the line. + y0 (int): Y-coordinate of the start of the line. + w (int): Width of the line. + c (int): color. + + Returns: + (Area): The bounding box of the line. + """ + if y0 < 0 or y0 > canvas.height or x0 < -w or x0 > canvas.width: + return + fill_rect(canvas, x0, y0, w, 1, c) + return Area(x0, y0, w, 1) + + +def line(canvas, x0, y0, x1, y1, c): + """ + Line drawing function. Will draw a single pixel wide line starting at + x0, y0 and ending at x1, y1. + + Args: + x0 (int): X-coordinate of the start of the line. + y0 (int): Y-coordinate of the start of the line. + x1 (int): X-coordinate of the end of the line. + y1 (int): Y-coordinate of the end of the line. + c (int): color. + + Returns: + (Area): The bounding box of the line. + """ + if x0 == x1: + return vline(canvas, x0, y0, abs(y1 - y0) + 1, c) + if y0 == y1: + return hline(canvas, x0, y0, abs(x1 - x0) + 1, c) + + steep = abs(y1 - y0) > abs(x1 - x0) + if steep: + x0, y0 = y0, x0 + x1, y1 = y1, x1 + if x0 > x1: + x0, x1 = x1, x0 + y0, y1 = y1, y0 + dx = x1 - x0 + dy = abs(y1 - y0) + err = dx // 2 + ystep = 0 + ystep = 1 if y0 < y1 else -1 + while x0 <= x1: + if steep: + pixel(canvas, y0, x0, c) + else: + pixel(canvas, x0, y0, c) + err -= dy + if err < 0: + y0 += ystep + err += dx + x0 += 1 + return Area(min(x0, x1), min(y0, y1), abs(x1 - x0), abs(y1 - y0)) + + +def pixel(canvas, x, y, c): + """ + Draw a single pixel at the specified x, y location. Uses the canvas's pixel method if available, + otherwise writes directly to the buffer. + + Args: + x (int): X-coordinate of the pixel. + y (int): Y-coordinate of the pixel. + c (int): color. + + Returns: + (Area): The bounding box of the pixel. + """ + if hasattr(canvas, "pixel"): + canvas.pixel(x, y, c) + else: + rgb565_color = (c & 0xFFFF).to_bytes(2, "little") + canvas.buffer[(y * canvas.width + x) * 2 : (y * canvas.width + x) * 2 + 2] = rgb565_color + return Area(x, y, 1, 1) + + +def poly(canvas, x, y, coords, c, f=False): + """ + Given a list of coordinates, draw an arbitrary (convex or concave) closed polygon at the given x, y location + using the given color. + + The coords must be specified as an array of integers, e.g. array('h', [x0, y0, x1, y1, ... xn, yn]) or a + list or tuple of points, e.g. [(x0, y0), (x1, y1), ... (xn, yn)]. + + The optional f parameter can be set to True to fill the polygon. Otherwise, just a one-pixel outline is drawn. + + Args: + x (int): X-coordinate of the polygon's position. + y (int): Y-coordinate of the polygon's position. + coords (list): List of coordinates. + c (int): color. + f (bool): Fill the polygon (default: False). + + Returns: + (Area): The bounding box of the polygon. + """ + + # Convert the coords to a list of x, y tuples if it is not already + if isinstance(coords, list): + vertices = coords + elif isinstance(coords, tuple): + vertices = list(coords) + else: + # Check that the coords array has an even number of elements + if len(coords) % 2 != 0: + raise ValueError("coords must have an even number of elements") + vertices = [(coords[i], coords[i + 1]) for i in range(0, len(coords), 2)] + + # Check that the polygon has at least 3 vertices + if len(vertices) < 3: + raise ValueError("polygon must have at least 3 vertices") + + # Close the polygon if it is not already closed + if vertices[0] != vertices[-1]: + vertices.append(vertices[0]) + + # Offset vertices by (x, y) + vertices = [(x + vertex[0], y + vertex[1]) for vertex in vertices] + + # Find the rectangle bounding box of the polygon + left = min(vertex[0] for vertex in vertices) + right = max(vertex[0] for vertex in vertices) + top = min(vertex[1] for vertex in vertices) + bottom = max(vertex[1] for vertex in vertices) + + if f: + # Fill the polygon using scanline algorithm + # Calculate the minimum and maximum y-coordinates in the polygon + y_min = min(vertex[1] for vertex in vertices) + y_max = max(vertex[1] for vertex in vertices) + + # Iterate through each y-coordinate within the bounding box + for y_scan in range(y_min, y_max + 1): + # Determine intersections with the polygon edges + intersections = [] + for i in range(len(vertices) - 1): + x1, y1 = vertices[i] + x2, y2 = vertices[i + 1] + # Check if the scanline intersects the edge + if y1 <= y_scan < y2 or y2 <= y_scan < y1: + # Calculate the intersection point using linear interpolation + x_intersection = x1 + ((y_scan - y1) / (y2 - y1)) * (x2 - x1) + intersections.append(x_intersection) + + # Sort intersections in increasing order + intersections.sort() + + # Draw horizontal lines between pairs of intersection points + for i in range(0, len(intersections), 2): + x_start = int(intersections[i]) + x_end = int(intersections[i + 1]) + hline(canvas, x_start, y_scan, x_end - x_start, c) + else: + for i in range(len(vertices) - 1): + line( + canvas, + vertices[i][0], + vertices[i][1], + vertices[i + 1][0], + vertices[i + 1][1], + c, + ) + return Area(left, top, right - left, bottom - top) + + +def polygon(canvas, points, x, y, color, angle=0, center_x=0, center_y=0): + """ + Draw a polygon on the canvas. + + Args: + points (list): List of points to draw. + x (int): X-coordinate of the polygon's position. + y (int): Y-coordinate of the polygon's position. + color (int): color. + angle (float): Rotation angle in radians (default: 0). + center_x (int): X-coordinate of the rotation center (default: 0). + center_y (int): Y-coordinate of the rotation center (default: 0). + + Raises: + ValueError: If the polygon has less than 3 points. + + Returns: + (Area): The bounding box of the polygon. + """ + # MIT License + # Copyright (c) 2024 Brad Barnett + # Copyright (c) 2020-2023 Russ Hughes + # Copyright (c) 2019 Ivan Belokobylskiy + if len(points) < 3: + raise ValueError("Polygon must have at least 3 points.") + + # fmt: off + if angle: + cos_a = math.cos(angle) + sin_a = math.sin(angle) + rotated = [ + (x + center_x + int((point[0] - center_x) * cos_a - (point[1] - center_y) * sin_a), + y + center_y + int((point[0] - center_x) * sin_a + (point[1] - center_y) * cos_a)) + for point in points + ] + else: + rotated = [(x + int((point[0])), y + int((point[1]))) for point in points] + + # Find the rectangle bounding box of the polygon + left = min(vertex[0] for vertex in rotated) + right = max(vertex[0] for vertex in rotated) + top = min(vertex[1] for vertex in rotated) + bottom = max(vertex[1] for vertex in rotated) + + for i in range(1, len(rotated)): + line(canvas, rotated[i - 1][0], rotated[i - 1][1], rotated[i][0], rotated[i][1], color) + # fmt: on + return Area(left, top, right - left, bottom - top) + + +def rect(canvas, x0, y0, w, h, c, f=False): + """ + Rectangle drawing function. Will draw a single pixel wide rectangle starting at + x0, y0 and extending w, h pixels. + + Args: + x0 (int): X-coordinate of the top-left corner of the rectangle. + y0 (int): Y-coordinate of the top-left corner of the rectangle. + w (int): Width of the rectangle. + h (int): Height of the rectangle. + c (int): color. + f (bool): Fill the rectangle (default: False). + + Returns: + (Area): The bounding box of the rectangle. + """ + if f: + return fill_rect(canvas, x0, y0, w, h, c) + if y0 < -h or y0 > canvas.height or x0 < -w or x0 > canvas.width: + return + hline(canvas, x0, y0, w, c) + hline(canvas, x0, y0 + h - 1, w, c) + vline(canvas, x0, y0, h, c) + vline(canvas, x0 + w - 1, y0, h, c) + return Area(x0, y0, w, h) + + +def round_rect(canvas, x0, y0, w, h, r, c, f=False): + """ + Rounded rectangle drawing function. Will draw a single pixel wide rounded rectangle starting at + x0, y0 and extending w, h pixels with the specified radius. + + Args: + x0 (int): X-coordinate of the top-left corner of the rectangle. + y0 (int): Y-coordinate of the top-left corner of the rectangle. + w (int): Width of the rectangle. + h (int): Height of the rectangle. + r (int): Radius of the corners. + c (int): color. + f (bool): Fill the rectangle (default: False). + + Returns: + (Area): The bounding box of the rectangle. + """ + # If the radius is 0, just draw a rectangle + if r == 0: + return rect(canvas, x0, y0, w, h, c, f) + + # If filled, draw the rounded rectangle using the _fill_round_rect function + if f: + return _fill_round_rect(canvas, x0, y0, w, h, r, c) + + # ensure that the r will only ever half of the shortest side or less + r = int(min(r, w / 2, h / 2)) + + hline(canvas, x0 + r, y0, w - 2 * r, c) # top + hline(canvas, x0 + r, y0 + h - 1, w - 2 * r, c) # bottom + vline(canvas, x0, y0 + r, h - 2 * r, c) # left + vline(canvas, x0 + w - 1, y0 + r, h - 2 * r, c) # right + _circle_helper(canvas, x0 + w // 2, y0 + h // 2, r, c, w // 2 - r, h // 2 - r) + return Area(x0, y0, w, h) + + +def _fill_round_rect(canvas, x0, y0, w, h, r, c): + """ + Filled rounded rectangle drawing function. Will draw a filled rounded rectangle starting at + x0, y0 and extending w, h pixels with the specified radius. + """ + + # ensure that the r will only ever be half of the shortest side or less + r = int(min(r, w / 2, h / 2)) + fill_rect(canvas, x0 + r, y0, w - 2 * r, h, c) # center + _fill_circle_helper(canvas, x0 + w // 2, y0 + h // 2, r, c, w // 2 - r, h // 2 - r) + return Area(x0, y0, w, h) + + +def triangle(canvas, x0, y0, x1, y1, x2, y2, c, f=False): + # pylint: disable=too-many-arguments + """ + Triangle drawing function. Draws a single pixel wide triangle with vertices at + (x0, y0), (x1, y1), and (x2, y2). + + Args: + x0 (int): X-coordinate of the first vertex. + y0 (int): Y-coordinate of the first vertex. + x1 (int): X-coordinate of the second vertex. + y1 (int): Y-coordinate of the second vertex. + x2 (int): X-coordinate of the third vertex. + y2 (int): Y-coordinate of the third vertex. + c (int): color. + f (bool): Fill the triangle (default: False). + + Returns: + (Area): The bounding box of the triangle. + """ + if f: + return _fill_triangle(canvas, x0, y0, x1, y1, x2, y2, c) + line(canvas, x0, y0, x1, y1, c) + line(canvas, x1, y1, x2, y2, c) + line(canvas, x2, y2, x0, y0, c) + left = min(x0, x1, x2) + top = min(y0, y1, y2) + right = max(x0, x1, x2) + bottom = max(y0, y1, y2) + return Area(left, top, right - left, bottom - top) + + +def _fill_triangle(canvas, x0, y0, x1, y1, x2, y2, c): + # pylint: disable=too-many-arguments + """ + Filled triangle drawing function. Will draw a filled triangle with vertices at + (x0, y0), (x1, y1), and (x2, y2). + """ + if y0 > y1: + y0, y1 = y1, y0 + x0, x1 = x1, x0 + if y1 > y2: + y2, y1 = y1, y2 + x2, x1 = x1, x2 + if y0 > y1: + y0, y1 = y1, y0 + x0, x1 = x1, x0 + a = 0 + b = 0 + last = 0 + if y0 == y2: + a = x0 + b = x0 + if x1 < a: + a = x1 + elif x1 > b: + b = x1 + if x2 < a: + a = x2 + elif x2 > b: + b = x2 + hline(canvas, a, y0, b - a + 1, c) + return + dx01 = x1 - x0 + dy01 = y1 - y0 + dx02 = x2 - x0 + dy02 = y2 - y0 + dx12 = x2 - x1 + dy12 = y2 - y1 + if dy01 == 0: + dy01 = 1 + if dy02 == 0: + dy02 = 1 + if dy12 == 0: + dy12 = 1 + sa = 0 + sb = 0 + y = y0 + last = y1 - 1 if y0 == y1 else y1 + while y <= last: + a = x0 + sa // dy01 + b = x0 + sb // dy02 + sa += dx01 + sb += dx02 + if a > b: + a, b = b, a + hline(canvas, a, y, b - a + 1, c) + y += 1 + sa = dx12 * (y - y1) + sb = dx02 * (y - y0) + while y <= y2: + a = x1 + sa // dy12 + b = x0 + sb // dy02 + sa += dx12 + sb += dx02 + if a > b: + a, b = b, a + hline(canvas, a, y, b - a + 1, c) + y += 1 + left = min(x0, x1, x2) + top = min(y0, y1, y2) + right = max(x0, x1, x2) + bottom = max(y0, y1, y2) + return Area(left, top, right - left, bottom - top) + + +def vline(canvas, x0, y0, h, c): + """ + Horizontal line drawing function. Will draw a single pixel wide line. + + Args: + x0 (int): X-coordinate of the start of the line. + y0 (int): Y-coordinate of the start of the line. + h (int): Height of the line. + c (int): color. + + Returns: + (Area): The bounding box of the line. + """ + if y0 < -h or y0 > canvas.height or x0 < 0 or x0 > canvas.width: + return + fill_rect(canvas, x0, y0, 1, h, c) + return Area(x0, y0, 1, h) diff --git a/lib/graphics/framebuf.py b/lib/graphics/framebuf.py new file mode 100644 index 0000000..3821187 --- /dev/null +++ b/lib/graphics/framebuf.py @@ -0,0 +1,786 @@ +# SPDX-FileCopyrightText: 2016 Damien P. George +# Copyright 2024-2026 Brad Barnett +# SPDX-License-Identifier: MIT + +""" +framebuf.py - a pure-Python, drop-in replacement for MicroPython's built-in +``framebuf`` module (https://docs.micropython.org/en/latest/library/framebuf.html) +for use on CPython and CircuitPython. + +This module intentionally mirrors MicroPython's C implementation +(``extmod/modframebuf.c``, by Damien P. George) as closely as possible: + +- Same format constants, same method signatures, same return values (``None`` + for every drawing method; only ``pixel()`` without a color argument returns + a value). +- No pydisplay extensions (no ``RGB888``, no ``Area`` return values, no + ``arc``/``circle``/``blit_rect``/etc.) and no imports from ``graphics``. + +For the pydisplay-flavored subclass that adds ``Area`` bounding boxes, +``RGB888``, extra shapes, and file I/O, see ``graphics.FrameBuffer`` +(``graphics/_framebuf_plus.py``), which subclasses this module. +""" + +try: + from ulab import numpy as np +except ImportError: + try: + import numpy as np + except ImportError: + np = None + + +# Framebuf format constants (values match MicroPython's framebuf module): +MONO_VLSB = 0 +MVLSB = 0 +RGB565 = 1 +GS4_HMSB = 2 +MONO_HLSB = 3 +MONO_HMSB = 4 +GS2_HMSB = 5 +GS8 = 6 + + +class MVLSBFormat: + """Single bit displays, vertically mapped (e.g. SSD1306 OLED).""" + + depth = 1 + + @staticmethod + def set_pixel(fb, x, y, color): + index = (y >> 3) * fb._stride + x + offset = y & 0x07 + fb._buffer[index] = (fb._buffer[index] & ~(0x01 << offset) & 0xFF) | ( + (color != 0) << offset + ) + + @staticmethod + def get_pixel(fb, x, y): + index = (y >> 3) * fb._stride + x + offset = y & 0x07 + return (fb._buffer[index] >> offset) & 0x01 + + @staticmethod + def fill_rect(fb, x, y, width, height, color): + while height > 0: + index = (y >> 3) * fb._stride + x + offset = y & 0x07 + for w_w in range(width): + fb._buffer[index + w_w] = (fb._buffer[index + w_w] & ~(0x01 << offset) & 0xFF) | ( + (color != 0) << offset + ) + y += 1 + height -= 1 + + +class MHLSBFormat: + """Single bit displays, horizontally mapped, bit 0 = rightmost pixel (e.g. PBM).""" + + depth = 1 + + @staticmethod + def set_pixel(fb, x, y, color): + index = (x + y * fb._stride) >> 3 + offset = 7 - (x & 0x07) + fb._buffer[index] = (fb._buffer[index] & ~(0x01 << offset) & 0xFF) | ( + (color != 0) << offset + ) + + @staticmethod + def get_pixel(fb, x, y): + index = (x + y * fb._stride) >> 3 + offset = 7 - (x & 0x07) + return (fb._buffer[index] >> offset) & 0x01 + + @staticmethod + def fill_rect(fb, x, y, width, height, color): + advance = fb._stride >> 3 + col = 1 if color else 0 + while width: + index = (x >> 3) + y * advance + offset = 7 - (x & 0x07) + bit = 0x01 << offset + for _ in range(height): + fb._buffer[index] = (fb._buffer[index] & ~bit & 0xFF) | (col << offset) + index += advance + x += 1 + width -= 1 + + +class MHMSBFormat: + """Single bit displays, horizontally mapped, bit 0 = leftmost pixel.""" + + depth = 1 + + @staticmethod + def set_pixel(fb, x, y, color): + index = (x + y * fb._stride) >> 3 + offset = x & 0x07 + fb._buffer[index] = (fb._buffer[index] & ~(0x01 << offset) & 0xFF) | ( + (color != 0) << offset + ) + + @staticmethod + def get_pixel(fb, x, y): + index = (x + y * fb._stride) >> 3 + offset = x & 0x07 + return (fb._buffer[index] >> offset) & 0x01 + + @staticmethod + def fill_rect(fb, x, y, width, height, color): + advance = fb._stride >> 3 + col = 1 if color else 0 + while width: + index = (x >> 3) + y * advance + offset = x & 0x07 + bit = 0x01 << offset + for _ in range(height): + fb._buffer[index] = (fb._buffer[index] & ~bit & 0xFF) | (col << offset) + index += advance + x += 1 + width -= 1 + + +class GS2HMSBFormat: + """2-bit grayscale, horizontally mapped MSB-first (e.g. HT16K33 8x8 Matrix).""" + + depth = 2 + + @staticmethod + def set_pixel(fb, x, y, color): + index = (x + y * fb._stride) >> 2 + pixel = fb._buffer[index] + shift = (x & 0b11) << 1 + mask = 0b11 << shift + color = (color & 0b11) << shift + fb._buffer[index] = color | (pixel & (~mask & 0xFF)) + + @staticmethod + def get_pixel(fb, x, y): + index = (x + y * fb._stride) >> 2 + pixel = fb._buffer[index] + shift = (x & 0b11) << 1 + return (pixel >> shift) & 0b11 + + @staticmethod + def fill_rect(fb, x, y, width, height, color): + for _x in range(x, x + width): + for _y in range(y, y + height): + GS2HMSBFormat.set_pixel(fb, _x, _y, color) + + +class GS4HMSBFormat: + """4-bit grayscale, horizontally mapped, two pixels per byte.""" + + depth = 4 + + @staticmethod + def set_pixel(fb, x, y, color): + index = (x + y * fb._stride) >> 1 + if x & 0x01: + fb._buffer[index] = (color & 0x0F) | (fb._buffer[index] & 0xF0) + else: + fb._buffer[index] = ((color & 0x0F) << 4) | (fb._buffer[index] & 0x0F) + + @staticmethod + def get_pixel(fb, x, y): + index = (x + y * fb._stride) >> 1 + if x & 0x01: + return fb._buffer[index] & 0x0F + return fb._buffer[index] >> 4 + + @staticmethod + def fill_rect(fb, x, y, width, height, color): + for _y in range(y, y + height): + for _x in range(x, x + width): + GS4HMSBFormat.set_pixel(fb, _x, _y, color) + + +class GS8Format: + """8-bit grayscale/palette, one byte per pixel.""" + + depth = 8 + + @staticmethod + def set_pixel(fb, x, y, color): + fb._buffer[y * fb._stride + x] = color & 0xFF + + @staticmethod + def get_pixel(fb, x, y): + return fb._buffer[y * fb._stride + x] + + @staticmethod + def fill_rect(fb, x, y, width, height, color): + row = bytes((color & 0xFF,)) * width + for _y in range(y, y + height): + index = _y * fb._stride + x + fb._buffer[index : index + width] = row + + +class RGB565Format: + """16-bit color, two bytes per pixel, little-endian.""" + + depth = 16 + + @staticmethod + def set_pixel(fb, x, y, color): + index = (x + y * fb._stride) * 2 + fb._buffer[index : index + 2] = (color & 0xFFFF).to_bytes(2, "little") + + @staticmethod + def get_pixel(fb, x, y): + index = (x + y * fb._stride) * 2 + return int.from_bytes(fb._buffer[index : index + 2], "little") + + @staticmethod + def fill_rect(fb, x, y, width, height, color): + stride = fb._stride + rgb565_color = (color & 0xFFFF).to_bytes(2, "little") + if np: + rgb565_color_int = int.from_bytes(rgb565_color, "little") + arr = np.frombuffer(fb._buffer, dtype=np.uint16) + for _y in range(y, y + height): + arr[_y * stride + x : _y * stride + x + width] = rgb565_color_int + else: + for _y in range(y, y + height): + offset = _y * stride + for _x in range(x, x + width): + index = (offset + _x) * 2 + fb._buffer[index : index + 2] = rgb565_color + + +def _setpixel_checked(fb, x, y, color, mask): + if mask and 0 <= x < fb._width and 0 <= y < fb._height: + fb._format.set_pixel(fb, x, y, color) + + +def _fill_rect(fb, x, y, w, h, color): + """Clip ``x, y, w, h`` to the framebuffer bounds, then delegate to the format.""" + if h < 1 or w < 1 or x + w <= 0 or y + h <= 0 or y >= fb._height or x >= fb._width: + return + xend = min(fb._width, x + w) + yend = min(fb._height, y + h) + x = max(x, 0) + y = max(y, 0) + fb._format.fill_rect(fb, x, y, xend - x, yend - y, color) + + +def _line(fb, x1, y1, x2, y2, c): + dx = x2 - x1 + if dx > 0: + sx = 1 + else: + dx = -dx + sx = -1 + + dy = y2 - y1 + if dy > 0: + sy = 1 + else: + dy = -dy + sy = -1 + + steep = dy > dx + if steep: + x1, y1 = y1, x1 + dx, dy = dy, dx + sx, sy = sy, sx + + e = 2 * dy - dx + for _ in range(dx): + if steep: + if 0 <= y1 < fb._width and 0 <= x1 < fb._height: + fb._format.set_pixel(fb, y1, x1, c) + else: + if 0 <= x1 < fb._width and 0 <= y1 < fb._height: + fb._format.set_pixel(fb, x1, y1, c) + while e >= 0: + y1 += sy + e -= 2 * dx + x1 += sx + e += 2 * dy + + _setpixel_checked(fb, x2, y2, c, 1) + + +_ELLIPSE_MASK_FILL = 0x10 +_ELLIPSE_MASK_ALL = 0x0F + + +def _draw_ellipse_points(fb, cx, cy, x, y, c, mask): + if mask & _ELLIPSE_MASK_FILL: + if mask & 0x1: + _fill_rect(fb, cx, cy - y, x + 1, 1, c) + if mask & 0x2: + _fill_rect(fb, cx - x, cy - y, x + 1, 1, c) + if mask & 0x4: + _fill_rect(fb, cx - x, cy + y, x + 1, 1, c) + if mask & 0x8: + _fill_rect(fb, cx, cy + y, x + 1, 1, c) + else: + _setpixel_checked(fb, cx + x, cy - y, c, mask & 0x1) + _setpixel_checked(fb, cx - x, cy - y, c, mask & 0x2) + _setpixel_checked(fb, cx - x, cy + y, c, mask & 0x4) + _setpixel_checked(fb, cx + x, cy + y, c, mask & 0x8) + + +def _ellipse(fb, cx, cy, xr, yr, c, f=False, m=_ELLIPSE_MASK_ALL): + mask = (_ELLIPSE_MASK_FILL if f else 0) | (m & _ELLIPSE_MASK_ALL) + if xr == 0 and yr == 0: + _setpixel_checked(fb, cx, cy, c, mask & _ELLIPSE_MASK_ALL) + return + + two_asquare = 2 * xr * xr + two_bsquare = 2 * yr * yr + x = xr + y = 0 + xchange = yr * yr * (1 - 2 * xr) + ychange = xr * xr + ellipse_error = 0 + stoppingx = two_bsquare * xr + stoppingy = 0 + while stoppingx >= stoppingy: + _draw_ellipse_points(fb, cx, cy, x, y, c, mask) + y += 1 + stoppingy += two_asquare + ellipse_error += ychange + ychange += two_asquare + if (2 * ellipse_error + xchange) > 0: + x -= 1 + stoppingx -= two_bsquare + ellipse_error += xchange + xchange += two_bsquare + + x = 0 + y = yr + xchange = yr * yr + ychange = xr * xr * (1 - 2 * yr) + ellipse_error = 0 + stoppingx = 0 + stoppingy = two_asquare * yr + while stoppingx <= stoppingy: + _draw_ellipse_points(fb, cx, cy, x, y, c, mask) + x += 1 + stoppingx += two_bsquare + ellipse_error += xchange + xchange += two_bsquare + if (2 * ellipse_error + ychange) > 0: + y -= 1 + stoppingy -= two_asquare + ellipse_error += ychange + ychange += two_asquare + + +def _trunc_div(a, b): + """Integer division truncated toward zero, matching C's ``/`` for ints.""" + q = a // b + if (a % b != 0) and ((a < 0) != (b < 0)): + q += 1 + return q + + +def _poly_points(coords): + """Normalize ``coords`` (a flat array/list of ints, or a list of (x, y) pairs).""" + coords = list(coords) + if coords and isinstance(coords[0], (list, tuple)): + return [(int(px), int(py)) for px, py in coords] + if len(coords) % 2: + coords = coords[:-1] + return [(coords[i], coords[i + 1]) for i in range(0, len(coords), 2)] + + +def _poly_edges(pts): + """Yield (px1, py1, px2, py2) edges in the exact order MicroPython visits them: + point[0] -> point[n-1] -> point[n-2] -> ... -> point[1] -> point[0]. + + Line drawing is not always symmetric under endpoint reversal for steep + segments (integer rounding), so this order must match ``modframebuf.c`` + exactly for pixel-identical output. + """ + px1, py1 = pts[0] + for k in range(len(pts) - 1, -1, -1): + px2, py2 = pts[k] + yield px1, py1, px2, py2 + px1, py1 = px2, py2 + + +def _poly(fb, x, y, coords, c, f=False): + pts = _poly_points(coords) + if not pts: + return + edges = list(_poly_edges(pts)) + + if f: + y_min = min(p[1] for p in pts) + y_max = max(p[1] for p in pts) + for row in range(y_min, y_max + 1): + nodes = [] + for px1, py1, px2, py2 in edges: + if py1 != py2 and ((py1 > row and py2 <= row) or (py1 <= row and py2 > row)): + node = _trunc_div( + 32 * px1 + _trunc_div(32 * (px2 - px1) * (row - py1), py2 - py1) + 16, + 32, + ) + nodes.append(node) + elif row == max(py1, py2): + if py1 < py2: + _setpixel_checked(fb, x + px2, y + py2, c, 1) + elif py2 < py1: + _setpixel_checked(fb, x + px1, y + py1, c, 1) + else: + _line(fb, x + px1, y + py1, x + px2, y + py2, c) + if not nodes: + continue + nodes.sort() + for i in range(0, len(nodes) - 1, 2): + _fill_rect(fb, x + nodes[i], y + row, nodes[i + 1] - nodes[i] + 1, 1, c) + else: + for px1, py1, px2, py2 in edges: + _line(fb, x + px1, y + py1, x + px2, y + py2, c) + + +def _scroll(fb, xstep, ystep): + width = fb._width + height = fb._height + + if xstep < 0: + if -xstep >= width: + return + sx = 0 + xend = width + xstep + dx = 1 + else: + if xstep >= width: + return + sx = width - 1 + xend = xstep - 1 + dx = -1 + + if ystep < 0: + if -ystep >= height: + return + y = 0 + yend = height + ystep + dy = 1 + else: + if ystep >= height: + return + y = height - 1 + yend = ystep - 1 + dy = -1 + + while y != yend: + x = sx + while x != xend: + fb._format.set_pixel(fb, x, y, fb._format.get_pixel(fb, x - xstep, y - ystep)) + x += dx + y += dy + + +def _as_readonly_framebuffer(arg): + """Accept a ``FrameBuffer`` or a ``(buffer, width, height, format[, stride])`` tuple/list.""" + if hasattr(arg, "width") and hasattr(arg, "height") and hasattr(arg, "pixel"): + return arg + items = list(arg) + if len(items) < 4 or len(items) > 5: + raise ValueError("invalid framebuffer arguments") + return FrameBuffer(*items) + + +def _blit(fb, source, x, y, key=-1, palette=None): + source = _as_readonly_framebuffer(source) + if palette is not None: + palette = _as_readonly_framebuffer(palette) + + if x >= fb._width or y >= fb._height or -x >= source.width or -y >= source.height: + return + + x0 = max(0, x) + y0 = max(0, y) + x1 = max(0, -x) + y1 = max(0, -y) + x0end = min(fb._width, x + source.width) + y0end = min(fb._height, y + source.height) + + for cy0 in range(y0, y0end): + cx1 = x1 + for cx0 in range(x0, x0end): + col = source.pixel(cx1, y1) + if palette is not None: + col = palette.pixel(col, 0) + if col != key: + fb._format.set_pixel(fb, cx0, cy0, col) + cx1 += 1 + y1 += 1 + + +# font_petme128_8x8: 96 glyphs (ASCII 32-127), 8 bytes each. Each byte is one +# vertical column of 8 pixels with bit 0 at the top. Ported byte-for-byte from +# MicroPython's extmod/font_petme128_8x8.h (Damien P. George). +_FONT_PETME128_8X8 = ( + b"\x00\x00\x00\x00\x00\x00\x00\x00" # 32= + b"\x00\x00\x00\x4f\x4f\x00\x00\x00" # 33=! + b"\x00\x07\x07\x00\x00\x07\x07\x00" # 34=" + b"\x14\x7f\x7f\x14\x14\x7f\x7f\x14" # 35=# + b"\x00\x24\x2e\x6b\x6b\x3a\x12\x00" # 36=$ + b"\x00\x63\x33\x18\x0c\x66\x63\x00" # 37=% + b"\x00\x32\x7f\x4d\x4d\x77\x72\x50" # 38=& + b"\x00\x00\x00\x04\x06\x03\x01\x00" # 39=' + b"\x00\x00\x1c\x3e\x63\x41\x00\x00" # 40=( + b"\x00\x00\x41\x63\x3e\x1c\x00\x00" # 41=) + b"\x08\x2a\x3e\x1c\x1c\x3e\x2a\x08" # 42=* + b"\x00\x08\x08\x3e\x3e\x08\x08\x00" # 43=+ + b"\x00\x00\x80\xe0\x60\x00\x00\x00" # 44=, + b"\x00\x08\x08\x08\x08\x08\x08\x00" # 45=- + b"\x00\x00\x00\x60\x60\x00\x00\x00" # 46=. + b"\x00\x40\x60\x30\x18\x0c\x06\x02" # 47=/ + b"\x00\x3e\x7f\x49\x45\x7f\x3e\x00" # 48=0 + b"\x00\x40\x44\x7f\x7f\x40\x40\x00" # 49=1 + b"\x00\x62\x73\x51\x49\x4f\x46\x00" # 50=2 + b"\x00\x22\x63\x49\x49\x7f\x36\x00" # 51=3 + b"\x00\x18\x18\x14\x16\x7f\x7f\x10" # 52=4 + b"\x00\x27\x67\x45\x45\x7d\x39\x00" # 53=5 + b"\x00\x3e\x7f\x49\x49\x7b\x32\x00" # 54=6 + b"\x00\x03\x03\x79\x7d\x07\x03\x00" # 55=7 + b"\x00\x36\x7f\x49\x49\x7f\x36\x00" # 56=8 + b"\x00\x26\x6f\x49\x49\x7f\x3e\x00" # 57=9 + b"\x00\x00\x00\x24\x24\x00\x00\x00" # 58=: + b"\x00\x00\x80\xe4\x64\x00\x00\x00" # 59=; + b"\x00\x08\x1c\x36\x63\x41\x41\x00" # 60=< + b"\x00\x14\x14\x14\x14\x14\x14\x00" # 61== + b"\x00\x41\x41\x63\x36\x1c\x08\x00" # 62=> + b"\x00\x02\x03\x51\x59\x0f\x06\x00" # 63=? + b"\x00\x3e\x7f\x41\x4d\x4f\x2e\x00" # 64=@ + b"\x00\x7c\x7e\x0b\x0b\x7e\x7c\x00" # 65=A + b"\x00\x7f\x7f\x49\x49\x7f\x36\x00" # 66=B + b"\x00\x3e\x7f\x41\x41\x63\x22\x00" # 67=C + b"\x00\x7f\x7f\x41\x63\x3e\x1c\x00" # 68=D + b"\x00\x7f\x7f\x49\x49\x41\x41\x00" # 69=E + b"\x00\x7f\x7f\x09\x09\x01\x01\x00" # 70=F + b"\x00\x3e\x7f\x41\x49\x7b\x3a\x00" # 71=G + b"\x00\x7f\x7f\x08\x08\x7f\x7f\x00" # 72=H + b"\x00\x00\x41\x7f\x7f\x41\x00\x00" # 73=I + b"\x00\x20\x60\x41\x7f\x3f\x01\x00" # 74=J + b"\x00\x7f\x7f\x1c\x36\x63\x41\x00" # 75=K + b"\x00\x7f\x7f\x40\x40\x40\x40\x00" # 76=L + b"\x00\x7f\x7f\x06\x0c\x06\x7f\x7f" # 77=M + b"\x00\x7f\x7f\x0e\x1c\x7f\x7f\x00" # 78=N + b"\x00\x3e\x7f\x41\x41\x7f\x3e\x00" # 79=O + b"\x00\x7f\x7f\x09\x09\x0f\x06\x00" # 80=P + b"\x00\x1e\x3f\x21\x61\x7f\x5e\x00" # 81=Q + b"\x00\x7f\x7f\x19\x39\x6f\x46\x00" # 82=R + b"\x00\x26\x6f\x49\x49\x7b\x32\x00" # 83=S + b"\x00\x01\x01\x7f\x7f\x01\x01\x00" # 84=T + b"\x00\x3f\x7f\x40\x40\x7f\x3f\x00" # 85=U + b"\x00\x1f\x3f\x60\x60\x3f\x1f\x00" # 86=V + b"\x00\x7f\x7f\x30\x18\x30\x7f\x7f" # 87=W + b"\x00\x63\x77\x1c\x1c\x77\x63\x00" # 88=X + b"\x00\x07\x0f\x78\x78\x0f\x07\x00" # 89=Y + b"\x00\x61\x71\x59\x4d\x47\x43\x00" # 90=Z + b"\x00\x00\x7f\x7f\x41\x41\x00\x00" # 91=[ + b"\x00\x02\x06\x0c\x18\x30\x60\x40" # 92= + b"\x00\x00\x41\x41\x7f\x7f\x00\x00" # 93=] + b"\x00\x08\x0c\x06\x06\x0c\x08\x00" # 94=^ + b"\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0" # 95=_ + b"\x00\x00\x01\x03\x06\x04\x00\x00" # 96=` + b"\x00\x20\x74\x54\x54\x7c\x78\x00" # 97=a + b"\x00\x7f\x7f\x44\x44\x7c\x38\x00" # 98=b + b"\x00\x38\x7c\x44\x44\x6c\x28\x00" # 99=c + b"\x00\x38\x7c\x44\x44\x7f\x7f\x00" # 100=d + b"\x00\x38\x7c\x54\x54\x5c\x58\x00" # 101=e + b"\x00\x08\x7e\x7f\x09\x03\x02\x00" # 102=f + b"\x00\x98\xbc\xa4\xa4\xfc\x7c\x00" # 103=g + b"\x00\x7f\x7f\x04\x04\x7c\x78\x00" # 104=h + b"\x00\x00\x00\x7d\x7d\x00\x00\x00" # 105=i + b"\x00\x40\xc0\x80\x80\xfd\x7d\x00" # 106=j + b"\x00\x7f\x7f\x30\x38\x6c\x44\x00" # 107=k + b"\x00\x00\x41\x7f\x7f\x40\x00\x00" # 108=l + b"\x00\x7c\x7c\x18\x30\x18\x7c\x7c" # 109=m + b"\x00\x7c\x7c\x04\x04\x7c\x78\x00" # 110=n + b"\x00\x38\x7c\x44\x44\x7c\x38\x00" # 111=o + b"\x00\xfc\xfc\x24\x24\x3c\x18\x00" # 112=p + b"\x00\x18\x3c\x24\x24\xfc\xfc\x00" # 113=q + b"\x00\x7c\x7c\x04\x04\x0c\x08\x00" # 114=r + b"\x00\x48\x5c\x54\x54\x74\x20\x00" # 115=s + b"\x04\x04\x3f\x7f\x44\x64\x20\x00" # 116=t + b"\x00\x3c\x7c\x40\x40\x7c\x3c\x00" # 117=u + b"\x00\x1c\x3c\x60\x60\x3c\x1c\x00" # 118=v + b"\x00\x1c\x7c\x30\x18\x30\x7c\x1c" # 119=w + b"\x00\x44\x6c\x38\x38\x6c\x44\x00" # 120=x + b"\x00\x9c\xbc\xa0\xa0\xfc\x7c\x00" # 121=y + b"\x00\x44\x64\x74\x5c\x4c\x44\x00" # 122=z + b"\x00\x08\x08\x3e\x77\x41\x41\x00" # 123={ + b"\x00\x00\x00\xff\xff\x00\x00\x00" # 124=| + b"\x00\x41\x41\x77\x3e\x08\x08\x00" # 125=} + b"\x00\x02\x03\x01\x03\x02\x03\x01" # 126=~ + b"\xaa\x55\xaa\x55\xaa\x55\xaa\x55" # 127 +) + + +def _text(fb, s, x0, y0, c=1): + x = x0 + for ch in s: + code = ord(ch) + if code < 32 or code > 127: + code = 127 + base = (code - 32) * 8 + for j in range(8): + if 0 <= x < fb._width: + vline_data = _FONT_PETME128_8X8[base + j] + y = y0 + while vline_data: + if (vline_data & 1) and 0 <= y < fb._height: + fb._format.set_pixel(fb, x, y, c) + vline_data >>= 1 + y += 1 + x += 1 + + +class FrameBuffer: + """ + FrameBuffer object. + + Args: + buffer (bytearray): An object with a buffer protocol, large enough for + every pixel defined by width, height and format. + width (int): The width of the frame buffer in pixels. + height (int): The height of the frame buffer in pixels. + format (int): The format of the frame buffer. One of: + - ``MONO_VLSB``: Single bit displays (like SSD1306 OLED) + - ``MONO_HLSB``: Single bit files like PBM (Portable BitMap) + - ``MONO_HMSB``: Single bit displays where the bits in a byte are horizontally mapped + Each byte occupies 8 horizontal pixels with bit 0 being the leftmost. + - ``RGB565``: 16-bit color displays + - ``GS2_HMSB``: 2-bit color displays like the HT16K33 8x8 Matrix + - ``GS4_HMSB``: 4-bit grayscale displays + - ``GS8``: 8-bit grayscale/palette displays + stride (int): The number of pixels between each horizontal line of the frame buffer + If not given, it is assumed to be equal to the width. + """ + + def __init__(self, buffer, width, height, format, stride=None): + if width < 1 or height < 1: + raise ValueError("invalid width/height") + self._buffer = buffer + self._width = width + self._height = height + self._stride = stride if stride is not None else width + if format == MONO_VLSB: + self._format = MVLSBFormat() + elif format == MONO_HLSB: + self._stride = (self._stride + 7) & ~7 + self._format = MHLSBFormat() + elif format == MONO_HMSB: + self._stride = (self._stride + 7) & ~7 + self._format = MHMSBFormat() + elif format == RGB565: + self._format = RGB565Format() + elif format == GS2_HMSB: + self._stride = (self._stride + 3) & ~3 + self._format = GS2HMSBFormat() + elif format == GS4_HMSB: + self._stride = (self._stride + 1) & ~1 + self._format = GS4HMSBFormat() + elif format == GS8: + self._format = GS8Format() + else: + raise ValueError("invalid format") + + @property + def width(self): + """The width of the FrameBuffer in pixels.""" + return self._width + + @property + def height(self): + """The height of the FrameBuffer in pixels.""" + return self._height + + def fill(self, c): + """Fill the entire FrameBuffer with the specified color. + + Equivalent to ``fill_rect(0, 0, width, height, c)`` (matching + MicroPython, which has no separate per-format ``fill``). + """ + _fill_rect(self, 0, 0, self._width, self._height, c) + + def fill_rect(self, x, y, w, h, c): + """Draw a filled rectangle at the given location and size, in the given color.""" + _fill_rect(self, x, y, w, h, c) + + def pixel(self, x, y, c=None): + """ + Get or set the color of a given pixel. + + If ``c`` is not given, the color of the pixel is returned (or ``None`` if + ``x, y`` is out of bounds). If ``c`` is given, the pixel is set and + ``None`` is returned (a no-op if ``x, y`` is out of bounds). + """ + if x < 0 or x >= self._width or y < 0 or y >= self._height: + return None + if c is None: + return self._format.get_pixel(self, x, y) + self._format.set_pixel(self, x, y, c) + return None + + def hline(self, x, y, w, c): + """Draw a single pixel wide horizontal line.""" + _fill_rect(self, x, y, w, 1, c) + + def vline(self, x, y, h, c): + """Draw a single pixel wide vertical line.""" + _fill_rect(self, x, y, 1, h, c) + + def rect(self, x, y, w, h, c, f=False): + """Draw a rectangle at the given location, size and color. ``f`` fills it.""" + if f: + _fill_rect(self, x, y, w, h, c) + else: + _fill_rect(self, x, y, w, 1, c) + _fill_rect(self, x, y + h - 1, w, 1, c) + _fill_rect(self, x, y, 1, h, c) + _fill_rect(self, x + w - 1, y, 1, h, c) + + def line(self, x1, y1, x2, y2, c): + """Draw a single pixel wide line from (x1, y1) to (x2, y2).""" + _line(self, x1, y1, x2, y2, c) + + def ellipse(self, x, y, xr, yr, c, f=False, m=_ELLIPSE_MASK_ALL): + """ + Draw an ellipse centered at (x, y) with radii xr, yr. ``f`` fills it. + + ``m`` is a 4-bit quadrant mask (bit 0 = Q1 top-right, bit 1 = Q2 + top-left, bit 2 = Q3 bottom-left, bit 3 = Q4 bottom-right). + """ + _ellipse(self, x, y, xr, yr, c, f, m) + + def poly(self, x, y, coords, c, f=False): + """ + Draw a closed polygon at (x, y) using an array/list of coordinate pairs. + + ``coords`` may be an ``array('h', [x0, y0, x1, y1, ...])`` (as documented + for MicroPython) or a flat list/tuple of ints, or a list of (x, y) pairs. + ``f`` fills the polygon; otherwise only the outline is drawn. + """ + _poly(self, x, y, coords, c, f) + + def scroll(self, xstep, ystep): + """Shift the contents of the FrameBuffer by (xstep, ystep) pixels.""" + _scroll(self, xstep, ystep) + + def blit(self, source, x, y, key=-1, palette=None): + """Draw another FrameBuffer (or readonly tuple/list) on top of this one at (x, y).""" + _blit(self, source, x, y, key, palette) + + def text(self, s, x, y, c=1): + """ + Write text to the FrameBuffer using (x, y) as the upper-left corner. + + All characters are 8x8 pixels; there is no way to change the font. + """ + _text(self, s, x, y, c) + + +def FrameBuffer1(buffer, width, height, format, stride=None): + """Create a new FrameBuffer object. Here only for historical reasons.""" + return FrameBuffer(buffer, width, height, format, stride) diff --git a/scripts/build.py b/scripts/build.py new file mode 100755 index 0000000..1a489e4 --- /dev/null +++ b/scripts/build.py @@ -0,0 +1,504 @@ +#!/usr/bin/env python3 +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2022 Jim Mussared +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# pydisplay — upstream micropython-lib ``tools/build.py`` (MIP index compiler). +# Renamed from ``scripts/publish_mip_index.py``; kept as ``build.py`` so +# ``publish_make_pyproject.py`` can ``from build import ensure_path_exists, error_color``. +# +# Used in this repo: +# - ``scripts/publish_mip_ghpages.sh`` — build ``mip/PyDevices`` and push gh-pages +# - ``.github/workflows/publish-micropython-lib.yml`` — calls publish_mip_ghpages.sh +# - ``scripts/publish_make_pyproject.py`` — shared ``ensure_path_exists`` / ``error_color`` +# +# Direct use (micropython-lib checkout required as ``--lib-dir``): +# ./scripts/build.py --lib-dir ../micropython-lib \\ +# --micropython /tmp/micropython \\ +# --mpy-cross /tmp/micropython/mpy-cross/build/mpy-cross \\ +# --output /tmp/mip-out + +# This script compiles all packages in this repository (excluding unix-ffi) +# into a directory suitable for serving to "mip" via a static web server. + +# Usage: +# ./scripts/build.py --output /tmp/micropython-lib/v2 --lib-dir ../micropython-lib + +# The output directory (--output) will have the following layout +# / +# index.json +# file/ +# 1d/ +# 1dddc25d +# c3/ +# c31d7eb7 +# c3a3934b +# e3/ +# e39dbf64 +# ... +# package/ +# 6/ <-- mpy version +# aioble/ +# latest.json +# 0.1.json +# ... +# hmac/ +# latest.json +# 3.4.2-3.json +# ... +# pyjwt/ +# latest.json +# 0.1.json +# ... +# 7/ <-- other mpy versions +# ... +# py/ <-- "source" distribution +# ... +# ... + +# index.json is: +# { +# "v": 2, <-- file format version +# "updated": , +# "packages": { +# { +# "name": "aioble", +# "version": "0.1", <-- Latest version of this package (always present, may be empty). +# "author": "", <-- Optional author (always present, may be empty). +# "description": "...", <-- Optional description (always present, may be empty). +# "license": "MIT", <-- SPDX short identifier (required). +# "versions": { +# "6": ["0.1", "0.2"], +# "7": ["0.2", "0.3", "0.4"], +# ... <-- Other bytecode versions +# "py": ["0.1", "0.2", "0.3", "0.4"] +# }, +# // The following entries were added in file format version 2. +# path: "micropython/bluetooth/aioble", +# }, +# ... +# } +# } + +# Each file in the "file" directory is the file contents (usually .mpy), named +# by the prefix of the sha256 hash of the contents. Files are never removed, and +# collisions are detected and will fail the compile, and the prefix length should +# be increased. +# As of September 2022, there are no collisions with a hash prefix length of 4, +# so the default of 8 should be sufficient for a while. Increasing the length +# doesn't invalidate old packages. + +# Each package json (either latest.json or {version}.json) is: +# { +# "v": 1, <-- file format version +# "hashes": [ +# ["aioble/server.mpy", "e39dbf64"], +# ... +# ], +# "urls": [ <-- not used by micropython-lib packages +# ["target/path.py", "http://url/foo/bar/path.py"], +# ... +# ], +# "deps": [ <-- not used by micropython-lib packages +# ["name", "version"], +# ... +# ] +# "version": "0.1" +# } + +# mip (or other tools) should request /package/{mpy_version}/{package_name}/{version}.json. + +import glob +import hashlib +import json +import os +import shutil +import sys +import tempfile +import time + +_JSON_VERSION_INDEX = 2 +_JSON_VERSION_PACKAGE = 1 + + +_COLOR_ERROR_ON = "\033[1;31m" +_COLOR_ERROR_OFF = "\033[0m" + + +# Create all directories in the path (such that the file can be created). +def ensure_path_exists(file_path): + path = os.path.dirname(file_path) + if not os.path.isdir(path): + os.makedirs(path) + + +# Returns the sha256 of the specified file object. +def _get_file_hash(f): + hs256 = hashlib.sha256() + hs256.update(f.read()) + return hs256.hexdigest() + + +# Returns true if the two files contain identical contents. +def _identical_files(path_a, path_b): + with open(path_a, "rb") as fa, open(path_b, "rb") as fb: + return fa.read() == fb.read() + + +# Helper to write the object as json to the specified path, creating any +# directories as required. +def _write_json(obj, path, minify=False): + ensure_path_exists(path) + with open(path, "w") as f: + json.dump( + obj, f, indent=(None if minify else 2), separators=((",", ":") if minify else None) + ) + f.write("\n") + + +# Write the package json to package/{"py" or mpy_version}/{package}/{version}.json. +def _write_package_json( + package_json, out_package_dir, mpy_version, package_name, version, replace +): + path = os.path.join(out_package_dir, mpy_version, package_name, version + ".json") + if replace or not os.path.exists(path): + _write_json(package_json, path, minify=True) + + +# Format s with bold red. +def error_color(s): + return _COLOR_ERROR_ON + s + _COLOR_ERROR_OFF + + +# Copy src to "file"/{short_hash[0:2]}/{short_hash}. +def _write_hashed_file(package_name, src, target_path, out_file_dir, hash_prefix_len): + # Generate the full sha256 and the hash prefix to use as the output path. + file_hash = _get_file_hash(src) + short_file_hash = file_hash[:hash_prefix_len] + # Group files into subdirectories using the first two bytes of the hash prefix. + output_file = os.path.join(short_file_hash[:2], short_file_hash) + output_file_path = os.path.join(out_file_dir, output_file) + + if os.path.exists(output_file_path): + # If the file exists (e.g. from a previous run of this script), then ensure + # that it's actually the same file. + if not _identical_files(src.name, output_file_path): + print( + error_color("Hash collision processing:"), + package_name, + file=sys.stderr, + ) + print(" File: ", target_path, file=sys.stderr) + print(" Short hash: ", short_file_hash, file=sys.stderr) + print(" Full hash: ", file_hash, file=sys.stderr) + with open(output_file_path, "rb") as f: + print(" Target hash: ", _get_file_hash(f), file=sys.stderr) + print("Try increasing --hash-prefix (currently {})".format(hash_prefix_len)) + sys.exit(1) + else: + # Create new file. + ensure_path_exists(output_file_path) + shutil.copyfile(src.name, output_file_path) + + return short_file_hash + + +# Convert the tagged .py file into a .mpy file and copy to the "file" output +# directory with it's hashed name. Updates the package_json with the file +# hash. +def _compile_as_mpy( + package_name, + package_json, + tagged_path, + target_path, + opt, + mpy_cross, + mpy_cross_path, + out_file_dir, + hash_prefix_len, +): + with tempfile.NamedTemporaryFile(mode="rb", suffix=".mpy", delete=True) as mpy_tempfile: + try: + mpy_cross.compile( + tagged_path, + dest=mpy_tempfile.name, + src_path=target_path, + opt=opt, + mpy_cross=mpy_cross_path, + ) + except mpy_cross.CrossCompileError as e: + print( + error_color("Error:"), + "Unable to compile", + target_path, + "in package", + package_name, + file=sys.stderr, + ) + print(e) + sys.exit(1) + + short_mpy_hash = _write_hashed_file( + package_name, mpy_tempfile, target_path, out_file_dir, hash_prefix_len + ) + + # Add the file to the package json. + target_path_mpy = target_path[:-2] + "mpy" + package_json["hashes"].append((target_path_mpy, short_mpy_hash)) + + +# Copy the tagged .py file to the "file" output directory with it's hashed +# name. Updates the package_json with the file hash. +def _copy_as_py( + package_name, package_json, tagged_path, target_path, out_file_dir, hash_prefix_len +): + with open(tagged_path, "rb") as tagged_file: + short_py_hash = _write_hashed_file( + package_name, tagged_file, target_path, out_file_dir, hash_prefix_len + ) + # Add the file to the package json. + package_json["hashes"].append((target_path, short_py_hash)) + + +# Update to the latest metadata, and add any new versions to the package in +# the index json. +def _update_index_package_metadata(index_package_json, metadata, mpy_version, package_path): + index_package_json["version"] = metadata.version or "" + index_package_json["author"] = "" # TODO: Make manifestfile.py capture this. + index_package_json["description"] = metadata.description or "" + index_package_json["license"] = metadata.license or "MIT" + if "versions" not in index_package_json: + index_package_json["versions"] = {} + if metadata.version: + for v in ("py", mpy_version): + if v not in index_package_json["versions"]: + index_package_json["versions"][v] = [] + if metadata.version not in index_package_json["versions"][v]: + print(" New version {}={}".format(v, metadata.version)) + index_package_json["versions"][v].append(metadata.version) + + # The following entries were added in file format version 2. + index_package_json["path"] = package_path + + +def build(output_path, hash_prefix_len, mpy_cross_path, lib_dir): + import manifestfile + import mpy_cross + + out_file_dir = os.path.join(output_path, "file") + out_package_dir = os.path.join(output_path, "package") + + path_vars = { + "MPY_LIB_DIR": lib_dir, + } + + index_json_path = os.path.join(output_path, "index.json") + + try: + with open(index_json_path) as f: + print("Updating existing index.json") + index_json = json.load(f) + except FileNotFoundError: + print("Creating new index.json") + index_json = {"packages": []} + + index_json["v"] = _JSON_VERSION_INDEX + index_json["updated"] = int(time.time()) + + # For now, don't process unix-ffi. In the future this can be extended to + # allow a way to request unix-ffi packages via mip. + lib_dirs = ["micropython", "python-stdlib", "python-ecosys"] + + mpy_version, _mpy_sub_version = mpy_cross.mpy_version(mpy_cross=mpy_cross_path) + mpy_version = str(mpy_version) + print("Generating bytecode version", mpy_version) + + prev_cwd = os.getcwd() + os.chdir(lib_dir) + try: + for lib_dir_name in lib_dirs: + for manifest_path in glob.glob( + os.path.join(lib_dir_name, "**", "manifest.py"), recursive=True + ): + package_path = os.path.dirname(manifest_path) + print("{}".format(package_path)) + # .../foo/manifest.py -> foo + package_name = os.path.basename(os.path.dirname(manifest_path)) + + # Compile the manifest. + manifest = manifestfile.ManifestFile(manifestfile.MODE_COMPILE, path_vars) + manifest.execute(manifest_path) + + # Append this package to the index. + if not manifest.metadata().version: + print(error_color("Warning:"), package_name, "doesn't have a version.") + + # Try to find this package in the previous index.json. + for p in index_json["packages"]: + if p["name"] == package_name: + index_package_json = p + break + else: + print(" First-time package") + index_package_json = { + "name": package_name, + } + index_json["packages"].append(index_package_json) + + _update_index_package_metadata( + index_package_json, manifest.metadata(), mpy_version, package_path + ) + + # This is the package json that mip/mpremote downloads. + mpy_package_json = { + "v": _JSON_VERSION_PACKAGE, + "hashes": [], + "version": manifest.metadata().version or "", + } + py_package_json = { + "v": _JSON_VERSION_PACKAGE, + "hashes": [], + "version": manifest.metadata().version or "", + } + + for result in manifest.files(): + # This isn't allowed in micropython-lib anyway. + if result.file_type != manifestfile.FILE_TYPE_LOCAL: + print( + error_color("Error:"), "Non-local file not supported.", file=sys.stderr + ) + sys.exit(1) + + if not result.target_path.endswith(".py"): + print( + error_color("Error:"), + "Target path isn't a .py file:", + result.target_path, + file=sys.stderr, + ) + sys.exit(1) + + # Tag each file with the package metadata and compile to .mpy + # (and copy the .py directly). + with manifestfile.tagged_py_file( + result.full_path, result.metadata + ) as tagged_path: + _compile_as_mpy( + package_name, + mpy_package_json, + tagged_path, + result.target_path, + result.opt, + mpy_cross, + mpy_cross_path, + out_file_dir, + hash_prefix_len, + ) + _copy_as_py( + package_name, + py_package_json, + tagged_path, + result.target_path, + out_file_dir, + hash_prefix_len, + ) + + # Create/replace {package}/latest.json. + _write_package_json( + mpy_package_json, + out_package_dir, + mpy_version, + package_name, + "latest", + replace=True, + ) + _write_package_json( + py_package_json, out_package_dir, "py", package_name, "latest", replace=True + ) + + # Write {package}/{version}.json, but only if it doesn't already + # exist. A package version is "locked" the first time it's seen + # by this script. + if manifest.metadata().version: + _write_package_json( + mpy_package_json, + out_package_dir, + mpy_version, + package_name, + manifest.metadata().version, + replace=False, + ) + _write_package_json( + py_package_json, + out_package_dir, + "py", + package_name, + manifest.metadata().version, + replace=False, + ) + + finally: + os.chdir(prev_cwd) + + # Write updated package index json, sorted by package name. + index_json["packages"].sort(key=lambda p: p["name"]) + _write_json(index_json, index_json_path, minify=False) + + +def main(): + import argparse + + cmd_parser = argparse.ArgumentParser(description="Compile micropython-lib for serving to mip.") + cmd_parser.add_argument("--output", required=True, help="output directory") + cmd_parser.add_argument("--hash-prefix", default=8, type=int, help="hash prefix length") + cmd_parser.add_argument("--mpy-cross", default=None, help="optional path to mpy-cross binary") + cmd_parser.add_argument("--micropython", default=None, help="path to micropython repo") + cmd_parser.add_argument( + "--lib-dir", + default=None, + help="micropython-lib repo root (scan cwd); default: pydisplay repo root", + ) + args = cmd_parser.parse_args() + + _scripts_dir = os.path.dirname(os.path.abspath(__file__)) + sys.path.insert(0, _scripts_dir) + + if args.micropython: + sys.path.append(os.path.join(args.micropython, "tools")) # for manifestfile + sys.path.append(os.path.join(args.micropython, "mpy-cross")) # for mpy_cross + + lib_dir = os.path.abspath( + args.lib_dir or os.path.join(os.path.dirname(__file__), ".."), + ) + + build( + args.output, + hash_prefix_len=max(4, args.hash_prefix), + mpy_cross_path=args.mpy_cross, + lib_dir=lib_dir, + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/manifestfile.py b/scripts/manifestfile.py new file mode 100755 index 0000000..ece8305 --- /dev/null +++ b/scripts/manifestfile.py @@ -0,0 +1,668 @@ +#!/usr/bin/env python3 +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2022 Jim Mussared +# Copyright (c) 2019 Damien P. George +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from __future__ import print_function + +import os +import sys +import tempfile +from collections import namedtuple + +__all__ = ["ManifestFile", "ManifestFileError"] + +# Allow freeze*() etc. +MODE_FREEZE = 1 +# Only allow include/require/module/package. +MODE_COMPILE = 2 +# Same as compile, but handles require(..., pypi="name") as a requirements.txt entry. +MODE_PYPROJECT = 3 + +# In compile mode, .py -> KIND_COMPILE_AS_MPY +# In freeze mode, .py -> KIND_FREEZE_AS_MPY, .mpy->KIND_FREEZE_MPY +KIND_AUTO = 1 +# Freeze-mode only, .py -> KIND_FREEZE_AS_MPY, .mpy->KIND_FREEZE_MPY +KIND_FREEZE_AUTO = 2 + +# Freeze-mode only, The .py file will be frozen as text. +KIND_FREEZE_AS_STR = 3 +# Freeze-mode only, The .py file will be compiled and frozen as bytecode. +KIND_FREEZE_AS_MPY = 4 +# Freeze-mode only, The .mpy file will be frozen directly. +KIND_FREEZE_MPY = 5 +# Compile mode only, the .py file should be compiled to .mpy. +KIND_COMPILE_AS_MPY = 6 + +# File on the local filesystem. +FILE_TYPE_LOCAL = 1 +# URL to file. (TODO) +FILE_TYPE_HTTP = 2 + +# Default list of libraries in micropython-lib to search for library packages. +BASE_LIBRARY_NAMES = ("micropython", "python-stdlib", "python-ecosys") + + +class ManifestFileError(Exception): + pass + + +class ManifestIgnoreException(Exception): + pass + + +class ManifestUsePyPIException(Exception): + def __init__(self, pypi_name): + self.pypi_name = pypi_name + + +# The set of files that this manifest references. +ManifestOutput = namedtuple( # noqa: PYI024 + "ManifestOutput", + [ + "file_type", # FILE_TYPE_*. + "full_path", # The input file full path. + "target_path", # The target path on the device. + "timestamp", # Last modified date of the input file. + "kind", # KIND_*. + "metadata", # Metadata for the containing package. + "opt", # Optimisation level (or None). + ], +) + + +# Represents the metadata for a package. +class ManifestPackageMetadata: + def __init__(self, is_require=False): + self._is_require = is_require + self._initialised = False + + self.version = None + self.description = None + self.license = None + self.author = None + + # Annotate a package as being from the python standard library. + self.stdlib = False + + # Allows a python-ecosys package to be annotated with the + # corresponding name in PyPI. e.g. micropython-lib/requests is based + # on pypi/requests. + self.pypi = None + # For a micropython package, this is the name that we will publish it + # to PyPI as. e.g. micropython-lib/senml publishes as + # pypi/micropython-senml. + self.pypi_publish = None + + def update( + self, + mode, + description=None, + version=None, + license=None, + author=None, + stdlib=False, + pypi=None, + pypi_publish=None, + ): + if self._initialised: + raise ManifestFileError("Duplicate call to metadata().") + + # In MODE_PYPROJECT, if this manifest is being evaluated as a result + # of a require(), then figure out if it should be replaced by a PyPI + # dependency instead. + if mode == MODE_PYPROJECT and self._is_require: + if stdlib: + # No dependency required at all for CPython. + raise ManifestIgnoreException + if pypi_publish or pypi: + # In the case where a package is both based on a PyPI package and + # provides one, preference depending on the published one. + # (This should be pretty rare). + raise ManifestUsePyPIException(pypi_publish or pypi) + + self.description = description + self.version = version + self.license = license + self.author = author + self.pypi = pypi + self.pypi_publish = pypi_publish + self._initialised = True + + def check_initialised(self, mode): + # Ensure that metadata() is the first thing a manifest.py does. + # This is to ensure that we early-exit if it should be replaced by a pypi dependency. + if mode in (MODE_COMPILE, MODE_PYPROJECT) and not self._initialised: + raise ManifestFileError("metadata() must be the first command in a manifest file.") + + def __str__(self): + return "version={} description={} license={} author={} pypi={} pypi_publish={}".format( + self.version, self.description, self.license, self.author, self.pypi, self.pypi_publish + ) + + +# Turns a dict of options into a object with attributes used to turn the +# kwargs passed to include() and require into the "options" global in the +# included manifest. +# options = IncludeOptions(foo="bar", blah="stuff") +# options.foo # "bar" +# options.blah # "stuff" +class IncludeOptions: + def __init__(self, **kwargs): + self._kwargs = kwargs + self._defaults = {} + + def defaults(self, **kwargs): + self._defaults = kwargs + + def __getattr__(self, name): + return self._kwargs.get(name, self._defaults.get(name, None)) + + +class ManifestFile: + def __init__(self, mode, path_vars=None): + # See MODE_* constants above. + self._mode = mode + # Path substitution variables. + self._path_vars = path_vars or {} + # List of files (as ManifestFileResult) references by this manifest. + self._manifest_files = [] + # List of PyPI dependencies (when mode=MODE_PYPROJECT). + self._pypi_dependencies = [] + # Don't allow including the same file twice. + self._visited = set() + # Stack of metadata for each level. + self._metadata = [ManifestPackageMetadata()] + # Registered external libraries. + self._libraries = {} + # List of directories to search for packages. + self._library_dirs = [] + # Add default micropython-lib libraries if $(MPY_LIB_DIR) has been specified. + if self._path_vars["MPY_LIB_DIR"]: + for lib in BASE_LIBRARY_NAMES: + self.add_library(lib, os.path.join("$(MPY_LIB_DIR)", lib)) + + def _resolve_path(self, path): + # Convert path to an absolute path, applying variable substitutions. + for name, value in self._path_vars.items(): + if value is not None: + path = path.replace("$({})".format(name), value) + return os.path.abspath(path) + + def _manifest_globals(self, kwargs): + # This is the "API" available to a manifest file. + g = { + "metadata": self.metadata, + "include": self.include, + "require": self.require, + "add_library": self.add_library, + "package": self.package, + "module": self.module, + "options": IncludeOptions(**kwargs), + } + + # Extra legacy functions only for freeze mode. + if self._mode == MODE_FREEZE: + g.update( + { + "freeze": self.freeze, + "freeze_as_str": self.freeze_as_str, + "freeze_as_mpy": self.freeze_as_mpy, + "freeze_mpy": self.freeze_mpy, + } + ) + + return g + + def files(self): + return self._manifest_files + + def pypi_dependencies(self): + # In MODE_PYPROJECT, this will return a list suitable for requirements.txt. + return self._pypi_dependencies + + def execute(self, manifest_file): + if manifest_file.endswith(".py"): + # Execute file from filesystem. + self.include(manifest_file) + else: + # Execute manifest code snippet. + try: + exec(manifest_file, self._manifest_globals({})) + except Exception as er: + raise ManifestFileError("Error in manifest: {}".format(er)) from er + + def _add_file(self, full_path, target_path, kind=KIND_AUTO, opt=None): + # Check file exists and get timestamp. + try: + stat = os.stat(full_path) + timestamp = stat.st_mtime + except OSError as e: + raise ManifestFileError("Cannot stat {}".format(full_path)) from e + + # Map the AUTO kinds to their actual kind based on mode and extension. + _, ext = os.path.splitext(full_path) + if self._mode == MODE_FREEZE: + if kind in ( + KIND_AUTO, + KIND_FREEZE_AUTO, + ): + if ext.lower() == ".py": + kind = KIND_FREEZE_AS_MPY + elif ext.lower() == ".mpy": + kind = KIND_FREEZE_MPY + else: + if kind != KIND_AUTO: + raise ManifestFileError("Not in freeze mode") + if ext.lower() != ".py": + raise ManifestFileError("Expected .py file") + kind = KIND_COMPILE_AS_MPY + + self._manifest_files.append( + ManifestOutput( + FILE_TYPE_LOCAL, full_path, target_path, timestamp, kind, self._metadata[-1], opt + ) + ) + + def _search(self, base_path, package_path, files, exts, kind, opt=None, strict=False): + base_path = self._resolve_path(base_path) + + if files is not None: + # Use explicit list of files (relative to package_path). + for file in files: + if package_path: + file = os.path.join(package_path, file) + self._add_file(os.path.join(base_path, file), file, kind=kind, opt=opt) + else: + if base_path: + prev_cwd = os.getcwd() + os.chdir(self._resolve_path(base_path)) + + # Find all candidate files. + for dirpath, _, filenames in os.walk(package_path or ".", followlinks=True): + for file in filenames: + file = os.path.relpath(os.path.join(dirpath, file), ".") + _, ext = os.path.splitext(file) + if ext.lower() in exts: + self._add_file( + os.path.join(base_path, file), + file, + kind=kind, + opt=opt, + ) + elif strict: + raise ManifestFileError("Unexpected file type") + + if base_path: + os.chdir(prev_cwd) + + def metadata(self, **kwargs): + """ + From within a manifest file, use this to set the metadata for the + package described by current manifest. + + After executing a manifest file (via execute()), call this + to obtain the metadata for the top-level manifest file. + + See ManifestPackageMetadata.update() for valid kwargs. + """ + if kwargs: + self._metadata[-1].update(self._mode, **kwargs) + return self._metadata[-1] + + def include(self, manifest_path, is_require=False, **kwargs): + """ + Include another manifest. + + The manifest argument can be a string (filename) or an iterable of + strings. + + Relative paths are resolved with respect to the current manifest file. + + If the path is to a directory, then it implicitly includes the + manifest.py file inside that directory. + + Optional kwargs can be provided which will be available to the + included script via the `options` variable. + + e.g. include("path.py", extra_features=True) + + in path.py: + options.defaults(standard_features=True) + + # freeze minimal modules. + if options.standard_features: + # freeze standard modules. + if options.extra_features: + # freeze extra modules. + """ + if is_require: + self._metadata[-1].check_initialised(self._mode) + + if not isinstance(manifest_path, str): + for m in manifest_path: + self.include(m, **kwargs) + else: + manifest_path = self._resolve_path(manifest_path) + # Including a directory grabs the manifest.py inside it. + if os.path.isdir(manifest_path): + manifest_path = os.path.join(manifest_path, "manifest.py") + if manifest_path in self._visited: + return + self._visited.add(manifest_path) + if is_require: + # This include is the result of require("name"), so push a new + # package metadata onto the stack. + self._metadata.append(ManifestPackageMetadata(is_require=True)) + try: + with open(manifest_path) as f: + # Make paths relative to this manifest file while processing it. + # Applies to includes and input files. + prev_cwd = os.getcwd() + os.chdir(os.path.dirname(manifest_path)) + try: + exec(f.read(), self._manifest_globals(kwargs)) + finally: + os.chdir(prev_cwd) + except ManifestIgnoreException: + # e.g. MODE_PYPROJECT and this was a stdlib dependency. No-op. + pass + except ManifestUsePyPIException as e: + # e.g. MODE_PYPROJECT and this was a package from + # python-ecosys. Add PyPI dependency instead. + self._pypi_dependencies.append(e.pypi_name) + except Exception as e: + raise ManifestFileError( + "Error in manifest file: {}: {}".format(manifest_path, e) + ) from e + if is_require: + self._metadata.pop() + + def _require_from_path(self, library_path, name, version, extra_kwargs): + for root, _dirnames, filenames in os.walk(library_path): # type: ignore[type-arg] + if os.path.basename(root) == name and "manifest.py" in filenames: + self.include(root, is_require=True, **extra_kwargs) + return True + return False + + def require(self, name, version=None, pypi=None, library=None, **kwargs): + """ + Require a package by name from micropython-lib. + + Optionally specify pipy="package-name" to indicate that this should + use the named package from PyPI when building for CPython. + + Optionally specify library="name" to reference a package from a + library that has been previously registered with add_library(). Otherwise + the list of library paths will be used. + """ + self._metadata[-1].check_initialised(self._mode) + + if self._mode == MODE_PYPROJECT and pypi: + # In PYPROJECT mode, allow overriding the PyPI dependency name + # explicitly. Otherwise if the dependent package has metadata + # (pypi_publish) or metadata(pypi) we will use that. + self._pypi_dependencies.append(pypi) + return + + if library is not None: + # Find package in external library. + if library not in self._libraries: + raise ValueError("Unknown library '{}' for require('{}').".format(library, name)) + library_path = self._libraries[library] + # Search for {library_path}/**/{name}/manifest.py. + if self._require_from_path(library_path, name, version, kwargs): + return + raise ValueError( + "Package '{}' not found in external library '{}' ({}).".format( + name, library, library_path + ) + ) + + for lib_dir in self._library_dirs: + # Search for {lib_dir}/**/{name}/manifest.py. + if self._require_from_path(lib_dir, name, version, kwargs): + return + + if pypi: + # PyPI-only dependency (e.g. pygame-ce). Not bundled in MIP / micropython-lib. + return + + raise ValueError("Package '{}' not found in any known library.".format(name)) + + def add_library(self, library, library_path, prepend=False): + """ + Register the path to an external named library. + + The path will be automatically searched when using require(). By default the + added library is added to the end of the list of libraries to search. Pass + `prepend=True` to add it to the start of the list. + + Additionally, the added library can be explicitly requested by using + `require("name", library="library")`. + """ + library_path = self._resolve_path(library_path) + self._libraries[library] = library_path + self._library_dirs.insert(0 if prepend else len(self._library_dirs), library_path) + + def package(self, package_path, files=None, base_path=".", opt=None): + """ + Define a package, optionally restricting to a set of files. + + Simple case, a package in the current directory: + package("foo") + will include all .py files in foo, and will be stored as foo/bar/baz.py. + + If the package isn't in the current directory, use base_path: + package("foo", base_path="src") + + To restrict to certain files in the package use files (note: paths should be relative to the package): + package("foo", files=["bar/baz.py"]) + """ + self._metadata[-1].check_initialised(self._mode) + + # Include "base_path/package_path/**/*.py" --> "package_path/**/*.py" + self._search(base_path, package_path, files, exts=(".py",), kind=KIND_AUTO, opt=opt) + + def module(self, module_path, base_path=".", opt=None): + """ + Include a single Python file as a module. + + If the file is in the current directory: + module("foo.py") + + Otherwise use base_path to locate the file: + module("foo.py", "src/drivers") + """ + self._metadata[-1].check_initialised(self._mode) + + # Include "base_path/module_path" --> "module_path" + base_path = self._resolve_path(base_path) + _, ext = os.path.splitext(module_path) + if ext.lower() != ".py": + raise ManifestFileError("module must be .py file") + # TODO: version None + self._add_file(os.path.join(base_path, module_path), module_path, opt=opt) + + def _freeze_internal(self, path, script, exts, kind, opt): + if script is None: + self._search(path, None, None, exts=exts, kind=kind, opt=opt) + elif isinstance(script, str) and os.path.isdir(os.path.join(path, script)): + self._search(path, script, None, exts=exts, kind=kind, opt=opt) + elif not isinstance(script, str): + self._search(path, None, script, exts=exts, kind=kind, opt=opt) + else: + self._search(path, None, (script,), exts=exts, kind=kind, opt=opt) + + def freeze(self, path, script=None, opt=None): + """ + Freeze the input, automatically determining its type. A .py script + will be compiled to a .mpy first then frozen, and a .mpy file will be + frozen directly. + + `path` must be a directory, which is the base directory to _search for + files from. When importing the resulting frozen modules, the name of + the module will start after `path`, ie `path` is excluded from the + module name. + + If `path` is relative, it is resolved to the current manifest.py. + Use $(MPY_DIR), $(MPY_LIB_DIR), $(PORT_DIR), $(BOARD_DIR) if you need + to access specific paths. + + If `script` is None all files in `path` will be frozen. + + If `script` is an iterable then freeze() is called on all items of the + iterable (with the same `path` and `opt` passed through). + + If `script` is a string then it specifies the file or directory to + freeze, and can include extra directories before the file or last + directory. The file or directory will be _searched for in `path`. If + `script` is a directory then all files in that directory will be frozen. + + `opt` is the optimisation level to pass to mpy-cross when compiling .py + to .mpy. + """ + self._freeze_internal( + path, + script, + exts=( + ".py", + ".mpy", + ), + kind=KIND_FREEZE_AUTO, + opt=opt, + ) + + def freeze_as_str(self, path): + """ + Freeze the given `path` and all .py scripts within it as a string, + which will be compiled upon import. + """ + self._search(path, None, None, exts=(".py",), kind=KIND_FREEZE_AS_STR) + + def freeze_as_mpy(self, path, script=None, opt=None): + """ + Freeze the input (see above) by first compiling the .py scripts to + .mpy files, then freezing the resulting .mpy files. + """ + self._freeze_internal(path, script, exts=(".py",), kind=KIND_FREEZE_AS_MPY, opt=opt) + + def freeze_mpy(self, path, script=None, opt=None): + """ + Freeze the input (see above), which must be .mpy files that are + frozen directly. + """ + self._freeze_internal(path, script, exts=(".mpy",), kind=KIND_FREEZE_MPY, opt=opt) + + +# Generate a temporary file with a line appended to the end that adds __version__. +class tagged_py_file: + def __init__(self, path, metadata): + self._path = path + self._metadata = metadata + self._dest_path = None + + def __enter__(self): + dest_fd, self._dest_path = tempfile.mkstemp(suffix=".py", text=True) + try: + with os.fdopen(dest_fd, "w") as dest, open(self._path, "r") as src: + contents = src.read() + dest.write(contents) + + # Don't overwrite a version definition if the file already has one in it. + if self._metadata.version and "__version__ =" not in contents: + dest.write("\n\n__version__ = {}\n".format(repr(self._metadata.version))) + except Exception: + os.unlink(self._dest_path) + raise + return self._dest_path + + def __exit__(self, exc_type, exc, tb): + os.unlink(self._dest_path) + return False + + +def main(): + import argparse + + cmd_parser = argparse.ArgumentParser(description="List the files referenced by a manifest.") + cmd_parser.add_argument("--freeze", action="store_true", help="freeze mode") + cmd_parser.add_argument("--compile", action="store_true", help="compile mode") + cmd_parser.add_argument("--pyproject", action="store_true", help="pyproject mode") + cmd_parser.add_argument( + "--lib", + default=os.path.join(os.path.dirname(__file__), "../lib/micropython-lib"), + help="path to micropython-lib repo", + ) + cmd_parser.add_argument( + "--unix-ffi", action="store_true", help="prepend unix-ffi to the library path" + ) + cmd_parser.add_argument("--port", default=None, help="path to port dir") + cmd_parser.add_argument("--board", default=None, help="path to board dir") + cmd_parser.add_argument( + "--top", + default=os.path.join(os.path.dirname(__file__), ".."), + help="path to micropython repo", + ) + cmd_parser.add_argument("files", nargs="+", help="input manifest.py") + args = cmd_parser.parse_args() + + path_vars = { + "MPY_DIR": os.path.abspath(args.top) if args.top else None, + "BOARD_DIR": os.path.abspath(args.board) if args.board else None, + "PORT_DIR": os.path.abspath(args.port) if args.port else None, + "MPY_LIB_DIR": os.path.abspath(args.lib) if args.lib else None, + } + + mode = None + if args.freeze: + mode = MODE_FREEZE + elif args.compile: + mode = MODE_COMPILE + elif args.pyproject: + mode = MODE_PYPROJECT + else: + print("Error: No mode specified.", file=sys.stderr) + sys.exit(1) + + m = ManifestFile(mode, path_vars) + if args.unix_ffi: + m.add_library("unix-ffi", os.path.join("$(MPY_LIB_DIR)", "unix-ffi"), prepend=True) + for manifest_file in args.files: + try: + m.execute(manifest_file) + except ManifestFileError as er: + print(er, file=sys.stderr) + sys.exit(1) + print(m.metadata()) + for f in m.files(): + print(f) + if mode == MODE_PYPROJECT: + for r in m.pypi_dependencies(): + print("pypi-require:", r) + + +if __name__ == "__main__": + main() diff --git a/scripts/publish_make_pyproject.py b/scripts/publish_make_pyproject.py new file mode 100755 index 0000000..cb93ecd --- /dev/null +++ b/scripts/publish_make_pyproject.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python3 +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2023 Jim Mussared +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# This script makes a CPython-compatible package from a micropython-lib package +# with a pyproject.toml that can be built (via hatch) and deployed to PyPI. +# Requires that the project sets the pypi_publish= kwarg in its metadata(). + +# Usage: +# ./scripts/publish_make_pyproject.py --output /tmp/foo micropython/foo +# python -m build /tmp/foo +# python -m twine upload /tmp/foo/dist/*.whl + +import os +import re +import shutil +import sys +from email.utils import parseaddr + +_scripts_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, _scripts_dir) + +from build import ensure_path_exists, error_color # noqa: E402 + +DEFAULT_AUTHOR = "micropython-lib " +DEFAULT_LICENSE = "MIT" +HOME_PAGE = "https://github.com/PyDevices/graphics" +DOCS_PAGE = "https://pydevices.github.io/graphics" +ISSUES_PAGE = "https://github.com/PyDevices/graphics/issues" +TESTPYPI = "https://test.pypi.org/simple/" + + +def _resolve_mpy_lib_dir(): + """Micropython-lib root for require() resolution when building PyPI packages.""" + env_dir = os.environ.get("MICROPYTHON_LIB_DIR") + if env_dir: + return os.path.abspath(env_dir) + repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + sibling = os.path.join(os.path.dirname(repo_root), "micropython-lib") + if os.path.isdir(os.path.join(sibling, "micropython")): + return sibling + return repo_root + + +def quoted_escape(s): + return s.replace('"', '\\"') + + +def build(manifest_path, output_path): + import manifestfile + + if not manifest_path.endswith(".py"): + # Allow specifying either the directory or the manifest file explicitly. + manifest_path = os.path.join(manifest_path, "manifest.py") + + print("Generating pyproject for {} in {}...".format(manifest_path, output_path)) + + toml_path = os.path.join(output_path, "pyproject.toml") + ensure_path_exists(toml_path) + + path_vars = { + "MPY_LIB_DIR": _resolve_mpy_lib_dir(), + } + + # .../foo/manifest.py -> foo + package_name = os.path.basename(os.path.dirname(manifest_path)) + + # Compile the manifest. + manifest = manifestfile.ManifestFile(manifestfile.MODE_PYPROJECT, path_vars) + manifest.add_library("testpypi", TESTPYPI, prepend=False) + manifest.execute(manifest_path) + + # If a package doesn't have a pypi name, then assume it isn't intended to + # be publishable. + if not manifest.metadata().pypi_publish: + print(error_color("Error:"), package_name, "doesn't have a pypi_publish name.") + sys.exit(1) + + # These should be in all packages eventually. + if not manifest.metadata().version: + print(error_color("Error:"), package_name, "doesn't have a version.") + sys.exit(1) + if not manifest.metadata().description: + print(error_color("Error:"), package_name, "doesn't have a description.") + sys.exit(1) + + # This is the root path of all .py files that are copied. We ensure that + # they all match. + top_level_package = None + root_modules = [] + + for result in manifest.files(): + # This isn't allowed in micropython-lib anyway. + if result.file_type != manifestfile.FILE_TYPE_LOCAL: + print(error_color("Error:"), "Non-local file not supported.", file=sys.stderr) + sys.exit(1) + + # "foo/bar/baz.py" --> "foo" + # "foo/nested/bar/baz.py" --> "foo" (not "foo/nested") + # "baz.py" --> "" + normalized = result.target_path.replace("\\", "/") + result_package = normalized.split("/", 1)[0] if "/" in normalized else "" + + if not result_package: + root_modules.append(normalized) + with manifestfile.tagged_py_file(result.full_path, result.metadata) as tagged_path: + dest_path = os.path.join(output_path, result.target_path) + ensure_path_exists(dest_path) + shutil.copyfile(tagged_path, dest_path) + continue + if top_level_package and result_package != top_level_package: + # This likely suggests that something needs to use require(..., pypi="..."). + print( + error_color("Error:"), + "More than one top-level package: {}, {}.".format( + result_package, top_level_package + ), + file=sys.stderr, + ) + sys.exit(1) + top_level_package = result_package + + # Tag each file with the package metadata and copy the .py directly. + with manifestfile.tagged_py_file(result.full_path, result.metadata) as tagged_path: + dest_path = os.path.join(output_path, result.target_path) + ensure_path_exists(dest_path) + shutil.copyfile(tagged_path, dest_path) + + if not top_level_package and not root_modules: + print(error_color("Error:"), "No package or module files in manifest.", file=sys.stderr) + sys.exit(1) + + # Copy README.md if it exists + readme_path = os.path.join(os.path.dirname(manifest_path), "README.md") + readme_toml = "" + if os.path.exists(readme_path): + shutil.copyfile(readme_path, os.path.join(output_path, "README.md")) + readme_toml = 'readme = "README.md"' + + # Apply default author and license, otherwise use the package metadata. + license_toml = 'license = {{ text = "{}" }}'.format( + quoted_escape(manifest.metadata().license or DEFAULT_LICENSE) + ) + author_name, author_email = parseaddr(manifest.metadata().author or DEFAULT_AUTHOR) + author_toml = 'authors = [ {{ name = "{}", email = "{}"}} ]'.format( + quoted_escape(author_name), quoted_escape(author_email) + ) + + # Write pyproject.toml. + with open(toml_path, "w") as toml_file: + print("# Generated by makepyproject.py", file=toml_file) + + print( + """ +[build-system] +requires = [ + "hatchling" +] +build-backend = "hatchling.build" +""", + file=toml_file, + ) + + print( + """ +[project] +name = "{}" +description = "{}" +{} +{} +version = "{}" +dependencies = [{}] +urls = {{ Homepage = "{}", Documentation = "{}", Issues = "{}" }} +{} +""".format( + quoted_escape(manifest.metadata().pypi_publish), + quoted_escape(manifest.metadata().description), + author_toml, + license_toml, + quoted_escape(manifest.metadata().version), + ", ".join('"{}"'.format(quoted_escape(r)) for r in manifest.pypi_dependencies()), + quoted_escape(HOME_PAGE), + quoted_escape(DOCS_PAGE), + quoted_escape(ISSUES_PAGE), + readme_toml, + ), + file=toml_file, + ) + + if top_level_package: + print( + """ +[tool.hatch.build.targets.wheel] +packages = ["{}"] +""".format(top_level_package), + file=toml_file, + ) + if root_modules: + print("[tool.hatch.build.targets.wheel.force-include]", file=toml_file) + for module_path in root_modules: + print( + '"{}" = "{}"'.format(module_path, module_path), + file=toml_file, + ) + else: + only_include = ", ".join('"{}"'.format(m) for m in root_modules) + print( + """ +[tool.hatch.build.targets.wheel] +only-include = [{}] +""".format(only_include), + file=toml_file, + ) + + print("Done.") + + +def main(): + import argparse + + cmd_parser = argparse.ArgumentParser( + description="Generate a project that can be pushed to PyPI." + ) + cmd_parser.add_argument("--output", required=True, help="output directory") + cmd_parser.add_argument("--micropython", default=None, help="path to micropython repo") + cmd_parser.add_argument("manifest", help="input package path") + args = cmd_parser.parse_args() + + if args.micropython: + sys.path.append(os.path.join(args.micropython, "tools")) # for manifestfile + + build(args.manifest, args.output) + + +if __name__ == "__main__": + main() diff --git a/scripts/publish_micropython_lib.sh b/scripts/publish_micropython_lib.sh new file mode 100755 index 0000000..ad51827 --- /dev/null +++ b/scripts/publish_micropython_lib.sh @@ -0,0 +1,199 @@ +#!/usr/bin/env bash +# Sync graphics into PyDevices/micropython-lib, build TestPyPI wheels, push MIP index. +# +# CI: MICROPYTHON_LIB_DIR=../micropython-lib ./scripts/publish_micropython_lib.sh --push +# MIP: mip.install("graphics", index="https://PyDevices.github.io/micropython-lib/mip/PyDevices") + +set -euo pipefail + +SKIP_PYPI=0 +DO_PUSH=0 +COMMIT_MESSAGE="" +INTERACTIVE_COMMIT=0 +CLI_VERSION="" + +usage() { + cat <<'EOF' +Usage: ./scripts/publish_micropython_lib.sh [OPTION] + +Copy graphics lib/ into micropython-lib, optionally upload TestPyPI wheels, +then commit (and optionally push) on the PyDevices branch. + +Options: + --skip-pypi Sync manifests only; skip hatch/twine TestPyPI uploads. + --version X.Y.Z Release version (overrides tag / GRAPHICS_VERSION). + --commit-message MSG Commit micropython-lib changes (non-interactive). + --push Push micropython-lib after commit. + --help, -h Show this message. + +Environment: + MICROPYTHON_LIB_DIR micropython-lib checkout (default: ../micropython-lib) + GRAPHICS_VERSION Release version (overrides git tag on current commit) + TESTPYPI_API_TOKEN TestPyPI token for twine (when not using --skip-pypi) +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --skip-pypi) SKIP_PYPI=1; shift ;; + --version) CLI_VERSION=$2; shift 2 ;; + --commit-message) COMMIT_MESSAGE=$2; shift 2 ;; + --push) DO_PUSH=1; shift ;; + --help | -h) usage; exit 0 ;; + *) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;; + esac +done + +if [[ -z "$COMMIT_MESSAGE" ]] && [[ -t 0 ]]; then + INTERACTIVE_COMMIT=1 +fi + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOURCE_REPO="$(cd "$SCRIPT_DIR/.." && pwd)" + +normalize_version() { + local v="${1#v}" + v="$(echo "$v" | tr -d '[:space:]')" + if [[ ! "$v" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then + echo "Error: invalid semver: $1 (expected X.Y.Z)" >&2 + return 1 + fi + echo "$v" +} + +resolve_version() { + if [[ -n "$CLI_VERSION" ]]; then + normalize_version "$CLI_VERSION" + return + fi + if [[ -n "${GRAPHICS_VERSION:-}" ]]; then + normalize_version "$GRAPHICS_VERSION" + return + fi + local tag + tag="$(git -C "$SOURCE_REPO" describe --tags --exact-match 2>/dev/null || true)" + if [[ -n "$tag" ]]; then + normalize_version "$tag" + return + fi + echo "Error: no release version. Tag HEAD (vX.Y.Z), pass --version, or set GRAPHICS_VERSION." >&2 + return 1 +} + +VERSION="$(resolve_version)" || exit 1 +echo "Release version: $VERSION" + +DESCRIPTION_PREFIX="graphics" +AUTHOR="Brad Barnett " +LICENSE="MIT" +BASENAME=graphics +PYPI_NAME=graphics-py +DEST_REPO="${MICROPYTHON_LIB_DIR:-$SOURCE_REPO/../micropython-lib}" +DEST_REPO="$(cd "$DEST_REPO" 2>/dev/null && pwd || echo "$DEST_REPO")" +export MICROPYTHON_LIB_DIR="$DEST_REPO" +SOURCE_DIR=$SOURCE_REPO/lib/graphics +DEST_DIR=$DEST_REPO/micropython/$BASENAME +PYPI_DIR=$SOURCE_REPO/wheels +README_FULL_PATH=$SOURCE_REPO/README.md + +RSYNC_EXCLUDES=( + --exclude '__pycache__/' + --exclude '*.pyc' + --exclude '*.pyo' + --exclude '.git/' +) + +build_and_upload_pypi() { + if [[ "$SKIP_PYPI" -eq 1 ]]; then + return 0 + fi + rm -rf dist + hatch build + if [[ -n "${TESTPYPI_API_TOKEN:-}" ]]; then + TWINE_USERNAME=__token__ TWINE_PASSWORD="$TESTPYPI_API_TOKEN" \ + twine upload --repository testpypi --verbose dist/* + else + twine upload --repository testpypi --verbose dist/* + fi +} + +# Concurrent tag publishes from sibling repos share micropython-lib PyDevices. +push_micropython_lib() { + local repo="$1" + local branch + branch="$(git -C "$repo" rev-parse --abbrev-ref HEAD)" + local max_attempts=8 + local attempt=1 + while true; do + if git -C "$repo" push origin "HEAD:${branch}"; then + return 0 + fi + if (( attempt >= max_attempts )); then + echo "Error: push to micropython-lib ${branch} failed after ${max_attempts} attempts" >&2 + return 1 + fi + echo "Push rejected (likely concurrent publish); rebase onto origin/${branch} and retry (${attempt}/${max_attempts})..." + git -C "$repo" fetch origin "${branch}" + if ! git -C "$repo" rebase "origin/${branch}"; then + git -C "$repo" rebase --abort 2>/dev/null || true + git -C "$repo" fetch --deepen=100 origin "${branch}" || git -C "$repo" fetch --unshallow origin || true + git -C "$repo" rebase "origin/${branch}" + fi + attempt=$((attempt + 1)) + sleep "$attempt" + done +} + +echo +echo "Processing $BASENAME" +mkdir -p "$DEST_DIR/$BASENAME" +# --delete drops removed modules so the sync matches lib/ +rsync -a --delete "${RSYNC_EXCLUDES[@]}" "$SOURCE_DIR/" "$DEST_DIR/$BASENAME/" + +cat < "$DEST_DIR/manifest.py" +metadata( + description="Pure-Python graphics for MicroPython/CircuitPython/CPython (FrameBuffer, Draw, fonts); import as graphics", + version="$VERSION", + author="$AUTHOR", + license="$LICENSE", + pypi_publish="$PYPI_NAME", +) +package("$BASENAME") +EOF + +# Retire the legacy pydisplay/graphics tree after extraction. +LEGACY_DIR="$DEST_REPO/micropython/pydisplay/graphics" +if [[ -d "$LEGACY_DIR" ]]; then + rm -rf "$LEGACY_DIR" +fi + +cp "$README_FULL_PATH" "$DEST_DIR/README.md" + +if [[ "$SKIP_PYPI" -eq 0 ]]; then + ./scripts/publish_make_pyproject.py --output "$PYPI_DIR/$BASENAME" "$DEST_DIR/manifest.py" + pushd "$PYPI_DIR/$BASENAME" + build_and_upload_pypi + popd +fi + +find "$DEST_DIR" \( \ + -type d \( -name __pycache__ -o -name .mypy_cache -o -name .ruff_cache \) \ + -o -type f \( -name '*.pyc' -o -name '*.pyo' \) \ +\) -print0 2>/dev/null | xargs -0 rm -rf 2>/dev/null || true + +if [[ "$INTERACTIVE_COMMIT" -eq 1 ]] || [[ -n "$COMMIT_MESSAGE" ]]; then + if [[ "$INTERACTIVE_COMMIT" -eq 1 ]] && [[ -z "$COMMIT_MESSAGE" ]]; then + read -r -p "Enter micropython-lib commit message: " COMMIT_MESSAGE + fi + if [[ -n "$COMMIT_MESSAGE" ]]; then + if [[ -z "$(git -C "$DEST_REPO" status --porcelain)" ]]; then + echo "No changes to commit in $DEST_REPO" + else + git -C "$DEST_REPO" add . + git -C "$DEST_REPO" commit -s -m "$COMMIT_MESSAGE" + if [[ "$DO_PUSH" -eq 1 ]]; then + push_micropython_lib "$DEST_REPO" + fi + fi + fi +fi diff --git a/scripts/publish_mip_ghpages.sh b/scripts/publish_mip_ghpages.sh new file mode 100755 index 0000000..a63ca0b --- /dev/null +++ b/scripts/publish_mip_ghpages.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# Build mip/PyDevices from micropython-lib and push to gh-pages. +# +# Requires: +# MICROPYTHON_LIB_DIR checkout of PyDevices/micropython-lib (PyDevices branch) +# PYDISPLAY_DIR pydisplay repo root (default: parent of scripts/) +# +# Optional: +# MICROPYTHON_DIR micropython source for mpy-cross (default: /tmp/micropython) +# MIP_INDEX_OUTPUT build output dir (default: /tmp/mip-index) +# MIP_INDEX_SUBDIR gh-pages subdir under mip/ (default: PyDevices) +# GITHUB_SHA used in commit message (Actions sets this) +# +# Push credentials: configure git remote on MICROPYTHON_LIB_DIR before calling +# (see .github/workflows/publish-micropython-lib.yml). + +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +GRAPHICS_DIR="${GRAPHICS_DIR:-$ROOT}" +LIB_DIR="${MICROPYTHON_LIB_DIR:?set MICROPYTHON_LIB_DIR}" +MPY_DIR="${MICROPYTHON_DIR:-/tmp/micropython}" +INDEX_OUT="${MIP_INDEX_OUTPUT:-/tmp/mip-index}" +MIP_SUBDIR="${MIP_INDEX_SUBDIR:-PyDevices}" +MPY_CROSS="$MPY_DIR/mpy-cross/build/mpy-cross" +PAGES_PATH="${MIP_GHPAGES_WORKTREE:-/tmp/micropython-lib-gh-pages}" + +if [[ ! -x "$MPY_CROSS" ]]; then + echo "Building mpy-cross in $MPY_DIR" + if [[ ! -d "$MPY_DIR/.git" ]]; then + git clone --depth=1 https://github.com/micropython/micropython.git "$MPY_DIR" + fi + make -C "$MPY_DIR/mpy-cross" -j"$(nproc)" CFLAGS_EXTRA=-O0 +fi + +rm -rf "$INDEX_OUT" +mkdir -p "$INDEX_OUT" + +echo "Compiling MIP index from $LIB_DIR -> $INDEX_OUT" +python3 "$GRAPHICS_DIR/scripts/build.py" \ + --lib-dir "$LIB_DIR" \ + --micropython "$MPY_DIR" \ + --mpy-cross "$MPY_CROSS" \ + --output "$INDEX_OUT" + +cd "$LIB_DIR" +git config user.name 'github-actions[bot]' +git config user.email 'github-actions[bot]@users.noreply.github.com' + +NEW_BRANCH=0 +if git fetch --depth=1 origin gh-pages:gh-pages; then + if git worktree list | grep -q "$PAGES_PATH"; then + git worktree remove --force "$PAGES_PATH" || true + fi + git worktree add "$PAGES_PATH" gh-pages +else + echo "Creating gh-pages branch..." + git worktree add --force "$PAGES_PATH" HEAD + cd "$PAGES_PATH" + git switch --orphan gh-pages + NEW_BRANCH=1 + cd "$LIB_DIR" +fi + +DEST_PATH="$PAGES_PATH/mip/$MIP_SUBDIR" +rm -rf "$DEST_PATH" +mkdir -p "$DEST_PATH" +cp -r "$INDEX_OUT/." "$DEST_PATH/" + +cd "$PAGES_PATH" +git add . +SHA="${GITHUB_SHA:-local}" +git diff --staged --quiet && { + echo "No MIP index changes to publish" + exit 0 +} +git commit -m "graphics: Update mip/$MIP_SUBDIR from PyDevices/graphics ${SHA}." + +if [[ "$NEW_BRANCH" -eq 0 ]]; then + git pull --rebase origin gh-pages +fi +git push origin gh-pages + +echo "Published https://PyDevices.github.io/micropython-lib/mip/$MIP_SUBDIR" diff --git a/scripts/sync_fonts.py b/scripts/sync_fonts.py index 33fc65c..aae17f9 100644 --- a/scripts/sync_fonts.py +++ b/scripts/sync_fonts.py @@ -1,16 +1,14 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: MIT -"""Generate C font headers from pydisplay ``lib/graphics/_font_8x*.py`` romfont data. +"""Generate C font headers from ``lib/graphics/_font_8x*.py`` romfont data. -Canonical font bytes live in pydisplay; this script writes ``font_8x8.h``, -``font_8x14.h``, and ``font_8x16.h`` under ``include/``. +Canonical font bytes live in the pure-Python package; this script writes +``font_8x8.h``, ``font_8x14.h``, and ``font_8x16.h`` under ``include/``. Usage (from repo root):: python3 scripts/sync_fonts.py python3 scripts/sync_fonts.py --check - -Default pydisplay path: ``../../pydisplay`` relative to this graphics repo. """ from __future__ import annotations @@ -22,7 +20,7 @@ _GRAPHICS_DIR = Path(__file__).resolve().parents[1] _INCLUDE_DIR = _GRAPHICS_DIR / "include" -_DEFAULT_PYDISPLAY = _GRAPHICS_DIR.parent.parent / "pydisplay" +_LIB_GRAPHICS = _GRAPHICS_DIR / "lib" / "graphics" def _load_font_bytes(py_path: Path) -> bytes: @@ -55,7 +53,7 @@ def _render_header(name: str, array_name: str, data: bytes, height: int) -> str: #define {guard} #include -/* romfont from pydisplay src/lib/graphics/_font_{name.lower()}.py ({height}px, 256 glyphs) */ +/* romfont from lib/graphics/_font_{name.lower()}.py ({height}px, 256 glyphs) */ static const uint8_t {array_name}[] = {{ {_format_c_array(data)} }}; @@ -67,11 +65,11 @@ def _render_header(name: str, array_name: str, data: bytes, height: int) -> str: """ -def sync_fonts(*, pydisplay: Path, check: bool = False) -> None: +def sync_fonts(*, check: bool = False) -> None: fonts = ( - ("8X8", "font_8x8", 8, pydisplay / "src/lib/graphics/_font_8x8.py"), - ("8X14", "font_8x14", 14, pydisplay / "src/lib/graphics/_font_8x14.py"), - ("8X16", "font_8x16", 16, pydisplay / "src/lib/graphics/_font_8x16.py"), + ("8X8", "font_8x8", 8, _LIB_GRAPHICS / "_font_8x8.py"), + ("8X14", "font_8x14", 14, _LIB_GRAPHICS / "_font_8x14.py"), + ("8X16", "font_8x16", 16, _LIB_GRAPHICS / "_font_8x16.py"), ) errors = [] for c_name, array_name, height, py_path in fonts: @@ -102,15 +100,9 @@ def sync_fonts(*, pydisplay: Path, check: bool = False) -> None: def main() -> None: parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument( - "--pydisplay", - type=Path, - default=_DEFAULT_PYDISPLAY, - help="path to pydisplay repo (default: ../../pydisplay)", - ) parser.add_argument("--check", action="store_true", help="fail if headers are missing or stale") args = parser.parse_args() - sync_fonts(pydisplay=args.pydisplay.resolve(), check=args.check) + sync_fonts(check=args.check) if __name__ == "__main__": From 13df9bcec4dbc6c9927ae5f93745c1052f7173f9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 26 Jul 2026 12:32:59 +0000 Subject: [PATCH 6/6] Add pip-installable graphics-py packaging and host moved unit tests. lib/pyproject.toml lets CI/consumers pip-install the pure-Python package without a sibling checkout. Port graphics-focused tests from pydisplay. --- lib/pyproject.toml | 19 +++ tests/_env.py | 11 ++ tests/test_blit_hooks.py | 106 +++++++++++++++++ tests/test_clip.py | 40 +++++++ tests/test_draw.py | 110 ++++++++++++++++++ tests/test_files.py | 222 ++++++++++++++++++++++++++++++++++++ tests/test_font.py | 81 +++++++++++++ tests/test_framebuf_plus.py | 127 +++++++++++++++++++++ tests/test_shapes.py | 178 +++++++++++++++++++++++++++++ 9 files changed, 894 insertions(+) create mode 100644 lib/pyproject.toml create mode 100644 tests/_env.py create mode 100644 tests/test_blit_hooks.py create mode 100644 tests/test_clip.py create mode 100644 tests/test_draw.py create mode 100644 tests/test_files.py create mode 100644 tests/test_font.py create mode 100644 tests/test_framebuf_plus.py create mode 100644 tests/test_shapes.py diff --git a/lib/pyproject.toml b/lib/pyproject.toml new file mode 100644 index 0000000..0ff6fb3 --- /dev/null +++ b/lib/pyproject.toml @@ -0,0 +1,19 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "graphics-py" +version = "0.0.10" +description = "Pure-Python graphics (FrameBuffer, Draw, fonts); import as graphics" +readme = "graphics/README.md" +license = "MIT" +requires-python = ">=3.8" +authors = [{ name = "Brad Barnett", email = "contact@pydevices.com" }] +urls = { Homepage = "https://github.com/PyDevices/graphics", Issues = "https://github.com/PyDevices/graphics/issues" } + +[tool.hatch.build.targets.wheel] +packages = ["graphics"] + +[tool.hatch.build.targets.sdist] +include = ["graphics"] diff --git a/tests/_env.py b/tests/_env.py new file mode 100644 index 0000000..95f6271 --- /dev/null +++ b/tests/_env.py @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""Put ``lib/`` on ``sys.path`` so ``import graphics`` works in-repo.""" + +import os +import sys + +_LIB = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "lib")) +if _LIB not in sys.path: + sys.path.insert(0, _LIB) diff --git a/tests/test_blit_hooks.py b/tests/test_blit_hooks.py new file mode 100644 index 0000000..c515e55 --- /dev/null +++ b/tests/test_blit_hooks.py @@ -0,0 +1,106 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""Tests for graphics blit hook dispatch.""" + +import unittest + +import _env # noqa: F401 +from _support import make_fb + +from graphics import Area, Draw, blit, blit_rect +from graphics._blit_hooks import ( + blit_rect_dispatch, + canvas_accepts_blit_rect, + canvas_accepts_blit_transparent, + try_fast_framebuffer_blit, +) + + +class _BlitRectCanvas: + color_depth = 16 + + def __init__(self, width, height): + self.width = width + self.height = height + self.calls = [] + + def blit_rect(self, buf, x, y, w, h): + self.calls.append((bytes(buf), x, y, w, h)) + + +class _BlitTransparentCanvas(_BlitRectCanvas): + def __init__(self, width, height): + super().__init__(width, height) + self.transparent_calls = [] + + def blit_transparent(self, buf, x, y, w, h, key): + self.transparent_calls.append((bytes(buf), x, y, w, h, key)) + + +class TestBlitHooks(unittest.TestCase): + def test_framebuffer_rejects_rect_hook(self): + fb = make_fb(w=8, h=8) + self.assertFalse(canvas_accepts_blit_rect(fb)) + self.assertFalse(canvas_accepts_blit_transparent(fb)) + + def test_display_like_canvas_accepts_hooks(self): + canvas = _BlitRectCanvas(16, 16) + self.assertTrue(canvas_accepts_blit_rect(canvas)) + canvas = _BlitTransparentCanvas(16, 16) + self.assertTrue(canvas_accepts_blit_transparent(canvas)) + + def test_draw_blit_uses_display_blit_rect(self): + src = make_fb(w=4, h=4) + src.fill(0xBEEF) + display = _BlitRectCanvas(16, 16) + draw = Draw(display) + area = draw.blit(src, 2, 2) + self.assertEqual(area, Area(2, 2, 4, 4)) + self.assertEqual(len(display.calls), 1) + _buf, x, y, w, h = display.calls[0] + self.assertEqual((x, y, w, h), (2, 2, 4, 4)) + self.assertEqual(len(_buf), 4 * 4 * 2) + + def test_blit_rect_dispatch_to_display(self): + canvas = _BlitRectCanvas(8, 8) + buf = bytearray(2 * 2 * 2) + blit_rect_dispatch(canvas, buf, 1, 1, 2, 2) + self.assertEqual(len(canvas.calls), 1) + + def test_blit_rect_dispatch_to_framebuffer(self): + dst = make_fb(w=8, h=8) + dst.fill(0) + buf = bytearray([0x34, 0x12, 0x78, 0x56]) + blit_rect(dst, buf, 1, 1, 2, 1) + self.assertEqual(dst.pixel(1, 1), 0x1234) + self.assertEqual(dst.pixel(2, 1), 0x5678) + + def test_try_fast_framebuffer_blit_clips(self): + src = make_fb(w=4, h=4) + src.fill(0x00FF) + dst = make_fb(w=8, h=8) + dst.fill(0) + area = try_fast_framebuffer_blit(dst, src, 6, 6) + self.assertEqual(area, Area(6, 6, 2, 2)) + self.assertEqual(dst.pixel(6, 6), 0x00FF) + self.assertEqual(dst.pixel(7, 7), 0x00FF) + self.assertEqual(dst.pixel(5, 5), 0) + + def test_blit_transparent_uses_display_hook(self): + from graphics import blit_transparent + + canvas = _BlitTransparentCanvas(8, 8) + buf = bytearray(2 * 2 * 2) + blit_transparent(canvas, buf, 0, 0, 2, 2, 0) + self.assertEqual(len(canvas.transparent_calls), 1) + + def test_framebuffer_blit_rect_no_recursion(self): + dst = make_fb(w=8, h=8) + buf = bytearray(2 * 2 * 2) + area = dst.blit_rect(buf, 0, 0, 2, 2) + self.assertEqual(area, Area(0, 0, 2, 2)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_clip.py b/tests/test_clip.py new file mode 100644 index 0000000..ec7db10 --- /dev/null +++ b/tests/test_clip.py @@ -0,0 +1,40 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""Tests for clip-region helpers.""" + +import unittest + +import _env # noqa: F401 +from _support import make_fb + +from graphics import Area +from graphics._clip import ClippedCanvas, crop_rgb565_buffer, intersect_rect + + +class TestClipHelpers(unittest.TestCase): + def test_intersect_rect(self): + self.assertEqual( + intersect_rect(0, 0, 10, 10, Area(5, 5, 10, 10)), + Area(5, 5, 5, 5), + ) + self.assertIsNone(intersect_rect(0, 0, 2, 2, Area(10, 10, 2, 2))) + + def test_crop_rgb565_buffer(self): + buf = bytearray([0, 1, 2, 3, 4, 5, 6, 7]) + cropped = crop_rgb565_buffer(buf, 2, 1, 0, 1, 2) + self.assertEqual(bytes(cropped), bytes([2, 3, 6, 7])) + + def test_clipped_canvas_fill_rect(self): + fb = make_fb(w=8, h=8) + fb.fill(0) + clipped = ClippedCanvas(fb, Area(2, 2, 3, 3)) + clipped.fill_rect(0, 0, 8, 8, 0xFFFF) + self.assertEqual(fb.pixel(1, 1), 0) + self.assertEqual(fb.pixel(2, 2), 0xFFFF) + self.assertEqual(fb.pixel(4, 4), 0xFFFF) + self.assertEqual(fb.pixel(5, 5), 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_draw.py b/tests/test_draw.py new file mode 100644 index 0000000..be9a526 --- /dev/null +++ b/tests/test_draw.py @@ -0,0 +1,110 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""Tests for the ``graphics.Draw`` convenience wrapper. + +``Draw`` binds a canvas once and forwards each call to ``graphics._shapes`` / +``graphics._font``, returning the ``Area`` bounding box. +""" + +import unittest + +import _env # noqa: F401 +from _support import count_set, make_fb + +from graphics import Area, Draw + +_WHITE = 0xFFFF + + +class TestDraw(unittest.TestCase): + def setUp(self): + self.canvas = make_fb(w=16, h=16) + self.canvas.fill(0) + self.draw = Draw(self.canvas) + + def test_binds_canvas(self): + self.assertIs(self.draw.canvas, self.canvas) + + def test_fill(self): + self.assertEqual(self.draw.fill(_WHITE), Area(0, 0, 16, 16)) + self.assertEqual(count_set(self.canvas), 16 * 16) + + def test_fill_rect(self): + self.assertEqual(self.draw.fill_rect(2, 2, 3, 3, _WHITE), Area(2, 2, 3, 3)) + self.assertEqual(count_set(self.canvas), 9) + + def test_pixel(self): + self.assertEqual(self.draw.pixel(4, 5, _WHITE), Area(4, 5, 1, 1)) + self.assertEqual(self.canvas.pixel(4, 5), _WHITE) + + def test_hline(self): + self.assertEqual(self.draw.hline(0, 0, 4, _WHITE), Area(0, 0, 4, 1)) + + def test_rect(self): + self.assertEqual(self.draw.rect(1, 1, 5, 5, _WHITE), Area(1, 1, 5, 5)) + + def test_circle(self): + self.assertEqual(self.draw.circle(8, 8, 3, _WHITE), Area(5, 5, 6, 6)) + self.assertGreater(count_set(self.canvas), 0) + + def test_line(self): + self.assertIsInstance(self.draw.line(0, 0, 5, 5, _WHITE), Area) + + def test_text(self): + self.assertIsInstance(self.draw.text("Hi", 0, 0, _WHITE), Area) + self.assertGreater(count_set(self.canvas), 0) + + def test_blit_transparent(self): + buf = bytearray(2 * 2 * 2) + buf[0:2] = (0xFF, 0xFF) + buf[2:4] = (0x00, 0x00) + area = self.draw.blit_transparent(buf, 0, 0, 2, 2, key=0) + self.assertEqual(area, Area(0, 0, 2, 2)) + + def test_clip_fill_rect(self): + with self.draw.clip(2, 2, 4, 4): + self.draw.fill_rect(0, 0, 16, 16, _WHITE) + self.assertEqual(self.canvas.pixel(1, 1), 0) + self.assertEqual(self.canvas.pixel(2, 2), _WHITE) + self.assertEqual(self.canvas.pixel(5, 5), _WHITE) + self.assertEqual(self.canvas.pixel(6, 6), 0) + + def test_clip_pixel_outside_is_noop(self): + with self.draw.clip(4, 4, 2, 2): + self.draw.pixel(0, 0, _WHITE) + self.draw.pixel(4, 4, _WHITE) + self.assertEqual(self.canvas.pixel(0, 0), 0) + self.assertEqual(self.canvas.pixel(4, 4), _WHITE) + + def test_nested_clip_intersects(self): + with self.draw.clip(0, 0, 8, 8), self.draw.clip(4, 4, 8, 8): + self.draw.fill_rect(0, 0, 16, 16, _WHITE) + self.assertEqual(self.canvas.pixel(3, 3), 0) + self.assertEqual(self.canvas.pixel(4, 4), _WHITE) + self.assertEqual(self.canvas.pixel(7, 7), _WHITE) + self.assertEqual(self.canvas.pixel(8, 8), 0) + + def test_clip_restored_after_context(self): + with self.draw.clip(2, 2, 2, 2): + self.draw.fill_rect(2, 2, 2, 2, _WHITE) + self.draw.pixel(0, 0, _WHITE) + self.assertEqual(self.canvas.pixel(0, 0), _WHITE) + + def test_clip_fill_only_region(self): + self.canvas.fill(0) + with self.draw.clip(1, 1, 3, 3): + self.draw.fill(_WHITE) + self.assertEqual(self.canvas.pixel(0, 0), 0) + self.assertEqual(self.canvas.pixel(2, 2), _WHITE) + self.assertEqual(self.canvas.pixel(4, 4), 0) + + def test_clip_accepts_area(self): + with self.draw.clip(Area(2, 2, 2, 2)): + self.draw.fill_rect(0, 0, 16, 16, _WHITE) + self.assertEqual(self.canvas.pixel(2, 2), _WHITE) + self.assertEqual(self.canvas.pixel(4, 4), 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_files.py b/tests/test_files.py new file mode 100644 index 0000000..aa311e5 --- /dev/null +++ b/tests/test_files.py @@ -0,0 +1,222 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""Tests for ``graphics`` image file I/O (save / from_file and the converters). + +Covers the formats whose pure-Python read/write paths round-trip cleanly on +CPython: PBM (MONO_HLSB), PGM (grayscale), and BMP (RGB565). +""" + +import os +import struct +import tempfile +import unittest + +import _env # noqa: F401 + +from graphics import ( + BMP565, + GS2_HMSB, + GS4_HMSB, + GS8, + MONO_HLSB, + MONO_VLSB, + RGB565, + FrameBuffer, + bmp_to_framebuffer, + load_image, + pbm_to_framebuffer, + pgm_to_framebuffer, + save_image, +) + + +class _TmpDirTest(unittest.TestCase): + def setUp(self): + self._dir = tempfile.mkdtemp(prefix="graphics_files_") + + def tearDown(self): + import shutil + + shutil.rmtree(self._dir, ignore_errors=True) + + def _path(self, name): + return os.path.join(self._dir, name) + + +class TestSaveLoadRoundTrip(_TmpDirTest): + def test_pbm_roundtrip(self): + fb = FrameBuffer(bytearray((16 + 7) // 8 * 8), 16, 8, MONO_HLSB) + fb.fill(0) + fb.fill_rect(1, 1, 3, 3, 1) + path = self._path("img.pbm") + fb.save(path) + + loaded = FrameBuffer.from_file(path) + self.assertEqual(loaded.width, 16) + self.assertEqual(loaded.height, 8) + self.assertEqual(loaded.format, MONO_HLSB) + self.assertEqual(bytes(loaded.buffer), bytes(fb.buffer)) + self.assertEqual(loaded.pixel(2, 2), 1) + + def test_pgm_roundtrip(self): + # Build via per-pixel writes (GS2 fill() would convert the buffer to a + # list, which save cannot serialise). + fb = FrameBuffer(bytearray((16 + 3) // 4 * 8), 16, 8, GS2_HMSB) + fb.fill_rect(1, 1, 3, 3, 3) + path = self._path("img.pgm") + fb.save(path) + + loaded = FrameBuffer.from_file(path) + self.assertEqual(loaded.width, 16) + self.assertEqual(loaded.height, 8) + self.assertEqual(loaded.format, GS2_HMSB) + self.assertEqual(bytes(loaded.buffer), bytes(fb.buffer)) + self.assertEqual(loaded.pixel(2, 2), 3) + + def test_save_appends_extension(self): + fb = FrameBuffer(bytearray((8 + 7) // 8 * 8), 8, 8, MONO_HLSB) + fb.fill(0) + fb.save(self._path("noext")) + self.assertTrue(os.path.exists(self._path("noext.pbm"))) + + def test_rgb565_saves_bmp_signature(self): + fb = FrameBuffer(bytearray(8 * 8 * 2), 8, 8, RGB565) + fb.fill(0x1234) + path = self._path("img.bmp") + fb.save(path) + with open(path, "rb") as f: + self.assertEqual(f.read(2), b"BM") + + def test_gs4_roundtrip(self): + buf = bytearray((8 + 1) // 2 * 8) + buf[0] = 0x1E + buf[4] = 0xF0 + fb = FrameBuffer(buf, 8, 8, GS4_HMSB) + path = self._path("img.pgm") + fb.save(path) + loaded = FrameBuffer.from_file(path) + self.assertEqual(loaded.format, GS4_HMSB) + self.assertEqual(bytes(loaded.buffer), bytes(fb.buffer)) + + def test_gs8_roundtrip(self): + buf = bytearray(8 * 8) + buf[10] = 200 + buf[63] = 42 + fb = FrameBuffer(buf, 8, 8, GS8) + path = self._path("img.pgm") + save_image(fb, path) + loaded = load_image(path) + self.assertEqual(loaded.format, GS8) + self.assertEqual(bytes(loaded.buffer), bytes(fb.buffer)) + + def test_rgb565_roundtrip(self): + fb = FrameBuffer(bytearray(4 * 4 * 2), 4, 4, RGB565) + fb.pixel(1, 2, 0xBEEF) + path = self._path("img.bmp") + fb.save(path) + loaded = load_image(path) + self.assertEqual(loaded.format, RGB565) + self.assertEqual(loaded.pixel(1, 2), 0xBEEF) + self.assertEqual(bytes(loaded.buffer), bytes(fb.buffer)) + + +class TestConverters(_TmpDirTest): + def test_pbm_to_framebuffer(self): + path = self._path("hand.pbm") + with open(path, "wb") as f: + f.write(b"P4\n2 2\n") + f.write(bytes([0b11000000, 0b00000000])) # 1 byte per row, 2 rows + fb = pbm_to_framebuffer(path) + self.assertEqual((fb.width, fb.height), (2, 2)) + self.assertEqual(fb.format, MONO_HLSB) + self.assertEqual(fb.pixel(0, 0), 1) + self.assertEqual(fb.pixel(0, 1), 0) + + def test_pgm_to_framebuffer_gs8(self): + path = self._path("hand.pgm") + with open(path, "wb") as f: + f.write(b"P5\n2 2\n255\n") + f.write(bytes([10, 20, 30, 40])) + fb = pgm_to_framebuffer(path) + self.assertEqual((fb.width, fb.height), (2, 2)) + self.assertEqual(fb.format, GS8) + self.assertEqual(fb.pixel(0, 0), 10) + self.assertEqual(fb.pixel(1, 1), 40) + + def test_pgm_to_framebuffer_gs4(self): + path = self._path("hand.pgm") + with open(path, "wb") as f: + f.write(b"P5\n2 2\n15\n") + f.write(bytes([0x1E, 0x00])) + fb = pgm_to_framebuffer(path) + self.assertEqual((fb.width, fb.height), (2, 2)) + self.assertEqual(fb.format, GS4_HMSB) + self.assertEqual(bytes(fb.buffer), b"\x1e\x00") + + def test_save_unsupported_format_raises(self): + fb = FrameBuffer(bytearray(8), 8, 8, MONO_VLSB) + with self.assertRaises(ValueError): + save_image(fb, self._path("out.pbm")) + + def test_pbm_bad_magic_raises(self): + path = self._path("bad.pbm") + with open(path, "wb") as f: + f.write(b"XX\n1 1\n\x00") + with self.assertRaises(ValueError): + pbm_to_framebuffer(path) + + +class TestBmp565(_TmpDirTest): + def _write_minimal_bmp(self, path, width, height, pixels): + from graphics._bmp565 import write_bmp565_file + + with open(path, "wb") as f: + write_bmp565_file(f, pixels, width, height) + + def test_bmp_to_framebuffer(self): + path = self._path("tiny.bmp") + pixels = bytearray([0x34, 0x12, 0x78, 0x56]) # two RGB565 pixels, top row + self._write_minimal_bmp(path, 2, 1, pixels) + fb = bmp_to_framebuffer(path) + self.assertEqual((fb.width, fb.height), (2, 1)) + self.assertEqual(fb.format, RGB565) + self.assertEqual(fb.pixel(0, 0), 0x1234) + self.assertEqual(fb.pixel(1, 0), 0x5678) + + def test_bmp565_roundtrip(self): + path = self._path("asset.bmp") + pixels = bytearray(4 * 2 * 2) + for i in range(4): + struct.pack_into(" height 8. + data = memoryview(bytearray(256 * 8)) + f = Font(data, 8) + self.assertEqual(f.height, 8) + self.assertEqual(f.font_name, "memoryview") + + +class TestTextHelpers(unittest.TestCase): + def setUp(self): + self.fb = make_fb(w=64, h=32) + self.fb.fill(0) + + def test_text8_returns_area(self): + self.assertEqual(text8(self.fb, "Hi", 0, 0, 0xFFFF), Area(0, 0, 16, 8)) + + def test_text14_returns_area(self): + self.assertEqual(text14(self.fb, "Hi", 0, 0, 0xFFFF), Area(0, 0, 16, 14)) + + def test_text16_returns_area(self): + self.assertEqual(text16(self.fb, "Hi", 0, 0, 0xFFFF), Area(0, 0, 16, 16)) + + def test_text_actually_draws_pixels(self): + text8(self.fb, "A", 0, 0, 0xFFFF) + self.assertGreater(count_set(self.fb), 0) + + def test_text_blank_string_draws_nothing(self): + text8(self.fb, " ", 0, 0, 0xFFFF) + self.assertEqual(count_set(self.fb), 0) + + def test_newline_advances_a_row(self): + bbox = text8(self.fb, "A\nB", 0, 0, 0xFFFF) + # two lines of an 8px-high font + self.assertEqual(bbox.h, 16) + + +class TestTextSelector(unittest.TestCase): + def setUp(self): + self.fb = make_fb(w=64, h=32) + self.fb.fill(0) + + def test_height_dispatch(self): + self.assertEqual(text(self.fb, "X", 0, 0, 0xFFFF, height=8).h, 8) + self.assertEqual(text(self.fb, "X", 0, 0, 0xFFFF, height=14).h, 14) + self.assertEqual(text(self.fb, "X", 0, 0, 0xFFFF, height=16).h, 16) + + def test_unsupported_height_raises(self): + with self.assertRaises(ValueError): + text(self.fb, "X", 0, 0, 0xFFFF, height=13) + + def test_missing_font_file_raises(self): + with self.assertRaises(FileNotFoundError): + Font("/nonexistent/font8x8.bin", 8) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_framebuf_plus.py b/tests/test_framebuf_plus.py new file mode 100644 index 0000000..72d8297 --- /dev/null +++ b/tests/test_framebuf_plus.py @@ -0,0 +1,127 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""Tests for ``graphics.FrameBuffer`` (the ``_framebuf_plus`` wrapper). + +This is the ``FrameBuffer`` exported from the package. Unlike the bare +``framebuf`` base class, every drawing method returns an :class:`graphics.Area` +bounding box and the buffer/format/color_depth are exposed as properties. +""" + +import unittest + +import _env # noqa: F401 +from _support import make_fb + +import graphics +from graphics import ( + GS2_HMSB, + GS4_HMSB, + GS8, + MONO_HLSB, + MONO_HMSB, + MONO_VLSB, + RGB565, + Area, + FrameBuffer, +) + +_DEPTHS = { + MONO_VLSB: 1, + MONO_HLSB: 1, + MONO_HMSB: 1, + RGB565: 16, + GS2_HMSB: 2, + GS4_HMSB: 4, + GS8: 8, +} + + +class TestProperties(unittest.TestCase): + def test_dimensions_and_format(self): + buffer = bytearray(20 * 10 * 2) + fb = FrameBuffer(buffer, 20, 10, RGB565) + self.assertEqual(fb.width, 20) + self.assertEqual(fb.height, 10) + self.assertEqual(fb.format, RGB565) + self.assertIs(fb.buffer, buffer) + + def test_color_depth_for_each_format(self): + for fmt, depth in _DEPTHS.items(): + with self.subTest(format=fmt): + fb = make_fb(fmt, 8, 8) + self.assertEqual(fb.color_depth, depth) + + def test_invalid_format_raises(self): + with self.assertRaises(ValueError): + FrameBuffer(bytearray(64), 4, 4, 99) + + +class TestMethodsReturnArea(unittest.TestCase): + def setUp(self): + self.fb = make_fb(RGB565, 16, 16) + + def test_fill_returns_area(self): + self.assertEqual(self.fb.fill(0), Area(0, 0, 16, 16)) + + def test_fill_rect_returns_area(self): + self.assertEqual(self.fb.fill_rect(2, 3, 4, 5, 0xFFFF), Area(2, 3, 4, 5)) + + def test_pixel_set_returns_area(self): + self.assertEqual(self.fb.pixel(1, 1, 0xFFFF), Area(1, 1, 1, 1)) + + def test_pixel_get_returns_int(self): + self.fb.pixel(1, 1, 0x1234) + self.assertEqual(self.fb.pixel(1, 1), 0x1234) + + def test_hline_returns_area(self): + self.assertEqual(self.fb.hline(0, 0, 5, 0xFFFF), Area(0, 0, 5, 1)) + + def test_vline_returns_area(self): + self.assertEqual(self.fb.vline(0, 0, 5, 0xFFFF), Area(0, 0, 1, 5)) + + def test_rect_returns_area(self): + self.assertEqual(self.fb.rect(1, 1, 5, 6, 0xFFFF), Area(1, 1, 5, 6)) + + def test_line_returns_area(self): + self.assertIsInstance(self.fb.line(0, 0, 5, 5, 0xFFFF), Area) + + def test_ellipse_returns_area(self): + self.assertEqual(self.fb.ellipse(8, 8, 3, 2, 0xFFFF), Area(5, 6, 6, 4)) + + def test_blit_returns_area(self): + src = make_fb(RGB565, 4, 4) + src.fill(0xABCD) + self.assertEqual(self.fb.blit(src, 2, 2), Area(2, 2, 4, 4)) + self.assertEqual(self.fb.pixel(2, 2), 0xABCD) + + def test_scroll_returns_area(self): + self.fb.fill_rect(0, 0, 4, 4, 0xFFFF) + self.assertEqual(self.fb.scroll(1, 0), Area(0, 0, 16, 16)) + + +class TestDrawingEffects(unittest.TestCase): + def setUp(self): + self.fb = make_fb(RGB565, 16, 16) + self.fb.fill(0) + + def test_fill_rect_sets_interior_only(self): + self.fb.fill_rect(4, 4, 3, 3, 0xFFFF) + self.assertEqual(self.fb.pixel(4, 4), 0xFFFF) + self.assertEqual(self.fb.pixel(6, 6), 0xFFFF) + self.assertEqual(self.fb.pixel(7, 7), 0) + + def test_rect_outline_is_hollow(self): + self.fb.rect(2, 2, 6, 6, 0xFFFF) + # corners drawn, centre untouched + self.assertEqual(self.fb.pixel(2, 2), 0xFFFF) + self.assertEqual(self.fb.pixel(7, 7), 0xFFFF) + self.assertEqual(self.fb.pixel(4, 4), 0) + + def test_rect_filled(self): + self.fb.rect(2, 2, 6, 6, 0xFFFF, True) + self.assertEqual(self.fb.pixel(4, 4), 0xFFFF) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_shapes.py b/tests/test_shapes.py new file mode 100644 index 0000000..3eb9464 --- /dev/null +++ b/tests/test_shapes.py @@ -0,0 +1,178 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""Tests for the module-level drawing primitives in ``graphics._shapes``. + +These are the functions re-exported from the package top level (``hline``, +``rect``, ``circle`` ...). They take the canvas as the first argument and +return an :class:`graphics.Area` bounding box. +""" + +from array import array +import unittest + +import _env # noqa: F401 +from _support import count_set, make_fb + +from graphics import ( + Area, + arc, + blit, + circle, + ellipse, + fill, + fill_rect, + gradient_rect, + hline, + line, + pixel, + poly, + polygon, + rect, + round_rect, + triangle, + vline, +) + +_WHITE = 0xFFFF + + +class TestPrimitiveLines(unittest.TestCase): + def setUp(self): + self.c = make_fb(w=16, h=16) + self.c.fill(0) + + def test_pixel(self): + self.assertEqual(pixel(self.c, 3, 4, _WHITE), Area(3, 4, 1, 1)) + self.assertEqual(self.c.pixel(3, 4), _WHITE) + + def test_hline(self): + self.assertEqual(hline(self.c, 1, 2, 5, _WHITE), Area(1, 2, 5, 1)) + self.assertTrue(all(self.c.pixel(x, 2) == _WHITE for x in range(1, 6))) + self.assertEqual(self.c.pixel(6, 2), 0) + + def test_vline(self): + self.assertEqual(vline(self.c, 1, 2, 5, _WHITE), Area(1, 2, 1, 5)) + self.assertTrue(all(self.c.pixel(1, y) == _WHITE for y in range(2, 7))) + + def test_line_diagonal_marks_endpoints(self): + bbox = line(self.c, 0, 0, 5, 5, _WHITE) + self.assertIsInstance(bbox, Area) + self.assertEqual(self.c.pixel(0, 0), _WHITE) + self.assertEqual(self.c.pixel(5, 5), _WHITE) + + +class TestFilledShapes(unittest.TestCase): + def setUp(self): + self.c = make_fb(w=16, h=16) + self.c.fill(0) + + def test_fill(self): + self.assertEqual(fill(self.c, _WHITE), Area(0, 0, 16, 16)) + self.assertEqual(count_set(self.c), 16 * 16) + + def test_fill_rect(self): + self.assertEqual(fill_rect(self.c, 2, 2, 3, 3, _WHITE), Area(2, 2, 3, 3)) + self.assertEqual(count_set(self.c), 9) + + def test_rect_outline_vs_fill(self): + self.assertEqual(rect(self.c, 1, 1, 6, 6, _WHITE), Area(1, 1, 6, 6)) + outline = count_set(self.c) + self.c.fill(0) + rect(self.c, 1, 1, 6, 6, _WHITE, True) + filled = count_set(self.c) + self.assertGreater(filled, outline) + self.assertEqual(filled, 36) + + def test_round_rect_zero_radius_is_rect(self): + self.assertEqual(round_rect(self.c, 0, 0, 10, 8, 0, _WHITE), Area(0, 0, 10, 8)) + + +class TestCurves(unittest.TestCase): + def setUp(self): + self.c = make_fb(w=32, h=32) + self.c.fill(0) + + def test_circle_bbox_and_draws(self): + self.assertEqual(circle(self.c, 16, 16, 5, _WHITE), Area(11, 11, 10, 10)) + self.assertGreater(count_set(self.c), 0) + + def test_ellipse_returns_area(self): + self.assertIsInstance(ellipse(self.c, 16, 16, 6, 4, _WHITE), Area) + self.assertGreater(count_set(self.c), 0) + + def test_arc_returns_area(self): + self.assertIsInstance(arc(self.c, 16, 16, 6, 0, 90, _WHITE), Area) + + +class TestPolygons(unittest.TestCase): + def setUp(self): + self.c = make_fb(w=16, h=16) + self.c.fill(0) + + def test_triangle_outline_and_fill(self): + self.assertIsInstance(triangle(self.c, 0, 0, 8, 0, 4, 6, _WHITE), Area) + outline = count_set(self.c) + self.c.fill(0) + triangle(self.c, 0, 0, 8, 0, 4, 6, _WHITE, True) + self.assertGreater(count_set(self.c), outline) + + def test_poly_with_point_list(self): + bbox = poly(self.c, 0, 0, [(0, 0), (8, 0), (4, 6)], _WHITE) + self.assertEqual(bbox, Area(0, 0, 8, 6)) + self.assertGreater(count_set(self.c), 0) + + def test_poly_requires_three_vertices(self): + with self.assertRaises(ValueError): + poly(self.c, 0, 0, [(0, 0), (1, 1)], _WHITE) + + def test_poly_flat_array_must_be_even(self): + with self.assertRaises(ValueError): + poly(self.c, 0, 0, array("h", [0, 0, 1, 1, 2]), _WHITE) + + def test_polygon_requires_three_points(self): + with self.assertRaises(ValueError): + polygon(self.c, [(0, 0), (1, 1)], 0, 0, _WHITE) + + def test_polygon_returns_area(self): + self.assertEqual( + polygon(self.c, [(0, 0), (8, 0), (4, 6)], 0, 0, _WHITE), + Area(0, 0, 8, 6), + ) + + +class TestGradientAndBlit(unittest.TestCase): + def test_gradient_without_second_color_is_solid_fill(self): + c = make_fb(w=8, h=8) + c.fill(0) + self.assertEqual(gradient_rect(c, 0, 0, 8, 8, 0x1234), Area(0, 0, 8, 8)) + self.assertEqual(c.pixel(0, 0), 0x1234) + self.assertEqual(c.pixel(7, 7), 0x1234) + + def test_vertical_gradient_changes_down_the_rows(self): + c = make_fb(w=8, h=8) + c.fill(0) + gradient_rect(c, 0, 0, 8, 8, 0x0000, 0xFFFF, vertical=True) + self.assertEqual(c.pixel(0, 0), 0) + self.assertNotEqual(c.pixel(0, 7), 0) + + def test_blit_copies_pixels(self): + src = make_fb(w=4, h=4) + src.fill(0xABCD) + dst = make_fb(w=16, h=16) + dst.fill(0) + self.assertEqual(blit(dst, src, 2, 2), Area(2, 2, 4, 4)) + self.assertEqual(dst.pixel(2, 2), 0xABCD) + self.assertEqual(dst.pixel(0, 0), 0) + + def test_blit_fully_out_of_bounds_is_noop(self): + src = make_fb(w=4, h=4) + src.fill(0xABCD) + dst = make_fb(w=16, h=16) + dst.fill(0) + self.assertIsNone(blit(dst, src, 100, 100)) + self.assertEqual(count_set(dst), 0) + + +if __name__ == "__main__": + unittest.main()