Skip to content

Commit

Permalink
feat(sw): Add method to convert a htiled I1 buffer to vtiled (lvgl#7129)
Browse files Browse the repository at this point in the history
  • Loading branch information
faxe1008 authored Oct 30, 2024
1 parent 3f3de6a commit be85ad7
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 0 deletions.
25 changes: 25 additions & 0 deletions docs/details/main-components/display.rst
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,31 @@ meaning ``(horizontal_resolution x vertical_resolution / 8) + 8`` bytes.
As LVGL can not handle fractional width make sure to round the horizontal resolution
to 8 bits (for example 90 to 96).

The :cpp:func:`lv_draw_sw_i1_convert_to_vtiled` function is used to convert a draw buffer in I1 color format from a row-wise (htiled) to a column-wise (vtiled) buffer layout.
This conversion is necessary for certain display controllers that require a different draw buffer mapping. The function assumes that the buffer width and height are rounded to a multiple of 8.
The bit order of the resulting vtiled buffer can be specified using the `bit_order_lsb` parameter.
For more details, refer to the implementation in :cpp:func:`lv_draw_sw_i1_convert_to_vtiled` in :file:`src/draw/sw/lv_draw_sw.c`.

To ensure that the redrawn areas start and end on byte boundaries, you can add a rounder callback to your display driver. This callback will round the width and height to the nearest multiple of 8.

Here is an example of how to implement and set a rounder callback:

.. code:: c
static void my_rounder_cb(lv_event_t *e)
{
lv_area_t *area = lv_event_get_param(e);
/* Round the height to the nearest multiple of 8 */
area->y1 = (area->y1 & ~0x7);
area->y2 = (area->y2 | 0x7);
}
lv_display_add_event_cb(display, my_rounder_cb, LV_EVENT_INVALIDATE_AREA, display);
In this example, the `my_rounder_cb` function rounds the coordinates of the redrawn area to the nearest multiple of 8.
The `x1` and `y1` coordinates are rounded down, while the `x2` and `y2` coordinates are rounded up. This ensures that the
width and height of the redrawn area are always multiples of 8.

Constraints on Redrawn Area
---------------------------
Expand Down
29 changes: 29 additions & 0 deletions src/draw/sw/lv_draw_sw.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,35 @@ void lv_draw_sw_i1_invert(void * buf, uint32_t buf_size)
}
}

void lv_draw_sw_i1_convert_to_vtiled(const void * buf, uint32_t buf_size, uint32_t width, uint32_t height,
void * out_buf,
uint32_t out_buf_size, bool bit_order_lsb)
{
LV_ASSERT(buf && out_buf);
LV_ASSERT(width % 8 == 0 && height % 8 == 0);
LV_ASSERT(buf_size == (width / 8) * height);
LV_ASSERT(out_buf_size >= buf_size);

lv_memset(out_buf, 0, out_buf_size);

const uint8_t * src_buf = (uint8_t *)buf;
uint8_t * dst_buf = (uint8_t *)out_buf;

for(uint32_t y = 0; y < height; y++) {
for(uint32_t x = 0; x < width; x++) {
uint32_t src_index = y * width + x;
uint32_t dst_index = x * height + y;
uint8_t bit = (src_buf[src_index / 8] >> (7 - (src_index % 8))) & 0x01;
if(bit_order_lsb) {
dst_buf[dst_index / 8] |= (bit << (dst_index % 8));
}
else {
dst_buf[dst_index / 8] |= (bit << (7 - (dst_index % 8)));
}
}
}
}

void lv_draw_sw_rotate(const void * src, void * dest, int32_t src_width, int32_t src_height, int32_t src_stride,
int32_t dest_stride, lv_display_rotation_t rotation, lv_color_format_t color_format)
{
Expand Down
17 changes: 17 additions & 0 deletions src/draw/sw/lv_draw_sw.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,23 @@ void lv_draw_sw_rgb565_swap(void * buf, uint32_t buf_size_px);
*/
void lv_draw_sw_i1_invert(void * buf, uint32_t buf_size);


/**
* Convert a draw buffer in I1 color format from htiled (row-wise)
* to vtiled (column-wise) buffer layout. The conversion assumes that the buffer width
* and height is rounded to a multiple of 8.
* @param buf pointer to the buffer to be converted
* @param buf_size size of the buffer in bytes
* @param width width of the buffer
* @param height height of the buffer
* @param out_buf pointer to the output buffer
* @param out_buf_size size of the output buffer in bytes
* @param bit_order_lsb bit order of the resulting vtiled buffer
*/
void lv_draw_sw_i1_convert_to_vtiled(const void * buf, uint32_t buf_size, uint32_t width, uint32_t height,
void * out_buf,
uint32_t out_buf_size, bool bit_order_lsb);

/**
* Rotate a buffer into another buffer
* @param src the source buffer
Expand Down
129 changes: 129 additions & 0 deletions tests/src/test_cases/draw/test_draw_sw_post_process.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,4 +315,133 @@ void test_invert(void)
TEST_ASSERT_EQUAL_UINT8_ARRAY(&expected_buf[3], &buf3[3], 2);
}

void test_vtile_small(void)
{
uint8_t src_buf[8] = {0x3C, 0x42, 0x81, 0xA5, 0x81, 0x81, 0x42, 0x3C};
uint8_t dst_buf[8] = {0};

uint8_t expected_buf_msb[8] = {0x3C, 0x42, 0x91, 0x81, 0x81, 0x91, 0x42, 0x3C};
uint8_t expected_buf_lsb[8] = {0x3C, 0x42, 0x89, 0x81, 0x81, 0x89, 0x42, 0x3C};

lv_draw_sw_i1_convert_to_vtiled(src_buf, 8, 8, 8, dst_buf, 8, false);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buf_msb, dst_buf, 8);

lv_draw_sw_i1_convert_to_vtiled(src_buf, 8, 8, 8, dst_buf, 8, true);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buf_lsb, dst_buf, 8);
}

void test_vtile_rectangular(void)
{
uint8_t src_buf[80] = {
0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x80, 0x83, 0xE1, 0x00,
0x40, 0x80, 0x84, 0x01, 0x00,
0x40, 0x41, 0x08, 0x01, 0x00,
0x40, 0x41, 0x10, 0x01, 0x00,
0x40, 0x41, 0x20, 0x01, 0x00,
0x40, 0x41, 0x20, 0x01, 0x00,
0x40, 0x41, 0x20, 0x01, 0x00,
0x40, 0x41, 0x20, 0x01, 0x00,
0x40, 0x23, 0x20, 0x01, 0x00,
0x40, 0x22, 0x20, 0xF1, 0x00,
0x40, 0x14, 0x20, 0x11, 0x00,
0x40, 0x14, 0x10, 0x11, 0x00,
0x40, 0x08, 0x08, 0x11, 0x00,
0x7E, 0x08, 0x07, 0xF1, 0xF8,
0x00, 0x00, 0x00, 0x00, 0x00,
};
uint8_t dst_buf[80] = {0};

uint8_t expected_buf_msb[80] = {
0x00, 0x00,
0x7F, 0xFE,
0x00, 0x02,
0x00, 0x02,
0x00, 0x02,
0x00, 0x02,
0x00, 0x02,
0x00, 0x00,
0x60, 0x00,
0x1F, 0x80,
0x00, 0x60,
0x00, 0x18,
0x00, 0x06,
0x00, 0x18,
0x00, 0x60,
0x3F, 0x80,
0xC0, 0x00,
0x00, 0x00,
0x07, 0xF0,
0x08, 0x08,
0x10, 0x04,
0x20, 0x02,
0x80, 0x02,
0x80, 0x02,
0x80, 0x22,
0x80, 0x22,
0x80, 0x22,
0x00, 0x3E,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0xFF, 0xFE,
0x00, 0x02,
0x00, 0x02,
0x00, 0x02,
0x00, 0x02,
0x00, 0x02,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
};
uint8_t expected_buf_lsb[80] = {
0x00, 0x00,
0xFE, 0x7F,
0x00, 0x40,
0x00, 0x40,
0x00, 0x40,
0x00, 0x40,
0x00, 0x40,
0x00, 0x00,
0x06, 0x00,
0xF8, 0x01,
0x00, 0x06,
0x00, 0x18,
0x00, 0x60,
0x00, 0x18,
0x00, 0x06,
0xF8, 0x03,
0x06, 0x00,
0x00, 0x00,
0xE0, 0x0F,
0x10, 0x10,
0x08, 0x20,
0x04, 0x40,
0x02, 0x40,
0x02, 0x40,
0x02, 0x44,
0x02, 0x44,
0x02, 0x44,
0x00, 0x7C,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0xFE, 0x7F,
0x00, 0x40,
0x00, 0x40,
0x00, 0x40,
0x00, 0x40,
0x00, 0x40,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
};

lv_draw_sw_i1_convert_to_vtiled(src_buf, 80, 40, 16, dst_buf, 80, false);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buf_msb, dst_buf, 8);

lv_draw_sw_i1_convert_to_vtiled(src_buf, 80, 40, 16, dst_buf, 80, true);
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buf_lsb, dst_buf, 8);
}

#endif

0 comments on commit be85ad7

Please sign in to comment.