The BIG COMMIT(tm)
[supertux.git] / lib / video / drawing_context.cpp
1 //  $Id$
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 "../video/drawing_context.h"
26 #include "../video/surface.h"
27 #include "../app/globals.h"
28 #include "../video/font.h"
29
30 using namespace SuperTux;
31
32 DrawingContext::DrawingContext()
33 {
34   transform.draw_effect = NONE_EFFECT;
35   transform.zoom = 1;
36   transform.alpha = 255;
37 }
38
39 DrawingContext::~DrawingContext()
40 {
41 }
42
43 void
44 DrawingContext::draw_surface(const Surface* surface, const Vector& position,
45     int layer, Uint32 drawing_effect)
46 {
47   assert(surface != 0);
48   
49   DrawingRequest request;
50
51   request.type = SURFACE;
52   request.layer = layer;
53   request.request_data = const_cast<Surface*> (surface);
54   request.pos = transform.apply(position);
55
56   if(request.pos.x >= screen->w || request.pos.y >= screen->h
57       || request.pos.x + surface->w < 0 || request.pos.y + surface->h < 0)
58     return;
59   
60   request.drawing_effect = drawing_effect;
61   request.drawing_effect = transform.draw_effect | drawing_effect;
62   request.zoom = transform.zoom;
63   request.alpha = transform.alpha;
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, Uint32 drawing_effect)
71 {
72   assert(surface != 0);
73
74   DrawingRequest request;
75
76   request.type = SURFACE_PART;
77   request.layer = layer;
78   request.pos = transform.apply(dest);
79   request.drawing_effect = 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   request.request_data = surfacepartrequest;
87
88   drawingrequests.push_back(request);
89 }
90
91 void
92 DrawingContext::draw_text(Font* font, const std::string& text,
93     const Vector& position, int allignment, int layer,
94     Uint32 drawing_effect)
95 {
96   DrawingRequest request;
97
98   request.type = TEXT;
99   request.layer = layer;
100   request.pos = transform.apply(position);
101   request.drawing_effect = drawing_effect;
102   request.alpha = transform.alpha;
103
104   TextRequest* textrequest = new TextRequest;
105   textrequest->font = font;
106   textrequest->text = text;
107   textrequest->allignment = allignment;
108   request.request_data = textrequest;
109
110   drawingrequests.push_back(request);
111 }
112
113 void
114 DrawingContext::draw_center_text(Font* font, const std::string& text,
115     const Vector& position, int layer, Uint32 drawing_effect)
116 {
117 draw_text(font, text, Vector(position.x + screen->w/2, position.y),
118           CENTER_ALLIGN, layer, drawing_effect);
119 }
120
121 void
122 DrawingContext::draw_gradient(Color top, Color bottom, int layer)
123 {
124   DrawingRequest request;
125
126   request.type = GRADIENT;
127   request.layer = layer;
128   request.pos = Vector(0,0);
129
130   GradientRequest* gradientrequest = new GradientRequest;
131   gradientrequest->top = top;
132   gradientrequest->bottom = bottom;
133   request.request_data = gradientrequest;
134
135   drawingrequests.push_back(request);
136 }
137
138 void
139 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
140         Color color, int layer)
141 {
142   DrawingRequest request;
143
144   request.type = FILLRECT;
145   request.layer = layer;
146   request.pos = transform.apply(topleft);
147
148   FillRectRequest* fillrectrequest = new FillRectRequest;
149   fillrectrequest->size = size;
150   fillrectrequest->color = color;
151   request.request_data = fillrectrequest;
152
153   drawingrequests.push_back(request);
154 }
155
156 void
157 DrawingContext::draw_surface_part(DrawingRequest& request)
158 {
159   SurfacePartRequest* surfacepartrequest
160     = (SurfacePartRequest*) request.request_data;
161
162   surfacepartrequest->surface->impl->draw_part(
163       surfacepartrequest->source.x, surfacepartrequest->source.y,
164       request.pos.x, request.pos.y,
165       surfacepartrequest->size.x, surfacepartrequest->size.y, request.alpha,
166       request.drawing_effect);
167
168   delete surfacepartrequest;
169 }
170
171 void
172 DrawingContext::draw_gradient(DrawingRequest& request)
173 {
174   GradientRequest* gradientrequest = (GradientRequest*) request.request_data;
175   const Color& top = gradientrequest->top;
176   const Color& bottom = gradientrequest->bottom;
177   
178 #ifndef NOOPENGL
179   if(use_gl)
180     {
181       glBegin(GL_QUADS);
182       glColor3ub(top.red, top.green, top.blue);
183       glVertex2f(0, 0);
184       glVertex2f(screen->w, 0);
185       glColor3ub(bottom.red, bottom.green, bottom.blue);
186       glVertex2f(screen->w, screen->h);
187       glVertex2f(0, screen->h);
188       glEnd();
189     }
190   else
191   {
192 #endif
193     if(&top == &bottom)
194       {
195       fillrect(0, 0, screen->w, screen->h, top.red, top.green, top.blue);
196       }
197     else
198       {
199       float redstep = (float(bottom.red)-float(top.red)) / float(screen->h);
200       float greenstep = (float(bottom.green)-float(top.green)) / float(screen->h);
201       float bluestep = (float(bottom.blue) - float(top.blue)) / float(screen->h);
202
203       for(float y = 0; y < screen->h; y += 2)
204         fillrect(0, (int)y, screen->w, 2,
205             int(float(top.red) + redstep * y),
206             int(float(top.green) + greenstep * y),
207             int(float(top.blue) + bluestep * y), 255);
208       }
209 #ifndef NOOPENGL
210
211     }
212 #endif
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, textrequest->allignment, request.drawing_effect, request.alpha);
223
224   delete textrequest;
225 }
226
227 void
228 DrawingContext::draw_filled_rect(DrawingRequest& request)
229 {
230   FillRectRequest* fillrectrequest = (FillRectRequest*) request.request_data;
231
232   float x = request.pos.x;
233   float y = request.pos.y;
234   float w = fillrectrequest->size.x;
235   float h = fillrectrequest->size.y;
236
237 #ifndef NOOPENGL
238   if(use_gl)
239     {
240       glEnable(GL_BLEND);
241       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
242       glColor4ub(fillrectrequest->color.red, fillrectrequest->color.green,
243           fillrectrequest->color.blue, fillrectrequest->color.alpha);
244
245       glBegin(GL_POLYGON);
246       glVertex2f(x, y);
247       glVertex2f(x+w, y);
248       glVertex2f(x+w, y+h);
249       glVertex2f(x, y+h);
250       glEnd();
251       glDisable(GL_BLEND);
252     }
253   else
254     {
255 #endif
256       SDL_Rect src, rect;
257       SDL_Surface *temp = NULL;
258                                                                                 
259       rect.x = (int)x;
260       rect.y = (int)y;
261       rect.w = (int)w;
262       rect.h = (int)h;
263                                                                                 
264       if(fillrectrequest->color.alpha != 255)
265         {
266           temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel,
267                                       screen->format->Rmask,
268                                       screen->format->Gmask,
269                                       screen->format->Bmask,
270                                       screen->format->Amask);
271                                                                                 
272                                                                                 
273           src.x = 0;
274           src.y = 0;
275           src.w = rect.w;
276           src.h = rect.h;
277                                                                                 
278           SDL_FillRect(temp, &src, SDL_MapRGB(screen->format, 
279                 fillrectrequest->color.red, fillrectrequest->color.green,
280                 fillrectrequest->color.blue));
281                                                                                 
282           SDL_SetAlpha(temp, SDL_SRCALPHA, fillrectrequest->color.alpha);
283                                                                                 
284           SDL_BlitSurface(temp,0,screen,&rect);
285                                                                                 
286           SDL_FreeSurface(temp);
287         }
288       else
289         SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 
290               fillrectrequest->color.red, fillrectrequest->color.green,
291               fillrectrequest->color.blue));
292                                                                                 
293 #ifndef NOOPENGL
294                                                                                 
295     }
296 #endif
297
298   delete fillrectrequest;
299 }
300
301 void
302 DrawingContext::do_drawing()
303 {
304   std::stable_sort(drawingrequests.begin(), drawingrequests.end());
305
306   for(DrawingRequests::iterator i = drawingrequests.begin();
307       i != drawingrequests.end(); ++i) {
308     switch(i->type) {
309       case SURFACE:
310       {
311         const Surface* surface = (const Surface*) i->request_data;
312
313         if(i->zoom != 1.0)
314           surface->impl->draw_stretched(i->pos.x * i->zoom, i->pos.y * i->zoom,
315                          (int)(surface->w * i->zoom), (int)(surface->h * i->zoom),
316                          i->alpha, i->drawing_effect);
317         else
318           surface->impl->draw(i->pos.x, i->pos.y, i->alpha, i->drawing_effect);
319         break;
320       }
321       case SURFACE_PART:
322         draw_surface_part(*i);
323         break;
324       case GRADIENT:
325         draw_gradient(*i);
326         break;
327       case TEXT:
328         draw_text(*i);
329         break;
330       case FILLRECT:
331         draw_filled_rect(*i);
332         break;
333     }
334   }
335
336   // update screen
337   if(use_gl)
338     SDL_GL_SwapBuffers();
339   else
340     SDL_Flip(screen);
341
342   drawingrequests.clear();
343 }
344
345 void
346 DrawingContext::push_transform()
347 {
348   transformstack.push_back(transform);
349 }
350
351 void
352 DrawingContext::pop_transform()
353 {
354   assert(!transformstack.empty());
355
356   transform = transformstack.back();
357   transformstack.pop_back();
358 }
359
360 void
361 DrawingContext::set_drawing_effect(int effect)
362 {
363   transform.draw_effect = effect;
364 }
365
366 void
367 DrawingContext::set_zooming(float zoom)
368 {
369   transform.zoom = zoom;
370 }
371
372 void
373 DrawingContext::set_alpha(int alpha)
374 {
375   transform.alpha = alpha;
376 }