Skip to content

Sprite (Off-Screen Canvas)

Class: LGFX_Sprite / M5Canvas (from M5GFX)
Header: M5GFX.h (auto-includes LGFX_Sprite)

A sprite is an off-screen drawing buffer. You draw into it with the same API as M5Cardputer.Display, then blit it to the screen in a single operation. This enables double-buffered rendering for flicker-free animation and smooth UI.

M5Canvas is a convenience subclass of LGFX_Sprite that sets PSRAM allocation by default.


M5Canvas vs LGFX_Sprite

M5Canvas sprite(&M5Cardputer.Display);  // auto PSRAM, binds parent display
LGFX_Sprite sprite;                     // no parent, manual PSRAM

Use M5Canvas for most cases. Use LGFX_Sprite only when you want explicit control over PSRAM via setPsram().


Lifecycle

void* createSprite(int32_t w, int32_t h);  // Allocate off-screen buffer
void deleteSprite();                       // Free the buffer
M5Canvas sprite(&M5Cardputer.Display);
sprite.setColorDepth(16);                // MUST be called BEFORE createSprite
sprite.createSprite(320, 240);

// ... draw into sprite ...

sprite.deleteSprite();                   // Clean up when done

Buffer Management

void setPsram(bool enabled);                       // Use PSRAM for next createSprite
void* setColorDepth(color_depth_t depth);          // Change color depth (re-creates buffer)
void setBuffer(void* buffer, int32_t w, int32_t h, uint8_t bpp = 0);  // Wrap external memory
void* getBuffer();                                 // Raw buffer pointer
uint32_t bufferLength();                           // Buffer size in bytes

Color depth options: 1, 4, 8, 16, 24, 32 (bits per pixel). For M5Cardputer, 16 is usually best.

Warning

setColorDepth() and setPsram() must be called before createSprite(). Changing color depth after creation destroys and re-creates the buffer.


Pushing to Display

void pushSprite(int32_t x, int32_t y);                              // Push to parent display
void pushSprite(LovyanGFX* dst, int32_t x, int32_t y);             // Push to any display
void pushSprite(int32_t x, int32_t y, uint16_t transparent);        // With transparent color
sprite.pushSprite(0, 0);                 // Full sprite at (0,0) on parent
sprite.pushSprite(&M5Cardputer.Display, 10, 20);  // At (10,20)
sprite.pushSprite(0, 0, TFT_BLACK);      // TFT_BLACK pixels are transparent

Rotated / Zoomed Push

void pushRotated(float angle);                                     // Rotate & push
void pushRotated(float angle, uint16_t transparent);               // With transparency
void pushRotateZoom(float angle, float zoom_x, float zoom_y);     // Rotate + scale
void pushAffine(const float matrix[6]);                            // Affine transform

Inherited Drawing Methods

LGFX_Sprite inherits ALL LGFXBase drawing methods, so everything from Display (M5GFX) works on sprites:

sprite.fillScreen(TFT_BLACK);
sprite.fillRect(10, 10, 100, 50, TFT_RED);
sprite.drawCircle(160, 120, 60, TFT_GREEN);
sprite.drawString("Hello", 10, 10);

Double-Buffered Rendering

M5Canvas backBuffer(&M5Cardputer.Display);

void setup() {
    M5Cardputer.begin();
    backBuffer.setColorDepth(16);
    backBuffer.createSprite(320, 240);
}

void loop() {
    M5Cardputer.update();

    // Draw everything to back buffer
    backBuffer.fillScreen(TFT_BLACK);
    backBuffer.drawString("FPS counter", 10, 10);
    // ... more drawing ...

    // Blit in one shot — no flicker
    backBuffer.pushSprite(0, 0);
}

Low-BPP Sprites with Palette

For memory-constrained scenarios, use 8-bit (or lower) color depth with a palette:

M5Canvas sprite(&M5Cardputer.Display);
sprite.setColorDepth(8);
sprite.createSprite(320, 240);

sprite.createPalette();
sprite.setPaletteColor(0, TFT_BLACK);
sprite.setPaletteColor(1, TFT_WHITE);
sprite.setPaletteColor(2, TFT_RED);

sprite.fillScreen(0);           // Use palette index 0
sprite.drawString("Hi", 10, 10);   // Text color uses palette
sprite.pushSprite(0, 0);

Loading Sprite from BMP

bool createFromBmpFile(const char* path);     // From SD card
bool createFromBmp(const uint8_t* data, uint32_t len);  // From memory
sprite.createFromBmpFile("/logo.bmp");
sprite.pushSprite(0, 0);

This replaces the sprite buffer with the BMP content (no need to call createSprite first).


Quick Example: Simple Game Loop

M5Canvas canvas(&M5Cardputer.Display);

void setup() {
    M5Cardputer.begin();
    canvas.setColorDepth(16);
    canvas.createSprite(320, 240);
}

int x = 100, y = 100;

void loop() {
    M5Cardputer.update();

    // Clear back buffer
    canvas.fillScreen(TFT_BLACK);

    // Move player
    auto& keys = M5Cardputer.Keyboard.keysState();
    if (M5Cardputer.Keyboard.isKeyPressed('w')) y--;
    if (M5Cardputer.Keyboard.isKeyPressed('s')) y++;
    if (M5Cardputer.Keyboard.isKeyPressed('a')) x--;
    if (M5Cardputer.Keyboard.isKeyPressed('d')) x++;

    // Draw game objects
    canvas.fillRect(x, y, 16, 16, TFT_GREEN);

    // Flip to screen
    canvas.pushSprite(0, 0);
}

void cleanup() {
    canvas.deleteSprite();
}