- memleak fix and menu fix from MatzeB
[supertux.git] / src / texture.cpp
index ae7cec4..e372063 100644 (file)
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
 //
-// C Implementation: texture
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
 //
-// Description:
-//
-//
-// Author: Tobias Glaesser <tobi.web@gmx.de>, (C) 2004
-//
-// Copyright: See COPYING file that comes with this distribution
-//
-//
-
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+//  02111-1307, USA.
+
+#include <assert.h>
+#include <iostream>
 #include "SDL.h"
 #include "SDL_image.h"
 #include "texture.h"
 #include "globals.h"
 #include "setup.h"
 
+Surface::Surfaces Surface::surfaces;
+
+SurfaceData::SurfaceData(SDL_Surface* temp, int use_alpha_)
+  : type(SURFACE), surface(0), use_alpha(use_alpha_)
+{
+  // Copy the given surface and make sure that it is not stored in
+  // video memory
+  surface = SDL_CreateRGBSurface(temp->flags & (~SDL_HWSURFACE),
+                                 temp->w, temp->h,
+                                 temp->format->BitsPerPixel,
+                                 temp->format->Rmask,
+                                 temp->format->Gmask,
+                                 temp->format->Bmask,
+                                 temp->format->Amask);
+  if(!surface)
+    st_abort("No memory left.", "");
+  SDL_SetAlpha(temp,0,0);
+  SDL_BlitSurface(temp, NULL, surface, NULL);
+}
+
+SurfaceData::SurfaceData(const std::string& file_, int use_alpha_)
+  : type(LOAD), surface(0), file(file_), use_alpha(use_alpha_)
+{
+}
+  
+SurfaceData::SurfaceData(const std::string& file_, int x_, int y_, int w_, int h_, int use_alpha_)
+  : type(LOAD_PART), surface(0), file(file_), use_alpha(use_alpha_),
+    x(x_), y(y_), w(w_), h(h_)
+{
+}
+
+SurfaceData::~SurfaceData()
+{
+  SDL_FreeSurface(surface);  
+}
+
+SurfaceImpl*
+SurfaceData::create()
+{
+  if (use_gl)
+    return create_SurfaceOpenGL();
+  else
+    return create_SurfaceSDL();
+}
+
+SurfaceSDL*
+SurfaceData::create_SurfaceSDL()
+{
+  switch(type)
+    {
+    case LOAD:
+      return new SurfaceSDL(file, use_alpha);
+    case LOAD_PART:
+      return new SurfaceSDL(file, x, y, w, h, use_alpha);
+    case SURFACE:
+      return new SurfaceSDL(surface, use_alpha);
+    }
+  assert(0);
+}
+
+SurfaceOpenGL*
+SurfaceData::create_SurfaceOpenGL()
+{
+  switch(type)
+    {
+    case LOAD:
+      return new SurfaceOpenGL(file, use_alpha);
+    case LOAD_PART:
+      return new SurfaceOpenGL(file, x, y, w, h, use_alpha);
+    case SURFACE:
+      return new SurfaceOpenGL(surface, use_alpha);
+    }
+  assert(0);
+}
+
+
 /* Quick utility function for texture creation */
 static int power_of_two(int input)
 {
@@ -28,59 +114,96 @@ static int power_of_two(int input)
 }
 
 Surface::Surface(SDL_Surface* surf, int use_alpha)
+  : data(surf, use_alpha), w(0), h(0)
 {
-  if (use_gl)
-    impl = new SurfaceOpenGL(surf, use_alpha);
-  else
-    impl = new SurfaceSDL(surf, use_alpha);
-
-  w = impl->w;
h = impl->h;
+  impl = data.create();
+  if (impl) 
+    {
+      w = impl->w;
+      h = impl->h;
+    }
 surfaces.push_back(this);
 }
 
 Surface::Surface(const std::string& file, int use_alpha)
+  : data(file, use_alpha), w(0), h(0)
 {
-  if (use_gl)
-    impl = new SurfaceOpenGL(file, use_alpha);
-  else
-    impl = new SurfaceSDL(file, use_alpha);
-
-  w = impl->w;
-  h = impl->h;
+  impl = data.create();
+  if (impl) 
+    {
+      w = impl->w;
+      h = impl->h;
+    }
+  surfaces.push_back(this);
 }
 
 Surface::Surface(const std::string& file, int x, int y, int w, int h, int use_alpha)
+  : data(file, x, y, w, h, use_alpha), w(0), h(0)
 {
-  if (use_gl)
-    impl = new SurfaceOpenGL(file, x, y, w, h, use_alpha);
-  else
-    impl = new SurfaceSDL(file, x, y, w, h, use_alpha);
+  impl = data.create();
+  if (impl) 
+    {
+      w = impl->w;
+      h = impl->h;
+    }
+  surfaces.push_back(this);
+}
 
-  w = impl->w;
-  h = impl->h;
+void
+Surface::reload()
+{
+  delete impl;
+  impl = data.create();
+  if (impl) 
+    {
+      w = impl->w;
+      h = impl->h;  
+    }
 }
 
 Surface::~Surface()
 {
+  surfaces.remove(this);
   delete impl;
 }
 
 void
+Surface::reload_all()
+{
+  for(Surfaces::iterator i = surfaces.begin(); i != surfaces.end(); ++i)
+    {
+      (*i)->reload();
+    }
+}
+
+void
 Surface::draw(float x, float y, Uint8 alpha, bool update)
 {
-  if (impl) impl->draw(x, y, alpha, update);
+  if (impl) 
+    {
+      if (impl->draw(x, y, alpha, update) == -2)
+        reload();
+    }
 }
 
 void
 Surface::draw_bg(Uint8 alpha, bool update)
 {
-  if (impl) impl->draw_bg(alpha, update);
+  if (impl)
+    {
+      if (impl->draw_bg(alpha, update) == -2)
+        reload();
+    }
 }
 
 void
 Surface::draw_part(float sx, float sy, float x, float y, float w, float h,  Uint8 alpha, bool update)
 {
-  if (impl) impl->draw_part(sx, sy, x, y, w, h, alpha, update);
+  if (impl)
+    {
+      if (impl->draw_part(sx, sy, x, y, w, h, alpha, update) == -2)
+        reload();
+    }
 }
 
 SDL_Surface*
@@ -200,6 +323,22 @@ sdl_surface_from_sdl_surface(SDL_Surface* sdl_surf, int use_alpha)
   return sdl_surface;
 }
 
+//---------------------------------------------------------------------------
+
+SurfaceImpl::SurfaceImpl()
+{
+}
+
+SurfaceImpl::~SurfaceImpl()
+{
+  SDL_FreeSurface(sdl_surface);
+}
+
+SDL_Surface* SurfaceImpl::get_sdl_surface() const
+{
+  return sdl_surface;
+}
+
 #ifndef NOOPENGL
 SurfaceOpenGL::SurfaceOpenGL(SDL_Surface* surf, int use_alpha)
 {
@@ -230,7 +369,6 @@ SurfaceOpenGL::SurfaceOpenGL(const std::string& file, int x, int y, int w, int h
 
 SurfaceOpenGL::~SurfaceOpenGL()
 {
-  SDL_FreeSurface(sdl_surface);
   glDeleteTextures(1, &gl_texture);
 }
 
@@ -246,7 +384,7 @@ SurfaceOpenGL::create_gl(SDL_Surface * surf, GLuint * tex)
   h = power_of_two(surf->h),
 
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
-    conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel,
+  conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel,
                                 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
 #else
   conv = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, surf->format->BitsPerPixel,
@@ -284,7 +422,7 @@ SurfaceOpenGL::create_gl(SDL_Surface * surf, GLuint * tex)
   SDL_FreeSurface(conv);
 }
 
-void
+int
 SurfaceOpenGL::draw(float x, float y, Uint8 alpha, bool update)
 {
   float pw = power_of_two(w);
@@ -311,12 +449,12 @@ SurfaceOpenGL::draw(float x, float y, Uint8 alpha, bool update)
   glDisable(GL_TEXTURE_2D);
   glDisable(GL_BLEND);
   
-  /* Avoid compiler warnings */
-  if(update)
-    {}
+  (void) update; // avoid compiler warning
+  
+  return 0;
 }
 
-void
+int
 SurfaceOpenGL::draw_bg(Uint8 alpha, bool update)
 {
   float pw = power_of_two(w);
@@ -340,12 +478,12 @@ SurfaceOpenGL::draw_bg(Uint8 alpha, bool update)
   
   glDisable(GL_TEXTURE_2D);
 
-  /* Avoid compiler warnings */
-  if(update)
-    {}
+  (void) update; // avoid compiler warning
+
+  return 0;
 }
 
-void
+int
 SurfaceOpenGL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, bool update)
 {
   float pw = power_of_two(int(this->w));
@@ -378,6 +516,7 @@ SurfaceOpenGL::draw_part(float sx, float sy, float x, float y, float w, float h,
   /* Avoid compiler warnings */
   if(update)
     {}
+  return 0;
 }
 #endif
 
@@ -402,7 +541,7 @@ SurfaceSDL::SurfaceSDL(const std::string& file, int x, int y, int w, int h,  int
   h = sdl_surface->h;
 }
 
-void
+int
 SurfaceSDL::draw(float x, float y, Uint8 alpha, bool update)
 {
   SDL_Rect dest;
@@ -413,16 +552,18 @@ SurfaceSDL::draw(float x, float y, Uint8 alpha, bool update)
   dest.h = h;
   
   if(alpha != 255) /* SDL isn't capable of this kind of alpha :( therefore we'll leave now. */
-  return;
+    return -1;
   
   SDL_SetAlpha(sdl_surface ,SDL_SRCALPHA,alpha);
-  SDL_BlitSurface(sdl_surface, NULL, screen, &dest);
+  int ret = SDL_BlitSurface(sdl_surface, NULL, screen, &dest);
   
   if (update == UPDATE)
     SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
+
+  return ret;
 }
 
-void
+int
 SurfaceSDL::draw_bg(Uint8 alpha, bool update)
 {
   SDL_Rect dest;
@@ -433,14 +574,17 @@ SurfaceSDL::draw_bg(Uint8 alpha, bool update)
   dest.h = screen->h;
 
   if(alpha != 255)
-  SDL_SetAlpha(sdl_surface ,SDL_SRCALPHA,alpha);
-  SDL_SoftStretch(sdl_surface, NULL, screen, &dest);
+    SDL_SetAlpha(sdl_surface ,SDL_SRCALPHA,alpha);
+  
+  int ret = SDL_SoftStretch(sdl_surface, NULL, screen, &dest);
   
   if (update == UPDATE)
     SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
+
+  return ret;
 }
 
-void
+int
 SurfaceSDL::draw_part(float sx, float sy, float x, float y, float w, float h, Uint8 alpha, bool update)
 {
   SDL_Rect src, dest;
@@ -456,17 +600,18 @@ SurfaceSDL::draw_part(float sx, float sy, float x, float y, float w, float h, Ui
   dest.h = (int)h;
 
   if(alpha != 255)
-  SDL_SetAlpha(sdl_surface ,SDL_SRCALPHA,alpha);
+    SDL_SetAlpha(sdl_surface ,SDL_SRCALPHA,alpha);
   
-  SDL_BlitSurface(sdl_surface, &src, screen, &dest);
+  int ret = SDL_BlitSurface(sdl_surface, &src, screen, &dest);
 
   if (update == UPDATE)
     update_rect(screen, dest.x, dest.y, dest.w, dest.h);
+  
+  return ret;
 }
 
 SurfaceSDL::~SurfaceSDL()
 {
-  SDL_FreeSurface(sdl_surface);
 }
 
 /* EOF */