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