- More work on scripting interface
[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) // 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   return hl * w;
92 }
93
94 float
95 Font::get_text_height(const std::string& text) const
96 {
97   /** Let's calculate height of the text */
98   std::string::size_type l, hh;
99   hh = h; l = 0;
100   while(true)
101     {
102     l = text.find("\n", l+1);
103     if(l == std::string::npos)
104       break;
105     hh += h + 2;
106     }
107
108   return hh;
109 }
110
111 float
112 Font::get_height() const
113 {
114   return h;
115 }
116
117 void
118 Font::draw(const std::string& text, const Vector& pos_, FontAlignment alignment,
119     uint32_t drawing_effect, uint8_t alpha) const
120 {
121   /* Cut lines changes into seperate strings, needed to support center/right text
122      alignments with break lines.
123      Feel free to replace this hack with a more elegant solution
124   */
125   char temp[1024];
126   std::string::size_type l, i, y;
127   bool done = false;
128   i = y = 0;
129
130   while(!done) {
131     l = text.find("\n", i);
132     if(l == std::string::npos) {
133       l = text.size();
134       done = true;
135     }
136     
137     temp[text.copy(temp, l - i, i)] = '\0';
138     
139     // calculate X positions based on the alignment type
140     Vector pos = Vector(pos_);
141     if(alignment == CENTER_ALLIGN)
142       pos.x -= get_text_width(temp) / 2;
143     else if(alignment == RIGHT_ALLIGN)
144       pos.x -= get_text_width(temp);
145
146     draw_text(temp, pos + Vector(0,y), drawing_effect, alpha);
147
148     i = l+1;
149     y += h + 2;
150   }
151 }
152
153 void
154 Font::draw_text(const std::string& text, const Vector& pos, 
155     uint32_t drawing_effect, uint8_t alpha) const
156 {
157   if(shadowsize > 0)
158     draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize),
159                drawing_effect, alpha);
160
161   draw_chars(chars, text, pos, drawing_effect, alpha);
162 }
163
164 void
165 Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos,
166                  uint32_t drawing_effect, uint8_t alpha) const
167 {
168   SurfaceImpl* impl = pchars->impl;
169
170   Vector p = pos;
171   for(size_t i = 0; i < text.size(); ++i) {
172     int c = (unsigned char) text[i];
173     if(c > 127) // correct for the 32 controlchars at 128-159
174       c -= 32;
175     // a non-printable character?
176     if(c == '\n') {
177       p.x = pos.x;
178       p.y += h + 2;
179       continue;
180     }
181     if(c == ' ' || c < first_char || c > last_char) {
182       p.x += w;
183       continue;
184     }
185     
186     int index = c - first_char;
187     int source_x = (index % 16) * w;
188     int source_y = (index / 16) * h;
189
190     impl->draw_part(source_x, source_y, p.x, p.y, w, h, alpha, drawing_effect);
191     p.x += w;
192   }
193 }