-Some cleanups in text scrolling code
[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 #include <stdexcept>
26
27 #include "app/globals.h"
28 #include "lisp/parser.h"
29 #include "lisp/lisp.h"
30 #include "screen.h"
31 #include "font.h"
32 #include "drawing_context.h"
33
34 using namespace SuperTux;
35
36 Font::Font(const std::string& file, FontType ntype, int nw, int nh,
37         int nshadowsize)
38     : chars(0), shadow_chars(0), type(ntype), w(nw), h(nh),
39       shadowsize(nshadowsize)
40 {
41   chars = new Surface(file, true);
42  
43   switch(type) {
44     case TEXT:
45       first_char = 32;
46       break;
47     case NUM:
48       first_char = 48;
49       break;
50   }
51   last_char = first_char + (chars->h / h) * 16;
52   if(last_char > 127) // we have left out some control chars at 128-159
53     last_char += 32;
54    
55   // Load shadow font.
56   if(shadowsize > 0) {
57     SDL_Surface* conv = SDL_DisplayFormatAlpha(chars->impl->get_sdl_surface());
58     int pixels = conv->w * conv->h;
59     SDL_LockSurface(conv);
60     for(int i = 0; i < pixels; ++i) {
61       Uint32 *p = (Uint32 *)conv->pixels + i;
62       *p = *p & conv->format->Amask;
63     }
64     SDL_UnlockSurface(conv);
65     SDL_SetAlpha(conv, SDL_SRCALPHA, 128);
66     shadow_chars = new Surface(conv, true);
67     SDL_FreeSurface(conv);
68   }
69 }
70
71 Font::~Font()
72 {
73   delete chars;
74   delete shadow_chars;
75 }
76
77 float
78 Font::get_text_width(const std::string& text) const
79 {
80   /** Let's calculate the size of the biggest paragraph */
81   std::string::size_type l, hl, ol;
82   hl = 0; l = 0;
83   while(true)
84     {
85     ol = l;
86     l = text.find("\n", l+1);
87     if(l == std::string::npos)
88       break;
89     if(hl < l-ol)
90       hl = l-ol;
91     }
92   if(hl == 0)
93     hl = text.size();
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     {
136     l = text.find("\n", i);
137     if(l == std::string::npos)
138       {
139       l = text.size();
140       done = true;
141       }
142
143     temp[text.copy(temp, l - i, i)] = '\0';
144
145     // calculate X positions based on the alignment type
146     Vector pos = Vector(pos_);
147     if(alignment == CENTER_ALLIGN)
148       pos.x -= get_text_width(temp) / 2;
149     else if(alignment == RIGHT_ALLIGN)
150       pos.x -= get_text_width(temp);
151
152     draw_text(temp, pos + Vector(0,y), drawing_effect, alpha);
153
154     i = l+1;
155     y += h + 2;
156     }
157 }
158
159 void
160 Font::draw_text(const std::string& text, const Vector& pos, 
161     uint32_t drawing_effect, uint8_t alpha) const
162 {
163   if(shadowsize > 0)
164     draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize),
165                drawing_effect, alpha);
166
167   draw_chars(chars, text, pos, drawing_effect, alpha);
168 }
169
170 void
171 Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos,
172                  uint32_t drawing_effect, uint8_t alpha) const
173 {
174   SurfaceImpl* impl = pchars->impl;
175
176   Vector p = pos;
177   for(size_t i = 0; i < text.size(); ++i)
178   {
179     int c = (unsigned char) text[i];
180     if(c > 127) // correct for the 32 controlchars at 128-159
181       c -= 32;
182     // a non-printable character?
183     if(c == '\n') {
184       p.x = pos.x;
185       p.y += h + 2;
186       continue;
187     }
188     if(c == ' ' || c < first_char || c > last_char) {
189       p.x += w;
190       continue;
191     }
192     
193     int index = c - first_char;
194     int source_x = (index % 16) * w;
195     int source_y = (index / 16) * h;
196
197     impl->draw_part(source_x, source_y, p.x, p.y, w, h, alpha, drawing_effect);
198     p.x += w;
199   }
200 }
201