Made code -Wshadow clean, missed a bunch of issues in the last commit
[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   clear_drawing_requests(lightmap_requests);
57   clear_drawing_requests(drawing_requests);
58
59   obstack_free(&obst, NULL);
60 }
61
62 void
63 DrawingContext::clear_drawing_requests(DrawingRequests& requests_)
64 {
65   for(auto& request : requests_)
66   {
67     if (request->request_data)
68     {
69       request->request_data->~DrawingRequestData();
70     }
71     request->~DrawingRequest();
72   }
73   requests_.clear();
74 }
75
76 void
77 DrawingContext::draw_surface(SurfacePtr surface, const Vector& position,
78                              float angle, const Color& color, const Blend& blend,
79                              int layer)
80 {
81   assert(surface != 0);
82
83   DrawingRequest* request = new(obst) DrawingRequest();
84
85   request->target = target;
86   request->type = SURFACE;
87   request->pos = transform.apply(position);
88
89   if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
90      || request->pos.x + surface->get_width() < 0
91      || request->pos.y + surface->get_height() < 0)
92     return;
93
94   request->layer = layer;
95   request->drawing_effect = transform.drawing_effect;
96   request->alpha = transform.alpha;
97   request->angle = angle;
98   request->color = color;
99   request->blend = blend;
100
101   SurfaceRequest* surfacerequest = new(obst) SurfaceRequest();
102   surfacerequest->surface = surface.get();
103   request->request_data = surfacerequest;
104
105   requests->push_back(request);
106 }
107
108 void
109 DrawingContext::draw_surface(SurfacePtr surface, const Vector& position,
110                              int layer)
111 {
112   draw_surface(surface, position, 0.0f, Color(1.0f, 1.0f, 1.0f), Blend(), layer);
113 }
114
115 void
116 DrawingContext::draw_surface_part(SurfacePtr surface,
117                                   const Rectf& srcrect, const Rectf& dstrect,
118                                   int layer)
119 {
120   assert(surface != 0);
121
122   DrawingRequest* request = new(obst) DrawingRequest();
123
124   request->target = target;
125   request->type = SURFACE_PART;
126   request->pos = transform.apply(dstrect.p1);
127   request->layer = layer;
128   request->drawing_effect = transform.drawing_effect;
129   request->alpha = transform.alpha;
130
131   SurfacePartRequest* surfacepartrequest = new(obst) SurfacePartRequest();
132   surfacepartrequest->srcrect = srcrect;
133   surfacepartrequest->dstsize = dstrect.get_size();
134   surfacepartrequest->surface = surface.get();
135
136   request->request_data = surfacepartrequest;
137
138   requests->push_back(request);
139 }
140
141 void
142 DrawingContext::draw_text(FontPtr font, const std::string& text,
143                           const Vector& position, FontAlignment alignment, int layer, Color color)
144 {
145   DrawingRequest* request = new(obst) DrawingRequest();
146
147   request->target = target;
148   request->type = TEXT;
149   request->pos = transform.apply(position);
150   request->layer = layer;
151   request->drawing_effect = transform.drawing_effect;
152   request->alpha = transform.alpha;
153   request->color = color;
154
155   TextRequest* textrequest = new(obst) TextRequest();
156   textrequest->font = font.get();
157   textrequest->text = text;
158   textrequest->alignment = alignment;
159   request->request_data = textrequest;
160
161   requests->push_back(request);
162 }
163
164 void
165 DrawingContext::draw_center_text(FontPtr font, const std::string& text,
166                                  const Vector& position, int layer, Color color)
167 {
168   draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
169             ALIGN_CENTER, layer, color);
170 }
171
172 void
173 DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
174 {
175   DrawingRequest* request = new(obst) DrawingRequest();
176
177   request->target = target;
178   request->type = GRADIENT;
179   request->pos = Vector(0,0);
180   request->layer = layer;
181
182   request->drawing_effect = transform.drawing_effect;
183   request->alpha = transform.alpha;
184
185   GradientRequest* gradientrequest = new(obst) GradientRequest();
186   gradientrequest->top = top;
187   gradientrequest->bottom = bottom;
188   request->request_data = gradientrequest;
189
190   requests->push_back(request);
191 }
192
193 void
194 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
195                                  const Color& color, int layer)
196 {
197   DrawingRequest* request = new(obst) DrawingRequest();
198
199   request->target = target;
200   request->type = FILLRECT;
201   request->pos = transform.apply(topleft);
202   request->layer = layer;
203
204   request->drawing_effect = transform.drawing_effect;
205   request->alpha = transform.alpha;
206
207   FillRectRequest* fillrectrequest = new(obst) FillRectRequest();
208   fillrectrequest->size = size;
209   fillrectrequest->color = color;
210   fillrectrequest->color.alpha = color.alpha * transform.alpha;
211   fillrectrequest->radius = 0.0f;
212   request->request_data = fillrectrequest;
213
214   requests->push_back(request);
215 }
216
217 void
218 DrawingContext::draw_filled_rect(const Rectf& rect, const Color& color,
219                                  int layer)
220 {
221   draw_filled_rect(rect, color, 0.0f, layer);
222 }
223
224 void
225 DrawingContext::draw_filled_rect(const Rectf& rect, const Color& color, float radius, int layer)
226 {
227   DrawingRequest* request = new(obst) DrawingRequest();
228
229   request->target = target;
230   request->type   = FILLRECT;
231   request->pos    = transform.apply(rect.p1);
232   request->layer  = layer;
233
234   request->drawing_effect = transform.drawing_effect;
235   request->alpha = transform.alpha;
236
237   FillRectRequest* fillrectrequest = new(obst) FillRectRequest;
238   fillrectrequest->size = Vector(rect.get_width(), rect.get_height());
239   fillrectrequest->color = color;
240   fillrectrequest->color.alpha = color.alpha * transform.alpha;
241   fillrectrequest->radius = radius;
242   request->request_data = fillrectrequest;
243
244   requests->push_back(request);
245 }
246
247 void
248 DrawingContext::draw_inverse_ellipse(const Vector& pos, const Vector& size, const Color& color, int layer)
249 {
250   DrawingRequest* request = new(obst) DrawingRequest();
251
252   request->target = target;
253   request->type   = INVERSEELLIPSE;
254   request->pos    = transform.apply(pos);
255   request->layer  = layer;
256
257   request->drawing_effect = transform.drawing_effect;
258   request->alpha = transform.alpha;
259
260   InverseEllipseRequest* ellipse = new(obst)InverseEllipseRequest;
261
262   ellipse->color        = color;
263   ellipse->color.alpha  = color.alpha * transform.alpha;
264   ellipse->size         = size;
265   request->request_data = ellipse;
266
267   requests->push_back(request);
268 }
269
270 Rectf
271 DrawingContext::get_cliprect() const
272 {
273   return Rectf(get_translation().x, get_translation().y,
274                get_translation().x + SCREEN_WIDTH,
275                get_translation().y + SCREEN_HEIGHT);
276 }
277
278 void
279 DrawingContext::get_light(const Vector& position, Color* color)
280 {
281   if( ambient_color.red == 1.0f && ambient_color.green == 1.0f
282       && ambient_color.blue  == 1.0f ) {
283     *color = Color( 1.0f, 1.0f, 1.0f);
284     return;
285   }
286
287   DrawingRequest* request = new(obst) DrawingRequest();
288   request->target = target;
289   request->type = GETLIGHT;
290   request->pos = transform.apply(position);
291
292   //There is no light offscreen.
293   if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
294      || request->pos.x < 0 || request->pos.y < 0){
295     *color = Color( 0, 0, 0);
296     return;
297   }
298
299   request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
300   GetLightRequest* getlightrequest = new(obst) GetLightRequest();
301   getlightrequest->color_ptr = color;
302   request->request_data = getlightrequest;
303   lightmap_requests.push_back(request);
304 }
305
306 void
307 DrawingContext::do_drawing()
308 {
309   assert(transformstack.empty());
310   assert(target_stack.empty());
311   transformstack.clear();
312   target_stack.clear();
313
314   //Use Lightmap if ambient color is not white.
315   bool use_lightmap = ( ambient_color.red != 1.0f ||
316                         ambient_color.green != 1.0f ||
317                         ambient_color.blue != 1.0f );
318
319   // PART1: create lightmap
320   if(use_lightmap) {
321     lightmap.start_draw(ambient_color);
322     handle_drawing_requests(lightmap_requests);
323     lightmap.end_draw();
324
325     DrawingRequest* request = new(obst) DrawingRequest();
326     request->target = NORMAL;
327     request->type = DRAW_LIGHTMAP;
328     request->layer = LAYER_HUD - 1;
329     drawing_requests.push_back(request);
330   }
331   renderer.start_draw();
332   handle_drawing_requests(drawing_requests);
333   renderer.end_draw();
334
335   clear_drawing_requests(lightmap_requests);
336   clear_drawing_requests(drawing_requests);
337
338   obstack_free(&obst, NULL);
339   obstack_init(&obst);
340
341   // if a screenshot was requested, take one
342   if (screenshot_requested) {
343     renderer.do_take_screenshot();
344     screenshot_requested = false;
345   }
346
347   renderer.flip();
348 }
349
350 class RequestPtrCompare
351 {
352 public:
353   bool operator()(const DrawingRequest* r1, const DrawingRequest* r2) const
354   {
355     return *r1 < *r2;
356   }
357 };
358
359 void
360 DrawingContext::handle_drawing_requests(DrawingRequests& requests_)
361 {
362   std::stable_sort(requests_.begin(), requests_.end(), RequestPtrCompare());
363
364   DrawingRequests::const_iterator i;
365   for(i = requests_.begin(); i != requests_.end(); ++i) {
366     const DrawingRequest& request = **i;
367
368     switch(request.target) {
369       case NORMAL:
370         switch(request.type) {
371           case SURFACE:
372             renderer.draw_surface(request);
373             break;
374           case SURFACE_PART:
375             renderer.draw_surface_part(request);
376             break;
377           case GRADIENT:
378             renderer.draw_gradient(request);
379             break;
380           case TEXT:
381           {
382             const TextRequest* textrequest = (TextRequest*) request.request_data;
383             textrequest->font->draw(&renderer, textrequest->text, request.pos,
384                                     textrequest->alignment, request.drawing_effect, request.color, request.alpha);
385           }
386           break;
387           case FILLRECT:
388             renderer.draw_filled_rect(request);
389             break;
390           case INVERSEELLIPSE:
391             renderer.draw_inverse_ellipse(request);
392             break;
393           case DRAW_LIGHTMAP:
394             lightmap.do_draw();
395             break;
396           case GETLIGHT:
397             lightmap.get_light(request);
398             break;
399         }
400         break;
401       case LIGHTMAP:
402         switch(request.type) {
403           case SURFACE:
404             lightmap.draw_surface(request);
405             break;
406           case SURFACE_PART:
407             lightmap.draw_surface_part(request);
408             break;
409           case GRADIENT:
410             lightmap.draw_gradient(request);
411             break;
412           case TEXT:
413           {
414             const TextRequest* textrequest = (TextRequest*) request.request_data;
415             textrequest->font->draw(&renderer, textrequest->text, request.pos,
416                                     textrequest->alignment, request.drawing_effect, request.color, request.alpha);
417           }
418           break;
419           case FILLRECT:
420             lightmap.draw_filled_rect(request);
421             break;
422           case INVERSEELLIPSE:
423             assert(!"InverseEllipse doesn't make sense on the lightmap");
424             break;
425           case DRAW_LIGHTMAP:
426             lightmap.do_draw();
427             break;
428           case GETLIGHT:
429             lightmap.get_light(request);
430             break;
431         }
432         break;
433     }
434   }
435 }
436
437 void
438 DrawingContext::push_transform()
439 {
440   transformstack.push_back(transform);
441 }
442
443 void
444 DrawingContext::pop_transform()
445 {
446   assert(!transformstack.empty());
447
448   transform = transformstack.back();
449   transformstack.pop_back();
450 }
451
452 void
453 DrawingContext::set_drawing_effect(DrawingEffect effect)
454 {
455   transform.drawing_effect = effect;
456 }
457
458 DrawingEffect
459 DrawingContext::get_drawing_effect() const
460 {
461   return transform.drawing_effect;
462 }
463
464 void
465 DrawingContext::set_alpha(float alpha)
466 {
467   transform.alpha = alpha;
468 }
469
470 float
471 DrawingContext::get_alpha() const
472 {
473   return transform.alpha;
474 }
475
476 void
477 DrawingContext::push_target()
478 {
479   target_stack.push_back(target);
480 }
481
482 void
483 DrawingContext::pop_target()
484 {
485   set_target(target_stack.back());
486   target_stack.pop_back();
487 }
488
489 void
490 DrawingContext::set_target(Target target_)
491 {
492   this->target = target_;
493   if(target_ == LIGHTMAP) {
494     requests = &lightmap_requests;
495   } else {
496     assert(target_ == NORMAL);
497     requests = &drawing_requests;
498   }
499 }
500
501 void
502 DrawingContext::set_ambient_color( Color new_color )
503 {
504   ambient_color = new_color;
505 }
506
507 void
508 DrawingContext::take_screenshot()
509 {
510   screenshot_requested = true;
511 }
512
513 /* EOF */