Fixed improper variables definition in header file
[wmaker-crm.git] / wrlib / scale.c
blob6990afe2313bbd4a20f7fa7b683360311f6df235
1 /* scale.c - image scaling
3 * Raster graphics library
5 * Copyright (c) 1997-2003 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., 51 Franklin St, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
23 #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.14159265358979323846
33 #endif
35 #include <assert.h>
37 #include "wraster.h"
40 *----------------------------------------------------------------------
41 * RScaleImage--
42 * Creates a scaled copy of an image.
44 * Returns:
45 * The new scaled image.
47 *----------------------------------------------------------------------
49 RImage *RScaleImage(RImage * image, unsigned new_width, unsigned new_height)
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;
59 if (new_width == image->width && new_height == image->height)
60 return RCloneImage(image);
62 img = RCreateImage(new_width, new_height, image->format == RRGBAFormat);
64 if (!img)
65 return NULL;
67 /* fixed point math idea taken from Imlib by
68 * Carsten Haitzler (Rasterman) */
69 dx = (image->width << 16) / new_width;
70 dy = (image->height << 16) / new_height;
72 py = 0;
74 d = img->data;
76 if (image->format == RRGBAFormat) {
77 for (y = 0; y < new_height; y++) {
78 t = image->width * (py >> 16);
80 s = image->data + (t << 2); /* image->data+t*4 */
82 ox = 0;
83 px = 0;
84 for (x = 0; x < new_width; x++) {
85 px += dx;
87 *(d++) = *(s);
88 *(d++) = *(s + 1);
89 *(d++) = *(s + 2);
90 *(d++) = *(s + 3);
92 t = (px - ox) >> 16;
93 ox += t << 16;
95 s += t << 2; /* t*4 */
97 py += dy;
99 } else {
100 for (y = 0; y < new_height; y++) {
101 t = image->width * (py >> 16);
103 s = image->data + (t << 1) + t; /* image->data+t*3 */
105 ox = 0;
106 px = 0;
107 for (x = 0; x < new_width; x++) {
108 px += dx;
110 *(d++) = *(s);
111 *(d++) = *(s + 1);
112 *(d++) = *(s + 2);
114 t = (px - ox) >> 16;
115 ox += t << 16;
117 s += (t << 1) + t; /* t*3 */
119 py += dy;
123 return img;
127 * Filtered Image Rescaling code copy/pasted from
128 * Graphics Gems III
129 * Public Domain 1991 by Dale Schumacher
133 * filter function definitions
135 #define box_support (0.5)
137 static double box_filter(double t)
139 if ((t > -0.5) && (t <= 0.5))
140 return (1.0);
141 return (0.0);
144 #define triangle_support (1.0)
146 static double triangle_filter(double t)
148 if (t < 0.0)
149 t = -t;
150 if (t < 1.0)
151 return (1.0 - t);
152 return (0.0);
155 #define bell_support (1.5)
157 static double bell_filter(double t) /* box (*) box (*) box */
159 if (t < 0)
160 t = -t;
161 if (t < .5)
162 return (.75 - (t * t));
163 if (t < 1.5) {
164 t = (t - 1.5);
165 return (.5 * (t * t));
167 return (0.0);
170 #define B_spline_support (2.0)
172 static double B_spline_filter(double t) /* box (*) box (*) box (*) box */
174 double tt;
176 if (t < 0)
177 t = -t;
178 if (t < 1) {
179 tt = t * t;
180 return ((.5 * tt * t) - tt + (2.0 / 3.0));
181 } else if (t < 2) {
182 t = 2 - t;
183 return ((1.0 / 6.0) * (t * t * t));
185 return (0.0);
188 static double sinc(double x)
190 x *= PI;
191 if (x != 0)
192 return (sin(x) / x);
193 return (1.0);
196 #define Lanczos3_support (3.0)
198 static double Lanczos3_filter(double t)
200 if (t < 0)
201 t = -t;
202 if (t < 3.0)
203 return (sinc(t) * sinc(t / 3.0));
204 return (0.0);
207 #define Mitchell_support (2.0)
209 #define B (1.0 / 3.0)
210 #define C (1.0 / 3.0)
212 static double Mitchell_filter(double t)
214 double tt;
216 tt = t * t;
217 if (t < 0)
218 t = -t;
219 if (t < 1.0) {
220 t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt))
221 + ((-18.0 + 12.0 * B + 6.0 * C) * tt)
222 + (6.0 - 2 * B));
223 return (t / 6.0);
224 } else if (t < 2.0) {
225 t = (((-1.0 * B - 6.0 * C) * (t * tt))
226 + ((6.0 * B + 30.0 * C) * tt)
227 + ((-12.0 * B - 48.0 * C) * t)
228 + (8.0 * B + 24 * C));
229 return (t / 6.0);
231 return (0.0);
234 static double (*filterf)(double) = Mitchell_filter;
235 static double fwidth = Mitchell_support;
237 void _wraster_change_filter(int type)
239 switch (type) {
240 case RBoxFilter:
241 filterf = box_filter;
242 fwidth = box_support;
243 break;
244 case RTriangleFilter:
245 filterf = triangle_filter;
246 fwidth = triangle_support;
247 break;
248 case RBellFilter:
249 filterf = bell_filter;
250 fwidth = bell_support;
251 break;
252 case RBSplineFilter:
253 filterf = B_spline_filter;
254 fwidth = B_spline_support;
255 break;
256 case RLanczos3Filter:
257 filterf = Lanczos3_filter;
258 fwidth = Lanczos3_support;
259 break;
260 default:
261 case RMitchellFilter:
262 filterf = Mitchell_filter;
263 fwidth = Mitchell_support;
264 break;
269 * image rescaling routine
272 typedef struct {
273 int pixel;
274 double weight;
275 } CONTRIB;
277 typedef struct {
278 int n; /* number of contributors */
279 CONTRIB *p; /* pointer to list of contributions */
280 } CLIST;
282 /* clamp the input to the specified range */
283 #define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : v)
285 /* return of calloc is not checked if NULL in the function below! */
286 RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
288 CLIST *contrib; /* array of contribution lists */
289 RImage *tmp; /* intermediate image */
290 double xscale, yscale; /* zoom scale factors */
291 int i, j, k; /* loop variables */
292 int n; /* pixel number */
293 double center, left, right; /* filter calculation variables */
294 double width, fscale; /* filter calculation variables */
295 double rweight, gweight, bweight;
296 RImage *dst;
297 unsigned char *p;
298 unsigned char *sp;
299 int sch = src->format == RRGBAFormat ? 4 : 3;
301 dst = RCreateImage(new_width, new_height, False);
303 /* create intermediate image to hold horizontal zoom */
304 tmp = RCreateImage(dst->width, src->height, False);
305 xscale = (double)new_width / (double)src->width;
306 yscale = (double)new_height / (double)src->height;
308 /* pre-calculate filter contributions for a row */
309 contrib = (CLIST *) calloc(new_width, sizeof(CLIST));
310 if (xscale < 1.0) {
311 width = fwidth / xscale;
312 fscale = 1.0 / xscale;
313 for (i = 0; i < new_width; ++i) {
314 contrib[i].n = 0;
315 contrib[i].p = (CONTRIB *) calloc((int) ceil(width * 2 + 1), sizeof(CONTRIB));
316 center = (double)i / xscale;
317 left = ceil(center - width);
318 right = floor(center + width);
319 for (j = left; j <= right; ++j) {
320 rweight = center - (double)j;
321 rweight = (*filterf) (rweight / fscale) / fscale;
322 if (j < 0) {
323 n = -j;
324 } else if (j >= src->width) {
325 n = (src->width - j) + src->width - 1;
326 } else {
327 n = j;
329 k = contrib[i].n++;
330 contrib[i].p[k].pixel = n * sch;
331 contrib[i].p[k].weight = rweight;
334 } else {
336 for (i = 0; i < new_width; ++i) {
337 contrib[i].n = 0;
338 contrib[i].p = (CONTRIB *) calloc((int) ceil(fwidth * 2 + 1), sizeof(CONTRIB));
339 center = (double)i / xscale;
340 left = ceil(center - fwidth);
341 right = floor(center + fwidth);
342 for (j = left; j <= right; ++j) {
343 rweight = center - (double)j;
344 rweight = (*filterf) (rweight);
345 if (j < 0) {
346 n = -j;
347 } else if (j >= src->width) {
348 n = (src->width - j) + src->width - 1;
349 } else {
350 n = j;
352 k = contrib[i].n++;
353 contrib[i].p[k].pixel = n * sch;
354 contrib[i].p[k].weight = rweight;
359 /* apply filter to zoom horizontally from src to tmp */
360 p = tmp->data;
362 for (k = 0; k < tmp->height; ++k) {
363 CONTRIB *pp;
365 sp = src->data + src->width * k * sch;
367 for (i = 0; i < tmp->width; ++i) {
368 rweight = gweight = bweight = 0.0;
370 pp = contrib[i].p;
372 for (j = 0; j < contrib[i].n; ++j) {
373 rweight += sp[pp[j].pixel] * pp[j].weight;
374 gweight += sp[pp[j].pixel + 1] * pp[j].weight;
375 bweight += sp[pp[j].pixel + 2] * pp[j].weight;
377 *p++ = CLAMP(rweight, 0, 255);
378 *p++ = CLAMP(gweight, 0, 255);
379 *p++ = CLAMP(bweight, 0, 255);
383 /* free the memory allocated for horizontal filter weights */
384 for (i = 0; i < new_width; ++i) {
385 free(contrib[i].p);
387 free(contrib);
389 /* pre-calculate filter contributions for a column */
390 contrib = (CLIST *) calloc(dst->height, sizeof(CLIST));
391 if (yscale < 1.0) {
392 width = fwidth / yscale;
393 fscale = 1.0 / yscale;
394 for (i = 0; i < dst->height; ++i) {
395 contrib[i].n = 0;
396 contrib[i].p = (CONTRIB *) calloc((int) ceil(width * 2 + 1), sizeof(CONTRIB));
397 center = (double)i / yscale;
398 left = ceil(center - width);
399 right = floor(center + width);
400 for (j = left; j <= right; ++j) {
401 rweight = center - (double)j;
402 rweight = (*filterf) (rweight / fscale) / fscale;
403 if (j < 0) {
404 n = -j;
405 } else if (j >= tmp->height) {
406 n = (tmp->height - j) + tmp->height - 1;
407 } else {
408 n = j;
410 k = contrib[i].n++;
411 contrib[i].p[k].pixel = n * 3;
412 contrib[i].p[k].weight = rweight;
415 } else {
416 for (i = 0; i < dst->height; ++i) {
417 contrib[i].n = 0;
418 contrib[i].p = (CONTRIB *) calloc((int) ceil(fwidth * 2 + 1), sizeof(CONTRIB));
419 center = (double)i / yscale;
420 left = ceil(center - fwidth);
421 right = floor(center + fwidth);
422 for (j = left; j <= right; ++j) {
423 rweight = center - (double)j;
424 rweight = (*filterf) (rweight);
425 if (j < 0) {
426 n = -j;
427 } else if (j >= tmp->height) {
428 n = (tmp->height - j) + tmp->height - 1;
429 } else {
430 n = j;
432 k = contrib[i].n++;
433 contrib[i].p[k].pixel = n * 3;
434 contrib[i].p[k].weight = rweight;
439 /* apply filter to zoom vertically from tmp to dst */
440 sp = malloc(tmp->height * 3);
442 for (k = 0; k < new_width; ++k) {
443 CONTRIB *pp;
445 p = dst->data + k * 3;
447 /* copy a column into a row */
449 int i;
450 unsigned char *p, *d;
452 d = sp;
453 for (i = tmp->height, p = tmp->data + k * 3; i-- > 0; p += tmp->width * 3) {
454 *d++ = *p;
455 *d++ = *(p + 1);
456 *d++ = *(p + 2);
459 for (i = 0; i < new_height; ++i) {
460 rweight = gweight = bweight = 0.0;
462 pp = contrib[i].p;
464 for (j = 0; j < contrib[i].n; ++j) {
465 rweight += sp[pp[j].pixel] * pp[j].weight;
466 gweight += sp[pp[j].pixel + 1] * pp[j].weight;
467 bweight += sp[pp[j].pixel + 2] * pp[j].weight;
469 *p = CLAMP(rweight, 0, 255);
470 *(p + 1) = CLAMP(gweight, 0, 255);
471 *(p + 2) = CLAMP(bweight, 0, 255);
472 p += new_width * 3;
475 free(sp);
477 /* free the memory allocated for vertical filter weights */
478 for (i = 0; i < dst->height; ++i) {
479 free(contrib[i].p);
481 free(contrib);
483 RReleaseImage(tmp);
485 return dst;