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