- fixed 'When you jump into the roof or a bonus and fall back down you collide with...
[supertux.git] / src / 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 "type.h"
39
40 /* Needed for line calculations */
41 #define SGN(x) ((x)>0 ? 1 : ((x)==0 ? 0:(-1)))
42 #define ABS(x) ((x)>0 ? (x) : (-x))
43
44 /* --- CLEAR SCREEN --- */
45
46 void clearscreen(int r, int g, int b)
47 {
48 #ifndef NOOPENGL
49   if(use_gl)
50     {
51       glClearColor(r/256, g/256, b/256, 1.0);
52       glClear(GL_COLOR_BUFFER_BIT);
53     }
54   else
55   {
56 #endif
57
58     SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, r, g, b));
59 #ifndef NOOPENGL
60
61     }
62 #endif
63 }
64
65 /* --- DRAWS A VERTICAL GRADIENT --- */
66
67 void drawgradient(Color top_clr, Color bot_clr)
68 {
69 #ifndef NOOPENGL
70   if(use_gl)
71     {
72       glBegin(GL_QUADS);
73       glColor3ub(top_clr.red, top_clr.green, top_clr.blue);
74       glVertex2f(0, 0);
75       glVertex2f(screen->w, 0);
76       glColor3ub(bot_clr.red, bot_clr.green, bot_clr.blue);
77       glVertex2f(screen->w, screen->h);
78       glVertex2f(0, screen->h);
79       glEnd();
80     }
81   else
82   {
83 #endif
84
85     for(float y = 0; y < screen->h; y += 2)
86       fillrect(0, (int)y, screen->w, 2,
87        (int)(((float)(top_clr.red-bot_clr.red)/(0-screen->h)) * y + top_clr.red),
88        (int)(((float)(top_clr.green-bot_clr.green)/(0-screen->h)) * y + top_clr.green),
89        (int)(((float)(top_clr.blue-bot_clr.blue)/(0-screen->h)) * y + top_clr.blue),
90        255);
91 /* calculates the color for each line, based in the generic equation for functions: y = mx + b */
92
93 #ifndef NOOPENGL
94
95     }
96 #endif
97 }
98
99 /* --- FADE IN --- */
100
101 /** Fades the given surface into a black one. If fade_out is true, it will fade out, else
102 it will fade in */
103
104 void fade(Surface *surface, int seconds, bool fade_out);
105
106 void fade(const std::string& surface, int seconds, bool fade_out)
107 {
108 Surface* sur = new Surface(datadir + surface, IGNORE_ALPHA);
109 fade(sur, seconds, fade_out);
110 delete sur;
111 }
112
113 void fade(Surface *surface, int seconds, bool fade_out)
114 {
115 float alpha;
116 if (fade_out)
117   alpha = 0;
118 else
119   alpha = 255;
120
121   int cur_time, old_time;
122   cur_time = SDL_GetTicks();
123
124   while(alpha >= 0 && alpha < 256)
125     {
126     surface->draw(0,0,(int)alpha);
127     flipscreen();
128
129     old_time = cur_time;
130     cur_time = SDL_GetTicks();
131
132     /* Calculate the next alpha value */
133     float calc = (float) ((cur_time - old_time) / seconds);
134     if(fade_out)
135       alpha += 255 * calc;
136     else
137       alpha -= 255 * calc;
138     }
139 }
140
141 /** This fade shrinks to the given point */
142
143 #define LOOP_DELAY 20
144 void shrink_fade(Point point, int fade_time)
145 {
146 float left_inc  = (float)point.x / ((float)fade_time / LOOP_DELAY);
147 float right_inc = ((float)screen->w - point.x) / ((float)fade_time / LOOP_DELAY);
148 float up_inc    = (float)point.y / ((float)fade_time / LOOP_DELAY);
149 float down_inc  = ((float)screen->h - point.y) / ((float)fade_time / LOOP_DELAY);
150
151 float left_cor = 0, right_cor = 0, up_cor = 0, down_cor = 0;
152
153 while(left_cor < point.x && right_cor < screen->w - point.x &&
154       up_cor < point.y && down_cor < screen->h - point.y)
155   {
156   left_cor  += left_inc;
157   right_cor += right_inc;
158   up_cor    += up_inc;
159   down_cor  += down_inc;
160
161   fillrect(0, 0, left_cor, screen->h, 0,0,0);  // left side
162   fillrect(screen->w - right_cor, 0, right_cor, screen->h, 0,0,0);  // right side
163   fillrect(0, 0, screen->w, up_cor, 0,0,0);  // up side
164   fillrect(0, screen->h - down_cor, screen->w, down_cor, 0,0,0);  // down side
165
166   flipscreen();
167   SDL_Delay(LOOP_DELAY);
168   }
169 }
170
171 /* 'Stolen' from the SDL documentation.
172  * Set the pixel at (x, y) to the given value
173  * NOTE: The surface must be locked before calling this!
174  */
175 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
176 {
177   int bpp = surface->format->BytesPerPixel;
178   /* Here p is the address to the pixel we want to set */
179   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
180
181   switch(bpp)
182     {
183     case 1:
184       *p = pixel;
185       break;
186
187     case 2:
188       *(Uint16 *)p = pixel;
189       break;
190
191     case 3:
192       if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
193         {
194           p[0] = (pixel >> 16) & 0xff;
195           p[1] = (pixel >> 8) & 0xff;
196           p[2] = pixel & 0xff;
197         }
198       else
199         {
200           p[0] = pixel & 0xff;
201           p[1] = (pixel >> 8) & 0xff;
202           p[2] = (pixel >> 16) & 0xff;
203         }
204       break;
205
206     case 4:
207       *(Uint32 *)p = pixel;
208       break;
209     }
210 }
211
212 /* Draw a single pixel on the screen. */
213 void drawpixel(int x, int y, Uint32 pixel)
214 {
215   /* Lock the screen for direct access to the pixels */
216   if ( SDL_MUSTLOCK(screen) )
217     {
218       if ( SDL_LockSurface(screen) < 0 )
219         {
220           fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
221           return;
222         }
223     }
224
225   if(!(x < 0 || y < 0 || x > screen->w || y > screen->h))
226     putpixel(screen, x, y, pixel);
227
228   if ( SDL_MUSTLOCK(screen) )
229     {
230       SDL_UnlockSurface(screen);
231     }
232   /* Update just the part of the display that we've changed */
233   SDL_UpdateRect(screen, x, y, 1, 1);
234 }
235
236 void drawline(int x1, int y1, int x2, int y2, int r, int g, int b, int a)
237 {
238 #ifndef NOOPENGL
239   if(use_gl)
240     {
241       glEnable(GL_BLEND);
242       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
243       glColor4ub(r, g, b,a);
244
245       glBegin(GL_LINES);
246       glVertex2f(x1, y1);
247       glVertex2f(x2, y2);
248       glEnd();
249       glDisable(GL_BLEND);
250     }
251   else
252     {
253 #endif
254
255       /* Basic unantialiased Bresenham line algorithm */
256       int lg_delta, sh_delta, cycle, lg_step, sh_step;
257       Uint32 color = SDL_MapRGBA(screen->format, r, g, b, a);
258
259       lg_delta = x2 - x1;
260       sh_delta = y2 - y1;
261       lg_step = SGN(lg_delta);
262       lg_delta = ABS(lg_delta);
263       sh_step = SGN(sh_delta);
264       sh_delta = ABS(sh_delta);
265       if (sh_delta < lg_delta)
266         {
267           cycle = lg_delta >> 1;
268           while (x1 != x2)
269             {
270               drawpixel(x1, y1, color);
271               cycle += sh_delta;
272               if (cycle > lg_delta)
273                 {
274                   cycle -= lg_delta;
275                   y1 += sh_step;
276                 }
277               x1 += lg_step;
278             }
279           drawpixel(x1, y1, color);
280         }
281       cycle = sh_delta >> 1;
282       while (y1 != y2)
283         {
284           drawpixel(x1, y1, color);
285           cycle += lg_delta;
286           if (cycle > sh_delta)
287             {
288               cycle -= sh_delta;
289               x1 += lg_step;
290             }
291           y1 += sh_step;
292         }
293       drawpixel(x1, y1, color);
294 #ifndef NOOPENGL
295
296     }
297 #endif
298 }
299
300 /* --- FILL A RECT --- */
301
302 void fillrect(float x, float y, float w, float h, int r, int g, int b, int a)
303 {
304 if(w < 0)
305         {
306         x += w;
307         w = -w;
308         }
309 if(h < 0)
310         {
311         y += h;
312         h = -h;
313         }
314
315 #ifndef NOOPENGL
316   if(use_gl)
317     {
318       glEnable(GL_BLEND);
319       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
320       glColor4ub(r, g, b,a);
321
322       glBegin(GL_POLYGON);
323       glVertex2f(x, y);
324       glVertex2f(x+w, y);
325       glVertex2f(x+w, y+h);
326       glVertex2f(x, y+h);
327       glEnd();
328       glDisable(GL_BLEND);
329     }
330   else
331     {
332 #endif
333       SDL_Rect src, rect;
334       SDL_Surface *temp = NULL;
335
336       rect.x = (int)x;
337       rect.y = (int)y;
338       rect.w = (int)w;
339       rect.h = (int)h;
340
341       if(a != 255)
342         {
343           temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel,
344                                       screen->format->Rmask,
345                                       screen->format->Gmask,
346                                       screen->format->Bmask,
347                                       screen->format->Amask);
348
349
350           src.x = 0;
351           src.y = 0;
352           src.w = rect.w;
353           src.h = rect.h;
354
355           SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, r, g, b));
356
357           SDL_SetAlpha(temp, SDL_SRCALPHA, a);
358
359           SDL_BlitSurface(temp,0,screen,&rect);
360
361           SDL_FreeSurface(temp);
362         }
363       else
364         SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b));
365
366 #ifndef NOOPENGL
367
368     }
369 #endif
370 }
371
372
373 /* --- UPDATE SCREEN --- */
374
375 void updatescreen(void)
376 {
377   if(use_gl)  /*clearscreen(0,0,0);*/
378     SDL_GL_SwapBuffers();
379   else
380     SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
381 }
382
383 void flipscreen(void)
384 {
385   if(use_gl)
386     SDL_GL_SwapBuffers();
387   else
388     SDL_Flip(screen);
389 }
390
391 void fadeout()
392 {
393   clearscreen(0, 0, 0);
394   white_text->draw_align("Loading...", screen->w/2, screen->h/2, A_HMIDDLE, A_TOP);
395   flipscreen();
396 }
397
398 void update_rect(SDL_Surface *scr, Sint32 x, Sint32 y, Sint32 w, Sint32 h)
399 {
400   if(!use_gl)
401     SDL_UpdateRect(scr, x, y, w, h);
402 }
403