cleaned up font drawing code and added support for latin1 font of litespeed
[supertux.git] / src / screen / screen.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
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  02111-1307, USA.
19
20 #include <iostream>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <SDL.h>
27 #include <SDL_image.h>
28
29 #ifndef WIN32
30 #include <sys/types.h>
31 #include <ctype.h>
32 #endif
33
34 #include "defines.h"
35 #include "globals.h"
36 #include "screen.h"
37 #include "setup.h"
38 #include "drawing_context.h"
39 #include "type.h"
40
41 /* Needed for line calculations */
42 #define SGN(x) ((x)>0 ? 1 : ((x)==0 ? 0:(-1)))
43 #define ABS(x) ((x)>0 ? (x) : (-x))
44
45 /* --- FADE IN --- */
46
47 /** Fades the given surface into a black one. If fade_out is true, it will fade out, else
48 it will fade in */
49
50 #if 0
51 void fade(Surface *surface, int seconds, bool fade_out);
52
53 void fade(const std::string& surface, int seconds, bool fade_out)
54 {
55 Surface* sur = new Surface(datadir + surface, IGNORE_ALPHA);
56 fade(sur, seconds, fade_out);
57 delete sur;
58 }
59
60 void fade(Surface *surface, int seconds, bool fade_out)
61 {
62 float alpha;
63 if (fade_out)
64   alpha = 0;
65 else
66   alpha = 255;
67
68   int cur_time, old_time;
69   cur_time = SDL_GetTicks();
70
71   while(alpha >= 0 && alpha < 256)
72     {
73     surface->draw(0,0,(int)alpha);
74     flipscreen();
75
76     old_time = cur_time;
77     cur_time = SDL_GetTicks();
78
79     /* Calculate the next alpha value */
80     float calc = (float) ((cur_time - old_time) / seconds);
81     if(fade_out)
82       alpha += 255 * calc;
83     else
84       alpha -= 255 * calc;
85     }
86 }
87 #endif
88
89 /* 'Stolen' from the SDL documentation.
90  * Set the pixel at (x, y) to the given value
91  * NOTE: The surface must be locked before calling this!
92  */
93 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
94 {
95   int bpp = surface->format->BytesPerPixel;
96   /* Here p is the address to the pixel we want to set */
97   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
98
99   switch(bpp)
100     {
101     case 1:
102       *p = pixel;
103       break;
104
105     case 2:
106       *(Uint16 *)p = pixel;
107       break;
108
109     case 3:
110       if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
111         {
112           p[0] = (pixel >> 16) & 0xff;
113           p[1] = (pixel >> 8) & 0xff;
114           p[2] = pixel & 0xff;
115         }
116       else
117         {
118           p[0] = pixel & 0xff;
119           p[1] = (pixel >> 8) & 0xff;
120           p[2] = (pixel >> 16) & 0xff;
121         }
122       break;
123
124     case 4:
125       *(Uint32 *)p = pixel;
126       break;
127     }
128 }
129
130 /* Draw a single pixel on the screen. */
131 void drawpixel(int x, int y, Uint32 pixel)
132 {
133   /* Lock the screen for direct access to the pixels */
134   if ( SDL_MUSTLOCK(screen) )
135     {
136       if ( SDL_LockSurface(screen) < 0 )
137         {
138           fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
139           return;
140         }
141     }
142
143   if(!(x < 0 || y < 0 || x > screen->w || y > screen->h))
144     putpixel(screen, x, y, pixel);
145
146   if ( SDL_MUSTLOCK(screen) )
147     {
148       SDL_UnlockSurface(screen);
149     }
150   /* Update just the part of the display that we've changed */
151   SDL_UpdateRect(screen, x, y, 1, 1);
152 }
153
154 void drawline(int x1, int y1, int x2, int y2, int r, int g, int b, int a)
155 {
156 #ifndef NOOPENGL
157   if(use_gl)
158     {
159       glEnable(GL_BLEND);
160       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
161       glColor4ub(r, g, b,a);
162
163       glBegin(GL_LINES);
164       glVertex2f(x1, y1);
165       glVertex2f(x2, y2);
166       glEnd();
167       glDisable(GL_BLEND);
168     }
169   else
170     {
171 #endif
172
173       /* Basic unantialiased Bresenham line algorithm */
174       int lg_delta, sh_delta, cycle, lg_step, sh_step;
175       Uint32 color = SDL_MapRGBA(screen->format, r, g, b, a);
176
177       lg_delta = x2 - x1;
178       sh_delta = y2 - y1;
179       lg_step = SGN(lg_delta);
180       lg_delta = ABS(lg_delta);
181       sh_step = SGN(sh_delta);
182       sh_delta = ABS(sh_delta);
183       if (sh_delta < lg_delta)
184         {
185           cycle = lg_delta >> 1;
186           while (x1 != x2)
187             {
188               drawpixel(x1, y1, color);
189               cycle += sh_delta;
190               if (cycle > lg_delta)
191                 {
192                   cycle -= lg_delta;
193                   y1 += sh_step;
194                 }
195               x1 += lg_step;
196             }
197           drawpixel(x1, y1, color);
198         }
199       cycle = sh_delta >> 1;
200       while (y1 != y2)
201         {
202           drawpixel(x1, y1, color);
203           cycle += lg_delta;
204           if (cycle > sh_delta)
205             {
206               cycle -= sh_delta;
207               x1 += lg_step;
208             }
209           y1 += sh_step;
210         }
211       drawpixel(x1, y1, color);
212 #ifndef NOOPENGL
213
214     }
215 #endif
216 }
217
218 /* --- FILL A RECT --- */
219
220 void fillrect(float x, float y, float w, float h, int r, int g, int b, int a)
221 {
222 if(w < 0)
223         {
224         x += w;
225         w = -w;
226         }
227 if(h < 0)
228         {
229         y += h;
230         h = -h;
231         }
232
233 #ifndef NOOPENGL
234   if(use_gl)
235     {
236       glEnable(GL_BLEND);
237       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
238       glColor4ub(r, g, b,a);
239
240       glBegin(GL_POLYGON);
241       glVertex2f(x, y);
242       glVertex2f(x+w, y);
243       glVertex2f(x+w, y+h);
244       glVertex2f(x, y+h);
245       glEnd();
246       glDisable(GL_BLEND);
247     }
248   else
249     {
250 #endif
251       SDL_Rect src, rect;
252       SDL_Surface *temp = NULL;
253
254       rect.x = (int)x;
255       rect.y = (int)y;
256       rect.w = (int)w;
257       rect.h = (int)h;
258
259       if(a != 255)
260         {
261           temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel,
262                                       screen->format->Rmask,
263                                       screen->format->Gmask,
264                                       screen->format->Bmask,
265                                       screen->format->Amask);
266
267
268           src.x = 0;
269           src.y = 0;
270           src.w = rect.w;
271           src.h = rect.h;
272
273           SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, r, g, b));
274
275           SDL_SetAlpha(temp, SDL_SRCALPHA, a);
276
277           SDL_BlitSurface(temp,0,screen,&rect);
278
279           SDL_FreeSurface(temp);
280         }
281       else
282         SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b));
283
284 #ifndef NOOPENGL
285
286     }
287 #endif
288 }
289
290
291 #define LOOP_DELAY 20.0
292
293 void fadeout(int fade_time)
294 {
295   float alpha_inc  = 256 / (fade_time / LOOP_DELAY);
296   float alpha = 256;
297
298   while(alpha > 0)
299     {
300     alpha -= alpha_inc;
301     fillrect(0, 0, screen->w, screen->h, 0,0,0, (int)alpha_inc);  // left side
302                                                    
303     DrawingContext context; // ugly...
304     context.do_drawing();
305
306     SDL_Delay(int(LOOP_DELAY));
307     }
308
309   fillrect(0, 0, screen->w, screen->h, 0, 0, 0, 255);
310   
311   DrawingContext context;
312   context.draw_text_center(white_text, "Loading...",
313       Vector(0, screen->h/2), LAYER_FOREGROUND1);
314   context.do_drawing();
315 }
316
317 void shrink_fade(const Vector& point, int fade_time)
318 {
319   float left_inc  = point.x / ((float)fade_time / LOOP_DELAY);
320   float right_inc = (screen->w - point.x) / ((float)fade_time / LOOP_DELAY);
321   float up_inc    = point.y / ((float)fade_time / LOOP_DELAY);
322   float down_inc  = (screen->h - point.y) / ((float)fade_time / LOOP_DELAY);
323                                                                                 
324   float left_cor = 0, right_cor = 0, up_cor = 0, down_cor = 0;
325                                                                                 
326   while(left_cor < point.x && right_cor < screen->w - point.x &&
327       up_cor < point.y && down_cor < screen->h - point.y)
328   {
329     left_cor  += left_inc;
330     right_cor += right_inc;
331     up_cor    += up_inc;
332     down_cor  += down_inc;
333                                                                                 
334     fillrect(0, 0, left_cor, screen->h, 0,0,0);  // left side
335     fillrect(screen->w - right_cor, 0, right_cor, screen->h, 0,0,0);  // right side
336     fillrect(0, 0, screen->w, up_cor, 0,0,0);  // up side
337     fillrect(0, screen->h - down_cor, screen->w, down_cor+1, 0,0,0);  // down side                                                                                
338     DrawingContext context; // ugly...
339     context.do_drawing();
340     
341     SDL_Delay(int(LOOP_DELAY));
342   }
343 }
344