- fixed problem with last_menu not being able to handle menues deeper than two submenues
[supertux.git] / src / screen.cpp
1 /*
2   screen.c
3   
4   Super Tux - Screen Functions
5   
6   by Bill Kendrick
7   bill@newbreedsoftware.com
8   http://www.newbreedsoftware.com/supertux/
9   
10   April 11, 2000 - March 15, 2004
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include <SDL.h>
19 #include <SDL_image.h>
20
21 #ifndef WIN32
22 #include <sys/types.h>
23 #include <ctype.h>
24 #endif
25
26 #include "defines.h"
27 #include "globals.h"
28 #include "screen.h"
29 #include "setup.h"
30 #include "type.h"
31
32 /* Needed for line calculations */
33 #define SGN(x) ((x)>0 ? 1 : ((x)==0 ? 0:(-1)))
34 #define ABS(x) ((x)>0 ? (x) : (-x))
35
36 /* --- CLEAR SCREEN --- */
37
38 void clearscreen(int r, int g, int b)
39 {
40 #ifndef NOOPENGL
41   if(use_gl)
42     {
43       glClearColor(r/256, g/256, b/256, 1.0);
44       glClear(GL_COLOR_BUFFER_BIT);
45     }
46   else
47   {
48 #endif
49
50     SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, r, g, b));
51 #ifndef NOOPENGL
52
53     }
54 #endif
55 }
56
57 /* --- DRAWS A VERTICAL GRADIENT --- */
58
59 void drawgradient(Color top_clr, Color bot_clr)
60 {
61 #ifndef NOOPENGL
62   if(use_gl)
63     {
64       glBegin(GL_QUADS);
65       glColor3ub(top_clr.red, top_clr.green, top_clr.blue);
66       glVertex2f(0, 0);
67       glVertex2f(640, 0);
68       glColor3ub(bot_clr.red, bot_clr.green, bot_clr.blue);
69       glVertex2f(640, 480);
70       glVertex2f(0, 480);
71       glEnd();
72     }
73   else
74   {
75 #endif
76
77     for(float y = 0; y < 480; y += 2)
78       fillrect(0, (int)y, 640, 2,
79                      (int)(((float)(top_clr.red-bot_clr.red)/(0-480)) * y + top_clr.red),
80                      (int)(((float)(top_clr.green-bot_clr.green)/(0-480)) * y + top_clr.green),
81                      (int)(((float)(top_clr.blue-bot_clr.blue)/(0-480)) * y + top_clr.blue), 255);
82 /* calculates the color for each line, based in the generic equation for functions: y = mx + b */
83
84 #ifndef NOOPENGL
85
86     }
87 #endif
88 }
89
90 /* 'Stolen' from the SDL documentation.
91  * Set the pixel at (x, y) to the given value
92  * NOTE: The surface must be locked before calling this!
93  */
94 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
95 {
96   int bpp = surface->format->BytesPerPixel;
97   /* Here p is the address to the pixel we want to set */
98   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
99
100   switch(bpp)
101     {
102     case 1:
103       *p = pixel;
104       break;
105
106     case 2:
107       *(Uint16 *)p = pixel;
108       break;
109
110     case 3:
111       if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
112         {
113           p[0] = (pixel >> 16) & 0xff;
114           p[1] = (pixel >> 8) & 0xff;
115           p[2] = pixel & 0xff;
116         }
117       else
118         {
119           p[0] = pixel & 0xff;
120           p[1] = (pixel >> 8) & 0xff;
121           p[2] = (pixel >> 16) & 0xff;
122         }
123       break;
124
125     case 4:
126       *(Uint32 *)p = pixel;
127       break;
128     }
129 }
130
131 /* Draw a single pixel on the screen. */
132 void drawpixel(int x, int y, Uint32 pixel)
133 {
134   /* Lock the screen for direct access to the pixels */
135   if ( SDL_MUSTLOCK(screen) )
136     {
137       if ( SDL_LockSurface(screen) < 0 )
138         {
139           fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
140           return;
141         }
142     }
143
144   if(!(x < 0 || y < 0 || x > screen->w || y > screen->h))
145     putpixel(screen, x, y, pixel);
146
147   if ( SDL_MUSTLOCK(screen) )
148     {
149       SDL_UnlockSurface(screen);
150     }
151   /* Update just the part of the display that we've changed */
152   SDL_UpdateRect(screen, x, y, 1, 1);
153 }
154
155 void drawline(int x1, int y1, int x2, int y2, int r, int g, int b, int a)
156 {
157 #ifndef NOOPENGL
158   if(use_gl)
159     {
160       glEnable(GL_BLEND);
161       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
162       glColor4ub(r, g, b,a);
163
164       glBegin(GL_LINES);
165       glVertex2f(x1, y1);
166       glVertex2f(x2, y2);
167       glEnd();
168       glDisable(GL_BLEND);
169     }
170   else
171     {
172 #endif
173
174       /* Basic unantialiased Bresenham line algorithm */
175       int lg_delta, sh_delta, cycle, lg_step, sh_step;
176       Uint32 color = SDL_MapRGBA(screen->format, r, g, b, a);
177
178       lg_delta = x2 - x1;
179       sh_delta = y2 - y1;
180       lg_step = SGN(lg_delta);
181       lg_delta = ABS(lg_delta);
182       sh_step = SGN(sh_delta);
183       sh_delta = ABS(sh_delta);
184       if (sh_delta < lg_delta)
185         {
186           cycle = lg_delta >> 1;
187           while (x1 != x2)
188             {
189               drawpixel(x1, y1, color);
190               cycle += sh_delta;
191               if (cycle > lg_delta)
192                 {
193                   cycle -= lg_delta;
194                   y1 += sh_step;
195                 }
196               x1 += lg_step;
197             }
198           drawpixel(x1, y1, color);
199         }
200       cycle = sh_delta >> 1;
201       while (y1 != y2)
202         {
203           drawpixel(x1, y1, color);
204           cycle += lg_delta;
205           if (cycle > sh_delta)
206             {
207               cycle -= sh_delta;
208               x1 += lg_step;
209             }
210           y1 += sh_step;
211         }
212       drawpixel(x1, y1, color);
213 #ifndef NOOPENGL
214
215     }
216 #endif
217 }
218
219 /* --- FILL A RECT --- */
220
221 void fillrect(float x, float y, float w, float h, int r, int g, int b, int a)
222 {
223 if(w < 0)
224         {
225         x += w;
226         w = -w;
227         }
228 if(h < 0)
229         {
230         y += h;
231         h = -h;
232         }
233
234 #ifndef NOOPENGL
235   if(use_gl)
236     {
237       glEnable(GL_BLEND);
238       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
239       glColor4ub(r, g, b,a);
240
241       glBegin(GL_POLYGON);
242       glVertex2f(x, y);
243       glVertex2f(x+w, y);
244       glVertex2f(x+w, y+h);
245       glVertex2f(x, y+h);
246       glEnd();
247       glDisable(GL_BLEND);
248     }
249   else
250     {
251 #endif
252       SDL_Rect src, rect;
253       SDL_Surface *temp = NULL;
254
255       rect.x = (int)x;
256       rect.y = (int)y;
257       rect.w = (int)w;
258       rect.h = (int)h;
259
260       if(a != 255)
261         {
262           temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel,
263                                       screen->format->Rmask,
264                                       screen->format->Gmask,
265                                       screen->format->Bmask,
266                                       screen->format->Amask);
267
268
269           src.x = 0;
270           src.y = 0;
271           src.w = rect.w;
272           src.h = rect.h;
273
274           SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, r, g, b));
275
276           SDL_SetAlpha(temp, SDL_SRCALPHA, a);
277
278           SDL_BlitSurface(temp,0,screen,&rect);
279
280           SDL_FreeSurface(temp);
281         }
282       else
283         SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b));
284
285 #ifndef NOOPENGL
286
287     }
288 #endif
289 }
290
291
292 /* --- UPDATE SCREEN --- */
293
294 void updatescreen(void)
295 {
296   if(use_gl)  /*clearscreen(0,0,0);*/
297     SDL_GL_SwapBuffers();
298   else
299     SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
300 }
301
302 void flipscreen(void)
303 {
304   if(use_gl)
305     SDL_GL_SwapBuffers();
306   else
307     SDL_Flip(screen);
308 }
309
310 void update_rect(SDL_Surface *scr, Sint32 x, Sint32 y, Sint32 w, Sint32 h)
311 {
312   if(!use_gl)
313     SDL_UpdateRect(scr, x, y, w, h);
314 }
315