diff --git a/Kconfig b/Kconfig index 20fdfa7c7..f31462593 100644 --- a/Kconfig +++ b/Kconfig @@ -229,6 +229,14 @@ menu "LVGL configuration" 0: use a simple renderer capable of drawing only simple rectangles with gradient, images, texts, and straight lines only, 1: use a complex renderer capable of drawing rounded corners, shadow, skew lines, and arcs too. + config LV_USE_DRAW_SW_COMPLEX_GRADIENTS + bool "Enable drawing complex gradients in software" + default n + depends on LV_USE_DRAW_SW + help + 0: do not enable complex gradients + 1: enable complex gradients (linear at an angle, radial or conical) + config LV_DRAW_SW_SHADOW_CACHE_SIZE int "Allow buffering some shadow calculation" depends on LV_DRAW_SW_COMPLEX diff --git a/demos/render/lv_demo_render.c b/demos/render/lv_demo_render.c index a56e1482f..182bb69b7 100644 --- a/demos/render/lv_demo_render.c +++ b/demos/render/lv_demo_render.c @@ -834,6 +834,238 @@ static void blend_mode_cb(lv_obj_t * parent) } +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + +static lv_obj_t * create_linear_gradient_obj(lv_obj_t * parent, int32_t col, int32_t row, lv_grad_dsc_t * grad, + int32_t x1, int32_t y1, lv_grad_extend_t extend, bool use_opa_map, int32_t radius) +{ + const lv_color_t grad_color[2] = { + LV_COLOR_MAKE(0xd5, 0x03, 0x47), + LV_COLOR_MAKE(0x00, 0x00, 0x00), + }; + + const lv_opa_t grad_opa[2] = { + LV_OPA_100, LV_OPA_0, + }; + + /*init gradient color map*/ + lv_gradient_init_stops(grad, grad_color, use_opa_map ? grad_opa : NULL, NULL, sizeof(grad_color) / sizeof(lv_color_t)); + + /*init gradient parameters*/ + grad->dir = LV_GRAD_DIR_LINEAR; + grad->params.linear.start.x = 0; /*vector start x position*/ + grad->params.linear.start.y = 0; /*vector start y position*/ + grad->params.linear.end.x = x1; /*vector end x position*/ + grad->params.linear.end.y = y1; /*vector end y position*/ + grad->extend = extend; /*color pattern outside the vector*/ + + /*create rectangle*/ + lv_obj_t * obj = lv_obj_create(parent); + lv_obj_remove_style_all(obj); + lv_obj_set_size(obj, 70, 50); + lv_obj_set_style_radius(obj, radius, 0); + lv_obj_set_style_bg_opa(obj, LV_OPA_COVER, 0); + lv_obj_set_style_opa(obj, opa_saved, 0); + + /*set gradient as background*/ + lv_obj_set_style_bg_grad(obj, grad, 0); + + add_to_cell(obj, col, row); + + return obj; +} + +static void linear_gradient_cb(lv_obj_t * parent) +{ + static const int32_t grid_cols[] = { 53, 53, 53, 53, 53, 53, 53, 53, 53, LV_GRID_TEMPLATE_LAST }; + static const int32_t grid_rows[] = { 32, 40, 40, 40, 40, 40, 40, LV_GRID_TEMPLATE_LAST }; + lv_obj_set_grid_dsc_array(parent, grid_cols, grid_rows); + + const char * opa_txt[] = { "no opa", "no opa round", "stop opa", "stop opa round" }; + int32_t radius_values[] = { 0, 20, 0, 20 }; + bool opa_map_values[] = { false, false, true, true }; + + const char * offs_txt[] = { "pad", "repeat", "reflect" }; + int32_t x1_values[] = { lv_pct(100), lv_pct(15), lv_pct(30)}; + int32_t y1_values[] = { lv_pct(100), lv_pct(30), lv_pct(15) }; + lv_grad_extend_t extend_values[] = { LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT, LV_GRAD_EXTEND_REFLECT }; + + static lv_grad_dsc_t grad_values[3][4]; + + uint32_t y; + for(y = 0; y < 3; y++) { + lv_obj_t * offs_label = lv_label_create(parent); + lv_label_set_text(offs_label, offs_txt[y]); + lv_obj_set_grid_cell(offs_label, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 1 + y * 2, 2); + } + + uint32_t x; + for(x = 0; x < 4; x++) { + lv_obj_t * op_label = lv_label_create(parent); + lv_label_set_text(op_label, opa_txt[x]); + lv_obj_set_grid_cell(op_label, LV_GRID_ALIGN_CENTER, 1 + x * 2, 2, LV_GRID_ALIGN_CENTER, 0, 1); + + for(y = 0; y < 3; y++) { + create_linear_gradient_obj(parent, 1 + x * 2, 1 + y * 2, &grad_values[y][x], x1_values[y], y1_values[y], + extend_values[y], opa_map_values[x], radius_values[x]); + } + } +} + +static lv_obj_t * create_radial_gradient_obj(lv_obj_t * parent, int32_t col, int32_t row, lv_grad_dsc_t * grad, + int32_t offs, int32_t r0, lv_grad_extend_t extend, bool use_opa_map, int32_t radius) +{ + const lv_color_t grad_color[2] = { + LV_COLOR_MAKE(0xd5, 0x03, 0x47), + LV_COLOR_MAKE(0x00, 0x00, 0x00), + }; + + const lv_opa_t grad_opa[2] = { + LV_OPA_100, LV_OPA_0, + }; + + /*init gradient color map*/ + lv_gradient_init_stops(grad, grad_color, use_opa_map ? grad_opa : NULL, NULL, sizeof(grad_color) / sizeof(lv_color_t)); + + /*init gradient parameters*/ + grad->dir = LV_GRAD_DIR_RADIAL; + grad->params.radial.focal.x = lv_pct(50); /*start circle center x position*/ + grad->params.radial.focal.y = lv_pct(50); /*start circle center y position*/ + grad->params.radial.focal_extent.x = grad->params.radial.focal.x + r0; /*start circle point x coordinate*/ + grad->params.radial.focal_extent.y = grad->params.radial.focal.y; /*start circle point y coordinate*/ + grad->params.radial.end.x = grad->params.radial.focal.x + offs; /*end circle center x position*/ + grad->params.radial.end.y = grad->params.radial.focal.y + offs; /*end circle center y position*/ + grad->params.radial.end_extent.x = grad->params.radial.end.x; /*end circle point x coordinate*/ + grad->params.radial.end_extent.y = lv_pct(85); /*end circle point y coordinate*/ + grad->extend = extend; /*color pattern outside the border circles*/ + + /*create rectangle*/ + lv_obj_t * obj = lv_obj_create(parent); + lv_obj_remove_style_all(obj); + lv_obj_set_size(obj, 70, 50); + lv_obj_set_style_radius(obj, radius, 0); + lv_obj_set_style_bg_opa(obj, LV_OPA_COVER, 0); + lv_obj_set_style_opa(obj, opa_saved, 0); + + /*set gradient as background*/ + lv_obj_set_style_bg_grad(obj, grad, 0); + + add_to_cell(obj, col, row); + + return obj; +} + +static void radial_gradient_cb(lv_obj_t * parent) +{ + static const int32_t grid_cols[] = { 53, 53, 53, 53, 53, 53, 53, 53, 53, LV_GRID_TEMPLATE_LAST }; + static const int32_t grid_rows[] = { 32, 40, 40, 40, 40, 40, 40, LV_GRID_TEMPLATE_LAST }; + lv_obj_set_grid_dsc_array(parent, grid_cols, grid_rows); + + const char * opa_txt[] = { "no opa", "no opa round", "stop opa", "stop opa round" }; + int32_t radius_values[] = { 0, 20, 0, 20 }; + bool opa_map_values[] = { false, false, true, true }; + + const char * offs_txt[] = { "pad", "repeat", "reflect" }; + lv_grad_extend_t extend_values[] = { LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT, LV_GRAD_EXTEND_REFLECT}; + + static lv_grad_dsc_t grad_values[3][4]; + + uint32_t y; + for(y = 0; y < 3; y++) { + lv_obj_t * offs_label = lv_label_create(parent); + lv_label_set_text(offs_label, offs_txt[y]); + lv_obj_set_grid_cell(offs_label, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 1 + y * 2, 2); + } + + uint32_t x; + for(x = 0; x < 4; x++) { + lv_obj_t * op_label = lv_label_create(parent); + lv_label_set_text(op_label, opa_txt[x]); + lv_obj_set_grid_cell(op_label, LV_GRID_ALIGN_CENTER, 1 + x * 2, 2, LV_GRID_ALIGN_CENTER, 0, 1); + + for(y = 0; y < 3; y++) { + create_radial_gradient_obj(parent, 1 + x * 2, 1 + y * 2, &grad_values[y][x], y * 5, 0, extend_values[y], + opa_map_values[x], radius_values[x]); + } + } +} + +static lv_obj_t * create_conical_gradient_obj(lv_obj_t * parent, int32_t col, int32_t row, lv_grad_dsc_t * grad, + int32_t a0, int32_t a1, lv_grad_extend_t extend, bool use_opa_map, int32_t radius) +{ + const lv_color_t grad_color[2] = { + LV_COLOR_MAKE(0xd5, 0x03, 0x47), + LV_COLOR_MAKE(0x00, 0x00, 0x00), + }; + + const lv_opa_t grad_opa[2] = { + LV_OPA_100, LV_OPA_0, + }; + + /*init gradient color map*/ + lv_gradient_init_stops(grad, grad_color, use_opa_map ? grad_opa : NULL, NULL, sizeof(grad_color) / sizeof(lv_color_t)); + + /*init gradient parameters*/ + grad->dir = LV_GRAD_DIR_CONICAL; + grad->params.conical.center.x = lv_pct(50); /*center x position*/ + grad->params.conical.center.y = lv_pct(50); /*center y position*/ + grad->params.conical.start_angle = a0; /*start angle*/ + grad->params.conical.end_angle = a1; /*end angle*/ + grad->extend = extend; /*color pattern outside the vector*/ + + /*create rectangle*/ + lv_obj_t * obj = lv_obj_create(parent); + lv_obj_remove_style_all(obj); + lv_obj_set_size(obj, 70, 50); + lv_obj_set_style_radius(obj, radius, 0); + lv_obj_set_style_bg_opa(obj, LV_OPA_COVER, 0); + lv_obj_set_style_opa(obj, opa_saved, 0); + + /*set gradient as background*/ + lv_obj_set_style_bg_grad(obj, grad, 0); + + add_to_cell(obj, col, row); + + return obj; +} + +static void conical_gradient_cb(lv_obj_t * parent) +{ + static const int32_t grid_cols[] = { 53, 53, 53, 53, 53, 53, 53, 53, 53, LV_GRID_TEMPLATE_LAST }; + static const int32_t grid_rows[] = { 32, 40, 40, 40, 40, 40, 40, LV_GRID_TEMPLATE_LAST }; + lv_obj_set_grid_dsc_array(parent, grid_cols, grid_rows); + + const char * opa_txt[] = { "no opa", "no opa round", "stop opa", "stop opa round" }; + int32_t radius_values[] = { 0, 20, 0, 20 }; + bool opa_map_values[] = { false, false, true, true }; + + const char * offs_txt[] = { "pad", "repeat", "reflect" }; + lv_grad_extend_t extend_values[] = { LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT, LV_GRAD_EXTEND_REFLECT }; + + static lv_grad_dsc_t grad_values[3][4]; + + uint32_t y; + for(y = 0; y < 3; y++) { + lv_obj_t * offs_label = lv_label_create(parent); + lv_label_set_text(offs_label, offs_txt[y]); + lv_obj_set_grid_cell(offs_label, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 1 + y * 2, 2); + } + + uint32_t x; + for(x = 0; x < 4; x++) { + lv_obj_t * op_label = lv_label_create(parent); + lv_label_set_text(op_label, opa_txt[x]); + lv_obj_set_grid_cell(op_label, LV_GRID_ALIGN_CENTER, 1 + x * 2, 2, LV_GRID_ALIGN_CENTER, 0, 1); + + for(y = 0; y < 3; y++) { + create_conical_gradient_obj(parent, 1 + x * 2, 1 + y * 2, &grad_values[y][x], 10, 100, extend_values[y], + opa_map_values[x], radius_values[x]); + } + } +} + +#endif + /********************** * STATIC VARIABLES **********************/ @@ -854,6 +1086,12 @@ static scene_dsc_t scenes[] = { {.name = "layer_normal", .create_cb = layer_normal_cb}, {.name = "blend_mode", .create_cb = blend_mode_cb}, +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + {.name = "linear_gradient", .create_cb = linear_gradient_cb}, + {.name = "radial_gradient", .create_cb = radial_gradient_cb}, + {.name = "conical_gradient", .create_cb = conical_gradient_cb}, +#endif + {.name = "", .create_cb = NULL} }; diff --git a/demos/render/lv_demo_render.h b/demos/render/lv_demo_render.h index beab602da..5a1fc6729 100644 --- a/demos/render/lv_demo_render.h +++ b/demos/render/lv_demo_render.h @@ -43,6 +43,11 @@ typedef enum { LV_DEMO_RENDER_SCENE_TRIANGLE, LV_DEMO_RENDER_SCENE_LAYER_NORMAL, LV_DEMO_RENDER_SCENE_BLEND_MODE, +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + LV_DEMO_RENDER_SCENE_LINEAR_GRADIENT, + LV_DEMO_RENDER_SCENE_RADIAL_GRADIENT, + LV_DEMO_RENDER_SCENE_CONICAL_GRADIENT, +#endif _LV_DEMO_RENDER_SCENE_NUM, } lv_demo_render_scene_t; diff --git a/examples/styles/lv_example_style.h b/examples/styles/lv_example_style.h index 680443cc5..d02158ede 100644 --- a/examples/styles/lv_example_style.h +++ b/examples/styles/lv_example_style.h @@ -1,4 +1,4 @@ -/** +/** * @file lv_example_style.h * */ @@ -40,6 +40,9 @@ void lv_example_style_12(void); void lv_example_style_13(void); void lv_example_style_14(void); void lv_example_style_15(void); +void lv_example_style_16(void); +void lv_example_style_17(void); +void lv_example_style_18(void); /********************** * MACROS diff --git a/examples/styles/lv_example_style_16.c b/examples/styles/lv_example_style_16.c new file mode 100644 index 000000000..4798ad4dc --- /dev/null +++ b/examples/styles/lv_example_style_16.c @@ -0,0 +1,73 @@ +#include "../lv_examples.h" +#if LV_BUILD_EXAMPLES + +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + +/** + * Simulate metallic knob using conical gradient + * For best effect set LV_GRADIENT_MAX_STOPS to 8 or at least 3 + */ +void lv_example_style_16(void) +{ +#if LV_GRADIENT_MAX_STOPS >= 8 + static const lv_color_t grad_colors[8] = { + LV_COLOR_MAKE(0xe8, 0xe8, 0xe8), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xfa, 0xfa, 0xfa), + LV_COLOR_MAKE(0x79, 0x79, 0x79), + LV_COLOR_MAKE(0x48, 0x48, 0x48), + LV_COLOR_MAKE(0x4b, 0x4b, 0x4b), + LV_COLOR_MAKE(0x70, 0x70, 0x70), + LV_COLOR_MAKE(0xe8, 0xe8, 0xe8), + }; +#elif LV_GRADIENT_MAX_STOPS >= 3 + static const lv_color_t grad_colors[3] = { + LV_COLOR_MAKE(0xe8, 0xe8, 0xe8), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0x79, 0x79, 0x79), + }; +#else + static const lv_color_t grad_colors[2] = { + LV_COLOR_MAKE(0xe8, 0xe8, 0xe8), + LV_COLOR_MAKE(0x79, 0x79, 0x79), + }; +#endif + + /*Create a style with gradient background and shadow*/ + static lv_style_t style; + lv_style_init(&style); + lv_style_set_radius(&style, 500); + lv_style_set_bg_opa(&style, LV_OPA_COVER); + lv_style_set_shadow_color(&style, lv_color_black()); + lv_style_set_shadow_width(&style, 50); + lv_style_set_shadow_offset_x(&style, 20); + lv_style_set_shadow_offset_y(&style, 20); + lv_style_set_shadow_opa(&style, LV_OPA_50); + + /*First define a color gradient. In this example we use a gray color map with random values.*/ + static lv_grad_dsc_t grad; + + lv_gradient_init_stops(&grad, grad_colors, NULL, NULL, sizeof(grad_colors) / sizeof(lv_color_t)); + + /*Make a conical gradient with the center in the middle of the object*/ +#if LV_GRADIENT_MAX_STOPS >= 8 + lv_grad_conical_init(&grad, LV_GRAD_CENTER, LV_GRAD_CENTER, 0, 120, LV_GRAD_EXTEND_REFLECT); +#elif LV_GRADIENT_MAX_STOPS >= 3 + lv_grad_conical_init(&grad, LV_GRAD_CENTER, LV_GRAD_CENTER, 45, 125, LV_GRAD_EXTEND_REFLECT); +#else + lv_grad_conical_init(&grad, LV_GRAD_CENTER, LV_GRAD_CENTER, 45, 110, LV_GRAD_EXTEND_REFLECT); +#endif + + /*Set gradient as background*/ + lv_style_set_bg_grad(&style, &grad); + + /*Create an object with the new style*/ + lv_obj_t * obj = lv_obj_create(lv_screen_active()); + lv_obj_add_style(obj, &style, 0); + lv_obj_set_size(obj, 200, 200); + lv_obj_center(obj); +} + +#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/ + +#endif /*LV_BUILD_EXAMPLES*/ diff --git a/examples/styles/lv_example_style_17.c b/examples/styles/lv_example_style_17.c new file mode 100644 index 000000000..18edc1e47 --- /dev/null +++ b/examples/styles/lv_example_style_17.c @@ -0,0 +1,42 @@ +#include "../lv_examples.h" +#if LV_BUILD_EXAMPLES + +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + +/** + * Using radial gradient as background + */ +void lv_example_style_17(void) +{ + static const lv_color_t grad_colors[2] = { + LV_COLOR_MAKE(0x9B, 0x18, 0x42), + LV_COLOR_MAKE(0x00, 0x00, 0x00), + }; + + int32_t width = lv_display_get_horizontal_resolution(NULL); + int32_t height = lv_display_get_vertical_resolution(NULL); + + static lv_style_t style; + lv_style_init(&style); + + /*First define a color gradient. In this example we use a purple to black color map.*/ + static lv_grad_dsc_t grad; + + lv_gradient_init_stops(&grad, grad_colors, NULL, NULL, sizeof(grad_colors) / sizeof(lv_color_t)); + + /*Make a radial gradient with the center in the middle of the object, extending to the farthest corner*/ + lv_grad_radial_init(&grad, LV_GRAD_CENTER, LV_GRAD_CENTER, LV_GRAD_RIGHT, LV_GRAD_BOTTOM, LV_GRAD_EXTEND_PAD); + + /*Set gradient as background*/ + lv_style_set_bg_grad(&style, &grad); + + /*Create an object with the new style*/ + lv_obj_t * obj = lv_obj_create(lv_screen_active()); + lv_obj_add_style(obj, &style, 0); + lv_obj_set_size(obj, width, height); + lv_obj_center(obj); +} + +#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/ + +#endif /*LV_BUILD_EXAMPLES*/ diff --git a/examples/styles/lv_example_style_18.c b/examples/styles/lv_example_style_18.c new file mode 100644 index 000000000..01c21a084 --- /dev/null +++ b/examples/styles/lv_example_style_18.c @@ -0,0 +1,88 @@ +#include "../lv_examples.h" +#if LV_BUILD_EXAMPLES + +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + +/** + * Using various gradients for button background + */ +void lv_example_style_18(void) +{ + static const lv_color_t grad_colors[2] = { + LV_COLOR_MAKE(0x26, 0xa0, 0xda), + LV_COLOR_MAKE(0x31, 0x47, 0x55), + }; + + /*Create a linear gradient going from the top left corner to the bottom at an angle, with reflected color map*/ + static lv_style_t style_with_linear_gradient_bg; + static lv_grad_dsc_t linear_gradient_dsc; /*NOTE: the gradient descriptor must be static or global variable!*/ + + lv_style_init(&style_with_linear_gradient_bg); + lv_gradient_init_stops(&linear_gradient_dsc, grad_colors, NULL, NULL, sizeof(grad_colors) / sizeof(lv_color_t)); + lv_grad_linear_init(&linear_gradient_dsc, lv_pct(0), lv_pct(0), lv_pct(20), lv_pct(100), LV_GRAD_EXTEND_REFLECT); + lv_style_set_bg_grad(&style_with_linear_gradient_bg, &linear_gradient_dsc); + lv_style_set_bg_opa(&style_with_linear_gradient_bg, LV_OPA_COVER); + + /*Create a radial gradient with the center in the top left 1/3rd of the object, extending to the bottom right corner, with reflected color map*/ + static lv_style_t style_with_radial_gradient_bg; + static lv_grad_dsc_t radial_gradient_dsc; /*NOTE: the gradient descriptor must be static or global variable!*/ + + lv_style_init(&style_with_radial_gradient_bg); + lv_gradient_init_stops(&radial_gradient_dsc, grad_colors, NULL, NULL, sizeof(grad_colors) / sizeof(lv_color_t)); + lv_grad_radial_init(&radial_gradient_dsc, lv_pct(30), lv_pct(30), lv_pct(100), lv_pct(100), LV_GRAD_EXTEND_REFLECT); + lv_style_set_bg_grad(&style_with_radial_gradient_bg, &radial_gradient_dsc); + lv_style_set_bg_opa(&style_with_radial_gradient_bg, LV_OPA_COVER); + + /*Create buttons with different gradient styles*/ + + lv_obj_t * btn; + lv_obj_t * label; + + /*Simple horizontal gradient*/ + btn = lv_button_create(lv_screen_active()); + lv_obj_set_style_bg_color(btn, grad_colors[0], 0); + lv_obj_set_style_bg_grad_color(btn, grad_colors[1], 0); + lv_obj_set_style_bg_grad_dir(btn, LV_GRAD_DIR_HOR, 0); + lv_obj_set_size(btn, 150, 50); + lv_obj_align(btn, LV_ALIGN_CENTER, 0, -100); + + label = lv_label_create(btn); + lv_label_set_text(label, "Horizontal"); + lv_obj_center(label); + + /*Simple vertical gradient*/ + btn = lv_button_create(lv_screen_active()); + lv_obj_set_style_bg_color(btn, grad_colors[0], 0); + lv_obj_set_style_bg_grad_color(btn, grad_colors[1], 0); + lv_obj_set_style_bg_grad_dir(btn, LV_GRAD_DIR_VER, 0); + lv_obj_set_size(btn, 150, 50); + lv_obj_align(btn, LV_ALIGN_CENTER, 0, -40); + + label = lv_label_create(btn); + lv_label_set_text(label, "Vertical"); + lv_obj_center(label); + + /*Complex linear gradient*/ + btn = lv_button_create(lv_screen_active()); + lv_obj_add_style(btn, &style_with_linear_gradient_bg, 0); + lv_obj_set_size(btn, 150, 50); + lv_obj_align(btn, LV_ALIGN_CENTER, 0, 20); + + label = lv_label_create(btn); + lv_label_set_text(label, "Linear"); + lv_obj_center(label); + + /*Complex radial gradient*/ + btn = lv_button_create(lv_screen_active()); + lv_obj_add_style(btn, &style_with_radial_gradient_bg, 0); + lv_obj_set_size(btn, 150, 50); + lv_obj_align(btn, LV_ALIGN_CENTER, 0, 80); + + label = lv_label_create(btn); + lv_label_set_text(label, "Radial"); + lv_obj_center(label); +} + +#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/ + +#endif /*LV_BUILD_EXAMPLES*/ diff --git a/lv_conf_template.h b/lv_conf_template.h index d0e0d3799..7580efb0f 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -1,4 +1,4 @@ -/** +/** * @file lv_conf.h * Configuration file for v9.1.1-dev */ @@ -154,6 +154,9 @@ #if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_CUSTOM #define LV_DRAW_SW_ASM_CUSTOM_INCLUDE "" #endif + + /* Enable drawing complex gradients in software: linear at an angle, radial or conical */ + #define LV_USE_DRAW_SW_COMPLEX_GRADIENTS 0 #endif /* Use NXP's VG-Lite GPU on iMX RTxxx platforms. */ diff --git a/src/draw/sw/lv_draw_sw.h b/src/draw/sw/lv_draw_sw.h index 23f1c58dd..ff6204e7e 100644 --- a/src/draw/sw/lv_draw_sw.h +++ b/src/draw/sw/lv_draw_sw.h @@ -1,4 +1,4 @@ -/** +/** * @file lv_draw_sw.h * */ @@ -72,7 +72,7 @@ void lv_draw_sw_deinit(void); * @param dsc the draw descriptor * @param coords the coordinates of the rectangle */ -void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords); +void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, lv_draw_fill_dsc_t * dsc, const lv_area_t * coords); /** * Draw border with SW render. diff --git a/src/draw/sw/lv_draw_sw_fill.c b/src/draw/sw/lv_draw_sw_fill.c index 027f3048d..af111ed62 100644 --- a/src/draw/sw/lv_draw_sw_fill.c +++ b/src/draw/sw/lv_draw_sw_fill.c @@ -1,4 +1,4 @@ -/** +/** * @file lv_draw_sw_fill.c * */ @@ -42,7 +42,7 @@ * GLOBAL FUNCTIONS **********************/ -void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, const lv_area_t * coords) +void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, lv_draw_fill_dsc_t * dsc, const lv_area_t * coords) { if(dsc->opa <= LV_OPA_MIN) return; @@ -103,10 +103,10 @@ void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, /*Get gradient if appropriate*/ lv_grad_t * grad = lv_gradient_get(&dsc->grad, coords_bg_w, coords_bg_h); lv_opa_t * grad_opa_map = NULL; - if(grad && grad_dir == LV_GRAD_DIR_HOR) { + bool transp = false; + if(grad && grad_dir >= LV_GRAD_DIR_HOR) { blend_dsc.src_area = &blend_area; blend_dsc.src_buf = grad->color_map + clipped_coords.x1 - bg_coords.x1; - bool transp = false; uint32_t s; for(s = 0; s < dsc->grad.stops_count; s++) { if(dsc->grad.stops[s].opa != LV_OPA_COVER) { @@ -114,18 +114,45 @@ void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, break; } } - - if(transp) grad_opa_map = grad->opa_map + clipped_coords.x1 - bg_coords.x1; - + if(grad_dir == LV_GRAD_DIR_HOR) { + if(transp) grad_opa_map = grad->opa_map + clipped_coords.x1 - bg_coords.x1; + } blend_dsc.src_color_format = LV_COLOR_FORMAT_RGB888; } +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + + /*Prepare complex gradient*/ + if(grad_dir >= LV_GRAD_DIR_LINEAR) { + switch(grad_dir) { + case LV_GRAD_DIR_LINEAR: + lv_gradient_linear_setup(&dsc->grad, coords); + break; + case LV_GRAD_DIR_RADIAL: + lv_gradient_radial_setup(&dsc->grad, coords); + break; + case LV_GRAD_DIR_CONICAL: + lv_gradient_conical_setup(&dsc->grad, coords); + break; + default: + LV_LOG_WARN("Gradient type is not supported"); + return; + } + blend_dsc.src_area = &blend_area; + /* For complex gradients we reuse the color map buffer for the pixel data */ + blend_dsc.src_buf = grad->color_map; + grad_opa_map = grad->opa_map; + } +#endif + /* Draw the top of the rectangle line by line and mirror it to the bottom. */ for(h = 0; h < rout; h++) { int32_t top_y = bg_coords.y1 + h; int32_t bottom_y = bg_coords.y2 - h; if(top_y < clipped_coords.y1 && bottom_y > clipped_coords.y2) continue; /*This line is clipped now*/ + bool preblend = false; + /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER. * It saves calculating the final opa in lv_draw_sw_blend*/ lv_memset(mask_buf, opa, clipped_w); @@ -137,19 +164,37 @@ void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, blend_area.y1 = top_y; blend_area.y2 = top_y; - if(grad_dir == LV_GRAD_DIR_VER) { - blend_dsc.color = grad->color_map[top_y - bg_coords.y1]; - blend_dsc.opa = grad->opa_map[top_y - bg_coords.y1]; + switch(grad_dir) { + case LV_GRAD_DIR_VER: + blend_dsc.color = grad->color_map[top_y - bg_coords.y1]; + blend_dsc.opa = grad->opa_map[top_y - bg_coords.y1]; + break; + case LV_GRAD_DIR_HOR: + hor_grad_processed = true; + preblend = grad_opa_map != NULL; + break; +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + case LV_GRAD_DIR_LINEAR: + lv_gradient_linear_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, top_y - bg_coords.y1, coords_bg_w, grad); + preblend = true; + break; + case LV_GRAD_DIR_RADIAL: + lv_gradient_radial_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, top_y - bg_coords.y1, coords_bg_w, grad); + preblend = true; + break; + case LV_GRAD_DIR_CONICAL: + lv_gradient_conical_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, top_y - bg_coords.y1, coords_bg_w, grad); + preblend = true; + break; +#endif } - else if(grad_dir == LV_GRAD_DIR_HOR) { - hor_grad_processed = true; - if(grad_opa_map) { - int32_t i; - for(i = 0; i < clipped_w; i++) { - if(grad_opa_map[i] < LV_OPA_MAX) mask_buf[i] = (mask_buf[i] * grad_opa_map[i]) >> 8; - } - blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; + /* pre-blend the mask */ + if(preblend) { + int32_t i; + for(i = 0; i < clipped_w; i++) { + if(grad_opa_map[i] < LV_OPA_MAX) mask_buf[i] = (mask_buf[i] * grad_opa_map[i]) >> 8; } + blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; } lv_draw_sw_blend(draw_unit, &blend_dsc); } @@ -158,18 +203,42 @@ void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, blend_area.y1 = bottom_y; blend_area.y2 = bottom_y; - if(grad_dir == LV_GRAD_DIR_VER) { - blend_dsc.color = grad->color_map[bottom_y - bg_coords.y1]; - blend_dsc.opa = grad->opa_map[bottom_y - bg_coords.y1]; + switch(grad_dir) { + case LV_GRAD_DIR_VER: + blend_dsc.color = grad->color_map[bottom_y - bg_coords.y1]; + blend_dsc.opa = grad->opa_map[bottom_y - bg_coords.y1]; + break; + case LV_GRAD_DIR_HOR: + preblend = !hor_grad_processed && (grad_opa_map != NULL); + break; +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + case LV_GRAD_DIR_LINEAR: + lv_gradient_linear_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, bottom_y - bg_coords.y1, coords_bg_w, grad); + preblend = true; + break; + case LV_GRAD_DIR_RADIAL: + lv_gradient_radial_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, bottom_y - bg_coords.y1, coords_bg_w, grad); + preblend = true; + break; + case LV_GRAD_DIR_CONICAL: + lv_gradient_conical_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, bottom_y - bg_coords.y1, coords_bg_w, grad); + preblend = true; + break; +#endif } - else if(hor_grad_processed == false && grad_dir == LV_GRAD_DIR_HOR) { - if(grad_opa_map) { - int32_t i; - for(i = 0; i < clipped_w; i++) { - if(grad_opa_map[i] < LV_OPA_MAX) mask_buf[i] = (mask_buf[i] * grad_opa_map[i]) >> 8; - } - blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; + /* pre-blend the mask */ + if(preblend) { + int32_t i; + if(grad_dir >= LV_GRAD_DIR_LINEAR) { + /*Need to generate the mask again, because we have mixed in the upper part of the gradient*/ + lv_memset(mask_buf, opa, clipped_w); + blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, top_y, clipped_w); + if(blend_dsc.mask_res == LV_DRAW_SW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; } + for(i = 0; i < clipped_w; i++) { + if(grad_opa_map[i] < LV_OPA_MAX) mask_buf[i] = (mask_buf[i] * grad_opa_map[i]) >> 8; + } + blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; } lv_draw_sw_blend(draw_unit, &blend_dsc); } @@ -188,23 +257,44 @@ void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, /*With gradient draw line by line*/ else { blend_dsc.opa = opa; - if(grad_dir == LV_GRAD_DIR_VER) { - blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_FULL_COVER; - } - else if(grad_dir == LV_GRAD_DIR_HOR) { - blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; - blend_dsc.mask_buf = grad_opa_map; + switch(grad_dir) { + case LV_GRAD_DIR_VER: + blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_FULL_COVER; + break; + case LV_GRAD_DIR_HOR: + blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED; + blend_dsc.mask_buf = grad_opa_map; + break; + case LV_GRAD_DIR_LINEAR: + case LV_GRAD_DIR_RADIAL: + case LV_GRAD_DIR_CONICAL: + blend_dsc.mask_res = transp ? LV_DRAW_SW_MASK_RES_CHANGED : LV_DRAW_SW_MASK_RES_FULL_COVER; + blend_dsc.mask_buf = grad_opa_map; } - int32_t h_end = bg_coords.y2 - rout; - for(h = bg_coords.y1 + rout; h <= h_end; h++) { + int32_t h_start = LV_MAX(bg_coords.y1 + rout, clipped_coords.y1); + int32_t h_end = LV_MIN(bg_coords.y2 - rout, clipped_coords.y2); + for(h = h_start; h <= h_end; h++) { blend_area.y1 = h; blend_area.y2 = h; - if(grad_dir == LV_GRAD_DIR_VER) { - blend_dsc.color = grad->color_map[h - bg_coords.y1]; - if(opa >= LV_OPA_MAX) blend_dsc.opa = grad->opa_map[h - bg_coords.y1]; - else blend_dsc.opa = LV_OPA_MIX2(grad->opa_map[h - bg_coords.y1], opa); + switch(grad_dir) { + case LV_GRAD_DIR_VER: + blend_dsc.color = grad->color_map[h - bg_coords.y1]; + if(opa >= LV_OPA_MAX) blend_dsc.opa = grad->opa_map[h - bg_coords.y1]; + else blend_dsc.opa = LV_OPA_MIX2(grad->opa_map[h - bg_coords.y1], opa); + break; +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + case LV_GRAD_DIR_LINEAR: + lv_gradient_linear_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, h - bg_coords.y1, coords_bg_w, grad); + break; + case LV_GRAD_DIR_RADIAL: + lv_gradient_radial_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, h - bg_coords.y1, coords_bg_w, grad); + break; + case LV_GRAD_DIR_CONICAL: + lv_gradient_conical_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, h - bg_coords.y1, coords_bg_w, grad); + break; +#endif } lv_draw_sw_blend(draw_unit, &blend_dsc); } @@ -217,6 +307,21 @@ void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, const lv_draw_fill_dsc_t * dsc, if(grad) { lv_gradient_cleanup(grad); } +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + if(grad_dir >= LV_GRAD_DIR_LINEAR) { + switch(grad_dir) { + case LV_GRAD_DIR_LINEAR: + lv_gradient_linear_cleanup(&dsc->grad); + break; + case LV_GRAD_DIR_RADIAL: + lv_gradient_radial_cleanup(&dsc->grad); + break; + case LV_GRAD_DIR_CONICAL: + lv_gradient_conical_cleanup(&dsc->grad); + break; + } + } +#endif #endif } diff --git a/src/draw/sw/lv_draw_sw_gradient.c b/src/draw/sw/lv_draw_sw_gradient.c index 2e855644c..5fb94f7c8 100644 --- a/src/draw/sw/lv_draw_sw_gradient.c +++ b/src/draw/sw/lv_draw_sw_gradient.c @@ -1,4 +1,4 @@ -/** +/** * @file lv_draw_sw_gradient.c * */ @@ -11,6 +11,7 @@ #include "../../misc/lv_types.h" #include "../../osal/lv_os.h" +#include "../../misc/lv_math.h" /********************* * DEFINES @@ -25,12 +26,61 @@ #define ALIGN(X) (((X) + 3) & ~3) #endif +/********************** + * TYPEDEFS + **********************/ + +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + +typedef struct { + /* w = (-b(xp, yp) + sqrt(sqr(b(xp, yp)) - 4 * a * c(xp, yp))) / (2 * a) */ + int32_t x0; /* center of the start circle */ + int32_t y0; /* center of the start circle */ + int32_t r0; /* radius of the start circle */ + int32_t inv_dr; /* 1 / (r1 - r0) */ + int32_t a4; /* 4 * a */ + int32_t inv_a4; /* 1 / (4 * a) */ + int32_t dx; + /* b(xp, yp) = xp * bpx + yp * bpy + bc */ + int32_t bpx; + int32_t bpy; + int32_t bc; + lv_area_t clip_area; + lv_grad_t * cgrad; /*256 element cache buffer containing the gradient color map*/ +} lv_grad_radial_state_t; + +typedef struct { + /* w = a * xp + b * yp + c */ + int32_t a; + int32_t b; + int32_t c; + lv_grad_t * cgrad; /*256 element cache buffer containing the gradient color map*/ +} lv_grad_linear_state_t; + +typedef struct { + /* w = a * xp + b * yp + c */ + int32_t x0; + int32_t y0; + int32_t a; + int32_t da; + int32_t inv_da; + lv_grad_t * cgrad; /*256 element cache buffer containing the gradient color map*/ +} lv_grad_conical_state_t; + +#endif + /********************** * STATIC PROTOTYPES **********************/ typedef lv_result_t (*op_cache_t)(lv_grad_t * c, void * ctx); static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, int32_t w, int32_t h); +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + + static inline int32_t extend_w(int32_t w, lv_grad_extend_t extend); + +#endif + /********************** * STATIC VARIABLE **********************/ @@ -41,7 +91,20 @@ static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, int32_t w, int32_t h); static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, int32_t w, int32_t h) { - int32_t size = g->dir == LV_GRAD_DIR_HOR ? w : h; + int32_t size; + switch(g->dir) { + case LV_GRAD_DIR_HOR: + case LV_GRAD_DIR_LINEAR: + case LV_GRAD_DIR_RADIAL: + case LV_GRAD_DIR_CONICAL: + size = w; + break; + case LV_GRAD_DIR_VER: + size = h; + break; + default: + size = 64; + } size_t req_size = ALIGN(sizeof(lv_grad_t)) + ALIGN(size * sizeof(lv_color_t)) + ALIGN(size * sizeof(lv_opa_t)); lv_grad_t * item = lv_malloc(req_size); @@ -55,6 +118,25 @@ static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, int32_t w, int32_t h) return item; } +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + +static inline int32_t extend_w(int32_t w, lv_grad_extend_t extend) +{ + if(extend == LV_GRAD_EXTEND_PAD) { /**< Repeat the same color*/ + return w < 0 ? 0 : LV_MIN(w, 255); + } + if(extend == LV_GRAD_EXTEND_REPEAT) { /**< Repeat the pattern*/ + return w & 255; + } + /*LV_GRAD_EXTEND_REFLECT*/ + w &= 511; + if(w > 255) + w ^= 511; /* 511 - w */ + return w; +} + +#endif + /********************** * FUNCTIONS **********************/ @@ -137,4 +219,450 @@ void lv_gradient_cleanup(lv_grad_t * grad) lv_free(grad); } +void lv_gradient_init_stops(lv_grad_dsc_t * grad, const lv_color_t colors[], const lv_opa_t opa[], + const uint8_t fracs[], int num_stops) +{ + LV_ASSERT(num_stops <= LV_GRADIENT_MAX_STOPS); + grad->stops_count = num_stops; + for(int i = 0; i < num_stops; i++) { + grad->stops[i].color = colors[i]; + grad->stops[i].opa = opa != NULL ? opa[i] : LV_OPA_COVER; + grad->stops[i].frac = fracs != NULL ? fracs[i] : 255 * i / (num_stops - 1); + } +} + +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + +/* + Calculate radial gradient based on the following equation: + + | P - (C1 - C0)w - C0 | = (r1 - r0)w + r0, where + + P: {xp, yp} is the point of interest + C0: {x0, y0} is the center of the start circle + C1: {x1, y1} is the center of the end circle + r0 is the radius of the start circle + r1 is the radius of the end circle + w is the unknown variable + || is the length of the vector + + The above equation can be rewritten as: + + ((r1-r0)^2 - (x1-x0)^2 - (y1-y0)^2) * w^2 + 2*((xp-x0)*(x1-x0) + (yp-y0)*(y1-y0)) * w + (-(xp-x0)^2 - (yp-y0)^) = 0 + + The roots of the quadratical equation can be obtained using the well-known formula (-b +- sqrt(b^2 - 4ac)) / 2a + We only need the more positive root. + + Let's denote + dx = x1 - x0 + dy = y1 - y0 + dr = r1 - r0 + + Thus: + + w = (-b(xp, yp) + sqrt(sqr(b(xp, yp)) - 4 * a * c(xp, yp))) / (2 * a), where + + b(xp, yp) = 2dx * xp + 2dy * yp + 2(r0 * dr - x0 * dx - y0 * dy) + c(xp, yp) = r0^2 - (xp - x0)^2 - (yp - y0)^2 + + Rewrite b(xp, yp) as: + + b(xp, yp) = xp * bpx + yp * bpy + bc, where + + bpx = 2dx + bpy = 2dy + bc = 2(r0 * dr - x0 * dx - y0 * dy) + + We can pre-calculate the constants, because they do not depend on the pixel coordinates. + +*/ + +void lv_gradient_radial_setup(lv_grad_dsc_t * dsc, const lv_area_t * coords) +{ + lv_point_t start = dsc->params.radial.focal; + lv_point_t end = dsc->params.radial.end; + lv_point_t start_extent = dsc->params.radial.focal_extent; + lv_point_t end_extent = dsc->params.radial.end_extent; + lv_grad_radial_state_t * state = lv_malloc(sizeof(lv_grad_radial_state_t)); + dsc->state = state; + + /* Convert from percentage coordinates */ + int32_t wdt = lv_area_get_width(coords); + int32_t hgt = lv_area_get_height(coords); + + start.x = lv_pct_to_px(start.x, wdt); + end.x = lv_pct_to_px(end.x, wdt); + start_extent.x = lv_pct_to_px(start_extent.x, wdt); + end_extent.x = lv_pct_to_px(end_extent.x, wdt); + start.y = lv_pct_to_px(start.y, hgt); + end.y = lv_pct_to_px(end.y, hgt); + start_extent.y = lv_pct_to_px(start_extent.y, hgt); + end_extent.y = lv_pct_to_px(end_extent.y, hgt); + + /* Calculate radii */ + int16_t r_start = lv_sqrt32(lv_sqr(start_extent.x - start.x) + lv_sqr(start_extent.y - start.y)); + int16_t r_end = lv_sqrt32(lv_sqr(end_extent.x - end.x) + lv_sqr(end_extent.y - end.y)); + LV_ASSERT(r_end != 0); + + /* Create gradient color map */ + state->cgrad = lv_gradient_get(dsc, 256, 0); + + state->x0 = start.x; + state->y0 = start.y; + state->r0 = r_start; + int32_t dr = r_end - r_start; + if(end.x == start.x && end.y == start.y) { + LV_ASSERT(dr != 0); + state->a4 = lv_sqr(dr) << 2; + state->bpx = 0; + state->bpy = 0; + state->bc = (state->r0 * dr) << 1; + state->dx = 0; + state->inv_dr = (1 << (8 + 16)) / dr; + } + else { + int32_t dx = end.x - start.x; + int32_t dy = end.y - start.y; + state->dx = dx; /* needed for incremental calculation */ + state->a4 = (lv_sqr(dr) - lv_sqr(dx) - lv_sqr(dy)) << 2; + /* b(xp, yp) = xp * bpx + yp * bpy + bc */ + state->bpx = dx << 1; + state->bpy = dy << 1; + state->bc = (state->r0 * dr - state->x0 * dx - state->y0 * dy) << 1; + } + state->inv_a4 = state->a4 != 0 ? (1 << (13 + 16)) / state->a4 : 0; + /* check for possible clipping */ + if(dsc->extend == LV_GRAD_EXTEND_PAD && + /* if extend mode is 'pad', then we can clip to the end circle's bounding box, if the start circle is entirely within the end circle */ + (lv_sqr(start.x - end.x) + lv_sqr(start.y - end.y) < lv_sqr(r_end - r_start))) { + if(r_end > r_start) { + lv_area_set(&state->clip_area, end.x - r_end, end.y - r_end, end.x + r_end, end.y + r_end); + } + else { + lv_area_set(&state->clip_area, start.x - r_start, start.y - r_start, start.x + r_start, start.y + r_start); + } + } + else { + state->clip_area.x1 = -0x7fffffff; + } +} + +void lv_gradient_radial_cleanup(lv_grad_dsc_t * dsc) +{ + lv_grad_radial_state_t * state = dsc->state; + if(state == NULL) + return; + if(state->cgrad) + lv_gradient_cleanup(state->cgrad); + lv_free(state); +} + +void LV_ATTRIBUTE_FAST_MEM lv_gradient_radial_get_line(lv_grad_dsc_t * dsc, int32_t xp, int32_t yp, + int32_t width, lv_grad_t * result) +{ + lv_grad_radial_state_t * state = (lv_grad_radial_state_t *)dsc->state; + lv_color_t * buf = result->color_map; + lv_opa_t * opa = result->opa_map; + lv_grad_t * grad = state->cgrad; + + int32_t w; /* the result: this is an offset into the 256 element gradient color table */ + int32_t b, db, c, dc; + + /* check for possible clipping */ + if(state->clip_area.x1 != -0x7fffffff) { + /* fill line with end color for pixels outside the clipped region */ + lv_color_t * _buf = buf; + lv_opa_t * _opa = opa; + lv_color_t _c = grad->color_map[255]; + lv_opa_t _o = grad->opa_map[255]; + int32_t _w = width; + for(; _w > 0; _w--) { + *_buf++ = _c; + *_opa++ = _o; + } + /* is this line fully outside the clip area? */ + if(yp < state->clip_area.y1 || + yp >= state->clip_area.y2 || + xp >= state->clip_area.x2 || + xp + width < state->clip_area.x1) { + return; + } + else { /* not fully outside: clip line to the bounding box */ + int32_t _x1 = LV_MAX(xp, state->clip_area.x1); + int32_t _x2 = LV_MIN(xp + width, state->clip_area.x2); + buf += _x1 - xp; + opa += _x1 - xp; + xp = _x1; + width = _x2 - _x1; + } + } + + b = xp * state->bpx + yp * state->bpy + state->bc; + c = lv_sqr(state->r0) - lv_sqr(xp - state->x0) - lv_sqr(yp - state->y0); + /* We can save some calculations by using the previous values of b and c */ + db = state->dx << 1; + dc = ((xp - state->x0) << 1) + 1; + + if(state->a4 == 0) { /* not a quadratic equation: solve linear equation: w = -c/b */ + for(; width > 0; width--) { + w = extend_w(b == 0 ? 0 : -(c << 8) / b, dsc->extend); + *buf++ = grad->color_map[w]; + *opa++ = grad->opa_map[w]; + b += db; + c -= dc; + dc += 2; + } + } + else { /* solve quadratical equation */ + if(state->bpx || + state->bpy) { /* general case (circles are not concentric): w = (-b + sqrt(b^2 - 4ac))/2a (we only need the more positive root)*/ + int32_t a4 = state->a4 >> 4; + for(; width > 0; width--) { + int32_t det = lv_sqr(b >> 4) - (a4 * (c >> 4)); /* b^2 shifted down by 2*4=8, 4ac shifted down by 8 */ + /* check determinant: if negative, then there is no solution: use starting color */ + w = det < 0 ? 0 : extend_w(((lv_sqrt32(det) - (b >> 4)) * state->inv_a4) >> 16, + dsc->extend); /* square root shifted down by 4 (includes *256 to set output range) */ + *buf++ = grad->color_map[w]; + *opa++ = grad->opa_map[w]; + b += db; + c -= dc; + dc += 2; + } + } + else { /* special case: concentric circles: w = (sqrt((xp-x0)^2 + (yx-y0)^2)-r0)/(r1-r0) */ + c = lv_sqr(xp - state->x0) + lv_sqr(yp - state->y0); + for(; width > 0; width--) { + w = extend_w((((lv_sqrt32(c) - state->r0)) * state->inv_dr) >> 16, dsc->extend); + *buf++ = grad->color_map[w]; + *opa++ = grad->opa_map[w]; + c += dc; + dc += 2; + } + } + } +} + +/* + Calculate linear gradient based on the following equation: + + w = ((P - C0) x (C1 - C0)) / | C1 - C0 |^2, where + + P: {xp, yp} is the point of interest + C0: {x0, y0} is the start point of the gradient vector + C1: {x1, y1} is the end point of the gradient vector + w is the unknown variable + + || is the length of the vector + x is a dot product + + The above equation can be rewritten as: + + w = xp * (dx / (dx^2 + dy^2)) + yp * (dy / (dx^2 + dy^2)) - (x0 * dx + y0 * dy) / (dx^2 + dy^2), where + + dx = x1 - x0 + dy = y1 - y0 + + We can pre-calculate the constants, because they do not depend on the pixel coordinates. + +*/ + +void lv_gradient_linear_setup(lv_grad_dsc_t * dsc, const lv_area_t * coords) +{ + lv_point_t start = dsc->params.linear.start; + lv_point_t end = dsc->params.linear.end; + lv_grad_linear_state_t * state = lv_malloc(sizeof(lv_grad_linear_state_t)); + dsc->state = state; + + /* Create gradient color map */ + state->cgrad = lv_gradient_get(dsc, 256, 0); + + /* Convert from percentage coordinates */ + int32_t wdt = lv_area_get_width(coords); + int32_t hgt = lv_area_get_height(coords); + + start.x = lv_pct_to_px(start.x, wdt); + end.x = lv_pct_to_px(end.x, wdt); + start.y = lv_pct_to_px(start.y, hgt); + end.y = lv_pct_to_px(end.y, hgt); + + /* Precalculate constants */ + int32_t dx = end.x - start.x; + int32_t dy = end.y - start.y; + + int32_t l2 = lv_sqr(dx) + lv_sqr(dy); + state->a = (dx << 16) / l2; + state->b = (dy << 16) / l2; + state->c = ((start.x * dx + start.y * dy) << 16) / l2; +} + +void lv_gradient_linear_cleanup(lv_grad_dsc_t * dsc) +{ + lv_grad_linear_state_t * state = dsc->state; + if(state == NULL) + return; + if(state->cgrad) + lv_free(state->cgrad); + lv_free(state); +} + +void LV_ATTRIBUTE_FAST_MEM lv_gradient_linear_get_line(lv_grad_dsc_t * dsc, int32_t xp, int32_t yp, + int32_t width, lv_grad_t * result) +{ + lv_grad_linear_state_t * state = (lv_grad_linear_state_t *)dsc->state; + lv_color_t * buf = result->color_map; + lv_opa_t * opa = result->opa_map; + lv_grad_t * grad = state->cgrad; + + int32_t w; /* the result: this is an offset into the 256 element gradient color table */ + int32_t x, d; + + x = xp * state->a + yp * state->b - state->c; + d = state->a; + + for(; width > 0; width--) { + w = extend_w(x >> 8, dsc->extend); + *buf++ = grad->color_map[w]; + *opa++ = grad->opa_map[w]; + x += d; + } +} + +/* + Calculate conical gradient based on the following equation: + + w = (atan((yp - y0)/(xp - x0)) - alpha) / (beta - alpha), where + + P: {xp, yp} is the point of interest + C0: {x0, y0} is the center of the gradient + alpha is the start angle + beta is the end angle + w is the unknown variable +*/ + +void lv_gradient_conical_setup(lv_grad_dsc_t * dsc, const lv_area_t * coords) +{ + lv_point_t c0 = dsc->params.conical.center; + int32_t alpha = dsc->params.conical.start_angle % 360; + int32_t beta = dsc->params.conical.end_angle % 360; + lv_grad_conical_state_t * state = lv_malloc(sizeof(lv_grad_conical_state_t)); + dsc->state = state; + + /* Create gradient color map */ + state->cgrad = lv_gradient_get(dsc, 256, 0); + + /* Convert from percentage coordinates */ + int32_t wdt = lv_area_get_width(coords); + int32_t hgt = lv_area_get_height(coords); + + c0.x = lv_pct_to_px(c0.x, wdt); + c0.y = lv_pct_to_px(c0.y, hgt); + + /* Precalculate constants */ + if(beta <= alpha) + beta += 360; + state->x0 = c0.x; + state->y0 = c0.y; + state->a = alpha; + state->da = beta - alpha; + state->inv_da = (1 << 16) / (beta - alpha); +} + +void lv_gradient_conical_cleanup(lv_grad_dsc_t * dsc) +{ + lv_grad_conical_state_t * state = dsc->state; + if(state == NULL) + return; + if(state->cgrad) + lv_free(state->cgrad); + lv_free(state); +} + +void LV_ATTRIBUTE_FAST_MEM lv_gradient_conical_get_line(lv_grad_dsc_t * dsc, int32_t xp, int32_t yp, + int32_t width, lv_grad_t * result) +{ + lv_grad_conical_state_t * state = (lv_grad_conical_state_t *)dsc->state; + lv_color_t * buf = result->color_map; + lv_opa_t * opa = result->opa_map; + lv_grad_t * grad = state->cgrad; + + int32_t w; /* the result: this is an offset into the 256 element gradient color table */ + int32_t dx = xp - state->x0; + int32_t dy = yp - state->y0; + + if(dy == 0) { /* we will eventually go through the center of the conical: need an extra test in the loop to avoid both dx and dy being zero in atan2 */ + for(; width > 0; width--) { + if(dx == 0) { + w = 0; + } + else { + int32_t d = lv_atan2(dy, dx) - state->a; + if(d < 0) + d += 360; + w = extend_w((d * state->inv_da) >> 8, dsc->extend); + } + *buf++ = grad->color_map[w]; + *opa++ = grad->opa_map[w]; + dx++; + } + } + else { + for(; width > 0; width--) { + int32_t d = lv_atan2(dy, dx) - state->a; + if(d < 0) + d += 360; + w = extend_w((d * state->inv_da) >> 8, dsc->extend); + *buf++ = grad->color_map[w]; + *opa++ = grad->opa_map[w]; + dx++; + } + } +} + +void lv_grad_linear_init(lv_grad_dsc_t * dsc, int32_t from_x, int32_t from_y, int32_t to_x, int32_t to_y, + lv_grad_extend_t extend) +{ + dsc->dir = LV_GRAD_DIR_LINEAR; + dsc->params.linear.start.x = from_x; + dsc->params.linear.start.y = from_y; + dsc->params.linear.end.x = to_x; + dsc->params.linear.end.y = to_y; + dsc->extend = extend; +} + +void lv_grad_radial_init(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t to_x, int32_t to_y, + lv_grad_extend_t extend) +{ + dsc->dir = LV_GRAD_DIR_RADIAL; + dsc->params.radial.focal.x = center_x; + dsc->params.radial.focal.y = center_y; + dsc->params.radial.focal_extent.x = center_x; + dsc->params.radial.focal_extent.y = center_y; + dsc->params.radial.end.x = center_x; + dsc->params.radial.end.y = center_y; + dsc->params.radial.end_extent.x = to_x; + dsc->params.radial.end_extent.y = to_y; + dsc->extend = extend; +} + +void lv_grad_conical_init(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t start_angle, + int32_t end_angle, lv_grad_extend_t extend) +{ + dsc->dir = LV_GRAD_DIR_CONICAL; + dsc->params.conical.center.x = center_x; + dsc->params.conical.center.y = center_y; + dsc->params.conical.start_angle = start_angle; + dsc->params.conical.end_angle = end_angle; + dsc->extend = extend; +} + +void lv_grad_radial_set_focal(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t radius) +{ + dsc->params.radial.focal.x = center_x; + dsc->params.radial.focal.y = center_y; + dsc->params.radial.focal_extent.x = center_x + radius; + dsc->params.radial.focal_extent.y = center_y; +} + +#endif /* LV_USE_DRAW_SW_COMPLEX_GRADIENTS */ + #endif /*LV_USE_DRAW_SW*/ diff --git a/src/draw/sw/lv_draw_sw_gradient.h b/src/draw/sw/lv_draw_sw_gradient.h index 09bb3771c..71a803b9d 100644 --- a/src/draw/sw/lv_draw_sw_gradient.h +++ b/src/draw/sw/lv_draw_sw_gradient.h @@ -1,4 +1,4 @@ -/** +/** * @file lv_draw_sw_gradient.h * */ @@ -25,6 +25,12 @@ extern "C" { #error LVGL needs at least 2 stops for gradients. Please increase the LV_GRADIENT_MAX_STOPS #endif +#define LV_GRAD_LEFT LV_PCT(0) +#define LV_GRAD_RIGHT LV_PCT(100) +#define LV_GRAD_TOP LV_PCT(0) +#define LV_GRAD_BOTTOM LV_PCT(100) +#define LV_GRAD_CENTER LV_PCT(50) + /********************** * TYPEDEFS **********************/ @@ -58,6 +64,140 @@ lv_grad_t * lv_gradient_get(const lv_grad_dsc_t * gradient, int32_t w, int32_t h */ void lv_gradient_cleanup(lv_grad_t * grad); +/** + * Initialize gradient color map from a table + * @param grad pointer to a gradient descriptor + * @param colors color array + * @param fracs position array (0..255): if NULL, then colors are distributed evenly + * @param opa opacity array: if NULL, then LV_OPA_COVER is assumed + * @param num_stops number of gradient stops (1..LV_GRADIENT_MAX_STOPS) + */ +void lv_gradient_init_stops(lv_grad_dsc_t * grad, const lv_color_t colors[], const lv_opa_t opa[], + const uint8_t fracs[], int num_stops); + +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + +/** + * Helper function to initialize linear gradient + * @param dsc gradient descriptor + * @param from_x start x position: can be a coordinate or an lv_pct() value + * predefined constants LV_GRAD_LEFT, LV_GRAD_RIGHT, LV_GRAD_TOP, LV_GRAD_BOTTOM, LV_GRAD_CENTER can be used as well + * @param from_y start y position + * @param to_x end x position + * @param to_y end y position + * @param extend one of LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT or LV_GRAD_EXTEND_REFLECT + */ +void lv_grad_linear_init(lv_grad_dsc_t * dsc, int32_t from_x, int32_t from_y, int32_t to_x, int32_t to_y, + lv_grad_extend_t extend); + +/** + * Helper function to initialize radial gradient + * @param dsc gradient descriptor + * @param center_x center x position: can be a coordinate or an lv_pct() value + * predefined constants LV_GRAD_LEFT, LV_GRAD_RIGHT, LV_GRAD_TOP, LV_GRAD_BOTTOM, LV_GRAD_CENTER can be used as well + * @param center_y center y position + * @param to_x point on the end circle x position + * @param to_y point on the end circle y position + * @param extend one of LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT or LV_GRAD_EXTEND_REFLECT + */ +void lv_grad_radial_init(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t to_x, int32_t to_y, + lv_grad_extend_t extend); + +/** + * Set focal (starting) circle of a radial gradient + * @param dsc gradient descriptor + * @param center_x center x position: can be a coordinate or an lv_pct() value + * predefined constants LV_GRAD_LEFT, LV_GRAD_RIGHT, LV_GRAD_TOP, LV_GRAD_BOTTOM, LV_GRAD_CENTER can be used as well + * @param center_y center y position + * @param radius radius of the starting circle (NOTE: this must be a scalar number, not percentage) + */ +void lv_grad_radial_set_focal(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t radius); + +/** + * Helper function to initialize conical gradient + * @param dsc gradient descriptor + * @param center_x center x position: can be a coordinate or an lv_pct() value + * predefined constants LV_GRAD_LEFT, LV_GRAD_RIGHT, LV_GRAD_TOP, LV_GRAD_BOTTOM, LV_GRAD_CENTER can be used as well + * @param center_y center y position + * @param start_angle start angle in degrees + * @param end_angle end angle in degrees + * @param extend one of LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT or LV_GRAD_EXTEND_REFLECT + */ +void lv_grad_conical_init(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t start_angle, + int32_t end_angle, lv_grad_extend_t extend); + +/** + * Calculate constants from the given parameters that are used during rendering + * @param dsc gradient descriptor + */ +void lv_gradient_linear_setup(lv_grad_dsc_t * dsc, const lv_area_t * coords); + +/** + * Free up the allocated memory for the gradient calculation + * @param dsc gradient descriptor + */ +void lv_gradient_linear_cleanup(lv_grad_dsc_t * dsc); + +/** + * Calculate a line segment of a linear gradient + * @param dsc gradient descriptor + * @param xp starting point x coordinate in gradient space + * @param yp starting point y coordinate in gradient space + * @param width width of the line segment in pixels + * @param result color buffer for the resulting line segment + */ +void /* LV_ATTRIBUTE_FAST_MEM */ lv_gradient_linear_get_line(lv_grad_dsc_t * dsc, int32_t xp, int32_t yp, int32_t width, + lv_grad_t * result); + +/** + * Calculate constants from the given parameters that are used during rendering + * @param dsc gradient descriptor + */ +void lv_gradient_radial_setup(lv_grad_dsc_t * dsc, const lv_area_t * coords); + +/** + * Free up the allocated memory for the gradient calculation + * @param dsc gradient descriptor + */ +void lv_gradient_radial_cleanup(lv_grad_dsc_t * dsc); + +/** + * Calculate a line segment of a radial gradient + * @param dsc gradient descriptor + * @param xp starting point x coordinate in gradient space + * @param yp starting point y coordinate in gradient space + * @param width width of the line segment in pixels + * @param result color buffer for the resulting line segment + */ +void /* LV_ATTRIBUTE_FAST_MEM */ lv_gradient_radial_get_line(lv_grad_dsc_t * dsc, int32_t xp, int32_t yp, int32_t width, + lv_grad_t * result); + +/** + * Calculate constants from the given parameters that are used during rendering + * @param dsc gradient descriptor + */ +void lv_gradient_conical_setup(lv_grad_dsc_t * dsc, const lv_area_t * coords); + +/** + * Free up the allocated memory for the gradient calculation + * @param dsc gradient descriptor + */ +void lv_gradient_conical_cleanup(lv_grad_dsc_t * dsc); + +/** + * Calculate a line segment of a conical gradient + * @param dsc gradient descriptor + * @param xp starting point x coordinate in gradient space + * @param yp starting point y coordinate in gradient space + * @param width width of the line segment in pixels + * @param result color buffer for the resulting line segment + */ +void /* LV_ATTRIBUTE_FAST_MEM */ lv_gradient_conical_get_line(lv_grad_dsc_t * dsc, int32_t xp, int32_t yp, + int32_t width, + lv_grad_t * result); + +#endif /*LV_USE_DRAW_SW_COMPLEX_GRADIENTS*/ + #endif /*LV_USE_DRAW_SW*/ #ifdef __cplusplus diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index c0e52ff91..ca603e9de 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -419,6 +419,15 @@ #endif #endif #endif + + /* Enable drawing complex gradients in software: linear at an angle, radial or conical */ + #ifndef LV_USE_DRAW_SW_COMPLEX_GRADIENTS + #ifdef CONFIG_LV_USE_DRAW_SW_COMPLEX_GRADIENTS + #define LV_USE_DRAW_SW_COMPLEX_GRADIENTS CONFIG_LV_USE_DRAW_SW_COMPLEX_GRADIENTS + #else + #define LV_USE_DRAW_SW_COMPLEX_GRADIENTS 0 + #endif + #endif #endif /* Use NXP's VG-Lite GPU on iMX RTxxx platforms. */ diff --git a/src/misc/lv_math.c b/src/misc/lv_math.c index 59e946062..d326ea29d 100644 --- a/src/misc/lv_math.c +++ b/src/misc/lv_math.c @@ -1,4 +1,4 @@ -/** +/** * @file lv_math.c * */ @@ -222,6 +222,95 @@ void LV_ATTRIBUTE_FAST_MEM lv_sqrt(uint32_t x, lv_sqrt_res_t * q, uint32_t mask) q->f = (root & 0xf) << 4; } +/* +// Alternative Integer Square Root function +// Contributors include Arne Steinarson for the basic approximation idea, +// Dann Corbit and Mathew Hendry for the first cut at the algorithm, +// Lawrence Kirby for the rearrangement, improvments and range optimization +// and Paul Hsieh for the round-then-adjust idea. +*/ +int32_t LV_ATTRIBUTE_FAST_MEM lv_sqrt32(uint32_t x) +{ + static const unsigned char sqq_table[] = { + 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, + 59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, + 84, 86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, + 103, 104, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, + 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157, + 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, + 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, + 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, + 189, 189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197, + 198, 199, 199, 200, 201, 201, 202, 203, 203, 204, 204, 205, 206, 206, + 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213, 214, 214, 215, + 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223, + 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, 230, 231, + 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, + 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, + 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, + 253, 254, 254, 255 + }; + + int32_t xn; + + if(x >= 0x10000) + if(x >= 0x1000000) + if(x >= 0x10000000) + if(x >= 0x40000000) { + if(x >= 65535UL * 65535UL) + return 65535; + xn = sqq_table[x >> 24] << 8; + } + else + xn = sqq_table[x >> 22] << 7; + else if(x >= 0x4000000) + xn = sqq_table[x >> 20] << 6; + else + xn = sqq_table[x >> 18] << 5; + else { + if(x >= 0x100000) + if(x >= 0x400000) + xn = sqq_table[x >> 16] << 4; + else + xn = sqq_table[x >> 14] << 3; + else if(x >= 0x40000) + xn = sqq_table[x >> 12] << 2; + else + xn = sqq_table[x >> 10] << 1; + + goto nr1; + } + else if(x >= 0x100) { + if(x >= 0x1000) + if(x >= 0x4000) + xn = (sqq_table[x >> 8] >> 0) + 1; + else + xn = (sqq_table[x >> 6] >> 1) + 1; + else if(x >= 0x400) + xn = (sqq_table[x >> 4] >> 2) + 1; + else + xn = (sqq_table[x >> 2] >> 3) + 1; + + goto adj; + } + else + return sqq_table[x] >> 4; + + /* Run two iterations of the standard convergence formula */ + + xn = (xn + 1 + x / xn) / 2; +nr1: + xn = (xn + 1 + x / xn) / 2; +adj: + + if(xn * xn > x) /* Correct rounding if necessary */ + xn--; + + return xn; +} + uint16_t lv_atan2(int x, int y) { /** diff --git a/src/misc/lv_math.h b/src/misc/lv_math.h index c81717a63..d96ed2eb7 100644 --- a/src/misc/lv_math.h +++ b/src/misc/lv_math.h @@ -1,4 +1,4 @@ -/** +/** * @file lv_math.h * */ @@ -111,6 +111,22 @@ void /* LV_ATTRIBUTE_FAST_MEM */ lv_sqrt(uint32_t x, lv_sqrt_res_t * q, uint32_t //! @endcond +/** + * Alternative (fast, approximate) implementation for getting the square root of an integer. + * @param x integer which square root should be calculated + */ +int32_t /* LV_ATTRIBUTE_FAST_MEM */ lv_sqrt32(uint32_t x); + +/** + * Calculate the square of an integer (input range is 0..32767). + * @param x input + * @return square + */ +static inline int32_t lv_sqr(int32_t x) +{ + return x * x; +} + /** * Calculate the integer exponents. * @param base diff --git a/src/misc/lv_style.h b/src/misc/lv_style.h index bca6c4265..5bbe1ed7c 100644 --- a/src/misc/lv_style.h +++ b/src/misc/lv_style.h @@ -1,4 +1,4 @@ -/** +/** * @file lv_style.h * */ @@ -130,15 +130,31 @@ typedef uint8_t lv_border_side_t; * The direction of the gradient. */ enum _lv_grad_dir_t { - LV_GRAD_DIR_NONE, /**< No gradient (the `grad_color` property is ignored)*/ - LV_GRAD_DIR_VER, /**< Vertical (top to bottom) gradient*/ - LV_GRAD_DIR_HOR, /**< Horizontal (left to right) gradient*/ + LV_GRAD_DIR_NONE, /**< No gradient (the `grad_color` property is ignored)*/ + LV_GRAD_DIR_VER, /**< Simple vertical (top to bottom) gradient*/ + LV_GRAD_DIR_HOR, /**< Simple horizontal (left to right) gradient*/ + LV_GRAD_DIR_LINEAR, /**< Linear gradient defined by start and end points. Can be at any angle.*/ + LV_GRAD_DIR_RADIAL, /**< Radial gradient defined by start and end circles*/ + LV_GRAD_DIR_CONICAL, /**< Conical gradient defined by center point, start and end angles*/ +}; + +/** + * Gradient behavior outside the defined range. +*/ +enum _lv_grad_extend_t { + LV_GRAD_EXTEND_PAD, /**< Repeat the same color*/ + LV_GRAD_EXTEND_REPEAT, /**< Repeat the pattern*/ + LV_GRAD_EXTEND_REFLECT, /**< Repeat the pattern mirrored*/ }; #ifdef DOXYGEN typedef _lv_grad_dir_t lv_grad_dir_t; +typedef _lv_grad_type_t lv_grad_type_t; +typedef _lv_grad_extend_t lv_grad_extend_t; #else typedef uint8_t lv_grad_dir_t; +typedef uint8_t lv_grad_type_t; +typedef uint8_t lv_grad_extend_t; #endif /*DOXYGEN*/ /** A gradient stop definition. @@ -152,10 +168,37 @@ typedef struct { /** A descriptor of a gradient. */ typedef struct { - lv_gradient_stop_t stops[LV_GRADIENT_MAX_STOPS]; /**< A gradient stop array */ - uint8_t stops_count; /**< The number of used stops in the array */ - lv_grad_dir_t dir : 3; /**< The gradient direction. - * Any of LV_GRAD_DIR_HOR, LV_GRAD_DIR_VER, LV_GRAD_DIR_NONE */ + lv_gradient_stop_t stops[LV_GRADIENT_MAX_STOPS]; /**< A gradient stop array */ + uint8_t stops_count; /**< The number of used stops in the array */ + lv_grad_dir_t dir : 3; /**< The gradient direction. + * Any of LV_GRAD_DIR_NONE, LV_GRAD_DIR_VER, LV_GRAD_DIR_HOR, + LV_GRAD_TYPE_LINEAR, LV_GRAD_TYPE_RADIAL, LV_GRAD_TYPE_CONICAL */ + lv_grad_extend_t extend : 2; /**< Behaviour outside the defined range. + * LV_GRAD_EXTEND_NONE, LV_GRAD_EXTEND_PAD, LV_GRAD_EXTEND_REPEAT, LV_GRAD_EXTEND_REFLECT */ +#if LV_USE_DRAW_SW_COMPLEX_GRADIENTS + union { + /*Linear gradient parameters*/ + struct { + lv_point_t start; /**< Linear gradient vector start point */ + lv_point_t end; /**< Linear gradient vector end point */ + } linear; + /*Radial gradient parameters*/ + struct { + lv_point_t focal; /**< Center of the focal (starting) circle in local coordinates */ + /* (can be the same as the ending circle to create concentric circles) */ + lv_point_t focal_extent; /**< Point on the circle (can be the same as the center) */ + lv_point_t end; /**< Center of the ending circle in local coordinates */ + lv_point_t end_extent; /**< Point on the circle determining the radius of the gradient */ + } radial; + /*Conical gradient parameters*/ + struct { + lv_point_t center; /**< Conical gradient center point */ + int16_t start_angle; /**< Start angle 0..3600 */ + int16_t end_angle; /**< End angle 0..3600 */ + } conical; + } params; + void * state; +#endif } lv_grad_dsc_t; /** diff --git a/tests/ref_imgs/draw/render/al88/demo_render_conical_gradient_opa_128.png b/tests/ref_imgs/draw/render/al88/demo_render_conical_gradient_opa_128.png new file mode 100644 index 000000000..c733bc6b9 Binary files /dev/null and b/tests/ref_imgs/draw/render/al88/demo_render_conical_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/al88/demo_render_conical_gradient_opa_255.png b/tests/ref_imgs/draw/render/al88/demo_render_conical_gradient_opa_255.png new file mode 100644 index 000000000..999f1a84e Binary files /dev/null and b/tests/ref_imgs/draw/render/al88/demo_render_conical_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/al88/demo_render_linear_gradient_opa_128.png b/tests/ref_imgs/draw/render/al88/demo_render_linear_gradient_opa_128.png new file mode 100644 index 000000000..dbfea2936 Binary files /dev/null and b/tests/ref_imgs/draw/render/al88/demo_render_linear_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/al88/demo_render_linear_gradient_opa_255.png b/tests/ref_imgs/draw/render/al88/demo_render_linear_gradient_opa_255.png new file mode 100644 index 000000000..c3c355c6e Binary files /dev/null and b/tests/ref_imgs/draw/render/al88/demo_render_linear_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/al88/demo_render_radial_gradient_opa_128.png b/tests/ref_imgs/draw/render/al88/demo_render_radial_gradient_opa_128.png new file mode 100644 index 000000000..5f712d85d Binary files /dev/null and b/tests/ref_imgs/draw/render/al88/demo_render_radial_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/al88/demo_render_radial_gradient_opa_255.png b/tests/ref_imgs/draw/render/al88/demo_render_radial_gradient_opa_255.png new file mode 100644 index 000000000..57631ff5d Binary files /dev/null and b/tests/ref_imgs/draw/render/al88/demo_render_radial_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/argb8888/demo_render__opa_255.png b/tests/ref_imgs/draw/render/argb8888/demo_render__opa_255.png new file mode 100644 index 000000000..50b5d721d Binary files /dev/null and b/tests/ref_imgs/draw/render/argb8888/demo_render__opa_255.png differ diff --git a/tests/ref_imgs/draw/render/argb8888/demo_render_conical_gradient_opa_128.png b/tests/ref_imgs/draw/render/argb8888/demo_render_conical_gradient_opa_128.png new file mode 100644 index 000000000..45b689ccf Binary files /dev/null and b/tests/ref_imgs/draw/render/argb8888/demo_render_conical_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/argb8888/demo_render_conical_gradient_opa_255.png b/tests/ref_imgs/draw/render/argb8888/demo_render_conical_gradient_opa_255.png new file mode 100644 index 000000000..8b86c9909 Binary files /dev/null and b/tests/ref_imgs/draw/render/argb8888/demo_render_conical_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/argb8888/demo_render_linear_gradient_opa_128.png b/tests/ref_imgs/draw/render/argb8888/demo_render_linear_gradient_opa_128.png new file mode 100644 index 000000000..32dbf9139 Binary files /dev/null and b/tests/ref_imgs/draw/render/argb8888/demo_render_linear_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/argb8888/demo_render_linear_gradient_opa_255.png b/tests/ref_imgs/draw/render/argb8888/demo_render_linear_gradient_opa_255.png new file mode 100644 index 000000000..0200fa558 Binary files /dev/null and b/tests/ref_imgs/draw/render/argb8888/demo_render_linear_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/argb8888/demo_render_radial_gradient_opa_128.png b/tests/ref_imgs/draw/render/argb8888/demo_render_radial_gradient_opa_128.png new file mode 100644 index 000000000..cc4de9346 Binary files /dev/null and b/tests/ref_imgs/draw/render/argb8888/demo_render_radial_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/argb8888/demo_render_radial_gradient_opa_255.png b/tests/ref_imgs/draw/render/argb8888/demo_render_radial_gradient_opa_255.png new file mode 100644 index 000000000..10eff7197 Binary files /dev/null and b/tests/ref_imgs/draw/render/argb8888/demo_render_radial_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/l8/demo_render_conical_gradient_opa_128.png b/tests/ref_imgs/draw/render/l8/demo_render_conical_gradient_opa_128.png new file mode 100644 index 000000000..9ea7fd66c Binary files /dev/null and b/tests/ref_imgs/draw/render/l8/demo_render_conical_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/l8/demo_render_conical_gradient_opa_255.png b/tests/ref_imgs/draw/render/l8/demo_render_conical_gradient_opa_255.png new file mode 100644 index 000000000..98b49a560 Binary files /dev/null and b/tests/ref_imgs/draw/render/l8/demo_render_conical_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/l8/demo_render_linear_gradient_opa_128.png b/tests/ref_imgs/draw/render/l8/demo_render_linear_gradient_opa_128.png new file mode 100644 index 000000000..71f466dbd Binary files /dev/null and b/tests/ref_imgs/draw/render/l8/demo_render_linear_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/l8/demo_render_linear_gradient_opa_255.png b/tests/ref_imgs/draw/render/l8/demo_render_linear_gradient_opa_255.png new file mode 100644 index 000000000..54914c3bd Binary files /dev/null and b/tests/ref_imgs/draw/render/l8/demo_render_linear_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/l8/demo_render_radial_gradient_opa_128.png b/tests/ref_imgs/draw/render/l8/demo_render_radial_gradient_opa_128.png new file mode 100644 index 000000000..d28b557f5 Binary files /dev/null and b/tests/ref_imgs/draw/render/l8/demo_render_radial_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/l8/demo_render_radial_gradient_opa_255.png b/tests/ref_imgs/draw/render/l8/demo_render_radial_gradient_opa_255.png new file mode 100644 index 000000000..6541802bc Binary files /dev/null and b/tests/ref_imgs/draw/render/l8/demo_render_radial_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/rgb565/demo_render__opa_255.png b/tests/ref_imgs/draw/render/rgb565/demo_render__opa_255.png new file mode 100644 index 000000000..c519245b4 Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb565/demo_render__opa_255.png differ diff --git a/tests/ref_imgs/draw/render/rgb565/demo_render_conical_gradient_opa_128.png b/tests/ref_imgs/draw/render/rgb565/demo_render_conical_gradient_opa_128.png new file mode 100644 index 000000000..273075cea Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb565/demo_render_conical_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/rgb565/demo_render_conical_gradient_opa_255.png b/tests/ref_imgs/draw/render/rgb565/demo_render_conical_gradient_opa_255.png new file mode 100644 index 000000000..687734d3c Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb565/demo_render_conical_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/rgb565/demo_render_linear_gradient_opa_128.png b/tests/ref_imgs/draw/render/rgb565/demo_render_linear_gradient_opa_128.png new file mode 100644 index 000000000..5545cdc7d Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb565/demo_render_linear_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/rgb565/demo_render_linear_gradient_opa_255.png b/tests/ref_imgs/draw/render/rgb565/demo_render_linear_gradient_opa_255.png new file mode 100644 index 000000000..fbedf327f Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb565/demo_render_linear_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/rgb565/demo_render_radial_gradient_opa_128.png b/tests/ref_imgs/draw/render/rgb565/demo_render_radial_gradient_opa_128.png new file mode 100644 index 000000000..3d425eb1c Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb565/demo_render_radial_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/rgb565/demo_render_radial_gradient_opa_255.png b/tests/ref_imgs/draw/render/rgb565/demo_render_radial_gradient_opa_255.png new file mode 100644 index 000000000..dd0475638 Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb565/demo_render_radial_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/rgb888/demo_render__opa_255.png b/tests/ref_imgs/draw/render/rgb888/demo_render__opa_255.png new file mode 100644 index 000000000..50b5d721d Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb888/demo_render__opa_255.png differ diff --git a/tests/ref_imgs/draw/render/rgb888/demo_render_conical_gradient_opa_128.png b/tests/ref_imgs/draw/render/rgb888/demo_render_conical_gradient_opa_128.png new file mode 100644 index 000000000..99e365fd6 Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb888/demo_render_conical_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/rgb888/demo_render_conical_gradient_opa_255.png b/tests/ref_imgs/draw/render/rgb888/demo_render_conical_gradient_opa_255.png new file mode 100644 index 000000000..c128cb91b Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb888/demo_render_conical_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/rgb888/demo_render_linear_gradient_opa_128.png b/tests/ref_imgs/draw/render/rgb888/demo_render_linear_gradient_opa_128.png new file mode 100644 index 000000000..a61511979 Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb888/demo_render_linear_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/rgb888/demo_render_linear_gradient_opa_255.png b/tests/ref_imgs/draw/render/rgb888/demo_render_linear_gradient_opa_255.png new file mode 100644 index 000000000..fde01873d Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb888/demo_render_linear_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/rgb888/demo_render_radial_gradient_opa_128.png b/tests/ref_imgs/draw/render/rgb888/demo_render_radial_gradient_opa_128.png new file mode 100644 index 000000000..c23eb9737 Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb888/demo_render_radial_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/rgb888/demo_render_radial_gradient_opa_255.png b/tests/ref_imgs/draw/render/rgb888/demo_render_radial_gradient_opa_255.png new file mode 100644 index 000000000..1a2605086 Binary files /dev/null and b/tests/ref_imgs/draw/render/rgb888/demo_render_radial_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/xrgb8888/demo_render__opa_255.png b/tests/ref_imgs/draw/render/xrgb8888/demo_render__opa_255.png new file mode 100644 index 000000000..50b5d721d Binary files /dev/null and b/tests/ref_imgs/draw/render/xrgb8888/demo_render__opa_255.png differ diff --git a/tests/ref_imgs/draw/render/xrgb8888/demo_render_conical_gradient_opa_128.png b/tests/ref_imgs/draw/render/xrgb8888/demo_render_conical_gradient_opa_128.png new file mode 100644 index 000000000..99e365fd6 Binary files /dev/null and b/tests/ref_imgs/draw/render/xrgb8888/demo_render_conical_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/xrgb8888/demo_render_conical_gradient_opa_255.png b/tests/ref_imgs/draw/render/xrgb8888/demo_render_conical_gradient_opa_255.png new file mode 100644 index 000000000..c128cb91b Binary files /dev/null and b/tests/ref_imgs/draw/render/xrgb8888/demo_render_conical_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/xrgb8888/demo_render_linear_gradient_opa_128.png b/tests/ref_imgs/draw/render/xrgb8888/demo_render_linear_gradient_opa_128.png new file mode 100644 index 000000000..a61511979 Binary files /dev/null and b/tests/ref_imgs/draw/render/xrgb8888/demo_render_linear_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/xrgb8888/demo_render_linear_gradient_opa_255.png b/tests/ref_imgs/draw/render/xrgb8888/demo_render_linear_gradient_opa_255.png new file mode 100644 index 000000000..fde01873d Binary files /dev/null and b/tests/ref_imgs/draw/render/xrgb8888/demo_render_linear_gradient_opa_255.png differ diff --git a/tests/ref_imgs/draw/render/xrgb8888/demo_render_radial_gradient_opa_128.png b/tests/ref_imgs/draw/render/xrgb8888/demo_render_radial_gradient_opa_128.png new file mode 100644 index 000000000..c23eb9737 Binary files /dev/null and b/tests/ref_imgs/draw/render/xrgb8888/demo_render_radial_gradient_opa_128.png differ diff --git a/tests/ref_imgs/draw/render/xrgb8888/demo_render_radial_gradient_opa_255.png b/tests/ref_imgs/draw/render/xrgb8888/demo_render_radial_gradient_opa_255.png new file mode 100644 index 000000000..1a2605086 Binary files /dev/null and b/tests/ref_imgs/draw/render/xrgb8888/demo_render_radial_gradient_opa_255.png differ diff --git a/tests/src/lv_test_conf_full.h b/tests/src/lv_test_conf_full.h index 36a974339..7769f57d5 100644 --- a/tests/src/lv_test_conf_full.h +++ b/tests/src/lv_test_conf_full.h @@ -138,3 +138,5 @@ #define LV_USE_FREETYPE 1 #define LV_FREETYPE_USE_LVGL_PORT 0 #define LV_FREETYPE_CACHE_FT_GLYPH_CNT 10 + +#define LV_USE_DRAW_SW_COMPLEX_GRADIENTS 1