dbf-halloween2015
diff src/image.cc @ 0:50683c78264e
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 01 Nov 2015 00:09:12 +0200 |
parents | |
children | c37fe5d8a4ed |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/image.cc Sun Nov 01 00:09:12 2015 +0200 1.3 @@ -0,0 +1,365 @@ 1.4 +#include <string.h> 1.5 +#include <stdlib.h> 1.6 +#include <alloca.h> 1.7 +#include "opengl.h" 1.8 +#include "image.h" 1.9 +#include "imago2.h" 1.10 + 1.11 + 1.12 +static unsigned int next_pow2(unsigned int x); 1.13 + 1.14 +Image::Image() 1.15 +{ 1.16 + width = height = tex_width = tex_height = 0; 1.17 + pixels = 0; 1.18 + tex = 0; 1.19 + tex_valid = false; 1.20 +} 1.21 + 1.22 +Image::~Image() 1.23 +{ 1.24 + destroy(); 1.25 +} 1.26 + 1.27 +Image::Image(const Image &img) 1.28 +{ 1.29 + pixels = 0; 1.30 + create(img.width, img.height, img.pixels); 1.31 +} 1.32 + 1.33 +Image &Image::operator =(const Image &img) 1.34 +{ 1.35 + if(&img != this) { 1.36 + create(img.width, img.height, img.pixels); 1.37 + } 1.38 + return *this; 1.39 +} 1.40 + 1.41 +bool Image::create(int width, int height, unsigned char *pixels) 1.42 +{ 1.43 + destroy(); 1.44 + 1.45 + try { 1.46 + unsigned char *tmp = new unsigned char[width * height * 4]; 1.47 + this->pixels = tmp; 1.48 + this->width = width; 1.49 + this->height = height; 1.50 + } 1.51 + catch(...) { 1.52 + return false; 1.53 + } 1.54 + 1.55 + if(pixels) { 1.56 + memcpy(this->pixels, pixels, width * height * 4); 1.57 + } 1.58 + return true; 1.59 +} 1.60 + 1.61 +void Image::destroy() 1.62 +{ 1.63 + delete [] pixels; 1.64 + pixels = 0; 1.65 + width = height = 0; 1.66 + 1.67 + if(tex) { 1.68 + glDeleteTextures(1, &tex); 1.69 + tex = 0; 1.70 + tex_valid = false; 1.71 + } 1.72 +} 1.73 + 1.74 +bool Image::load(const char *fname) 1.75 +{ 1.76 + int xsz, ysz; 1.77 + unsigned char *pix = (unsigned char*)img_load_pixels(fname, &xsz, &ysz, IMG_FMT_RGBA32); 1.78 + if(!pix) { 1.79 + return false; 1.80 + } 1.81 + return create(xsz, ysz, pix); 1.82 +} 1.83 + 1.84 +bool Image::save(const char *fname) const 1.85 +{ 1.86 + return img_save_pixels(fname, pixels, width, height, IMG_FMT_RGBA32) != -1; 1.87 +} 1.88 + 1.89 +unsigned int Image::texture() const 1.90 +{ 1.91 + if(!pixels) { 1.92 + return 0; 1.93 + } 1.94 + if(!tex) { 1.95 + glGenTextures(1, &tex); 1.96 + } 1.97 + 1.98 + if(!tex_valid) { 1.99 + tex_width = next_pow2(width); 1.100 + tex_height = next_pow2(height); 1.101 + 1.102 + glBindTexture(GL_TEXTURE_2D, tex); 1.103 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 1.104 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 1.105 + 1.106 + if(GLEW_SGIS_generate_mipmap) { 1.107 + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, 1); 1.108 + 1.109 + void *data = (width == tex_width && height == tex_height) ? pixels : 0; 1.110 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 1.111 + if(!data) { 1.112 + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 1.113 + } 1.114 + } else { 1.115 + gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 1.116 + } 1.117 + 1.118 + if(GLEW_EXT_texture_filter_anisotropic) { 1.119 + static float max_aniso = -1.0; 1.120 + 1.121 + if(max_aniso < 0.0) { 1.122 + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_aniso); 1.123 + printf("using anisotropic filtering: x%g\n", max_aniso); 1.124 + } 1.125 + 1.126 + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); 1.127 + } 1.128 + tex_valid = true; 1.129 + } 1.130 + return tex; 1.131 +} 1.132 + 1.133 +int Image::texture_width() const 1.134 +{ 1.135 + return tex_width; 1.136 +} 1.137 + 1.138 +int Image::texture_height() const 1.139 +{ 1.140 + return tex_height; 1.141 +} 1.142 + 1.143 +void Image::invalidate_texture() 1.144 +{ 1.145 + tex_valid = false; 1.146 +} 1.147 + 1.148 + 1.149 +void clear_image(Image *img) 1.150 +{ 1.151 + clear_image(img, 0, 0, 0, 255); 1.152 +} 1.153 + 1.154 +void clear_image(Image *img, float r, float g, float b, float a) 1.155 +{ 1.156 + if(!img->pixels) { 1.157 + return; 1.158 + } 1.159 + 1.160 + unsigned char col[4]; 1.161 + unsigned char *ptr = img->pixels; 1.162 + int npix = img->width * img->height; 1.163 + 1.164 + col[0] = (int)(r * 255.0); 1.165 + col[1] = (int)(g * 255.0); 1.166 + col[2] = (int)(b * 255.0); 1.167 + col[3] = (int)(a * 255.0); 1.168 + 1.169 + for(int i=0; i<npix; i++) { 1.170 + for(int j=0; j<4; j++) { 1.171 + ptr[j] = col[j]; 1.172 + } 1.173 + ptr += 4; 1.174 + } 1.175 +} 1.176 + 1.177 +void clear_image_alpha(Image *img, float a) 1.178 +{ 1.179 + if(!img->pixels) { 1.180 + return; 1.181 + } 1.182 + 1.183 + unsigned char alpha = (int)(a * 255.0); 1.184 + unsigned char *ptr = img->pixels; 1.185 + int npix = img->width * img->height; 1.186 + 1.187 + for(int i=0; i<npix; i++) { 1.188 + ptr[3] = alpha; 1.189 + ptr += 4; 1.190 + } 1.191 +} 1.192 + 1.193 +bool combine_image(Image *dest, const Image *aimg, const Image *bimg, ImgCombine op, float t) 1.194 +{ 1.195 + int xsz = dest->width; 1.196 + int ysz = dest->height; 1.197 + int npixels = xsz * ysz; 1.198 + int nbytes = npixels * 4; 1.199 + int tint = (int)(t * 255); 1.200 + 1.201 + if(aimg->width != xsz || bimg->width != xsz || aimg->height != ysz || bimg->height != ysz) { 1.202 + return false; 1.203 + } 1.204 + 1.205 + unsigned char *dptr = dest->pixels; 1.206 + const unsigned char *aptr = aimg->pixels; 1.207 + const unsigned char *bptr = bimg->pixels; 1.208 + 1.209 + switch(op) { 1.210 + case IMG_OP_ADD: 1.211 + for(int i=0; i<nbytes; i++) { 1.212 + unsigned int x = *aptr++ + *bptr++; 1.213 + *dptr++ = x > 255 ? 255 : x; 1.214 + } 1.215 + break; 1.216 + 1.217 + case IMG_OP_SUB: 1.218 + for(int i=0; i<nbytes; i++) { 1.219 + int x = (int)*aptr++ - (int)*bptr++; 1.220 + *dptr++ = x < 0 ? 0 : x; 1.221 + } 1.222 + break; 1.223 + 1.224 + case IMG_OP_MUL: 1.225 + for(int i=0; i<nbytes; i++) { 1.226 + unsigned int x = ((unsigned int)*aptr++ * (unsigned int)*bptr++) >> 8; 1.227 + *dptr++ = x > 255 ? 255 : x; 1.228 + } 1.229 + break; 1.230 + 1.231 + case IMG_OP_LERP: 1.232 + for(int i=0; i<nbytes; i++) { 1.233 + int x = (int)*aptr + ((((int)*bptr - (int)*aptr) * tint) >> 8); 1.234 + *dptr++ = x > 255 ? 255 : (x < 0 ? 0 : x); 1.235 + } 1.236 + break; 1.237 + 1.238 + default: 1.239 + break; 1.240 + } 1.241 + 1.242 + dest->invalidate_texture(); 1.243 + return true; 1.244 +} 1.245 + 1.246 +void convolve_horiz_image(Image *dest, float *kern, int ksz, float scale) 1.247 +{ 1.248 + if((ksz & 1) == 0) { 1.249 + fprintf(stderr, "%s: kernel size (%d) must be odd, skipping last value\n", __FUNCTION__, ksz); 1.250 + --ksz; 1.251 + } 1.252 + if(scale == 0.0) { 1.253 + // calculate scale factor 1.254 + float sum = 0.0; 1.255 + for(int i=0; i<ksz; i++) { 1.256 + sum += kern[i]; 1.257 + } 1.258 + scale = 1.0 / sum; 1.259 + } 1.260 + int krad = ksz / 2; 1.261 + float *buf = (float*)alloca(dest->width * 4 * sizeof *buf); 1.262 + unsigned char *sptr = dest->pixels; 1.263 + 1.264 + for(int i=0; i<dest->height; i++) { 1.265 + float *bptr = buf; 1.266 + for(int j=0; j<dest->width * 4; j++) { 1.267 + *bptr++ = (float)(sptr[j] / 255.0); 1.268 + } 1.269 + 1.270 + for(int j=0; j<dest->width; j++) { 1.271 + float col[] = {0, 0, 0, 0}; 1.272 + 1.273 + for(int k=0; k<ksz; k++) { 1.274 + int idx = j + k - krad; 1.275 + if(idx < 0) idx = 0; 1.276 + if(idx >= dest->width) idx = dest->width - 1; 1.277 + 1.278 + col[0] += buf[idx * 4] * kern[k]; 1.279 + col[1] += buf[idx * 4 + 1] * kern[k]; 1.280 + col[2] += buf[idx * 4 + 2] * kern[k]; 1.281 + col[3] += buf[idx * 4 + 3] * kern[k]; 1.282 + } 1.283 + 1.284 + int ri = (int)(col[0] * scale * 255.0); 1.285 + int gi = (int)(col[1] * scale * 255.0); 1.286 + int bi = (int)(col[2] * scale * 255.0); 1.287 + int ai = (int)(col[3] * scale * 255.0); 1.288 + 1.289 + sptr[0] = ri < 0 ? 0 : (ri > 255 ? 255 : ri); 1.290 + sptr[1] = gi < 0 ? 0 : (gi > 255 ? 255 : gi); 1.291 + sptr[2] = bi < 0 ? 0 : (bi > 255 ? 255 : bi); 1.292 + sptr[3] = ai < 0 ? 0 : (ai > 255 ? 255 : ai); 1.293 + sptr += 4; 1.294 + } 1.295 + } 1.296 + 1.297 + dest->invalidate_texture(); 1.298 +} 1.299 + 1.300 +void convolve_vert_image(Image *dest, float *kern, int ksz, float scale) 1.301 +{ 1.302 + if((ksz & 1) == 0) { 1.303 + fprintf(stderr, "%s: kernel size (%d) must be odd, skipping last value\n", __FUNCTION__, ksz); 1.304 + --ksz; 1.305 + } 1.306 + if(scale == 0.0) { 1.307 + // calculate scale factor 1.308 + float sum = 0.0; 1.309 + for(int i=0; i<ksz; i++) { 1.310 + sum += kern[i]; 1.311 + } 1.312 + scale = 1.0 / sum; 1.313 + } 1.314 + int krad = ksz / 2; 1.315 + float *buf = (float*)alloca(dest->height * 4 * sizeof *buf); 1.316 + unsigned char *sptr = dest->pixels; 1.317 + 1.318 + for(int i=0; i<dest->width; i++) { 1.319 + float *bptr = buf; 1.320 + sptr = dest->pixels + i * 4; 1.321 + 1.322 + for(int j=0; j<dest->height; j++) { 1.323 + for(int k=0; k<4; k++) { 1.324 + *bptr++ = (float)(sptr[k] / 255.0); 1.325 + } 1.326 + sptr += dest->width * 4; 1.327 + } 1.328 + 1.329 + sptr = dest->pixels + i * 4; 1.330 + 1.331 + for(int j=0; j<dest->height; j++) { 1.332 + float col[] = {0, 0, 0, 0}; 1.333 + 1.334 + for(int k=0; k<ksz; k++) { 1.335 + int idx = j + k - krad; 1.336 + if(idx < 0) idx = 0; 1.337 + if(idx >= dest->height) idx = dest->height - 1; 1.338 + 1.339 + col[0] += buf[idx * 4] * kern[k]; 1.340 + col[1] += buf[idx * 4 + 1] * kern[k]; 1.341 + col[2] += buf[idx * 4 + 2] * kern[k]; 1.342 + col[3] += buf[idx * 4 + 3] * kern[k]; 1.343 + } 1.344 + 1.345 + int ri = (int)(col[0] * scale * 255.0); 1.346 + int gi = (int)(col[1] * scale * 255.0); 1.347 + int bi = (int)(col[2] * scale * 255.0); 1.348 + int ai = (int)(col[3] * scale * 255.0); 1.349 + 1.350 + sptr[0] = ri < 0 ? 0 : (ri > 255 ? 255 : ri); 1.351 + sptr[1] = gi < 0 ? 0 : (gi > 255 ? 255 : gi); 1.352 + sptr[2] = bi < 0 ? 0 : (bi > 255 ? 255 : bi); 1.353 + sptr[3] = ai < 0 ? 0 : (ai > 255 ? 255 : ai); 1.354 + sptr += dest->width * 4; 1.355 + } 1.356 + } 1.357 +} 1.358 + 1.359 +static unsigned int next_pow2(unsigned int x) 1.360 +{ 1.361 + x--; 1.362 + x = (x >> 1) | x; 1.363 + x = (x >> 2) | x; 1.364 + x = (x >> 4) | x; 1.365 + x = (x >> 8) | x; 1.366 + x = (x >> 16) | x; 1.367 + return x + 1; 1.368 +}