From a1049d6ebb21f08248ef79402faa35568f12d7f8 Mon Sep 17 00:00:00 2001 From: Valentin Roland Date: Sun, 1 Sep 2024 23:09:46 +0200 Subject: [PATCH] add previously black 8ppB LUT --- examples/fb_mode_test/main/main.c | 59 +++++++++++++++++------------ examples/test/main/main.c | 4 +- src/output_common/lut.c | 62 +++++++++++++++++++++++++------ test/test_diff.c | 5 +-- test/test_lut.c | 22 ++++++++++- 5 files changed, 111 insertions(+), 41 deletions(-) diff --git a/examples/fb_mode_test/main/main.c b/examples/fb_mode_test/main/main.c index 76e91c2f..4ddb8f9b 100644 --- a/examples/fb_mode_test/main/main.c +++ b/examples/fb_mode_test/main/main.c @@ -49,46 +49,59 @@ void clear() { memset(framebuffer, 0xFF, fb_size); } -void test_8ppB() { - clear(); - - // bytes in a line in 8ppB mode +/** + * Draw triangles at varying alignments into the framebuffer in 8ppB mode. + * start_line, start_column specify the start position. + * The bits that belong to a triangle are flipped, i.e., it is drawn at the + * inverse color to the background it is drawn onto. + */ +void draw_8bpp_triangles(int start_line, int start_column) { + start_column /= 8; int line_bytes = epd_width() / 8; - int start_line = 100; - int start_column = 80 / 8; - - // draw differently aligned triangles to check for uniformity for (int align = 0; align < 16; align++) { for (int height = 0; height < 16; height++) { for (int len = 0; len < height; len++) { int line = (start_line + 16 * align + height); int column = align + len; uint8_t* line_address = framebuffer + (line_bytes * line); - *(line_address + start_column + column / 8) &= ~(1 << (column % 8)); + *(line_address + start_column + column / 8) ^= 1 << (column % 8); } } } +} - int black_start_column = 160 / 8; +void test_8ppB() { + clear(); + EpdRect area = epd_full_screen(); - // draw a black area for later - for (int line = 0; line < 200; line++) { + // bytes in a line in 8ppB mode + int line_bytes = epd_width() / 8; + + int start_line = 100; + + // draw differently aligned black triangles to check for uniformity + draw_8bpp_triangles(start_line, 80); + + int black_start_column = 160; + + // draw a black area + for (int line = 0; line < 300; line++) { uint8_t* line_address = framebuffer + (line_bytes * (start_line + line)); - memset(line_address + black_start_column, 0, 32); + memset(line_address + black_start_column / 8, 0, 32); } + // draw white triangles on the black background + draw_8bpp_triangles(start_line, black_start_column + 16); + + // update the display. In the first update, white pixels are no-opps, + // in the second update, black pixels are no-ops. + enum EpdDrawMode mode; epd_poweron(); - checkError(epd_draw_base( - epd_full_screen(), - framebuffer, - epd_full_screen(), - MODE_PACKING_8PPB | MODE_DU | PREVIOUSLY_WHITE, - 25, - NULL, - NULL, - &epdiy_ED047TC2 - )); + mode = MODE_PACKING_8PPB | MODE_DU | PREVIOUSLY_WHITE; + checkError(epd_draw_base(area, framebuffer, area, mode, 25, NULL, NULL, &epdiy_ED047TC2)); + mode = MODE_PACKING_8PPB | MODE_DU | PREVIOUSLY_BLACK; + checkError(epd_draw_base(area, framebuffer, area, mode, 25, NULL, NULL, &epdiy_ED047TC2)); epd_poweroff(); } diff --git a/examples/test/main/main.c b/examples/test/main/main.c index 9fa5e22d..c15e9330 100644 --- a/examples/test/main/main.c +++ b/examples/test/main/main.c @@ -9,7 +9,7 @@ static void print_banner(const char* text) { void app_main(void) { print_banner("Running all the registered tests"); UNITY_BEGIN(); - unity_run_tests_by_tag("lut", false); - // unity_run_all_tests(); + // unity_run_tests_by_tag("lut", false); + unity_run_all_tests(); UNITY_END(); } diff --git a/src/output_common/lut.c b/src/output_common/lut.c index e0a64dbe..eca7530a 100644 --- a/src/output_common/lut.c +++ b/src/output_common/lut.c @@ -19,15 +19,15 @@ * Since we disable the PSRAM workaround here for performance reasons. */ -/* Python script for generating the 8ppB lookup table: - * for i in range(256): +/* Python script for generating the 8ppB, starting at white lookup table: + for i in range(256): number = 0; for b in range(8): - if not (i & (b << 1)): + if not (i & (1 << b)): number |= 1 << (2*b) print ('0x%04x,'%number) */ -const uint32_t lut_8ppB_black[256] = { +const uint32_t lut_8ppB_start_at_white[256] = { 0x5555, 0x5554, 0x5551, 0x5550, 0x5545, 0x5544, 0x5541, 0x5540, 0x5515, 0x5514, 0x5511, 0x5510, 0x5505, 0x5504, 0x5501, 0x5500, 0x5455, 0x5454, 0x5451, 0x5450, 0x5445, 0x5444, 0x5441, 0x5440, 0x5415, 0x5414, 0x5411, 0x5410, 0x5405, 0x5404, 0x5401, 0x5400, 0x5155, 0x5154, 0x5151, 0x5150, @@ -52,6 +52,39 @@ const uint32_t lut_8ppB_black[256] = { 0x0005, 0x0004, 0x0001, 0x0000 }; +/* Python script for generating the 8ppB, starting at black lookup table: + for i in range(256): + number = 0; + for b in range(8): + if (i & (1 << b)): + number |= 2 << (2*b) + print ('0x%04x,'%number) + */ +const uint32_t lut_8ppB_start_at_black[256] = { + 0x0000, 0x0002, 0x0008, 0x000a, 0x0020, 0x0022, 0x0028, 0x002a, 0x0080, 0x0082, 0x0088, 0x008a, + 0x00a0, 0x00a2, 0x00a8, 0x00aa, 0x0200, 0x0202, 0x0208, 0x020a, 0x0220, 0x0222, 0x0228, 0x022a, + 0x0280, 0x0282, 0x0288, 0x028a, 0x02a0, 0x02a2, 0x02a8, 0x02aa, 0x0800, 0x0802, 0x0808, 0x080a, + 0x0820, 0x0822, 0x0828, 0x082a, 0x0880, 0x0882, 0x0888, 0x088a, 0x08a0, 0x08a2, 0x08a8, 0x08aa, + 0x0a00, 0x0a02, 0x0a08, 0x0a0a, 0x0a20, 0x0a22, 0x0a28, 0x0a2a, 0x0a80, 0x0a82, 0x0a88, 0x0a8a, + 0x0aa0, 0x0aa2, 0x0aa8, 0x0aaa, 0x2000, 0x2002, 0x2008, 0x200a, 0x2020, 0x2022, 0x2028, 0x202a, + 0x2080, 0x2082, 0x2088, 0x208a, 0x20a0, 0x20a2, 0x20a8, 0x20aa, 0x2200, 0x2202, 0x2208, 0x220a, + 0x2220, 0x2222, 0x2228, 0x222a, 0x2280, 0x2282, 0x2288, 0x228a, 0x22a0, 0x22a2, 0x22a8, 0x22aa, + 0x2800, 0x2802, 0x2808, 0x280a, 0x2820, 0x2822, 0x2828, 0x282a, 0x2880, 0x2882, 0x2888, 0x288a, + 0x28a0, 0x28a2, 0x28a8, 0x28aa, 0x2a00, 0x2a02, 0x2a08, 0x2a0a, 0x2a20, 0x2a22, 0x2a28, 0x2a2a, + 0x2a80, 0x2a82, 0x2a88, 0x2a8a, 0x2aa0, 0x2aa2, 0x2aa8, 0x2aaa, 0x8000, 0x8002, 0x8008, 0x800a, + 0x8020, 0x8022, 0x8028, 0x802a, 0x8080, 0x8082, 0x8088, 0x808a, 0x80a0, 0x80a2, 0x80a8, 0x80aa, + 0x8200, 0x8202, 0x8208, 0x820a, 0x8220, 0x8222, 0x8228, 0x822a, 0x8280, 0x8282, 0x8288, 0x828a, + 0x82a0, 0x82a2, 0x82a8, 0x82aa, 0x8800, 0x8802, 0x8808, 0x880a, 0x8820, 0x8822, 0x8828, 0x882a, + 0x8880, 0x8882, 0x8888, 0x888a, 0x88a0, 0x88a2, 0x88a8, 0x88aa, 0x8a00, 0x8a02, 0x8a08, 0x8a0a, + 0x8a20, 0x8a22, 0x8a28, 0x8a2a, 0x8a80, 0x8a82, 0x8a88, 0x8a8a, 0x8aa0, 0x8aa2, 0x8aa8, 0x8aaa, + 0xa000, 0xa002, 0xa008, 0xa00a, 0xa020, 0xa022, 0xa028, 0xa02a, 0xa080, 0xa082, 0xa088, 0xa08a, + 0xa0a0, 0xa0a2, 0xa0a8, 0xa0aa, 0xa200, 0xa202, 0xa208, 0xa20a, 0xa220, 0xa222, 0xa228, 0xa22a, + 0xa280, 0xa282, 0xa288, 0xa28a, 0xa2a0, 0xa2a2, 0xa2a8, 0xa2aa, 0xa800, 0xa802, 0xa808, 0xa80a, + 0xa820, 0xa822, 0xa828, 0xa82a, 0xa880, 0xa882, 0xa888, 0xa88a, 0xa8a0, 0xa8a2, 0xa8a8, 0xa8aa, + 0xaa00, 0xaa02, 0xaa08, 0xaa0a, 0xaa20, 0xaa22, 0xaa28, 0xaa2a, 0xaa80, 0xaa82, 0xaa88, 0xaa8a, + 0xaaa0, 0xaaa2, 0xaaa8, 0xaaaa, +}; + static inline int min(int x, int y) { return x < y ? x : y; } @@ -64,8 +97,6 @@ uint32_t skipping; __attribute__((optimize("O3"))) void IRAM_ATTR reorder_line_buffer(uint32_t* line_data, int buf_len) { - uint16_t* load = (uint16_t*)line_data; - uint16_t* store = (uint16_t*)line_data; for (uint32_t i = 0; i < buf_len / 4; i++) { uint32_t val = *line_data; *(line_data++) = val >> 16 | ((val & 0x0000FFFF) << 16); @@ -390,8 +421,16 @@ static void build_2ppB_lut_64k_from_15(uint8_t* lut, const EpdWaveformPhases* ph build_2ppB_lut_64k_static_from(lut, phases, 0xF, frame); } -static void build_8ppB_lut_256b_from_15(uint8_t* lut, const EpdWaveformPhases* phases, int frame) { - memcpy(lut, lut_8ppB_black, sizeof(lut_8ppB_black)); +static void build_8ppB_lut_256b_from_white( + uint8_t* lut, const EpdWaveformPhases* phases, int frame +) { + memcpy(lut, lut_8ppB_start_at_white, sizeof(lut_8ppB_start_at_white)); +} + +static void build_8ppB_lut_256b_from_black( + uint8_t* lut, const EpdWaveformPhases* phases, int frame +) { + memcpy(lut, lut_8ppB_start_at_black, sizeof(lut_8ppB_start_at_black)); } LutFunctionPair find_lut_functions(enum EpdDrawMode mode, uint32_t lut_size) { @@ -433,16 +472,17 @@ LutFunctionPair find_lut_functions(enum EpdDrawMode mode, uint32_t lut_size) { } } } else if (mode & MODE_PACKING_8PPB) { - if (lut_size < sizeof(lut_8ppB_black)) { + if (lut_size < sizeof(lut_8ppB_start_at_white)) { return pair; } if (mode & PREVIOUSLY_WHITE) { - pair.build_func = &build_8ppB_lut_256b_from_15; + pair.build_func = &build_8ppB_lut_256b_from_white; pair.lookup_func = &calc_epd_input_8ppB; return pair; } else if (mode & PREVIOUSLY_BLACK) { - // FIXME: to implement + pair.build_func = &build_8ppB_lut_256b_from_black; + pair.lookup_func = &calc_epd_input_8ppB; return pair; } } diff --git a/test/test_diff.c b/test/test_diff.c index d8fb8c03..b71aae25 100644 --- a/test/test_diff.c +++ b/test/test_diff.c @@ -86,7 +86,7 @@ TEST_CASE("simple aligned diff works", "[epdiy,unit]") { diff_test_buffers_init(&bufs, example_len); // This should trigger use of vector extensions on the S3 - TEST_ASSERT((uint32_t)bufs.to % 16 == 0) + TEST_ASSERT((uint32_t)bufs.to % 16 == 0); // fully aligned dirty = _epd_interlace_line( @@ -109,7 +109,7 @@ TEST_CASE("dirtynes for diff without changes is correct", "[epdiy,unit]") { diff_test_buffers_init(&bufs, example_len); // This should trigger use of vector extensions on the S3 - TEST_ASSERT((uint32_t)bufs.to % 16 == 0) + TEST_ASSERT((uint32_t)bufs.to % 16 == 0); // both use "from" buffer dirty = _epd_interlace_line( @@ -132,7 +132,6 @@ TEST_CASE("dirtynes for diff without changes is correct", "[epdiy,unit]") { TEST_CASE("different 4-byte alignments work", "[epdiy,unit]") { const int example_len = DEFAULT_EXAMPLE_LEN; - const uint8_t NULL_ARRAY[DEFAULT_EXAMPLE_LEN * 2] = { 0 }; DiffTestBuffers bufs; bool dirty; diff --git a/test/test_lut.c b/test/test_lut.c index 86beeb32..3d1195b1 100644 --- a/test/test_lut.c +++ b/test/test_lut.c @@ -23,10 +23,14 @@ static const uint8_t result_pattern_2ppB_white[8] = { 0x00, 0x01, 0x50, 0x55, 0x55, 0x55, 0x00, 0x55 }; static const uint8_t result_pattern_2ppB_black[8] = { 0xAA, 0xA8, 0x0A, 0x82, 0xAA, 0xAA, 0xAA, 0x20 }; -static const uint8_t result_pattern_8ppB[32] +static const uint8_t result_pattern_8ppB_on_white[32] = { 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x54, 0x55, 0x55, 0x54, 0x44, 0x11, 0x44, 0x11, 0x11, 0x44, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x15, 0x55 }; +static const uint8_t result_pattern_8ppB_on_black[32] + = { 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x22, 0x88, 0x22, 0x88, 0x88, 0x22, + 0x88, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x80, 0x00 }; typedef void (*lut_func_t)(const uint32_t*, uint8_t*, const uint8_t*, uint32_t); static uint8_t waveform_phases[16][4]; @@ -252,7 +256,7 @@ TEST_CASE("2ppB lookup LCD, 1k LUT, previously black", "[epdiy,unit,lut]") { TEST_CASE("8ppB lookup LCD, 1k LUT, previously white", "[epdiy,unit,lut]") { LutTestBuffers bufs; - lut_test_buffers_init(&bufs, DEFAULT_EXAMPLE_LEN / 2, result_pattern_8ppB, 0.5); + lut_test_buffers_init(&bufs, DEFAULT_EXAMPLE_LEN / 2, result_pattern_8ppB_on_white, 0.5); enum EpdDrawMode mode = MODE_DU | MODE_PACKING_8PPB | PREVIOUSLY_WHITE; LutFunctionPair func_pair = find_lut_functions(mode, 1 << 10); @@ -261,5 +265,19 @@ TEST_CASE("8ppB lookup LCD, 1k LUT, previously white", "[epdiy,unit,lut]") { func_pair.build_func(bufs.lut, &test_waveform, 0); test_with_alignments(&bufs, func_pair.lookup_func); + diff_test_buffers_free(&bufs); +} + +TEST_CASE("8ppB lookup LCD, 1k LUT, previously black", "[epdiy,unit,lut]") { + LutTestBuffers bufs; + lut_test_buffers_init(&bufs, DEFAULT_EXAMPLE_LEN / 2, result_pattern_8ppB_on_black, 0.5); + + enum EpdDrawMode mode = MODE_DU | MODE_PACKING_8PPB | PREVIOUSLY_BLACK; + LutFunctionPair func_pair = find_lut_functions(mode, 1 << 10); + TEST_ASSERT_NOT_NULL(func_pair.build_func); + TEST_ASSERT_NOT_NULL(func_pair.lookup_func); + func_pair.build_func(bufs.lut, &test_waveform, 0); + test_with_alignments(&bufs, func_pair.lookup_func); + diff_test_buffers_free(&bufs); } \ No newline at end of file