260c6f75322cd3e7fd674b28988cd0f460f47c89
[supertux.git] / lib / video / font.cpp
1 //  $Id$
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
21 #include <cstdlib>
22 #include <cstring>
23
24 #include "../app/globals.h"
25 #include "../video/screen.h"
26 #include "../video/font.h"
27 #include "../video/drawing_context.h"
28 #include "../utils/lispreader.h"
29
30 using namespace SuperTux;
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) // 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;
78   hl = 0; l = -1;
79   while(true)
80     {
81     l = text.find("\n", l+1);
82     if(l == std::string::npos)
83       break;
84     if(hl < l)
85       hl = l;
86     }
87   if(hl == 0)
88     hl = text.size();
89
90   return hl * w;
91 }
92
93 float
94 Font::get_text_height(const std::string& text) const
95 {
96   /** Let's calculate height of the text */
97   std::string::size_type l, hh;
98   hh = h; l = -1;
99   while(true)
100     {
101     l = text.find("\n", l+1);
102     if(l == (int)std::string::npos)
103       break;
104     hh += h + 2;
105     }
106
107   return hh;
108 }
109
110 float
111 Font::get_height() const
112 {
113   return h;
114 }
115
116 void
117 Font::draw(const std::string& text, const Vector& pos_, int allignment, Uint32 drawing_effect, int alpha)
118 {
119   // calculate X positions based on the allignment type
120   Vector pos = Vector(pos_);
121   if(allignment == CENTER_ALLIGN)
122     pos.x -= get_text_width(text) / 2;
123   else if(allignment == RIGHT_ALLIGN)
124     pos.x -= get_text_width(text);
125
126   /* Cut lines changes into seperate strings, needed to support center/right text
127      allignments with break lines.
128      Feel free to replace this hack with a more elegant solution
129   */
130   char temp[1024];
131   std::string::size_type l;
132   unsigned int i, y;
133   i = y = 0;
134
135   while(true)
136     {
137     l = text.find("\n", i);
138     if(l == std::string::npos)
139       {
140       temp[text.copy(temp, text.size() - i, i)] = '\0';
141       draw_text(temp, pos + Vector(0,y), drawing_effect, alpha);
142       break;
143       }
144     temp[text.copy(temp, l - i, i)] = '\0';
145     draw_text(temp, pos + Vector(0,y), drawing_effect, alpha);
146
147     i = l+1;
148     y += h + 2;
149     }
150 }
151
152 void
153 Font::draw_text(const std::string& text, const Vector& pos, Uint32 drawing_effect, int alpha)
154 {
155   if(shadowsize > 0)
156     draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize),
157                drawing_effect, alpha);
158
159   draw_chars(chars, text, pos, drawing_effect, alpha);
160 }
161
162 void
163 Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos,
164                  Uint32 drawing_effect, int alpha)
165 {
166   SurfaceImpl* impl = pchars->impl;
167
168   Vector p = pos;
169   for(size_t i = 0; i < text.size(); ++i)
170   {
171     int c = (unsigned char) text[i];
172     if(c > 127) // correct for the 32 controlchars at 128-159
173       c -= 32;
174     // a non-printable character?
175     if(c == '\n') {
176       p.x = pos.x;
177       p.y += h + 2;
178       continue;
179     }
180     if(c == ' ' || c < first_char || c > last_char) {
181       p.x += w;
182       continue;
183     }
184     
185     int index = c - first_char;
186     int source_x = (index % 16) * w;
187     int source_y = (index / 16) * h;
188
189     impl->draw_part(source_x, source_y, p.x, p.y, w, h, alpha, drawing_effect);
190     p.x += w;
191   }
192 }
193
194 /* --- SCROLL TEXT FUNCTION --- */
195
196 #define MAX_VEL     10
197 #define SPEED_INC   0.01
198 #define SCROLL      60
199 #define ITEMS_SPACE 4
200
201 void SuperTux::display_text_file(const std::string& file, float scroll_speed, Font* heading_font, Font* normal_font, Font* small_font, Font* reference_font )
202 {
203   std::string text;
204   std::vector<std::string> names;
205
206   LispReader* reader = LispReader::load(datadir + "/" + file, "supertux-text");
207
208   if(!reader)
209     {
210     std::cerr << "Error: Could not open text. Ignoring...\n";
211     return;
212     }
213
214   reader->read_string("text", text, true);
215   std::string background_file;
216   reader->read_string("background", background_file, true);
217   delete reader;
218
219   // Split text string lines into a vector
220   names.clear();
221   unsigned int i, l;
222   i = 0;
223   while(true)
224     {
225     l = text.find("\n", i);
226
227     if(l == std::string::npos)
228       {
229       char temp[1024];
230       temp[text.copy(temp, text.size() - i, i)] = '\0';
231       names.push_back(temp);
232       break;
233       }
234
235     char temp[1024];
236     temp[text.copy(temp, l-i, i)] = '\0';
237     names.push_back(temp);
238
239     i = l+1;
240     }
241
242   // load background image
243   Surface* background = new Surface(datadir + "/images/background/" + background_file, false);
244
245   int done = 0;
246   float scroll = 0;
247   float speed = scroll_speed / 50;
248
249   DrawingContext context;
250   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
251
252   Uint32 lastticks = SDL_GetTicks();
253   while(!done)
254     {
255       /* in case of input, exit */
256       SDL_Event event;
257       while(SDL_PollEvent(&event))
258         switch(event.type)
259           {
260           case SDL_KEYDOWN:
261             switch(event.key.keysym.sym)
262               {
263               case SDLK_UP:
264                 speed -= SPEED_INC;
265                 break;
266               case SDLK_DOWN:
267                 speed += SPEED_INC;
268                 break;
269               case SDLK_SPACE:
270               case SDLK_RETURN:
271                 if(speed >= 0)
272                   scroll += SCROLL;
273                 break;
274               case SDLK_ESCAPE:
275                 done = 1;
276                 break;
277               default:
278                 break;
279               }
280             break;
281           case SDL_QUIT:
282             done = 1;
283             break;
284           default:
285             break;
286           }
287
288       if(speed > MAX_VEL)
289         speed = MAX_VEL;
290       else if(speed < -MAX_VEL)
291         speed = -MAX_VEL;
292
293       /* draw the credits */
294       context.draw_surface(background, Vector(0,0), 0);
295
296       float y = 0;
297       for(size_t i = 0; i < names.size(); i++) {
298         if(names[i].size() == 0) {
299           y += normal_font->get_height() + ITEMS_SPACE;
300           continue;
301         }
302
303         Font* font = 0;
304         switch(names[i][0])
305         {
306           case ' ': font = small_font; break;
307           case '\t': font = normal_font; break;
308           case '-': font = heading_font; break;
309           case '*': font = reference_font; break;
310           default: font = reference_font; break;
311         }
312
313         context.draw_text(font,
314             names[i].substr(1, names[i].size()-1),
315             Vector(screen->w/2, screen->h + y - scroll), CENTER_ALLIGN, LAYER_FOREGROUND1);
316         y += font->get_height() + ITEMS_SPACE;
317       }
318
319       context.do_drawing();
320
321       if(screen->h+y-scroll < 0 && 20+screen->h+y-scroll < 0)
322         done = 1;
323
324       Uint32 ticks = SDL_GetTicks();
325       scroll += speed * (ticks - lastticks);
326       lastticks = ticks;
327       if(scroll < 0)
328         scroll = 0;
329
330       SDL_Delay(10);
331     }
332
333   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
334   delete background;
335 }
336