Added support for gradients :)
[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(int top_r, int top_g, int top_b, int bot_r, int bot_g, int bot_b)
60 {
61 #ifndef NOOPENGL
62   if(use_gl)
63     {
64       glBegin(GL_QUADS);
65       glColor3ub(top_r, top_g, top_b);
66       glVertex2f(0, 0);
67       glVertex2f(640, 0);
68       glColor3ub(bot_r, bot_g, bot_b);
69       glVertex2f(640, 480);
70       glVertex2f(0, 480);
71       glEnd();
72     }
73   else
74   {
75 #endif
76
77   SDL_Rect r;
78   r.x = 0;
79   r.w = 640;
80   r.h = 2;
81
82     for(float y = 0; y < 480; y += 2)
83       {
84       r.y = (int)y;
85
86       SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, (int)(((float)(top_r-bot_r)/640) * y + top_r), (int)(((float)(top_g-bot_g)/640) * y + top_g), (int)(((float)(top_b-bot_b)/640) * y + top_b)));
87       }
88 /* calculates the color for each line, based in the generic equation for functions: y = mx + b */
89
90 #ifndef NOOPENGL
91
92     }
93 #endif
94 }
95
96 /* 'Stolen' from the SDL documentation.
97  * Set the pixel at (x, y) to the given value
98  * NOTE: The surface must be locked before calling this!
99  */
100 void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
101 {
102   int bpp = surface->format->BytesPerPixel;
103   /* Here p is the address to the pixel we want to set */
104   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
105
106   switch(bpp)
107     {
108     case 1:
109       *p = pixel;
110       break;
111
112     case 2:
113       *(Uint16 *)p = pixel;
114       break;
115
116     case 3:
117       if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
118         {
119           p[0] = (pixel >> 16) & 0xff;
120           p[1] = (pixel >> 8) & 0xff;
121           p[2] = pixel & 0xff;
122         }
123       else
124         {
125           p[0] = pixel & 0xff;
126           p[1] = (pixel >> 8) & 0xff;
127           p[2] = (pixel >> 16) & 0xff;
128         }
129       break;
130
131     case 4:
132       *(Uint32 *)p = pixel;
133       break;
134     }
135 }
136
137 /* Draw a single pixel on the screen. */
138 void drawpixel(int x, int y, Uint32 pixel)
139 {
140   /* Lock the screen for direct access to the pixels */
141   if ( SDL_MUSTLOCK(screen) )
142     {
143       if ( SDL_LockSurface(screen) < 0 )
144         {
145           fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
146           return;
147         }
148     }
149
150   if(!(x < 0 || y < 0 || x > screen->w || y > screen->h))
151     putpixel(screen, x, y, pixel);
152
153   if ( SDL_MUSTLOCK(screen) )
154     {
155       SDL_UnlockSurface(screen);
156     }
157   /* Update just the part of the display that we've changed */
158   SDL_UpdateRect(screen, x, y, 1, 1);
159 }
160
161 void drawline(int x1, int y1, int x2, int y2, int r, int g, int b, int a)
162 {
163 #ifndef NOOPENGL
164   if(use_gl)
165     {
166       glEnable(GL_BLEND);
167       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
168       glColor4ub(r, g, b,a);
169
170       glBegin(GL_LINES);
171       glVertex2f(x1, y1);
172       glVertex2f(x2, y2);
173       glEnd();
174       glDisable(GL_BLEND);
175     }
176   else
177     {
178 #endif
179
180       /* Basic unantialiased Bresenham line algorithm */
181       int lg_delta, sh_delta, cycle, lg_step, sh_step;
182       Uint32 color = SDL_MapRGBA(screen->format, r, g, b, a);
183
184       lg_delta = x2 - x1;
185       sh_delta = y2 - y1;
186       lg_step = SGN(lg_delta);
187       lg_delta = ABS(lg_delta);
188       sh_step = SGN(sh_delta);
189       sh_delta = ABS(sh_delta);
190       if (sh_delta < lg_delta)
191         {
192           cycle = lg_delta >> 1;
193           while (x1 != x2)
194             {
195               drawpixel(x1, y1, color);
196               cycle += sh_delta;
197               if (cycle > lg_delta)
198                 {
199                   cycle -= lg_delta;
200                   y1 += sh_step;
201                 }
202               x1 += lg_step;
203             }
204           drawpixel(x1, y1, color);
205         }
206       cycle = sh_delta >> 1;
207       while (y1 != y2)
208         {
209           drawpixel(x1, y1, color);
210           cycle += lg_delta;
211           if (cycle > sh_delta)
212             {
213               cycle -= sh_delta;
214               x1 += lg_step;
215             }
216           y1 += sh_step;
217         }
218       drawpixel(x1, y1, color);
219 #ifndef NOOPENGL
220
221     }
222 #endif
223 }
224
225 /* --- FILL A RECT --- */
226
227 void fillrect(float x, float y, float w, float h, int r, int g, int b, int a)
228 {
229 if(w < 0)
230         {
231         x += w;
232         w = -w;
233         }
234 if(h < 0)
235         {
236         y += h;
237         h = -h;
238         }
239
240 #ifndef NOOPENGL
241   if(use_gl)
242     {
243       glEnable(GL_BLEND);
244       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
245       glColor4ub(r, g, b,a);
246
247       glBegin(GL_POLYGON);
248       glVertex2f(x, y);
249       glVertex2f(x+w, y);
250       glVertex2f(x+w, y+h);
251       glVertex2f(x, y+h);
252       glEnd();
253       glDisable(GL_BLEND);
254     }
255   else
256     {
257 #endif
258       SDL_Rect src, rect;
259       SDL_Surface *temp = NULL;
260
261       rect.x = (int)x;
262       rect.y = (int)y;
263       rect.w = (int)w;
264       rect.h = (int)h;
265
266       if(a != 255)
267         {
268           temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel,
269                                       screen->format->Rmask,
270                                       screen->format->Gmask,
271                                       screen->format->Bmask,
272                                       screen->format->Amask);
273
274
275           src.x = 0;
276           src.y = 0;
277           src.w = rect.w;
278           src.h = rect.h;
279
280           SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, r, g, b));
281
282           SDL_SetAlpha(temp, SDL_SRCALPHA, a);
283
284           SDL_BlitSurface(temp,0,screen,&rect);
285
286           SDL_FreeSurface(temp);
287         }
288       else
289         SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b));
290
291 #ifndef NOOPENGL
292
293     }
294 #endif
295 }
296
297
298 /* --- UPDATE SCREEN --- */
299
300 void updatescreen(void)
301 {
302   if(use_gl)  /*clearscreen(0,0,0);*/
303     SDL_GL_SwapBuffers();
304   else
305     SDL_UpdateRect(screen, 0, 0, screen->w, screen->h);
306 }
307
308 void flipscreen(void)
309 {
310   if(use_gl)
311     SDL_GL_SwapBuffers();
312   else
313     SDL_Flip(screen);
314 }
315
316 void update_rect(SDL_Surface *scr, Sint32 x, Sint32 y, Sint32 w, Sint32 h)
317 {
318   if(!use_gl)
319     SDL_UpdateRect(scr, x, y, w, h);
320 }
321