added 3dengfx into the repo, probably not the correct version for this
[summerhack] / src / 3dengfx / src / gfx / img_manip.cpp
1 /*
2 Copyright 2004 John Tsiombikas <nuclear@siggraph.org>
3
4 This file is part of the 3dengfx, realtime visualization system.
5
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.
10
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.
15
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
19 */
20
21 /* image manipulation
22  * author: Mihalis Georgoulopoulos 2004
23  * modified: John Tsiombikas 2004
24  */
25
26 #include <cstdio>
27 #include <cstdlib>
28 #include <cmath>
29 #include <cstring>
30 #include <cctype>
31 #include "img_manip.hpp"
32 #include "color.hpp"
33 #include "common/err_msg.h"
34
35 // Macros
36 #define PACK_ARGB32(a,r,g,b)    PACK_COLOR32(a,r,g,b)
37
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)
42
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);
45
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;
51         
52         for(int i=0; i<sz; i++) {
53                 *ptr++ = pcol;
54         }
55 }
56
57 // ------------ resampling ------------------
58
59 static bool resample_line(scalar_t *dst, int dst_width, int dst_pitch, scalar_t *src, int src_width, int src_pitch)
60 {
61         if (!dst || !src) return false;
62
63         scalar_t x0,x1,x2,x3,t;
64         int i0,i1,i2,i3;
65         for (int i=0;i<dst_width;i++)
66         {
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;
71
72                 x0 = src[i0 * src_pitch];
73                 x1 = src[i1 * src_pitch];
74                 x2 = src[i2 * src_pitch];
75                 x3 = src[i3 * src_pitch];
76
77                 t = ((scalar_t)i * (scalar_t)src_width) / (scalar_t)dst_width;
78                 t -= i1;
79
80                 // write the destination element
81                 dst[i * dst_pitch] = cerp(x0, x1, x2, x3, t);
82         }
83
84         return true;
85 }
86
87 static bool resample2d(scalar_t *dst, int dst_w, int dst_h, scalar_t *src, int src_w, int src_h)
88 {
89         if (!src || !dst) return false;
90
91         if (dst_w == src_w && dst_h == src_h)
92         {
93                 memcpy(dst, src, dst_w * dst_h * sizeof(scalar_t));
94                 return true;
95         }
96
97         // first resample along x
98         scalar_t *temp = (scalar_t*)malloc(dst_w * src_h * sizeof(scalar_t));
99
100         if (dst_w == src_w)
101         {
102                 memcpy(temp, src, src_w * src_h * sizeof(scalar_t));
103         }
104         else
105         {
106                 // horizontal resample
107                 for (int i=0;i<src_h;i++)
108                 {
109                         resample_line(temp + i*dst_w, dst_w, 1, src + i * src_w, src_w, 1);
110                 }
111         }
112
113         // Now temp is stretched horizontally
114         // stretch vertically
115         if (dst_h == src_h)
116         {
117                 memcpy(dst, temp, dst_w * dst_h * sizeof(scalar_t));
118         }
119         else
120         {
121                 // vertical resample
122                 for (int i=0; i<dst_w; i++)
123                 {
124                         resample_line(dst+i, dst_h, dst_w, temp + i, src_h, dst_w);
125                 }
126         }
127
128         // cleanup
129         free(temp);
130
131         return true;
132 }
133
134 static bool pack_scalar_rgb2dw(Pixel *dst, scalar_t *ac, scalar_t *rc, scalar_t *gc, scalar_t *bc, int samples)
135 {
136         if (!dst || !ac || !rc || !gc || !bc) return false;
137
138         int a, r, g, b;
139
140         for (int i=0;i<samples;i++)
141         {
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);
146
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);
151
152                 dst[i] = PACK_ARGB32(a, r, g, b);
153         }
154
155         return true;
156 }
157
158 bool resample_pixel_buffer(PixelBuffer *pb, int w, int h)
159 {
160         if (!pb || !pb->buffer || pb->width < 0 || pb->height < 0) return false;
161
162         if ((int)pb->width == w && (int)pb->height == h) return true;
163
164         // split channels
165         scalar_t *a, *newa, *r, *newr, *g, *newg, *b, *newb;
166
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));
171
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));
176
177         for(int i=0; i<(int)(pb->width * pb->height); i++)
178         {
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]);
183         }
184
185         // resample
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);
190
191         // pack
192         Pixel *temp = (Pixel*)malloc(w * h * sizeof(Pixel));
193         pack_scalar_rgb2dw(temp, newa, newr, newg, newb, w * h);
194         free(pb->buffer);
195         pb->buffer = temp;
196         temp = 0;
197         pb->width = w;
198         pb->height = h;
199
200         // cleanup
201         free(a); free(r); free(g); free(b);
202         free(newa); free(newr); free(newg); free(newb);
203
204         return true;
205 }
206
207
208 // --- static inline functions ---
209
210 static inline scalar_t cerp(scalar_t x0, scalar_t x1, scalar_t x2, scalar_t x3, scalar_t t)
211 {
212         scalar_t a0, a1, a2, a3, t2;
213
214         t2 = t * t;
215         a0 = x3 - x2 - x0 + x1;
216         a1 = x0 - x1 - a0;
217         a2 = x2 - x0;
218         a3 = x1;
219
220         return(a0 * t * t2 + a1 * t2 + a2 * t + a3);
221 }
222
223
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))
227
228 static inline int clamp_integer(int i, int from, int to)
229 {
230         return CLAMP(i, from, to);
231 }
232
233 // Kernels
234 //----------------------------------------------------------------
235
236 static ImgSamplingMode samp_mode = SAMPLE_CLAMP;
237
238 static inline int map_index(int c, int dim)
239 {
240
241         switch (samp_mode)
242         {
243                 case SAMPLE_WRAP:
244                 {
245                         if (c < 0) 
246                         {
247                                 while(c < 0) c += dim;
248                                 return c;
249                         }
250                         if (c >= dim) return c % dim;
251                         break;
252                 }
253                 case SAMPLE_MIRROR:
254                 {
255                         if (c < 0) return -c;
256                         if (c >= dim) return dim - c - 1;
257                         break;
258                 }
259
260                 case SAMPLE_CLAMP:
261                 default:
262                 {
263                         if (c < 0) return 0;
264                         if (c >= dim) return dim - 1;
265                         break;
266                 }
267         }
268
269         return c;
270 }
271
272 static void split_channels(Pixel *img, Pixel *a, Pixel *r, Pixel *g, Pixel *b, unsigned int pixel_count)
273 {
274         for(unsigned int i=0; i<pixel_count; i++)
275         {
276                 *a++ = GETA(*img);
277                 *r++ = GETR(*img);
278                 *g++ = GETG(*img);
279                 *b++ = GETB(*img);
280                 img++;
281         }
282 }
283
284 static void join_channels(Pixel *img, Pixel *a, Pixel *r, Pixel *g, Pixel *b, unsigned int pixel_count)
285 {
286         for(unsigned int i=0; i<pixel_count; i++)
287         {
288                 *img++ = PACK_ARGB32(*a++, *r++, *g++, *b++);
289         }
290 }
291
292 static inline Pixel fetch_pixel(int x, int y, Pixel *img, int w, int h)
293 {
294         x = map_index(x,w);
295         y = map_index(y,h);
296
297         return img[x+w*y];
298 }
299
300 static Pixel* apply_kernel_to_channel(int *kernel, int kernel_dim, Pixel *img, int w, int h)
301 {
302         // only odd kernels
303         if (!(kernel_dim % 2))  return 0;
304         if (!kernel || !img)  return 0;
305         if ((w <= 0) || (h <= 0)) return 0; 
306
307         int kernel_l = kernel_dim * kernel_dim;
308         int kernel_center = kernel_dim / 2;
309         int kernel_sum = 0;
310         for (int i=0; i<kernel_l; i++)
311         {
312                 kernel_sum += kernel[i];
313         }
314
315         // allocate memory
316         Pixel *temp = (Pixel*)malloc(w * h * sizeof(Pixel));
317
318         // pain loop
319         for(int j=0; j<h; j++)
320         {
321                 for(int i=0; i<w; i++)
322                 {
323                         int sum=0;
324
325                         // kernel loop
326                         for(int kj=0; kj<kernel_dim; kj++)
327                         {
328                                 for(int ki=0; ki<kernel_dim; ki++)
329                                 {
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];
332                                 }
333                         }// end kernel loop
334
335                         if(kernel_sum) {
336                                 sum /= kernel_sum;
337                         }
338                         temp[i + j * w] = CLAMP(sum, 0, 255);
339                 }
340         } // end pain loop
341
342         return temp;
343 }
344
345 bool apply_kernel(PixelBuffer *pb, int *kernel, int kernel_dim, ImgSamplingMode sampling)
346 {
347         if(!pb || !pb->buffer) return false;
348         if(pb->width <= 0 || pb->height <= 0) return false;
349         if(!(kernel_dim / 2)) return false;
350         
351         unsigned int sz = pb->width * pb->height;
352
353         // set sampling mode
354         samp_mode = sampling;
355
356         // allocate memory
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));
361
362         // split channels
363         split_channels(pb->buffer, tempa, tempr, tempg, tempb, sz);
364
365         // apply kernel
366         Pixel *a = apply_kernel_to_channel(kernel, kernel_dim, tempa, pb->width, pb->height);
367         free(tempa);
368
369         Pixel *r = apply_kernel_to_channel(kernel, kernel_dim, tempr, pb->width, pb->height);
370         free(tempr);
371
372         Pixel *g = apply_kernel_to_channel(kernel, kernel_dim, tempg, pb->width, pb->height);
373         free(tempg);
374
375         Pixel *b = apply_kernel_to_channel(kernel, kernel_dim, tempb, pb->width, pb->height);
376         free(tempb);
377
378         // join channels
379         join_channels(pb->buffer, a, r, g, b, sz);
380
381         free(a);
382         free(r);
383         free(g);
384         free(b);
385
386         return true;
387 }
388
389 int* load_kernel(const char* filename, int *dim)
390 {
391         // try to open the file
392         FILE *input = fopen(filename, "r");
393
394         if (!input) return 0;
395
396         fseek(input , 0 , SEEK_END);
397         int size = ftell(input);
398         fseek(input , 0 , SEEK_SET);
399
400         // allocate memory
401         //char *s = (char*) malloc(size+1);
402
403         char s[2048];
404
405         int i;
406         
407         for(i=0; i<size; i++)
408                 s[i] = getc(input);
409
410         s[i] = 0;
411
412         // clear comments and commas
413         int j=0;
414         while (j<size)
415         {
416                 if (s[j] == '/')
417                 {
418                         while(s[j] != '\n' && s[j] != 0)
419                         {
420                                 s[j] = ' ';
421                                 j++;
422                         }
423                 }
424                 else 
425                 {
426                         if (s[j]==',') s[j]= ' ';
427                         j++;
428                 }
429         }
430
431         // remove everything indeed
432         for(j=0; j<size; j++) {
433                 if (s[j] == '\n' || s[j] == '\t') s[j] = ' ';
434         }
435
436         int num;
437         int index = 0;
438         char temp[24];
439
440         int i2=0;
441
442         while(s[index] == ' ') index++;
443         while(s[index] != ' ')
444         {
445                 temp[i2] = s[index];
446                 index++; i2++;
447         }
448         temp[i2] = 0;
449
450         if(!isdigit(temp[0])) {
451                 fclose(input);
452                 error("load_kernel() failed, invalid kernel file format: %s\n", filename);
453                 return 0;
454         }
455         num = atoi(temp);
456
457         int *kernel = (int*)malloc(num * num * sizeof(int));
458
459         for (int n=0; n<num*num; n++)
460         {
461                 i2 = 0;
462
463                 while (s[index] == ' ') index++;
464                 while (s[index] != ' ')
465                 {
466                         temp[i2] = s[index];
467                         index++; i2++;
468                 }
469                 temp[i2] = 0;
470
471                 if(!isdigit(temp[0]) && temp[0] != '-' && temp[0] != '+') {
472                         fclose(input);
473                         free(kernel);
474                         error("load_kernel() failed, invalid kernel file format: %s\n", filename);
475                         return 0;
476                 }
477                 kernel[n] = atoi(temp);
478         }
479
480         //cleanup
481         fclose(input);
482
483         *dim = num;
484         return kernel;
485 }
486
487 /* SobelEdge - (JT)
488  * Applies the sobel edge detection algorithm to the pixel buffer
489  */
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};
493
494         PixelBuffer horiz = *pb;
495         PixelBuffer vert = *pb;
496
497         if(!apply_kernel(&horiz, sobel_horiz, 3, sampling)) return false;
498         if(!apply_kernel(&vert, sobel_vert, 3, sampling)) return false;
499
500         Pixel *vptr = vert.buffer;
501         Pixel *hptr = horiz.buffer;
502         Pixel *dest = pb->buffer;
503         int sz = pb->width * pb->height;
504
505         for(int i=0; i<sz; i++) {
506                 Color vcol = unpack_color32(*vptr++);
507                 Color hcol = unpack_color32(*hptr++);
508
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);
512
513                 *dest++ = pack_color32(Color(r, g, b));
514         }
515
516         return true;
517 }
518
519
520 static inline Pixel blur_pixels(Pixel p1, Pixel p2)
521 {
522         // static temp colors
523         static Pixel tempc1, tempc2, tempc3, tempc4;
524         
525         // blur all channels in a SIMD-like manner
526         tempc1 = tempc2 = p1;
527         tempc3 = tempc4 = p2;
528
529         // divide by 2
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;
535
536         tempc1 = (tempc1 + tempc3) & 0xff00ff00;
537         tempc2 = (tempc2 + tempc4) & 0x00ff00ff;
538
539         return tempc1 | tempc2;
540 }
541
542
543 bool blur(PixelBuffer *pb, ImgSamplingMode sampling)
544 {
545         if(!pb) return false;
546         if(pb->width <= 0 || pb->height <= 0) return false;
547
548         samp_mode = sampling;
549
550         Pixel *temp = (Pixel*)malloc(pb->width * pb->height * sizeof(Pixel));
551
552         Pixel *scanline = pb->buffer;
553         Pixel *dst_scanline = temp;
554
555         // blur horizontally
556         for(unsigned int j=0; j<pb->height; j++)
557         {
558                 for(unsigned int i=0; i<pb->width; i++)
559                 {
560                         dst_scanline[i] = blur_pixels(scanline[map_index(i-1 , pb->width)], scanline[map_index(i+1 , pb->width)]);
561                 }       
562                 scanline += pb->width;
563                 dst_scanline += pb->width;
564         }
565
566         // blur vertically
567         for(unsigned int i=0; i<pb->width; i++)
568         {
569                 for(unsigned int j=0;j<pb->height;j++)
570                 {
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]);
572                 }
573         }
574
575         // cleanup
576         free(temp);     
577
578         return true;
579 }