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