78cfd39abacbed9c2f702ca95b3e25e20da3d2b2
[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 <config.h>
22
23 #include <cstdlib>
24 #include <cstring>
25
26 #include "app/globals.h"
27 #include "screen.h"
28 #include "font.h"
29 #include "drawing_context.h"
30 #include "utils/lispreader.h"
31
32 using namespace SuperTux;
33
34 Font::Font(const std::string& file, FontType ntype, int nw, int nh,
35         int nshadowsize)
36     : chars(0), shadow_chars(0), type(ntype), w(nw), h(nh),
37       shadowsize(nshadowsize)
38 {
39   chars = new Surface(file, true);
40  
41   switch(type) {
42     case TEXT:
43       first_char = 32;
44       break;
45     case NUM:
46       first_char = 48;
47       break;
48   }
49   last_char = first_char + (chars->h / h) * 16;
50   if(last_char > 127) // we have left out some control chars at 128-159
51     last_char += 32;
52    
53   // Load shadow font.
54   if(shadowsize > 0) {
55     SDL_Surface* conv = SDL_DisplayFormatAlpha(chars->impl->get_sdl_surface());
56     int pixels = conv->w * conv->h;
57     SDL_LockSurface(conv);
58     for(int i = 0; i < pixels; ++i) {
59       Uint32 *p = (Uint32 *)conv->pixels + i;
60       *p = *p & conv->format->Amask;
61     }
62     SDL_UnlockSurface(conv);
63     SDL_SetAlpha(conv, SDL_SRCALPHA, 128);
64     shadow_chars = new Surface(conv, true);
65     SDL_FreeSurface(conv);
66   }
67 }
68
69 Font::~Font()
70 {
71   delete chars;
72   delete shadow_chars;
73 }
74
75 float
76 Font::get_text_width(const std::string& text) const
77 {
78   /** Let's calculate the size of the biggest paragraph */
79   std::string::size_type l, hl, ol;
80   hl = 0; l = 0;
81   while(true)
82     {
83     ol = l;
84     l = text.find("\n", l+1);
85     if(l == std::string::npos)
86       break;
87     if(hl < l-ol)
88       hl = l-ol;
89     }
90   if(hl == 0)
91     hl = text.size();
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_, int allignment, Uint32 drawing_effect, int alpha)
121 {
122   /* Cut lines changes into seperate strings, needed to support center/right text
123      allignments with break lines.
124      Feel free to replace this hack with a more elegant solution
125   */
126   char temp[1024];
127   std::string::size_type l, i, y;
128   bool done = false;
129   i = y = 0;
130
131   while(!done)
132     {
133     l = text.find("\n", i);
134     if(l == std::string::npos)
135       {
136       l = text.size();
137       done = true;
138       }
139
140     temp[text.copy(temp, l - i, i)] = '\0';
141
142     // calculate X positions based on the allignment type
143     Vector pos = Vector(pos_);
144     if(allignment == CENTER_ALLIGN)
145       pos.x -= get_text_width(temp) / 2;
146     else if(allignment == RIGHT_ALLIGN)
147       pos.x -= get_text_width(temp);
148
149     draw_text(temp, pos + Vector(0,y), drawing_effect, alpha);
150
151     i = l+1;
152     y += h + 2;
153     }
154 }
155
156 void
157 Font::draw_text(const std::string& text, const Vector& pos, Uint32 drawing_effect, int alpha)
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 void
167 Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos,
168                  Uint32 drawing_effect, int alpha)
169 {
170   SurfaceImpl* impl = pchars->impl;
171
172   Vector p = pos;
173   for(size_t i = 0; i < text.size(); ++i)
174   {
175     int c = (unsigned char) text[i];
176     if(c > 127) // correct for the 32 controlchars at 128-159
177       c -= 32;
178     // a non-printable character?
179     if(c == '\n') {
180       p.x = pos.x;
181       p.y += h + 2;
182       continue;
183     }
184     if(c == ' ' || c < first_char || c > last_char) {
185       p.x += w;
186       continue;
187     }
188     
189     int index = c - first_char;
190     int source_x = (index % 16) * w;
191     int source_y = (index / 16) * h;
192
193     impl->draw_part(source_x, source_y, p.x, p.y, w, h, alpha, drawing_effect);
194     p.x += w;
195   }
196 }
197
198 /* --- SCROLL TEXT FUNCTION --- */
199
200 #define MAX_VEL     10
201 #define SPEED_INC   0.01
202 #define SCROLL      60
203 #define ITEMS_SPACE 4
204
205 void SuperTux::display_text_file(const std::string& file, float scroll_speed, Font* heading_font, Font* normal_font, Font* small_font, Font* reference_font )
206 {
207   std::string text;
208   std::vector<std::string> names;
209
210   LispReader* reader = LispReader::load(datadir + "/" + file, "supertux-text");
211
212   if(!reader)
213     {
214     std::cerr << "Error: Could not open text. Ignoring...\n";
215     return;
216     }
217
218   reader->read_string("text", text, true);
219   std::string background_file;
220   reader->read_string("background", background_file, true);
221   delete reader;
222
223   // Split text string lines into a vector
224   names.clear();
225   std::string::size_type i, l;
226   i = 0;
227   while(true)
228     {
229     l = text.find("\n", i);
230
231     if(l == std::string::npos)
232       {
233       char temp[1024];
234       temp[text.copy(temp, text.size() - i, i)] = '\0';
235       names.push_back(temp);
236       break;
237       }
238
239     char temp[1024];
240     temp[text.copy(temp, l-i, i)] = '\0';
241     names.push_back(temp);
242
243     i = l+1;
244     }
245
246   // load background image
247   Surface* background = new Surface(datadir + "/images/background/" + background_file, false);
248
249   int done = 0;
250   float scroll = 0;
251   float speed = scroll_speed / 50;
252
253   DrawingContext context;
254   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
255
256   Uint32 lastticks = SDL_GetTicks();
257   while(!done)
258     {
259       /* in case of input, exit */
260       SDL_Event event;
261       while(SDL_PollEvent(&event))
262         switch(event.type)
263           {
264           case SDL_KEYDOWN:
265             switch(event.key.keysym.sym)
266               {
267               case SDLK_UP:
268                 speed -= SPEED_INC;
269                 break;
270               case SDLK_DOWN:
271                 speed += SPEED_INC;
272                 break;
273               case SDLK_SPACE:
274               case SDLK_RETURN:
275                 if(speed >= 0)
276                   scroll += SCROLL;
277                 break;
278               case SDLK_ESCAPE:
279                 done = 1;
280                 break;
281               default:
282                 break;
283               }
284             break;
285           case SDL_QUIT:
286             done = 1;
287             break;
288           default:
289             break;
290           }
291
292       if(speed > MAX_VEL)
293         speed = MAX_VEL;
294       else if(speed < -MAX_VEL)
295         speed = -MAX_VEL;
296
297       /* draw the credits */
298       context.draw_surface(background, Vector(0,0), 0);
299
300       float y = 0;
301       for(size_t i = 0; i < names.size(); i++) {
302         if(names[i].size() == 0) {
303           y += normal_font->get_height() + ITEMS_SPACE;
304           continue;
305         }
306
307         Font* font = 0;
308         switch(names[i][0])
309         {
310           case ' ': font = small_font; break;
311           case '\t': font = normal_font; break;
312           case '-': font = heading_font; break;
313           case '*': font = reference_font; break;
314           default: font = reference_font; break;
315         }
316
317         context.draw_text(font,
318             names[i].substr(1, names[i].size()-1),
319             Vector(screen->w/2, screen->h + y - scroll), CENTER_ALLIGN, LAYER_FOREGROUND1);
320         y += font->get_height() + ITEMS_SPACE;
321       }
322
323       context.do_drawing();
324
325       if(screen->h+y-scroll < 0 && 20+screen->h+y-scroll < 0)
326         done = 1;
327
328       Uint32 ticks = SDL_GetTicks();
329       scroll += speed * (ticks - lastticks);
330       lastticks = ticks;
331       if(scroll < 0)
332         scroll = 0;
333
334       SDL_Delay(10);
335     }
336
337   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
338   delete background;
339 }
340