20130420
[gdash.git] / src / gfx / fontmanager.cpp
blob95e0fdea8c36d5047519c9defabedfb3c8e64db4
1 /*
2 * Copyright (c) 2007-2013, Czirkos Zoltan http://code.google.com/p/gdash/
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
19 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 #include "config.h"
26 #include <vector>
27 #include <algorithm>
28 #include <memory>
30 #include "gfx/pixbuf.hpp"
31 #include "gfx/screen.hpp"
32 #include "gfx/pixbuffactory.hpp"
33 #include "cave/colors.hpp"
34 #include "gfx/cellrenderer.hpp"
35 #include "gfx/fontmanager.hpp"
36 #include "misc/printf.hpp"
37 #include "misc/logger.hpp"
39 #include "c64_font.cpp"
42 /**
43 * @brief A RenderedFont stores glyphs rendered for a given font and a given color.
44 * This is an abstract base class, the narrow and wide font are derived from this.
46 class RenderedFont {
47 public:
48 /// Number of characters on the font map.
49 enum {
50 CHARS_X = 32,
51 CHARS_Y = 4,
52 NUM_OF_CHARS = CHARS_X * CHARS_Y
55 /// Constructor.
56 /// @param bitmap_ Raw font data.
57 /// @param color_ The color of drawing.
58 /// @param pixbuf_factory The pixbuf factory used to create glyphs.
59 RenderedFont(std::vector<unsigned char> const &bitmap_, unsigned font_size_, GdColor const &color_, Screen &screen);
61 /// Destructor.
62 virtual ~RenderedFont();
64 /// Return the pixmap for a given character; maybe after creating it.
65 /// @param j The ASCII (or GDash) code of the character
66 Pixmap const &get_character(int j) const;
68 /// GdColor::get_uint() code of color, for easy searching in a font manager.
69 guint32 uint;
71 protected:
72 /// Raw font data.
73 std::vector<unsigned char> const &bitmap;
75 /// Font size (pixbufs)
76 unsigned int font_size;
78 /// The Screen for which this font is rendered.
79 Screen &screen;
81 /// RGBA format of color in pixbufs.
82 guint32 col;
84 /// RGBA format of transparent pixel in pixbufs.
85 guint32 transparent;
87 /// The rendered glyphs are stored in this array as pixmaps.
88 mutable Pixmap *_character[NUM_OF_CHARS];
90 RenderedFont(const RenderedFont &); // not implemented
91 RenderedFont &operator=(const RenderedFont &); // not implemented
93 private:
94 /// Render a single character. The narrow and wide fonts implement this.
95 virtual Pixmap *render_character(int j) const = 0;
98 class RenderedFontNarrow: public RenderedFont {
99 private:
100 virtual Pixmap *render_character(int j) const;
102 public:
103 RenderedFontNarrow(std::vector<unsigned char> const &bitmap_, unsigned font_size_, GdColor const &color, Screen &screen)
104 : RenderedFont(bitmap_, font_size_, color, screen) {}
107 class RenderedFontWide: public RenderedFont {
108 private:
109 virtual Pixmap *render_character(int j) const;
111 public:
112 RenderedFontWide(std::vector<unsigned char> const &bitmap_, unsigned font_size_, GdColor const &color, Screen &screen)
113 : RenderedFont(bitmap_, font_size_, color, screen) {}
117 RenderedFont::~RenderedFont() {
118 for (unsigned i = 0; i < NUM_OF_CHARS; ++i)
119 delete _character[i];
123 RenderedFont::RenderedFont(std::vector<unsigned char> const &bitmap_, unsigned font_size_, GdColor const &color, Screen &screen)
124 : uint(color.get_uint()),
125 bitmap(bitmap_),
126 font_size(font_size_),
127 screen(screen),
128 _character() {
129 g_assert(font_size_*font_size_*NUM_OF_CHARS == bitmap_.size());
130 col = Pixbuf::rgba_pixel_from_color(color, 0xff); /* opaque */
131 transparent = Pixbuf::rgba_pixel_from_color(GdColor::from_rgb(0, 0, 0), 0x00); /* transparent black */
134 Pixmap *RenderedFontNarrow::render_character(int j) const {
135 int y1 = (j / CHARS_X) * font_size;
136 int x1 = (j % CHARS_X) * font_size;
138 std::auto_ptr<Pixbuf> image(screen.pixbuf_factory.create(font_size, font_size));
139 for (unsigned y = 0; y < font_size; y++) {
140 guint32 *p = image->get_row(y);
141 for (unsigned x = 0; x < font_size; x++) {
142 /* the font array is encoded the same way as a c64-colored pixbuf. see c64_gfx_data...() */
143 if (bitmap[(y1 + y) * (CHARS_X * font_size) + x1 + x] != 1) /* 1 is black there!! */
144 p[x] = col; /* normal */
145 else
146 p[x] = transparent; /* normal */
149 return screen.create_scaled_pixmap_from_pixbuf(*image, true);
152 Pixmap *RenderedFontWide::render_character(int j) const {
153 int y1 = (j / CHARS_X) * font_size;
154 int x1 = (j % CHARS_X) * font_size;
156 std::auto_ptr<Pixbuf> image(screen.pixbuf_factory.create(font_size * 2, font_size));
157 for (unsigned y = 0; y < font_size; y++) {
158 guint32 *p = image->get_row(y);
159 for (unsigned x = 0; x < font_size; x++) {
160 /* the font array is encoded the same way as a c64-colored pixbuf. see c64_gfx_data...() */
161 if (bitmap[(y1 + y) * (CHARS_X * font_size) + x1 + x] != 1) { /* 1 is black there!! */
162 p[2 * x + 0] = col; /* normal */
163 p[2 * x + 1] = col;
164 } else {
165 p[2 * x + 0] = transparent; /* normal */
166 p[2 * x + 1] = transparent; /* normal */
170 return screen.create_scaled_pixmap_from_pixbuf(*image, true);
173 Pixmap const &RenderedFont::get_character(int j) const {
174 g_assert(j < NUM_OF_CHARS);
175 if (_character[j] == NULL)
176 _character[j] = render_character(j);
177 return *_character[j];
181 /* check if given surface is ok to be a gdash theme. */
182 bool FontManager::is_pixbuf_ok_for_theme(const Pixbuf &surface) {
183 if ((surface.get_width() % RenderedFont::CHARS_X != 0)
184 || (surface.get_height() % RenderedFont::CHARS_Y != 0)
185 || (surface.get_width() / RenderedFont::CHARS_X != surface.get_height() / RenderedFont::CHARS_Y)) {
186 gd_critical(CPrintf("image should contain %d chars in a row and %d in a column!") % int(RenderedFont::CHARS_X) % int(RenderedFont::CHARS_Y));
187 return false;
190 return true; /* passed checks */
193 bool FontManager::is_image_ok_for_theme(PixbufFactory &pixbuf_factory, const char *filename) {
194 try {
195 Pixbuf *image = pixbuf_factory.create_from_file(filename);
196 /* if the image is loaded */
197 SetLoggerContextForFunction scf(filename);
198 bool result = is_pixbuf_ok_for_theme(*image);
199 delete image;
200 return result;
201 } catch (...) {
202 return false;
206 bool FontManager::loadfont_image(Pixbuf const &image) {
207 if (!is_pixbuf_ok_for_theme(image))
208 return false;
210 clear();
211 font = Pixbuf::c64_gfx_data_from_pixbuf(image);
212 font_size = image.get_width() / RenderedFont::CHARS_X;
213 return true;
216 /* load theme from image file. */
217 /* return true if successful. */
218 bool FontManager::loadfont_file(const std::string &filename) {
219 /* load cell graphics */
220 /* load from file */
221 try {
222 Pixbuf *image = screen.pixbuf_factory.create_from_file(filename.c_str());
223 bool result = loadfont_image(*image);
224 delete image;
225 if (!result)
226 gd_critical(CPrintf("%s: invalid font bitmap") % filename);
227 return result;
228 } catch (std::exception &e) {
229 gd_critical(CPrintf("%s: unable to load image (%s)") % filename % e.what());
230 return false;
234 /* load the theme from the given file. */
235 /* if successful, ok. */
236 /* if fails, or no theme specified, load the builtin */
237 void FontManager::load_theme(const std::string &theme_file) {
238 if (theme_file != "" && loadfont_file(theme_file)) {
239 /* loaded from png file */
240 } else {
241 Pixbuf *image = screen.pixbuf_factory.create_from_inline(sizeof(c64_font), c64_font);
242 bool result = loadfont_image(*image);
243 g_assert(result == true); // to check the builting font
244 delete image;
248 FontManager::FontManager(Screen &screen, const std::string &theme_file)
250 PixmapStorage(screen),
251 current_color(GD_GDASH_WHITE),
252 screen(screen) {
253 load_theme(theme_file);
256 FontManager::~FontManager() {
257 clear();
260 struct FindRenderedFont {
261 guint32 uint;
262 FindRenderedFont(guint32 uint_): uint(uint_) {}
263 bool operator()(const RenderedFont *font) const {
264 return font->uint == uint;
268 RenderedFont *FontManager::narrow(const GdColor &c) {
269 // find font in list
270 container::iterator it = find_if(_narrow.begin(), _narrow.end(), FindRenderedFont(c.get_uint()));
271 if (it == _narrow.end()) {
272 // if not found, create it
273 RenderedFont *newfont = new RenderedFontNarrow(font, font_size, c, screen);
274 _narrow.push_front(newfont);
275 // if list became too long, remove one from the end
276 if (_narrow.size() > 8) {
277 delete _narrow.back();
278 _narrow.pop_back();
280 } else {
281 // put the font found to the beginning of the list
282 std::swap(*_narrow.begin(), *it);
284 return *_narrow.begin();
287 RenderedFont *FontManager::wide(const GdColor &c) {
288 // find font in list
289 container::iterator it = find_if(_wide.begin(), _wide.end(), FindRenderedFont(c.get_uint()));
290 if (it == _wide.end()) {
291 // if not found, create it
292 RenderedFont *newfont = new RenderedFontWide(font, font_size, c, screen);
293 _wide.push_front(newfont);
294 // if list became too long, remove one from the end
295 if (_wide.size() > 8) {
296 delete _wide.back();
297 _wide.pop_back();
299 } else {
300 // put the font found to the beginning of the list
301 std::swap(*_wide.begin(), *it);
303 return *_wide.begin();
306 /* function which draws characters on the screen. used internally. */
307 /* x=-1 -> center horizontally */
308 int FontManager::blittext_internal(int x, int y, char const *text, bool widefont) {
309 char *normalized = g_utf8_normalize(text, -1, G_NORMALIZE_ALL);
310 gunichar *ucs = g_utf8_to_ucs4(normalized, -1, NULL, NULL, NULL);
311 g_free(normalized);
313 RenderedFont const *font = widefont ? wide(current_color) : narrow(current_color);
314 int w = font->get_character(' ').get_width();
315 int h = get_line_height();
317 if (x == -1) {
318 gunichar c;
319 int len = 0;
320 for (int i = 0; (c = ucs[i]) != '\0'; ++i) {
321 if (c == GD_COLOR_SETCOLOR)
322 i += 1; /* do not count; skip next char */
323 else if (c >= 0x300 && c < 0x370)
324 ; /* do not count, diacritical. */
325 else
326 len++; /* count char */
328 x = screen.get_width() / 2 - (w * len) / 2;
331 int xc = x;
332 gunichar c;
333 for (int i = 0; (c = ucs[i]) != '\0'; ++i) {
334 if (c >= 0x300 && c < 0x370) {
335 // unicode diacritical mark block
336 switch (c) {
337 case 0x301:
338 screen.blit(font->get_character(GD_ACUTE_CHAR), xc - w, y);
339 break;
340 case 0x308:
341 screen.blit(font->get_character(GD_UMLAUT_CHAR), xc - w, y);
342 break;
343 case 0x30B:
344 screen.blit(font->get_character(GD_DOUBLE_ACUTE_CHAR), xc - w, y);
345 break;
347 continue;
349 /* color change "request", next character is a gdash color code */
350 if (c == GD_COLOR_SETCOLOR) {
351 i++;
352 c = ucs[i];
353 /* 64 was added in colors.hpp, now subtract it */
354 c -= 64;
355 current_color = GdColor::from_gdash_index(c);
356 font = widefont ? wide(current_color) : narrow(current_color);
358 continue;
361 /* some unicode hack - substitutions */
362 switch (c) {
363 case 0x00AB:
364 c = '<';
365 break; /* LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */
366 case 0x00BB:
367 c = '>';
368 break; /* RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */
369 case 0x2010:
370 c = '-';
371 break; /* hyphen */
372 case 0x2011:
373 c = '-';
374 break; /* non-breaking hyphen */
375 case 0x2012:
376 c = '-';
377 break; /* figure dash */
378 case 0x2013:
379 c = '-';
380 break; /* en dash */
381 case 0x2014:
382 c = '-';
383 break; /* em dash */
384 case 0x2015:
385 c = '-';
386 break; /* horizontal bar */
387 case 0x2018:
388 c = '\'';
389 break; /* left single quotation mark */
390 case 0x2019:
391 c = '\'';
392 break; /* right single quotation mark */
393 case 0x201A:
394 c = ',';
395 break; /* low single comma quotation mark */
396 case 0x201B:
397 c = '\'';
398 break; /* high-reversed-9 quotation mark */
399 case 0x201C:
400 c = '\"';
401 break; /* left double quotation mark */
402 case 0x201D:
403 c = '\"';
404 break; /* right double quotation mark */
405 case 0x201E:
406 c = '\"';
407 break; /* low double quotation mark */
408 case 0x201F:
409 c = '\"';
410 break; /* double reversed comma quotation mark */
411 case 0x2032:
412 c = '\'';
413 break; /* prime */
414 case 0x2033:
415 c = '\"';
416 break; /* double prime */
417 case 0x2034:
418 c = '\"';
419 break; /* triple prime */
420 case 0x2035:
421 c = '\'';
422 break; /* reversed prime */
423 case 0x2036:
424 c = '\"';
425 break; /* reversed double prime */
426 case 0x2037:
427 c = '\"';
428 break; /* reversed triple prime */
429 case 0x2039:
430 c = '<';
431 break; /* single left-pointing angle quotation mark */
432 case 0x203A:
433 c = '>';
434 break; /* single right-pointing angle quotation mark */
437 if (c == '\n') { /* if it is an enter */
438 y += h;
439 xc = x;
440 } else
441 if (c == '\t') { /* if it a tabulator */
442 do {
443 xc += w;
444 } while ((xc - x) / w % 8 != 0);
445 } else {
446 gunichar i;
448 if (c < RenderedFont::NUM_OF_CHARS)
449 i = c;
450 else
451 i = GD_UNKNOWN_CHAR;
453 screen.blit(font->get_character(i), xc, y);
454 xc += w;
458 g_free(ucs);
459 return xc;
462 void FontManager::clear() {
463 for (container::iterator it = _narrow.begin(); it != _narrow.end(); ++it)
464 delete *it;
465 _narrow.clear();
466 for (container::iterator it = _wide.begin(); it != _wide.end(); ++it)
467 delete *it;
468 _wide.clear();
471 void FontManager::release_pixmaps() {
472 clear();
475 int FontManager::get_font_height() const {
476 return font_size * screen.get_pixmap_scale();
479 int FontManager::get_line_height() const {
480 return (font_size * 1.4) * screen.get_pixmap_scale();
483 int FontManager::get_font_width_wide() const {
484 return font_size * 2 * screen.get_pixmap_scale();
488 int FontManager::get_font_width_narrow() const {
489 return font_size * screen.get_pixmap_scale();