]> Dogcows Code - chaz/yoink/blob - src/texture.cc
new classes: resource, tilemap, animation
[chaz/yoink] / src / texture.cc
1
2 /*******************************************************************************
3
4 Copyright (c) 2009, Charles McGarvey
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 * Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 *******************************************************************************/
28
29 #include <cstdlib>
30
31 #include <boost/bind.hpp>
32
33 #include <SDL/SDL.h>
34 #include <SDL/SDL_image.h>
35
36 #include "dispatcher.hh"
37 #include "opengl.hh"
38
39 #include "texture.hh"
40
41
42 namespace dc {
43
44
45 class texture_impl
46 {
47 void unloadFromGL()
48 {
49 if (object)
50 {
51 glDeleteTextures(1, &object);
52 object = 0;
53 }
54 }
55
56 void contextRecreated(const notification& note)
57 {
58 unloadFromGL();
59 uploadToGL();
60 }
61
62 public:
63 texture_impl(texture* outside, bool keepInMemory)
64 : interface(outside), keepData(keepInMemory), object(0), imageData(0)
65 {
66 dispatcher::instance().addHandler("video.context_recreated",
67 boost::bind(&texture_impl::contextRecreated, this, _1),
68 this);
69 }
70
71 ~texture_impl()
72 {
73 dispatcher::instance().removeHandler(this);
74
75 if (imageData)
76 {
77 SDL_FreeSurface(imageData);
78 }
79 unloadFromGL();
80 }
81
82
83 static int powerOfTwo(int input)
84 {
85 int value = 1;
86
87 while (value < input)
88 {
89 value <<= 1;
90 }
91 return value;
92 }
93
94 static SDL_Surface* prepareImageForGL(SDL_Surface* surface)
95 {
96 // Adapted from some public domain code. This stuff is common enough
97 // that it really should be included in SDL_image... We need this
98 // because images loaded with SDL_image aren't exactly OpenGL-ready
99 // right out of the box.
100
101 int w = powerOfTwo(surface->w);
102 int h = powerOfTwo(surface->h);
103
104 // 1. OpenGL images must (generally) have dimensions of a power-of-two.
105
106 SDL_Surface* image = SDL_CreateRGBSurface
107 (
108 SDL_SWSURFACE,
109 w, h,
110 32,
111 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
112 0x000000FF,
113 0x0000FF00,
114 0x00FF0000,
115 0xFF000000
116 #else
117 0xFF000000,
118 0x00FF0000,
119 0x0000FF00,
120 0x000000FF
121 #endif
122 );
123
124 if (!image)
125 {
126 return 0;
127 }
128
129 Uint32 savedFlags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
130 Uint8 savedAlpha = surface->format->alpha;
131 if (savedFlags & SDL_SRCALPHA)
132 {
133 SDL_SetAlpha(surface, 0, 0);
134 }
135
136 SDL_Rect srcArea, destArea;
137 srcArea.x = 0; destArea.x = 0;
138 srcArea.y = 0; destArea.y = h - surface->h;
139 srcArea.w = surface->w;
140 srcArea.h = surface->h;
141 SDL_BlitSurface(surface, &srcArea, image, &destArea);
142
143 if (savedFlags & SDL_SRCALPHA)
144 {
145 SDL_SetAlpha(surface, savedFlags, savedAlpha);
146 }
147
148 // 2. OpenGL textures make more sense when they are "upside down."
149
150 Uint8 line[image->pitch];
151
152 Uint8 *pixels = static_cast<Uint8*>(image->pixels);
153 Uint16 pitch = image->pitch;
154 int ybegin = 0;
155 int yend = image->h - 1;
156
157 if (SDL_MUSTLOCK(image)) SDL_LockSurface(image);
158 while (ybegin < yend)
159 {
160 memcpy(line, pixels + pitch * ybegin, pitch);
161 memcpy(pixels + pitch * ybegin, pixels + pitch * yend, pitch);
162 memcpy(pixels + pitch * yend, line, pitch);
163 ybegin++;
164 yend--;
165 }
166 if (SDL_MUSTLOCK(image)) SDL_UnlockSurface(image);
167
168 return image;
169 }
170
171
172 void loadImageData()
173 {
174 SDL_Surface* surface;
175
176 surface = IMG_Load(interface->getPathToFile().c_str());
177
178 if (!surface)
179 {
180 throw texture::exception("loading failed");
181 }
182
183 imageData = prepareImageForGL(surface);
184 SDL_FreeSurface(surface);
185
186 if (!imageData)
187 {
188 throw texture::exception("");
189 }
190
191 if (imageData->format->BytesPerPixel == 3)
192 {
193 mode = GL_RGB;
194 }
195 else if (imageData->format->BytesPerPixel == 4)
196 {
197 mode = GL_RGBA;
198 }
199 else
200 {
201 SDL_FreeSurface(imageData);
202 throw texture::exception("image is not the required 24 or 32 bpp");
203 }
204
205 width = imageData->w;
206 height = imageData->h;
207 }
208
209
210 void uploadToGL()
211 {
212 if (object)
213 {
214 // Already loaded.
215 return;
216 }
217
218 if (!imageData)
219 {
220 loadImageData();
221 }
222
223 glGenTextures(1, &object);
224
225 glBindTexture(GL_TEXTURE_2D, object);
226
227 glTexImage2D
228 (
229 GL_TEXTURE_2D,
230 0,
231 mode,
232 imageData->w,
233 imageData->h,
234 0,
235 mode,
236 GL_UNSIGNED_BYTE,
237 imageData->pixels
238 );
239
240 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
241 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
242
243 if (!keepData)
244 {
245 SDL_FreeSurface(imageData);
246 imageData = 0;
247 }
248 }
249
250
251 unsigned width;
252 unsigned height;
253 int mode;
254 GLuint object;
255
256 bool keepData;
257 SDL_Surface* imageData;
258
259 texture* interface;
260 };
261
262
263 texture::texture(const std::string& name, bool keepInMemory)
264 : resource(name), impl(new texture_impl(this, keepInMemory)) {}
265
266
267 void texture::bind()
268 {
269 glBindTexture(GL_TEXTURE_2D, getObject());
270 }
271
272 GLuint texture::getObject()
273 {
274 if (!impl->object)
275 {
276 impl->uploadToGL();
277 }
278
279 return impl->object;
280 }
281
282
283 unsigned texture::getWidth()
284 {
285 if (!impl->object)
286 {
287 impl->uploadToGL();
288 }
289
290 return impl->width;
291 }
292
293 unsigned texture::getHeight()
294 {
295 if (!impl->object)
296 {
297 impl->uploadToGL();
298 }
299
300 return impl->height;
301 }
302
303
304 } // namespace dc
305
This page took 0.039932 seconds and 4 git commands to generate.