20130313
[gdash.git] / src / gfx / fontmanager.cpp
blob120e49bd5bb955e2fa15383b00c461099e057858
1 /*
2 * Copyright (c) 2007-2013, Czirkos Zoltan http://code.google.com/p/gdash/
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include "config.h"
19 #include <vector>
20 #include <algorithm>
22 #include "gfx/pixbuf.hpp"
23 #include "gfx/pixmap.hpp"
24 #include "gfx/screen.hpp"
25 #include "gfx/pixbuffactory.hpp"
26 #include "cave/helper/colors.hpp"
27 #include "gfx/cellrenderer.hpp"
28 #include "gfx/fontmanager.hpp"
29 #include "misc/printf.hpp"
30 #include "misc/logger.hpp"
32 #include "c64_font.cpp"
35 /**
36 * @brief A RenderedFont stores glyphs rendered for a given font and a given color.
37 * This is an abstract base class, the narrow and wide font are derived from this.
39 class RenderedFont {
40 public:
41 /// Number of characters on the font map.
42 enum {
43 CHARS_X=32,
44 CHARS_Y=4,
45 NUM_OF_CHARS=CHARS_X*CHARS_Y
48 /// Constructor.
49 /// @param bitmap_ Raw font data.
50 /// @param color_ The color of drawing.
51 /// @param pixbuf_factory_ The pixbuf factory used to create glyphs.
52 RenderedFont(std::vector<unsigned char> const& bitmap_, unsigned font_size_, GdColor const& color_, PixbufFactory const& pixbuf_factory_);
54 /// Destructor.
55 virtual ~RenderedFont();
57 /// Return the pixmap for a given character; maybe after creating it.
58 /// @param j The ASCII (or GDash) code of the character
59 Pixmap const &get_character(int j) const;
61 /// GdColor::get_uint() code of color, for easy searching in a font manager.
62 guint32 uint;
64 protected:
65 /// Raw font data.
66 std::vector<unsigned char> const& bitmap;
68 /// Font size (pixbufs)
69 unsigned int font_size;
71 /// Pixbuf factory for drawing.
72 PixbufFactory const& pixbuf_factory;
74 /// RGBA format of color in pixbufs.
75 guint32 col;
77 /// RGBA format of transparent pixel in pixbufs.
78 guint32 transparent;
80 /// The rendered glyphs are stored in this array as pixmaps.
81 mutable Pixmap *_character[NUM_OF_CHARS];
83 RenderedFont(const RenderedFont&); // not implemented
84 RenderedFont& operator=(const RenderedFont&); // not implemented
86 private:
87 /// Render a single character. The narrow and wide fonts implement this.
88 virtual void render_character(int j) const=0;
91 class RenderedFontNarrow: public RenderedFont {
92 private:
93 virtual void render_character(int j) const;
95 public:
96 RenderedFontNarrow(std::vector<unsigned char> const& bitmap_, unsigned font_size_, GdColor const& color, PixbufFactory const& pixbuf_factory_)
97 : RenderedFont(bitmap_, font_size_, color, pixbuf_factory_) {}
100 class RenderedFontWide: public RenderedFont {
101 private:
102 virtual void render_character(int j) const;
104 public:
105 RenderedFontWide(std::vector<unsigned char> const& bitmap_, unsigned font_size_, GdColor const& color, PixbufFactory const& pixbuf_factory_)
106 : RenderedFont(bitmap_, font_size_, color, pixbuf_factory_) {}
110 RenderedFont::RenderedFont(std::vector<unsigned char> const& bitmap_, unsigned font_size_, GdColor const& color, PixbufFactory const& pixbuf_factory_)
111 : uint(color.get_uint()),
112 bitmap(bitmap_),
113 font_size(font_size_),
114 pixbuf_factory(pixbuf_factory_),
115 _character() {
116 g_assert(font_size_*font_size_*NUM_OF_CHARS==bitmap_.size());
117 col=Pixbuf::rgba_pixel_from_color(color, 0xff); /* opaque */
118 transparent=Pixbuf::rgba_pixel_from_color(GdColor::from_rgb(0, 0, 0), 0x00); /* color does not matter as totally transparent */
121 void RenderedFontNarrow::render_character(int j) const {
122 int y1=(j/CHARS_X)*font_size;
123 int x1=(j%CHARS_X)*font_size;
125 Pixbuf *image=pixbuf_factory.create(font_size, font_size);
126 image->lock();
127 for (unsigned y=0; y<font_size; y++) {
128 guint32 *p=image->get_row(y);
129 for (unsigned x=0; x<font_size; x++) {
130 /* the font array is encoded the same way as a c64-colored pixbuf. see c64_gfx_data...() */
131 if (bitmap[(y1+y)*(CHARS_X*font_size)+x1+x]!=1) /* 1 is black there!! */
132 p[x]=col; /* normal */
133 else
134 p[x]=transparent; /* normal */
137 image->unlock();
138 _character[j]=pixbuf_factory.create_pixmap_from_pixbuf(*image, true); // true=preserve alpha channel!
139 delete image;
142 void RenderedFontWide::render_character(int j) const {
143 int y1=(j/CHARS_X)*font_size;
144 int x1=(j%CHARS_X)*font_size;
146 Pixbuf *image=pixbuf_factory.create(font_size*2, font_size);
147 image->lock();
148 for (unsigned y=0; y<font_size; y++) {
149 guint32 *p=image->get_row(y);
150 for (unsigned x=0; x<font_size; x++) {
151 /* the font array is encoded the same way as a c64-colored pixbuf. see c64_gfx_data...() */
152 if (bitmap[(y1+y)*(CHARS_X*font_size)+x1+x]!=1) { /* 1 is black there!! */
153 p[0]=col; /* normal */
154 p[1]=col;
156 else {
157 p[0]=transparent; /* normal */
158 p[1]=transparent; /* normal */
160 p+=2;
163 image->unlock();
164 _character[j]=pixbuf_factory.create_pixmap_from_pixbuf(*image, true); // true=preserve alpha channel!
165 delete image;
168 RenderedFont::~RenderedFont() {
169 for (unsigned i=0; i<NUM_OF_CHARS; ++i)
170 delete _character[i];
173 Pixmap const &RenderedFont::get_character(int j) const {
174 g_assert(j<NUM_OF_CHARS);
175 if (_character[j]==0)
176 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;
189 if (!surface.has_alpha()) {
190 gd_critical("image should have an alpha channel!");
191 return false;
194 return true; /* passed checks */
197 bool FontManager::is_image_ok_for_theme(PixbufFactory &pixbuf_factory, const char *filename) {
198 try {
199 Pixbuf *image=pixbuf_factory.create_from_file(filename);
200 /* if the image is loaded */
201 SetLoggerContextForFunction scf(filename);
202 bool result=is_pixbuf_ok_for_theme(*image);
203 delete image;
204 return result;
205 } catch (...) {
206 return false;
210 bool FontManager::loadfont_image(Pixbuf const& image) {
211 if (!is_pixbuf_ok_for_theme(image))
212 return false;
214 clear();
215 font = Pixbuf::c64_gfx_data_from_pixbuf(image);
216 font_size = image.get_width()/RenderedFont::CHARS_X;
217 return true;
220 /* load theme from image file. */
221 /* return true if successful. */
222 bool FontManager::loadfont_file(const std::string& filename) {
223 /* load cell graphics */
224 /* load from file */
225 try {
226 Pixbuf *image=pixbuf_factory.create_from_file(filename.c_str());
227 bool result=loadfont_image(*image);
228 delete image;
229 if (!result)
230 gd_critical(CPrintf("%s: invalid font bitmap") % filename);
231 return result;
232 } catch (std::exception &e) {
233 gd_critical(CPrintf("%s: unable to load image (%s)") % filename % e.what());
234 return false;
238 /* load the theme from the given file. */
239 /* if successful, ok. */
240 /* if fails, or no theme specified, load the builtin */
241 void FontManager::load_theme(const std::string& theme_file) {
242 if (theme_file!="" && loadfont_file(theme_file)) {
243 /* loaded from png file */
244 } else {
245 Pixbuf *image=pixbuf_factory.create_from_inline(sizeof(c64_font), c64_font);
246 bool result=loadfont_image(*image);
247 g_assert(result==true); // to check the builting font
248 delete image;
252 FontManager::FontManager(const PixbufFactory& pixbuf_factory_, const std::string& theme_file)
254 pixbuf_factory(pixbuf_factory_) {
255 load_theme(theme_file);
258 FontManager::~FontManager() {
259 clear();
262 struct FindRenderedFont {
263 guint32 uint;
264 FindRenderedFont(guint32 uint_): uint(uint_) {}
265 bool operator()(const RenderedFont* font) const {
266 return font->uint==uint;
270 RenderedFont* FontManager::narrow(const GdColor &c) {
271 // find font in list
272 container::iterator it=find_if(_narrow.begin(), _narrow.end(), FindRenderedFont(c.get_uint()));
273 if (it==_narrow.end()) {
274 // if not found, create it
275 RenderedFont *newfont=new RenderedFontNarrow(font, font_size, c, pixbuf_factory);
276 _narrow.push_front(newfont);
277 // if list became too long, remove one from the end
278 if (_narrow.size()>16) {
279 delete _narrow.back();
280 _narrow.erase(--_narrow.end());
282 } else
283 // put the font found to the beginning of the list
284 std::swap(*_narrow.begin(), *it);
285 return *_narrow.begin();
288 RenderedFont* FontManager::wide(const GdColor &c) {
289 // find font in list
290 container::iterator it=find_if(_wide.begin(), _wide.end(), FindRenderedFont(c.get_uint()));
291 if (it==_wide.end()) {
292 // if not found, create it
293 RenderedFont *newfont=new RenderedFontWide(font, font_size, c, pixbuf_factory);
294 _wide.push_front(newfont);
295 // if list became too long, remove one from the end
296 if (_wide.size()>16) {
297 delete _wide.back();
298 _wide.erase(--_wide.end());
300 } else {
301 // put the font found to the beginning of the list
302 std::swap(*_wide.begin(), *it);
304 return *_wide.begin();
307 /* function which draws characters on the screen. used internally. */
308 /* x=-1 -> center horizontally */
309 int FontManager::blittext_internal(Screen &screen, int x, int y, char const *text, bool widefont) {
310 char *normalized = g_utf8_normalize(text, -1, G_NORMALIZE_ALL);
311 gunichar *ucs = g_utf8_to_ucs4(normalized, -1, NULL, NULL, NULL);
312 g_free(normalized);
314 RenderedFont const *font = widefont ? wide(current_color) : narrow(current_color);
315 int w = font->get_character(' ').get_width();
316 int h = get_line_height();
318 if (x==-1) {
319 gunichar c;
320 int len = 0;
321 for (int i = 0; (c = ucs[i]) != '\0'; ++i) {
322 if (c==GD_COLOR_SETCOLOR)
323 i += 1; /* do not count; skip next char */
324 else if (c>=0x300 && c<0x370)
325 ; /* do not count, diacritical. */
326 else
327 len++; /* count char */
329 x = screen.get_width()/2 - (w*len)/2;
332 int xc=x;
333 gunichar c;
334 for (int i = 0; (c = ucs[i]) != '\0'; ++i) {
335 if (c>=0x300 && c<0x370) {
336 // unicode diacritical mark block
337 switch (c) {
338 case 0x301:
339 screen.blit(font->get_character(GD_ACUTE_CHAR), xc-w, y);
340 break;
341 case 0x308:
342 screen.blit(font->get_character(GD_UMLAUT_CHAR), xc-w, y);
343 break;
344 case 0x30B:
345 screen.blit(font->get_character(GD_DOUBLE_ACUTE_CHAR), xc-w, y);
346 break;
348 continue;
350 /* color change "request", next character is a gdash color code */
351 if (c==GD_COLOR_SETCOLOR) {
352 i++;
353 c = ucs[i];
354 /* 64 was added in colors.hpp, now subtract it */
355 c-=64;
356 current_color = GdColor::from_gdash_index(c);
357 font = widefont ? wide(current_color) : narrow(current_color);
359 continue;
362 /* some unicode hack - substitutions */
363 switch (c) {
364 case 0x2018: c='\''; break; /* left single quotation mark */
365 case 0x2019: c='\''; break; /* right single quotation mark */
366 case 0x201A: c=','; break; /* low single comma quotation mark */
367 case 0x201B: c='\''; break; /* high-reversed-9 quotation mark */
368 case 0x201C: c='\"'; break; /* left double quotation mark */
369 case 0x201D: c='\"'; break; /* right double quotation mark */
370 case 0x201E: c='\"'; break; /* low double quotation mark */
371 case 0x201F: c='\"'; break; /* double reversed comma quotation mark */
372 case 0x2032: c='\''; break; /* prime */
373 case 0x2033: c='\"'; break; /* double prime */
374 case 0x2034: c='\"'; break; /* triple prime */
375 case 0x2035: c='\''; break; /* reversed prime */
376 case 0x2036: c='\"'; break; /* reversed double prime */
377 case 0x2037: c='\"'; break; /* reversed triple prime */
378 case 0x2039: c='<'; break; /* single left-pointing angle quotation mark */
379 case 0x203A: c='>'; break; /* single right-pointing angle quotation mark */
382 if (c=='\n') {
383 /* if it is an enter */
384 y+=h;
385 xc=x;
386 } else {
387 gunichar i;
389 if (c<RenderedFont::NUM_OF_CHARS)
390 i=c;
391 else
392 i=GD_UNKNOWN_CHAR;
394 screen.blit(font->get_character(i), xc, y);
395 xc+=w;
399 g_free(ucs);
400 return xc;
403 void FontManager::clear() {
404 for (container::iterator it=_narrow.begin(); it!=_narrow.end(); ++it)
405 delete *it;
406 _narrow.clear();
407 for (container::iterator it=_wide.begin(); it!=_wide.end(); ++it)
408 delete *it;
409 _wide.clear();
412 int FontManager::get_pixmap_scale() const {
413 return pixbuf_factory.get_pixmap_scale();
416 int FontManager::get_font_height() const {
417 return font_size*pixbuf_factory.get_pixmap_scale();
420 int FontManager::get_line_height() const {
421 return (font_size*1.4)*pixbuf_factory.get_pixmap_scale();
424 int FontManager::get_font_width_wide() const {
425 return font_size*2*pixbuf_factory.get_pixmap_scale();
429 int FontManager::get_font_width_narrow() const {
430 return font_size*pixbuf_factory.get_pixmap_scale();