feat(obj): add transform matrix attribute (#7187)

This commit is contained in:
VIFEX 2024-11-09 15:01:22 +08:00 committed by GitHub
parent f89ac3677e
commit 43a3c65b82
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 283 additions and 14 deletions

View File

@ -111,6 +111,7 @@ void lv_example_msgbox_2(void);
void lv_example_obj_1(void);
void lv_example_obj_2(void);
void lv_example_obj_3(void);
void lv_example_roller_1(void);
void lv_example_roller_2(void);

View File

@ -11,3 +11,8 @@ Make an object draggable
.. lv_example:: widgets/obj/lv_example_obj_2
:language: c
Transform object using a 3x3 matrix
-----------------------------------
.. lv_example:: widgets/obj/lv_example_obj_3
:language: c

View File

@ -0,0 +1,42 @@
#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES
#if LV_DRAW_TRANSFORM_USE_MATRIX
static void timer_cb(lv_timer_t * timer)
{
lv_obj_t * obj = lv_timer_get_user_data(timer);
static float value = 0.1f;
lv_matrix_t matrix;
lv_matrix_identity(&matrix);
lv_matrix_scale(&matrix, value, 1);
lv_matrix_rotate(&matrix, value * 360);
lv_obj_set_transform(obj, &matrix);
value += 0.01f;
if(value > 2.0f) {
lv_obj_reset_transform(obj);
value = 0.1f;
}
}
void lv_example_obj_3(void)
{
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_center(obj);
lv_timer_create(timer_cb, 20, obj);
}
#else
void lv_example_obj_3(void)
{
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_label_set_text_static(label, "LV_DRAW_TRANSFORM_USE_MATRIX is not enabled");
lv_obj_center(label);
}
#endif /*LV_DRAW_TRANSFORM_USE_MATRIX*/
#endif /*LV_BUILD_EXAMPLES*/

View File

@ -541,6 +541,13 @@ static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
lv_event_remove_all(&obj->spec_attr->event_list);
#if LV_DRAW_TRANSFORM_USE_MATRIX
if(obj->spec_attr->matrix) {
lv_free(obj->spec_attr->matrix);
obj->spec_attr->matrix = NULL;
}
#endif
lv_free(obj->spec_attr);
obj->spec_attr = NULL;
}

View File

@ -10,6 +10,7 @@
#include "../layouts/lv_layout_private.h"
#include "lv_obj_event_private.h"
#include "lv_obj_draw_private.h"
#include "lv_obj_style_private.h"
#include "lv_obj_private.h"
#include "../display/lv_display.h"
#include "../display/lv_display_private.h"
@ -983,6 +984,82 @@ void lv_obj_center(lv_obj_t * obj)
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
}
void lv_obj_set_transform(lv_obj_t * obj, const lv_matrix_t * matrix)
{
#if LV_DRAW_TRANSFORM_USE_MATRIX
LV_ASSERT_OBJ(obj, MY_CLASS);
if(!matrix) {
lv_obj_reset_transform(obj);
return;
}
lv_obj_allocate_spec_attr(obj);
if(!obj->spec_attr->matrix) {
obj->spec_attr->matrix = lv_malloc(sizeof(lv_matrix_t));;
LV_ASSERT_MALLOC(obj->spec_attr->matrix);
}
/* Invalidate the old area */
lv_obj_invalidate(obj);
/* Copy the matrix */
*obj->spec_attr->matrix = *matrix;
/* Matrix is set. Update the layer type */
lv_obj_update_layer_type(obj);
/* Invalidate the new area */
lv_obj_invalidate(obj);
#else
LV_UNUSED(obj);
LV_UNUSED(matrix);
LV_LOG_WARN("Transform matrix is not used because LV_DRAW_TRANSFORM_USE_MATRIX is disabled");
#endif
}
void lv_obj_reset_transform(lv_obj_t * obj)
{
#if LV_DRAW_TRANSFORM_USE_MATRIX
LV_ASSERT_OBJ(obj, MY_CLASS);
if(!obj->spec_attr) {
return;
}
if(!obj->spec_attr->matrix) {
return;
}
/* Invalidate the old area */
lv_obj_invalidate(obj);
/* Free the matrix */
lv_free(obj->spec_attr->matrix);
obj->spec_attr->matrix = NULL;
/* Matrix is cleared. Update the layer type */
lv_obj_update_layer_type(obj);
/* Invalidate the new area */
lv_obj_invalidate(obj);
#else
LV_UNUSED(obj);
#endif
}
const lv_matrix_t * lv_obj_get_transform(const lv_obj_t * obj)
{
#if LV_DRAW_TRANSFORM_USE_MATRIX
LV_ASSERT_OBJ(obj, MY_CLASS);
if(obj->spec_attr) {
return obj->spec_attr->matrix;
}
#else
LV_UNUSED(obj);
#endif
return NULL;
}
/**********************
* STATIC FUNCTIONS
**********************/
@ -1170,6 +1247,31 @@ static void layout_update_core(lv_obj_t * obj)
static void transform_point_array(const lv_obj_t * obj, lv_point_t * p, size_t p_count, bool inv)
{
#if LV_DRAW_TRANSFORM_USE_MATRIX
const lv_matrix_t * obj_matrix = lv_obj_get_transform(obj);
if(obj_matrix) {
lv_matrix_t m;
lv_matrix_identity(&m);
lv_matrix_translate(&m, obj->coords.x1, obj->coords.y1);
lv_matrix_multiply(&m, obj_matrix);
lv_matrix_translate(&m, -obj->coords.x1, -obj->coords.y1);
if(inv) {
lv_matrix_t inv_m;
lv_matrix_inverse(&inv_m, &m);
m = inv_m;
}
for(size_t i = 0; i < p_count; i++) {
lv_point_precise_t p_precise = lv_point_to_precise(&p[i]);
lv_point_precise_t res = lv_matrix_transform_precise_point(&m, &p_precise);
p[i] = lv_point_from_precise(&res);
}
return;
}
#endif /* LV_DRAW_TRANSFORM_USE_MATRIX */
int32_t angle = lv_obj_get_style_transform_rotation(obj, 0);
int32_t scale_x = lv_obj_get_style_transform_scale_x_safe(obj, 0);
int32_t scale_y = lv_obj_get_style_transform_scale_y_safe(obj, 0);

View File

@ -197,6 +197,21 @@ void lv_obj_align_to(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, in
*/
void lv_obj_center(lv_obj_t * obj);
/**
* Set the transfrom matrix of an object
* @param obj pointer to an object
* @param matrix pointer to a matrix to set
* @note `LV_DRAW_TRANSFORM_USE_MATRIX` needs to be enabled.
*/
void lv_obj_set_transform(lv_obj_t * obj, const lv_matrix_t * matrix);
/**
* Reset the transfrom matrix of an object to identity matrix
* @param obj pointer to an object
* @note `LV_DRAW_TRANSFORM_USE_MATRIX` needs to be enabled.
*/
void lv_obj_reset_transform(lv_obj_t * obj);
/**
* Copy the coordinates of an object to an area
* @param obj pointer to an object
@ -342,6 +357,13 @@ void lv_obj_move_to(lv_obj_t * obj, int32_t x, int32_t y);
void lv_obj_move_children_by(lv_obj_t * obj, int32_t x_diff, int32_t y_diff, bool ignore_floating);
/**
* Get the transform matrix of an object
* @param obj pointer to an object
* @return pointer to the transform matrix or NULL if not set
*/
const lv_matrix_t * lv_obj_get_transform(const lv_obj_t * obj);
/**
* Transform a point using the angle and zoom style properties of an object
* @param obj pointer to an object whose style properties should be used

View File

@ -31,6 +31,9 @@ extern "C" {
struct _lv_obj_spec_attr_t {
lv_obj_t ** children; /**< Store the pointer of the children in an array.*/
lv_group_t * group_p;
#if LV_DRAW_TRANSFORM_USE_MATRIX
lv_matrix_t * matrix; /**< The transform matrix*/
#endif
lv_event_list_t event_list;
lv_point_t scroll; /**< The current X/Y scroll offset*/

View File

@ -966,6 +966,9 @@ static void trans_anim_completed_cb(lv_anim_t * a)
static lv_layer_type_t calculate_layer_type(lv_obj_t * obj)
{
#if LV_DRAW_TRANSFORM_USE_MATRIX
if(lv_obj_get_transform(obj) != NULL) return LV_LAYER_TYPE_TRANSFORM;
#endif
if(lv_obj_get_style_transform_rotation(obj, 0) != 0) return LV_LAYER_TYPE_TRANSFORM;
if(lv_obj_get_style_transform_scale_x(obj, 0) != 256) return LV_LAYER_TYPE_TRANSFORM;
if(lv_obj_get_style_transform_scale_y(obj, 0) != 256) return LV_LAYER_TYPE_TRANSFORM;

View File

@ -969,11 +969,17 @@ static bool alpha_test_area_on_obj(lv_obj_t * obj, const lv_area_t * area)
#if LV_DRAW_TRANSFORM_USE_MATRIX
static void refr_obj_matrix(lv_layer_t * layer, lv_obj_t * obj)
static bool obj_get_matrix(lv_obj_t * obj, lv_matrix_t * matrix)
{
lv_matrix_t ori_matrix = layer->matrix;
lv_matrix_t obj_matrix;
lv_matrix_identity(&obj_matrix);
lv_matrix_identity(matrix);
const lv_matrix_t * obj_matrix = lv_obj_get_transform(obj);
if(obj_matrix) {
lv_matrix_translate(matrix, obj->coords.x1, obj->coords.y1);
lv_matrix_multiply(matrix, obj_matrix);
lv_matrix_translate(matrix, -obj->coords.x1, -obj->coords.y1);
return true;
}
lv_point_t pivot = {
.x = lv_obj_get_style_transform_pivot_x(obj, 0),
@ -991,39 +997,55 @@ static void refr_obj_matrix(lv_layer_t * layer, lv_obj_t * obj)
if(scale_x <= 0 || scale_y <= 0) {
/* NOT draw if scale is negative or zero */
return;
return false;
}
/* generate the obj matrix */
lv_matrix_translate(&obj_matrix, pivot.x, pivot.y);
lv_matrix_translate(matrix, pivot.x, pivot.y);
if(rotation != 0) {
lv_matrix_rotate(&obj_matrix, rotation * 0.1f);
lv_matrix_rotate(matrix, rotation * 0.1f);
}
if(scale_x != LV_SCALE_NONE || scale_y != LV_SCALE_NONE) {
lv_matrix_scale(
&obj_matrix,
matrix,
(float)scale_x / LV_SCALE_NONE,
(float)scale_y / LV_SCALE_NONE
);
}
if(skew_x != 0 || skew_y != 0) {
lv_matrix_skew(&obj_matrix, skew_x, skew_y);
lv_matrix_skew(matrix, skew_x, skew_y);
}
lv_matrix_translate(&obj_matrix, -pivot.x, -pivot.y);
lv_matrix_translate(matrix, -pivot.x, -pivot.y);
return true;
}
static void refr_obj_matrix(lv_layer_t * layer, lv_obj_t * obj)
{
lv_matrix_t obj_matrix;
if(!obj_get_matrix(obj, &obj_matrix)) {
/* NOT draw if obj matrix is not available */
return;
}
lv_matrix_t matrix_inv;
if(!lv_matrix_inverse(&matrix_inv, &obj_matrix)) {
/* NOT draw if matrix is not invertible */
return;
}
/* save original matrix */
lv_matrix_t ori_matrix = layer->matrix;
/* apply the obj matrix */
lv_matrix_multiply(&layer->matrix, &obj_matrix);
/* calculate clip area without transform */
lv_matrix_t matrix_reverse;
lv_matrix_inverse(&matrix_reverse, &obj_matrix);
lv_area_t clip_area = layer->_clip_area;
lv_area_t clip_area_ori = layer->_clip_area;
clip_area = lv_matrix_transform_area(&matrix_reverse, &clip_area);
clip_area = lv_matrix_transform_area(&matrix_inv, &clip_area);
/* increase the clip area by 1 pixel to avoid rounding errors */
if(!lv_matrix_is_identity_or_translation(&obj_matrix)) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,62 @@
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "unity/unity.h"
void setUp(void)
{
/* Function run before every test */
}
void tearDown(void)
{
/* Function run after every test */
lv_obj_clean(lv_screen_active());
}
void test_obj_transform(void)
{
#if LV_DRAW_TRANSFORM_USE_MATRIX
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_set_size(obj, 100, 100);
lv_obj_center(obj);
lv_matrix_t matrix;
lv_matrix_identity(&matrix);
lv_obj_set_transform(obj, &matrix);
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/obj_transform_identity.png");
lv_matrix_identity(&matrix);
lv_matrix_translate(&matrix, 50, 100);
lv_obj_set_transform(obj, &matrix);
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/obj_transform_translate.png");
lv_matrix_identity(&matrix);
lv_matrix_rotate(&matrix, 30);
lv_obj_set_transform(obj, &matrix);
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/obj_transform_rotate.png");
lv_matrix_identity(&matrix);
lv_matrix_scale(&matrix, 0.8f, 1.6f);
lv_obj_set_transform(obj, &matrix);
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/obj_transform_scale.png");
lv_matrix_identity(&matrix);
lv_matrix_skew(&matrix, 10.0f, 20.0f);
lv_obj_set_transform(obj, &matrix);
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/obj_transform_skew.png");
const lv_matrix_t * obj_transform = lv_obj_get_transform(obj);
TEST_ASSERT_NOT_NULL(obj_transform);
TEST_ASSERT_EQUAL(lv_memcmp(&matrix, obj_transform, sizeof(lv_matrix_t)), 0);
lv_obj_reset_transform(obj);
TEST_ASSERT_EQUAL_SCREENSHOT("widgets/obj_transform_identity.png");
TEST_ASSERT_NULL(lv_obj_get_transform(obj));
#else
TEST_PASS();
#endif
}
#endif