Selectively mask a rounded rectangle, where the mask can change during runtime

I am using lv_port_linux with version 9.

I am making a “background indicator” for a progress bar. The idea is that there is a second indicator that goes between the actual indicator of the bar and an arbrtrary set point. I am implementing this by creating a canvas, making a layer from that canvas, drawing a rectangle to it, and then masking the canvas layer and finalizing the layer.


static lv_obj_t *rectangle_canvas;
LV_DRAW_BUF_DEFINE(draw_buf_rectangle, BAR_RECT_WIDTH, BAR_RECT_HEIGHT, LV_COLOR_FORMAT_ARGB8888); /* necessary to have alpha channel in draw buffer for masking */
lv_draw_rect_dsc_t rectangle_desc;
lv_layer_t rectangle_layer;
lv_draw_mask_rect_dsc_t mask_rect_desc;

// variables are statically defined at top of source file.


pr_dbg("creating canvas");
    rectangle_canvas = lv_canvas_create(charging_scr); 
    if(!rectangle_canvas) {
        pr_err("failed to create rectangle canvas");
        return -errno;
    }
    pr_dbg("setting draw buf");
    lv_canvas_set_draw_buf(rectangle_canvas, &draw_buf_rectangle);
    
    lv_draw_rect_dsc_init(&rectangle_desc);
    rectangle_desc.radius = 15;
    rectangle_desc.bg_opa = LV_OPA_COVER;
    rectangle_desc.bg_color = lv_color_hex(0x676767);
    rectangle_desc.bg_grad.stops_count = 0;
    /* everything else is zeroed */

    lv_obj_set_pos(rectangle_canvas, LEFT_BAR_MARGIN, TOP_BAR_MARGIN); /* same as rectangle, may need to adjust because of padding & such */
    lv_canvas_fill_bg(rectangle_canvas, lv_color_hex(HEX_BLACK), LV_OPA_COVER);
    pr_dbg("init layer");
    lv_canvas_init_layer(rectangle_canvas, &rectangle_layer);
    lv_area_t rect_area = {0, 0, BAR_RECT_WIDTH, BAR_RECT_HEIGHT};
    lv_draw_rect(&rectangle_layer, &rectangle_desc, &rect_area);

    lv_draw_mask_rect_dsc_init(&mask_rect_desc);
    mask_rect_desc.radius = 0;
    /* TODO: set area and maybe draw descriptor */
    mask_rect_desc.area.x1 = 10;
    mask_rect_desc.area.x2 = 30;
    mask_rect_desc.area.y1 = 0;
    mask_rect_desc.area.y2 = BAR_RECT_HEIGHT;
    pr_dbg("masking");
    lv_draw_mask_rect(&rectangle_layer, &mask_rect_desc);
    pr_dbg("finishing layer");
    lv_canvas_finish_layer(rectangle_canvas, &rectangle_layer);

it is currently segmentation faulting after calling lv_canvas_finish_layer, but it has been hard to track down exactly where, it may be in an asynchronous or scheduled function. Thank you for any guidance.

Update: I have no clue how to use the lv_draw_mask_rect function. I can’t get it to stop seg faulting when I call finish layer nor can I track down exactly where it is seg faulting. The easier way to accomplish this in my opinion is this.

static lv_obj_t *bg_rectangle;
static uint8_t bitmask_data[BAR_RECT_WIDTH * BAR_RECT_HEIGHT * 1];
static lv_image_dsc_t bitmask = {
    .header.w = BAR_RECT_WIDTH,
    .header.h = BAR_RECT_HEIGHT,
    .header.cf = LV_COLOR_FORMAT_A8,
    .data_size = BAR_RECT_WIDTH * BAR_RECT_HEIGHT * 1,
    .data = bitmask_data,
}; 
 bg_rectangle = lv_obj_create(charging_scr);
 if (!bg_rectangle) {
      pr_err("failed to create set rectangle");
      return -1;
  }
  if(lv_obj_set_style_radius(bg_rectangle, 15, LV_PART_MAIN) < 0) {
      pr_err("failed to set set rectangle radius");
      return -1;
  }
  if(lv_obj_set_size(bg_rectangle, BAR_RECT_WIDTH, BAR_RECT_HEIGHT) < 0) {
      pr_err("failed to set set rectangle size");
      return -1;
  }
  if(lv_obj_set_pos(bg_rectangle, LEFT_BAR_MARGIN, TOP_BAR_MARGIN) < 0) {
      pr_err("failed to set set rectangle position");
      return -1;
  }
  if(lv_obj_set_style_bg_color(bg_rectangle, lv_color_hex(0x676767), LV_PART_MAIN) < 0) {
      pr_err("failed to set set rectangle color");
      return -1;
  }
  set_pixels(bitmask_data, 0, BAR_RECT_WIDTH, LV_OPA_COVER);
  bg_rect_left = 0;
  bg_rect_right = BAR_RECT_WIDTH;
  if(lv_obj_set_style_bitmap_mask_src(bg_rectangle, &bitmask, LV_PART_MAIN) < 0) {
      pr_err("failed to set bitmap");
      return -1;
  }
void set_pixels(uint8_t *bitmap, int start_x, int stop_x, uint8_t val) {
    for(int i = start_x; i < BAR_RECT_HEIGHT*BAR_RECT_WIDTH; i += BAR_RECT_WIDTH) {
        memset(&bitmap[i], val, stop_x-start_x);
    }
}

Hope this helps the next aspirational v9 masker.

I have found a separate issue with applying this.

As seen in the image above, the rectangle is successfully masked to create a kind of second indicator up to a set point, however, there is a slight discoloration on the top and bottom of the rectangle. The alpha image is set to all LV_OPA_COVER, and the top and bottom are clearly shown (hence the white border). I have tried removing shadow opacity but this did nothing. Are there any other style properties that could cause this?

This happens as well when the cap is at 100, showing the rounded part.

Update to my forum monologue about this issue: The discoloration only happens on the rows where the radius is applied, and doesn’t happen at all if the radius is set to 0. The reason for this is highly unclear, as the mask is clearly applied to the whole widget.

If someone with more knowledge of the drawing backend could provide some insight and a potential work around that would be very much appreciated.

A potential alternative if this problem can’t be resolved would be to draw the rectangle to a canvas and then go through the draw buffer manually to change opacity values.

I have found a solution to finish this soliloquy:

static lv_obj_t *rectangle_canvas;
LV_DRAW_BUF_DEFINE(draw_buf_rectangle, BAR_RECT_WIDTH, BAR_RECT_HEIGHT, LV_COLOR_FORMAT_ARGB8888); /* necessary to have alpha channel in draw buffer for masking */
lv_draw_rect_dsc_t rectangle_desc;
pr_dbg("creating canvas");
    rectangle_canvas = lv_canvas_create(charging_scr); 
    if(!rectangle_canvas) {
        pr_err("failed to create rectangle canvas");
        return -errno;
    }
    pr_dbg("setting draw buf");
    lv_canvas_set_draw_buf(rectangle_canvas, &draw_buf_rectangle);
    if(lv_obj_set_pos(rectangle_canvas, LEFT_BAR_MARGIN, TOP_BAR_MARGIN) < 0) {
        pr_err("failed to set set rectangle position");
        return -1;
    }
    
    lv_draw_rect_dsc_init(&rectangle_desc);
    rectangle_desc.radius = 15;
    rectangle_desc.bg_opa = LV_OPA_COVER;
    rectangle_desc.bg_color = lv_color_hex(0x676767);
    rectangle_desc.bg_grad.stops_count = 0;
    /* everything else is zeroed */
    
    
    lv_canvas_fill_bg(rectangle_canvas, lv_color_hex(HEX_BLACK), 0);
    pr_dbg("init layer");
    lv_canvas_init_layer(rectangle_canvas, &rectangle_layer);
    lv_area_t rect_area = {0, 0, BAR_RECT_WIDTH, BAR_RECT_HEIGHT};
    lv_draw_rect(&rectangle_layer, &rectangle_desc, &rect_area);
    lv_canvas_finish_layer(rectangle_canvas, &rectangle_layer);

    int new_left = (int)((charging_level/100.0 * BAR_RECT_WIDTH) + .5);
    if (new_left > bg_rect_left) 
        set_pixel_alpha(bg_rect_left, new_left, 0);
    else 
        set_pixel_alpha(new_left, bg_rect_left, LV_OPA_COVER);
    bg_rect_left = new_left;
    lv_canvas_set_draw_buf(rectangle_canvas, &draw_buf_rectangle);
    lv_obj_invalidate(rectangle_canvas);
    int new_right = (int)((charging_cap/100.0 * BAR_RECT_WIDTH) + .5);
    if(new_right > bg_rect_right)
        set_pixel_alpha(bg_rect_right, new_right, LV_OPA_COVER);
    else   
        set_pixel_alpha(new_right, bg_rect_right, 0);
    bg_rect_right = new_right;
    lv_canvas_set_draw_buf(rectangle_canvas, &draw_buf_rectangle);
    lv_obj_invalidate(rectangle_canvas);
void set_pixel_alpha(int start_x, int stop_x, uint8_t val) {
    pr_dbg("writing val %d to start %x to stop %x", val, start_x, stop_x);
    for (int i = 0; i < BAR_RECT_HEIGHT; i++) {
        for(int j = start_x; j < stop_x; j++) {
            lv_color32_t * px = lv_draw_buf_goto_xy(&draw_buf_rectangle, j, i);
            px->alpha = val;
        }
    }
}

All relevant portions of code included. The idea is to draw the rectangle once and then only modify the alpha values in the draw buffer. This removes the color problem mentioned above that seems to come from drawing a bitmask onto a widget.

Hopefully this is a useful example of masking, which I’ve been struggling with. If anyone finds any mistakes, please leave a note for the record.