1 //
2 // Copyright (C) 2009 Nick Gasson
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 //
17
18 #include "ITexture.hpp"
19 #include "ILogger.hpp"
20
21 #include <map>
22 #include <sstream>
23 #include <stdexcept>
24
25 #include <GL/glew.h>
26 #include <GL/gl.h>
27 #include <SDL.h>
28 #include <SDL_image.h>
29
30 class Texture : public ITexture {
31 public:
32 Texture(const string &file);
33 ~Texture();
34
texture() const35 GLuint texture() const { return my_texture; }
36 void bind();
37
width() const38 int width() const { return my_width; }
height() const39 int height() const { return my_height; }
40
41 private:
42 GLuint my_texture;
43 int my_width, my_height;
44
45 static bool is_power_of_two(int n);
46 static bool is_texture_size_supported(
47 int width, int height, int ncols = 4, GLenum format = GL_RGBA);
48 };
49
50 // Texture cache
51 namespace {
52 map<string, ITexturePtr> the_texture_cache;
53 }
54
load_texture(const string & a_file_name)55 ITexturePtr load_texture(const string& a_file_name)
56 {
57 map<string, ITexturePtr>::iterator it =
58 the_texture_cache.find(a_file_name);
59
60 if (it != the_texture_cache.end())
61 return (*it).second;
62 else {
63 ITexturePtr ptr(new Texture(a_file_name));
64 the_texture_cache[a_file_name] = ptr;
65 return ptr;
66 }
67 }
68
load_texture(IResourcePtr a_res,const string & a_file_name)69 ITexturePtr load_texture(IResourcePtr a_res, const string& a_file_name)
70 {
71 // Hack alert! Just use the handle to find out the file name
72 // This should be replaced with a solution where all textures come
73 // from resources...
74 string real_file_name;
75 {
76 IResource::Handle h = a_res->open_file(a_file_name);
77 real_file_name = h.file_name();
78 } // Handle closed here
79
80 return load_texture(real_file_name);
81 }
82
Texture(const string & file)83 Texture::Texture(const string &file)
84 {
85 SDL_Surface *surface = IMG_Load(file.c_str());
86 if (NULL == surface) {
87 ostringstream os;
88 os << "Failed to load image: " << IMG_GetError();
89 throw runtime_error(os.str());
90 }
91
92 if (!is_power_of_two(surface->w))
93 warn() << file << " width not a power of 2";
94 if (!is_power_of_two(surface->h))
95 warn() << file << " height not a power of 2";
96
97 if (!is_texture_size_supported(surface->w, surface->h))
98 warn() << file << " bigger than max OpenGL texture";
99
100 int ncols = surface->format->BytesPerPixel;
101 GLenum texture_format;
102 if (ncols == 4) {
103 // Contains an alpha channel
104 if (surface->format->Rmask == 0x000000ff)
105 texture_format = GL_RGBA;
106 else
107 texture_format = GL_BGRA;
108 }
109 else if (ncols == 3) {
110 if (surface->format->Rmask == 0x000000ff)
111 texture_format = GL_RGB;
112 else
113 texture_format = GL_BGR;
114 }
115 else {
116 ostringstream os;
117 os << "Unsupported image colour format: " << file;
118 throw runtime_error(os.str());
119 }
120
121 my_width = surface->w;
122 my_height = surface->h;
123
124 glGenTextures(1, &my_texture);
125 glBindTexture(GL_TEXTURE_2D, my_texture);
126
127 // Use GL_NEAREST here for better performance
128 // Or GL_LINEAR for better apppearance
129 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
130 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
131
132 // Load the surface's data into the texture
133 glTexImage2D(GL_TEXTURE_2D, 0, ncols, surface->w, surface->h, 0,
134 texture_format, GL_UNSIGNED_BYTE, surface->pixels);
135
136 SDL_FreeSurface(surface);
137
138 log() << "Loaded texture " << file;
139 }
140
~Texture()141 Texture::~Texture()
142 {
143 glDeleteTextures(1, &my_texture);
144 }
145
is_power_of_two(int n)146 bool Texture::is_power_of_two(int n)
147 {
148 return (n & (n - 1)) == 0;
149 }
150
is_texture_size_supported(int width,int height,int ncols,GLenum format)151 bool Texture::is_texture_size_supported(int width, int height, int ncols,
152 GLenum format)
153 {
154 glTexImage2D(GL_PROXY_TEXTURE_2D, 0, ncols, width, height, 0, format,
155 GL_UNSIGNED_BYTE, NULL);
156 glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
157 glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
158
159 return height != 0 && width != 0;
160 }
161
bind()162 void Texture::bind()
163 {
164 glBindTexture(GL_TEXTURE_2D, my_texture);
165 }
166