7c836952727f11c53411b2a3a4b441066b320b0a
[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.hpp"
26 #include "surface.hpp"
27 #include "font.hpp"
28 #include "main.hpp"
29 #include "gameconfig.hpp"
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)
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;
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)
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;
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 {
111   DrawingRequest request;
112
113   request.type = TEXT;
114   request.pos = transform.apply(position);
115   request.layer = layer;
116   request.drawing_effect = transform.drawing_effect;
117   request.zoom = transform.zoom;
118   request.alpha = transform.alpha;
119
120   TextRequest* textrequest = new TextRequest;
121   textrequest->font = font;
122   textrequest->text = text;
123   textrequest->alignment = alignment;
124   request.request_data = textrequest;
125
126   drawingrequests.push_back(request);
127 }
128
129 void
130 DrawingContext::draw_center_text(const Font* font, const std::string& text,
131     const Vector& position, int layer)
132 {
133   draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
134       CENTER_ALLIGN, layer);
135 }
136
137 void
138 DrawingContext::draw_gradient(Color top, Color bottom, int layer)
139 {
140   DrawingRequest request;
141
142   request.type = GRADIENT;
143   request.pos = Vector(0,0);
144   request.layer = layer;
145
146   request.drawing_effect = transform.drawing_effect;
147   request.zoom = transform.zoom;
148   request.alpha = transform.alpha;
149
150   GradientRequest* gradientrequest = new GradientRequest;
151   gradientrequest->top = top;
152   gradientrequest->bottom = bottom;
153   request.request_data = gradientrequest;
154
155   drawingrequests.push_back(request);
156 }
157
158 void
159 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
160         Color color, int layer)
161 {
162   DrawingRequest request;
163
164   request.type = FILLRECT;
165   request.pos = transform.apply(topleft);
166   request.layer = layer;
167
168   request.drawing_effect = transform.drawing_effect;
169   request.zoom = transform.zoom;
170   request.alpha = transform.alpha;                    
171
172   FillRectRequest* fillrectrequest = new FillRectRequest;
173   fillrectrequest->size = size;
174   fillrectrequest->color = color;
175   fillrectrequest->color.alpha
176       = (int) ((float) fillrectrequest->color.alpha 
177               * ((float) transform.alpha / 255.0));
178   request.request_data = fillrectrequest;
179
180   drawingrequests.push_back(request);
181 }
182
183 void
184 DrawingContext::draw_surface_part(DrawingRequest& request)
185 {
186   SurfacePartRequest* surfacepartrequest
187     = (SurfacePartRequest*) request.request_data;
188
189   surfacepartrequest->surface->impl->draw_part(
190       surfacepartrequest->source.x, surfacepartrequest->source.y,
191       request.pos.x, request.pos.y,
192       surfacepartrequest->size.x, surfacepartrequest->size.y, request.alpha,
193       request.drawing_effect);
194
195   delete surfacepartrequest;
196 }
197
198 void
199 DrawingContext::draw_gradient(DrawingRequest& request)
200 {
201   GradientRequest* gradientrequest = (GradientRequest*) request.request_data;
202   const Color& top = gradientrequest->top;
203   const Color& bottom = gradientrequest->bottom;
204   
205   glBegin(GL_QUADS);
206   glColor3ub(top.red, top.green, top.blue);
207   glVertex2f(0, 0);
208   glVertex2f(SCREEN_WIDTH, 0);
209   glColor3ub(bottom.red, bottom.green, bottom.blue);
210   glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
211   glVertex2f(0, SCREEN_HEIGHT);
212   glEnd();
213
214   delete gradientrequest;
215 }
216
217 void
218 DrawingContext::draw_text(DrawingRequest& request)
219 {
220   TextRequest* textrequest = (TextRequest*) request.request_data;
221
222   textrequest->font->draw(textrequest->text, request.pos,
223       textrequest->alignment, request.drawing_effect, request.alpha);
224
225   delete textrequest;
226 }
227
228 void
229 DrawingContext::draw_filled_rect(DrawingRequest& request)
230 {
231   FillRectRequest* fillrectrequest = (FillRectRequest*) request.request_data;
232
233   float x = request.pos.x;
234   float y = request.pos.y;
235   float w = fillrectrequest->size.x;
236   float h = fillrectrequest->size.y;
237
238   glEnable(GL_BLEND);
239   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
240   glColor4ub(fillrectrequest->color.red, fillrectrequest->color.green,
241              fillrectrequest->color.blue, fillrectrequest->color.alpha);
242   
243   glBegin(GL_POLYGON);
244   glVertex2f(x, y);
245   glVertex2f(x+w, y);
246   glVertex2f(x+w, y+h);
247   glVertex2f(x, y+h);
248   glEnd();
249   glDisable(GL_BLEND);
250
251   delete fillrectrequest;
252 }
253
254 void
255 DrawingContext::do_drawing()
256 {
257 #ifdef DEBUG
258   assert(transformstack.empty());
259 #endif
260   transformstack.clear();
261     
262   std::stable_sort(drawingrequests.begin(), drawingrequests.end());
263
264   for(DrawingRequests::iterator i = drawingrequests.begin();
265       i != drawingrequests.end(); ++i) {
266     switch(i->type) {
267       case SURFACE:
268       {
269         const Surface* surface = (const Surface*) i->request_data;
270
271         if(i->zoom != 1.0)
272           surface->impl->draw_stretched(i->pos.x * i->zoom, i->pos.y * i->zoom,
273                          (int)(surface->w * i->zoom), (int)(surface->h * i->zoom),
274                          i->alpha, i->drawing_effect);
275         else
276           surface->impl->draw(i->pos.x, i->pos.y, i->alpha, i->drawing_effect);
277         break;
278       }
279       case SURFACE_PART:
280         draw_surface_part(*i);
281         break;
282       case GRADIENT:
283         draw_gradient(*i);
284         break;
285       case TEXT:
286         draw_text(*i);
287         break;
288       case FILLRECT:
289         draw_filled_rect(*i);
290         break;
291     }
292   }
293
294   drawingrequests.clear();
295
296   // update screen
297   SDL_GL_SwapBuffers();
298 }
299
300 void
301 DrawingContext::push_transform()
302 {
303   transformstack.push_back(transform);
304 }
305
306 void
307 DrawingContext::pop_transform()
308 {
309   assert(!transformstack.empty());
310
311   transform = transformstack.back();
312   transformstack.pop_back();
313 }
314
315 void
316 DrawingContext::set_drawing_effect(uint32_t effect)
317 {
318   transform.drawing_effect = effect;
319 }
320
321 uint32_t
322 DrawingContext::get_drawing_effect() const
323 {
324   return transform.drawing_effect;
325 }
326
327 void
328 DrawingContext::set_zooming(float zoom)
329 {
330   transform.zoom = zoom;
331 }
332
333 void
334 DrawingContext::set_alpha(uint8_t alpha)
335 {
336   transform.alpha = alpha;
337 }
338
339 uint8_t
340 DrawingContext::get_alpha() const
341 {
342   return transform.alpha;
343 }