LovyanGFX入門 その2 フォント描画系

概要

前回は基本描画系を調べましたが、今回はフォント系です。このへんは中に入っているスケッチ例を見れば詳しく書いてあるのですが、個人的におすすめする方法を中心に書いてあります。

上記にまとめるときに使ったデータがあります。

フォント描画系の概要

フォント描画系はdraw系関数とprint系関数にわかれています。draw系関数は座標と文字列を指定して描画して、改行などの概念がありません。print系関数はカーソルを指定してから描画して、改行すれば次の行からの描画になります。

また歴史的経緯だと思うのですが、Arduino UNOなどはprintfが使えないので、似たような機能をがんばって実装しています。一方ESP32はprintf関数が使えるので、歴史的経緯の関数群を使うのをやめてすべてprint系関数のみでよいと思います。

そこで、おすすめはprint系関数のみ利用して、draw系は使わないことです。drawFloat()関数を使うよりprintf(“%6.2f”)とかのほうがわかりやすいですよね?

基本のテキスト描画

  // 画面塗り潰し
  M5.Lcd.fillScreen(TFT_LIGHTGREY);
  // カーソルを左上にリセット
  M5.Lcd.setCursor(0, 0);
  // フォント指定
  M5.Lcd.setFont(&fonts::Font0);
  // フォント色セット
  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
  M5.Lcd.setTextSize(1);
  M5.Lcd.print("print");
  M5.Lcd.println("println");
  M5.Lcd.printf("printf $d\n", 10);

ざっくりした、テキスト描画例です。

カーソル指定

  M5.Lcd.setCursor(0, 0);

setCursor()でカーソルの場所を指定します。XとY座標をドット単位で指定できます。描画した文字の分だけ移動していきます。

フォント指定

  M5.Lcd.setFont(&fonts::Font0);

ここはちょっと違いますね。互換性のためにsetTextFont(0)の書式も残っていますが、基本は上の書き方をします。

フォント色指定

  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);

文字色と、背景色を指定します。文字色のみ指定すると、背景色を塗りません。詳細は後ほど紹介します。

文字出力

  M5.Lcd.print("print");
  M5.Lcd.println("println");
  M5.Lcd.printf("printf $d\n", 10);

printは文字列を単純に出力します。改行は\nを出力しない限りしません。printlnは自動的に改行されるprintです。printの最後で”\n”をつけた場合と同じ出力になります。printfはフォーマット付きで出力ができます。

このへんの書式はSerialクラスと一緒です。print(78, HEX)みたいな出力もできますが、printf(“%02X”, 78)みたいな出力の方がわかりやすいですよね?

フォントについて

setFont()関数で指定ができますのは紹介しましたが、ビルドインで利用できるフォントが60種類あります!

上記ですべてのフォントを確認することができます。

FontSample種類文字幅横幅縦幅
&fonts::Font0英数固定長68
&fonts::Font2英数可変長816
&fonts::Font4英数可変長1426
&fonts::Font6数字のみ固定長2748
&fonts::Font7数字のみ固定長3248
&fonts::Font8数字のみ固定長5575

基本的には上記の5種類が基本的に利用するフォントになります。この中で一番使いやすいのがFont0になります。横6ドット、縦8ドットの固定サイズで英数が使えます。Font2とFont4は横幅が可変なので英字をたくさん表示したいときに適していますが、文字によって横幅が違うので使いにくい場合もあります。Font6以降は数字と一部の記号のみなので注意してください。

内蔵フォント以外を利用することも可能です。後ほど日本語フォントを使う方法を書きたいと思います。

フォントサイズについて

わかりやすいように拡大してあります。フォントサイズですが、元の大きさからの倍率を指定します。つまりフォントサイズ1が標準のフォントです。フォントサイズ2にすると縦横が2倍に拡大されます。

LovyanGFXは更に、3.5などの実数指定が可能になっています。縦8ドットのFont0を1.5指定することで12ドットで描画することなどが可能です。これはかなり便利です。

  M5.Lcd.setTextSize(2, 4);
  M5.Lcd.println("Size2x4");

さらに、setTextSize()関数に2つ引数を渡すと横幅と縦幅の倍数を個別に指定することが可能です。2と4を指定した場合横2倍、縦4倍に拡大して描画してくれます!

元になったライブラリの設計に依存しているので、フォントサイズが高さではなく、倍率指定なので注意してください。

カーソルについて

  M5.Lcd.setCursor(64, 32);
  M5.Lcd.println("ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz");

指定した座標から描画を開始して、画面端になり文字が画面内に収まらない場合、改行をしてY座標をフォントサイズ分だけ移動し、X座標は0から描画を開始します。指定したX座標ではなく0に戻るので注意してください。

自動改行してほしくない場合(setTextWrap)

  M5.Lcd.setTextWrap(false);
  M5.Lcd.println("ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz");

setTextWrap(false)を指定すると、自動折返しを行わなくなります。勝手に改行されると画面レイアウトが崩れるので、falseで使うのが安全ではあります。

描画範囲指定

  M5.Lcd.setCursor(64, 64);
  M5.Lcd.setClipRect(64, 64, 128, 128);
  M5.Lcd.println("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
  M5.Lcd.clearClipRect();
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println("1234567890");

setClipRect()関数を使うと、描画されるエリアを指定することができます。これはすべての描画関数に影響を与えるので、フォント以外にも影響しますので注意してください。

また、フォントの自動改行なども影響しますので、範囲外から描画をはじめると範囲内になったところで、自動改行が急に動いて意図しない描画になりますので注意してください。

また、範囲指定をしたら必ずclearClipRect()関数で解除しないと、これ以降の描画がすべておかしくなるので危険な関数です。

フォント描画色について

  M5.Lcd.setCursor(0, 0);
  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
  M5.Lcd.println("setTextColor(TFT_WHITE, TFT_BLACK)");
  M5.Lcd.setTextColor(TFT_RED, TFT_RED);
  M5.Lcd.println("setTextColor(TFT_RED, TFT_RED)");
  M5.Lcd.setTextColor(TFT_RED);
  M5.Lcd.println("setTextColor(TFT_RED)");

色はあまり重要視されていないのですが、結構奥が深いです。一行目はsetTextColor(TFT_WHITE, TFT_BLACK)で文字色を白、背景色を黒で描画しています。デフォルトはこの色になっています。

2行目は赤を文字色と背景色に指定しています。この状態では背景色は描画されず、文字の赤だけ描画されます。3行目は赤のみを指定しています。この状態は2行目と同じく、背景色が描画されない指定になります。

この状態は文字を描画する場合には背景を自分で描画し直す必要があります。背景が画像などの場合にはこの設定にして、毎回文字を描画するエリアを画像で上書きしてから文字を描画します。ベタ塗り背景の場合には背景色で塗り潰した方が楽ですが、fillRect()などで描画エリアの塗り潰しを行ってからテキストを描画します。

  M5.Lcd.setTextColor(TFT_RED, TFT_BLACK);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println("ABCDE");
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println("123");
  M5.Lcd.setTextColor(TFT_RED);
  M5.Lcd.setCursor(0, 60);
  M5.Lcd.println("ABCDE");
  M5.Lcd.setCursor(0, 60);
  M5.Lcd.println("123");
  M5.Lcd.setCursor(0, 120);
  M5.Lcd.println("ABCDE");
  M5.Lcd.fillRect(0, 120, 240, 16, TFT_LIGHTGREY);
  M5.Lcd.setCursor(0, 120);
  M5.Lcd.println("123");

上記が複数回描画したパターンです。一番上は背景色を指定しているので一見大丈夫そうに見えますが、文字の長さが文字書くなったのでDEが残っています。このパターンはprintf(“%10s”, str)などでスペースを含めて固定長を描画するなどの工夫が必要になります。

真ん中は背景色がないので、複数回描画すると文字が重なって読めなくなっています。

下は文字の描画前にfillRect()で塗り潰しているので、正常に見えます。ただし塗り潰し範囲が広い場合には画面にちらつきが発生しますし、描画に時間もかかりますので最低限の範囲だけ塗り潰したほうが好ましいです。

チラツキ防止は次回以降に紹介するスプライトを利用することで対策が可能です。

フォントサイズ取得

  M5.Lcd.setFont(&fonts::Font0);
  M5.Lcd.setTextSize(1);
  M5.Lcd.printf("Font0 size=1, str=%s, W=%d, H=%d\n", "III", M5.Lcd.textWidth("III"), M5.Lcd.fontHeight());
  M5.Lcd.setTextSize(2);
  M5.Lcd.printf("Font0 size=2, str=%s, W=%d, H=%d\n", "III", M5.Lcd.textWidth("III"), M5.Lcd.fontHeight());
  M5.Lcd.setFont(&fonts::Font2);
  M5.Lcd.setTextSize(1);
  M5.Lcd.printf("Font2 size=1, str=%s, W=%d, H=%d\n", "III", M5.Lcd.textWidth("III"), M5.Lcd.fontHeight());
  M5.Lcd.setFont(&fonts::Font2);
  M5.Lcd.setTextSize(2);
  M5.Lcd.printf("Font2 size=2, str=%s, W=%d, H=%d\n", "III", M5.Lcd.textWidth("III"), M5.Lcd.fontHeight());
  M5.Lcd.setFont(&fonts::Font4);
  M5.Lcd.setTextSize(1.25);
  M5.Lcd.printf("Font4 size=1.25, str=%s, W=%d, H=%d\n", "III", M5.Lcd.textWidth("III"), M5.Lcd.fontHeight());

ちょっと長いですが、フォントの縦幅は指定したフォントの高さ×縦の倍率です。同じ高さでもフォントによって、上下余白のとり方が違うので見た目の大きさが違います。Font0は余白のないフォントなので、拡大すると大きく見えます。Font2などは上下に予約があるので、きれいなのですが小さく感じますね。

横幅は描画する文字列を渡して取得します。可変フォントの場合には描画文字列によって横幅が変わるからです。

文字列のセンタリング

  String str = "ABCIII";
  int x = 0;
  int y = 32;
  M5.Lcd.setFont(&fonts::Font0);
  M5.Lcd.setTextSize(2);
  x = M5.Lcd.width() / 2 - M5.Lcd.textWidth(str) / 2;
  M5.Lcd.setCursor(x, y);
  M5.Lcd.print(str);
  y += 40;
  M5.Lcd.setTextSize(4);
  x = M5.Lcd.width() / 2 - M5.Lcd.textWidth(str) / 2;
  M5.Lcd.setCursor(x, y);
  M5.Lcd.print(str);
  y += 40;
  M5.Lcd.setFont(&fonts::Font2);
  M5.Lcd.setTextSize(2);
  x = M5.Lcd.width() / 2 - M5.Lcd.textWidth(str) / 2;
  M5.Lcd.setCursor(x, y);
  M5.Lcd.print(str);

センタリングや右寄せに関しては、自分で文字列の横幅を取得してから計算をする必要があります。ちょっと面倒ですが横幅分マイナスにすれば右寄せになりますね。

日本語フォント利用

日本語フォントは内蔵していないので、別途用意する必要があります。今回は私が作成したefontを利用してみたいと思います。

上記で配布されているフォントデータをArduinoで利用しやすい形式に変換したライブラリになります。

上記に詳しく書いてありますが、日本語フォントではなく多言語フォントになっています。読み込む文字を指定して、プログラムの中にフォントを埋め込む方式になっています。

全部の文字を埋め込むのがかんたんなのですが、ハングル文字や中国語の漢字などが大量にあり、容量が大きくなるのでまずはよく使う日本語のみを指定するのがよいと思います。

#include <efontEnableJa.h>
#include <efontFontData.h>

1行目で文字の種類を選びます。Jaなので日本語のみです。一般的には更に小さいefontEnableJaMini.hで大抵の文字は表示できるはずです。機種依存文字などはフルバージョンのJaが必要になります。

フォントの読み込みは必ず一番最初に行ってください。

  M5.Lcd.setFont(&fonts::efont);
  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
  M5.Lcd.setTextSize(1);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println("縦16ドット日本語フォントが利用できます");
  M5.Lcd.setTextSize(1.5);
  M5.Lcd.println("1.5倍サイズ");
  M5.Lcd.setTextSize(2);
  M5.Lcd.println("2倍サイズ");

これで日本語が使えるようになりました。拡大するとちょっと汚くなりますが、日本語が描画できると便利ですよね!

ただし、結構な容量のフォントを内蔵していますので、フラッシュサイズを圧迫します。そしてフラッシュサイズが肥大化することで、スケッチのコンパイルと転送時間が伸びてしまいます。その点を注意して利用してみてください。

基本初期化項目

  // 画面クリア
  M5.Lcd.fillScreen(TFT_BLACK);
  // フォントセット
  M5.Lcd.setFont(&fonts::Font0);
  // 描画色、背景色を設定。描画色=背景色で背景を描画しない
  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
  // フォント表示倍率
  M5.Lcd.setTextSize(1.0);
  
  // テキスト自動折返し
  M5.Lcd.setTextWrap(true);
  // カーソルセット
  M5.Lcd.setCursor(0, 0);

最低限上記の項目は設定しておいたほうがよいと思います。

紹介した関数一覧

関数説明
print(str[])文字列描画
println(c[])改行付き文字列描画
printf(*format,…)フォーマットしてい文字列描画
setFont(*font)フォント指定
setTextSize(size)フォントの縦横の倍率を指定
setTextSize(sx, sy)フォントの横と縦の倍率を指定
getTextSizeX()フォントの横の倍率を取得
getTextSizeY()フォントの縦の倍率を取得
setCursor(x, y)カーソルを指定する
getCursorX()カーソルのX座標を取得する
getCursorY()カーソルのY座標を取得する
setTextWrap(wrapX, wrapY=false)テキスト自動改行設定
setClipRect(x, y, w, h)描画範囲指定
clearClipRect()描画範囲指定解除
setTextColor(fgcolor, bgcolor)フォントの描画色、背景色を指定
textWidth(*string)文字列の横幅取得
fontHeight()フォントの縦幅取得

まとめ

基本的なテキスト描画は説明できたと思います。draw系の命令では結構面倒な設定がたくさんありますが、今回はまるっと省きました、、、

上記はM5Stackの標準ライブラリの関数紹介ですが、こちらですとdraw系の説明もちょっとだけしていますので参考にしてみてください。

フォント周りですが、実はもっといろいろ読み込ませることで、他のフォントが利用できるようになっています。しかしながら、フォントデータを作るのはちょっと大変なので、なかなか普通の人は手がでないと思いますし、フォントの著作権なども注意しながら利用する必要があります。

そこでefontで変換にしたツールを利用して、もっとフォントの種類を増やしたいと思ったのですが、、、あれ、、、変換に使ったソースファイルが無いぞ!!!

むかーし使っていたパソコンの仮想環境の中に入っていた気がするのですが、行方不明(涙) ちょっと現状のLovyanGFXのフォント読み込みを調べ直して、一から作るか、、、

続編

コメント