- removed joystick setup menu, since it won't work anyway
[supertux.git] / src / texture.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 <assert.h>
22 #include <iostream>
23 #include <algorithm>
24 #include "SDL.h"
25 #include "SDL_image.h"
26 #include "texture.h"
27 #include "globals.h"
28 #include "setup.h"
29
30 Surface::Surfaces Surface::surfaces;
31
32 SurfaceData::SurfaceData(SDL_Surface* temp, int use_alpha_)
33   : type(SURFACE), surface(0), use_alpha(use_alpha_)
34 {
35   // Copy the given surface and make sure that it is not stored in
36   // video memory
37   surface = SDL_CreateRGBSurface(temp->flags & (~SDL_HWSURFACE),
38                                  temp->w, temp->h,
39                                  temp->format->BitsPerPixel,
40                                  temp->format->Rmask,
41                                  temp->format->Gmask,
42                                  temp->format->Bmask,
43                                  temp->format->Amask);
44   if(!surface)
45     st_abort("No memory left.", "");
46   SDL_SetAlpha(temp,0,0);
47   SDL_BlitSurface(temp, NULL, surface, NULL);
48 }
49
50 SurfaceData::SurfaceData(const std::string& file_, int use_alpha_)
51   : type(LOAD), surface(0), file(file_), use_alpha(use_alpha_)
52 {
53 }
54   
55 SurfaceData::SurfaceData(const std::string& file_, int x_, int y_, int w_, int h_, int use_alpha_)
56   : type(LOAD_PART), surface(0), file(file_), use_alpha(use_alpha_),
57     x(x_), y(y_), w(w_), h(h_)
58 {
59 }
60
61 SurfaceData::~SurfaceData()
62 {
63   SDL_FreeSurface(surface);  
64 }
65
66 SurfaceImpl*
67 SurfaceData::create()
68 {
69   if (use_gl)
70     return create_SurfaceOpenGL();
71   else
72     return create_SurfaceSDL();
73 }
74
75 SurfaceSDL*
76 SurfaceData::create_SurfaceSDL()
77 {
78   switch(type)
79     {
80     case LOAD:
81       return new SurfaceSDL(file, use_alpha);
82     case LOAD_PART:
83       return new SurfaceSDL(file, x, y, w, h, use_alpha);
84     case SURFACE:
85       return new SurfaceSDL(surface, use_alpha);
86     }
87   assert(0);
88 }
89
90 SurfaceOpenGL*
91 SurfaceData::create_SurfaceOpenGL()
92 {
93   switch(type)
94     {
95     case LOAD:
96       return new SurfaceOpenGL(file, use_alpha);
97     case LOAD_PART:
98       return new SurfaceOpenGL(file, x, y, w, h, use_alpha);
99     case SURFACE:
100       return new SurfaceOpenGL(surface, use_alpha);
101     }
102   assert(0);
103 }
104
105
106 /* Quick utility function for texture creation */
107 static int power_of_two(int input)
108 {
109   int value = 1;
110
111   while ( value < input ) {
112     value <<= 1;
113   }
114   return value;
115 }
116
117 Surface::Surface(SDL_Surface* surf, int use_alpha)
118   : data(surf, use_alpha), w(0), h(0)
119 {
120   impl = data.create();
121   if (impl) 
122     {
123       w = impl->w;
124       h = impl->h;
125     }
126   surfaces.push_back(this);
127 }
128
129 Surface::Surface(const std::string& file, int use_alpha)
130   : data(file, use_alpha), w(0), h(0)
131 {
132   impl = data.create();
133   if (impl) 
134     {
135       w = impl->w;
136       h = impl->h;
137     }
138   surfaces.push_back(this);
139 }
140
141 Surface::Surface(const std::string& file, int x, int y, int w, int h, int use_alpha)
142   : data(file, x, y, w, h, use_alpha), w(0), h(0)
143 {
144   impl = data.create();
145   if (impl) 
146     {
147       w = impl->w;
148       h = impl->h;
149     }
150   surfaces.push_back(this);
151 }
152
153 void
154 Surface::reload()
155 {
156   delete impl;
157   impl = data.create();
158   if (impl) 
159     {
160       w = impl->w;
161       h = impl->h;  
162     }
163 }
164
165 Surface::~Surface()
166 {
167 #ifdef DEBUG
168   bool found = false;
169   for(std::list<Surface*>::iterator i = surfaces.begin(); i != surfaces.end();
170           ++i) {
171       if(*i == this) {
172           found = true; break;
173       }
174   }
175   if(!found)
176     printf("Error: Surface freed twice!!!\n");
177 #endif
178   surfaces.remove(this);
179   delete impl;
180 }
181
182 void
183 Surface::reload_all()
184 {
185   for(Surfaces::iterator i = surfaces.begin(); i != surfaces.end(); ++i)
186     {
187       (*i)->reload();
188     }
189 }
190
191 void
192 Surface::debug_check()
193 {
194   for(Surfaces::iterator i = surfaces.begin(); i != surfaces.end(); ++i)
195     {
196       printf("Surface not freed: T:%d F:%s.\n", (*i)->data.type, 
197           (*i)->data.file.c_str());
198     }
199 }
200
201 void
202 Surface::draw(float x, float y, Uint8 alpha, bool update)
203 {
204   if (impl) 
205     {
206       if (impl->draw(x, y, alpha, update) == -2)
207         reload();
208     }
209 }
210
211 void
212 Surface::draw_bg(Uint8 alpha, bool update)
213 {
214   if (impl)
215     {
216       if (impl->draw_bg(alpha, update) == -2)
217         reload();
218     }
219 }
220
221 void
222 Surface::draw_part(float sx, float sy, float x, float y, float w, float h,  Uint8 alpha, bool update)
223 {
224   if (impl)
225     {
226       if (impl->draw_part(sx, sy, x, y, w, h, alpha, update) == -2)
227         reload();
228     }
229 }
230
231 SDL_Surface*
232 sdl_surface_part_from_file(const std::string& file, int x, int y, int w, int h,  int use_alpha)
233 {
234   SDL_Rect src;
235   SDL_Surface * sdl_surface;
236   SDL_Surface * temp;
237   SDL_Surface * conv;
238
239   temp = IMG_Load(file.c_str());
240
241   if (temp == NULL)
242     st_abort("Can't load", file);
243
244   /* Set source rectangle for conv: */
245
246   src.x = x;
247   src.y = y;
248   src.w = w;
249   src.h = h;
250
251   conv = SDL_CreateRGBSurface(temp->flags, w, h, temp->format->BitsPerPixel,
252                               temp->format->Rmask,
253                               temp->format->Gmask,
254                               temp->format->Bmask,
255                               temp->format->Amask);
256
257   /* #if SDL_BYTEORDER == SDL_BIG_ENDIAN
258      0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
259      #else
260
261      0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
262      #endif*/
263
264   SDL_SetAlpha(temp,0,0);
265
266   SDL_BlitSurface(temp, &src, conv, NULL);
267   if(use_alpha == IGNORE_ALPHA && !use_gl)
268     sdl_surface = SDL_DisplayFormat(conv);
269   else
270     sdl_surface = SDL_DisplayFormatAlpha(conv);
271
272   if (sdl_surface == NULL)
273     st_abort("Can't covert to display format", file);
274
275   if (use_alpha == IGNORE_ALPHA && !use_gl)
276     SDL_SetAlpha(sdl_surface, 0, 0);
277
278   SDL_FreeSurface(temp);
279   SDL_FreeSurface(conv);
280   
281   return sdl_surface;
282 }
283
284 SDL_Surface*
285 sdl_surface_from_file(const std::string& file, int use_alpha)
286 {
287   SDL_Surface* sdl_surface;
288   SDL_Surface* temp;
289   
290   temp = IMG_Load(file.c_str());
291
292   if (temp == NULL)
293     st_abort("Can't load", file);
294
295   if(use_alpha == IGNORE_ALPHA && !use_gl)
296     sdl_surface = SDL_DisplayFormat(temp);
297   else
298     sdl_surface = SDL_DisplayFormatAlpha(temp);
299   
300   if (sdl_surface == NULL)
301     st_abort("Can't covert to display format", file);
302
303   if (use_alpha == IGNORE_ALPHA && !use_gl)
304     SDL_SetAlpha(sdl_surface, 0, 0);
305
306   SDL_FreeSurface(temp);
307
308   return sdl_surface;
309 }
310
311 SDL_Surface* 
312 sdl_surface_from_sdl_surface(SDL_Surface* sdl_surf, int use_alpha)
313 {
314   SDL_Surface* sdl_surface;
315   Uint32 saved_flags;
316   Uint8  saved_alpha;
317   
318   /* Save the alpha blending attributes */
319   saved_flags = sdl_surf->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
320   saved_alpha = sdl_surf->format->alpha;
321   if ( (saved_flags & SDL_SRCALPHA)
322        == SDL_SRCALPHA )
323     {
324       SDL_SetAlpha(sdl_surf, 0, 0);
325     }
326    
327   if(use_alpha == IGNORE_ALPHA && !use_gl)
328     sdl_surface = SDL_DisplayFormat(sdl_surf);
329   else
330     sdl_surface = SDL_DisplayFormatAlpha(sdl_surf);
331
332   /* Restore the alpha blending attributes */
333   if ( (saved_flags & SDL_SRCALPHA)
334        == SDL_SRCALPHA )
335     {
336       SDL_SetAlpha(sdl_surface, saved_flags, saved_alpha);
337     }
338   
339   if (sdl_surface == NULL)
340     st_abort("Can't covert to display format", "SURFACE");
341
342   if (use_alpha == IGNORE_ALPHA && !use_gl)
343     SDL_SetAlpha(sdl_surface, 0, 0);
344
345   return sdl_surface;
346 }
347
348 //---------------------------------------------------------------------------
349
350 SurfaceImpl::SurfaceImpl()
351 {
352 }
353
354 SurfaceImpl::~SurfaceImpl()
355 {
356   SDL_FreeSurface(sdl_surface);
357 }
358
359 SDL_Surface* SurfaceImpl::get_sdl_surface() const
360 {
361   return sdl_surface;
362 }
363
364 #ifndef NOOPENGL
365 SurfaceOpenGL::SurfaceOpenGL(SDL_Surface* surf, int use_alpha)
366 {
367   sdl_surface = sdl_surface_from_sdl_surface(surf, use_alpha);
368   create_gl(sdl_surface,&gl_texture);
369
370   w = sdl_surface->w;
371   h = sdl_surface->h;
372 }
373
374 SurfaceOpenGL::SurfaceOpenGL(const std::string& file, int use_alpha) 
375 {
376   sdl_surface = sdl_surface_from_file(file, use_alpha);
377   create_gl(sdl_surface,&gl_texture);
378
379   w = sdl_surface->w;
380   h = sdl_surface->h;
381 }
382
383 SurfaceOpenGL::SurfaceOpenGL(const std::string& file, int x, int y, int w, int h, int use_alpha)
384 {
385   sdl_surface = sdl_surface_part_from_file(file,x,y,w,h,use_alpha);
386   create_gl(sdl_surface, &gl_texture);
387
388   w = sdl_surface->w;
389   h = sdl_surface->h;
390 }
391
392 SurfaceOpenGL::~SurfaceOpenGL()
393 {
394   glDeleteTextures(1, &gl_texture);
395 }
396
397 void
398 SurfaceOpenGL::create_gl(SDL_Surface * surf, GLuint * tex)
399 {
400   Uint32 saved_flags;
401   Uint8  saved_alpha;
402   int w, h;
403   SDL_Surface *conv;
404
405   w = power_of_two(surf->w);
406   h = power_of_two(surf->h),
407
408 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
409   conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel,
410                                 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
411 #else
412   conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel,
413                               0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
414 #endif
415
416   /* Save the alpha blending attributes */
417   saved_flags = surf->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
418   saved_alpha = surf->format->alpha;
419   if ( (saved_flags & SDL_SRCALPHA)
420        == SDL_SRCALPHA )
421     {
422       SDL_SetAlpha(surf, 0, 0);
423     }
424
425   SDL_BlitSurface(surf, 0, conv, 0);
426
427   /* Restore the alpha blending attributes */
428   if ( (saved_flags & SDL_SRCALPHA)
429        == SDL_SRCALPHA )
430     {
431       SDL_SetAlpha(surf, saved_flags, saved_alpha);
432     }
433
434   glGenTextures(1, &*tex);
435   glBindTexture(GL_TEXTURE_2D , *tex);
436   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
437   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
438   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
439   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
440   glPixelStorei(GL_UNPACK_ROW_LENGTH, conv->pitch / conv->format->BytesPerPixel);
441   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, conv->pixels);
442   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
443       
444   SDL_FreeSurface(conv);
445 }
446
447 int
448 SurfaceOpenGL::draw(float x, float y, Uint8 alpha, bool update)
449 {
450   float pw = power_of_two(w);
451   float ph = power_of_two(h);
452
453   glEnable(GL_TEXTURE_2D);
454   glEnable(GL_BLEND);
455   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
456
457   glColor4ub(alpha, alpha, alpha, alpha);
458
459   glBindTexture(GL_TEXTURE_2D, gl_texture);
460
461   glBegin(GL_QUADS);
462   glTexCoord2f(0, 0);
463   glVertex2f(x, y);
464   glTexCoord2f((float)w / pw, 0);
465   glVertex2f((float)w+x, y);
466   glTexCoord2f((float)w / pw, (float)h / ph);  glVertex2f((float)w+x, (float)h+y);
467   glTexCoord2f(0, (float)h / ph);
468   glVertex2f(x, (float)h+y);
469   glEnd();
470   
471   glDisable(GL_TEXTURE_2D);
472   glDisable(GL_BLEND);
473   
474   (void) update; // avoid compiler warning
475   
476   return 0;
477 }
478
479 int
480 SurfaceOpenGL::draw_bg(Uint8 alpha, bool update)
481 {
482   float pw = power_of_two(w);
483   float ph = power_of_two(h);
484
485   glColor3ub(alpha, alpha, alpha);
486
487   glEnable(GL_TEXTURE_2D);
488   glBindTexture(GL_TEXTURE_2D, gl_texture);
489
490   glBegin(GL_QUADS);
491   glTexCoord2f(0, 0);
492   glVertex2f(0, 0);
493   glTexCoord2f((float)w / pw, 0);
494   glVertex2f(screen->w, 0);
495   glTexCoord2f((float)w / pw, (float)h / ph);
496   glVertex2f(screen->w, screen->h);
497   glTexCoord2f(0, (float)h / ph);
498   glVertex2f(0, screen->h);
499   glEnd();
500   
501   glDisable(GL_TEXTURE_2D);
502
503   (void) update; // avoid compiler warning
504
505   return 0;
506 }
507
508 int
509 SurfaceOpenGL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, bool update)
510 {
511   float pw = power_of_two(int(this->w));
512   float ph = power_of_two(int(this->h));
513
514   glBindTexture(GL_TEXTURE_2D, gl_texture);
515
516   glEnable(GL_BLEND);
517   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
518
519   glColor4ub(alpha, alpha, alpha, alpha);
520
521   glEnable(GL_TEXTURE_2D);
522
523
524   glBegin(GL_QUADS);
525   glTexCoord2f(sx / pw, sy / ph);
526   glVertex2f(x, y);
527   glTexCoord2f((float)(sx + w) / pw, sy / ph);
528   glVertex2f(w+x, y);
529   glTexCoord2f((sx+w) / pw, (sy+h) / ph);
530   glVertex2f(w +x, h+y);
531   glTexCoord2f(sx / pw, (float)(sy+h) / ph);
532   glVertex2f(x, h+y);
533   glEnd();
534
535   glDisable(GL_TEXTURE_2D);
536   glDisable(GL_BLEND);
537
538   (void) update; // avoid warnings
539   return 0;
540 }
541 #endif
542
543 SurfaceSDL::SurfaceSDL(SDL_Surface* surf, int use_alpha)
544 {
545   sdl_surface = sdl_surface_from_sdl_surface(surf, use_alpha);
546   w = sdl_surface->w;
547   h = sdl_surface->h;
548 }
549
550 SurfaceSDL::SurfaceSDL(const std::string& file, int use_alpha)
551 {
552   sdl_surface = sdl_surface_from_file(file, use_alpha);
553   w = sdl_surface->w;
554   h = sdl_surface->h;
555 }
556
557 SurfaceSDL::SurfaceSDL(const std::string& file, int x, int y, int w, int h,  int use_alpha)
558 {
559   sdl_surface = sdl_surface_part_from_file(file, x, y, w, h, use_alpha);
560   w = sdl_surface->w;
561   h = sdl_surface->h;
562 }
563
564 int
565 SurfaceSDL::draw(float x, float y, Uint8 alpha, bool update)
566 {
567   SDL_Rect dest;
568
569   dest.x = (int)x;
570   dest.y = (int)y;
571   dest.w = w;
572   dest.h = h;
573   
574   if(alpha != 255) /* SDL isn't capable of this kind of alpha :( therefore we'll leave now. */
575     return -1;
576   
577   SDL_SetAlpha(sdl_surface ,SDL_SRCALPHA,alpha);
578   int ret = SDL_BlitSurface(sdl_surface, NULL, screen, &dest);
579   
580   if (update == UPDATE)
581     SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
582
583   return ret;
584 }
585
586 int
587 SurfaceSDL::draw_bg(Uint8 alpha, bool update)
588 {
589   SDL_Rect dest;
590   
591   dest.x = 0;
592   dest.y = 0;
593   dest.w = screen->w;
594   dest.h = screen->h;
595
596   if(alpha != 255)
597     SDL_SetAlpha(sdl_surface ,SDL_SRCALPHA,alpha);
598   
599   int ret = SDL_SoftStretch(sdl_surface, NULL, screen, &dest);
600   
601   if (update == UPDATE)
602     SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
603
604   return ret;
605 }
606
607 int
608 SurfaceSDL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, bool update)
609 {
610   SDL_Rect src, dest;
611
612   src.x = (int)sx;
613   src.y = (int)sy;
614   src.w = (int)w;
615   src.h = (int)h;
616
617   dest.x = (int)x;
618   dest.y = (int)y;
619   dest.w = (int)w;
620   dest.h = (int)h;
621
622   if(alpha != 255)
623     SDL_SetAlpha(sdl_surface ,SDL_SRCALPHA,alpha);
624   
625   int ret = SDL_BlitSurface(sdl_surface, &src, screen, &dest);
626
627   if (update == UPDATE)
628     update_rect(screen, dest.x, dest.y, dest.w, dest.h);
629   
630   return ret;
631 }
632
633 SurfaceSDL::~SurfaceSDL()
634 {
635 }
636
637 /* EOF */