diff --git a/main.c b/main.c index 8b1ebd4b9fe..5828cbe1c27 100644 --- a/main.c +++ b/main.c @@ -80,6 +80,10 @@ #include "shared-bindings/epaperdisplay/EPaperDisplay.h" #endif +#if CIRCUITPY_PICOGAME_RGB444 +#include "shared-module/picogame/__init__.h" +#endif + #if CIRCUITPY_KEYPAD #include "shared-module/keypad/__init__.h" #endif @@ -374,6 +378,11 @@ static void cleanup_after_vm(mp_obj_t exception) { atexit_reset(); #endif + // Restore the panel's pixel format while the display bus is still alive. + #if CIRCUITPY_PICOGAME_RGB444 + picogame_reset(); + #endif + // Turn off the display and flush the filesystem before the heap disappears. #if CIRCUITPY_DISPLAYIO reset_displays(); diff --git a/shared-module/picogame/__init__.c b/shared-module/picogame/__init__.c index ddd700959ed..ee69e6ec4ac 100644 --- a/shared-module/picogame/__init__.c +++ b/shared-module/picogame/__init__.c @@ -976,6 +976,12 @@ bool picogame_strip_begin( return true; } +#if CIRCUITPY_PICOGAME_RGB444 +// The display currently switched to RGB444, or NULL. The strip path packs for it and +// picogame_reset() restores it to RGB565 at the end of the program. +static picogame_output_t *rgb444_display = NULL; +#endif + // --- output transport seam (busdisplay backend): the ONLY per-strip display ops the generic // picogame_render_region orchestrator below touches, so a non-CircuitPython port (MicroPython // framebuf/SPI) swaps just strip_begin + these two + set_invert/set_pixel_format. See __init__.h. @@ -993,6 +999,14 @@ void picogame_render_region( int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t background, int ox, int oy) { + #if CIRCUITPY_PICOGAME_RGB444 + // RGB444 packs 2 px into 3 bytes, so widen the region to even x bounds. + bool rgb444 = (display == rgb444_display); + if (rgb444) { + x0 &= ~1; + x1 = (x1 + 1) & ~1; + } + #endif int region_w, strip_h; int cx0 = x0, cy0 = y0, cx1 = x1, cy1 = y1; // strip_begin clamps these to the panel in place if (!picogame_strip_begin(display, &cx0, &cy0, &cx1, &cy1, buffer_pixels, ®ion_w, &strip_h)) { @@ -1001,7 +1015,13 @@ void picogame_render_region( for (int sy = cy0; sy < cy1; sy += strip_h) { int sh = picogame_imin(strip_h, cy1 - sy); mp_obj_t exc = picogame_blit_strip_layers(buffer, region_w, sy, sh, cx0, items, kinds, n, background, ox, oy); - picogame_out_strip_send(display, (uint8_t *)buffer, region_w * sh * 2); + size_t nbytes = (size_t)region_w * sh * 2; + #if CIRCUITPY_PICOGAME_RGB444 + if (rgb444) { + nbytes = picogame_pack_rgb444(buffer, (size_t)region_w * sh); // in place, 3/4 the bytes + } + #endif + picogame_out_strip_send(display, (uint8_t *)buffer, nbytes); if (exc != MP_OBJ_NULL) { // a StripDraw callback raised a BaseException: close the picogame_out_strip_end(display); // bus, then re-raise (Ctrl-C / reload) nlr_raise(MP_OBJ_TO_PTR(exc)); @@ -1029,6 +1049,7 @@ void picogame_set_invert(picogame_output_t *display, bool on) { // Set the panel pixel format (COLMOD 0x3A): rgb444 -> 12-bit RGB444 (0x53), else 16-bit RGB565 // (0x55). Asserting it on every Display construct also recovers from a previous program that left // the panel in the other format (survives soft reset). + void picogame_set_pixel_format(picogame_output_t *display, bool rgb444) { uint8_t cmd = 0x3A; uint8_t param = rgb444 ? 0x53 : 0x55; @@ -1038,6 +1059,20 @@ void picogame_set_pixel_format(picogame_output_t *display, bool rgb444) { display->bus.send(display->bus.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, &cmd, 1); display->bus.send(display->bus.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, ¶m, 1); displayio_display_bus_end_transaction(&display->bus); + rgb444_display = rgb444 ? display : NULL; +} + +// Called at the end of a program, before reset_displays() takes the bus away. +void picogame_reset(void) { + if (rgb444_display == NULL) { + return; + } + // A released display has a None bus. + if (mp_obj_get_type(rgb444_display->bus.bus) == &mp_type_NoneType) { + rgb444_display = NULL; + return; + } + picogame_set_pixel_format(rgb444_display, false); // clears rgb444_display } // Pack a strip of `npix` (must be even) WIRE-order RGB565 pixels IN-PLACE to ST7789 12-bit RGB444 diff --git a/shared-module/picogame/__init__.h b/shared-module/picogame/__init__.h index a4377292219..412856716b5 100644 --- a/shared-module/picogame/__init__.h +++ b/shared-module/picogame/__init__.h @@ -184,7 +184,8 @@ void picogame_blit_bitmap_affine( // // Strip-path contract a backend provides: // picogame_strip_begin - open a window for [x0,y0,x1,y1); return strip geometry -// picogame_out_strip_send - push one composited strip (region_w*sh px, wire RGB565) +// picogame_out_strip_send - push one composited strip (region_w*sh px, wire RGB565; packed to +// RGB444 first when that panel is in 12-bit mode) // picogame_out_strip_end - close the transaction // picogame_set_invert - panel hardware colour inversion (a free full-screen flash) // picogame_set_pixel_format - panel COLMOD (RGB565/RGB444), when CIRCUITPY_PICOGAME_RGB444 @@ -248,6 +249,10 @@ bool picogame_fb_take_invert_dirty(void); // Set panel pixel format (COLMOD): rgb444 -> 12-bit RGB444, else 16-bit RGB565. void picogame_set_pixel_format(picogame_output_t *display, bool rgb444); +// Put a panel left in RGB444 back to RGB565 at the end of a program, so the console stays +// readable. Call before reset_displays(). +void picogame_reset(void); + // Pack `npix` (even) wire-order RGB565 pixels in `buf` IN-PLACE to 12-bit RGB444; returns bytes. size_t picogame_pack_rgb444(uint16_t *buf, size_t npix); #endif