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