{"id":50811716,"url":"https://github.com/jef-sure/esp32-component-dgx","last_synced_at":"2026-06-13T05:05:51.603Z","repository":{"id":357654163,"uuid":"1231071339","full_name":"jef-sure/esp32-component-dgx","owner":"jef-sure","description":"DGX is a small, opinionated display graphics library for microcontrollers.","archived":false,"fork":false,"pushed_at":"2026-05-30T17:10:35.000Z","size":262,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-30T18:10:41.372Z","etag":null,"topics":["8080","display","esp32","gc9a01","graphics","i2c","ili9341","lcd","oled","screen","spi","ssd1306","ssd1351","st7565r","st7735","st7789","st7920","tft","virtual"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jef-sure.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-06T15:42:56.000Z","updated_at":"2026-05-30T17:10:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jef-sure/esp32-component-dgx","commit_stats":null,"previous_names":["jef-sure/esp32-component-dgx"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jef-sure/esp32-component-dgx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jef-sure%2Fesp32-component-dgx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jef-sure%2Fesp32-component-dgx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jef-sure%2Fesp32-component-dgx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jef-sure%2Fesp32-component-dgx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jef-sure","download_url":"https://codeload.github.com/jef-sure/esp32-component-dgx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jef-sure%2Fesp32-component-dgx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34272660,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["8080","display","esp32","gc9a01","graphics","i2c","ili9341","lcd","oled","screen","spi","ssd1306","ssd1351","st7565r","st7735","st7789","st7920","tft","virtual"],"created_at":"2026-06-13T05:05:50.805Z","updated_at":"2026-06-13T05:05:51.597Z","avatar_url":"https://github.com/jef-sure.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DGX\n\nDGX is a small display graphics library for microcontrollers. I started writing\nit years ago, back when most people reached for Adafruit GFX or TFT_eSPI, and\nI wanted something shaped to my own taste: a clean split between bus, screen\nand drawing code, no hidden global state, possibility to use several equal\nscreens simulteneously.\n\nToday it lives as an ESP-IDF component with drivers for a handful of common\npanels, a couple of RAM-backed virtual screens, a UTF-8 text renderer, and a\nsmall offline tool for converting fonts.\n\n## Contents\n\n- [What's in the box](#whats-in-the-box)\n- [How it fits together](#how-it-fits-together)\n- [Getting started](#getting-started)\n- [A minimal example](#a-minimal-example)\n- [Supported panels](#supported-panels)\n- [API reference](#api-reference)\n  - [Drawing primitives](#drawing-primitives)\n  - [Text and fonts](#text-and-fonts)\n  - [Colors](#colors)\n  - [Virtual screens](#virtual-screens-api)\n  - [Arc gauge](#arc-gauge)\n  - [Bus backends](#bus-backends)\n  - [Flush control and batching](#flush-control-and-batching)\n- [Tutorials](#tutorials)\n  - [Flicker-free animation with a virtual screen](#flicker-free-animation-with-a-virtual-screen)\n  - [Driving a monochrome panel](#driving-a-monochrome-panel)\n  - [Building an arc gauge](#building-an-arc-gauge)\n  - [Generating a custom font](#generating-a-custom-font)\n- [Coordinates and orientation](#coordinates-and-orientation)\n- [Build options](#build-options)\n- [Known gaps](#known-gaps)\n- [Repository layout](#repository-layout)\n- [License](#license)\n\n\n## What's in the box\n\n- Panel drivers for ST7735, ST7789, ILI9341, GC9A01, SSD1351, SSD1306, ST7565R\n  and ST7920.\n- SPI, I2C and 8-bit parallel (I80) transports for ESP32.\n- A color RAM-backed virtual screen and a 1-bit monochrome virtual screen.\n  They are useful on their own, as staging buffers for animation, or as shadow\n  buffers behind monochrome controllers.\n- A two-head compositor (`vscreen_2h`) that exposes two child screens as one\n  logical screen.\n- Drawing primitives — pixels, lines, rectangles, circles, filled quads — and\n  an arc gauge helper.\n- UTF-8 text rendering with 8-way orientation, glyph lookup, layout and bounds\n  queries, plus a small \"morph\" helper for animating between glyphs.\n- `font2c`, an offline tool that turns TTF/BDF-style fonts into C source/header\n  pairs, and a set of pre-converted fonts in `src/fonts/`.\n\n## How it fits together\n\nEverything above the transport layer talks to a `dgx_screen_t` vtable. Bus\nbackends produce a `dgx_bus_protocols_t`. Driver constructors return either a\nplain `dgx_screen_t *` for RAM-only screens, or a `dgx_screen_with_bus_t *`\nfor hardware panels. The drawing and font code never touches SPI, I2C or P8\ndirectly — it only goes through the screen vtable. Monochrome panels keep a\nRAM page buffer and flush it to the controller, and the ST7920 driver reuses\nthe color virtual screen the same way.\n\n```text\napplication\n  └─ dgx_draw.h / dgx_font.h / dgx_gauge.h\n        └─ dgx_screen_t  (vtable)\n              ├─ dgx_screen_with_bus_t  → bus backend (SPI / I2C / P8)\n              ├─ dgx_bw_vscreen_t       → 1-bit RAM buffer\n              ├─ dgx_vscreen_t          → color RAM buffer\n              └─ vscreen_2h             → composes two child screens\n```\n\n## Getting started\n\nDGX is a component, not a standalone firmware image. There are two normal ways\nto use it:\n\n1. Drop it into your own ESP-IDF project under `components/` (a git submodule\n   at `components/dgx` works well).\n2. Build the bundled demo in `examples/screen_demo` to see it run end-to-end.\n\nEither way, enable only the pieces you need in `menuconfig`, under the **DGX**\nmenu. Drivers automatically pull in the transports they require.\n\nTo build and flash the demo:\n\n```sh\ncd examples/screen_demo\nidf.py set-target esp32\nidf.py menuconfig\nidf.py build\nidf.py flash monitor\n```\n\nIn your own application, just build the project the normal ESP-IDF way. Once\nDGX is on the component search path, ESP-IDF will pick it up automatically.\n\n## A minimal example\n\nHere's a complete setup for an ST7789 panel over SPI:\n\n```c\n#include \"bus/dgx_spi_esp32.h\"\n#include \"drivers/st7789.h\"\n#include \"dgx_bits.h\"\n#include \"dgx_colors.h\"\n#include \"dgx_draw.h\"\n#include \"dgx_font.h\"\n#include \"fonts/ArialRegular12.h\"\n\n// 1. create a bus\ndgx_bus_protocols_t *bus =\n  dgx_spi_init(SPI2_HOST, SPI_DMA_CH_AUTO,\n               GPIO_NUM_23, GPIO_NUM_19,\n               GPIO_NUM_18, GPIO_NUM_5,\n               GPIO_NUM_27, 40 * 1000 * 1000, 0);\n\n// 2. create a screen on top of that bus\ndgx_screen_t *scr = dgx_st7789_init(bus, GPIO_NUM_33, 16, DgxScreenRGB);\n\n// 3. draw something\ndgx_fill_rectangle(scr, 0, 0, scr-\u003ewidth, scr-\u003eheight,\n                   DGX_BLACK(dgx_rgb_to_16));\ndgx_font_string_utf8_screen(scr, 10, 18, \"Hello world\",\n                            DGX_WHITE(dgx_rgb_to_16),\n                            DgxOutputNormal, 1, ArialRegular12(),\n                            NULL, NULL);\n```\n\nA working version of this lives in\n[examples/screen_demo](examples/screen_demo).\n\n## Supported panels\n\nEach driver returns a `dgx_screen_t *` you can draw on. Color panels take a\npixel depth and a channel order; monochrome panels keep a RAM buffer that DGX\nflushes for you. Use `GPIO_NUM_NC` for any reset/CS line your board does not\nwire.\n\n| Controller | Type | Buses | Constructor | Kconfig option |\n| --- | --- | --- | --- | --- |\n| ST7735 | color TFT | SPI, P8 | `dgx_st7735_init(bus, rst, color_bits, cbo)` | `CONFIG_DGX_ENABLE_SPI_ST7735` |\n| ST7789 | color TFT | SPI, P8 | `dgx_st7789_init(bus, rst, color_bits, cbo)` | `CONFIG_DGX_ENABLE_SPI_ST7789` |\n| ILI9341 | color TFT | SPI, P8 | `dgx_ili9341_init(bus, rst, color_bits, cbo)` | `CONFIG_DGX_ENABLE_SPI_ILI_9341` |\n| GC9A01 | round color TFT | SPI, P8 | `dgx_gc9a01_init(bus, rst, color_bits, cbo)` | `CONFIG_DGX_ENABLE_SPI_GC9A01` |\n| SSD1351 | color OLED | SPI, P8 | `dgx_ssd1351_init(bus, rst, color_bits, cbo)` | `CONFIG_DGX_ENABLE_SSD1351` |\n| SSD1306 | mono OLED | SPI, I2C | `dgx_ssd1306_init(bus, resolution, is_ext_vcc, rst)` | `CONFIG_DGX_ENABLE_SSD1306` |\n| ST7565R | mono LCD | SPI | `dgx_st7565r_init(bus, rst)` | `CONFIG_DGX_ENABLE_ST7565R` |\n| ST7920 | mono LCD | SPI, I2C, P8 | `dgx_st7920_init(bus, rst, cs)` | `CONFIG_DGX_ENABLE_ST7920` |\n\nCommon parameters:\n\n- `bus` — a `dgx_bus_protocols_t *` from `dgx_spi_init()`, `dgx_i2c_init()` or\n  `dgx_p8_init()`.\n- `rst` — reset GPIO, or `GPIO_NUM_NC` if the panel has no reset line.\n- `color_bits` — pixel depth; `16` (RGB565) is the usual choice.\n- `cbo` — channel order, `DgxScreenRGB` or `DgxScreenBGR`.\n\nColor panels also expose an orientation setter\n(`dgx_\u003cdriver\u003e_orientation(scr, dir_x, dir_y, swap_xy)`) that reprograms the\ncontroller's scan direction. A few drivers add extras: GC9A01 has\n`dgx_gc9a01_display_off()` / `dgx_gc9a01_display_on()`, SSD1351 has\n`dgx_ssd1351_brightness()`, and SSD1306 has `dgx_ssd1306_contrast()`.\n\nFor SSD1306, pick a geometry from `ssd1306_resolution_t`: `SSD1306_128X32`,\n`SSD1306_128X64`, `SSD1306_96X16`, `SSD1306_64X48` or `SSD1306_72X40`.\n\n## API reference\n\nEvery drawing and text call takes a `dgx_screen_t *`, so the same code works\nacross hardware panels and virtual screens. Colors are plain `uint32_t` values\npacked for the target pixel format (see [Colors](#colors)).\n\n### Drawing primitives\n\nDeclared in [include/dgx_draw.h](include/dgx_draw.h):\n\n| Function | Description |\n| --- | --- |\n| `dgx_set_pixel(scr, x, y, color)` | Set one pixel. |\n| `dgx_get_pixel(scr, x, y)` | Read one pixel (reliable on virtual screens only). |\n| `dgx_draw_line(scr, x1, y1, x2, y2, color)` | Single-pixel line. |\n| `dgx_draw_line_thick(scr, x1, y1, x2, y2, width, color)` | Thick line with caps. |\n| `dgx_draw_line_mask(scr, x1, y1, x2, y2, color, bg, mask, mask_bits)` | Dashed/dotted line; returns the rotated mask to continue the pattern. |\n| `dgx_fill_rectangle(scr, x, y, w, h, color)` | Filled rectangle. |\n| `dgx_draw_circle(scr, x, y, r, color)` | Circle outline. |\n| `dgx_solid_circle(scr, x, y, r, color)` | Filled circle. |\n| `dgx_draw_triangle_solid(scr, x0, y0, x1, y1, x2, y2, color)` | Filled triangle. |\n| `dgx_draw_polygon4_solid(scr, x0..y3, color)` | Filled simple quadrilateral (convex or concave). |\n\n### Text and fonts\n\nDeclared in [include/dgx_font.h](include/dgx_font.h):\n\n| Function | Description |\n| --- | --- |\n| `dgx_font_string_utf8_screen(scr, x, y, str, color, orientation, scale, font, draw_func, param)` | Render a UTF-8 string. Pass `NULL, NULL` for the last two for default rendering. |\n| `dgx_font_char_to_screen(scr, x, y, codePoint, color, orientation, scale, font, draw_func, param)` | Render one code point. |\n| `dgx_font_string_bounds(str, font, ycorner, height)` | Measure a string; returns width, fills top offset and height. |\n| `dgx_font_find_glyph(codePoint, font, xAdvance)` | Look up a single glyph. |\n| `decodeUTF8next(chr, idx)` | Decode the next UTF-8 code point. |\n| `dgx_font_make_morph_struct(...)` / `dgx_font_make_morph_struct_destroy(...)` | Build/free a glyph-to-glyph morph descriptor for animation. |\n\n`orientation` is a `dgx_output_orientation_t`: `DgxOutputNormal`,\n`DgxOutputMirrorX`, `DgxOutputMirrorY`, `DgxOutputRotate180`,\n`DgxOutputTranspose`, `DgxOutputRotate90CCW`, `DgxOutputRotate90CW` or\n`DgxOutputTransverse`. `scale` is an integer multiplier (`1` = native size).\nEach bundled font header under `include/fonts/` exposes an accessor, e.g.\n`ArialRegular12()`.\n\n### Colors\n\nThe helpers in [include/dgx_colors.h](include/dgx_colors.h) are macros, not\nconstants. You pair them with either the uppercase packing macros from\n[include/dgx_bits.h](include/dgx_bits.h) or the inline `dgx_rgb_to_*()`\nwrappers, so the same color name works across pixel formats:\n\n```c\nDGX_LIGHTGREY(DGX_RGB_16)   // pure macro expansion, RGB565\nDGX_RED(dgx_rgb_to_16)      // inline wrapper, also RGB565\nDGX_RED(DGX_RGB_24)         // 24-bit RGB\nDGX_WHITE(DGX_RGB_12)       // 12-bit packed color\n```\n\nAvailable packers: `DGX_RGB_12` / `dgx_rgb_to_12`, `DGX_RGB_16` /\n`dgx_rgb_to_16`, `DGX_RGB_18` / `dgx_rgb_to_18`, `DGX_RGB_24` /\n`dgx_rgb_to_24`. Named colors include `DGX_BLACK`, `DGX_WHITE`, `DGX_RED`,\n`DGX_GREEN`, `DGX_BLUE`, `DGX_CYAN`, `DGX_MAGENTA`, `DGX_YELLOW`,\n`DGX_ORANGE`, `DGX_NAVY`, `DGX_GOLD`, `DGX_SILVER`, `DGX_SKYBLUE` and more.\nFor most ESP32 TFT panels here, `DGX_RGB_16` is the right choice.\n\n### Virtual screens (API)\n\nVirtual screens let you draw into RAM first and decide later when and where to\npush the result — handy for flicker-free animation, off-screen composition,\nshadow buffers behind monochrome controllers, region copies, and LUT-expanded\n8-bit assets. Declared in\n[include/drivers/vscreen.h](include/drivers/vscreen.h) (color) and\n[include/dgx_bw_screen.h](include/dgx_bw_screen.h) (1-bit):\n\n| Function | Description |\n| --- | --- |\n| `dgx_vscreen_init(width, height, color_bits, cbo)` | Allocate a color RAM screen. |\n| `dgx_vscreen_clone(src)` | Allocate a copy with the same geometry and pixels. |\n| `dgx_vscreen_copy(dst, src)` | Copy a whole same-sized screen. |\n| `dgx_vscreen_to_vscreen(dst, x, y, src, has_transparency)` | Blit one RAM screen onto another. |\n| `dgx_vscreen_to_vscreen_oriented(dst, x, y, src, has_transparency, orientation)` | Blit with rotation/mirroring. |\n| `dgx_vscreen_to_screen(dst, x, y, src)` | Push a RAM screen to any destination (incl. hardware). |\n| `dgx_vscreen_region_to_screen(dst, x, y, src, x_src, y_src, w, h)` | Push a sub-region. |\n| `dgx_vscreen_to_screen_oriented(...)` / `dgx_vscreen_region_to_screen_oriented(...)` | Oriented variants. |\n| `dgx_vscreen8_to_screen16(dst, x, y, src, lut, has_transparency)` | Expand an 8-bit indexed screen into 16-bit through a LUT. |\n| `dgx_bw_init(width, height)` | Allocate a 1-bit monochrome RAM screen. |\n| `dgx_vscreen_2h_init(left, right)` | Compose two screens as one wide logical screen. |\n\nFree any screen with `dgx_screen_destroy(\u0026scr)`.\n\n### Arc gauge\n\nDeclared in [include/dgx_gauge.h](include/dgx_gauge.h):\n\n| Function | Description |\n| --- | --- |\n| `dgx_gauge_init(gauge, scr, cx, cy, inner_radius, width, start_angle, sweep_degrees, min, max, bg_color, color_fn)` | Configure a ring gauge. `color_fn` maps a value to a step color. |\n| `dgx_gauge_set_value(gauge, value)` | Update the value, redrawing only changed steps. |\n| `dgx_gauge_redraw(gauge)` | Redraw the whole gauge. |\n\n### Bus backends\n\nDeclared under [include/bus/](include/bus):\n\n| Function | Description |\n| --- | --- |\n| `dgx_spi_init(host_id, dma_chan, mosi, miso, sclk, cs, dc, clock_speed_hz, cpolpha_mode)` | SPI master bus. |\n| `dgx_i2c_init(i2c_num, i2c_address, sda, sclk, clock_speed_hz)` | I2C master bus. |\n| `dgx_p8_init(d0..d7, wr, rd, cs, dc, pclk_hz)` | 8-bit parallel (I80) bus. |\n\nEach returns a `dgx_bus_protocols_t *` to hand to a driver constructor. A\ndriver pulls in only the transports it needs; enable them in `menuconfig`.\n\n### Flush control and batching\n\nMost DGX drawing APIs flush the touched region automatically by calling\n`scr-\u003eupdate_screen(...)` when the current top-level operation finishes.\nThe `in_progress` field on `dgx_screen_t` is the nesting counter that suppresses\nthose intermediate flushes while a larger operation is still building up.\n\nDeclared in [include/dgx_screen.h](include/dgx_screen.h):\n\n| API | Description |\n| --- | --- |\n| `scr-\u003ein_progress` | Nesting depth for deferred flushes. `0` means a standalone draw may flush immediately; `\u003e 0` means wait until the outermost operation finishes. |\n| `dgx_screen_progress_up(scr)` | Increment the nesting counter before a batched operation. Returns the new depth. |\n| `dgx_screen_progress_down(scr)` | Decrement the nesting counter after a batched operation. Returns the new depth; when it becomes `0`, callers typically issue one final `update_screen(...)` for the combined dirty area. |\n| `dgx_screen_destroy(\u0026scr)` | Destroy a screen allocated by a driver or virtual screen constructor. |\n\nGuidelines:\n\n- Most applications should not touch `in_progress` at all.\n- If you batch manually, prefer `dgx_screen_progress_up()` and\n  `dgx_screen_progress_down()` over modifying the field directly.\n- Always pair every `up` with one `down`.\n- While the counter is positive, track the dirty rectangle yourself and flush\n  once when the counter drops back to `0`.\n\n## Tutorials\n\n### Flicker-free animation with a virtual screen\n\nDraw each frame into RAM, then push the finished frame to the panel in one go.\nThis avoids the visible tearing you get when drawing primitives straight to the\ndisplay line by line.\n\n```c\n#include \"drivers/vscreen.h\"\n#include \"dgx_draw.h\"\n#include \"dgx_colors.h\"\n#include \"dgx_bits.h\"\n\n// off-screen buffer matching the panel format\ndgx_screen_t *frame = dgx_vscreen_init(scr-\u003ewidth, scr-\u003eheight, 16, DgxScreenRGB);\n\nfor (int x = 0; x \u003c scr-\u003ewidth; ++x) {\n    // 1. clear and draw this frame into RAM\n    dgx_fill_rectangle(frame, 0, 0, frame-\u003ewidth, frame-\u003eheight,\n                       DGX_BLACK(dgx_rgb_to_16));\n    dgx_solid_circle(frame, x, frame-\u003eheight / 2, 12,\n                     DGX_CYAN(dgx_rgb_to_16));\n\n    // 2. blit the whole frame to the panel at once\n    dgx_vscreen_to_screen(scr, 0, 0, frame);\n}\n\ndgx_screen_destroy(\u0026frame);\n```\n\nUse `dgx_vscreen_region_to_screen()` to push only the part that changed, or\n`dgx_vscreen_to_screen_oriented()` to rotate the buffer on the way out.\n\n### Driving a monochrome panel\n\nMonochrome controllers keep a 1-bit RAM page buffer. You draw with the same\nprimitives; the driver flushes RAM to the controller through the screen's\n`update_screen` function. In normal use you do not need to call\n`update_screen` yourself, because the draw and font code already does that\nwhen needed. Colors are simply non-zero (on) or zero (off).\n\n```c\n#include \"bus/dgx_i2c_esp32.h\"\n#include \"drivers/ssd1306.h\"\n#include \"dgx_draw.h\"\n#include \"dgx_font.h\"\n#include \"fonts/TerminusTTFMedium12.h\"\n\ndgx_bus_protocols_t *bus =\n    dgx_i2c_init(I2C_NUM_0, 0x3C, GPIO_NUM_21, GPIO_NUM_22, 400000);\n\ndgx_screen_t *oled =\n    dgx_ssd1306_init(bus, SSD1306_128X64, 0, GPIO_NUM_NC);\n\ndgx_fill_rectangle(oled, 0, 0, oled-\u003ewidth, oled-\u003eheight, 0); // clear\ndgx_font_string_utf8_screen(oled, 0, 12, \"Hello OLED\", 1,\n                            DgxOutputNormal, 1, TerminusTTFMedium12(),\n                            NULL, NULL);\n```\n\n### Building an arc gauge\n\n```c\n#include \"dgx_gauge.h\"\n#include \"dgx_colors.h\"\n#include \"dgx_bits.h\"\n\nstatic uint32_t gauge_color(int value) {\n    return value \u003c 70 ? DGX_GREEN(dgx_rgb_to_16)\n                      : DGX_RED(dgx_rgb_to_16);\n}\n\ndgx_gauge_t gauge;\ndgx_gauge_init(\u0026gauge, scr,\n               scr-\u003ewidth / 2, scr-\u003eheight / 2, // center\n               40, 12,                          // inner radius, ring width\n               2.36f, 270,                      // start angle (rad), sweep\n               0, 100,                          // value range\n               DGX_DARKGREY(dgx_rgb_to_16),     // inactive step color\n               gauge_color);\n\ndgx_gauge_set_value(\u0026gauge, 42); // redraws only the steps that changed\n```\n\n### Generating a custom font\n\nWhen the bundled fonts are not enough, `font2c` converts a TTF/BDF font to a\nC source/header pair offline. You need FreeType development headers installed:\n\n```sh\ncc font2c/font2c.c -o font2c/font2c $(pkg-config --cflags --libs freetype2)\n```\n\nRun it with a font file, a pixel size, and one or more inclusive Unicode\nranges:\n\n```sh\n./font2c path/to/YourFont.ttf 16 0x20 0x7e 0x410 0x44f\n./font2c \u003cfont file\u003e \u003csize\u003e \u003cstart range\u003e \u003cend range\u003e [\u003cstart range\u003e \u003cend range\u003e]*\n```\n\nIt writes a `.c` and a matching `.h` named after the font family, style and\nsize. To use the generated font:\n\n1. Move the `.c` file into `src/fonts/` and the `.h` file into\n   `include/fonts/`.\n2. Rerun CMake configure once — fonts are added through `file(GLOB ...)`, so\n   the build system needs to notice the new file.\n3. Include the generated header and pass its accessor to the text API, just\n   like a bundled font.\n\nIf you prefer a GUI workflow, another option is\n[FontCreator](https://github.com/Llerr/FontCreator), which generates embedded\nC font data and lets you edit glyphs interactively. Use its exported `.c` and\n`.h` files the same way: place the source in `src/fonts/`, the header in\n`include/fonts/`, rerun CMake configure once, then include the generated\nheader in your application.\n\n## Coordinates and orientation\n\nVirtual screens use a single canonical layout:\n\n- origin in the top-left, `x` going right, `y` going down;\n- pixels stored row-major at offset `x + y * width`;\n- `set_area`, `write_area` and `read_area` always use those canonical bounds\n  and traverse left-to-right, top-to-bottom.\n\nHardware panel drivers have their own orientation setters\n(`dgx_st7789_orientation()`, `dgx_gc9a01_orientation()` and so on) that\nreprogram the controller's scan direction. Text rendering takes an explicit\n`dgx_output_orientation_t`, which means you can draw rotated or mirrored text\non any screen without touching the framebuffer layout.\n\nThe `dir_x`, `dir_y` and `swap_xy` fields on the screen struct are currently\n*metadata* on virtual screens — they describe the screen but do not transform\nframebuffer access. In practice, treat virtual screens as always stored in\ntheir canonical layout.\n\n## Build options\n\nEverything is wired through Kconfig. Toggle only what you need; drivers pull\nin the transports they require.\n\n| Option | Adds | Notes |\n| --- | --- | --- |\n| `CONFIG_DGX_ENABLE_SPI` | `bus/spi_esp32.c` | SPI transport |\n| `CONFIG_DGX_ENABLE_I2C` | `bus/i2c_esp32.c` | I2C transport |\n| `CONFIG_DGX_ENABLE_P8` | `bus/p8_esp32.c` | 8-bit parallel (I80) transport |\n| `CONFIG_DGX_ENABLE_SPI_ST7735` | `drivers/st7735.c` | needs SPI or P8 |\n| `CONFIG_DGX_ENABLE_SPI_ST7789` | `drivers/st7789.c` | needs SPI or P8 |\n| `CONFIG_DGX_ENABLE_SPI_GC9A01` | `drivers/gc9a01.c` | needs SPI or P8 |\n| `CONFIG_DGX_ENABLE_SPI_ILI_9341` | `drivers/ili9341.c` | needs SPI or P8 |\n| `CONFIG_DGX_ENABLE_SSD1351` | `drivers/ssd1351.c` | needs SPI or P8 |\n| `CONFIG_DGX_ENABLE_SSD1306` | `drivers/ssd1306.c` | needs SPI or I2C; selects `V_BW_SCREEN` |\n| `CONFIG_DGX_ENABLE_ST7565R` | `drivers/st7565r.c` | needs SPI; selects `V_BW_SCREEN` |\n| `CONFIG_DGX_ENABLE_ST7920` | `drivers/st7920.c` | needs SPI/I2C/P8; selects `VSCREEN` |\n| `CONFIG_DGX_ENABLE_V_BW_SCREEN` | `bw_screen.c` | 1-bit virtual screen |\n| `CONFIG_DGX_ENABLE_VSCREEN` | `drivers/vscreen.c` | color RAM-backed screen |\n| `CONFIG_DGX_ENABLE_VSCREEN_2H` | `drivers/vscreen_2h.c` | needs `VSCREEN` |\n\n`dgx_lcd_init.c`, the drawing and font code, and everything in `src/fonts/`\nare always compiled. Fonts are picked up via a `file(GLOB)` on\n`src/fonts/*.c`, so the linker keeps only the font objects your application\nactually references. If you add or remove a font file, rerun CMake configure\nonce.\n\n## Known gaps\n\nA couple of practical limitations are worth knowing up front:\n\n- **Pixel readback is reliable on virtual screens.** They keep the full\n  framebuffer in RAM, so `get_pixel()` behaves as expected there. On physical\n  panels, hardware readback is still incomplete and should not be relied on.\n- **Virtual screens don't honor `dir_x`/`dir_y`/`swap_xy` for framebuffer\n  access.** Those fields describe orientation metadata, but they do not\n  rotate or mirror the stored pixel data.\n\n## Repository layout\n\n```\ninclude/                 public headers\n  bus/                   transport interfaces (SPI, I2C, P8)\n  drivers/               panel drivers + virtual screens\n  fonts/                 generated font headers\nsrc/                     implementations matching include/\n  bus/                   ESP32 bus backends\n  drivers/               panel + virtual screen sources\n  fonts/                 generated font sources (glob-built)\nfont2c/                  offline TTF/BDF -\u003e C font generator\nexamples/screen_demo/    minimal end-to-end example\nKconfig                  feature toggles\nCMakeLists.txt           ESP-IDF component build\n```\n\nSee [CHANGES.md](CHANGES.md) for the release history.\n\n## License\n\nSee [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjef-sure%2Fesp32-component-dgx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjef-sure%2Fesp32-component-dgx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjef-sure%2Fesp32-component-dgx/lists"}