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