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