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