2 Copyright 2004 John Tsiombikas <nuclear@siggraph.org>
4 This file is part of the 3dengfx, realtime visualization system.
6 3dengfx is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 3dengfx is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with 3dengfx; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * author: Mihalis Georgoulopoulos 2004
23 * modified: John Tsiombikas 2004
31 #include "img_manip.hpp"
33 #include "common/err_msg.h"
36 #define PACK_ARGB32(a,r,g,b) PACK_COLOR32(a,r,g,b)
38 #define GETA(c) (((c) >> ALPHA_SHIFT32) & ALPHA_MASK32)
39 #define GETR(c) (((c) >> RED_SHIFT32) & RED_MASK32)
40 #define GETG(c) (((c) >> GREEN_SHIFT32) & GREEN_MASK32)
41 #define GETB(c) (((c) >> BLUE_SHIFT32) & BLUE_MASK32)
43 static inline scalar_t cerp(scalar_t x0, scalar_t x1, scalar_t x2, scalar_t x3, scalar_t t);
44 static inline int clamp_integer(int i, int from, int to);
46 // ------------ simple operations ----------------
47 void clear_pixel_buffer(PixelBuffer *pb, const Color &col) {
48 int sz = pb->width * pb->height;
49 Pixel pcol = pack_color32(col);
50 Pixel *ptr = pb->buffer;
52 for(int i=0; i<sz; i++) {
57 // ------------ resampling ------------------
59 static bool resample_line(scalar_t *dst, int dst_width, int dst_pitch, scalar_t *src, int src_width, int src_pitch)
61 if (!dst || !src) return false;
63 scalar_t x0,x1,x2,x3,t;
65 for (int i=0;i<dst_width;i++)
67 i1 = (i*src_width)/dst_width;
68 i0 = i1 - 1; if(i0 < 0) i0 = 0;
69 i2 = i1 + 1; if(i2 >= src_width) i2 = src_width - 1;
70 i3 = i1 + 2; if(i3 >= src_width) i3 = src_width - 1;
72 x0 = src[i0 * src_pitch];
73 x1 = src[i1 * src_pitch];
74 x2 = src[i2 * src_pitch];
75 x3 = src[i3 * src_pitch];
77 t = ((scalar_t)i * (scalar_t)src_width) / (scalar_t)dst_width;
80 // write the destination element
81 dst[i * dst_pitch] = cerp(x0, x1, x2, x3, t);
87 static bool resample2d(scalar_t *dst, int dst_w, int dst_h, scalar_t *src, int src_w, int src_h)
89 if (!src || !dst) return false;
91 if (dst_w == src_w && dst_h == src_h)
93 memcpy(dst, src, dst_w * dst_h * sizeof(scalar_t));
97 // first resample along x
98 scalar_t *temp = (scalar_t*)malloc(dst_w * src_h * sizeof(scalar_t));
102 memcpy(temp, src, src_w * src_h * sizeof(scalar_t));
106 // horizontal resample
107 for (int i=0;i<src_h;i++)
109 resample_line(temp + i*dst_w, dst_w, 1, src + i * src_w, src_w, 1);
113 // Now temp is stretched horizontally
114 // stretch vertically
117 memcpy(dst, temp, dst_w * dst_h * sizeof(scalar_t));
122 for (int i=0; i<dst_w; i++)
124 resample_line(dst+i, dst_h, dst_w, temp + i, src_h, dst_w);
134 static bool pack_scalar_rgb2dw(Pixel *dst, scalar_t *ac, scalar_t *rc, scalar_t *gc, scalar_t *bc, int samples)
136 if (!dst || !ac || !rc || !gc || !bc) return false;
140 for (int i=0;i<samples;i++)
142 a = (int)(ac[i] + 0.5f);
143 r = (int)(rc[i] + 0.5f);
144 g = (int)(gc[i] + 0.5f);
145 b = (int)(bc[i] + 0.5f);
147 a = clamp_integer(a, 0, 255);
148 r = clamp_integer(r, 0, 255);
149 g = clamp_integer(g, 0, 255);
150 b = clamp_integer(b, 0, 255);
152 dst[i] = PACK_ARGB32(a, r, g, b);
158 bool resample_pixel_buffer(PixelBuffer *pb, int w, int h)
160 if (!pb || !pb->buffer || pb->width < 0 || pb->height < 0) return false;
162 if ((int)pb->width == w && (int)pb->height == h) return true;
165 scalar_t *a, *newa, *r, *newr, *g, *newg, *b, *newb;
167 a = (scalar_t*)malloc(pb->width * pb->height * sizeof(scalar_t));
168 r = (scalar_t*)malloc(pb->width * pb->height * sizeof(scalar_t));
169 g = (scalar_t*)malloc(pb->width * pb->height * sizeof(scalar_t));
170 b = (scalar_t*)malloc(pb->width * pb->height * sizeof(scalar_t));
172 newa = (scalar_t*)malloc(w * h * sizeof(scalar_t));
173 newr = (scalar_t*)malloc(w * h * sizeof(scalar_t));
174 newg = (scalar_t*)malloc(w * h * sizeof(scalar_t));
175 newb = (scalar_t*)malloc(w * h * sizeof(scalar_t));
177 for(int i=0; i<(int)(pb->width * pb->height); i++)
179 a[i] = GETA(pb->buffer[i]);
180 r[i] = GETR(pb->buffer[i]);
181 g[i] = GETG(pb->buffer[i]);
182 b[i] = GETB(pb->buffer[i]);
186 resample2d(newa, w, h, a, pb->width, pb->height);
187 resample2d(newr, w, h, r, pb->width, pb->height);
188 resample2d(newg, w, h, g, pb->width, pb->height);
189 resample2d(newb, w, h, b, pb->width, pb->height);
192 Pixel *temp = (Pixel*)malloc(w * h * sizeof(Pixel));
193 pack_scalar_rgb2dw(temp, newa, newr, newg, newb, w * h);
201 free(a); free(r); free(g); free(b);
202 free(newa); free(newr); free(newg); free(newb);
208 // --- static inline functions ---
210 static inline scalar_t cerp(scalar_t x0, scalar_t x1, scalar_t x2, scalar_t x3, scalar_t t)
212 scalar_t a0, a1, a2, a3, t2;
215 a0 = x3 - x2 - x0 + x1;
220 return(a0 * t * t2 + a1 * t2 + a2 * t + a3);
224 #define MIN(a, b) ((a) < (b) ? (a) : (b))
225 #define MAX(a, b) ((a) > (b) ? (a) : (b))
226 #define CLAMP(n, l, h) MIN(MAX((n), (l)), (h))
228 static inline int clamp_integer(int i, int from, int to)
230 return CLAMP(i, from, to);
234 //----------------------------------------------------------------
236 static ImgSamplingMode samp_mode = SAMPLE_CLAMP;
238 static inline int map_index(int c, int dim)
247 while(c < 0) c += dim;
250 if (c >= dim) return c % dim;
255 if (c < 0) return -c;
256 if (c >= dim) return dim - c - 1;
264 if (c >= dim) return dim - 1;
272 static void split_channels(Pixel *img, Pixel *a, Pixel *r, Pixel *g, Pixel *b, unsigned int pixel_count)
274 for(unsigned int i=0; i<pixel_count; i++)
284 static void join_channels(Pixel *img, Pixel *a, Pixel *r, Pixel *g, Pixel *b, unsigned int pixel_count)
286 for(unsigned int i=0; i<pixel_count; i++)
288 *img++ = PACK_ARGB32(*a++, *r++, *g++, *b++);
292 static inline Pixel fetch_pixel(int x, int y, Pixel *img, int w, int h)
300 static Pixel* apply_kernel_to_channel(int *kernel, int kernel_dim, Pixel *img, int w, int h)
303 if (!(kernel_dim % 2)) return 0;
304 if (!kernel || !img) return 0;
305 if ((w <= 0) || (h <= 0)) return 0;
307 int kernel_l = kernel_dim * kernel_dim;
308 int kernel_center = kernel_dim / 2;
310 for (int i=0; i<kernel_l; i++)
312 kernel_sum += kernel[i];
316 Pixel *temp = (Pixel*)malloc(w * h * sizeof(Pixel));
319 for(int j=0; j<h; j++)
321 for(int i=0; i<w; i++)
326 for(int kj=0; kj<kernel_dim; kj++)
328 for(int ki=0; ki<kernel_dim; ki++)
330 int pixel = (int)fetch_pixel(i + ki - kernel_center, j + kj - kernel_center, img, w, h);
331 sum += pixel * kernel[ki + kernel_dim * kj];
338 temp[i + j * w] = CLAMP(sum, 0, 255);
345 bool apply_kernel(PixelBuffer *pb, int *kernel, int kernel_dim, ImgSamplingMode sampling)
347 if(!pb || !pb->buffer) return false;
348 if(pb->width <= 0 || pb->height <= 0) return false;
349 if(!(kernel_dim / 2)) return false;
351 unsigned int sz = pb->width * pb->height;
354 samp_mode = sampling;
357 Pixel *tempa = (Pixel*)malloc(sz * sizeof(Pixel));
358 Pixel *tempr = (Pixel*)malloc(sz * sizeof(Pixel));
359 Pixel *tempg = (Pixel*)malloc(sz * sizeof(Pixel));
360 Pixel *tempb = (Pixel*)malloc(sz * sizeof(Pixel));
363 split_channels(pb->buffer, tempa, tempr, tempg, tempb, sz);
366 Pixel *a = apply_kernel_to_channel(kernel, kernel_dim, tempa, pb->width, pb->height);
369 Pixel *r = apply_kernel_to_channel(kernel, kernel_dim, tempr, pb->width, pb->height);
372 Pixel *g = apply_kernel_to_channel(kernel, kernel_dim, tempg, pb->width, pb->height);
375 Pixel *b = apply_kernel_to_channel(kernel, kernel_dim, tempb, pb->width, pb->height);
379 join_channels(pb->buffer, a, r, g, b, sz);
389 int* load_kernel(const char* filename, int *dim)
391 // try to open the file
392 FILE *input = fopen(filename, "r");
394 if (!input) return 0;
396 fseek(input , 0 , SEEK_END);
397 int size = ftell(input);
398 fseek(input , 0 , SEEK_SET);
401 //char *s = (char*) malloc(size+1);
407 for(i=0; i<size; i++)
412 // clear comments and commas
418 while(s[j] != '\n' && s[j] != 0)
426 if (s[j]==',') s[j]= ' ';
431 // remove everything indeed
432 for(j=0; j<size; j++) {
433 if (s[j] == '\n' || s[j] == '\t') s[j] = ' ';
442 while(s[index] == ' ') index++;
443 while(s[index] != ' ')
450 if(!isdigit(temp[0])) {
452 error("load_kernel() failed, invalid kernel file format: %s\n", filename);
457 int *kernel = (int*)malloc(num * num * sizeof(int));
459 for (int n=0; n<num*num; n++)
463 while (s[index] == ' ') index++;
464 while (s[index] != ' ')
471 if(!isdigit(temp[0]) && temp[0] != '-' && temp[0] != '+') {
474 error("load_kernel() failed, invalid kernel file format: %s\n", filename);
477 kernel[n] = atoi(temp);
488 * Applies the sobel edge detection algorithm to the pixel buffer
490 bool sobel_edge(PixelBuffer *pb, ImgSamplingMode sampling) {
491 int sobel_horiz[] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
492 int sobel_vert[] = {-1, -2, -1, 0, 0, 0, 1, 2, 1};
494 PixelBuffer horiz = *pb;
495 PixelBuffer vert = *pb;
497 if(!apply_kernel(&horiz, sobel_horiz, 3, sampling)) return false;
498 if(!apply_kernel(&vert, sobel_vert, 3, sampling)) return false;
500 Pixel *vptr = vert.buffer;
501 Pixel *hptr = horiz.buffer;
502 Pixel *dest = pb->buffer;
503 int sz = pb->width * pb->height;
505 for(int i=0; i<sz; i++) {
506 Color vcol = unpack_color32(*vptr++);
507 Color hcol = unpack_color32(*hptr++);
509 scalar_t r = sqrt(hcol.r * hcol.r + vcol.r * vcol.r);
510 scalar_t g = sqrt(hcol.g * hcol.g + vcol.g * vcol.g);
511 scalar_t b = sqrt(hcol.b * hcol.b + vcol.b * vcol.b);
513 *dest++ = pack_color32(Color(r, g, b));
520 static inline Pixel blur_pixels(Pixel p1, Pixel p2)
522 // static temp colors
523 static Pixel tempc1, tempc2, tempc3, tempc4;
525 // blur all channels in a SIMD-like manner
526 tempc1 = tempc2 = p1;
527 tempc3 = tempc4 = p2;
530 // dividing 2 channels with a single operation
531 tempc1 &= 0xff00ff00; tempc1 >>= 1;
532 tempc2 &= 0x00ff00ff; tempc2 >>= 1;
533 tempc3 &= 0xff00ff00; tempc3 >>= 1;
534 tempc4 &= 0x00ff00ff; tempc4 >>= 1;
536 tempc1 = (tempc1 + tempc3) & 0xff00ff00;
537 tempc2 = (tempc2 + tempc4) & 0x00ff00ff;
539 return tempc1 | tempc2;
543 bool blur(PixelBuffer *pb, ImgSamplingMode sampling)
545 if(!pb) return false;
546 if(pb->width <= 0 || pb->height <= 0) return false;
548 samp_mode = sampling;
550 Pixel *temp = (Pixel*)malloc(pb->width * pb->height * sizeof(Pixel));
552 Pixel *scanline = pb->buffer;
553 Pixel *dst_scanline = temp;
556 for(unsigned int j=0; j<pb->height; j++)
558 for(unsigned int i=0; i<pb->width; i++)
560 dst_scanline[i] = blur_pixels(scanline[map_index(i-1 , pb->width)], scanline[map_index(i+1 , pb->width)]);
562 scanline += pb->width;
563 dst_scanline += pb->width;
567 for(unsigned int i=0; i<pb->width; i++)
569 for(unsigned int j=0;j<pb->height;j++)
571 pb->buffer[i+j*pb->width] = blur_pixels(temp[i+map_index(j-1,pb->height)*pb->width], temp[i+map_index(j+1,pb->height)*pb->width]);