fixed bug in RCOmbineAreaWithOpaq
[wmaker-crm.git] / wrlib / scale.c
blob97544aceac13a3b0f726dc02a7c6912e008617bd
1 /* scale.c - image scaling
2 *
3 * Raster graphics library
4 *
5 * Copyright (c) 1997, 1988, 1999 Alfredo K. Kojima
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.
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.
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.
22 #include <config.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <X11/Xlib.h>
29 #include <math.h>
31 #ifndef PI
32 #define PI 3.141592
33 #endif
35 #include <assert.h>
37 #include "wraster.h"
41 *----------------------------------------------------------------------
42 * RScaleImage--
43 * Creates a scaled copy of an image.
45 * Returns:
46 * The new scaled image.
48 *----------------------------------------------------------------------
50 #ifndef broken_code
51 RImage*
52 RScaleImage(RImage *image, unsigned new_width, unsigned new_height)
54 int ox;
55 int px, py;
56 register int x, y, t;
57 int dx, dy;
58 unsigned char *s;
59 unsigned char *d;
60 RImage *img;
62 assert(new_width >= 0 && new_height >= 0);
64 if (new_width == image->width && new_height == image->height)
65 return RCloneImage(image);
67 img = RCreateImage(new_width, new_height, image->format==RRGBAFormat);
69 if (!img)
70 return NULL;
72 /* fixed point math idea taken from Imlib by
73 * Carsten Haitzler (Rasterman) */
74 dx = (image->width<<16)/new_width;
75 dy = (image->height<<16)/new_height;
77 py = 0;
79 d = img->data;
81 if (image->format == RRGBAFormat) {
82 for (y=0; y<new_height; y++) {
83 t = image->width*(py>>16);
85 s = image->data+(t<<2); /* image->data+t*4 */
87 ox = 0;
88 px = 0;
89 for (x=0; x<new_width; x++) {
90 px += dx;
92 *(d++) = *(s);
93 *(d++) = *(s+1);
94 *(d++) = *(s+2);
95 *(d++) = *(s+3);
97 t = (px - ox)>>16;
98 ox += t<<16;
100 s += t<<2; /* t*4 */
102 py += dy;
104 } else {
105 for (y=0; y<new_height; y++) {
106 t = image->width*(py>>16);
108 s = image->data+(t<<1)+t; /* image->data+t*3 */
110 ox = 0;
111 px = 0;
112 for (x=0; x<new_width; x++) {
113 px += dx;
115 *(d++) = *(s);
116 *(d++) = *(s+1);
117 *(d++) = *(s+2);
119 t = (px - ox)>>16;
120 ox += t<<16;
122 s += (t<<1)+t; /* t*3 */
124 py += dy;
128 return img;
131 #else
132 RImage*
133 RScaleImage(RImage *src, unsigned new_width, unsigned new_height)
135 int ddy, ee;
136 int h2;
137 int yd;
138 int xd, xs;
139 RImage *dst;
140 int e, xd2;
141 unsigned char *sr, *sg, *sb, *sa;
142 unsigned char *dr, *dg, *db, *da;
143 int ys = 0;
147 dst = RCreateImage(new_width, new_height, src->data[3]!=NULL);
149 ddy = src->height/2;
150 ee = (ddy/2) - dst->height;
151 h2 = new_height/2;
153 xd = dst->width;
154 xs = src->width/2;
155 e = (src->width/2)-xd;
156 xd2 = xd/2;
158 sr = src->data[0];
159 sg = src->data[1];
160 sb = src->data[2];
161 sa = src->data[3];
163 dr = dst->data[0];
164 dg = dst->data[1];
165 db = dst->data[2];
166 da = dst->data[3];
168 if (sa == NULL) {
169 for (yd = 0; yd < new_height; yd++) {
170 int x;
172 sr = src->data[0] + ys * src->width;
173 sg = src->data[1] + ys * src->width;
174 sb = src->data[2] + ys * src->width;
176 for (x = 0; x < xd; x++) {
177 *(dr++) = *sr;
178 *(dg++) = *sg;
179 *(db++) = *sb;
181 while (e >= 0) {
182 sr++;
183 sg++;
184 sb++;
185 e -= xd2;
187 e += xs;
189 while (ee >= 0) {
190 ys++;
191 ee -= h2;
193 ee += ddy;
195 } else {
196 for (yd = 0; yd < new_height; yd++) {
197 int x;
199 sr = src->data[0] + ys * src->width;
200 sg = src->data[1] + ys * src->width;
201 sb = src->data[2] + ys * src->width;
202 sa = src->data[3] + ys * src->width;
204 for (x = 0; x < xd; x++) {
205 *(dr++) = *sr;
206 *(dg++) = *sg;
207 *(db++) = *sb;
208 *(da++) = *sa;
210 while (e >= 0) {
211 sr++;
212 sg++;
213 sb++;
214 sa++;
215 e -= xd2;
217 e += xs;
219 while (ee >= 0) {
220 ys++;
221 ee -= h2;
223 ee += ddy;
227 return dst;
229 #endif
234 * Filtered Image Rescaling code copy/pasted from
235 * Graphics Gems III
236 * Public Domain 1991 by Dale Schumacher
241 * filter function definitions
243 #if 0
244 #define filter_support (1.0)
246 static double
247 filter(t)
248 double t;
250 /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */
251 if(t < 0.0) t = -t;
252 if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0);
253 return(0.0);
255 #endif
256 #define box_support (0.5)
258 static double
259 box_filter(t)
260 double t;
262 if((t > -0.5) && (t <= 0.5)) return(1.0);
263 return(0.0);
266 #define triangle_support (1.0)
268 static double
269 triangle_filter(t)
270 double t;
272 if(t < 0.0) t = -t;
273 if(t < 1.0) return(1.0 - t);
274 return(0.0);
277 #define bell_support (1.5)
279 static double
280 bell_filter(t) /* box (*) box (*) box */
281 double t;
283 if(t < 0) t = -t;
284 if(t < .5) return(.75 - (t * t));
285 if(t < 1.5) {
286 t = (t - 1.5);
287 return(.5 * (t * t));
289 return(0.0);
292 #define B_spline_support (2.0)
294 static double
295 B_spline_filter(t) /* box (*) box (*) box (*) box */
296 double t;
298 double tt;
300 if(t < 0) t = -t;
301 if(t < 1) {
302 tt = t * t;
303 return((.5 * tt * t) - tt + (2.0 / 3.0));
304 } else if(t < 2) {
305 t = 2 - t;
306 return((1.0 / 6.0) * (t * t * t));
308 return(0.0);
311 static double
312 sinc(x)
313 double x;
315 x *= PI;
316 if(x != 0) return(sin(x) / x);
317 return(1.0);
320 #define Lanczos3_support (3.0)
322 static double
323 Lanczos3_filter(t)
324 double t;
326 if(t < 0) t = -t;
327 if(t < 3.0) return(sinc(t) * sinc(t/3.0));
328 return(0.0);
331 #define Mitchell_support (2.0)
333 #define B (1.0 / 3.0)
334 #define C (1.0 / 3.0)
336 static double
337 Mitchell_filter(t)
338 double t;
340 double tt;
342 tt = t * t;
343 if(t < 0) t = -t;
344 if(t < 1.0) {
345 t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt))
346 + ((-18.0 + 12.0 * B + 6.0 * C) * tt)
347 + (6.0 - 2 * B));
348 return(t / 6.0);
349 } else if(t < 2.0) {
350 t = (((-1.0 * B - 6.0 * C) * (t * tt))
351 + ((6.0 * B + 30.0 * C) * tt)
352 + ((-12.0 * B - 48.0 * C) * t)
353 + (8.0 * B + 24 * C));
354 return(t / 6.0);
356 return(0.0);
359 static double (*filterf)() = Mitchell_filter;
360 static double fwidth = Mitchell_support;
362 void
363 _wraster_change_filter(int type)
365 switch (type) {
366 case RBoxFilter:
367 filterf = box_filter;
368 fwidth = box_support;
369 break;
370 case RTriangleFilter:
371 filterf = triangle_filter;
372 fwidth = triangle_support;
373 break;
374 case RBellFilter:
375 filterf = bell_filter;
376 fwidth = bell_support;
377 break;
378 case RBSplineFilter:
379 filterf = B_spline_filter;
380 fwidth = B_spline_support;
381 break;
382 case RLanczos3Filter:
383 filterf = Lanczos3_filter;
384 fwidth = Lanczos3_support;
385 break;
386 default:
387 case RMitchellFilter:
388 filterf = Mitchell_filter;
389 fwidth = Mitchell_support;
390 break;
396 * image rescaling routine
399 typedef struct {
400 int pixel;
401 double weight;
402 } CONTRIB;
404 typedef struct {
405 int n; /* number of contributors */
406 CONTRIB *p; /* pointer to list of contributions */
407 } CLIST;
409 CLIST *contrib; /* array of contribution lists */
411 /* clamp the input to the specified range */
412 #define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : v)
415 RImage*
416 RSmoothScaleImage(RImage *src, unsigned new_width, unsigned new_height)
418 RImage *tmp; /* intermediate image */
419 double xscale, yscale; /* zoom scale factors */
420 int i, j, k; /* loop variables */
421 int n; /* pixel number */
422 double center, left, right; /* filter calculation variables */
423 double width, fscale; /* filter calculation variables */
424 double rweight, gweight, bweight;
425 RImage *dst;
426 unsigned char *p;
427 unsigned char *sp;
428 int sch = src->format == RRGBAFormat ? 4 : 3;
431 dst = RCreateImage(new_width, new_height, False);
433 /* create intermediate image to hold horizontal zoom */
434 tmp = RCreateImage(dst->width, src->height, False);
435 xscale = (double)new_width / (double)src->width;
436 yscale = (double)new_height / (double)src->height;
438 /* pre-calculate filter contributions for a row */
439 contrib = (CLIST *)calloc(new_width, sizeof(CLIST));
440 if (xscale < 1.0) {
441 width = fwidth / xscale;
442 fscale = 1.0 / xscale;
443 for (i = 0; i < new_width; ++i) {
444 contrib[i].n = 0;
445 contrib[i].p = (CONTRIB *)calloc((int)(width * 2 + 1),
446 sizeof(CONTRIB));
447 center = (double) i / xscale;
448 left = ceil(center - width);
449 right = floor(center + width);
450 for(j = left; j <= right; ++j) {
451 rweight = center - (double) j;
452 rweight = (*filterf)(rweight / fscale) / fscale;
453 if(j < 0) {
454 n = -j;
455 } else if(j >= src->width) {
456 n = (src->width - j) + src->width - 1;
457 } else {
458 n = j;
460 k = contrib[i].n++;
461 contrib[i].p[k].pixel = n*sch;
462 contrib[i].p[k].weight = rweight;
465 } else {
467 for(i = 0; i < new_width; ++i) {
468 contrib[i].n = 0;
469 contrib[i].p = (CONTRIB *)calloc((int) (fwidth * 2 + 1),
470 sizeof(CONTRIB));
471 center = (double) i / xscale;
472 left = ceil(center - fwidth);
473 right = floor(center + fwidth);
474 for(j = left; j <= right; ++j) {
475 rweight = center - (double) j;
476 rweight = (*filterf)(rweight);
477 if(j < 0) {
478 n = -j;
479 } else if(j >= src->width) {
480 n = (src->width - j) + src->width - 1;
481 } else {
482 n = j;
484 k = contrib[i].n++;
485 contrib[i].p[k].pixel = n*sch;
486 contrib[i].p[k].weight = rweight;
491 /* apply filter to zoom horizontally from src to tmp */
492 p = tmp->data;
495 for(k = 0; k < tmp->height; ++k) {
496 CONTRIB *pp;
498 sp = src->data + src->width*k*sch;
500 for(i = 0; i < tmp->width; ++i) {
501 rweight = gweight = bweight = 0.0;
503 pp = contrib[i].p;
505 for(j = 0; j < contrib[i].n; ++j) {
506 rweight += sp[pp[j].pixel] * pp[j].weight;
507 gweight += sp[pp[j].pixel+1] * pp[j].weight;
508 bweight += sp[pp[j].pixel+2] * pp[j].weight;
510 *p++ = CLAMP(rweight, 0, 255);
511 *p++ = CLAMP(gweight, 0, 255);
512 *p++ = CLAMP(bweight, 0, 255);
516 /* free the memory allocated for horizontal filter weights */
517 for(i = 0; i < tmp->width; ++i) {
518 free(contrib[i].p);
520 free(contrib);
522 /* pre-calculate filter contributions for a column */
523 contrib = (CLIST *)calloc(dst->height, sizeof(CLIST));
524 if(yscale < 1.0) {
525 width = fwidth / yscale;
526 fscale = 1.0 / yscale;
527 for(i = 0; i < dst->height; ++i) {
528 contrib[i].n = 0;
529 contrib[i].p = (CONTRIB *)calloc((int) (width * 2 + 1),
530 sizeof(CONTRIB));
531 center = (double) i / yscale;
532 left = ceil(center - width);
533 right = floor(center + width);
534 for(j = left; j <= right; ++j) {
535 rweight = center - (double) j;
536 rweight = (*filterf)(rweight / fscale) / fscale;
537 if(j < 0) {
538 n = -j;
539 } else if(j >= tmp->height) {
540 n = (tmp->height - j) + tmp->height - 1;
541 } else {
542 n = j;
544 k = contrib[i].n++;
545 contrib[i].p[k].pixel = n*3;
546 contrib[i].p[k].weight = rweight;
549 } else {
550 for(i = 0; i < dst->height; ++i) {
551 contrib[i].n = 0;
552 contrib[i].p = (CONTRIB *)calloc((int) (fwidth * 2 + 1),
553 sizeof(CONTRIB));
554 center = (double) i / yscale;
555 left = ceil(center - fwidth);
556 right = floor(center + fwidth);
557 for(j = left; j <= right; ++j) {
558 rweight = center - (double) j;
559 rweight = (*filterf)(rweight);
560 if(j < 0) {
561 n = -j;
562 } else if(j >= tmp->height) {
563 n = (tmp->height - j) + tmp->height - 1;
564 } else {
565 n = j;
567 k = contrib[i].n++;
568 contrib[i].p[k].pixel = n*3;
569 contrib[i].p[k].weight = rweight;
574 /* apply filter to zoom vertically from tmp to dst */
575 sp = malloc(tmp->height*3);
577 for(k = 0; k < new_width; ++k) {
578 CONTRIB *pp;
580 p = dst->data + k*3;
582 /* copy a column into a row */
584 int i;
585 unsigned char *p, *d;
587 d = sp;
588 for(i = tmp->height, p = tmp->data + k*3; i-- > 0;
589 p += tmp->width*3) {
590 *d++ = *p;
591 *d++ = *(p+1);
592 *d++ = *(p+2);
595 for(i = 0; i < new_height; ++i) {
596 rweight = gweight = bweight = 0.0;
598 pp = contrib[i].p;
600 for(j = 0; j < contrib[i].n; ++j) {
601 rweight += sp[pp[j].pixel] * pp[j].weight;
602 gweight += sp[pp[j].pixel+1] * pp[j].weight;
603 bweight += sp[pp[j].pixel+2] * pp[j].weight;
605 *p = CLAMP(rweight, 0, 255);
606 *(p+1) = CLAMP(gweight, 0, 255);
607 *(p+2) = CLAMP(bweight, 0, 255);
608 p += new_width*3;
611 free(sp);
613 /* free the memory allocated for vertical filter weights */
614 for(i = 0; i < dst->height; ++i) {
615 free(contrib[i].p);
617 free(contrib);
619 RDestroyImage(tmp);
621 return dst;