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