ArduinoでaitendoのTFT液晶(M-Z18SPI-2PB)を制御するライブラリを作成するための調査(3)

投稿者: | ↻ : 2019年1月28日

はじめに

ArduinoでaitendoのTFT液晶(M-Z18SPI-2PB)を制御するライブラリを作成するための調査(2)」の続きで、"Adafruit_GFX"クラスを解析します。

調査

Adafruit-GFX-Library

コードの詳細を追う前に、README.mdに概要が書かれているので先に確認します。翻訳の言い回しは微妙だけども描画の共通処理を提供するクラスだと想定できます。

これはすべての私たちのディスプレイ用のコアグラフィックスライブラリであり、グラフィックスプリミティブ(点、線、円など)の共通セットを提供します。 私たちが持っている各ディスプレイデバイス用のハードウェア固有のライブラリとペアにする必要があります(下位レベルの関数を処理するため)。

Adafruit-GFX-LibraryのREADME.mdから抜粋(Google翻訳)

これを踏まえてコードを追います。

Adafruit_GFX.h & Adafruit_GFX.cpp

"Adafruit_GFX"クラスの変数と関数を確認します。

drawPixel関数

ざっと見てdrawPixel関数がピクセル(点)を描画する処理で、末尾の"=0"から純粋仮想関数だと読み取れます。コメントにもサブクラスでの定義が必須と書かれています。

class Adafruit_GFX : public Print {
...
  // This MUST be defined by the subclass:
  virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0;

液晶画面に点が打てれば制御が出来てることの確認が出来るので、この関数から切り崩します。

writePixel関数

drawPixel関数にハードウェア固有の処理を吸収させているなら、このライブラリから呼び出されている場所があるはずです。複数見つかりますが、"Adafruit_GFX"クラスに関係するのはwritePixcel関数の一ヶ所で実装は、drawPixcel関数を呼び出しているだけです。

ringo@stupiddog  (master) $ grep -5 -n drawPixel Adafruit_GFX.cpp
129-    // Overwrite in subclasses if desired!
130-}
131-
132-void Adafruit_GFX::writePixel(int16_t x, int16_t y, uint16_t color){
133-    // Overwrite in subclasses if startWrite is defined!
134:    drawPixel(x, y, color);
135-}
136-
137-// (x,y) is topmost point; if unsure, calling function
138-// should sort endpoints or call writeLine() instead
139-void Adafruit_GFX::writeFastVLine(int16_t x, int16_t y,

コメントからサブクラスで拡張できる場所として用意されているようです。

その他の描画関数

点を描画するwritePixel関数が見つかったので、この関数の利用のされ方を調べます。

macOSでDoxygenを使ってC,C++ソースコードからドキュメントを作成する」でドキュメント自動生成してコールグラフを確認します。

色々な関数から呼び出されていますが、どれも関数名から処理が推測できます。writeLine関数から処理を探ります。処理はコメントの通りブレゼンハムのアルゴリズムで直線を描画しています。その処理の中で点を描画する場所でwritePixel関数を呼び出しているのが分かります。

ここで確認したのは、液晶制御に固有の処理はwritePixel関数(drawPixel関数)内へ隠蔽して、図形の描画はハードウェアに依存していないということ。

// Bresenham's algorithm - thx wikpedia
void Adafruit_GFX::writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
        uint16_t color) {
    int16_t steep = abs(y1 - y0) > abs(x1 - x0);
    if (steep) {
        _swap_int16_t(x0, y0);
        _swap_int16_t(x1, y1);
    }

    if (x0 > x1) {
        _swap_int16_t(x0, x1);
        _swap_int16_t(y0, y1);
    }

    int16_t dx, dy;
    dx = x1 - x0;
    dy = abs(y1 - y0);

    int16_t err = dx / 2;
    int16_t ystep;

    if (y0 < y1) {
        ystep = 1;
    } else {
        ystep = -1;
    }

    for (; x0<=x1; x0++) {
        if (steep) {
            writePixel(y0, x0, color);
        } else {
            writePixel(x0, y0, color);
        }
        err -= dy;
        if (err < 0) {
            y0 += ystep;
            err += dx;
        }
    }
}

まとめ

グラフィック系でよくある作りなので、液晶制御の実装はサブクラスにあると判断し、他の描画関数の解析は後回しにします。次はAdafruit-ST7735-LibraryAdafruit_ST7735クラスを解析します。