merge dev-7.0

This commit is contained in:
Gabor Kiss-Vamosi
2020-01-05 17:05:54 +01:00
16 changed files with 186 additions and 125 deletions

View File

@@ -4,7 +4,7 @@
# https://aka.ms/yaml
trigger:
- master
- dev-7.0
pool:
vmImage: 'ubuntu-latest'
@@ -14,10 +14,7 @@ steps:
displayName: 'Run a one-line script'
- script: |
echo Start build
echo root content:
ls
cd tests
echo tests content:
python build.py
displayName: 'Build'
displayName: 'Build'

View File

@@ -584,9 +584,6 @@ typedef void * lv_obj_user_data_t;
/*--END OF LV_CONF_H--*/
/*Be sure every define has a default value*/
#include "lvgl/src/lv_conf_checker.h"
#endif /*LV_CONF_H*/
#endif /*End of "Content enable"*/

View File

@@ -10,12 +10,14 @@
#include <stdint.h>
#ifdef LV_CONF_PATH
#if defined(LV_CONF_PATH)
#define __LV_TO_STR_AUX(x) #x
#define __LV_TO_STR(x) __LV_TO_STR_AUX(x)
#include __LV_TO_STR(LV_CONF_PATH)
#undef __LV_TO_STR_AUX
#undef __LV_TO_STR
#elif defined(LV_CONF_INCLUDE_SIMPLE)
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif

View File

@@ -1488,6 +1488,10 @@ lv_res_t lv_event_send(lv_obj_t * obj, lv_event_t event, const void * data)
*/
lv_res_t lv_event_send_func(lv_event_cb_t event_xcb, lv_obj_t * obj, lv_event_t event, const void * data)
{
if(obj != NULL) {
LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
}
/* Build a simple linked list from the objects used in the events
* It's important to know if an this object was deleted by a nested event
* called from this `even_cb`. */
@@ -2820,12 +2824,14 @@ bool lv_obj_is_focused(const lv_obj_t * obj)
*------------------*/
/**
* Hit-test an object given a particular point in screen space.
* @param obj object to hit-test
* Check if a given screen-space point is on an object's coordinates.
*
* This method is intended to be used mainly by advanced hit testing algorithms to check
* whether the point is even within the object (as an optimization).
* @param obj object to check
* @param point screen-space point
* @return true if the object is considered under the point
*/
bool lv_obj_hittest(lv_obj_t * obj, lv_point_t * point) {
bool lv_obj_is_point_on_coords(lv_obj_t * obj, const lv_point_t * point) {
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
lv_area_t ext_area;
ext_area.x1 = obj->coords.x1 - obj->ext_click_pad_hor;
@@ -2847,15 +2853,24 @@ bool lv_obj_hittest(lv_obj_t * obj, lv_point_t * point) {
#endif
return false;
}
return true;
}
/**
* Hit-test an object given a particular point in screen space.
* @param obj object to hit-test
* @param point screen-space point
* @return true if the object is considered under the point
*/
bool lv_obj_hittest(lv_obj_t * obj, lv_point_t * point) {
if(obj->adv_hittest) {
lv_hit_test_info_t hit_info;
hit_info.point = point;
hit_info.result = true;
obj->signal_cb(obj, LV_SIGNAL_HIT_TEST, &hit_info);
if(!hit_info.result)
return false;
}
return true;
return hit_info.result;
} else
return lv_obj_is_point_on_coords(obj, point);
}
/**

View File

@@ -993,6 +993,22 @@ lv_event_cb_t lv_obj_get_event_cb(const lv_obj_t * obj);
* Other get
*-----------------*/
/**
* Check if a given screen-space point is on an object's coordinates.
*
* This method is intended to be used mainly by advanced hit testing algorithms to check
* whether the point is even within the object (as an optimization).
* @param obj object to check
* @param point screen-space point
*/
bool lv_obj_is_point_on_coords(lv_obj_t * obj, const lv_point_t * point);
/**
* Hit-test an object given a particular point in screen space.
* @param obj object to hit-test
* @param point screen-space point
* @return true if the object is considered under the point
*/
bool lv_obj_hittest(lv_obj_t * obj, lv_point_t * point);
/**

View File

@@ -207,6 +207,7 @@ void lv_draw_label(const lv_area_t * coords, const lv_area_t * mask, lv_draw_lab
char *bidi_txt = lv_mem_buf_get(line_end - line_start + 1);
lv_bidi_process_paragraph(txt + line_start, bidi_txt, line_end - line_start, dsc->bidi_dir, NULL, 0);
#else
(void)bidi_dir;
const char *bidi_txt = txt + line_start;
#endif

View File

@@ -29,12 +29,11 @@
/**********************
* STATIC VARIABLES
**********************/
lv_mem_buf_t _lv_mem_buf[LV_MEM_BUF_MAX_NUM];
#if(!defined(LV_ENABLE_GC)) || LV_ENABLE_GC == 0
LV_ROOTS
#endif /* LV_ENABLE_GC */
/**********************
* MACROS
**********************/

View File

@@ -24,8 +24,6 @@ extern "C" {
* DEFINES
*********************/
extern lv_mem_buf_t _lv_mem_buf[LV_MEM_BUF_MAX_NUM];
#define LV_ITERATE_ROOTS(f) \
f(lv_ll_t, _lv_task_ll) /*Linked list to store the lv_tasks*/ \
f(lv_ll_t, _lv_disp_ll) /*Linked list of screens*/ \
@@ -37,6 +35,7 @@ extern lv_mem_buf_t _lv_mem_buf[LV_MEM_BUF_MAX_NUM];
f(lv_ll_t, _lv_img_defoder_ll) \
f(lv_img_cache_entry_t*, _lv_img_cache_array) \
f(void*, _lv_task_act) \
f(lv_mem_buf_arr_t , _lv_mem_buf) \
#define LV_DEFINE_ROOT(root_type, root_name) root_type root_name;
#define LV_ROOTS LV_ITERATE_ROOTS(LV_DEFINE_ROOT)

View File

@@ -136,8 +136,8 @@ uint32_t lv_ll_get_len(const lv_ll_t * ll_p);
* @param ll_p
* @param n1_p
* @param n2_p
*/
void lv_ll_swap(lv_ll_t * ll_p, void * n1_p, void * n2_p);
*/
/**
* Move a nodw before an other node in the same linked list

View File

@@ -16,6 +16,10 @@
#include LV_MEM_CUSTOM_INCLUDE
#endif
#if defined(LV_GC_INCLUDE)
#include LV_GC_INCLUDE
#endif /* LV_ENABLE_GC */
/*********************
* DEFINES
*********************/
@@ -403,23 +407,23 @@ void * lv_mem_buf_get(uint32_t size)
/*Try to find a free buffer with suitable size */
uint8_t i;
for(i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
if(_lv_mem_buf[i].used == 0 && _lv_mem_buf[i].size >= size) {
_lv_mem_buf[i].used = 1;
return _lv_mem_buf[i].p;
if(LV_GC_ROOT(_lv_mem_buf[i]).used == 0 && LV_GC_ROOT(_lv_mem_buf[i]).size >= size) {
LV_GC_ROOT(_lv_mem_buf[i]).used = 1;
return LV_GC_ROOT(_lv_mem_buf[i]).p;
}
}
/*Reallocate a free buffer*/
for(i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
if(_lv_mem_buf[i].used == 0) {
_lv_mem_buf[i].used = 1;
_lv_mem_buf[i].size = size;
if(LV_GC_ROOT(_lv_mem_buf[i]).used == 0) {
LV_GC_ROOT(_lv_mem_buf[i]).used = 1;
LV_GC_ROOT(_lv_mem_buf[i]).size = size;
/*if this fails you probably need to increase your LV_MEM_SIZE/heap size*/
_lv_mem_buf[i].p = lv_mem_realloc(_lv_mem_buf[i].p, size);
if(_lv_mem_buf[i].p == NULL) {
LV_GC_ROOT(_lv_mem_buf[i]).p = lv_mem_realloc(LV_GC_ROOT(_lv_mem_buf[i]).p, size);
if(LV_GC_ROOT(_lv_mem_buf[i]).p == NULL) {
LV_LOG_ERROR("lv_mem_buf_get: Out of memory, can't allocate a new buffer (increase your LV_MEM_SIZE/heap size)")
}
return _lv_mem_buf[i].p;
return LV_GC_ROOT(_lv_mem_buf[i]).p;
}
}
@@ -436,8 +440,8 @@ void lv_mem_buf_release(void * p)
{
uint8_t i;
for(i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
if(_lv_mem_buf[i].p == p) {
_lv_mem_buf[i].used = 0;
if(LV_GC_ROOT(_lv_mem_buf[i]).p == p) {
LV_GC_ROOT(_lv_mem_buf[i]).used = 0;
return;
}
}
@@ -452,11 +456,11 @@ void lv_mem_buf_free_all(void)
{
uint8_t i;
for(i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
if(_lv_mem_buf[i].p) {
lv_mem_free(_lv_mem_buf[i].p);
_lv_mem_buf[i].p = NULL;
_lv_mem_buf[i].used = 0;
_lv_mem_buf[i].size = 0;
if(LV_GC_ROOT(_lv_mem_buf[i]).p) {
lv_mem_free(LV_GC_ROOT(_lv_mem_buf[i]).p);
LV_GC_ROOT(_lv_mem_buf[i]).p = NULL;
LV_GC_ROOT(_lv_mem_buf[i]).used = 0;
LV_GC_ROOT(_lv_mem_buf[i]).size = 0;
}
}
}

View File

@@ -52,6 +52,9 @@ typedef struct {
uint8_t used :1;
}lv_mem_buf_t;
typedef lv_mem_buf_t lv_mem_buf_arr_t[LV_MEM_BUF_MAX_NUM];
extern lv_mem_buf_arr_t _lv_mem_buf;
/**********************
* GLOBAL PROTOTYPES
**********************/

View File

@@ -25,6 +25,12 @@
#define LV_BAR_SIZE_MIN 4 /*hor. pad and ver. pad cannot make the indicator smaller then this [px]*/
#if LV_USE_ANIMATION
#define LV_BAR_IS_ANIMATING(anim_struct) (((anim_struct).anim_state) != LV_BAR_ANIM_STATE_INV)
#define LV_BAR_GET_ANIM_VALUE(orig_value, anim_struct) (LV_BAR_IS_ANIMATING(anim_struct) ? ((anim_struct).anim_end) : (orig_value))
#else
#define LV_BAR_GET_ANIM_VALUE(orig_value, anim_struct) (orig_value)
#endif
/**********************
* TYPEDEFS
**********************/
@@ -264,12 +270,8 @@ int16_t lv_bar_get_value(const lv_obj_t * bar)
LV_ASSERT_OBJ(bar, LV_OBJX_NAME);
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
/*If animated tell that it's already at the end value*/
#if LV_USE_ANIMATION
if(ext->cur_value_anim.is_animating) return ext->cur_value_anim.anim_end;
#endif
/*No animation, simple return the current value*/
return ext->cur_value;
return LV_BAR_GET_ANIM_VALUE(ext->cur_value, ext->cur_value_anim);
}
/**
@@ -285,12 +287,7 @@ int16_t lv_bar_get_start_value(const lv_obj_t * bar)
if(ext->type != LV_BAR_TYPE_CUSTOM) return ext->min_value;
/*If animated tell that it's already at the end value*/
#if LV_USE_ANIMATION
if(ext->start_value_anim.is_animating) return ext->start_value_anim.anim_end;
#endif
/*No animation, simple return the current value*/
return ext->start_value;
return LV_BAR_GET_ANIM_VALUE(ext->start_value, ext->start_value_anim);
}
/**
@@ -398,16 +395,7 @@ static void draw_indic(lv_obj_t * bar, const lv_area_t * clip_area)
int32_t range = ext->max_value - ext->min_value;
bool hor = objw >= objh ? true : false;
bool sym = false;
if(ext->type == LV_BAR_TYPE_SYM && ext->min_value < 0 && ext->max_value > 0) sym = true;
bool cur_value_anim = false;
#if LV_USE_ANIMATION
if(ext->cur_value_anim.is_animating) cur_value_anim = true;
#endif
bool start_value_anim = false;
#if LV_USE_ANIMATION
if(ext->start_value_anim.is_animating) start_value_anim = true;
#endif
if(ext->type == LV_BAR_TYPE_SYM && ext->min_value < 0 && ext->max_value > 0 && ext->start_value == ext->min_value) sym = true;
/*Calculate the indicator area*/
lv_style_value_t bg_left = lv_obj_get_style_value(bar, LV_BAR_PART_BG, LV_STYLE_PAD_LEFT);
@@ -434,69 +422,70 @@ static void draw_indic(lv_obj_t * bar, const lv_area_t * clip_area)
lv_coord_t indich = lv_area_get_height(&ext->indic_area);
/*Calculate the indicator length*/
lv_coord_t indic_length;
lv_coord_t anim_length = hor ? indicw : indich;
lv_coord_t anim_cur_value, anim_start_value;
if(cur_value_anim) {
#if LV_USE_ANIMATION
anim_cur_value = ext->cur_value_anim.anim_val;
#endif
} else {
anim_cur_value = ext->cur_value;
}
lv_coord_t anim_cur_value_x, anim_start_value_x;
if(start_value_anim) {
#if LV_USE_ANIMATION
anim_start_value = ext->start_value_anim.anim_val;
#endif
} else {
anim_start_value = ext->start_value;
}
lv_coord_t * axis1, * axis2;
lv_coord_t (*indic_length_calc)(const lv_area_t *area);
indic_length = (int32_t)((int32_t)(hor ? indicw : indich) * ((anim_cur_value - ext->min_value) - (anim_start_value - ext->min_value))) /
(ext->max_value - ext->min_value);
/*Horizontal bar*/
if(hor) {
ext->indic_area.x2 = ext->indic_area.x1 + indic_length - 1;
if(sym) {
lv_coord_t zero;
zero = ext->indic_area.x1 + (-ext->min_value * indicw) / range;
if(ext->indic_area.x2 > zero)
ext->indic_area.x1 = zero;
else {
ext->indic_area.x1 = ext->indic_area.x2;
ext->indic_area.x2 = zero;
}
} else {
lv_coord_t increment = ((anim_start_value-ext->min_value) * indicw) / range;
ext->indic_area.x1 += increment;
ext->indic_area.x2 += increment;
}
axis1 = &ext->indic_area.x1;
axis2 = &ext->indic_area.x2;
indic_length_calc = lv_area_get_width;
} else {
axis1 = &ext->indic_area.y1;
axis2 = &ext->indic_area.y2;
indic_length_calc = lv_area_get_height;
}
/*Vertical bar*/
else {
ext->indic_area.y1 = ext->indic_area.y2 - indic_length + 1;
if(sym) {
lv_coord_t zero;
zero = ext->indic_area.y2 - (-ext->min_value * objh) / range;
if(ext->indic_area.y1 < zero)
ext->indic_area.y2 = zero;
else {
ext->indic_area.y2 = ext->indic_area.y1;
ext->indic_area.y1 = zero;
}
} else {
lv_coord_t increment = ((anim_start_value-ext->min_value) * objh) / range;
ext->indic_area.y1 += increment;
ext->indic_area.y2 += increment;
}
#if LV_USE_ANIMATION
if(LV_BAR_IS_ANIMATING(ext->start_value_anim)) {
lv_coord_t anim_start_value_start_x =
(int32_t)((int32_t)anim_length * (ext->start_value_anim.anim_start - ext->min_value)) / range;
lv_coord_t anim_start_value_end_x =
(int32_t)((int32_t)anim_length * (ext->start_value_anim.anim_end - ext->min_value)) / range;
anim_start_value_x = (((anim_start_value_end_x - anim_start_value_start_x) * ext->start_value_anim.anim_state) >> LV_BAR_ANIM_STATE_NORM);
} else
#endif
{
anim_start_value_x = (int32_t)((int32_t)anim_length * (ext->start_value - ext->min_value)) / range;
}
#if LV_USE_ANIMATION
if(LV_BAR_IS_ANIMATING(ext->cur_value_anim)) {
lv_coord_t anim_cur_value_start_x =
(int32_t)((int32_t)anim_length * (ext->cur_value_anim.anim_start - ext->min_value)) / range;
lv_coord_t anim_cur_value_end_x =
(int32_t)((int32_t)anim_length * (ext->cur_value_anim.anim_end - ext->min_value)) / range;
anim_cur_value_x = (((anim_cur_value_end_x - anim_cur_value_start_x) * ext->cur_value_anim.anim_state) >> LV_BAR_ANIM_STATE_NORM);
} else
#endif
{
anim_cur_value_x = (int32_t)((int32_t)anim_length * (ext->cur_value - ext->min_value)) / range;
}
/* Set the indicator length */
*axis2 = *axis1 + anim_cur_value_x;
*axis1 += anim_start_value_x;
if(sym) {
lv_coord_t zero;
zero = *axis1 + (-ext->min_value * anim_length) / range;
if(*axis2 > zero)
*axis1 = zero;
else {
*axis1 = *axis2;
*axis2 = zero;
}
}
/*Draw the indicator*/
/*Do not draw a zero length indicator*/
if(!sym && indic_length == 0) return;
if(!sym && indic_length_calc(&ext->indic_area) == 0) return;
uint16_t bg_radius = lv_obj_get_style_value(bar, LV_BAR_PART_BG, LV_STYLE_RADIUS);
lv_coord_t short_side = LV_MATH_MIN(objw, objh);
@@ -639,7 +628,7 @@ static lv_style_dsc_t * lv_bar_get_style(lv_obj_t * bar, uint8_t part)
#if LV_USE_ANIMATION
static void lv_bar_anim(lv_bar_anim_t * var, lv_anim_value_t value)
{
var->anim_val = value;
var->anim_state = value;
lv_obj_invalidate(var->bar);
}
@@ -647,7 +636,7 @@ static void lv_bar_anim_ready(lv_anim_t * a)
{
lv_bar_anim_t * var = a->var;
lv_bar_ext_t * ext = lv_obj_get_ext_attr(var->bar);
var->is_animating = false;
var->anim_state = LV_BAR_ANIM_STATE_INV;
if(var == &ext->cur_value_anim)
ext->cur_value = var->anim_end;
else if(var == &ext->start_value_anim)
@@ -662,7 +651,7 @@ static void lv_bar_set_value_with_anim(lv_obj_t * bar, int16_t new_value, int16_
} else {
lv_bar_ext_t *ext = lv_obj_get_ext_attr(bar);
/*No animation in progress -> simply set the values*/
if(!anim_info->is_animating) {
if(anim_info->anim_state == LV_BAR_ANIM_STATE_INV) {
anim_info->anim_start = *value_ptr;
anim_info->anim_end = new_value;
}
@@ -676,8 +665,8 @@ static void lv_bar_set_value_with_anim(lv_obj_t * bar, int16_t new_value, int16_
lv_anim_t a;
a.var = anim_info;
a.start = anim_info->anim_start;
a.end = anim_info->anim_end;
a.start = LV_BAR_ANIM_STATE_START;
a.end = LV_BAR_ANIM_STATE_END;
a.exec_cb = (lv_anim_exec_xcb_t)lv_bar_anim;
a.path_cb = lv_anim_path_linear;
a.ready_cb = lv_bar_anim_ready;
@@ -689,7 +678,6 @@ static void lv_bar_set_value_with_anim(lv_obj_t * bar, int16_t new_value, int16_
a.repeat_pause = 0;
lv_anim_create(&a);
anim_info->is_animating = true;
}
}
@@ -698,7 +686,7 @@ static void lv_bar_init_anim(lv_obj_t * bar, lv_bar_anim_t * bar_anim)
bar_anim->bar = bar;
bar_anim->anim_start = 0;
bar_anim->anim_end = 0;
bar_anim->is_animating = false;
bar_anim->anim_state = LV_BAR_ANIM_STATE_INV;
}
#endif

View File

@@ -27,6 +27,18 @@ extern "C" {
* DEFINES
*********************/
/** Bar animation start value. (Not the real value of the Bar just indicates process animation)*/
#define LV_BAR_ANIM_STATE_START 0
/** Bar animation end value. (Not the real value of the Bar just indicates process animation)*/
#define LV_BAR_ANIM_STATE_END 256
/** Mark no animation is in progress */
#define LV_BAR_ANIM_STATE_INV -1
/** log2(LV_BAR_ANIM_STATE_END) used to normalize data*/
#define LV_BAR_ANIM_STATE_NORM 8
/**********************
* TYPEDEFS
**********************/
@@ -43,8 +55,7 @@ typedef struct {
lv_obj_t * bar;
lv_anim_value_t anim_start;
lv_anim_value_t anim_end;
lv_anim_value_t anim_val;
uint8_t is_animating : 1;
lv_anim_value_t anim_state;
} lv_bar_anim_t;
#endif

View File

@@ -861,9 +861,10 @@ static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param)
lv_indev_type_t indev_type = lv_indev_get_type(indev);
/*If not focused by an input device assume the last input device*/
if(indev_type == LV_INDEV_TYPE_NONE) {
indev_type = lv_indev_get_type(lv_indev_get_next(NULL));
}
if(indev == NULL) {
indev = lv_indev_get_next(NULL);
indev_type = lv_indev_get_type(indev);
}
if(indev_type == LV_INDEV_TYPE_POINTER) {
/*Select the clicked button*/

View File

@@ -900,9 +900,14 @@ static lv_res_t lv_cpicker_signal(lv_obj_t * cpicker, lv_signal_t sign, void * p
static bool lv_cpicker_hit(lv_obj_t * cpicker, const lv_point_t * p)
{
bool is_point_on_coords = lv_obj_is_point_on_coords(cpicker, p);
if(!is_point_on_coords)
return false;
lv_cpicker_ext_t * ext = (lv_cpicker_ext_t *)lv_obj_get_ext_attr(cpicker);
if(ext->type != LV_CPICKER_TYPE_DISC || ext->preview)
return true;
const lv_style_t * style_main = lv_cpicker_get_style(cpicker, LV_CPICKER_STYLE_MAIN);
lv_area_t area_mid;
lv_area_copy(&area_mid, &cpicker->coords);

View File

@@ -98,6 +98,7 @@ lv_obj_t * lv_img_create(lv_obj_t * par, const lv_obj_t * copy)
if(copy == NULL) {
lv_obj_set_click(new_img, false);
lv_obj_set_adv_hittest(new_img, true); /*Images have fast hit-testing*/
/* Enable auto size for non screens
* because image screens are wallpapers
* and must be screen sized*/
@@ -502,6 +503,8 @@ static lv_design_res_t lv_img_design(lv_obj_t * img, const lv_area_t * clip_area
cover = lv_area_is_in(clip_area, &img->coords, 0) ? LV_DESIGN_RES_COVER : LV_DESIGN_RES_NOT_COVER;
}
if(lv_obj_get_style_opa(img, LV_IMG_PART_MAIN, LV_STYLE_IMAGE_OPA) < LV_OPA_MAX) return false;
return cover;
} else if(mode == LV_DESIGN_DRAW_MAIN) {
if(ext->h == 0 || ext->w == 0) return true;
@@ -599,6 +602,26 @@ static lv_res_t lv_img_signal(lv_obj_t * img, lv_signal_t sign, void * param)
lv_coord_t d = ds.i / 2;
img->ext_draw_pad = LV_MATH_MAX(img->ext_draw_pad, d);
}
} else if(sign == LV_SIGNAL_HIT_TEST) {
lv_hit_test_info_t *info = param;
if(ext->zoom != 256 && ext->angle == 0) {
lv_coord_t origin_width = lv_area_get_width(&img->coords);
lv_coord_t origin_height = lv_area_get_height(&img->coords);
lv_coord_t scaled_width = (origin_width * ext->zoom + 255) / 256;
lv_coord_t scaled_height = (origin_height * ext->zoom + 255) / 256;
lv_coord_t width_offset = (origin_width - scaled_width) / 2;
lv_coord_t height_offset = (origin_height - scaled_height) / 2;
lv_area_t coords;
lv_area_copy(&coords, &img->coords);
coords.x1 += width_offset;
coords.x2 -= width_offset;
coords.y1 += height_offset;
coords.y2 -= height_offset;
info->result = lv_area_is_point_on(&coords, info->point, 0);
} else
info->result = lv_obj_is_point_on_coords(img, info->point);
}
return res;