True type fontをbitmapにレンダリングするプログラムを作成しました。レンダリングにはFreeTypeライブラリを使用しました。プログラムの作成はRaspberry Pi4で行っています。画像の表示にはEGGX / ProCALLライブラリを使用しました。
複数行の表示と角度を指定して表示できるようにしました。
アンチエリアシングあり
#include <stdint.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <wchar.h> #include <locale.h> #include <math.h> #include <ft2build.h> #include FT_FREETYPE_H #include <eggx.h> /* EGGX を使う時に必要 */ int strwlen(char *buf) { // LC_CTYPE をネイティブロケールに変更 if (setlocale(LC_CTYPE, "") == NULL) { fprintf(stderr, "do not set locale.\n"); exit(EXIT_FAILURE); } int i = 0; int count = 0; while (buf[i] != '\0') { int res = mblen(&buf[i], MB_CUR_MAX); if (res < 0) { fprintf(stderr, "Contains illegal characters.\n"); exit(EXIT_FAILURE); } i += res; count++; } return count; } #define SIZE_OF_ARRAY(array) (sizeof(array) / sizeof(array[0])) int main(int argc, char *argv[]) { FT_Library library; FT_Face face; FT_GlyphSlot slot; int error, win; char text[] = "hello,world!\nこんにちは世界\nrotation\n回転"; wchar_t ws[256]; /* ワイド文字保存用 */ int num_chars = strwlen(text); int draw_x = 100; /* 表示座標 x */ int draw_y = 200; /* 表示座標 y */ int len = mbstowcs(ws, text, SIZE_OF_ARRAY(ws)); if (len == -1) { fprintf(stderr, "mbstowcs error\n"); exit(EXIT_FAILURE); } gsetinitialattributes(DISABLE, BOTTOM_LEFT_ORIGIN); /*eggxの座標設定を通常のグラフィックスと同じに*/ win = gopen(500, 400); /* 1000x600 ピクセルのグラフィックス用ウィンドゥを開く */ layer(win, 0, 1); /* 描画レイヤを設定 */ // FreeTypeの初期化とTrueTypeフォントの読み込み error = FT_Init_FreeType(&library); if (error) { fprintf(stderr, "error FT_Init_FreeType\n"); exit(EXIT_FAILURE); } error = FT_New_Face(library, "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc", 0, &face); if (error == FT_Err_Unknown_File_Format) { fprintf(stderr, "unsupport fonts format\n"); exit(EXIT_FAILURE); } else if (error) { fprintf(stderr, "fonts file not found\n"); exit(EXIT_FAILURE); } slot = face->glyph; error = FT_Set_Pixel_Sizes(face, 0, 60); //ピクセル単位でサイズ指定 //error = FT_Set_Char_Size( face, 0, 16 * 64, 300, 300); //ポイント単位でサイズ指定 if (error) { fprintf(stderr, "FT_Set_Pixel_Sizes\n"); exit(EXIT_FAILURE); } FT_Matrix matrix; /* transformation matrix */ double angle = M_PI / 4.0; /* 回転角度 */ FT_Vector pen; /* untransformed origin */ int line = 0; /* set up matrix */ matrix.xx = (FT_Fixed)(cos(angle) * 0x10000L); matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L); matrix.yx = (FT_Fixed)(sin(angle) * 0x10000L); matrix.yy = (FT_Fixed)(cos(angle) * 0x10000L); pen.x = draw_x * 64; pen.y = (-draw_y) * 64; for (int n = 0; n < num_chars; n++) { int i; FT_Bitmap bitmap; FT_Set_Transform(face, &matrix, &pen); switch (ws[n]) { case '\r': pen.x = draw_x * 64 + cos(angle - M_PI / 2) * line * (face->size->metrics.height); pen.y = -draw_y * 64 + sin(angle - M_PI / 2) * line * (face->size->metrics.height); break; case '\t': /*tabは正しく計算していません */ pen.x = pen.x - sin(angle - M_PI / 2) * (face->size->metrics.max_advance); pen.y = pen.y + cos(angle - M_PI / 2) * (face->size->metrics.max_advance); break; case '\n': line++; pen.x = draw_x * 64 + cos(angle - M_PI / 2) * line * (face->size->metrics.height); pen.y = -draw_y * 64 + sin(angle - M_PI / 2) * line * (face->size->metrics.height); //(face->size->metrics.height>>6) 1文字の最大高さ 次の行のベースライン 固定小数点 26.6 bit break; default: // n文字目の文字をビットマップ化 error = FT_Load_Char(face, ws[n], FT_LOAD_RENDER); // 横書き if (error) { fprintf(stderr, "FT_Load_Char error\n"); exit(EXIT_FAILURE); } bitmap = slot->bitmap; for (i = 0; i < bitmap.rows * bitmap.width; i++) { newrgbcolor(win, bitmap.buffer[i], bitmap.buffer[i], bitmap.buffer[i]); int x = i % bitmap.width + slot->bitmap_left; int y = i / bitmap.width - slot->bitmap_top; if (bitmap.buffer[i] != 0) pset(win, x, y); /* 点を描く */ } pen.x += slot->advance.x; pen.y += slot->advance.y; } } newrgbcolor(win, 0, 0, 255); fillcirc(win, draw_x, draw_y, 3, 3);/* 基準点 */ copylayer(win, 1, 0); /* レイヤ1をレイヤ0に瞬時にコピー */ ggetch(); /* キー入力があるまで待つ */ gclose(win); /* グラフィックス用ウィンドゥを閉じる */ error = FT_Done_Face( face ); if (error) { fprintf(stderr, "error FT_Done_Face\n"); exit(EXIT_FAILURE); } error = FT_Done_FreeType(library); if (error) { fprintf(stderr, "error FT_Done_FreeType\n"); exit(EXIT_FAILURE); } return 0; }
アンチエリアシングなし
#include <stdint.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <wchar.h> #include <locale.h> #include <math.h> #include <ft2build.h> #include FT_FREETYPE_H #include <eggx.h> /* EGGX を使う時に必要 */ int strwlen(char *buf) { // LC_CTYPE をネイティブロケールに変更 if( setlocale( LC_CTYPE, "" ) == NULL ){ fprintf(stderr, "do not set locale.\n"); exit(EXIT_FAILURE); } int i = 0; int count = 0; while( buf[i] != '\0' ){ int res = mblen( &buf[i], MB_CUR_MAX ); if( res < 0 ){ fprintf(stderr, "Contains illegal characters.\n"); exit(EXIT_FAILURE); } i += res; count++; } return count; } #define SIZE_OF_ARRAY(array) (sizeof(array)/sizeof(array[0])) int main(int argc,char *argv[]){ FT_Library library; FT_Face face; FT_GlyphSlot slot; int error,win; char text[] = "hello,world!\nこんにちは世界\nrotation\n回転"; wchar_t ws[256]; /* ワイド文字保存用 */ int num_chars = strwlen(text); int draw_x = 100; int draw_y = 200; int len = mbstowcs( ws, text, SIZE_OF_ARRAY(ws) ); if( len == -1 ){ fprintf(stderr, "mbstowcs error\n"); exit( EXIT_FAILURE ); } gsetinitialattributes(DISABLE, BOTTOM_LEFT_ORIGIN); /*eggxの座標設定を通常のグラフィックスと同じに*/ win = gopen(500,400); /* 1000x600 ピクセルのグラフィックス用ウィンドゥを開く */ layer(win, 0, 1); /* 描画レイヤを設定 */ // FreeTypeの初期化とTrueTypeフォントの読み込み error = FT_Init_FreeType( &library ); if(error) { fprintf(stderr,"error FT_Init_FreeType\n"); exit(EXIT_FAILURE); } error = FT_New_Face( library, "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc", 0, &face ); if(error == FT_Err_Unknown_File_Format) { fprintf(stderr,"unsupport fonts format\n"); exit(EXIT_FAILURE); } else if(error) { fprintf(stderr,"fonts file not found\n"); exit(EXIT_FAILURE); } slot = face->glyph; error = FT_Set_Pixel_Sizes( face, 0, 60 ); //ピクセル単位でサイズ指定 //error = FT_Set_Char_Size( face, 0, 16 * 64, 300, 300); //ポイント単位でサイズ指定 if(error) { fprintf(stderr,"FT_Set_Pixel_Sizes\n"); exit(EXIT_FAILURE); } FT_Matrix matrix; /* transformation matrix */ double angle = M_PI/4.0; /* 回転角度 */ FT_Vector pen; /* untransformed origin */ int line=0; /* set up matrix */ matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); pen.x = draw_x*64; pen.y = (-draw_y)*64; for (int n = 0;n < num_chars; n++ ){ FT_Bitmap bitmap; FT_Set_Transform( face, &matrix, &pen ); switch(ws[n]) { case '\r': pen.x = draw_x*64 + cos(angle-M_PI/2) * line * (face->size->metrics.height); pen.y = -draw_y*64 + sin(angle-M_PI/2) * line * (face->size->metrics.height); break; case '\t': /*tabは正しく計算していません */ pen.x = pen.x - sin(angle-M_PI/2)*(face->size->metrics.max_advance); pen.y = pen.y + cos(angle-M_PI/2)*(face->size->metrics.max_advance); break; case '\n': line++; pen.x = draw_x*64 + cos(angle-M_PI/2) * line * (face->size->metrics.height); pen.y = -draw_y*64 + sin(angle-M_PI/2) * line * (face->size->metrics.height); //(face->size->metrics.height>>6) 1文字の最大高さ 次の行のベースライン 固定小数点 26.6 bit break; default: // n文字目の文字をビットマップ化 error = FT_Load_Char( face, ws[n], FT_LOAD_RENDER |FT_LOAD_MONOCHROME );// 横書き アンチエリアシングなし if(error) { fprintf(stderr,"FT_Load_Char error\n"); exit(EXIT_FAILURE); } bitmap = slot->bitmap; newrgbcolor(win,255,255,255);/*白色*/ for( int loopy = 0; loopy < bitmap.rows ; loopy++){ for(int byte_index=0;byte_index<bitmap.pitch;byte_index++) { int byte_value,rowstart,end_loop,num_bits_done; byte_value = bitmap.buffer[loopy*bitmap.pitch+byte_index]; num_bits_done = byte_index*8; rowstart = loopy*bitmap.width + byte_index*8; if(8 > (bitmap.width - num_bits_done)) { end_loop = bitmap.width - num_bits_done; } else { end_loop = 8; } for(int bit_index=0;bit_index<end_loop;bit_index++) { int bit = byte_value & (1<<(7-bit_index)); if(bit!=0) { int x = (bit_index+rowstart)%bitmap.width + slot->bitmap_left; int y = (bit_index+rowstart)/bitmap.width - slot->bitmap_top; pset(win,x,y); /* 点を描く */ } } } } pen.x += slot->advance.x ; pen.y += slot->advance.y ; } } newrgbcolor(win,0,0,255); fillcirc (win,draw_x,draw_y,3,3); /* 基準点 */ copylayer(win, 1, 0); /* レイヤ1をレイヤ0に瞬時にコピー */ ggetch(); /* キー入力があるまで待つ */ gclose(win); /* グラフィックス用ウィンドゥを閉じる */ error = FT_Done_Face( face ); if(error) { fprintf(stderr,"error FT_Done_Face\n"); exit(EXIT_FAILURE); } error = FT_Done_FreeType( library ); if(error) { fprintf(stderr,"error FT_Done_FreeType\n"); exit(EXIT_FAILURE); } return 0; }
実行結果
参考