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,
36 *----------------------------------------------------------------------
38 * Creates a scaled copy of an image.
41 * The new scaled image.
43 *----------------------------------------------------------------------
45 RImage
*RScaleImage(RImage
* image
, unsigned new_width
, unsigned new_height
)
55 if (new_width
== image
->width
&& new_height
== image
->height
)
56 return RCloneImage(image
);
58 img
= RCreateImage(new_width
, new_height
, image
->format
== RRGBAFormat
);
63 /* fixed point math idea taken from Imlib by
64 * Carsten Haitzler (Rasterman) */
65 dx
= (image
->width
<< 16) / new_width
;
66 dy
= (image
->height
<< 16) / new_height
;
72 if (image
->format
== RRGBAFormat
) {
73 for (y
= 0; y
< new_height
; y
++) {
74 t
= image
->width
* (py
>> 16);
76 s
= image
->data
+ (t
<< 2); /* image->data+t*4 */
80 for (x
= 0; x
< new_width
; x
++) {
91 s
+= t
<< 2; /* t*4 */
96 for (y
= 0; y
< new_height
; y
++) {
97 t
= image
->width
* (py
>> 16);
99 s
= image
->data
+ (t
<< 1) + t
; /* image->data+t*3 */
103 for (x
= 0; x
< new_width
; x
++) {
113 s
+= (t
<< 1) + t
; /* t*3 */
123 * Filtered Image Rescaling code copy/pasted from
125 * Public Domain 1991 by Dale Schumacher
129 * filter function definitions
131 #define box_support (0.5)
133 static double box_filter(double t
)
135 if ((t
> -0.5) && (t
<= 0.5))
140 #define triangle_support (1.0)
142 static double triangle_filter(double t
)
151 #define bell_support (1.5)
153 static double bell_filter(double t
) /* box (*) box (*) box */
158 return (.75 - (t
* t
));
161 return (.5 * (t
* t
));
166 #define B_spline_support (2.0)
168 static double B_spline_filter(double t
) /* box (*) box (*) box (*) box */
176 return ((.5 * tt
* t
) - tt
+ (2.0 / 3.0));
179 return ((1.0 / 6.0) * (t
* t
* t
));
184 static double sinc(double x
)
187 * The original code did this:
189 * This code is unsafe, it should be:
190 * if (fabs(x) > EPSILON) ...
192 * But the call to fabs is already done in the *ONLY* function
193 * that call sinc: 'Lanczos3_filter'
195 * The goal was to avoid a Divide-by-0 error, now we also
196 * avoid a +/-inf result too
204 #define Lanczos3_support (3.0)
206 static double Lanczos3_filter(double t
)
211 return (sinc(t
) * sinc(t
/ 3.0));
215 #define Mitchell_support (2.0)
217 #define B (1.0 / 3.0)
218 #define C (1.0 / 3.0)
220 static double Mitchell_filter(double t
)
228 t
= (((12.0 - 9.0 * B
- 6.0 * C
) * (t
* tt
))
229 + ((-18.0 + 12.0 * B
+ 6.0 * C
) * tt
)
232 } else if (t
< 2.0) {
233 t
= (((-1.0 * B
- 6.0 * C
) * (t
* tt
))
234 + ((6.0 * B
+ 30.0 * C
) * tt
)
235 + ((-12.0 * B
- 48.0 * C
) * t
)
236 + (8.0 * B
+ 24 * C
));
242 static double (*filterf
)(double) = Mitchell_filter
;
243 static double fwidth
= Mitchell_support
;
245 void wraster_change_filter(RScalingFilter type
)
249 filterf
= box_filter
;
250 fwidth
= box_support
;
252 case RTriangleFilter
:
253 filterf
= triangle_filter
;
254 fwidth
= triangle_support
;
257 filterf
= bell_filter
;
258 fwidth
= bell_support
;
261 filterf
= B_spline_filter
;
262 fwidth
= B_spline_support
;
264 case RLanczos3Filter
:
265 filterf
= Lanczos3_filter
;
266 fwidth
= Lanczos3_support
;
269 case RMitchellFilter
:
270 filterf
= Mitchell_filter
;
271 fwidth
= Mitchell_support
;
277 * image rescaling routine
286 int n
; /* number of contributors */
287 CONTRIB
*p
; /* pointer to list of contributions */
290 /* clamp the input to the specified range */
291 #define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : v)
293 /* return of calloc is not checked if NULL in the function below! */
294 RImage
*RSmoothScaleImage(RImage
* src
, unsigned new_width
, unsigned new_height
)
296 CLIST
*contrib
; /* array of contribution lists */
297 RImage
*tmp
; /* intermediate image */
298 double xscale
, yscale
; /* zoom scale factors */
299 int i
, j
, k
; /* loop variables */
300 int n
; /* pixel number */
301 double center
, left
, right
; /* filter calculation variables */
302 double width
, fscale
; /* filter calculation variables */
303 double rweight
, gweight
, bweight
;
307 int sch
= src
->format
== RRGBAFormat
? 4 : 3;
309 dst
= RCreateImage(new_width
, new_height
, False
);
311 /* create intermediate image to hold horizontal zoom */
312 tmp
= RCreateImage(dst
->width
, src
->height
, False
);
313 xscale
= (double)new_width
/ (double)src
->width
;
314 yscale
= (double)new_height
/ (double)src
->height
;
316 /* pre-calculate filter contributions for a row */
317 contrib
= (CLIST
*) calloc(new_width
, sizeof(CLIST
));
319 width
= fwidth
/ xscale
;
320 fscale
= 1.0 / xscale
;
321 for (i
= 0; i
< new_width
; ++i
) {
323 contrib
[i
].p
= (CONTRIB
*) calloc((int) ceil(width
* 2 + 1), sizeof(CONTRIB
));
324 center
= (double)i
/ xscale
;
325 left
= ceil(center
- width
);
326 right
= floor(center
+ width
);
327 for (j
= left
; j
<= right
; ++j
) {
328 rweight
= center
- (double)j
;
329 rweight
= (*filterf
) (rweight
/ fscale
) / fscale
;
332 } else if (j
>= src
->width
) {
333 n
= (src
->width
- j
) + src
->width
- 1;
338 contrib
[i
].p
[k
].pixel
= n
* sch
;
339 contrib
[i
].p
[k
].weight
= rweight
;
344 for (i
= 0; i
< new_width
; ++i
) {
346 contrib
[i
].p
= (CONTRIB
*) calloc((int) ceil(fwidth
* 2 + 1), sizeof(CONTRIB
));
347 center
= (double)i
/ xscale
;
348 left
= ceil(center
- fwidth
);
349 right
= floor(center
+ fwidth
);
350 for (j
= left
; j
<= right
; ++j
) {
351 rweight
= center
- (double)j
;
352 rweight
= (*filterf
) (rweight
);
355 } else if (j
>= src
->width
) {
356 n
= (src
->width
- j
) + src
->width
- 1;
361 contrib
[i
].p
[k
].pixel
= n
* sch
;
362 contrib
[i
].p
[k
].weight
= rweight
;
367 /* apply filter to zoom horizontally from src to tmp */
370 for (k
= 0; k
< tmp
->height
; ++k
) {
373 sp
= src
->data
+ src
->width
* k
* sch
;
375 for (i
= 0; i
< tmp
->width
; ++i
) {
376 rweight
= gweight
= bweight
= 0.0;
380 for (j
= 0; j
< contrib
[i
].n
; ++j
) {
381 rweight
+= sp
[pp
[j
].pixel
] * pp
[j
].weight
;
382 gweight
+= sp
[pp
[j
].pixel
+ 1] * pp
[j
].weight
;
383 bweight
+= sp
[pp
[j
].pixel
+ 2] * pp
[j
].weight
;
385 *p
++ = CLAMP(rweight
, 0, 255);
386 *p
++ = CLAMP(gweight
, 0, 255);
387 *p
++ = CLAMP(bweight
, 0, 255);
391 /* free the memory allocated for horizontal filter weights */
392 for (i
= 0; i
< new_width
; ++i
) {
397 /* pre-calculate filter contributions for a column */
398 contrib
= (CLIST
*) calloc(dst
->height
, sizeof(CLIST
));
400 width
= fwidth
/ yscale
;
401 fscale
= 1.0 / yscale
;
402 for (i
= 0; i
< dst
->height
; ++i
) {
404 contrib
[i
].p
= (CONTRIB
*) calloc((int) ceil(width
* 2 + 1), sizeof(CONTRIB
));
405 center
= (double)i
/ yscale
;
406 left
= ceil(center
- width
);
407 right
= floor(center
+ width
);
408 for (j
= left
; j
<= right
; ++j
) {
409 rweight
= center
- (double)j
;
410 rweight
= (*filterf
) (rweight
/ fscale
) / fscale
;
413 } else if (j
>= tmp
->height
) {
414 n
= (tmp
->height
- j
) + tmp
->height
- 1;
419 contrib
[i
].p
[k
].pixel
= n
* 3;
420 contrib
[i
].p
[k
].weight
= rweight
;
424 for (i
= 0; i
< dst
->height
; ++i
) {
426 contrib
[i
].p
= (CONTRIB
*) calloc((int) ceil(fwidth
* 2 + 1), sizeof(CONTRIB
));
427 center
= (double)i
/ yscale
;
428 left
= ceil(center
- fwidth
);
429 right
= floor(center
+ fwidth
);
430 for (j
= left
; j
<= right
; ++j
) {
431 rweight
= center
- (double)j
;
432 rweight
= (*filterf
) (rweight
);
435 } else if (j
>= tmp
->height
) {
436 n
= (tmp
->height
- j
) + tmp
->height
- 1;
441 contrib
[i
].p
[k
].pixel
= n
* 3;
442 contrib
[i
].p
[k
].weight
= rweight
;
447 /* apply filter to zoom vertically from tmp to dst */
448 sp
= malloc(tmp
->height
* 3);
450 for (k
= 0; k
< new_width
; ++k
) {
453 p
= dst
->data
+ k
* 3;
455 /* copy a column into a row */
458 unsigned char *p
, *d
;
461 for (i
= tmp
->height
, p
= tmp
->data
+ k
* 3; i
-- > 0; p
+= tmp
->width
* 3) {
467 for (i
= 0; i
< new_height
; ++i
) {
468 rweight
= gweight
= bweight
= 0.0;
472 for (j
= 0; j
< contrib
[i
].n
; ++j
) {
473 rweight
+= sp
[pp
[j
].pixel
] * pp
[j
].weight
;
474 gweight
+= sp
[pp
[j
].pixel
+ 1] * pp
[j
].weight
;
475 bweight
+= sp
[pp
[j
].pixel
+ 2] * pp
[j
].weight
;
477 *p
= CLAMP(rweight
, 0, 255);
478 *(p
+ 1) = CLAMP(gweight
, 0, 255);
479 *(p
+ 2) = CLAMP(bweight
, 0, 255);
485 /* free the memory allocated for vertical filter weights */
486 for (i
= 0; i
< dst
->height
; ++i
) {