237aff3efeb200182c875c5ca810fb4876b5709b
[supertux.git] / src / video / texture_manager.cpp
1 #include <config.h>
2
3 #include "texture_manager.hpp"
4
5 #include <assert.h>
6 #include <SDL.h>
7 #include <SDL_image.h>
8 #include <GL/gl.h>
9 #include <GL/glext.h>
10 #include <iostream>
11 #include <sstream>
12 #include <stdexcept>
13 #include "physfs/physfs_sdl.hpp"
14 #include "image_texture.hpp"
15 #include "glutil.hpp"
16 #include "file_system.hpp"
17
18 TextureManager* texture_manager = NULL;
19
20 TextureManager::TextureManager()
21 {
22 }
23
24 TextureManager::~TextureManager()
25 {
26   for(ImageTextures::iterator i = image_textures.begin();
27       i != image_textures.end(); ++i) {
28     if(i->second == NULL)
29       continue;
30 #ifdef DEBUG
31     std::cerr << "Warning: Texture '" << i->first << "' not freed\n";
32 #endif
33     delete i->second;
34   }
35 }
36
37 ImageTexture*
38 TextureManager::get(const std::string& _filename)
39 {
40   std::string filename = FileSystem::normalize(_filename);
41   ImageTextures::iterator i = image_textures.find(filename);
42
43   ImageTexture* texture = NULL;
44   if(i != image_textures.end())
45     texture = i->second;
46
47   if(texture == NULL) {
48     texture = create_image_texture(filename);
49     image_textures[filename] = texture;
50   }
51
52   return texture;
53 }
54
55 void
56 TextureManager::release(ImageTexture* texture)
57 {
58   image_textures[texture->filename] = NULL;
59   delete texture;
60 }
61
62 void
63 TextureManager::register_texture(Texture* texture)
64 {
65   textures.insert(texture);
66 }
67
68 void
69 TextureManager::remove_texture(Texture* texture)
70 {
71   textures.erase(texture);
72 }
73
74 static inline int next_power_of_two(int val)
75 {
76   int result = 1;
77   while(result < val)
78     result *= 2;
79   return result;
80 }
81
82 ImageTexture*
83 TextureManager::create_image_texture(const std::string& filename)
84 {
85   SDL_Surface* image = IMG_Load_RW(get_physfs_SDLRWops(filename), 1);
86   if(image == NULL) {
87     std::ostringstream msg;
88     msg << "Couldn't load image '" << filename << "' :" << SDL_GetError();
89     throw std::runtime_error(msg.str());
90   }
91
92   int texture_w = next_power_of_two(image->w);
93   int texture_h = next_power_of_two(image->h);
94
95 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
96   SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE,
97       texture_w, texture_h, 32,
98       0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
99 #else
100   SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE,
101       texture_w, texture_h, 32,
102       0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
103 #endif
104
105   if(convert == 0)
106     throw std::runtime_error("Couldn't create texture: out of memory");
107
108   SDL_SetAlpha(image, 0, 0);
109   SDL_BlitSurface(image, 0, convert, 0);
110
111   ImageTexture* result = NULL;
112   try {
113     result = new ImageTexture(convert);
114     result->filename = filename;
115     result->image_width = image->w;
116     result->image_height = image->h;
117   } catch(...) {
118     delete result;
119     SDL_FreeSurface(convert);
120     throw;
121   }
122   
123   SDL_FreeSurface(convert);
124   return result;
125 }
126
127 void
128 TextureManager::save_textures()
129 {
130   glPixelStorei(GL_PACK_ROW_LENGTH, 0);
131   glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
132   glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
133   glPixelStorei(GL_PACK_SKIP_ROWS, 0);
134   glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
135   glPixelStorei(GL_PACK_ALIGNMENT, 1);
136   for(Textures::iterator i = textures.begin(); i != textures.end(); ++i) {
137     save_texture(*i);
138   }
139   for(ImageTextures::iterator i = image_textures.begin();
140       i != image_textures.end(); ++i) {
141     save_texture(i->second);
142   }
143 }
144
145 void
146 TextureManager::save_texture(Texture* texture)
147 {
148   SavedTexture saved_texture;
149   saved_texture.texture = texture;
150   glBindTexture(GL_TEXTURE_2D, texture->get_handle());
151   glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,
152                            &saved_texture.width);
153   glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT,
154                            &saved_texture.height);
155   glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BORDER,
156                            &saved_texture.border);
157   glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
158                       &saved_texture.min_filter);
159   glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
160                       &saved_texture.mag_filter);
161   glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
162                       &saved_texture.wrap_s);
163   glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
164                       &saved_texture.wrap_t);
165
166   size_t pixelssize = saved_texture.width * saved_texture.height * 4;
167   saved_texture.pixels = new char[pixelssize];
168   
169   glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE,
170                 saved_texture.pixels);
171
172   saved_textures.push_back(saved_texture);
173
174   glDeleteTextures(1, &(texture->handle));
175   texture->handle = 0;
176
177   assert_gl("retrieving texture");
178 }
179
180 void
181 TextureManager::reload_textures()
182 {
183   glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
184   glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
185   glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
186   glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
187   glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
188   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
189   
190   for(std::vector<SavedTexture>::iterator i = saved_textures.begin();
191       i != saved_textures.end(); ++i) {
192     SavedTexture& saved_texture = *i;
193     
194     GLuint handle;
195     glGenTextures(1, &handle);
196     assert_gl("creating texture handle");
197
198     glBindTexture(GL_TEXTURE_2D, handle);
199     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
200                  saved_texture.width, saved_texture.height,
201                  saved_texture.border, GL_RGBA,
202                  GL_UNSIGNED_BYTE, saved_texture.pixels);
203     delete[] saved_texture.pixels;
204     assert_gl("uploading texture pixel data");
205
206     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
207                     saved_texture.min_filter);
208     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
209                     saved_texture.mag_filter);
210     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
211                     saved_texture.wrap_s);
212     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
213                     saved_texture.wrap_t);
214
215     assert_gl("setting texture_params");
216     saved_texture.texture->handle = handle;
217   }
218
219   saved_textures.clear();
220 }
221