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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user