1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-17 23:35:47 +00:00

[lvgl] Implement better software rotation (#7595)

This commit is contained in:
Clyde Stubbs
2024-10-16 14:26:06 +11:00
committed by GitHub
parent b274d6901a
commit 6a86d92781
7 changed files with 201 additions and 58 deletions

View File

@@ -69,30 +69,38 @@ std::string lv_event_code_name_for(uint8_t event_code) {
}
return str_sprintf("%2d", event_code);
}
static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) {
// make sure all coordinates are even
if (area->x1 & 1)
area->x1--;
if (!(area->x2 & 1))
area->x2++;
if (area->y1 & 1)
area->y1--;
if (!(area->y2 & 1))
area->y2++;
// cater for display driver chips with special requirements for bounds of partial
// draw areas. Extend the draw area to satisfy:
// * Coordinates must be a multiple of draw_rounding
auto *comp = static_cast<LvglComponent *>(disp_drv->user_data);
auto draw_rounding = comp->draw_rounding;
// round down the start coordinates
area->x1 = area->x1 / draw_rounding * draw_rounding;
area->y1 = area->y1 / draw_rounding * draw_rounding;
// round up the end coordinates
area->x2 = (area->x2 + draw_rounding) / draw_rounding * draw_rounding - 1;
area->y2 = (area->y2 + draw_rounding) / draw_rounding * draw_rounding - 1;
}
lv_event_code_t lv_api_event; // NOLINT
lv_event_code_t lv_update_event; // NOLINT
void LvglComponent::dump_config() { ESP_LOGCONFIG(TAG, "LVGL:"); }
void LvglComponent::dump_config() {
ESP_LOGCONFIG(TAG, "LVGL:");
ESP_LOGCONFIG(TAG, " Rotation: %d", this->rotation);
ESP_LOGCONFIG(TAG, " Draw rounding: %d", (int) this->draw_rounding);
}
void LvglComponent::set_paused(bool paused, bool show_snow) {
this->paused_ = paused;
this->show_snow_ = show_snow;
this->snow_line_ = 0;
if (!paused && lv_scr_act() != nullptr) {
lv_disp_trig_activity(this->disp_); // resets the inactivity time
lv_obj_invalidate(lv_scr_act());
}
this->pause_callbacks_.call(paused);
}
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) {
lv_obj_add_event_cb(obj, callback, event, this);
}
@@ -133,19 +141,64 @@ void LvglComponent::show_prev_page(lv_scr_load_anim_t anim, uint32_t time) {
} while (this->pages_[this->current_page_]->skip); // skip empty pages()
this->show_page(this->current_page_, anim, time);
}
void LvglComponent::draw_buffer_(const lv_area_t *area, const uint8_t *ptr) {
void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) {
auto width = lv_area_get_width(area);
auto height = lv_area_get_height(area);
auto x1 = area->x1;
auto y1 = area->y1;
lv_color_t *dst = this->rotate_buf_;
switch (this->rotation) {
case display::DISPLAY_ROTATION_90_DEGREES:
for (lv_coord_t x = height - 1; x-- != 0;) {
for (lv_coord_t y = 0; y != width; y++) {
dst[y * height + x] = *ptr++;
}
}
y1 = x1;
x1 = this->disp_drv_.ver_res - area->y1 - height;
width = height;
height = lv_area_get_width(area);
break;
case display::DISPLAY_ROTATION_180_DEGREES:
for (lv_coord_t y = height; y-- != 0;) {
for (lv_coord_t x = width; x-- != 0;) {
dst[y * width + x] = *ptr++;
}
}
x1 = this->disp_drv_.hor_res - x1 - width;
y1 = this->disp_drv_.ver_res - y1 - height;
break;
case display::DISPLAY_ROTATION_270_DEGREES:
for (lv_coord_t x = 0; x != height; x++) {
for (lv_coord_t y = width; y-- != 0;) {
dst[y * height + x] = *ptr++;
}
}
x1 = y1;
y1 = this->disp_drv_.hor_res - area->x1 - width;
width = height;
height = lv_area_get_width(area);
break;
default:
dst = ptr;
break;
}
for (auto *display : this->displays_) {
display->draw_pixels_at(area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area), ptr,
display::COLOR_ORDER_RGB, LV_BITNESS, LV_COLOR_16_SWAP);
ESP_LOGV(TAG, "draw buffer x1=%d, y1=%d, width=%d, height=%d", x1, y1, width, height);
display->draw_pixels_at(x1, y1, width, height, (const uint8_t *) dst, display::COLOR_ORDER_RGB, LV_BITNESS,
LV_COLOR_16_SWAP);
}
}
void LvglComponent::flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
if (!this->paused_) {
auto now = millis();
this->draw_buffer_(area, (const uint8_t *) color_p);
ESP_LOGV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area),
lv_area_get_height(area), (int) (millis() - now));
this->draw_buffer_(area, color_p);
ESP_LOGVV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area),
lv_area_get_height(area), (int) (millis() - now));
}
lv_disp_flush_ready(disp_drv);
}
@@ -160,6 +213,13 @@ IdleTrigger::IdleTrigger(LvglComponent *parent, TemplatableValue<uint32_t> timeo
});
}
PauseTrigger::PauseTrigger(LvglComponent *parent, TemplatableValue<bool> paused) : paused_(std::move(paused)) {
parent->add_on_pause_callback([this](bool pausing) {
if (this->paused_.value() == pausing)
this->trigger();
});
}
#ifdef USE_LVGL_TOUCHSCREEN
LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) {
lv_indev_drv_init(&this->drv_);
@@ -261,23 +321,31 @@ void LvKeyboardType::set_obj(lv_obj_t *lv_obj) {
#endif // USE_LVGL_KEYBOARD
void LvglComponent::write_random_() {
// length of 2 lines in 32 bit units
// we write 2 lines for the benefit of displays that won't write one line at a time.
size_t line_len = this->disp_drv_.hor_res * LV_COLOR_DEPTH / 8 / 4 * 2;
for (size_t i = 0; i != line_len; i++) {
((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32();
int iterations = 6 - lv_disp_get_inactive_time(this->disp_) / 60000;
if (iterations <= 0)
iterations = 1;
while (iterations-- != 0) {
auto col = random_uint32() % this->disp_drv_.hor_res;
col = col / this->draw_rounding * this->draw_rounding;
auto row = random_uint32() % this->disp_drv_.ver_res;
row = row / this->draw_rounding * this->draw_rounding;
auto size = (random_uint32() % 32) / this->draw_rounding * this->draw_rounding - 1;
lv_area_t area;
area.x1 = col;
area.y1 = row;
area.x2 = col + size;
area.y2 = row + size;
if (area.x2 >= this->disp_drv_.hor_res)
area.x2 = this->disp_drv_.hor_res - 1;
if (area.y2 >= this->disp_drv_.ver_res)
area.y2 = this->disp_drv_.ver_res - 1;
size_t line_len = lv_area_get_width(&area) * lv_area_get_height(&area) / 2;
for (size_t i = 0; i != line_len; i++) {
((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32();
}
this->draw_buffer_(&area, (lv_color_t *) this->draw_buf_.buf1);
}
lv_area_t area;
area.x1 = 0;
area.x2 = this->disp_drv_.hor_res - 1;
if (this->snow_line_ == this->disp_drv_.ver_res / 2) {
area.y1 = static_cast<lv_coord_t>(random_uint32() % (this->disp_drv_.ver_res / 2) * 2);
} else {
area.y1 = this->snow_line_++ * 2;
}
// write 2 lines
area.y2 = area.y1 + 1;
this->draw_buffer_(&area, (const uint8_t *) this->draw_buf_.buf1);
}
void LvglComponent::setup() {
@@ -291,7 +359,7 @@ void LvglComponent::setup() {
auto *display = this->displays_[0];
size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_;
auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8;
auto *buf = lv_custom_mem_alloc(buf_bytes);
auto *buf = lv_custom_mem_alloc(buf_bytes); // NOLINT
if (buf == nullptr) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
@@ -307,26 +375,30 @@ void LvglComponent::setup() {
this->disp_drv_.full_refresh = this->full_refresh_;
this->disp_drv_.flush_cb = static_flush_cb;
this->disp_drv_.rounder_cb = rounder_cb;
switch (display->get_rotation()) {
case display::DISPLAY_ROTATION_0_DEGREES:
break;
case display::DISPLAY_ROTATION_90_DEGREES:
this->disp_drv_.sw_rotate = true;
this->disp_drv_.rotated = LV_DISP_ROT_90;
break;
case display::DISPLAY_ROTATION_180_DEGREES:
this->disp_drv_.sw_rotate = true;
this->disp_drv_.rotated = LV_DISP_ROT_180;
break;
case display::DISPLAY_ROTATION_270_DEGREES:
this->disp_drv_.sw_rotate = true;
this->disp_drv_.rotated = LV_DISP_ROT_270;
break;
this->rotation = display->get_rotation();
if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) {
this->rotate_buf_ = static_cast<lv_color_t *>(lv_custom_mem_alloc(buf_bytes)); // NOLINT
if (this->rotate_buf_ == nullptr) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
#endif
this->mark_failed();
this->status_set_error("Memory allocation failure");
return;
}
}
display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES);
this->disp_drv_.hor_res = (lv_coord_t) display->get_width();
this->disp_drv_.ver_res = (lv_coord_t) display->get_height();
ESP_LOGV(TAG, "sw_rotate = %d, rotated=%d", this->disp_drv_.sw_rotate, this->disp_drv_.rotated);
switch (this->rotation) {
default:
this->disp_drv_.hor_res = (lv_coord_t) display->get_width();
this->disp_drv_.ver_res = (lv_coord_t) display->get_height();
break;
case display::DISPLAY_ROTATION_90_DEGREES:
case display::DISPLAY_ROTATION_270_DEGREES:
this->disp_drv_.ver_res = (lv_coord_t) display->get_width();
this->disp_drv_.hor_res = (lv_coord_t) display->get_height();
break;
}
this->disp_ = lv_disp_drv_register(&this->disp_drv_);
for (const auto &v : this->init_lambdas_)
v(this);