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