Is it possible to implement a Blur filter in LVGL?

Hi @Yuri_Alves_Bordin ,

Thank you for your links, I see you are using different porch & clock rate settings(do you have a souce for these?), I will explore this further at some point.

I will think about the blur issue, it will be easier for me to comment once I have my hardware working correctly.

I will come back to you here once I have thought some more. If you try anything in the meantime feel free to post your findings here. :slight_smile:

Kind Regards,

Pete

yes i have got the Clock rate and other settings based on a example code they seller provided

Downloaded the code here
https://drive.google.com/drive/folders/1rsi7wycEW5ftoMOvmkPMJFN4dEbzOj7p
that download link can be found on the oficial seller website

i remember trying to run the code the had and not woking then creating mine and just using some parts of the original code

the code provided here:

where made when i was still very new to C and to LVGL basically learning while doing it

Thanks for the info @Yuri_Alves_Bordin. Much appreciated will speak soon about the blur.

Kind Regards,

Pete

Thanks for taking you time i will be waiting for you

Hi @Yuri_Alves_Bordin ,

Just a quick comment, why did you not want to use PSRAM? I have managed a quick test like this and it appears to work on my board…

void test( lv_obj_t *parent ) {

	lv_obj_t *ta;

	ta = lv_textarea_create( parent );
	lv_textarea_set_text( ta, "Some Test in a box!");
    lv_obj_set_size(ta, 200, 150);
    lv_obj_align(ta, LV_ALIGN_TOP_LEFT, 30, 30);

    lv_obj_t *canvas = lv_canvas_create(parent);
    lv_color_t *canvas_buf = heap_caps_malloc( LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA( EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES ), MALLOC_CAP_SPIRAM);
    memset( canvas_buf, 0xFF, LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA( EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES ) );
    lv_canvas_set_buffer(canvas, canvas_buf, EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES, LV_IMG_CF_TRUE_COLOR_ALPHA);

    blur_obj( ta, canvas, 10 );
}

ESP_IDF psram settings:

Kind Regards,

Pete

@pete-pjb That solution actually worked its just a little slow drawing but worked thanks for taking your time

@Yuri_Alves_Bordin ,

I’ll keep thinking about it if I come up with anything better I’ll let you know.

Kind Regards,

Pete

1 Like

this code is taking a picture of the whole screen and storing in the canvas? is that right?

void blur_obj( lv_obj_t *obj, lv_obj_t *canvas, uint16_t blur_radius ) {

lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_img_dsc_t *snapshot = lv_snapshot_take(obj, LV_IMG_CF_TRUE_COLOR_ALPHA);
lv_coord_t coord_ext_size = _lv_obj_get_ext_draw_size(obj);
lv_canvas_draw_img(canvas, obj->coords.x1 - coord_ext_size, obj->coords.y1 - coord_ext_size, snapshot, &img_dsc);
lv_area_t area = { obj->coords.x1 - coord_ext_size, obj->coords.y1 - coord_ext_size, obj->coords.x2 + coord_ext_size, obj->coords.y2 + coord_ext_size};

lv_canvas_blur_hor(canvas, &area, blur_radius);
lv_canvas_blur_ver(canvas, &area, blur_radius);

}

Hi @Yuri_Alves_Bordin ,

It actually just takes a picture of the lv_obj_t passed to the function, which in the test is just the text area, it blurs it, then puts it on the canvas at the same coordinates as the original object.

Kind Regards,

Pete

1 Like

I have been thinking if would be possible to use a very smaller buffer like 100 x 100 and then create the blurred image on the canvas step by step

takes a snapshot of the very top of the area that will be blurred blurs it in the canvas now empty the buffer and the snapshot to take a new one of 100 pixels for example under the first snapshot and keep this cycle until it had passed though all the canvas

just for documenting i have made this blur function that gives a better appearance result than the actual LVGL but also have a strange bug that i cant use a radius value bigger than 5

void lv_canvas_box_blur(lv_obj_t *obj, const lv_area_t *area, uint16_t r) {
    LV_ASSERT_OBJ(obj, MY_CLASS);

    if (r == 0) return;

    lv_canvas_t *canvas = (lv_canvas_t *)obj;

    lv_area_t a;
    if (area) {
        lv_area_copy(&a, area);
        if (a.x1 < 0) a.x1 = 0;
        if (a.y1 < 0) a.y1 = 0;
        if (a.x2 > canvas->dsc.header.w - 1) a.x2 = canvas->dsc.header.w - 1;
        if (a.y2 > canvas->dsc.header.h - 1) a.y2 = canvas->dsc.header.h - 1;
    } else {
        a.x1 = 0;
        a.y1 = 0;
        a.x2 = canvas->dsc.header.w - 1;
        a.y2 = canvas->dsc.header.h - 1;
    }

    lv_color_t color = lv_obj_get_style_img_recolor(obj, LV_PART_MAIN);

    bool has_alpha = lv_img_cf_has_alpha(canvas->dsc.header.cf);

    lv_coord_t x, y;

    // Horizontal blur
    for (y = a.y1; y <= a.y2; y++) {
        for (x = a.x1; x <= a.x2; x++) {
            uint32_t r_sum = 0;
            uint32_t g_sum = 0;
            uint32_t b_sum = 0;
            uint32_t a_sum = 0;
            uint32_t pixel_count = 0;

            for (lv_coord_t i = x - r; i <= x + r; i++) {
                if (i >= a.x1 && i <= a.x2) {
                    lv_color_t c = lv_img_buf_get_px_color(&canvas->dsc, i, y, color);
                    if (has_alpha) {
                        lv_opa_t opa = lv_img_buf_get_px_alpha(&canvas->dsc, i, y);
                        a_sum += opa;
                    }
                    r_sum += c.ch.red;
                    g_sum += c.ch.green;
                    b_sum += c.ch.blue;
                    pixel_count++;
                }
            }

            lv_color_t blurred_color;
            blurred_color.ch.red = r_sum / pixel_count;
            blurred_color.ch.green = g_sum / pixel_count;
            blurred_color.ch.blue = b_sum / pixel_count;

            if (has_alpha) {
                lv_opa_t blurred_alpha = a_sum / pixel_count;
                blurred_color.full = (blurred_alpha << 24) | (blurred_color.full & 0x00FFFFFF);
            }

            lv_img_buf_set_px_color(&canvas->dsc, x, y, blurred_color);
        }
    }

    // Vertical blur
    for (x = a.x1; x <= a.x2; x++) {
        for (y = a.y1; y <= a.y2; y++) {
            uint32_t r_sum = 0;
            uint32_t g_sum = 0;
            uint32_t b_sum = 0;
            uint32_t a_sum = 0;
            uint32_t pixel_count = 0;

            for (lv_coord_t j = y - r; j <= y + r; j++) {
                if (j >= a.y1 && j <= a.y2) {
                    lv_color_t c = lv_img_buf_get_px_color(&canvas->dsc, x, j, color);
                    if (has_alpha) {
                        lv_opa_t opa = lv_img_buf_get_px_alpha(&canvas->dsc, x, j);
                        a_sum += opa;
                    }
                    r_sum += c.ch.red;
                    g_sum += c.ch.green;
                    b_sum += c.ch.blue;
                    pixel_count++;
                }
            }

            lv_color_t blurred_color;
            blurred_color.ch.red = r_sum / pixel_count;
            blurred_color.ch.green = g_sum / pixel_count;
            blurred_color.ch.blue = b_sum / pixel_count;

            if (has_alpha) {
                lv_opa_t blurred_alpha = a_sum / pixel_count;
                blurred_color.full = (blurred_alpha << 24) | (blurred_color.full & 0x00FFFFFF);
            }

            lv_img_buf_set_px_color(&canvas->dsc, x, y, blurred_color);
        }
    }

    lv_obj_invalidate(obj);
}

Optimized the code


void lv_canvas_box_blur(lv_obj_t *obj, const lv_area_t *area, uint16_t r) {
    LV_ASSERT_OBJ(obj, MY_CLASS);

    if (r == 0) return;

    lv_canvas_t *canvas = (lv_canvas_t *)obj;

    lv_area_t a;
    if (area) {
        lv_area_copy(&a, area);
        if (a.x1 < 0) a.x1 = 0;
        if (a.y1 < 0) a.y1 = 0;
        if (a.x2 > canvas->dsc.header.w - 1) a.x2 = canvas->dsc.header.w - 1;
        if (a.y2 > canvas->dsc.header.h - 1) a.y2 = canvas->dsc.header.h - 1;
    } else {
        a.x1 = 0;
        a.y1 = 0;
        a.x2 = canvas->dsc.header.w - 1;
        a.y2 = canvas->dsc.header.h - 1;
    }

    lv_color_t color = lv_obj_get_style_img_recolor(obj, LV_PART_MAIN);

    bool has_alpha = lv_img_cf_has_alpha(canvas->dsc.header.cf);

    lv_coord_t x, y;

    // Horizontal blur
    for (y = a.y1; y <= a.y2; y++) {
        uint32_t r_sum = 0;
        uint32_t g_sum = 0;
        uint32_t b_sum = 0;
        uint32_t a_sum = 0;
        uint32_t pixel_count = 0;

        for (x = a.x1 - r; x <= a.x2 + r; x++) {
            if (x >= a.x1 && x <= a.x2) {
                lv_color_t c = lv_img_buf_get_px_color(&canvas->dsc, x, y, color);
                if (has_alpha) {
                    lv_opa_t opa = lv_img_buf_get_px_alpha(&canvas->dsc, x, y);
                    a_sum += opa;
                }
                r_sum += c.ch.red;
                g_sum += c.ch.green;
                b_sum += c.ch.blue;
                pixel_count++;
            }
            if (x >= a.x1 + r) {
                // Apply the blur color to the pixel on the left edge of the area
                lv_color_t blurred_color;
                blurred_color.ch.red = r_sum / pixel_count;
                blurred_color.ch.green = g_sum / pixel_count;
                blurred_color.ch.blue = b_sum / pixel_count;

                if (has_alpha) {
                    lv_opa_t blurred_alpha = a_sum / pixel_count;
                    blurred_color.full = (blurred_alpha << 24) | (blurred_color.full & 0x00FFFFFF);
                }

                lv_img_buf_set_px_color(&canvas->dsc, x - r, y, blurred_color);

                // Remove the pixel contribution on the left edge from the sums
                lv_color_t c = lv_img_buf_get_px_color(&canvas->dsc, x - r, y, color);
                if (has_alpha) {
                    lv_opa_t opa = lv_img_buf_get_px_alpha(&canvas->dsc, x - r, y);
                    a_sum -= opa;
                }
                r_sum -= c.ch.red;
                g_sum -= c.ch.green;
                b_sum -= c.ch.blue;
                pixel_count--;
            }
        }
    }

    // Vertical blur
    for (x = a.x1; x <= a.x2; x++) {
        uint32_t r_sum = 0;
        uint32_t g_sum = 0;
        uint32_t b_sum = 0;
        uint32_t a_sum = 0;
        uint32_t pixel_count = 0;

        for (y = a.y1 - r; y <= a.y2 + r; y++) {
            if (y >= a.y1 && y <= a.y2) {
                lv_color_t c = lv_img_buf_get_px_color(&canvas->dsc, x, y, color);
                if (has_alpha) {
                    lv_opa_t opa = lv_img_buf_get_px_alpha(&canvas->dsc, x, y);
                    a_sum += opa;
                }
                r_sum += c.ch.red;
                g_sum += c.ch.green;
                b_sum += c.ch.blue;
                pixel_count++;
            }
            if (y >= a.y1 + r) {
                // Apply the blur color to the pixel on the top edge of the area
                lv_color_t blurred_color;
                blurred_color.ch.red = r_sum / pixel_count;
                blurred_color.ch.green = g_sum / pixel_count;
                blurred_color.ch.blue = b_sum / pixel_count;

                if (has_alpha) {
                    lv_opa_t blurred_alpha = a_sum / pixel_count;
                    blurred_color.full = (blurred_alpha << 24) | (blurred_color.full & 0x00FFFFFF);
                }

                lv_img_buf_set_px_color(&canvas->dsc, x, y - r, blurred_color);

                // Remove the pixel contribution on the top edge from the sums
                lv_color_t c = lv_img_buf_get_px_color(&canvas->dsc, x, y - r, color);
                if (has_alpha) {
                    lv_opa_t opa = lv_img_buf_get_px_alpha(&canvas->dsc, x, y - r);
                    a_sum -= opa;
                }
                r_sum -= c.ch.red;
                g_sum -= c.ch.green;
                b_sum -= c.ch.blue;
                pixel_count--;
            }
        }
    }

    lv_obj_invalidate(obj);
}



2 Likes