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