Дисплей и графика

Инициализация дисплейного драйвера, вывод на панель и работа со Sprite-буфером.

5. Display - вывод на панель

Низкоуровневый RGB565-интерфейс. В приложении используется через plat->display().

Метод Описание
bool begin(uint8_t rotation) Инициализация панели (обычно вызывается через Platform::beginDisplay)
bool setRotation(uint8_t rotation) Поворот 0..3 на лету
uint16_t width() const / uint16_t height() const Текущее разрешение с учётом поворота
void fillScreen565(uint16_t color565) Заливка всего экрана
void writeRect565(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *pixels, int32_t stridePixels) Вывод прямоугольника. Синхронно: возвращается, когда данные уже ушли в панель
void writeRect565Async(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *pixels, int32_t stridePixels) То же через DMA: ставит передачу в очередь и возвращается сразу
void waitDMA() Дождаться завершения асинхронных передач

Ключевые понятия

  • stridePixels - шаг строки источника в пикселях. Позволяет выводить произвольное окно из большого буфера кадра: writeRect565(100, 50, 60, 40, frame, frameWidth). Для плотно упакованного источника передайте w.
  • Синхронный путь просто и предсказуемо: вернулись - можно менять буфер.
  • Асинхронный путь освобождает CPU для отрисовки следующего кадра. Два правила:
    1. буфер-источник должен оставаться валидным до waitDMA() (DMA читает его прямо из памяти);
    2. перед повторным использованием буфера вызовите waitDMA().
  • При swap = true асинхронный путь прозрачно прогоняет данные через двойной DMA-буфер (копирование со свопом): цвета верны, но копирование ложится на CPU.

Пример

Поставить кадр N в очередь → рисовать кадр N+1 в другой буфер → waitDMA() → поменять буферы:

Sprite bufA(plat), bufB(plat);
if (!bufA.createSprite(w, h) || !bufB.createSprite(w, h))
    return;

Sprite *front = &bufA;   // его сейчас передаёт DMA
Sprite *back  = &bufB;   // в него рисуем

while (!plat->shouldQuit())
{
    renderScene(*back);                       // CPU рисует, пока DMA занят
    display->waitDMA();                       // предыдущий кадр ушёл
    display->writeRect565Async(0, 0, w, h,
        static_cast<const uint16_t *>(back->getBuffer()), w);
    std::swap(front, back);                   // теперь рисуем в другой буфер
}

Драйверы

Драйвер Формат Макс. частота Особенности
ST7789 / ST7796 RGB565 80 МГц Общий шаблонный движок, очередь DMA-транзакций с двойной буферизацией
ILI9488 RGB666 60 МГц Конвейер с конвертацией 565 → 666

6. Sprite - offscreen-графика

Модуль включается опцией PIPCORE_ENABLE_GRAPHICS (по умолчанию включён вместе с драйвером дисплея).

Sprite - изображение в RAM с клиппингом и быстрым блиттингом. Память выделяется через Platform::alloc (внутренняя RAM в приоритете) и освобождается в деструкторе или deleteSprite(). Копировать спрайт нельзя, для обмена буферами есть swap().

Sprite sp(plat);
if (!sp.createSprite(128, 64))
{
    // не хватило памяти
    return;
}
sp.fillScreen(Sprite::color565(0, 0, 0));
sp.fillRect(10, 10, 40, 20, Sprite::color565(255, 128, 0));
sp.writeToDisplay(*plat->display(), 0, 0, 128, 64);

Создание и служебные методы

Метод Описание
Sprite() / explicit Sprite(Platform *platform) Конструктор. Платформа нужна для выделения памяти - передайте её здесь или через setPlatform()
bool createSprite(int16_t w, int16_t h) Выделить буфер w × h. false - не хватило памяти
void deleteSprite() Освободить буфер
int16_t width() const / int16_t height() const Размер
void setPlatform(Platform *platform) Сменить платформу-аллокатор
void *getBuffer() / const void *getBuffer() const Прямой доступ к пикселям (для своих ядер отрисовки). Буфер хранит значения в байтовом порядке, готовом к DMA (после swap16)
void swap(Sprite &other) Обменять буферы без копирования (удобно для двойной буферизации)

Рисование

Метод Описание
void fillScreen(uint16_t color565) Залить весь спрайт
void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color565) Залитый прямоугольник
void drawPixel(int16_t x, int16_t y, uint16_t color565) Пиксель. Вне клип-зоны молча игнорируется
void pushImage(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *pixels565) Вставить плотно упакованное изображение

Вывод

Метод Описание
void writeToDisplay(Display &display, int16_t x, int16_t y, int16_t w, int16_t h) const Вывести область на дисплей: x, y - позиция на дисплее, w, h - размер области
void pushSprite(Sprite *dst, int16_t x, int16_t y) const Спрайт на спрайт (композиция offscreen-сцен)

Клиппинг

Метод Описание
void setClipRect(int16_t x, int16_t y, int16_t w, int16_t h) Зона отсечения: всё вне неё не рисуется. Удобно для окон и скролла
void getClipRect(int32_t *x, int32_t *y, int32_t *w, int32_t *h) const Текущая зона
SpriteClip clipRegion(int16_t x, int16_t y, int16_t w, int16_t h) const Пересечение прямоугольника с клип-зоной - для собственных циклов отрисовки
struct SpriteClip
{
    int16_t rx1, ry1;   // левый верхний угол видимой части
    int16_t cw, ch;     // её размер
    bool visible;       // false - пересечение пусто
};

Цветовые утилиты

static constexpr - вызываются без объекта: Sprite::color565(...).

Метод Описание
uint16_t color565(uint8_t r, uint8_t g, uint8_t b) RGB888 → RGB565
uint16_t swap16(uint16_t v) Байтовый своп
uint16_t blend565(uint16_t bg, uint16_t fg, uint8_t alpha) Альфа-смешение, alpha 0…255 (0 - фон, 255 - передний план)
uint8_t u8clamp(int v) Ограничение значения диапазоном 0…255