forgot more stuff
[supertux.git] / src / video / drawing_context.cpp
1 //  $Id: drawing_context.cpp 2334 2005-04-04 16:26:14Z grumbel $
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2004 Matthias Braun <matze@braunis.de
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 <algorithm>
22 #include <cassert>
23 #include <iostream>
24
25 #include "drawing_context.h"
26 #include "surface.h"
27 #include "app/globals.h"
28 #include "font.h"
29 #include "gameconfig.h"
30
31 using namespace SuperTux;
32
33 DrawingContext::DrawingContext(SDL_Surface* targetsurface)
34 {
35   if(targetsurface) {
36     screen = targetsurface;
37   } else {
38     screen = SDL_GetVideoSurface();
39   }
40 }
41
42 DrawingContext::~DrawingContext()
43 {
44 }
45
46 void
47 DrawingContext::draw_surface(const Surface* surface, const Vector& position,
48     int layer, uint32_t drawing_effect)
49 {
50   assert(surface != 0);
51   
52   DrawingRequest request;
53
54   request.type = SURFACE;
55   request.pos = transform.apply(position);
56
57   if(request.pos.x >= SCREEN_WIDTH || request.pos.y >= SCREEN_HEIGHT
58       || request.pos.x + surface->w < 0 || request.pos.y + surface->h < 0)
59     return;
60
61   request.layer = layer;
62   request.drawing_effect = transform.drawing_effect | drawing_effect;
63   request.zoom = transform.zoom;
64   request.alpha = transform.alpha;
65   request.request_data = const_cast<Surface*> (surface);  
66
67   drawingrequests.push_back(request);
68 }
69
70 void
71 DrawingContext::draw_surface_part(const Surface* surface, const Vector& source,
72     const Vector& size, const Vector& dest, int layer, uint32_t drawing_effect)
73 {
74   assert(surface != 0);
75
76   DrawingRequest request;
77
78   request.type = SURFACE_PART;
79   request.pos = transform.apply(dest);
80   request.layer = layer;
81   request.drawing_effect = transform.drawing_effect | drawing_effect;
82   request.alpha = transform.alpha;
83   
84   SurfacePartRequest* surfacepartrequest = new SurfacePartRequest();
85   surfacepartrequest->size = size;
86   surfacepartrequest->source = source;
87   surfacepartrequest->surface = surface;
88
89   // clip on screen borders
90   if(request.pos.x < 0) {
91     surfacepartrequest->size.x += request.pos.x;
92     if(surfacepartrequest->size.x <= 0)
93       return;
94     surfacepartrequest->source.x -= request.pos.x;
95     request.pos.x = 0;
96   }
97   if(request.pos.y < 0) {
98     surfacepartrequest->size.y += request.pos.y;
99     if(surfacepartrequest->size.y <= 0)
100       return;
101     surfacepartrequest->source.y -= request.pos.y;
102     request.pos.y = 0;
103   }
104   request.request_data = surfacepartrequest;
105
106   drawingrequests.push_back(request);
107 }
108
109 void
110 DrawingContext::draw_text(const Font* font, const std::string& text,
111     const Vector& position, FontAlignment alignment, int layer,
112     uint32_t drawing_effect)
113 {
114   DrawingRequest request;
115
116   request.type = TEXT;
117   request.pos = transform.apply(position);
118   request.layer = layer;
119   request.drawing_effect = transform.drawing_effect | drawing_effect;
120   request.zoom = transform.zoom;
121   request.alpha = transform.alpha;
122
123   TextRequest* textrequest = new TextRequest;
124   textrequest->font = font;
125   textrequest->text = text;
126   textrequest->alignment = alignment;
127   request.request_data = textrequest;
128
129   drawingrequests.push_back(request);
130 }
131
132 void
133 DrawingContext::draw_center_text(const Font* font, const std::string& text,
134     const Vector& position, int layer, uint32_t drawing_effect)
135 {
136   draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
137       CENTER_ALLIGN, layer, drawing_effect);
138 }
139
140 void
141 DrawingContext::draw_gradient(Color top, Color bottom, int layer)
142 {
143   DrawingRequest request;
144
145   request.type = GRADIENT;
146   request.pos = Vector(0,0);
147   request.layer = layer;
148
149   request.drawing_effect = transform.drawing_effect;
150   request.zoom = transform.zoom;
151   request.alpha = transform.alpha;
152
153   GradientRequest* gradientrequest = new GradientRequest;
154   gradientrequest->top = top;
155   gradientrequest->bottom = bottom;
156   request.request_data = gradientrequest;
157
158   drawingrequests.push_back(request);
159 }
160
161 void
162 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
163         Color color, int layer)
164 {
165   DrawingRequest request;
166
167   request.type = FILLRECT;
168   request.pos = transform.apply(topleft);
169   request.layer = layer;
170
171   request.drawing_effect = transform.drawing_effect;
172   request.zoom = transform.zoom;
173   request.alpha = transform.alpha;                    
174
175   FillRectRequest* fillrectrequest = new FillRectRequest;
176   fillrectrequest->size = size;
177   fillrectrequest->color = color;
178   request.request_data = fillrectrequest;
179
180   drawingrequests.push_back(request);
181 }
182
183 void
184 DrawingContext::draw_surface_part(DrawingRequest& request)
185 {
186   SurfacePartRequest* surfacepartrequest
187     = (SurfacePartRequest*) request.request_data;
188
189   surfacepartrequest->surface->impl->draw_part(
190       surfacepartrequest->source.x, surfacepartrequest->source.y,
191       request.pos.x, request.pos.y,
192       surfacepartrequest->size.x, surfacepartrequest->size.y, request.alpha,
193       request.drawing_effect);
194
195   delete surfacepartrequest;
196 }
197
198 void
199 DrawingContext::draw_gradient(DrawingRequest& request)
200 {
201   GradientRequest* gradientrequest = (GradientRequest*) request.request_data;
202   const Color& top = gradientrequest->top;
203   const Color& bottom = gradientrequest->bottom;
204   
205 #ifndef NOOPENGL
206   if(config->use_gl)
207     {
208       glBegin(GL_QUADS);
209       glColor3ub(top.red, top.green, top.blue);
210       glVertex2f(0, 0);
211       glVertex2f(SCREEN_WIDTH, 0);
212       glColor3ub(bottom.red, bottom.green, bottom.blue);
213       glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
214       glVertex2f(0, SCREEN_HEIGHT);
215       glEnd();
216     }
217   else
218   {
219 #endif
220     if(&top == &bottom)
221       {
222       fillrect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, top.red, top.green, top.blue);
223       }
224     else
225       {
226       float redstep = (float(bottom.red)-float(top.red)) / float(SCREEN_HEIGHT);
227       float greenstep = (float(bottom.green)-float(top.green)) / float(SCREEN_HEIGHT);
228       float bluestep = (float(bottom.blue) - float(top.blue)) / float(SCREEN_HEIGHT);
229
230       for(float y = 0; y < SCREEN_HEIGHT; y += 2)
231         fillrect(0, (int)y, SCREEN_WIDTH, 2,
232             int(float(top.red) + redstep * y),
233             int(float(top.green) + greenstep * y),
234             int(float(top.blue) + bluestep * y), 255);
235       }
236 #ifndef NOOPENGL
237
238     }
239 #endif
240
241   delete gradientrequest;
242 }
243
244 void
245 DrawingContext::draw_text(DrawingRequest& request)
246 {
247   TextRequest* textrequest = (TextRequest*) request.request_data;
248
249   textrequest->font->draw(textrequest->text, request.pos,
250       textrequest->alignment, request.drawing_effect, request.alpha);
251
252   delete textrequest;
253 }
254
255 void
256 DrawingContext::draw_filled_rect(DrawingRequest& request)
257 {
258   FillRectRequest* fillrectrequest = (FillRectRequest*) request.request_data;
259
260   float x = request.pos.x;
261   float y = request.pos.y;
262   float w = fillrectrequest->size.x;
263   float h = fillrectrequest->size.y;
264
265 #ifndef NOOPENGL
266   if(config->use_gl)
267     {
268       glEnable(GL_BLEND);
269       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
270       glColor4ub(fillrectrequest->color.red, fillrectrequest->color.green,
271           fillrectrequest->color.blue, fillrectrequest->color.alpha);
272
273       glBegin(GL_POLYGON);
274       glVertex2f(x, y);
275       glVertex2f(x+w, y);
276       glVertex2f(x+w, y+h);
277       glVertex2f(x, y+h);
278       glEnd();
279       glDisable(GL_BLEND);
280     }
281   else
282     {
283 #endif
284       SDL_Rect src, rect;
285       SDL_Surface *temp = NULL;
286                                                                                 
287       rect.x = (int)x;
288       rect.y = (int)y;
289       rect.w = (int)w;
290       rect.h = (int)h;
291                                                                                 
292       if(fillrectrequest->color.alpha != 255)
293         {
294           temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel,
295                                       screen->format->Rmask,
296                                       screen->format->Gmask,
297                                       screen->format->Bmask,
298                                       screen->format->Amask);
299                                                                                 
300                                                                                 
301           src.x = 0;
302           src.y = 0;
303           src.w = rect.w;
304           src.h = rect.h;
305                                                                                 
306           SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, 
307                 fillrectrequest->color.red, fillrectrequest->color.green,
308                 fillrectrequest->color.blue));
309                                                                                 
310           SDL_SetAlpha(temp, SDL_SRCALPHA, fillrectrequest->color.alpha);
311                                                                                 
312           SDL_BlitSurface(temp,0,screen,&rect);
313                                                                                 
314           SDL_FreeSurface(temp);
315         }
316       else
317         SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 
318               fillrectrequest->color.red, fillrectrequest->color.green,
319               fillrectrequest->color.blue));
320                                                                                 
321 #ifndef NOOPENGL
322                                                                                 
323     }
324 #endif
325
326   delete fillrectrequest;
327 }
328
329 void
330 DrawingContext::do_drawing()
331 {
332 #ifdef DEBUG
333   assert(transformstack.empty());
334 #endif
335   transformstack.clear();
336     
337   std::stable_sort(drawingrequests.begin(), drawingrequests.end());
338
339   for(DrawingRequests::iterator i = drawingrequests.begin();
340       i != drawingrequests.end(); ++i) {
341     switch(i->type) {
342       case SURFACE:
343       {
344         const Surface* surface = (const Surface*) i->request_data;
345
346         if(i->zoom != 1.0)
347           surface->impl->draw_stretched(i->pos.x * i->zoom, i->pos.y * i->zoom,
348                          (int)(surface->w * i->zoom), (int)(surface->h * i->zoom),
349                          i->alpha, i->drawing_effect);
350         else
351           surface->impl->draw(i->pos.x, i->pos.y, i->alpha, i->drawing_effect);
352         break;
353       }
354       case SURFACE_PART:
355         draw_surface_part(*i);
356         break;
357       case GRADIENT:
358         draw_gradient(*i);
359         break;
360       case TEXT:
361         draw_text(*i);
362         break;
363       case FILLRECT:
364         draw_filled_rect(*i);
365         break;
366     }
367   }
368
369   // update screen
370   if(config->use_gl)
371     SDL_GL_SwapBuffers();
372   else
373     SDL_Flip(screen);
374
375   drawingrequests.clear();
376 }
377
378 void
379 DrawingContext::push_transform()
380 {
381   transformstack.push_back(transform);
382 }
383
384 void
385 DrawingContext::pop_transform()
386 {
387   assert(!transformstack.empty());
388
389   transform = transformstack.back();
390   transformstack.pop_back();
391 }
392
393 void
394 DrawingContext::set_drawing_effect(int effect)
395 {
396   transform.drawing_effect = effect;
397 }
398
399 void
400 DrawingContext::set_zooming(float zoom)
401 {
402   transform.zoom = zoom;
403 }
404
405 void
406 DrawingContext::set_alpha(int alpha)
407 {
408   transform.alpha = alpha;
409 }