qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

quantum_painter.md (43284B)


      1 # Quantum Painter {#quantum-painter}
      2 
      3 Quantum 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 
      5 Due to the complexity, there is no support for Quantum Painter on AVR-based boards.
      6 
      7 To enable overall Quantum Painter to be built into your firmware, add the following to `rules.mk`:
      8 
      9 ```make
     10 QUANTUM_PAINTER_ENABLE = yes
     11 QUANTUM_PAINTER_DRIVERS += ......
     12 ```
     13 
     14 You will also likely need to select an appropriate driver in `rules.mk`, which is listed below.
     15 
     16 ::: warning
     17 Quantum 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 
     20 The 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 
     22 Supported 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 
     59 Drivers 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 
     67 This command converts images to a format usable by QMK, i.e. the QGF File Format.
     68 
     69 **Usage**:
     70 
     71 ```
     72 usage: qmk painter-convert-graphics [-h] [-w] [-d] [-r] -f FORMAT [-o OUTPUT] -i INPUT [-v]
     73 
     74 options:
     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 
     88 The `INPUT` argument can be any image file loadable by Python's Pillow module. Common formats include PNG, or Animated GIF.
     89 
     90 The `OUTPUT` argument needs to be a directory, and will default to the same directory as the input argument.
     91 
     92 The `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/
    112 Writing /home/qmk/qmk_firmware/keyboards/my_keeb/generated/my_image.qgf.h...
    113 Writing /home/qmk/qmk_firmware/keyboards/my_keeb/generated/my_image.qgf.c...
    114 ```
    115 
    116 ==== `qmk painter-make-font-image`
    117 
    118 This command converts a TTF font to an intermediate format for editing, before converting to the QFF File Format.
    119 
    120 **Usage**:
    121 
    122 ```
    123 usage: qmk painter-make-font-image [-h] [-a] [-u UNICODE_GLYPHS] [-n] [-s SIZE] -o OUTPUT -f FONT
    124 
    125 optional 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 
    137 The `FONT` argument is generally a TrueType Font file (TTF).
    138 
    139 The `OUTPUT` argument is the output image to generate, generally something like `my_font.png`.
    140 
    141 The `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 
    151 This command converts an intermediate font image to the QFF File Format.
    152 
    153 This 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 ```
    166 usage: qmk painter-convert-font-image [-h] [-w] [-r] -f FORMAT [-u UNICODE_GLYPHS] [-n] [-o OUTPUT] [-i INPUT]
    167 
    168 options:
    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 
    183 The 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 "ĄȽɂɻɣɈʣ"
    190 Writing /home/qmk/qmk_firmware/keyboards/my_keeb/generated/noto11.qff.h...
    191 Writing /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 
    202 Most TFT display panels use a 5-pin interface -- SPI SCK, SPI MOSI, SPI CS, D/C, and RST pins.
    203 
    204 For these displays, QMK's `spi_master` must already be correctly configured for the platform you're building for.
    205 
    206 The pin assignments for SPI CS, D/C, and RST are specified during device construction.
    207 
    208 :::::tabs
    209 
    210 ==== GC9A01
    211 
    212 Enabling support for the GC9A01 in Quantum Painter is done by adding the following to `rules.mk`:
    213 
    214 ```make
    215 QUANTUM_PAINTER_ENABLE = yes
    216 QUANTUM_PAINTER_DRIVERS += gc9a01_spi
    217 ```
    218 
    219 Creating a GC9A01 device in firmware can then be done with the following API:
    220 
    221 ```c
    222 painter_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 
    225 The device handle returned from the `qp_gc9a01_make_spi_device` function can be used to perform all other drawing operations.
    226 
    227 The 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 
    234 Native color format rgb565 is compatible with GC9A01
    235 
    236 ==== ILI9163
    237 
    238 Enabling support for the ILI9163 in Quantum Painter is done by adding the following to `rules.mk`:
    239 
    240 ```make
    241 QUANTUM_PAINTER_ENABLE = yes
    242 QUANTUM_PAINTER_DRIVERS += ili9163_spi
    243 ```
    244 
    245 Creating a ILI9163 device in firmware can then be done with the following API:
    246 
    247 ```c
    248 painter_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 
    251 The device handle returned from the `qp_ili9163_make_spi_device` function can be used to perform all other drawing operations.
    252 
    253 The 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 
    260 Native color format rgb565 is compatible with ILI9163
    261 
    262 ==== ILI9341
    263 
    264 Enabling support for the ILI9341 in Quantum Painter is done by adding the following to `rules.mk`:
    265 
    266 ```make
    267 QUANTUM_PAINTER_ENABLE = yes
    268 QUANTUM_PAINTER_DRIVERS += ili9341_spi
    269 ```
    270 
    271 Creating a ILI9341 device in firmware can then be done with the following API:
    272 
    273 ```c
    274 painter_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 
    277 The device handle returned from the `qp_ili9341_make_spi_device` function can be used to perform all other drawing operations.
    278 
    279 The 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 
    286 Native color format rgb565 is compatible with ILI9341
    287 
    288 ==== ILI9486
    289 
    290 Enabling support for the ILI9486 in Quantum Painter is done by adding the following to `rules.mk`:
    291 
    292 ```make
    293 QUANTUM_PAINTER_ENABLE = yes
    294 QUANTUM_PAINTER_DRIVERS += ili9486_spi
    295 ```
    296 
    297 Creating a ILI9486 device in firmware can then be done with the following API:
    298 
    299 ```c
    300 painter_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 
    303 There'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
    306 painter_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 
    309 The device handle returned from these functions can be used to perform all other drawing operations.
    310 
    311 The 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 
    318 Native color format rgb888 is compatible with ILI9486
    319 Native color format rgb565 is compatible with ILI9486 Waveshare
    320 
    321 ==== ILI9488
    322 
    323 Enabling support for the ILI9488 in Quantum Painter is done by adding the following to `rules.mk`:
    324 
    325 ```make
    326 QUANTUM_PAINTER_ENABLE = yes
    327 QUANTUM_PAINTER_DRIVERS += ili9488_spi
    328 ```
    329 
    330 Creating a ILI9488 device in firmware can then be done with the following API:
    331 
    332 ```c
    333 painter_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 
    336 The device handle returned from the `qp_ili9488_make_spi_device` function can be used to perform all other drawing operations.
    337 
    338 The 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 
    345 Native color format rgb888 is compatible with ILI9488
    346 
    347 ==== ST7735
    348 
    349 Enabling support for the ST7735 in Quantum Painter is done by adding the following to `rules.mk`:
    350 
    351 ```make
    352 QUANTUM_PAINTER_ENABLE = yes
    353 QUANTUM_PAINTER_DRIVERS += st7735_spi
    354 ```
    355 
    356 Creating a ST7735 device in firmware can then be done with the following API:
    357 
    358 ```c
    359 painter_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 
    362 The device handle returned from the `qp_st7735_make_spi_device` function can be used to perform all other drawing operations.
    363 
    364 The 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 
    371 Native color format rgb565 is compatible with ST7735
    372 
    373 ::: warning
    374 Some 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 
    379 Enabling support for the ST7789 in Quantum Painter is done by adding the following to `rules.mk`:
    380 
    381 ```make
    382 QUANTUM_PAINTER_ENABLE = yes
    383 QUANTUM_PAINTER_DRIVERS += st7789_spi
    384 ```
    385 
    386 Creating a ST7789 device in firmware can then be done with the following API:
    387 
    388 ```c
    389 painter_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 
    392 The device handle returned from the `qp_st7789_make_spi_device` function can be used to perform all other drawing operations.
    393 
    394 The 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 
    401 Native color format rgb565 is compatible with ST7789
    402 
    403 ::: warning
    404 Some 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 
    411 OLED 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 
    413 When 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 
    415 For 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 
    421 Enabling support for the SSD1351 in Quantum Painter is done by adding the following to `rules.mk`:
    422 
    423 ```make
    424 QUANTUM_PAINTER_ENABLE = yes
    425 QUANTUM_PAINTER_DRIVERS += ssd1351_spi
    426 ```
    427 
    428 Creating a SSD1351 device in firmware can then be done with the following API:
    429 
    430 ```c
    431 painter_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 
    434 The device handle returned from the `qp_ssd1351_make_spi_device` function can be used to perform all other drawing operations.
    435 
    436 The 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 
    443 Native color format rgb565 is compatible with SSD1351
    444 
    445 ==== SH1106
    446 
    447 Enabling support for the SH1106 in Quantum Painter is done by adding the following to `rules.mk`:
    448 
    449 ```make
    450 QUANTUM_PAINTER_ENABLE = yes
    451 # For SPI:
    452 QUANTUM_PAINTER_DRIVERS += sh1106_spi
    453 # For I2C:
    454 QUANTUM_PAINTER_DRIVERS += sh1106_i2c
    455 ```
    456 
    457 Creating a SH1106 device in firmware can then be done with the following APIs:
    458 
    459 ```c
    460 // SPI-based SH1106:
    461 painter_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:
    463 painter_device_t qp_sh1106_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address);
    464 ```
    465 
    466 The device handle returned from the `qp_sh1106_make_???_device` function can be used to perform all other drawing operations.
    467 
    468 The 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 
    477 Native color format mono2 is compatible with SH1106
    478 
    479 ==== SSD1306
    480 
    481 SSD1306 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 
    485 Enabling support for the LD7032 in Quantum Painter is done by adding the following to `rules.mk`:
    486 
    487 ```make
    488 QUANTUM_PAINTER_ENABLE = yes
    489 # For SPI:
    490 QUANTUM_PAINTER_DRIVERS += ld7032_spi
    491 # For I2C:
    492 QUANTUM_PAINTER_DRIVERS += ld7032_i2c
    493 ```
    494 
    495 Creating a SH1106 device in firmware can then be done with the following APIs:
    496 
    497 ```c
    498 // SPI-based LD7032:
    499 painter_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:
    501 painter_device_t qp_ld7032_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address);
    502 ```
    503 
    504 The device handle returned from the `qp_ld7032_make_???_device` function can be used to perform all other drawing operations.
    505 
    506 The 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 
    515 Native color format mono2 is compatible with LD7032.
    516 
    517 :::::
    518 
    519 ===== Surface
    520 
    521 Quantum 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
    524 These 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 
    527 Enabling support for surfaces in Quantum Painter is done by adding the following to `rules.mk`:
    528 
    529 ```make
    530 QUANTUM_PAINTER_ENABLE = yes
    531 QUANTUM_PAINTER_DRIVERS += surface
    532 ```
    533 
    534 Creating a surface in firmware can then be done with the following APIs:
    535 
    536 ```c
    537 // 24bpp RGB888 surface:
    538 painter_device_t qp_make_rgb888_surface(uint16_t panel_width, uint16_t panel_height, void *buffer);
    539 // 16bpp RGB565 surface:
    540 painter_device_t qp_make_rgb565_surface(uint16_t panel_width, uint16_t panel_height, void *buffer);
    541 // 1bpp monochrome surface:
    542 painter_device_t qp_make_mono1bpp_surface(uint16_t panel_width, uint16_t panel_height, void *buffer);
    543 ```
    544 
    545 The `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:
    549 uint8_t framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(240, 80, 16)];
    550 ```
    551 
    552 The device handle returned from the `qp_make_?????_surface` function can be used to perform all other drawing operations.
    553 
    554 Example:
    555 
    556 ```c
    557 static painter_device_t my_surface;
    558 static uint8_t my_framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(240, 80, 16)]; // Allocate a buffer for a 16bpp 240x80 RGB565 display
    559 void 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 
    566 The 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 
    573 To transfer the contents of the surface to another display of the same pixel format, the following API can be invoked:
    574 
    575 ```c
    576 bool qp_surface_draw(painter_device_t surface, painter_device_t display, uint16_t x, uint16_t y, bool entire_surface);
    577 ```
    578 
    579 The `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
    582 The surface and display panel must have the same native pixel format.
    583 :::
    584 
    585 ::: tip
    586 Calling `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 
    593 All 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 
    595 To 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 
    604 The 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
    607 Drawing 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 
    610 All 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
    617 Colors 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
    627 bool qp_init(painter_device_t device, painter_rotation_t rotation);
    628 ```
    629 
    630 The `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
    633 static painter_device_t display;
    634 void 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
    643 bool qp_power(painter_device_t device, bool power_on);
    644 ```
    645 
    646 The `qp_power` function instructs the display whether or not the display panel should be on or off.
    647 
    648 ::: warning
    649 If 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
    653 static uint8_t last_backlight = 255;
    654 void 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 
    663 void 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
    676 bool qp_clear(painter_device_t device);
    677 ```
    678 
    679 The `qp_clear` function clears the display's screen.
    680 
    681 ==== Display Flush
    682 
    683 ```c
    684 bool qp_flush(painter_device_t device);
    685 ```
    686 
    687 The `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
    690 Some 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
    694 void 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
    714 bool qp_setpixel(painter_device_t device, uint16_t x, uint16_t y, uint8_t hue, uint8_t sat, uint8_t val);
    715 ```
    716 
    717 The `qp_setpixel` can be used to set a specific pixel on the screen to the supplied color.
    718 
    719 ::: tip
    720 Using `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
    724 void 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
    740 bool 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 
    743 The `qp_line` can be used to draw lines on the screen with the supplied color.
    744 
    745 ```c
    746 void 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
    762 bool 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 
    765 The `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
    768 void 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
    784 bool 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 
    787 The `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
    790 void 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
    806 bool 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 
    809 The `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
    812 void 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 
    829 Making 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
    832 SRC += 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
    845 painter_image_handle_t qp_load_image_mem(const void *buffer);
    846 ```
    847 
    848 The `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 
    852 See the [CLI Commands](quantum_painter#quantum-painter-cli) for instructions on how to convert images to [QGF](quantum_painter_qgf).
    853 
    854 ::: tip
    855 The 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 
    858 Image 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
    869 bool qp_close_image(painter_image_handle_t image);
    870 ```
    871 
    872 The `qp_close_image` function releases resources related to the loading of the supplied image.
    873 
    874 ==== Draw image
    875 
    876 ```c
    877 bool qp_drawimage(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image);
    878 bool 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 
    881 The `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
    885 static painter_image_handle_t my_image;
    886 void 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
    897 deferred_token qp_animate(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image);
    898 deferred_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 
    901 The `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 
    903 Once an image has been set to animate, it will loop indefinitely until stopped, with no user intervention required.
    904 
    905 Both 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
    909 static painter_image_handle_t my_image;
    910 static deferred_token my_anim;
    911 void 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
    922 void qp_stop_animation(deferred_token anim_token);
    923 ```
    924 
    925 The `qp_stop_animation` function stops the previously-started animation.
    926 ```c
    927 void 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 
    938 Making 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
    941 SRC += 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
    954 painter_font_handle_t qp_load_font_mem(const void *buffer);
    955 ```
    956 
    957 The `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 
    961 See the [CLI Commands](quantum_painter#quantum-painter-cli) for instructions on how to convert TTF fonts to [QFF](quantum_painter_qff).
    962 
    963 ::: tip
    964 The 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 
    967 Font 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
    976 bool qp_close_font(painter_font_handle_t font);
    977 ```
    978 
    979 The `qp_close_font` function releases resources related to the loading of the supplied font.
    980 
    981 ==== Measure Text
    982 
    983 ```c
    984 int16_t qp_textwidth(painter_font_handle_t font, const char *str);
    985 ```
    986 
    987 The `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
    992 int16_t qp_drawtext(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str);
    993 int16_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 
    996 The `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
   1000 static painter_font_handle_t my_font;
   1001 void 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 
   1019 These functions allow external code to retrieve the current width, height, rotation, and drawing offsets.
   1020 
   1021 ::::tabs
   1022 
   1023 === Width
   1024 
   1025 ```c
   1026 uint16_t qp_get_width(painter_device_t device);
   1027 ```
   1028 
   1029 === Height
   1030 
   1031 ```c
   1032 uint16_t qp_get_height(painter_device_t device);
   1033 ```
   1034 
   1035 === Rotation
   1036 
   1037 ```c
   1038 painter_rotation_t qp_get_rotation(painter_device_t device);
   1039 ```
   1040 
   1041 === Offset X
   1042 
   1043 ```c
   1044 uint16_t qp_get_offset_x(painter_device_t device);
   1045 ```
   1046 
   1047 === Offset Y
   1048 
   1049 ```c
   1050 uint16_t qp_get_offset_y(painter_device_t device);
   1051 ```
   1052 
   1053 === Everything
   1054 
   1055 Convenience function to call all the previous ones at once.
   1056 Note: You can pass `NULL` for the values you are not interested in.
   1057 
   1058 ```c
   1059 void 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
   1067 void qp_set_viewport_offsets(painter_device_t device, uint16_t offset_x, uint16_t offset_y);
   1068 ```
   1069 
   1070 The `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
   1075 bool qp_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom);
   1076 ```
   1077 
   1078 The `qp_viewport` function controls where raw pixel data is written to.
   1079 
   1080 ==== Stream Pixel Data
   1081 
   1082 ```c
   1083 bool qp_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count);
   1084 ```
   1085 
   1086 The `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
   1089 Under 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 ::::::