Removed pointless check for screen mode change, as we are creating the window for...
[supertux.git] / src / video / drawing_context.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "video/drawing_context.hpp"
18
19 #include <algorithm>
20 #include <config.h>
21
22 #include "math/sizef.hpp"
23 #include "supertux/gameconfig.hpp"
24 #include "supertux/globals.hpp"
25 #include "util/obstackpp.hpp"
26 #include "video/drawing_request.hpp"
27 #include "video/lightmap.hpp"
28 #include "video/renderer.hpp"
29 #include "video/surface.hpp"
30 #include "video/texture.hpp"
31 #include "video/texture_manager.hpp"
32 #include "video/video_systems.hpp"
33
34 DrawingContext::DrawingContext(Renderer& renderer, Lightmap& lightmap) :
35   renderer(renderer),
36   lightmap(lightmap),
37   transformstack(),
38   transform(),
39   blend_stack(),
40   blend_mode(),
41   drawing_requests(),
42   lightmap_requests(),
43   requests(),
44   ambient_color(1.0f, 1.0f, 1.0f, 1.0f),
45   target(NORMAL),
46   target_stack(),
47   obst(),
48   screenshot_requested(false)
49 {
50   requests = &drawing_requests;
51   obstack_init(&obst);
52 }
53
54 DrawingContext::~DrawingContext()
55 {
56   obstack_free(&obst, NULL);
57 }
58
59 void
60 DrawingContext::draw_surface(SurfacePtr surface, const Vector& position,
61                              float angle, const Color& color, const Blend& blend,
62                              int layer)
63 {
64   assert(surface != 0);
65
66   DrawingRequest* request = new(obst) DrawingRequest();
67
68   request->target = target;
69   request->type = SURFACE;
70   request->pos = transform.apply(position);
71
72   if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
73      || request->pos.x + surface->get_width() < 0
74      || request->pos.y + surface->get_height() < 0)
75     return;
76
77   request->layer = layer;
78   request->drawing_effect = transform.drawing_effect;
79   request->alpha = transform.alpha;
80   request->angle = angle;
81   request->color = color;
82   request->blend = blend;
83
84   request->request_data = surface.get();
85
86   requests->push_back(request);
87 }
88
89 void
90 DrawingContext::draw_surface(SurfacePtr surface, const Vector& position,
91                              int layer)
92 {
93   draw_surface(surface, position, 0.0f, Color(1.0f, 1.0f, 1.0f), Blend(), layer);
94 }
95
96 void
97 DrawingContext::draw_surface_part(SurfacePtr surface, const Vector& source,
98                                   const Vector& size, const Vector& dest, int layer)
99 {
100   assert(surface != 0);
101
102   DrawingRequest* request = new(obst) DrawingRequest();
103
104   request->target = target;
105   request->type = SURFACE_PART;
106   request->pos = transform.apply(dest);
107   request->layer = layer;
108   request->drawing_effect = transform.drawing_effect;
109   request->alpha = transform.alpha;
110
111   SurfacePartRequest* surfacepartrequest = new(obst) SurfacePartRequest();
112   surfacepartrequest->size = size;
113   surfacepartrequest->source = source;
114   surfacepartrequest->surface = surface.get();
115
116   // clip on screen borders
117   if(request->pos.x < 0) {
118     surfacepartrequest->size.x += request->pos.x;
119     if(surfacepartrequest->size.x <= 0)
120       return;
121     surfacepartrequest->source.x -= request->pos.x;
122     request->pos.x = 0;
123   }
124   if(request->pos.y < 0) {
125     surfacepartrequest->size.y += request->pos.y;
126     if(surfacepartrequest->size.y <= 0)
127       return;
128     surfacepartrequest->source.y -= request->pos.y;
129     request->pos.y = 0;
130   }
131   request->request_data = surfacepartrequest;
132
133   requests->push_back(request);
134 }
135
136 void
137 DrawingContext::draw_text(FontPtr font, const std::string& text,
138                           const Vector& position, FontAlignment alignment, int layer, Color color)
139 {
140   DrawingRequest* request = new(obst) DrawingRequest();
141
142   request->target = target;
143   request->type = TEXT;
144   request->pos = transform.apply(position);
145   request->layer = layer;
146   request->drawing_effect = transform.drawing_effect;
147   request->alpha = transform.alpha;
148   request->color = color;
149
150   TextRequest* textrequest = new(obst) TextRequest();
151   textrequest->font = font.get();
152   textrequest->text = text;
153   textrequest->alignment = alignment;
154   request->request_data = textrequest;
155
156   requests->push_back(request);
157 }
158
159 void
160 DrawingContext::draw_center_text(FontPtr font, const std::string& text,
161                                  const Vector& position, int layer, Color color)
162 {
163   draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
164             ALIGN_CENTER, layer, color);
165 }
166
167 void
168 DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
169 {
170   DrawingRequest* request = new(obst) DrawingRequest();
171
172   request->target = target;
173   request->type = GRADIENT;
174   request->pos = Vector(0,0);
175   request->layer = layer;
176
177   request->drawing_effect = transform.drawing_effect;
178   request->alpha = transform.alpha;
179
180   GradientRequest* gradientrequest = new(obst) GradientRequest();
181   gradientrequest->top = top;
182   gradientrequest->bottom = bottom;
183   request->request_data = gradientrequest;
184
185   requests->push_back(request);
186 }
187
188 void
189 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
190                                  const Color& color, int layer)
191 {
192   DrawingRequest* request = new(obst) DrawingRequest();
193
194   request->target = target;
195   request->type = FILLRECT;
196   request->pos = transform.apply(topleft);
197   request->layer = layer;
198
199   request->drawing_effect = transform.drawing_effect;
200   request->alpha = transform.alpha;
201
202   FillRectRequest* fillrectrequest = new(obst) FillRectRequest();
203   fillrectrequest->size = size;
204   fillrectrequest->color = color;
205   fillrectrequest->color.alpha = color.alpha * transform.alpha;
206   fillrectrequest->radius = 0.0f;
207   request->request_data = fillrectrequest;
208
209   requests->push_back(request);
210 }
211
212 void
213 DrawingContext::draw_filled_rect(const Rectf& rect, const Color& color,
214                                  int layer)
215 {
216   draw_filled_rect(rect, color, 0.0f, layer);
217 }
218
219 void
220 DrawingContext::draw_filled_rect(const Rectf& rect, const Color& color, float radius, int layer)
221 {
222   DrawingRequest* request = new(obst) DrawingRequest();
223
224   request->target = target;
225   request->type   = FILLRECT;
226   request->pos    = transform.apply(rect.p1);
227   request->layer  = layer;
228
229   request->drawing_effect = transform.drawing_effect;
230   request->alpha = transform.alpha;
231
232   FillRectRequest* fillrectrequest = new(obst) FillRectRequest;
233   fillrectrequest->size = Vector(rect.get_width(), rect.get_height());
234   fillrectrequest->color = color;
235   fillrectrequest->color.alpha = color.alpha * transform.alpha;
236   fillrectrequest->radius = radius;
237   request->request_data = fillrectrequest;
238
239   requests->push_back(request); 
240 }
241
242 void
243 DrawingContext::draw_inverse_ellipse(const Vector& pos, const Vector& size, const Color& color, int layer)
244 {
245   DrawingRequest* request = new(obst) DrawingRequest();
246
247   request->target = target;
248   request->type   = INVERSEELLIPSE;
249   request->pos    = transform.apply(pos);
250   request->layer  = layer;
251
252   request->drawing_effect = transform.drawing_effect;
253   request->alpha = transform.alpha;
254
255   InverseEllipseRequest* ellipse = new(obst)InverseEllipseRequest;
256   
257   ellipse->color        = color;
258   ellipse->color.alpha  = color.alpha * transform.alpha;
259   ellipse->size         = size;
260   request->request_data = ellipse;
261
262   requests->push_back(request);     
263 }
264
265 Rectf
266 DrawingContext::get_cliprect() const
267 {
268   return Rectf(get_translation().x, get_translation().y,
269                get_translation().x + SCREEN_WIDTH, 
270                get_translation().y + SCREEN_HEIGHT);
271 }
272
273 void
274 DrawingContext::get_light(const Vector& position, Color* color)
275 {
276   if( ambient_color.red == 1.0f && ambient_color.green == 1.0f
277       && ambient_color.blue  == 1.0f ) {
278     *color = Color( 1.0f, 1.0f, 1.0f);
279     return;
280   }
281
282   DrawingRequest* request = new(obst) DrawingRequest();
283   request->target = target;
284   request->type = GETLIGHT;
285   request->pos = transform.apply(position);
286
287   //There is no light offscreen.
288   if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
289      || request->pos.x < 0 || request->pos.y < 0){
290     *color = Color( 0, 0, 0);
291     return;
292   }
293
294   request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
295   GetLightRequest* getlightrequest = new(obst) GetLightRequest();
296   getlightrequest->color_ptr = color;
297   request->request_data = getlightrequest;
298   lightmap_requests.push_back(request);
299 }
300
301 void
302 DrawingContext::do_drawing()
303 {
304   assert(transformstack.empty());
305   assert(target_stack.empty());
306   transformstack.clear();
307   target_stack.clear();
308
309   //Use Lightmap if ambient color is not white.
310   bool use_lightmap = ( ambient_color.red != 1.0f   || ambient_color.green != 1.0f ||
311                         ambient_color.blue  != 1.0f );
312
313   // PART1: create lightmap
314   if(use_lightmap) {
315     lightmap.start_draw(ambient_color);
316     handle_drawing_requests(lightmap_requests);
317     lightmap.end_draw();
318
319     DrawingRequest* request = new(obst) DrawingRequest();
320     request->target = NORMAL;
321     request->type = DRAW_LIGHTMAP;
322     request->layer = LAYER_HUD - 1;
323     drawing_requests.push_back(request);
324   }
325   lightmap_requests.clear();
326
327   handle_drawing_requests(drawing_requests);
328   drawing_requests.clear();
329   obstack_free(&obst, NULL);
330   obstack_init(&obst);
331
332   // if a screenshot was requested, take one
333   if (screenshot_requested) {
334     renderer.do_take_screenshot();
335     screenshot_requested = false;
336   }
337
338   renderer.flip();
339 }
340
341 class RequestPtrCompare
342 {
343 public:
344   bool operator()(const DrawingRequest* r1, const DrawingRequest* r2) const
345   {
346     return *r1 < *r2;
347   }
348 };
349
350 void
351 DrawingContext::handle_drawing_requests(DrawingRequests& requests)
352 {
353   std::stable_sort(requests.begin(), requests.end(), RequestPtrCompare());
354
355   DrawingRequests::const_iterator i;
356   for(i = requests.begin(); i != requests.end(); ++i) {
357     const DrawingRequest& request = **i;
358
359     switch(request.target) {
360       case NORMAL:
361         switch(request.type) {
362           case SURFACE:
363             renderer.draw_surface(request);
364             break;
365           case SURFACE_PART:
366             renderer.draw_surface_part(request);
367             break;
368           case GRADIENT:
369             renderer.draw_gradient(request);
370             break;
371           case TEXT:
372           {
373             const TextRequest* textrequest = (TextRequest*) request.request_data;
374             textrequest->font->draw(&renderer, textrequest->text, request.pos,
375                                     textrequest->alignment, request.drawing_effect, request.color, request.alpha);
376           }
377           break;
378           case FILLRECT:
379             renderer.draw_filled_rect(request);
380             break;
381           case INVERSEELLIPSE:
382             renderer.draw_inverse_ellipse(request);
383             break;
384           case DRAW_LIGHTMAP:
385             lightmap.do_draw();
386             break;
387           case GETLIGHT:
388             lightmap.get_light(request);
389             break;
390         }
391         break;
392       case LIGHTMAP:
393         switch(request.type) {
394           case SURFACE:
395             lightmap.draw_surface(request);
396             break;
397           case SURFACE_PART:
398             lightmap.draw_surface_part(request);
399             break;
400           case GRADIENT:
401             lightmap.draw_gradient(request);
402             break;
403           case TEXT:
404           {
405             const TextRequest* textrequest = (TextRequest*) request.request_data;
406             textrequest->font->draw(&renderer, textrequest->text, request.pos,
407                                     textrequest->alignment, request.drawing_effect, request.color, request.alpha);
408           }
409           break;
410           case FILLRECT:
411             lightmap.draw_filled_rect(request);
412             break;
413           case INVERSEELLIPSE:
414             assert(!"InverseEllipse doesn't make sense on the lightmap");
415             break;
416           case DRAW_LIGHTMAP:
417             lightmap.do_draw();
418             break;
419           case GETLIGHT:
420             lightmap.get_light(request);
421             break;
422         }
423         break;
424     }
425   }
426 }
427
428 void
429 DrawingContext::push_transform()
430 {
431   transformstack.push_back(transform);
432 }
433
434 void
435 DrawingContext::pop_transform()
436 {
437   assert(!transformstack.empty());
438
439   transform = transformstack.back();
440   transformstack.pop_back();
441 }
442
443 void
444 DrawingContext::set_drawing_effect(DrawingEffect effect)
445 {
446   transform.drawing_effect = effect;
447 }
448
449 DrawingEffect
450 DrawingContext::get_drawing_effect() const
451 {
452   return transform.drawing_effect;
453 }
454
455 void
456 DrawingContext::set_alpha(float alpha)
457 {
458   transform.alpha = alpha;
459 }
460
461 float
462 DrawingContext::get_alpha() const
463 {
464   return transform.alpha;
465 }
466
467 void
468 DrawingContext::push_target()
469 {
470   target_stack.push_back(target);
471 }
472
473 void
474 DrawingContext::pop_target()
475 {
476   set_target(target_stack.back());
477   target_stack.pop_back();
478 }
479
480 void
481 DrawingContext::set_target(Target target)
482 {
483   this->target = target;
484   if(target == LIGHTMAP) {
485     requests = &lightmap_requests;
486   } else {
487     assert(target == NORMAL);
488     requests = &drawing_requests;
489   }
490 }
491
492 void
493 DrawingContext::set_ambient_color( Color new_color )
494 {
495   ambient_color = new_color;
496 }
497
498 void 
499 DrawingContext::take_screenshot()
500 {
501   screenshot_requested = true;
502 }
503
504 /* EOF */