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