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