* From now on, there is UTF-8 decoding in the Font class. It should support all chara...
[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.h"
27 #include "lisp/lisp.h"
28 #include "screen.h"
29 #include "font.h"
30 #include "drawing_context.h"
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   last_char = first_char + (chars->h / h) * 16;
48   if(last_char > 127 && last_char < 160) // we have left out some control chars at 128-159
49     last_char += 32;
50    
51   // Load shadow font.
52   if(shadowsize > 0) {
53     SDL_Surface* conv = SDL_DisplayFormatAlpha(chars->impl->get_sdl_surface());
54     int pixels = conv->w * conv->h;
55     SDL_LockSurface(conv);
56     for(int i = 0; i < pixels; ++i) {
57       Uint32 *p = (Uint32 *)conv->pixels + i;
58       *p = *p & conv->format->Amask;
59     }
60     SDL_UnlockSurface(conv);
61     SDL_SetAlpha(conv, SDL_SRCALPHA, 128);
62     shadow_chars = new Surface(conv, true);
63     SDL_FreeSurface(conv);
64   }
65 }
66
67 Font::~Font()
68 {
69   delete chars;
70   delete shadow_chars;
71 }
72
73 float
74 Font::get_text_width(const std::string& text) const
75 {
76   /** Let's calculate the size of the biggest paragraph */
77   std::string::size_type l, hl, ol;
78   hl = 0; l = 0;
79   while(true)
80     {
81     ol = l;
82     l = text.find("\n", l+1);
83     if(l == std::string::npos)
84       break;
85     if(hl < l-ol)
86       hl = l-ol;
87     }
88   if(hl == 0)
89     hl = text.size();
90
91   for (uint i = 0; i < text.size(); i++)
92     if ((unsigned char) text[i] > 0xC2 && (unsigned char) text[i] < 0xC6)
93       hl--;  // control characters are a WASTE.
94
95   return hl * w;
96 }
97
98 float
99 Font::get_text_height(const std::string& text) const
100 {
101   /** Let's calculate height of the text */
102   std::string::size_type l, hh;
103   hh = h; l = 0;
104   while(true)
105     {
106     l = text.find("\n", l+1);
107     if(l == std::string::npos)
108       break;
109     hh += h + 2;
110     }
111
112   return hh;
113 }
114
115 float
116 Font::get_height() const
117 {
118   return h;
119 }
120
121 void
122 Font::draw(const std::string& text, const Vector& pos_, FontAlignment alignment,
123     uint32_t drawing_effect, uint8_t alpha) const
124 {
125   /* Cut lines changes into seperate strings, needed to support center/right text
126      alignments with break lines.
127      Feel free to replace this hack with a more elegant solution
128   */
129   char temp[1024];
130   std::string::size_type l, i, y;
131   bool done = false;
132   i = y = 0;
133
134   while(!done) {
135     l = text.find("\n", i);
136     if(l == std::string::npos) {
137       l = text.size();
138       done = true;
139     }
140     
141     temp[text.copy(temp, l - i, i)] = '\0';
142     
143     // calculate X positions based on the alignment type
144     Vector pos = Vector(pos_);
145     if(alignment == CENTER_ALLIGN)
146       pos.x -= get_text_width(temp) / 2;
147     else if(alignment == RIGHT_ALLIGN)
148       pos.x -= get_text_width(temp);
149
150     draw_text(temp, pos + Vector(0,y), drawing_effect, alpha);
151
152     i = l+1;
153     y += h + 2;
154   }
155 }
156
157 void
158 Font::draw_text(const std::string& text, const Vector& pos, 
159     uint32_t drawing_effect, uint8_t alpha) const
160 {
161   if(shadowsize > 0)
162     draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize),
163                drawing_effect, alpha);
164
165   draw_chars(chars, text, pos, drawing_effect, alpha);
166 }
167
168 void
169 Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos,
170                  uint32_t drawing_effect, uint8_t alpha) const
171 {
172   SurfaceImpl* impl = pchars->impl;
173   int utf8suppl = 0;
174
175   Vector p = pos;
176   for(size_t i = 0; i < text.size(); ++i) {
177     int c = (unsigned char) text[i];
178     int d = 0;
179     if(c > 127 && c < 160) // correct for the 32 controlchars at 128-159
180       c -= 32;
181     if (c > 0xC2 && text.size() == i+1)  // string ends with control char
182     {
183       std::cerr << "String \"" << text << "\" is malformed.\n";
184       return;
185     }
186     else
187       d = (unsigned char) text[i+1];
188     
189     if (c == 0xC3 && d < 160) // first-byte identifier of U0080 ("C1 Control Characters and Latin-1 Supplement")
190     {                         // iso-8859-1 equiv character is capital A with tilde above, signified as "C3 83" in utf-8
191       utf8suppl = 64;
192       continue;
193     }
194     else if (c == 0xC3 && d >= 160) // U0080 pt. 2
195     {
196       utf8suppl = 32;
197       continue;
198     }
199     else if (c == 0xC4 && d < 160)
200     {
201       utf8suppl = 128;
202       continue;
203     }
204     else if (c == 0xC4 && d >= 160)
205     {
206       utf8suppl = 96;
207       continue;
208     }
209     else if (c == 0xC5 && d < 160) // first-byte identifier of U0100 ("Latin Extended-A")
210     {                              // iso-8859-1 equiv character is capital A with ring above, signified as "C3 85" in utf-8
211       utf8suppl = 192;
212       continue;
213     }
214     else if (c == 0xC5 && d >= 160) // first-byte identifier of U0100 ("Latin Extended-A")
215     {                               // iso-8859-1 equiv character is capital A with ring above, signified as "C3 85" in utf-8
216       utf8suppl = 160;
217       continue;
218     }
219     // insert more clauses here once somebody will need them
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 == ' ' || c < first_char || c > last_char) {
228       p.x += w;
229       continue;
230     }
231
232     c += utf8suppl;
233     utf8suppl = 0;
234     
235     int index = c - first_char;
236     int source_x = (index % 16) * w;
237     int source_y = (index / 16) * h;
238
239     impl->draw_part(source_x, source_y, p.x, p.y, w, h, alpha, drawing_effect);
240     p.x += w;
241   }
242 }