More texture fixes, including a flag to build power of 2 textures
[ne.git] / src / backend / texture.c
blob1459ca1a902251bd9f3da21ab3d81ddcc5e5ef88
1 /************************************************************************
2 This file is part of NE.
4 NE 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.
9 NE 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.
14 You should have received a copy of the GNU General Public License
15 along with NE. If not, see <http://www.gnu.org/licenses/>.
16 ************************************************************************/
18 #include "texture.h"
19 #include "error.h"
20 #include "video.h"
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <GL/gl.h>
25 #include <GL/glu.h>
26 #include <SDL/SDL_image.h>
28 /* Undefine the following if you're targeting
29 * hardware that supports non-power-of-2 textures.
30 * You'll save some video memory...
32 //#define NONPOW2
34 #define FAIL_ON_GLMEM \
35 { \
36 if (glGetError() == GL_OUT_OF_MEMORY) { \
37 fprintf(stderr, "Out of memory!"); \
38 exit(1); \
39 } \
42 void t_init()
44 static int init = 0;
45 if (init) return;
46 init = 1;
48 //glMatrixMode(GL_TEXTURE);
49 //glLoadIdentity();
50 //glScalef(-1,1,1);
53 struct texture *t_create()
55 struct texture *t = (struct texture*)malloc(sizeof(struct texture));
57 t->ready = 0;
58 t->gl_handle = 0;
60 return t;
63 void t_destroy(struct texture *t)
65 t_unload(t);
66 free(t);
69 int t_ready(struct texture *t)
71 return t->ready;
74 static int nearest_pow2(int s)
76 int v = 1;
77 while (v < s) {
78 v <<= 1;
80 return v;
83 static SDL_Surface *flip_fit_surf(SDL_Surface *surf)
85 #ifdef NONPOW2
86 int w = surf->w;
87 int h = surf->h;
88 #else
89 int w = nearest_pow2(surf->w);
90 int h = nearest_pow2(surf->h);
91 #endif
93 #ifdef DEBUG
94 if (w != surf->w || h != surf->h) {
95 printf("DEBUG: resizing surface from %dx%d to %dx%d for GL\n",
96 surf->w, surf->h, w, h);
98 #endif
100 SDL_Surface *n = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
101 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
102 0x000000FF,
103 0x0000FF00,
104 0x00FF0000,
105 0xFF000000
106 #else
107 0xFF000000,
108 0x00FF0000,
109 0x0000FF00,
110 0x000000FF
111 #endif
114 if (!n) return NULL;
116 Uint32 flags = surf->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
117 Uint8 alpha = surf->format->alpha;
119 // turn off alpha for the blit
120 if (flags&SDL_SRCALPHA)
121 SDL_SetAlpha(surf, 0, 0);
123 SDL_Rect src, dest;
124 src.x = dest.x = 0;
125 src.y = 0;
126 dest.y = h - surf->h; // copy to the bottom of the pow2 texture
127 src.w = surf->w;
128 src.h = surf->h;
129 SDL_BlitSurface(surf, &src, n, &dest);
131 // turn alpha back on if we turned it off
132 if (flags&SDL_SRCALPHA)
133 SDL_SetAlpha(surf, flags, alpha);
135 // now, flip the image
136 Uint8 *line = malloc(n->pitch * sizeof(Uint8));
138 Uint8 *pix = (Uint8*)n->pixels;
139 Uint16 pitch = n->pitch;
141 SDL_LockSurface(n);
142 int i;
143 for (i=0; i < n->h/2; i++) {
144 int end = n->h - i - 1;
145 memcpy(line, pix + pitch*i, pitch); // save
146 memcpy(pix + pitch*i, pix + pitch*end, pitch); // low to high
147 memcpy(pix + pitch*end, line, pitch); // saved to low
149 SDL_UnlockSurface(n);
151 free(line);
153 return n;
156 void t_load(struct texture *t, const char *filename)
158 t_unload(t);
159 t->ready = 0;
161 SDL_Surface *img = IMG_Load(filename);
163 if (!img) {
164 fprintf(stderr, "Texture load error: %s\n", IMG_GetError());
165 return;
168 if (img->format->BytesPerPixel < 2) {
169 fprintf(stderr, "Texture load error: %s is not true color\n", filename);
170 return;
173 SDL_Surface *tmp = flip_fit_surf(img);
175 glGenTextures(1, &t->gl_handle);
176 glBindTexture(GL_TEXTURE_2D, t->gl_handle);
178 t->width = img->w;
179 t->height = img->h;
181 t->u = (float)img->w / tmp->w;
182 t->v = (float)img->h / tmp->h;
184 SDL_FreeSurface(img);
185 img = tmp;
187 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
188 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
190 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
191 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
193 GLuint mode = GL_RGB;
194 if (img->format->BytesPerPixel == 4) mode = GL_RGBA;
196 int mipmap = 0;
198 if (!mipmap) {
199 glTexImage2D(GL_TEXTURE_2D, 0, img->format->BytesPerPixel,
200 img->w, img->h, 0, mode, GL_UNSIGNED_BYTE, img->pixels);
202 else {
203 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
204 gluBuild2DMipmaps(GL_TEXTURE_2D, img->format->BytesPerPixel, img->w, img->h,
205 mode, GL_UNSIGNED_BYTE, img->pixels);
208 SDL_FreeSurface(img);
210 FAIL_ON_GLMEM
212 t->ready = 1;
215 void t_unload(struct texture *t)
217 if (t_ready(t)) {
218 glDeleteTextures(1, &t->gl_handle);
222 void t_apply(struct texture *t)
224 GLint lastmode;
225 glGetIntegerv(GL_MATRIX_MODE, &lastmode);
226 glMatrixMode(GL_TEXTURE);
227 glLoadIdentity();
228 glScalef(t->u, t->v, 1.0);
229 glBindTexture(GL_TEXTURE_2D, t->gl_handle);
230 glMatrixMode(lastmode);
233 void t_clear()
235 GLint lastmode;
236 glGetIntegerv(GL_MATRIX_MODE, &lastmode);
237 glMatrixMode(GL_TEXTURE);
238 glLoadIdentity();
239 glBindTexture(GL_TEXTURE_2D, 0);
240 glMatrixMode(lastmode);
243 void t_set(struct texture *t, GLuint name, GLuint value)
245 t_apply(t);
246 glTexParameteri(GL_TEXTURE_2D,name,value);
249 struct texture *t_screenshot()
251 struct texture *t = t_create();
253 glGenTextures(1, &t->gl_handle);
254 glBindTexture(GL_TEXTURE_2D, t->gl_handle);
256 const struct video_info* vi = v_info();
258 #ifdef NONPOW2
259 int w = vi->width;
260 int h = vi->height;
261 #else
262 int w = nearest_pow2(vi->width);
263 int h = nearest_pow2(vi->height);
264 #endif
266 t->width = vi->width;
267 t->height = vi->height;
269 #ifdef DEBUG
270 if (w != vi->width || h != vi->height) {
271 printf("DEBUG: resizing surface from %dx%d to %dx%d for GL\n",
272 vi->width, vi->height, w, h);
274 #endif
276 t->u = (float)t->width / w;
277 t->v = (float)t->height / h;
279 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, w, h, 0);
281 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
282 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
284 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
285 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
287 FAIL_ON_GLMEM
289 t->ready = 1;
291 return t;