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., 675 Mass Ave, Cambridge, MA 02139, USA.
32 #define PI 3.14159265358979323846
41 *----------------------------------------------------------------------
43 * Creates a scaled copy of an image.
46 * The new scaled image.
48 *----------------------------------------------------------------------
52 RScaleImage(RImage
*image
, unsigned new_width
, unsigned new_height
)
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
);
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
;
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 */
89 for (x
=0; x
<new_width
; x
++) {
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 */
112 for (x
=0; x
<new_width
; x
++) {
122 s
+= (t
<<1)+t
; /* t*3 */
133 RScaleImage(RImage
*src
, unsigned new_width
, unsigned new_height
)
141 unsigned char *sr
, *sg
, *sb
, *sa
;
142 unsigned char *dr
, *dg
, *db
, *da
;
147 dst
= RCreateImage(new_width
, new_height
, src
->data
[3]!=NULL
);
150 ee
= (ddy
/2) - dst
->height
;
155 e
= (src
->width
/2)-xd
;
169 for (yd
= 0; yd
< new_height
; yd
++) {
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
++) {
196 for (yd
= 0; yd
< new_height
; yd
++) {
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
++) {
234 * Filtered Image Rescaling code copy/pasted from
236 * Public Domain 1991 by Dale Schumacher
241 * filter function definitions
244 #define filter_support (1.0)
250 /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */
252 if(t
< 1.0) return((2.0 * t
- 3.0) * t
* t
+ 1.0);
256 #define box_support (0.5)
262 if((t
> -0.5) && (t
<= 0.5)) return(1.0);
266 #define triangle_support (1.0)
273 if(t
< 1.0) return(1.0 - t
);
277 #define bell_support (1.5)
280 bell_filter(t
) /* box (*) box (*) box */
284 if(t
< .5) return(.75 - (t
* t
));
287 return(.5 * (t
* t
));
292 #define B_spline_support (2.0)
295 B_spline_filter(t
) /* box (*) box (*) box (*) box */
303 return((.5 * tt
* t
) - tt
+ (2.0 / 3.0));
306 return((1.0 / 6.0) * (t
* t
* t
));
316 if(x
!= 0) return(sin(x
) / x
);
320 #define Lanczos3_support (3.0)
327 if(t
< 3.0) return(sinc(t
) * sinc(t
/3.0));
331 #define Mitchell_support (2.0)
333 #define B (1.0 / 3.0)
334 #define C (1.0 / 3.0)
345 t
= (((12.0 - 9.0 * B
- 6.0 * C
) * (t
* tt
))
346 + ((-18.0 + 12.0 * B
+ 6.0 * C
) * tt
)
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
));
359 static double (*filterf
)() = Mitchell_filter
;
360 static double fwidth
= Mitchell_support
;
363 _wraster_change_filter(int type
)
367 filterf
= box_filter
;
368 fwidth
= box_support
;
370 case RTriangleFilter
:
371 filterf
= triangle_filter
;
372 fwidth
= triangle_support
;
375 filterf
= bell_filter
;
376 fwidth
= bell_support
;
379 filterf
= B_spline_filter
;
380 fwidth
= B_spline_support
;
382 case RLanczos3Filter
:
383 filterf
= Lanczos3_filter
;
384 fwidth
= Lanczos3_support
;
387 case RMitchellFilter
:
388 filterf
= Mitchell_filter
;
389 fwidth
= Mitchell_support
;
396 * image rescaling routine
405 int n
; /* number of contributors */
406 CONTRIB
*p
; /* pointer to list of contributions */
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 /* return of calloc is not checked if NULL in the function below! */
417 RSmoothScaleImage(RImage
*src
, unsigned new_width
, unsigned new_height
)
419 RImage
*tmp
; /* intermediate image */
420 double xscale
, yscale
; /* zoom scale factors */
421 int i
, j
, k
; /* loop variables */
422 int n
; /* pixel number */
423 double center
, left
, right
; /* filter calculation variables */
424 double width
, fscale
; /* filter calculation variables */
425 double rweight
, gweight
, bweight
;
429 int sch
= src
->format
== RRGBAFormat
? 4 : 3;
432 dst
= RCreateImage(new_width
, new_height
, False
);
434 /* create intermediate image to hold horizontal zoom */
435 tmp
= RCreateImage(dst
->width
, src
->height
, False
);
436 xscale
= (double)new_width
/ (double)src
->width
;
437 yscale
= (double)new_height
/ (double)src
->height
;
439 /* pre-calculate filter contributions for a row */
440 contrib
= (CLIST
*)calloc(new_width
, sizeof(CLIST
));
442 width
= fwidth
/ xscale
;
443 fscale
= 1.0 / xscale
;
444 for (i
= 0; i
< new_width
; ++i
) {
446 contrib
[i
].p
= (CONTRIB
*)calloc((int)(width
* 2 + 1),
448 center
= (double) i
/ xscale
;
449 left
= ceil(center
- width
);
450 right
= floor(center
+ width
);
451 for(j
= left
; j
<= right
; ++j
) {
452 rweight
= center
- (double) j
;
453 rweight
= (*filterf
)(rweight
/ fscale
) / fscale
;
456 } else if(j
>= src
->width
) {
457 n
= (src
->width
- j
) + src
->width
- 1;
462 contrib
[i
].p
[k
].pixel
= n
*sch
;
463 contrib
[i
].p
[k
].weight
= rweight
;
468 for(i
= 0; i
< new_width
; ++i
) {
470 contrib
[i
].p
= (CONTRIB
*)calloc((int) (fwidth
* 2 + 1),
472 center
= (double) i
/ xscale
;
473 left
= ceil(center
- fwidth
);
474 right
= floor(center
+ fwidth
);
475 for(j
= left
; j
<= right
; ++j
) {
476 rweight
= center
- (double) j
;
477 rweight
= (*filterf
)(rweight
);
480 } else if(j
>= src
->width
) {
481 n
= (src
->width
- j
) + src
->width
- 1;
486 contrib
[i
].p
[k
].pixel
= n
*sch
;
487 contrib
[i
].p
[k
].weight
= rweight
;
492 /* apply filter to zoom horizontally from src to tmp */
496 for(k
= 0; k
< tmp
->height
; ++k
) {
499 sp
= src
->data
+ src
->width
*k
*sch
;
501 for(i
= 0; i
< tmp
->width
; ++i
) {
502 rweight
= gweight
= bweight
= 0.0;
506 for(j
= 0; j
< contrib
[i
].n
; ++j
) {
507 rweight
+= sp
[pp
[j
].pixel
] * pp
[j
].weight
;
508 gweight
+= sp
[pp
[j
].pixel
+1] * pp
[j
].weight
;
509 bweight
+= sp
[pp
[j
].pixel
+2] * pp
[j
].weight
;
511 *p
++ = CLAMP(rweight
, 0, 255);
512 *p
++ = CLAMP(gweight
, 0, 255);
513 *p
++ = CLAMP(bweight
, 0, 255);
517 /* free the memory allocated for horizontal filter weights */
518 for(i
= 0; i
< tmp
->width
; ++i
) {
523 /* pre-calculate filter contributions for a column */
524 contrib
= (CLIST
*)calloc(dst
->height
, sizeof(CLIST
));
526 width
= fwidth
/ yscale
;
527 fscale
= 1.0 / yscale
;
528 for(i
= 0; i
< dst
->height
; ++i
) {
530 contrib
[i
].p
= (CONTRIB
*)calloc((int) (width
* 2 + 1),
532 center
= (double) i
/ yscale
;
533 left
= ceil(center
- width
);
534 right
= floor(center
+ width
);
535 for(j
= left
; j
<= right
; ++j
) {
536 rweight
= center
- (double) j
;
537 rweight
= (*filterf
)(rweight
/ fscale
) / fscale
;
540 } else if(j
>= tmp
->height
) {
541 n
= (tmp
->height
- j
) + tmp
->height
- 1;
546 contrib
[i
].p
[k
].pixel
= n
*3;
547 contrib
[i
].p
[k
].weight
= rweight
;
551 for(i
= 0; i
< dst
->height
; ++i
) {
553 contrib
[i
].p
= (CONTRIB
*)calloc((int) (fwidth
* 2 + 1),
555 center
= (double) i
/ yscale
;
556 left
= ceil(center
- fwidth
);
557 right
= floor(center
+ fwidth
);
558 for(j
= left
; j
<= right
; ++j
) {
559 rweight
= center
- (double) j
;
560 rweight
= (*filterf
)(rweight
);
563 } else if(j
>= tmp
->height
) {
564 n
= (tmp
->height
- j
) + tmp
->height
- 1;
569 contrib
[i
].p
[k
].pixel
= n
*3;
570 contrib
[i
].p
[k
].weight
= rweight
;
575 /* apply filter to zoom vertically from tmp to dst */
576 sp
= malloc(tmp
->height
*3);
578 for(k
= 0; k
< new_width
; ++k
) {
583 /* copy a column into a row */
586 unsigned char *p
, *d
;
589 for(i
= tmp
->height
, p
= tmp
->data
+ k
*3; i
-- > 0;
596 for(i
= 0; i
< new_height
; ++i
) {
597 rweight
= gweight
= bweight
= 0.0;
601 for(j
= 0; j
< contrib
[i
].n
; ++j
) {
602 rweight
+= sp
[pp
[j
].pixel
] * pp
[j
].weight
;
603 gweight
+= sp
[pp
[j
].pixel
+1] * pp
[j
].weight
;
604 bweight
+= sp
[pp
[j
].pixel
+2] * pp
[j
].weight
;
606 *p
= CLAMP(rweight
, 0, 255);
607 *(p
+1) = CLAMP(gweight
, 0, 255);
608 *(p
+2) = CLAMP(bweight
, 0, 255);
614 /* free the memory allocated for vertical filter weights */
615 for(i
= 0; i
< dst
->height
; ++i
) {