pfnfont.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include "pfnfont.h"
  4. PFNFont::PFNFont(uint8_t *pfn_font, size_t pfnfont_size, void (*draw_font)(font_gryph_t *,int,int))
  5. {
  6. is_valid = false;
  7. font_data = pfn_font;
  8. font_size = pfnfont_size;
  9. draw_font_callback = draw_font;
  10. aligned_bitmap_buffer = NULL;
  11. // ヘッダサイズとシグニチャを検証
  12. if (pfnfont_size < sizeof(pfn_font_header_t) || memcmp(font_data, "PINOTFN", 7) != 0) {
  13. return; // 不正なフォントデータ
  14. }
  15. // first blockの先頭アドレス
  16. first_block = font_data + sizeof(pfn_font_header_t);
  17. if (first_block >= font_data + pfnfont_size) return; // データサイズが不正
  18. // ブロックをたどりフォントビットマップの最大サイズを調べる
  19. bitmap_buffer_size = 0;
  20. uint8_t *current_block_ptr = first_block;
  21. while(current_block_ptr < (font_data + pfnfont_size)) {
  22. pfn_block_header_t *current_block_header = (pfn_block_header_t *)current_block_ptr;
  23. // このブロックのビットマップサイズ(バイト単位)
  24. int bitmap_size = (current_block_header->width * current_block_header->height + 7) / 8;
  25. bitmap_buffer_size = max(bitmap_buffer_size, bitmap_size);
  26. // このブロックのデータ部分の合計サイズ
  27. int block_data_size = current_block_header->num_glyphs * (bitmap_size + current_block_header->codepoint_size);
  28. // 次のブロックへポインタを進める (ヘッダサイズ + データサイズ)
  29. current_block_ptr += sizeof(pfn_block_header_t) + block_data_size;
  30. }
  31. // 4バイトアラインメントの配列としてメモリを確保
  32. if (bitmap_buffer_size > 0) {
  33. aligned_bitmap_buffer = (uint8_t *)new uint32_t[(bitmap_buffer_size + 3) / 4];
  34. } else {
  35. aligned_bitmap_buffer = NULL;
  36. }
  37. is_valid = true;
  38. }
  39. PFNFont::~PFNFont()
  40. {
  41. // 確保したメモリを開放
  42. delete[] aligned_bitmap_buffer;
  43. }
  44. // UTF-8文字列へのポインタを受け取り、1文字デコードしてコードポイントを返す
  45. uint32_t PFNFont::utf8_to_codepoint(const char **utf8_str)
  46. {
  47. uint32_t codepoint = 0;
  48. const uint8_t* s = (const uint8_t*)*utf8_str;
  49. if (s[0] < 0x80) { // 1-byte (ASCII)
  50. codepoint = s[0];
  51. *utf8_str += 1;
  52. } else if ((s[0] & 0xE0) == 0xC0) { // 2-byte sequence
  53. codepoint = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F);
  54. *utf8_str += 2;
  55. } else if ((s[0] & 0xF0) == 0xE0) { // 3-byte sequence
  56. codepoint = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
  57. *utf8_str += 3;
  58. } else if ((s[0] & 0xF8) == 0xF0) { // 4-byte sequence
  59. codepoint = ((s[0] & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
  60. *utf8_str += 4;
  61. } else {
  62. // 不正なUTF-8シーケンスの場合は、1バイト進めてエラーを示す
  63. codepoint = '?';
  64. *utf8_str += 1;
  65. }
  66. return codepoint;
  67. }
  68. // コードポイントサイズに合わせてgryphのコードポイントを得るユーティリティ関数
  69. inline uint32_t PFNFont::get_codepoint_from_glyph(uint8_t *glyph_data, uint8_t codepoint_size)
  70. {
  71. uint32_t codepoint = 0;
  72. if(codepoint_size == 1) {
  73. pfn_glyph1_t *glyph = (pfn_glyph1_t *)glyph_data;
  74. codepoint = glyph->codepoint;
  75. }
  76. else if(codepoint_size == 2){
  77. pfn_glyph2_t *glyph = (pfn_glyph2_t *)glyph_data;
  78. codepoint = glyph->codepoint;
  79. }
  80. else if(codepoint_size == 4){
  81. pfn_glyph4_t *glyph = (pfn_glyph4_t *)glyph_data;
  82. codepoint = glyph->codepoint;
  83. }
  84. return codepoint;
  85. }
  86. // コードポイントがあるブロックを返す
  87. uint8_t* PFNFont::find_glyph_in_block(uint32_t codepoint)
  88. {
  89. uint8_t *current_block = first_block;
  90. do {
  91. pfn_block_header_t *current_block_header = (pfn_block_header_t *)current_block;
  92. uint8_t codepoint_size = current_block_header->codepoint_size;
  93. uint8_t font_height = current_block_header->height;
  94. uint8_t font_width = current_block_header->width;
  95. uint16_t num_glyphs = current_block_header->num_glyphs;
  96. int glyph_size = (font_width * font_height + 7) / 8 + codepoint_size;
  97. // ブロックのデータ部分のサイズ
  98. int block_data_size = glyph_size * num_glyphs;
  99. uint8_t *first_glyph = current_block + sizeof(pfn_block_header_t);
  100. uint32_t first_codepoint = get_codepoint_from_glyph(first_glyph, codepoint_size);
  101. uint8_t *last_glyph = first_glyph + (num_glyphs - 1) * glyph_size;
  102. uint32_t last_codepoint = get_codepoint_from_glyph(last_glyph, codepoint_size);
  103. if(codepoint < first_codepoint || codepoint > last_codepoint) {
  104. // 次のブロックへ移動 (ヘッダサイズ + データサイズ)
  105. current_block += sizeof(pfn_block_header_t) + block_data_size;
  106. if(current_block >= font_data + font_size) {
  107. current_block = NULL;
  108. break;
  109. }
  110. }
  111. else {
  112. break;
  113. }
  114. } while(true);
  115. return current_block;
  116. }
  117. // コードポイントからフォントグリフを得る
  118. font_gryph_t *PFNFont::get_gryph_data(uint32_t codepoint)
  119. {
  120. uint8_t *block = find_glyph_in_block(codepoint);
  121. if (block == NULL) {
  122. // 要求されたグリフが見つからない場合、代替文字('?')を探すか、NULLを返す
  123. // ここでは'?'が存在すればそれを返し、なければNULLを返す
  124. if (codepoint != '?') {
  125. return get_gryph_data('?');
  126. }
  127. return NULL;
  128. }
  129. pfn_block_header_t *block_header = (pfn_block_header_t *)block;
  130. uint8_t font_height = block_header->height;
  131. uint8_t font_width = block_header->width;
  132. uint16_t num_glyphs = block_header->num_glyphs;
  133. uint8_t codepoint_size = block_header->codepoint_size;
  134. uint8_t *first_glyph = block + sizeof(pfn_block_header_t);
  135. int glyph_size = (font_width * font_height + 7) / 8 + codepoint_size;
  136. // 二分探索でグリフを検索
  137. int low = 0;
  138. int high = num_glyphs - 1;
  139. uint8_t *found_glyph = NULL;
  140. while (low <= high) {
  141. int mid = low + (high - low) / 2;
  142. uint8_t *current_glyph = first_glyph + mid * glyph_size;
  143. uint32_t current_codepoint = get_codepoint_from_glyph(current_glyph, codepoint_size);
  144. if (current_codepoint == codepoint) {
  145. found_glyph = current_glyph;
  146. break;
  147. } else if (current_codepoint < codepoint) {
  148. low = mid + 1;
  149. } else {
  150. high = mid - 1;
  151. }
  152. }
  153. if (found_glyph == NULL) {
  154. // find_glyph_in_blockが成功していればここには到達しないはずだが、念のため
  155. return (codepoint != '?') ? get_gryph_data('?') : NULL;
  156. }
  157. int bitmap_data_size = (font_width * font_height + 7) / 8;
  158. gryph_data.width = font_width;
  159. gryph_data.height = font_height;
  160. // ビットマップデータをアラインされたバッファにコピー
  161. memcpy(aligned_bitmap_buffer, found_glyph + codepoint_size, bitmap_data_size);
  162. gryph_data.bitmap = aligned_bitmap_buffer;
  163. return &gryph_data;
  164. }
  165. // 文字列を描画する
  166. void PFNFont::draw_string(const char *str, int x, int y)
  167. {
  168. const char *p = str;
  169. while(*p != '\0') {
  170. uint32_t codepoint = utf8_to_codepoint(&p);
  171. font_gryph_t *gryph = get_gryph_data(codepoint);
  172. if(gryph != NULL && draw_font_callback != NULL) {
  173. draw_font_callback(gryph, x, y);
  174. x += gryph->width;
  175. }
  176. }
  177. }