Change to the linux kernel coding style
[wmaker-crm.git] / wrlib / scale.c
1 /* scale.c - image scaling
2  *
3  * Raster graphics library
4  *
5  * Copyright (c) 1997-2003 Alfredo K. Kojima
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #include <config.h>
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <X11/Xlib.h>
28 #include <math.h>
29
30 #ifndef PI
31 #define PI 3.14159265358979323846
32 #endif
33
34 #include <assert.h>
35
36 #include "wraster.h"
37
38 /*
39  *----------------------------------------------------------------------
40  * RScaleImage--
41  *      Creates a scaled copy of an image.
42  *
43  * Returns:
44  *      The new scaled image.
45  *
46  *----------------------------------------------------------------------
47  */
48 #ifndef broken_code
49 RImage *RScaleImage(RImage * image, unsigned new_width, unsigned new_height)
50 {
51         int ox;
52         int px, py;
53         register int x, y, t;
54         int dx, dy;
55         unsigned char *s;
56         unsigned char *d;
57         RImage *img;
58
59         assert(new_width >= 0 && new_height >= 0);
60
61         if (new_width == image->width && new_height == image->height)
62                 return RCloneImage(image);
63
64         img = RCreateImage(new_width, new_height, image->format == RRGBAFormat);
65
66         if (!img)
67                 return NULL;
68
69         /* fixed point math idea taken from Imlib by
70          * Carsten Haitzler (Rasterman) */
71         dx = (image->width << 16) / new_width;
72         dy = (image->height << 16) / new_height;
73
74         py = 0;
75
76         d = img->data;
77
78         if (image->format == RRGBAFormat) {
79                 for (y = 0; y < new_height; y++) {
80                         t = image->width * (py >> 16);
81
82                         s = image->data + (t << 2);     /* image->data+t*4 */
83
84                         ox = 0;
85                         px = 0;
86                         for (x = 0; x < new_width; x++) {
87                                 px += dx;
88
89                                 *(d++) = *(s);
90                                 *(d++) = *(s + 1);
91                                 *(d++) = *(s + 2);
92                                 *(d++) = *(s + 3);
93
94                                 t = (px - ox) >> 16;
95                                 ox += t << 16;
96
97                                 s += t << 2;    /* t*4 */
98                         }
99                         py += dy;
100                 }
101         } else {
102                 for (y = 0; y < new_height; y++) {
103                         t = image->width * (py >> 16);
104
105                         s = image->data + (t << 1) + t; /* image->data+t*3 */
106
107                         ox = 0;
108                         px = 0;
109                         for (x = 0; x < new_width; x++) {
110                                 px += dx;
111
112                                 *(d++) = *(s);
113                                 *(d++) = *(s + 1);
114                                 *(d++) = *(s + 2);
115
116                                 t = (px - ox) >> 16;
117                                 ox += t << 16;
118
119                                 s += (t << 1) + t;      /* t*3 */
120                         }
121                         py += dy;
122                 }
123         }
124
125         return img;
126 }
127
128 #else
129 RImage *RScaleImage(RImage * src, unsigned new_width, unsigned new_height)
130 {
131         int ddy, ee;
132         int h2;
133         int yd;
134         int xd, xs;
135         RImage *dst;
136         int e, xd2;
137         unsigned char *sr, *sg, *sb, *sa;
138         unsigned char *dr, *dg, *db, *da;
139         int ys = 0;
140
141         dst = RCreateImage(new_width, new_height, src->data[3] != NULL);
142
143         ddy = src->height / 2;
144         ee = (ddy / 2) - dst->height;
145         h2 = new_height / 2;
146
147         xd = dst->width;
148         xs = src->width / 2;
149         e = (src->width / 2) - xd;
150         xd2 = xd / 2;
151
152         sr = src->data[0];
153         sg = src->data[1];
154         sb = src->data[2];
155         sa = src->data[3];
156
157         dr = dst->data[0];
158         dg = dst->data[1];
159         db = dst->data[2];
160         da = dst->data[3];
161
162         if (sa == NULL) {
163                 for (yd = 0; yd < new_height; yd++) {
164                         int x;
165
166                         sr = src->data[0] + ys * src->width;
167                         sg = src->data[1] + ys * src->width;
168                         sb = src->data[2] + ys * src->width;
169
170                         for (x = 0; x < xd; x++) {
171                                 *(dr++) = *sr;
172                                 *(dg++) = *sg;
173                                 *(db++) = *sb;
174
175                                 while (e >= 0) {
176                                         sr++;
177                                         sg++;
178                                         sb++;
179                                         e -= xd2;
180                                 }
181                                 e += xs;
182                         }
183                         while (ee >= 0) {
184                                 ys++;
185                                 ee -= h2;
186                         }
187                         ee += ddy;
188                 }
189         } else {
190                 for (yd = 0; yd < new_height; yd++) {
191                         int x;
192
193                         sr = src->data[0] + ys * src->width;
194                         sg = src->data[1] + ys * src->width;
195                         sb = src->data[2] + ys * src->width;
196                         sa = src->data[3] + ys * src->width;
197
198                         for (x = 0; x < xd; x++) {
199                                 *(dr++) = *sr;
200                                 *(dg++) = *sg;
201                                 *(db++) = *sb;
202                                 *(da++) = *sa;
203
204                                 while (e >= 0) {
205                                         sr++;
206                                         sg++;
207                                         sb++;
208                                         sa++;
209                                         e -= xd2;
210                                 }
211                                 e += xs;
212                         }
213                         while (ee >= 0) {
214                                 ys++;
215                                 ee -= h2;
216                         }
217                         ee += ddy;
218                 }
219         }
220
221         return dst;
222 }
223 #endif
224
225 /*
226  * Filtered Image Rescaling code copy/pasted from
227  * Graphics Gems III
228  * Public Domain 1991 by Dale Schumacher
229  */
230
231 /*
232  *      filter function definitions
233  */
234 #if 0
235 #define filter_support          (1.0)
236
237 static double filter(t)
238 double t;
239 {
240         /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */
241         if (t < 0.0)
242                 t = -t;
243         if (t < 1.0)
244                 return ((2.0 * t - 3.0) * t * t + 1.0);
245         return (0.0);
246 }
247 #endif
248 #define box_support             (0.5)
249
250 static double box_filter(t)
251 double t;
252 {
253         if ((t > -0.5) && (t <= 0.5))
254                 return (1.0);
255         return (0.0);
256 }
257
258 #define triangle_support        (1.0)
259
260 static double triangle_filter(t)
261 double t;
262 {
263         if (t < 0.0)
264                 t = -t;
265         if (t < 1.0)
266                 return (1.0 - t);
267         return (0.0);
268 }
269
270 #define bell_support            (1.5)
271
272 static double bell_filter(t)    /* box (*) box (*) box */
273 double t;
274 {
275         if (t < 0)
276                 t = -t;
277         if (t < .5)
278                 return (.75 - (t * t));
279         if (t < 1.5) {
280                 t = (t - 1.5);
281                 return (.5 * (t * t));
282         }
283         return (0.0);
284 }
285
286 #define B_spline_support        (2.0)
287
288 static double B_spline_filter(t)        /* box (*) box (*) box (*) box */
289 double t;
290 {
291         double tt;
292
293         if (t < 0)
294                 t = -t;
295         if (t < 1) {
296                 tt = t * t;
297                 return ((.5 * tt * t) - tt + (2.0 / 3.0));
298         } else if (t < 2) {
299                 t = 2 - t;
300                 return ((1.0 / 6.0) * (t * t * t));
301         }
302         return (0.0);
303 }
304
305 static double sinc(x)
306 double x;
307 {
308         x *= PI;
309         if (x != 0)
310                 return (sin(x) / x);
311         return (1.0);
312 }
313
314 #define Lanczos3_support        (3.0)
315
316 static double Lanczos3_filter(t)
317 double t;
318 {
319         if (t < 0)
320                 t = -t;
321         if (t < 3.0)
322                 return (sinc(t) * sinc(t / 3.0));
323         return (0.0);
324 }
325
326 #define Mitchell_support        (2.0)
327
328 #define B       (1.0 / 3.0)
329 #define C       (1.0 / 3.0)
330
331 static double Mitchell_filter(t)
332 double t;
333 {
334         double tt;
335
336         tt = t * t;
337         if (t < 0)
338                 t = -t;
339         if (t < 1.0) {
340                 t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt))
341                      + ((-18.0 + 12.0 * B + 6.0 * C) * tt)
342                      + (6.0 - 2 * B));
343                 return (t / 6.0);
344         } else if (t < 2.0) {
345                 t = (((-1.0 * B - 6.0 * C) * (t * tt))
346                      + ((6.0 * B + 30.0 * C) * tt)
347                      + ((-12.0 * B - 48.0 * C) * t)
348                      + (8.0 * B + 24 * C));
349                 return (t / 6.0);
350         }
351         return (0.0);
352 }
353
354 static double (*filterf) () = Mitchell_filter;
355 static double fwidth = Mitchell_support;
356
357 void _wraster_change_filter(int type)
358 {
359         switch (type) {
360         case RBoxFilter:
361                 filterf = box_filter;
362                 fwidth = box_support;
363                 break;
364         case RTriangleFilter:
365                 filterf = triangle_filter;
366                 fwidth = triangle_support;
367                 break;
368         case RBellFilter:
369                 filterf = bell_filter;
370                 fwidth = bell_support;
371                 break;
372         case RBSplineFilter:
373                 filterf = B_spline_filter;
374                 fwidth = B_spline_support;
375                 break;
376         case RLanczos3Filter:
377                 filterf = Lanczos3_filter;
378                 fwidth = Lanczos3_support;
379                 break;
380         default:
381         case RMitchellFilter:
382                 filterf = Mitchell_filter;
383                 fwidth = Mitchell_support;
384                 break;
385         }
386 }
387
388 /*
389  *      image rescaling routine
390  */
391
392 typedef struct {
393         int pixel;
394         double weight;
395 } CONTRIB;
396
397 typedef struct {
398         int n;                  /* number of contributors */
399         CONTRIB *p;             /* pointer to list of contributions */
400 } CLIST;
401
402 CLIST *contrib;                 /* array of contribution lists */
403
404 /* clamp the input to the specified range */
405 #define CLAMP(v,l,h)    ((v)<(l) ? (l) : (v) > (h) ? (h) : v)
406
407 /* return of calloc is not checked if NULL in the function below! */
408 RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
409 {
410         RImage *tmp;            /* intermediate image */
411         double xscale, yscale;  /* zoom scale factors */
412         int i, j, k;            /* loop variables */
413         int n;                  /* pixel number */
414         double center, left, right;     /* filter calculation variables */
415         double width, fscale;   /* filter calculation variables */
416         double rweight, gweight, bweight;
417         RImage *dst;
418         unsigned char *p;
419         unsigned char *sp;
420         int sch = src->format == RRGBAFormat ? 4 : 3;
421
422         dst = RCreateImage(new_width, new_height, False);
423
424         /* create intermediate image to hold horizontal zoom */
425         tmp = RCreateImage(dst->width, src->height, False);
426         xscale = (double)new_width / (double)src->width;
427         yscale = (double)new_height / (double)src->height;
428
429         /* pre-calculate filter contributions for a row */
430         contrib = (CLIST *) calloc(new_width, sizeof(CLIST));
431         if (xscale < 1.0) {
432                 width = fwidth / xscale;
433                 fscale = 1.0 / xscale;
434                 for (i = 0; i < new_width; ++i) {
435                         contrib[i].n = 0;
436                         contrib[i].p = (CONTRIB *) calloc((int)(width * 2 + 1), sizeof(CONTRIB));
437                         center = (double)i / xscale;
438                         left = ceil(center - width);
439                         right = floor(center + width);
440                         for (j = left; j <= right; ++j) {
441                                 rweight = center - (double)j;
442                                 rweight = (*filterf) (rweight / fscale) / fscale;
443                                 if (j < 0) {
444                                         n = -j;
445                                 } else if (j >= src->width) {
446                                         n = (src->width - j) + src->width - 1;
447                                 } else {
448                                         n = j;
449                                 }
450                                 k = contrib[i].n++;
451                                 contrib[i].p[k].pixel = n * sch;
452                                 contrib[i].p[k].weight = rweight;
453                         }
454                 }
455         } else {
456
457                 for (i = 0; i < new_width; ++i) {
458                         contrib[i].n = 0;
459                         contrib[i].p = (CONTRIB *) calloc((int)(fwidth * 2 + 1), sizeof(CONTRIB));
460                         center = (double)i / xscale;
461                         left = ceil(center - fwidth);
462                         right = floor(center + fwidth);
463                         for (j = left; j <= right; ++j) {
464                                 rweight = center - (double)j;
465                                 rweight = (*filterf) (rweight);
466                                 if (j < 0) {
467                                         n = -j;
468                                 } else if (j >= src->width) {
469                                         n = (src->width - j) + src->width - 1;
470                                 } else {
471                                         n = j;
472                                 }
473                                 k = contrib[i].n++;
474                                 contrib[i].p[k].pixel = n * sch;
475                                 contrib[i].p[k].weight = rweight;
476                         }
477                 }
478         }
479
480         /* apply filter to zoom horizontally from src to tmp */
481         p = tmp->data;
482
483         for (k = 0; k < tmp->height; ++k) {
484                 CONTRIB *pp;
485
486                 sp = src->data + src->width * k * sch;
487
488                 for (i = 0; i < tmp->width; ++i) {
489                         rweight = gweight = bweight = 0.0;
490
491                         pp = contrib[i].p;
492
493                         for (j = 0; j < contrib[i].n; ++j) {
494                                 rweight += sp[pp[j].pixel] * pp[j].weight;
495                                 gweight += sp[pp[j].pixel + 1] * pp[j].weight;
496                                 bweight += sp[pp[j].pixel + 2] * pp[j].weight;
497                         }
498                         *p++ = CLAMP(rweight, 0, 255);
499                         *p++ = CLAMP(gweight, 0, 255);
500                         *p++ = CLAMP(bweight, 0, 255);
501                 }
502         }
503
504         /* free the memory allocated for horizontal filter weights */
505         for (i = 0; i < tmp->width; ++i) {
506                 free(contrib[i].p);
507         }
508         free(contrib);
509
510         /* pre-calculate filter contributions for a column */
511         contrib = (CLIST *) calloc(dst->height, sizeof(CLIST));
512         if (yscale < 1.0) {
513                 width = fwidth / yscale;
514                 fscale = 1.0 / yscale;
515                 for (i = 0; i < dst->height; ++i) {
516                         contrib[i].n = 0;
517                         contrib[i].p = (CONTRIB *) calloc((int)(width * 2 + 1), sizeof(CONTRIB));
518                         center = (double)i / yscale;
519                         left = ceil(center - width);
520                         right = floor(center + width);
521                         for (j = left; j <= right; ++j) {
522                                 rweight = center - (double)j;
523                                 rweight = (*filterf) (rweight / fscale) / fscale;
524                                 if (j < 0) {
525                                         n = -j;
526                                 } else if (j >= tmp->height) {
527                                         n = (tmp->height - j) + tmp->height - 1;
528                                 } else {
529                                         n = j;
530                                 }
531                                 k = contrib[i].n++;
532                                 contrib[i].p[k].pixel = n * 3;
533                                 contrib[i].p[k].weight = rweight;
534                         }
535                 }
536         } else {
537                 for (i = 0; i < dst->height; ++i) {
538                         contrib[i].n = 0;
539                         contrib[i].p = (CONTRIB *) calloc((int)(fwidth * 2 + 1), sizeof(CONTRIB));
540                         center = (double)i / yscale;
541                         left = ceil(center - fwidth);
542                         right = floor(center + fwidth);
543                         for (j = left; j <= right; ++j) {
544                                 rweight = center - (double)j;
545                                 rweight = (*filterf) (rweight);
546                                 if (j < 0) {
547                                         n = -j;
548                                 } else if (j >= tmp->height) {
549                                         n = (tmp->height - j) + tmp->height - 1;
550                                 } else {
551                                         n = j;
552                                 }
553                                 k = contrib[i].n++;
554                                 contrib[i].p[k].pixel = n * 3;
555                                 contrib[i].p[k].weight = rweight;
556                         }
557                 }
558         }
559
560         /* apply filter to zoom vertically from tmp to dst */
561         sp = malloc(tmp->height * 3);
562
563         for (k = 0; k < new_width; ++k) {
564                 CONTRIB *pp;
565
566                 p = dst->data + k * 3;
567
568                 /* copy a column into a row */
569                 {
570                         int i;
571                         unsigned char *p, *d;
572
573                         d = sp;
574                         for (i = tmp->height, p = tmp->data + k * 3; i-- > 0; p += tmp->width * 3) {
575                                 *d++ = *p;
576                                 *d++ = *(p + 1);
577                                 *d++ = *(p + 2);
578                         }
579                 }
580                 for (i = 0; i < new_height; ++i) {
581                         rweight = gweight = bweight = 0.0;
582
583                         pp = contrib[i].p;
584
585                         for (j = 0; j < contrib[i].n; ++j) {
586                                 rweight += sp[pp[j].pixel] * pp[j].weight;
587                                 gweight += sp[pp[j].pixel + 1] * pp[j].weight;
588                                 bweight += sp[pp[j].pixel + 2] * pp[j].weight;
589                         }
590                         *p = CLAMP(rweight, 0, 255);
591                         *(p + 1) = CLAMP(gweight, 0, 255);
592                         *(p + 2) = CLAMP(bweight, 0, 255);
593                         p += new_width * 3;
594                 }
595         }
596         free(sp);
597
598         /* free the memory allocated for vertical filter weights */
599         for (i = 0; i < dst->height; ++i) {
600                 free(contrib[i].p);
601         }
602         free(contrib);
603
604         RReleaseImage(tmp);
605
606         return dst;
607 }