M5Stackとフォント GFXFFへの対応 [ESP32]
前回ではランレングスに対応しました。今回はGFX Free Fontと言うフォントの対応です。
GFX Free Fontもプロポーショナルフォントですが、特に筆記体を表示するのに向いている様な気がします。
マイコンにとって最も処理の負荷が小さそうなのは等幅フォントですが、等幅フォントでは例えば小文字の'i'が連続した時に、'i'と'i'の間が大きく開いて体裁が良くありません。
プロポーショナルフォントなら文字毎に適切な文字間隔を指定できるので、とても体裁が良くなります。しかし制御が増えるのでその点は良し悪しです。
ランレングスはSPIとは相性が良さそうな気がしますね。
GFXフォントの使い方の解説(本家?)はここから。
https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts
Customフォルダーの中のフォントは以下のサイトで生成された物?
http://oleddisplay.squix.ch/#/home
GFX Free Font(GFXFF)を使う為のもっとも簡単な方法は、すでにM5ライブラリに用意されているメソッドを使う事です。
例えば
setFreeFont( GFXFFにアクセスする為の構造体のポインタ );でこれから使用するフォントを選択。
drawString( テキスト, 横位置, 縦位置, フォント番号 );で指定された位置に先に指定されたフォントでテキストを描画します。まあつまりこんな感じで、
これらメソッドはM5Stack.hには記載されていませんが、M5Display.hの中でTFT_eSPIクラスが継承されているのでIn_eSPI.hを参考にすると良いでしょう。
しかし相変わらずこのメソッドでは画面の右端に行った時に折り返しをしてくれません、、、よね?
折り返しとか改行とかを行いたいなら、自分で描画メソッドを作るしかない?
以下ではその解説を!
1.GFXフォーマットのフォントデータファイルは3つの要素で構成されている。
1) 文字をビットマップデータ化したデータ領域
Yelllowtail_32.hであれば
const uint8_t Yellowtail_32Bitmaps[] PROGMEM
配列名は任意でこのファイルの中でしか利用されないが、static宣言はされていない。
2) 該当文字の属性データ。例えば該当文字のデータが始まる位置を、上記ビットマップデータ
の先頭からのオフセットとか、文字の横サイズ、文字の縦サイズ、次の文字までのピッチ、
描画原点からの横と縦のオフセット等が配列化されている。
3) ビットマップデータ、上記属性データ、行間のピッチをまとめた物。
文字を表示する場合はこの変数をアクセスする事となる。
2.GFXフォーマット
gfxfont.hの中で2つの構造体が定義されている。
1) GFXglyph
Yelllowtail_32.hの中の文字'A'のGFXglyphレコード
{ 1141, 24, 24, 20, 1, -23 }, // 'A'
bitmapOffset : ビットマップデータ領域の先頭からのオフセット。この場合は1141。
width, height : 文字の横幅 = 24ピクセル。高さ = 24ピクセル。
xAdvance : 次の文字までの横方向のピッチ = 20
xOffset, yOffset : 文字の描画開始位置からの横方向のオフセット = 1。
文字の描画開始位置からの縦方向のオフセット = -23。
2) GFXfont
Yelllowtail_32.hの中のGFXfontレコード
const GFXfont Yellowtail_32 PROGMEM = {
(uint8_t *)Yellowtail_32Bitmaps,(GFXglyph *)Yellowtail_32Glyphs,0x20, 0x7D, 45};
bitmap : ビットマップデータ全体へのポインタ
glyph : GFXglyph構造体の配列のポインタ
first, last : 最初の文字、最後の文字。ASCIIコードからfirstを引いた値を
GFXglyph構造体配列のインデックスとする。
yAdvance : 次の行までの高さ。
3.GFXフォーマットの文字データの展開
該当文字のGFXglyph構造体レコードを取得すると、該当文字データの開始アドレスを
取得できる。
このデータはランレングス(RUN LENGTH)の様な圧縮は行われておらず、ビット列を
ビットマップとして画面上に展開する。しかしFont16.cの様な無圧縮のビットマップ
データをそのままメモリ領域に展開してしまうと文字サイズが大きい時は必要なメモリ
領域も大きくなってしまうので文字の描画開始位置を指定する事でメモリサイズの
増大を防いでいる。
例えば'A'であれば文字の上の辺りから描画開始しなければならないが、'.'であれば
随分下の位置からの描画開始となる。
以下はYelllowtail_32.hの文字'A'と文字'.'のデータである。
文字'A'のビットマップデータ
0x00,0x00,0x1E,0x00, 0x00,0x3F,0x00,0x00,
0x7E,0x00,0x01,0xEE, 0x00,0x03,0xDC,0x00,
0x07,0x9C,0x00,0x0F, 0x38,0x00,0x1E,0x38,
0x00,0x1C,0x78,0x00, 0x38,0x70,0x00,0x70,
0x70,0x00,0xE0,0xE0, 0x01,0xE0,0xE0,0x03,
0xC1,0xC0,0x7F,0xFF, 0xC0,0x7F,0xFF,0x80,
0x0E,0x03,0x80,0x1C, 0x07,0x00,0x38,0x07,
0x00,0x78,0x0F,0x00, 0x70,0x0E,0x00,0xE0,
0x1E,0x00,0xE0,0x1C, 0x00,0xC0,0x38,0x00, // 'A'
でかい!
文字'.'のビットマップデータ
0x7F,0xE0, // '.'
文字によって横幅、高さ、描画開始位置等の属性が異なり、文字を描画する為には
その情報にアクセスしなければならない。
文字'A'の属性データ
{ 1141, 24, 24, 20, 1, -23 }, // 'A'
文字'.'の属性データ
{ 431, 4, 3, 9, 2, -2 }, // '.'
文字'A'のxOffsetは1、yOffsetは-23。
文字'.'のxOffsetは2、yOffsetは-2。
xOffsetは文字開始位置からの相対位置、yOffsetはyAdvanceからの相対位置となる。
基本的にM5stackの描画は左上から右下方向に開始され、Xが大きくなれば右に、
Yが大きくなれば下に進む。
それぞれの文字の描画開始位置は横方向ならxOffsetから、
縦方向なら yAdvance + yOffset(注1)から開始する。Yelllowtail_32.hのyAdvanceは45である。
お試しプログラムは以下のリンク
https://1drv.ms/u/s!AgxfaDqma1yrhk6AY-NUsBh7pyXb
(注1) とか言いつつソースには高さ方向で調整が入っているじゃあないかい!
※TFT_eSPIクラスにはreadPixelメソッドが存在するので、ピクセルの読み出し可能か?と期待しましたが、見事にFFFFが返ってくるorz
GFX Free Fontもプロポーショナルフォントですが、特に筆記体を表示するのに向いている様な気がします。
マイコンにとって最も処理の負荷が小さそうなのは等幅フォントですが、等幅フォントでは例えば小文字の'i'が連続した時に、'i'と'i'の間が大きく開いて体裁が良くありません。
プロポーショナルフォントなら文字毎に適切な文字間隔を指定できるので、とても体裁が良くなります。しかし制御が増えるのでその点は良し悪しです。
ランレングスはSPIとは相性が良さそうな気がしますね。
GFXフォントの使い方の解説(本家?)はここから。
https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts
Customフォルダーの中のフォントは以下のサイトで生成された物?
http://oleddisplay.squix.ch/#/home
GFX Free Font(GFXFF)を使う為のもっとも簡単な方法は、すでにM5ライブラリに用意されているメソッドを使う事です。
例えば
setFreeFont( GFXFFにアクセスする為の構造体のポインタ );でこれから使用するフォントを選択。
drawString( テキスト, 横位置, 縦位置, フォント番号 );で指定された位置に先に指定されたフォントでテキストを描画します。まあつまりこんな感じで、
M5.Lcd.setFreeFont( &Yellowtail_32 ); // Select the font M5.Lcd.drawString( "hello world.", 0, 0, 1 );
これらメソッドはM5Stack.hには記載されていませんが、M5Display.hの中でTFT_eSPIクラスが継承されているのでIn_eSPI.hを参考にすると良いでしょう。
しかし相変わらずこのメソッドでは画面の右端に行った時に折り返しをしてくれません、、、よね?
折り返しとか改行とかを行いたいなら、自分で描画メソッドを作るしかない?
以下ではその解説を!
1.GFXフォーマットのフォントデータファイルは3つの要素で構成されている。
1) 文字をビットマップデータ化したデータ領域
Yelllowtail_32.hであれば
const uint8_t Yellowtail_32Bitmaps[] PROGMEM
配列名は任意でこのファイルの中でしか利用されないが、static宣言はされていない。
2) 該当文字の属性データ。例えば該当文字のデータが始まる位置を、上記ビットマップデータ
の先頭からのオフセットとか、文字の横サイズ、文字の縦サイズ、次の文字までのピッチ、
描画原点からの横と縦のオフセット等が配列化されている。
3) ビットマップデータ、上記属性データ、行間のピッチをまとめた物。
文字を表示する場合はこの変数をアクセスする事となる。
2.GFXフォーマット
gfxfont.hの中で2つの構造体が定義されている。
1) GFXglyph
typedef struct { // Data stored PER GLYPH uint16_t bitmapOffset; // Pointer into GFXfont->bitmap uint8_t width, height; // Bitmap dimensions in pixels uint8_t xAdvance; // Distance to advance cursor (x axis) int8_t xOffset, yOffset; // Dist from cursor pos to UL corner } GFXglyph;
Yelllowtail_32.hの中の文字'A'のGFXglyphレコード
{ 1141, 24, 24, 20, 1, -23 }, // 'A'
bitmapOffset : ビットマップデータ領域の先頭からのオフセット。この場合は1141。
width, height : 文字の横幅 = 24ピクセル。高さ = 24ピクセル。
xAdvance : 次の文字までの横方向のピッチ = 20
xOffset, yOffset : 文字の描画開始位置からの横方向のオフセット = 1。
文字の描画開始位置からの縦方向のオフセット = -23。
2) GFXfont
typedef struct { // Data stored for FONT AS A WHOLE: uint8_t *bitmap; // Glyph bitmaps, concatenated GFXglyph *glyph; // Glyph array uint8_t first, last; // ASCII extents uint8_t yAdvance; // Newline distance (y axis) } GFXfont;
Yelllowtail_32.hの中のGFXfontレコード
const GFXfont Yellowtail_32 PROGMEM = {
(uint8_t *)Yellowtail_32Bitmaps,(GFXglyph *)Yellowtail_32Glyphs,0x20, 0x7D, 45};
bitmap : ビットマップデータ全体へのポインタ
glyph : GFXglyph構造体の配列のポインタ
first, last : 最初の文字、最後の文字。ASCIIコードからfirstを引いた値を
GFXglyph構造体配列のインデックスとする。
yAdvance : 次の行までの高さ。
3.GFXフォーマットの文字データの展開
該当文字のGFXglyph構造体レコードを取得すると、該当文字データの開始アドレスを
取得できる。
このデータはランレングス(RUN LENGTH)の様な圧縮は行われておらず、ビット列を
ビットマップとして画面上に展開する。しかしFont16.cの様な無圧縮のビットマップ
データをそのままメモリ領域に展開してしまうと文字サイズが大きい時は必要なメモリ
領域も大きくなってしまうので文字の描画開始位置を指定する事でメモリサイズの
増大を防いでいる。
例えば'A'であれば文字の上の辺りから描画開始しなければならないが、'.'であれば
随分下の位置からの描画開始となる。
以下はYelllowtail_32.hの文字'A'と文字'.'のデータである。
文字'A'のビットマップデータ
0x00,0x00,0x1E,0x00, 0x00,0x3F,0x00,0x00,
0x7E,0x00,0x01,0xEE, 0x00,0x03,0xDC,0x00,
0x07,0x9C,0x00,0x0F, 0x38,0x00,0x1E,0x38,
0x00,0x1C,0x78,0x00, 0x38,0x70,0x00,0x70,
0x70,0x00,0xE0,0xE0, 0x01,0xE0,0xE0,0x03,
0xC1,0xC0,0x7F,0xFF, 0xC0,0x7F,0xFF,0x80,
0x0E,0x03,0x80,0x1C, 0x07,0x00,0x38,0x07,
0x00,0x78,0x0F,0x00, 0x70,0x0E,0x00,0xE0,
0x1E,0x00,0xE0,0x1C, 0x00,0xC0,0x38,0x00, // 'A'
でかい!
文字'.'のビットマップデータ
0x7F,0xE0, // '.'
文字によって横幅、高さ、描画開始位置等の属性が異なり、文字を描画する為には
その情報にアクセスしなければならない。
文字'A'の属性データ
{ 1141, 24, 24, 20, 1, -23 }, // 'A'
文字'.'の属性データ
{ 431, 4, 3, 9, 2, -2 }, // '.'
文字'A'のxOffsetは1、yOffsetは-23。
文字'.'のxOffsetは2、yOffsetは-2。
xOffsetは文字開始位置からの相対位置、yOffsetはyAdvanceからの相対位置となる。
基本的にM5stackの描画は左上から右下方向に開始され、Xが大きくなれば右に、
Yが大きくなれば下に進む。
それぞれの文字の描画開始位置は横方向ならxOffsetから、
縦方向なら yAdvance + yOffset(注1)から開始する。Yelllowtail_32.hのyAdvanceは45である。
お試しプログラムは以下のリンク
https://1drv.ms/u/s!AgxfaDqma1yrhk6AY-NUsBh7pyXb
(注1) とか言いつつソースには高さ方向で調整が入っているじゃあないかい!
※TFT_eSPIクラスにはreadPixelメソッドが存在するので、ピクセルの読み出し可能か?と期待しましたが、見事にFFFFが返ってくるorz
2019-03-12 22:29
nice!(0)
コメント(0)
コメント 0