2018-08-15 14:01:01 -04:00
|
|
|
|
2019-01-17 03:20:16 -05:00
|
|
|
#include <string.h>
|
2019-06-06 18:11:02 -04:00
|
|
|
|
2019-01-16 15:04:42 -05:00
|
|
|
#include "shared-module/displayio/__init__.h"
|
2018-08-15 14:01:01 -04:00
|
|
|
|
2019-02-28 22:36:50 -05:00
|
|
|
#include "lib/utils/interrupt_char.h"
|
2019-02-15 16:52:25 -05:00
|
|
|
#include "py/reload.h"
|
2019-02-28 22:36:50 -05:00
|
|
|
#include "py/runtime.h"
|
2019-04-08 19:58:50 -04:00
|
|
|
#include "shared-bindings/board/__init__.h"
|
2019-01-17 03:20:16 -05:00
|
|
|
#include "shared-bindings/displayio/Bitmap.h"
|
2019-01-16 15:04:42 -05:00
|
|
|
#include "shared-bindings/displayio/Display.h"
|
2019-01-17 03:20:16 -05:00
|
|
|
#include "shared-bindings/displayio/Group.h"
|
|
|
|
#include "shared-bindings/displayio/Palette.h"
|
2019-05-16 19:45:38 -04:00
|
|
|
#include "shared-module/displayio/area.h"
|
2019-02-14 15:54:34 -05:00
|
|
|
#include "supervisor/shared/autoreload.h"
|
2019-01-24 20:25:08 -05:00
|
|
|
#include "supervisor/shared/display.h"
|
2019-01-25 19:59:18 -05:00
|
|
|
#include "supervisor/memory.h"
|
2018-08-15 14:01:01 -04:00
|
|
|
|
2020-01-24 14:33:28 -05:00
|
|
|
#include "supervisor/spi_flash_api.h"
|
|
|
|
#include "py/mpconfig.h"
|
|
|
|
|
2020-08-06 17:03:31 -04:00
|
|
|
#if CIRCUITPY_SHARPDISPLAY
|
|
|
|
#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h"
|
|
|
|
#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h"
|
|
|
|
#endif
|
|
|
|
|
2019-01-17 03:20:16 -05:00
|
|
|
primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT];
|
2018-08-15 14:01:01 -04:00
|
|
|
|
2021-03-04 14:40:50 -05:00
|
|
|
displayio_buffer_transform_t null_transform = {
|
|
|
|
.x = 0,
|
|
|
|
.y = 0,
|
|
|
|
.dx = 1,
|
|
|
|
.dy = 1,
|
|
|
|
.scale = 1,
|
|
|
|
.width = 0,
|
|
|
|
.height = 0,
|
|
|
|
.mirror_x = false,
|
|
|
|
.mirror_y = false,
|
|
|
|
.transpose_xy = false
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-08-18 11:59:55 -04:00
|
|
|
#if CIRCUITPY_RGBMATRIX
|
2020-08-06 17:03:31 -04:00
|
|
|
STATIC bool any_display_uses_this_framebuffer(mp_obj_base_t *obj) {
|
2020-04-01 12:59:15 -04:00
|
|
|
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
|
2020-08-06 17:03:31 -04:00
|
|
|
if (displays[i].display_base.type == &framebufferio_framebufferdisplay_type) {
|
2021-03-15 09:57:36 -04:00
|
|
|
framebufferio_framebufferdisplay_obj_t *display = &displays[i].framebuffer_display;
|
2020-08-06 17:03:31 -04:00
|
|
|
if (display->framebuffer == obj) {
|
2020-04-01 12:59:15 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2020-04-01 17:10:19 -04:00
|
|
|
#endif
|
2020-04-01 12:59:15 -04:00
|
|
|
|
2019-06-06 18:11:02 -04:00
|
|
|
|
2019-08-14 17:17:35 -04:00
|
|
|
void displayio_background(void) {
|
2019-02-28 22:36:50 -05:00
|
|
|
if (mp_hal_is_interrupted()) {
|
|
|
|
return;
|
|
|
|
}
|
2019-03-16 16:49:32 -04:00
|
|
|
if (reload_requested) {
|
|
|
|
// Reload is about to happen, so don't redisplay.
|
2019-02-15 16:52:25 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-07 00:08:16 -05:00
|
|
|
|
2019-01-17 03:20:16 -05:00
|
|
|
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
|
2019-01-17 21:51:40 -05:00
|
|
|
if (displays[i].display.base.type == NULL || displays[i].display.base.type == &mp_type_NoneType) {
|
2019-03-07 00:08:16 -05:00
|
|
|
// Skip null display.
|
2019-01-17 03:20:16 -05:00
|
|
|
continue;
|
|
|
|
}
|
2019-08-02 19:17:38 -04:00
|
|
|
if (displays[i].display.base.type == &displayio_display_type) {
|
2019-08-14 17:17:35 -04:00
|
|
|
displayio_display_background(&displays[i].display);
|
2021-03-15 09:57:36 -04:00
|
|
|
#if CIRCUITPY_FRAMEBUFFERIO
|
2020-03-10 14:12:01 -04:00
|
|
|
} else if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) {
|
|
|
|
framebufferio_framebufferdisplay_background(&displays[i].framebuffer_display);
|
2021-03-15 09:57:36 -04:00
|
|
|
#endif
|
2019-08-16 21:34:00 -04:00
|
|
|
} else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
|
|
|
|
displayio_epaperdisplay_background(&displays[i].epaper_display);
|
2018-08-15 14:01:01 -04:00
|
|
|
}
|
|
|
|
}
|
2019-03-07 00:08:16 -05:00
|
|
|
|
2019-01-16 15:04:42 -05:00
|
|
|
}
|
|
|
|
|
2019-01-25 19:59:18 -05:00
|
|
|
void common_hal_displayio_release_displays(void) {
|
2019-08-21 00:35:42 -04:00
|
|
|
// Release displays before busses so that they can send any final commands to turn the display
|
|
|
|
// off properly.
|
|
|
|
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
|
|
|
|
mp_const_obj_t display_type = displays[i].display.base.type;
|
|
|
|
if (display_type == NULL || display_type == &mp_type_NoneType) {
|
|
|
|
continue;
|
|
|
|
} else if (display_type == &displayio_display_type) {
|
|
|
|
release_display(&displays[i].display);
|
|
|
|
} else if (display_type == &displayio_epaperdisplay_type) {
|
|
|
|
release_epaperdisplay(&displays[i].epaper_display);
|
2021-03-15 09:57:36 -04:00
|
|
|
#if CIRCUITPY_FRAMEBUFFERIO
|
2020-03-10 14:12:01 -04:00
|
|
|
} else if (display_type == &framebufferio_framebufferdisplay_type) {
|
|
|
|
release_framebufferdisplay(&displays[i].framebuffer_display);
|
2021-03-15 09:57:36 -04:00
|
|
|
#endif
|
2019-08-21 00:35:42 -04:00
|
|
|
}
|
|
|
|
displays[i].display.base.type = &mp_type_NoneType;
|
|
|
|
}
|
2019-01-25 19:59:18 -05:00
|
|
|
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
|
|
|
|
mp_const_obj_t bus_type = displays[i].fourwire_bus.base.type;
|
2019-08-14 18:53:58 -04:00
|
|
|
if (bus_type == NULL || bus_type == &mp_type_NoneType) {
|
2019-01-25 19:59:18 -05:00
|
|
|
continue;
|
|
|
|
} else if (bus_type == &displayio_fourwire_type) {
|
|
|
|
common_hal_displayio_fourwire_deinit(&displays[i].fourwire_bus);
|
2019-07-05 22:01:54 -04:00
|
|
|
} else if (bus_type == &displayio_i2cdisplay_type) {
|
|
|
|
common_hal_displayio_i2cdisplay_deinit(&displays[i].i2cdisplay_bus);
|
2019-01-25 19:59:18 -05:00
|
|
|
} else if (bus_type == &displayio_parallelbus_type) {
|
|
|
|
common_hal_displayio_parallelbus_deinit(&displays[i].parallel_bus);
|
2021-03-15 09:57:36 -04:00
|
|
|
#if CIRCUITPY_RGBMATRIX
|
2020-04-15 16:01:24 -04:00
|
|
|
} else if (bus_type == &rgbmatrix_RGBMatrix_type) {
|
|
|
|
common_hal_rgbmatrix_rgbmatrix_deinit(&displays[i].rgbmatrix);
|
2021-03-15 09:57:36 -04:00
|
|
|
#endif
|
|
|
|
#if CIRCUITPY_SHARPDISPLAY
|
2020-08-06 17:03:31 -04:00
|
|
|
} else if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) {
|
|
|
|
common_hal_sharpdisplay_framebuffer_deinit(&displays[i].sharpdisplay);
|
2021-03-15 09:57:36 -04:00
|
|
|
#endif
|
2019-01-25 19:59:18 -05:00
|
|
|
}
|
|
|
|
displays[i].fourwire_bus.base.type = &mp_type_NoneType;
|
|
|
|
}
|
2019-01-17 03:20:16 -05:00
|
|
|
|
2019-01-25 19:59:18 -05:00
|
|
|
supervisor_stop_terminal();
|
|
|
|
}
|
2019-01-17 03:20:16 -05:00
|
|
|
|
|
|
|
void reset_displays(void) {
|
|
|
|
// The SPI buses used by FourWires may be allocated on the heap so we need to move them inline.
|
|
|
|
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
|
2019-07-05 22:01:54 -04:00
|
|
|
if (displays[i].fourwire_bus.base.type == &displayio_fourwire_type) {
|
2021-03-15 09:57:36 -04:00
|
|
|
displayio_fourwire_obj_t *fourwire = &displays[i].fourwire_bus;
|
|
|
|
if (((uint32_t)fourwire->bus) < ((uint32_t)&displays) ||
|
|
|
|
((uint32_t)fourwire->bus) > ((uint32_t)&displays + CIRCUITPY_DISPLAY_LIMIT)) {
|
|
|
|
busio_spi_obj_t *original_spi = fourwire->bus;
|
2019-07-05 22:01:54 -04:00
|
|
|
#if BOARD_SPI
|
2021-03-15 09:57:36 -04:00
|
|
|
// We don't need to move original_spi if it is the board.SPI object because it is
|
|
|
|
// statically allocated already. (Doing so would also make it impossible to reference in
|
|
|
|
// a subsequent VM run.)
|
|
|
|
if (original_spi == common_hal_board_get_spi()) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-01-24 14:25:36 -05:00
|
|
|
#endif
|
2020-01-24 14:33:28 -05:00
|
|
|
#ifdef BOARD_USE_INTERNAL_SPI
|
2021-03-15 09:57:36 -04:00
|
|
|
if (original_spi == (mp_obj_t)(&supervisor_flash_spi_bus)) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-07-05 22:01:54 -04:00
|
|
|
#endif
|
|
|
|
memcpy(&fourwire->inline_bus, original_spi, sizeof(busio_spi_obj_t));
|
|
|
|
fourwire->bus = &fourwire->inline_bus;
|
|
|
|
// Check for other displays that use the same spi bus and swap them too.
|
|
|
|
for (uint8_t j = i + 1; j < CIRCUITPY_DISPLAY_LIMIT; j++) {
|
|
|
|
if (displays[i].fourwire_bus.base.type == &displayio_fourwire_type &&
|
2021-03-15 09:57:36 -04:00
|
|
|
displays[i].fourwire_bus.bus == original_spi) {
|
2019-07-05 22:01:54 -04:00
|
|
|
displays[i].fourwire_bus.bus = &fourwire->inline_bus;
|
|
|
|
}
|
2019-04-09 14:36:10 -04:00
|
|
|
}
|
2019-07-05 22:01:54 -04:00
|
|
|
}
|
|
|
|
} else if (displays[i].i2cdisplay_bus.base.type == &displayio_i2cdisplay_type) {
|
2021-03-15 09:57:36 -04:00
|
|
|
displayio_i2cdisplay_obj_t *i2c = &displays[i].i2cdisplay_bus;
|
|
|
|
if (((uint32_t)i2c->bus) < ((uint32_t)&displays) ||
|
|
|
|
((uint32_t)i2c->bus) > ((uint32_t)&displays + CIRCUITPY_DISPLAY_LIMIT)) {
|
|
|
|
busio_i2c_obj_t *original_i2c = i2c->bus;
|
2019-07-24 16:25:34 -04:00
|
|
|
#if BOARD_I2C
|
2021-03-15 09:57:36 -04:00
|
|
|
// We don't need to move original_i2c if it is the board.I2C object because it is
|
|
|
|
// statically allocated already. (Doing so would also make it impossible to reference in
|
|
|
|
// a subsequent VM run.)
|
|
|
|
if (original_i2c == common_hal_board_get_i2c()) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-07-24 16:25:34 -04:00
|
|
|
#endif
|
|
|
|
memcpy(&i2c->inline_bus, original_i2c, sizeof(busio_i2c_obj_t));
|
|
|
|
i2c->bus = &i2c->inline_bus;
|
|
|
|
// Check for other displays that use the same i2c bus and swap them too.
|
|
|
|
for (uint8_t j = i + 1; j < CIRCUITPY_DISPLAY_LIMIT; j++) {
|
|
|
|
if (displays[i].i2cdisplay_bus.base.type == &displayio_i2cdisplay_type &&
|
2021-03-15 09:57:36 -04:00
|
|
|
displays[i].i2cdisplay_bus.bus == original_i2c) {
|
2019-07-24 16:25:34 -04:00
|
|
|
displays[i].i2cdisplay_bus.bus = &i2c->inline_bus;
|
2019-07-05 22:01:54 -04:00
|
|
|
}
|
2019-01-17 03:20:16 -05:00
|
|
|
}
|
|
|
|
}
|
2021-03-15 09:57:36 -04:00
|
|
|
#if CIRCUITPY_RGBMATRIX
|
2020-04-15 16:01:24 -04:00
|
|
|
} else if (displays[i].rgbmatrix.base.type == &rgbmatrix_RGBMatrix_type) {
|
2021-03-15 09:57:36 -04:00
|
|
|
rgbmatrix_rgbmatrix_obj_t *pm = &displays[i].rgbmatrix;
|
|
|
|
if (!any_display_uses_this_framebuffer(&pm->base)) {
|
2020-04-15 16:01:24 -04:00
|
|
|
common_hal_rgbmatrix_rgbmatrix_deinit(pm);
|
2020-04-01 12:59:15 -04:00
|
|
|
}
|
2021-03-15 09:57:36 -04:00
|
|
|
#endif
|
|
|
|
#if CIRCUITPY_SHARPDISPLAY
|
2020-08-06 17:03:31 -04:00
|
|
|
} else if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) {
|
2021-03-15 09:57:36 -04:00
|
|
|
sharpdisplay_framebuffer_obj_t *sharp = &displays[i].sharpdisplay;
|
2020-08-18 10:31:49 -04:00
|
|
|
common_hal_sharpdisplay_framebuffer_reset(sharp);
|
2021-03-15 09:57:36 -04:00
|
|
|
#endif
|
2019-07-24 16:25:34 -04:00
|
|
|
} else {
|
2019-08-02 19:17:38 -04:00
|
|
|
// Not an active display bus.
|
2019-01-17 03:20:16 -05:00
|
|
|
continue;
|
|
|
|
}
|
2019-08-14 18:53:58 -04:00
|
|
|
}
|
2019-07-24 16:25:34 -04:00
|
|
|
|
2019-08-14 18:53:58 -04:00
|
|
|
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
|
2019-07-24 16:25:34 -04:00
|
|
|
// Reset the displayed group. Only the first will get the terminal but
|
|
|
|
// that's ok.
|
2019-08-02 19:17:38 -04:00
|
|
|
if (displays[i].display.base.type == &displayio_display_type) {
|
2019-08-22 03:33:27 -04:00
|
|
|
reset_display(&displays[i].display);
|
2019-08-02 19:17:38 -04:00
|
|
|
} else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
|
2021-03-15 09:57:36 -04:00
|
|
|
displayio_epaperdisplay_obj_t *display = &displays[i].epaper_display;
|
2019-08-02 19:17:38 -04:00
|
|
|
common_hal_displayio_epaperdisplay_show(display, NULL);
|
2021-03-15 09:57:36 -04:00
|
|
|
#if CIRCUITPY_FRAMEBUFFERIO
|
2020-03-10 14:12:01 -04:00
|
|
|
} else if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) {
|
2020-07-20 09:52:35 -04:00
|
|
|
framebufferio_framebufferdisplay_reset(&displays[i].framebuffer_display);
|
2021-03-15 09:57:36 -04:00
|
|
|
#endif
|
2019-08-02 19:17:38 -04:00
|
|
|
}
|
2019-01-17 03:20:16 -05:00
|
|
|
}
|
2019-06-06 17:49:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void displayio_gc_collect(void) {
|
|
|
|
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
|
2021-03-15 09:57:36 -04:00
|
|
|
#if CIRCUITPY_RGBMATRIX
|
2020-04-15 16:01:24 -04:00
|
|
|
if (displays[i].rgbmatrix.base.type == &rgbmatrix_RGBMatrix_type) {
|
|
|
|
rgbmatrix_rgbmatrix_collect_ptrs(&displays[i].rgbmatrix);
|
2020-03-10 14:12:01 -04:00
|
|
|
}
|
2021-03-15 09:57:36 -04:00
|
|
|
#endif
|
|
|
|
#if CIRCUITPY_SHARPDISPLAY
|
2020-08-12 08:39:12 -04:00
|
|
|
if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) {
|
2020-08-06 17:03:31 -04:00
|
|
|
common_hal_sharpdisplay_framebuffer_collect_ptrs(&displays[i].sharpdisplay);
|
|
|
|
}
|
2021-03-15 09:57:36 -04:00
|
|
|
#endif
|
2020-03-10 14:12:01 -04:00
|
|
|
|
2019-06-06 17:49:32 -04:00
|
|
|
if (displays[i].display.base.type == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alternatively, we could use gc_collect_root over the whole object,
|
|
|
|
// but this is more precise, and is the only field that needs marking.
|
2019-08-02 19:17:38 -04:00
|
|
|
if (displays[i].display.base.type == &displayio_display_type) {
|
2019-08-16 21:34:00 -04:00
|
|
|
displayio_display_collect_ptrs(&displays[i].display);
|
2021-03-15 09:57:36 -04:00
|
|
|
#if CIRCUITPY_FRAMEBUFFERIO
|
2020-03-10 14:12:01 -04:00
|
|
|
} else if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) {
|
|
|
|
framebufferio_framebufferdisplay_collect_ptrs(&displays[i].framebuffer_display);
|
2021-03-15 09:57:36 -04:00
|
|
|
#endif
|
2019-08-02 19:17:38 -04:00
|
|
|
} else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
|
2019-08-16 21:34:00 -04:00
|
|
|
displayio_epaperdisplay_collect_ptrs(&displays[i].epaper_display);
|
2019-08-02 19:17:38 -04:00
|
|
|
}
|
2019-06-06 17:49:32 -04:00
|
|
|
}
|
2018-08-15 14:01:01 -04:00
|
|
|
}
|
2019-05-16 19:45:38 -04:00
|
|
|
|
2021-03-15 09:57:36 -04:00
|
|
|
void displayio_area_copy(const displayio_area_t *src, displayio_area_t *dst) {
|
2019-06-06 18:11:02 -04:00
|
|
|
dst->x1 = src->x1;
|
|
|
|
dst->y1 = src->y1;
|
|
|
|
dst->x2 = src->x2;
|
|
|
|
dst->y2 = src->y2;
|
|
|
|
}
|
|
|
|
|
2021-03-15 09:57:36 -04:00
|
|
|
void displayio_area_scale(displayio_area_t *area, uint16_t scale) {
|
2019-06-06 18:11:02 -04:00
|
|
|
area->x1 *= scale;
|
|
|
|
area->y1 *= scale;
|
|
|
|
area->x2 *= scale;
|
|
|
|
area->y2 *= scale;
|
|
|
|
}
|
|
|
|
|
2021-03-15 09:57:36 -04:00
|
|
|
void displayio_area_shift(displayio_area_t *area, int16_t dx, int16_t dy) {
|
2019-05-16 19:45:38 -04:00
|
|
|
area->x1 += dx;
|
|
|
|
area->y1 += dy;
|
|
|
|
area->x2 += dx;
|
|
|
|
area->y2 += dy;
|
|
|
|
}
|
|
|
|
|
2021-03-15 09:57:36 -04:00
|
|
|
bool displayio_area_compute_overlap(const displayio_area_t *a,
|
|
|
|
const displayio_area_t *b,
|
|
|
|
displayio_area_t *overlap) {
|
2019-05-16 19:45:38 -04:00
|
|
|
overlap->x1 = a->x1;
|
|
|
|
if (b->x1 > overlap->x1) {
|
|
|
|
overlap->x1 = b->x1;
|
|
|
|
}
|
|
|
|
overlap->x2 = a->x2;
|
|
|
|
if (b->x2 < overlap->x2) {
|
|
|
|
overlap->x2 = b->x2;
|
|
|
|
}
|
2019-06-06 18:11:02 -04:00
|
|
|
if (overlap->x1 >= overlap->x2) {
|
2019-05-16 19:45:38 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
overlap->y1 = a->y1;
|
|
|
|
if (b->y1 > overlap->y1) {
|
|
|
|
overlap->y1 = b->y1;
|
|
|
|
}
|
|
|
|
overlap->y2 = a->y2;
|
|
|
|
if (b->y2 < overlap->y2) {
|
|
|
|
overlap->y2 = b->y2;
|
|
|
|
}
|
2019-06-06 18:11:02 -04:00
|
|
|
if (overlap->y1 >= overlap->y2) {
|
2019-05-16 19:45:38 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
displayio: area: add displayo_area_copy_coords, displayio_area_empty
.. and simplify the implmentation of displayio_area_union
This _slightly_ changes the behavior of displayio_area_union:
Formerly, if one of the areas was empty, its coordinates were still
used in the min/max calculations.
Now, if one of the areas is empty, the result gets the other area's coords
In particular, taking the union of the empty area with coords (0,0,0,0)
with the non-empty area (x1,y1,x2,y2) would give the area (0,0,x2,y2)
before, and (x1,y1,x2,y2) after the change.
2021-03-18 10:04:53 -04:00
|
|
|
void displayio_copy_coords(const displayio_area_t *src, displayio_area_t *dest) {
|
|
|
|
dest->x1 = src->x1;
|
|
|
|
dest->y1 = src->y1;
|
|
|
|
dest->x2 = src->x2;
|
|
|
|
dest->y2 = src->y2;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool displayio_area_empty(const displayio_area_t *a) {
|
|
|
|
return (a->x1 == a->x2) || (a->y1 == a->y2);
|
|
|
|
}
|
|
|
|
|
2021-03-18 10:06:00 -04:00
|
|
|
void displayio_area_canon(displayio_area_t *a) {
|
2021-03-19 21:30:37 -04:00
|
|
|
if (a->x1 > a->x2) {
|
2021-03-18 10:06:00 -04:00
|
|
|
int16_t t = a->x1;
|
|
|
|
a->x1 = a->x2;
|
|
|
|
a->x2 = t;
|
|
|
|
}
|
2021-03-19 21:30:37 -04:00
|
|
|
if (a->y1 > a->y2) {
|
2021-03-18 10:06:00 -04:00
|
|
|
int16_t t = a->y1;
|
|
|
|
a->y1 = a->y2;
|
|
|
|
a->y2 = t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-15 09:57:36 -04:00
|
|
|
void displayio_area_union(const displayio_area_t *a,
|
|
|
|
const displayio_area_t *b,
|
|
|
|
displayio_area_t *u) {
|
2019-06-06 18:11:02 -04:00
|
|
|
|
displayio: area: add displayo_area_copy_coords, displayio_area_empty
.. and simplify the implmentation of displayio_area_union
This _slightly_ changes the behavior of displayio_area_union:
Formerly, if one of the areas was empty, its coordinates were still
used in the min/max calculations.
Now, if one of the areas is empty, the result gets the other area's coords
In particular, taking the union of the empty area with coords (0,0,0,0)
with the non-empty area (x1,y1,x2,y2) would give the area (0,0,x2,y2)
before, and (x1,y1,x2,y2) after the change.
2021-03-18 10:04:53 -04:00
|
|
|
if (displayio_area_empty(a)) {
|
|
|
|
displayio_copy_coords(b, u);
|
|
|
|
return;
|
2019-06-06 18:11:02 -04:00
|
|
|
}
|
displayio: area: add displayo_area_copy_coords, displayio_area_empty
.. and simplify the implmentation of displayio_area_union
This _slightly_ changes the behavior of displayio_area_union:
Formerly, if one of the areas was empty, its coordinates were still
used in the min/max calculations.
Now, if one of the areas is empty, the result gets the other area's coords
In particular, taking the union of the empty area with coords (0,0,0,0)
with the non-empty area (x1,y1,x2,y2) would give the area (0,0,x2,y2)
before, and (x1,y1,x2,y2) after the change.
2021-03-18 10:04:53 -04:00
|
|
|
if (displayio_area_empty(b)) {
|
|
|
|
displayio_copy_coords(a, u);
|
|
|
|
return;
|
2019-06-06 18:11:02 -04:00
|
|
|
}
|
displayio: area: add displayo_area_copy_coords, displayio_area_empty
.. and simplify the implmentation of displayio_area_union
This _slightly_ changes the behavior of displayio_area_union:
Formerly, if one of the areas was empty, its coordinates were still
used in the min/max calculations.
Now, if one of the areas is empty, the result gets the other area's coords
In particular, taking the union of the empty area with coords (0,0,0,0)
with the non-empty area (x1,y1,x2,y2) would give the area (0,0,x2,y2)
before, and (x1,y1,x2,y2) after the change.
2021-03-18 10:04:53 -04:00
|
|
|
u->x1 = MIN(a->x1, b->x1);
|
|
|
|
u->y1 = MIN(a->y1, b->y1);
|
|
|
|
u->x2 = MAX(a->x2, b->x2);
|
|
|
|
u->y2 = MAX(a->y2, b->y2);
|
2019-06-06 18:11:02 -04:00
|
|
|
}
|
|
|
|
|
2021-03-15 09:57:36 -04:00
|
|
|
uint16_t displayio_area_width(const displayio_area_t *area) {
|
2019-05-22 18:00:47 -04:00
|
|
|
return area->x2 - area->x1;
|
2019-05-16 19:45:38 -04:00
|
|
|
}
|
|
|
|
|
2021-03-15 09:57:36 -04:00
|
|
|
uint16_t displayio_area_height(const displayio_area_t *area) {
|
2019-05-22 18:00:47 -04:00
|
|
|
return area->y2 - area->y1;
|
2019-05-16 19:45:38 -04:00
|
|
|
}
|
|
|
|
|
2021-03-15 09:57:36 -04:00
|
|
|
uint32_t displayio_area_size(const displayio_area_t *area) {
|
2019-05-16 19:45:38 -04:00
|
|
|
return displayio_area_width(area) * displayio_area_height(area);
|
|
|
|
}
|
|
|
|
|
2021-03-15 09:57:36 -04:00
|
|
|
bool displayio_area_equal(const displayio_area_t *a, const displayio_area_t *b) {
|
2019-05-16 19:45:38 -04:00
|
|
|
return a->x1 == b->x1 &&
|
|
|
|
a->y1 == b->y1 &&
|
|
|
|
a->x2 == b->x2 &&
|
|
|
|
a->y2 == b->y2;
|
|
|
|
}
|
2019-06-06 18:11:02 -04:00
|
|
|
|
|
|
|
// Original and whole must be in the same coordinate space.
|
|
|
|
void displayio_area_transform_within(bool mirror_x, bool mirror_y, bool transpose_xy,
|
2021-03-15 09:57:36 -04:00
|
|
|
const displayio_area_t *original,
|
|
|
|
const displayio_area_t *whole,
|
|
|
|
displayio_area_t *transformed) {
|
2019-06-06 18:11:02 -04:00
|
|
|
if (mirror_x) {
|
|
|
|
transformed->x1 = whole->x1 + (whole->x2 - original->x2);
|
|
|
|
transformed->x2 = whole->x2 - (original->x1 - whole->x1);
|
|
|
|
} else {
|
|
|
|
transformed->x1 = original->x1;
|
|
|
|
transformed->x2 = original->x2;
|
|
|
|
}
|
|
|
|
if (mirror_y) {
|
|
|
|
transformed->y1 = whole->y1 + (whole->y2 - original->y2);
|
|
|
|
transformed->y2 = whole->y2 - (original->y1 - whole->y1);
|
|
|
|
} else {
|
|
|
|
transformed->y1 = original->y1;
|
|
|
|
transformed->y2 = original->y2;
|
|
|
|
}
|
|
|
|
if (transpose_xy) {
|
|
|
|
int16_t y1 = transformed->y1;
|
|
|
|
int16_t y2 = transformed->y2;
|
|
|
|
transformed->y1 = whole->y1 + (transformed->x1 - whole->x1);
|
|
|
|
transformed->y2 = whole->y1 + (transformed->x2 - whole->x1);
|
|
|
|
transformed->x2 = whole->x1 + (y2 - whole->y1);
|
|
|
|
transformed->x1 = whole->x1 + (y1 - whole->y1);
|
|
|
|
}
|
|
|
|
}
|
2020-03-28 11:34:18 -04:00
|
|
|
|
2020-03-10 14:12:01 -04:00
|
|
|
primary_display_t *allocate_display(void) {
|
2020-03-28 11:34:18 -04:00
|
|
|
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
|
|
|
|
mp_const_obj_t display_type = displays[i].display.base.type;
|
|
|
|
if (display_type == NULL || display_type == &mp_type_NoneType) {
|
|
|
|
return &displays[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
2020-03-10 14:12:01 -04:00
|
|
|
}
|
2020-03-28 11:34:18 -04:00
|
|
|
|
2020-03-10 14:12:01 -04:00
|
|
|
primary_display_t *allocate_display_or_raise(void) {
|
2020-03-28 11:34:18 -04:00
|
|
|
primary_display_t *result = allocate_display();
|
2020-03-10 14:12:01 -04:00
|
|
|
if (result) {
|
|
|
|
return result;
|
2020-03-28 11:34:18 -04:00
|
|
|
}
|
2020-03-10 14:12:01 -04:00
|
|
|
mp_raise_RuntimeError(translate("Too many displays"));
|
2020-03-28 11:34:18 -04:00
|
|
|
}
|
2020-03-30 11:47:14 -04:00
|
|
|
primary_display_t *allocate_display_bus(void) {
|
|
|
|
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
|
2020-08-17 22:07:24 -04:00
|
|
|
mp_const_obj_t display_bus_type = displays[i].bus_base.type;
|
2020-08-17 22:09:21 -04:00
|
|
|
if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) {
|
2020-03-30 11:47:14 -04:00
|
|
|
return &displays[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
primary_display_t *allocate_display_bus_or_raise(void) {
|
|
|
|
primary_display_t *result = allocate_display_bus();
|
|
|
|
if (result) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
mp_raise_RuntimeError(translate("Too many display busses"));
|
|
|
|
}
|