diff --git a/esphome/components/lvgl/gradient.py b/esphome/components/lvgl/gradient.py index c4a3c8f2c..e075433d0 100644 --- a/esphome/components/lvgl/gradient.py +++ b/esphome/components/lvgl/gradient.py @@ -1,3 +1,5 @@ +from operator import itemgetter + from esphome import config_validation as cv import esphome.codegen as cg from esphome.const import ( @@ -11,6 +13,7 @@ from esphome.core import ID from esphome.cpp_generator import MockObj from .defines import CONF_GRADIENTS, CONF_OPA, LV_DITHER, add_define, add_warning +from .helpers import add_lv_use from .lv_validation import lv_color, lv_percentage, opacity from .lvcode import lv from .types import lv_color_t, lv_gradient_t, lv_opa_t @@ -50,6 +53,7 @@ GRADIENT_SCHEMA = cv.ensure_list( async def gradients_to_code(config): + add_lv_use("gradient") max_stops = 2 if any(CONF_DITHER in x for x in config.get(CONF_GRADIENTS, ())): add_warning( @@ -58,7 +62,7 @@ async def gradients_to_code(config): for gradient in config.get(CONF_GRADIENTS, ()): var = MockObj(cg.new_Pvariable(gradient[CONF_ID]), "->") idbase = gradient[CONF_ID].id - stops = gradient[CONF_STOPS] + stops = sorted(gradient[CONF_STOPS], key=itemgetter(CONF_POSITION)) max_stops = max(max_stops, len(stops)) if gradient[CONF_DIRECTION].startswith("VER"): lv.grad_vertical_init(var) diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index d8248e4aa..0308e6b78 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -864,6 +864,32 @@ void lv_scale_draw_event_cb(lv_event_t *e, int16_t range_start, int16_t range_en } #endif // USE_LVGL_SCALE +#ifdef USE_LVGL_GRADIENT +/** + * + * @param dsc The gradient descriptor containing the color stops + * @param pos The current position to calculate the color for + * @return The color for the given position + */ + +lv_color_t lv_grad_calculate_color(const lv_grad_dsc_t *dsc, int32_t pos) { + if (dsc->stops_count == 0) + return lv_color_black(); + if (dsc->stops_count == 1 || pos <= dsc->stops[0].frac) + return dsc->stops[0].color; + if (pos >= dsc->stops[dsc->stops_count - 1].frac) + return dsc->stops[dsc->stops_count - 1].color; + int i = 1; + while (i < dsc->stops_count && dsc->stops[i].frac < pos) + i++; + auto *stop1 = &dsc->stops[i - 1]; + auto *stop2 = &dsc->stops[i]; + int32_t range = stop2->frac - stop1->frac; + int32_t offset = pos - stop1->frac; + return lv_color_mix(stop2->color, stop1->color, range == 0 ? 0 : (offset * 255) / range); +} +#endif + static void lv_container_constructor(const lv_obj_class_t *class_p, lv_obj_t *obj) { LV_TRACE_OBJ_CREATE("begin"); LV_UNUSED(class_p); diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 146866f5b..83cf9cc09 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -115,6 +115,16 @@ inline void lv_animimg_set_src(lv_obj_t *img, std::vector images int16_t lv_get_needle_angle_for_value(lv_obj_t *obj, int value); #endif +#ifdef USE_LVGL_GRADIENT +/** + * + * @param dsc The gradient descriptor containing the color stops + * @param pos The current position to calculate the color for + * @return The color for the given position + */ + +lv_color_t lv_grad_calculate_color(const lv_grad_dsc_t *dsc, int32_t pos); +#endif // Parent class for things that wrap an LVGL object class LvCompound { public: diff --git a/esphome/core/defines.h b/esphome/core/defines.h index daca55d68..592c8c46a 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -96,6 +96,7 @@ #define USE_LVGL_CHECKBOX #define USE_LVGL_DROPDOWN #define USE_LVGL_FONT +#define USE_LVGL_GRADIENT #define USE_LVGL_IMAGE #define USE_LVGL_IMAGEBUTTON #define USE_LVGL_KEY_LISTENER