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