4 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include <SDL_image.h>
27 #include "physfs/physfs_sdl.hpp"
29 #include "lisp/parser.hpp"
30 #include "lisp/lisp.hpp"
33 #include "drawing_context.hpp"
37 bool has_multibyte_mark(unsigned char c);
38 uint32_t decode_utf8(const std::string& text, size_t& p);
40 bool vline_empty(SDL_Surface* surface, int x, int start_y, int end_y, Uint8 threshold)
42 Uint8* pixels = (Uint8*)surface->pixels;
44 for(int y = start_y; y < end_y; ++y)
46 const Uint8& p = pixels[surface->pitch*y + x*surface->format->BytesPerPixel + 3];
56 Font::Font(const std::string& file, const std::string& shadowfile,
57 int w, int h, int shadowsize)
58 : glyph_surface(0), shadow_chars(0),
59 char_width(w), char_height(h),
60 shadowsize(shadowsize)
62 glyph_surface = new Surface(file);
63 shadow_chars = new Surface(shadowfile);
66 char_count = ((int) glyph_surface->get_height() / char_height) * 16;
68 for(uint32_t i = 0; i < char_count; ++i)
70 float x = (i % 16) * char_width;
71 float y = (i / 16) * char_height;
72 glyphs.push_back(Rect(x, y,
73 x + char_width, y + char_height));
77 Font::Font(const std::string& filename, int char_width_, int char_height_)
78 : glyph_surface(0), shadow_chars(0),
79 char_width(char_width_), char_height(char_height_),
82 glyph_surface = new Surface(filename);
85 char_count = ((int) glyph_surface->get_height() / char_height) * 16;
87 // Load the surface into RAM and scan the pixel data for characters
88 SDL_Surface* surface = IMG_Load_RW(get_physfs_SDLRWops(filename), 1);
90 std::ostringstream msg;
91 msg << "Couldn't load image '" << filename << "' :" << SDL_GetError();
92 throw std::runtime_error(msg.str());
95 SDL_LockSurface(surface);
97 for(uint32_t i = 0; i < char_count; ++i)
99 int x = (i % 16) * char_width;
100 int y = (i / 16) * char_height;
103 while (left < x + char_width &&
104 vline_empty(surface, left, y, y + char_height, 0))
107 int right = x + char_width - 1;
108 while (right > left &&
109 vline_empty(surface, right, y, y + char_height, 0))
113 glyphs.push_back(Rect(left, y,
114 right+1, y + char_height));
115 else // glyph is completly transparent
116 glyphs.push_back(Rect(x, y,
117 x + char_width, y + char_height));
121 SDL_UnlockSurface(surface);
123 SDL_FreeSurface(surface);
128 delete glyph_surface;
133 Font::get_text_width(const std::string& text) const
135 float curr_width = 0;
136 float last_width = 0;
138 // FIXME: add UTF8 decode here
139 for(std::string::size_type i = 0; i < text.size(); ++i)
141 uint32_t chr = text[i];
144 last_width = std::max(last_width, curr_width);
149 curr_width += glyphs[static_cast<unsigned char>(text[i])].get_width() + 1;
153 return std::max(curr_width, last_width);
157 Font::get_text_height(const std::string& text) const
159 std::string::size_type text_height = char_height;
161 for(std::string::size_type i = 0; i < text.size(); ++i)
164 text_height += char_height + 2;
171 Font::get_height() const
177 Font::wrap_to_chars(const std::string& s, int line_length, std::string* overflow)
179 // if text is already smaller, return full text
180 if ((int)s.length() <= line_length) {
181 if (overflow) *overflow = "";
185 // if we can find a whitespace character to break at, return text up to this character
187 while ((i > 0) && (s[i] != ' ')) i--;
189 if (overflow) *overflow = s.substr(i+1);
190 return s.substr(0, i);
193 // FIXME: wrap at line_length, taking care of multibyte characters
194 if (overflow) *overflow = "";
199 Font::draw(const std::string& text, const Vector& pos_, FontAlignment alignment,
200 DrawingEffect drawing_effect, float alpha) const
202 /* Cut lines changes into seperate strings, needed to support center/right text
203 alignments with break lines.
204 Feel free to replace this hack with a more elegant solution
207 std::string::size_type l, i, y;
212 l = text.find("\n", i);
213 if(l == std::string::npos) {
218 if(l > sizeof(temp)-1)
221 temp[text.copy(temp, l - i, i)] = '\0';
223 // calculate X positions based on the alignment type
224 Vector pos = Vector(pos_);
225 if(alignment == CENTER_ALLIGN)
226 pos.x -= get_text_width(temp) / 2;
227 else if(alignment == RIGHT_ALLIGN)
228 pos.x -= get_text_width(temp);
230 draw_text(temp, pos + Vector(0,y), drawing_effect, alpha);
233 y += char_height + 2;
238 Font::draw_text(const std::string& text, const Vector& pos,
239 DrawingEffect drawing_effect, float alpha) const
242 draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize),
243 drawing_effect, alpha);
245 draw_chars(glyph_surface, text, pos, drawing_effect, alpha);
249 Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos,
250 DrawingEffect drawing_effect, float alpha) const
254 while(i < text.size()) {
257 c = decode_utf8(text, i); // FIXME: this seems wrong, since when incrementing i by
259 catch (std::runtime_error) {
260 log_debug << "Malformed utf-8 sequence beginning with " << *((uint32_t*)(text.c_str() + i)) << " found " << std::endl;
265 int font_index = c - first_char;
267 // a non-printable character?
270 p.y += char_height + 2;
274 p.x += glyphs[font_index].get_width();
278 // we don't have the control chars 0x80-0xa0 in the font
282 log_debug << "Unsupported utf-8 character '" << c << "' found" << std::endl;
287 if(font_index < 0 || font_index >= (int) char_count) {
288 log_debug << "Unsupported utf-8 character found" << std::endl;
292 const Rect& glyph = glyphs[font_index];
293 pchars->draw_part(glyph.get_left(), glyph.get_top(),
295 glyph.get_width(), glyph.get_height(),
296 alpha, drawing_effect);
297 p.x += glyphs[font_index].get_width();
305 * returns true if this byte matches a bitmask of 10xx.xxxx, i.e. it is the 2nd, 3rd or 4th byte of a multibyte utf8 string
307 bool has_multibyte_mark(unsigned char c) {
308 return ((c & 0300) == 0200);
312 * gets unicode character at byte position @a p of UTF-8 encoded @a text, then advances @a p to the next character.
313 * @throws std::runtime_error if decoding fails.
314 * See unicode standard section 3.10 table 3-5 and 3-6 for details.
316 uint32_t decode_utf8(const std::string& text, size_t& p)
318 uint32_t c1 = (unsigned char) text[p+0];
320 if (has_multibyte_mark(c1)) std::runtime_error("Malformed utf-8 sequence");
322 if ((c1 & 0200) == 0000) {
323 // 0xxx.xxxx: 1 byte sequence
327 else if ((c1 & 0340) == 0300) {
328 // 110x.xxxx: 2 byte sequence
329 if(p+1 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
330 uint32_t c2 = (unsigned char) text[p+1];
331 if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
333 return (c1 & 0037) << 6 | (c2 & 0077);
335 else if ((c1 & 0360) == 0340) {
336 // 1110.xxxx: 3 byte sequence
337 if(p+2 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
338 uint32_t c2 = (unsigned char) text[p+1];
339 uint32_t c3 = (unsigned char) text[p+2];
340 if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
341 if (!has_multibyte_mark(c3)) throw std::runtime_error("Malformed utf-8 sequence");
343 return (c1 & 0017) << 12 | (c2 & 0077) << 6 | (c3 & 0077);
345 else if ((c1 & 0370) == 0360) {
346 // 1111.0xxx: 4 byte sequence
347 if(p+3 >= text.size()) throw std::range_error("Malformed utf-8 sequence");
348 uint32_t c2 = (unsigned char) text[p+1];
349 uint32_t c3 = (unsigned char) text[p+2];
350 uint32_t c4 = (unsigned char) text[p+4];
351 if (!has_multibyte_mark(c2)) throw std::runtime_error("Malformed utf-8 sequence");
352 if (!has_multibyte_mark(c3)) throw std::runtime_error("Malformed utf-8 sequence");
353 if (!has_multibyte_mark(c4)) throw std::runtime_error("Malformed utf-8 sequence");
355 return (c1 & 0007) << 18 | (c2 & 0077) << 12 | (c3 & 0077) << 6 | (c4 & 0077);
357 throw std::runtime_error("Malformed utf-8 sequence");