7e7aaff06929740b9c6f64b0987b484d94ad95bf
[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.h"
26 #include "surface.h"
27 #include "font.h"
28 #include "main.h"
29 #include "gameconfig.h"
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 #ifndef NOOPENGL
206   if(config->use_gl)
207     {
208       glBegin(GL_QUADS);
209       glColor3ub(top.red, top.green, top.blue);
210       glVertex2f(0, 0);
211       glVertex2f(SCREEN_WIDTH, 0);
212       glColor3ub(bottom.red, bottom.green, bottom.blue);
213       glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
214       glVertex2f(0, SCREEN_HEIGHT);
215       glEnd();
216     }
217   else
218   {
219 #endif
220     if(&top == &bottom)
221       {
222       fillrect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, top.red, top.green, top.blue);
223       }
224     else
225       {
226       float redstep = (float(bottom.red)-float(top.red)) / float(SCREEN_HEIGHT);
227       float greenstep = (float(bottom.green)-float(top.green)) / float(SCREEN_HEIGHT);
228       float bluestep = (float(bottom.blue) - float(top.blue)) / float(SCREEN_HEIGHT);
229
230       for(float y = 0; y < SCREEN_HEIGHT; y += 2)
231         fillrect(0, (int)y, SCREEN_WIDTH, 2,
232             int(float(top.red) + redstep * y),
233             int(float(top.green) + greenstep * y),
234             int(float(top.blue) + bluestep * y), 255);
235       }
236 #ifndef NOOPENGL
237
238     }
239 #endif
240
241   delete gradientrequest;
242 }
243
244 void
245 DrawingContext::draw_text(DrawingRequest& request)
246 {
247   TextRequest* textrequest = (TextRequest*) request.request_data;
248
249   textrequest->font->draw(textrequest->text, request.pos,
250       textrequest->alignment, request.drawing_effect, request.alpha);
251
252   delete textrequest;
253 }
254
255 void
256 DrawingContext::draw_filled_rect(DrawingRequest& request)
257 {
258   FillRectRequest* fillrectrequest = (FillRectRequest*) request.request_data;
259
260   float x = request.pos.x;
261   float y = request.pos.y;
262   float w = fillrectrequest->size.x;
263   float h = fillrectrequest->size.y;
264
265 #ifndef NOOPENGL
266   if(config->use_gl)
267     {
268       glEnable(GL_BLEND);
269       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
270       glColor4ub(fillrectrequest->color.red, fillrectrequest->color.green,
271           fillrectrequest->color.blue, fillrectrequest->color.alpha);
272
273       glBegin(GL_POLYGON);
274       glVertex2f(x, y);
275       glVertex2f(x+w, y);
276       glVertex2f(x+w, y+h);
277       glVertex2f(x, y+h);
278       glEnd();
279       glDisable(GL_BLEND);
280     }
281   else
282     {
283 #endif
284       SDL_Rect src, rect;
285       SDL_Surface *temp = NULL;
286                                                                                 
287       rect.x = (int)x;
288       rect.y = (int)y;
289       rect.w = (int)w;
290       rect.h = (int)h;
291                                                                                 
292       if(fillrectrequest->color.alpha != 255)
293         {
294           temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel,
295                                       screen->format->Rmask,
296                                       screen->format->Gmask,
297                                       screen->format->Bmask,
298                                       screen->format->Amask);
299                                                                                 
300                                                                                 
301           src.x = 0;
302           src.y = 0;
303           src.w = rect.w;
304           src.h = rect.h;
305                                                                                 
306           SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, 
307                 fillrectrequest->color.red, fillrectrequest->color.green,
308                 fillrectrequest->color.blue));
309                                                                                 
310           SDL_SetAlpha(temp, SDL_SRCALPHA, fillrectrequest->color.alpha);
311                                                                                 
312           SDL_BlitSurface(temp,0,screen,&rect);
313                                                                                 
314           SDL_FreeSurface(temp);
315         }
316       else
317         SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 
318               fillrectrequest->color.red, fillrectrequest->color.green,
319               fillrectrequest->color.blue));
320                                                                                 
321 #ifndef NOOPENGL
322                                                                                 
323     }
324 #endif
325
326   delete fillrectrequest;
327 }
328
329 void
330 DrawingContext::do_drawing()
331 {
332 #ifdef DEBUG
333   assert(transformstack.empty());
334 #endif
335   transformstack.clear();
336     
337   std::stable_sort(drawingrequests.begin(), drawingrequests.end());
338
339   for(DrawingRequests::iterator i = drawingrequests.begin();
340       i != drawingrequests.end(); ++i) {
341     switch(i->type) {
342       case SURFACE:
343       {
344         const Surface* surface = (const Surface*) i->request_data;
345
346         if(i->zoom != 1.0)
347           surface->impl->draw_stretched(i->pos.x * i->zoom, i->pos.y * i->zoom,
348                          (int)(surface->w * i->zoom), (int)(surface->h * i->zoom),
349                          i->alpha, i->drawing_effect);
350         else
351           surface->impl->draw(i->pos.x, i->pos.y, i->alpha, i->drawing_effect);
352         break;
353       }
354       case SURFACE_PART:
355         draw_surface_part(*i);
356         break;
357       case GRADIENT:
358         draw_gradient(*i);
359         break;
360       case TEXT:
361         draw_text(*i);
362         break;
363       case FILLRECT:
364         draw_filled_rect(*i);
365         break;
366     }
367   }
368
369   // update screen
370   if(config->use_gl)
371     SDL_GL_SwapBuffers();
372   else
373     SDL_Flip(screen);
374
375   drawingrequests.clear();
376 }
377
378 void
379 DrawingContext::push_transform()
380 {
381   transformstack.push_back(transform);
382 }
383
384 void
385 DrawingContext::pop_transform()
386 {
387   assert(!transformstack.empty());
388
389   transform = transformstack.back();
390   transformstack.pop_back();
391 }
392
393 void
394 DrawingContext::set_drawing_effect(uint32_t effect)
395 {
396   transform.drawing_effect = effect;
397 }
398
399 uint32_t
400 DrawingContext::get_drawing_effect() const
401 {
402   return transform.drawing_effect;
403 }
404
405 void
406 DrawingContext::set_zooming(float zoom)
407 {
408   transform.zoom = zoom;
409 }
410
411 void
412 DrawingContext::set_alpha(uint8_t alpha)
413 {
414   transform.alpha = alpha;
415 }
416
417 uint8_t
418 DrawingContext::get_alpha() const
419 {
420   return transform.alpha;
421 }