LoginSignup
2
2

More than 1 year has passed since last update.

stb_easy_font.hのご紹介

Last updated at Posted at 2022-12-09

この記事はC++ Advent Calendar 2022の10日目の記事です。

TL;DR & 前書き

  • この記事は、stb_easy_font.h についての使い方の説明になります。

簡単な自己紹介

画像処理系の組み込みエンジニアです。OpenCV communityにもちょくちょくコメントしています。よろしくお願いします!

本当は C++20の「モジュール」について記事を書こうとしたのですが、時間が無く、ゆるふわネタです。ごめんなさい。

煽られてしまいましたか…

フォントエンジン・・・フォントエンジンの記事が読みたいとな!!

ということで、STBのフォントエンジン記事を書きますかね?

ただし、STBはSTBでも、stb_truetype.h ではなく、stb_easy_font.h です!

そもそもSTBとは何ぞや?

see https://github.com/nothings/stb ...

single-file public domain (or MIT licensed) libraries for C/C++

「ヘッダファイル1つで機能を実現するライブラリ」ですかね。

例えば、画像データを読み込みたいならば stb_image.h、書き込みたいならstb_image_write.h をincludeすれば、この中に実行部分も入っているのですぐに使えます。

OpenCV 5.0以後では、stb_truetype.h を使って、UTF-8で日本語なんかも書けるようになっちゃいます!さて、それはそれとして……

stb_easy_font.h とは?

ヘッダファイルによると「3D Rendering向けのbitmap font」とのこと。

// stb_easy_font.h - v1.1 - bitmap font for 3D rendering - public domain
// Sean Barrett, Feb 2015
//
//    Easy-to-deploy,
//    reasonably compact,
//    extremely inefficient performance-wise,
//    crappy-looking,
//    ASCII-only,
//    bitmap font for use in 3D APIs.
//

解説

本体部分はたった3行である。

    char buffer[99999];
    char text[]="C++ Advent Calendar 2022\nHappy New Year !!!\nWith stb_easy_font";
    int num_quads = stb_easy_font_print(0, 0, text, NULL, buffer, sizeof(buffer));

この3行を実行すると、bufferの中に、指定した文字列を描画するための座標情報が保持される。

Offset 内容
+0x0 X1座標(Float)
+0x4 Y1座標(Float)
+0x8 Z1座標(Float, 0固定)
+0xC 色情報
+0x10 X2座標(Float)
+0x14 Y2座標(Float)
+0x18 Z2座標(Float, 0固定)
+0x1C 色情報
+0x20 X3座標(Float)
+0x24 Y3座標(Float)
+0x28 Z3座標(Float, 0固定)
+0x2C 色情報
+0x30 X4座標(Float)
+0x34 Y4座標(Float)
+0x38 Z4座標(Float, 0固定)
+0x3C 色情報

本来は...

当該データをOpenGLで処理して・・・ということができていたらしい。

今回は...

これに対して、テスト結果を確認するために、画像を描画しなければならない。

  • pixel()関数 - フレームバッファに画素を書き出す
  • line()関数 - フレームバッファに直線を書き出す
  • PBMでの画像出力。

出力結果

こんな感じで、無事に文字出力ができました、と。

image.png

以上となります。

おまけ:サンプルコードを以下に示す。

サンプルコード
// g++ main.cpp -o a.out
// ./a.out > test.pbm

#include <iostream>
#include "stb_easy_font.h"

const int screen_height = 60;
const int screen_width  = 200;
uint32_t fb[screen_height][screen_width] = { 0 };

void pset(int x, int y)
{
  if ( ( x < 0 ) || ( screen_width  < x ) ) { return ; }
  if ( ( y < 0 ) || ( screen_height < y ) ) { return ; }
  fb[y][x] = 1;
}

void line(float x1, float y1, float x2, float y2 )
{
  int x,y;
  float sx, sy, ex, ey;

  if ( x1 == x2 )
  {
    // Vertial line
    if( y1 < y2 )
    {
      x = x1; sy = y1; ey = y2;
    }
    else
    {
      x = x1; sy = y2; ey = y1;
    }

    for( y = sy; y <= ey; y++ )
    {
      pset(x,y);
    }
  }else{
    // Non vertial line
    if( x1 < x2 )
    {
      sx = x1; ex = x2; sy = y1; ey = y2;
    }
    else
    {
      sx = x2; ex = x1; sy = y2; ey = y1;
    }

    for( x = sx; x <= ex; x++ )
    {
      y = sy + ( x - sx )  * ( ey - sy ) / ( ex - sx );
      pset(x,y);
    }
  }
}

int main()
{
    // Drawing
    char buffer[99999];
    char text[]="C++ Advent Calendar 2022\nHappy New Year !!!\nWith stb_easy_font";
    int num_quads = stb_easy_font_print(0, 0, text, NULL, buffer, sizeof(buffer));

    // Rendering
    for(int i = 0 ; i < num_quads; i++ )
    {
        const float x1 = *(float*)&( buffer[i * 64 + 16 * 0 + 0 ] );
        const float y1 = *(float*)&( buffer[i * 64 + 16 * 0 + 4 ] );
        const float x2 = *(float*)&( buffer[i * 64 + 16 * 1 + 0 ] );
        const float y2 = *(float*)&( buffer[i * 64 + 16 * 1 + 4 ] );
        const float x3 = *(float*)&( buffer[i * 64 + 16 * 2 + 0 ] );
        const float y3 = *(float*)&( buffer[i * 64 + 16 * 2 + 4 ] );
        const float x4 = *(float*)&( buffer[i * 64 + 16 * 3 + 0 ] );
        const float y4 = *(float*)&( buffer[i * 64 + 16 * 3 + 4 ] );

        line(x1, y1,  x2, y2 );
        line(x2, y2,  x3, y3 );
        line(x3, y3,  x4, y4 );
        line(x4, y4,  x1, y1 );
    }

    // Export to PBM
    std::cout << "P1 " << screen_width << " " << screen_height << std::endl; ;
    for(int iy=0; iy < screen_height; iy++ )
    {
      for(int ix=0; ix < screen_width; ix++ )
      {
        std::cout << ( fb[iy][ix] == 0 ? 0 : 1) << " ";
      }
      std::cout << std::endl;
    }
    return 0;
}
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2