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