Apply filters when reloading Surfaces.
[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 - 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   if(effect & SEMI_TRANSPARENT)
788     alpha = 128;
789
790   float pw = power_of_two(sw);
791   float ph = power_of_two(sh);
792
793   if(effect & SEMI_TRANSPARENT)
794     alpha = 128;
795
796   glEnable(GL_TEXTURE_2D);
797   glEnable(GL_BLEND);
798   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
799
800   glColor4ub(alpha, alpha, alpha, alpha);
801
802   glBindTexture(GL_TEXTURE_2D, gl_texture);
803
804   glBegin(GL_QUADS);
805
806   if(effect & VERTICAL_FLIP & HORIZONTAL_FLIP)
807     {
808     glTexCoord2f(0, 0);
809     glVertex2f((float)sw+x, (float)sh+y);
810
811     glTexCoord2f((float)w / pw, 0);
812     glVertex2f(x, (float)sh+y);
813
814     glTexCoord2f((float)w / pw, (float)h / ph);
815     glVertex2f(x, y);
816
817     glTexCoord2f(0, (float)h / ph);
818     glVertex2f((float)sw+x, y);
819     }
820   else if(effect & VERTICAL_FLIP)
821     {
822     glTexCoord2f(0, 0);
823     glVertex2f(x, (float)sh+y);
824
825     glTexCoord2f((float)w / pw, 0);
826     glVertex2f((float)sw+x, (float)sh+y);
827
828     glTexCoord2f((float)w / pw, (float)h / ph);
829     glVertex2f((float)sw+x, y);
830     
831     glTexCoord2f(0, (float)h / ph);
832     glVertex2f(x, y);
833     }
834   else if(effect & HORIZONTAL_FLIP)
835     {
836     glTexCoord2f(0, 0);
837     glVertex2f((float)sw+x, y);
838
839     glTexCoord2f((float)w / pw, 0);
840     glVertex2f(x, y);
841
842     glTexCoord2f((float)w / pw, (float)h / ph);
843     glVertex2f(x, (float)sh+y);
844
845     glTexCoord2f(0, (float)h / ph);
846     glVertex2f((float)sw+x, (float)sh+y);
847     }
848   else
849     {
850     glTexCoord2f(0, 0);
851     glVertex2f(x, y);
852
853     glTexCoord2f((float)w / pw, 0);
854     glVertex2f((float)sw+x, y);
855
856     glTexCoord2f((float)w / pw, (float)h / ph);
857     glVertex2f((float)sw+x, (float)sh+y);
858
859     glTexCoord2f(0, (float)h / ph);
860     glVertex2f(x, (float)sh+y);
861     }
862   glEnd();
863
864   glDisable(GL_TEXTURE_2D);
865   glDisable(GL_BLEND);
866
867   return 0;
868 }
869
870 void
871 SurfaceOpenGL::apply_filter(int filter, Color color)
872 {
873   ::apply_filter_to_surface(sdl_surface, filter, color);
874   create_gl(sdl_surface,&gl_texture);
875
876   w = sdl_surface->w;
877   h = sdl_surface->h;
878 }
879
880 #endif
881
882 SurfaceSDL::SurfaceSDL(SDL_Surface* surf, bool use_alpha)
883 {
884   sdl_surface = sdl_surface_from_sdl_surface(surf, use_alpha);
885   w = sdl_surface->w;
886   h = sdl_surface->h;
887 }
888
889 SurfaceSDL::SurfaceSDL(const std::string& file, bool use_alpha)
890 {
891   sdl_surface = sdl_surface_from_file(file, use_alpha);
892   w = sdl_surface->w;
893   h = sdl_surface->h;
894 }
895
896 SurfaceSDL::SurfaceSDL(const std::string& file, int x, int y, int w, int h,  bool use_alpha)
897 {
898   sdl_surface = sdl_surface_part_from_file(file, x, y, w, h, use_alpha);
899   w = sdl_surface->w;
900   h = sdl_surface->h;
901 }
902
903 SurfaceSDL::SurfaceSDL(Color top_gradient, Color bottom_gradient, int w, int h)
904 {
905   sdl_surface = sdl_surface_from_gradient(top_gradient, bottom_gradient, w, h);
906   w = sdl_surface->w;
907   h = sdl_surface->h;
908 }
909
910 int
911 SurfaceSDL::draw(float x, float y, Uint8 alpha, Uint32 effect)
912 {
913   SDL_Rect dest;
914
915   dest.x = (int)x;
916   dest.y = (int)y;
917   dest.w = w;
918   dest.h = h;
919
920   if(effect & SEMI_TRANSPARENT)
921     alpha = 128;
922
923   if(effect & VERTICAL_FLIP & HORIZONTAL_FLIP)
924     {
925     // FIXME: this hack is damn slow. Just keep it cause it isn't that used.
926     for(float sx = 0; sx < w; sx++)
927       for(float sy = 0; sy < h; sy++)
928         if(draw_part(sx, sy, x+(w-sx), y+(h-sy), 1, 1, alpha, NONE_EFFECT) == -2)
929           return -2;
930     return 0;
931     }
932   else if(effect & VERTICAL_FLIP)    // FIXME: feel free to replace this hack
933     {
934     for(float sy = 0; sy < h; sy++)
935       if(draw_part(0, sy, x, y+(h-sy), w, 1, alpha, NONE_EFFECT) == -2)
936         return -2;
937     return 0;
938     }
939   else if(effect & HORIZONTAL_FLIP)    // FIXME: feel free to replace this hack
940     {
941     for(float sx = 0; sx < w; sx++)
942       if(draw_part(sx, 0, x+(w-sx), y, 1, h, alpha, NONE_EFFECT) == -2)
943         return -2;
944     return 0;
945     }
946
947   if(alpha != 255)
948     {
949     /* Create a Surface, make it using colorkey, blit surface into temp, apply alpha
950       to temp sur, blit the temp into the screen */
951     /* Note: this has to be done, since SDL doesn't allow to set alpha to surfaces that
952       already have an alpha mask yet... */
953
954     SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags,
955                                     sdl_surface->w, sdl_surface->h, sdl_surface->format->BitsPerPixel,
956                                     sdl_surface->format->Rmask, sdl_surface->format->Gmask,
957                                     sdl_surface->format->Bmask,
958                                     0);
959     int colorkey = SDL_MapRGB(sdl_surface_copy->format, 255, 0, 255);
960     SDL_FillRect(sdl_surface_copy, NULL, colorkey);
961     SDL_SetColorKey(sdl_surface_copy, SDL_SRCCOLORKEY, colorkey);
962
963
964     SDL_BlitSurface(sdl_surface, NULL, sdl_surface_copy, NULL);
965     SDL_SetAlpha(sdl_surface_copy ,SDL_SRCALPHA,alpha);
966
967     int ret = SDL_BlitSurface(sdl_surface_copy, NULL, screen, &dest);
968
969     SDL_FreeSurface (sdl_surface_copy);
970     return ret;
971     }
972
973   int ret = SDL_BlitSurface(sdl_surface, NULL, screen, &dest);
974
975   return ret;
976 }
977
978 int
979 SurfaceSDL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, Uint32 effect)
980 {
981   SDL_Rect src, dest;
982
983   src.x = (int)sx;
984   src.y = (int)sy;
985   src.w = (int)w;
986   src.h = (int)h;
987
988   dest.x = (int)x;
989   dest.y = (int)y;
990   dest.w = (int)w;
991   dest.h = (int)h;
992
993   if(effect & SEMI_TRANSPARENT)
994     alpha = 128;
995
996   if(effect & VERTICAL_FLIP & HORIZONTAL_FLIP)
997     {
998     // FIXME: this hack is damn slow. Just keep it cause it isn't that used.
999     for(float sx_ = 0; sx_ < w; sx++)
1000       for(float sy_ = 0; sy_ < h; sy++)
1001         if(draw_part(sx_, sy_, sx+(w-sx_), sy+(h-sy_), 1, 1, alpha, NONE_EFFECT) == -2)
1002           return -2;
1003     return 0;
1004     }
1005   else if(effect & VERTICAL_FLIP)    // FIXME: feel free to replace this hack
1006     {
1007     for(float sy_ = sy; sy_ < h; sy_++)
1008       if(draw_part(sx, sy_, x, y+(h-sy_), w, 1, alpha, NONE_EFFECT) == -2)
1009         return -2;
1010     return 0;
1011     }
1012   else if(effect & HORIZONTAL_FLIP)    // FIXME: feel free to replace this hack
1013     {
1014     for(float sx_ = 0; sx_ < w; sx_++)
1015       if(draw_part(sx_, 0, sx+(w-sx_), sy, 1, h, alpha, NONE_EFFECT) == -2)
1016         return -2;
1017     return 0;
1018     }
1019
1020   if(alpha != 255)
1021     {
1022     /* Create a Surface, make it using colorkey, blit surface into temp, apply alpha
1023       to temp sur, blit the temp into the screen */
1024     /* Note: this has to be done, since SDL doesn't allow to set alpha to surfaces that
1025       already have an alpha mask, yet... */
1026
1027     SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags,
1028                                     (int)w, (int)h, sdl_surface->format->BitsPerPixel,
1029                                     sdl_surface->format->Rmask, sdl_surface->format->Gmask,
1030                                     sdl_surface->format->Bmask,
1031                                     0);
1032     int colorkey = SDL_MapRGB(sdl_surface_copy->format, 255, 0, 255);
1033     SDL_FillRect(sdl_surface_copy, NULL, colorkey);
1034     SDL_SetColorKey(sdl_surface_copy, SDL_SRCCOLORKEY, colorkey);
1035
1036
1037     SDL_BlitSurface(sdl_surface, &src, sdl_surface_copy, NULL);
1038     SDL_SetAlpha(sdl_surface_copy ,SDL_SRCALPHA,alpha);
1039
1040     int ret = SDL_BlitSurface(sdl_surface_copy, NULL, screen, &dest);
1041
1042     SDL_FreeSurface (sdl_surface_copy);
1043     return ret;
1044     }
1045
1046   int ret = SDL_BlitSurface(sdl_surface, &src, screen, &dest);
1047
1048   return ret;
1049 }
1050
1051 int
1052 SurfaceSDL::draw_stretched(float x, float y, int sw, int sh, Uint8 alpha, Uint32 effect)
1053 {
1054   SDL_Rect dest;
1055
1056   dest.x = (int)x;
1057   dest.y = (int)y;
1058   dest.w = (int)sw;
1059   dest.h = (int)sh;
1060
1061   if(effect & SEMI_TRANSPARENT)
1062     alpha = 128;
1063
1064   SDL_Surface* sdl_surface_copy = SDL_CreateRGBSurface (sdl_surface->flags,
1065                                   sw, sh, sdl_surface->format->BitsPerPixel,
1066                                   sdl_surface->format->Rmask, sdl_surface->format->Gmask,
1067                                   sdl_surface->format->Bmask,
1068                                   0);
1069
1070   SDL_BlitSurface(sdl_surface, NULL, sdl_surface_copy, NULL);
1071   SDL_SoftStretch(sdl_surface_copy, NULL, sdl_surface_copy, &dest);
1072
1073   if(alpha != 255)
1074     SDL_SetAlpha(sdl_surface_copy,SDL_SRCALPHA,alpha);
1075
1076   int ret = SDL_BlitSurface(sdl_surface_copy,NULL,screen,&dest);
1077   SDL_FreeSurface(sdl_surface_copy);
1078
1079   return ret;
1080 }
1081
1082 void
1083 SurfaceSDL::apply_filter(int filter, Color color)
1084 {
1085   ::apply_filter_to_surface(sdl_surface, filter, color);
1086
1087   w = sdl_surface->w;
1088   h = sdl_surface->h;
1089 }
1090
1091 SurfaceSDL::~SurfaceSDL()
1092 {}
1093
1094 /* EOF */