SVG image decoder

Description

What LVGL version are you using?

v9.0

What do you want to achieve?

I’d like to display an image in SVG format.

What have you tried so far?

I’ve noticed that recently ThorVG has been integrated in LVGL, but I can’t understand if it’s just to programmatically draw vector graphics or if it’s possible to also render an SVG image. The corresponding image decoder seems to be missing for now.

any update on this? I also want to do the same render the SVG file using LVGL with help of ThorVG, however not getting any lead

I haven’t really understood whether something within LVGL or ThorVG allow to display an entire SVG file; right now I’m falling back to parsing the SVG and displaying it piece by piece thanks to LVGL’s vector graphics API (which in turn relies on ThorVG).

Hello Mattia,
I am also looking into rendering svg images in LVGL and thorvg. can you kindly give a lead on how you parsed the .svg file? is there any LVGL example that i can use to follow up on this?

Regards,
djgj

In the end SVG is just XML, so I picked an XML parser (my choice was xml.c) and then used the result to draw elements with LVGL’s vector graphics API. This is a crude example:

    struct xml_document *document = xml_parse_document((uint8_t *)source, strlen(source));

    if (!document) {
        printf("Could parse document\n");
        exit(EXIT_FAILURE);
    }
    struct xml_node *root = xml_document_root(document);


    // lv_demo_vector_graphic();

    lv_draw_buf_t *draw_buf = lv_draw_buf_create(480, 320, LV_COLOR_FORMAT_ARGB8888, LV_STRIDE_AUTO);
    lv_obj_t      *canvas   = lv_canvas_create(lv_scr_act());
    lv_canvas_set_draw_buf(canvas, draw_buf);

    lv_layer_t layer;
    lv_canvas_init_layer(canvas, &layer);

    // Draw vector
    lv_vector_dsc_t  *ctx  = lv_vector_dsc_create(&layer);
    lv_vector_path_t *path = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM);


    lv_area_t rect = {0, 0, 480, 320};
    lv_vector_dsc_set_fill_color(ctx, lv_color_white());
    lv_vector_clear_area(ctx, &rect);

       for (size_t i = 0; i < xml_node_children(svg); i++) {
        struct xml_node   *xml_child = xml_node_child(svg, i);
        struct xml_string *xml_name  = xml_node_name(xml_child);

        char *name_str = copy_string(xml_name);

        printf("%s\n", name_str);
        if (strcmp(name_str, "rect") == 0) {
            printf("Found rect\n");
            display_rect(ctx, path, xml_child);
        }

        free(name_str);
    }

Where display_rect is:

static int display_rect(lv_vector_dsc_t *ctx, lv_vector_path_t *path, struct xml_node *node) {
    struct {
        uint8_t x : 1;
        uint8_t y : 1;
        uint8_t width : 1;
        uint8_t height : 1;
        uint8_t fill : 1;
    } arguments = {0};

    uint8_t tmp = 0;

    int32_t  x      = 0;
    int32_t  y      = 0;
    int32_t  width  = 0;
    int32_t  height = 0;
    uint32_t fill   = 0;
    int32_t  rx     = 0;
    int32_t  ry     = 0;

    for (size_t i = 0; i < xml_node_attributes(node); i++) {
        struct xml_string *xml_attribute_content = xml_node_attribute_content(node, i);
        struct xml_string *xml_attribute_name    = xml_node_attribute_name(node, i);

        char *attribute_name    = copy_string(xml_attribute_name);
        char *attribute_content = copy_string(xml_attribute_content);

        if (strcmp(attribute_name, "x") == 0) {
            arguments.x = 1;
            x           = atoi(attribute_content);
        } else if (strcmp(attribute_name, "y") == 0) {
            arguments.y = 1;
            y           = atoi(attribute_content);
        } else if (strcmp(attribute_name, "rx") == 0) {
            rx = atoi(attribute_content);
        } else if (strcmp(attribute_name, "ry") == 0) {
            ry = atoi(attribute_content);
        } else if (strcmp(attribute_name, "width") == 0) {
            arguments.width = 1;
            width           = atoi(attribute_content);
        } else if (strcmp(attribute_name, "height") == 0) {
            arguments.height = 1;
            height           = atoi(attribute_content);
        } else if (strcmp(attribute_name, "fill") == 0) {
            if (attribute_content[0] == '#') {
                fill = strtol(&attribute_content[1], NULL, 16);
            } else if (strcmp(attribute_content, "white")) {
                tmp = 1;
            } else if (strcmp(attribute_content, "black")) {
                tmp  = 1;
                fill = 0xFFFFFF;
            }
            arguments.fill = 1;
        } else if (strcmp(attribute_name, "stroke") == 0) {
        } else if (strcmp(attribute_name, "stroke-width") == 0) {
        }

        free(attribute_name);
        free(attribute_content);
    }

    if (arguments.x && arguments.y && arguments.width && arguments.height) {
        lv_vector_path_t *path2 = lv_vector_path_create(LV_VECTOR_PATH_QUALITY_MEDIUM);
        lv_vector_dsc_identity(ctx);

        lv_area_t rect = {x, y, x + width, y + height};
        lv_vector_path_append_rect(path2, &rect, rx, ry);
        lv_vector_dsc_set_fill_color(ctx, lv_color_make((fill >> 16) & 0xFF, (fill >> 8) & 0xFF, fill & 0xFF));

        //"<rect x=\"-200\" y=\"0\" width=\"550\" height=\"180\" stroke=\"black\" fill=\"white\" stroke-width=\"0\" "
        //"transform=\"skewX(-50) translate(120 0)\" rx=\"10\" ry=\"10\" />"
        if (tmp) {
            lv_matrix_t matrix;
            lv_matrix_identity(&matrix);
            lv_matrix_skew(&matrix, -50, 0);
            lv_matrix_translate(&matrix, 120, 0);
            lv_vector_dsc_set_transform(ctx, &matrix);
        }

        lv_vector_path_append_path(path, path2);
        lv_vector_dsc_add_path(ctx, path);     // draw a path
                                               //
        if (tmp) {
            lv_matrix_t matrix;
            lv_matrix_identity(&matrix);
            lv_vector_dsc_set_transform(ctx, &matrix);
        }

        printf("Rect %i-%i, %i-%i, 0x%X\n", (int)rect.x1, (int)rect.y1, (int)rect.x2, (int)rect.y2, (int)fill);
        lv_vector_path_delete(path2);
    }

    return 0;
}

This is code for LVGL 9.1. The vector graphics API is still evolving and not properly documented, so I had to learn how it works by trial and error. I hope this helps.

Thank you very much Mattia. This definitely helps.