keyboard stuff
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::::::