keyboard stuff
at master 1094 lines 43 kB view raw view rendered
1# Quantum Painter {#quantum-painter} 2 3Quantum Painter is the standardised API for graphical displays. It currently includes support for basic drawing primitives, as well as custom images, animations, and fonts. 4 5Due to the complexity, there is no support for Quantum Painter on AVR-based boards. 6 7To enable overall Quantum Painter to be built into your firmware, add the following to `rules.mk`: 8 9```make 10QUANTUM_PAINTER_ENABLE = yes 11QUANTUM_PAINTER_DRIVERS += ...... 12``` 13 14You will also likely need to select an appropriate driver in `rules.mk`, which is listed below. 15 16::: warning 17Quantum Painter is not currently integrated with system-level operations such as when the keyboard goes into suspend. Users will need to handle this manually at the current time. 18::: 19 20The QMK CLI can be used to convert from normal images such as PNG files or animated GIFs, as well as fonts from TTF files. 21 22Supported devices: 23 24| Display Panel | Panel Type | Size | Comms Transport | Driver | 25|----------------|--------------------|------------------|-----------------|------------------------------------------| 26| GC9A01 | RGB LCD (circular) | 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += gc9a01_spi` | 27| ILI9163 | RGB LCD | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9163_spi` | 28| ILI9341 | RGB LCD | 240x320 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9341_spi` | 29| ILI9486 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9486_spi` | 30| ILI9488 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9488_spi` | 31| LD7032 (SPI) | Monochrome OLED | 128x40 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ld7032_spi` | 32| LD7032 (I2C) | Monochrome OLED | 128x40 | I2C | `QUANTUM_PAINTER_DRIVERS += ld7032_i2c` | 33| SSD1351 | RGB OLED | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ssd1351_spi` | 34| ST7735 | RGB LCD | 132x162, 80x160 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7735_spi` | 35| ST7789 | RGB LCD | 240x320, 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7789_spi` | 36| SH1106 (SPI) | Monochrome OLED | 128x64 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += sh1106_spi` | 37| SH1106 (I2C) | Monochrome OLED | 128x64 | I2C | `QUANTUM_PAINTER_DRIVERS += sh1106_i2c` | 38| SSD1306 (SPI) | Monochrome OLED | 128x64 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += sh1106_spi` | 39| SSD1306 (I2C) | Monochrome OLED | 128x32 | I2C | `QUANTUM_PAINTER_DRIVERS += sh1106_i2c` | 40| Surface | Virtual | User-defined | None | `QUANTUM_PAINTER_DRIVERS += surface` | 41 42## Quantum Painter Configuration {#quantum-painter-config} 43 44| Option | Default | Purpose | 45|---------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 46| `QUANTUM_PAINTER_DISPLAY_TIMEOUT` | `30000` | This controls the amount of time (in milliseconds) that all displays will remain on after the last user input. If set to `0`, the display will remain on indefinitely. | 47| `QUANTUM_PAINTER_TASK_THROTTLE` | `1` | This controls the amount of time (in milliseconds) that the Quantum Painter internal task will wait between each execution. Affects animations, display timeout, and LVGL timing if enabled. | 48| `QUANTUM_PAINTER_NUM_IMAGES` | `8` | The maximum number of images/animations that can be loaded at any one time. | 49| `QUANTUM_PAINTER_NUM_FONTS` | `4` | The maximum number of fonts that can be loaded at any one time. | 50| `QUANTUM_PAINTER_CONCURRENT_ANIMATIONS` | `4` | The maximum number of animations that can be executed at the same time. | 51| `QUANTUM_PAINTER_LOAD_FONTS_TO_RAM` | `FALSE` | Whether or not fonts should be loaded to RAM. Relevant for fonts stored in off-chip persistent storage, such as external flash. | 52| `QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE` | `1024` | The limit of the amount of pixel data that can be transmitted in one transaction to the display. Higher values require more RAM on the MCU. | 53| `QUANTUM_PAINTER_SUPPORTS_256_PALETTE` | `FALSE` | If 256-color palettes are supported. Requires significantly more RAM on the MCU. | 54| `QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS` | `FALSE` | If native color range is supported. Requires significantly more RAM on the MCU. | 55| `QUANTUM_PAINTER_DEBUG` | _unset_ | Prints out significant amounts of debugging information to CONSOLE output. Significant performance degradation, use only for debugging. | 56| `QUANTUM_PAINTER_DEBUG_ENABLE_FLUSH_TASK_OUTPUT` | _unset_ | By default, debug output is disabled while the internal task is flushing the display(s). If you want to keep it enabled, add this to your `config.h`. Note: Console will get clogged. | 57 58 59Drivers have their own set of configurable options, and are described in their respective sections. 60 61## Quantum Painter CLI Commands {#quantum-painter-cli} 62 63:::::tabs 64 65==== `qmk painter-convert-graphics` 66 67This command converts images to a format usable by QMK, i.e. the QGF File Format. 68 69**Usage**: 70 71``` 72usage: qmk painter-convert-graphics [-h] [-w] [-d] [-r] -f FORMAT [-o OUTPUT] -i INPUT [-v] 73 74options: 75 -h, --help show this help message and exit 76 -w, --raw Writes out the QGF file as raw data instead of c/h combo. 77 -d, --no-deltas Disables the use of delta frames when encoding animations. 78 -r, --no-rle Disables the use of RLE when encoding images. 79 -f FORMAT, --format FORMAT 80 Output format, valid types: rgb888, rgb565, pal256, pal16, pal4, pal2, mono256, mono16, mono4, mono2 81 -o OUTPUT, --output OUTPUT 82 Specify output directory. Defaults to same directory as input. 83 -i INPUT, --input INPUT 84 Specify input graphic file. 85 -v, --verbose Turns on verbose output. 86``` 87 88The `INPUT` argument can be any image file loadable by Python's Pillow module. Common formats include PNG, or Animated GIF. 89 90The `OUTPUT` argument needs to be a directory, and will default to the same directory as the input argument. 91 92The `FORMAT` argument can be any of the following: 93 94| Format | Meaning | 95|-----------|-------------------------------------------------------------------------------------------| 96| `rgb888` | 16,777,216 colors in 8-8-8 RGB format (requires `QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS`) | 97| `rgb565` | 65,536 colors in 5-6-5 RGB format (requires `QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS`) | 98| `pal256` | 256-color palette (requires `QUANTUM_PAINTER_SUPPORTS_256_PALETTE`) | 99| `pal16` | 16-color palette | 100| `pal4` | 4-color palette | 101| `pal2` | 2-color palette | 102| `mono256` | 256-shade grayscale (requires `QUANTUM_PAINTER_SUPPORTS_256_PALETTE`) | 103| `mono16` | 16-shade grayscale | 104| `mono4` | 4-shade grayscale | 105| `mono2` | 2-shade grayscale | 106 107**Examples**: 108 109``` 110$ cd /home/qmk/qmk_firmware/keyboards/my_keeb 111$ qmk painter-convert-graphics -f mono16 -i my_image.gif -o ./generated/ 112Writing /home/qmk/qmk_firmware/keyboards/my_keeb/generated/my_image.qgf.h... 113Writing /home/qmk/qmk_firmware/keyboards/my_keeb/generated/my_image.qgf.c... 114``` 115 116==== `qmk painter-make-font-image` 117 118This command converts a TTF font to an intermediate format for editing, before converting to the QFF File Format. 119 120**Usage**: 121 122``` 123usage: qmk painter-make-font-image [-h] [-a] [-u UNICODE_GLYPHS] [-n] [-s SIZE] -o OUTPUT -f FONT 124 125optional arguments: 126 -h, --help show this help message and exit 127 -a, --no-aa Disable anti-aliasing on fonts. 128 -u UNICODE_GLYPHS, --unicode-glyphs UNICODE_GLYPHS 129 Also generate the specified unicode glyphs. 130 -n, --no-ascii Disables output of the full ASCII character set (0x20..0x7E), exporting only the glyphs specified. 131 -s SIZE, --size SIZE Specify font size. Default 12. 132 -o OUTPUT, --output OUTPUT 133 Specify output image path. 134 -f FONT, --font FONT Specify input font file. 135``` 136 137The `FONT` argument is generally a TrueType Font file (TTF). 138 139The `OUTPUT` argument is the output image to generate, generally something like `my_font.png`. 140 141The `UNICODE_GLYPHS` argument allows for specifying extra unicode glyphs to generate, and accepts a string. 142 143**Examples**: 144 145``` 146$ qmk painter-make-font-image --font NotoSans-ExtraCondensedBold.ttf --size 11 -o noto11.png --unicode-glyphs "ĄȽɂɻɣɈʣ" 147``` 148 149==== `qmk painter-convert-font-image` 150 151This command converts an intermediate font image to the QFF File Format. 152 153This command expects an image that conforms to the following format: 154 155* Top-left pixel (at `0,0`) is the "delimiter" color: 156 * Each glyph in the font starts when a pixel of this color is found on the first row 157 * The first row is discarded when converting to the QFF format 158* The number of delimited glyphs must match the supplied arguments to the command: 159 * The full ASCII set `0x20..0x7E` (if `--no-ascii` was not specified) 160 * The corresponding number of unicode glyphs if any were specified with `--unicode-glyphs` 161* The order of the glyphs matches the ASCII set, if any, followed by the Unicode glyph set, if any. 162 163**Usage**: 164 165``` 166usage: qmk painter-convert-font-image [-h] [-w] [-r] -f FORMAT [-u UNICODE_GLYPHS] [-n] [-o OUTPUT] [-i INPUT] 167 168options: 169 -h, --help show this help message and exit 170 -w, --raw Writes out the QFF file as raw data instead of c/h combo. 171 -r, --no-rle Disable the use of RLE to minimise converted image size. 172 -f FORMAT, --format FORMAT 173 Output format, valid types: rgb565, pal256, pal16, pal4, pal2, mono256, mono16, mono4, mono2 174 -u UNICODE_GLYPHS, --unicode-glyphs UNICODE_GLYPHS 175 Also generate the specified unicode glyphs. 176 -n, --no-ascii Disables output of the full ASCII character set (0x20..0x7E), exporting only the glyphs specified. 177 -o OUTPUT, --output OUTPUT 178 Specify output directory. Defaults to same directory as input. 179 -i INPUT, --input INPUT 180 Specify input graphic file. 181``` 182 183The same arguments for `--no-ascii` and `--unicode-glyphs` need to be specified, as per `qmk painter-make-font-image`. 184 185**Examples**: 186 187``` 188$ cd /home/qmk/qmk_firmware/keyboards/my_keeb 189$ qmk painter-convert-font-image --input noto11.png -f mono4 --unicode-glyphs "ĄȽɂɻɣɈʣ" 190Writing /home/qmk/qmk_firmware/keyboards/my_keeb/generated/noto11.qff.h... 191Writing /home/qmk/qmk_firmware/keyboards/my_keeb/generated/noto11.qff.c... 192``` 193 194::::: 195 196## Quantum Painter Display Drivers {#quantum-painter-drivers} 197 198::::::tabs 199 200===== LCD 201 202Most TFT display panels use a 5-pin interface -- SPI SCK, SPI MOSI, SPI CS, D/C, and RST pins. 203 204For these displays, QMK's `spi_master` must already be correctly configured for the platform you're building for. 205 206The pin assignments for SPI CS, D/C, and RST are specified during device construction. 207 208:::::tabs 209 210==== GC9A01 211 212Enabling support for the GC9A01 in Quantum Painter is done by adding the following to `rules.mk`: 213 214```make 215QUANTUM_PAINTER_ENABLE = yes 216QUANTUM_PAINTER_DRIVERS += gc9a01_spi 217``` 218 219Creating a GC9A01 device in firmware can then be done with the following API: 220 221```c 222painter_device_t qp_gc9a01_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 223``` 224 225The device handle returned from the `qp_gc9a01_make_spi_device` function can be used to perform all other drawing operations. 226 227The maximum number of displays can be configured by changing the following in your `config.h` (default is 1): 228 229```c 230// 3 displays: 231#define GC9A01_NUM_DEVICES 3 232``` 233 234Native color format rgb565 is compatible with GC9A01 235 236==== ILI9163 237 238Enabling support for the ILI9163 in Quantum Painter is done by adding the following to `rules.mk`: 239 240```make 241QUANTUM_PAINTER_ENABLE = yes 242QUANTUM_PAINTER_DRIVERS += ili9163_spi 243``` 244 245Creating a ILI9163 device in firmware can then be done with the following API: 246 247```c 248painter_device_t qp_ili9163_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 249``` 250 251The device handle returned from the `qp_ili9163_make_spi_device` function can be used to perform all other drawing operations. 252 253The maximum number of displays can be configured by changing the following in your `config.h` (default is 1): 254 255```c 256// 3 displays: 257#define ILI9163_NUM_DEVICES 3 258``` 259 260Native color format rgb565 is compatible with ILI9163 261 262==== ILI9341 263 264Enabling support for the ILI9341 in Quantum Painter is done by adding the following to `rules.mk`: 265 266```make 267QUANTUM_PAINTER_ENABLE = yes 268QUANTUM_PAINTER_DRIVERS += ili9341_spi 269``` 270 271Creating a ILI9341 device in firmware can then be done with the following API: 272 273```c 274painter_device_t qp_ili9341_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 275``` 276 277The device handle returned from the `qp_ili9341_make_spi_device` function can be used to perform all other drawing operations. 278 279The maximum number of displays can be configured by changing the following in your `config.h` (default is 1): 280 281```c 282// 3 displays: 283#define ILI9341_NUM_DEVICES 3 284``` 285 286Native color format rgb565 is compatible with ILI9341 287 288==== ILI9486 289 290Enabling support for the ILI9486 in Quantum Painter is done by adding the following to `rules.mk`: 291 292```make 293QUANTUM_PAINTER_ENABLE = yes 294QUANTUM_PAINTER_DRIVERS += ili9486_spi 295``` 296 297Creating a ILI9486 device in firmware can then be done with the following API: 298 299```c 300painter_device_t qp_ili9486_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 301``` 302 303There's another variant for this [Waveshare module](https://www.waveshare.com/wiki/3.5inch_TFT_Touch_Shield), because it has a quirky SPI->Parallel converter. You can create it with: 304 305```c 306painter_device_t qp_ili9486_make_spi_waveshare_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 307``` 308 309The device handle returned from these functions can be used to perform all other drawing operations. 310 311The maximum number of displays can be configured by changing the following in your `config.h` (default is 1): 312 313```c 314// 3 displays: 315#define ILI9486_NUM_DEVICES 3 316``` 317 318Native color format rgb888 is compatible with ILI9486 319Native color format rgb565 is compatible with ILI9486 Waveshare 320 321==== ILI9488 322 323Enabling support for the ILI9488 in Quantum Painter is done by adding the following to `rules.mk`: 324 325```make 326QUANTUM_PAINTER_ENABLE = yes 327QUANTUM_PAINTER_DRIVERS += ili9488_spi 328``` 329 330Creating a ILI9488 device in firmware can then be done with the following API: 331 332```c 333painter_device_t qp_ili9488_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 334``` 335 336The device handle returned from the `qp_ili9488_make_spi_device` function can be used to perform all other drawing operations. 337 338The maximum number of displays can be configured by changing the following in your `config.h` (default is 1): 339 340```c 341// 3 displays: 342#define ILI9488_NUM_DEVICES 3 343``` 344 345Native color format rgb888 is compatible with ILI9488 346 347==== ST7735 348 349Enabling support for the ST7735 in Quantum Painter is done by adding the following to `rules.mk`: 350 351```make 352QUANTUM_PAINTER_ENABLE = yes 353QUANTUM_PAINTER_DRIVERS += st7735_spi 354``` 355 356Creating a ST7735 device in firmware can then be done with the following API: 357 358```c 359painter_device_t qp_st7735_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 360``` 361 362The device handle returned from the `qp_st7735_make_spi_device` function can be used to perform all other drawing operations. 363 364The maximum number of displays can be configured by changing the following in your `config.h` (default is 1): 365 366```c 367// 3 displays: 368#define ST7735_NUM_DEVICES 3 369``` 370 371Native color format rgb565 is compatible with ST7735 372 373::: warning 374Some ST7735 devices are known to have different drawing offsets -- despite being a 132x162 pixel display controller internally, some display panels are only 80x160, or smaller. These may require an offset to be applied; see `qp_set_viewport_offsets` above for information on how to override the offsets if they aren't correctly rendered. 375::: 376 377==== ST7789 378 379Enabling support for the ST7789 in Quantum Painter is done by adding the following to `rules.mk`: 380 381```make 382QUANTUM_PAINTER_ENABLE = yes 383QUANTUM_PAINTER_DRIVERS += st7789_spi 384``` 385 386Creating a ST7789 device in firmware can then be done with the following API: 387 388```c 389painter_device_t qp_st7789_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 390``` 391 392The device handle returned from the `qp_st7789_make_spi_device` function can be used to perform all other drawing operations. 393 394The maximum number of displays can be configured by changing the following in your `config.h` (default is 1): 395 396```c 397// 3 displays: 398#define ST7789_NUM_DEVICES 3 399``` 400 401Native color format rgb565 is compatible with ST7789 402 403::: warning 404Some ST7789 devices are known to have different drawing offsets -- despite being a 240x320 pixel display controller internally, some display panels are only 240x240, or smaller. These may require an offset to be applied; see `qp_set_viewport_offsets` above for information on how to override the offsets if they aren't correctly rendered. 405::: 406 407::::: 408 409===== OLED 410 411OLED displays tend to use 5-pin SPI when at larger resolutions, or when using color -- SPI SCK, SPI MOSI, SPI CS, D/C, and RST pins. Smaller OLEDs may use I2C instead. 412 413When using these displays, either `spi_master` or `i2c_master` must already be correctly configured for both the platform and panel you're building for. 414 415For SPI, the pin assignments for SPI CS, D/C, and RST are specified during device construction -- for I2C the panel's address is specified instead. 416 417:::::tabs 418 419==== SSD1351 420 421Enabling support for the SSD1351 in Quantum Painter is done by adding the following to `rules.mk`: 422 423```make 424QUANTUM_PAINTER_ENABLE = yes 425QUANTUM_PAINTER_DRIVERS += ssd1351_spi 426``` 427 428Creating a SSD1351 device in firmware can then be done with the following API: 429 430```c 431painter_device_t qp_ssd1351_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 432``` 433 434The device handle returned from the `qp_ssd1351_make_spi_device` function can be used to perform all other drawing operations. 435 436The maximum number of displays can be configured by changing the following in your `config.h` (default is 1): 437 438```c 439// 3 displays: 440#define SSD1351_NUM_DEVICES 3 441``` 442 443Native color format rgb565 is compatible with SSD1351 444 445==== SH1106 446 447Enabling support for the SH1106 in Quantum Painter is done by adding the following to `rules.mk`: 448 449```make 450QUANTUM_PAINTER_ENABLE = yes 451# For SPI: 452QUANTUM_PAINTER_DRIVERS += sh1106_spi 453# For I2C: 454QUANTUM_PAINTER_DRIVERS += sh1106_i2c 455``` 456 457Creating a SH1106 device in firmware can then be done with the following APIs: 458 459```c 460// SPI-based SH1106: 461painter_device_t qp_sh1106_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 462// I2C-based SH1106: 463painter_device_t qp_sh1106_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address); 464``` 465 466The device handle returned from the `qp_sh1106_make_???_device` function can be used to perform all other drawing operations. 467 468The maximum number of displays of each type can be configured by changing the following in your `config.h` (default is 1): 469 470```c 471// 3 SPI displays: 472#define SH1106_NUM_SPI_DEVICES 3 473// 3 I2C displays: 474#define SH1106_NUM_I2C_DEVICES 3 475``` 476 477Native color format mono2 is compatible with SH1106 478 479==== SSD1306 480 481SSD1306 and SH1106 are almost entirely identical, to the point of being indisinguishable by Quantum Painter. Enable SH1106 support in Quantum Painter and create SH1106 devices in firmware to perform drawing operations on SSD1306 displays. 482 483==== LD7032 484 485Enabling support for the LD7032 in Quantum Painter is done by adding the following to `rules.mk`: 486 487```make 488QUANTUM_PAINTER_ENABLE = yes 489# For SPI: 490QUANTUM_PAINTER_DRIVERS += ld7032_spi 491# For I2C: 492QUANTUM_PAINTER_DRIVERS += ld7032_i2c 493``` 494 495Creating a SH1106 device in firmware can then be done with the following APIs: 496 497```c 498// SPI-based LD7032: 499painter_device_t qp_ld7032_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode); 500// I2C-based LD7032: 501painter_device_t qp_ld7032_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address); 502``` 503 504The device handle returned from the `qp_ld7032_make_???_device` function can be used to perform all other drawing operations. 505 506The maximum number of displays of each type can be configured by changing the following in your `config.h` (default is 1): 507 508```c 509// 3 SPI displays: 510#define LD7032_NUM_SPI_DEVICES 3 511// 3 I2C displays: 512#define LD7032_NUM_I2C_DEVICES 3 513``` 514 515Native color format mono2 is compatible with LD7032. 516 517::::: 518 519===== Surface 520 521Quantum Painter has a surface driver which is able to target a buffer in RAM. In general, surfaces keep track of the "dirty" region -- the area that has been drawn to since the last flush -- so that when transferring to the display they can transfer the minimal amount of data to achieve the end result. 522 523::: warning 524These generally require significant amounts of RAM, so at large sizes and/or higher bit depths, they may not be usable on all MCUs. 525::: 526 527Enabling support for surfaces in Quantum Painter is done by adding the following to `rules.mk`: 528 529```make 530QUANTUM_PAINTER_ENABLE = yes 531QUANTUM_PAINTER_DRIVERS += surface 532``` 533 534Creating a surface in firmware can then be done with the following APIs: 535 536```c 537// 24bpp RGB888 surface: 538painter_device_t qp_make_rgb888_surface(uint16_t panel_width, uint16_t panel_height, void *buffer); 539// 16bpp RGB565 surface: 540painter_device_t qp_make_rgb565_surface(uint16_t panel_width, uint16_t panel_height, void *buffer); 541// 1bpp monochrome surface: 542painter_device_t qp_make_mono1bpp_surface(uint16_t panel_width, uint16_t panel_height, void *buffer); 543``` 544 545The `buffer` is a user-supplied area of memory, which can be statically allocated using `SURFACE_REQUIRED_BUFFER_BYTE_SIZE`: 546 547```c 548// Buffer required for a 240x80 16bpp surface: 549uint8_t framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(240, 80, 16)]; 550``` 551 552The device handle returned from the `qp_make_?????_surface` function can be used to perform all other drawing operations. 553 554Example: 555 556```c 557static painter_device_t my_surface; 558static uint8_t my_framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(240, 80, 16)]; // Allocate a buffer for a 16bpp 240x80 RGB565 display 559void keyboard_post_init_kb(void) { 560 my_surface = qp_rgb565_make_surface(240, 80, my_framebuffer); 561 qp_init(my_surface, QP_ROTATION_0); 562 keyboard_post_init_user(); 563} 564``` 565 566The maximum number of surfaces can be configured by changing the following in your `config.h` (default is 1): 567 568```c 569// 3 surfaces: 570#define SURFACE_NUM_DEVICES 3 571``` 572 573To transfer the contents of the surface to another display of the same pixel format, the following API can be invoked: 574 575```c 576bool qp_surface_draw(painter_device_t surface, painter_device_t display, uint16_t x, uint16_t y, bool entire_surface); 577``` 578 579The `surface` is the surface to copy out from. The `display` is the target display to draw into. `x` and `y` are the target location to draw the surface pixel data. Under normal circumstances, the location should be consistent, as the dirty region is calculated with respect to the `x` and `y` coordinates -- changing those will result in partial, overlapping draws. `entire_surface` whether the entire surface should be drawn, instead of just the dirty region. 580 581::: warning 582The surface and display panel must have the same native pixel format. 583::: 584 585::: tip 586Calling `qp_flush()` on the surface resets its dirty region. Copying the surface contents to the display also automatically resets the dirty region. 587::: 588 589:::::: 590 591## Quantum Painter Drawing API {#quantum-painter-api} 592 593All APIs require a `painter_device_t` object as their first parameter -- this object comes from the specific device initialisation, and instructions on creating it can be found in each driver's respective section. 594 595To use any of the APIs, you need to include `qp.h`: 596```c 597#include <qp.h> 598``` 599 600::::::tabs 601 602===== General Notes 603 604The coordinate system used in Quantum Painter generally accepts `left`, `top`, `right`, and `bottom` instead of x/y/width/height, and each coordinate is inclusive of where pixels should be drawn. This is required as some datatypes used by display panels have a maximum value of `255` -- for any value or geometry extent that matches `256`, this would be represented as a `0`, instead. 605 606::: tip 607Drawing a horizontal line 8 pixels long, starting from 4 pixels inside the left side of the display, will need `left=4`, `right=11`. 608::: 609 610All color data matches the standard QMK HSV triplet definitions: 611 612* Hue is of the range `0...255` and is internally mapped to 0...360 degrees. 613* Saturation is of the range `0...255` and is internally mapped to 0...100% saturation. 614* Value is of the range `0...255` and is internally mapped to 0...100% brightness. 615 616::: tip 617Colors used in Quantum Painter are not subject to the RGB lighting CIE curve, if it is enabled. 618::: 619 620===== Device Control 621 622:::::tabs 623 624==== Display Initialisation 625 626```c 627bool qp_init(painter_device_t device, painter_rotation_t rotation); 628``` 629 630The `qp_init` function is used to initialise a display device after it has been created. This accepts a rotation parameter (`QP_ROTATION_0`, `QP_ROTATION_90`, `QP_ROTATION_180`, `QP_ROTATION_270`), which makes sure that the orientation of what's drawn on the display is correct. 631 632```c 633static painter_device_t display; 634void keyboard_post_init_kb(void) { 635 display = qp_make_.......; // Create the display 636 qp_init(display, QP_ROTATION_0); // Initialise the display 637} 638``` 639 640==== Display Power 641 642```c 643bool qp_power(painter_device_t device, bool power_on); 644``` 645 646The `qp_power` function instructs the display whether or not the display panel should be on or off. 647 648::: warning 649If there is a separate backlight controlled through the normal QMK backlight API, this is not controlled by the `qp_power` function and needs to be manually handled elsewhere. 650::: 651 652```c 653static uint8_t last_backlight = 255; 654void suspend_power_down_user(void) { 655 if (last_backlight == 255) { 656 last_backlight = get_backlight_level(); 657 } 658 backlight_set(0); 659 rgb_matrix_set_suspend_state(true); 660 qp_power(display, false); 661} 662 663void suspend_wakeup_init_user(void) { 664 qp_power(display, true); 665 rgb_matrix_set_suspend_state(false); 666 if (last_backlight != 255) { 667 backlight_set(last_backlight); 668 } 669 last_backlight = 255; 670} 671``` 672 673==== Display Clear 674 675```c 676bool qp_clear(painter_device_t device); 677``` 678 679The `qp_clear` function clears the display's screen. 680 681==== Display Flush 682 683```c 684bool qp_flush(painter_device_t device); 685``` 686 687The `qp_flush` function ensures that all drawing operations are "pushed" to the display. This should be done as the last operation whenever a sequence of draws occur, and guarantees that any changes are applied. 688 689::: warning 690Some display panels may seem to work even without a call to `qp_flush` -- this may be because the driver cannot queue drawing operations and needs to display them immediately when invoked. In general, calling `qp_flush` at the end is still considered "best practice". 691::: 692 693```c 694void housekeeping_task_user(void) { 695 static uint32_t last_draw = 0; 696 if (timer_elapsed32(last_draw) > 33) { // Throttle to 30fps 697 last_draw = timer_read32(); 698 // Draw a rect based off the current RGB color 699 qp_rect(display, 0, 7, 0, 239, rgb_matrix_get_hue(), 255, 255); 700 qp_flush(display); 701 } 702} 703``` 704 705::::: 706 707===== Drawing Primitives 708 709:::::tabs 710 711==== Set Pixel 712 713```c 714bool qp_setpixel(painter_device_t device, uint16_t x, uint16_t y, uint8_t hue, uint8_t sat, uint8_t val); 715``` 716 717The `qp_setpixel` can be used to set a specific pixel on the screen to the supplied color. 718 719::: tip 720Using `qp_setpixel` for large amounts of drawing operations is inefficient and should be avoided unless they cannot be achieved with other drawing APIs. 721::: 722 723```c 724void housekeeping_task_user(void) { 725 static uint32_t last_draw = 0; 726 if (timer_elapsed32(last_draw) > 33) { // Throttle to 30fps 727 last_draw = timer_read32(); 728 // Draw a 240px high vertical rainbow line on X=0: 729 for (int i = 0; i < 239; ++i) { 730 qp_setpixel(display, 0, i, i, 255, 255); 731 } 732 qp_flush(display); 733 } 734} 735``` 736 737==== Draw Line 738 739```c 740bool qp_line(painter_device_t device, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t hue, uint8_t sat, uint8_t val); 741``` 742 743The `qp_line` can be used to draw lines on the screen with the supplied color. 744 745```c 746void housekeeping_task_user(void) { 747 static uint32_t last_draw = 0; 748 if (timer_elapsed32(last_draw) > 33) { // Throttle to 30fps 749 last_draw = timer_read32(); 750 // Draw 8px-wide rainbow down the left side of the display 751 for (int i = 0; i < 239; ++i) { 752 qp_line(display, 0, i, 7, i, i, 255, 255); 753 } 754 qp_flush(display); 755 } 756} 757``` 758 759==== Draw Rect 760 761```c 762bool qp_rect(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom, uint8_t hue, uint8_t sat, uint8_t val, bool filled); 763``` 764 765The `qp_rect` can be used to draw rectangles on the screen with the supplied color, with or without a background fill. If not filled, any pixels inside the rectangle will be left as-is. 766 767```c 768void housekeeping_task_user(void) { 769 static uint32_t last_draw = 0; 770 if (timer_elapsed32(last_draw) > 33) { // Throttle to 30fps 771 last_draw = timer_read32(); 772 // Draw 8px-wide rainbow filled rectangles down the left side of the display 773 for (int i = 0; i < 239; i+=8) { 774 qp_rect(display, 0, i, 7, i+7, i, 255, 255, true); 775 } 776 qp_flush(display); 777 } 778} 779``` 780 781==== Draw Circle 782 783```c 784bool qp_circle(painter_device_t device, uint16_t x, uint16_t y, uint16_t radius, uint8_t hue, uint8_t sat, uint8_t val, bool filled); 785``` 786 787The `qp_circle` can be used to draw circles on the screen with the supplied color, with or without a background fill. If not filled, any pixels inside the circle will be left as-is. 788 789```c 790void housekeeping_task_user(void) { 791 static uint32_t last_draw = 0; 792 if (timer_elapsed32(last_draw) > 33) { // Throttle to 30fps 793 last_draw = timer_read32(); 794 // Draw r=4 filled circles down the left side of the display 795 for (int i = 0; i < 239; i+=8) { 796 qp_circle(display, 4, 4+i, 4, i, 255, 255, true); 797 } 798 qp_flush(display); 799 } 800} 801``` 802 803==== Draw Ellipse 804 805```c 806bool qp_ellipse(painter_device_t device, uint16_t x, uint16_t y, uint16_t sizex, uint16_t sizey, uint8_t hue, uint8_t sat, uint8_t val, bool filled); 807``` 808 809The `qp_ellipse` can be used to draw ellipses on the screen with the supplied color, with or without a background fill. If not filled, any pixels inside the ellipses will be left as-is. 810 811```c 812void housekeeping_task_user(void) { 813 static uint32_t last_draw = 0; 814 if (timer_elapsed32(last_draw) > 33) { // Throttle to 30fps 815 last_draw = timer_read32(); 816 // Draw 16x8 filled ellipses down the left side of the display 817 for (int i = 0; i < 239; i+=8) { 818 qp_ellipse(display, 8, 4+i, 16, 8, i, 255, 255, true); 819 } 820 qp_flush(display); 821 } 822} 823``` 824 825::::: 826 827===== Image Functions 828 829Making an image available for use requires compiling it into your firmware. To do so, assuming you've created `my_image.qgf.c` and `my_image.qgf.h` as per the CLI examples above, you'd add the following to your `rules.mk`: 830 831```make 832SRC += my_image.qgf.c 833``` 834 835...and in your `keymap.c`, you'd add to the top of the file: 836```c 837#include "my_image.qgf.h" 838``` 839 840:::::tabs 841 842==== Load Image 843 844```c 845painter_image_handle_t qp_load_image_mem(const void *buffer); 846``` 847 848The `qp_load_image_mem` function loads a QGF image from memory or flash. 849 850`qp_load_image_mem` returns a handle to the loaded image, which can then be used to draw to the screen using `qp_drawimage`, `qp_drawimage_recolor`, `qp_animate`, or `qp_animate_recolor`. If an image is no longer required, it can be unloaded by calling `qp_close_image` below. 851 852See the [CLI Commands](quantum_painter#quantum-painter-cli) for instructions on how to convert images to [QGF](quantum_painter_qgf). 853 854::: tip 855The total number of images available to load at any one time is controlled by the configurable option `QUANTUM_PAINTER_NUM_IMAGES` in the table above. If more images are required, the number should be increased in `config.h`. 856::: 857 858Image information is available through accessing the handle: 859 860| Property | Accessor | 861|-------------|----------------------| 862| Width | `image->width` | 863| Height | `image->height` | 864| Frame Count | `image->frame_count` | 865 866==== Unload Image 867 868```c 869bool qp_close_image(painter_image_handle_t image); 870``` 871 872The `qp_close_image` function releases resources related to the loading of the supplied image. 873 874==== Draw image 875 876```c 877bool qp_drawimage(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image); 878bool qp_drawimage_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg); 879``` 880 881The `qp_drawimage` and `qp_drawimage_recolor` functions draw the supplied image to the screen at the supplied location, with the latter function allowing for monochrome-based images to be recolored. 882 883```c 884// Draw an image on the bottom-right of the 240x320 display on initialisation 885static painter_image_handle_t my_image; 886void keyboard_post_init_kb(void) { 887 my_image = qp_load_image_mem(gfx_my_image); 888 if (my_image != NULL) { 889 qp_drawimage(display, (240 - my_image->width), (320 - my_image->height), my_image); 890 } 891} 892``` 893 894==== Animate Image 895 896```c 897deferred_token qp_animate(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image); 898deferred_token qp_animate_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg); 899``` 900 901The `qp_animate` and `qp_animate_recolor` functions draw the supplied image to the screen at the supplied location, with the latter function allowing for monochrome-based animations to be recolored. They also set up internal timing such that each frame is rendered at the correct time as per the animated image. 902 903Once an image has been set to animate, it will loop indefinitely until stopped, with no user intervention required. 904 905Both functions return a `deferred_token`, which can then be used to stop the animation, using `qp_stop_animation` below. 906 907```c 908// Animate an image on the bottom-right of the 240x320 display on initialisation 909static painter_image_handle_t my_image; 910static deferred_token my_anim; 911void keyboard_post_init_kb(void) { 912 my_image = qp_load_image_mem(gfx_my_image); 913 if (my_image != NULL) { 914 my_anim = qp_animate(display, (240 - my_image->width), (320 - my_image->height), my_image); 915 } 916} 917``` 918 919==== Stop Animation 920 921```c 922void qp_stop_animation(deferred_token anim_token); 923``` 924 925The `qp_stop_animation` function stops the previously-started animation. 926```c 927void housekeeping_task_user(void) { 928 if (some_random_stop_reason) { 929 qp_stop_animation(my_anim); 930 } 931} 932``` 933 934::::: 935 936===== Font Functions 937 938Making a font available for use requires compiling it into your firmware. To do so, assuming you've created `my_font.qff.c` and `my_font.qff.h` as per the CLI examples above, you'd add the following to your `rules.mk`: 939 940```make 941SRC += noto11.qff.c 942``` 943 944...and in your `keymap.c`, you'd add to the top of the file: 945```c 946#include "noto11.qff.h" 947``` 948 949:::::tabs 950 951==== Load Font 952 953```c 954painter_font_handle_t qp_load_font_mem(const void *buffer); 955``` 956 957The `qp_load_font_mem` function loads a QFF font from memory or flash. 958 959`qp_load_font_mem` returns a handle to the loaded font, which can then be measured using `qp_textwidth`, or drawn to the screen using `qp_drawtext`, or `qp_drawtext_recolor`. If a font is no longer required, it can be unloaded by calling `qp_close_font` below. 960 961See the [CLI Commands](quantum_painter#quantum-painter-cli) for instructions on how to convert TTF fonts to [QFF](quantum_painter_qff). 962 963::: tip 964The total number of fonts available to load at any one time is controlled by the configurable option `QUANTUM_PAINTER_NUM_FONTS` in the table above. If more fonts are required, the number should be increased in `config.h`. 965::: 966 967Font information is available through accessing the handle: 968 969| Property | Accessor | 970|-------------|----------------------| 971| Line Height | `image->line_height` | 972 973==== Unload Font 974 975```c 976bool qp_close_font(painter_font_handle_t font); 977``` 978 979The `qp_close_font` function releases resources related to the loading of the supplied font. 980 981==== Measure Text 982 983```c 984int16_t qp_textwidth(painter_font_handle_t font, const char *str); 985``` 986 987The `qp_textwidth` function allows measurement of how many pixels wide the supplied string would result in, for the given font. 988 989==== Draw Text 990 991```c 992int16_t qp_drawtext(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str); 993int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg); 994``` 995 996The `qp_drawtext` and `qp_drawtext_recolor` functions draw the supplied string to the screen at the given location using the font supplied, with the latter function allowing for monochrome-based fonts to be recolored. 997 998```c 999// Draw a text message on the bottom-right of the 240x320 display on initialisation 1000static painter_font_handle_t my_font; 1001void keyboard_post_init_kb(void) { 1002 my_font = qp_load_font_mem(font_noto11); 1003 if (my_font != NULL) { 1004 static const char *text = "Hello from QMK!"; 1005 int16_t width = qp_textwidth(my_font, text); 1006 qp_drawtext(display, (240 - width), (320 - my_font->line_height), my_font, text); 1007 } 1008} 1009``` 1010 1011::::: 1012 1013===== Advanced Functions 1014 1015:::::tabs 1016 1017==== Getters 1018 1019These functions allow external code to retrieve the current width, height, rotation, and drawing offsets. 1020 1021::::tabs 1022 1023=== Width 1024 1025```c 1026uint16_t qp_get_width(painter_device_t device); 1027``` 1028 1029=== Height 1030 1031```c 1032uint16_t qp_get_height(painter_device_t device); 1033``` 1034 1035=== Rotation 1036 1037```c 1038painter_rotation_t qp_get_rotation(painter_device_t device); 1039``` 1040 1041=== Offset X 1042 1043```c 1044uint16_t qp_get_offset_x(painter_device_t device); 1045``` 1046 1047=== Offset Y 1048 1049```c 1050uint16_t qp_get_offset_y(painter_device_t device); 1051``` 1052 1053=== Everything 1054 1055Convenience function to call all the previous ones at once. 1056Note: You can pass `NULL` for the values you are not interested in. 1057 1058```c 1059void qp_get_geometry(painter_device_t device, uint16_t *width, uint16_t *height, painter_rotation_t *rotation, uint16_t *offset_x, uint16_t *offset_y); 1060``` 1061 1062:::: 1063 1064==== Set Viewport Offsets 1065 1066```c 1067void qp_set_viewport_offsets(painter_device_t device, uint16_t offset_x, uint16_t offset_y); 1068``` 1069 1070The `qp_set_viewport_offsets` function can be used to offset all subsequent drawing operations. For example, if a display controller is internally 240x320, but the display panel is 240x240 and has a Y offset of 80 pixels, you could invoke `qp_set_viewport_offsets(display, 0, 80);` and the drawing positioning would be corrected. 1071 1072==== Set Viewport 1073 1074```c 1075bool qp_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom); 1076``` 1077 1078The `qp_viewport` function controls where raw pixel data is written to. 1079 1080==== Stream Pixel Data 1081 1082```c 1083bool qp_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count); 1084``` 1085 1086The `qp_pixdata` function allows raw pixel data to be streamed to the display. It requires a native pixel count rather than the number of bytes to transfer, to ensure display panel data alignment is respected. E.g. for display panels using RGB565 internal format, sending 10 pixels will result in 20 bytes of transfer. 1087 1088::: warning 1089Under normal circumstances, users will not need to manually call either `qp_viewport` or `qp_pixdata`. These allow for writing of raw pixel information, in the display panel's native format, to the area defined by the viewport. 1090::: 1091 1092::::: 1093 1094::::::