概要
LovyanGFXやM5Unified(M5GFX)を利用していると、ちらつくことがあります。これは背景を一度クリアしてから描画しているときに背景描画がみえてから、その後描画されるからです。これを低減させるのにはスプライトを利用したダブルバッファが必要になります。メモリサイズとかの指定が面倒だったり、描画座標を調整するのが面倒になるのでとりあえずかんたんにちらつき防止できるライブラリを作成してみました。
作成物

上記になります。

原理的には上記で説明してあるのですが、画面に直接描画するとちらつきます。そこで一度メモリに描画してから一括転送をすることでちらつき防止を行います。ただし、画面全体分のスプライトはESP32のメモリ的に確保できません。PSRAMを利用することで確保可能ですが、DMA転送ができなくなるので遅くなります。
そこで、画面を分割をして3回に分けて描画することでちらつき防止をする技術があるのですが、結構面倒です。このライブラリではそこを簡易的な方法で解決しています。
void drawScene(LGFXVirtualCanvas &g)
{
g.fillScreen(TFT_NAVY);
g.setTextColor(TFT_WHITE);
g.drawCentreString("LGFXVirtualCanvas", g.width() / 2, g.height() / 2 - 16);
g.drawCentreString("Hello, tiled world!", g.width() / 2, g.height() / 2 + 4);
}
上記のように描画関数を作成して、その中で描画をします。
void drawView(LGFXVirtualCanvas &g, Ball &b)
{
g.fillScreen(TFT_BLACK);
g.drawRect(0, 0, g.width(), g.height(), TFT_DARKGREY);
g.fillCircle(b.x, b.y, 10, TFT_CYAN);
}
実際には引数なしで描画をすると固定画面になるので上記のように任意の引数を追加できます。
void loop()
{
// Bounce inside the viewport (local coordinates).
ball.x += ball.vx;
ball.y += ball.vy;
if (ball.x < 10 || ball.x > view.width() - 10)
ball.vx = -ball.vx;
if (ball.y < 10 || ball.y > view.height() - 10)
ball.vy = -ball.vy;
view.render(drawView, ball); // only the 200x150 region is updated
delay(16);
}
loopなどの中からball変数の中身を更新してから、view.render()で描画関数と引数を渡して描画をさせます。
仕組み
renderから呼ばれる描画関数は実は分割された数だけ何度も呼び出される形となります。たとえば画面を3分割した場合には3回呼ばれます。そして描画先のLGFXVirtualCanvasで何度目の呼び出しかを把握して、描画範囲をずらしながら描画を実施します。
描画対象外の座標に対する描画はスキップされるのですが、重い描画を行うと何度も呼び出される分遅くなるので注意が必要です。特に横長のくし状の描画エリアなので縦に全面描画する線などが大量にあったり、画像描画などが重なると重くなります。
そしてデフォルトではダブルバッファのサイズが19 KBと極端に小さいです。小さい分気軽に利用できるのですが、その分遅くなります。ただし、単純な画面の場合にはそれほど気にならないと思います。
まとめ
省メモリでかんたんにちらつき防止できるのが利点です。そして描画関数に分割することによって描画とロジックが綺麗に分離できます。その結果画面キャプチャがやりやすくなったり、テスト面でも有利な点があります。
また、オプションですが前回の描画内容をハッシュで保存しておき、変更が無かった場合には転送をスキップする機能などもあります。通常のSPIでは利用しないほうがいいのですが転送が遅いUSBデバイスなどに描画するときには便利だと思います。


コメント