rev |
line source |
nuclear@0
|
1 #include <string.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@3
|
3 #ifdef _MSC_VER
|
nuclear@3
|
4 #include <malloc.h>
|
nuclear@3
|
5 #else
|
nuclear@0
|
6 #include <alloca.h>
|
nuclear@3
|
7 #endif
|
nuclear@0
|
8 #include "opengl.h"
|
nuclear@0
|
9 #include "image.h"
|
nuclear@0
|
10 #include "imago2.h"
|
nuclear@0
|
11
|
nuclear@0
|
12
|
nuclear@0
|
13 static unsigned int next_pow2(unsigned int x);
|
nuclear@0
|
14
|
nuclear@0
|
15 Image::Image()
|
nuclear@0
|
16 {
|
nuclear@0
|
17 width = height = tex_width = tex_height = 0;
|
nuclear@0
|
18 pixels = 0;
|
nuclear@0
|
19 tex = 0;
|
nuclear@0
|
20 tex_valid = false;
|
nuclear@0
|
21 }
|
nuclear@0
|
22
|
nuclear@0
|
23 Image::~Image()
|
nuclear@0
|
24 {
|
nuclear@0
|
25 destroy();
|
nuclear@0
|
26 }
|
nuclear@0
|
27
|
nuclear@0
|
28 Image::Image(const Image &img)
|
nuclear@0
|
29 {
|
nuclear@0
|
30 pixels = 0;
|
nuclear@0
|
31 create(img.width, img.height, img.pixels);
|
nuclear@0
|
32 }
|
nuclear@0
|
33
|
nuclear@0
|
34 Image &Image::operator =(const Image &img)
|
nuclear@0
|
35 {
|
nuclear@0
|
36 if(&img != this) {
|
nuclear@0
|
37 create(img.width, img.height, img.pixels);
|
nuclear@0
|
38 }
|
nuclear@0
|
39 return *this;
|
nuclear@0
|
40 }
|
nuclear@0
|
41
|
nuclear@0
|
42 bool Image::create(int width, int height, unsigned char *pixels)
|
nuclear@0
|
43 {
|
nuclear@0
|
44 destroy();
|
nuclear@0
|
45
|
nuclear@0
|
46 try {
|
nuclear@0
|
47 unsigned char *tmp = new unsigned char[width * height * 4];
|
nuclear@0
|
48 this->pixels = tmp;
|
nuclear@0
|
49 this->width = width;
|
nuclear@0
|
50 this->height = height;
|
nuclear@0
|
51 }
|
nuclear@0
|
52 catch(...) {
|
nuclear@0
|
53 return false;
|
nuclear@0
|
54 }
|
nuclear@0
|
55
|
nuclear@0
|
56 if(pixels) {
|
nuclear@0
|
57 memcpy(this->pixels, pixels, width * height * 4);
|
nuclear@0
|
58 }
|
nuclear@0
|
59 return true;
|
nuclear@0
|
60 }
|
nuclear@0
|
61
|
nuclear@0
|
62 void Image::destroy()
|
nuclear@0
|
63 {
|
nuclear@0
|
64 delete [] pixels;
|
nuclear@0
|
65 pixels = 0;
|
nuclear@0
|
66 width = height = 0;
|
nuclear@0
|
67
|
nuclear@0
|
68 if(tex) {
|
nuclear@0
|
69 glDeleteTextures(1, &tex);
|
nuclear@0
|
70 tex = 0;
|
nuclear@0
|
71 tex_valid = false;
|
nuclear@0
|
72 }
|
nuclear@0
|
73 }
|
nuclear@0
|
74
|
nuclear@0
|
75 bool Image::load(const char *fname)
|
nuclear@0
|
76 {
|
nuclear@0
|
77 int xsz, ysz;
|
nuclear@0
|
78 unsigned char *pix = (unsigned char*)img_load_pixels(fname, &xsz, &ysz, IMG_FMT_RGBA32);
|
nuclear@0
|
79 if(!pix) {
|
nuclear@0
|
80 return false;
|
nuclear@0
|
81 }
|
nuclear@0
|
82 return create(xsz, ysz, pix);
|
nuclear@0
|
83 }
|
nuclear@0
|
84
|
nuclear@0
|
85 bool Image::save(const char *fname) const
|
nuclear@0
|
86 {
|
nuclear@0
|
87 return img_save_pixels(fname, pixels, width, height, IMG_FMT_RGBA32) != -1;
|
nuclear@0
|
88 }
|
nuclear@0
|
89
|
nuclear@0
|
90 unsigned int Image::texture() const
|
nuclear@0
|
91 {
|
nuclear@0
|
92 if(!pixels) {
|
nuclear@0
|
93 return 0;
|
nuclear@0
|
94 }
|
nuclear@0
|
95 if(!tex) {
|
nuclear@0
|
96 glGenTextures(1, &tex);
|
nuclear@0
|
97 }
|
nuclear@0
|
98
|
nuclear@0
|
99 if(!tex_valid) {
|
nuclear@0
|
100 tex_width = next_pow2(width);
|
nuclear@0
|
101 tex_height = next_pow2(height);
|
nuclear@0
|
102
|
nuclear@0
|
103 glBindTexture(GL_TEXTURE_2D, tex);
|
nuclear@0
|
104 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
nuclear@0
|
105 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
nuclear@0
|
106
|
nuclear@0
|
107 if(GLEW_SGIS_generate_mipmap) {
|
nuclear@0
|
108 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, 1);
|
nuclear@0
|
109
|
nuclear@0
|
110 void *data = (width == tex_width && height == tex_height) ? pixels : 0;
|
nuclear@0
|
111 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
nuclear@0
|
112 if(!data) {
|
nuclear@0
|
113 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
nuclear@0
|
114 }
|
nuclear@0
|
115 } else {
|
nuclear@0
|
116 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
nuclear@0
|
117 }
|
nuclear@0
|
118
|
nuclear@0
|
119 if(GLEW_EXT_texture_filter_anisotropic) {
|
nuclear@0
|
120 static float max_aniso = -1.0;
|
nuclear@0
|
121
|
nuclear@0
|
122 if(max_aniso < 0.0) {
|
nuclear@0
|
123 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_aniso);
|
nuclear@0
|
124 printf("using anisotropic filtering: x%g\n", max_aniso);
|
nuclear@0
|
125 }
|
nuclear@0
|
126
|
nuclear@0
|
127 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso);
|
nuclear@0
|
128 }
|
nuclear@0
|
129 tex_valid = true;
|
nuclear@0
|
130 }
|
nuclear@0
|
131 return tex;
|
nuclear@0
|
132 }
|
nuclear@0
|
133
|
nuclear@0
|
134 int Image::texture_width() const
|
nuclear@0
|
135 {
|
nuclear@0
|
136 return tex_width;
|
nuclear@0
|
137 }
|
nuclear@0
|
138
|
nuclear@0
|
139 int Image::texture_height() const
|
nuclear@0
|
140 {
|
nuclear@0
|
141 return tex_height;
|
nuclear@0
|
142 }
|
nuclear@0
|
143
|
nuclear@0
|
144 void Image::invalidate_texture()
|
nuclear@0
|
145 {
|
nuclear@0
|
146 tex_valid = false;
|
nuclear@0
|
147 }
|
nuclear@0
|
148
|
nuclear@0
|
149
|
nuclear@0
|
150 void clear_image(Image *img)
|
nuclear@0
|
151 {
|
nuclear@0
|
152 clear_image(img, 0, 0, 0, 255);
|
nuclear@0
|
153 }
|
nuclear@0
|
154
|
nuclear@0
|
155 void clear_image(Image *img, float r, float g, float b, float a)
|
nuclear@0
|
156 {
|
nuclear@0
|
157 if(!img->pixels) {
|
nuclear@0
|
158 return;
|
nuclear@0
|
159 }
|
nuclear@0
|
160
|
nuclear@0
|
161 unsigned char col[4];
|
nuclear@0
|
162 unsigned char *ptr = img->pixels;
|
nuclear@0
|
163 int npix = img->width * img->height;
|
nuclear@0
|
164
|
nuclear@0
|
165 col[0] = (int)(r * 255.0);
|
nuclear@0
|
166 col[1] = (int)(g * 255.0);
|
nuclear@0
|
167 col[2] = (int)(b * 255.0);
|
nuclear@0
|
168 col[3] = (int)(a * 255.0);
|
nuclear@0
|
169
|
nuclear@0
|
170 for(int i=0; i<npix; i++) {
|
nuclear@0
|
171 for(int j=0; j<4; j++) {
|
nuclear@0
|
172 ptr[j] = col[j];
|
nuclear@0
|
173 }
|
nuclear@0
|
174 ptr += 4;
|
nuclear@0
|
175 }
|
nuclear@0
|
176 }
|
nuclear@0
|
177
|
nuclear@0
|
178 void clear_image_alpha(Image *img, float a)
|
nuclear@0
|
179 {
|
nuclear@0
|
180 if(!img->pixels) {
|
nuclear@0
|
181 return;
|
nuclear@0
|
182 }
|
nuclear@0
|
183
|
nuclear@0
|
184 unsigned char alpha = (int)(a * 255.0);
|
nuclear@0
|
185 unsigned char *ptr = img->pixels;
|
nuclear@0
|
186 int npix = img->width * img->height;
|
nuclear@0
|
187
|
nuclear@0
|
188 for(int i=0; i<npix; i++) {
|
nuclear@0
|
189 ptr[3] = alpha;
|
nuclear@0
|
190 ptr += 4;
|
nuclear@0
|
191 }
|
nuclear@0
|
192 }
|
nuclear@0
|
193
|
nuclear@0
|
194 bool combine_image(Image *dest, const Image *aimg, const Image *bimg, ImgCombine op, float t)
|
nuclear@0
|
195 {
|
nuclear@0
|
196 int xsz = dest->width;
|
nuclear@0
|
197 int ysz = dest->height;
|
nuclear@0
|
198 int npixels = xsz * ysz;
|
nuclear@0
|
199 int nbytes = npixels * 4;
|
nuclear@0
|
200 int tint = (int)(t * 255);
|
nuclear@0
|
201
|
nuclear@0
|
202 if(aimg->width != xsz || bimg->width != xsz || aimg->height != ysz || bimg->height != ysz) {
|
nuclear@0
|
203 return false;
|
nuclear@0
|
204 }
|
nuclear@0
|
205
|
nuclear@0
|
206 unsigned char *dptr = dest->pixels;
|
nuclear@0
|
207 const unsigned char *aptr = aimg->pixels;
|
nuclear@0
|
208 const unsigned char *bptr = bimg->pixels;
|
nuclear@0
|
209
|
nuclear@0
|
210 switch(op) {
|
nuclear@0
|
211 case IMG_OP_ADD:
|
nuclear@0
|
212 for(int i=0; i<nbytes; i++) {
|
nuclear@0
|
213 unsigned int x = *aptr++ + *bptr++;
|
nuclear@0
|
214 *dptr++ = x > 255 ? 255 : x;
|
nuclear@0
|
215 }
|
nuclear@0
|
216 break;
|
nuclear@0
|
217
|
nuclear@0
|
218 case IMG_OP_SUB:
|
nuclear@0
|
219 for(int i=0; i<nbytes; i++) {
|
nuclear@0
|
220 int x = (int)*aptr++ - (int)*bptr++;
|
nuclear@0
|
221 *dptr++ = x < 0 ? 0 : x;
|
nuclear@0
|
222 }
|
nuclear@0
|
223 break;
|
nuclear@0
|
224
|
nuclear@0
|
225 case IMG_OP_MUL:
|
nuclear@0
|
226 for(int i=0; i<nbytes; i++) {
|
nuclear@0
|
227 unsigned int x = ((unsigned int)*aptr++ * (unsigned int)*bptr++) >> 8;
|
nuclear@0
|
228 *dptr++ = x > 255 ? 255 : x;
|
nuclear@0
|
229 }
|
nuclear@0
|
230 break;
|
nuclear@0
|
231
|
nuclear@0
|
232 case IMG_OP_LERP:
|
nuclear@0
|
233 for(int i=0; i<nbytes; i++) {
|
nuclear@0
|
234 int x = (int)*aptr + ((((int)*bptr - (int)*aptr) * tint) >> 8);
|
nuclear@0
|
235 *dptr++ = x > 255 ? 255 : (x < 0 ? 0 : x);
|
nuclear@0
|
236 }
|
nuclear@0
|
237 break;
|
nuclear@0
|
238
|
nuclear@0
|
239 default:
|
nuclear@0
|
240 break;
|
nuclear@0
|
241 }
|
nuclear@0
|
242
|
nuclear@0
|
243 dest->invalidate_texture();
|
nuclear@0
|
244 return true;
|
nuclear@0
|
245 }
|
nuclear@0
|
246
|
nuclear@0
|
247 void convolve_horiz_image(Image *dest, float *kern, int ksz, float scale)
|
nuclear@0
|
248 {
|
nuclear@0
|
249 if((ksz & 1) == 0) {
|
nuclear@0
|
250 fprintf(stderr, "%s: kernel size (%d) must be odd, skipping last value\n", __FUNCTION__, ksz);
|
nuclear@0
|
251 --ksz;
|
nuclear@0
|
252 }
|
nuclear@0
|
253 if(scale == 0.0) {
|
nuclear@0
|
254 // calculate scale factor
|
nuclear@0
|
255 float sum = 0.0;
|
nuclear@0
|
256 for(int i=0; i<ksz; i++) {
|
nuclear@0
|
257 sum += kern[i];
|
nuclear@0
|
258 }
|
nuclear@0
|
259 scale = 1.0 / sum;
|
nuclear@0
|
260 }
|
nuclear@0
|
261 int krad = ksz / 2;
|
nuclear@0
|
262 float *buf = (float*)alloca(dest->width * 4 * sizeof *buf);
|
nuclear@0
|
263 unsigned char *sptr = dest->pixels;
|
nuclear@0
|
264
|
nuclear@0
|
265 for(int i=0; i<dest->height; i++) {
|
nuclear@0
|
266 float *bptr = buf;
|
nuclear@0
|
267 for(int j=0; j<dest->width * 4; j++) {
|
nuclear@0
|
268 *bptr++ = (float)(sptr[j] / 255.0);
|
nuclear@0
|
269 }
|
nuclear@0
|
270
|
nuclear@0
|
271 for(int j=0; j<dest->width; j++) {
|
nuclear@0
|
272 float col[] = {0, 0, 0, 0};
|
nuclear@0
|
273
|
nuclear@0
|
274 for(int k=0; k<ksz; k++) {
|
nuclear@0
|
275 int idx = j + k - krad;
|
nuclear@0
|
276 if(idx < 0) idx = 0;
|
nuclear@0
|
277 if(idx >= dest->width) idx = dest->width - 1;
|
nuclear@0
|
278
|
nuclear@0
|
279 col[0] += buf[idx * 4] * kern[k];
|
nuclear@0
|
280 col[1] += buf[idx * 4 + 1] * kern[k];
|
nuclear@0
|
281 col[2] += buf[idx * 4 + 2] * kern[k];
|
nuclear@0
|
282 col[3] += buf[idx * 4 + 3] * kern[k];
|
nuclear@0
|
283 }
|
nuclear@0
|
284
|
nuclear@0
|
285 int ri = (int)(col[0] * scale * 255.0);
|
nuclear@0
|
286 int gi = (int)(col[1] * scale * 255.0);
|
nuclear@0
|
287 int bi = (int)(col[2] * scale * 255.0);
|
nuclear@0
|
288 int ai = (int)(col[3] * scale * 255.0);
|
nuclear@0
|
289
|
nuclear@0
|
290 sptr[0] = ri < 0 ? 0 : (ri > 255 ? 255 : ri);
|
nuclear@0
|
291 sptr[1] = gi < 0 ? 0 : (gi > 255 ? 255 : gi);
|
nuclear@0
|
292 sptr[2] = bi < 0 ? 0 : (bi > 255 ? 255 : bi);
|
nuclear@0
|
293 sptr[3] = ai < 0 ? 0 : (ai > 255 ? 255 : ai);
|
nuclear@0
|
294 sptr += 4;
|
nuclear@0
|
295 }
|
nuclear@0
|
296 }
|
nuclear@0
|
297
|
nuclear@0
|
298 dest->invalidate_texture();
|
nuclear@0
|
299 }
|
nuclear@0
|
300
|
nuclear@0
|
301 void convolve_vert_image(Image *dest, float *kern, int ksz, float scale)
|
nuclear@0
|
302 {
|
nuclear@0
|
303 if((ksz & 1) == 0) {
|
nuclear@0
|
304 fprintf(stderr, "%s: kernel size (%d) must be odd, skipping last value\n", __FUNCTION__, ksz);
|
nuclear@0
|
305 --ksz;
|
nuclear@0
|
306 }
|
nuclear@0
|
307 if(scale == 0.0) {
|
nuclear@0
|
308 // calculate scale factor
|
nuclear@0
|
309 float sum = 0.0;
|
nuclear@0
|
310 for(int i=0; i<ksz; i++) {
|
nuclear@0
|
311 sum += kern[i];
|
nuclear@0
|
312 }
|
nuclear@0
|
313 scale = 1.0 / sum;
|
nuclear@0
|
314 }
|
nuclear@0
|
315 int krad = ksz / 2;
|
nuclear@0
|
316 float *buf = (float*)alloca(dest->height * 4 * sizeof *buf);
|
nuclear@0
|
317 unsigned char *sptr = dest->pixels;
|
nuclear@0
|
318
|
nuclear@0
|
319 for(int i=0; i<dest->width; i++) {
|
nuclear@0
|
320 float *bptr = buf;
|
nuclear@0
|
321 sptr = dest->pixels + i * 4;
|
nuclear@0
|
322
|
nuclear@0
|
323 for(int j=0; j<dest->height; j++) {
|
nuclear@0
|
324 for(int k=0; k<4; k++) {
|
nuclear@0
|
325 *bptr++ = (float)(sptr[k] / 255.0);
|
nuclear@0
|
326 }
|
nuclear@0
|
327 sptr += dest->width * 4;
|
nuclear@0
|
328 }
|
nuclear@0
|
329
|
nuclear@0
|
330 sptr = dest->pixels + i * 4;
|
nuclear@0
|
331
|
nuclear@0
|
332 for(int j=0; j<dest->height; j++) {
|
nuclear@0
|
333 float col[] = {0, 0, 0, 0};
|
nuclear@0
|
334
|
nuclear@0
|
335 for(int k=0; k<ksz; k++) {
|
nuclear@0
|
336 int idx = j + k - krad;
|
nuclear@0
|
337 if(idx < 0) idx = 0;
|
nuclear@0
|
338 if(idx >= dest->height) idx = dest->height - 1;
|
nuclear@0
|
339
|
nuclear@0
|
340 col[0] += buf[idx * 4] * kern[k];
|
nuclear@0
|
341 col[1] += buf[idx * 4 + 1] * kern[k];
|
nuclear@0
|
342 col[2] += buf[idx * 4 + 2] * kern[k];
|
nuclear@0
|
343 col[3] += buf[idx * 4 + 3] * kern[k];
|
nuclear@0
|
344 }
|
nuclear@0
|
345
|
nuclear@0
|
346 int ri = (int)(col[0] * scale * 255.0);
|
nuclear@0
|
347 int gi = (int)(col[1] * scale * 255.0);
|
nuclear@0
|
348 int bi = (int)(col[2] * scale * 255.0);
|
nuclear@0
|
349 int ai = (int)(col[3] * scale * 255.0);
|
nuclear@0
|
350
|
nuclear@0
|
351 sptr[0] = ri < 0 ? 0 : (ri > 255 ? 255 : ri);
|
nuclear@0
|
352 sptr[1] = gi < 0 ? 0 : (gi > 255 ? 255 : gi);
|
nuclear@0
|
353 sptr[2] = bi < 0 ? 0 : (bi > 255 ? 255 : bi);
|
nuclear@0
|
354 sptr[3] = ai < 0 ? 0 : (ai > 255 ? 255 : ai);
|
nuclear@0
|
355 sptr += dest->width * 4;
|
nuclear@0
|
356 }
|
nuclear@0
|
357 }
|
nuclear@0
|
358 }
|
nuclear@0
|
359
|
nuclear@0
|
360 static unsigned int next_pow2(unsigned int x)
|
nuclear@0
|
361 {
|
nuclear@0
|
362 x--;
|
nuclear@0
|
363 x = (x >> 1) | x;
|
nuclear@0
|
364 x = (x >> 2) | x;
|
nuclear@0
|
365 x = (x >> 4) | x;
|
nuclear@0
|
366 x = (x >> 8) | x;
|
nuclear@0
|
367 x = (x >> 16) | x;
|
nuclear@0
|
368 return x + 1;
|
nuclear@0
|
369 }
|