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