Scrolling waterfall in canvas

I am migrating from v8.4 to v9.3 in v8.4 I can scroll the canvas by using lv_canvas_transform.
So the source and destination is the same canvas. I use an extra buffer to copy the previous canvas minus 1 line and copy it using lv_canvas_transform to the new position. This works quite well and fast.
However in v9 there is no lv_canvas_transform, the examples show the use of a 2nd canvas and an image object. However when I try this it is very slow and does not give the same result.

Is there a more easy and performing way to copy to and from a canvas in v9.3?

		memcpy(buf.data(), cbuf.data() + LV_CANVAS_BUF_SIZE_TRUE_COLOR(width, 1), LV_CANVAS_BUF_SIZE_TRUE_COLOR(width, height - 1));
		img.data = (uint8_t *)buf.data();
		img.header.cf = LV_IMG_CF_TRUE_COLOR;
		img.header.w = width;
		img.header.h = height - 1;
		lv_canvas_transform(canvas, &img, 0, LV_IMG_ZOOM_NONE, 0, 0, width, height - 1, true);

In v9.3 I try the following but does not look ok to me. There should an easy way to copy to and from the canvas

	lv_obj_t *canvas_temp_buffer = lv_canvas_create(obj_parent);
	lv_canvas_set_buffer(canvas_temp_buffer, temp_buffer.data(), width, height - 1, LV_COLOR_FORMAT_NATIVE);
	//lv_canvas_fill_bg(canvas_temp_buffer, lv_color_black(), LV_OPA_COVER);
	lv_obj_set_pos(canvas_temp_buffer, _x, _y + 1);
	
	 lv_layer_t layer;
	lv_canvas_init_layer(canvas_temp_buffer, &layer);

	lv_image_dsc_t img;
	lv_draw_buf_to_image(lv_canvas_get_draw_buf(canvas_temp_buffer), &img);
	lv_draw_image_dsc_t img_dsc;
	lv_draw_image_dsc_init(&img_dsc);
	img_dsc.src = &img;

	lv_area_t coords_img = {0, 1, width, height - 1};
	lv_draw_image(&layer, &img_dsc, &coords_img);

Environment

raspberry pi linux
lvgl v9.3

I found a solution don’t know if this works for all types of devices but works for me copied parts form lv_draw_buf_copy

void Waterfall::Draw()
{
	std::unique_lock<std::mutex> lock(mutexSingleEntry);
	
	waterfallfloor = 10;
	lv_layer_t layer;

	lv_draw_buf_t *buf_ptr = lv_canvas_get_draw_buf(canvas);
	uint32_t line_width = buf_ptr->header.w;
	uint32_t dest_stride = buf_ptr->header.stride;
	uint32_t src_stride = buf_ptr->header.stride;
	uint32_t line_bytes = (line_width * lv_color_format_get_bpp((lv_color_format_t)buf_ptr->header.cf) + 7) >> 3;

	int32_t start_y, end_y;
	start_y = 0;
	end_y = buf_ptr->header.h - 1;
	uint8_t *src_bufc = (uint8_t *)lv_draw_buf_goto_xy(buf_ptr, 0, 0);
	uint8_t *dest_bufc = (uint8_t *)temp_buffer.data();

	for (; start_y < end_y; start_y++)
	{
		lv_memcpy(dest_bufc, src_bufc, line_bytes);
		dest_bufc += dest_stride;
		src_bufc += src_stride;
	}

	src_bufc = (uint8_t *)temp_buffer.data();
	dest_bufc = (uint8_t *)lv_draw_buf_goto_xy(buf_ptr, 0, 1);
	start_y = 1;
	end_y = buf_ptr->header.h - 1;

	for (; start_y < end_y; start_y++)
	{
		lv_memcpy(dest_bufc, src_bufc, line_bytes);
		dest_bufc += dest_stride;
		src_bufc += src_stride;
	}
	for (lv_coord_t i = 0; i < width; i++)
	{
		lv_color_t c = heatmap(waterfallfloor + 20.0 * log10(lv_rand(1, 10)), min, max);
		if (waterfallflow)
			lv_canvas_set_px(canvas, i, height - 1, c, LV_OPA_COVER);
		else
			lv_canvas_set_px(canvas, i, 0, c, LV_OPA_COVER);
	}
}

The only problem remaining is that lv_canvas_fill_bg seems to write to dual monitors, not the correct screen only.

Dual monitor issue is also resolved I was using LV_COLOR_FORMAT_NATIVE but when the displays have different colordepts LV_COLOR_FORMAT_NATIVE does not switch between the displays but is fixed to LV_COLOR_DEPTH so fixed.