Update Serbian translation from master branch
[wmaker-crm.git] / wrlib / scale.c
blob93c5172d9f702a76d251e874edbba91b186610da
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>
30 #include <assert.h>
32 #include "wraster.h"
33 #include "scale.h"
34 #include "wr_i18n.h"
38 *----------------------------------------------------------------------
39 * RScaleImage--
40 * Creates a scaled copy of an image.
42 * Returns:
43 * The new scaled image.
45 *----------------------------------------------------------------------
47 RImage *RScaleImage(RImage * image, unsigned new_width, unsigned new_height)
49 int ox;
50 int px, py;
51 register int x, y, t;
52 int dx, dy;
53 unsigned char *s;
54 unsigned char *d;
55 RImage *img;
57 if (image == NULL)
58 return NULL;
60 if (new_width == image->width && new_height == image->height)
61 return RCloneImage(image);
63 img = RCreateImage(new_width, new_height, image->format == RRGBAFormat);
65 if (!img)
66 return NULL;
68 /* fixed point math idea taken from Imlib by
69 * Carsten Haitzler (Rasterman) */
70 dx = (image->width << 16) / new_width;
71 dy = (image->height << 16) / new_height;
73 py = 0;
75 d = img->data;
77 if (image->format == RRGBAFormat) {
78 for (y = 0; y < new_height; y++) {
79 t = image->width * (py >> 16);
81 s = image->data + (t << 2); /* image->data+t*4 */
83 ox = 0;
84 px = 0;
85 for (x = 0; x < new_width; x++) {
86 px += dx;
88 *(d++) = *(s);
89 *(d++) = *(s + 1);
90 *(d++) = *(s + 2);
91 *(d++) = *(s + 3);
93 t = (px - ox) >> 16;
94 ox += t << 16;
96 s += t << 2; /* t*4 */
98 py += dy;
100 } else {
101 for (y = 0; y < new_height; y++) {
102 t = image->width * (py >> 16);
104 s = image->data + (t << 1) + t; /* image->data+t*3 */
106 ox = 0;
107 px = 0;
108 for (x = 0; x < new_width; x++) {
109 px += dx;
111 *(d++) = *(s);
112 *(d++) = *(s + 1);
113 *(d++) = *(s + 2);
115 t = (px - ox) >> 16;
116 ox += t << 16;
118 s += (t << 1) + t; /* t*3 */
120 py += dy;
124 return img;
128 * Filtered Image Rescaling code copy/pasted from
129 * Graphics Gems III
130 * Public Domain 1991 by Dale Schumacher
134 * filter function definitions
136 #define box_support (0.5)
138 static double box_filter(double t)
140 if ((t > -0.5) && (t <= 0.5))
141 return (1.0);
142 return (0.0);
145 #define triangle_support (1.0)
147 static double triangle_filter(double t)
149 if (t < 0.0)
150 t = -t;
151 if (t < 1.0)
152 return (1.0 - t);
153 return (0.0);
156 #define bell_support (1.5)
158 static double bell_filter(double t) /* box (*) box (*) box */
160 if (t < 0)
161 t = -t;
162 if (t < .5)
163 return (.75 - (t * t));
164 if (t < 1.5) {
165 t = (t - 1.5);
166 return (.5 * (t * t));
168 return (0.0);
171 #define B_spline_support (2.0)
173 static double B_spline_filter(double t) /* box (*) box (*) box (*) box */
175 double tt;
177 if (t < 0)
178 t = -t;
179 if (t < 1) {
180 tt = t * t;
181 return ((.5 * tt * t) - tt + (2.0 / 3.0));
182 } else if (t < 2) {
183 t = 2 - t;
184 return ((1.0 / 6.0) * (t * t * t));
186 return (0.0);
189 static double sinc(double x)
192 * The original code did this:
193 * if (x != 0) ...
194 * This code is unsafe, it should be:
195 * if (fabs(x) > EPSILON) ...
197 * But the call to fabs is already done in the *ONLY* function
198 * that call sinc: 'Lanczos3_filter'
200 * The goal was to avoid a Divide-by-0 error, now we also
201 * avoid a +/-inf result too
203 x *= WM_PI;
204 if (x > 1.0E-9)
205 return (sin(x) / x);
206 return (1.0);
209 #define Lanczos3_support (3.0)
211 static double Lanczos3_filter(double t)
213 if (t < 0)
214 t = -t;
215 if (t < 3.0)
216 return (sinc(t) * sinc(t / 3.0));
217 return (0.0);
220 #define Mitchell_support (2.0)
222 #define B (1.0 / 3.0)
223 #define C (1.0 / 3.0)
225 static double Mitchell_filter(double t)
227 double tt;
229 tt = t * t;
230 if (t < 0)
231 t = -t;
232 if (t < 1.0) {
233 t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt))
234 + ((-18.0 + 12.0 * B + 6.0 * C) * tt)
235 + (6.0 - 2 * B));
236 return (t / 6.0);
237 } else if (t < 2.0) {
238 t = (((-1.0 * B - 6.0 * C) * (t * tt))
239 + ((6.0 * B + 30.0 * C) * tt)
240 + ((-12.0 * B - 48.0 * C) * t)
241 + (8.0 * B + 24 * C));
242 return (t / 6.0);
244 return (0.0);
247 static double (*filterf)(double) = Mitchell_filter;
248 static double fwidth = Mitchell_support;
250 void wraster_change_filter(RScalingFilter type)
252 switch (type) {
253 case RBoxFilter:
254 filterf = box_filter;
255 fwidth = box_support;
256 break;
257 case RTriangleFilter:
258 filterf = triangle_filter;
259 fwidth = triangle_support;
260 break;
261 case RBellFilter:
262 filterf = bell_filter;
263 fwidth = bell_support;
264 break;
265 case RBSplineFilter:
266 filterf = B_spline_filter;
267 fwidth = B_spline_support;
268 break;
269 case RLanczos3Filter:
270 filterf = Lanczos3_filter;
271 fwidth = Lanczos3_support;
272 break;
273 default:
274 case RMitchellFilter:
275 filterf = Mitchell_filter;
276 fwidth = Mitchell_support;
277 break;
282 * image rescaling routine
285 typedef struct {
286 int pixel;
287 double weight;
288 } CONTRIB;
290 typedef struct {
291 int n; /* number of contributors */
292 CONTRIB *p; /* pointer to list of contributions */
293 } CLIST;
295 /* clamp the input to the specified range */
296 #define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : v)
298 /* return of calloc is not checked if NULL in the function below! */
299 RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
301 CLIST *contrib; /* array of contribution lists */
302 RImage *tmp; /* intermediate image */
303 double xscale, yscale; /* zoom scale factors */
304 int i, j, k; /* loop variables */
305 int n; /* pixel number */
306 double center, left, right; /* filter calculation variables */
307 double width, fscale; /* filter calculation variables */
308 double rweight, gweight, bweight;
309 RImage *dst;
310 unsigned char *p;
311 unsigned char *sp;
312 int sch = src->format == RRGBAFormat ? 4 : 3;
314 dst = RCreateImage(new_width, new_height, False);
316 /* create intermediate image to hold horizontal zoom */
317 tmp = RCreateImage(dst->width, src->height, False);
318 xscale = (double)new_width / (double)src->width;
319 yscale = (double)new_height / (double)src->height;
321 /* pre-calculate filter contributions for a row */
322 contrib = (CLIST *) calloc(new_width, sizeof(CLIST));
323 if (xscale < 1.0) {
324 width = fwidth / xscale;
325 fscale = 1.0 / xscale;
326 for (i = 0; i < new_width; ++i) {
327 contrib[i].n = 0;
328 contrib[i].p = (CONTRIB *) calloc((int) ceil(width * 2 + 1), sizeof(CONTRIB));
329 center = (double)i / xscale;
330 left = ceil(center - width);
331 right = floor(center + width);
332 for (j = left; j <= right; ++j) {
333 rweight = center - (double)j;
334 rweight = (*filterf) (rweight / fscale) / fscale;
335 if (j < 0) {
336 n = -j;
337 } else if (j >= src->width) {
338 n = (src->width - j) + src->width - 1;
339 } else {
340 n = j;
342 k = contrib[i].n++;
343 contrib[i].p[k].pixel = n * sch;
344 contrib[i].p[k].weight = rweight;
347 } else {
349 for (i = 0; i < new_width; ++i) {
350 contrib[i].n = 0;
351 contrib[i].p = (CONTRIB *) calloc((int) ceil(fwidth * 2 + 1), sizeof(CONTRIB));
352 center = (double)i / xscale;
353 left = ceil(center - fwidth);
354 right = floor(center + fwidth);
355 for (j = left; j <= right; ++j) {
356 rweight = center - (double)j;
357 rweight = (*filterf) (rweight);
358 if (j < 0) {
359 n = -j;
360 } else if (j >= src->width) {
361 n = (src->width - j) + src->width - 1;
362 } else {
363 n = j;
365 k = contrib[i].n++;
366 contrib[i].p[k].pixel = n * sch;
367 contrib[i].p[k].weight = rweight;
372 /* apply filter to zoom horizontally from src to tmp */
373 p = tmp->data;
375 for (k = 0; k < tmp->height; ++k) {
376 CONTRIB *pp;
378 sp = src->data + src->width * k * sch;
380 for (i = 0; i < tmp->width; ++i) {
381 rweight = gweight = bweight = 0.0;
383 pp = contrib[i].p;
385 for (j = 0; j < contrib[i].n; ++j) {
386 rweight += sp[pp[j].pixel] * pp[j].weight;
387 gweight += sp[pp[j].pixel + 1] * pp[j].weight;
388 bweight += sp[pp[j].pixel + 2] * pp[j].weight;
390 *p++ = CLAMP(rweight, 0, 255);
391 *p++ = CLAMP(gweight, 0, 255);
392 *p++ = CLAMP(bweight, 0, 255);
396 /* free the memory allocated for horizontal filter weights */
397 for (i = 0; i < new_width; ++i) {
398 free(contrib[i].p);
400 free(contrib);
402 /* pre-calculate filter contributions for a column */
403 contrib = (CLIST *) calloc(dst->height, sizeof(CLIST));
404 if (yscale < 1.0) {
405 width = fwidth / yscale;
406 fscale = 1.0 / yscale;
407 for (i = 0; i < dst->height; ++i) {
408 contrib[i].n = 0;
409 contrib[i].p = (CONTRIB *) calloc((int) ceil(width * 2 + 1), sizeof(CONTRIB));
410 center = (double)i / yscale;
411 left = ceil(center - width);
412 right = floor(center + width);
413 for (j = left; j <= right; ++j) {
414 rweight = center - (double)j;
415 rweight = (*filterf) (rweight / fscale) / fscale;
416 if (j < 0) {
417 n = -j;
418 } else if (j >= tmp->height) {
419 n = (tmp->height - j) + tmp->height - 1;
420 } else {
421 n = j;
423 k = contrib[i].n++;
424 contrib[i].p[k].pixel = n * 3;
425 contrib[i].p[k].weight = rweight;
428 } else {
429 for (i = 0; i < dst->height; ++i) {
430 contrib[i].n = 0;
431 contrib[i].p = (CONTRIB *) calloc((int) ceil(fwidth * 2 + 1), sizeof(CONTRIB));
432 center = (double)i / yscale;
433 left = ceil(center - fwidth);
434 right = floor(center + fwidth);
435 for (j = left; j <= right; ++j) {
436 rweight = center - (double)j;
437 rweight = (*filterf) (rweight);
438 if (j < 0) {
439 n = -j;
440 } else if (j >= tmp->height) {
441 n = (tmp->height - j) + tmp->height - 1;
442 } else {
443 n = j;
445 k = contrib[i].n++;
446 contrib[i].p[k].pixel = n * 3;
447 contrib[i].p[k].weight = rweight;
452 /* apply filter to zoom vertically from tmp to dst */
453 sp = malloc(tmp->height * 3);
455 for (k = 0; k < new_width; ++k) {
456 CONTRIB *pp;
458 p = dst->data + k * 3;
460 /* copy a column into a row */
462 int i;
463 unsigned char *p, *d;
465 d = sp;
466 for (i = tmp->height, p = tmp->data + k * 3; i-- > 0; p += tmp->width * 3) {
467 *d++ = *p;
468 *d++ = *(p + 1);
469 *d++ = *(p + 2);
472 for (i = 0; i < new_height; ++i) {
473 rweight = gweight = bweight = 0.0;
475 pp = contrib[i].p;
477 for (j = 0; j < contrib[i].n; ++j) {
478 rweight += sp[pp[j].pixel] * pp[j].weight;
479 gweight += sp[pp[j].pixel + 1] * pp[j].weight;
480 bweight += sp[pp[j].pixel + 2] * pp[j].weight;
482 *p = CLAMP(rweight, 0, 255);
483 *(p + 1) = CLAMP(gweight, 0, 255);
484 *(p + 2) = CLAMP(bweight, 0, 255);
485 p += new_width * 3;
488 free(sp);
490 /* free the memory allocated for vertical filter weights */
491 for (i = 0; i < dst->height; ++i) {
492 free(contrib[i].p);
494 free(contrib);
496 RReleaseImage(tmp);
498 return dst;