2 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
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.
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.
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/>.
17 #include "video/drawing_context.hpp"
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"
34 DrawingContext::DrawingContext(Renderer& renderer, Lightmap& lightmap) :
44 ambient_color(1.0f, 1.0f, 1.0f, 1.0f),
48 screenshot_requested(false)
50 requests = &drawing_requests;
54 DrawingContext::~DrawingContext()
56 clear_drawing_requests(lightmap_requests);
57 clear_drawing_requests(drawing_requests);
59 obstack_free(&obst, NULL);
63 DrawingContext::clear_drawing_requests(DrawingRequests& requests)
65 for(auto& request : requests)
67 if (request->request_data)
69 request->request_data->~DrawingRequestData();
71 request->~DrawingRequest();
77 DrawingContext::draw_surface(SurfacePtr surface, const Vector& position,
78 float angle, const Color& color, const Blend& blend,
83 DrawingRequest* request = new(obst) DrawingRequest();
85 request->target = target;
86 request->type = SURFACE;
87 request->pos = transform.apply(position);
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)
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;
101 SurfaceRequest* surfacerequest = new(obst) SurfaceRequest();
102 surfacerequest->surface = surface.get();
103 request->request_data = surfacerequest;
105 requests->push_back(request);
109 DrawingContext::draw_surface(SurfacePtr surface, const Vector& position,
112 draw_surface(surface, position, 0.0f, Color(1.0f, 1.0f, 1.0f), Blend(), layer);
116 DrawingContext::draw_surface_part(SurfacePtr surface, const Vector& source,
117 const Vector& size, const Vector& dest, int layer)
119 assert(surface != 0);
121 DrawingRequest* request = new(obst) DrawingRequest();
123 request->target = target;
124 request->type = SURFACE_PART;
125 request->pos = transform.apply(dest);
126 request->layer = layer;
127 request->drawing_effect = transform.drawing_effect;
128 request->alpha = transform.alpha;
130 SurfacePartRequest* surfacepartrequest = new(obst) SurfacePartRequest();
131 surfacepartrequest->size = size;
132 surfacepartrequest->source = source;
133 surfacepartrequest->surface = surface.get();
135 // clip on screen borders
136 if(request->pos.x < 0) {
137 surfacepartrequest->size.x += request->pos.x;
138 if(surfacepartrequest->size.x <= 0)
140 surfacepartrequest->source.x -= request->pos.x;
143 if(request->pos.y < 0) {
144 surfacepartrequest->size.y += request->pos.y;
145 if(surfacepartrequest->size.y <= 0)
147 surfacepartrequest->source.y -= request->pos.y;
150 request->request_data = surfacepartrequest;
152 requests->push_back(request);
156 DrawingContext::draw_text(FontPtr font, const std::string& text,
157 const Vector& position, FontAlignment alignment, int layer, Color color)
159 DrawingRequest* request = new(obst) DrawingRequest();
161 request->target = target;
162 request->type = TEXT;
163 request->pos = transform.apply(position);
164 request->layer = layer;
165 request->drawing_effect = transform.drawing_effect;
166 request->alpha = transform.alpha;
167 request->color = color;
169 TextRequest* textrequest = new(obst) TextRequest();
170 textrequest->font = font.get();
171 textrequest->text = text;
172 textrequest->alignment = alignment;
173 request->request_data = textrequest;
175 requests->push_back(request);
179 DrawingContext::draw_center_text(FontPtr font, const std::string& text,
180 const Vector& position, int layer, Color color)
182 draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
183 ALIGN_CENTER, layer, color);
187 DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
189 DrawingRequest* request = new(obst) DrawingRequest();
191 request->target = target;
192 request->type = GRADIENT;
193 request->pos = Vector(0,0);
194 request->layer = layer;
196 request->drawing_effect = transform.drawing_effect;
197 request->alpha = transform.alpha;
199 GradientRequest* gradientrequest = new(obst) GradientRequest();
200 gradientrequest->top = top;
201 gradientrequest->bottom = bottom;
202 request->request_data = gradientrequest;
204 requests->push_back(request);
208 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
209 const Color& color, int layer)
211 DrawingRequest* request = new(obst) DrawingRequest();
213 request->target = target;
214 request->type = FILLRECT;
215 request->pos = transform.apply(topleft);
216 request->layer = layer;
218 request->drawing_effect = transform.drawing_effect;
219 request->alpha = transform.alpha;
221 FillRectRequest* fillrectrequest = new(obst) FillRectRequest();
222 fillrectrequest->size = size;
223 fillrectrequest->color = color;
224 fillrectrequest->color.alpha = color.alpha * transform.alpha;
225 fillrectrequest->radius = 0.0f;
226 request->request_data = fillrectrequest;
228 requests->push_back(request);
232 DrawingContext::draw_filled_rect(const Rectf& rect, const Color& color,
235 draw_filled_rect(rect, color, 0.0f, layer);
239 DrawingContext::draw_filled_rect(const Rectf& rect, const Color& color, float radius, int layer)
241 DrawingRequest* request = new(obst) DrawingRequest();
243 request->target = target;
244 request->type = FILLRECT;
245 request->pos = transform.apply(rect.p1);
246 request->layer = layer;
248 request->drawing_effect = transform.drawing_effect;
249 request->alpha = transform.alpha;
251 FillRectRequest* fillrectrequest = new(obst) FillRectRequest;
252 fillrectrequest->size = Vector(rect.get_width(), rect.get_height());
253 fillrectrequest->color = color;
254 fillrectrequest->color.alpha = color.alpha * transform.alpha;
255 fillrectrequest->radius = radius;
256 request->request_data = fillrectrequest;
258 requests->push_back(request);
262 DrawingContext::draw_inverse_ellipse(const Vector& pos, const Vector& size, const Color& color, int layer)
264 DrawingRequest* request = new(obst) DrawingRequest();
266 request->target = target;
267 request->type = INVERSEELLIPSE;
268 request->pos = transform.apply(pos);
269 request->layer = layer;
271 request->drawing_effect = transform.drawing_effect;
272 request->alpha = transform.alpha;
274 InverseEllipseRequest* ellipse = new(obst)InverseEllipseRequest;
276 ellipse->color = color;
277 ellipse->color.alpha = color.alpha * transform.alpha;
278 ellipse->size = size;
279 request->request_data = ellipse;
281 requests->push_back(request);
285 DrawingContext::get_cliprect() const
287 return Rectf(get_translation().x, get_translation().y,
288 get_translation().x + SCREEN_WIDTH,
289 get_translation().y + SCREEN_HEIGHT);
293 DrawingContext::get_light(const Vector& position, Color* color)
295 if( ambient_color.red == 1.0f && ambient_color.green == 1.0f
296 && ambient_color.blue == 1.0f ) {
297 *color = Color( 1.0f, 1.0f, 1.0f);
301 DrawingRequest* request = new(obst) DrawingRequest();
302 request->target = target;
303 request->type = GETLIGHT;
304 request->pos = transform.apply(position);
306 //There is no light offscreen.
307 if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
308 || request->pos.x < 0 || request->pos.y < 0){
309 *color = Color( 0, 0, 0);
313 request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
314 GetLightRequest* getlightrequest = new(obst) GetLightRequest();
315 getlightrequest->color_ptr = color;
316 request->request_data = getlightrequest;
317 lightmap_requests.push_back(request);
321 DrawingContext::do_drawing()
323 assert(transformstack.empty());
324 assert(target_stack.empty());
325 transformstack.clear();
326 target_stack.clear();
328 //Use Lightmap if ambient color is not white.
329 bool use_lightmap = ( ambient_color.red != 1.0f ||
330 ambient_color.green != 1.0f ||
331 ambient_color.blue != 1.0f );
333 // PART1: create lightmap
335 lightmap.start_draw(ambient_color);
336 handle_drawing_requests(lightmap_requests);
339 DrawingRequest* request = new(obst) DrawingRequest();
340 request->target = NORMAL;
341 request->type = DRAW_LIGHTMAP;
342 request->layer = LAYER_HUD - 1;
343 drawing_requests.push_back(request);
345 handle_drawing_requests(drawing_requests);
347 clear_drawing_requests(lightmap_requests);
348 clear_drawing_requests(drawing_requests);
350 obstack_free(&obst, NULL);
353 // if a screenshot was requested, take one
354 if (screenshot_requested) {
355 renderer.do_take_screenshot();
356 screenshot_requested = false;
362 class RequestPtrCompare
365 bool operator()(const DrawingRequest* r1, const DrawingRequest* r2) const
372 DrawingContext::handle_drawing_requests(DrawingRequests& requests)
374 std::stable_sort(requests.begin(), requests.end(), RequestPtrCompare());
376 DrawingRequests::const_iterator i;
377 for(i = requests.begin(); i != requests.end(); ++i) {
378 const DrawingRequest& request = **i;
380 switch(request.target) {
382 switch(request.type) {
384 renderer.draw_surface(request);
387 renderer.draw_surface_part(request);
390 renderer.draw_gradient(request);
394 const TextRequest* textrequest = (TextRequest*) request.request_data;
395 textrequest->font->draw(&renderer, textrequest->text, request.pos,
396 textrequest->alignment, request.drawing_effect, request.color, request.alpha);
400 renderer.draw_filled_rect(request);
403 renderer.draw_inverse_ellipse(request);
409 lightmap.get_light(request);
414 switch(request.type) {
416 lightmap.draw_surface(request);
419 lightmap.draw_surface_part(request);
422 lightmap.draw_gradient(request);
426 const TextRequest* textrequest = (TextRequest*) request.request_data;
427 textrequest->font->draw(&renderer, textrequest->text, request.pos,
428 textrequest->alignment, request.drawing_effect, request.color, request.alpha);
432 lightmap.draw_filled_rect(request);
435 assert(!"InverseEllipse doesn't make sense on the lightmap");
441 lightmap.get_light(request);
450 DrawingContext::push_transform()
452 transformstack.push_back(transform);
456 DrawingContext::pop_transform()
458 assert(!transformstack.empty());
460 transform = transformstack.back();
461 transformstack.pop_back();
465 DrawingContext::set_drawing_effect(DrawingEffect effect)
467 transform.drawing_effect = effect;
471 DrawingContext::get_drawing_effect() const
473 return transform.drawing_effect;
477 DrawingContext::set_alpha(float alpha)
479 transform.alpha = alpha;
483 DrawingContext::get_alpha() const
485 return transform.alpha;
489 DrawingContext::push_target()
491 target_stack.push_back(target);
495 DrawingContext::pop_target()
497 set_target(target_stack.back());
498 target_stack.pop_back();
502 DrawingContext::set_target(Target target)
504 this->target = target;
505 if(target == LIGHTMAP) {
506 requests = &lightmap_requests;
508 assert(target == NORMAL);
509 requests = &drawing_requests;
514 DrawingContext::set_ambient_color( Color new_color )
516 ambient_color = new_color;
520 DrawingContext::take_screenshot()
522 screenshot_requested = true;