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