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