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