openmv/modules/py_display.c
iabdalkader daf2bb30da misc: Restructure repo.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-04-13 08:28:34 +02:00

320 lines
16 KiB
C

/*
* SPDX-License-Identifier: MIT
*
* Copyright (C) 2013-2024 OpenMV, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Display Python module.
*/
#include "omv_boardconfig.h"
#if MICROPY_PY_DISPLAY
#include "py/obj.h"
#include "py/objarray.h"
#include "py/mphal.h"
#include "py/runtime.h"
#include "py_helper.h"
#include "py_image.h"
#include "py_display.h"
static mp_obj_t py_display_width(mp_obj_t self_in) {
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_int(self->width);
}
static MP_DEFINE_CONST_FUN_OBJ_1(py_display_width_obj, py_display_width);
static mp_obj_t py_display_height(mp_obj_t self_in) {
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_int(self->height);
}
static MP_DEFINE_CONST_FUN_OBJ_1(py_display_height_obj, py_display_height);
static mp_obj_t py_display_triple_buffer(mp_obj_t self_in) {
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_int(self->triple_buffer);
}
static MP_DEFINE_CONST_FUN_OBJ_1(py_display_triple_buffer_obj, py_display_triple_buffer);
static mp_obj_t py_display_bgr(mp_obj_t self_in) {
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_int(self->bgr);
}
static MP_DEFINE_CONST_FUN_OBJ_1(py_display_bgr_obj, py_display_bgr);
static mp_obj_t py_display_byte_swap(mp_obj_t self_in) {
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_int(self->byte_swap);
}
static MP_DEFINE_CONST_FUN_OBJ_1(py_display_byte_swap_obj, py_display_byte_swap);
static mp_obj_t py_display_framesize(mp_obj_t self_in) {
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_int(self->framesize);
}
static MP_DEFINE_CONST_FUN_OBJ_1(py_display_framesize_obj, py_display_framesize);
static mp_obj_t py_display_refresh(mp_obj_t self_in) {
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_int(self->refresh);
}
static MP_DEFINE_CONST_FUN_OBJ_1(py_display_refresh_obj, py_display_refresh);
static mp_obj_t py_display_deinit(mp_obj_t self_in) {
py_display_obj_t *self = MP_OBJ_TO_PTR(self_in);
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
if (display_p->deinit != NULL) {
display_p->deinit(self);
}
if (self->bl_controller != mp_const_none) {
mp_obj_t dest[2];
mp_load_method_maybe(self->bl_controller, MP_QSTR_deinit, dest);
if (dest[0] != MP_OBJ_NULL) {
mp_call_method_n_kw(0, 0, dest);
}
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(py_display_deinit_obj, py_display_deinit);
static mp_obj_t py_display_clear(size_t n_args, const mp_obj_t *args) {
py_display_obj_t *self = MP_OBJ_TO_PTR(args[0]);
bool display_off = (n_args > 1 && mp_obj_get_int(args[1]));
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
if (display_p->clear != NULL) {
display_p->clear(self, display_off);
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_display_clear_obj, 1, 2, py_display_clear);
static mp_obj_t py_display_backlight(size_t n_args, const mp_obj_t *args) {
py_display_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (n_args > 1) {
uint32_t intensity = mp_obj_get_int(args[1]);
if (intensity > 100) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Intensity ranges between 0 (off) and 100 (full on)"));
}
if (self->bl_controller != mp_const_none) {
// If the display has a backlight controller set, call it first.
mp_obj_t dest[3];
mp_load_method(self->bl_controller, MP_QSTR_backlight, dest);
dest[2] = args[1];
mp_call_method_n_kw(1, 0, dest);
} else {
// Otherwise, if the display protocol's set_backlight is set, call it.
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
if (display_p->set_backlight != NULL) {
display_p->set_backlight(self, intensity);
} else {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Display does not support backlight control."));
}
}
self->intensity = intensity;
} else {
return mp_obj_new_int(self->intensity);
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_display_backlight_obj, 1, 2, py_display_backlight);
static mp_obj_t py_display_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum {
ARG_image, ARG_x, ARG_y, ARG_x_scale, ARG_y_scale, ARG_roi,
ARG_channel, ARG_alpha, ARG_color_palette, ARG_alpha_palette, ARG_hint
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_image, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
{ MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
{ MP_QSTR_x_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_y_scale, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_roi, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_rgb_channel, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_alpha, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 255 } },
{ MP_QSTR_color_palette, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_alpha_palette, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_hint, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
};
// Parse args.
py_display_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
fb_alloc_mark();
image_t *image = py_helper_arg_to_image(args[ARG_image].u_obj, ARG_IMAGE_ANY | ARG_IMAGE_ALLOC);
rectangle_t roi = py_helper_arg_to_roi(args[ARG_roi].u_obj, image);
if (args[ARG_channel].u_int < -1 || args[ARG_channel].u_int > 2) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("RGB channel can be 0, 1, or 2"));
}
if (args[ARG_alpha].u_int < 0 || args[ARG_alpha].u_int > 255) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Alpha ranges between 0 and 255"));
}
float x_scale = 1.0f;
float y_scale = 1.0f;
py_helper_arg_to_scale(args[ARG_x_scale].u_obj, args[ARG_y_scale].u_obj, &x_scale, &y_scale);
if (y_scale < 0 && !self->triple_buffer) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Vertical flip requires triple buffering!"));
}
const uint16_t *color_palette = py_helper_arg_to_palette(args[ARG_color_palette].u_obj, PIXFORMAT_RGB565);
const uint8_t *alpha_palette = py_helper_arg_to_palette(args[ARG_alpha_palette].u_obj, PIXFORMAT_GRAYSCALE);
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
display_p->write(self, image, args[ARG_x].u_int, args[ARG_y].u_int, x_scale, y_scale, &roi,
args[ARG_channel].u_int, args[ARG_alpha].u_int, color_palette, alpha_palette, args[ARG_hint].u_int);
fb_alloc_free_till_mark();
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(py_display_write_obj, 2, py_display_write);
static mp_obj_t py_display_bus_write(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_cmd, ARG_args, ARG_dcs };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_cmd, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_args, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_dcs, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
};
// Parse args.
py_display_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
if (display_p->bus_write == NULL) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Expected a display controller"));
} else if (args[ARG_args].u_obj == mp_const_none) {
display_p->bus_write(self, args[ARG_cmd].u_int, NULL, 0, args[ARG_dcs].u_bool);
} else if (mp_obj_is_int(args[ARG_args].u_obj)) {
uint8_t arg = mp_obj_get_int(args[ARG_args].u_obj);
display_p->bus_write(self, args[ARG_cmd].u_int, &arg, 1, args[ARG_dcs].u_bool);
} else {
mp_buffer_info_t rbuf;
mp_get_buffer_raise(args[ARG_args].u_obj, &rbuf, MP_BUFFER_READ);
display_p->bus_write(self, args[ARG_cmd].u_int, rbuf.buf, rbuf.len, args[ARG_dcs].u_bool);
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(py_display_bus_write_obj, 1, py_display_bus_write);
static mp_obj_t py_display_bus_read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_cmd, ARG_len, ARG_args, ARG_dcs };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_cmd, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_len, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_args, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_dcs, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_bool = false } },
};
// Parse args.
py_display_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
mp_obj_array_t *wbuf = MP_OBJ_TO_PTR(mp_obj_new_bytearray_by_ref(args[ARG_len].u_int,
m_new(byte, args[ARG_len].u_int)));
if (display_p->bus_read == NULL) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Expected a display controller"));
} else if (args[ARG_args].u_obj == mp_const_none) {
display_p->bus_read(self, args[ARG_cmd].u_int, NULL, 0, wbuf->items, wbuf->len, args[ARG_dcs].u_bool);
} else if (mp_obj_is_int(args[ARG_args].u_obj)) {
uint8_t arg = mp_obj_get_int(args[ARG_args].u_obj);
display_p->bus_read(self, args[ARG_cmd].u_int, &arg, 1, wbuf->items, wbuf->len, args[ARG_dcs].u_bool);
} else {
mp_buffer_info_t rbuf;
mp_get_buffer_raise(args[ARG_args].u_obj, &rbuf, MP_BUFFER_READ);
display_p->bus_read(self, args[ARG_cmd].u_int, rbuf.buf, rbuf.len,
wbuf->items, wbuf->len, args[ARG_dcs].u_bool);
}
return MP_OBJ_FROM_PTR(wbuf);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(py_display_bus_read_obj, 1, py_display_bus_read);
static const mp_rom_map_elem_t py_display_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_display) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&py_display_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&py_display_width_obj) },
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&py_display_height_obj) },
{ MP_ROM_QSTR(MP_QSTR_triple_buffer), MP_ROM_PTR(&py_display_triple_buffer_obj) },
{ MP_ROM_QSTR(MP_QSTR_bgr), MP_ROM_PTR(&py_display_bgr_obj) },
{ MP_ROM_QSTR(MP_QSTR_byte_swap), MP_ROM_PTR(&py_display_byte_swap_obj) },
{ MP_ROM_QSTR(MP_QSTR_framesize), MP_ROM_PTR(&py_display_framesize_obj) },
{ MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&py_display_refresh_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&py_display_clear_obj) },
{ MP_ROM_QSTR(MP_QSTR_backlight), MP_ROM_PTR(&py_display_backlight_obj) },
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&py_display_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_bus_write), MP_ROM_PTR(&py_display_bus_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_bus_read), MP_ROM_PTR(&py_display_bus_read_obj) },
};
MP_DEFINE_CONST_DICT(py_display_locals_dict, py_display_locals_dict_table);
static const mp_rom_map_elem_t globals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_display) },
{ MP_ROM_QSTR(MP_QSTR_QVGA), MP_ROM_INT(DISPLAY_RESOLUTION_QVGA) },
{ MP_ROM_QSTR(MP_QSTR_TQVGA), MP_ROM_INT(DISPLAY_RESOLUTION_TQVGA) },
{ MP_ROM_QSTR(MP_QSTR_FHVGA), MP_ROM_INT(DISPLAY_RESOLUTION_FHVGA) },
{ MP_ROM_QSTR(MP_QSTR_FHVGA2), MP_ROM_INT(DISPLAY_RESOLUTION_FHVGA2) },
{ MP_ROM_QSTR(MP_QSTR_VGA), MP_ROM_INT(DISPLAY_RESOLUTION_VGA) },
{ MP_ROM_QSTR(MP_QSTR_THVGA), MP_ROM_INT(DISPLAY_RESOLUTION_THVGA) },
{ MP_ROM_QSTR(MP_QSTR_FWVGA), MP_ROM_INT(DISPLAY_RESOLUTION_FWVGA) },
{ MP_ROM_QSTR(MP_QSTR_FWVGA2), MP_ROM_INT(DISPLAY_RESOLUTION_FWVGA2) },
{ MP_ROM_QSTR(MP_QSTR_TFWVGA), MP_ROM_INT(DISPLAY_RESOLUTION_TFWVGA) },
{ MP_ROM_QSTR(MP_QSTR_TFWVGA2), MP_ROM_INT(DISPLAY_RESOLUTION_TFWVGA2) },
{ MP_ROM_QSTR(MP_QSTR_SVGA), MP_ROM_INT(DISPLAY_RESOLUTION_SVGA) },
{ MP_ROM_QSTR(MP_QSTR_WSVGA), MP_ROM_INT(DISPLAY_RESOLUTION_WSVGA) },
{ MP_ROM_QSTR(MP_QSTR_XGA), MP_ROM_INT(DISPLAY_RESOLUTION_XGA) },
{ MP_ROM_QSTR(MP_QSTR_SXGA), MP_ROM_INT(DISPLAY_RESOLUTION_SXGA) },
{ MP_ROM_QSTR(MP_QSTR_SXGA2), MP_ROM_INT(DISPLAY_RESOLUTION_SXGA2) },
{ MP_ROM_QSTR(MP_QSTR_UXGA), MP_ROM_INT(DISPLAY_RESOLUTION_UXGA) },
{ MP_ROM_QSTR(MP_QSTR_HD), MP_ROM_INT(DISPLAY_RESOLUTION_HD) },
{ MP_ROM_QSTR(MP_QSTR_FHD), MP_ROM_INT(DISPLAY_RESOLUTION_FHD) },
#ifdef OMV_SPI_DISPLAY_CONTROLLER
{ MP_ROM_QSTR(MP_QSTR_SPIDisplay), MP_ROM_PTR(&py_spi_display_type) },
#endif
#ifdef OMV_RGB_DISPLAY_CONTROLLER
{ MP_ROM_QSTR(MP_QSTR_RGBDisplay), MP_ROM_PTR(&py_rgb_display_type) },
#endif
#ifdef OMV_DSI_DISPLAY_CONTROLLER
{ MP_ROM_QSTR(MP_QSTR_DSIDisplay), MP_ROM_PTR(&py_dsi_display_type) },
#endif
#if OMV_DISPLAY_CEC_ENABLE || OMV_DISPLAY_DDC_ENABLE
{ MP_ROM_QSTR(MP_QSTR_DisplayData), MP_ROM_PTR(&py_display_data_type) },
#endif
};
static MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table);
const mp_obj_module_t display_module = {
.base = { &mp_type_module },
.globals = (mp_obj_t) &globals_dict,
};
MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_display, display_module);
#endif // MICROPY_PY_DISPLAY