renamed all .h to .hpp
[supertux.git] / src / video / font.cpp
1 //  $Id: font.cpp 2298 2005-03-30 12:01:02Z matzebraun $
2 // 
3 //  SuperTux
4 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
5 //
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.
10 //
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.
15 // 
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
19 //  02111-1307, USA.
20 #include <config.h>
21
22 #include <cstdlib>
23 #include <cstring>
24 #include <stdexcept>
25
26 #include "lisp/parser.hpp"
27 #include "lisp/lisp.hpp"
28 #include "screen.hpp"
29 #include "font.hpp"
30 #include "drawing_context.hpp"
31
32 Font::Font(const std::string& file, FontType ntype, int nw, int nh,
33         int nshadowsize)
34     : chars(0), shadow_chars(0), type(ntype), w(nw), h(nh),
35       shadowsize(nshadowsize)
36 {
37   chars = new Surface(file, true);
38  
39   switch(type) {
40     case TEXT:
41       first_char = 32;
42       break;
43     case NUM:
44       first_char = 48;
45       break;
46   }
47   char_count = (chars->h / h) * 16;
48    
49   // Load shadow font.
50   if(shadowsize > 0) {
51     SDL_Surface* conv = SDL_DisplayFormatAlpha(chars->impl->get_sdl_surface());
52     int pixels = conv->w * conv->h;
53     SDL_LockSurface(conv);
54     for(int i = 0; i < pixels; ++i) {
55       Uint32 *p = (Uint32 *)conv->pixels + i;
56       *p = *p & conv->format->Amask;
57     }
58     SDL_UnlockSurface(conv);
59     SDL_SetAlpha(conv, SDL_SRCALPHA, 128);
60     shadow_chars = new Surface(conv, true);
61     SDL_FreeSurface(conv);
62   }
63 }
64
65 Font::~Font()
66 {
67   delete chars;
68   delete shadow_chars;
69 }
70
71 float
72 Font::get_text_width(const std::string& text) const
73 {
74   /** Let's calculate the size of the biggest paragraph */
75   std::string::size_type l, hl, ol;
76   hl = 0; l = 0;
77   while(true)
78     {
79     ol = l;
80     l = text.find("\n", l+1);
81     if(l == std::string::npos)
82       break;
83     if(hl < l-ol)
84       hl = l-ol;
85     }
86   if(hl == 0)
87     hl = text.size();
88
89   for (uint i = 0; i < text.size(); i++)
90     if ((unsigned char) text[i] > 0xC2 && (unsigned char) text[i] < 0xC6)
91       hl--;  // control characters are a WASTE.
92
93   return hl * w;
94 }
95
96 float
97 Font::get_text_height(const std::string& text) const
98 {
99   /** Let's calculate height of the text */
100   std::string::size_type l, hh;
101   hh = h; l = 0;
102   while(true)
103     {
104     l = text.find("\n", l+1);
105     if(l == std::string::npos)
106       break;
107     hh += h + 2;
108     }
109
110   return hh;
111 }
112
113 float
114 Font::get_height() const
115 {
116   return h;
117 }
118
119 void
120 Font::draw(const std::string& text, const Vector& pos_, FontAlignment alignment,
121     uint32_t drawing_effect, uint8_t alpha) const
122 {
123   /* Cut lines changes into seperate strings, needed to support center/right text
124      alignments with break lines.
125      Feel free to replace this hack with a more elegant solution
126   */
127   char temp[1024];
128   std::string::size_type l, i, y;
129   bool done = false;
130   i = y = 0;
131
132   while(!done) {
133     l = text.find("\n", i);
134     if(l == std::string::npos) {
135       l = text.size();
136       done = true;
137     }
138     
139     temp[text.copy(temp, l - i, i)] = '\0';
140     
141     // calculate X positions based on the alignment type
142     Vector pos = Vector(pos_);
143     if(alignment == CENTER_ALLIGN)
144       pos.x -= get_text_width(temp) / 2;
145     else if(alignment == RIGHT_ALLIGN)
146       pos.x -= get_text_width(temp);
147
148     draw_text(temp, pos + Vector(0,y), drawing_effect, alpha);
149
150     i = l+1;
151     y += h + 2;
152   }
153 }
154
155 void
156 Font::draw_text(const std::string& text, const Vector& pos, 
157     uint32_t drawing_effect, uint8_t alpha) const
158 {
159   if(shadowsize > 0)
160     draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize),
161                drawing_effect, alpha);
162
163   draw_chars(chars, text, pos, drawing_effect, alpha);
164 }
165
166 /** decoding of a byte stream to a single unicode character.
167  * This should be correct for well formed utf-8 sequences but doesn't check for
168  * all forms of illegal sequences.
169  * (see unicode standard section 3.10 table 3-5 and 3-6 for details)
170  */
171 uint32_t decode_utf8(const std::string& text, size_t& p)
172 {
173   // 1 byte sequence
174   uint32_t c = (unsigned char) text[p++];
175   if(c <= 0x7F) {
176     return c;
177   }
178   
179   // 2 byte sequence
180   if(p >= text.size())
181     throw std::runtime_error("Malformed utf-8 sequence");
182   uint32_t c2 = (unsigned char) text[p++];
183   if(c <= 0xDF) {
184     if(c < 0xC2)
185       throw std::runtime_error("Malformed utf-8 sequence");
186     return (c & 0x1F) << 6 | (c2 & 0x3F);
187   }
188   
189   // 3 byte sequence
190   if(p >= text.size())
191     throw std::runtime_error("Malformed utf-8 sequence");
192   uint32_t c3 = (unsigned char) text[p++];
193   if(c <= 0xEF) {
194     return (c & 0x0F) << 12 | (c2 & 0x3F) << 6 | (c3 & 0x3F);
195   }
196   
197   // 4 byte sequence
198   if(p >= text.size())
199     throw std::runtime_error("Malformed utf-8 sequence");
200   uint32_t c4 = (unsigned char) text[p++];
201   if(c <= 0xF4) {
202     return (c & 0x07) << 18 | (c2 & 0x3F) << 12 | (c3 & 0x3F) << 6 
203       | (c4 & 0x3F);
204   }
205
206   throw std::runtime_error("Malformed utf-8 sequence");
207 }
208
209 void
210 Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos,
211                  uint32_t drawing_effect, uint8_t alpha) const
212 {
213   SurfaceImpl* impl = pchars->impl;
214
215   Vector p = pos;
216   size_t i = 0;
217   while(i < text.size()) {
218     uint32_t c = decode_utf8(text, i);
219     ssize_t font_index;
220
221     // a non-printable character?
222     if(c == '\n') {                                      
223       p.x = pos.x;
224       p.y += h + 2;
225       continue;
226     }
227     if(c == ' ') {
228       p.x += w;
229       continue;
230     }
231
232     font_index = c - first_char;
233     // we don't have the control chars 0x80-0xa0 in the font
234     if(c >= 0x80) {
235       font_index -= 32;
236       if(c <= 0xa0) {
237 #ifdef DEBUG
238         std::cout << "Unsupported utf-8 character '" << c << "' found\n";
239 #endif
240         font_index = 0;
241       }
242     }
243         
244     if(font_index < 0 || font_index >= (ssize_t) char_count) {
245 #ifdef DEBUG
246       std::cout << "Unsupported utf-8 character found\n";
247 #endif
248       font_index = 0;
249     }                   
250
251     int source_x = (font_index % 16) * w;
252     int source_y = (font_index / 16) * h;
253     impl->draw_part(source_x, source_y, p.x, p.y, w, h, alpha, drawing_effect);
254     p.x += w;
255   }
256 }