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