bdf-font.cc 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
  2. // Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation version 2.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program. If not, see <http://gnu.org/licenses/gpl-2.0.txt>
  15. // Some old g++ installations need this macro to be defined for PRIx64.
  16. #ifndef __STDC_FORMAT_MACROS
  17. # define __STDC_FORMAT_MACROS
  18. #endif
  19. #include <inttypes.h>
  20. #include "graphics.h"
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24. // The little question-mark box "�" for unknown code.
  25. static const uint32_t kUnicodeReplacementCodepoint = 0xFFFD;
  26. // Bitmap for one row. This limits the number of available columns.
  27. // Make wider if running into trouble.
  28. typedef uint64_t rowbitmap_t;
  29. namespace rgb_matrix {
  30. struct Font::Glyph {
  31. int device_width, device_height;
  32. int width, height;
  33. int x_offset, y_offset;
  34. rowbitmap_t bitmap[0]; // contains 'height' elements.
  35. };
  36. Font::Font() : font_height_(-1), base_line_(0) {}
  37. Font::~Font() {
  38. for (CodepointGlyphMap::iterator it = glyphs_.begin();
  39. it != glyphs_.end(); ++it) {
  40. free(it->second);
  41. }
  42. }
  43. // TODO: that might not be working for all input files yet.
  44. bool Font::LoadFont(const char *path) {
  45. if (!path || !*path) return false;
  46. FILE *f = fopen(path, "r");
  47. if (f == NULL)
  48. return false;
  49. uint32_t codepoint;
  50. char buffer[1024];
  51. int dummy;
  52. Glyph tmp;
  53. Glyph *current_glyph = NULL;
  54. int row = 0;
  55. int bitmap_shift = 0;
  56. while (fgets(buffer, sizeof(buffer), f)) {
  57. if (sscanf(buffer, "FONTBOUNDINGBOX %d %d %d %d",
  58. &dummy, &font_height_, &dummy, &base_line_) == 4) {
  59. base_line_ += font_height_;
  60. }
  61. else if (sscanf(buffer, "ENCODING %ud", &codepoint) == 1) {
  62. // parsed.
  63. }
  64. else if (sscanf(buffer, "DWIDTH %d %d", &tmp.device_width, &tmp.device_height
  65. ) == 2) {
  66. // parsed.
  67. }
  68. else if (sscanf(buffer, "BBX %d %d %d %d", &tmp.width, &tmp.height,
  69. &tmp.x_offset, &tmp.y_offset) == 4) {
  70. current_glyph = (Glyph*) malloc(sizeof(Glyph)
  71. + tmp.height * sizeof(rowbitmap_t));
  72. *current_glyph = tmp;
  73. // We only get number of bytes large enough holding our width. We want
  74. // it always left-aligned.
  75. bitmap_shift =
  76. 8 * (sizeof(rowbitmap_t) - ((current_glyph->width + 7) / 8))
  77. - current_glyph->x_offset;
  78. row = -1; // let's not start yet, wait for BITMAP
  79. }
  80. else if (strncmp(buffer, "BITMAP", strlen("BITMAP")) == 0) {
  81. row = 0;
  82. }
  83. else if (current_glyph && row >= 0 && row < current_glyph->height
  84. && (sscanf(buffer, "%" PRIx64, &current_glyph->bitmap[row]) == 1)) {
  85. current_glyph->bitmap[row] <<= bitmap_shift;
  86. row++;
  87. }
  88. else if (strncmp(buffer, "ENDCHAR", strlen("ENDCHAR")) == 0) {
  89. if (current_glyph && row == current_glyph->height) {
  90. free(glyphs_[codepoint]); // just in case there was one.
  91. glyphs_[codepoint] = current_glyph;
  92. current_glyph = NULL;
  93. }
  94. }
  95. }
  96. fclose(f);
  97. return true;
  98. }
  99. Font *Font::CreateOutlineFont() const {
  100. Font *r = new Font();
  101. const int kBorder = 1;
  102. r->font_height_ = font_height_ + 2*kBorder;
  103. r->base_line_ = base_line_ + kBorder;
  104. for (CodepointGlyphMap::const_iterator it = glyphs_.begin();
  105. it != glyphs_.end(); ++it) {
  106. const Glyph *orig = it->second;
  107. const int height = orig->height + 2 * kBorder;
  108. const size_t alloc_size = sizeof(Glyph) + height * sizeof(rowbitmap_t);
  109. Glyph *const tmp_glyph = (Glyph*) calloc(1, alloc_size);
  110. tmp_glyph->width = orig->width + 2*kBorder;
  111. tmp_glyph->height = height;
  112. tmp_glyph->device_width = orig->device_width + 2*kBorder;
  113. tmp_glyph->device_height = height;
  114. tmp_glyph->y_offset = orig->y_offset - kBorder;
  115. // TODO: we don't really need bounding box, right ?
  116. const rowbitmap_t fill_pattern = 0b111;
  117. const rowbitmap_t start_mask = 0b010;
  118. // Fill the border
  119. for (int h = 0; h < orig->height; ++h) {
  120. rowbitmap_t fill = fill_pattern;
  121. rowbitmap_t orig_bitmap = orig->bitmap[h] >> kBorder;
  122. for (rowbitmap_t m = start_mask; m; m <<= 1, fill <<= 1) {
  123. if (orig_bitmap & m) {
  124. tmp_glyph->bitmap[h+kBorder-1] |= fill;
  125. tmp_glyph->bitmap[h+kBorder+0] |= fill;
  126. tmp_glyph->bitmap[h+kBorder+1] |= fill;
  127. }
  128. }
  129. }
  130. // Remove original font again.
  131. for (int h = 0; h < orig->height; ++h) {
  132. rowbitmap_t orig_bitmap = orig->bitmap[h] >> kBorder;
  133. tmp_glyph->bitmap[h+kBorder] &= ~orig_bitmap;
  134. }
  135. r->glyphs_[it->first] = tmp_glyph;
  136. }
  137. return r;
  138. }
  139. const Font::Glyph *Font::FindGlyph(uint32_t unicode_codepoint) const {
  140. CodepointGlyphMap::const_iterator found = glyphs_.find(unicode_codepoint);
  141. if (found == glyphs_.end())
  142. return NULL;
  143. return found->second;
  144. }
  145. int Font::CharacterWidth(uint32_t unicode_codepoint) const {
  146. const Glyph *g = FindGlyph(unicode_codepoint);
  147. return g ? g->device_width : -1;
  148. }
  149. int Font::DrawGlyph(Canvas *c, int x_pos, int y_pos,
  150. const Color &color, const Color *bgcolor,
  151. uint32_t unicode_codepoint) const {
  152. const Glyph *g = FindGlyph(unicode_codepoint);
  153. if (g == NULL) g = FindGlyph(kUnicodeReplacementCodepoint);
  154. if (g == NULL) return 0;
  155. y_pos = y_pos - g->height - g->y_offset;
  156. for (int y = 0; y < g->height; ++y) {
  157. const rowbitmap_t row = g->bitmap[y];
  158. rowbitmap_t x_mask = (1LL<<63);
  159. for (int x = 0; x < g->device_width; ++x, x_mask >>= 1) {
  160. if (row & x_mask) {
  161. c->SetPixel(x_pos + x, y_pos + y, color.r, color.g, color.b);
  162. } else if (bgcolor) {
  163. c->SetPixel(x_pos + x, y_pos + y, bgcolor->r, bgcolor->g, bgcolor->b);
  164. }
  165. }
  166. }
  167. return g->device_width;
  168. }
  169. int Font::DrawGlyph(Canvas *c, int x_pos, int y_pos, const Color &color,
  170. uint32_t unicode_codepoint) const {
  171. return DrawGlyph(c, x_pos, y_pos, color, NULL, unicode_codepoint);
  172. }
  173. } // namespace rgb_matrix