- More work on scripting interface
[supertux.git] / src / video / surface.cpp
1 //  $Id: surface.cpp 2175 2004-11-24 19:02:49Z sik0fewl $
2 //
3 //  SuperTux
4 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.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
19 //  02111-1307, USA.
20 #include <config.h>
21
22 #include <cassert>
23 #include <iostream>
24 #include <algorithm>
25 #include <stdexcept>
26 #include <sstream>
27
28 #include <SDL.h>
29 #include <SDL_image.h>
30
31 #include "gameconfig.h"
32 #include "video/surface.h"
33 #include "video/screen.h"
34
35 Surface::Surfaces Surface::surfaces;
36
37 extern SDL_Surface* screen;
38
39 SurfaceData::SurfaceData(SDL_Surface* temp, bool use_alpha_)
40     : type(SURFACE), surface(0), use_alpha(use_alpha_),
41       x(0), y(0), w(0), h(0)
42 {
43   // Copy the given surface and make sure that it is not stored in
44   // video memory
45   surface = SDL_CreateRGBSurface(temp->flags & (~SDL_HWSURFACE),
46                                  temp->w, temp->h,
47                                  temp->format->BitsPerPixel,
48                                  temp->format->Rmask,
49                                  temp->format->Gmask,
50                                  temp->format->Bmask,
51                                  temp->format->Amask);
52   if(!surface)
53     throw std::runtime_error("No memory left for surface");
54   
55   SDL_SetAlpha(temp,0,0);
56   SDL_BlitSurface(temp, NULL, surface, NULL);
57 }
58
59 SurfaceData::SurfaceData(const std::string& file_, bool use_alpha_)
60     : type(LOAD), surface(0), file(file_), use_alpha(use_alpha_)
61 {}
62
63 SurfaceData::SurfaceData(const std::string& file_, int x_, int y_,
64     int w_, int h_, bool use_alpha_)
65     : type(LOAD_PART), surface(0), file(file_), use_alpha(use_alpha_),
66     x(x_), y(y_), w(w_), h(h_)
67 {}
68
69 SurfaceData::SurfaceData(Color top_gradient_, Color bottom_gradient_,
70     int w_, int h_)
71     : type(GRADIENT), surface(0), use_alpha(false), w(w_), h(h_)
72 {
73   top_gradient = top_gradient_;
74   bottom_gradient = bottom_gradient_;
75 }
76
77
78 SurfaceData::~SurfaceData()
79 {
80   SDL_FreeSurface(surface);
81 }
82
83 SurfaceImpl*
84 SurfaceData::create()
85 {
86   if (config->use_gl)
87     return create_SurfaceOpenGL();
88   else
89     return create_SurfaceSDL();
90 }
91
92 SurfaceSDL*
93 SurfaceData::create_SurfaceSDL()
94 {
95   switch(type)
96   {
97   case LOAD:
98     return new SurfaceSDL(file, use_alpha);
99   case LOAD_PART:
100     return new SurfaceSDL(file, x, y, w, h, use_alpha);
101   case SURFACE:
102     return new SurfaceSDL(surface, use_alpha);
103   case GRADIENT:
104     return new SurfaceSDL(top_gradient, bottom_gradient, w, h);
105   }
106   assert(0);
107 }
108
109 SurfaceOpenGL*
110 SurfaceData::create_SurfaceOpenGL()
111 {
112   switch(type)
113   {
114     case LOAD:
115       return new SurfaceOpenGL(file, use_alpha);
116     case LOAD_PART:
117       return new SurfaceOpenGL(file, x, y, w, h, use_alpha);
118     case SURFACE:
119       return new SurfaceOpenGL(surface, use_alpha);
120     case GRADIENT:
121       return new SurfaceOpenGL(top_gradient, bottom_gradient, w, h);
122     default:
123       assert(false);
124   }
125 }
126
127 /* Quick utility function for texture creation */
128 static int power_of_two(int input)
129 {
130   int value = 1;
131
132   while ( value < input )
133   {
134     value <<= 1;
135   }
136   return value;
137 }
138
139 Surface::Surface(SDL_Surface* surf, bool use_alpha)
140     : data(surf, use_alpha), w(0), h(0)
141 {
142   impl = data.create();
143   if (impl)
144   {
145     w = impl->w;
146     h = impl->h;
147   }
148   surfaces.push_back(this);
149 }
150
151 Surface::Surface(const std::string& file, bool use_alpha)
152     : data(file, use_alpha), w(0), h(0)
153 {
154   impl = data.create();
155   if (impl)
156   {
157     w = impl->w;
158     h = impl->h;
159   }
160   surfaces.push_back(this);
161 }
162
163 Surface::Surface(const std::string& file, int x, int y, int w_, int h_, bool use_alpha)
164     : data(file, x, y, w_, h_, use_alpha), w(0), h(0)
165 {
166   impl = data.create();
167   if (impl)
168   {
169     w = impl->w;
170     h = impl->h;
171   }
172   surfaces.push_back(this);
173 }
174
175 Surface::Surface(Color top_background, Color bottom_background, int w_, int h_)
176     : data(top_background, bottom_background, w_, h_), w(0), h(0)
177 {
178   impl = data.create();
179   if (impl)
180   {
181     w = impl->w;
182     h = impl->h;
183   }
184   surfaces.push_back(this);
185 }
186
187 void
188 Surface::reload()
189 {
190   delete impl;
191   impl = data.create();
192   if (impl)
193   {
194     w = impl->w;
195     h = impl->h;
196     for(std::vector<SurfaceData::Filter>::iterator i =
197         data.applied_filters.begin(); i != data.applied_filters.end();
198         i++)
199       impl->apply_filter(i->type, i->color);
200   }
201 }
202
203 void Surface::apply_filter(int filter, Color color)
204 {
205   impl->apply_filter(filter, color);
206
207   SurfaceData::Filter apply_filter;
208   apply_filter.type = filter;
209   apply_filter.color = color;
210   data.applied_filters.push_back(apply_filter);
211 }
212
213 Surface::~Surface()
214 {
215 #ifdef DEBUG
216   bool found = false;
217   for(std::list<Surface*>::iterator i = surfaces.begin(); i != surfaces.end();
218       ++i)
219   {
220     if(*i == this)
221     {
222       found = true; break;
223     }
224   }
225   if(!found)
226     printf("Error: Surface freed twice!!!\n");
227 #endif
228   surfaces.remove(this);
229   delete impl;
230 }
231
232 void
233 Surface::reload_all()
234 {
235   for(Surfaces::iterator i = surfaces.begin(); i != surfaces.end(); ++i)
236   {
237     (*i)->reload();
238   }
239 }
240
241 void
242 Surface::debug_check()
243 {
244   for(Surfaces::iterator i = surfaces.begin(); i != surfaces.end(); ++i)
245   {
246     printf("Surface not freed: T:%d F:%s.\n", (*i)->data.type,
247            (*i)->data.file.c_str());
248   }
249 }
250
251 void
252 apply_filter_to_surface(SDL_Surface* surface, int filter, Color color)
253 {
254   if(filter == HORIZONTAL_FLIP_FILTER) {
255     SDL_Surface* sur_copy = sdl_surface_from_sdl_surface(surface, true);
256     SDL_BlitSurface(surface, NULL, sur_copy, NULL);
257     SDL_SetAlpha(sur_copy,0,0);
258
259     SDL_Rect src, dst;
260     src.y = dst.y = 0;
261     src.w = dst.w = 1;
262     src.h = dst.h = sur_copy->h;
263     for(int x = 0; x < sur_copy->w; x++)
264     {
265       src.x = x; dst.x = sur_copy->w-1 - x;
266       SDL_BlitSurface(sur_copy, &src, surface, &dst);
267     }
268
269     SDL_FreeSurface(sur_copy);
270   } else if(filter == MASK_FILTER) {
271     SDL_Surface* sur_copy = sdl_surface_from_sdl_surface(surface, true);
272
273     Uint8 r,g,b,a;
274
275     SDL_LockSurface(sur_copy);
276     for(int x = 0; x < sur_copy->w; x++)
277       for(int y = 0; y < sur_copy->h; y++) {
278         SDL_GetRGBA(getpixel(sur_copy,x,y), sur_copy->format, &r,&g,&b,&a);
279         if(a != 0) {
280           putpixel(sur_copy, x,y, color.map_rgba(sur_copy));
281         }
282       }
283     SDL_UnlockSurface(sur_copy);
284
285     SDL_BlitSurface(sur_copy, NULL, surface, NULL);
286     SDL_FreeSurface(sur_copy);
287   }
288 }
289
290 SDL_Surface*
291 sdl_surface_part_from_file(const std::string& file, int x, int y, int w, int h,  bool use_alpha)
292 {
293   SDL_Rect src;
294   SDL_Surface * sdl_surface;
295   SDL_Surface * temp;
296   SDL_Surface * conv;
297
298   temp = IMG_Load(file.c_str());
299
300   if (temp == NULL) {
301     std::stringstream msg;
302     msg << "Couldn't load '" << file << "': " << SDL_GetError();
303     throw std::runtime_error(msg.str());
304   }
305   
306   /* Set source rectangle for conv: */
307
308   src.x = x;
309   src.y = y;
310   src.w = w;
311   src.h = h;
312
313   conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, temp->format->BitsPerPixel,
314                               temp->format->Rmask,
315                               temp->format->Gmask,
316                               temp->format->Bmask,
317                               temp->format->Amask);
318
319   SDL_SetAlpha(temp,0,0);
320
321   SDL_BlitSurface(temp, &src, conv, NULL);
322   if(use_alpha == false && !config->use_gl)
323     sdl_surface = SDL_DisplayFormat(conv);
324   else
325     sdl_surface = SDL_DisplayFormatAlpha(conv);
326
327   if (sdl_surface == NULL) {
328     std::stringstream msg;
329     msg << "Can't convert file '" << file << "' to display format.";
330     throw std::runtime_error(msg.str());
331   }
332
333   if (use_alpha == false && !config->use_gl)
334     SDL_SetAlpha(sdl_surface, 0, 0);
335
336   SDL_FreeSurface(temp);
337   SDL_FreeSurface(conv);
338
339   return sdl_surface;
340 }
341
342 SDL_Surface*
343 sdl_surface_from_file(const std::string& file, bool use_alpha)
344 {
345   SDL_Surface* sdl_surface;
346   SDL_Surface* temp;
347
348   temp = IMG_Load(file.c_str());
349
350   if (temp == NULL) {
351     std::stringstream msg;
352     msg << "Couldn't load file '" << file << "': " << SDL_GetError();
353     throw std::runtime_error(msg.str());
354   }
355
356   if(use_alpha == false && !config->use_gl)
357     sdl_surface = SDL_DisplayFormat(temp);
358   else
359     sdl_surface = SDL_DisplayFormatAlpha(temp);
360
361   if (sdl_surface == NULL) {
362     std::stringstream msg;
363     msg << "Couldn't convert file '" << file << "' to display format";
364     throw std::runtime_error(msg.str());
365   }
366
367   if (use_alpha == false && !config->use_gl)
368     SDL_SetAlpha(sdl_surface, 0, 0);
369
370   SDL_FreeSurface(temp);
371
372   return sdl_surface;
373 }
374
375 SDL_Surface*
376 sdl_surface_from_sdl_surface(SDL_Surface* sdl_surf, bool use_alpha)
377 {
378   SDL_Surface* sdl_surface;
379 #if 0
380   Uint32 saved_flags;
381   Uint8  saved_alpha;
382
383   /* Save the alpha blending attributes */
384   saved_flags = sdl_surf->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
385   saved_alpha = sdl_surf->format->alpha;
386   if ( (saved_flags & SDL_SRCALPHA)
387        == SDL_SRCALPHA )
388   {
389     SDL_SetAlpha(sdl_surf, 0, 0);
390   }
391 #endif
392
393   if(use_alpha == false && !config->use_gl)
394     sdl_surface = SDL_DisplayFormat(sdl_surf);
395   else
396     sdl_surface = SDL_DisplayFormatAlpha(sdl_surf);
397
398 #if 0
399   /* Restore the alpha blending attributes */
400   if ( (saved_flags & SDL_SRCALPHA)
401        == SDL_SRCALPHA )
402   {
403     SDL_SetAlpha(sdl_surface, saved_flags, saved_alpha);
404   }
405 #endif
406
407   if (sdl_surface == NULL) {
408     std::stringstream msg;
409     msg << "Can't convert surface to display format.";
410     throw std::runtime_error(msg.str());
411   }
412
413   if (use_alpha == false && !config->use_gl)
414     SDL_SetAlpha(sdl_surface, 0, 0);
415
416   return sdl_surface;
417 }
418
419 SDL_Surface*
420 sdl_surface_from_gradient(Color top, Color bottom, int w, int h)
421 {
422   SDL_Surface* sdl_surface
423     = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
424                     screen->format->BitsPerPixel, screen->format->Rmask,
425                     screen->format->Gmask, screen->format->Bmask, 0);
426
427   if(sdl_surface == 0)
428     throw std::runtime_error("Can't create gradient surface");
429
430   if(top == bottom) {
431     SDL_FillRect(sdl_surface, NULL, SDL_MapRGB(sdl_surface->format,
432         top.red, top.green, top.blue));
433   } else {
434     float redstep = (float(bottom.red)-float(top.red)) / float(h);
435     float greenstep = (float(bottom.green)-float(top.green)) / float(h);
436     float bluestep = (float(bottom.blue) - float(top.blue)) / float(h);
437
438     SDL_Rect rect;
439     rect.x = 0;
440     rect.w = w;
441     rect.h = 1;
442     for(float y = 0; y < h; y++) {
443       rect.y = (int)y;
444       SDL_FillRect(sdl_surface, &rect, SDL_MapRGB(sdl_surface->format,
445             int(float(top.red) + redstep * y),
446             int(float(top.green) + greenstep * y),
447             int(float(top.blue) + bluestep * y)));
448     }
449   }
450
451   return sdl_surface;
452 }
453
454 //---------------------------------------------------------------------------
455
456 SurfaceImpl::SurfaceImpl()
457 {}
458
459 SurfaceImpl::~SurfaceImpl()
460 {
461   SDL_FreeSurface(sdl_surface);
462 }
463
464 SDL_Surface* SurfaceImpl::get_sdl_surface() const
465 {
466   return sdl_surface;
467 }
468
469 SurfaceOpenGL::SurfaceOpenGL(SDL_Surface* surf, bool use_alpha)
470 {
471   sdl_surface = sdl_surface_from_sdl_surface(surf, use_alpha);
472   create_gl(sdl_surface,&gl_texture);
473
474   w = sdl_surface->w;
475   h = sdl_surface->h;
476 }
477
478 SurfaceOpenGL::SurfaceOpenGL(const std::string& file, bool use_alpha)
479 {
480   sdl_surface = sdl_surface_from_file(file, use_alpha);
481   create_gl(sdl_surface,&gl_texture);
482
483   w = sdl_surface->w;
484   h = sdl_surface->h;
485 }
486
487 SurfaceOpenGL::SurfaceOpenGL(const std::string& file_, int x_, int y_,
488     int w_, int h_, bool use_alpha_)
489 {
490   sdl_surface = sdl_surface_part_from_file(file_,x_,y_,w_,h_,use_alpha_);
491   
492   create_gl(sdl_surface, &gl_texture);
493   w = sdl_surface->w;
494   h = sdl_surface->h;  
495 }
496
497 SurfaceOpenGL::SurfaceOpenGL(Color top_gradient, Color bottom_gradient,
498     int _w, int _h)
499 {
500   sdl_surface = sdl_surface_from_gradient(top_gradient, bottom_gradient,_w,_h);
501   create_gl(sdl_surface, &gl_texture);
502   w = sdl_surface->w;
503   h = sdl_surface->h;  
504 }
505
506 SurfaceOpenGL::~SurfaceOpenGL()
507 {
508   glDeleteTextures(1, &gl_texture);
509 }
510
511 void
512 SurfaceOpenGL::create_gl(SDL_Surface * surf, GLuint * tex)
513 {
514   Uint32 saved_flags;
515   Uint8  saved_alpha;
516   int w, h;
517   SDL_Surface *conv;
518
519   w = power_of_two(surf->w);
520   h = power_of_two(surf->h),
521
522 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
523       conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel,
524                                   0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
525 #else
526       conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel,
527                                   0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
528 #endif
529
530   /* Save the alpha blending attributes */
531   saved_flags = surf->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
532   saved_alpha = surf->format->alpha;
533   if ( (saved_flags & SDL_SRCALPHA)
534        == SDL_SRCALPHA )
535   {
536     SDL_SetAlpha(surf, 0, 0);
537   }
538
539   SDL_BlitSurface(surf, 0, conv, 0);
540
541   /* Restore the alpha blending attributes */
542   if ( (saved_flags & SDL_SRCALPHA)
543        == SDL_SRCALPHA )
544   {
545     SDL_SetAlpha(surf, saved_flags, saved_alpha);
546   }
547
548   // We check all the pixels of the surface to figure out which
549   // internal format OpenGL should use for storing it, ie. if no alpha
550   // is present store in RGB instead of RGBA, this saves a few bytes
551   // of memory, but much more importantly it makes the game look
552   // *much* better in 16bit color mode
553   int internal_format = GL_RGB10_A2;
554   bool has_alpha = false;
555
556   unsigned char* buf = static_cast<unsigned char*>(conv->pixels);
557   for (int y = 0; y < surf->h; ++y)
558     for (int x = 0; x < surf->w; ++x)
559       {
560         if (buf[(conv->pitch*y + x*4) + 3] != 255)
561           {
562             has_alpha = true;
563             break;
564           }
565       }
566
567   if (!has_alpha)
568     {
569       internal_format = GL_RGB;
570     }
571
572   glGenTextures(1, &*tex);
573   glBindTexture(GL_TEXTURE_2D , *tex);
574   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
575   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
576   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
577   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
578   glPixelStorei(GL_UNPACK_ROW_LENGTH, conv->pitch / conv->format->BytesPerPixel);
579   glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, conv->pixels);
580   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
581
582   SDL_FreeSurface(conv);
583 }
584
585 int
586 SurfaceOpenGL::draw(float x, float y, Uint8 alpha, Uint32 effect)
587 {
588   float pw = power_of_two(w);
589   float ph = power_of_two(h);
590
591   if(effect & SEMI_TRANSPARENT)
592     alpha = 128;
593
594   glEnable(GL_TEXTURE_2D);
595   glEnable(GL_BLEND);
596   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
597
598   glColor4ub(alpha, alpha, alpha, alpha);
599
600   glBindTexture(GL_TEXTURE_2D, gl_texture);
601
602   glBegin(GL_QUADS);
603
604   if(effect & VERTICAL_FLIP & HORIZONTAL_FLIP)
605     {
606     glTexCoord2f(0, 0);
607     glVertex2f((float)w+x, (float)h+y);
608
609     glTexCoord2f((float)w / pw, 0);
610     glVertex2f(x, (float)h+y);
611
612     glTexCoord2f((float)w / pw, (float)h / ph);
613     glVertex2f(x, y);
614
615     glTexCoord2f(0, (float)h / ph);
616     glVertex2f((float)w+x, y);
617     }
618   else if(effect & VERTICAL_FLIP)
619     {
620     glTexCoord2f(0, 0);
621     glVertex2f(x, (float)h+y);
622
623     glTexCoord2f((float)w / pw, 0);
624     glVertex2f((float)w+x, (float)h+y);
625
626     glTexCoord2f((float)w / pw, (float)h / ph);
627     glVertex2f((float)w+x, y);
628     
629     glTexCoord2f(0, (float)h / ph);
630     glVertex2f(x, y);
631     }
632   else if(effect & HORIZONTAL_FLIP)
633     {
634     glTexCoord2f(0, 0);
635     glVertex2f((float)w+x, y);
636
637     glTexCoord2f((float)w / pw, 0);
638     glVertex2f(x, y);
639
640     glTexCoord2f((float)w / pw, (float)h / ph);
641     glVertex2f(x, (float)h+y);
642
643     glTexCoord2f(0, (float)h / ph);
644     glVertex2f((float)w+x, (float)h+y);
645     }
646   else
647     {
648     glTexCoord2f(0, 0);
649     glVertex2f(x, y);
650
651     glTexCoord2f((float)w / pw, 0);
652     glVertex2f((float)w+x, y);
653
654     glTexCoord2f((float)w / pw, (float)h / ph);
655     glVertex2f((float)w+x, (float)h+y);
656
657     glTexCoord2f(0, (float)h / ph);
658     glVertex2f(x, (float)h+y);
659     }
660   glEnd();
661
662   glDisable(GL_TEXTURE_2D);
663   glDisable(GL_BLEND);
664
665   return 0;
666 }
667
668 int
669 SurfaceOpenGL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, Uint32 effect)
670 {
671   float pw = power_of_two(int(this->w));
672   float ph = power_of_two(int(this->h));
673
674   if(effect & SEMI_TRANSPARENT)
675     alpha = 128;
676
677   glBindTexture(GL_TEXTURE_2D, gl_texture);
678
679   glEnable(GL_BLEND);
680   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
681
682   glColor4ub(alpha, alpha, alpha, alpha);
683
684   glEnable(GL_TEXTURE_2D);
685
686
687   glBegin(GL_QUADS);
688
689   if(effect & VERTICAL_FLIP & HORIZONTAL_FLIP)
690     {
691     glTexCoord2f(sx / pw, (float)(sy+h) / ph);
692     glVertex2f((float)w+x, (float)h+y);
693
694     glTexCoord2f((sx+w) / pw, (sy+h) / ph);
695     glVertex2f(x, (float)h+y);
696
697     glTexCoord2f((float)(sx + w) / pw, sy / ph);
698     glVertex2f(x, y);
699
700     glTexCoord2f(sx / pw, sy / ph);
701     glVertex2f((float)w+x, y);
702     }
703   else if(effect & VERTICAL_FLIP)
704     {
705     glTexCoord2f(sx / pw, sy / ph);
706     glVertex2f(x, y);
707
708     glTexCoord2f((float)(sx + w) / pw, sy / ph);
709     glVertex2f(w+x, y);
710
711     glTexCoord2f((sx+w) / pw, (sy+h) / ph);
712     glVertex2f(w +x, h+y);
713
714     glTexCoord2f(sx / pw, (float)(sy+h) / ph);
715     glVertex2f(x, h+y);
716     }
717   else if(effect & HORIZONTAL_FLIP)
718     {
719     glTexCoord2f(sx / pw, sy / ph);
720     glVertex2f((float)w+x, y);
721
722     glTexCoord2f((float)(sx + w) / pw, sy / ph);
723     glVertex2f(x, y);
724
725     glTexCoord2f((sx+w) / pw, (sy+h) / ph);
726     glVertex2f(x, (float)h+y);
727
728     glTexCoord2f(sx / pw, (float)(sy+h) / ph);
729     glVertex2f((float)w+x, (float)h+y);
730     }
731   else
732     {
733     glTexCoord2f(sx / pw, (float)(sy+h) / ph);
734     glVertex2f(x, h+y);
735
736     glTexCoord2f((sx+w) / pw, (sy+h) / ph);
737     glVertex2f(w +x, h+y);
738
739     glTexCoord2f((float)(sx + w) / pw, sy / ph);
740     glVertex2f(w+x, y);
741
742     glTexCoord2f(sx / pw, sy / ph);
743     glVertex2f(x, y);
744     }
745
746   glEnd();
747
748   glDisable(GL_TEXTURE_2D);
749   glDisable(GL_BLEND);
750
751   return 0;
752 }
753
754 int
755 SurfaceOpenGL::draw_stretched(float x, float y, int sw, int sh, Uint8 alpha, Uint32 effect)
756 {
757   float pw = power_of_two(sw);
758   float ph = power_of_two(sh);
759
760   if(effect & SEMI_TRANSPARENT)
761     alpha = 128;
762
763   glEnable(GL_TEXTURE_2D);
764   glEnable(GL_BLEND);
765   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
766
767   glColor4ub(alpha, alpha, alpha, alpha);
768
769   glBindTexture(GL_TEXTURE_2D, gl_texture);
770
771   glBegin(GL_QUADS);
772
773   if(effect & VERTICAL_FLIP & HORIZONTAL_FLIP)
774     {
775     glTexCoord2f(0, 0);
776     glVertex2f((float)sw+x, (float)sh+y);
777
778     glTexCoord2f((float)w / pw, 0);
779     glVertex2f(x, (float)sh+y);
780
781     glTexCoord2f((float)w / pw, (float)h / ph);
782     glVertex2f(x, y);
783
784     glTexCoord2f(0, (float)h / ph);
785     glVertex2f((float)sw+x, y);
786     }
787   else if(effect & VERTICAL_FLIP)
788     {
789     glTexCoord2f(0, 0);
790     glVertex2f(x, (float)sh+y);
791
792     glTexCoord2f((float)w / pw, 0);
793     glVertex2f((float)sw+x, (float)sh+y);
794
795     glTexCoord2f((float)w / pw, (float)h / ph);
796     glVertex2f((float)sw+x, y);
797     
798     glTexCoord2f(0, (float)h / ph);
799     glVertex2f(x, y);
800     }
801   else if(effect & HORIZONTAL_FLIP)
802     {
803     glTexCoord2f(0, 0);
804     glVertex2f((float)sw+x, y);
805
806     glTexCoord2f((float)w / pw, 0);
807     glVertex2f(x, y);
808
809     glTexCoord2f((float)w / pw, (float)h / ph);
810     glVertex2f(x, (float)sh+y);
811
812     glTexCoord2f(0, (float)h / ph);
813     glVertex2f((float)sw+x, (float)sh+y);
814     }
815   else
816     {
817     glTexCoord2f(0, 0);
818     glVertex2f(x, y);
819
820     glTexCoord2f((float)w / pw, 0);
821     glVertex2f((float)sw+x, y);
822
823     glTexCoord2f((float)w / pw, (float)h / ph);
824     glVertex2f((float)sw+x, (float)sh+y);
825
826     glTexCoord2f(0, (float)h / ph);
827     glVertex2f(x, (float)sh+y);
828     }
829   glEnd();
830
831   glDisable(GL_TEXTURE_2D);
832   glDisable(GL_BLEND);
833
834   return 0;
835 }
836
837 void
838 SurfaceOpenGL::apply_filter(int filter, Color color)
839 {
840   ::apply_filter_to_surface(sdl_surface, filter, color);
841   create_gl(sdl_surface,&gl_texture);
842
843   w = sdl_surface->w;
844   h = sdl_surface->h;
845 }
846
847 SurfaceSDL::SurfaceSDL(SDL_Surface* surf, bool use_alpha)
848 {
849   sdl_surface = sdl_surface_from_sdl_surface(surf, use_alpha);
850   w = sdl_surface->w;
851   h = sdl_surface->h;
852 }
853
854 SurfaceSDL::SurfaceSDL(const std::string& file, bool use_alpha)
855 {
856   sdl_surface = sdl_surface_from_file(file, use_alpha);
857   w = sdl_surface->w;
858   h = sdl_surface->h;
859 }
860
861 SurfaceSDL::SurfaceSDL(const std::string& file, int x, int y, int _w, int _h,
862     bool use_alpha)
863 {
864   sdl_surface = sdl_surface_part_from_file(file, x, y, _w, _h, use_alpha);
865   w = sdl_surface->w;
866   h = sdl_surface->h;  
867 }
868
869 SurfaceSDL::SurfaceSDL(Color top_gradient, Color bottom_gradient,
870     int _w, int _h)
871 {
872   sdl_surface = sdl_surface_from_gradient(top_gradient, bottom_gradient,_w,_h);
873   w = sdl_surface->w;
874   h = sdl_surface->h;  
875 }
876
877 int
878 SurfaceSDL::draw(float x, float y, Uint8 alpha, Uint32 effect)
879 {
880   SDL_Rect dest;
881
882   dest.x = (int)x;
883   dest.y = (int)y;
884   dest.w = w;
885   dest.h = h;
886
887   if(effect & SEMI_TRANSPARENT)
888     alpha = 128;
889
890   if(effect & VERTICAL_FLIP & HORIZONTAL_FLIP)
891     {
892     // FIXME: this hack is damn slow. Just keep it cause it isn't that used.
893     for(float sx = 0; sx < w; sx++)
894       for(float sy = 0; sy < h; sy++)
895         if(draw_part(sx, sy, x+(w-sx), y+(h-sy), 1, 1, alpha, NONE_EFFECT) == -2)
896           return -2;
897     return 0;
898     }
899   else if(effect & VERTICAL_FLIP)    // FIXME: feel free to replace this hack
900     {
901     for(float sy = 0; sy < h; sy++)
902       if(draw_part(0, sy, x, y+(h-sy), w, 1, alpha, NONE_EFFECT) == -2)
903         return -2;
904     return 0;
905     }
906   else if(effect & HORIZONTAL_FLIP)    // FIXME: feel free to replace this hack
907     {
908     for(float sx = 0; sx < w; sx++)
909       if(draw_part(sx, 0, x+(w-sx), y, 1, h, alpha, NONE_EFFECT) == -2)
910         return -2;
911     return 0;
912     }
913
914   if(alpha != 255)
915     {
916     /* Create a Surface, make it using colorkey, blit surface into temp, apply alpha
917       to temp sur, blit the temp into the screen */
918     /* Note: this has to be done, since SDL doesn't allow to set alpha to surfaces that
919       already have an alpha mask yet... */
920
921     SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags,
922                                     sdl_surface->w, sdl_surface->h, sdl_surface->format->BitsPerPixel,
923                                     sdl_surface->format->Rmask, sdl_surface->format->Gmask,
924                                     sdl_surface->format->Bmask,
925                                     0);
926     int colorkey = SDL_MapRGB(sdl_surface_copy->format, 255, 0, 255);
927     SDL_FillRect(sdl_surface_copy, NULL, colorkey);
928     SDL_SetColorKey(sdl_surface_copy, SDL_SRCCOLORKEY, colorkey);
929
930
931     SDL_BlitSurface(sdl_surface, NULL, sdl_surface_copy, NULL);
932     SDL_SetAlpha(sdl_surface_copy ,SDL_SRCALPHA,alpha);
933
934     int ret = SDL_BlitSurface(sdl_surface_copy, NULL, screen, &dest);
935
936     SDL_FreeSurface (sdl_surface_copy);
937     return ret;
938     }
939
940   int ret = SDL_BlitSurface(sdl_surface, NULL, screen, &dest);
941
942   return ret;
943 }
944
945 int
946 SurfaceSDL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, Uint32 effect)
947 {
948   SDL_Rect src, dest;
949
950   src.x = (int)sx;
951   src.y = (int)sy;
952   src.w = (int)w;
953   src.h = (int)h;
954
955   dest.x = (int)x;
956   dest.y = (int)y;
957   dest.w = (int)w;
958   dest.h = (int)h;
959
960   if(effect & SEMI_TRANSPARENT)
961     alpha = 128;
962
963   if(effect & VERTICAL_FLIP & HORIZONTAL_FLIP)
964     {
965     // FIXME: this hack is damn slow. Just keep it cause it isn't that used.
966     for(float sx_ = 0; sx_ < w; sx++)
967       for(float sy_ = 0; sy_ < h; sy++)
968         if(draw_part(sx_, sy_, sx+(w-sx_), sy+(h-sy_), 1, 1, alpha, NONE_EFFECT) == -2)
969           return -2;
970     return 0;
971     }
972   else if(effect & VERTICAL_FLIP)    // FIXME: feel free to replace this hack
973     {
974     for(float sy_ = sy; sy_ < h; sy_++)
975       if(draw_part(sx, sy_, x, y+(h-sy_), w, 1, alpha, NONE_EFFECT) == -2)
976         return -2;
977     return 0;
978     }
979   else if(effect & HORIZONTAL_FLIP)    // FIXME: feel free to replace this hack
980     {
981     for(float sx_ = 0; sx_ < w; sx_++)
982       if(draw_part(sx_, 0, sx+(w-sx_), sy, 1, h, alpha, NONE_EFFECT) == -2)
983         return -2;
984     return 0;
985     }
986
987   if(alpha != 255)
988     {
989     /* Create a Surface, make it using colorkey, blit surface into temp, apply alpha
990       to temp sur, blit the temp into the screen */
991     /* Note: this has to be done, since SDL doesn't allow to set alpha to surfaces that
992       already have an alpha mask, yet... */
993
994     SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags,
995                                     (int)w, (int)h, sdl_surface->format->BitsPerPixel,
996                                     sdl_surface->format->Rmask, sdl_surface->format->Gmask,
997                                     sdl_surface->format->Bmask,
998                                     0);
999     int colorkey = SDL_MapRGB(sdl_surface_copy->format, 255, 0, 255);
1000     SDL_FillRect(sdl_surface_copy, NULL, colorkey);
1001     SDL_SetColorKey(sdl_surface_copy, SDL_SRCCOLORKEY, colorkey);
1002
1003
1004     SDL_BlitSurface(sdl_surface, &src, sdl_surface_copy, NULL);
1005     SDL_SetAlpha(sdl_surface_copy ,SDL_SRCALPHA,alpha);
1006
1007     int ret = SDL_BlitSurface(sdl_surface_copy, NULL, screen, &dest);
1008
1009     SDL_FreeSurface (sdl_surface_copy);
1010     return ret;
1011     }
1012
1013   int ret = SDL_BlitSurface(sdl_surface, &src, screen, &dest);
1014
1015   return ret;
1016 }
1017
1018 int
1019 SurfaceSDL::draw_stretched(float x, float y, int sw, int sh, Uint8 alpha, Uint32 effect)
1020 {
1021   SDL_Rect dest;
1022
1023   dest.x = (int)x;
1024   dest.y = (int)y;
1025   dest.w = (int)sw;
1026   dest.h = (int)sh;
1027
1028   if(effect & SEMI_TRANSPARENT)
1029     alpha = 128;
1030
1031   SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags,
1032                                   sw, sh, sdl_surface->format->BitsPerPixel,
1033                                   sdl_surface->format->Rmask, sdl_surface->format->Gmask,
1034                                   sdl_surface->format->Bmask,
1035                                   0);
1036
1037   SDL_BlitSurface(sdl_surface, NULL, sdl_surface_copy, NULL);
1038   SDL_SoftStretch(sdl_surface_copy, NULL, sdl_surface_copy, &dest);
1039
1040   if(alpha != 255)
1041     SDL_SetAlpha(sdl_surface_copy,SDL_SRCALPHA,alpha);
1042
1043   int ret = SDL_BlitSurface(sdl_surface_copy,NULL,screen,&dest);
1044   SDL_FreeSurface(sdl_surface_copy);
1045
1046   return ret;
1047 }
1048
1049 void
1050 SurfaceSDL::apply_filter(int filter, Color color)
1051 {
1052   ::apply_filter_to_surface(sdl_surface, filter, color);
1053
1054   w = sdl_surface->w;
1055   h = sdl_surface->h;
1056 }
1057
1058 SurfaceSDL::~SurfaceSDL()
1059 {}