- More work on scripting interface
[supertux.git] / src / video / screen.cpp
1 //  $Id: screen.cpp 2334 2005-04-04 16:26:14Z grumbel $
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 #include <config.h>
20
21 #include <iostream>
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25 #include <cerrno>
26 #include <assert.h>
27
28 #include <unistd.h>
29
30 #include <SDL.h>
31 #include <SDL_image.h>
32
33 #ifndef WIN32
34 #include <sys/types.h>
35 #include <ctype.h>
36 #endif
37
38 #include "gameconfig.h"
39 #include "screen.h"
40 #include "main.h"
41 #include "video/drawing_context.h"
42 #include "math/vector.h"
43
44 static const float LOOP_DELAY = 20.0;
45 extern SDL_Surface* screen;
46
47 /* 'Stolen' from the SDL documentation.
48  * Return the pixel value at (x, y)
49  * NOTE: The surface must be locked before calling this!
50  */
51 Uint32 getpixel(SDL_Surface *surface, int x, int y)
52 {
53     int bpp = surface->format->BytesPerPixel;
54     /* Here p is the address to the pixel we want to retrieve */
55     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
56
57     switch(bpp) {
58     case 1:
59         return *p;
60
61     case 2:
62         return *(Uint16 *)p;
63
64     case 3:
65         if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
66             return p[0] << 16 | p[1] << 8 | p[2];
67         else
68             return p[0] | p[1] << 8 | p[2] << 16;
69
70     case 4:
71         return *(Uint32 *)p;
72
73     default:
74         return 0;       /* shouldn't happen, but avoids warnings */
75     }
76 }
77
78 /* 'Stolen' from the SDL documentation.
79  * Set the pixel at (x, y) to the given value
80  * NOTE: The surface must be locked before calling this!
81  */
82 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
83 {
84   int bpp = surface->format->BytesPerPixel;
85   /* Here p is the address to the pixel we want to set */
86   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
87
88   switch(bpp) {
89     case 1:
90       *p = pixel;
91       break;
92
93     case 2:
94       *(Uint16 *)p = pixel;
95       break;
96
97     case 3:
98       if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
99         {
100           p[0] = (pixel >> 16) & 0xff;
101           p[1] = (pixel >> 8) & 0xff;
102           p[2] = pixel & 0xff;
103         }
104       else
105         {
106           p[0] = pixel & 0xff;
107           p[1] = (pixel >> 8) & 0xff;
108           p[2] = (pixel >> 16) & 0xff;
109         }
110       break;
111
112     case 4:
113       *(Uint32 *)p = pixel;
114       break;
115
116     default:
117       assert(false);
118   }
119 }
120
121 /* Draw a single pixel on the screen. */
122 void drawpixel(int x, int y, Uint32 pixel)
123 {
124   /* Lock the screen for direct access to the pixels */
125   if ( SDL_MUSTLOCK(screen) )
126     {
127       if ( SDL_LockSurface(screen) < 0 )
128         {
129           fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
130           return;
131         }
132     }
133
134   if(!(x < 0 || y < 0 || x > SCREEN_WIDTH || y > SCREEN_HEIGHT))
135     putpixel(screen, x, y, pixel);
136
137   if ( SDL_MUSTLOCK(screen) )
138     {
139       SDL_UnlockSurface(screen);
140     }
141   /* Update just the part of the display that we've changed */
142   SDL_UpdateRect(screen, x, y, 1, 1);
143 }
144
145 /* --- FILL A RECT --- */
146
147 void fillrect(float x, float y, float w, float h, int r, int g, int b, int a)
148 {
149   if(w < 0) {
150     x += w;
151     w = -w;
152   }
153   if(h < 0) {
154     y += h;
155     h = -h;
156   }
157
158   if(config->use_gl) {
159     glEnable(GL_BLEND);
160     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
161     glColor4ub(r, g, b,a);
162     
163     glBegin(GL_POLYGON);
164     glVertex2f(x, y);
165     glVertex2f(x+w, y);
166     glVertex2f(x+w, y+h);
167     glVertex2f(x, y+h);
168     glEnd();
169     glDisable(GL_BLEND);
170   } else {
171     SDL_Rect src, rect;
172     SDL_Surface *temp = NULL;
173
174     rect.x = (int)x;
175     rect.y = (int)y;
176     rect.w = (int)w;
177     rect.h = (int)h;
178     
179     if(a != 255) {
180       temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel,
181                                   screen->format->Rmask,
182                                   screen->format->Gmask,
183                                   screen->format->Bmask,
184                                   screen->format->Amask);
185       
186       
187       src.x = 0;
188       src.y = 0;
189       src.w = rect.w;
190       src.h = rect.h;
191       
192       SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, r, g, b));
193       SDL_SetAlpha(temp, SDL_SRCALPHA, a);
194       SDL_BlitSurface(temp,0,screen,&rect);
195       SDL_FreeSurface(temp);
196     } else {
197       SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b));
198     }
199   }
200 }
201
202 /* Needed for line calculations */
203 #define SGN(x) ((x)>0 ? 1 : ((x)==0 ? 0:(-1)))
204 #define ABS(x) ((x)>0 ? (x) : (-x))
205
206 void draw_line(float x1, float y1, float x2, float y2,
207                          int r, int g, int b, int a)
208 {
209   if(config->use_gl) {
210     glEnable(GL_BLEND);
211     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
212     glColor4ub(r, g, b,a);
213     
214     glBegin(GL_LINES);
215     glVertex2f(x1, y1);
216     glVertex2f(x2, y2);
217     glEnd();
218     glDisable(GL_BLEND);
219   } else {
220     /* Basic unantialiased Bresenham line algorithm */
221     int lg_delta, sh_delta, cycle, lg_step, sh_step;
222     Uint32 color = SDL_MapRGBA(screen->format, r, g, b, a);
223     
224     lg_delta = (int)(x2 - x1);
225     sh_delta = (int)(y2 - y1);
226     lg_step = SGN(lg_delta);
227     lg_delta = ABS(lg_delta);
228     sh_step = SGN(sh_delta);
229     sh_delta = ABS(sh_delta);
230     if (sh_delta < lg_delta) {
231       cycle = lg_delta >> 1;
232       while (x1 != x2) {
233         drawpixel((int)x1, (int)y1, color);
234         cycle += sh_delta;
235         if (cycle > lg_delta) {
236           cycle -= lg_delta;
237           y1 += sh_step;
238         }
239         x1 += lg_step;
240       }
241       drawpixel((int)x1, (int)y1, color);
242     }
243     cycle = sh_delta >> 1;
244     while (y1 != y2) {
245       drawpixel((int)x1, (int)y1, color);
246       cycle += lg_delta;
247       if (cycle > sh_delta) {
248         cycle -= sh_delta;
249         x1 += lg_step;
250       }
251       y1 += sh_step;
252     }
253     drawpixel((int)x1, (int)y1, color);
254   }
255 }
256
257
258 void fadeout(int fade_time)
259 {
260   float alpha_inc  = 256 / (fade_time / LOOP_DELAY);
261   float alpha = 256;
262
263   while(alpha > 0) {
264     alpha -= alpha_inc;
265     fillrect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0,0,0, (int)alpha_inc);  // left side
266                                                    
267     DrawingContext context; // ugly...
268     context.do_drawing();
269     
270     SDL_Delay(int(LOOP_DELAY));
271   }
272
273   fillrect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 255);
274 }
275
276 void shrink_fade(const Vector& point, int fade_time)
277 {
278   float left_inc  = point.x / ((float)fade_time / LOOP_DELAY);
279   float right_inc = (SCREEN_WIDTH - point.x) / ((float)fade_time / LOOP_DELAY);
280   float up_inc    = point.y / ((float)fade_time / LOOP_DELAY);
281   float down_inc  = (SCREEN_HEIGHT - point.y) / ((float)fade_time / LOOP_DELAY);
282                                                                                 
283   float left_cor = 0, right_cor = 0, up_cor = 0, down_cor = 0;
284                                                                                 
285   while(left_cor < point.x && right_cor < SCREEN_WIDTH - point.x &&
286       up_cor < point.y && down_cor < SCREEN_HEIGHT - point.y) {
287     left_cor  += left_inc;
288     right_cor += right_inc;
289     up_cor    += up_inc;
290     down_cor  += down_inc;
291                                                                                 
292     fillrect(0, 0, left_cor, SCREEN_HEIGHT, 0,0,0);  // left side
293     fillrect(SCREEN_WIDTH - right_cor, 0, right_cor, SCREEN_HEIGHT, 0,0,0);  // right side
294     fillrect(0, 0, SCREEN_WIDTH, up_cor, 0,0,0);  // up side
295     fillrect(0, SCREEN_HEIGHT - down_cor, SCREEN_WIDTH, down_cor+1, 0,0,0);  // down side                                                                                
296     DrawingContext context; // ugly...
297     context.do_drawing();
298     
299     SDL_Delay(int(LOOP_DELAY));
300   }
301 }