added gfximage_free to gfximage.{c,h}
[swftools.git] / lib / gfximage.c
1 #include <stdlib.h>
2 #include <math.h>
3 #include <memory.h>
4 #include "jpeg.h"
5 #include "png.h"
6 #include "mem.h"
7 #include "gfximage.h"
8 #include "types.h"
9
10 void gfximage_save_jpeg(gfximage_t*img, const char*filename, int quality)
11 {
12     int x,y;
13     int l = img->width*img->height;
14     unsigned char*data = (unsigned char*)rfx_alloc(img->width*img->height*3);
15     int s,t;
16     for(t=0,s=0;t<l;s+=3,t++) {
17         data[s+0] = img->data[t].r;
18         data[s+1] = img->data[t].g;
19         data[s+2] = img->data[t].b;
20     }
21     jpeg_save(data, img->width, img->height, quality, filename);
22 }
23
24 void gfximage_save_png(gfximage_t*image, const char*filename)
25 {
26     writePNG(filename, (void*)image->data, image->width, image->height);
27 }
28
29 typedef struct scale_lookup {
30     int pos;
31     unsigned int weight;
32 } scale_lookup_t;
33
34 typedef struct rgba_int {
35     unsigned int r,g,b,a;
36 } rgba_int_t;
37
38 static int bicubic = 0;
39
40 static scale_lookup_t**make_scale_lookup(int width, int newwidth)
41 {
42     scale_lookup_t*lookupx = (scale_lookup_t*)rfx_alloc((width>newwidth?width:newwidth)*2*sizeof(scale_lookup_t));
43     scale_lookup_t**lblockx = (scale_lookup_t**)rfx_alloc((newwidth+1)*sizeof(scale_lookup_t**));
44     double fx = ((double)width)/((double)newwidth);
45     double px = 0;
46     int x;
47     scale_lookup_t*p_x = lookupx;
48
49     if(newwidth<=width) {
50         for(x=0;x<newwidth;x++) {
51             double ex = px + fx;
52             int fromx = (int)px;
53             int tox = (int)ex;
54             double rem = fromx+1-px;
55             int i = (int)(256/fx);
56             int xweight = (int)(rem*256/fx);
57             int xx;
58             int w = 0;
59             lblockx[x] = p_x;
60             if(tox>=width) tox = width-1;
61             for(xx=fromx;xx<=tox;xx++) {
62                 if(xx==fromx && xx==tox) p_x->weight = 256;
63                 else if(xx==fromx) p_x->weight = xweight;
64                 else if(xx==tox) p_x->weight = 256-w;
65                 else p_x->weight = i;
66                 w+=p_x->weight;
67                 p_x->pos = xx;
68                 p_x++;
69             }
70             px = ex;
71         }
72     } else {
73         for(x=0;x<newwidth;x++) {
74             int ix1 = (int)px;
75             int ix2 = ((int)px)+1;
76             double r = px-ix1;
77             if(ix2>=width) ix2=width-1;
78             lblockx[x] = p_x;
79             if(bicubic)
80                 r = -2*r*r*r+3*r*r;
81             p_x[0].weight = (int)(256*(1-r));
82             p_x[0].pos = ix1;
83             p_x[1].weight = 256-p_x[0].weight;
84             p_x[1].pos = ix2;
85             p_x+=2;
86             px += fx;
87         }
88     }
89     lblockx[newwidth] = p_x;
90     return lblockx;
91 }
92
93 static void encodeMonochromeImage(gfxcolor_t*data, int width, int height, gfxcolor_t*colors)
94 {
95     int t;
96     int len = width*height;
97
98     U32* img = (U32*)data;
99     U32 color1 = img[0];
100     U32 color2 = 0;
101     for(t=1;t<len;t++) {
102         if(img[t] != color1) {
103             color2 = img[t];
104             break;
105         }
106     }
107     *(U32*)&colors[0] = color1;
108     *(U32*)&colors[1] = color2;
109     for(t=0;t<len;t++) {
110         if(img[t] == color1) {
111             img[t] = 0;
112         } else {
113             img[t] = 0xffffffff;
114         }
115     }
116 }
117
118 static void decodeMonochromeImage(gfxcolor_t*data, int width, int height, gfxcolor_t*colors)
119 {
120     int t;
121     int len = width*height;
122
123     for(t=0;t<len;t++) {
124         U32 m = data[t].r;
125         data[t].r = (colors[0].r * (255-m) + colors[1].r * m) >> 8;
126         data[t].g = (colors[0].g * (255-m) + colors[1].g * m) >> 8;
127         data[t].b = (colors[0].b * (255-m) + colors[1].b * m) >> 8;
128         data[t].a = (colors[0].a * (255-m) + colors[1].a * m) >> 8;
129     }
130 }
131
132 void blurImage(gfxcolor_t*src, int width, int height, int r)  __attribute__ ((noinline));
133
134 void blurImage(gfxcolor_t*src, int width, int height, int r)
135 {
136     int e = 2; // r times e is the sampling interval
137     double*gauss = (double*)rfx_alloc(r*e*sizeof(double));
138     double sum=0;
139     int x;
140     for(x=0;x<r*e;x++) {
141         double t = (x - r*e/2.0)/r;
142         gauss[x] = exp(-0.5*t*t);
143         sum += gauss[x];
144     }
145     int*weights = (int*)rfx_alloc(r*e*sizeof(int));
146     for(x=0;x<r*e;x++) {
147         weights[x] = (int)(gauss[x]*65536.0001/sum);
148     }
149     int range = r*e/2;
150
151     gfxcolor_t*tmp = rfx_alloc(sizeof(gfxcolor_t)*width*height);
152
153     int y;
154     for(y=0;y<height;y++) {
155         gfxcolor_t*s = &src[y*width];
156         gfxcolor_t*d = &tmp[y*width];
157         for(x=0;x<range;x++) {
158             d[x] = s[x];
159         }
160         for(x=range;x<width-range;x++) {
161             int r=0;
162             int g=0;
163             int b=0;
164             int a=0;
165             int*f = weights;
166             int xx;
167             for(xx=x-range;xx<x+range;xx++) {
168                 r += s[xx].r * f[0];
169                 g += s[xx].g * f[0];
170                 b += s[xx].b * f[0];
171                 a += s[xx].a * f[0];
172                 f++;
173             }
174             d[x].r = r >> 16;
175             d[x].g = g >> 16;
176             d[x].b = b >> 16;
177             d[x].a = a >> 16;
178         }
179         for(x=width-range;x<width;x++) {
180             d[x] = s[x];
181         }
182     }
183
184     for(x=0;x<width;x++) {
185         gfxcolor_t*s = &tmp[x];
186         gfxcolor_t*d = &src[x];
187         int yy=0;
188         for(y=0;y<range;y++) {
189             d[yy] = s[yy];
190             yy+=width;
191         }
192         for(y=range;y<height-range;y++) {
193             int r=0;
194             int g=0;
195             int b=0;
196             int a=0;
197             int*f = weights;
198             int cy,cyy=yy-range*width;
199             for(cy=y-range;cy<y+range;cy++) {
200                 r += s[cyy].r * f[0];
201                 g += s[cyy].g * f[0];
202                 b += s[cyy].b * f[0];
203                 a += s[cyy].a * f[0];
204                 cyy += width;
205                 f++;
206             }
207             d[yy].r = r >> 16;
208             d[yy].g = g >> 16;
209             d[yy].b = b >> 16;
210             d[yy].a = a >> 16;
211             yy += width;
212         }
213         for(y=0;y<range;y++) {
214             d[yy] = s[yy];
215             yy += width;
216         }
217     }
218
219     rfx_free(tmp);
220     rfx_free(weights);
221     rfx_free(gauss);
222 }
223
224 int swf_ImageGetNumberOfPaletteEntries2(gfxcolor_t*_img, int width, int height)
225 {
226     int len = width*height;
227     int t;
228     U32* img = (U32*)_img;
229     U32 color1 = img[0];
230     U32 color2 = 0;
231     for(t=1;t<len;t++) {
232         if(img[t] != color1) {
233             color2 = img[t];
234             break;
235         }
236     }
237     if(t==len)
238         return 1;
239
240     for(;t<len;t++) {
241         if(img[t] != color1 && img[t] != color2) {
242             return width*height;
243         }
244     }
245     return 2;
246 }
247
248 gfximage_t* gfximage_rescale(gfximage_t*image, int newwidth, int newheight)
249 {
250     int x,y;
251     gfxcolor_t* newdata; 
252     scale_lookup_t *p, **lblockx,**lblocky;
253     rgba_int_t*tmpline;
254     int monochrome = 0;
255     gfxcolor_t monochrome_colors[2];
256     
257     if(newwidth<1 || newheight<1)
258         return 0;
259
260     int width = image->width;
261     int height = image->height;
262     gfxcolor_t*data = image->data;
263
264     if(swf_ImageGetNumberOfPaletteEntries2(data, width, height) == 2) {
265         monochrome=1;
266         encodeMonochromeImage(data, width, height, monochrome_colors);
267         int r1 = width / newwidth;
268         int r2 = height / newheight;
269         int r = r1<r2?r1:r2;
270         if(r>4) {
271             /* high-resolution monochrome images are usually dithered, so 
272                low-pass filter them first to get rid of any moire patterns */
273             blurImage(data, width, height, r+1);
274         }
275     }
276
277     tmpline = (rgba_int_t*)rfx_alloc(width*sizeof(rgba_int_t));
278     newdata = (gfxcolor_t*)rfx_alloc(newwidth*newheight*sizeof(gfxcolor_t));
279   
280     lblockx = make_scale_lookup(width, newwidth);
281     lblocky = make_scale_lookup(height, newheight);
282
283     for(p=lblocky[0];p<lblocky[newheight];p++)
284         p->pos*=width;
285
286     for(y=0;y<newheight;y++) {
287         gfxcolor_t*destline = &newdata[y*newwidth];
288         
289         /* create lookup table for y */
290         rgba_int_t*l = tmpline;
291         scale_lookup_t*p_y,*p_x;
292         memset(tmpline, 0, width*sizeof(rgba_int_t));
293         for(p_y=lblocky[y];p_y<lblocky[y+1];p_y++) {
294             gfxcolor_t*line = &data[p_y->pos];
295             scale_lookup_t*p_x;
296             int weight = p_y->weight;
297             for(x=0;x<width;x++) {
298                 tmpline[x].r += line[x].r*weight;
299                 tmpline[x].g += line[x].g*weight;
300                 tmpline[x].b += line[x].b*weight;
301                 tmpline[x].a += line[x].a*weight;
302             }
303         }
304
305         /* process x direction */
306         p_x = lblockx[0];
307         for(x=0;x<newwidth;x++) {
308             unsigned int r=0,g=0,b=0,a=0;
309             scale_lookup_t*p_x_to = lblockx[x+1];
310             do { 
311                 rgba_int_t* col = &tmpline[p_x->pos];
312                 unsigned int weight = p_x->weight;
313                 r += col->r*weight;
314                 g += col->g*weight;
315                 b += col->b*weight;
316                 a += col->a*weight;
317                 p_x++;
318             } while (p_x<p_x_to);
319
320             destline->r = r >> 16;
321             destline->g = g >> 16;
322             destline->b = b >> 16;
323             destline->a = a >> 16;
324            
325             destline++;
326         }
327     }
328
329     if(monochrome)
330         decodeMonochromeImage(newdata, newwidth, newheight, monochrome_colors);
331
332     rfx_free(tmpline);
333     rfx_free(*lblockx);
334     rfx_free(lblockx);
335     rfx_free(*lblocky);
336     rfx_free(lblocky);
337
338     gfximage_t*image2 = (gfximage_t*)malloc(sizeof(gfximage_t));
339     image2->data = newdata;
340     image2->width = newwidth;
341     image2->height = newheight;
342     return image2;
343 }
344
345 void gfximage_free(gfximage_t*b)
346 {
347     free(b->data);
348     b->data = 0;
349     free(b);
350 }
351