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