Blit
blit_buffer() stamps a whole block of pixels onto the display in one shot. Draw a sprite once
into a buffer in RAM, then copy it wherever — and however many times — you need it.
1 | |
This is also where the color display's memory budget shows up for the first time, and the arithmetic explains something about this entire kit — more sharply here than on the smaller smartwatch kit, because every screen dimension is 1.5 times bigger, and area (which is what a byte count actually tracks) scales by 1.5 squared.
Two Bytes Per Pixel Changes Everything
On the OLED, a sprite was one bit per pixel. Here it is two bytes — sixteen times bigger.
| Sprite | OLED (1 bit/px) | This display (RGB565, 2 bytes/px) |
|---|---|---|
| A 96 × 72 eye | 864 bytes | 13,824 bytes |
| A full screen | 1,024 bytes | 259,200 bytes |
| RP2040 RAM, total | 264 KB | 264 KB |
Look at that last column. A full-screen buffer would be 259,200 bytes out of the roughly 270,336
bytes (264 KB) an RP2040 has — about 96% of it, before MicroPython's own interpreter, heap, and
your program take their share. The smaller smartwatch kit hit the same wall at a gentler angle: its
240 × 240 buffer would have cost 115,200 bytes, only about 43% of the same chip's RAM. Scaling every
screen dimension by 1.5 scaled the byte count by 2.25, and that difference is enough to turn
"expensive" into "impossible." That is the real reason this driver has no frame buffer, and the
reason there is no show() anywhere in this kit.
Every Constraint in This Kit Comes From That Number
No frame buffer means no show(), which means every drawing call lands on the glass immediately, which means erasing has to be careful and animations only redraw what moved. One arithmetic fact, and the whole kit is shaped by it.
blit_buffer() Is Opaque
framebuf's blit() took a key color to skip, so you could stamp a sprite over a background and
let the background show through. This driver has no such option — blit_buffer() sends a solid
rectangle of pixels, background and all.
So shapes.blit_keyed() does it the hard way: it walks each row, finds the runs of non-key pixels,
and sends only those. Read it and you will know exactly what framebuf was doing for you.
display.blit_buffer() |
shapes.blit_keyed() |
|
|---|---|---|
| What it sends | One command, then the whole block | One command per run, per row |
| Background pixels | Painted over whatever was there | Skipped |
| Speed | Fast | Noticeably slower |
| Use it when | The sprite sits on a known background | The sprite must sit on top of something |
Sample Program Code
The program builds one eye in an off-screen buffer, then stamps it four times — twice on plain black, and twice over a striped background so you can see exactly what each kind of blit covers up. The eye sprite is 96 × 72, the smartwatch kit's 64 × 48 scaled by 1.5.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | |
Here's what that program draws on the display:

Compare the two bottom eyes carefully. The left one carries a black rectangle with it and punches a hole in the stripes. The right one lets the stripes run right up to the edge of the eye, because every black pixel in the sprite was skipped.
Look closely at the numbers behind that picture, too. A sprite is a rectangle, and this screen
is a circle — scaling a rectangle up does not just make it bigger, it can walk its corners off
the glass. The top two eyes sit close enough to the rim that their sprite corners just reach
config.SAFE_RADIUS, 168 pixels from center; nothing is lost, since those corners are only the
sprite's black background, but the display still spends SPI bytes sending black to a spot no one
can see. The striped band underneath had the same problem: scaling the smartwatch kit's
y=110–210 band by 1.5 would run it to y=315, but a 270-pixel-wide row that low sticks out past
the edge of the circle. So the lab stops the band at y=280 instead — the last row where a span
that wide still fits inside the safe circle.
The Sprite Class Is Worth Stealing
Look at what Sprite actually is: an object with hline() and pixel() methods that writes into
a bytearray instead of a display. shapes.ellipse() cannot tell the difference and does not need
to — it just calls hline() on whatever it was handed.
That is a trick worth remembering well beyond this lab. Any drawing function that only talks to an interface can be pointed at something other than a screen.
Transparency Is Not a Property of a Color
Black is not special here. blit_keyed() skips whatever color you point it at, so if you build your sprite on a red background and pass red as the key, red becomes the transparent one. The key is a choice you make, not a fact about the pixel.
Things to Try
- Do the memory arithmetic yourself. Work out the byte cost of the eye sprite (96 × 72 × 2 = 13,824), then a full-screen buffer (360 × 360 × 2 = 259,200), and compare both to an RP2040's 264 KB. Then run the same sums for the smartwatch kit's 64 × 48 sprite and 240 × 240 screen, and notice that scaling every dimension by 1.5 scaled every byte count by 2.25.
- Time the two bottom blits. The plain one sends one command and 13,824 bytes. The keyed one sends a command per run, per row — 72 rows here, against the smartwatch kit's 48, so it pays half again as many command overheads. Nobody has measured exactly how much slower that makes it on this panel; run it and find out.
- Change the sprite's background to
config.REDand pass that as the key. Same behavior, different color skipped. - Blit the eye eight times in a ring around the center, checking each spot against
config.inside_circle()first. One buffer, eight stamps, and no ellipse math after the first one.
References
- Drawing Ellipses — the function that draws into the sprite buffer
- Color and Bits — what those two bytes per pixel actually contain
- Only Redraw What Changed — the other answer to "how do I avoid sending the whole screen?"
- Blitting Buffers — the same lab on the smaller 240 × 240 kit, where the eye sprite is 64 × 48 instead of 96 × 72