From 4b03c991a3cb0c8c017ad245df13da1ce9aa98ea Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 14 Nov 2025 13:30:53 +0000 Subject: [PATCH 01/81] extmod/modframebuf.c: Add byte-swapped RGB565 format. This is required for identifying which bits are in which channels for alpha blending. It also has some utility when working with display devices where the display memory is opposite endianness from the microcontroller, as it permits more straightforward translation from colors to bytes. --- extmod/modframebuf.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 593125aa16f91..4f5fb900d1b53 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -57,13 +57,14 @@ typedef struct _mp_framebuf_p_t { } mp_framebuf_p_t; // constants for formats -#define FRAMEBUF_MVLSB (0) -#define FRAMEBUF_RGB565 (1) -#define FRAMEBUF_GS2_HMSB (5) -#define FRAMEBUF_GS4_HMSB (2) -#define FRAMEBUF_GS8 (6) -#define FRAMEBUF_MHLSB (3) -#define FRAMEBUF_MHMSB (4) +#define FRAMEBUF_MVLSB (0) +#define FRAMEBUF_RGB565 (1) +#define FRAMEBUF_RGB565_BS (7) +#define FRAMEBUF_GS2_HMSB (5) +#define FRAMEBUF_GS4_HMSB (2) +#define FRAMEBUF_GS8 (6) +#define FRAMEBUF_MHLSB (3) +#define FRAMEBUF_MHMSB (4) // Functions for MHLSB and MHMSB @@ -137,6 +138,29 @@ static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsign } } +// Functions for RGB565_BS format + +static void rgb565_bs_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { + col = __builtin_bswap16(col); + ((uint16_t *)fb->buf)[x + y * fb->stride] = col; +} + +static uint32_t rgb565_bs_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { + uint32_t col = ((uint16_t *)fb->buf)[x + y * fb->stride]; + return __builtin_bswap16(col); +} + +static void rgb565_bs_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + col = __builtin_bswap16(col); + uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; + while (h--) { + for (unsigned int ww = w; ww; --ww) { + *b++ = col; + } + b += fb->stride - w; + } +} + // Functions for GS2_HMSB format static void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { @@ -234,6 +258,7 @@ static void gs8_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned static mp_framebuf_p_t formats[] = { [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, + [FRAMEBUF_RGB565_BS] = {rgb565_bs_setpixel, rgb565_bs_getpixel, rgb565_bs_fill_rect}, [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect}, [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect}, [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect}, @@ -909,6 +934,7 @@ static const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_BS), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) }, From eb96c715dd7f3eff0b9a5d6e3e5b42b30f6dbcc4 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 14 Nov 2025 13:44:53 +0000 Subject: [PATCH 02/81] tests/extmod/framebuf16_bs.py Add tests for byteswapped RGB565 format. --- tests/extmod/framebuf16_bs.py | 66 +++++++++++++++++++++++++++++++ tests/extmod/framebuf16_bs.py.exp | 57 ++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 tests/extmod/framebuf16_bs.py create mode 100644 tests/extmod/framebuf16_bs.py.exp diff --git a/tests/extmod/framebuf16_bs.py b/tests/extmod/framebuf16_bs.py new file mode 100644 index 0000000000000..d3e0e681b1c6d --- /dev/null +++ b/tests/extmod/framebuf16_bs.py @@ -0,0 +1,66 @@ +try: + import framebuf, sys +except ImportError: + print("SKIP") + raise SystemExit + +# This test and its .exp file is based on a little-endian architecture. +if sys.byteorder != "little": + print("SKIP") + raise SystemExit + + +def printbuf(): + print("--8<--") + for y in range(h): + print(buf[y * w * 2 : (y + 1) * w * 2]) + print("-->8--") + + +w = 4 +h = 5 +buf = bytearray(w * h * 2) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565_BS) + +# fill +fbuf.fill(0xFFFF) +printbuf() +fbuf.fill(0x0000) +printbuf() + +# put pixel +fbuf.pixel(0, 0, 0xEEEE) +fbuf.pixel(3, 0, 0xEE00) +fbuf.pixel(0, 4, 0x00EE) +fbuf.pixel(3, 4, 0x0EE0) +printbuf() + +# get pixel +print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) + +# scroll +fbuf.fill(0x0000) +fbuf.pixel(2, 2, 0xFFFF) +printbuf() +fbuf.scroll(0, 1) +printbuf() +fbuf.scroll(1, 0) +printbuf() +fbuf.scroll(-1, -2) +printbuf() + +w2 = 2 +h2 = 3 +buf2 = bytearray(w2 * h2 * 2) +fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565) + +fbuf2.fill(0x0000) +fbuf2.pixel(0, 0, 0x0EE0) +fbuf2.pixel(0, 2, 0xEE00) +fbuf2.pixel(1, 0, 0x00EE) +fbuf2.pixel(1, 2, 0xE00E) +fbuf.fill(0xFFFF) +fbuf.blit(fbuf2, 3, 3, 0x0000) +fbuf.blit(fbuf2, -1, -1, 0x0000) +fbuf.blit(fbuf2, 16, 16, 0x0000) +printbuf() diff --git a/tests/extmod/framebuf16_bs.py.exp b/tests/extmod/framebuf16_bs.py.exp new file mode 100644 index 0000000000000..e539d7908dc3b --- /dev/null +++ b/tests/extmod/framebuf16_bs.py.exp @@ -0,0 +1,57 @@ +--8<-- +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\xee\xee\x00\x00\x00\x00\xee\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\xee\x00\x00\x00\x00\x0e\xe0') +-->8-- +238 0 +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xe0\x0e\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\x0e\xe0') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +-->8-- From 19ba0e01f0749f4e6c6a9734301481091edf37b4 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 10:17:52 +0000 Subject: [PATCH 03/81] extmod/modframebuf.c: Add RGB565_BS to valid format cases. Without this, we don't know the bits per pixel. --- extmod/modframebuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 4f5fb900d1b53..b16e3aa2ff171 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -335,6 +335,7 @@ static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, bpp = 8; break; case FRAMEBUF_RGB565: + case FRAMEBUF_RGB565_BS: bpp = 16; break; default: From 845dcadedc8703892be438f52c8b4ccbdf90cde2 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 10:36:21 +0000 Subject: [PATCH 04/81] tests/extmod/framebuf16_bs.py: Correct buffer type. --- tests/extmod/framebuf16_bs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/extmod/framebuf16_bs.py b/tests/extmod/framebuf16_bs.py index d3e0e681b1c6d..ffcfb2779789b 100644 --- a/tests/extmod/framebuf16_bs.py +++ b/tests/extmod/framebuf16_bs.py @@ -52,7 +52,7 @@ def printbuf(): w2 = 2 h2 = 3 buf2 = bytearray(w2 * h2 * 2) -fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565) +fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565_BS) fbuf2.fill(0x0000) fbuf2.pixel(0, 0, 0x0EE0) From 86ce41da19082a1462e8438645e668244d989150 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 10:55:16 +0000 Subject: [PATCH 05/81] run-tests.py: Skip framebuf16_bs tests on platforms without slicing. --- tests/run-tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-tests.py b/tests/run-tests.py index d8d2c42ad22e7..a4a4ee679aab0 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -280,6 +280,7 @@ def open(self, path, mode): "extmod/btree1.py", "extmod/deflate_decompress.py", "extmod/framebuf16.py", + "extmod/framebuf16_bs.py", "extmod/framebuf4.py", "extmod/machine1.py", "extmod/time_mktime.py", From e197cd4bb4d97b8fe30329ed1b4a3be9b06a9231 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 10:56:35 +0000 Subject: [PATCH 06/81] docs/framebuf.rst: Add documentation for new format. --- docs/library/framebuf.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index e2a231207d673..e221d974ab4b0 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -194,6 +194,12 @@ Constants Red Green Blue (16-bit, 5+6+5) color format +.. data:: framebuf.RGB565_BS + + Red Green Blue (16-bit, 5+6+5, byte-swapped) color format + This defines a 16-bit format where the bytes are stored in memory with + opposite endianness to the native endianness. + .. data:: framebuf.GS2_HMSB Grayscale (2-bit) color format From 91cfa9934a94eb844e9dee6c1156832ac696522d Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 11:48:29 +0000 Subject: [PATCH 07/81] examples/natmod/framebuf.c: Add RGB565_BS to example native module. --- examples/natmod/framebuf/framebuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c index 5fd7c6be3a456..76b6bee995995 100644 --- a/examples/natmod/framebuf/framebuf.c +++ b/examples/natmod/framebuf/framebuf.c @@ -44,6 +44,7 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a mp_store_global(MP_QSTR_MVLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB)); mp_store_global(MP_QSTR_MONO_VLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB)); mp_store_global(MP_QSTR_RGB565, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); + mp_store_global(MP_QSTR_RGB565_BS, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); mp_store_global(MP_QSTR_GS2_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS2_HMSB)); mp_store_global(MP_QSTR_GS4_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS4_HMSB)); mp_store_global(MP_QSTR_GS8, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS8)); From 7b66bd1e1831e5af1ad4494240a1bbc9c1e314db Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 11:52:08 +0000 Subject: [PATCH 08/81] extmod/modframebuf.c: Reduce codesize. Re-use the rgb565_fill_rect() function to avoid repeated code. --- extmod/modframebuf.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index b16e3aa2ff171..c6db7b42ac9f8 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -151,14 +151,7 @@ static uint32_t rgb565_bs_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, } static void rgb565_bs_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - col = __builtin_bswap16(col); - uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; - while (h--) { - for (unsigned int ww = w; ww; --ww) { - *b++ = col; - } - b += fb->stride - w; - } + rgb565_fill_rect(fb, x, y, w, h, __builtin_bswap16(col)); } // Functions for GS2_HMSB format From 700085e384dbee1367a012eaa4f275768aba8371 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 12:14:58 +0000 Subject: [PATCH 09/81] extmod/modframbeuf.c: Completely remove byteswapped fill rect. --- extmod/modframebuf.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index c6db7b42ac9f8..18ff5dc8a3554 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -118,7 +118,7 @@ static void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigne } } -// Functions for RGB565 format +// Functions for RGB565 and RGB565_BS format static void rgb565_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { ((uint16_t *)fb->buf)[x + y * fb->stride] = col; @@ -128,18 +128,6 @@ static uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, uns return ((uint16_t *)fb->buf)[x + y * fb->stride]; } -static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; - while (h--) { - for (unsigned int ww = w; ww; --ww) { - *b++ = col; - } - b += fb->stride - w; - } -} - -// Functions for RGB565_BS format - static void rgb565_bs_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { col = __builtin_bswap16(col); ((uint16_t *)fb->buf)[x + y * fb->stride] = col; @@ -150,8 +138,17 @@ static uint32_t rgb565_bs_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, return __builtin_bswap16(col); } -static void rgb565_bs_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - rgb565_fill_rect(fb, x, y, w, h, __builtin_bswap16(col)); +static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + if (fb->format == FRAMEBUF_RGB565_BS) { + col = __builtin_bswap16(col); + } + uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; + while (h--) { + for (unsigned int ww = w; ww; --ww) { + *b++ = col; + } + b += fb->stride - w; + } } // Functions for GS2_HMSB format @@ -251,7 +248,7 @@ static void gs8_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned static mp_framebuf_p_t formats[] = { [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, - [FRAMEBUF_RGB565_BS] = {rgb565_bs_setpixel, rgb565_bs_getpixel, rgb565_bs_fill_rect}, + [FRAMEBUF_RGB565_BS] = {rgb565_bs_setpixel, rgb565_bs_getpixel, rgb565_fill_rect}, [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect}, [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect}, [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect}, From 1948f3614cbfc7c4583752af1a4c7b6e6965c949 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 15:09:27 +0000 Subject: [PATCH 10/81] extmod/modframebuf.c: Make RGB565 endianness explicit. This keeps the internal C code more-or-less unchanged, but exposes the byte-order as "RGB565_LE" and "RGB565_BE" for little and big endian versions. Updates docs and tests and, as a bonus, turns on RGB565 testing for big-endian native systems. --- docs/library/framebuf.rst | 17 ++++-- extmod/modframebuf.c | 11 +++- tests/extmod/framebuf16.py | 95 ++++++++++++++++--------------- tests/extmod/framebuf16.py.exp | 58 +++++++++++++++++++ tests/extmod/framebuf16_bs.py | 66 --------------------- tests/extmod/framebuf16_bs.py.exp | 57 ------------------- tests/run-tests.py | 1 - 7 files changed, 130 insertions(+), 175 deletions(-) delete mode 100644 tests/extmod/framebuf16_bs.py delete mode 100644 tests/extmod/framebuf16_bs.py.exp diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index e221d974ab4b0..dfed26b042e43 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -192,13 +192,20 @@ Constants .. data:: framebuf.RGB565 - Red Green Blue (16-bit, 5+6+5) color format + Red Green Blue (16-bit, 5+6+5, native) color format in native byte-order. -.. data:: framebuf.RGB565_BS +.. data:: framebuf.RGB565_LE - Red Green Blue (16-bit, 5+6+5, byte-swapped) color format - This defines a 16-bit format where the bytes are stored in memory with - opposite endianness to the native endianness. + Red Green Blue (16-bit, 5+6+5, little-endian) color format in little-endian + byte order. This defines a 16-bit format where the bytes are stored in + little-endian order. If the system is little-endian, this is the same as + ``RGB565``. + +.. data:: framebuf.RGB565_BE + + Red Green Blue (16-bit, 5+6+5, big-endian) color format in big-endian byte + order. This defines a 16-bit format where the bytes are stored in + big-endian order. If the system is big-endian, this is the same as ``RGB565``. .. data:: framebuf.GS2_HMSB diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 18ff5dc8a3554..4b07fe20346d1 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -119,6 +119,9 @@ static void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigne } // Functions for RGB565 and RGB565_BS format +// +// Internally we use 'native' and 'byte-swapped' formats, and then expose those as big- or little- +// endian to Python as appropriate. static void rgb565_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { ((uint16_t *)fb->buf)[x + y * fb->stride] = col; @@ -925,7 +928,13 @@ static const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, - { MP_ROM_QSTR(MP_QSTR_RGB565_BS), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, +#if MP_ENDIANNESS_LITTLE + { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, +#elif MP_ENDIANNESS_BIG + { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, +#endif // MP_ENDIANNESS_... { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) }, diff --git a/tests/extmod/framebuf16.py b/tests/extmod/framebuf16.py index 9f373c331e51f..0da85fe95f306 100644 --- a/tests/extmod/framebuf16.py +++ b/tests/extmod/framebuf16.py @@ -4,10 +4,14 @@ print("SKIP") raise SystemExit -# This test and its .exp file is based on a little-endian architecture. -if sys.byteorder != "little": - print("SKIP") - raise SystemExit +if ( + sys.byteorder == "little" and framebuf.RGB565 == framebuf.RGB565_LE +) or ( + sys.byteorder == "big" and framebuf.RGB565 == framebuf.RGB565_BE +): + print("Native format matches expected value.") +else: + print("Unexpected native format.") def printbuf(): @@ -17,50 +21,51 @@ def printbuf(): print("-->8--") -w = 4 -h = 5 -buf = bytearray(w * h * 2) -fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565) +for format in [framebuf.RGB565_LE, framebuf.RGB565_BE]: + w = 4 + h = 5 + buf = bytearray(w * h * 2) + fbuf = framebuf.FrameBuffer(buf, w, h, format) -# fill -fbuf.fill(0xFFFF) -printbuf() -fbuf.fill(0x0000) -printbuf() + # fill + fbuf.fill(0xFFFF) + printbuf() + fbuf.fill(0x0000) + printbuf() -# put pixel -fbuf.pixel(0, 0, 0xEEEE) -fbuf.pixel(3, 0, 0xEE00) -fbuf.pixel(0, 4, 0x00EE) -fbuf.pixel(3, 4, 0x0EE0) -printbuf() + # put pixel + fbuf.pixel(0, 0, 0xEEEE) + fbuf.pixel(3, 0, 0xEE00) + fbuf.pixel(0, 4, 0x00EE) + fbuf.pixel(3, 4, 0x0EE0) + printbuf() -# get pixel -print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) + # get pixel + print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) -# scroll -fbuf.fill(0x0000) -fbuf.pixel(2, 2, 0xFFFF) -printbuf() -fbuf.scroll(0, 1) -printbuf() -fbuf.scroll(1, 0) -printbuf() -fbuf.scroll(-1, -2) -printbuf() + # scroll + fbuf.fill(0x0000) + fbuf.pixel(2, 2, 0xFFFF) + printbuf() + fbuf.scroll(0, 1) + printbuf() + fbuf.scroll(1, 0) + printbuf() + fbuf.scroll(-1, -2) + printbuf() -w2 = 2 -h2 = 3 -buf2 = bytearray(w2 * h2 * 2) -fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565) + w2 = 2 + h2 = 3 + buf2 = bytearray(w2 * h2 * 2) + fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, format) -fbuf2.fill(0x0000) -fbuf2.pixel(0, 0, 0x0EE0) -fbuf2.pixel(0, 2, 0xEE00) -fbuf2.pixel(1, 0, 0x00EE) -fbuf2.pixel(1, 2, 0xE00E) -fbuf.fill(0xFFFF) -fbuf.blit(fbuf2, 3, 3, 0x0000) -fbuf.blit(fbuf2, -1, -1, 0x0000) -fbuf.blit(fbuf2, 16, 16, 0x0000) -printbuf() + fbuf2.fill(0x0000) + fbuf2.pixel(0, 0, 0x0EE0) + fbuf2.pixel(0, 2, 0xEE00) + fbuf2.pixel(1, 0, 0x00EE) + fbuf2.pixel(1, 2, 0xE00E) + fbuf.fill(0xFFFF) + fbuf.blit(fbuf2, 3, 3, 0x0000) + fbuf.blit(fbuf2, -1, -1, 0x0000) + fbuf.blit(fbuf2, 16, 16, 0x0000) + printbuf() diff --git a/tests/extmod/framebuf16.py.exp b/tests/extmod/framebuf16.py.exp index c41dc19d07140..bb23762efcf75 100644 --- a/tests/extmod/framebuf16.py.exp +++ b/tests/extmod/framebuf16.py.exp @@ -1,3 +1,4 @@ +Native format matches expected value. --8<-- bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') @@ -55,3 +56,60 @@ bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') bytearray(b'\xff\xff\xff\xff\xff\xff\xe0\x0e') bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -->8-- +--8<-- +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\xee\xee\x00\x00\x00\x00\xee\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\xee\x00\x00\x00\x00\x0e\xe0') +-->8-- +238 0 +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xe0\x0e\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\x0e\xe0') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +-->8-- diff --git a/tests/extmod/framebuf16_bs.py b/tests/extmod/framebuf16_bs.py deleted file mode 100644 index ffcfb2779789b..0000000000000 --- a/tests/extmod/framebuf16_bs.py +++ /dev/null @@ -1,66 +0,0 @@ -try: - import framebuf, sys -except ImportError: - print("SKIP") - raise SystemExit - -# This test and its .exp file is based on a little-endian architecture. -if sys.byteorder != "little": - print("SKIP") - raise SystemExit - - -def printbuf(): - print("--8<--") - for y in range(h): - print(buf[y * w * 2 : (y + 1) * w * 2]) - print("-->8--") - - -w = 4 -h = 5 -buf = bytearray(w * h * 2) -fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565_BS) - -# fill -fbuf.fill(0xFFFF) -printbuf() -fbuf.fill(0x0000) -printbuf() - -# put pixel -fbuf.pixel(0, 0, 0xEEEE) -fbuf.pixel(3, 0, 0xEE00) -fbuf.pixel(0, 4, 0x00EE) -fbuf.pixel(3, 4, 0x0EE0) -printbuf() - -# get pixel -print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) - -# scroll -fbuf.fill(0x0000) -fbuf.pixel(2, 2, 0xFFFF) -printbuf() -fbuf.scroll(0, 1) -printbuf() -fbuf.scroll(1, 0) -printbuf() -fbuf.scroll(-1, -2) -printbuf() - -w2 = 2 -h2 = 3 -buf2 = bytearray(w2 * h2 * 2) -fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565_BS) - -fbuf2.fill(0x0000) -fbuf2.pixel(0, 0, 0x0EE0) -fbuf2.pixel(0, 2, 0xEE00) -fbuf2.pixel(1, 0, 0x00EE) -fbuf2.pixel(1, 2, 0xE00E) -fbuf.fill(0xFFFF) -fbuf.blit(fbuf2, 3, 3, 0x0000) -fbuf.blit(fbuf2, -1, -1, 0x0000) -fbuf.blit(fbuf2, 16, 16, 0x0000) -printbuf() diff --git a/tests/extmod/framebuf16_bs.py.exp b/tests/extmod/framebuf16_bs.py.exp deleted file mode 100644 index e539d7908dc3b..0000000000000 --- a/tests/extmod/framebuf16_bs.py.exp +++ /dev/null @@ -1,57 +0,0 @@ ---8<-- -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') --->8-- ---8<-- -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') --->8-- ---8<-- -bytearray(b'\xee\xee\x00\x00\x00\x00\xee\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\xee\x00\x00\x00\x00\x0e\xe0') --->8-- -238 0 ---8<-- -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') --->8-- ---8<-- -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') --->8-- ---8<-- -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') --->8-- ---8<-- -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') -bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') -bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') --->8-- ---8<-- -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xe0\x0e\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -bytearray(b'\xff\xff\xff\xff\xff\xff\x0e\xe0') -bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') --->8-- diff --git a/tests/run-tests.py b/tests/run-tests.py index a4a4ee679aab0..d8d2c42ad22e7 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -280,7 +280,6 @@ def open(self, path, mode): "extmod/btree1.py", "extmod/deflate_decompress.py", "extmod/framebuf16.py", - "extmod/framebuf16_bs.py", "extmod/framebuf4.py", "extmod/machine1.py", "extmod/time_mktime.py", From f1f616d9720bf070b793d4df1f78b270809813da Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 17 Nov 2025 15:35:54 +0000 Subject: [PATCH 11/81] extmod/modframebuf.c: Fix code style --- examples/natmod/framebuf/framebuf.c | 8 +++++++- extmod/modframebuf.c | 6 +++--- tests/extmod/framebuf16.py | 4 +--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c index 76b6bee995995..e05799d1eb389 100644 --- a/examples/natmod/framebuf/framebuf.c +++ b/examples/natmod/framebuf/framebuf.c @@ -44,7 +44,13 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a mp_store_global(MP_QSTR_MVLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB)); mp_store_global(MP_QSTR_MONO_VLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB)); mp_store_global(MP_QSTR_RGB565, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); - mp_store_global(MP_QSTR_RGB565_BS, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); + #if MP_ENDIANNESS_LITTLE + mp_store_global(MP_QSTR_RGB565_LE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); + mp_store_global(MP_QSTR_RGB565_BE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); + #else + mp_store_global(MP_QSTR_RGB565_BE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); + mp_store_global(MP_QSTR_RGB565_LE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); + #endif mp_store_global(MP_QSTR_GS2_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS2_HMSB)); mp_store_global(MP_QSTR_GS4_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS4_HMSB)); mp_store_global(MP_QSTR_GS8, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS8)); diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 4b07fe20346d1..be80000c3550f 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -928,13 +928,13 @@ static const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, -#if MP_ENDIANNESS_LITTLE + #if MP_ENDIANNESS_LITTLE { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565) }, { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, -#elif MP_ENDIANNESS_BIG + #elif MP_ENDIANNESS_BIG { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565) }, { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, -#endif // MP_ENDIANNESS_... + #endif // MP_ENDIANNESS_... { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) }, diff --git a/tests/extmod/framebuf16.py b/tests/extmod/framebuf16.py index 0da85fe95f306..7d4c21ff9ada3 100644 --- a/tests/extmod/framebuf16.py +++ b/tests/extmod/framebuf16.py @@ -4,9 +4,7 @@ print("SKIP") raise SystemExit -if ( - sys.byteorder == "little" and framebuf.RGB565 == framebuf.RGB565_LE -) or ( +if (sys.byteorder == "little" and framebuf.RGB565 == framebuf.RGB565_LE) or ( sys.byteorder == "big" and framebuf.RGB565 == framebuf.RGB565_BE ): print("Native format matches expected value.") From eaf625ca86e80efeec8eb01f9cd54a18640512ec Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 09:17:22 +0000 Subject: [PATCH 12/81] Add blit with alpha mask method. --- extmod/modframebuf.c | 118 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 593125aa16f91..2dcae68067aa5 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -255,6 +255,47 @@ static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, uns return formats[fb->format].getpixel(fb, x, y); } +typedef struct __attribute__((packed)) rgb565 { + uint8_t b:5; + uint8_t g:6; + uint8_t r:5; +} rgb565; +typedef union { + uint16_t u16; + rgb565 rgb; +} urgb565; + +static uint32_t alpha_blend(uint32_t c1, uint32_t c2, uint32_t alpha) { + // Add one to alpha, otherwise 0x00 and 0x01 are identical after >> 8 + return ((c1 * (0x100 - alpha)) + (alpha + 1) * c2) >> 8; +} + +static void setpixel_alpha(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t alpha) { + if (alpha <= 0) { + // nothing to do + return; + } else if (alpha >= 0xff) { + // no blending needed + setpixel(fb, x, y, col); + } + uint16_t pix_col = formats[fb->format].getpixel(fb, x, y); + if (fb->format == FRAMEBUF_RGB565) { + uint16_t col16 = col; + urgb565 pix_col_struct = *(urgb565 *)&pix_col; + urgb565 col_struct = *(urgb565 *)&col16; + col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); + col_struct.rgb.g = alpha_blend(pix_col_struct.rgb.g, col_struct.rgb.g, alpha); + col_struct.rgb.b = alpha_blend(pix_col_struct.rgb.b, col_struct.rgb.b, alpha); + col_struct.rgb.r = MIN(col_struct.rgb.r, 0b11111); + col_struct.rgb.g = MIN(col_struct.rgb.g, 0b111111); + col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); + col = *(uint16_t *)&col_struct; + } else { + col = alpha_blend(pix_col, col, alpha); + } + formats[fb->format].setpixel(fb, x, y, col); +} + static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) { // No operation needed. @@ -781,6 +822,83 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 6, framebuf_blit); +static mp_obj_t framebuf_blit_mask(size_t n_args, const mp_obj_t *args_in) { + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); + + mp_obj_framebuf_t source; + get_readonly_framebuffer(args_in[1], &source); + + mp_int_t x = mp_obj_get_int(args_in[2]); + mp_int_t y = mp_obj_get_int(args_in[3]); + + mp_obj_framebuf_t mask; + get_readonly_framebuffer(args_in[4], &mask); + if (mask.width != source.width || mask.height != source.height) { + // mask and source must be the same shape + mp_raise_ValueError(MP_ERROR_TEXT("Mask and source different sizes.")); + } + size_t alpha_mul; + switch (mask.format) { + case FRAMEBUF_MVLSB: + case FRAMEBUF_MHLSB: + case FRAMEBUF_MHMSB: + alpha_mul = 0x100; + break; + case FRAMEBUF_GS8: + alpha_mul = 1; + break; + case FRAMEBUF_GS4_HMSB: + alpha_mul = 0x11; + break; + case FRAMEBUF_GS2_HMSB: + alpha_mul = 0x1111; + break; + default: + // other formats can't easily be converted to alpha + mp_raise_ValueError(MP_ERROR_TEXT("invalid mask format")); + } + + mp_obj_framebuf_t palette; + palette.buf = NULL; + if (n_args > 5 && args_in[5] != mp_const_none) { + get_readonly_framebuffer(args_in[5], &palette); + } + + if ( + (x >= self->width) || + (y >= self->height) || + (-x >= source.width) || + (-y >= source.height) + ) { + // Out of bounds, no-op. + return mp_const_none; + } + + // Clip. + int x0 = MAX(0, x); + int y0 = MAX(0, y); + int x1 = MAX(0, -x); + int y1 = MAX(0, -y); + int x0end = MIN(self->width, x + source.width); + int y0end = MIN(self->height, y + source.height); + + for (; y0 < y0end; ++y0) { + int cx1 = x1; + for (int cx0 = x0; cx0 < x0end; ++cx0) { + uint32_t col = getpixel(&source, cx1, y1); + if (palette.buf) { + col = getpixel(&palette, col, 0); + } + uint32_t alpha = getpixel(&mask, cx1, y1) * alpha_mul; + setpixel_alpha(self, cx0, y0, col, alpha); + ++cx1; + } + ++y1; + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_mask_obj, 5, 6, framebuf_blit_mask); + static mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t xstep = mp_obj_get_int(xstep_in); From 27d8ccc5d979d3479428e485fe8b93b06f76f939 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 09:32:46 +0000 Subject: [PATCH 13/81] Fix code formatting and add ew blit routine to module. --- extmod/modframebuf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 2dcae68067aa5..ccadc6f44eafc 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -256,9 +256,9 @@ static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, uns } typedef struct __attribute__((packed)) rgb565 { - uint8_t b:5; - uint8_t g:6; - uint8_t r:5; + uint8_t b : 5; + uint8_t g : 6; + uint8_t r : 5; } rgb565; typedef union { uint16_t u16; @@ -996,6 +996,7 @@ static const mp_rom_map_elem_t framebuf_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_poly), MP_ROM_PTR(&framebuf_poly_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) }, + { MP_ROM_QSTR(MP_QSTR_blit_mask), MP_ROM_PTR(&framebuf_blit_mask_obj) }, { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) }, { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) }, }; From 301842dceb9bce521be5962c02d277467595885f Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 10:12:13 +0000 Subject: [PATCH 14/81] Try merged version of blit methods. --- extmod/modframebuf.c | 115 +++++++++++++------------------------------ 1 file changed, 35 insertions(+), 80 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index ccadc6f44eafc..05c03371368c4 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -776,88 +776,40 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { mp_int_t x = mp_obj_get_int(args_in[2]); mp_int_t y = mp_obj_get_int(args_in[3]); + mp_int_t key = -1; + size_t alpha_mul = 0; + mp_obj_framebuf_t mask; if (n_args > 4) { - key = mp_obj_get_int(args_in[4]); - } - mp_obj_framebuf_t palette; - palette.buf = NULL; - if (n_args > 5 && args_in[5] != mp_const_none) { - get_readonly_framebuffer(args_in[5], &palette); - } - - if ( - (x >= self->width) || - (y >= self->height) || - (-x >= source.width) || - (-y >= source.height) - ) { - // Out of bounds, no-op. - return mp_const_none; - } - - // Clip. - int x0 = MAX(0, x); - int y0 = MAX(0, y); - int x1 = MAX(0, -x); - int y1 = MAX(0, -y); - int x0end = MIN(self->width, x + source.width); - int y0end = MIN(self->height, y + source.height); - - for (; y0 < y0end; ++y0) { - int cx1 = x1; - for (int cx0 = x0; cx0 < x0end; ++cx0) { - uint32_t col = getpixel(&source, cx1, y1); - if (palette.buf) { - col = getpixel(&palette, col, 0); + if (mp_obj_get_type(args_in[4]) == &mp_type_int) { + key = mp_obj_get_int(args_in[4]); + } else { + get_readonly_framebuffer(args_in[4], &mask); + if (mask.width != source.width || mask.height != source.height) { + // mask and source must be the same shape + mp_raise_ValueError(MP_ERROR_TEXT("Mask and source different sizes.")); } - if (col != (uint32_t)key) { - setpixel(self, cx0, y0, col); + switch (mask.format) { + case FRAMEBUF_MVLSB: + case FRAMEBUF_MHLSB: + case FRAMEBUF_MHMSB: + alpha_mul = 0x100; + break; + case FRAMEBUF_GS8: + alpha_mul = 1; + break; + case FRAMEBUF_GS4_HMSB: + alpha_mul = 0x11; + break; + case FRAMEBUF_GS2_HMSB: + alpha_mul = 0x1111; + break; + default: + // other formats can't easily be converted to alpha + mp_raise_ValueError(MP_ERROR_TEXT("invalid mask format")); } - ++cx1; } - ++y1; - } - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 6, framebuf_blit); - -static mp_obj_t framebuf_blit_mask(size_t n_args, const mp_obj_t *args_in) { - mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); - - mp_obj_framebuf_t source; - get_readonly_framebuffer(args_in[1], &source); - - mp_int_t x = mp_obj_get_int(args_in[2]); - mp_int_t y = mp_obj_get_int(args_in[3]); - - mp_obj_framebuf_t mask; - get_readonly_framebuffer(args_in[4], &mask); - if (mask.width != source.width || mask.height != source.height) { - // mask and source must be the same shape - mp_raise_ValueError(MP_ERROR_TEXT("Mask and source different sizes.")); } - size_t alpha_mul; - switch (mask.format) { - case FRAMEBUF_MVLSB: - case FRAMEBUF_MHLSB: - case FRAMEBUF_MHMSB: - alpha_mul = 0x100; - break; - case FRAMEBUF_GS8: - alpha_mul = 1; - break; - case FRAMEBUF_GS4_HMSB: - alpha_mul = 0x11; - break; - case FRAMEBUF_GS2_HMSB: - alpha_mul = 0x1111; - break; - default: - // other formats can't easily be converted to alpha - mp_raise_ValueError(MP_ERROR_TEXT("invalid mask format")); - } - mp_obj_framebuf_t palette; palette.buf = NULL; if (n_args > 5 && args_in[5] != mp_const_none) { @@ -889,15 +841,19 @@ static mp_obj_t framebuf_blit_mask(size_t n_args, const mp_obj_t *args_in) { if (palette.buf) { col = getpixel(&palette, col, 0); } - uint32_t alpha = getpixel(&mask, cx1, y1) * alpha_mul; - setpixel_alpha(self, cx0, y0, col, alpha); + if (alpha_mul) { + uint32_t alpha = getpixel(&mask, cx1, y1) * alpha_mul; + setpixel_alpha(self, cx0, y0, col, alpha); + } else if (col != (uint32_t)key) { + setpixel(self, cx0, y0, col); + } ++cx1; } ++y1; } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_mask_obj, 5, 6, framebuf_blit_mask); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 6, framebuf_blit); static mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); @@ -996,7 +952,6 @@ static const mp_rom_map_elem_t framebuf_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_poly), MP_ROM_PTR(&framebuf_poly_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) }, - { MP_ROM_QSTR(MP_QSTR_blit_mask), MP_ROM_PTR(&framebuf_blit_mask_obj) }, { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) }, { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) }, }; From 03eca9dd85877f478fda92011d36bc8c073e6e34 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 10:31:24 +0000 Subject: [PATCH 15/81] Add an alpha paramter. --- extmod/modframebuf.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 05c03371368c4..2ae9fd24b6709 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -778,13 +778,23 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { mp_int_t y = mp_obj_get_int(args_in[3]); mp_int_t key = -1; - size_t alpha_mul = 0; - mp_obj_framebuf_t mask; if (n_args > 4) { - if (mp_obj_get_type(args_in[4]) == &mp_type_int) { - key = mp_obj_get_int(args_in[4]); + key = mp_obj_get_int(args_in[4]); + } + mp_obj_framebuf_t palette; + palette.buf = NULL; + if (n_args > 5 && args_in[5] != mp_const_none) { + get_readonly_framebuffer(args_in[5], &palette); + } + + mp_int_t alpha = 0x100; + mp_int_t alpha_mul = 0; + mp_obj_framebuf_t mask; + if (n_args > 6 && args_in[6] != mp_const_none) { + if (mp_obj_get_type(args_in[6]) == &mp_type_int) { + alpha = mp_obj_get_int(args_in[6]); } else { - get_readonly_framebuffer(args_in[4], &mask); + get_readonly_framebuffer(args_in[6], &mask); if (mask.width != source.width || mask.height != source.height) { // mask and source must be the same shape mp_raise_ValueError(MP_ERROR_TEXT("Mask and source different sizes.")); @@ -810,11 +820,6 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { } } } - mp_obj_framebuf_t palette; - palette.buf = NULL; - if (n_args > 5 && args_in[5] != mp_const_none) { - get_readonly_framebuffer(args_in[5], &palette); - } if ( (x >= self->width) || @@ -842,10 +847,10 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { col = getpixel(&palette, col, 0); } if (alpha_mul) { - uint32_t alpha = getpixel(&mask, cx1, y1) * alpha_mul; + alpha = getpixel(&mask, cx1, y1) * alpha_mul; + } + if (col != (uint32_t)key) { setpixel_alpha(self, cx0, y0, col, alpha); - } else if (col != (uint32_t)key) { - setpixel(self, cx0, y0, col); } ++cx1; } @@ -853,7 +858,7 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 6, framebuf_blit); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 7, framebuf_blit); static mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); From 87daa742486d026e32a4c08b577e986d0e582123 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 10:59:31 +0000 Subject: [PATCH 16/81] Add flag for alpha support. --- extmod/modframebuf.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 2ae9fd24b6709..f4d891e900cc9 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -255,6 +255,7 @@ static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, uns return formats[fb->format].getpixel(fb, x, y); } +#if MICROPY_PY_FRAMEBUF_ALPHA typedef struct __attribute__((packed)) rgb565 { uint8_t b : 5; uint8_t g : 6; @@ -295,6 +296,7 @@ static void setpixel_alpha(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, } formats[fb->format].setpixel(fb, x, y, col); } +#endif static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) { @@ -787,6 +789,7 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { get_readonly_framebuffer(args_in[5], &palette); } + #if MICROPY_PY_FRAMEBUF_ALPHA mp_int_t alpha = 0x100; mp_int_t alpha_mul = 0; mp_obj_framebuf_t mask; @@ -819,6 +822,7 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { mp_raise_ValueError(MP_ERROR_TEXT("invalid mask format")); } } + #endif } if ( @@ -846,12 +850,19 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { if (palette.buf) { col = getpixel(&palette, col, 0); } + + #if MICROPY_PY_FRAMEBUF_ALPHA if (alpha_mul) { alpha = getpixel(&mask, cx1, y1) * alpha_mul; } if (col != (uint32_t)key) { setpixel_alpha(self, cx0, y0, col, alpha); } + #else + if (col != (uint32_t)key) { + setpixel(self, cx0, y0, col); + } + #endif ++cx1; } ++y1; From 46de420d270e41963ff0b12495bba1913fecded2 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 11:15:01 +0000 Subject: [PATCH 17/81] Add flags properly; optimize a bit. --- examples/natmod/framebuf/framebuf.c | 1 + extmod/modframebuf.c | 61 ++++++++++++++++------------- py/mpconfig.h | 5 +++ 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c index 5fd7c6be3a456..dd3c7f035385c 100644 --- a/examples/natmod/framebuf/framebuf.c +++ b/examples/natmod/framebuf/framebuf.c @@ -1,5 +1,6 @@ #define MICROPY_PY_ARRAY (1) #define MICROPY_PY_FRAMEBUF (1) +#define MICROPY_PY_FRAMEBUF_ALPHA (1) #include "py/dynruntime.h" diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index f4d891e900cc9..ed5499bf6492e 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -296,7 +296,7 @@ static void setpixel_alpha(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, } formats[fb->format].setpixel(fb, x, y, col); } -#endif +#endif // MICROPY_PY_FRAMEBUF_ALPHA static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) { @@ -779,6 +779,25 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { mp_int_t x = mp_obj_get_int(args_in[2]); mp_int_t y = mp_obj_get_int(args_in[3]); + if ( + (x >= self->width) || + (y >= self->height) || + (-x >= source.width) || + (-y >= source.height) + ) { + // Out of bounds, no-op, leave early. + return mp_const_none; + } + + // Clip. + int x0 = MAX(0, x); + int y0 = MAX(0, y); + int x1 = MAX(0, -x); + int y1 = MAX(0, -y); + int x0end = MIN(self->width, x + source.width); + int y0end = MIN(self->height, y + source.height); + + // Key and palette argument handling. mp_int_t key = -1; if (n_args > 4) { key = mp_obj_get_int(args_in[4]); @@ -822,27 +841,8 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { mp_raise_ValueError(MP_ERROR_TEXT("invalid mask format")); } } - #endif } - if ( - (x >= self->width) || - (y >= self->height) || - (-x >= source.width) || - (-y >= source.height) - ) { - // Out of bounds, no-op. - return mp_const_none; - } - - // Clip. - int x0 = MAX(0, x); - int y0 = MAX(0, y); - int x1 = MAX(0, -x); - int y1 = MAX(0, -y); - int x0end = MIN(self->width, x + source.width); - int y0end = MIN(self->height, y + source.height); - for (; y0 < y0end; ++y0) { int cx1 = x1; for (int cx0 = x0; cx0 < x0end; ++cx0) { @@ -850,23 +850,30 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { if (palette.buf) { col = getpixel(&palette, col, 0); } - - #if MICROPY_PY_FRAMEBUF_ALPHA - if (alpha_mul) { - alpha = getpixel(&mask, cx1, y1) * alpha_mul; - } + alpha = getpixel(&mask, cx1, y1) * alpha_mul; if (col != (uint32_t)key) { setpixel_alpha(self, cx0, y0, col, alpha); } - #else + ++cx1; + } + ++y1; + } + #else + for (; y0 < y0end; ++y0) { + int cx1 = x1; + for (int cx0 = x0; cx0 < x0end; ++cx0) { + uint32_t col = getpixel(&source, cx1, y1); + if (palette.buf) { + col = getpixel(&palette, col, 0); + } if (col != (uint32_t)key) { setpixel(self, cx0, y0, col); } - #endif ++cx1; } ++y1; } + #endif // MICROPY_PY_FRAMEBUF_ALPHA return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 7, framebuf_blit); diff --git a/py/mpconfig.h b/py/mpconfig.h index 97c40389b32f0..4d84dc2ff81b4 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -2111,6 +2111,11 @@ typedef time_t mp_timestamp_t; #define MICROPY_PY_FRAMEBUF (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +// Whether to support alpha blending in framebuf module +#ifndef MICROPY_PY_FRAMEBUF_ALPHA +#define MICROPY_PY_FRAMEBUF_ALPHA (MICROPY_PY_FRAMEBUF && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + #ifndef MICROPY_PY_BTREE #define MICROPY_PY_BTREE (0) #endif From 1cdd46de7ad7f7f67e307d4197deb3b8f3cb88cd Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 11:31:46 +0000 Subject: [PATCH 18/81] More fixes, make tests pass on unix. --- extmod/modframebuf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index ed5499bf6492e..de11defbfbb81 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -850,7 +850,9 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { if (palette.buf) { col = getpixel(&palette, col, 0); } - alpha = getpixel(&mask, cx1, y1) * alpha_mul; + if (alpha_mul) { + alpha = getpixel(&mask, cx1, y1) * alpha_mul; + } if (col != (uint32_t)key) { setpixel_alpha(self, cx0, y0, col, alpha); } From 6964f3b7ff192d20e51efcf1b9f9165c786daada Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 12:07:09 +0000 Subject: [PATCH 19/81] Add a simple test for fixed alpha. --- extmod/modframebuf.c | 17 ++++++++------- tests/extmod/framebuf_alpha.py | 35 ++++++++++++++++++++++++++++++ tests/extmod/framebuf_alpha.py.exp | 24 ++++++++++++++++++++ 3 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 tests/extmod/framebuf_alpha.py create mode 100644 tests/extmod/framebuf_alpha.py.exp diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index de11defbfbb81..280930edf4a26 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -789,14 +789,6 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { return mp_const_none; } - // Clip. - int x0 = MAX(0, x); - int y0 = MAX(0, y); - int x1 = MAX(0, -x); - int y1 = MAX(0, -y); - int x0end = MIN(self->width, x + source.width); - int y0end = MIN(self->height, y + source.height); - // Key and palette argument handling. mp_int_t key = -1; if (n_args > 4) { @@ -808,6 +800,14 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { get_readonly_framebuffer(args_in[5], &palette); } + // Clip. + int x0 = MAX(0, x); + int y0 = MAX(0, y); + int x1 = MAX(0, -x); + int y1 = MAX(0, -y); + int x0end = MIN(self->width, x + source.width); + int y0end = MIN(self->height, y + source.height); + #if MICROPY_PY_FRAMEBUF_ALPHA mp_int_t alpha = 0x100; mp_int_t alpha_mul = 0; @@ -1013,6 +1013,7 @@ static const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) }, { MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_ROM_INT(FRAMEBUF_MHLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_ROM_INT(FRAMEBUF_MHMSB) }, + { MP_ROM_QSTR(MP_QSTR_ALPHA), MP_ROM_INT(MICROPY_PY_FRAMEBUF_ALPHA) }, }; static MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table); diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py new file mode 100644 index 0000000000000..80a7d9c27068e --- /dev/null +++ b/tests/extmod/framebuf_alpha.py @@ -0,0 +1,35 @@ +# Test FrameBuffer.blit method. + +try: + import framebuf +except ImportError: + print("SKIP") + raise SystemExit + +if not framebuf.ALPHA: + print("SKIP") + raise SystemExit + + +def printbuf(): + print("--8<--") + for y in range(h): + for x in range(w): + print("%02x" % buf[(x + y * w)], end="") + print() + print("-->8--") + + +w = 5 +h = 4 +buf = bytearray(w * h) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) + +fbuf2 = framebuf.FrameBuffer(bytearray(4), 2, 2, framebuf.GS8) +fbuf2.fill(0xFF) + +# Blit another FrameBuffer, at various locations with alpha. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.blit(fbuf2, x, y, -1, None, 0x7F) + printbuf() diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp new file mode 100644 index 0000000000000..68ab09ee7b2ff --- /dev/null +++ b/tests/extmod/framebuf_alpha.py.exp @@ -0,0 +1,24 @@ +--8<-- +7f00000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +7f7f000000 +7f7f000000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 +007f7f0000 +007f7f0000 +0000000000 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +000000007f +-->8-- From 4e3021c5749ab97d31fc56325fd736c34d3c5599 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 14:08:02 +0000 Subject: [PATCH 20/81] Add more tests. --- examples/natmod/framebuf/framebuf.c | 1 + tests/extmod/framebuf_alpha.py | 75 +++++++++++++++- tests/extmod/framebuf_alpha.py.exp | 132 ++++++++++++++++++++++++++-- 3 files changed, 198 insertions(+), 10 deletions(-) diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c index dd3c7f035385c..0369a475b4da7 100644 --- a/examples/natmod/framebuf/framebuf.c +++ b/examples/natmod/framebuf/framebuf.c @@ -50,6 +50,7 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a mp_store_global(MP_QSTR_GS8, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS8)); mp_store_global(MP_QSTR_MONO_HLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MHLSB)); mp_store_global(MP_QSTR_MONO_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MHMSB)); + mp_store_global(MP_QSTR_ALPHA, MP_OBJ_NEW_SMALL_INT(MICROPY_PY_FRAMEBUF_ALPHA)); MP_DYNRUNTIME_INIT_EXIT } diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index 80a7d9c27068e..229fd73453587 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -1,5 +1,6 @@ # Test FrameBuffer.blit method. +import sys try: import framebuf except ImportError: @@ -10,12 +11,17 @@ print("SKIP") raise SystemExit +# This test and its .exp file is based on a little-endian architecture. +if sys.byteorder != "little": + print("SKIP") + raise SystemExit + -def printbuf(): +def printbuf(bpp=1): print("--8<--") for y in range(h): - for x in range(w): - print("%02x" % buf[(x + y * w)], end="") + for x in range(w * bpp): + print("%02x" % buf[(x + y * w * bpp)], end="") print() print("-->8--") @@ -26,10 +32,71 @@ def printbuf(): fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) fbuf2 = framebuf.FrameBuffer(bytearray(4), 2, 2, framebuf.GS8) -fbuf2.fill(0xFF) +fbuf2.fill(0x7f) # Blit another FrameBuffer, at various locations with alpha. for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): fbuf.fill(0) fbuf.blit(fbuf2, x, y, -1, None, 0x7F) printbuf() + +# Blit another FrameBuffer, with alpha mask. +alphas = [[0, 0x3f], [0x7f, 0xff]] +for bpp, format in [(8, framebuf.GS8), (4, framebuf.GS4_HMSB), (2, framebuf.GS2_HMSB), (1, framebuf.MONO_HLSB)]: + mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) + for x in [0, 1]: + for y in [0, 1]: + mask.pixel(x, y, alphas[x][y] >> (8 - bpp)) + + fbuf.fill(0) + fbuf.blit(fbuf2, 1, 1, -1, None, mask) + printbuf() + +# Blit another FrameBuffer, with alpha mask, non-black background. +alphas = [[0, 0x3f], [0x7f, 0xff]] +for bpp, format in [(8, framebuf.GS8), (4, framebuf.GS4_HMSB), (2, framebuf.GS2_HMSB), (1, framebuf.MONO_HLSB)]: + mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) + for x in [0, 1]: + for y in [0, 1]: + mask.pixel(x, y, alphas[x][y] >> (8 - bpp)) + + fbuf.fill(0xef) + fbuf.blit(fbuf2, 1, 1, -1, None, mask) + printbuf() + +# Now in color +buf = bytearray(2 * w * h) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565) + +fbuf2 = framebuf.FrameBuffer(bytearray(8), 2, 2, framebuf.RGB565) +fbuf2.fill(0b1111101111100000) + +# Blit a color FrameBuffer, at various locations with alpha. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.blit(fbuf2, x, y, -1, None, 0x7F) + printbuf(2) + +# Blit a color FrameBuffer, with alpha mask. +alphas = [[0, 0x3f], [0x7f, 0xff]] +for bpp, format in [(8, framebuf.GS8), (4, framebuf.GS4_HMSB), (2, framebuf.GS2_HMSB), (1, framebuf.MONO_HLSB)]: + mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) + for x in [0, 1]: + for y in [0, 1]: + mask.pixel(x, y, alphas[x][y] >> (8 - bpp)) + + fbuf.fill(0) + fbuf.blit(fbuf2, 1, 1, -1, None, mask) + printbuf(2) + +# Blit a color FrameBuffer, with alpha mask, non-black background. +alphas = [[0, 0x3f], [0x7f, 0xff]] +for bpp, format in [(8, framebuf.GS8), (4, framebuf.GS4_HMSB), (2, framebuf.GS2_HMSB), (1, framebuf.MONO_HLSB)]: + mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) + for x in [0, 1]: + for y in [0, 1]: + mask.pixel(x, y, alphas[x][y] >> (8 - bpp)) + + fbuf.fill(0b00000_111111_00000) + fbuf.blit(fbuf2, 1, 1, -1, None, mask) + printbuf(2) diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index 68ab09ee7b2ff..0a82799c9878e 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -1,24 +1,144 @@ --8<-- -7f00000000 +3f00000000 0000000000 0000000000 0000000000 -->8-- --8<-- -7f7f000000 -7f7f000000 +3f3f000000 +3f3f000000 0000000000 0000000000 -->8-- --8<-- 0000000000 -007f7f0000 -007f7f0000 +003f3f0000 +003f3f0000 0000000000 -->8-- --8<-- 0000000000 0000000000 0000000000 -000000007f +000000003f +-->8-- +--8<-- +0000000000 +00003f0000 +001f7f0000 +0000000000 +-->8-- +--8<-- +0000000000 +00003b0000 +00197f0000 +0000000000 +-->8-- +--8<-- +0000000000 +00007f0000 +00007f0000 +0000000000 +-->8-- +--8<-- +0000000000 +0000000000 +00007f0000 +0000000000 +-->8-- +--8<-- +efefefefef +efefb7efef +efd37fefef +efefefefef +-->8-- +--8<-- +efefefefef +efefbbefef +efd97fefef +efefefefef +-->8-- +--8<-- +efefefefef +efef7fefef +efef7fefef +efefefefef +-->8-- +--8<-- +efefefefef +efefefefef +efef7fefef +efefefefef +-->8-- +--8<-- +e0790000000000000000 +00000000000000000000 +00000000000000000000 +00000000000000000000 +-->8-- +--8<-- +e079e079000000000000 +e079e079000000000000 +00000000000000000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +0000e079e07900000000 +0000e079e07900000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000000000000000 +00000000000000000000 +0000000000000000e079 +-->8-- +--8<-- +00000000000000000000 +00000000e07900000000 +0000e038e0fb00000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000c07100000000 +0000c030e0fb00000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000e0fb00000000 +00000000e0fb00000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000000000000000 +00000000e0fb00000000 +00000000000000000000 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e07de007e007 +e007e03ee0fbe007e007 +e007e007e007e007e007 +-->8-- +--8<-- +e007e007e007e007e007 +e007e0070076e007e007 +e0070037e0fbe007e007 +e007e007e007e007e007 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e0fbe007e007 +e007e007e0fbe007e007 +e007e007e007e007e007 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e007e007e007 +e007e007e0fbe007e007 +e007e007e007e007e007 -->8-- From 13e6204856b8397c5efb7d36f9a6838d76cd97df Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 14:21:56 +0000 Subject: [PATCH 21/81] Do something sensible for small masks. --- extmod/modframebuf.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 280930edf4a26..1353bf7268242 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -817,10 +817,6 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { alpha = mp_obj_get_int(args_in[6]); } else { get_readonly_framebuffer(args_in[6], &mask); - if (mask.width != source.width || mask.height != source.height) { - // mask and source must be the same shape - mp_raise_ValueError(MP_ERROR_TEXT("Mask and source different sizes.")); - } switch (mask.format) { case FRAMEBUF_MVLSB: case FRAMEBUF_MHLSB: @@ -851,7 +847,8 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { col = getpixel(&palette, col, 0); } if (alpha_mul) { - alpha = getpixel(&mask, cx1, y1) * alpha_mul; + // if source bigger than mask, wrap around + alpha = getpixel(&mask, cx1 % mask.width, y1 % mask.height) * alpha_mul; } if (col != (uint32_t)key) { setpixel_alpha(self, cx0, y0, col, alpha); From ed78bdf20cb1c9b79d6ae541b8ce89c9b83883d8 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 14:40:18 +0000 Subject: [PATCH 22/81] Fix codestyle. --- tests/extmod/framebuf_alpha.py | 41 +++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index 229fd73453587..cf295e4956185 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -1,6 +1,7 @@ # Test FrameBuffer.blit method. import sys + try: import framebuf except ImportError: @@ -32,7 +33,7 @@ def printbuf(bpp=1): fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) fbuf2 = framebuf.FrameBuffer(bytearray(4), 2, 2, framebuf.GS8) -fbuf2.fill(0x7f) +fbuf2.fill(0x7F) # Blit another FrameBuffer, at various locations with alpha. for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): @@ -41,8 +42,13 @@ def printbuf(bpp=1): printbuf() # Blit another FrameBuffer, with alpha mask. -alphas = [[0, 0x3f], [0x7f, 0xff]] -for bpp, format in [(8, framebuf.GS8), (4, framebuf.GS4_HMSB), (2, framebuf.GS2_HMSB), (1, framebuf.MONO_HLSB)]: +alphas = [[0, 0x3F], [0x7F, 0xFF]] +for bpp, format in [ + (8, framebuf.GS8), + (4, framebuf.GS4_HMSB), + (2, framebuf.GS2_HMSB), + (1, framebuf.MONO_HLSB), +]: mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) for x in [0, 1]: for y in [0, 1]: @@ -53,14 +59,19 @@ def printbuf(bpp=1): printbuf() # Blit another FrameBuffer, with alpha mask, non-black background. -alphas = [[0, 0x3f], [0x7f, 0xff]] -for bpp, format in [(8, framebuf.GS8), (4, framebuf.GS4_HMSB), (2, framebuf.GS2_HMSB), (1, framebuf.MONO_HLSB)]: +alphas = [[0, 0x3F], [0x7F, 0xFF]] +for bpp, format in [ + (8, framebuf.GS8), + (4, framebuf.GS4_HMSB), + (2, framebuf.GS2_HMSB), + (1, framebuf.MONO_HLSB), +]: mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) for x in [0, 1]: for y in [0, 1]: mask.pixel(x, y, alphas[x][y] >> (8 - bpp)) - fbuf.fill(0xef) + fbuf.fill(0xEF) fbuf.blit(fbuf2, 1, 1, -1, None, mask) printbuf() @@ -78,8 +89,13 @@ def printbuf(bpp=1): printbuf(2) # Blit a color FrameBuffer, with alpha mask. -alphas = [[0, 0x3f], [0x7f, 0xff]] -for bpp, format in [(8, framebuf.GS8), (4, framebuf.GS4_HMSB), (2, framebuf.GS2_HMSB), (1, framebuf.MONO_HLSB)]: +alphas = [[0, 0x3F], [0x7F, 0xFF]] +for bpp, format in [ + (8, framebuf.GS8), + (4, framebuf.GS4_HMSB), + (2, framebuf.GS2_HMSB), + (1, framebuf.MONO_HLSB), +]: mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) for x in [0, 1]: for y in [0, 1]: @@ -90,8 +106,13 @@ def printbuf(bpp=1): printbuf(2) # Blit a color FrameBuffer, with alpha mask, non-black background. -alphas = [[0, 0x3f], [0x7f, 0xff]] -for bpp, format in [(8, framebuf.GS8), (4, framebuf.GS4_HMSB), (2, framebuf.GS2_HMSB), (1, framebuf.MONO_HLSB)]: +alphas = [[0, 0x3F], [0x7F, 0xFF]] +for bpp, format in [ + (8, framebuf.GS8), + (4, framebuf.GS4_HMSB), + (2, framebuf.GS2_HMSB), + (1, framebuf.MONO_HLSB), +]: mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) for x in [0, 1]: for y in [0, 1]: From 05b55c9c38a1c8a45858ca297352a017c14d0474 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 14:48:29 +0000 Subject: [PATCH 23/81] Attempt to fix problem with mod on ESP8266. --- extmod/modframebuf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 1353bf7268242..6efad78b396c2 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -803,8 +803,8 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { // Clip. int x0 = MAX(0, x); int y0 = MAX(0, y); - int x1 = MAX(0, -x); - int y1 = MAX(0, -y); + mp_int_t x1 = MAX(0, -x); + mp_int_t y1 = MAX(0, -y); int x0end = MIN(self->width, x + source.width); int y0end = MIN(self->height, y + source.height); @@ -840,7 +840,7 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { } for (; y0 < y0end; ++y0) { - int cx1 = x1; + mp_int_t cx1 = x1; for (int cx0 = x0; cx0 < x0end; ++cx0) { uint32_t col = getpixel(&source, cx1, y1); if (palette.buf) { From 7ce6632dd15f16a83e1ed86d295f1e6fb908628c Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 15:29:57 +0000 Subject: [PATCH 24/81] Revert wrapping of masks. --- extmod/modframebuf.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 6efad78b396c2..280930edf4a26 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -803,8 +803,8 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { // Clip. int x0 = MAX(0, x); int y0 = MAX(0, y); - mp_int_t x1 = MAX(0, -x); - mp_int_t y1 = MAX(0, -y); + int x1 = MAX(0, -x); + int y1 = MAX(0, -y); int x0end = MIN(self->width, x + source.width); int y0end = MIN(self->height, y + source.height); @@ -817,6 +817,10 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { alpha = mp_obj_get_int(args_in[6]); } else { get_readonly_framebuffer(args_in[6], &mask); + if (mask.width != source.width || mask.height != source.height) { + // mask and source must be the same shape + mp_raise_ValueError(MP_ERROR_TEXT("Mask and source different sizes.")); + } switch (mask.format) { case FRAMEBUF_MVLSB: case FRAMEBUF_MHLSB: @@ -840,15 +844,14 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { } for (; y0 < y0end; ++y0) { - mp_int_t cx1 = x1; + int cx1 = x1; for (int cx0 = x0; cx0 < x0end; ++cx0) { uint32_t col = getpixel(&source, cx1, y1); if (palette.buf) { col = getpixel(&palette, col, 0); } if (alpha_mul) { - // if source bigger than mask, wrap around - alpha = getpixel(&mask, cx1 % mask.width, y1 % mask.height) * alpha_mul; + alpha = getpixel(&mask, cx1, y1) * alpha_mul; } if (col != (uint32_t)key) { setpixel_alpha(self, cx0, y0, col, alpha); From 450e04c03d27a1379e28cf120e71db2c48e98b23 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 15:38:15 +0000 Subject: [PATCH 25/81] Add alpha argument to pixel method. --- extmod/modframebuf.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 280930edf4a26..2fa2fe566f7e0 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -422,12 +422,16 @@ static mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args_in) { return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y)); } else { // set - setpixel(self, x, y, mp_obj_get_int(args_in[3])); + mp_int_t alpha = 0x100; + if (n_args == 4) { + alpha = mp_obj_get_int(args_in[4]); + } + setpixel_alpha(self, x, y, mp_obj_get_int(args_in[3]), alpha); } } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 5, framebuf_pixel); static mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args_in) { (void)n_args; From ea094f52e1826c25716e9da76e0d5b7b221e8b47 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 18 Nov 2025 15:46:38 +0000 Subject: [PATCH 26/81] Properly handle case where no alpha turned on. --- extmod/modframebuf.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 2fa2fe566f7e0..f33a365b99e36 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -422,11 +422,13 @@ static mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args_in) { return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y)); } else { // set - mp_int_t alpha = 0x100; + #if MICROPY_PY_FRAMEBUF_ALPHA if (n_args == 4) { - alpha = mp_obj_get_int(args_in[4]); + setpixel_alpha(self, x, y, mp_obj_get_int(args_in[3]), mp_obj_get_int(args_in[4])); + return mp_const_none; } - setpixel_alpha(self, x, y, mp_obj_get_int(args_in[3]), alpha); + #endif //MICROPY_PY_FRAMEBUF_ALPHA + setpixel(self, x, y, mp_obj_get_int(args_in[3])); } } return mp_const_none; From 8d89f5e6e5d3ba58e8d9f471d4d0ae77834ac535 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 19 Nov 2025 08:36:41 +0000 Subject: [PATCH 27/81] Fix arguments, codestyle. --- extmod/modframebuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index f33a365b99e36..076c104b35b9d 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -423,11 +423,11 @@ static mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args_in) { } else { // set #if MICROPY_PY_FRAMEBUF_ALPHA - if (n_args == 4) { + if (n_args >= 5) { setpixel_alpha(self, x, y, mp_obj_get_int(args_in[3]), mp_obj_get_int(args_in[4])); return mp_const_none; } - #endif //MICROPY_PY_FRAMEBUF_ALPHA + #endif // MICROPY_PY_FRAMEBUF_ALPHA setpixel(self, x, y, mp_obj_get_int(args_in[3])); } } From 4e395dfd382d9efc610a13f46f629e233af778a9 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 19 Nov 2025 10:47:25 +0000 Subject: [PATCH 28/81] Refactoring; make fill_rect() and pixel() work; tests. --- extmod/modframebuf.c | 175 +++++++++++++++-------------- tests/extmod/framebuf_alpha.py | 12 ++ tests/extmod/framebuf_alpha.py.exp | 48 ++++++++ 3 files changed, 150 insertions(+), 85 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 076c104b35b9d..9a7034d3d6d72 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -241,19 +241,6 @@ static mp_framebuf_p_t formats[] = { [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect}, }; -static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { - formats[fb->format].setpixel(fb, x, y, col); -} - -static void setpixel_checked(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask) { - if (mask && 0 <= x && x < fb->width && 0 <= y && y < fb->height) { - setpixel(fb, x, y, col); - } -} - -static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { - return formats[fb->format].getpixel(fb, x, y); -} #if MICROPY_PY_FRAMEBUF_ALPHA typedef struct __attribute__((packed)) rgb565 { @@ -271,34 +258,58 @@ static uint32_t alpha_blend(uint32_t c1, uint32_t c2, uint32_t alpha) { return ((c1 * (0x100 - alpha)) + (alpha + 1) * c2) >> 8; } -static void setpixel_alpha(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t alpha) { +static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32_t col, mp_int_t alpha) { if (alpha <= 0) { // nothing to do return; - } else if (alpha >= 0xff) { - // no blending needed - setpixel(fb, x, y, col); + } else if (alpha < 0xff) { + uint16_t pix_col = formats[fb->format].getpixel(fb, x, y); + if (fb->format == FRAMEBUF_RGB565) { + uint16_t col16 = col; + urgb565 pix_col_struct = *(urgb565 *)&pix_col; + urgb565 col_struct = *(urgb565 *)&col16; + col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); + col_struct.rgb.g = alpha_blend(pix_col_struct.rgb.g, col_struct.rgb.g, alpha); + col_struct.rgb.b = alpha_blend(pix_col_struct.rgb.b, col_struct.rgb.b, alpha); + col_struct.rgb.r = MIN(col_struct.rgb.r, 0b11111); + col_struct.rgb.g = MIN(col_struct.rgb.g, 0b111111); + col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); + col = *(uint16_t *)&col_struct; + } else { + col = alpha_blend(pix_col, col, alpha); + } + } + formats[fb->format].setpixel(fb, x, y, col); +} + +static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col, mp_int_t alpha) { + if (alpha == 0 || h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) { + // No operation needed. + return; } - uint16_t pix_col = formats[fb->format].getpixel(fb, x, y); - if (fb->format == FRAMEBUF_RGB565) { - uint16_t col16 = col; - urgb565 pix_col_struct = *(urgb565 *)&pix_col; - urgb565 col_struct = *(urgb565 *)&col16; - col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); - col_struct.rgb.g = alpha_blend(pix_col_struct.rgb.g, col_struct.rgb.g, alpha); - col_struct.rgb.b = alpha_blend(pix_col_struct.rgb.b, col_struct.rgb.b, alpha); - col_struct.rgb.r = MIN(col_struct.rgb.r, 0b11111); - col_struct.rgb.g = MIN(col_struct.rgb.g, 0b111111); - col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); - col = *(uint16_t *)&col_struct; + + // clip to the framebuffer + int xend = MIN(fb->width, x + w); + int yend = MIN(fb->height, y + h); + x = MAX(x, 0); + y = MAX(y, 0); + + if (alpha >= 0xff) { + formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col); } else { - col = alpha_blend(pix_col, col, alpha); + for (; y < yend; ++y) { + for (int x0 = x; x0 < xend; ++x0) { + setpixel(fb, x0, y, col, alpha); + } + } } +} +#else +static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col, mp_int_t alpha) { formats[fb->format].setpixel(fb, x, y, col); } -#endif // MICROPY_PY_FRAMEBUF_ALPHA -static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { +static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col, mp_int_t alpha) { if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) { // No operation needed. return; @@ -312,6 +323,17 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col); } +#endif // MICROPY_PY_FRAMEBUF_ALPHA + +static void setpixel_checked(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask, mp_int_t alpha) { + if (mask && alpha > 0 && 0 <= x && x < fb->width && 0 <= y && y < fb->height) { + setpixel(fb, x, y, col, alpha); + } +} + +static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { + return formats[fb->format].getpixel(fb, x, y); +} static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, unsigned int buf_flags, mp_obj_framebuf_t *o) { @@ -405,12 +427,13 @@ static MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill); static mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); - mp_int_t args[5]; // x, y, w, h, col - framebuf_args(args_in, args, 5); - fill_rect(self, args[0], args[1], args[2], args[3], args[4]); + mp_int_t args[6]; // x, y, w, h, col, alpha + args[5] = 0x100; + framebuf_args(args_in, args, n_args - 1); + fill_rect(self, args[0], args[1], args[2], args[3], args[4], args[5]); return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 7, framebuf_fill_rect); static mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); @@ -422,13 +445,8 @@ static mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args_in) { return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y)); } else { // set - #if MICROPY_PY_FRAMEBUF_ALPHA - if (n_args >= 5) { - setpixel_alpha(self, x, y, mp_obj_get_int(args_in[3]), mp_obj_get_int(args_in[4])); - return mp_const_none; - } - #endif // MICROPY_PY_FRAMEBUF_ALPHA - setpixel(self, x, y, mp_obj_get_int(args_in[3])); + mp_int_t alpha = (n_args >= 5) ? mp_obj_get_int(args_in[4]) : 0x100; + setpixel(self, x, y, mp_obj_get_int(args_in[3]), alpha); } } return mp_const_none; @@ -442,7 +460,7 @@ static mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args_in) { mp_int_t args[4]; // x, y, w, col framebuf_args(args_in, args, 4); - fill_rect(self, args[0], args[1], args[2], 1, args[3]); + fill_rect(self, args[0], args[1], args[2], 1, args[3], 0x100); return mp_const_none; } @@ -455,7 +473,7 @@ static mp_obj_t framebuf_vline(size_t n_args, const mp_obj_t *args_in) { mp_int_t args[4]; // x, y, h, col framebuf_args(args_in, args, 4); - fill_rect(self, args[0], args[1], 1, args[2], args[3]); + fill_rect(self, args[0], args[1], 1, args[2], args[3], 0x100); return mp_const_none; } @@ -466,12 +484,12 @@ static mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args_in) { mp_int_t args[5]; // x, y, w, h, col framebuf_args(args_in, args, 5); if (n_args > 6 && mp_obj_is_true(args_in[6])) { - fill_rect(self, args[0], args[1], args[2], args[3], args[4]); + fill_rect(self, args[0], args[1], args[2], args[3], args[4], 0x100); } else { - fill_rect(self, args[0], args[1], args[2], 1, args[4]); - fill_rect(self, args[0], args[1] + args[3] - 1, args[2], 1, args[4]); - fill_rect(self, args[0], args[1], 1, args[3], args[4]); - fill_rect(self, args[0] + args[2] - 1, args[1], 1, args[3], args[4]); + fill_rect(self, args[0], args[1], args[2], 1, args[4], 0x100); + fill_rect(self, args[0], args[1] + args[3] - 1, args[2], 1, args[4], 0x100); + fill_rect(self, args[0], args[1], 1, args[3], args[4], 0x100); + fill_rect(self, args[0] + args[2] - 1, args[1], 1, args[3], args[4], 0x100); } return mp_const_none; } @@ -517,11 +535,11 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t for (mp_int_t i = 0; i < dx; ++i) { if (steep) { if (0 <= y1 && y1 < fb->width && 0 <= x1 && x1 < fb->height) { - setpixel(fb, y1, x1, col); + setpixel(fb, y1, x1, col, 0x100); } } else { if (0 <= x1 && x1 < fb->width && 0 <= y1 && y1 < fb->height) { - setpixel(fb, x1, y1, col); + setpixel(fb, x1, y1, col, 0x100); } } while (e >= 0) { @@ -532,7 +550,7 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t e += 2 * dy; } - setpixel_checked(fb, x2, y2, col, 1); + setpixel_checked(fb, x2, y2, col, 1, 0x100); } static mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) { @@ -560,22 +578,22 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_lin static void draw_ellipse_points(const mp_obj_framebuf_t *fb, mp_int_t cx, mp_int_t cy, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask) { if (mask & ELLIPSE_MASK_FILL) { if (mask & ELLIPSE_MASK_Q1) { - fill_rect(fb, cx, cy - y, x + 1, 1, col); + fill_rect(fb, cx, cy - y, x + 1, 1, col, 0x100); } if (mask & ELLIPSE_MASK_Q2) { - fill_rect(fb, cx - x, cy - y, x + 1, 1, col); + fill_rect(fb, cx - x, cy - y, x + 1, 1, col, 0x100); } if (mask & ELLIPSE_MASK_Q3) { - fill_rect(fb, cx - x, cy + y, x + 1, 1, col); + fill_rect(fb, cx - x, cy + y, x + 1, 1, col, 0x100); } if (mask & ELLIPSE_MASK_Q4) { - fill_rect(fb, cx, cy + y, x + 1, 1, col); + fill_rect(fb, cx, cy + y, x + 1, 1, col, 0x100); } } else { - setpixel_checked(fb, cx + x, cy - y, col, mask & ELLIPSE_MASK_Q1); - setpixel_checked(fb, cx - x, cy - y, col, mask & ELLIPSE_MASK_Q2); - setpixel_checked(fb, cx - x, cy + y, col, mask & ELLIPSE_MASK_Q3); - setpixel_checked(fb, cx + x, cy + y, col, mask & ELLIPSE_MASK_Q4); + setpixel_checked(fb, cx + x, cy - y, col, mask & ELLIPSE_MASK_Q1, 0x100); + setpixel_checked(fb, cx - x, cy - y, col, mask & ELLIPSE_MASK_Q2, 0x100); + setpixel_checked(fb, cx - x, cy + y, col, mask & ELLIPSE_MASK_Q3, 0x100); + setpixel_checked(fb, cx + x, cy + y, col, mask & ELLIPSE_MASK_Q4, 0x100); } } @@ -590,7 +608,7 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { mask |= ELLIPSE_MASK_ALL; } if (args[2] == 0 && args[3] == 0) { - setpixel_checked(self, args[0], args[1], args[4], mask & ELLIPSE_MASK_ALL); + setpixel_checked(self, args[0], args[1], args[4], mask & ELLIPSE_MASK_ALL, 0x100); return mp_const_none; } mp_int_t two_asquare = 2 * args[2] * args[2]; @@ -701,9 +719,9 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { } else if (row == MAX(py1, py2)) { // At local-minima, try and manually fill in the pixels that get missed above. if (py1 < py2) { - setpixel_checked(self, x + px2, y + py2, col, 1); + setpixel_checked(self, x + px2, y + py2, col, 1, 0x100); } else if (py2 < py1) { - setpixel_checked(self, x + px1, y + py1, col, 1); + setpixel_checked(self, x + px1, y + py1, col, 1, 0x100); } else { // Even though this is a hline and would be faster to // use fill_rect, use line() because it handles x2 < @@ -737,7 +755,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // Fill between each pair of nodes. for (i = 0; i < n_nodes; i += 2) { - fill_rect(self, x + nodes[i], y + row, (nodes[i + 1] - nodes[i]) + 1, 1, col); + fill_rect(self, x + nodes[i], y + row, (nodes[i + 1] - nodes[i]) + 1, 1, col, 0x100); } } } else { @@ -814,8 +832,8 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { int x0end = MIN(self->width, x + source.width); int y0end = MIN(self->height, y + source.height); - #if MICROPY_PY_FRAMEBUF_ALPHA mp_int_t alpha = 0x100; + #if MICROPY_PY_FRAMEBUF_ALPHA mp_int_t alpha_mul = 0; mp_obj_framebuf_t mask; if (n_args > 6 && args_in[6] != mp_const_none) { @@ -848,6 +866,7 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { } } } + #endif // MICROPY_PY_FRAMEBUF_ALPHA for (; y0 < y0end; ++y0) { int cx1 = x1; @@ -856,32 +875,18 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { if (palette.buf) { col = getpixel(&palette, col, 0); } + #if MICROPY_PY_FRAMEBUF_ALPHA if (alpha_mul) { alpha = getpixel(&mask, cx1, y1) * alpha_mul; } + #endif // MICROPY_PY_FRAMEBUF_ALPHA if (col != (uint32_t)key) { - setpixel_alpha(self, cx0, y0, col, alpha); - } - ++cx1; - } - ++y1; - } - #else - for (; y0 < y0end; ++y0) { - int cx1 = x1; - for (int cx0 = x0; cx0 < x0end; ++cx0) { - uint32_t col = getpixel(&source, cx1, y1); - if (palette.buf) { - col = getpixel(&palette, col, 0); - } - if (col != (uint32_t)key) { - setpixel(self, cx0, y0, col); + setpixel(self, cx0, y0, col, alpha); } ++cx1; } ++y1; } - #endif // MICROPY_PY_FRAMEBUF_ALPHA return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 7, framebuf_blit); @@ -924,7 +929,7 @@ static mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ys } for (; y != yend; y += dy) { for (unsigned x = sx; x != xend; x += dx) { - setpixel(self, x, y, getpixel(self, x - xstep, y - ystep)); + setpixel(self, x, y, getpixel(self, x - xstep, y - ystep), 0x100); } } return mp_const_none; @@ -958,7 +963,7 @@ static mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args_in) { for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column if (vline_data & 1) { // only draw if pixel set if (0 <= y && y < self->height) { // clip y - setpixel(self, x0, y, col); + setpixel(self, x0, y, col, 0x100); } } } diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index cf295e4956185..237be77c31baf 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -35,6 +35,18 @@ def printbuf(bpp=1): fbuf2 = framebuf.FrameBuffer(bytearray(4), 2, 2, framebuf.GS8) fbuf2.fill(0x7F) +# set pixel at various locations with alpha. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.pixel(x, y, 0x7F, 0x7F) + printbuf() + +# rect at various locations with alpha. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.fill_rect(x, y, 2, 2, 0x7F, 0x7F) + printbuf() + # Blit another FrameBuffer, at various locations with alpha. for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): fbuf.fill(0) diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index 0a82799c9878e..44a928523052a 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -1,4 +1,52 @@ --8<-- +0000000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +3f00000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 +003f000000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +000000003f +-->8-- +--8<-- +3f00000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +3f3f000000 +3f3f000000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 +003f3f0000 +003f3f0000 +0000000000 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +000000003f +-->8-- +--8<-- 3f00000000 0000000000 0000000000 From 564b857842b2d0124280f61a2cfdd590957f6a9a Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 19 Nov 2025 11:36:08 +0000 Subject: [PATCH 29/81] Add alpha for vline, hline, rect; tests. --- extmod/modframebuf.c | 32 ++++++------- tests/extmod/framebuf_alpha.py | 18 ++++++++ tests/extmod/framebuf_alpha.py.exp | 72 ++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 16 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 9a7034d3d6d72..53a9a8f521f7c 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -427,8 +427,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill); static mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); - mp_int_t args[6]; // x, y, w, h, col, alpha - args[5] = 0x100; + mp_int_t args[6] = {0, 0, 0, 0, 0, 0x100}; // x, y, w, h, col, alpha framebuf_args(args_in, args, n_args - 1); fill_rect(self, args[0], args[1], args[2], args[3], args[4], args[5]); return mp_const_none; @@ -457,43 +456,44 @@ static mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args_in) { (void)n_args; mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); - mp_int_t args[4]; // x, y, w, col - framebuf_args(args_in, args, 4); + mp_int_t args[5] = {0, 0, 0, 0, 0x100}; // x, y, w, col, alpha + framebuf_args(args_in, args, n_args - 1); - fill_rect(self, args[0], args[1], args[2], 1, args[3], 0x100); + fill_rect(self, args[0], args[1], args[2], 1, args[3], args[4]); return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 5, framebuf_hline); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 6, framebuf_hline); static mp_obj_t framebuf_vline(size_t n_args, const mp_obj_t *args_in) { (void)n_args; mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); - mp_int_t args[4]; // x, y, h, col - framebuf_args(args_in, args, 4); + mp_int_t args[5] = {0, 0, 0, 0, 0x100}; // x, y, h, col, alpha + framebuf_args(args_in, args, n_args - 1); - fill_rect(self, args[0], args[1], 1, args[2], args[3], 0x100); + fill_rect(self, args[0], args[1], 1, args[2], args[3], args[4]); return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 5, framebuf_vline); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 6, framebuf_vline); static mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); mp_int_t args[5]; // x, y, w, h, col framebuf_args(args_in, args, 5); + mp_int_t alpha = (n_args > 7) ? mp_obj_get_int(args_in[7]) : 0x100; if (n_args > 6 && mp_obj_is_true(args_in[6])) { - fill_rect(self, args[0], args[1], args[2], args[3], args[4], 0x100); + fill_rect(self, args[0], args[1], args[2], args[3], args[4], alpha); } else { - fill_rect(self, args[0], args[1], args[2], 1, args[4], 0x100); - fill_rect(self, args[0], args[1] + args[3] - 1, args[2], 1, args[4], 0x100); - fill_rect(self, args[0], args[1], 1, args[3], args[4], 0x100); - fill_rect(self, args[0] + args[2] - 1, args[1], 1, args[3], args[4], 0x100); + fill_rect(self, args[0], args[1], args[2], 1, args[4], alpha); + fill_rect(self, args[0], args[1] + args[3] - 1, args[2], 1, args[4], alpha); + fill_rect(self, args[0], args[1] + 1, 1, args[3] - 2, args[4], alpha); + fill_rect(self, args[0] + args[2] - 1, args[1] + 1, 1, args[3] - 2, args[4], alpha); } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 7, framebuf_rect); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 8, framebuf_rect); static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col) { mp_int_t dx = x2 - x1; diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index 237be77c31baf..a5af2cce44144 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -47,6 +47,24 @@ def printbuf(bpp=1): fbuf.fill_rect(x, y, 2, 2, 0x7F, 0x7F) printbuf() +# hline at various locations with alpha. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.hline(x, y, 2, 0x7F, 0x7F) + printbuf() + +# vline at various locations with alpha. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.vline(x, y, 2, 0x7F, 0x7F) + printbuf() + +# unfilled rect at various locations with alpha. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.rect(x, y, 3, 3, 0x7F, False, 0x7F) + printbuf() + # Blit another FrameBuffer, at various locations with alpha. for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): fbuf.fill(0) diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index 44a928523052a..7b73e140ccc29 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -47,6 +47,78 @@ 000000003f -->8-- --8<-- +0000000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +3f3f000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 +003f3f0000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +000000003f +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +3f00000000 +3f00000000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 +003f000000 +003f000000 +0000000000 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +000000003f +-->8-- +--8<-- +003f000000 +3f3f000000 +0000000000 +0000000000 +-->8-- +--8<-- +3f3f3f0000 +3f003f0000 +3f3f3f0000 +0000000000 +-->8-- +--8<-- +0000000000 +003f3f3f00 +003f003f00 +003f3f3f00 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +000000003f +-->8-- +--8<-- 3f00000000 0000000000 0000000000 From c586302404245d9b67918cf149d20017bbee9976 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 19 Nov 2025 12:10:03 +0000 Subject: [PATCH 30/81] Add alpha for text; tests for same. --- extmod/modframebuf.c | 5 +++-- tests/extmod/framebuf_alpha.py | 6 ++++++ tests/extmod/framebuf_alpha.py.exp | 24 ++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 53a9a8f521f7c..7f60d6c07752b 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -946,6 +946,7 @@ static mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args_in) { if (n_args >= 5) { col = mp_obj_get_int(args_in[4]); } + mp_int_t alpha = (n_args >= 6) ? mp_obj_get_int(args_in[5]) : 0x100; // loop over chars for (; *str; ++str) { @@ -963,7 +964,7 @@ static mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args_in) { for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column if (vline_data & 1) { // only draw if pixel set if (0 <= y && y < self->height) { // clip y - setpixel(self, x0, y, col, 0x100); + setpixel(self, x0, y, col, alpha); } } } @@ -972,7 +973,7 @@ static mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args_in) { } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 6, framebuf_text); #if !MICROPY_ENABLE_DYNRUNTIME static const mp_rom_map_elem_t framebuf_locals_dict_table[] = { diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index a5af2cce44144..45303de4059f4 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -105,6 +105,12 @@ def printbuf(bpp=1): fbuf.blit(fbuf2, 1, 1, -1, None, mask) printbuf() +# text at various locations with alpha. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.text("x", x, y, 0x7F, 0x7F) + printbuf() + # Now in color buf = bytearray(2 * w * h) fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565) diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index 7b73e140ccc29..20184e60b8727 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -191,6 +191,30 @@ efef7fefef efefefefef -->8-- --8<-- +0000000000 +3f3f00003f +003f3f3f3f +00003f3f00 +-->8-- +--8<-- +0000000000 +0000000000 +003f3f0000 +00003f3f3f +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +00003f3f00 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- e0790000000000000000 00000000000000000000 00000000000000000000 From 324832b26329a6dd8b99d33facd8d894a7ce3e3b Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 19 Nov 2025 13:03:43 +0000 Subject: [PATCH 31/81] Add a macro for standard alpha args. --- extmod/modframebuf.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 7f60d6c07752b..a9689c4ec9988 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -243,6 +243,10 @@ static mp_framebuf_p_t formats[] = { #if MICROPY_PY_FRAMEBUF_ALPHA +#ifndef FRAMEBUF_GET_ALPHA_ARG +#define FRAMEBUF_GET_ALPHA_ARG(idx) ((n_args > idx) ? mp_obj_get_int(args_in[idx]) : 0x100) +#endif //GET_ALPHA_ARG + typedef struct __attribute__((packed)) rgb565 { uint8_t b : 5; uint8_t g : 6; @@ -305,6 +309,10 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u } } #else +#ifndef FRAMEBUF_GET_ALPHA_ARG +#define FRAMEBUF_GET_ALPHA_ARG(idx) (0x100) +#endif //GET_ALPHA_ARG + static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col, mp_int_t alpha) { formats[fb->format].setpixel(fb, x, y, col); } @@ -444,7 +452,7 @@ static mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args_in) { return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y)); } else { // set - mp_int_t alpha = (n_args >= 5) ? mp_obj_get_int(args_in[4]) : 0x100; + mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(4); setpixel(self, x, y, mp_obj_get_int(args_in[3]), alpha); } } @@ -482,7 +490,7 @@ static mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); mp_int_t args[5]; // x, y, w, h, col framebuf_args(args_in, args, 5); - mp_int_t alpha = (n_args > 7) ? mp_obj_get_int(args_in[7]) : 0x100; + mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(7); if (n_args > 6 && mp_obj_is_true(args_in[6])) { fill_rect(self, args[0], args[1], args[2], args[3], args[4], alpha); } else { @@ -946,7 +954,7 @@ static mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args_in) { if (n_args >= 5) { col = mp_obj_get_int(args_in[4]); } - mp_int_t alpha = (n_args >= 6) ? mp_obj_get_int(args_in[5]) : 0x100; + mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(5); // loop over chars for (; *str; ++str) { From 3897b7959efe683a0f5e3785b0db26826c0b9a5f Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 19 Nov 2025 13:51:00 +0000 Subject: [PATCH 32/81] Refactor macro to remove temp vars in most cases. --- extmod/modframebuf.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index a9689c4ec9988..c626dec774f8d 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -245,7 +245,7 @@ static mp_framebuf_p_t formats[] = { #if MICROPY_PY_FRAMEBUF_ALPHA #ifndef FRAMEBUF_GET_ALPHA_ARG #define FRAMEBUF_GET_ALPHA_ARG(idx) ((n_args > idx) ? mp_obj_get_int(args_in[idx]) : 0x100) -#endif //GET_ALPHA_ARG +#endif // GET_ALPHA_ARG typedef struct __attribute__((packed)) rgb565 { uint8_t b : 5; @@ -311,7 +311,7 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u #else #ifndef FRAMEBUF_GET_ALPHA_ARG #define FRAMEBUF_GET_ALPHA_ARG(idx) (0x100) -#endif //GET_ALPHA_ARG +#endif // GET_ALPHA_ARG static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col, mp_int_t alpha) { formats[fb->format].setpixel(fb, x, y, col); @@ -435,9 +435,9 @@ static MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill); static mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); - mp_int_t args[6] = {0, 0, 0, 0, 0, 0x100}; // x, y, w, h, col, alpha - framebuf_args(args_in, args, n_args - 1); - fill_rect(self, args[0], args[1], args[2], args[3], args[4], args[5]); + mp_int_t args[5]; // x, y, w, h, col + framebuf_args(args_in, args, 5); + fill_rect(self, args[0], args[1], args[2], args[3], args[4], FRAMEBUF_GET_ALPHA_ARG(6)); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 7, framebuf_fill_rect); @@ -452,8 +452,7 @@ static mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args_in) { return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y)); } else { // set - mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(4); - setpixel(self, x, y, mp_obj_get_int(args_in[3]), alpha); + setpixel(self, x, y, mp_obj_get_int(args_in[3]), FRAMEBUF_GET_ALPHA_ARG(4)); } } return mp_const_none; @@ -464,10 +463,10 @@ static mp_obj_t framebuf_hline(size_t n_args, const mp_obj_t *args_in) { (void)n_args; mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); - mp_int_t args[5] = {0, 0, 0, 0, 0x100}; // x, y, w, col, alpha - framebuf_args(args_in, args, n_args - 1); + mp_int_t args[4]; // x, y, w, col + framebuf_args(args_in, args, 4); - fill_rect(self, args[0], args[1], args[2], 1, args[3], args[4]); + fill_rect(self, args[0], args[1], args[2], 1, args[3], FRAMEBUF_GET_ALPHA_ARG(5)); return mp_const_none; } @@ -477,10 +476,10 @@ static mp_obj_t framebuf_vline(size_t n_args, const mp_obj_t *args_in) { (void)n_args; mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); - mp_int_t args[5] = {0, 0, 0, 0, 0x100}; // x, y, h, col, alpha - framebuf_args(args_in, args, n_args - 1); + mp_int_t args[4]; // x, y, h, col + framebuf_args(args_in, args, 4); - fill_rect(self, args[0], args[1], 1, args[2], args[3], args[4]); + fill_rect(self, args[0], args[1], 1, args[2], args[3], FRAMEBUF_GET_ALPHA_ARG(5)); return mp_const_none; } From 0a97e27e8c292a431563e95a61b35eaeeb6454d8 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 19 Nov 2025 16:13:18 +0000 Subject: [PATCH 33/81] Add alpha for ellipses. Requires some re-writing so each pixel is drawn exactly once. Should be much more efficient for wide ellipses in addition to drawing correctly with alpha. --- extmod/modframebuf.c | 62 +++++++++++++++++++---------- tests/extmod/framebuf_alpha.py | 16 ++++++++ tests/extmod/framebuf_alpha.py.exp | 64 ++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 20 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index c626dec774f8d..dc406ec9c1e35 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -582,25 +582,40 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_lin #define ELLIPSE_MASK_Q3 (0x04) #define ELLIPSE_MASK_Q4 (0x08) -static void draw_ellipse_points(const mp_obj_framebuf_t *fb, mp_int_t cx, mp_int_t cy, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask) { +static void draw_ellipse_points(const mp_obj_framebuf_t *fb, mp_int_t cx, mp_int_t cy, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask, mp_int_t alpha) { if (mask & ELLIPSE_MASK_FILL) { - if (mask & ELLIPSE_MASK_Q1) { - fill_rect(fb, cx, cy - y, x + 1, 1, col, 0x100); - } - if (mask & ELLIPSE_MASK_Q2) { - fill_rect(fb, cx - x, cy - y, x + 1, 1, col, 0x100); - } - if (mask & ELLIPSE_MASK_Q3) { - fill_rect(fb, cx - x, cy + y, x + 1, 1, col, 0x100); - } - if (mask & ELLIPSE_MASK_Q4) { - fill_rect(fb, cx, cy + y, x + 1, 1, col, 0x100); + if (y == 0 && (mask & ELLIPSE_MASK_ALL)) { + // on y-axis, draw one hline + mp_int_t x_min = (mask & (ELLIPSE_MASK_Q2 | ELLIPSE_MASK_Q3)) ? -x : 0; + mp_int_t x_max = (mask & (ELLIPSE_MASK_Q1 | ELLIPSE_MASK_Q4)) ? x : 0; + fill_rect(fb, cx + x_min, cy, x_max - x_min + 1, 1, col, alpha); + } else { + if (mask & (ELLIPSE_MASK_Q1 | ELLIPSE_MASK_Q2)) { + // draw one hline above + mp_int_t x_min = (mask & ELLIPSE_MASK_Q2) ? -x : 0; + mp_int_t x_max = (mask & ELLIPSE_MASK_Q1) ? x : 0; + fill_rect(fb, cx + x_min, cy - y, x_max - x_min + 1, 1, col, alpha); + } + if (mask & (ELLIPSE_MASK_Q3 | ELLIPSE_MASK_Q4)) { + // draw one hline below + mp_int_t x_min = (mask & ELLIPSE_MASK_Q3) ? -x : 0; + mp_int_t x_max = (mask & ELLIPSE_MASK_Q4) ? x : 0; + fill_rect(fb, cx + x_min, cy + y, x_max - x_min + 1, 1, col, alpha); + } } } else { - setpixel_checked(fb, cx + x, cy - y, col, mask & ELLIPSE_MASK_Q1, 0x100); - setpixel_checked(fb, cx - x, cy - y, col, mask & ELLIPSE_MASK_Q2, 0x100); - setpixel_checked(fb, cx - x, cy + y, col, mask & ELLIPSE_MASK_Q3, 0x100); - setpixel_checked(fb, cx + x, cy + y, col, mask & ELLIPSE_MASK_Q4, 0x100); + if (y == 0) { + setpixel_checked(fb, cx + x, cy, col, mask & (ELLIPSE_MASK_Q1 | ELLIPSE_MASK_Q4), alpha); + setpixel_checked(fb, cx - x, cy, col, mask & (ELLIPSE_MASK_Q2 | ELLIPSE_MASK_Q3), alpha); + } else if (x == 0) { + setpixel_checked(fb, cx, cy - y, col, mask & (ELLIPSE_MASK_Q1 | ELLIPSE_MASK_Q2), alpha); + setpixel_checked(fb, cx, cy + y, col, mask & (ELLIPSE_MASK_Q3 | ELLIPSE_MASK_Q4), alpha); + } else { + setpixel_checked(fb, cx + x, cy - y, col, mask & ELLIPSE_MASK_Q1, alpha); + setpixel_checked(fb, cx - x, cy - y, col, mask & ELLIPSE_MASK_Q2, alpha); + setpixel_checked(fb, cx - x, cy + y, col, mask & ELLIPSE_MASK_Q3, alpha); + setpixel_checked(fb, cx + x, cy + y, col, mask & ELLIPSE_MASK_Q4, alpha); + } } } @@ -614,8 +629,9 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { } else { mask |= ELLIPSE_MASK_ALL; } + mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(8); if (args[2] == 0 && args[3] == 0) { - setpixel_checked(self, args[0], args[1], args[4], mask & ELLIPSE_MASK_ALL, 0x100); + setpixel_checked(self, args[0], args[1], args[4], mask & ELLIPSE_MASK_ALL, alpha); return mp_const_none; } mp_int_t two_asquare = 2 * args[2] * args[2]; @@ -628,7 +644,7 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { mp_int_t stoppingx = two_bsquare * args[2]; mp_int_t stoppingy = 0; while (stoppingx >= stoppingy) { // 1st set of points, y' > -1 - draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask); + draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask, alpha); y += 1; stoppingy += two_asquare; ellipse_error += ychange; @@ -649,12 +665,18 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { stoppingx = 0; stoppingy = two_asquare * args[3]; while (stoppingx <= stoppingy) { // 2nd set of points, y' < -1 - draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask); + if (!(mask & ELLIPSE_MASK_FILL)) { + draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask, alpha); + } x += 1; stoppingx += two_bsquare; ellipse_error += xchange; xchange += two_bsquare; if ((2 * ellipse_error + ychange) > 0) { + if (mask & ELLIPSE_MASK_FILL) { + // moving to new scanline, draw line *once* + draw_ellipse_points(self, args[0], args[1], x - 1, y, args[4], mask, alpha); + } y -= 1; stoppingy -= two_asquare; ellipse_error += ychange; @@ -663,7 +685,7 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_ellipse_obj, 6, 8, framebuf_ellipse); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_ellipse_obj, 6, 9, framebuf_ellipse); #if MICROPY_PY_ARRAY diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index 45303de4059f4..3c538ec3718b4 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -157,3 +157,19 @@ def printbuf(bpp=1): fbuf.fill(0b00000_111111_00000) fbuf.blit(fbuf2, 1, 1, -1, None, mask) printbuf(2) + +# Ellipse +w = 30 +h = 30 +buf = bytearray(w * h) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) + +# Outline +fbuf.fill(0) +fbuf.ellipse(15, 15, 12, 6, 0x7F, False, 0b1111, 0x7F) +printbuf() + +# Fill +fbuf.fill(0) +fbuf.ellipse(15, 15, 6, 12, 0xAA, True, 0b1111, 0x7F) +printbuf() diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index 20184e60b8727..f029b28dd0436 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -286,3 +286,67 @@ e007e007e007e007e007 e007e007e0fbe007e007 e007e007e007e007e007 -->8-- +--8<-- +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +00000000000000000000003f3f3f3f3f3f3f3f3f00000000000000000000 +00000000000000003f3f3f0000000000000000003f3f3f00000000000000 +0000000000003f3f0000000000000000000000000000003f3f0000000000 +00000000003f000000000000000000000000000000000000003f00000000 +000000003f0000000000000000000000000000000000000000003f000000 +0000003f00000000000000000000000000000000000000000000003f0000 +0000003f00000000000000000000000000000000000000000000003f0000 +0000003f00000000000000000000000000000000000000000000003f0000 +000000003f0000000000000000000000000000000000000000003f000000 +00000000003f000000000000000000000000000000000000003f00000000 +0000000000003f3f0000000000000000000000000000003f3f0000000000 +00000000000000003f3f3f0000000000000000003f3f3f00000000000000 +00000000000000000000003f3f3f3f3f3f3f3f3f00000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +-->8-- +--8<-- +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000055555500000000000000000000000000 +000000000000000000000000005555555555000000000000000000000000 +000000000000000000000000555555555555550000000000000000000000 +000000000000000000000055555555555555555500000000000000000000 +000000000000000000000055555555555555555500000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000000055555555555555555500000000000000000000 +000000000000000000000055555555555555555500000000000000000000 +000000000000000000000000555555555555550000000000000000000000 +000000000000000000000000005555555555000000000000000000000000 +000000000000000000000000000055555500000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +-->8-- From 1e55a9e22a1bd62123b7e0e0f1d56ce13cc91fce Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 20 Nov 2025 08:23:45 +0000 Subject: [PATCH 34/81] Test turning alpha off, for size. --- py/mpconfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 4d84dc2ff81b4..13876ec6d2917 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -2113,7 +2113,7 @@ typedef time_t mp_timestamp_t; // Whether to support alpha blending in framebuf module #ifndef MICROPY_PY_FRAMEBUF_ALPHA -#define MICROPY_PY_FRAMEBUF_ALPHA (MICROPY_PY_FRAMEBUF && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#define MICROPY_PY_FRAMEBUF_ALPHA (0) //(MICROPY_PY_FRAMEBUF && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif #ifndef MICROPY_PY_BTREE From 6e29873926ad50657ddefce42fe64ecad92e54d7 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 20 Nov 2025 09:03:17 +0000 Subject: [PATCH 35/81] Add alpha for lines and polygons; polygons aren't perfect. --- extmod/modframebuf.c | 45 ++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index dc406ec9c1e35..750aef706fbc6 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -502,7 +502,11 @@ static mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args_in) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 8, framebuf_rect); -static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col) { +static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha) { + if (alpha <= 0) { + // nothing to do + return; + } mp_int_t dx = x2 - x1; mp_int_t sx; if (dx > 0) { @@ -542,11 +546,11 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t for (mp_int_t i = 0; i < dx; ++i) { if (steep) { if (0 <= y1 && y1 < fb->width && 0 <= x1 && x1 < fb->height) { - setpixel(fb, y1, x1, col, 0x100); + setpixel(fb, y1, x1, col, alpha); } } else { if (0 <= x1 && x1 < fb->width && 0 <= y1 && y1 < fb->height) { - setpixel(fb, x1, y1, col, 0x100); + setpixel(fb, x1, y1, col, alpha); } } while (e >= 0) { @@ -557,7 +561,7 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t e += 2 * dy; } - setpixel_checked(fb, x2, y2, col, 1, 0x100); + setpixel_checked(fb, x2, y2, col, 1, alpha); } static mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) { @@ -567,11 +571,11 @@ static mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) { mp_int_t args[5]; // x1, y1, x2, y2, col framebuf_args(args_in, args, 5); - line(self, args[0], args[1], args[2], args[3], args[4]); + line(self, args[0], args[1], args[2], args[3], args[4], FRAMEBUF_GET_ALPHA_ARG(6)); return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 7, framebuf_line); // Q2 Q1 // Q3 Q4 @@ -630,6 +634,10 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { mask |= ELLIPSE_MASK_ALL; } mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(8); + if (alpha <= 0) { + // nothing to do + return mp_const_none; + } if (args[2] == 0 && args[3] == 0) { setpixel_checked(self, args[0], args[1], args[4], mask & ELLIPSE_MASK_ALL, alpha); return mp_const_none; @@ -710,6 +718,11 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_int_t col = mp_obj_get_int(args_in[4]); bool fill = n_args > 5 && mp_obj_is_true(args_in[5]); + mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(6); + if (alpha <= 0) { + // nothing to do + return mp_const_none; + } if (fill) { // This implements an integer version of http://alienryderflex.com/polygon_fill/ @@ -748,14 +761,14 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { } else if (row == MAX(py1, py2)) { // At local-minima, try and manually fill in the pixels that get missed above. if (py1 < py2) { - setpixel_checked(self, x + px2, y + py2, col, 1, 0x100); + setpixel_checked(self, x + px2, y + py2, col, 1, alpha); } else if (py2 < py1) { - setpixel_checked(self, x + px1, y + py1, col, 1, 0x100); + setpixel_checked(self, x + px1, y + py1, col, 1, alpha); } else { // Even though this is a hline and would be faster to // use fill_rect, use line() because it handles x2 < // x1. - line(self, x + px1, y + py1, x + px2, y + py2, col); + line(self, x + px1, y + py1, x + px2, y + py2, col, alpha); } } @@ -784,7 +797,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // Fill between each pair of nodes. for (i = 0; i < n_nodes; i += 2) { - fill_rect(self, x + nodes[i], y + row, (nodes[i + 1] - nodes[i]) + 1, 1, col, 0x100); + fill_rect(self, x + nodes[i], y + row, (nodes[i + 1] - nodes[i]) + 1, 1, col, alpha); } } } else { @@ -795,7 +808,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { do { mp_int_t py2 = poly_int(&bufinfo, i--); mp_int_t px2 = poly_int(&bufinfo, i--); - line(self, x + px1, y + py1, x + px2, y + py2, col); + line(self, x + px1, y + py1, x + px2, y + py2, col, alpha); px1 = px2; py1 = py2; } while (i >= 0); @@ -803,7 +816,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_poly_obj, 5, 6, framebuf_poly); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_poly_obj, 5, 7, framebuf_poly); #endif // MICROPY_PY_ARRAY @@ -868,6 +881,10 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { if (n_args > 6 && args_in[6] != mp_const_none) { if (mp_obj_get_type(args_in[6]) == &mp_type_int) { alpha = mp_obj_get_int(args_in[6]); + if (alpha <= 0) { + // nothing to do + return mp_const_none; + } } else { get_readonly_framebuffer(args_in[6], &mask); if (mask.width != source.width || mask.height != source.height) { @@ -976,6 +993,10 @@ static mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args_in) { col = mp_obj_get_int(args_in[4]); } mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(5); + if (alpha <= 0) { + // nothing to do + return mp_const_none; + } // loop over chars for (; *str; ++str) { From 978a246c7f659c840383666637ac6bc70d9758aa Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 20 Nov 2025 09:15:22 +0000 Subject: [PATCH 36/81] Restore alpha blending code. --- py/mpconfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 13876ec6d2917..4d84dc2ff81b4 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -2113,7 +2113,7 @@ typedef time_t mp_timestamp_t; // Whether to support alpha blending in framebuf module #ifndef MICROPY_PY_FRAMEBUF_ALPHA -#define MICROPY_PY_FRAMEBUF_ALPHA (0) //(MICROPY_PY_FRAMEBUF && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#define MICROPY_PY_FRAMEBUF_ALPHA (MICROPY_PY_FRAMEBUF && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif #ifndef MICROPY_PY_BTREE From 3368af0fd877e454089f1b8ffa1b09ce7aa9ac40 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 20 Nov 2025 09:57:48 +0000 Subject: [PATCH 37/81] Add antialiased lines. --- extmod/modframebuf.c | 199 +++++++++++++++++++++---------- tests/extmod/framebuf1.py | 6 + tests/extmod/framebuf_polygon.py | 5 + 3 files changed, 145 insertions(+), 65 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 750aef706fbc6..8550fbe674432 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -308,6 +308,8 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u } } } + + #else #ifndef FRAMEBUF_GET_ALPHA_ARG #define FRAMEBUF_GET_ALPHA_ARG(idx) (0x100) @@ -331,6 +333,7 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col); } + #endif // MICROPY_PY_FRAMEBUF_ALPHA static void setpixel_checked(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask, mp_int_t alpha) { @@ -343,6 +346,132 @@ static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, uns return formats[fb->format].getpixel(fb, x, y); } +#if MICROPY_PY_FRAMEBUF_ALPHA +static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha, bool draw_last) { + setpixel_checked(fb, x1, y1, col, 1, alpha); + if (x1 == x2 && y1 == y2) { + // nothing more to do + return; + } + if (draw_last) { + setpixel_checked(fb, x2, y2, col, 1, alpha); + } + + mp_int_t dx = x2 - x1; + mp_int_t dy = y2 - y1; + if (dx + dy < 0) { + // swap ends + mp_int_t temp; + dx = -dx; + dy = -dy; + temp = x1; + x1 = x2; + x2 = temp; + temp = y1; + y1 = y2; + y2 = temp; + } + + bool steep; + if (dy > dx || dy < -dx) { + // swap x and y + mp_int_t temp; + temp = x1; + x1 = y1; + y1 = temp; + temp = dx; + dx = dy; + dy = temp; + steep = true; + } else { + steep = false; + } + + // Fixed point with 8 bits of fractional part. + // dx != 0 is guaranteed + mp_int_t gradient = ((dy << 8) / dx); + + mp_int_t y_intercept = (y1 << 8) + gradient; + if (steep) { + for (mp_int_t x = x1 + 1; x < x1 + dx; ++x) { + setpixel_checked(fb, y_intercept >> 8, x, col, 1, (alpha * (0x100 - (y_intercept & 0xff))) >> 8); + setpixel_checked(fb, (y_intercept >> 8) + 1, x, col, 1, (alpha * (y_intercept & 0xff)) >> 8); + y_intercept += gradient; + } + } else { + for (mp_int_t x = x1 + 1; x < x1 + dx; ++x) { + setpixel_checked(fb, x, y_intercept >> 8, col, 1, (alpha * (0x100 - (y_intercept & 0xff))) >> 8); + setpixel_checked(fb, x, (y_intercept >> 8) + 1, col, 1, (alpha * (y_intercept & 0xff)) >> 8); + y_intercept += gradient; + } + } +} +#else +static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha, bool draw_last) { + if (alpha <= 0) { + // nothing to do + return; + } + mp_int_t dx = x2 - x1; + mp_int_t sx; + if (dx > 0) { + sx = 1; + } else { + dx = -dx; + sx = -1; + } + + mp_int_t dy = y2 - y1; + mp_int_t sy; + if (dy > 0) { + sy = 1; + } else { + dy = -dy; + sy = -1; + } + + bool steep; + if (dy > dx) { + mp_int_t temp; + temp = x1; + x1 = y1; + y1 = temp; + temp = dx; + dx = dy; + dy = temp; + temp = sx; + sx = sy; + sy = temp; + steep = true; + } else { + steep = false; + } + + mp_int_t e = 2 * dy - dx; + for (mp_int_t i = 0; i < dx; ++i) { + if (steep) { + if (0 <= y1 && y1 < fb->width && 0 <= x1 && x1 < fb->height) { + setpixel(fb, y1, x1, col, alpha); + } + } else { + if (0 <= x1 && x1 < fb->width && 0 <= y1 && y1 < fb->height) { + setpixel(fb, x1, y1, col, alpha); + } + } + while (e >= 0) { + y1 += sy; + e -= 2 * dx; + } + x1 += sx; + e += 2 * dy; + } + + if (draw_last) { + setpixel_checked(fb, x2, y2, col, 1, alpha); + } +} +#endif // MICROPY_PY_FRAMEBUF_ALPHA + static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, unsigned int buf_flags, mp_obj_framebuf_t *o) { mp_int_t width = mp_obj_get_int(args_in[1]); @@ -502,68 +631,6 @@ static mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args_in) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 8, framebuf_rect); -static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha) { - if (alpha <= 0) { - // nothing to do - return; - } - mp_int_t dx = x2 - x1; - mp_int_t sx; - if (dx > 0) { - sx = 1; - } else { - dx = -dx; - sx = -1; - } - - mp_int_t dy = y2 - y1; - mp_int_t sy; - if (dy > 0) { - sy = 1; - } else { - dy = -dy; - sy = -1; - } - - bool steep; - if (dy > dx) { - mp_int_t temp; - temp = x1; - x1 = y1; - y1 = temp; - temp = dx; - dx = dy; - dy = temp; - temp = sx; - sx = sy; - sy = temp; - steep = true; - } else { - steep = false; - } - - mp_int_t e = 2 * dy - dx; - for (mp_int_t i = 0; i < dx; ++i) { - if (steep) { - if (0 <= y1 && y1 < fb->width && 0 <= x1 && x1 < fb->height) { - setpixel(fb, y1, x1, col, alpha); - } - } else { - if (0 <= x1 && x1 < fb->width && 0 <= y1 && y1 < fb->height) { - setpixel(fb, x1, y1, col, alpha); - } - } - while (e >= 0) { - y1 += sy; - e -= 2 * dx; - } - x1 += sx; - e += 2 * dy; - } - - setpixel_checked(fb, x2, y2, col, 1, alpha); -} - static mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) { (void)n_args; @@ -571,7 +638,7 @@ static mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) { mp_int_t args[5]; // x1, y1, x2, y2, col framebuf_args(args_in, args, 5); - line(self, args[0], args[1], args[2], args[3], args[4], FRAMEBUF_GET_ALPHA_ARG(6)); + line(self, args[0], args[1], args[2], args[3], args[4], FRAMEBUF_GET_ALPHA_ARG(6), true); return mp_const_none; } @@ -768,7 +835,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // Even though this is a hline and would be faster to // use fill_rect, use line() because it handles x2 < // x1. - line(self, x + px1, y + py1, x + px2, y + py2, col, alpha); + line(self, x + px1, y + py1, x + px2, y + py2, col, alpha, true); } } @@ -808,10 +875,12 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { do { mp_int_t py2 = poly_int(&bufinfo, i--); mp_int_t px2 = poly_int(&bufinfo, i--); - line(self, x + px1, y + py1, x + px2, y + py2, col, alpha); + line(self, x + px1, y + py1, x + px2, y + py2, col, alpha, false); px1 = px2; py1 = py2; } while (i >= 0); + // always set last point + setpixel_checked(self, x + px1, y + py1, col, 1, alpha); } return mp_const_none; diff --git a/tests/extmod/framebuf1.py b/tests/extmod/framebuf1.py index f5e92579f2fd7..f9dfc23c347a3 100644 --- a/tests/extmod/framebuf1.py +++ b/tests/extmod/framebuf1.py @@ -4,6 +4,12 @@ print("SKIP") raise SystemExit +if framebuf.ALPHA: + # not handling aa lines yet + print("SKIP") + raise SystemExit + + w = 5 h = 16 size = ((w + 7) & ~7) * ((h + 7) & ~7) // 8 diff --git a/tests/extmod/framebuf_polygon.py b/tests/extmod/framebuf_polygon.py index da05be2c4db52..69467e503b81b 100644 --- a/tests/extmod/framebuf_polygon.py +++ b/tests/extmod/framebuf_polygon.py @@ -11,6 +11,11 @@ print("SKIP") raise SystemExit +if framebuf.ALPHA: + # Not testing for anti-aliased lines yet + print("SKIP") + raise SystemExit + def print_buffer(buffer, width, height): for row in range(height): From cc00da8fc81dd1b3c9e1a67d90ecd15f84c8d146 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 20 Nov 2025 16:56:07 +0000 Subject: [PATCH 38/81] Add A-buffer based polygons; update tests. --- extmod/modframebuf.c | 203 +++++- tests/extmod/framebuf1.py | 7 +- tests/extmod/framebuf1.py.exp | 6 +- tests/extmod/framebuf_alpha.py | 25 + tests/extmod/framebuf_alpha.py.exp | 88 ++- tests/extmod/framebuf_polygon.py | 2 +- tests/extmod/framebuf_polygon_alpha.py | 264 ++++++++ tests/extmod/framebuf_polygon_alpha.py.exp | 686 +++++++++++++++++++++ 8 files changed, 1237 insertions(+), 44 deletions(-) create mode 100644 tests/extmod/framebuf_polygon_alpha.py create mode 100644 tests/extmod/framebuf_polygon_alpha.py.exp diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 8550fbe674432..6ebb6ff84f8ce 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -258,8 +258,7 @@ typedef union { } urgb565; static uint32_t alpha_blend(uint32_t c1, uint32_t c2, uint32_t alpha) { - // Add one to alpha, otherwise 0x00 and 0x01 are identical after >> 8 - return ((c1 * (0x100 - alpha)) + (alpha + 1) * c2) >> 8; + return (c1 * (0xff - alpha) + alpha * c2 + 0x80) >> 8; } static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32_t col, mp_int_t alpha) { @@ -768,6 +767,201 @@ static mp_int_t poly_int(mp_buffer_info_t *bufinfo, size_t index) { return mp_obj_get_int(mp_binary_get_val_array(bufinfo->typecode, bufinfo->buf, index)); } +#if MICROPY_PY_FRAMEBUF_ALPHA + +typedef struct edge { + mp_int_t y1; + mp_int_t y2; + mp_int_t x1; + mp_int_t slope; +} edge; + +static void insert_edge(edge *edge_table, int n_edges, mp_int_t py1, mp_int_t py2, mp_int_t px1, mp_int_t slope) { + edge e = { + py1, + py2, + px1, + slope, + }; + edge current; + // simple linear insert: could do binary insert more efficiently and/or use memcpy + for (int i = 0; i < n_edges; ++i) { + current = edge_table[i]; + if (e.y1 <= current.y1 || (e.y1 == current.y1 && e.x1 <= current.x1)) { + edge_table[i] = e; + e = current; + } + } + edge_table[n_edges] = e; +} + +static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); + + mp_int_t x = mp_obj_get_int(args_in[1]); + mp_int_t y = mp_obj_get_int(args_in[2]); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args_in[3], &bufinfo, MP_BUFFER_READ); + // If an odd number of values was given, this rounds down to multiple of two. + int n_poly = bufinfo.len / (mp_binary_get_size('@', bufinfo.typecode, NULL) * 2); + + if (n_poly == 0) { + return mp_const_none; + } + + mp_int_t col = mp_obj_get_int(args_in[4]); + bool fill = n_args > 5 && mp_obj_is_true(args_in[5]); + mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(6); + + if (fill) { + // This implements a variant of the A-buffer algorithm for polygon fill. + + // We compute two scanlines per row, one at -0.25 and one at +0.25 from + // the pixel center (where the integer coordinates are). Each scanline + // samples at the 0.25 multiples. + // + // +---+---+ -0.5 + // X-X-X-X-| + // +---+---+ 0 + // X-X-X-X-| + // +---+---+ +0.5 + // -0.5 0 +0.5 + // + // Where an edge intersects a scanline, we set a bit mask of all to the + // right of it, and xor that with the mask from the previous edges. + // + // When we move to the next pixel we copy the last bit on each scanline + // and multiply by 0b1111 to extend it to the entire row, and use that as + // the initial mask for that pixel. + + // Build a table of edges and data + // The table consists of entries (y_min, y_max, x_min, 1/slope) + edge edge_table[n_poly]; + mp_int_t n_edges = 0; + mp_int_t px1 = poly_int(&bufinfo, 2*n_poly-2); + mp_int_t py1 = poly_int(&bufinfo, 2*n_poly-1); + mp_int_t y_start = self->height - y; + mp_int_t y_end = 0 - y; + mp_int_t x_start = px1; + mp_int_t x_end = px1; + for (int i = 0; i < n_poly; ++i) { + mp_int_t px2 = poly_int(&bufinfo, 2*i); + mp_int_t py2 = poly_int(&bufinfo, 2*i + 1); + // track the min and max extent of the polygon + y_start = MIN(y_start, py2); + y_end = MAX(y_end, py2); + x_start = MIN(x_start, px2); + x_end = MAX(x_end, px2); + + if (py1 < py2) { // going up + if (py1 >= y || py2 <= y + self->height) { // intersects screen vertically + insert_edge(edge_table, n_edges, py1, py2, px1, ((px2 - px1) << 12) / (py2 - py1)); + ++n_edges; + } + } else if (py1 > py2) { // going down + if (py2 >= y || py1 <= y + self->height) { // intersects screen vertically + insert_edge(edge_table, n_edges, py2, py1, px2, ((px2 - px1) << 12) / (py2 - py1)); + ++n_edges; + } + } // ... and ignore horizontal edges + + px1 = px2; + py1 = py2; + } + + int last_edge_index = 0; + mp_int_t scanline_intersects[n_edges][2]; + int n_intersects[2]; + + for (mp_int_t row = y_start; row <= y_end; row++) { + uint8_t mask = 0; + + while (last_edge_index < n_edges && edge_table[last_edge_index].y1 <= row) { + ++last_edge_index; + } + + n_intersects[0] = 0; + n_intersects[1] = 0; + for (mp_int_t line = 0; line < 2; ++line) { + mp_int_t y1 = (line == 0) ? ((row << 2) - 1) : ((row << 2) + 1); + for (int i = 0; i < last_edge_index; ++i) { + edge e = edge_table[i]; + if ((e.y2 << 2) < y1) { + continue; + } else if ((e.y1 << 2) > y1) { + continue; + } + // round x-value to nearest 0.25 using an additional 12-bits of precision + mp_int_t x1 = (e.x1 << 2) + (((y1 - (e.y1 << 2)) * e.slope + (1 << 11)) >> 12); + // XXX binary insertion? + for (int j=0; j < n_intersects[line]; j++) { + mp_int_t current = scanline_intersects[j][line]; + if (current > x1) { + scanline_intersects[j][line] = x1; + x1 = current; + } + } + scanline_intersects[n_intersects[line]][line] = x1; + ++n_intersects[line]; + } + } + + int column; + if (n_intersects[0] != 0) { + if (n_intersects[1] != 0) { + column = MIN(scanline_intersects[0][0], scanline_intersects[0][1]) >> 2; + } else { + column = scanline_intersects[0][0] >> 2; + } + } else { + if (n_intersects[1] != 0) { + column = scanline_intersects[0][1] >> 2; + } else { + // blank line - shouldn't happen + continue; + } + } + int intersect_indices[2] = {0, 0}; + while (intersect_indices[0] < n_intersects[0] || intersect_indices[1] < n_intersects[1]) { + for (mp_int_t line = 0; line < 2; ++line) { + if (intersect_indices[line] >= n_intersects[line]) { + continue; + } + mp_int_t x1 = scanline_intersects[intersect_indices[line]][line] - (column << 2) + 2; + while ((x1 >= 0) && (x1 < 4)) { + mask ^= (0b1111 >> x1) << (4 * line); + ++intersect_indices[line]; + if (intersect_indices[line] > n_intersects[line]) {break;}; + x1 = scanline_intersects[intersect_indices[line]][line] - (column << 2) + 2; + } + } + setpixel_checked(self, x + column, y + row, col, 1, (__builtin_popcount(mask) * alpha) >> 3); + mask = (mask & 0b00010001) * 0b1111; + column++; + } + } + } else { + // Outline only. + mp_int_t px1 = poly_int(&bufinfo, 0); + mp_int_t py1 = poly_int(&bufinfo, 1); + int i = n_poly * 2 - 1; + do { + mp_int_t py2 = poly_int(&bufinfo, i--); + mp_int_t px2 = poly_int(&bufinfo, i--); + line(self, x + px1, y + py1, x + px2, y + py2, col, alpha, false); + px1 = px2; + py1 = py2; + } while (i >= 0); + // draw endpoint of last line if poly is not closed + if (px1 != poly_int(&bufinfo, 0) || py1 != poly_int(&bufinfo, 1)) { + setpixel_checked(self, x + px1, y + py1, col, 1, alpha); + } + } + + return mp_const_none; +} +#else static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); @@ -875,16 +1069,15 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { do { mp_int_t py2 = poly_int(&bufinfo, i--); mp_int_t px2 = poly_int(&bufinfo, i--); - line(self, x + px1, y + py1, x + px2, y + py2, col, alpha, false); + line(self, x + px1, y + py1, x + px2, y + py2, col, alpha, true); px1 = px2; py1 = py2; } while (i >= 0); - // always set last point - setpixel_checked(self, x + px1, y + py1, col, 1, alpha); } return mp_const_none; } +#endif // MICROPY_PY_FRAMEBUF_ALPHA static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_poly_obj, 5, 7, framebuf_poly); #endif // MICROPY_PY_ARRAY diff --git a/tests/extmod/framebuf1.py b/tests/extmod/framebuf1.py index f9dfc23c347a3..887a736344c44 100644 --- a/tests/extmod/framebuf1.py +++ b/tests/extmod/framebuf1.py @@ -4,11 +4,6 @@ print("SKIP") raise SystemExit -if framebuf.ALPHA: - # not handling aa lines yet - print("SKIP") - raise SystemExit - w = 5 h = 16 @@ -76,7 +71,7 @@ # line steep negative gradient fbuf.fill(0) - fbuf.line(3, 3, 2, 1, 1) + fbuf.line(3, 3, 2, 0, 1) print("line", buf) # scroll diff --git a/tests/extmod/framebuf1.py.exp b/tests/extmod/framebuf1.py.exp index 4f18e48eca6f1..d26263184675c 100644 --- a/tests/extmod/framebuf1.py.exp +++ b/tests/extmod/framebuf1.py.exp @@ -10,7 +10,7 @@ vline bytearray(b'\x00\xff\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x rect bytearray(b'\x00\x0e\n\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') fill_rect bytearray(b'\x00\x0e\x0e\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') line bytearray(b'\x00\x02\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') -line bytearray(b'\x00\x00\x06\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +line bytearray(b'\x00\x00\x03\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') @@ -32,7 +32,7 @@ vline bytearray(b'@@@@@@@@@@@@@@@@') rect bytearray(b'\x00pPp\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') fill_rect bytearray(b'\x00ppp\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') line bytearray(b'\x00@ \x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') -line bytearray(b'\x00 \x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +line bytearray(b' \x10\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00') @@ -54,7 +54,7 @@ vline bytearray(b'\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x rect bytearray(b'\x00\x0e\n\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') fill_rect bytearray(b'\x00\x0e\x0e\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') line bytearray(b'\x00\x02\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') -line bytearray(b'\x00\x04\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +line bytearray(b'\x04\x04\x08\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00') diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index 3c538ec3718b4..50fc2da577c64 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -41,6 +41,11 @@ def printbuf(bpp=1): fbuf.pixel(x, y, 0x7F, 0x7F) printbuf() +# check that alpha 0x01 gives *something* for color 0x80 or more +fbuf.fill(0) +fbuf.pixel(0, 0, 0x80, 0x01) +printbuf() + # rect at various locations with alpha. for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): fbuf.fill(0) @@ -65,6 +70,26 @@ def printbuf(bpp=1): fbuf.rect(x, y, 3, 3, 0x7F, False, 0x7F) printbuf() +# steep antialiased line. +fbuf.fill(0) +fbuf.line(1, 1, 2, 3, 0xFF) +printbuf() + +# shallow antialiased line. +fbuf.fill(0) +fbuf.line(1, 1, 3, 2, 0xFF) +printbuf() + +# steep antialiased line with alpha. +fbuf.fill(0) +fbuf.line(1, 1, 2, 3, 0xFF, 0x7F) +printbuf() + +# shallow antialiased line with alpha. +fbuf.fill(0) +fbuf.line(1, 1, 3, 2, 0xFF, 0x7F) +printbuf() + # Blit another FrameBuffer, at various locations with alpha. for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): fbuf.fill(0) diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index f029b28dd0436..ff75a313043d8 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -23,6 +23,12 @@ 000000003f -->8-- --8<-- +0100000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- 3f00000000 0000000000 0000000000 @@ -119,6 +125,30 @@ 000000003f -->8-- --8<-- +0000000000 +00ff000000 +0080800000 +0000ff0000 +-->8-- +--8<-- +0000000000 +00ff800000 +000080ff00 +0000000000 +-->8-- +--8<-- +0000000000 +007f000000 +003f3f0000 +00007f0000 +-->8-- +--8<-- +0000000000 +007f3f0000 +00003f7f00 +0000000000 +-->8-- +--8<-- 3f00000000 0000000000 0000000000 @@ -174,8 +204,8 @@ efefefefef -->8-- --8<-- efefefefef -efefbbefef -efd97fefef +efefbaefef +efd87fefef efefefefef -->8-- --8<-- @@ -241,7 +271,7 @@ e079e079000000000000 --8<-- 00000000000000000000 00000000e07900000000 -0000e038e0fb00000000 +00000041e0fb00000000 00000000000000000000 -->8-- --8<-- @@ -265,7 +295,7 @@ e079e079000000000000 --8<-- e007e007e007e007e007 e007e007e07de007e007 -e007e03ee0fbe007e007 +e007e046e0fbe007e007 e007e007e007e007e007 -->8-- --8<-- @@ -322,31 +352,31 @@ e007e007e007e007e007 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000055555500000000000000000000000000 -000000000000000000000000005555555555000000000000000000000000 -000000000000000000000000555555555555550000000000000000000000 -000000000000000000000055555555555555555500000000000000000000 -000000000000000000000055555555555555555500000000000000000000 -000000000000000000005555555555555555555555000000000000000000 -000000000000000000005555555555555555555555000000000000000000 -000000000000000000005555555555555555555555000000000000000000 -000000000000000000555555555555555555555555550000000000000000 -000000000000000000555555555555555555555555550000000000000000 -000000000000000000555555555555555555555555550000000000000000 -000000000000000000555555555555555555555555550000000000000000 -000000000000000000555555555555555555555555550000000000000000 -000000000000000000555555555555555555555555550000000000000000 -000000000000000000555555555555555555555555550000000000000000 -000000000000000000555555555555555555555555550000000000000000 -000000000000000000555555555555555555555555550000000000000000 -000000000000000000005555555555555555555555000000000000000000 -000000000000000000005555555555555555555555000000000000000000 -000000000000000000005555555555555555555555000000000000000000 -000000000000000000000055555555555555555500000000000000000000 -000000000000000000000055555555555555555500000000000000000000 -000000000000000000000000555555555555550000000000000000000000 -000000000000000000000000005555555555000000000000000000000000 -000000000000000000000000000055555500000000000000000000000000 +000000000000000000000000000054545400000000000000000000000000 +000000000000000000000000005454545454000000000000000000000000 +000000000000000000000000545454545454540000000000000000000000 +000000000000000000000054545454545454545400000000000000000000 +000000000000000000000054545454545454545400000000000000000000 +000000000000000000005454545454545454545454000000000000000000 +000000000000000000005454545454545454545454000000000000000000 +000000000000000000005454545454545454545454000000000000000000 +000000000000000000545454545454545454545454540000000000000000 +000000000000000000545454545454545454545454540000000000000000 +000000000000000000545454545454545454545454540000000000000000 +000000000000000000545454545454545454545454540000000000000000 +000000000000000000545454545454545454545454540000000000000000 +000000000000000000545454545454545454545454540000000000000000 +000000000000000000545454545454545454545454540000000000000000 +000000000000000000545454545454545454545454540000000000000000 +000000000000000000545454545454545454545454540000000000000000 +000000000000000000005454545454545454545454000000000000000000 +000000000000000000005454545454545454545454000000000000000000 +000000000000000000005454545454545454545454000000000000000000 +000000000000000000000054545454545454545400000000000000000000 +000000000000000000000054545454545454545400000000000000000000 +000000000000000000000000545454545454540000000000000000000000 +000000000000000000000000005454545454000000000000000000000000 +000000000000000000000000000054545400000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 -->8-- diff --git a/tests/extmod/framebuf_polygon.py b/tests/extmod/framebuf_polygon.py index 69467e503b81b..e510256bb0daf 100644 --- a/tests/extmod/framebuf_polygon.py +++ b/tests/extmod/framebuf_polygon.py @@ -12,7 +12,7 @@ raise SystemExit if framebuf.ALPHA: - # Not testing for anti-aliased lines yet + # Test non-antialiased version print("SKIP") raise SystemExit diff --git a/tests/extmod/framebuf_polygon_alpha.py b/tests/extmod/framebuf_polygon_alpha.py new file mode 100644 index 0000000000000..55120c47e3666 --- /dev/null +++ b/tests/extmod/framebuf_polygon_alpha.py @@ -0,0 +1,264 @@ +try: + import framebuf + from array import array +except ImportError: + print("SKIP") + raise SystemExit + + +# TODO: poly needs functions that aren't in dynruntime.h yet. +if not hasattr(framebuf.FrameBuffer, "poly"): + print("SKIP") + raise SystemExit + +if not framebuf.ALPHA: + # Testing for anti-aliased lines + print("SKIP") + raise SystemExit + + +def print_buffer(buffer, width, height): + for row in range(height): + for col in range(width): + val = buffer[(row * width) + col] + print(" {:02x}".format(val) if val else " ··", end="") + print() + + +buf = bytearray(70 * 70) + +w = 30 +h = 25 +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) +col = 0xFF +col_fill = 0x99 + +# This describes a arbitrary polygon (this happens to be a concave polygon in +# the shape of an upper-case letter 'M'). +poly = array( + "h", + ( + 0, + 20, + 3, + 20, + 3, + 10, + 6, + 17, + 9, + 10, + 9, + 20, + 12, + 20, + 12, + 3, + 9, + 3, + 6, + 10, + 3, + 3, + 0, + 3, + ), +) +# This describes the same polygon, but the points are in reverse order +# (it shouldn't matter if the polygon has clockwise or anti-clockwise +# winding). Also defined as a bytes instead of array. +poly_reversed = bytes( + ( + 0, + 3, + 3, + 3, + 6, + 10, + 9, + 3, + 12, + 3, + 12, + 20, + 9, + 20, + 9, + 10, + 6, + 17, + 3, + 10, + 3, + 20, + 0, + 20, + ) +) + +# Draw the line polygon (at the origin) and the reversed-order polygon (offset). +fbuf.fill(0) +fbuf.poly(0, 0, poly, col) +fbuf.poly(15, -2, poly_reversed, col) +print_buffer(buf, w, h) +print() + +# Same but filled. +fbuf.fill(0) +fbuf.poly(0, 0, poly, col_fill, True) +fbuf.poly(15, -2, poly_reversed, col_fill, True) +print_buffer(buf, w, h) +print() + +# Draw the fill then the outline to ensure that no fill goes outside the outline. +fbuf.fill(0) +fbuf.poly(0, 0, poly, col_fill, True) +fbuf.poly(0, 0, poly, col) +fbuf.poly(15, -2, poly, col_fill, True) +fbuf.poly(15, -2, poly, col) +print_buffer(buf, w, h) +print() + +# Draw the outline then the fill to ensure the fill completely covers the outline. +fbuf.fill(0) +fbuf.poly(0, 0, poly, col) +fbuf.poly(0, 0, poly, col_fill, True) +fbuf.poly(15, -2, poly, col) +fbuf.poly(15, -2, poly, col_fill, True) +print_buffer(buf, w, h) +print() + +# Draw polygons that will go out of bounds at each of the edges. +for x, y in ( + ( + -8, + -8, + ), + ( + 24, + -6, + ), + ( + 20, + 12, + ), + ( + -2, + 10, + ), +): + fbuf.fill(0) + fbuf.poly(x, y, poly, col) + print_buffer(buf, w, h) + print() + fbuf.fill(0) + fbuf.poly(x, y, poly_reversed, col, True) + print_buffer(buf, w, h) + print() + +# Edge cases: These two lists describe self-intersecting polygons +poly_hourglass = array("h", (0, 0, 9, 0, 0, 19, 9, 19)) +poly_star = array("h", (7, 0, 3, 18, 14, 5, 0, 5, 11, 18)) + +# As before, fill then outline. +fbuf.fill(0) +fbuf.poly(0, 2, poly_hourglass, col_fill, True) +fbuf.poly(0, 2, poly_hourglass, col) +fbuf.poly(12, 2, poly_star, col_fill, True) +fbuf.poly(12, 2, poly_star, col) +print_buffer(buf, w, h) +print() + +# Outline then fill. +fbuf.fill(0) +fbuf.poly(0, 2, poly_hourglass, col) +fbuf.poly(0, 2, poly_hourglass, col_fill, True) +fbuf.poly(12, 2, poly_star, col) +fbuf.poly(12, 2, poly_star, col_fill, True) +print_buffer(buf, w, h) +print() + +# Edge cases: These are "degenerate" polygons. +poly_empty = array("h") # Will draw nothing at all. +poly_one = array("h", (20, 20)) # Will draw a single point. +poly_two = array("h", (10, 10, 5, 5)) # Will draw a single line. +poly_wrong_length = array("h", (2, 2, 4)) # Will round down to one point. + +fbuf.fill(0) +fbuf.poly(0, 0, poly_empty, col) +fbuf.poly(0, 0, poly_one, col) +fbuf.poly(0, 0, poly_two, col) +fbuf.poly(0, 0, poly_wrong_length, col) +print_buffer(buf, w, h) +print() + +# A shape with a horizontal overhang. +poly_overhang = array("h", (0, 0, 0, 5, 5, 5, 5, 10, 10, 10, 10, 0)) + +fbuf.fill(0) +fbuf.poly(0, 0, poly_overhang, col) +fbuf.poly(0, 0, poly_overhang, col_fill, True) +print_buffer(buf, w, h) +print() + +fbuf.fill(0) +fbuf.poly(0, 0, poly_overhang, col_fill, True) +fbuf.poly(0, 0, poly_overhang, col) +print_buffer(buf, w, h) +print() + +# Triangles +w = 70 +h = 70 +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) +t1 = array("h", [40, 0, 20, 68, 62, 40]) +t2 = array("h", [40, 0, 0, 16, 20, 68]) + +fbuf.fill(0) +fbuf.poly(0, 0, t1, 0xFF, False) +fbuf.poly(0, 0, t2, 0xFF, False) +print_buffer(buf, w, h) + +fbuf.fill(0) +fbuf.poly(0, 0, t1, 0xFF, True) +fbuf.poly(0, 0, t2, 0xFF, True) +print_buffer(buf, w, h) + +# Now with alpha +w = 30 +h = 25 +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) +col = 0xFF +col_fill = 0x99 + +# Draw the line polygon (at the origin) and the reversed-order polygon (offset). +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, False, 0x7f) +fbuf.poly(15, -2, poly_reversed, col, False, 0x7f) +print_buffer(buf, w, h) +print() + +# Same but filled. +fbuf.fill(0) +fbuf.poly(0, 0, poly, col_fill, True, 0x7f) +fbuf.poly(15, -2, poly_reversed, col_fill, True, 0x7f) +print_buffer(buf, w, h) +print() + +# Draw the fill then the outline to ensure that no fill goes outside the outline. +fbuf.fill(0) +fbuf.poly(0, 0, poly, col_fill, True, 0x7f) +fbuf.poly(0, 0, poly, col, False, 0x7f) +fbuf.poly(15, -2, poly, col_fill, True, 0x7f) +fbuf.poly(15, -2, poly, col, False, 0x7f) +print_buffer(buf, w, h) +print() + +# Draw the outline then the fill to ensure the fill completely covers the outline. +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, False, 0x7f) +fbuf.poly(0, 0, poly, col_fill, True, 0x7f) +fbuf.poly(15, -2, poly, col, False, 0x7f) +fbuf.poly(15, -2, poly, col_fill, True, 0x7f) +print_buffer(buf, w, h) +print() diff --git a/tests/extmod/framebuf_polygon_alpha.py.exp b/tests/extmod/framebuf_polygon_alpha.py.exp new file mode 100644 index 0000000000000..df8a66d50c948 --- /dev/null +++ b/tests/extmod/framebuf_polygon_alpha.py.exp @@ -0,0 +1,686 @@ +·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· 92 6d ·· ·· ·· 6d 92 ·· ·· ff ·· ·· + ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff ·· ·· 26 d9 ·· ·· ·· d9 26 ·· ·· ff ·· ·· + ff ·· ·· 92 6d ·· ·· ·· 6d 92 ·· ·· ff ·· ·· ff ·· ·· ·· b8 47 ·· 47 b8 ·· ·· ·· ff ·· ·· + ff ·· ·· 26 d9 ·· ·· ·· d9 26 ·· ·· ff ·· ·· ff ·· ·· ·· 4c b3 ·· b3 4c ·· ·· ·· ff ·· ·· + ff ·· ·· ·· b8 47 ·· 47 b8 ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· de 3d de ·· ·· ·· ·· ff ·· ·· + ff ·· ·· ·· 4c b3 ·· b3 4c ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· 72 cc 72 ·· ·· ·· ·· ff ·· ·· + ff ·· ·· ·· ·· de 3d de ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ·· ·· 72 cc 72 ·· ·· ·· ·· ff ·· ·· ff ·· ·· fe 6d ·· ·· ·· 6d ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· fe d9 ·· ·· ·· d9 ff ·· ·· ff ·· ·· + ff ·· ·· ff 6d ·· ·· ·· 6d fe ·· ·· ff ·· ·· ff ·· ·· ff b8 47 ·· 47 b8 ff ·· ·· ff ·· ·· + ff ·· ·· ff d9 ·· ·· ·· d9 fe ·· ·· ff ·· ·· ff ·· ·· ff 4c b3 ·· b3 4c ff ·· ·· ff ·· ·· + ff ·· ·· ff b8 47 ·· 47 b8 ff ·· ·· ff ·· ·· ff ·· ·· ff ·· de 3d de ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff 4c b3 ·· b3 4c ff ·· ·· ff ·· ·· ff ·· ·· ff ·· 72 cc 72 ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· de 3d de ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· 72 cc 72 ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· + ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 26 4d 4d 26 ·· ·· ·· ·· ·· 26 4d 4d 26 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4d 99 99 86 ·· ·· ·· ·· ·· 86 99 99 4d ·· ·· + 26 4d 4d 26 ·· ·· ·· ·· ·· 26 4d 4d 26 ·· ·· 4d 99 99 99 39 ·· ·· ·· 39 99 99 99 4d ·· ·· + 4d 99 99 86 ·· ·· ·· ·· ·· 86 99 99 4d ·· ·· 4d 99 99 99 86 ·· ·· ·· 86 99 99 99 4d ·· ·· + 4d 99 99 99 39 ·· ·· ·· 39 99 99 99 4d ·· ·· 4d 99 99 99 99 13 ·· 13 99 99 99 99 4d ·· ·· + 4d 99 99 99 86 ·· ·· ·· 86 99 99 99 4d ·· ·· 4d 99 99 99 99 60 ·· 60 99 99 99 99 4d ·· ·· + 4d 99 99 99 99 13 ·· 13 99 99 99 99 4d ·· ·· 4d 99 99 99 99 99 26 99 99 99 99 99 4d ·· ·· + 4d 99 99 99 99 60 ·· 60 99 99 99 99 4d ·· ·· 4d 99 99 99 99 99 99 99 99 99 99 99 4d ·· ·· + 4d 99 99 99 99 99 26 99 99 99 99 99 4d ·· ·· 4d 99 99 60 99 99 99 99 99 60 99 99 4d ·· ·· + 4d 99 99 99 99 99 99 99 99 99 99 99 4d ·· ·· 4d 99 99 4d 60 99 99 99 60 4d 99 99 4d ·· ·· + 4d 99 99 60 99 99 99 99 99 60 99 99 4d ·· ·· 4d 99 99 4d 13 99 99 99 13 4d 99 99 4d ·· ·· + 4d 99 99 4d 60 99 99 99 60 4d 99 99 4d ·· ·· 4d 99 99 4d ·· 86 99 86 ·· 4d 99 99 4d ·· ·· + 4d 99 99 4d 13 99 99 99 13 4d 99 99 4d ·· ·· 4d 99 99 4d ·· 39 99 39 ·· 4d 99 99 4d ·· ·· + 4d 99 99 4d ·· 86 99 86 ·· 4d 99 99 4d ·· ·· 4d 99 99 4d ·· ·· 73 ·· ·· 4d 99 99 4d ·· ·· + 4d 99 99 4d ·· 39 99 39 ·· 4d 99 99 4d ·· ·· 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· + 4d 99 99 4d ·· ·· 73 ·· ·· 4d 99 99 4d ·· ·· 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· + 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· + 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· 26 4d 4d 26 ·· ·· ·· ·· ·· 26 4d 4d 26 ·· ·· + 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 26 4d 4d 26 ·· ·· ·· ·· ·· 26 4d 4d 26 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 99 99 cb 6d ·· ·· ·· 6d cb 99 99 ff ·· ·· + ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff 99 99 a8 e1 ·· ·· ·· e1 a8 99 99 ff ·· ·· + ff 99 99 cb 6d ·· ·· ·· 6d cb 99 99 ff ·· ·· ff 99 99 99 dd 47 ·· 47 dd 99 99 99 ff ·· ·· + ff 99 99 a8 e1 ·· ·· ·· e1 a8 99 99 ff ·· ·· ff 99 99 99 b7 b9 ·· b9 b7 99 99 99 ff ·· ·· + ff 99 99 99 dd 47 ·· 47 dd 99 99 99 ff ·· ·· ff 99 99 99 99 ea 3d ea 99 99 99 99 ff ·· ·· + ff 99 99 99 b7 b9 ·· b9 b7 99 99 99 ff ·· ·· ff 99 99 99 99 c6 d3 c6 99 99 99 99 ff ·· ·· + ff 99 99 99 99 ea 3d ea 99 99 99 99 ff ·· ·· ff 99 99 ff 99 99 ff 99 99 ff 99 99 ff ·· ·· + ff 99 99 99 99 c6 d3 c6 99 99 99 99 ff ·· ·· ff 99 99 ff c4 99 99 99 c4 fe 99 99 ff ·· ·· + ff 99 99 ff 99 99 ff 99 99 ff 99 99 ff ·· ·· ff 99 99 ff e7 99 99 99 e7 fe 99 99 ff ·· ·· + ff 99 99 ff c4 99 99 99 c4 fe 99 99 ff ·· ·· ff 99 99 ff bd b5 99 b5 bd ff 99 99 ff ·· ·· + ff 99 99 ff e7 99 99 99 e7 fe 99 99 ff ·· ·· ff 99 99 ff 4c db 99 db 4c ff 99 99 ff ·· ·· + ff 99 99 ff bd b5 99 b5 bd ff 99 99 ff ·· ·· ff 99 99 ff ·· e5 b1 e5 ·· ff 99 99 ff ·· ·· + ff 99 99 ff 4c db 99 db 4c ff 99 99 ff ·· ·· ff 99 99 ff ·· 72 e2 72 ·· ff 99 99 ff ·· ·· + ff 99 99 ff ·· e5 b1 e5 ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ff ·· ·· ff 99 99 ff ·· ·· + ff 99 99 ff ·· 72 e2 72 ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· + ff 99 99 ff ·· ·· ff ·· ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· + ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· + ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e5 cb cb e5 ·· ·· ·· ·· ·· e5 cb cb e5 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· cb 99 99 98 6d ·· ·· ·· 6d 98 99 99 cb ·· ·· + e5 cb cb e5 ·· ·· ·· ·· ·· e5 cb cb e5 ·· ·· cb 99 99 99 c0 ·· ·· ·· c0 99 99 99 cb ·· ·· + cb 99 99 98 6d ·· ·· ·· 6d 98 99 99 cb ·· ·· cb 99 99 99 9c 47 ·· 47 9c 99 99 99 cb ·· ·· + cb 99 99 99 c0 ·· ·· ·· c0 99 99 99 cb ·· ·· cb 99 99 99 99 af ·· af 99 99 99 99 cb ·· ·· + cb 99 99 99 9c 47 ·· 47 9c 99 99 99 cb ·· ·· cb 99 99 99 99 b2 3d b2 99 99 99 99 cb ·· ·· + cb 99 99 99 99 af ·· af 99 99 99 99 cb ·· ·· cb 99 99 99 99 99 be 99 99 99 99 99 cb ·· ·· + cb 99 99 99 99 b2 3d b2 99 99 99 99 cb ·· ·· cb 99 99 99 99 99 99 99 99 99 99 99 cb ·· ·· + cb 99 99 99 99 99 be 99 99 99 99 99 cb ·· ·· cb 99 99 be 99 99 99 99 99 be 99 99 cb ·· ·· + cb 99 99 99 99 99 99 99 99 99 99 99 cb ·· ·· cb 99 99 cb b0 99 99 99 b0 cb 99 99 cb ·· ·· + cb 99 99 be 99 99 99 99 99 be 99 99 cb ·· ·· cb 99 99 cb b3 99 99 99 b3 cb 99 99 cb ·· ·· + cb 99 99 cb b0 99 99 99 b0 cb 99 99 cb ·· ·· cb 99 99 cb 4c 9c 99 9c 4c cb 99 99 cb ·· ·· + cb 99 99 cb b3 99 99 99 b3 cb 99 99 cb ·· ·· cb 99 99 cb ·· c3 99 c3 ·· cb 99 99 cb ·· ·· + cb 99 99 cb 4c 9c 99 9c 4c cb 99 99 cb ·· ·· cb 99 99 cb ·· 72 a5 72 ·· cb 99 99 cb ·· ·· + cb 99 99 cb ·· c3 99 c3 ·· cb 99 99 cb ·· ·· cb 99 99 cb ·· ·· ff ·· ·· cb 99 99 cb ·· ·· + cb 99 99 cb ·· 72 a5 72 ·· cb 99 99 cb ·· ·· cb 99 99 cb ·· ·· ·· ·· ·· cb 99 99 cb ·· ·· + cb 99 99 cb ·· ·· ff ·· ·· cb 99 99 cb ·· ·· cb 99 99 cb ·· ·· ·· ·· ·· cb 99 99 cb ·· ·· + cb 99 99 cb ·· ·· ·· ·· ·· cb 99 99 cb ·· ·· e5 cb cb e5 ·· ·· ·· ·· ·· e5 cb cb e5 ·· ·· + cb 99 99 cb ·· ·· ·· ·· ·· cb 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + e5 cb cb e5 ·· ·· ·· ·· ·· e5 cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 6d fe ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + d9 fe ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + b8 ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 4c ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 9f ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 9f 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 20 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 40 80 80 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· b8 47 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· 4c b3 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· de + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· 72 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff 6d ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff d9 ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff b8 47 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff 4c b3 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· de + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· 72 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff df ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff 20 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff 9f + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 9f ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 9f ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 20 ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· df + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· 60 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 80 80 40 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· 92 6d ·· ·· ·· 6d 92 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· 26 d9 ·· ·· ·· d9 26 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· b8 47 ·· 47 b8 ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· 4c b3 ·· b3 4c ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· de 3d de ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· 72 cc 72 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff 6d ·· ·· ·· 6d fe + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff d9 ·· ·· ·· d9 fe + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 80 80 40 ·· ·· ·· ·· ·· 40 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff df ·· ·· ·· ·· ·· df + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff 60 ·· ·· ·· 60 ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff df ·· ·· ·· df ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff 20 ·· 20 ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff 9f ·· 9f ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff 40 ff ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff ff ff ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 9f ff ff ff ff ff 9f + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 9f ff ff ff 9f 80 + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 92 6d ·· ·· ·· 6d 92 ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 26 d9 ·· ·· ·· d9 26 ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· b8 47 ·· 47 b8 ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· 4c b3 ·· b3 4c ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· de 3d de ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 72 cc 72 ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff 6d ·· ·· ·· 6d fe ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff d9 ·· ·· ·· d9 fe ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff b8 47 ·· 47 b8 ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff 4c b3 ·· b3 4c ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 80 40 ·· ·· ·· ·· ·· 40 80 80 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff df ·· ·· ·· ·· ·· df ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff 60 ·· ·· ·· 60 ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff df ·· ·· ·· df ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff 20 ·· 20 ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff 9f ·· 9f ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ff 40 ff ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ff ff ff ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 9f ff ff ff ff ff 9f ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 80 9f ff ff ff 9f 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 80 20 ff ff ff 20 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 80 ·· df ff df ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 8f c9 99 99 99 99 99 99 c9 8f ·· ·· ·· ·· ·· ·· ·· ·· 38 f6 38 ·· ·· ·· ·· ·· ·· ·· ·· ·· + 0e f6 99 99 99 99 99 99 f6 0e ·· ·· ·· ·· ·· ·· ·· ·· 70 ea 70 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 9c c3 99 99 99 99 c3 9c ·· ·· ·· ·· ·· ·· ·· ·· ·· ae d2 ae ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 1c ed 99 99 99 99 ed 1c ·· ·· ·· ·· ·· ·· ·· ·· ·· e6 b0 e6 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· a9 bd 99 99 bd a9 ·· ·· ·· ·· ff ff ff ff ff fe fe ff ff ff ff ff ff ff ff ·· ·· ·· + ·· ·· 2a e5 99 99 e5 2a ·· ·· ·· ·· 28 e6 99 99 99 b8 b5 ·· b5 b8 99 99 99 e6 28 ·· ·· ·· + ·· ·· ·· b6 b8 b8 b6 ·· ·· ·· ·· ·· ·· 50 d3 99 99 cf 78 ·· 78 cf 99 99 d3 50 ·· ·· ·· ·· + ·· ·· ·· 38 dc e0 38 ·· ·· ·· ·· ·· ·· ·· 82 c6 99 dc 40 ·· 40 dc 99 c6 82 ·· ·· ·· ·· ·· + ·· ·· ·· ·· d1 dd ·· ·· ·· ·· ·· ·· ·· ·· ·· a6 bf f9 08 ·· 08 f9 bf a6 ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· d0 db ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d7 df ·· ·· ·· de d8 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 33 df db 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 fa 10 ·· 10 fa 68 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· b1 ba ba b1 ·· ·· ·· ·· ·· ·· ·· ·· ·· a6 c4 ee ·· ee c4 a6 ·· ·· ·· ·· ·· ·· ·· + ·· ·· 25 e8 99 99 e8 25 ·· ·· ·· ·· ·· ·· ·· ·· e0 a8 b2 f3 b2 a8 e0 ·· ·· ·· ·· ·· ·· ·· + ·· ·· a5 bf 99 99 bf a5 ·· ·· ·· ·· ·· ·· ·· 10 f5 99 cd a5 cd 99 f5 10 ·· ·· ·· ·· ·· ·· + ·· 17 f0 99 99 99 99 f0 17 ·· ·· ·· ·· ·· ·· 48 dc ba 98 ·· 98 ba dc 48 ·· ·· ·· ·· ·· ·· + ·· 98 c5 99 99 99 99 c5 98 ·· ·· ·· ·· ·· ·· 80 d9 c2 ·· ·· ·· c2 d9 80 ·· ·· ·· ·· ·· ·· + 09 f9 99 99 99 99 99 99 f9 09 ·· ·· ·· ·· ·· c9 ec ·· ·· ·· ·· ·· ed c9 ·· ·· ·· ·· ·· ·· + 8b cb 99 99 99 99 99 99 cb 8b ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· + ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + e5 cb cb cb cb cb cb cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 88 99 99 99 99 99 99 99 99 88 ·· ·· ·· ·· ·· ·· ·· ·· 38 c5 38 ·· ·· ·· ·· ·· ·· ·· ·· ·· + 0e b9 99 99 99 99 99 99 b9 0e ·· ·· ·· ·· ·· ·· ·· ·· 70 99 70 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 94 99 99 99 99 99 99 94 ·· ·· ·· ·· ·· ·· ·· ·· ·· a5 99 a5 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 1c b4 99 99 99 99 b4 1c ·· ·· ·· ·· ·· ·· ·· ·· ·· c4 99 c4 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· a0 99 99 99 99 a0 ·· ·· ·· ·· f1 cb cb cb cb cb d7 cb d8 cb cb cb cb cb f1 ·· ·· ·· + ·· ·· 2a af 99 99 af 2a ·· ·· ·· ·· 28 af 99 99 99 99 ac ·· ac 99 99 99 99 af 28 ·· ·· ·· + ·· ·· ·· ac 99 99 ac ·· ·· ·· ·· ·· ·· 50 9e 99 99 99 78 ·· 78 99 99 99 9e 50 ·· ·· ·· ·· + ·· ·· ·· 38 a9 a4 38 ·· ·· ·· ·· ·· ·· ·· 7c 96 99 a2 40 ·· 40 a2 99 96 7c ·· ·· ·· ·· ·· + ·· ·· ·· ·· c7 b3 ·· ·· ·· ·· ·· ·· ·· ·· ·· 9e 99 c7 08 ·· 08 c7 99 9e ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· c5 b1 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c2 c8 ·· ·· ·· c8 c3 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 33 ab b2 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 c8 10 ·· 10 c8 68 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· a8 99 99 a8 ·· ·· ·· ·· ·· ·· ·· ·· ·· 9e 99 bf ·· bf 99 9e ·· ·· ·· ·· ·· ·· ·· + ·· ·· 25 b1 99 99 b1 25 ·· ·· ·· ·· ·· ·· ·· ·· bf 99 99 c3 99 99 bf ·· ·· ·· ·· ·· ·· ·· + ·· ·· 9c 99 99 99 99 9c ·· ·· ·· ·· ·· ·· ·· 10 b8 99 98 a5 98 99 b8 10 ·· ·· ·· ·· ·· ·· + ·· 17 b6 99 99 99 99 b6 17 ·· ·· ·· ·· ·· ·· 48 9c 93 90 ·· 90 93 9c 48 ·· ·· ·· ·· ·· ·· + ·· 90 99 99 99 99 99 99 90 ·· ·· ·· ·· ·· ·· 80 99 af ·· ·· ·· af 99 80 ·· ·· ·· ·· ·· ·· + 09 bb 99 99 99 99 99 99 bb 09 ·· ·· ·· ·· ·· b6 c9 ·· ·· ·· ·· ·· c9 b5 ·· ·· ·· ·· ·· ·· + 83 99 99 99 99 99 99 99 99 83 ·· ·· ·· ·· ·· f1 ·· ·· ·· ·· ·· ·· ·· f1 ·· ·· ·· ·· ·· ·· + e5 cb cb cb cb cb cb cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + e5 cb cb cb cb cb cb cb cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + cb 99 99 99 99 99 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + cb 99 99 99 99 99 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + cb 99 99 99 99 99 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + cb 99 99 99 99 99 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + e5 cb cb cb cb b2 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· cb 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· cb 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· cb 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· cb 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· e5 cb cb cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 99 99 99 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 99 99 99 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 99 99 99 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 99 99 99 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ff ff ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 24 89 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 58 bd db ba f2 8b ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 26 8b f1 a7 42 ·· d3 a7 e7 18 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5a bf d9 74 0e ·· ·· ·· fa 3a 5c a3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 28 8d f3 a5 40 ·· ·· ·· ·· ·· 50 f7 ·· ·· cf 30 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5c c1 d7 72 0c ·· ·· ·· ·· ·· ·· ·· b6 c7 ·· ·· 44 bb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 2a 8f f5 a3 3e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ef 6d ·· ·· ·· b7 48 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5e c3 d5 70 0a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 19 fd ·· ·· ·· ·· 2c d3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 2c 91 f7 a1 3c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 91 e0 ·· ·· ·· ·· ·· 9f 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 c5 d3 6e 08 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· dd 97 ·· ·· ·· ·· ·· 14 eb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 2e 93 f9 9f 3a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fd 23 ·· ·· ·· ·· ·· ·· 87 78 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 62 c7 d1 6c 06 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 65 f2 ·· ·· ·· ·· ·· ·· ·· ·· fb 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· 30 95 fb 9d 38 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c2 bb ·· ·· ·· ·· ·· ·· ·· ·· 70 8f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· 64 c9 cf 6a 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f5 58 ·· ·· ·· ·· ·· ·· ·· ·· ·· e3 1c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 32 97 fd 9b 36 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 31 fb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 58 a7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 66 cb cd 68 02 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a1 d7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· cb 34 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 99 34 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e5 86 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 bf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 9d 62 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fe 0a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b3 4c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 3c c3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 78 eb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 28 d7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· d9 26 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ce ad ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9b 64 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 78 87 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f9 43 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 10 ef ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 16 e9 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 48 f8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 83 7c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· b3 4c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b0 cc ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f7 08 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· 52 ad ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ed 74 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 6c 93 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ef 10 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 10 fe ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 8d 72 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 8a e3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 54 ab ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 2c d3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d9 9e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c7 38 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· c9 36 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fc 2c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3c c3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· 68 97 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5d f4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· af 50 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· 06 f9 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· be bf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 24 db ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· a3 5c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f3 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 97 68 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· 42 bd ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 28 fc ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 0c f3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· df 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9b da ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· 7e 81 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e2 8d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f3 0c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· 1c e3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fe 14 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 97 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· b9 46 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 71 ee ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· db 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· 58 a7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ca b3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 50 af ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· f5 0a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f8 4b ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c3 3c ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· 93 6c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3f fa ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 38 c7 ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· 32 cd ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ab d0 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ab 54 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· cf 30 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ea 7b ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3a ff ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· 6e 91 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 06 fe ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 8f c5 ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· 0c f3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 84 e7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3c e5 70 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a9 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d5 a4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 91 c3 1a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 48 b7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fb 35 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3e e7 6e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e5 1a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 55 f6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 93 c1 18 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 83 7c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b9 c4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 e9 6c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 22 dd ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f1 68 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 95 bf 16 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· bf 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 1f fd ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 42 eb 6a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5e a1 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 95 de ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 97 bd 14 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fb 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df 94 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 44 ed 68 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 66 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fd 1d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 bb 12 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 38 c7 ·· ·· ·· ·· ·· ·· ·· ·· ·· 69 f0 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 46 ef 66 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d5 2a ·· ·· ·· ·· ·· ·· ·· ·· c5 b8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9b b9 10 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 74 8b ·· ·· ·· ·· ·· ·· ·· ·· f6 53 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 48 f1 64 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 12 ed ·· ·· ·· ·· ·· ·· ·· 36 fb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9d b7 0e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· af 50 ·· ·· ·· ·· ·· ·· a5 d4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4a f3 62 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4e b1 ·· ·· ·· ·· ·· ·· e7 82 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f b5 0c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· eb 14 ·· ·· ·· ·· ·· fe 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· 4c f5 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 89 76 ·· ·· ·· ·· 7d e9 ·· ·· ·· ·· ·· ·· ·· ·· ·· a1 b3 0a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 28 d7 ·· ·· ·· ·· d1 aa ·· ·· ·· ·· ·· ·· ·· 4e f7 5e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c5 3a ·· ·· ·· fa 3d ·· ·· ·· ·· ·· ·· a3 b1 08 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 64 9b ·· ·· 4d f7 ·· ·· ·· ·· ·· 50 f9 5c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 02 fd ·· ·· b4 c9 ·· ·· ·· ·· a5 af 06 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f 60 ·· ee 70 ·· ·· 52 fb 5a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3e c1 15 fe ·· ·· a7 ad 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· db 9d e1 54 fd 58 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 7a ed dc ab 02 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 58 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ce 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff e3 ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff c3 ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff c2 ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff ff ff ff ff cf ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff df 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff bf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 40 df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff bf ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· 40 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· 80 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff bf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 40 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· bf ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff bf ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff e3 ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff c3 ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ce ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff c3 ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff c2 ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 ff e2 ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c7 df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 7f 7f 7f 7f ·· ·· ·· ·· ·· 7f 7f 7f 7f ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 7f ·· ·· 48 36 ·· ·· ·· 36 48 ·· ·· 7f ·· ·· + 7f 7f 7f 7f ·· ·· ·· ·· ·· 7f 7f 7f 7f ·· ·· 7f ·· ·· 12 6c ·· ·· ·· 6c 12 ·· ·· 7f ·· ·· + 7f ·· ·· 48 36 ·· ·· ·· 36 48 ·· ·· 7f ·· ·· 7f ·· ·· ·· 5b 23 ·· 23 5b ·· ·· ·· 7f ·· ·· + 7f ·· ·· 12 6c ·· ·· ·· 6c 12 ·· ·· 7f ·· ·· 7f ·· ·· ·· 25 59 ·· 59 25 ·· ·· ·· 7f ·· ·· + 7f ·· ·· ·· 5b 23 ·· 23 5b ·· ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· 6e 1f 6e ·· ·· ·· ·· 7f ·· ·· + 7f ·· ·· ·· 25 59 ·· 59 25 ·· ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· 38 78 38 ·· ·· ·· ·· 7f ·· ·· + 7f ·· ·· ·· ·· 6e 1f 6e ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· + 7f ·· ·· ·· ·· 38 78 38 ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· a3 36 ·· ·· ·· 36 a3 ·· ·· 7f ·· ·· + 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 88 6c ·· ·· ·· 6c 88 ·· ·· 7f ·· ·· + 7f ·· ·· a3 36 ·· ·· ·· 36 a3 ·· ·· 7f ·· ·· 7f ·· ·· 7f 5b 23 ·· 23 5b 7f ·· ·· 7f ·· ·· + 7f ·· ·· 88 6c ·· ·· ·· 6c 88 ·· ·· 7f ·· ·· 7f ·· ·· 7f 25 59 ·· 59 25 7f ·· ·· 7f ·· ·· + 7f ·· ·· 7f 5b 23 ·· 23 5b 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· 6e 1f 6e ·· 7f ·· ·· 7f ·· ·· + 7f ·· ·· 7f 25 59 ·· 59 25 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· 38 78 38 ·· 7f ·· ·· 7f ·· ·· + 7f ·· ·· 7f ·· 6e 1f 6e ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· + 7f ·· ·· 7f ·· 38 78 38 ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· + 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· + 7f ·· ·· 7f ·· ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· 7f 7f 7f 7f ·· ·· ·· ·· ·· 7f 7f 7f 7f ·· ·· + 7f ·· ·· 7f ·· ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 7f 7f 7f 7f ·· ·· ·· ·· ·· 7f 7f 7f 7f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 13 26 26 13 ·· ·· ·· ·· ·· 13 26 26 13 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 26 4c 4c 42 ·· ·· ·· ·· ·· 42 4c 4c 26 ·· ·· + 13 26 26 13 ·· ·· ·· ·· ·· 13 26 26 13 ·· ·· 26 4c 4c 4c 1c ·· ·· ·· 1c 4c 4c 4c 26 ·· ·· + 26 4c 4c 42 ·· ·· ·· ·· ·· 42 4c 4c 26 ·· ·· 26 4c 4c 4c 42 ·· ·· ·· 42 4c 4c 4c 26 ·· ·· + 26 4c 4c 4c 1c ·· ·· ·· 1c 4c 4c 4c 26 ·· ·· 26 4c 4c 4c 4c 09 ·· 09 4c 4c 4c 4c 26 ·· ·· + 26 4c 4c 4c 42 ·· ·· ·· 42 4c 4c 4c 26 ·· ·· 26 4c 4c 4c 4c 2f ·· 2f 4c 4c 4c 4c 26 ·· ·· + 26 4c 4c 4c 4c 09 ·· 09 4c 4c 4c 4c 26 ·· ·· 26 4c 4c 4c 4c 4c 13 4c 4c 4c 4c 4c 26 ·· ·· + 26 4c 4c 4c 4c 2f ·· 2f 4c 4c 4c 4c 26 ·· ·· 26 4c 4c 4c 4c 4c 4c 4c 4c 4c 4c 4c 26 ·· ·· + 26 4c 4c 4c 4c 4c 13 4c 4c 4c 4c 4c 26 ·· ·· 26 4c 4c 2f 4c 4c 4c 4c 4c 2f 4c 4c 26 ·· ·· + 26 4c 4c 4c 4c 4c 4c 4c 4c 4c 4c 4c 26 ·· ·· 26 4c 4c 26 2f 4c 4c 4c 2f 26 4c 4c 26 ·· ·· + 26 4c 4c 2f 4c 4c 4c 4c 4c 2f 4c 4c 26 ·· ·· 26 4c 4c 26 09 4c 4c 4c 09 26 4c 4c 26 ·· ·· + 26 4c 4c 26 2f 4c 4c 4c 2f 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· 42 4c 42 ·· 26 4c 4c 26 ·· ·· + 26 4c 4c 26 09 4c 4c 4c 09 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· 1c 4c 1c ·· 26 4c 4c 26 ·· ·· + 26 4c 4c 26 ·· 42 4c 42 ·· 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· ·· 39 ·· ·· 26 4c 4c 26 ·· ·· + 26 4c 4c 26 ·· 1c 4c 1c ·· 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· + 26 4c 4c 26 ·· ·· 39 ·· ·· 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· + 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· + 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· 13 26 26 13 ·· ·· ·· ·· ·· 13 26 26 13 ·· ·· + 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 13 26 26 13 ·· ·· ·· ·· ·· 13 26 26 13 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 88 92 92 88 ·· ·· ·· ·· ·· 88 92 92 88 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 4c 4c 77 36 ·· ·· ·· 36 77 4c 4c 92 ·· ·· + 88 92 92 88 ·· ·· ·· ·· ·· 88 92 92 88 ·· ·· 92 4c 4c 58 7c ·· ·· ·· 7c 58 4c 4c 92 ·· ·· + 92 4c 4c 77 36 ·· ·· ·· 36 77 4c 4c 92 ·· ·· 92 4c 4c 4c 85 23 ·· 23 85 4c 4c 4c 92 ·· ·· + 92 4c 4c 58 7c ·· ·· ·· 7c 58 4c 4c 92 ·· ·· 92 4c 4c 4c 66 5e ·· 5e 66 4c 4c 4c 92 ·· ·· + 92 4c 4c 4c 85 23 ·· 23 85 4c 4c 4c 92 ·· ·· 92 4c 4c 4c 4c 88 1f 88 4c 4c 4c 4c 92 ·· ·· + 92 4c 4c 4c 66 5e ·· 5e 66 4c 4c 4c 92 ·· ·· 92 4c 4c 4c 4c 73 82 73 4c 4c 4c 4c 92 ·· ·· + 92 4c 4c 4c 4c 88 1f 88 4c 4c 4c 4c 92 ·· ·· 92 4c 4c a5 4c 4c a5 4c 4c a5 4c 4c 92 ·· ·· + 92 4c 4c 4c 4c 73 82 73 4c 4c 4c 4c 92 ·· ·· 92 4c 4c b3 71 4c 4c 4c 71 b3 4c 4c 92 ·· ·· + 92 4c 4c a5 4c 4c a5 4c 4c a5 4c 4c 92 ·· ·· 92 4c 4c 99 87 4c 4c 4c 87 99 4c 4c 92 ·· ·· + 92 4c 4c b3 71 4c 4c 4c 71 b3 4c 4c 92 ·· ·· 92 4c 4c 92 60 64 4c 64 60 92 4c 4c 92 ·· ·· + 92 4c 4c 99 87 4c 4c 4c 87 99 4c 4c 92 ·· ·· 92 4c 4c 92 25 83 4c 83 25 92 4c 4c 92 ·· ·· + 92 4c 4c 92 60 64 4c 64 60 92 4c 4c 92 ·· ·· 92 4c 4c 92 ·· 7d 61 7d ·· 92 4c 4c 92 ·· ·· + 92 4c 4c 92 25 83 4c 83 25 92 4c 4c 92 ·· ·· 92 4c 4c 92 ·· 38 96 38 ·· 92 4c 4c 92 ·· ·· + 92 4c 4c 92 ·· 7d 61 7d ·· 92 4c 4c 92 ·· ·· 92 4c 4c 92 ·· ·· 7f ·· ·· 92 4c 4c 92 ·· ·· + 92 4c 4c 92 ·· 38 96 38 ·· 92 4c 4c 92 ·· ·· 92 4c 4c 92 ·· ·· ·· ·· ·· 92 4c 4c 92 ·· ·· + 92 4c 4c 92 ·· ·· 7f ·· ·· 92 4c 4c 92 ·· ·· 92 4c 4c 92 ·· ·· ·· ·· ·· 92 4c 4c 92 ·· ·· + 92 4c 4c 92 ·· ·· ·· ·· ·· 92 4c 4c 92 ·· ·· 88 92 92 88 ·· ·· ·· ·· ·· 88 92 92 88 ·· ·· + 92 4c 4c 92 ·· ·· ·· ·· ·· 92 4c 4c 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 88 92 92 88 ·· ·· ·· ·· ·· 88 92 92 88 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 82 85 85 82 ·· ·· ·· ·· ·· 82 85 85 82 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 85 4c 4c 6b 36 ·· ·· ·· 36 6b 4c 4c 85 ·· ·· + 82 85 85 82 ·· ·· ·· ·· ·· 82 85 85 82 ·· ·· 85 4c 4c 55 74 ·· ·· ·· 74 55 4c 4c 85 ·· ·· + 85 4c 4c 6b 36 ·· ·· ·· 36 6b 4c 4c 85 ·· ·· 85 4c 4c 4c 76 23 ·· 23 76 4c 4c 4c 85 ·· ·· + 85 4c 4c 55 74 ·· ·· ·· 74 55 4c 4c 85 ·· ·· 85 4c 4c 4c 5e 5c ·· 5c 5e 4c 4c 4c 85 ·· ·· + 85 4c 4c 4c 76 23 ·· 23 76 4c 4c 4c 85 ·· ·· 85 4c 4c 4c 4c 7b 1f 7b 4c 4c 4c 4c 85 ·· ·· + 85 4c 4c 4c 5e 5c ·· 5c 5e 4c 4c 4c 85 ·· ·· 85 4c 4c 4c 4c 68 7c 68 4c 4c 4c 4c 85 ·· ·· + 85 4c 4c 4c 4c 7b 1f 7b 4c 4c 4c 4c 85 ·· ·· 85 4c 4c 8b 4c 4c 8b 4c 4c 8b 4c 4c 85 ·· ·· + 85 4c 4c 4c 4c 68 7c 68 4c 4c 4c 4c 85 ·· ·· 85 4c 4c 9f 67 4c 4c 4c 67 9f 4c 4c 85 ·· ·· + 85 4c 4c 8b 4c 4c 8b 4c 4c 8b 4c 4c 85 ·· ·· 85 4c 4c 8c 79 4c 4c 4c 79 8c 4c 4c 85 ·· ·· + 85 4c 4c 9f 67 4c 4c 4c 67 9f 4c 4c 85 ·· ·· 85 4c 4c 85 5e 5d 4c 5d 5e 85 4c 4c 85 ·· ·· + 85 4c 4c 8c 79 4c 4c 4c 79 8c 4c 4c 85 ·· ·· 85 4c 4c 85 25 74 4c 74 25 85 4c 4c 85 ·· ·· + 85 4c 4c 85 5e 5d 4c 5d 5e 85 4c 4c 85 ·· ·· 85 4c 4c 85 ·· 75 5b 75 ·· 85 4c 4c 85 ·· ·· + 85 4c 4c 85 25 74 4c 74 25 85 4c 4c 85 ·· ·· 85 4c 4c 85 ·· 38 84 38 ·· 85 4c 4c 85 ·· ·· + 85 4c 4c 85 ·· 75 5b 75 ·· 85 4c 4c 85 ·· ·· 85 4c 4c 85 ·· ·· 7f ·· ·· 85 4c 4c 85 ·· ·· + 85 4c 4c 85 ·· 38 84 38 ·· 85 4c 4c 85 ·· ·· 85 4c 4c 85 ·· ·· ·· ·· ·· 85 4c 4c 85 ·· ·· + 85 4c 4c 85 ·· ·· 7f ·· ·· 85 4c 4c 85 ·· ·· 85 4c 4c 85 ·· ·· ·· ·· ·· 85 4c 4c 85 ·· ·· + 85 4c 4c 85 ·· ·· ·· ·· ·· 85 4c 4c 85 ·· ·· 82 85 85 82 ·· ·· ·· ·· ·· 82 85 85 82 ·· ·· + 85 4c 4c 85 ·· ·· ·· ·· ·· 85 4c 4c 85 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 82 85 85 82 ·· ·· ·· ·· ·· 82 85 85 82 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + From e7f57938ffd23bbed49b35a97c302d1f08d5c950 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 21 Nov 2025 16:06:16 +0000 Subject: [PATCH 39/81] Refactor polygon code for speed and clarity. --- extmod/modframebuf.c | 211 +++++++++++++-------- tests/extmod/framebuf_polygon_alpha.py | 24 +-- tests/extmod/framebuf_polygon_alpha.py.exp | 40 ++-- 3 files changed, 165 insertions(+), 110 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 6ebb6ff84f8ce..827ff336915ee 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -776,18 +776,18 @@ typedef struct edge { mp_int_t slope; } edge; -static void insert_edge(edge *edge_table, int n_edges, mp_int_t py1, mp_int_t py2, mp_int_t px1, mp_int_t slope) { +static void insert_edge(edge* edge_table, size_t n_edges, mp_int_t py1, mp_int_t py2, mp_int_t px1, mp_int_t slope) { edge e = { py1, py2, - px1, + px1 + (slope >> 2), // bump to first sub-scanline intersection (increment by a quarter of the slope) slope, }; edge current; - // simple linear insert: could do binary insert more efficiently and/or use memcpy - for (int i = 0; i < n_edges; ++i) { + // simple linear ordered insertion + for (size_t i = 0; i < n_edges; ++i) { current = edge_table[i]; - if (e.y1 <= current.y1 || (e.y1 == current.y1 && e.x1 <= current.x1)) { + if (e.y1 <= current.y1) { edge_table[i] = e; e = current; } @@ -795,6 +795,30 @@ static void insert_edge(edge *edge_table, int n_edges, mp_int_t py1, mp_int_t py edge_table[n_edges] = e; } +typedef struct node { + mp_int_t x; + uint8_t mask; +} node; + +static size_t insert_node(node* node_table, size_t n_nodes, mp_int_t x, uint8_t mask) { + node n = {x, mask}; + node current; + // simple linear ordered insertion + for (size_t i = 0; i < n_nodes; ++i) { + current = node_table[i]; + if (n.x == current.x) { + n.mask = n.mask ^ current.mask; + node_table[i] = n; + return 0; + } else if (n.x < current.x) { + node_table[i] = n; + n = current; + } + } + node_table[n_nodes] = n; + return 1; +} + static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); @@ -804,18 +828,23 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(args_in[3], &bufinfo, MP_BUFFER_READ); // If an odd number of values was given, this rounds down to multiple of two. - int n_poly = bufinfo.len / (mp_binary_get_size('@', bufinfo.typecode, NULL) * 2); + size_t n_poly = bufinfo.len / (mp_binary_get_size('@', bufinfo.typecode, NULL) * 2); if (n_poly == 0) { + // Nothing to do. return mp_const_none; } mp_int_t col = mp_obj_get_int(args_in[4]); bool fill = n_args > 5 && mp_obj_is_true(args_in[5]); mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(6); + if (alpha <= 0) { + // Nothing to do. + return mp_const_none; + } if (fill) { - // This implements a variant of the A-buffer algorithm for polygon fill. + // This implements an integer version of the A-buffer algorithm for antialiased polygon fill. // We compute two scanlines per row, one at -0.25 and one at +0.25 from // the pixel center (where the integer coordinates are). Each scanline @@ -828,40 +857,51 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // +---+---+ +0.5 // -0.5 0 +0.5 // - // Where an edge intersects a scanline, we set a bit mask of all to the - // right of it, and xor that with the mask from the previous edges. + // Where an edge intersects a scanline, we round and set a bit mask of all + // bits to the right of it, and xor that with the mask from the previous edges. // // When we move to the next pixel we copy the last bit on each scanline // and multiply by 0b1111 to extend it to the entire row, and use that as // the initial mask for that pixel. + // + // Original paper: https://dl.acm.org/doi/pdf/10.1145/964965.808585 + // Explanation in the context of embedded systems: https://aykevl.nl/2024/02/tinygl-polygon/ + // We don't use a lookup table. // Build a table of edges and data - // The table consists of entries (y_min, y_max, x_min, 1/slope) + // The table consists of entries (y_min, y_max, x_min, 1/slope) and is ordered by y_min. + // The value of 1/slope is stored with 12 bits of fixed precision. + // Horizontal lines are ignored. edge edge_table[n_poly]; - mp_int_t n_edges = 0; - mp_int_t px1 = poly_int(&bufinfo, 2*n_poly-2); - mp_int_t py1 = poly_int(&bufinfo, 2*n_poly-1); - mp_int_t y_start = self->height - y; - mp_int_t y_end = 0 - y; + size_t n_edges = 0; + mp_int_t px1 = x + poly_int(&bufinfo, 2 * n_poly - 2); + mp_int_t py1 = y + poly_int(&bufinfo, 2 * n_poly - 1); + mp_int_t y_start = py1; + mp_int_t y_end = py1 + 1; mp_int_t x_start = px1; mp_int_t x_end = px1; - for (int i = 0; i < n_poly; ++i) { - mp_int_t px2 = poly_int(&bufinfo, 2*i); - mp_int_t py2 = poly_int(&bufinfo, 2*i + 1); + for (size_t i = 0; i < n_poly; ++i) { + mp_int_t px2 = x + poly_int(&bufinfo, 2 * i); + mp_int_t py2 = y + poly_int(&bufinfo, 2 * i + 1); + // track the min and max extent of the polygon y_start = MIN(y_start, py2); - y_end = MAX(y_end, py2); + y_end = MAX(y_end, py2 + 1); x_start = MIN(x_start, px2); x_end = MAX(x_end, px2); - if (py1 < py2) { // going up - if (py1 >= y || py2 <= y + self->height) { // intersects screen vertically - insert_edge(edge_table, n_edges, py1, py2, px1, ((px2 - px1) << 12) / (py2 - py1)); + if (py1 < py2) { + // going up + if (py1 <= self->height || py2 >= 0) { + // intersects buffer vertically + insert_edge(edge_table, n_edges, py1, py2, px1 << 12, ((px2 - px1) << 12) / (py2 - py1)); ++n_edges; } - } else if (py1 > py2) { // going down - if (py2 >= y || py1 <= y + self->height) { // intersects screen vertically - insert_edge(edge_table, n_edges, py2, py1, px2, ((px2 - px1) << 12) / (py2 - py1)); + } else if (py1 > py2) { + // going down + if (py2 <= self->height || py1 >= 0) { + // intersects buffer vertically + insert_edge(edge_table, n_edges, py2, py1, px2 << 12, ((px2 - px1) << 12) / (py2 - py1)); ++n_edges; } } // ... and ignore horizontal edges @@ -869,76 +909,91 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { px1 = px2; py1 = py2; } + y_start = MAX(0, y_start); + y_end = MIN(self->height, y_end); - int last_edge_index = 0; - mp_int_t scanline_intersects[n_edges][2]; - int n_intersects[2]; - - for (mp_int_t row = y_start; row <= y_end; row++) { - uint8_t mask = 0; + // Track edges which intersect scanlines. + size_t last_edge_index = 0; + for (mp_int_t row = y_start; row < y_end; row++) { + // Add any new edges that may intersect the subsample lines to those we consider. while (last_edge_index < n_edges && edge_table[last_edge_index].y1 <= row) { ++last_edge_index; } - n_intersects[0] = 0; - n_intersects[1] = 0; + size_t n_nodes = 0; + node nodes[2 * last_edge_index]; + for (mp_int_t line = 0; line < 2; ++line) { + // For each subsample line... + // Get y-value with 2 bits of fixed precision mp_int_t y1 = (line == 0) ? ((row << 2) - 1) : ((row << 2) + 1); - for (int i = 0; i < last_edge_index; ++i) { - edge e = edge_table[i]; - if ((e.y2 << 2) < y1) { + for (size_t i = 0; i < last_edge_index; ++i) { + // For each edge... + edge* e = &(edge_table[i]); + if ((e->y2 << 2) < y1) { + // Edge below subsample line. continue; - } else if ((e.y1 << 2) > y1) { + } else if ((e->y1 << 2) > y1) { + // Edge above subsample line (can happen for lower subsample line at start of edge). continue; } - // round x-value to nearest 0.25 using an additional 12-bits of precision - mp_int_t x1 = (e.x1 << 2) + (((y1 - (e.y1 << 2)) * e.slope + (1 << 11)) >> 12); - // XXX binary insertion? - for (int j=0; j < n_intersects[line]; j++) { - mp_int_t current = scanline_intersects[j][line]; - if (current > x1) { - scanline_intersects[j][line] = x1; - x1 = current; - } + // Find pixel and sub-pixel offsets. + // We adjust x so integer coordinates are in the center of each pixel and intersections + // with subsample pixels round to nearest quarter. This makes the antialiased values + // symmetric when given a symmetric shape. + mp_int_t x_adjusted = e->x1 + (1 << 11) + (1 << 9); + mp_int_t column = x_adjusted >> 12; + if (column >= self->width) { + // Outside of buffer on the high end, don't care about these points, + // but need to bump the x-value in case line eventually comes inside the buffer. + e->x1 += (e->slope >> 1); + continue; } - scanline_intersects[n_intersects[line]][line] = x1; - ++n_intersects[line]; + mp_int_t subpixel_offset = (x_adjusted - (column << 12)) >> 10; + + // Compute mask for subpixel scanline. + uint8_t mask = ((1 << (4 - subpixel_offset)) - 1) << (4 * line); + + // Insert the node, xor-ing the mask if there is a column match, otherwise sorting. + n_nodes += insert_node(nodes, n_nodes, column, mask); + + // Bump edge x value to next sub-scanline (increments by half the slope). + e->x1 += (e->slope >> 1); } } + if (!n_nodes) { + // No intersections we care about, go to next row. + continue; + } - int column; - if (n_intersects[0] != 0) { - if (n_intersects[1] != 0) { - column = MIN(scanline_intersects[0][0], scanline_intersects[0][1]) >> 2; - } else { - column = scanline_intersects[0][0] >> 2; - } - } else { - if (n_intersects[1] != 0) { - column = scanline_intersects[0][1] >> 2; - } else { - // blank line - shouldn't happen - continue; + // Now draw the pixels. + uint8_t mask = 0; + node current; + for (size_t i = 0; i < n_nodes; ++i) { + current = nodes[i]; + + // Update the mask + mask ^= current.mask; + + if (current.x >= 0) { + // pixel is inside the buffer, so draw the pixel + setpixel(self, current.x, row, col, (__builtin_popcount(mask) * alpha) >> 3); } - } - int intersect_indices[2] = {0, 0}; - while (intersect_indices[0] < n_intersects[0] || intersect_indices[1] < n_intersects[1]) { - for (mp_int_t line = 0; line < 2; ++line) { - if (intersect_indices[line] >= n_intersects[line]) { - continue; - } - mp_int_t x1 = scanline_intersects[intersect_indices[line]][line] - (column << 2) + 2; - while ((x1 >= 0) && (x1 < 4)) { - mask ^= (0b1111 >> x1) << (4 * line); - ++intersect_indices[line]; - if (intersect_indices[line] > n_intersects[line]) {break;}; - x1 = scanline_intersects[intersect_indices[line]][line] - (column << 2) + 2; + + // extend mask by last bits + mask = (mask & 0b00010001) * 0b1111; + + if (mask) { + // fill with run of pixels with same mask - can be fast + mp_int_t width; + if (i + 1 < n_nodes) { + width = nodes[i+1].x - current.x - 1; + } else { + width = self->width - current.x - 1; } + fill_rect(self, current.x + 1, row, width, 1, col, (__builtin_popcount(mask) * alpha) >> 3); } - setpixel_checked(self, x + column, y + row, col, 1, (__builtin_popcount(mask) * alpha) >> 3); - mask = (mask & 0b00010001) * 0b1111; - column++; } } } else { @@ -953,7 +1008,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { px1 = px2; py1 = py2; } while (i >= 0); - // draw endpoint of last line if poly is not closed + // draw endpoint of last line if polygon is not closed if (px1 != poly_int(&bufinfo, 0) || py1 != poly_int(&bufinfo, 1)) { setpixel_checked(self, x + px1, y + py1, col, 1, alpha); } diff --git a/tests/extmod/framebuf_polygon_alpha.py b/tests/extmod/framebuf_polygon_alpha.py index 55120c47e3666..49b237750b4e0 100644 --- a/tests/extmod/framebuf_polygon_alpha.py +++ b/tests/extmod/framebuf_polygon_alpha.py @@ -233,32 +233,32 @@ def print_buffer(buffer, width, height): # Draw the line polygon (at the origin) and the reversed-order polygon (offset). fbuf.fill(0) -fbuf.poly(0, 0, poly, col, False, 0x7f) -fbuf.poly(15, -2, poly_reversed, col, False, 0x7f) +fbuf.poly(0, 0, poly, col, False, 0x7F) +fbuf.poly(15, -2, poly_reversed, col, False, 0x7F) print_buffer(buf, w, h) print() # Same but filled. fbuf.fill(0) -fbuf.poly(0, 0, poly, col_fill, True, 0x7f) -fbuf.poly(15, -2, poly_reversed, col_fill, True, 0x7f) +fbuf.poly(0, 0, poly, col_fill, True, 0x7F) +fbuf.poly(15, -2, poly_reversed, col_fill, True, 0x7F) print_buffer(buf, w, h) print() # Draw the fill then the outline to ensure that no fill goes outside the outline. fbuf.fill(0) -fbuf.poly(0, 0, poly, col_fill, True, 0x7f) -fbuf.poly(0, 0, poly, col, False, 0x7f) -fbuf.poly(15, -2, poly, col_fill, True, 0x7f) -fbuf.poly(15, -2, poly, col, False, 0x7f) +fbuf.poly(0, 0, poly, col_fill, True, 0x7F) +fbuf.poly(0, 0, poly, col, False, 0x7F) +fbuf.poly(15, -2, poly, col_fill, True, 0x7F) +fbuf.poly(15, -2, poly, col, False, 0x7F) print_buffer(buf, w, h) print() # Draw the outline then the fill to ensure the fill completely covers the outline. fbuf.fill(0) -fbuf.poly(0, 0, poly, col, False, 0x7f) -fbuf.poly(0, 0, poly, col_fill, True, 0x7f) -fbuf.poly(15, -2, poly, col, False, 0x7f) -fbuf.poly(15, -2, poly, col_fill, True, 0x7f) +fbuf.poly(0, 0, poly, col, False, 0x7F) +fbuf.poly(0, 0, poly, col_fill, True, 0x7F) +fbuf.poly(15, -2, poly, col, False, 0x7F) +fbuf.poly(15, -2, poly, col_fill, True, 0x7F) print_buffer(buf, w, h) print() diff --git a/tests/extmod/framebuf_polygon_alpha.py.exp b/tests/extmod/framebuf_polygon_alpha.py.exp index df8a66d50c948..8864f19973e85 100644 --- a/tests/extmod/framebuf_polygon_alpha.py.exp +++ b/tests/extmod/framebuf_polygon_alpha.py.exp @@ -1,4 +1,4 @@ -·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· 92 6d ·· ·· ·· 6d 92 ·· ·· ff ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff ·· ·· 26 d9 ·· ·· ·· d9 26 ·· ·· ff ·· ·· @@ -128,9 +128,9 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 9f ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 20 ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + bf ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 9f ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -180,11 +180,11 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff df ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff 20 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 9f ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff 20 ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff 9f ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff 9f - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 9f ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 9f ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 20 ff @@ -320,10 +320,10 @@ ·· ·· a9 bd 99 99 bd a9 ·· ·· ·· ·· ff ff ff ff ff fe fe ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· 2a e5 99 99 e5 2a ·· ·· ·· ·· 28 e6 99 99 99 b8 b5 ·· b5 b8 99 99 99 e6 28 ·· ·· ·· ·· ·· ·· b6 b8 b8 b6 ·· ·· ·· ·· ·· ·· 50 d3 99 99 cf 78 ·· 78 cf 99 99 d3 50 ·· ·· ·· ·· - ·· ·· ·· 38 dc e0 38 ·· ·· ·· ·· ·· ·· ·· 82 c6 99 dc 40 ·· 40 dc 99 c6 82 ·· ·· ·· ·· ·· - ·· ·· ·· ·· d1 dd ·· ·· ·· ·· ·· ·· ·· ·· ·· a6 bf f9 08 ·· 08 f9 bf a6 ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· d0 db ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d7 df ·· ·· ·· de d8 ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 33 df db 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 fa 10 ·· 10 fa 68 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 38 dc dc 38 ·· ·· ·· ·· ·· ·· ·· 82 c6 99 dc 40 ·· 40 dc 99 c6 82 ·· ·· ·· ·· ·· + ·· ·· ·· ·· d1 d2 ·· ·· ·· ·· ·· ·· ·· ·· ·· a6 bf f9 08 ·· 08 f9 bf a6 ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· d0 cf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d7 df ·· ·· ·· de d8 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 33 df df 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 fa 10 ·· 10 fa 68 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b1 ba ba b1 ·· ·· ·· ·· ·· ·· ·· ·· ·· a6 c4 ee ·· ee c4 a6 ·· ·· ·· ·· ·· ·· ·· ·· ·· 25 e8 99 99 e8 25 ·· ·· ·· ·· ·· ·· ·· ·· e0 a8 b2 f3 b2 a8 e0 ·· ·· ·· ·· ·· ·· ·· ·· ·· a5 bf 99 99 bf a5 ·· ·· ·· ·· ·· ·· ·· 10 f5 99 cd a5 cd 99 f5 10 ·· ·· ·· ·· ·· ·· @@ -346,10 +346,10 @@ ·· ·· a0 99 99 99 99 a0 ·· ·· ·· ·· f1 cb cb cb cb cb d7 cb d8 cb cb cb cb cb f1 ·· ·· ·· ·· ·· 2a af 99 99 af 2a ·· ·· ·· ·· 28 af 99 99 99 99 ac ·· ac 99 99 99 99 af 28 ·· ·· ·· ·· ·· ·· ac 99 99 ac ·· ·· ·· ·· ·· ·· 50 9e 99 99 99 78 ·· 78 99 99 99 9e 50 ·· ·· ·· ·· - ·· ·· ·· 38 a9 a4 38 ·· ·· ·· ·· ·· ·· ·· 7c 96 99 a2 40 ·· 40 a2 99 96 7c ·· ·· ·· ·· ·· - ·· ·· ·· ·· c7 b3 ·· ·· ·· ·· ·· ·· ·· ·· ·· 9e 99 c7 08 ·· 08 c7 99 9e ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· c5 b1 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c2 c8 ·· ·· ·· c8 c3 ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 33 ab b2 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 c8 10 ·· 10 c8 68 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 38 a9 a9 38 ·· ·· ·· ·· ·· ·· ·· 7c 96 99 a2 40 ·· 40 a2 99 96 7c ·· ·· ·· ·· ·· + ·· ·· ·· ·· c7 c7 ·· ·· ·· ·· ·· ·· ·· ·· ·· 9e 99 c7 08 ·· 08 c7 99 9e ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· c5 c4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c2 c8 ·· ·· ·· c8 c3 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 33 ab ab 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 c8 10 ·· 10 c8 68 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a8 99 99 a8 ·· ·· ·· ·· ·· ·· ·· ·· ·· 9e 99 bf ·· bf 99 9e ·· ·· ·· ·· ·· ·· ·· ·· ·· 25 b1 99 99 b1 25 ·· ·· ·· ·· ·· ·· ·· ·· bf 99 99 c3 99 99 bf ·· ·· ·· ·· ·· ·· ·· ·· ·· 9c 99 99 99 99 9c ·· ·· ·· ·· ·· ·· ·· 10 b8 99 98 a5 98 99 b8 10 ·· ·· ·· ·· ·· ·· @@ -556,27 +556,27 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· bf ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff bf ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff e3 ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff c3 ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ce ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff c3 ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff c2 ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 ff e2 ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff e2 ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c7 df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· From ed1451ee5c94f281c6c607f12e197c6e86c62c6a Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 21 Nov 2025 16:36:32 +0000 Subject: [PATCH 40/81] See if uint16_t has better popcount support. --- extmod/modframebuf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 827ff336915ee..7e16c11457af6 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -800,7 +800,7 @@ typedef struct node { uint8_t mask; } node; -static size_t insert_node(node* node_table, size_t n_nodes, mp_int_t x, uint8_t mask) { +static size_t insert_node(node* node_table, size_t n_nodes, mp_int_t x, uint16_t mask) { node n = {x, mask}; node current; // simple linear ordered insertion @@ -953,7 +953,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_int_t subpixel_offset = (x_adjusted - (column << 12)) >> 10; // Compute mask for subpixel scanline. - uint8_t mask = ((1 << (4 - subpixel_offset)) - 1) << (4 * line); + uint16_t mask = ((1 << (4 - subpixel_offset)) - 1) << (line << 2); // Insert the node, xor-ing the mask if there is a column match, otherwise sorting. n_nodes += insert_node(nodes, n_nodes, column, mask); @@ -968,7 +968,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { } // Now draw the pixels. - uint8_t mask = 0; + uint16_t mask = 0; node current; for (size_t i = 0; i < n_nodes; ++i) { current = nodes[i]; From 963ddcacf8ae1d96e54cc13a20c983ff60d35587 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 21 Nov 2025 17:13:15 +0000 Subject: [PATCH 41/81] Try 32-bit popcount? --- extmod/modframebuf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 7e16c11457af6..0b1e7e78b820b 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -797,10 +797,10 @@ static void insert_edge(edge* edge_table, size_t n_edges, mp_int_t py1, mp_int_t typedef struct node { mp_int_t x; - uint8_t mask; + uint32_t mask; } node; -static size_t insert_node(node* node_table, size_t n_nodes, mp_int_t x, uint16_t mask) { +static size_t insert_node(node* node_table, size_t n_nodes, mp_int_t x, uint32_t mask) { node n = {x, mask}; node current; // simple linear ordered insertion @@ -953,7 +953,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_int_t subpixel_offset = (x_adjusted - (column << 12)) >> 10; // Compute mask for subpixel scanline. - uint16_t mask = ((1 << (4 - subpixel_offset)) - 1) << (line << 2); + uint32_t mask = ((1 << (4 - subpixel_offset)) - 1) << (line << 2); // Insert the node, xor-ing the mask if there is a column match, otherwise sorting. n_nodes += insert_node(nodes, n_nodes, column, mask); @@ -968,7 +968,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { } // Now draw the pixels. - uint16_t mask = 0; + uint32_t mask = 0; node current; for (size_t i = 0; i < n_nodes; ++i) { current = nodes[i]; From 887f4c7c3e191cebd69bbeac7f88c7c40b128ce1 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 21 Nov 2025 20:46:28 +0000 Subject: [PATCH 42/81] Use micropython's popcount helper. --- extmod/modframebuf.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 0b1e7e78b820b..f7c645a490040 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -29,6 +29,7 @@ #include "py/runtime.h" #include "py/binary.h" +#include "py/misc.h" #if MICROPY_PY_FRAMEBUF @@ -243,6 +244,7 @@ static mp_framebuf_p_t formats[] = { #if MICROPY_PY_FRAMEBUF_ALPHA + #ifndef FRAMEBUF_GET_ALPHA_ARG #define FRAMEBUF_GET_ALPHA_ARG(idx) ((n_args > idx) ? mp_obj_get_int(args_in[idx]) : 0x100) #endif // GET_ALPHA_ARG @@ -978,7 +980,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { if (current.x >= 0) { // pixel is inside the buffer, so draw the pixel - setpixel(self, current.x, row, col, (__builtin_popcount(mask) * alpha) >> 3); + setpixel(self, current.x, row, col, (mp_popcount(mask) * alpha) >> 3); } // extend mask by last bits @@ -992,7 +994,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { } else { width = self->width - current.x - 1; } - fill_rect(self, current.x + 1, row, width, 1, col, (__builtin_popcount(mask) * alpha) >> 3); + fill_rect(self, current.x + 1, row, width, 1, col, (mp_popcount(mask) * alpha) >> 3); } } } From 1fdd84ed43e037742009f96d45363f4a26a908b4 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sat, 22 Nov 2025 11:34:11 +0000 Subject: [PATCH 43/81] Test polygons on low-bit framebuffers and adapt 1-bit rendering. In particular, we make the alpha blend in mono polygon fill stronger, so that corners of squares are rendered when alpha is 0xFF. --- extmod/modframebuf.c | 6 + tests/extmod/framebuf_polygon_alpha.py | 111 +++++++++ tests/extmod/framebuf_polygon_alpha.py.exp | 260 +++++++++++++++++++++ 3 files changed, 377 insertions(+) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index f7c645a490040..f4cf2c23bc428 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -874,6 +874,12 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // The table consists of entries (y_min, y_max, x_min, 1/slope) and is ordered by y_min. // The value of 1/slope is stored with 12 bits of fixed precision. // Horizontal lines are ignored. + + if (self->format == FRAMEBUF_MHLSB || self->format == FRAMEBUF_MHMSB || self->format == FRAMEBUF_MVLSB) { + // Increase alpha for mono to get sharp corners. + alpha *= 2; + } + edge edge_table[n_poly]; size_t n_edges = 0; mp_int_t px1 = x + poly_int(&bufinfo, 2 * n_poly - 2); diff --git a/tests/extmod/framebuf_polygon_alpha.py b/tests/extmod/framebuf_polygon_alpha.py index 49b237750b4e0..b0e4b4cbdf11b 100644 --- a/tests/extmod/framebuf_polygon_alpha.py +++ b/tests/extmod/framebuf_polygon_alpha.py @@ -25,6 +25,22 @@ def print_buffer(buffer, width, height): print() +def print_buffer_mono(fbuf, width, height): + for row in range(height): + for col in range(width): + val = fbuf.pixel(col, row) + print(" **" if val else " ..", end="") + print() + + +def print_buffer_gs2(fbuf, width, height): + for row in range(height): + for col in range(width): + val = fbuf.pixel(col, row) + print(" {:02b}".format(val) if val else " ..", end="") + print() + + buf = bytearray(70 * 70) w = 30 @@ -192,6 +208,14 @@ def print_buffer(buffer, width, height): print_buffer(buf, w, h) print() +fbuf.fill(0) +fbuf.poly(0, 0, poly_empty, col, True) +fbuf.poly(0, 0, poly_one, col, True) +fbuf.poly(0, 0, poly_two, col, True) +fbuf.poly(0, 0, poly_wrong_length, col, True) +print_buffer(buf, w, h) +print() + # A shape with a horizontal overhang. poly_overhang = array("h", (0, 0, 0, 5, 5, 5, 5, 10, 10, 10, 10, 0)) @@ -262,3 +286,90 @@ def print_buffer(buffer, width, height): fbuf.poly(15, -2, poly, col_fill, True, 0x7F) print_buffer(buf, w, h) print() + +# Test 1-bit cases +w = 30 +h = 25 +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.MONO_HLSB) +col = 1 +col_fill = 1 + +# Draw the line polygon (at the origin) and the reversed-order polygon (offset). +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, False) +fbuf.poly(15, -2, poly_reversed, col, False) +print_buffer_mono(fbuf, w, h) +print() + +# Draw the line polygon (at the origin) and the reversed-order polygon (offset). +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, True) +fbuf.poly(15, -2, poly_reversed, col, True) +print_buffer_mono(fbuf, w, h) +print() + +# Draw the line polygon (at the origin) and the reversed-order polygon (offset). +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, True) +fbuf.poly(0, 0, poly, col, False) +fbuf.poly(15, -2, poly_reversed, col, True) +fbuf.poly(15, -2, poly_reversed, col, False) +print_buffer_mono(fbuf, w, h) +print() + +# Draw the line polygon (at the origin) and the reversed-order polygon (offset). +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, False) +fbuf.poly(0, 0, poly, col, True) +fbuf.poly(15, -2, poly_reversed, col, False) +fbuf.poly(15, -2, poly_reversed, col, True) +print_buffer_mono(fbuf, w, h) +print() + +fbuf.fill(0) +fbuf.poly(0, 0, poly_empty, col) +fbuf.poly(0, 0, poly_one, col) +fbuf.poly(0, 0, poly_two, col) +fbuf.poly(0, 0, poly_wrong_length, col) +print_buffer_mono(fbuf, w, h) +print() + + +# Test 2-bit cases +w = 30 +h = 25 +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS2_HMSB) +col = 0b11 +col_fill = 0b11 + +# Draw the line polygon (at the origin) and the reversed-order polygon (offset). +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, False) +fbuf.poly(15, -2, poly_reversed, col, False) +print_buffer_gs2(fbuf, w, h) +print() + +# Draw the line polygon (at the origin) and the reversed-order polygon (offset). +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, True) +fbuf.poly(15, -2, poly_reversed, col, True) +print_buffer_gs2(fbuf, w, h) +print() + +# Draw the line polygon (at the origin) and the reversed-order polygon (offset). +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, True) +fbuf.poly(0, 0, poly, col, False) +fbuf.poly(15, -2, poly_reversed, col, True) +fbuf.poly(15, -2, poly_reversed, col, False) +print_buffer_gs2(fbuf, w, h) +print() + +# Draw the line polygon (at the origin) and the reversed-order polygon (offset). +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, False) +fbuf.poly(0, 0, poly, col, True) +fbuf.poly(15, -2, poly_reversed, col, False) +fbuf.poly(15, -2, poly_reversed, col, True) +print_buffer_gs2(fbuf, w, h) +print() diff --git a/tests/extmod/framebuf_polygon_alpha.py.exp b/tests/extmod/framebuf_polygon_alpha.py.exp index 8864f19973e85..4d729bb184f31 100644 --- a/tests/extmod/framebuf_polygon_alpha.py.exp +++ b/tests/extmod/framebuf_polygon_alpha.py.exp @@ -388,6 +388,32 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + e5 cb cb cb cb cb cb cb cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· cb 99 99 99 99 99 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· cb 99 99 99 99 99 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -684,3 +710,237 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** .. .. ** .. .. .. .. .. ** .. .. ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** .. .. .. ** .. .. .. ** .. .. .. ** .. .. + ** .. .. ** .. .. .. .. .. ** .. .. ** .. .. ** .. .. .. ** .. .. .. ** .. .. .. ** .. .. + ** .. .. .. ** .. .. .. ** .. .. .. ** .. .. ** .. .. .. .. ** .. ** .. .. .. .. ** .. .. + ** .. .. .. ** .. .. .. ** .. .. .. ** .. .. ** .. .. .. .. ** .. ** .. .. .. .. ** .. .. + ** .. .. .. .. ** .. ** .. .. .. .. ** .. .. ** .. .. .. .. .. ** .. .. .. .. .. ** .. .. + ** .. .. .. .. ** .. ** .. .. .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. + ** .. .. .. .. .. ** .. .. .. .. .. ** .. .. ** .. .. ** .. .. .. .. .. ** .. .. ** .. .. + ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** ** .. .. .. ** ** .. .. ** .. .. + ** .. .. ** .. .. .. .. .. ** .. .. ** .. .. ** .. .. ** ** .. .. .. ** ** .. .. ** .. .. + ** .. .. ** ** .. .. .. ** ** .. .. ** .. .. ** .. .. ** .. ** .. ** .. ** .. .. ** .. .. + ** .. .. ** ** .. .. .. ** ** .. .. ** .. .. ** .. .. ** .. ** .. ** .. ** .. .. ** .. .. + ** .. .. ** .. ** .. ** .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. + ** .. .. ** .. ** .. ** .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. + ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. .. .. .. ** .. .. ** .. .. + ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. ** .. .. .. .. .. ** .. .. ** .. .. + ** .. .. ** .. .. .. .. .. ** .. .. ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** .. .. ** .. .. .. .. .. ** .. .. ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. + ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. + ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. ** ** ** ** ** ** .. ** ** ** ** ** ** .. .. + ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** .. ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. + ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. + ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. ** ** ** ** .. .. ** .. .. ** ** ** ** .. .. + ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. ** .. .. ** ** ** ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. + ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. ** ** ** ** ** ** .. ** ** ** ** ** ** .. .. + ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. ** ** ** ** ** ** .. ** ** ** ** ** ** .. .. + ** ** ** ** ** ** .. ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** .. ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. + ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. ** ** ** ** .. .. ** .. .. ** ** ** ** .. .. + ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. ** ** ** ** .. .. ** .. .. ** ** ** ** .. .. + ** ** ** ** .. .. ** .. .. ** ** ** ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. ** .. .. ** ** ** ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. + ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. ** ** ** ** ** ** .. ** ** ** ** ** ** .. .. + ** ** ** ** ** .. .. .. ** ** ** ** ** .. .. ** ** ** ** ** ** .. ** ** ** ** ** ** .. .. + ** ** ** ** ** ** .. ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** .. ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. + ** ** ** ** ** ** ** ** ** ** ** ** ** .. .. ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. + ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. ** ** ** ** .. .. ** .. .. ** ** ** ** .. .. + ** ** ** ** .. ** ** ** .. ** ** ** ** .. .. ** ** ** ** .. .. ** .. .. ** ** ** ** .. .. + ** ** ** ** .. .. ** .. .. ** ** ** ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. ** .. .. ** ** ** ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 11 .. .. 10 01 .. .. .. 01 10 .. .. 11 .. .. + 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. 11 .. .. .. 11 .. .. .. 11 .. .. .. 11 .. .. + 11 .. .. 10 01 .. .. .. 01 10 .. .. 11 .. .. 11 .. .. .. 10 01 .. 01 10 .. .. .. 11 .. .. + 11 .. .. .. 11 .. .. .. 11 .. .. .. 11 .. .. 11 .. .. .. 01 10 .. 10 01 .. .. .. 11 .. .. + 11 .. .. .. 10 01 .. 01 10 .. .. .. 11 .. .. 11 .. .. .. .. 11 .. 11 .. .. .. .. 11 .. .. + 11 .. .. .. 01 10 .. 10 01 .. .. .. 11 .. .. 11 .. .. .. .. 01 11 01 .. .. .. .. 11 .. .. + 11 .. .. .. .. 11 .. 11 .. .. .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. + 11 .. .. .. .. 01 11 01 .. .. .. .. 11 .. .. 11 .. .. 11 01 .. .. .. 01 11 .. .. 11 .. .. + 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 11 .. .. .. 11 11 .. .. 11 .. .. + 11 .. .. 11 01 .. .. .. 01 11 .. .. 11 .. .. 11 .. .. 11 10 01 .. 01 10 11 .. .. 11 .. .. + 11 .. .. 11 11 .. .. .. 11 11 .. .. 11 .. .. 11 .. .. 11 01 10 .. 10 01 11 .. .. 11 .. .. + 11 .. .. 11 10 01 .. 01 10 11 .. .. 11 .. .. 11 .. .. 11 .. 11 .. 11 .. 11 .. .. 11 .. .. + 11 .. .. 11 01 10 .. 10 01 11 .. .. 11 .. .. 11 .. .. 11 .. 01 11 01 .. 11 .. .. 11 .. .. + 11 .. .. 11 .. 11 .. 11 .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. + 11 .. .. 11 .. 01 11 01 .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. .. .. .. 11 .. .. 11 .. .. + 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. 11 .. .. .. .. .. 11 .. .. 11 .. .. + 11 .. .. 11 .. .. .. .. .. 11 .. .. 11 .. .. 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. + 11 .. .. 11 .. .. .. .. .. 11 .. .. 11 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 01 10 10 01 .. .. .. .. .. 01 10 10 01 .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 10 11 11 11 .. .. .. .. .. 11 11 11 10 .. .. + 01 10 10 01 .. .. .. .. .. 01 10 10 01 .. .. 10 11 11 11 01 .. .. .. 01 11 11 11 10 .. .. + 10 11 11 11 .. .. .. .. .. 11 11 11 10 .. .. 10 11 11 11 11 .. .. .. 11 11 11 11 10 .. .. + 10 11 11 11 01 .. .. .. 01 11 11 11 10 .. .. 10 11 11 11 11 .. .. .. 11 11 11 11 10 .. .. + 10 11 11 11 11 .. .. .. 11 11 11 11 10 .. .. 10 11 11 11 11 10 .. 10 11 11 11 11 10 .. .. + 10 11 11 11 11 .. .. .. 11 11 11 11 10 .. .. 10 11 11 11 11 11 01 11 11 11 11 11 10 .. .. + 10 11 11 11 11 10 .. 10 11 11 11 11 10 .. .. 10 11 11 11 11 11 11 11 11 11 11 11 10 .. .. + 10 11 11 11 11 11 01 11 11 11 11 11 10 .. .. 10 11 11 10 11 11 11 11 11 10 11 11 10 .. .. + 10 11 11 11 11 11 11 11 11 11 11 11 10 .. .. 10 11 11 10 10 11 11 11 10 10 11 11 10 .. .. + 10 11 11 10 11 11 11 11 11 10 11 11 10 .. .. 10 11 11 10 .. 11 11 11 .. 10 11 11 10 .. .. + 10 11 11 10 10 11 11 11 10 10 11 11 10 .. .. 10 11 11 10 .. 11 11 11 .. 10 11 11 10 .. .. + 10 11 11 10 .. 11 11 11 .. 10 11 11 10 .. .. 10 11 11 10 .. 01 11 01 .. 10 11 11 10 .. .. + 10 11 11 10 .. 11 11 11 .. 10 11 11 10 .. .. 10 11 11 10 .. .. 10 .. .. 10 11 11 10 .. .. + 10 11 11 10 .. 01 11 01 .. 10 11 11 10 .. .. 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. + 10 11 11 10 .. .. 10 .. .. 10 11 11 10 .. .. 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. + 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. + 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. 01 10 10 01 .. .. .. .. .. 01 10 10 01 .. .. + 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + 01 10 10 01 .. .. .. .. .. 01 10 10 01 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 11 11 11 11 01 .. .. .. 01 11 11 11 11 .. .. + 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. 11 11 11 11 11 .. .. .. 11 11 11 11 11 .. .. + 11 11 11 11 01 .. .. .. 01 11 11 11 11 .. .. 11 11 11 11 11 01 .. 01 11 11 11 11 11 .. .. + 11 11 11 11 11 .. .. .. 11 11 11 11 11 .. .. 11 11 11 11 11 10 .. 10 11 11 11 11 11 .. .. + 11 11 11 11 11 01 .. 01 11 11 11 11 11 .. .. 11 11 11 11 11 11 .. 11 11 11 11 11 11 .. .. + 11 11 11 11 11 10 .. 10 11 11 11 11 11 .. .. 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. + 11 11 11 11 11 11 .. 11 11 11 11 11 11 .. .. 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. + 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. + 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. + 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. 11 11 11 11 10 11 11 11 10 11 11 11 11 .. .. + 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. 11 11 11 11 01 11 11 11 01 11 11 11 11 .. .. + 11 11 11 11 10 11 11 11 10 11 11 11 11 .. .. 11 11 11 11 .. 11 11 11 .. 11 11 11 11 .. .. + 11 11 11 11 01 11 11 11 01 11 11 11 11 .. .. 11 11 11 11 .. 01 11 01 .. 11 11 11 11 .. .. + 11 11 11 11 .. 11 11 11 .. 11 11 11 11 .. .. 11 11 11 11 .. .. 11 .. .. 11 11 11 11 .. .. + 11 11 11 11 .. 01 11 01 .. 11 11 11 11 .. .. 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. + 11 11 11 11 .. .. 11 .. .. 11 11 11 11 .. .. 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. + 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. + 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 11 11 11 11 01 .. .. .. 01 11 11 11 11 .. .. + 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. 11 11 11 11 11 .. .. .. 11 11 11 11 11 .. .. + 11 11 11 11 01 .. .. .. 01 11 11 11 11 .. .. 11 11 11 11 11 01 .. 01 11 11 11 11 11 .. .. + 11 11 11 11 11 .. .. .. 11 11 11 11 11 .. .. 11 11 11 11 11 10 .. 10 11 11 11 11 11 .. .. + 11 11 11 11 11 01 .. 01 11 11 11 11 11 .. .. 11 11 11 11 11 11 .. 11 11 11 11 11 11 .. .. + 11 11 11 11 11 10 .. 10 11 11 11 11 11 .. .. 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. + 11 11 11 11 11 11 .. 11 11 11 11 11 11 .. .. 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. + 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. + 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. + 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. 11 11 11 11 10 11 11 11 10 11 11 11 11 .. .. + 11 11 11 11 11 11 11 11 11 11 11 11 11 .. .. 11 11 11 11 01 11 11 11 01 11 11 11 11 .. .. + 11 11 11 11 10 11 11 11 10 11 11 11 11 .. .. 11 11 11 11 .. 11 11 11 .. 11 11 11 11 .. .. + 11 11 11 11 01 11 11 11 01 11 11 11 11 .. .. 11 11 11 11 .. 01 11 01 .. 11 11 11 11 .. .. + 11 11 11 11 .. 11 11 11 .. 11 11 11 11 .. .. 11 11 11 11 .. .. 11 .. .. 11 11 11 11 .. .. + 11 11 11 11 .. 01 11 01 .. 11 11 11 11 .. .. 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. + 11 11 11 11 .. .. 11 .. .. 11 11 11 11 .. .. 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. + 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. + 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + 11 11 11 11 .. .. .. .. .. 11 11 11 11 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + From 7ecb61a7c73f3b8e88cdd1b51b65bce24b9bdb2e Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sat, 22 Nov 2025 19:24:01 +0000 Subject: [PATCH 44/81] Try some things to get failing tests working. --- examples/natmod/framebuf/framebuf.c | 11 ++++++++++ extmod/modframebuf.c | 22 +++++++++---------- ...ygon_alpha.py => framebuf_polygonalpha.py} | 0 ...ha.py.exp => framebuf_polygonalpha.py.exp} | 0 4 files changed, 22 insertions(+), 11 deletions(-) rename tests/extmod/{framebuf_polygon_alpha.py => framebuf_polygonalpha.py} (100%) rename tests/extmod/{framebuf_polygon_alpha.py.exp => framebuf_polygonalpha.py.exp} (100%) diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c index 0369a475b4da7..4106790a76dd2 100644 --- a/examples/natmod/framebuf/framebuf.c +++ b/examples/natmod/framebuf/framebuf.c @@ -13,6 +13,17 @@ void *memset(void *s, int c, size_t n) { } #endif +#if __has_builtin(__builtin_popcount) +#define mp_popcount(x) __builtin_popcount(x) +#else +static inline uint32_t mp_popcount(uint32_t x) { + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + return (x * 0x01010101) >> 24; +} +#endif // __has_builtin(__builtin_popcount) + mp_obj_full_type_t mp_type_framebuf; #include "extmod/modframebuf.c" diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index f4cf2c23bc428..9cfd8982ce25c 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -778,7 +778,7 @@ typedef struct edge { mp_int_t slope; } edge; -static void insert_edge(edge* edge_table, size_t n_edges, mp_int_t py1, mp_int_t py2, mp_int_t px1, mp_int_t slope) { +static void insert_edge(edge* edge_table, int n_edges, mp_int_t py1, mp_int_t py2, mp_int_t px1, mp_int_t slope) { edge e = { py1, py2, @@ -787,7 +787,7 @@ static void insert_edge(edge* edge_table, size_t n_edges, mp_int_t py1, mp_int_t }; edge current; // simple linear ordered insertion - for (size_t i = 0; i < n_edges; ++i) { + for (int i = 0; i < n_edges; ++i) { current = edge_table[i]; if (e.y1 <= current.y1) { edge_table[i] = e; @@ -802,11 +802,11 @@ typedef struct node { uint32_t mask; } node; -static size_t insert_node(node* node_table, size_t n_nodes, mp_int_t x, uint32_t mask) { +static int insert_node(node* node_table, int n_nodes, mp_int_t x, uint32_t mask) { node n = {x, mask}; node current; // simple linear ordered insertion - for (size_t i = 0; i < n_nodes; ++i) { + for (int i = 0; i < n_nodes; ++i) { current = node_table[i]; if (n.x == current.x) { n.mask = n.mask ^ current.mask; @@ -830,7 +830,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(args_in[3], &bufinfo, MP_BUFFER_READ); // If an odd number of values was given, this rounds down to multiple of two. - size_t n_poly = bufinfo.len / (mp_binary_get_size('@', bufinfo.typecode, NULL) * 2); + int n_poly = bufinfo.len / (mp_binary_get_size('@', bufinfo.typecode, NULL) * 2); if (n_poly == 0) { // Nothing to do. @@ -881,14 +881,14 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { } edge edge_table[n_poly]; - size_t n_edges = 0; + int n_edges = 0; mp_int_t px1 = x + poly_int(&bufinfo, 2 * n_poly - 2); mp_int_t py1 = y + poly_int(&bufinfo, 2 * n_poly - 1); mp_int_t y_start = py1; mp_int_t y_end = py1 + 1; mp_int_t x_start = px1; mp_int_t x_end = px1; - for (size_t i = 0; i < n_poly; ++i) { + for (int i = 0; i < n_poly; ++i) { mp_int_t px2 = x + poly_int(&bufinfo, 2 * i); mp_int_t py2 = y + poly_int(&bufinfo, 2 * i + 1); @@ -921,7 +921,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { y_end = MIN(self->height, y_end); // Track edges which intersect scanlines. - size_t last_edge_index = 0; + int last_edge_index = 0; for (mp_int_t row = y_start; row < y_end; row++) { // Add any new edges that may intersect the subsample lines to those we consider. @@ -929,14 +929,14 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { ++last_edge_index; } - size_t n_nodes = 0; + int n_nodes = 0; node nodes[2 * last_edge_index]; for (mp_int_t line = 0; line < 2; ++line) { // For each subsample line... // Get y-value with 2 bits of fixed precision mp_int_t y1 = (line == 0) ? ((row << 2) - 1) : ((row << 2) + 1); - for (size_t i = 0; i < last_edge_index; ++i) { + for (int i = 0; i < last_edge_index; ++i) { // For each edge... edge* e = &(edge_table[i]); if ((e->y2 << 2) < y1) { @@ -978,7 +978,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // Now draw the pixels. uint32_t mask = 0; node current; - for (size_t i = 0; i < n_nodes; ++i) { + for (int i = 0; i < n_nodes; ++i) { current = nodes[i]; // Update the mask diff --git a/tests/extmod/framebuf_polygon_alpha.py b/tests/extmod/framebuf_polygonalpha.py similarity index 100% rename from tests/extmod/framebuf_polygon_alpha.py rename to tests/extmod/framebuf_polygonalpha.py diff --git a/tests/extmod/framebuf_polygon_alpha.py.exp b/tests/extmod/framebuf_polygonalpha.py.exp similarity index 100% rename from tests/extmod/framebuf_polygon_alpha.py.exp rename to tests/extmod/framebuf_polygonalpha.py.exp From 574decfc311a1b3dac3c504032b63d72dbdd60e0 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 23 Nov 2025 09:21:00 +0000 Subject: [PATCH 45/81] Use completely self-contained popcount. --- examples/natmod/framebuf/framebuf.c | 11 ----------- extmod/modframebuf.c | 12 +++++++++--- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c index 4106790a76dd2..0369a475b4da7 100644 --- a/examples/natmod/framebuf/framebuf.c +++ b/examples/natmod/framebuf/framebuf.c @@ -13,17 +13,6 @@ void *memset(void *s, int c, size_t n) { } #endif -#if __has_builtin(__builtin_popcount) -#define mp_popcount(x) __builtin_popcount(x) -#else -static inline uint32_t mp_popcount(uint32_t x) { - x = x - ((x >> 1) & 0x55555555); - x = (x & 0x33333333) + ((x >> 2) & 0x33333333); - x = (x + (x >> 4)) & 0x0F0F0F0F; - return (x * 0x01010101) >> 24; -} -#endif // __has_builtin(__builtin_popcount) - mp_obj_full_type_t mp_type_framebuf; #include "extmod/modframebuf.c" diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 9cfd8982ce25c..f62eb7d1fa538 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -29,7 +29,6 @@ #include "py/runtime.h" #include "py/binary.h" -#include "py/misc.h" #if MICROPY_PY_FRAMEBUF @@ -771,6 +770,13 @@ static mp_int_t poly_int(mp_buffer_info_t *bufinfo, size_t index) { #if MICROPY_PY_FRAMEBUF_ALPHA +static inline uint32_t popcount(uint32_t x) { + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + return (x * 0x01010101) >> 24; +} + typedef struct edge { mp_int_t y1; mp_int_t y2; @@ -986,7 +992,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { if (current.x >= 0) { // pixel is inside the buffer, so draw the pixel - setpixel(self, current.x, row, col, (mp_popcount(mask) * alpha) >> 3); + setpixel(self, current.x, row, col, (popcount(mask) * alpha) >> 3); } // extend mask by last bits @@ -1000,7 +1006,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { } else { width = self->width - current.x - 1; } - fill_rect(self, current.x + 1, row, width, 1, col, (mp_popcount(mask) * alpha) >> 3); + fill_rect(self, current.x + 1, row, width, 1, col, (popcount(mask) * alpha) >> 3); } } } From 24f8f8564d6779f203199849054b966b7ee216a4 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 23 Nov 2025 10:41:26 +0000 Subject: [PATCH 46/81] Be particular about left shifts of negatives for fixed point. --- extmod/modframebuf.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index f62eb7d1fa538..8da5c342fd0a3 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -389,9 +389,9 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t // Fixed point with 8 bits of fractional part. // dx != 0 is guaranteed - mp_int_t gradient = ((dy << 8) / dx); + mp_int_t gradient = ((dy * 256) / dx); - mp_int_t y_intercept = (y1 << 8) + gradient; + mp_int_t y_intercept = (y1 * 256) + gradient; if (steep) { for (mp_int_t x = x1 + 1; x < x1 + dx; ++x) { setpixel_checked(fb, y_intercept >> 8, x, col, 1, (alpha * (0x100 - (y_intercept & 0xff))) >> 8); @@ -908,14 +908,14 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // going up if (py1 <= self->height || py2 >= 0) { // intersects buffer vertically - insert_edge(edge_table, n_edges, py1, py2, px1 << 12, ((px2 - px1) << 12) / (py2 - py1)); + insert_edge(edge_table, n_edges, py1, py2, px1 * (1 << 12), ((px2 - px1) * (1 << 12)) / (py2 - py1)); ++n_edges; } } else if (py1 > py2) { // going down if (py2 <= self->height || py1 >= 0) { // intersects buffer vertically - insert_edge(edge_table, n_edges, py2, py1, px2 << 12, ((px2 - px1) << 12) / (py2 - py1)); + insert_edge(edge_table, n_edges, py2, py1, px2 * (1 << 12), ((px2 - px1) * (1 << 12)) / (py2 - py1)); ++n_edges; } } // ... and ignore horizontal edges @@ -923,6 +923,10 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { px1 = px2; py1 = py2; } + if (n_edges == 0) { + // No non-horizontal edges: nothing to draw. + return mp_const_none; + } y_start = MAX(0, y_start); y_end = MIN(self->height, y_end); @@ -938,17 +942,17 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { int n_nodes = 0; node nodes[2 * last_edge_index]; - for (mp_int_t line = 0; line < 2; ++line) { + for (int line = 0; line < 2; ++line) { // For each subsample line... // Get y-value with 2 bits of fixed precision mp_int_t y1 = (line == 0) ? ((row << 2) - 1) : ((row << 2) + 1); for (int i = 0; i < last_edge_index; ++i) { // For each edge... edge* e = &(edge_table[i]); - if ((e->y2 << 2) < y1) { + if ((e->y2 * 4) < y1) { // Edge below subsample line. continue; - } else if ((e->y1 << 2) > y1) { + } else if ((e->y1 * 4) > y1) { // Edge above subsample line (can happen for lower subsample line at start of edge). continue; } @@ -964,7 +968,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { e->x1 += (e->slope >> 1); continue; } - mp_int_t subpixel_offset = (x_adjusted - (column << 12)) >> 10; + mp_int_t subpixel_offset = (x_adjusted - (column * (1 << 12))) >> 10; // Compute mask for subpixel scanline. uint32_t mask = ((1 << (4 - subpixel_offset)) - 1) << (line << 2); From 5e084e72cbc0003cbfbf0ed34eca810d470046d2 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 23 Nov 2025 10:43:52 +0000 Subject: [PATCH 47/81] Code clean-up. --- extmod/modframebuf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 8da5c342fd0a3..d229034f94abb 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -784,7 +784,7 @@ typedef struct edge { mp_int_t slope; } edge; -static void insert_edge(edge* edge_table, int n_edges, mp_int_t py1, mp_int_t py2, mp_int_t px1, mp_int_t slope) { +static void insert_edge(edge *edge_table, int n_edges, mp_int_t py1, mp_int_t py2, mp_int_t px1, mp_int_t slope) { edge e = { py1, py2, @@ -808,7 +808,7 @@ typedef struct node { uint32_t mask; } node; -static int insert_node(node* node_table, int n_nodes, mp_int_t x, uint32_t mask) { +static int insert_node(node *node_table, int n_nodes, mp_int_t x, uint32_t mask) { node n = {x, mask}; node current; // simple linear ordered insertion @@ -948,7 +948,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_int_t y1 = (line == 0) ? ((row << 2) - 1) : ((row << 2) + 1); for (int i = 0; i < last_edge_index; ++i) { // For each edge... - edge* e = &(edge_table[i]); + edge *e = &(edge_table[i]); if ((e->y2 * 4) < y1) { // Edge below subsample line. continue; @@ -1006,7 +1006,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // fill with run of pixels with same mask - can be fast mp_int_t width; if (i + 1 < n_nodes) { - width = nodes[i+1].x - current.x - 1; + width = nodes[i + 1].x - current.x - 1; } else { width = self->width - current.x - 1; } From 35f98aa028b4426c9154571610a2e51b26232679 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 23 Nov 2025 10:53:35 +0000 Subject: [PATCH 48/81] Some more minor clean-up. --- extmod/modframebuf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index d229034f94abb..3c1f9a9d8a7d5 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -310,7 +310,8 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u } -#else +#else // MICROPY_PY_FRAMEBUF_ALPHA + #ifndef FRAMEBUF_GET_ALPHA_ARG #define FRAMEBUF_GET_ALPHA_ARG(idx) (0x100) #endif // GET_ALPHA_ARG @@ -406,7 +407,7 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t } } } -#else +#else // MICROPY_PY_FRAMEBUF_ALPHA static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha, bool draw_last) { if (alpha <= 0) { // nothing to do @@ -1034,7 +1035,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { return mp_const_none; } -#else +#else // MICROPY_PY_FRAMEBUF_ALPHA static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]); From 231c207cfea9963b155a73d42c39dd1750f12ea9 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 23 Nov 2025 13:29:48 +0000 Subject: [PATCH 49/81] Check codesize when alpha is turned off. --- py/mpconfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 4d84dc2ff81b4..db1da22c06a41 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -2113,7 +2113,7 @@ typedef time_t mp_timestamp_t; // Whether to support alpha blending in framebuf module #ifndef MICROPY_PY_FRAMEBUF_ALPHA -#define MICROPY_PY_FRAMEBUF_ALPHA (MICROPY_PY_FRAMEBUF && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#define MICROPY_PY_FRAMEBUF_ALPHA (0) // (MICROPY_PY_FRAMEBUF && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif #ifndef MICROPY_PY_BTREE From 3b3d936f9f4f974f26948f006ad6d11f3cccc618 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 23 Nov 2025 15:11:51 +0000 Subject: [PATCH 50/81] Some more code reorganization; allow 1-bit masks in blit. --- extmod/modframebuf.c | 277 +++++++++++++++--------------- tests/extmod/framebuf_blit.py | 10 ++ tests/extmod/framebuf_blit.py.exp | 6 + 3 files changed, 156 insertions(+), 137 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 3c1f9a9d8a7d5..b828735052f9d 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -245,7 +245,7 @@ static mp_framebuf_p_t formats[] = { #if MICROPY_PY_FRAMEBUF_ALPHA #ifndef FRAMEBUF_GET_ALPHA_ARG -#define FRAMEBUF_GET_ALPHA_ARG(idx) ((n_args > idx) ? mp_obj_get_int(args_in[idx]) : 0x100) +#define FRAMEBUF_GET_ALPHA_ARG(idx) ((n_args > idx) ? mp_obj_get_int(args_in[idx]) : 0xFF) #endif // GET_ALPHA_ARG typedef struct __attribute__((packed)) rgb565 { @@ -313,7 +313,7 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u #else // MICROPY_PY_FRAMEBUF_ALPHA #ifndef FRAMEBUF_GET_ALPHA_ARG -#define FRAMEBUF_GET_ALPHA_ARG(idx) (0x100) +#define FRAMEBUF_GET_ALPHA_ARG(idx) (0xFF) #endif // GET_ALPHA_ARG static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col, mp_int_t alpha) { @@ -347,132 +347,6 @@ static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, uns return formats[fb->format].getpixel(fb, x, y); } -#if MICROPY_PY_FRAMEBUF_ALPHA -static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha, bool draw_last) { - setpixel_checked(fb, x1, y1, col, 1, alpha); - if (x1 == x2 && y1 == y2) { - // nothing more to do - return; - } - if (draw_last) { - setpixel_checked(fb, x2, y2, col, 1, alpha); - } - - mp_int_t dx = x2 - x1; - mp_int_t dy = y2 - y1; - if (dx + dy < 0) { - // swap ends - mp_int_t temp; - dx = -dx; - dy = -dy; - temp = x1; - x1 = x2; - x2 = temp; - temp = y1; - y1 = y2; - y2 = temp; - } - - bool steep; - if (dy > dx || dy < -dx) { - // swap x and y - mp_int_t temp; - temp = x1; - x1 = y1; - y1 = temp; - temp = dx; - dx = dy; - dy = temp; - steep = true; - } else { - steep = false; - } - - // Fixed point with 8 bits of fractional part. - // dx != 0 is guaranteed - mp_int_t gradient = ((dy * 256) / dx); - - mp_int_t y_intercept = (y1 * 256) + gradient; - if (steep) { - for (mp_int_t x = x1 + 1; x < x1 + dx; ++x) { - setpixel_checked(fb, y_intercept >> 8, x, col, 1, (alpha * (0x100 - (y_intercept & 0xff))) >> 8); - setpixel_checked(fb, (y_intercept >> 8) + 1, x, col, 1, (alpha * (y_intercept & 0xff)) >> 8); - y_intercept += gradient; - } - } else { - for (mp_int_t x = x1 + 1; x < x1 + dx; ++x) { - setpixel_checked(fb, x, y_intercept >> 8, col, 1, (alpha * (0x100 - (y_intercept & 0xff))) >> 8); - setpixel_checked(fb, x, (y_intercept >> 8) + 1, col, 1, (alpha * (y_intercept & 0xff)) >> 8); - y_intercept += gradient; - } - } -} -#else // MICROPY_PY_FRAMEBUF_ALPHA -static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha, bool draw_last) { - if (alpha <= 0) { - // nothing to do - return; - } - mp_int_t dx = x2 - x1; - mp_int_t sx; - if (dx > 0) { - sx = 1; - } else { - dx = -dx; - sx = -1; - } - - mp_int_t dy = y2 - y1; - mp_int_t sy; - if (dy > 0) { - sy = 1; - } else { - dy = -dy; - sy = -1; - } - - bool steep; - if (dy > dx) { - mp_int_t temp; - temp = x1; - x1 = y1; - y1 = temp; - temp = dx; - dx = dy; - dy = temp; - temp = sx; - sx = sy; - sy = temp; - steep = true; - } else { - steep = false; - } - - mp_int_t e = 2 * dy - dx; - for (mp_int_t i = 0; i < dx; ++i) { - if (steep) { - if (0 <= y1 && y1 < fb->width && 0 <= x1 && x1 < fb->height) { - setpixel(fb, y1, x1, col, alpha); - } - } else { - if (0 <= x1 && x1 < fb->width && 0 <= y1 && y1 < fb->height) { - setpixel(fb, x1, y1, col, alpha); - } - } - while (e >= 0) { - y1 += sy; - e -= 2 * dx; - } - x1 += sx; - e += 2 * dy; - } - - if (draw_last) { - setpixel_checked(fb, x2, y2, col, 1, alpha); - } -} -#endif // MICROPY_PY_FRAMEBUF_ALPHA - static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, unsigned int buf_flags, mp_obj_framebuf_t *o) { mp_int_t width = mp_obj_get_int(args_in[1]); @@ -632,6 +506,136 @@ static mp_obj_t framebuf_rect(size_t n_args, const mp_obj_t *args_in) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 8, framebuf_rect); +#if MICROPY_PY_FRAMEBUF_ALPHA +static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha, bool draw_last) { + // This implements Wu's antialiased line algorithm in 8-bit fixed-point. + // See https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm + + setpixel_checked(fb, x1, y1, col, 1, alpha); + if (x1 == x2 && y1 == y2) { + // nothing more to do + return; + } + if (draw_last) { + setpixel_checked(fb, x2, y2, col, 1, alpha); + } + + mp_int_t dx = x2 - x1; + mp_int_t dy = y2 - y1; + if (dx + dy < 0) { + // swap ends + mp_int_t temp; + dx = -dx; + dy = -dy; + temp = x1; + x1 = x2; + x2 = temp; + temp = y1; + y1 = y2; + y2 = temp; + } + + bool steep; + if (dy > dx || dy < -dx) { + // swap x and y + mp_int_t temp; + temp = x1; + x1 = y1; + y1 = temp; + temp = dx; + dx = dy; + dy = temp; + steep = true; + } else { + steep = false; + } + + // Fixed point with 8 bits of fractional part. + // dx != 0 is guaranteed + mp_int_t gradient = ((dy * 256) / dx); + + mp_int_t y_intercept = (y1 * 256) + gradient; + if (steep) { + for (mp_int_t x = x1 + 1; x < x1 + dx; ++x) { + setpixel_checked(fb, y_intercept >> 8, x, col, 1, (alpha * (0x100 - (y_intercept & 0xff))) >> 8); + setpixel_checked(fb, (y_intercept >> 8) + 1, x, col, 1, (alpha * (y_intercept & 0xff)) >> 8); + y_intercept += gradient; + } + } else { + for (mp_int_t x = x1 + 1; x < x1 + dx; ++x) { + setpixel_checked(fb, x, y_intercept >> 8, col, 1, (alpha * (0x100 - (y_intercept & 0xff))) >> 8); + setpixel_checked(fb, x, (y_intercept >> 8) + 1, col, 1, (alpha * (y_intercept & 0xff)) >> 8); + y_intercept += gradient; + } + } +} +#else // MICROPY_PY_FRAMEBUF_ALPHA +static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha, bool draw_last) { + // This implements Bresenham's line algorithm, see https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm + if (alpha <= 0) { + // nothing to do + return; + } + mp_int_t dx = x2 - x1; + mp_int_t sx; + if (dx > 0) { + sx = 1; + } else { + dx = -dx; + sx = -1; + } + + mp_int_t dy = y2 - y1; + mp_int_t sy; + if (dy > 0) { + sy = 1; + } else { + dy = -dy; + sy = -1; + } + + bool steep; + if (dy > dx) { + mp_int_t temp; + temp = x1; + x1 = y1; + y1 = temp; + temp = dx; + dx = dy; + dy = temp; + temp = sx; + sx = sy; + sy = temp; + steep = true; + } else { + steep = false; + } + + mp_int_t e = 2 * dy - dx; + for (mp_int_t i = 0; i < dx; ++i) { + if (steep) { + if (0 <= y1 && y1 < fb->width && 0 <= x1 && x1 < fb->height) { + setpixel(fb, y1, x1, col, alpha); + } + } else { + if (0 <= x1 && x1 < fb->width && 0 <= y1 && y1 < fb->height) { + setpixel(fb, x1, y1, col, alpha); + } + } + while (e >= 0) { + y1 += sy; + e -= 2 * dx; + } + x1 += sx; + e += 2 * dy; + } + + if (draw_last) { + setpixel_checked(fb, x2, y2, col, 1, alpha); + } +} +#endif // MICROPY_PY_FRAMEBUF_ALPHA + static mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args_in) { (void)n_args; @@ -655,6 +659,7 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 7, framebuf_lin #define ELLIPSE_MASK_Q4 (0x08) static void draw_ellipse_points(const mp_obj_framebuf_t *fb, mp_int_t cx, mp_int_t cy, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask, mp_int_t alpha) { + // Care needs to be taken to avoid drawing the same pixel twice when using transparency. if (mask & ELLIPSE_MASK_FILL) { if (y == 0 && (mask & ELLIPSE_MASK_ALL)) { // on y-axis, draw one hline @@ -1210,13 +1215,12 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { int x0end = MIN(self->width, x + source.width); int y0end = MIN(self->height, y + source.height); - mp_int_t alpha = 0x100; - #if MICROPY_PY_FRAMEBUF_ALPHA + mp_int_t alpha = 0xFF; mp_int_t alpha_mul = 0; mp_obj_framebuf_t mask; if (n_args > 6 && args_in[6] != mp_const_none) { if (mp_obj_get_type(args_in[6]) == &mp_type_int) { - alpha = mp_obj_get_int(args_in[6]); + alpha = FRAMEBUF_GET_ALPHA_ARG(6); if (alpha <= 0) { // nothing to do return mp_const_none; @@ -1231,8 +1235,9 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { case FRAMEBUF_MVLSB: case FRAMEBUF_MHLSB: case FRAMEBUF_MHMSB: - alpha_mul = 0x100; + alpha_mul = 0xFF; break; + #if MICROPY_PY_FRAMEBUF_ALPHA case FRAMEBUF_GS8: alpha_mul = 1; break; @@ -1242,13 +1247,13 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { case FRAMEBUF_GS2_HMSB: alpha_mul = 0x1111; break; + #endif // MICROPY_PY_FRAMEBUF_ALPHA default: // other formats can't easily be converted to alpha mp_raise_ValueError(MP_ERROR_TEXT("invalid mask format")); } } } - #endif // MICROPY_PY_FRAMEBUF_ALPHA for (; y0 < y0end; ++y0) { int cx1 = x1; @@ -1257,12 +1262,10 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { if (palette.buf) { col = getpixel(&palette, col, 0); } - #if MICROPY_PY_FRAMEBUF_ALPHA if (alpha_mul) { alpha = getpixel(&mask, cx1, y1) * alpha_mul; } - #endif // MICROPY_PY_FRAMEBUF_ALPHA - if (col != (uint32_t)key) { + if (col != (uint32_t)key && (alpha > 0)) { setpixel(self, cx0, y0, col, alpha); } ++cx1; @@ -1311,7 +1314,7 @@ static mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ys } for (; y != yend; y += dy) { for (unsigned x = sx; x != xend; x += dx) { - setpixel(self, x, y, getpixel(self, x - xstep, y - ystep), 0x100); + setpixel(self, x, y, getpixel(self, x - xstep, y - ystep), 0xFF); } } return mp_const_none; diff --git a/tests/extmod/framebuf_blit.py b/tests/extmod/framebuf_blit.py index b1d98b330a838..b85ffbb0570a0 100644 --- a/tests/extmod/framebuf_blit.py +++ b/tests/extmod/framebuf_blit.py @@ -49,6 +49,16 @@ def printbuf(): fbuf.blit(image, 1, 1, -1, palette) printbuf() +# Blit with a mono mask +mask = framebuf.FrameBuffer(bytearray(4), 2, 2, framebuf.MONO_HLSB) +for x in [0, 1]: + for y in [0, 1]: + mask.pixel(x, y, (x + y) % 2) + +fbuf.fill(0) +fbuf.blit(fbuf2, 1, 1, -1, None, mask) +printbuf() + # Not enough elements in the tuple. try: fbuf.blit((0, 0, 0), 0, 0) diff --git a/tests/extmod/framebuf_blit.py.exp b/tests/extmod/framebuf_blit.py.exp index e340f1990c783..aa3dcdfd11df5 100644 --- a/tests/extmod/framebuf_blit.py.exp +++ b/tests/extmod/framebuf_blit.py.exp @@ -40,6 +40,12 @@ ffff000000 00a2a10000 0000000000 -->8-- +--8<-- +0000000000 +0000ff0000 +00ff000000 +0000000000 +-->8-- ValueError ValueError ValueError From 4cc81197eefbe3852f2d73a2162c904ece4c4b71 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 23 Nov 2025 15:12:24 +0000 Subject: [PATCH 51/81] Add documentation for the alpha parameter. --- docs/library/framebuf.rst | 81 +++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 15 deletions(-) diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index e2a231207d673..40cde1dffb69d 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -62,14 +62,19 @@ The following methods draw shapes onto the FrameBuffer. Fill the entire FrameBuffer with the specified color. -.. method:: FrameBuffer.pixel(x, y[, c]) +.. method:: FrameBuffer.pixel(x, y[, c, alpha=0xFF]) If *c* is not given, get the color value of the specified pixel. If *c* is given, set the specified pixel to the given color. -.. method:: FrameBuffer.hline(x, y, w, c) -.. method:: FrameBuffer.vline(x, y, h, c) -.. method:: FrameBuffer.line(x1, y1, x2, y2, c) + The optional *alpha* parameter is a value from 0 to 255 which + indicates the transparency to use when setting the pixel (0 is + completely transparent, 255 is completely opaque). The if not + specified, the pixel is drawn opaquely. + +.. method:: FrameBuffer.hline(x, y, w, c[, alpha=0xFF]) +.. method:: FrameBuffer.vline(x, y, h, c[, alpha=0xFF]) +.. method:: FrameBuffer.line(x1, y1, x2, y2, c[, alpha=0xFF]) Draw a line from a set of coordinates using the given color and a thickness of 1 pixel. The `line` method draws the line up to @@ -77,14 +82,22 @@ The following methods draw shapes onto the FrameBuffer. methods draw horizontal and vertical lines respectively up to a given length. -.. method:: FrameBuffer.rect(x, y, w, h, c[, f]) + The optional *alpha* parameter is a value from 0 to 255 which + indicates the transparency to use when drawing the line. The + default is 255 (completely opaque). + +.. method:: FrameBuffer.rect(x, y, w, h, c[, f, alpha=0xFF]) - Draw a rectangle at the given location, size and color. + Draw a rectangle at the given location, size, color and transparency. The optional *f* parameter can be set to ``True`` to fill the rectangle. Otherwise just a one pixel outline is drawn. -.. method:: FrameBuffer.ellipse(x, y, xr, yr, c[, f, m]) + The optional *alpha* parameter is a value from 0 to 255 which + indicates the transparency to use when drawing the rectangle. The + default is 255 (completely opaque). + +.. method:: FrameBuffer.ellipse(x, y, xr, yr, c[, f, m, alpha=0xFF]) Draw an ellipse at the given location. Radii *xr* and *yr* define the geometry; equal values cause a circle to be drawn. The *c* parameter @@ -98,7 +111,11 @@ The following methods draw shapes onto the FrameBuffer. to be drawn, with bit 0 specifying Q1, b1 Q2, b2 Q3 and b3 Q4. Quadrants are numbered counterclockwise with Q1 being top right. -.. method:: FrameBuffer.poly(x, y, coords, c[, f]) + The optional *alpha* parameter is a value from 0 to 255 which + indicates the transparency to use when drawing the ellipse. The + default is 255 (completely opaque). + +.. method:: FrameBuffer.poly(x, y, coords, c[, f, alpha=0xFF]) Given a list of coordinates, draw an arbitrary (convex or concave) closed polygon at the given x, y location using the given color. @@ -109,16 +126,23 @@ The following methods draw shapes onto the FrameBuffer. The optional *f* parameter can be set to ``True`` to fill the polygon. Otherwise just a one pixel outline is drawn. + The optional *alpha* parameter is a value from 0 to 255 which + indicates the transparency to use when drawing the polygon. The + default is 255 (completely opaque). + Drawing text ------------ -.. method:: FrameBuffer.text(s, x, y[, c]) +.. method:: FrameBuffer.text(s, x, y[, c, alpha=0xFF]) Write text to the FrameBuffer using the coordinates as the upper-left corner of the text. The color of the text can be defined by the optional argument but is otherwise a default value of 1. All characters have dimensions of 8x8 pixels and there is currently no way to change the font. + The optional *alpha* parameter is a value from 0 to 255 which + indicates the transparency to use when drawing the text. The + default is 255 (completely opaque). Other methods ------------- @@ -128,14 +152,9 @@ Other methods Shift the contents of the FrameBuffer by the given vector. This may leave a footprint of the previous colors in the FrameBuffer. -.. method:: FrameBuffer.blit(fbuf, x, y, key=-1, palette=None) +.. method:: FrameBuffer.blit(fbuf, x, y, key=-1, palette=None, alpha=None) Draw another FrameBuffer on top of the current one at the given coordinates. - If *key* is specified then it should be a color integer and the - corresponding color will be considered transparent: all pixels with that - color value will not be drawn. (If the *palette* is specified then the *key* - is compared to the value from *palette*, not to the value directly from - *fbuf*.) *fbuf* can be another FrameBuffer instance, or a tuple or list of the form:: @@ -149,6 +168,12 @@ Other methods of the tuple/list are the same as the arguments to the constructor except that the *buffer* here can be read-only. + If *key* is specified then it should be a color integer and the + corresponding color will be considered transparent: all pixels with that + color value will not be drawn. (If the *palette* is specified then the *key* + is compared to the value from *palette*, not to the value directly from + *fbuf*.) + The *palette* argument enables blitting between FrameBuffers with differing formats. Typical usage is to render a monochrome or grayscale glyph/icon to a color display. The *palette* is a FrameBuffer instance whose format is @@ -160,6 +185,16 @@ Other methods current pixel will be that of that *palette* pixel whose x position is the color of the corresponding source pixel. + The *alpha* parameter is either a value from 0 to 255 which indicates the + transparency to use when overlaying the buffer, or is a monochrome or + grayscale mask buffer of the same size as the buffer being drawn, indicating + the transparency for each pixel. The mask buffer can either be another + FrameBuffer or a tuple as described above for the *fbuf* parameter. If both + *key* and *alpha* are used, any pixels of the *key* color will be transparent + and the *alpha* value will be ignored for those pixels. + + An alpha mask buffer cannot have the RGB565 pixel format. + Constants --------- @@ -205,3 +240,19 @@ Constants .. data:: framebuf.GS8 Grayscale (8-bit) color format + +.. data:: framebuf.ALPHA + + Whether or not the framebuf module was compiled with support for alpha + values. + + If this is False, all alpha parameters will be ignored, lines and + polygons will be rendered with non-anti-aliasing algorithms, and drawing + will be done full opaquely. In the blit method a monochrome buffer can be + used as the *alpha* parameter to provide a binary mask (0 is transparent, + 1 is opaque), but other values are ignored or are errors. + + If this is True, alpha parameters will be used and anti-aliased algorithms + will be used for rendering lines and polygons. + + There is currently no support for rendering antialiased ellipses. From 9bcc5ee8f489eb25e89198f546c1b24dc2c9b20d Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 23 Nov 2025 15:16:39 +0000 Subject: [PATCH 52/81] Re-word for clarity. --- docs/library/framebuf.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index 40cde1dffb69d..f4bb5f7d6a4b2 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -248,11 +248,12 @@ Constants If this is False, all alpha parameters will be ignored, lines and polygons will be rendered with non-anti-aliasing algorithms, and drawing - will be done full opaquely. In the blit method a monochrome buffer can be - used as the *alpha* parameter to provide a binary mask (0 is transparent, - 1 is opaque), but other values are ignored or are errors. + will be done full opaquely. In the ``framebuf.blit`` method a monochrome + buffer can be used as the *alpha* parameter to provide a binary mask (0 + is transparent, 1 is opaque), but other values are ignored or are errors. - If this is True, alpha parameters will be used and anti-aliased algorithms - will be used for rendering lines and polygons. + If this is True, alpha parameters will act as specified in the descriptions, + anti-aliased algorithms will be used for rendering lines and polygons, and + ``framebuf.blit`` can use grayscale masks. There is currently no support for rendering antialiased ellipses. From a93e411cdcbe9d8c50d6945b6008cfb9ad67e09a Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 23 Nov 2025 15:54:44 +0000 Subject: [PATCH 53/81] Refactoring to make comparison easier. Fix some tests. --- extmod/modframebuf.c | 82 +++++++++---------- tests/extmod/framebuf1.py | 1 - tests/extmod/framebuf_polygon.py | 2 +- ...ygonalpha.py => framebuf_polygon_alpha.py} | 3 +- ...a.py.exp => framebuf_polygon_alpha.py.exp} | 0 5 files changed, 40 insertions(+), 48 deletions(-) rename tests/extmod/{framebuf_polygonalpha.py => framebuf_polygon_alpha.py} (99%) rename tests/extmod/{framebuf_polygonalpha.py.exp => framebuf_polygon_alpha.py.exp} (100%) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index b828735052f9d..5359167af9291 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -285,7 +285,29 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 } formats[fb->format].setpixel(fb, x, y, col); } +#else // MICROPY_PY_FRAMEBUF_ALPHA + +#ifndef FRAMEBUF_GET_ALPHA_ARG +#define FRAMEBUF_GET_ALPHA_ARG(idx) (0xFF) +#endif // GET_ALPHA_ARG + +static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col, mp_int_t alpha) { + (void)alpha; + formats[fb->format].setpixel(fb, x, y, col); +} +#endif // MICROPY_PY_FRAMEBUF_ALPHA + +static void setpixel_checked(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask, mp_int_t alpha) { + if (mask && alpha > 0 && 0 <= x && x < fb->width && 0 <= y && y < fb->height) { + setpixel(fb, x, y, col, alpha); + } +} + +static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { + return formats[fb->format].getpixel(fb, x, y); +} +#if MICROPY_PY_FRAMEBUF_ALPHA static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col, mp_int_t alpha) { if (alpha == 0 || h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) { // No operation needed. @@ -308,19 +330,10 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u } } } - - #else // MICROPY_PY_FRAMEBUF_ALPHA - -#ifndef FRAMEBUF_GET_ALPHA_ARG -#define FRAMEBUF_GET_ALPHA_ARG(idx) (0xFF) -#endif // GET_ALPHA_ARG - -static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col, mp_int_t alpha) { - formats[fb->format].setpixel(fb, x, y, col); -} - static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col, mp_int_t alpha) { + (void)alpha; + if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) { // No operation needed. return; @@ -334,19 +347,8 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col); } - #endif // MICROPY_PY_FRAMEBUF_ALPHA -static void setpixel_checked(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, mp_int_t col, mp_int_t mask, mp_int_t alpha) { - if (mask && alpha > 0 && 0 <= x && x < fb->width && 0 <= y && y < fb->height) { - setpixel(fb, x, y, col, alpha); - } -} - -static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { - return formats[fb->format].getpixel(fb, x, y); -} - static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, unsigned int buf_flags, mp_obj_framebuf_t *o) { mp_int_t width = mp_obj_get_int(args_in[1]); @@ -572,10 +574,9 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t #else // MICROPY_PY_FRAMEBUF_ALPHA static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha, bool draw_last) { // This implements Bresenham's line algorithm, see https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm - if (alpha <= 0) { - // nothing to do - return; - } + (void)alpha; + (void)draw_last; + mp_int_t dx = x2 - x1; mp_int_t sx; if (dx > 0) { @@ -630,9 +631,7 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t e += 2 * dy; } - if (draw_last) { - setpixel_checked(fb, x2, y2, col, 1, alpha); - } + setpixel_checked(fb, x2, y2, col, 1, alpha); } #endif // MICROPY_PY_FRAMEBUF_ALPHA @@ -1059,10 +1058,6 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_int_t col = mp_obj_get_int(args_in[4]); bool fill = n_args > 5 && mp_obj_is_true(args_in[5]); mp_int_t alpha = FRAMEBUF_GET_ALPHA_ARG(6); - if (alpha <= 0) { - // nothing to do - return mp_const_none; - } if (fill) { // This implements an integer version of http://alienryderflex.com/polygon_fill/ @@ -1185,17 +1180,6 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { mp_int_t x = mp_obj_get_int(args_in[2]); mp_int_t y = mp_obj_get_int(args_in[3]); - - if ( - (x >= self->width) || - (y >= self->height) || - (-x >= source.width) || - (-y >= source.height) - ) { - // Out of bounds, no-op, leave early. - return mp_const_none; - } - // Key and palette argument handling. mp_int_t key = -1; if (n_args > 4) { @@ -1207,6 +1191,16 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { get_readonly_framebuffer(args_in[5], &palette); } + if ( + (x >= self->width) || + (y >= self->height) || + (-x >= source.width) || + (-y >= source.height) + ) { + // Out of bounds, no-op. + return mp_const_none; + } + // Clip. int x0 = MAX(0, x); int y0 = MAX(0, y); diff --git a/tests/extmod/framebuf1.py b/tests/extmod/framebuf1.py index 887a736344c44..bb1a5b0713abd 100644 --- a/tests/extmod/framebuf1.py +++ b/tests/extmod/framebuf1.py @@ -4,7 +4,6 @@ print("SKIP") raise SystemExit - w = 5 h = 16 size = ((w + 7) & ~7) * ((h + 7) & ~7) // 8 diff --git a/tests/extmod/framebuf_polygon.py b/tests/extmod/framebuf_polygon.py index e510256bb0daf..80712e04de7f2 100644 --- a/tests/extmod/framebuf_polygon.py +++ b/tests/extmod/framebuf_polygon.py @@ -12,7 +12,7 @@ raise SystemExit if framebuf.ALPHA: - # Test non-antialiased version + # This tests non-alpha version print("SKIP") raise SystemExit diff --git a/tests/extmod/framebuf_polygonalpha.py b/tests/extmod/framebuf_polygon_alpha.py similarity index 99% rename from tests/extmod/framebuf_polygonalpha.py rename to tests/extmod/framebuf_polygon_alpha.py index b0e4b4cbdf11b..1a1b81def0431 100644 --- a/tests/extmod/framebuf_polygonalpha.py +++ b/tests/extmod/framebuf_polygon_alpha.py @@ -5,14 +5,13 @@ print("SKIP") raise SystemExit - # TODO: poly needs functions that aren't in dynruntime.h yet. if not hasattr(framebuf.FrameBuffer, "poly"): print("SKIP") raise SystemExit if not framebuf.ALPHA: - # Testing for anti-aliased lines + # Testing for alpha and antialiased drawing print("SKIP") raise SystemExit diff --git a/tests/extmod/framebuf_polygonalpha.py.exp b/tests/extmod/framebuf_polygon_alpha.py.exp similarity index 100% rename from tests/extmod/framebuf_polygonalpha.py.exp rename to tests/extmod/framebuf_polygon_alpha.py.exp From 40e7d14dbe1b197d2c972fe7b16307e89ee6d136 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Sun, 23 Nov 2025 16:12:45 +0000 Subject: [PATCH 54/81] A few more fixed after refactor. --- extmod/modframebuf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 5359167af9291..dd3083a36437e 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -241,7 +241,6 @@ static mp_framebuf_p_t formats[] = { [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect}, }; - #if MICROPY_PY_FRAMEBUF_ALPHA #ifndef FRAMEBUF_GET_ALPHA_ARG @@ -574,7 +573,6 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t #else // MICROPY_PY_FRAMEBUF_ALPHA static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, mp_int_t col, mp_int_t alpha, bool draw_last) { // This implements Bresenham's line algorithm, see https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm - (void)alpha; (void)draw_last; mp_int_t dx = x2 - x1; From 1897e95eda18bfda2805a45d0287e8da9fe02e20 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 24 Nov 2025 13:31:00 +0000 Subject: [PATCH 55/81] Careful refactoring of alpha application for correctness. --- extmod/modframebuf.c | 58 +- py/mpconfig.h | 2 +- tests/extmod/framebuf_alpha.py.exp | 76 +- tests/extmod/framebuf_polygon_alpha.py.exp | 825 ++++++++++----------- 4 files changed, 490 insertions(+), 471 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index dd3083a36437e..272fc8b8c9700 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -257,8 +257,17 @@ typedef union { rgb565 rgb; } urgb565; +static uint32_t alpha_mult(uint32_t c, uint32_t alpha) { + // Efficient, correct alpha multiplication following Van Aken. + // See https://arxiv.org/pdf/2202.02864 + c *= alpha; + c += 0x80U; + c += c >> 8; + return c >> 8; +} + static uint32_t alpha_blend(uint32_t c1, uint32_t c2, uint32_t alpha) { - return (c1 * (0xff - alpha) + alpha * c2 + 0x80) >> 8; + return alpha_mult(c1, 0xff - alpha) + alpha_mult(c2, alpha); } static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32_t col, mp_int_t alpha) { @@ -551,21 +560,21 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t steep = false; } - // Fixed point with 8 bits of fractional part. + // Fixed point with 12 bits of fractional part. // dx != 0 is guaranteed - mp_int_t gradient = ((dy * 256) / dx); + mp_int_t gradient = ((dy * (1 << 12)) / dx); - mp_int_t y_intercept = (y1 * 256) + gradient; + mp_int_t y_intercept = (y1 * (1 << 12)) + gradient; if (steep) { for (mp_int_t x = x1 + 1; x < x1 + dx; ++x) { - setpixel_checked(fb, y_intercept >> 8, x, col, 1, (alpha * (0x100 - (y_intercept & 0xff))) >> 8); - setpixel_checked(fb, (y_intercept >> 8) + 1, x, col, 1, (alpha * (y_intercept & 0xff)) >> 8); + setpixel_checked(fb, y_intercept >> 12, x, col, 1, alpha_mult(0xFF - ((y_intercept >> 4) & 0xff), alpha)); + setpixel_checked(fb, (y_intercept >> 12) + 1, x, col, 1, alpha_mult((y_intercept >> 4) & 0xff, alpha)); y_intercept += gradient; } } else { for (mp_int_t x = x1 + 1; x < x1 + dx; ++x) { - setpixel_checked(fb, x, y_intercept >> 8, col, 1, (alpha * (0x100 - (y_intercept & 0xff))) >> 8); - setpixel_checked(fb, x, (y_intercept >> 8) + 1, col, 1, (alpha * (y_intercept & 0xff)) >> 8); + setpixel_checked(fb, x, y_intercept >> 12, col, 1, alpha_mult(0xFF - ((y_intercept >> 4) & 0xff), alpha)); + setpixel_checked(fb, x, (y_intercept >> 12) + 1, col, 1, alpha_mult((y_intercept >> 4) & 0xff, alpha)); y_intercept += gradient; } } @@ -884,8 +893,8 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // The value of 1/slope is stored with 12 bits of fixed precision. // Horizontal lines are ignored. + // Increase alpha for mono buffers to get sharp corners. if (self->format == FRAMEBUF_MHLSB || self->format == FRAMEBUF_MHMSB || self->format == FRAMEBUF_MVLSB) { - // Increase alpha for mono to get sharp corners. alpha *= 2; } @@ -952,10 +961,10 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { for (int i = 0; i < last_edge_index; ++i) { // For each edge... edge *e = &(edge_table[i]); - if ((e->y2 * 4) < y1) { + if ((e->y2 * (1 << 2)) < y1) { // Edge below subsample line. continue; - } else if ((e->y1 * 4) > y1) { + } else if ((e->y1 * (1 << 2)) > y1) { // Edge above subsample line (can happen for lower subsample line at start of edge). continue; } @@ -964,7 +973,14 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // with subsample pixels round to nearest quarter. This makes the antialiased values // symmetric when given a symmetric shape. mp_int_t x_adjusted = e->x1 + (1 << 11) + (1 << 9); - mp_int_t column = x_adjusted >> 12; + mp_int_t column; + // We need integer floor division here, as remainder matters. + if (x_adjusted > 0) { + column = x_adjusted >> 12; + } else { + // avoid undefined behaviour of shifting negatives. + column = ~((~x_adjusted) >> 12); + } if (column >= self->width) { // Outside of buffer on the high end, don't care about these points, // but need to bump the x-value in case line eventually comes inside the buffer. @@ -999,7 +1015,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { if (current.x >= 0) { // pixel is inside the buffer, so draw the pixel - setpixel(self, current.x, row, col, (popcount(mask) * alpha) >> 3); + setpixel(self, current.x, row, col, alpha_mult((popcount(mask)*0b1001001) >> 1, alpha)); } // extend mask by last bits @@ -1013,7 +1029,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { } else { width = self->width - current.x - 1; } - fill_rect(self, current.x + 1, row, width, 1, col, (popcount(mask) * alpha) >> 3); + fill_rect(self, current.x + 1, row, width, 1, col, alpha_mult((popcount(mask)*0b1001001) >> 1, alpha)); } } } @@ -1227,17 +1243,17 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { case FRAMEBUF_MVLSB: case FRAMEBUF_MHLSB: case FRAMEBUF_MHMSB: - alpha_mul = 0xFF; + alpha_mul = 0xff; break; #if MICROPY_PY_FRAMEBUF_ALPHA case FRAMEBUF_GS8: - alpha_mul = 1; + alpha_mul = 0x1; break; case FRAMEBUF_GS4_HMSB: - alpha_mul = 0x11; + alpha_mul = 0x10001; break; case FRAMEBUF_GS2_HMSB: - alpha_mul = 0x1111; + alpha_mul = 0x01010101; break; #endif // MICROPY_PY_FRAMEBUF_ALPHA default: @@ -1255,9 +1271,13 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { col = getpixel(&palette, col, 0); } if (alpha_mul) { + #if MICROPY_PY_FRAMEBUF_ALPHA alpha = getpixel(&mask, cx1, y1) * alpha_mul; + #else // MICROPY_PY_FRAMEBUF_ALPHA + alpha = getpixel(&mask, cx1, y1); + #endif // MICROPY_PY_FRAMEBUF_ALPHA } - if (col != (uint32_t)key && (alpha > 0)) { + if (col != (uint32_t)key && alpha) { setpixel(self, cx0, y0, col, alpha); } ++cx1; diff --git a/py/mpconfig.h b/py/mpconfig.h index db1da22c06a41..4d84dc2ff81b4 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -2113,7 +2113,7 @@ typedef time_t mp_timestamp_t; // Whether to support alpha blending in framebuf module #ifndef MICROPY_PY_FRAMEBUF_ALPHA -#define MICROPY_PY_FRAMEBUF_ALPHA (0) // (MICROPY_PY_FRAMEBUF && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#define MICROPY_PY_FRAMEBUF_ALPHA (MICROPY_PY_FRAMEBUF && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif #ifndef MICROPY_PY_BTREE diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index ff75a313043d8..1a05e8c1b1c3a 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -127,25 +127,25 @@ --8<-- 0000000000 00ff000000 -0080800000 +007f800000 0000ff0000 -->8-- --8<-- 0000000000 -00ff800000 +00ff7f0000 000080ff00 0000000000 -->8-- --8<-- 0000000000 007f000000 -003f3f0000 +003f400000 00007f0000 -->8-- --8<-- 0000000000 007f3f0000 -00003f7f00 +0000407f00 0000000000 -->8-- --8<-- @@ -180,8 +180,8 @@ -->8-- --8<-- 0000000000 -00003b0000 -00197f0000 +00007f0000 +007f7f0000 0000000000 -->8-- --8<-- @@ -204,8 +204,8 @@ efefefefef -->8-- --8<-- efefefefef -efefbaefef -efd87fefef +efef7fefef +ef7f7fefef efefefefef -->8-- --8<-- @@ -276,8 +276,8 @@ e079e079000000000000 -->8-- --8<-- 00000000000000000000 -00000000c07100000000 -0000c030e0fb00000000 +00000000e0fb00000000 +0000e0fbe0fb00000000 00000000000000000000 -->8-- --8<-- @@ -300,8 +300,8 @@ e007e007e007e007e007 -->8-- --8<-- e007e007e007e007e007 -e007e0070076e007e007 -e0070037e0fbe007e007 +e007e007e0fbe007e007 +e007e0fbe0fbe007e007 e007e007e007e007e007 -->8-- --8<-- @@ -352,31 +352,31 @@ e007e007e007e007e007 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000054545400000000000000000000000000 -000000000000000000000000005454545454000000000000000000000000 -000000000000000000000000545454545454540000000000000000000000 -000000000000000000000054545454545454545400000000000000000000 -000000000000000000000054545454545454545400000000000000000000 -000000000000000000005454545454545454545454000000000000000000 -000000000000000000005454545454545454545454000000000000000000 -000000000000000000005454545454545454545454000000000000000000 -000000000000000000545454545454545454545454540000000000000000 -000000000000000000545454545454545454545454540000000000000000 -000000000000000000545454545454545454545454540000000000000000 -000000000000000000545454545454545454545454540000000000000000 -000000000000000000545454545454545454545454540000000000000000 -000000000000000000545454545454545454545454540000000000000000 -000000000000000000545454545454545454545454540000000000000000 -000000000000000000545454545454545454545454540000000000000000 -000000000000000000545454545454545454545454540000000000000000 -000000000000000000005454545454545454545454000000000000000000 -000000000000000000005454545454545454545454000000000000000000 -000000000000000000005454545454545454545454000000000000000000 -000000000000000000000054545454545454545400000000000000000000 -000000000000000000000054545454545454545400000000000000000000 -000000000000000000000000545454545454540000000000000000000000 -000000000000000000000000005454545454000000000000000000000000 -000000000000000000000000000054545400000000000000000000000000 +000000000000000000000000000055555500000000000000000000000000 +000000000000000000000000005555555555000000000000000000000000 +000000000000000000000000555555555555550000000000000000000000 +000000000000000000000055555555555555555500000000000000000000 +000000000000000000000055555555555555555500000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000000055555555555555555500000000000000000000 +000000000000000000000055555555555555555500000000000000000000 +000000000000000000000000555555555555550000000000000000000000 +000000000000000000000000005555555555000000000000000000000000 +000000000000000000000000000055555500000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 --->8-- +-->8-- \ No newline at end of file diff --git a/tests/extmod/framebuf_polygon_alpha.py.exp b/tests/extmod/framebuf_polygon_alpha.py.exp index 4d729bb184f31..635ad957833a5 100644 --- a/tests/extmod/framebuf_polygon_alpha.py.exp +++ b/tests/extmod/framebuf_polygon_alpha.py.exp @@ -1,20 +1,20 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· 92 6d ·· ·· ·· 6d 92 ·· ·· ff ·· ·· - ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff ·· ·· 26 d9 ·· ·· ·· d9 26 ·· ·· ff ·· ·· - ff ·· ·· 92 6d ·· ·· ·· 6d 92 ·· ·· ff ·· ·· ff ·· ·· ·· b8 47 ·· 47 b8 ·· ·· ·· ff ·· ·· - ff ·· ·· 26 d9 ·· ·· ·· d9 26 ·· ·· ff ·· ·· ff ·· ·· ·· 4c b3 ·· b3 4c ·· ·· ·· ff ·· ·· - ff ·· ·· ·· b8 47 ·· 47 b8 ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· de 3d de ·· ·· ·· ·· ff ·· ·· - ff ·· ·· ·· 4c b3 ·· b3 4c ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· 72 cc 72 ·· ·· ·· ·· ff ·· ·· - ff ·· ·· ·· ·· de 3d de ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· - ff ·· ·· ·· ·· 72 cc 72 ·· ·· ·· ·· ff ·· ·· ff ·· ·· fe 6d ·· ·· ·· 6d ff ·· ·· ff ·· ·· - ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· fe d9 ·· ·· ·· d9 ff ·· ·· ff ·· ·· - ff ·· ·· ff 6d ·· ·· ·· 6d fe ·· ·· ff ·· ·· ff ·· ·· ff b8 47 ·· 47 b8 ff ·· ·· ff ·· ·· - ff ·· ·· ff d9 ·· ·· ·· d9 fe ·· ·· ff ·· ·· ff ·· ·· ff 4c b3 ·· b3 4c ff ·· ·· ff ·· ·· - ff ·· ·· ff b8 47 ·· 47 b8 ff ·· ·· ff ·· ·· ff ·· ·· ff ·· de 3d de ·· ff ·· ·· ff ·· ·· - ff ·· ·· ff 4c b3 ·· b3 4c ff ·· ·· ff ·· ·· ff ·· ·· ff ·· 72 cc 72 ·· ff ·· ·· ff ·· ·· - ff ·· ·· ff ·· de 3d de ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· - ff ·· ·· ff ·· 72 cc 72 ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff ·· ·· 24 db ·· ·· ·· db 24 ·· ·· ff ·· ·· + ff ·· ·· 92 6d ·· ·· ·· 6d 92 ·· ·· ff ·· ·· ff ·· ·· ·· b6 49 ·· 49 b6 ·· ·· ·· ff ·· ·· + ff ·· ·· 24 db ·· ·· ·· db 24 ·· ·· ff ·· ·· ff ·· ·· ·· 49 b6 ·· b6 49 ·· ·· ·· ff ·· ·· + ff ·· ·· ·· b6 49 ·· 49 b6 ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· db 43 db ·· ·· ·· ·· ff ·· ·· + ff ·· ·· ·· 49 b6 ·· b6 49 ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· 6d d0 6d ·· ·· ·· ·· ff ·· ·· + ff ·· ·· ·· ·· db 43 db ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ·· ·· 6d d0 6d ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff 6d ·· ·· ·· 6d ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff db ·· ·· ·· db ff ·· ·· ff ·· ·· + ff ·· ·· ff 6d ·· ·· ·· 6d ff ·· ·· ff ·· ·· ff ·· ·· ff b6 49 ·· 49 b6 ff ·· ·· ff ·· ·· + ff ·· ·· ff db ·· ·· ·· db ff ·· ·· ff ·· ·· ff ·· ·· ff 49 b6 ·· b6 49 ff ·· ·· ff ·· ·· + ff ·· ·· ff b6 49 ·· 49 b6 ff ·· ·· ff ·· ·· ff ·· ·· ff ·· db 43 db ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff 49 b6 ·· b6 49 ff ·· ·· ff ·· ·· ff ·· ·· ff ·· 6d d0 6d ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· db 43 db ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· 6d d0 6d ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -25,26 +25,26 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 26 4d 4d 26 ·· ·· ·· ·· ·· 26 4d 4d 26 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4d 99 99 86 ·· ·· ·· ·· ·· 86 99 99 4d ·· ·· - 26 4d 4d 26 ·· ·· ·· ·· ·· 26 4d 4d 26 ·· ·· 4d 99 99 99 39 ·· ·· ·· 39 99 99 99 4d ·· ·· - 4d 99 99 86 ·· ·· ·· ·· ·· 86 99 99 4d ·· ·· 4d 99 99 99 86 ·· ·· ·· 86 99 99 99 4d ·· ·· - 4d 99 99 99 39 ·· ·· ·· 39 99 99 99 4d ·· ·· 4d 99 99 99 99 13 ·· 13 99 99 99 99 4d ·· ·· - 4d 99 99 99 86 ·· ·· ·· 86 99 99 99 4d ·· ·· 4d 99 99 99 99 60 ·· 60 99 99 99 99 4d ·· ·· - 4d 99 99 99 99 13 ·· 13 99 99 99 99 4d ·· ·· 4d 99 99 99 99 99 26 99 99 99 99 99 4d ·· ·· - 4d 99 99 99 99 60 ·· 60 99 99 99 99 4d ·· ·· 4d 99 99 99 99 99 99 99 99 99 99 99 4d ·· ·· - 4d 99 99 99 99 99 26 99 99 99 99 99 4d ·· ·· 4d 99 99 60 99 99 99 99 99 60 99 99 4d ·· ·· - 4d 99 99 99 99 99 99 99 99 99 99 99 4d ·· ·· 4d 99 99 4d 60 99 99 99 60 4d 99 99 4d ·· ·· - 4d 99 99 60 99 99 99 99 99 60 99 99 4d ·· ·· 4d 99 99 4d 13 99 99 99 13 4d 99 99 4d ·· ·· - 4d 99 99 4d 60 99 99 99 60 4d 99 99 4d ·· ·· 4d 99 99 4d ·· 86 99 86 ·· 4d 99 99 4d ·· ·· - 4d 99 99 4d 13 99 99 99 13 4d 99 99 4d ·· ·· 4d 99 99 4d ·· 39 99 39 ·· 4d 99 99 4d ·· ·· - 4d 99 99 4d ·· 86 99 86 ·· 4d 99 99 4d ·· ·· 4d 99 99 4d ·· ·· 73 ·· ·· 4d 99 99 4d ·· ·· - 4d 99 99 4d ·· 39 99 39 ·· 4d 99 99 4d ·· ·· 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· - 4d 99 99 4d ·· ·· 73 ·· ·· 4d 99 99 4d ·· ·· 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· - 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· - 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· 26 4d 4d 26 ·· ·· ·· ·· ·· 26 4d 4d 26 ·· ·· - 4d 99 99 4d ·· ·· ·· ·· ·· 4d 99 99 4d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 26 4d 4d 26 ·· ·· ·· ·· ·· 26 4d 4d 26 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 2c 58 58 2c ·· ·· ·· ·· ·· 2c 58 58 2c ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 58 99 99 99 ·· ·· ·· ·· ·· 99 99 99 58 ·· ·· + 2c 58 58 2c ·· ·· ·· ·· ·· 2c 58 58 2c ·· ·· 58 99 99 99 41 ·· ·· ·· 41 99 99 99 58 ·· ·· + 58 99 99 99 ·· ·· ·· ·· ·· 99 99 99 58 ·· ·· 58 99 99 99 99 ·· ·· ·· 99 99 99 99 58 ·· ·· + 58 99 99 99 41 ·· ·· ·· 41 99 99 99 58 ·· ·· 58 99 99 99 99 16 ·· 16 99 99 99 99 58 ·· ·· + 58 99 99 99 99 ·· ·· ·· 99 99 99 99 58 ·· ·· 58 99 99 99 99 6d ·· 6d 99 99 99 99 58 ·· ·· + 58 99 99 99 99 16 ·· 16 99 99 99 99 58 ·· ·· 58 99 99 99 99 99 2c 99 99 99 99 99 58 ·· ·· + 58 99 99 99 99 6d ·· 6d 99 99 99 99 58 ·· ·· 58 99 99 99 99 99 99 99 99 99 99 99 58 ·· ·· + 58 99 99 99 99 99 2c 99 99 99 99 99 58 ·· ·· 58 99 99 6d 99 99 99 99 99 6d 99 99 58 ·· ·· + 58 99 99 99 99 99 99 99 99 99 99 99 58 ·· ·· 58 99 99 58 6d 99 99 99 6d 58 99 99 58 ·· ·· + 58 99 99 6d 99 99 99 99 99 6d 99 99 58 ·· ·· 58 99 99 58 16 99 99 99 16 58 99 99 58 ·· ·· + 58 99 99 58 6d 99 99 99 6d 58 99 99 58 ·· ·· 58 99 99 58 ·· 99 99 99 ·· 58 99 99 58 ·· ·· + 58 99 99 58 16 99 99 99 16 58 99 99 58 ·· ·· 58 99 99 58 ·· 41 99 41 ·· 58 99 99 58 ·· ·· + 58 99 99 58 ·· 99 99 99 ·· 58 99 99 58 ·· ·· 58 99 99 58 ·· ·· 83 ·· ·· 58 99 99 58 ·· ·· + 58 99 99 58 ·· 41 99 41 ·· 58 99 99 58 ·· ·· 58 99 99 58 ·· ·· ·· ·· ·· 58 99 99 58 ·· ·· + 58 99 99 58 ·· ·· 83 ·· ·· 58 99 99 58 ·· ·· 58 99 99 58 ·· ·· ·· ·· ·· 58 99 99 58 ·· ·· + 58 99 99 58 ·· ·· ·· ·· ·· 58 99 99 58 ·· ·· 58 99 99 58 ·· ·· ·· ·· ·· 58 99 99 58 ·· ·· + 58 99 99 58 ·· ·· ·· ·· ·· 58 99 99 58 ·· ·· 2c 58 58 2c ·· ·· ·· ·· ·· 2c 58 58 2c ·· ·· + 58 99 99 58 ·· ·· ·· ·· ·· 58 99 99 58 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 2c 58 58 2c ·· ·· ·· ·· ·· 2c 58 58 2c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -52,21 +52,21 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 99 99 cb 6d ·· ·· ·· 6d cb 99 99 ff ·· ·· - ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff 99 99 a8 e1 ·· ·· ·· e1 a8 99 99 ff ·· ·· - ff 99 99 cb 6d ·· ·· ·· 6d cb 99 99 ff ·· ·· ff 99 99 99 dd 47 ·· 47 dd 99 99 99 ff ·· ·· - ff 99 99 a8 e1 ·· ·· ·· e1 a8 99 99 ff ·· ·· ff 99 99 99 b7 b9 ·· b9 b7 99 99 99 ff ·· ·· - ff 99 99 99 dd 47 ·· 47 dd 99 99 99 ff ·· ·· ff 99 99 99 99 ea 3d ea 99 99 99 99 ff ·· ·· - ff 99 99 99 b7 b9 ·· b9 b7 99 99 99 ff ·· ·· ff 99 99 99 99 c6 d3 c6 99 99 99 99 ff ·· ·· - ff 99 99 99 99 ea 3d ea 99 99 99 99 ff ·· ·· ff 99 99 ff 99 99 ff 99 99 ff 99 99 ff ·· ·· - ff 99 99 99 99 c6 d3 c6 99 99 99 99 ff ·· ·· ff 99 99 ff c4 99 99 99 c4 fe 99 99 ff ·· ·· - ff 99 99 ff 99 99 ff 99 99 ff 99 99 ff ·· ·· ff 99 99 ff e7 99 99 99 e7 fe 99 99 ff ·· ·· - ff 99 99 ff c4 99 99 99 c4 fe 99 99 ff ·· ·· ff 99 99 ff bd b5 99 b5 bd ff 99 99 ff ·· ·· - ff 99 99 ff e7 99 99 99 e7 fe 99 99 ff ·· ·· ff 99 99 ff 4c db 99 db 4c ff 99 99 ff ·· ·· - ff 99 99 ff bd b5 99 b5 bd ff 99 99 ff ·· ·· ff 99 99 ff ·· e5 b1 e5 ·· ff 99 99 ff ·· ·· - ff 99 99 ff 4c db 99 db 4c ff 99 99 ff ·· ·· ff 99 99 ff ·· 72 e2 72 ·· ff 99 99 ff ·· ·· - ff 99 99 ff ·· e5 b1 e5 ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ff ·· ·· ff 99 99 ff ·· ·· - ff 99 99 ff ·· 72 e2 72 ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 99 99 d3 6d ·· ·· ·· 6d d3 99 99 ff ·· ·· + ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff 99 99 a7 e4 ·· ·· ·· e4 a7 99 99 ff ·· ·· + ff 99 99 d3 6d ·· ·· ·· 6d d3 99 99 ff ·· ·· ff 99 99 99 e2 49 ·· 49 e2 99 99 99 ff ·· ·· + ff 99 99 a7 e4 ·· ·· ·· e4 a7 99 99 ff ·· ·· ff 99 99 99 b6 bc ·· bc b6 99 99 99 ff ·· ·· + ff 99 99 99 e2 49 ·· 49 e2 99 99 99 ff ·· ·· ff 99 99 99 99 ea 43 ea 99 99 99 99 ff ·· ·· + ff 99 99 99 b6 bc ·· bc b6 99 99 99 ff ·· ·· ff 99 99 99 99 c5 d9 c5 99 99 99 99 ff ·· ·· + ff 99 99 99 99 ea 43 ea 99 99 99 99 ff ·· ·· ff 99 99 ff 99 99 ff 99 99 ff 99 99 ff ·· ·· + ff 99 99 99 99 c5 d9 c5 99 99 99 99 ff ·· ·· ff 99 99 ff c5 99 99 99 c5 ff 99 99 ff ·· ·· + ff 99 99 ff 99 99 ff 99 99 ff 99 99 ff ·· ·· ff 99 99 ff ea 99 99 99 ea ff 99 99 ff ·· ·· + ff 99 99 ff c5 99 99 99 c5 ff 99 99 ff ·· ·· ff 99 99 ff bc b6 99 b6 bc ff 99 99 ff ·· ·· + ff 99 99 ff ea 99 99 99 ea ff 99 99 ff ·· ·· ff 99 99 ff 49 e2 99 e2 49 ff 99 99 ff ·· ·· + ff 99 99 ff bc b6 99 b6 bc ff 99 99 ff ·· ·· ff 99 99 ff ·· e4 b3 e4 ·· ff 99 99 ff ·· ·· + ff 99 99 ff 49 e2 99 e2 49 ff 99 99 ff ·· ·· ff 99 99 ff ·· 6d e8 6d ·· ff 99 99 ff ·· ·· + ff 99 99 ff ·· e4 b3 e4 ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ff ·· ·· ff 99 99 ff ·· ·· + ff 99 99 ff ·· 6d e8 6d ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ff ·· ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -77,26 +77,26 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e5 cb cb e5 ·· ·· ·· ·· ·· e5 cb cb e5 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· cb 99 99 98 6d ·· ·· ·· 6d 98 99 99 cb ·· ·· - e5 cb cb e5 ·· ·· ·· ·· ·· e5 cb cb e5 ·· ·· cb 99 99 99 c0 ·· ·· ·· c0 99 99 99 cb ·· ·· - cb 99 99 98 6d ·· ·· ·· 6d 98 99 99 cb ·· ·· cb 99 99 99 9c 47 ·· 47 9c 99 99 99 cb ·· ·· - cb 99 99 99 c0 ·· ·· ·· c0 99 99 99 cb ·· ·· cb 99 99 99 99 af ·· af 99 99 99 99 cb ·· ·· - cb 99 99 99 9c 47 ·· 47 9c 99 99 99 cb ·· ·· cb 99 99 99 99 b2 3d b2 99 99 99 99 cb ·· ·· - cb 99 99 99 99 af ·· af 99 99 99 99 cb ·· ·· cb 99 99 99 99 99 be 99 99 99 99 99 cb ·· ·· - cb 99 99 99 99 b2 3d b2 99 99 99 99 cb ·· ·· cb 99 99 99 99 99 99 99 99 99 99 99 cb ·· ·· - cb 99 99 99 99 99 be 99 99 99 99 99 cb ·· ·· cb 99 99 be 99 99 99 99 99 be 99 99 cb ·· ·· - cb 99 99 99 99 99 99 99 99 99 99 99 cb ·· ·· cb 99 99 cb b0 99 99 99 b0 cb 99 99 cb ·· ·· - cb 99 99 be 99 99 99 99 99 be 99 99 cb ·· ·· cb 99 99 cb b3 99 99 99 b3 cb 99 99 cb ·· ·· - cb 99 99 cb b0 99 99 99 b0 cb 99 99 cb ·· ·· cb 99 99 cb 4c 9c 99 9c 4c cb 99 99 cb ·· ·· - cb 99 99 cb b3 99 99 99 b3 cb 99 99 cb ·· ·· cb 99 99 cb ·· c3 99 c3 ·· cb 99 99 cb ·· ·· - cb 99 99 cb 4c 9c 99 9c 4c cb 99 99 cb ·· ·· cb 99 99 cb ·· 72 a5 72 ·· cb 99 99 cb ·· ·· - cb 99 99 cb ·· c3 99 c3 ·· cb 99 99 cb ·· ·· cb 99 99 cb ·· ·· ff ·· ·· cb 99 99 cb ·· ·· - cb 99 99 cb ·· 72 a5 72 ·· cb 99 99 cb ·· ·· cb 99 99 cb ·· ·· ·· ·· ·· cb 99 99 cb ·· ·· - cb 99 99 cb ·· ·· ff ·· ·· cb 99 99 cb ·· ·· cb 99 99 cb ·· ·· ·· ·· ·· cb 99 99 cb ·· ·· - cb 99 99 cb ·· ·· ·· ·· ·· cb 99 99 cb ·· ·· e5 cb cb e5 ·· ·· ·· ·· ·· e5 cb cb e5 ·· ·· - cb 99 99 cb ·· ·· ·· ·· ·· cb 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - e5 cb cb e5 ·· ·· ·· ·· ·· e5 cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e2 c5 c5 e2 ·· ·· ·· ·· ·· e2 c5 c5 e2 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c5 99 99 99 6d ·· ·· ·· 6d 99 99 99 c5 ·· ·· + e2 c5 c5 e2 ·· ·· ·· ·· ·· e2 c5 c5 e2 ·· ·· c5 99 99 99 be ·· ·· ·· be 99 99 99 c5 ·· ·· + c5 99 99 99 6d ·· ·· ·· 6d 99 99 99 c5 ·· ·· c5 99 99 99 99 49 ·· 49 99 99 99 99 c5 ·· ·· + c5 99 99 99 be ·· ·· ·· be 99 99 99 c5 ·· ·· c5 99 99 99 99 b2 ·· b2 99 99 99 99 c5 ·· ·· + c5 99 99 99 99 49 ·· 49 99 99 99 99 c5 ·· ·· c5 99 99 99 99 ac 43 ac 99 99 99 99 c5 ·· ·· + c5 99 99 99 99 b2 ·· b2 99 99 99 99 c5 ·· ·· c5 99 99 99 99 99 c0 99 99 99 99 99 c5 ·· ·· + c5 99 99 99 99 ac 43 ac 99 99 99 99 c5 ·· ·· c5 99 99 99 99 99 99 99 99 99 99 99 c5 ·· ·· + c5 99 99 99 99 99 c0 99 99 99 99 99 c5 ·· ·· c5 99 99 b6 99 99 99 99 99 b6 99 99 c5 ·· ·· + c5 99 99 99 99 99 99 99 99 99 99 99 c5 ·· ·· c5 99 99 c5 ac 99 99 99 ac c5 99 99 c5 ·· ·· + c5 99 99 b6 99 99 99 99 99 b6 99 99 c5 ·· ·· c5 99 99 c5 b2 99 99 99 b2 c5 99 99 c5 ·· ·· + c5 99 99 c5 ac 99 99 99 ac c5 99 99 c5 ·· ·· c5 99 99 c5 49 99 99 99 49 c5 99 99 c5 ·· ·· + c5 99 99 c5 b2 99 99 99 b2 c5 99 99 c5 ·· ·· c5 99 99 c5 ·· be 99 be ·· c5 99 99 c5 ·· ·· + c5 99 99 c5 49 99 99 99 49 c5 99 99 c5 ·· ·· c5 99 99 c5 ·· 6d a0 6d ·· c5 99 99 c5 ·· ·· + c5 99 99 c5 ·· be 99 be ·· c5 99 99 c5 ·· ·· c5 99 99 c5 ·· ·· ff ·· ·· c5 99 99 c5 ·· ·· + c5 99 99 c5 ·· 6d a0 6d ·· c5 99 99 c5 ·· ·· c5 99 99 c5 ·· ·· ·· ·· ·· c5 99 99 c5 ·· ·· + c5 99 99 c5 ·· ·· ff ·· ·· c5 99 99 c5 ·· ·· c5 99 99 c5 ·· ·· ·· ·· ·· c5 99 99 c5 ·· ·· + c5 99 99 c5 ·· ·· ·· ·· ·· c5 99 99 c5 ·· ·· e2 c5 c5 e2 ·· ·· ·· ·· ·· e2 c5 c5 e2 ·· ·· + c5 99 99 c5 ·· ·· ·· ·· ·· c5 99 99 c5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + e2 c5 c5 e2 ·· ·· ·· ·· ·· e2 c5 c5 e2 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -105,10 +105,10 @@ ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 6d fe ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - d9 fe ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - b8 ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 4c ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 6d ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + db ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + b6 ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 49 ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -128,19 +128,19 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 9f ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 20 ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - bf ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 9f ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 9f 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 20 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 40 80 80 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· b6 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 24 ff ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + db ff ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff b6 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + b6 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 24 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 49 92 92 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -154,17 +154,17 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· b8 47 - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· 4c b3 - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· de - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· 72 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· b6 49 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· 49 b6 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· db + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff 6d ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff d9 ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff b8 47 - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff 4c b3 - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· de - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· 72 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff db ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff b6 49 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff 49 b6 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· db + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· @@ -180,21 +180,21 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 9f ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff 20 ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff 9f ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff 9f - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 9f ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 9f ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 20 ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· df - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· 60 - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 80 80 40 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff b6 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff 24 ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff b6 ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff b6 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff b6 ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff 92 b6 ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff 92 24 ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff 92 ·· ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff 92 ·· 6d + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff 92 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff 92 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff 92 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff 92 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 49 92 92 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -223,14 +223,14 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· 92 6d ·· ·· ·· 6d 92 - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· 26 d9 ·· ·· ·· d9 26 - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· b8 47 ·· 47 b8 ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· 4c b3 ·· b3 4c ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· de 3d de ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· 72 cc 72 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· 24 db ·· ·· ·· db 24 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· b6 49 ·· 49 b6 ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· 49 b6 ·· b6 49 ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· db 43 db ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· 6d d0 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff 6d ·· ·· ·· 6d fe - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff d9 ·· ·· ·· d9 fe + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff 6d ·· ·· ·· 6d ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff db ·· ·· ·· db ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -247,16 +247,16 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 80 80 40 ·· ·· ·· ·· ·· 40 - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff df ·· ·· ·· ·· ·· df - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff 60 ·· ·· ·· 60 ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff df ·· ·· ·· df ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff 20 ·· 20 ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff 9f ·· 9f ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff 40 ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 9f ff ff ff ff ff 9f - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff 80 9f ff ff ff 9f 80 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 49 92 92 49 ·· ·· ·· ·· ·· 49 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ·· ·· ·· ·· ·· ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff 6d ·· ·· ·· 6d ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ·· ·· ·· ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff 24 ·· 24 ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff b6 ·· b6 ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ff 49 ff ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ff ff ff ff ff + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff b6 ff ff ff ff ff b6 + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff 92 b6 ff ff ff b6 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -273,16 +273,16 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 6d ·· ·· ·· 6d 92 ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 26 d9 ·· ·· ·· d9 26 ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· b8 47 ·· 47 b8 ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· 4c b3 ·· b3 4c ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· de 3d de ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 72 cc 72 ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 24 db ·· ·· ·· db 24 ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· b6 49 ·· 49 b6 ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· 49 b6 ·· b6 49 ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· db 43 db ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 6d d0 6d ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff 6d ·· ·· ·· 6d fe ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff d9 ·· ·· ·· d9 fe ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff b8 47 ·· 47 b8 ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff 4c b3 ·· b3 4c ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff 6d ·· ·· ·· 6d ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff db ·· ·· ·· db ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff b6 49 ·· 49 b6 ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ff 49 b6 ·· b6 49 ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -297,40 +297,40 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 80 40 ·· ·· ·· ·· ·· 40 80 80 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff df ·· ·· ·· ·· ·· df ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff 60 ·· ·· ·· 60 ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff df ·· ·· ·· df ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff 20 ·· 20 ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff 9f ·· 9f ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff 40 ff ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 9f ff ff ff ff ff 9f ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 80 9f ff ff ff 9f 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 80 20 ff ff ff 20 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 80 ·· df ff df ·· 80 ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 92 49 ·· ·· ·· ·· ·· 49 92 92 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ·· ·· ·· ·· ·· ff ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff 6d ·· ·· ·· 6d ff ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ·· ·· ·· ff ff ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff 24 ·· 24 ff ff ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff b6 ·· b6 ff ff ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ff 49 ff ff ff ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff ff ff ff ff ff ff ff ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff b6 ff ff ff ff ff b6 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 92 b6 ff ff ff b6 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 92 24 ff ff ff 24 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 92 ·· ff ff ff ·· 92 ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 8f c9 99 99 99 99 99 99 c9 8f ·· ·· ·· ·· ·· ·· ·· ·· 38 f6 38 ·· ·· ·· ·· ·· ·· ·· ·· ·· - 0e f6 99 99 99 99 99 99 f6 0e ·· ·· ·· ·· ·· ·· ·· ·· 70 ea 70 ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 9c c3 99 99 99 99 c3 9c ·· ·· ·· ·· ·· ·· ·· ·· ·· ae d2 ae ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 1c ed 99 99 99 99 ed 1c ·· ·· ·· ·· ·· ·· ·· ·· ·· e6 b0 e6 ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· a9 bd 99 99 bd a9 ·· ·· ·· ·· ff ff ff ff ff fe fe ff ff ff ff ff ff ff ff ·· ·· ·· - ·· ·· 2a e5 99 99 e5 2a ·· ·· ·· ·· 28 e6 99 99 99 b8 b5 ·· b5 b8 99 99 99 e6 28 ·· ·· ·· - ·· ·· ·· b6 b8 b8 b6 ·· ·· ·· ·· ·· ·· 50 d3 99 99 cf 78 ·· 78 cf 99 99 d3 50 ·· ·· ·· ·· - ·· ·· ·· 38 dc dc 38 ·· ·· ·· ·· ·· ·· ·· 82 c6 99 dc 40 ·· 40 dc 99 c6 82 ·· ·· ·· ·· ·· - ·· ·· ·· ·· d1 d2 ·· ·· ·· ·· ·· ·· ·· ·· ·· a6 bf f9 08 ·· 08 f9 bf a6 ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· d0 cf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d7 df ·· ·· ·· de d8 ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 33 df df 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 fa 10 ·· 10 fa 68 ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· b1 ba ba b1 ·· ·· ·· ·· ·· ·· ·· ·· ·· a6 c4 ee ·· ee c4 a6 ·· ·· ·· ·· ·· ·· ·· - ·· ·· 25 e8 99 99 e8 25 ·· ·· ·· ·· ·· ·· ·· ·· e0 a8 b2 f3 b2 a8 e0 ·· ·· ·· ·· ·· ·· ·· - ·· ·· a5 bf 99 99 bf a5 ·· ·· ·· ·· ·· ·· ·· 10 f5 99 cd a5 cd 99 f5 10 ·· ·· ·· ·· ·· ·· - ·· 17 f0 99 99 99 99 f0 17 ·· ·· ·· ·· ·· ·· 48 dc ba 98 ·· 98 ba dc 48 ·· ·· ·· ·· ·· ·· - ·· 98 c5 99 99 99 99 c5 98 ·· ·· ·· ·· ·· ·· 80 d9 c2 ·· ·· ·· c2 d9 80 ·· ·· ·· ·· ·· ·· - 09 f9 99 99 99 99 99 99 f9 09 ·· ·· ·· ·· ·· c9 ec ·· ·· ·· ·· ·· ed c9 ·· ·· ·· ·· ·· ·· - 8b cb 99 99 99 99 99 99 cb 8b ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· + 90 c9 99 99 99 99 99 99 c9 90 ·· ·· ·· ·· ·· ·· ·· ·· 38 f7 38 ·· ·· ·· ·· ·· ·· ·· ·· ·· + 0d f8 99 99 99 99 99 99 f8 0d ·· ·· ·· ·· ·· ·· ·· ·· 71 eb 71 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 9d c4 99 99 99 99 c4 9d ·· ·· ·· ·· ·· ·· ·· ·· ·· b1 d2 b1 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 1a f0 99 99 99 99 f0 1b ·· ·· ·· ·· ·· ·· ·· ·· ·· ea ae ea ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· a9 bf 99 99 bf a9 ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· + ·· ·· 28 e8 99 99 e8 28 ·· ·· ·· ·· 27 e9 99 99 99 bb b1 ·· b1 bb 99 99 99 e9 27 ·· ·· ·· + ·· ·· ·· b6 b9 b9 b6 ·· ·· ·· ·· ·· ·· 4e d9 99 99 d2 71 ·· 71 d2 99 99 d9 4e ·· ·· ·· ·· + ·· ·· ·· 35 e1 e0 36 ·· ·· ·· ·· ·· ·· ·· 82 d0 99 e3 39 ·· 38 e4 99 d0 82 ·· ·· ·· ·· ·· + ·· ·· ·· ·· d2 d2 ·· ·· ·· ·· ·· ·· ·· ·· ·· a5 c0 ff ·· ·· ·· ff c0 a5 ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· d2 d2 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· da db ·· ·· ·· db d9 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 35 e1 e1 35 ·· ·· ·· ·· ·· ·· ·· ·· ·· 71 fa 13 ·· 13 f9 71 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· b5 b9 b9 b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· b1 c0 f2 ·· f2 c0 b1 ·· ·· ·· ·· ·· ·· ·· + ·· ·· 28 e8 99 99 e8 28 ·· ·· ·· ·· ·· ·· ·· ·· ea a4 b1 f6 b1 a4 ea ·· ·· ·· ·· ·· ·· ·· + ·· ·· a9 bf 99 99 bf a9 ·· ·· ·· ·· ·· ·· ·· 1c ef 99 d8 9e d8 99 ef 1c ·· ·· ·· ·· ·· ·· + ·· 1a f0 99 99 99 99 f0 1a ·· ·· ·· ·· ·· ·· 55 dd c8 94 ·· 94 c8 dd 55 ·· ·· ·· ·· ·· ·· + ·· 9c c4 99 99 99 99 c4 9d ·· ·· ·· ·· ·· ·· 8d d8 be ·· ·· ·· be d7 8e ·· ·· ·· ·· ·· ·· + 0d f8 99 99 99 99 99 99 f8 0d ·· ·· ·· ·· ·· d7 e9 ·· ·· ·· ·· ·· e9 d7 ·· ·· ·· ·· ·· ·· + 90 c9 99 99 99 99 99 99 c9 90 ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -338,26 +338,26 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - e5 cb cb cb cb cb cb cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 88 99 99 99 99 99 99 99 99 88 ·· ·· ·· ·· ·· ·· ·· ·· 38 c5 38 ·· ·· ·· ·· ·· ·· ·· ·· ·· - 0e b9 99 99 99 99 99 99 b9 0e ·· ·· ·· ·· ·· ·· ·· ·· 70 99 70 ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 94 99 99 99 99 99 99 94 ·· ·· ·· ·· ·· ·· ·· ·· ·· a5 99 a5 ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 1c b4 99 99 99 99 b4 1c ·· ·· ·· ·· ·· ·· ·· ·· ·· c4 99 c4 ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· a0 99 99 99 99 a0 ·· ·· ·· ·· f1 cb cb cb cb cb d7 cb d8 cb cb cb cb cb f1 ·· ·· ·· - ·· ·· 2a af 99 99 af 2a ·· ·· ·· ·· 28 af 99 99 99 99 ac ·· ac 99 99 99 99 af 28 ·· ·· ·· - ·· ·· ·· ac 99 99 ac ·· ·· ·· ·· ·· ·· 50 9e 99 99 99 78 ·· 78 99 99 99 9e 50 ·· ·· ·· ·· - ·· ·· ·· 38 a9 a9 38 ·· ·· ·· ·· ·· ·· ·· 7c 96 99 a2 40 ·· 40 a2 99 96 7c ·· ·· ·· ·· ·· - ·· ·· ·· ·· c7 c7 ·· ·· ·· ·· ·· ·· ·· ·· ·· 9e 99 c7 08 ·· 08 c7 99 9e ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· c5 c4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c2 c8 ·· ·· ·· c8 c3 ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 33 ab ab 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 c8 10 ·· 10 c8 68 ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· a8 99 99 a8 ·· ·· ·· ·· ·· ·· ·· ·· ·· 9e 99 bf ·· bf 99 9e ·· ·· ·· ·· ·· ·· ·· - ·· ·· 25 b1 99 99 b1 25 ·· ·· ·· ·· ·· ·· ·· ·· bf 99 99 c3 99 99 bf ·· ·· ·· ·· ·· ·· ·· - ·· ·· 9c 99 99 99 99 9c ·· ·· ·· ·· ·· ·· ·· 10 b8 99 98 a5 98 99 b8 10 ·· ·· ·· ·· ·· ·· - ·· 17 b6 99 99 99 99 b6 17 ·· ·· ·· ·· ·· ·· 48 9c 93 90 ·· 90 93 9c 48 ·· ·· ·· ·· ·· ·· - ·· 90 99 99 99 99 99 99 90 ·· ·· ·· ·· ·· ·· 80 99 af ·· ·· ·· af 99 80 ·· ·· ·· ·· ·· ·· - 09 bb 99 99 99 99 99 99 bb 09 ·· ·· ·· ·· ·· b6 c9 ·· ·· ·· ·· ·· c9 b5 ·· ·· ·· ·· ·· ·· - 83 99 99 99 99 99 99 99 99 83 ·· ·· ·· ·· ·· f1 ·· ·· ·· ·· ·· ·· ·· f1 ·· ·· ·· ·· ·· ·· - e5 cb cb cb cb cb cb cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + e2 c5 c5 c5 c5 c5 c5 c5 c5 e2 ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 89 99 99 99 99 99 99 99 99 89 ·· ·· ·· ·· ·· ·· ·· ·· 38 c0 38 ·· ·· ·· ·· ·· ·· ·· ·· ·· + 0d b2 99 99 99 99 99 99 b2 0d ·· ·· ·· ·· ·· ·· ·· ·· 71 99 71 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 95 99 99 99 99 99 99 95 ·· ·· ·· ·· ·· ·· ·· ·· ·· a8 99 a8 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 1a af 99 99 99 99 ae 1b ·· ·· ·· ·· ·· ·· ·· ·· ·· c3 99 c3 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· a0 99 99 99 99 a0 ·· ·· ·· ·· f1 c5 c5 c5 c5 c5 d3 c5 d3 c5 c5 c5 c5 c5 f1 ·· ·· ·· + ·· ·· 28 ab 99 99 ab 28 ·· ·· ·· ·· 27 ab 99 99 99 99 a8 ·· a8 99 99 99 99 ab 27 ·· ·· ·· + ·· ·· ·· ac 99 99 ac ·· ·· ·· ·· ·· ·· 4e 9c 99 99 99 71 ·· 71 99 99 99 9c 4e ·· ·· ·· ·· + ·· ·· ·· 35 a7 a7 36 ·· ·· ·· ·· ·· ·· ·· 7b 99 99 9f 39 ·· 38 9f 99 99 7b ·· ·· ·· ·· ·· + ·· ·· ·· ·· c7 c7 ·· ·· ·· ·· ·· ·· ·· ·· ·· 9d 99 c5 ·· ·· ·· c5 99 9d ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· c7 c7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c2 c3 ·· ·· ·· c3 c2 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 35 a7 a7 35 ·· ·· ·· ·· ·· ·· ·· ·· ·· 71 c2 13 ·· 13 c2 71 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ab 99 99 ac ·· ·· ·· ·· ·· ·· ·· ·· ·· a8 99 bc ·· bc 99 a8 ·· ·· ·· ·· ·· ·· ·· + ·· ·· 28 ab 99 99 ab 28 ·· ·· ·· ·· ·· ·· ·· ·· c3 99 99 bf 99 99 c3 ·· ·· ·· ·· ·· ·· ·· + ·· ·· a0 99 99 99 99 a0 ·· ·· ·· ·· ·· ·· ·· 1c ae 99 99 9e 99 99 ae 1c ·· ·· ·· ·· ·· ·· + ·· 1a af 99 99 99 99 af 1a ·· ·· ·· ·· ·· ·· 55 99 99 8d ·· 8d 99 99 55 ·· ·· ·· ·· ·· ·· + ·· 94 99 99 99 99 99 99 95 ·· ·· ·· ·· ·· ·· 8d 99 aa ·· ·· ·· aa 99 8e ·· ·· ·· ·· ·· ·· + 0d b2 99 99 99 99 99 99 b2 0d ·· ·· ·· ·· ·· bf c2 ·· ·· ·· ·· ·· c2 bf ·· ·· ·· ·· ·· ·· + 89 99 99 99 99 99 99 99 99 89 ·· ·· ·· ·· ·· f1 ·· ·· ·· ·· ·· ·· ·· f1 ·· ·· ·· ·· ·· ·· + e2 c5 c5 c5 c5 c5 c5 c5 c5 e2 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -414,17 +414,17 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - e5 cb cb cb cb cb cb cb cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - cb 99 99 99 99 99 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - cb 99 99 99 99 99 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - cb 99 99 99 99 99 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - cb 99 99 99 99 99 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - e5 cb cb cb cb b2 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· cb 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· cb 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· cb 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· cb 99 99 99 99 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· e5 cb cb cb cb e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + e2 c5 c5 c5 c5 c5 c5 c5 c5 c5 e2 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + c5 99 99 99 99 99 99 99 99 99 c5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + c5 99 99 99 99 99 99 99 99 99 c5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + c5 99 99 99 99 99 99 99 99 99 c5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + c5 99 99 99 99 99 99 99 99 99 c5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + e2 c5 c5 c5 c5 a7 99 99 99 99 c5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· c5 99 99 99 99 c5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· c5 99 99 99 99 c5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· c5 99 99 99 99 c5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· c5 99 99 99 99 c5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· e2 c5 c5 c5 c5 e2 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -466,163 +466,163 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 24 89 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 58 bd db ba f2 8b ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 26 8b f1 a7 42 ·· d3 a7 e7 18 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5a bf d9 74 0e ·· ·· ·· fa 3a 5c a3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 28 8d f3 a5 40 ·· ·· ·· ·· ·· 50 f7 ·· ·· cf 30 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5c c1 d7 72 0c ·· ·· ·· ·· ·· ·· ·· b6 c7 ·· ·· 44 bb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 2a 8f f5 a3 3e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ef 6d ·· ·· ·· b7 48 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5e c3 d5 70 0a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 19 fd ·· ·· ·· ·· 2c d3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 2c 91 f7 a1 3c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 91 e0 ·· ·· ·· ·· ·· 9f 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 c5 d3 6e 08 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· dd 97 ·· ·· ·· ·· ·· 14 eb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 2e 93 f9 9f 3a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fd 23 ·· ·· ·· ·· ·· ·· 87 78 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 62 c7 d1 6c 06 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 65 f2 ·· ·· ·· ·· ·· ·· ·· ·· fb 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· 30 95 fb 9d 38 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c2 bb ·· ·· ·· ·· ·· ·· ·· ·· 70 8f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· 64 c9 cf 6a 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f5 58 ·· ·· ·· ·· ·· ·· ·· ·· ·· e3 1c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 32 97 fd 9b 36 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 31 fb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 58 a7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 66 cb cd 68 02 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a1 d7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· cb 34 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 99 34 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e5 86 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 bf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 9d 62 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fe 0a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b3 4c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 3c c3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 78 eb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 28 d7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· d9 26 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ce ad ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9b 64 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 78 87 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f9 43 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 10 ef ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 16 e9 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 48 f8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 83 7c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· b3 4c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b0 cc ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f7 08 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· 52 ad ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ed 74 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 6c 93 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ef 10 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 10 fe ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 8d 72 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 8a e3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 54 ab ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 2c d3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d9 9e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c7 38 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· c9 36 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fc 2c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3c c3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· 68 97 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5d f4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· af 50 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· 06 f9 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· be bf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 24 db ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· a3 5c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f3 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 97 68 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· 42 bd ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 28 fc ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 0c f3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· df 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9b da ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· 7e 81 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e2 8d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f3 0c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· 1c e3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fe 14 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 97 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· b9 46 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 71 ee ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· db 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· 58 a7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ca b3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 50 af ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· f5 0a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f8 4b ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c3 3c ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· 93 6c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3f fa ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 38 c7 ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· 32 cd ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ab d0 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ab 54 ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· cf 30 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ea 7b ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3a ff ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· 6e 91 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 06 fe ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 8f c5 ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· 0c f3 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 84 e7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3c e5 70 ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a9 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d5 a4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 91 c3 1a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 48 b7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fb 35 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3e e7 6e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e5 1a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 55 f6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 93 c1 18 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 83 7c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b9 c4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 e9 6c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 22 dd ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f1 68 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 95 bf 16 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· bf 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 1f fd ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 42 eb 6a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5e a1 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 95 de ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 97 bd 14 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fb 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df 94 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 44 ed 68 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 66 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fd 1d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 bb 12 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 38 c7 ·· ·· ·· ·· ·· ·· ·· ·· ·· 69 f0 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 46 ef 66 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d5 2a ·· ·· ·· ·· ·· ·· ·· ·· c5 b8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9b b9 10 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 74 8b ·· ·· ·· ·· ·· ·· ·· ·· f6 53 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 48 f1 64 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 12 ed ·· ·· ·· ·· ·· ·· ·· 36 fb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9d b7 0e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· af 50 ·· ·· ·· ·· ·· ·· a5 d4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4a f3 62 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4e b1 ·· ·· ·· ·· ·· ·· e7 82 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f b5 0c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· eb 14 ·· ·· ·· ·· ·· fe 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· 4c f5 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 89 76 ·· ·· ·· ·· 7d e9 ·· ·· ·· ·· ·· ·· ·· ·· ·· a1 b3 0a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 28 d7 ·· ·· ·· ·· d1 aa ·· ·· ·· ·· ·· ·· ·· 4e f7 5e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c5 3a ·· ·· ·· fa 3d ·· ·· ·· ·· ·· ·· a3 b1 08 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 64 9b ·· ·· 4d f7 ·· ·· ·· ·· ·· 50 f9 5c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 02 fd ·· ·· b4 c9 ·· ·· ·· ·· a5 af 06 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f 60 ·· ee 70 ·· ·· 52 fb 5a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3e c1 15 fe ·· ·· a7 ad 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· db 9d e1 54 fd 58 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 7a ed dc ab 02 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 32 98 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 65 cb cd b3 f3 8c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 32 98 ff 9a 34 ·· d4 a7 e6 19 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 65 cb cd 67 ·· ·· ·· ·· fb 38 59 a6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 32 98 ff 9a 34 ·· ·· ·· ·· ·· 50 f7 ·· ·· cc 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 65 cc cd 67 ·· ·· ·· ·· ·· ·· ·· ·· b8 c7 ·· ·· 40 bf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 32 98 ff 9a 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f1 6a ·· ·· ·· b3 4c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 65 cc cd 67 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 1b fe ·· ·· ·· ·· 26 d9 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 32 99 ff 9a 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 93 e0 ·· ·· ·· ·· ·· 99 66 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 65 cc cd 66 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df 94 ·· ·· ·· ·· ·· 0d f2 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 32 99 ff 9a 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fe 1d ·· ·· ·· ·· ·· ·· 80 7f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 66 cc cd 66 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 68 f1 ·· ·· ·· ·· ·· ·· ·· ·· f3 0c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· 32 99 ff 99 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c6 b9 ·· ·· ·· ·· ·· ·· ·· ·· 66 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· 66 cc cd 66 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f7 52 ·· ·· ·· ·· ·· ·· ·· ·· ·· da 25 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 33 99 ff 99 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 37 fc ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4d b2 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 66 cc cc 66 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a6 d5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c0 3f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ff 99 33 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e8 81 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 33 cc ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 9d 62 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a7 58 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 3b c4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 7f ea ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 1a e5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· d8 27 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d3 a8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 8d 72 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 76 89 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fb 3a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 13 ec ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 50 f7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 74 8b ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· b1 4e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b6 c7 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e7 18 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· 4e b1 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f0 6b ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 5a a5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ec 13 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 19 fe ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· cd 32 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 8a 75 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 93 e0 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 41 be ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 27 d8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df 96 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b4 4b ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· c5 3a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fe 1f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 27 d8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· 62 9d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 67 f2 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9a 65 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c6 b9 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 0e f1 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· 9d 62 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f7 54 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 81 7e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· 3b c4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 35 fc ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f4 0b ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· d8 27 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a4 d5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 67 98 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· 76 89 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e8 81 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· db 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· 14 eb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 02 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4e b1 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· b1 4e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 7d ea ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c1 3e ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· 4f b0 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d2 a9 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 34 cb ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ec 13 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fb 3a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a8 57 ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· 8a 75 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4f f8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 1b e4 ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· 27 d8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b5 c8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 8e 71 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· c5 3a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f0 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 53 ff ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· 63 9c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 19 fe ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a8 ac ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 e1 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 53 fe 57 ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9e 61 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· de 97 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a9 ac 01 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3b c4 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· fe 21 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 53 fe 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d9 26 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 67 f2 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a9 ac 01 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 76 89 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c5 ba ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 53 fe 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 14 eb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· f6 55 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a9 ac 01 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b1 4e ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 33 fc ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 54 fe 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4f b0 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a4 d5 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a9 ab 01 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ed 12 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e8 83 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 54 fe 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 8a 75 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 04 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a9 ab 01 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 28 d7 ·· ·· ·· ·· ·· ·· ·· ·· ·· 7c eb ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 54 fe 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c5 3a ·· ·· ·· ·· ·· ·· ·· ·· d2 a9 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a9 ab 01 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 63 9c ·· ·· ·· ·· ·· ·· ·· ·· fb 3c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 54 ff 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· 4d f8 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a9 ab ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9e 61 ·· ·· ·· ·· ·· ·· b4 c9 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 54 ff 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 3c c3 ·· ·· ·· ·· ·· ·· f0 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· a9 ab ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· d9 26 ·· ·· ·· ·· 17 fe ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 54 ff 56 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 77 88 ·· ·· ·· ·· 90 e1 ·· ·· ·· ·· ·· ·· ·· ·· ·· aa ab ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 14 eb ·· ·· ·· ·· dd 98 ·· ·· ·· ·· ·· ·· ·· 54 ff 55 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b2 4d ·· ·· ·· fe 21 ·· ·· ·· ·· ·· ·· aa ab ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 4f b0 ·· ·· 65 f2 ·· ·· ·· ·· ·· 54 ff 55 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ed 12 ·· c4 bb ·· ·· ·· ·· aa ab ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 8a 75 ·· f6 57 ·· ·· 55 ff 55 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 28 d7 33 fc ·· ·· aa aa ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c6 b8 d6 55 ff 55 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 63 f6 d6 aa ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 55 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 58 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ce 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff e3 ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff c3 ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff c2 ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff ff ff ff ff cf ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff df 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· 80 df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· 40 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff bf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 40 df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff bf ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 80 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· 40 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· 80 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff bf ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 60 ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 40 ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff bf ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff ff ff ff ff ff ff ce ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff ff e3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ff ff ff ff ff c3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 80 ff ff ff ff ff ff ff ff c2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· bf ff ff ff ff ff ff cf ff ff ff ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff bf ff ff ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff e2 ff ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff ff ff ff e3 ff ff ff ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff ff ff c3 ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· df ff ff ff ce ff ff ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff ff ff ff ff ff ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 20 ff ff c3 ff ff ff ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 9f ff c2 ff ff df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 60 ff e2 ff 9f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· c7 df 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 40 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 63 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 49 b6 ff e5 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 49 b6 ff ff ff ff ff d5 ff ff 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ff ff ff ff d5 ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 49 b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 49 b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 49 b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 49 b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· 49 b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· 92 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· 49 b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff db ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 49 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff ff ff ff ff ff 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 24 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· db ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· 6d ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· 24 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 6d ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· 24 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· 49 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· 92 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· 24 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· db ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· 6d ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 24 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· 24 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff db ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 6d ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· 6d ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· 24 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 6d ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· 49 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 24 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· db ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 6d ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b6 ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 24 ff ff ff ff ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 6d ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 24 ff ff ff ff ff ff ff ff ff ff ff d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b6 ff ff ff ff ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 6d ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 ff ff ff ff ff ff ff ff d5 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 24 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· db ff ff ff ff ff ff e5 ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 6d ff ff ff ff ff ff d0 ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b6 ff ff ff ff ff ff ff ff ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 24 ff ff ff ff d5 ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff e5 ff ff ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 6d ff ff ff ff ff ff ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 24 ff ff d5 ff ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· b6 ff d5 ff ff ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 6d ff ff ff b6 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· e0 ff 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 49 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 7f 7f 7f 7f ·· ·· ·· ·· ·· 7f 7f 7f 7f ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 7f ·· ·· 48 36 ·· ·· ·· 36 48 ·· ·· 7f ·· ·· - 7f 7f 7f 7f ·· ·· ·· ·· ·· 7f 7f 7f 7f ·· ·· 7f ·· ·· 12 6c ·· ·· ·· 6c 12 ·· ·· 7f ·· ·· - 7f ·· ·· 48 36 ·· ·· ·· 36 48 ·· ·· 7f ·· ·· 7f ·· ·· ·· 5b 23 ·· 23 5b ·· ·· ·· 7f ·· ·· - 7f ·· ·· 12 6c ·· ·· ·· 6c 12 ·· ·· 7f ·· ·· 7f ·· ·· ·· 25 59 ·· 59 25 ·· ·· ·· 7f ·· ·· - 7f ·· ·· ·· 5b 23 ·· 23 5b ·· ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· 6e 1f 6e ·· ·· ·· ·· 7f ·· ·· - 7f ·· ·· ·· 25 59 ·· 59 25 ·· ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· 38 78 38 ·· ·· ·· ·· 7f ·· ·· - 7f ·· ·· ·· ·· 6e 1f 6e ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· - 7f ·· ·· ·· ·· 38 78 38 ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· a3 36 ·· ·· ·· 36 a3 ·· ·· 7f ·· ·· - 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 88 6c ·· ·· ·· 6c 88 ·· ·· 7f ·· ·· - 7f ·· ·· a3 36 ·· ·· ·· 36 a3 ·· ·· 7f ·· ·· 7f ·· ·· 7f 5b 23 ·· 23 5b 7f ·· ·· 7f ·· ·· - 7f ·· ·· 88 6c ·· ·· ·· 6c 88 ·· ·· 7f ·· ·· 7f ·· ·· 7f 25 59 ·· 59 25 7f ·· ·· 7f ·· ·· - 7f ·· ·· 7f 5b 23 ·· 23 5b 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· 6e 1f 6e ·· 7f ·· ·· 7f ·· ·· - 7f ·· ·· 7f 25 59 ·· 59 25 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· 38 78 38 ·· 7f ·· ·· 7f ·· ·· - 7f ·· ·· 7f ·· 6e 1f 6e ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· - 7f ·· ·· 7f ·· 38 78 38 ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 7f ·· ·· 49 36 ·· ·· ·· 36 49 ·· ·· 7f ·· ·· + 7f 7f 7f 7f ·· ·· ·· ·· ·· 7f 7f 7f 7f ·· ·· 7f ·· ·· 12 6d ·· ·· ·· 6d 12 ·· ·· 7f ·· ·· + 7f ·· ·· 49 36 ·· ·· ·· 36 49 ·· ·· 7f ·· ·· 7f ·· ·· ·· 5b 24 ·· 24 5b ·· ·· ·· 7f ·· ·· + 7f ·· ·· 12 6d ·· ·· ·· 6d 12 ·· ·· 7f ·· ·· 7f ·· ·· ·· 24 5b ·· 5b 24 ·· ·· ·· 7f ·· ·· + 7f ·· ·· ·· 5b 24 ·· 24 5b ·· ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· 6d 23 6d ·· ·· ·· ·· 7f ·· ·· + 7f ·· ·· ·· 24 5b ·· 5b 24 ·· ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· 36 7d 36 ·· ·· ·· ·· 7f ·· ·· + 7f ·· ·· ·· ·· 6d 23 6d ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· + 7f ·· ·· ·· ·· 36 7d 36 ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· a4 36 ·· ·· ·· 36 a4 ·· ·· 7f ·· ·· + 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 88 6d ·· ·· ·· 6d 88 ·· ·· 7f ·· ·· + 7f ·· ·· a4 36 ·· ·· ·· 36 a4 ·· ·· 7f ·· ·· 7f ·· ·· 7f 5b 24 ·· 24 5b 7f ·· ·· 7f ·· ·· + 7f ·· ·· 88 6d ·· ·· ·· 6d 88 ·· ·· 7f ·· ·· 7f ·· ·· 7f 24 5b ·· 5b 24 7f ·· ·· 7f ·· ·· + 7f ·· ·· 7f 5b 24 ·· 24 5b 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· 6d 23 6d ·· 7f ·· ·· 7f ·· ·· + 7f ·· ·· 7f 24 5b ·· 5b 24 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· 36 7d 36 ·· 7f ·· ·· 7f ·· ·· + 7f ·· ·· 7f ·· 6d 23 6d ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· + 7f ·· ·· 7f ·· 36 7d 36 ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· 7f 7f 7f 7f ·· ·· ·· ·· ·· 7f 7f 7f 7f ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· ·· 7f ·· ·· 7f ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -633,78 +633,78 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 13 26 26 13 ·· ·· ·· ·· ·· 13 26 26 13 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 26 4c 4c 42 ·· ·· ·· ·· ·· 42 4c 4c 26 ·· ·· - 13 26 26 13 ·· ·· ·· ·· ·· 13 26 26 13 ·· ·· 26 4c 4c 4c 1c ·· ·· ·· 1c 4c 4c 4c 26 ·· ·· - 26 4c 4c 42 ·· ·· ·· ·· ·· 42 4c 4c 26 ·· ·· 26 4c 4c 4c 42 ·· ·· ·· 42 4c 4c 4c 26 ·· ·· - 26 4c 4c 4c 1c ·· ·· ·· 1c 4c 4c 4c 26 ·· ·· 26 4c 4c 4c 4c 09 ·· 09 4c 4c 4c 4c 26 ·· ·· - 26 4c 4c 4c 42 ·· ·· ·· 42 4c 4c 4c 26 ·· ·· 26 4c 4c 4c 4c 2f ·· 2f 4c 4c 4c 4c 26 ·· ·· - 26 4c 4c 4c 4c 09 ·· 09 4c 4c 4c 4c 26 ·· ·· 26 4c 4c 4c 4c 4c 13 4c 4c 4c 4c 4c 26 ·· ·· - 26 4c 4c 4c 4c 2f ·· 2f 4c 4c 4c 4c 26 ·· ·· 26 4c 4c 4c 4c 4c 4c 4c 4c 4c 4c 4c 26 ·· ·· - 26 4c 4c 4c 4c 4c 13 4c 4c 4c 4c 4c 26 ·· ·· 26 4c 4c 2f 4c 4c 4c 4c 4c 2f 4c 4c 26 ·· ·· - 26 4c 4c 4c 4c 4c 4c 4c 4c 4c 4c 4c 26 ·· ·· 26 4c 4c 26 2f 4c 4c 4c 2f 26 4c 4c 26 ·· ·· - 26 4c 4c 2f 4c 4c 4c 4c 4c 2f 4c 4c 26 ·· ·· 26 4c 4c 26 09 4c 4c 4c 09 26 4c 4c 26 ·· ·· - 26 4c 4c 26 2f 4c 4c 4c 2f 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· 42 4c 42 ·· 26 4c 4c 26 ·· ·· - 26 4c 4c 26 09 4c 4c 4c 09 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· 1c 4c 1c ·· 26 4c 4c 26 ·· ·· - 26 4c 4c 26 ·· 42 4c 42 ·· 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· ·· 39 ·· ·· 26 4c 4c 26 ·· ·· - 26 4c 4c 26 ·· 1c 4c 1c ·· 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· - 26 4c 4c 26 ·· ·· 39 ·· ·· 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· - 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· - 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· 13 26 26 13 ·· ·· ·· ·· ·· 13 26 26 13 ·· ·· - 26 4c 4c 26 ·· ·· ·· ·· ·· 26 4c 4c 26 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 13 26 26 13 ·· ·· ·· ·· ·· 13 26 26 13 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 16 2c 2c 16 ·· ·· ·· ·· ·· 16 2c 2c 16 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 2c 57 57 4c ·· ·· ·· ·· ·· 4c 57 57 2c ·· ·· + 16 2c 2c 16 ·· ·· ·· ·· ·· 16 2c 2c 16 ·· ·· 2c 57 57 57 20 ·· ·· ·· 20 57 57 57 2c ·· ·· + 2c 57 57 4c ·· ·· ·· ·· ·· 4c 57 57 2c ·· ·· 2c 57 57 57 4c ·· ·· ·· 4c 57 57 57 2c ·· ·· + 2c 57 57 57 20 ·· ·· ·· 20 57 57 57 2c ·· ·· 2c 57 57 57 57 0b ·· 0b 57 57 57 57 2c ·· ·· + 2c 57 57 57 4c ·· ·· ·· 4c 57 57 57 2c ·· ·· 2c 57 57 57 57 37 ·· 37 57 57 57 57 2c ·· ·· + 2c 57 57 57 57 0b ·· 0b 57 57 57 57 2c ·· ·· 2c 57 57 57 57 57 16 57 57 57 57 57 2c ·· ·· + 2c 57 57 57 57 37 ·· 37 57 57 57 57 2c ·· ·· 2c 57 57 57 57 57 57 57 57 57 57 57 2c ·· ·· + 2c 57 57 57 57 57 16 57 57 57 57 57 2c ·· ·· 2c 57 57 37 57 57 57 57 57 37 57 57 2c ·· ·· + 2c 57 57 57 57 57 57 57 57 57 57 57 2c ·· ·· 2c 57 57 2c 37 57 57 57 37 2c 57 57 2c ·· ·· + 2c 57 57 37 57 57 57 57 57 37 57 57 2c ·· ·· 2c 57 57 2c 0b 57 57 57 0b 2c 57 57 2c ·· ·· + 2c 57 57 2c 37 57 57 57 37 2c 57 57 2c ·· ·· 2c 57 57 2c ·· 4c 57 4c ·· 2c 57 57 2c ·· ·· + 2c 57 57 2c 0b 57 57 57 0b 2c 57 57 2c ·· ·· 2c 57 57 2c ·· 20 57 20 ·· 2c 57 57 2c ·· ·· + 2c 57 57 2c ·· 4c 57 4c ·· 2c 57 57 2c ·· ·· 2c 57 57 2c ·· ·· 41 ·· ·· 2c 57 57 2c ·· ·· + 2c 57 57 2c ·· 20 57 20 ·· 2c 57 57 2c ·· ·· 2c 57 57 2c ·· ·· ·· ·· ·· 2c 57 57 2c ·· ·· + 2c 57 57 2c ·· ·· 41 ·· ·· 2c 57 57 2c ·· ·· 2c 57 57 2c ·· ·· ·· ·· ·· 2c 57 57 2c ·· ·· + 2c 57 57 2c ·· ·· ·· ·· ·· 2c 57 57 2c ·· ·· 2c 57 57 2c ·· ·· ·· ·· ·· 2c 57 57 2c ·· ·· + 2c 57 57 2c ·· ·· ·· ·· ·· 2c 57 57 2c ·· ·· 16 2c 2c 16 ·· ·· ·· ·· ·· 16 2c 2c 16 ·· ·· + 2c 57 57 2c ·· ·· ·· ·· ·· 2c 57 57 2c ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 16 2c 2c 16 ·· ·· ·· ·· ·· 16 2c 2c 16 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 88 92 92 88 ·· ·· ·· ·· ·· 88 92 92 88 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 92 4c 4c 77 36 ·· ·· ·· 36 77 4c 4c 92 ·· ·· - 88 92 92 88 ·· ·· ·· ·· ·· 88 92 92 88 ·· ·· 92 4c 4c 58 7c ·· ·· ·· 7c 58 4c 4c 92 ·· ·· - 92 4c 4c 77 36 ·· ·· ·· 36 77 4c 4c 92 ·· ·· 92 4c 4c 4c 85 23 ·· 23 85 4c 4c 4c 92 ·· ·· - 92 4c 4c 58 7c ·· ·· ·· 7c 58 4c 4c 92 ·· ·· 92 4c 4c 4c 66 5e ·· 5e 66 4c 4c 4c 92 ·· ·· - 92 4c 4c 4c 85 23 ·· 23 85 4c 4c 4c 92 ·· ·· 92 4c 4c 4c 4c 88 1f 88 4c 4c 4c 4c 92 ·· ·· - 92 4c 4c 4c 66 5e ·· 5e 66 4c 4c 4c 92 ·· ·· 92 4c 4c 4c 4c 73 82 73 4c 4c 4c 4c 92 ·· ·· - 92 4c 4c 4c 4c 88 1f 88 4c 4c 4c 4c 92 ·· ·· 92 4c 4c a5 4c 4c a5 4c 4c a5 4c 4c 92 ·· ·· - 92 4c 4c 4c 4c 73 82 73 4c 4c 4c 4c 92 ·· ·· 92 4c 4c b3 71 4c 4c 4c 71 b3 4c 4c 92 ·· ·· - 92 4c 4c a5 4c 4c a5 4c 4c a5 4c 4c 92 ·· ·· 92 4c 4c 99 87 4c 4c 4c 87 99 4c 4c 92 ·· ·· - 92 4c 4c b3 71 4c 4c 4c 71 b3 4c 4c 92 ·· ·· 92 4c 4c 92 60 64 4c 64 60 92 4c 4c 92 ·· ·· - 92 4c 4c 99 87 4c 4c 4c 87 99 4c 4c 92 ·· ·· 92 4c 4c 92 25 83 4c 83 25 92 4c 4c 92 ·· ·· - 92 4c 4c 92 60 64 4c 64 60 92 4c 4c 92 ·· ·· 92 4c 4c 92 ·· 7d 61 7d ·· 92 4c 4c 92 ·· ·· - 92 4c 4c 92 25 83 4c 83 25 92 4c 4c 92 ·· ·· 92 4c 4c 92 ·· 38 96 38 ·· 92 4c 4c 92 ·· ·· - 92 4c 4c 92 ·· 7d 61 7d ·· 92 4c 4c 92 ·· ·· 92 4c 4c 92 ·· ·· 7f ·· ·· 92 4c 4c 92 ·· ·· - 92 4c 4c 92 ·· 38 96 38 ·· 92 4c 4c 92 ·· ·· 92 4c 4c 92 ·· ·· ·· ·· ·· 92 4c 4c 92 ·· ·· - 92 4c 4c 92 ·· ·· 7f ·· ·· 92 4c 4c 92 ·· ·· 92 4c 4c 92 ·· ·· ·· ·· ·· 92 4c 4c 92 ·· ·· - 92 4c 4c 92 ·· ·· ·· ·· ·· 92 4c 4c 92 ·· ·· 88 92 92 88 ·· ·· ·· ·· ·· 88 92 92 88 ·· ·· - 92 4c 4c 92 ·· ·· ·· ·· ·· 92 4c 4c 92 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 88 92 92 88 ·· ·· ·· ·· ·· 88 92 92 88 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 8a 95 95 8a ·· ·· ·· ·· ·· 8a 95 95 8a ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 95 57 57 7f 36 ·· ·· ·· 36 7f 57 57 95 ·· ·· + 8a 95 95 8a ·· ·· ·· ·· ·· 8a 95 95 8a ·· ·· 95 57 57 63 7f ·· ·· ·· 7f 63 57 57 95 ·· ·· + 95 57 57 7f 36 ·· ·· ·· 36 7f 57 57 95 ·· ·· 95 57 57 57 8c 24 ·· 24 8c 57 57 57 95 ·· ·· + 95 57 57 63 7f ·· ·· ·· 7f 63 57 57 95 ·· ·· 95 57 57 57 6f 62 ·· 62 6f 57 57 57 95 ·· ·· + 95 57 57 57 8c 24 ·· 24 8c 57 57 57 95 ·· ·· 95 57 57 57 57 8c 23 8c 57 57 57 57 95 ·· ·· + 95 57 57 57 6f 62 ·· 62 6f 57 57 57 95 ·· ·· 95 57 57 57 57 7b 89 7b 57 57 57 57 95 ·· ·· + 95 57 57 57 57 8c 23 8c 57 57 57 57 95 ·· ·· 95 57 57 ab 57 57 ab 57 57 ab 57 57 95 ·· ·· + 95 57 57 57 57 7b 89 7b 57 57 57 57 95 ·· ·· 95 57 57 b7 7b 57 57 57 7b b8 57 57 95 ·· ·· + 95 57 57 ab 57 57 ab 57 57 ab 57 57 95 ·· ·· 95 57 57 9d 8c 57 57 57 8c 9c 57 57 95 ·· ·· + 95 57 57 b7 7b 57 57 57 7b b8 57 57 95 ·· ·· 95 57 57 95 62 6f 57 6f 62 95 57 57 95 ·· ·· + 95 57 57 9d 8c 57 57 57 8c 9c 57 57 95 ·· ·· 95 57 57 95 24 8c 57 8c 24 95 57 57 95 ·· ·· + 95 57 57 95 62 6f 57 6f 62 95 57 57 95 ·· ·· 95 57 57 95 ·· 7f 6e 7f ·· 95 57 57 95 ·· ·· + 95 57 57 95 24 8c 57 8c 24 95 57 57 95 ·· ·· 95 57 57 95 ·· 36 9e 36 ·· 95 57 57 95 ·· ·· + 95 57 57 95 ·· 7f 6e 7f ·· 95 57 57 95 ·· ·· 95 57 57 95 ·· ·· 7f ·· ·· 95 57 57 95 ·· ·· + 95 57 57 95 ·· 36 9e 36 ·· 95 57 57 95 ·· ·· 95 57 57 95 ·· ·· ·· ·· ·· 95 57 57 95 ·· ·· + 95 57 57 95 ·· ·· 7f ·· ·· 95 57 57 95 ·· ·· 95 57 57 95 ·· ·· ·· ·· ·· 95 57 57 95 ·· ·· + 95 57 57 95 ·· ·· ·· ·· ·· 95 57 57 95 ·· ·· 8a 95 95 8a ·· ·· ·· ·· ·· 8a 95 95 8a ·· ·· + 95 57 57 95 ·· ·· ·· ·· ·· 95 57 57 95 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 8a 95 95 8a ·· ·· ·· ·· ·· 8a 95 95 8a ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 82 85 85 82 ·· ·· ·· ·· ·· 82 85 85 82 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 85 4c 4c 6b 36 ·· ·· ·· 36 6b 4c 4c 85 ·· ·· - 82 85 85 82 ·· ·· ·· ·· ·· 82 85 85 82 ·· ·· 85 4c 4c 55 74 ·· ·· ·· 74 55 4c 4c 85 ·· ·· - 85 4c 4c 6b 36 ·· ·· ·· 36 6b 4c 4c 85 ·· ·· 85 4c 4c 4c 76 23 ·· 23 76 4c 4c 4c 85 ·· ·· - 85 4c 4c 55 74 ·· ·· ·· 74 55 4c 4c 85 ·· ·· 85 4c 4c 4c 5e 5c ·· 5c 5e 4c 4c 4c 85 ·· ·· - 85 4c 4c 4c 76 23 ·· 23 76 4c 4c 4c 85 ·· ·· 85 4c 4c 4c 4c 7b 1f 7b 4c 4c 4c 4c 85 ·· ·· - 85 4c 4c 4c 5e 5c ·· 5c 5e 4c 4c 4c 85 ·· ·· 85 4c 4c 4c 4c 68 7c 68 4c 4c 4c 4c 85 ·· ·· - 85 4c 4c 4c 4c 7b 1f 7b 4c 4c 4c 4c 85 ·· ·· 85 4c 4c 8b 4c 4c 8b 4c 4c 8b 4c 4c 85 ·· ·· - 85 4c 4c 4c 4c 68 7c 68 4c 4c 4c 4c 85 ·· ·· 85 4c 4c 9f 67 4c 4c 4c 67 9f 4c 4c 85 ·· ·· - 85 4c 4c 8b 4c 4c 8b 4c 4c 8b 4c 4c 85 ·· ·· 85 4c 4c 8c 79 4c 4c 4c 79 8c 4c 4c 85 ·· ·· - 85 4c 4c 9f 67 4c 4c 4c 67 9f 4c 4c 85 ·· ·· 85 4c 4c 85 5e 5d 4c 5d 5e 85 4c 4c 85 ·· ·· - 85 4c 4c 8c 79 4c 4c 4c 79 8c 4c 4c 85 ·· ·· 85 4c 4c 85 25 74 4c 74 25 85 4c 4c 85 ·· ·· - 85 4c 4c 85 5e 5d 4c 5d 5e 85 4c 4c 85 ·· ·· 85 4c 4c 85 ·· 75 5b 75 ·· 85 4c 4c 85 ·· ·· - 85 4c 4c 85 25 74 4c 74 25 85 4c 4c 85 ·· ·· 85 4c 4c 85 ·· 38 84 38 ·· 85 4c 4c 85 ·· ·· - 85 4c 4c 85 ·· 75 5b 75 ·· 85 4c 4c 85 ·· ·· 85 4c 4c 85 ·· ·· 7f ·· ·· 85 4c 4c 85 ·· ·· - 85 4c 4c 85 ·· 38 84 38 ·· 85 4c 4c 85 ·· ·· 85 4c 4c 85 ·· ·· ·· ·· ·· 85 4c 4c 85 ·· ·· - 85 4c 4c 85 ·· ·· 7f ·· ·· 85 4c 4c 85 ·· ·· 85 4c 4c 85 ·· ·· ·· ·· ·· 85 4c 4c 85 ·· ·· - 85 4c 4c 85 ·· ·· ·· ·· ·· 85 4c 4c 85 ·· ·· 82 85 85 82 ·· ·· ·· ·· ·· 82 85 85 82 ·· ·· - 85 4c 4c 85 ·· ·· ·· ·· ·· 85 4c 4c 85 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 82 85 85 82 ·· ·· ·· ·· ·· 82 85 85 82 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 83 87 87 83 ·· ·· ·· ·· ·· 83 87 87 83 ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 87 57 57 71 36 ·· ·· ·· 36 71 57 57 87 ·· ·· + 83 87 87 83 ·· ·· ·· ·· ·· 83 87 87 83 ·· ·· 87 57 57 5f 76 ·· ·· ·· 76 5f 57 57 87 ·· ·· + 87 57 57 71 36 ·· ·· ·· 36 71 57 57 87 ·· ·· 87 57 57 57 7a 24 ·· 24 7a 57 57 57 87 ·· ·· + 87 57 57 5f 76 ·· ·· ·· 76 5f 57 57 87 ·· ·· 87 57 57 57 67 60 ·· 60 67 57 57 57 87 ·· ·· + 87 57 57 57 7a 24 ·· 24 7a 57 57 57 87 ·· ·· 87 57 57 57 57 7d 23 7d 57 57 57 57 87 ·· ·· + 87 57 57 57 67 60 ·· 60 67 57 57 57 87 ·· ·· 87 57 57 57 57 6e 81 6e 57 57 57 57 87 ·· ·· + 87 57 57 57 57 7d 23 7d 57 57 57 57 87 ·· ·· 87 57 57 8e 57 57 8e 57 57 8e 57 57 87 ·· ·· + 87 57 57 57 57 6e 81 6e 57 57 57 57 87 ·· ·· 87 57 57 a0 6e 57 57 57 6e a0 57 57 87 ·· ·· + 87 57 57 8e 57 57 8e 57 57 8e 57 57 87 ·· ·· 87 57 57 8d 7d 57 57 57 7d 8d 57 57 87 ·· ·· + 87 57 57 a0 6e 57 57 57 6e a0 57 57 87 ·· ·· 87 57 57 87 60 67 57 67 60 87 57 57 87 ·· ·· + 87 57 57 8d 7d 57 57 57 7d 8d 57 57 87 ·· ·· 87 57 57 87 24 7a 57 7a 24 87 57 57 87 ·· ·· + 87 57 57 87 60 67 57 67 60 87 57 57 87 ·· ·· 87 57 57 87 ·· 76 66 76 ·· 87 57 57 87 ·· ·· + 87 57 57 87 24 7a 57 7a 24 87 57 57 87 ·· ·· 87 57 57 87 ·· 36 89 36 ·· 87 57 57 87 ·· ·· + 87 57 57 87 ·· 76 66 76 ·· 87 57 57 87 ·· ·· 87 57 57 87 ·· ·· 7f ·· ·· 87 57 57 87 ·· ·· + 87 57 57 87 ·· 36 89 36 ·· 87 57 57 87 ·· ·· 87 57 57 87 ·· ·· ·· ·· ·· 87 57 57 87 ·· ·· + 87 57 57 87 ·· ·· 7f ·· ·· 87 57 57 87 ·· ·· 87 57 57 87 ·· ·· ·· ·· ·· 87 57 57 87 ·· ·· + 87 57 57 87 ·· ·· ·· ·· ·· 87 57 57 87 ·· ·· 83 87 87 83 ·· ·· ·· ·· ·· 83 87 87 83 ·· ·· + 87 57 57 87 ·· ·· ·· ·· ·· 87 57 57 87 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + 83 87 87 83 ·· ·· ·· ·· ·· 83 87 87 83 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -880,9 +880,9 @@ 10 11 11 10 11 11 11 11 11 10 11 11 10 .. .. 10 11 11 10 .. 11 11 11 .. 10 11 11 10 .. .. 10 11 11 10 10 11 11 11 10 10 11 11 10 .. .. 10 11 11 10 .. 11 11 11 .. 10 11 11 10 .. .. 10 11 11 10 .. 11 11 11 .. 10 11 11 10 .. .. 10 11 11 10 .. 01 11 01 .. 10 11 11 10 .. .. - 10 11 11 10 .. 11 11 11 .. 10 11 11 10 .. .. 10 11 11 10 .. .. 10 .. .. 10 11 11 10 .. .. + 10 11 11 10 .. 11 11 11 .. 10 11 11 10 .. .. 10 11 11 10 .. .. 11 .. .. 10 11 11 10 .. .. 10 11 11 10 .. 01 11 01 .. 10 11 11 10 .. .. 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. - 10 11 11 10 .. .. 10 .. .. 10 11 11 10 .. .. 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. + 10 11 11 10 .. .. 11 .. .. 10 11 11 10 .. .. 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. 01 10 10 01 .. .. .. .. .. 01 10 10 01 .. .. 10 11 11 10 .. .. .. .. .. 10 11 11 10 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. @@ -942,5 +942,4 @@ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. - .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. From 2aa80615f5998ef5b79e046ea6fc0b5277939aa9 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 24 Nov 2025 13:37:31 +0000 Subject: [PATCH 56/81] Fix tests and codeformat. --- extmod/modframebuf.c | 4 ++-- tests/extmod/framebuf_alpha.py.exp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 272fc8b8c9700..83e8bfca8a529 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -1015,7 +1015,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { if (current.x >= 0) { // pixel is inside the buffer, so draw the pixel - setpixel(self, current.x, row, col, alpha_mult((popcount(mask)*0b1001001) >> 1, alpha)); + setpixel(self, current.x, row, col, alpha_mult((popcount(mask) * 0b1001001) >> 1, alpha)); } // extend mask by last bits @@ -1029,7 +1029,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { } else { width = self->width - current.x - 1; } - fill_rect(self, current.x + 1, row, width, 1, col, alpha_mult((popcount(mask)*0b1001001) >> 1, alpha)); + fill_rect(self, current.x + 1, row, width, 1, col, alpha_mult((popcount(mask) * 0b1001001) >> 1, alpha)); } } } diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index 1a05e8c1b1c3a..f718bb778c304 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -379,4 +379,4 @@ e007e007e007e007e007 000000000000000000000000000055555500000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 --->8-- \ No newline at end of file +-->8-- From a889490c11dedf667221730a4c0ac8a45792f059 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 24 Nov 2025 13:51:43 +0000 Subject: [PATCH 57/81] Fix missing line. --- tests/extmod/framebuf_polygon_alpha.py.exp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/extmod/framebuf_polygon_alpha.py.exp b/tests/extmod/framebuf_polygon_alpha.py.exp index 635ad957833a5..bd2565fb94f60 100644 --- a/tests/extmod/framebuf_polygon_alpha.py.exp +++ b/tests/extmod/framebuf_polygon_alpha.py.exp @@ -942,4 +942,5 @@ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. From f294739465da879280837b4990b922a8aeb4afc7 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 24 Nov 2025 14:34:32 +0000 Subject: [PATCH 58/81] Add support for RGB565_BS to alpha blending code. --- extmod/modframebuf.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 8afa83da67e86..54fc10aeb3ada 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -294,19 +294,23 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 return; } else if (alpha < 0xff) { uint16_t pix_col = formats[fb->format].getpixel(fb, x, y); - if (fb->format == FRAMEBUF_RGB565) { - uint16_t col16 = col; - urgb565 pix_col_struct = *(urgb565 *)&pix_col; - urgb565 col_struct = *(urgb565 *)&col16; - col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); - col_struct.rgb.g = alpha_blend(pix_col_struct.rgb.g, col_struct.rgb.g, alpha); - col_struct.rgb.b = alpha_blend(pix_col_struct.rgb.b, col_struct.rgb.b, alpha); - col_struct.rgb.r = MIN(col_struct.rgb.r, 0b11111); - col_struct.rgb.g = MIN(col_struct.rgb.g, 0b111111); - col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); - col = *(uint16_t *)&col_struct; - } else { - col = alpha_blend(pix_col, col, alpha); + // for RGB565 + uint16_t col16 = col & 0xFFFF; + urgb565 pix_col_struct = *(urgb565 *)&pix_col; + urgb565 col_struct = *(urgb565 *)&col16; + switch (fb->format) { + case FRAMEBUF_RGB565: + case FRAMEBUF_RGB565_BS: + col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); + col_struct.rgb.g = alpha_blend(pix_col_struct.rgb.g, col_struct.rgb.g, alpha); + col_struct.rgb.b = alpha_blend(pix_col_struct.rgb.b, col_struct.rgb.b, alpha); + col_struct.rgb.r = MIN(col_struct.rgb.r, 0b11111); + col_struct.rgb.g = MIN(col_struct.rgb.g, 0b111111); + col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); + col = *(uint16_t *)&col_struct; + break; + default: + col = alpha_blend(pix_col, col, alpha); } } formats[fb->format].setpixel(fb, x, y, col); From 737a9be110f5337867e25037575bfbc195e24cb3 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 25 Nov 2025 09:22:12 +0000 Subject: [PATCH 59/81] Try to square the circle on byteswapped RGB565. --- docs/library/framebuf.rst | 33 +++++++++++++++++ extmod/modframebuf.c | 68 +++++++++++++++++++++++++--------- tests/extmod/framebuf16.py | 7 ---- tests/extmod/framebuf16.py.exp | 1 - 4 files changed, 84 insertions(+), 25 deletions(-) diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index 765d7ce37e5f9..d8fdd16ba1ce5 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -229,6 +229,15 @@ Constants Red Green Blue (16-bit, 5+6+5, native) color format in native byte-order. +.. data:: framebuf.RGB565_BS + + Red Green Blue (16-bit, 5+6+5, non-native) color format. This format uses + color values supplied with a non-native bit-pattern (ie. instead of + ``rrrrrggggggbbbbb`` the values are byte-swapped ``gggbbbbbrrrrrggg`` where + the least-significant bits of the green channel occur first). This is a + legacy format to ease migration for the common case of systems which used + displays with an opposite byte-order to microcontroller. + .. data:: framebuf.RGB565_LE Red Green Blue (16-bit, 5+6+5, little-endian) color format in little-endian @@ -270,3 +279,27 @@ Constants ``framebuf.blit`` can use grayscale masks. There is currently no support for rendering antialiased ellipses. + +.. note:: + In Micropython 1.26 and earlier, the RGB565 format did not need + to know aout the internal color channels within each 16-bit value. + As a result, it did not care about the byte-order of the values stored + as pixels. Many display devices use big-endian RGB565, and so code that + used them from little-endian microcontrollers would simply provide colors + as big-endian RGB565 values (ie. using ``0b00000000_11111000`` for red + instead of ``0b11111000_00000000``). + + The introduction of support for alpha blending means that the RGB565 format + is assuming native byte-order for the layout of color channels within the + 16-bits of a pixel. This breaks code that uses byte-swapped color values, + but it can be adapted either by: + + * using firmware compiled with alpha support disabled via the + ``MICROPY_PY_FRAMEBUF_ALPHA`` flag; or + + * replacing the use of ``RGB565`` format with the byte-swapped + ``RGB565_BS`` format. + + New code which needs to support buffers of a particular byte-order should + simply use ``RGB565_BE`` or ``RGB565_LE`` as appropriate, and supply colors + in native RGB565 format. diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 54fc10aeb3ada..ddd2fe4964495 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -59,7 +59,9 @@ typedef struct _mp_framebuf_p_t { // constants for formats #define FRAMEBUF_MVLSB (0) #define FRAMEBUF_RGB565 (1) -#define FRAMEBUF_RGB565_BS (7) +#define FRAMEBUF_RGB565_BE (7) +#define FRAMEBUF_RGB565_LE (8) +#define FRAMEBUF_RGB565_BS (9) #define FRAMEBUF_GS2_HMSB (5) #define FRAMEBUF_GS4_HMSB (2) #define FRAMEBUF_GS8 (6) @@ -118,9 +120,9 @@ static void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigne } } -// Functions for RGB565 and RGB565_BS format +// Functions for RGB565 formats // -// Internally we use 'native' and 'byte-swapped' formats, and then expose those as big- or little- +// Internally we use 'native' and 'non-native' formats, and then expose those as big- or little- // endian to Python as appropriate. static void rgb565_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { @@ -131,20 +133,28 @@ static uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, uns return ((uint16_t *)fb->buf)[x + y * fb->stride]; } -static void rgb565_bs_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { +static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; + while (h--) { + for (unsigned int ww = w; ww; --ww) { + *b++ = col; + } + b += fb->stride - w; + } +} + +static void rgb565_non_native_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { col = __builtin_bswap16(col); ((uint16_t *)fb->buf)[x + y * fb->stride] = col; } -static uint32_t rgb565_bs_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { +static uint32_t rgb565_non_native_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { uint32_t col = ((uint16_t *)fb->buf)[x + y * fb->stride]; return __builtin_bswap16(col); } -static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - if (fb->format == FRAMEBUF_RGB565_BS) { - col = __builtin_bswap16(col); - } +static void rgb565_non_native_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + col = __builtin_bswap16(col); uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; while (h--) { for (unsigned int ww = w; ww; --ww) { @@ -251,7 +261,14 @@ static void gs8_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned static mp_framebuf_p_t formats[] = { [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, - [FRAMEBUF_RGB565_BS] = {rgb565_bs_setpixel, rgb565_bs_getpixel, rgb565_fill_rect}, + [FRAMEBUF_RGB565_BS] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, + #if MP_ENDIANNESS_BIG + [FRAMEBUF_RGB565_BE] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, + [FRAMEBUF_RGB565_LE] = {rgb565_non_native_setpixel, rgb565_non_native_getpixel, rgb565_non_native_fill_rect}, + #else // MP_ENDIANNESS_BIG + [FRAMEBUF_RGB565_BE] = {rgb565_non_native_setpixel, rgb565_non_native_getpixel, rgb565_non_native_fill_rect}, + [FRAMEBUF_RGB565_LE] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, + #endif // MP_ENDIANNESS_BIG [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect}, [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect}, [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect}, @@ -300,7 +317,24 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 urgb565 col_struct = *(urgb565 *)&col16; switch (fb->format) { case FRAMEBUF_RGB565: + col16 = col & 0xFFFF; + pix_col_struct = *(urgb565 *)&pix_col; + col_struct = *(urgb565 *)&col16; + col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); + col_struct.rgb.g = alpha_blend(pix_col_struct.rgb.g, col_struct.rgb.g, alpha); + col_struct.rgb.b = alpha_blend(pix_col_struct.rgb.b, col_struct.rgb.b, alpha); + col_struct.rgb.r = MIN(col_struct.rgb.r, 0b11111); + col_struct.rgb.g = MIN(col_struct.rgb.g, 0b111111); + col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); + col = *(uint16_t *)&col_struct; + break; case FRAMEBUF_RGB565_BS: + // The colors are specified in non-native endianness in Python. + // We need to byteswap to get native endianness. + col16 = __builtin_bswap16(col & 0xFFFF); + pix_col = __builtin_bswap16(pix_col); + pix_col_struct = *(urgb565 *)&pix_col; + col_struct = *(urgb565 *)&col16; col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); col_struct.rgb.g = alpha_blend(pix_col_struct.rgb.g, col_struct.rgb.g, alpha); col_struct.rgb.b = alpha_blend(pix_col_struct.rgb.b, col_struct.rgb.b, alpha); @@ -308,6 +342,8 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 col_struct.rgb.g = MIN(col_struct.rgb.g, 0b111111); col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); col = *(uint16_t *)&col_struct; + // And byteswap back to get non-native endianness. + col = __builtin_bswap16(col); break; default: col = alpha_blend(pix_col, col, alpha); @@ -420,6 +456,8 @@ static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, break; case FRAMEBUF_RGB565: case FRAMEBUF_RGB565_BS: + case FRAMEBUF_RGB565_BE: + case FRAMEBUF_RGB565_LE: bpp = 16; break; default: @@ -1444,13 +1482,9 @@ static const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, - #if MP_ENDIANNESS_LITTLE - { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565) }, - { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, - #elif MP_ENDIANNESS_BIG - { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565) }, - { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, - #endif // MP_ENDIANNESS_... + { MP_ROM_QSTR(MP_QSTR_RGB565_BS), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565_BE) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565_LE) }, { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) }, diff --git a/tests/extmod/framebuf16.py b/tests/extmod/framebuf16.py index 7d4c21ff9ada3..6de831dced1ca 100644 --- a/tests/extmod/framebuf16.py +++ b/tests/extmod/framebuf16.py @@ -4,13 +4,6 @@ print("SKIP") raise SystemExit -if (sys.byteorder == "little" and framebuf.RGB565 == framebuf.RGB565_LE) or ( - sys.byteorder == "big" and framebuf.RGB565 == framebuf.RGB565_BE -): - print("Native format matches expected value.") -else: - print("Unexpected native format.") - def printbuf(): print("--8<--") diff --git a/tests/extmod/framebuf16.py.exp b/tests/extmod/framebuf16.py.exp index bb23762efcf75..f34279e03fa68 100644 --- a/tests/extmod/framebuf16.py.exp +++ b/tests/extmod/framebuf16.py.exp @@ -1,4 +1,3 @@ -Native format matches expected value. --8<-- bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') From 245ed63dccfa4086bb530bdcbd13dfe162221498 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 26 Nov 2025 10:55:51 +0000 Subject: [PATCH 60/81] Fix corner case of double-drawn ellipse points. --- extmod/modframebuf.c | 10 +++-- tests/extmod/framebuf_alpha.py | 11 +++++ tests/extmod/framebuf_alpha.py.exp | 64 ++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index ddd2fe4964495..e75d61d9bba9a 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -607,7 +607,7 @@ static void line(const mp_obj_framebuf_t *fb, mp_int_t x1, mp_int_t y1, mp_int_t } bool steep; - if (dy > dx || dy < -dx) { + if (dy > dx) { // swap x and y mp_int_t temp; temp = x1; @@ -791,8 +791,12 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { mp_int_t ellipse_error = 0; mp_int_t stoppingx = two_bsquare * args[2]; mp_int_t stoppingy = 0; + mp_int_t last_drawn_x = -1; + mp_int_t last_drawn_y = -1; while (stoppingx >= stoppingy) { // 1st set of points, y' > -1 draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask, alpha); + last_drawn_x = x; + last_drawn_y = y; y += 1; stoppingy += two_asquare; ellipse_error += ychange; @@ -813,7 +817,7 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { stoppingx = 0; stoppingy = two_asquare * args[3]; while (stoppingx <= stoppingy) { // 2nd set of points, y' < -1 - if (!(mask & ELLIPSE_MASK_FILL)) { + if (!(mask & ELLIPSE_MASK_FILL) && (y != last_drawn_y || x != last_drawn_x)) { draw_ellipse_points(self, args[0], args[1], x, y, args[4], mask, alpha); } x += 1; @@ -821,7 +825,7 @@ static mp_obj_t framebuf_ellipse(size_t n_args, const mp_obj_t *args_in) { ellipse_error += xchange; xchange += two_bsquare; if ((2 * ellipse_error + ychange) > 0) { - if (mask & ELLIPSE_MASK_FILL) { + if ((mask & ELLIPSE_MASK_FILL) && y != last_drawn_y) { // moving to new scanline, draw line *once* draw_ellipse_points(self, args[0], args[1], x - 1, y, args[4], mask, alpha); } diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index 50fc2da577c64..5c50ac980a347 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -198,3 +198,14 @@ def printbuf(bpp=1): fbuf.fill(0) fbuf.ellipse(15, 15, 6, 12, 0xAA, True, 0b1111, 0x7F) printbuf() + +# Circle which might double-draw +# Outline +fbuf.fill(0) +fbuf.ellipse(15, 15, 6, 6, 0x7F, False, 0b1111, 0x7F) +printbuf() + +# Fill +fbuf.fill(0) +fbuf.ellipse(15, 15, 6, 6, 0xAA, True, 0b1111, 0x7F) +printbuf() \ No newline at end of file diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index f718bb778c304..87cdff33f165a 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -380,3 +380,67 @@ e007e007e007e007e007 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 -->8-- +--8<-- +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000003f3f3f3f3f000000000000000000000000 +0000000000000000000000003f00000000003f0000000000000000000000 +00000000000000000000003f000000000000003f00000000000000000000 +000000000000000000003f0000000000000000003f000000000000000000 +0000000000000000003f00000000000000000000003f0000000000000000 +0000000000000000003f00000000000000000000003f0000000000000000 +0000000000000000003f00000000000000000000003f0000000000000000 +0000000000000000003f00000000000000000000003f0000000000000000 +0000000000000000003f00000000000000000000003f0000000000000000 +000000000000000000003f0000000000000000003f000000000000000000 +00000000000000000000003f000000000000003f00000000000000000000 +0000000000000000000000003f00000000003f0000000000000000000000 +000000000000000000000000003f3f3f3f3f000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +-->8-- +--8<-- +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000005555555555000000000000000000000000 +000000000000000000000000555555555555550000000000000000000000 +000000000000000000000055555555555555555500000000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000555555555555555555555555550000000000000000 +000000000000000000005555555555555555555555000000000000000000 +000000000000000000000055555555555555555500000000000000000000 +000000000000000000000000555555555555550000000000000000000000 +000000000000000000000000005555555555000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000 +-->8-- From 50ae97ac29fdbccd6c13674b3e3014fceb74bd77 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 26 Nov 2025 13:05:50 +0000 Subject: [PATCH 61/81] Fix missing formats in case statement, minor style fixes. --- extmod/modframebuf.c | 12 +++++++----- tests/extmod/framebuf_alpha.py | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index e75d61d9bba9a..3742989b9b644 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -262,13 +262,13 @@ static mp_framebuf_p_t formats[] = { [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, [FRAMEBUF_RGB565_BS] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, - #if MP_ENDIANNESS_BIG + #if MP_ENDIANNESS_LITTLE + [FRAMEBUF_RGB565_LE] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, + [FRAMEBUF_RGB565_BE] = {rgb565_non_native_setpixel, rgb565_non_native_getpixel, rgb565_non_native_fill_rect}, + #else // MP_ENDIANNESS_LITTLE [FRAMEBUF_RGB565_BE] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, [FRAMEBUF_RGB565_LE] = {rgb565_non_native_setpixel, rgb565_non_native_getpixel, rgb565_non_native_fill_rect}, - #else // MP_ENDIANNESS_BIG - [FRAMEBUF_RGB565_BE] = {rgb565_non_native_setpixel, rgb565_non_native_getpixel, rgb565_non_native_fill_rect}, - [FRAMEBUF_RGB565_LE] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, - #endif // MP_ENDIANNESS_BIG + #endif // MP_ENDIANNESS_LITTLE [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect}, [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect}, [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect}, @@ -317,6 +317,8 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 urgb565 col_struct = *(urgb565 *)&col16; switch (fb->format) { case FRAMEBUF_RGB565: + case FRAMEBUF_RGB565_BE: + case FRAMEBUF_RGB565_LE: col16 = col & 0xFFFF; pix_col_struct = *(urgb565 *)&pix_col; col_struct = *(urgb565 *)&col16; diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index 5c50ac980a347..945935c42c41d 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -208,4 +208,4 @@ def printbuf(bpp=1): # Fill fbuf.fill(0) fbuf.ellipse(15, 15, 6, 6, 0xAA, True, 0b1111, 0x7F) -printbuf() \ No newline at end of file +printbuf() From 64e74c8fac09af00d754a9cf0b03a5cce62f62d1 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 26 Nov 2025 15:09:46 +0000 Subject: [PATCH 62/81] Be more careful with 32-bit non-native vs 16-bit non-native. --- extmod/modframebuf.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 3742989b9b644..bc460713f9bcd 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -144,21 +144,22 @@ static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsign } static void rgb565_non_native_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { - col = __builtin_bswap16(col); - ((uint16_t *)fb->buf)[x + y * fb->stride] = col; + uint16_t col16 = col; + ((uint16_t *)fb->buf)[x + y * fb->stride] = __builtin_bswap16(col16); } static uint32_t rgb565_non_native_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { - uint32_t col = ((uint16_t *)fb->buf)[x + y * fb->stride]; - return __builtin_bswap16(col); + uint32_t col = __builtin_bswap16(((uint16_t *)fb->buf)[x + y * fb->stride]); + return col; } static void rgb565_non_native_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - col = __builtin_bswap16(col); + uint16_t col16 = col; + col16 = __builtin_bswap16(col16); uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; while (h--) { for (unsigned int ww = w; ww; --ww) { - *b++ = col; + *b++ = col16; } b += fb->stride - w; } From a51cd7cd19fdfd54b62f38946528ac6db99495d3 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 26 Nov 2025 16:14:51 +0000 Subject: [PATCH 63/81] Be really precise about byteswapping. --- extmod/modframebuf.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index bc460713f9bcd..e63dc6074c124 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -143,19 +143,23 @@ static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsign } } +uint16_t bs16(uint16_t x) { + return (x >> 8) | (x << 8); +} + static void rgb565_non_native_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { uint16_t col16 = col; - ((uint16_t *)fb->buf)[x + y * fb->stride] = __builtin_bswap16(col16); + ((uint16_t *)fb->buf)[x + y * fb->stride] = bs16(col16); } static uint32_t rgb565_non_native_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { - uint32_t col = __builtin_bswap16(((uint16_t *)fb->buf)[x + y * fb->stride]); + uint32_t col = bs16(((uint16_t *)fb->buf)[x + y * fb->stride]); return col; } static void rgb565_non_native_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { uint16_t col16 = col; - col16 = __builtin_bswap16(col16); + col16 = bs16(col16); uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; while (h--) { for (unsigned int ww = w; ww; --ww) { @@ -334,8 +338,8 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 case FRAMEBUF_RGB565_BS: // The colors are specified in non-native endianness in Python. // We need to byteswap to get native endianness. - col16 = __builtin_bswap16(col & 0xFFFF); - pix_col = __builtin_bswap16(pix_col); + col16 = bs16(col & 0xFFFF); + pix_col = bs16(pix_col); pix_col_struct = *(urgb565 *)&pix_col; col_struct = *(urgb565 *)&col16; col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); @@ -346,7 +350,7 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); col = *(uint16_t *)&col_struct; // And byteswap back to get non-native endianness. - col = __builtin_bswap16(col); + col = bs16(col); break; default: col = alpha_blend(pix_col, col, alpha); From 0622bf1d7f1a44b34b2f605eaf8db2819a54938b Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Wed, 26 Nov 2025 17:04:00 +0000 Subject: [PATCH 64/81] More tweaking. --- extmod/modframebuf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index e63dc6074c124..726273c27baec 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -143,7 +143,7 @@ static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsign } } -uint16_t bs16(uint16_t x) { +static uint16_t bs16(uint16_t x) { return (x >> 8) | (x << 8); } @@ -338,7 +338,8 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 case FRAMEBUF_RGB565_BS: // The colors are specified in non-native endianness in Python. // We need to byteswap to get native endianness. - col16 = bs16(col & 0xFFFF); + col16 = col & 0xFFFF; + col16 = bs16(col); pix_col = bs16(pix_col); pix_col_struct = *(urgb565 *)&pix_col; col_struct = *(urgb565 *)&col16; From 80cb48c3d27179287954f8407090dafe8c13a2ea Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 08:42:40 +0000 Subject: [PATCH 65/81] Make alpha tests endian-independent, other fussing around. --- extmod/modframebuf.c | 4 +- tests/extmod/framebuf16.py | 2 +- tests/extmod/framebuf16.py.exp | 4 +- tests/extmod/framebuf_alpha.py | 108 +++++++------- tests/extmod/framebuf_alpha.py.exp | 228 ++++++++++++++++++++--------- 5 files changed, 217 insertions(+), 129 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 726273c27baec..1b0adaef552ec 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -59,9 +59,9 @@ typedef struct _mp_framebuf_p_t { // constants for formats #define FRAMEBUF_MVLSB (0) #define FRAMEBUF_RGB565 (1) +#define FRAMEBUF_RGB565_BS (9) #define FRAMEBUF_RGB565_BE (7) #define FRAMEBUF_RGB565_LE (8) -#define FRAMEBUF_RGB565_BS (9) #define FRAMEBUF_GS2_HMSB (5) #define FRAMEBUF_GS4_HMSB (2) #define FRAMEBUF_GS8 (6) @@ -144,7 +144,7 @@ static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsign } static uint16_t bs16(uint16_t x) { - return (x >> 8) | (x << 8); + return (x >> 8) | ((x & 0xff) << 8); } static void rgb565_non_native_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { diff --git a/tests/extmod/framebuf16.py b/tests/extmod/framebuf16.py index 6de831dced1ca..30cf798c9c958 100644 --- a/tests/extmod/framebuf16.py +++ b/tests/extmod/framebuf16.py @@ -32,7 +32,7 @@ def printbuf(): printbuf() # get pixel - print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) + print(fbuf.pixel(0, 4), fbuf.pixel(1, 1), fbuf.pixel(3, 4)) # scroll fbuf.fill(0x0000) diff --git a/tests/extmod/framebuf16.py.exp b/tests/extmod/framebuf16.py.exp index f34279e03fa68..6c869b9a6c003 100644 --- a/tests/extmod/framebuf16.py.exp +++ b/tests/extmod/framebuf16.py.exp @@ -19,7 +19,7 @@ bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\xee\x00\x00\x00\x00\x00\xe0\x0e') -->8-- -238 0 +238 0 3808 --8<-- bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') @@ -76,7 +76,7 @@ bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\xee\x00\x00\x00\x00\x0e\xe0') -->8-- -238 0 +238 0 3808 --8<-- bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index 945935c42c41d..57bdbf6a838ff 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -12,11 +12,6 @@ print("SKIP") raise SystemExit -# This test and its .exp file is based on a little-endian architecture. -if sys.byteorder != "little": - print("SKIP") - raise SystemExit - def printbuf(bpp=1): print("--8<--") @@ -136,53 +131,6 @@ def printbuf(bpp=1): fbuf.text("x", x, y, 0x7F, 0x7F) printbuf() -# Now in color -buf = bytearray(2 * w * h) -fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565) - -fbuf2 = framebuf.FrameBuffer(bytearray(8), 2, 2, framebuf.RGB565) -fbuf2.fill(0b1111101111100000) - -# Blit a color FrameBuffer, at various locations with alpha. -for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): - fbuf.fill(0) - fbuf.blit(fbuf2, x, y, -1, None, 0x7F) - printbuf(2) - -# Blit a color FrameBuffer, with alpha mask. -alphas = [[0, 0x3F], [0x7F, 0xFF]] -for bpp, format in [ - (8, framebuf.GS8), - (4, framebuf.GS4_HMSB), - (2, framebuf.GS2_HMSB), - (1, framebuf.MONO_HLSB), -]: - mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) - for x in [0, 1]: - for y in [0, 1]: - mask.pixel(x, y, alphas[x][y] >> (8 - bpp)) - - fbuf.fill(0) - fbuf.blit(fbuf2, 1, 1, -1, None, mask) - printbuf(2) - -# Blit a color FrameBuffer, with alpha mask, non-black background. -alphas = [[0, 0x3F], [0x7F, 0xFF]] -for bpp, format in [ - (8, framebuf.GS8), - (4, framebuf.GS4_HMSB), - (2, framebuf.GS2_HMSB), - (1, framebuf.MONO_HLSB), -]: - mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) - for x in [0, 1]: - for y in [0, 1]: - mask.pixel(x, y, alphas[x][y] >> (8 - bpp)) - - fbuf.fill(0b00000_111111_00000) - fbuf.blit(fbuf2, 1, 1, -1, None, mask) - printbuf(2) - # Ellipse w = 30 h = 30 @@ -209,3 +157,59 @@ def printbuf(bpp=1): fbuf.fill(0) fbuf.ellipse(15, 15, 6, 6, 0xAA, True, 0b1111, 0x7F) printbuf() + +# Now in color +for format in [framebuf.RGB565_LE, framebuf.RGB565_BE]: + w = 5 + h = 4 + buf = bytearray(2 * w * h) + fbuf = framebuf.FrameBuffer(buf, w, h, format) + + # set pixel at various locations with alpha. + fbuf.fill(0) + for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.pixel(x, y, 0b1111101111100000, 0x7F) + printbuf(2) + + fbuf2 = framebuf.FrameBuffer(bytearray(8), 2, 2, format) + fbuf2.fill(0b1111101111100000) + + # Blit a color FrameBuffer, at various locations with alpha. + for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.blit(fbuf2, x, y, -1, None, 0x7F) + printbuf(2) + + # Blit a color FrameBuffer, with alpha mask. + alphas = [[0, 0x3F], [0x7F, 0xFF]] + for bpp, format in [ + (8, framebuf.GS8), + (4, framebuf.GS4_HMSB), + (2, framebuf.GS2_HMSB), + (1, framebuf.MONO_HLSB), + ]: + mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) + for x in [0, 1]: + for y in [0, 1]: + mask.pixel(x, y, alphas[x][y] >> (8 - bpp)) + + fbuf.fill(0) + fbuf.blit(fbuf2, 1, 1, -1, None, mask) + printbuf(2) + + # Blit a color FrameBuffer, with alpha mask, non-black background. + alphas = [[0, 0x3F], [0x7F, 0xFF]] + for bpp, format in [ + (8, framebuf.GS8), + (4, framebuf.GS4_HMSB), + (2, framebuf.GS2_HMSB), + (1, framebuf.MONO_HLSB), + ]: + mask = framebuf.FrameBuffer(bytearray(4), 2, 2, format) + for x in [0, 1]: + for y in [0, 1]: + mask.pixel(x, y, alphas[x][y] >> (8 - bpp)) + + fbuf.fill(0b00000_111111_00000) + fbuf.blit(fbuf2, 1, 1, -1, None, mask) + printbuf(2) diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index 87cdff33f165a..6ffb6a07c2363 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -245,78 +245,6 @@ efefefefef 0000000000 -->8-- --8<-- -e0790000000000000000 -00000000000000000000 -00000000000000000000 -00000000000000000000 --->8-- ---8<-- -e079e079000000000000 -e079e079000000000000 -00000000000000000000 -00000000000000000000 --->8-- ---8<-- -00000000000000000000 -0000e079e07900000000 -0000e079e07900000000 -00000000000000000000 --->8-- ---8<-- -00000000000000000000 -00000000000000000000 -00000000000000000000 -0000000000000000e079 --->8-- ---8<-- -00000000000000000000 -00000000e07900000000 -00000041e0fb00000000 -00000000000000000000 --->8-- ---8<-- -00000000000000000000 -00000000e0fb00000000 -0000e0fbe0fb00000000 -00000000000000000000 --->8-- ---8<-- -00000000000000000000 -00000000e0fb00000000 -00000000e0fb00000000 -00000000000000000000 --->8-- ---8<-- -00000000000000000000 -00000000000000000000 -00000000e0fb00000000 -00000000000000000000 --->8-- ---8<-- -e007e007e007e007e007 -e007e007e07de007e007 -e007e046e0fbe007e007 -e007e007e007e007e007 --->8-- ---8<-- -e007e007e007e007e007 -e007e007e0fbe007e007 -e007e0fbe0fbe007e007 -e007e007e007e007e007 --->8-- ---8<-- -e007e007e007e007e007 -e007e007e0fbe007e007 -e007e007e0fbe007e007 -e007e007e007e007e007 --->8-- ---8<-- -e007e007e007e007e007 -e007e007e007e007e007 -e007e007e0fbe007e007 -e007e007e007e007e007 --->8-- ---8<-- 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 @@ -444,3 +372,159 @@ e007e007e007e007e007 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 -->8-- +--8<-- +e0790000000000000000 +0000e079000000000000 +00000000000000000000 +0000000000000000e079 +-->8-- +--8<-- +e0790000000000000000 +00000000000000000000 +00000000000000000000 +00000000000000000000 +-->8-- +--8<-- +e079e079000000000000 +e079e079000000000000 +00000000000000000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +0000e079e07900000000 +0000e079e07900000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000000000000000 +00000000000000000000 +0000000000000000e079 +-->8-- +--8<-- +00000000000000000000 +00000000e07900000000 +00000041e0fb00000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000e0fb00000000 +0000e0fbe0fb00000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000e0fb00000000 +00000000e0fb00000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000000000000000 +00000000e0fb00000000 +00000000000000000000 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e07de007e007 +e007e046e0fbe007e007 +e007e007e007e007e007 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e0fbe007e007 +e007e0fbe0fbe007e007 +e007e007e007e007e007 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e0fbe007e007 +e007e007e0fbe007e007 +e007e007e007e007e007 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e007e007e007 +e007e007e0fbe007e007 +e007e007e007e007e007 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +e0790000000000000000 +00000000000000000000 +00000000000000000000 +00000000000000000000 +-->8-- +--8<-- +e079e079000000000000 +e079e079000000000000 +00000000000000000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +0000e079e07900000000 +0000e079e07900000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000000000000000 +00000000000000000000 +0000000000000000e079 +-->8-- +--8<-- +00000000000000000000 +00000000e07900000000 +00000041e0fb00000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000e0fb00000000 +0000e0fbe0fb00000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000e0fb00000000 +00000000e0fb00000000 +00000000000000000000 +-->8-- +--8<-- +00000000000000000000 +00000000000000000000 +00000000e0fb00000000 +00000000000000000000 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e07de007e007 +e007e046e0fbe007e007 +e007e007e007e007e007 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e0fbe007e007 +e007e0fbe0fbe007e007 +e007e007e007e007e007 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e0fbe007e007 +e007e007e0fbe007e007 +e007e007e007e007e007 +-->8-- +--8<-- +e007e007e007e007e007 +e007e007e007e007e007 +e007e007e0fbe007e007 +e007e007e007e007e007 +-->8-- From cbf6c2282b11eb02a89a44c5335bdea0fbb939dc Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 09:13:02 +0000 Subject: [PATCH 66/81] Fix expected test values. --- tests/extmod/framebuf_alpha.py.exp | 66 +++++++++++++++--------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index 6ffb6a07c2363..0bc678d0dc5bc 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -451,80 +451,80 @@ e007e007e0fbe007e007 e007e007e007e007e007 -->8-- --8<-- -0000000000 -0000000000 -0000000000 -0000000000 +79e00000000000000000 +000079e0000000000000 +00000000000000000000 +000000000000000079e0 -->8-- --8<-- -e0790000000000000000 +79e00000000000000000 00000000000000000000 00000000000000000000 00000000000000000000 -->8-- --8<-- -e079e079000000000000 -e079e079000000000000 +79e079e0000000000000 +79e079e0000000000000 00000000000000000000 00000000000000000000 -->8-- --8<-- 00000000000000000000 -0000e079e07900000000 -0000e079e07900000000 +000079e079e000000000 +000079e079e000000000 00000000000000000000 -->8-- --8<-- 00000000000000000000 00000000000000000000 00000000000000000000 -0000000000000000e079 +000000000000000079e0 -->8-- --8<-- 00000000000000000000 -00000000e07900000000 -00000041e0fb00000000 +0000000079e000000000 +00004100fbe000000000 00000000000000000000 -->8-- --8<-- 00000000000000000000 -00000000e0fb00000000 -0000e0fbe0fb00000000 +00000000fbe000000000 +0000fbe0fbe000000000 00000000000000000000 -->8-- --8<-- 00000000000000000000 -00000000e0fb00000000 -00000000e0fb00000000 +00000000fbe000000000 +00000000fbe000000000 00000000000000000000 -->8-- --8<-- 00000000000000000000 00000000000000000000 -00000000e0fb00000000 +00000000fbe000000000 00000000000000000000 -->8-- --8<-- -e007e007e007e007e007 -e007e007e07de007e007 -e007e046e0fbe007e007 -e007e007e007e007e007 +07e007e007e007e007e0 +07e007e07de007e007e0 +07e046e0fbe007e007e0 +07e007e007e007e007e0 -->8-- --8<-- -e007e007e007e007e007 -e007e007e0fbe007e007 -e007e0fbe0fbe007e007 -e007e007e007e007e007 +07e007e007e007e007e0 +07e007e0fbe007e007e0 +07e0fbe0fbe007e007e0 +07e007e007e007e007e0 -->8-- --8<-- -e007e007e007e007e007 -e007e007e0fbe007e007 -e007e007e0fbe007e007 -e007e007e007e007e007 +07e007e007e007e007e0 +07e007e0fbe007e007e0 +07e007e0fbe007e007e0 +07e007e007e007e007e0 -->8-- --8<-- -e007e007e007e007e007 -e007e007e007e007e007 -e007e007e0fbe007e007 -e007e007e007e007e007 +07e007e007e007e007e0 +07e007e007e007e007e0 +07e007e0fbe007e007e0 +07e007e007e007e007e0 -->8-- From 1787b44132f85a206fafe5254cb3622693a549b4 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 10:03:47 +0000 Subject: [PATCH 67/81] More playing with tests and endianness. --- extmod/modframebuf.c | 4 ++-- tests/extmod/framebuf16.py | 4 ++++ tests/extmod/framebuf16.py.exp | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 1b0adaef552ec..789bd58923921 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -148,7 +148,7 @@ static uint16_t bs16(uint16_t x) { } static void rgb565_non_native_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { - uint16_t col16 = col; + uint16_t col16 = col & 0xffff; ((uint16_t *)fb->buf)[x + y * fb->stride] = bs16(col16); } @@ -158,7 +158,7 @@ static uint32_t rgb565_non_native_getpixel(const mp_obj_framebuf_t *fb, unsigned } static void rgb565_non_native_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - uint16_t col16 = col; + uint16_t col16 = col & 0xffff; col16 = bs16(col16); uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; while (h--) { diff --git a/tests/extmod/framebuf16.py b/tests/extmod/framebuf16.py index 30cf798c9c958..18643ad59892c 100644 --- a/tests/extmod/framebuf16.py +++ b/tests/extmod/framebuf16.py @@ -60,3 +60,7 @@ def printbuf(): fbuf.blit(fbuf2, -1, -1, 0x0000) fbuf.blit(fbuf2, 16, 16, 0x0000) printbuf() + + # check behavior of fill + fbuf.fill(0x00EE) + printbuf() diff --git a/tests/extmod/framebuf16.py.exp b/tests/extmod/framebuf16.py.exp index 6c869b9a6c003..270ea6d5f1894 100644 --- a/tests/extmod/framebuf16.py.exp +++ b/tests/extmod/framebuf16.py.exp @@ -56,6 +56,13 @@ bytearray(b'\xff\xff\xff\xff\xff\xff\xe0\x0e') bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -->8-- --8<-- +bytearray(b'\xee\x00\xee\x00\xee\x00\xee\x00') +bytearray(b'\xee\x00\xee\x00\xee\x00\xee\x00') +bytearray(b'\xee\x00\xee\x00\xee\x00\xee\x00') +bytearray(b'\xee\x00\xee\x00\xee\x00\xee\x00') +bytearray(b'\xee\x00\xee\x00\xee\x00\xee\x00') +-->8-- +--8<-- bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') @@ -112,3 +119,10 @@ bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') bytearray(b'\xff\xff\xff\xff\xff\xff\x0e\xe0') bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -->8-- ++--8<-- +bytearray(b'\x00\xee\x00\xee\x00\xee\x00\xee') +bytearray(b'\x00\xee\x00\xee\x00\xee\x00\xee') +bytearray(b'\x00\xee\x00\xee\x00\xee\x00\xee') +bytearray(b'\x00\xee\x00\xee\x00\xee\x00\xee') +bytearray(b'\x00\xee\x00\xee\x00\xee\x00\xee') +-->8-- From d0bb26dfc389556fee327c5f193040dbac22e289 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 10:15:44 +0000 Subject: [PATCH 68/81] Fix expected results. --- tests/extmod/framebuf16.py.exp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/extmod/framebuf16.py.exp b/tests/extmod/framebuf16.py.exp index 270ea6d5f1894..3792270da1f5e 100644 --- a/tests/extmod/framebuf16.py.exp +++ b/tests/extmod/framebuf16.py.exp @@ -119,7 +119,7 @@ bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') bytearray(b'\xff\xff\xff\xff\xff\xff\x0e\xe0') bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') -->8-- -+--8<-- +--8<-- bytearray(b'\x00\xee\x00\xee\x00\xee\x00\xee') bytearray(b'\x00\xee\x00\xee\x00\xee\x00\xee') bytearray(b'\x00\xee\x00\xee\x00\xee\x00\xee') From 97b9a9014de621da4ef75352913f0a7f45ddd2e3 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 11:00:03 +0000 Subject: [PATCH 69/81] Introspect setting of values in fill. --- extmod/modframebuf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 789bd58923921..61898dbcdca68 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -158,8 +158,11 @@ static uint32_t rgb565_non_native_getpixel(const mp_obj_framebuf_t *fb, unsigned } static void rgb565_non_native_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + printf("%u\n", col); uint16_t col16 = col & 0xffff; + printf("%u\n", col16); col16 = bs16(col16); + printf("%u\n", col16); uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; while (h--) { for (unsigned int ww = w; ww; --ww) { From 89785627d43426773c410a6da9c22b402cf632d3 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 12:02:10 +0000 Subject: [PATCH 70/81] Make it clear when doing big vs little endian fill. --- extmod/modframebuf.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 61898dbcdca68..ce281fae4831c 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -514,6 +514,11 @@ static mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, static mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t col = mp_obj_get_int(col_in); + if (self->format == FRAMEBUF_RGB565_BE) { + printf("big fill\n"); + } else if (self->format == FRAMEBUF_RGB565_BE) { + printf("little fill\n"); + } formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col); return mp_const_none; } From 4af3cd51c3c9a5cae1b19d624d7dec79b7100ced Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 12:20:06 +0000 Subject: [PATCH 71/81] More clarity with big/little calls. --- extmod/modframebuf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index ce281fae4831c..c36d80f21f95a 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -515,9 +515,9 @@ static mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t col = mp_obj_get_int(col_in); if (self->format == FRAMEBUF_RGB565_BE) { - printf("big fill\n"); - } else if (self->format == FRAMEBUF_RGB565_BE) { - printf("little fill\n"); + printf("big fill: %ld\n", col); + } else if (self->format == FRAMEBUF_RGB565_LE) { + printf("little fill: %ld\n", col); } formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col); return mp_const_none; From 6f1470b0d518654561652d6be52ae03476719822 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 12:42:06 +0000 Subject: [PATCH 72/81] Fix native module; start backing out changes to main module. --- examples/natmod/framebuf/framebuf.c | 10 +++------- extmod/modframebuf.c | 8 -------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c index 8e4b497fa619e..9cc7364bf6dd7 100644 --- a/examples/natmod/framebuf/framebuf.c +++ b/examples/natmod/framebuf/framebuf.c @@ -45,13 +45,9 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a mp_store_global(MP_QSTR_MVLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB)); mp_store_global(MP_QSTR_MONO_VLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB)); mp_store_global(MP_QSTR_RGB565, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); - #if MP_ENDIANNESS_LITTLE - mp_store_global(MP_QSTR_RGB565_LE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); - mp_store_global(MP_QSTR_RGB565_BE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); - #else - mp_store_global(MP_QSTR_RGB565_BE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565)); - mp_store_global(MP_QSTR_RGB565_LE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); - #endif + mp_store_global(MP_QSTR_RGB565_BS, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BS)); + mp_store_global(MP_QSTR_RGB565_BE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_BE)); + mp_store_global(MP_QSTR_RGB565_LE, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565_LE)); mp_store_global(MP_QSTR_GS2_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS2_HMSB)); mp_store_global(MP_QSTR_GS4_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS4_HMSB)); mp_store_global(MP_QSTR_GS8, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS8)); diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index c36d80f21f95a..789bd58923921 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -158,11 +158,8 @@ static uint32_t rgb565_non_native_getpixel(const mp_obj_framebuf_t *fb, unsigned } static void rgb565_non_native_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - printf("%u\n", col); uint16_t col16 = col & 0xffff; - printf("%u\n", col16); col16 = bs16(col16); - printf("%u\n", col16); uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; while (h--) { for (unsigned int ww = w; ww; --ww) { @@ -514,11 +511,6 @@ static mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, static mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) { mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t col = mp_obj_get_int(col_in); - if (self->format == FRAMEBUF_RGB565_BE) { - printf("big fill: %ld\n", col); - } else if (self->format == FRAMEBUF_RGB565_LE) { - printf("little fill: %ld\n", col); - } formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col); return mp_const_none; } From f728e2ab87199a1705407e7e963a726516864b72 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 13:00:03 +0000 Subject: [PATCH 73/81] Revert to earlier good version. --- extmod/modframebuf.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 789bd58923921..3742989b9b644 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -59,9 +59,9 @@ typedef struct _mp_framebuf_p_t { // constants for formats #define FRAMEBUF_MVLSB (0) #define FRAMEBUF_RGB565 (1) -#define FRAMEBUF_RGB565_BS (9) #define FRAMEBUF_RGB565_BE (7) #define FRAMEBUF_RGB565_LE (8) +#define FRAMEBUF_RGB565_BS (9) #define FRAMEBUF_GS2_HMSB (5) #define FRAMEBUF_GS4_HMSB (2) #define FRAMEBUF_GS8 (6) @@ -143,27 +143,22 @@ static void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsign } } -static uint16_t bs16(uint16_t x) { - return (x >> 8) | ((x & 0xff) << 8); -} - static void rgb565_non_native_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { - uint16_t col16 = col & 0xffff; - ((uint16_t *)fb->buf)[x + y * fb->stride] = bs16(col16); + col = __builtin_bswap16(col); + ((uint16_t *)fb->buf)[x + y * fb->stride] = col; } static uint32_t rgb565_non_native_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { - uint32_t col = bs16(((uint16_t *)fb->buf)[x + y * fb->stride]); - return col; + uint32_t col = ((uint16_t *)fb->buf)[x + y * fb->stride]; + return __builtin_bswap16(col); } static void rgb565_non_native_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { - uint16_t col16 = col & 0xffff; - col16 = bs16(col16); + col = __builtin_bswap16(col); uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; while (h--) { for (unsigned int ww = w; ww; --ww) { - *b++ = col16; + *b++ = col; } b += fb->stride - w; } @@ -338,9 +333,8 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 case FRAMEBUF_RGB565_BS: // The colors are specified in non-native endianness in Python. // We need to byteswap to get native endianness. - col16 = col & 0xFFFF; - col16 = bs16(col); - pix_col = bs16(pix_col); + col16 = __builtin_bswap16(col & 0xFFFF); + pix_col = __builtin_bswap16(pix_col); pix_col_struct = *(urgb565 *)&pix_col; col_struct = *(urgb565 *)&col16; col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); @@ -351,7 +345,7 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); col = *(uint16_t *)&col_struct; // And byteswap back to get non-native endianness. - col = bs16(col); + col = __builtin_bswap16(col); break; default: col = alpha_blend(pix_col, col, alpha); From 554251370947b6a07cc6b1ae86dcf9983ebba0bd Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 13:39:55 +0000 Subject: [PATCH 74/81] Tighten up code, improve documentation. --- docs/library/framebuf.rst | 10 ++++++---- extmod/modframebuf.c | 39 +++++++++++++++------------------------ 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index d8fdd16ba1ce5..e755493d711db 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -152,7 +152,7 @@ Other methods Shift the contents of the FrameBuffer by the given vector. This may leave a footprint of the previous colors in the FrameBuffer. -.. method:: FrameBuffer.blit(fbuf, x, y, key=-1, palette=None, alpha=None) +.. method:: FrameBuffer.blit(fbuf, x, y, key=-1, palette=None, alpha=0xFF) Draw another FrameBuffer on top of the current one at the given coordinates. @@ -298,8 +298,10 @@ Constants ``MICROPY_PY_FRAMEBUF_ALPHA`` flag; or * replacing the use of ``RGB565`` format with the byte-swapped - ``RGB565_BS`` format. + ``RGB565_BS`` format; or + + * using ``RGB565_BE`` or ``RGB565_LE`` as appropriate for the target + hardware and change color values to use native byte-order. New code which needs to support buffers of a particular byte-order should - simply use ``RGB565_BE`` or ``RGB565_LE`` as appropriate, and supply colors - in native RGB565 format. + use the last option. diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 3742989b9b644..59ebaa8973b01 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -311,41 +311,32 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 return; } else if (alpha < 0xff) { uint16_t pix_col = formats[fb->format].getpixel(fb, x, y); - // for RGB565 - uint16_t col16 = col & 0xFFFF; - urgb565 pix_col_struct = *(urgb565 *)&pix_col; - urgb565 col_struct = *(urgb565 *)&col16; + uint16_t col16 = col; + urgb565 pix_col_struct; + urgb565 col_struct; switch (fb->format) { - case FRAMEBUF_RGB565: - case FRAMEBUF_RGB565_BE: - case FRAMEBUF_RGB565_LE: - col16 = col & 0xFFFF; - pix_col_struct = *(urgb565 *)&pix_col; - col_struct = *(urgb565 *)&col16; - col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); - col_struct.rgb.g = alpha_blend(pix_col_struct.rgb.g, col_struct.rgb.g, alpha); - col_struct.rgb.b = alpha_blend(pix_col_struct.rgb.b, col_struct.rgb.b, alpha); - col_struct.rgb.r = MIN(col_struct.rgb.r, 0b11111); - col_struct.rgb.g = MIN(col_struct.rgb.g, 0b111111); - col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); - col = *(uint16_t *)&col_struct; - break; case FRAMEBUF_RGB565_BS: // The colors are specified in non-native endianness in Python. // We need to byteswap to get native endianness. - col16 = __builtin_bswap16(col & 0xFFFF); + col16 = __builtin_bswap16(col); pix_col = __builtin_bswap16(pix_col); + // fall through to other RGB cases... + case FRAMEBUF_RGB565: + case FRAMEBUF_RGB565_BE: + case FRAMEBUF_RGB565_LE: + // convert to bit-packed rgb struct pix_col_struct = *(urgb565 *)&pix_col; col_struct = *(urgb565 *)&col16; + // blend channels col_struct.rgb.r = alpha_blend(pix_col_struct.rgb.r, col_struct.rgb.r, alpha); col_struct.rgb.g = alpha_blend(pix_col_struct.rgb.g, col_struct.rgb.g, alpha); col_struct.rgb.b = alpha_blend(pix_col_struct.rgb.b, col_struct.rgb.b, alpha); - col_struct.rgb.r = MIN(col_struct.rgb.r, 0b11111); - col_struct.rgb.g = MIN(col_struct.rgb.g, 0b111111); - col_struct.rgb.b = MIN(col_struct.rgb.b, 0b11111); + // convert back to int col = *(uint16_t *)&col_struct; - // And byteswap back to get non-native endianness. - col = __builtin_bswap16(col); + if (fb->format == FRAMEBUF_RGB565_BS) { + // byteswap back to get non-native endianness for storage. + col = __builtin_bswap16(col); + } break; default: col = alpha_blend(pix_col, col, alpha); From abe2f5d964ddc45e48ba10e94fa0668d80a4b268 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 13:57:36 +0000 Subject: [PATCH 75/81] Fic error with alpha in 1-bit mask mode. Fix codestyle. --- extmod/modframebuf.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 59ebaa8973b01..496855c92e9ef 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -320,7 +320,7 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 // We need to byteswap to get native endianness. col16 = __builtin_bswap16(col); pix_col = __builtin_bswap16(pix_col); - // fall through to other RGB cases... + // fall through to other RGB cases... case FRAMEBUF_RGB565: case FRAMEBUF_RGB565_BE: case FRAMEBUF_RGB565_LE: @@ -1329,11 +1329,7 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) { col = getpixel(&palette, col, 0); } if (alpha_mul) { - #if MICROPY_PY_FRAMEBUF_ALPHA alpha = getpixel(&mask, cx1, y1) * alpha_mul; - #else // MICROPY_PY_FRAMEBUF_ALPHA - alpha = getpixel(&mask, cx1, y1); - #endif // MICROPY_PY_FRAMEBUF_ALPHA } if (col != (uint32_t)key && alpha) { setpixel(self, cx0, y0, col, alpha); From 535f6d3b6c5b8c482bd92fffedb1fe967c67d94e Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 14:11:27 +0000 Subject: [PATCH 76/81] Some builds unhappy with fall-through switch, --- extmod/modframebuf.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 496855c92e9ef..e75b13f69d0ef 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -315,15 +315,16 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 urgb565 pix_col_struct; urgb565 col_struct; switch (fb->format) { - case FRAMEBUF_RGB565_BS: - // The colors are specified in non-native endianness in Python. - // We need to byteswap to get native endianness. - col16 = __builtin_bswap16(col); - pix_col = __builtin_bswap16(pix_col); - // fall through to other RGB cases... case FRAMEBUF_RGB565: + case FRAMEBUF_RGB565_BS: case FRAMEBUF_RGB565_BE: case FRAMEBUF_RGB565_LE: + if (fb->format == FRAMEBUF_RGB565_BS) { + // The colors are specified in non-native endianness in Python. + // We need to byteswap to get native endianness. + col16 = __builtin_bswap16(col); + pix_col = __builtin_bswap16(pix_col); + } // convert to bit-packed rgb struct pix_col_struct = *(urgb565 *)&pix_col; col_struct = *(urgb565 *)&col16; From 2b55b7d81c212b9fc857f30e7ee029c5a07d2911 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 15:38:57 +0000 Subject: [PATCH 77/81] Clean up code comments. --- extmod/modframebuf.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index e75b13f69d0ef..aad5c612e3176 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -947,16 +947,16 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // Explanation in the context of embedded systems: https://aykevl.nl/2024/02/tinygl-polygon/ // We don't use a lookup table. - // Build a table of edges and data - // The table consists of entries (y_min, y_max, x_min, 1/slope) and is ordered by y_min. - // The value of 1/slope is stored with 12 bits of fixed precision. - // Horizontal lines are ignored. - // Increase alpha for mono buffers to get sharp corners. if (self->format == FRAMEBUF_MHLSB || self->format == FRAMEBUF_MHMSB || self->format == FRAMEBUF_MVLSB) { alpha *= 2; } + // Build an ordered table of edges and data + // The table consists of entries (y_min, y_max, x_min, 1/slope) and is ordered by y_min. + // The value of 1/slope is stored with 12 bits of fixed precision. + // Horizontal lines are ignored. + edge edge_table[n_poly]; int n_edges = 0; mp_int_t px1 = x + poly_int(&bufinfo, 2 * n_poly - 2); @@ -1001,7 +1001,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { y_start = MAX(0, y_start); y_end = MIN(self->height, y_end); - // Track edges which intersect scanlines. + // Track which edges are can possibly intersect subsample scanlines. int last_edge_index = 0; for (mp_int_t row = y_start; row < y_end; row++) { @@ -1010,6 +1010,8 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { ++last_edge_index; } + // Build an ordered table of intersection locations and masks on subsample scanlines. + // The table is ordered by pixel column and holds the mask for that pixel. int n_nodes = 0; node nodes[2 * last_edge_index]; @@ -1021,7 +1023,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // For each edge... edge *e = &(edge_table[i]); if ((e->y2 * (1 << 2)) < y1) { - // Edge below subsample line. + // Edge is below subsample line, ignore. continue; } else if ((e->y1 * (1 << 2)) > y1) { // Edge above subsample line (can happen for lower subsample line at start of edge). @@ -1041,7 +1043,7 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { column = ~((~x_adjusted) >> 12); } if (column >= self->width) { - // Outside of buffer on the high end, don't care about these points, + // Outside of buffer width: don't care about these points for this row, // but need to bump the x-value in case line eventually comes inside the buffer. e->x1 += (e->slope >> 1); continue; @@ -1063,31 +1065,36 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { continue; } - // Now draw the pixels. + // Now draw the pixels by running through the intersection nodes. uint32_t mask = 0; node current; for (int i = 0; i < n_nodes; ++i) { current = nodes[i]; - // Update the mask + // Update the mask. mask ^= current.mask; if (current.x >= 0) { - // pixel is inside the buffer, so draw the pixel + // The pixel is inside the buffer, so draw the pixel. + // The alpha of pixel is scaled by the number of bits in the mask (0-8, inclusive), + // So we multiply popcount by 255/9 with one bit of fixed point precision. setpixel(self, current.x, row, col, alpha_mult((popcount(mask) * 0b1001001) >> 1, alpha)); } - // extend mask by last bits + // Extend mask by last bits of each subscanline. mask = (mask & 0b00010001) * 0b1111; if (mask) { - // fill with run of pixels with same mask - can be fast + // Fill with a run of pixels with same mask - can be fast. + // Width is either distance to next node, or to width of buffer if no more nodes. mp_int_t width; if (i + 1 < n_nodes) { width = nodes[i + 1].x - current.x - 1; } else { width = self->width - current.x - 1; } + // Use fill_rect as it accounts for rectangles wider than buffer. + // See above for discussion of alpha computation. fill_rect(self, current.x + 1, row, width, 1, col, alpha_mult((popcount(mask) * 0b1001001) >> 1, alpha)); } } From e71650c9b2d1435202a6f7eeee0f3400c03162ee Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Thu, 27 Nov 2025 15:43:16 +0000 Subject: [PATCH 78/81] Remove some unused variables. --- extmod/modframebuf.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index aad5c612e3176..ba3921e9546c9 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -963,8 +963,6 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { mp_int_t py1 = y + poly_int(&bufinfo, 2 * n_poly - 1); mp_int_t y_start = py1; mp_int_t y_end = py1 + 1; - mp_int_t x_start = px1; - mp_int_t x_end = px1; for (int i = 0; i < n_poly; ++i) { mp_int_t px2 = x + poly_int(&bufinfo, 2 * i); mp_int_t py2 = y + poly_int(&bufinfo, 2 * i + 1); @@ -972,8 +970,6 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { // track the min and max extent of the polygon y_start = MIN(y_start, py2); y_end = MAX(y_end, py2 + 1); - x_start = MIN(x_start, px2); - x_end = MAX(x_end, px2); if (py1 < py2) { // going up From bd36222f54eae355bb4508636fd5a22811fe4859 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 28 Nov 2025 09:37:59 +0000 Subject: [PATCH 79/81] extmod/modframebuf: Add tests to improve coverage of framebuf changes. This is mainly adding tests for code branches not currently tested: - exceptions caused by bad masks in blit() - fast exit from certain functons when alpha is 0 - all lines are horizontal in polygon fill (nothing to draw) It also removes a small piece of dead code in polygon outline rendering.. Signed-off-by: Corran Webster --- extmod/modframebuf.c | 4 -- tests/extmod/framebuf_alpha.py | 72 ++++++++++++++++++++++ tests/extmod/framebuf_alpha.py.exp | 26 ++++++++ tests/extmod/framebuf_polygon_alpha.py | 10 +++ tests/extmod/framebuf_polygon_alpha.py.exp | 28 ++++++++- 5 files changed, 135 insertions(+), 5 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index ba3921e9546c9..68c28272b2c9b 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -1107,10 +1107,6 @@ static mp_obj_t framebuf_poly(size_t n_args, const mp_obj_t *args_in) { px1 = px2; py1 = py2; } while (i >= 0); - // draw endpoint of last line if polygon is not closed - if (px1 != poly_int(&bufinfo, 0) || py1 != poly_int(&bufinfo, 1)) { - setpixel_checked(self, x + px1, y + py1, col, 1, alpha); - } } return mp_const_none; diff --git a/tests/extmod/framebuf_alpha.py b/tests/extmod/framebuf_alpha.py index 57bdbf6a838ff..445cebde7f522 100644 --- a/tests/extmod/framebuf_alpha.py +++ b/tests/extmod/framebuf_alpha.py @@ -91,6 +91,11 @@ def printbuf(bpp=1): fbuf.blit(fbuf2, x, y, -1, None, 0x7F) printbuf() +# Blit another FrameBuffer with 0 alpha (test no-op branch). +fbuf.fill(0) +fbuf.blit(fbuf2, 1, 1, -1, None, 0x00) +printbuf() + # Blit another FrameBuffer, with alpha mask. alphas = [[0, 0x3F], [0x7F, 0xFF]] for bpp, format in [ @@ -125,12 +130,54 @@ def printbuf(bpp=1): fbuf.blit(fbuf2, 1, 1, -1, None, mask) printbuf() +# Try to blit with wrong-shaped mask (ValueError). +alphas = [[0, 0x3F], [0x7F, 0xFF]] +mask = framebuf.FrameBuffer(bytearray(6), 3, 2, framebuf.GS8) +for x in [0, 1]: + for y in [0, 1]: + mask.pixel(x, y, alphas[x][y] >> (8 - bpp)) +fbuf.fill(0x00) +try: + fbuf.blit(fbuf2, 1, 1, -1, None, mask) +except ValueError as exc: + print(exc) +except Exception as exc: + print("Unexpected error:", exc) +else: + print("No Error") + +# Try to blit with color mask (ValueError). +mask = framebuf.FrameBuffer(bytearray(8), 2, 2, framebuf.RGB565) +fbuf.fill(0x00) +try: + fbuf.blit(fbuf2, 1, 1, -1, None, mask) +except ValueError as exc: + print(exc) +except Exception as exc: + print("Unexpected error:", exc) +else: + print("No Error") + # text at various locations with alpha. for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): fbuf.fill(0) fbuf.text("x", x, y, 0x7F, 0x7F) printbuf() +# drawing with alpha 0 does nothing +fbuf.fill(0) + +fbuf.pixel(1, 1, 0x7F, 0) +fbuf.hline(1, 1, 2, 0x7F, 0) +fbuf.vline(1, 1, 2, 0x7F, 0) +fbuf.line(1, 1, 2, 2, 0x7F, 0) +fbuf.rect(1, 1, 2, 2, 0x7F, True, 0) +fbuf.rect(1, 1, 2, 2, 0x7F, False, 0) +fbuf.ellipse(1, 1, 2, 2, 0x7F, True, 0xF, 0) +fbuf.text("x", 1, 1, 0x7F, 0) + +printbuf() + # Ellipse w = 30 h = 30 @@ -213,3 +260,28 @@ def printbuf(bpp=1): fbuf.fill(0b00000_111111_00000) fbuf.blit(fbuf2, 1, 1, -1, None, mask) printbuf(2) + +# Basic tests of native and "byte-swapped" formats. +# Ensure order in output is little-endian, then big-endian +if sys.byteorder == "big": + formats = [framebuf.RGB565_BS, framebuf.RGB565] +else: + formats = [framebuf.RGB565, framebuf.RGB565_BS] + +for format in formats: + w = 5 + h = 4 + buf = bytearray(2 * w * h) + fbuf = framebuf.FrameBuffer(buf, w, h, format) + + if format == framebuf.RGB565_BS: + # Python provides byte-swapped color values. + col = 0b11100000_11111011 + else: + col = 0b11111011_11100000 + + # set pixel at various locations with alpha. + fbuf.fill(0) + for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.pixel(x, y, col, 0x7F) + printbuf(2) diff --git a/tests/extmod/framebuf_alpha.py.exp b/tests/extmod/framebuf_alpha.py.exp index 0bc678d0dc5bc..eda56564c1a6f 100644 --- a/tests/extmod/framebuf_alpha.py.exp +++ b/tests/extmod/framebuf_alpha.py.exp @@ -174,6 +174,12 @@ -->8-- --8<-- 0000000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 00003f0000 001f7f0000 0000000000 @@ -220,6 +226,8 @@ efefefefef efef7fefef efefefefef -->8-- +Mask and source different sizes. +invalid mask format --8<-- 0000000000 3f3f00003f @@ -245,6 +253,12 @@ efefefefef 0000000000 -->8-- --8<-- +0000000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000 @@ -528,3 +542,15 @@ e007e007e007e007e007 07e007e0fbe007e007e0 07e007e007e007e007e0 -->8-- +--8<-- +e0790000000000000000 +0000e079000000000000 +00000000000000000000 +0000000000000000e079 +-->8-- +--8<-- +79e00000000000000000 +000079e0000000000000 +00000000000000000000 +000000000000000079e0 +-->8-- diff --git a/tests/extmod/framebuf_polygon_alpha.py b/tests/extmod/framebuf_polygon_alpha.py index 1a1b81def0431..10d2ef2d66d79 100644 --- a/tests/extmod/framebuf_polygon_alpha.py +++ b/tests/extmod/framebuf_polygon_alpha.py @@ -198,12 +198,14 @@ def print_buffer_gs2(fbuf, width, height): poly_one = array("h", (20, 20)) # Will draw a single point. poly_two = array("h", (10, 10, 5, 5)) # Will draw a single line. poly_wrong_length = array("h", (2, 2, 4)) # Will round down to one point. +poly_all_horizontal = array("h", [20, 10, 15, 10, 20, 10]) # horizontal, empty interior fbuf.fill(0) fbuf.poly(0, 0, poly_empty, col) fbuf.poly(0, 0, poly_one, col) fbuf.poly(0, 0, poly_two, col) fbuf.poly(0, 0, poly_wrong_length, col) +fbuf.poly(0, 0, poly_all_horizontal, col) print_buffer(buf, w, h) print() @@ -212,6 +214,7 @@ def print_buffer_gs2(fbuf, width, height): fbuf.poly(0, 0, poly_one, col, True) fbuf.poly(0, 0, poly_two, col, True) fbuf.poly(0, 0, poly_wrong_length, col, True) +fbuf.poly(0, 0, poly_all_horizontal, col, True) print_buffer(buf, w, h) print() @@ -286,6 +289,13 @@ def print_buffer_gs2(fbuf, width, height): print_buffer(buf, w, h) print() +# Draw with alpha 0. +fbuf.fill(0) +fbuf.poly(0, 0, poly, col, False, 0) +fbuf.poly(15, -2, poly_reversed, col, False, 0) +print_buffer(buf, w, h) +print() + # Test 1-bit cases w = 30 h = 25 diff --git a/tests/extmod/framebuf_polygon_alpha.py.exp b/tests/extmod/framebuf_polygon_alpha.py.exp index bd2565fb94f60..378839f455452 100644 --- a/tests/extmod/framebuf_polygon_alpha.py.exp +++ b/tests/extmod/framebuf_polygon_alpha.py.exp @@ -372,7 +372,7 @@ ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· @@ -710,6 +710,32 @@ ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** ** ** ** .. .. .. .. .. ** ** ** ** .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ** .. .. ** .. .. .. .. .. ** .. .. ** .. .. From ea5b147640a039b29778786752bb833ead84d91a Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 28 Nov 2025 10:12:23 +0000 Subject: [PATCH 80/81] tests/extmod/framebuf_polygon_alpha: Add tests for offscreen polys. This adds a handful of tests to ensure that off-screen polygons don't draw anything. Signed-off-by: Corran Webster --- tests/extmod/framebuf_polygon_alpha.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/extmod/framebuf_polygon_alpha.py b/tests/extmod/framebuf_polygon_alpha.py index 10d2ef2d66d79..66be270e1af95 100644 --- a/tests/extmod/framebuf_polygon_alpha.py +++ b/tests/extmod/framebuf_polygon_alpha.py @@ -199,6 +199,10 @@ def print_buffer_gs2(fbuf, width, height): poly_two = array("h", (10, 10, 5, 5)) # Will draw a single line. poly_wrong_length = array("h", (2, 2, 4)) # Will round down to one point. poly_all_horizontal = array("h", [20, 10, 15, 10, 20, 10]) # horizontal, empty interior +poly_offscreen_left = array("h", [-5, 5, -5, 10, -10, 10, -10, 5]) # nothing to draw +poly_offscreen_right = array("h", [35, 5, 35, 10, 40, 10, 40, 5]) # nothing to draw +poly_offscreen_top = array("h", [5, -5, 5, -10, 10, -10, 10, -5]) # nothing to draw +poly_offscreen_bottom = array("h", [5, 40, 5, 45, 10, 45, 10, 40]) # nothing to draw fbuf.fill(0) fbuf.poly(0, 0, poly_empty, col) @@ -206,6 +210,10 @@ def print_buffer_gs2(fbuf, width, height): fbuf.poly(0, 0, poly_two, col) fbuf.poly(0, 0, poly_wrong_length, col) fbuf.poly(0, 0, poly_all_horizontal, col) +fbuf.poly(0, 0, poly_offscreen_left, col) +fbuf.poly(0, 0, poly_offscreen_right, col) +fbuf.poly(0, 0, poly_offscreen_top, col) +fbuf.poly(0, 0, poly_offscreen_bottom, col) print_buffer(buf, w, h) print() @@ -215,6 +223,10 @@ def print_buffer_gs2(fbuf, width, height): fbuf.poly(0, 0, poly_two, col, True) fbuf.poly(0, 0, poly_wrong_length, col, True) fbuf.poly(0, 0, poly_all_horizontal, col, True) +fbuf.poly(0, 0, poly_offscreen_left, col, True) +fbuf.poly(0, 0, poly_offscreen_right, col, True) +fbuf.poly(0, 0, poly_offscreen_top, col, True) +fbuf.poly(0, 0, poly_offscreen_bottom, col, True) print_buffer(buf, w, h) print() From 6d168d6340e17a4421d0dc8bd677cec7f919131a Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 28 Nov 2025 11:37:38 +0000 Subject: [PATCH 81/81] extmod/modframebuf: Rationalize RGB565 modes. Rather than having separate big-endian and little-endian modes at the C level use native and non-native, allowing RGB565 to be the same as one of RGB565_LE or RGB565_BE as appropriate. Signed-off-by: Corran Webster --- extmod/modframebuf.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 68c28272b2c9b..a090c341de45d 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -59,9 +59,8 @@ typedef struct _mp_framebuf_p_t { // constants for formats #define FRAMEBUF_MVLSB (0) #define FRAMEBUF_RGB565 (1) -#define FRAMEBUF_RGB565_BE (7) -#define FRAMEBUF_RGB565_LE (8) -#define FRAMEBUF_RGB565_BS (9) +#define FRAMEBUF_RGB565_NN (7) +#define FRAMEBUF_RGB565_BS (8) #define FRAMEBUF_GS2_HMSB (5) #define FRAMEBUF_GS4_HMSB (2) #define FRAMEBUF_GS8 (6) @@ -262,13 +261,7 @@ static mp_framebuf_p_t formats[] = { [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, [FRAMEBUF_RGB565_BS] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, - #if MP_ENDIANNESS_LITTLE - [FRAMEBUF_RGB565_LE] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, - [FRAMEBUF_RGB565_BE] = {rgb565_non_native_setpixel, rgb565_non_native_getpixel, rgb565_non_native_fill_rect}, - #else // MP_ENDIANNESS_LITTLE - [FRAMEBUF_RGB565_BE] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, - [FRAMEBUF_RGB565_LE] = {rgb565_non_native_setpixel, rgb565_non_native_getpixel, rgb565_non_native_fill_rect}, - #endif // MP_ENDIANNESS_LITTLE + [FRAMEBUF_RGB565_NN] = {rgb565_non_native_setpixel, rgb565_non_native_getpixel, rgb565_non_native_fill_rect}, [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect}, [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect}, [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect}, @@ -317,8 +310,7 @@ static void setpixel(const mp_obj_framebuf_t *fb, mp_int_t x, mp_int_t y, uint32 switch (fb->format) { case FRAMEBUF_RGB565: case FRAMEBUF_RGB565_BS: - case FRAMEBUF_RGB565_BE: - case FRAMEBUF_RGB565_LE: + case FRAMEBUF_RGB565_NN: if (fb->format == FRAMEBUF_RGB565_BS) { // The colors are specified in non-native endianness in Python. // We need to byteswap to get native endianness. @@ -450,8 +442,7 @@ static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, break; case FRAMEBUF_RGB565: case FRAMEBUF_RGB565_BS: - case FRAMEBUF_RGB565_BE: - case FRAMEBUF_RGB565_LE: + case FRAMEBUF_RGB565_NN: bpp = 16; break; default: @@ -1476,8 +1467,13 @@ static const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, { MP_ROM_QSTR(MP_QSTR_RGB565_BS), MP_ROM_INT(FRAMEBUF_RGB565_BS) }, - { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565_BE) }, - { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565_LE) }, + #if MP_ENDIANNESS_LITTLE + { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565_NN) }, + #else // MP_ENDIANNESS_LITTLE + { MP_ROM_QSTR(MP_QSTR_RGB565_LE), MP_ROM_INT(FRAMEBUF_RGB565_NN) }, + { MP_ROM_QSTR(MP_QSTR_RGB565_BE), MP_ROM_INT(FRAMEBUF_RGB565) }, + #endif // MP_ENDIANNESS_LITTLE { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) },