diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c30b033..184dc807 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ set(app_sources "src/epdiy.c" "src/output_common/line_queue.c" "src/output_common/render_context.c" "src/output_common/render_method.c" + "src/output_common/utils.c" "src/font.c" "src/displays.c" "src/diff.S" diff --git a/examples/fb_mode_test/main/main.c b/examples/fb_mode_test/main/main.c index 567b3c66..5d3a93bc 100644 --- a/examples/fb_mode_test/main/main.c +++ b/examples/fb_mode_test/main/main.c @@ -48,9 +48,9 @@ static inline void checkError(enum EpdDrawError err) { /** * Clears the screen to white and resets the framebuffer. */ -void clear() { +void clear(EpdRect area) { epd_poweron(); - epd_clear(); + epd_clear_area(area); epd_poweroff(); memset(framebuffer, 0xFF, fb_size); } @@ -189,13 +189,17 @@ void app_main() { fb_size = epd_width() * epd_height() / 2; framebuffer = heap_caps_aligned_alloc(16, fb_size, MALLOC_CAP_SPIRAM); - clear(); + clear(epd_full_screen()); test_8ppB(); memset(framebuffer, 0xFF, fb_size); test_2ppB(); + + // clear a small square to test selective clear. + EpdRect area = {.x = 250, .y = 150, .width=50, .height=50}; + clear(area); printf("going to sleep...\n"); epd_deinit(); diff --git a/src/output_common/lut.c b/src/output_common/lut.c index 4ae87233..cad01700 100644 --- a/src/output_common/lut.c +++ b/src/output_common/lut.c @@ -96,33 +96,6 @@ static inline int max(int x, int y) { // status tracker for row skipping uint32_t skipping; -__attribute__((optimize("O3"))) void IRAM_ATTR -reorder_line_buffer(uint32_t* line_data, int buf_len) { - for (uint32_t i = 0; i < buf_len / 4; i++) { - uint32_t val = *line_data; - *(line_data++) = val >> 16 | ((val & 0x0000FFFF) << 16); - } -} - -__attribute__((optimize("O3"))) void IRAM_ATTR -bit_shift_buffer_right(uint8_t* buf, uint32_t len, int shift) { - uint8_t carry = 0xFF << (8 - shift); - for (uint32_t i = 0; i < len; i++) { - uint8_t val = buf[i]; - buf[i] = (val >> shift) | carry; - carry = val << (8 - shift); - } -} - -__attribute__((optimize("O3"))) void IRAM_ATTR -nibble_shift_buffer_right(uint8_t* buf, uint32_t len) { - uint8_t carry = 0xF; - for (uint32_t i = 0; i < len; i++) { - uint8_t val = buf[i]; - buf[i] = (val << 4) | carry; - carry = (val & 0xF0) >> 4; - } -} __attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_8ppB( const uint32_t* line_data, uint8_t* epd_input, const uint8_t* lut, uint32_t epd_width diff --git a/src/output_common/lut.h b/src/output_common/lut.h index 364636e4..c69f7713 100644 --- a/src/output_common/lut.h +++ b/src/output_common/lut.h @@ -31,17 +31,3 @@ typedef struct { */ LutFunctionPair find_lut_functions(enum EpdDrawMode mode, uint32_t lut_size); -/* - * Reorder the output buffer to account for I2S FIFO order. - */ -void reorder_line_buffer(uint32_t* line_data, int buf_len); - -/** - * Apply a mask to a line buffer. - * `len` must be divisible by 4. - */ -void epd_apply_line_mask(uint8_t* buf, const uint8_t* mask, int len); - -// legacy functions -void bit_shift_buffer_right(uint8_t* buf, uint32_t len, int shift); -void nibble_shift_buffer_right(uint8_t* buf, uint32_t len); diff --git a/src/output_i2s/render_i2s.c b/src/output_i2s/render_i2s.c index 08db8106..8bb3b784 100644 --- a/src/output_i2s/render_i2s.c +++ b/src/output_i2s/render_i2s.c @@ -13,6 +13,7 @@ // output a row to the display. #include "../output_common/lut.h" +#include "../output_common/utils.h" #include "../output_common/render_context.h" #include "i2s_data_bus.h" #include "rmt_pulse.h" @@ -183,14 +184,11 @@ void i2s_do_update(RenderContext_t* ctx) { void IRAM_ATTR epd_push_pixels_i2s(RenderContext_t* ctx, EpdRect area, short time, int color) { int line_bytes = ctx->display_width / 4; uint8_t row[line_bytes]; - memset(row, 0, line_bytes); - const uint8_t color_choice[4] = { DARK_BYTE, CLEAR_BYTE, 0x00, 0xFF }; - for (uint32_t i = 0; i < area.width; i++) { - uint32_t position = i + area.x % 4; - uint8_t mask = color_choice[color] & (0b00000011 << (2 * (position % 4))); - row[area.x / 4 + position / 4] |= mask; - } + memset(row, color_choice[color], line_bytes); + + epd_populate_area_mask(ctx->line_mask, ctx->area, ctx->display_width); + epd_apply_line_mask(row, ctx->line_mask, ctx->display_width / 4); reorder_line_buffer((uint32_t*)row, line_bytes); i2s_start_frame(); diff --git a/src/output_lcd/lcd_driver.c b/src/output_lcd/lcd_driver.c index 234d87c8..b5b2630e 100644 --- a/src/output_lcd/lcd_driver.c +++ b/src/output_lcd/lcd_driver.c @@ -450,7 +450,7 @@ static esp_err_t allocate_lcd_buffers() { // allocate bounce buffers for (int i = 0; i < 2; i++) { - lcd.bounce_buffer[i] = heap_caps_aligned_calloc(4, 1, lcd.bb_size, dma_flags); + lcd.bounce_buffer[i] = heap_caps_aligned_calloc(16, 1, lcd.bb_size, dma_flags); ESP_RETURN_ON_FALSE(lcd.bounce_buffer[i], ESP_ERR_NO_MEM, TAG, "install interrupt failed"); } diff --git a/src/output_lcd/render_lcd.c b/src/output_lcd/render_lcd.c index 71aadabb..8b543cad 100644 --- a/src/output_lcd/render_lcd.c +++ b/src/output_lcd/render_lcd.c @@ -2,6 +2,7 @@ #include #include "../output_common/render_method.h" +#include "../output_common/utils.h" #ifdef RENDER_METHOD_LCD @@ -17,18 +18,39 @@ #include "lcd_driver.h" #include "render_lcd.h" + +void epd_apply_line_mask_VE(uint8_t * line, const uint8_t* mask, int mask_len); + + static bool IRAM_ATTR fill_line_noop(RenderContext_t* ctx, uint8_t* line) { memset(line, 0x00, ctx->display_width / 4); + ctx->lines_consumed++; return false; } static bool IRAM_ATTR fill_line_white(RenderContext_t* ctx, uint8_t* line) { + // do a no-op if we're out of the draw area + if (ctx->lines_consumed < ctx->area.y || ctx->lines_consumed >= ctx->area.y + ctx->area.height) { + return fill_line_noop(ctx, line); + } + memset(line, CLEAR_BYTE, ctx->display_width / 4); + // we use the non-VE version here, because the buffers are not extended to multiples of 16 bytes + epd_apply_line_mask(line, ctx->line_mask, ctx->display_width / 4); + ctx->lines_consumed++; return false; } static bool IRAM_ATTR fill_line_black(RenderContext_t* ctx, uint8_t* line) { + // do a no-op if we're out of the draw area + if (ctx->lines_consumed < ctx->area.y || ctx->lines_consumed >= ctx->area.y + ctx->area.height) { + return fill_line_noop(ctx, line); + } + memset(line, DARK_BYTE, ctx->display_width / 4); + // we use the non-VE version here, because the buffers are not extended to multiples of 16 bytes + epd_apply_line_mask(line, ctx->line_mask, ctx->display_width / 4); + ctx->lines_consumed++; return false; } @@ -100,6 +122,10 @@ void lcd_do_update(RenderContext_t* ctx) { void epd_push_pixels_lcd(RenderContext_t* ctx, short time, int color) { epd_set_mode(1); ctx->current_frame = 0; + ctx->lines_consumed = 0; + + epd_populate_area_mask(ctx->line_mask, ctx->area, ctx->display_width); + epd_lcd_frame_done_cb((frame_done_func_t)handle_lcd_frame_done, ctx); if (color == 0) { epd_lcd_line_source_cb((line_cb_func_t)&fill_line_black, ctx); @@ -194,7 +220,6 @@ lcd_calculate_frame(RenderContext_t* ctx, int thread_id) { ctx->lut_lookup_func(lp, buf, ctx->conversion_lut, ctx->display_width); // apply the line mask - void epd_apply_line_mask_VE(uint8_t * line, const uint8_t* mask, int mask_len); epd_apply_line_mask_VE(buf, ctx->line_mask, ctx->display_width / 4); lq_commit(lq);