pfnfont.cpp 8.0 KB

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