From f482abb4719120c46596ecb777ce547c10811610 Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:20:31 -0500 Subject: [PATCH] blit: accept MicroPython's tuple source in the bounding box framebuf.blit takes (buffer, width, height, format), and FrameBuffer.blit passed it through to the base blit fine, then read source.width for the Area it returns and raised AttributeError. The spectrum example hit it on python.exe with the source tree on the path. --- lib/pygraphics/_blit_hooks.py | 19 ++++++++++--------- tests/test_framebuf_plus.py | 7 +++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/pygraphics/_blit_hooks.py b/lib/pygraphics/_blit_hooks.py index 1a7af2a..0461dcf 100644 --- a/lib/pygraphics/_blit_hooks.py +++ b/lib/pygraphics/_blit_hooks.py @@ -43,22 +43,23 @@ def key_to_bytes(key, bpp): def clip_blit_bounds(canvas, source, x, y): """Return clipped destination/source origin as ``(x0, y0, w, h, src_x, src_y)``. - Returns ``None`` when the blit is fully outside the canvas. + Returns ``None`` when the blit is fully outside the canvas. ``source`` may + be a FrameBuffer or MicroPython's tuple form ``(buffer, width, height, + format[, stride])``, which ``framebuf.blit`` accepts too. """ - if ( - (-x >= source.width) - or (-y >= source.height) - or (x >= canvas.width) - or (y >= canvas.height) - ): + if isinstance(source, tuple): + src_w, src_h = source[1], source[2] + else: + src_w, src_h = source.width, source.height + if (-x >= src_w) or (-y >= src_h) or (x >= canvas.width) or (y >= canvas.height): return None x0 = max(0, x) y0 = max(0, y) src_x = max(0, -x) src_y = max(0, -y) - w = min(canvas.width, x + source.width) - x0 - h = min(canvas.height, y + source.height) - y0 + w = min(canvas.width, x + src_w) - x0 + h = min(canvas.height, y + src_h) - y0 return x0, y0, w, h, src_x, src_y diff --git a/tests/test_framebuf_plus.py b/tests/test_framebuf_plus.py index d53245c..fd77e95 100644 --- a/tests/test_framebuf_plus.py +++ b/tests/test_framebuf_plus.py @@ -93,6 +93,13 @@ def test_blit_returns_area(self): self.assertEqual(self.fb.blit(src, 2, 2), Area(2, 2, 4, 4)) self.assertEqual(self.fb.pixel(2, 2), 0xABCD) + def test_blit_tuple_source_returns_area(self): + # MicroPython's framebuf.blit takes (buffer, width, height, format). + buf = bytearray(b"\xcd\xab" * 12) + self.assertEqual(self.fb.blit((buf, 4, 3, RGB565), 2, 2), Area(2, 2, 4, 3)) + self.assertEqual(self.fb.pixel(2, 2), 0xABCD) + self.assertIsNone(self.fb.blit((buf, 4, 3, RGB565), -4, 0)) + def test_scroll_returns_area(self): self.fb.fill_rect(0, 0, 4, 4, 0xFFFF) self.assertEqual(self.fb.scroll(1, 0), Area(0, 0, 16, 16))