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.
31 #define PI 3.14159265358979323846
39 *----------------------------------------------------------------------
41 * Creates a scaled copy of an image.
44 * The new scaled image.
46 *----------------------------------------------------------------------
49 RImage
*RScaleImage(RImage
* image
, unsigned new_width
, unsigned new_height
)
59 assert(new_width
>= 0 && new_height
>= 0);
61 if (new_width
== image
->width
&& new_height
== image
->height
)
62 return RCloneImage(image
);
64 img
= RCreateImage(new_width
, new_height
, image
->format
== RRGBAFormat
);
69 /* fixed point math idea taken from Imlib by
70 * Carsten Haitzler (Rasterman) */
71 dx
= (image
->width
<< 16) / new_width
;
72 dy
= (image
->height
<< 16) / new_height
;
78 if (image
->format
== RRGBAFormat
) {
79 for (y
= 0; y
< new_height
; y
++) {
80 t
= image
->width
* (py
>> 16);
82 s
= image
->data
+ (t
<< 2); /* image->data+t*4 */
86 for (x
= 0; x
< new_width
; x
++) {
97 s
+= t
<< 2; /* t*4 */
102 for (y
= 0; y
< new_height
; y
++) {
103 t
= image
->width
* (py
>> 16);
105 s
= image
->data
+ (t
<< 1) + t
; /* image->data+t*3 */
109 for (x
= 0; x
< new_width
; x
++) {
119 s
+= (t
<< 1) + t
; /* t*3 */
129 RImage
*RScaleImage(RImage
* src
, unsigned new_width
, unsigned new_height
)
137 unsigned char *sr
, *sg
, *sb
, *sa
;
138 unsigned char *dr
, *dg
, *db
, *da
;
141 dst
= RCreateImage(new_width
, new_height
, src
->data
[3] != NULL
);
143 ddy
= src
->height
/ 2;
144 ee
= (ddy
/ 2) - dst
->height
;
149 e
= (src
->width
/ 2) - xd
;
163 for (yd
= 0; yd
< new_height
; yd
++) {
166 sr
= src
->data
[0] + ys
* src
->width
;
167 sg
= src
->data
[1] + ys
* src
->width
;
168 sb
= src
->data
[2] + ys
* src
->width
;
170 for (x
= 0; x
< xd
; x
++) {
190 for (yd
= 0; yd
< new_height
; yd
++) {
193 sr
= src
->data
[0] + ys
* src
->width
;
194 sg
= src
->data
[1] + ys
* src
->width
;
195 sb
= src
->data
[2] + ys
* src
->width
;
196 sa
= src
->data
[3] + ys
* src
->width
;
198 for (x
= 0; x
< xd
; x
++) {
226 * Filtered Image Rescaling code copy/pasted from
228 * Public Domain 1991 by Dale Schumacher
232 * filter function definitions
235 #define filter_support (1.0)
237 static double filter(t
)
240 /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */
244 return ((2.0 * t
- 3.0) * t
* t
+ 1.0);
248 #define box_support (0.5)
250 static double box_filter(t
)
253 if ((t
> -0.5) && (t
<= 0.5))
258 #define triangle_support (1.0)
260 static double triangle_filter(t
)
270 #define bell_support (1.5)
272 static double bell_filter(t
) /* box (*) box (*) box */
278 return (.75 - (t
* t
));
281 return (.5 * (t
* t
));
286 #define B_spline_support (2.0)
288 static double B_spline_filter(t
) /* box (*) box (*) box (*) box */
297 return ((.5 * tt
* t
) - tt
+ (2.0 / 3.0));
300 return ((1.0 / 6.0) * (t
* t
* t
));
305 static double sinc(x
)
314 #define Lanczos3_support (3.0)
316 static double Lanczos3_filter(t
)
322 return (sinc(t
) * sinc(t
/ 3.0));
326 #define Mitchell_support (2.0)
328 #define B (1.0 / 3.0)
329 #define C (1.0 / 3.0)
331 static double Mitchell_filter(t
)
340 t
= (((12.0 - 9.0 * B
- 6.0 * C
) * (t
* tt
))
341 + ((-18.0 + 12.0 * B
+ 6.0 * C
) * tt
)
344 } else if (t
< 2.0) {
345 t
= (((-1.0 * B
- 6.0 * C
) * (t
* tt
))
346 + ((6.0 * B
+ 30.0 * C
) * tt
)
347 + ((-12.0 * B
- 48.0 * C
) * t
)
348 + (8.0 * B
+ 24 * C
));
354 static double (*filterf
) () = Mitchell_filter
;
355 static double fwidth
= Mitchell_support
;
357 void _wraster_change_filter(int type
)
361 filterf
= box_filter
;
362 fwidth
= box_support
;
364 case RTriangleFilter
:
365 filterf
= triangle_filter
;
366 fwidth
= triangle_support
;
369 filterf
= bell_filter
;
370 fwidth
= bell_support
;
373 filterf
= B_spline_filter
;
374 fwidth
= B_spline_support
;
376 case RLanczos3Filter
:
377 filterf
= Lanczos3_filter
;
378 fwidth
= Lanczos3_support
;
381 case RMitchellFilter
:
382 filterf
= Mitchell_filter
;
383 fwidth
= Mitchell_support
;
389 * image rescaling routine
398 int n
; /* number of contributors */
399 CONTRIB
*p
; /* pointer to list of contributions */
402 CLIST
*contrib
; /* array of contribution lists */
404 /* clamp the input to the specified range */
405 #define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : v)
407 /* return of calloc is not checked if NULL in the function below! */
408 RImage
*RSmoothScaleImage(RImage
* src
, unsigned new_width
, unsigned new_height
)
410 RImage
*tmp
; /* intermediate image */
411 double xscale
, yscale
; /* zoom scale factors */
412 int i
, j
, k
; /* loop variables */
413 int n
; /* pixel number */
414 double center
, left
, right
; /* filter calculation variables */
415 double width
, fscale
; /* filter calculation variables */
416 double rweight
, gweight
, bweight
;
420 int sch
= src
->format
== RRGBAFormat
? 4 : 3;
422 dst
= RCreateImage(new_width
, new_height
, False
);
424 /* create intermediate image to hold horizontal zoom */
425 tmp
= RCreateImage(dst
->width
, src
->height
, False
);
426 xscale
= (double)new_width
/ (double)src
->width
;
427 yscale
= (double)new_height
/ (double)src
->height
;
429 /* pre-calculate filter contributions for a row */
430 contrib
= (CLIST
*) calloc(new_width
, sizeof(CLIST
));
432 width
= fwidth
/ xscale
;
433 fscale
= 1.0 / xscale
;
434 for (i
= 0; i
< new_width
; ++i
) {
436 contrib
[i
].p
= (CONTRIB
*) calloc((int)(width
* 2 + 1), sizeof(CONTRIB
));
437 center
= (double)i
/ xscale
;
438 left
= ceil(center
- width
);
439 right
= floor(center
+ width
);
440 for (j
= left
; j
<= right
; ++j
) {
441 rweight
= center
- (double)j
;
442 rweight
= (*filterf
) (rweight
/ fscale
) / fscale
;
445 } else if (j
>= src
->width
) {
446 n
= (src
->width
- j
) + src
->width
- 1;
451 contrib
[i
].p
[k
].pixel
= n
* sch
;
452 contrib
[i
].p
[k
].weight
= rweight
;
457 for (i
= 0; i
< new_width
; ++i
) {
459 contrib
[i
].p
= (CONTRIB
*) calloc((int)(fwidth
* 2 + 1), sizeof(CONTRIB
));
460 center
= (double)i
/ xscale
;
461 left
= ceil(center
- fwidth
);
462 right
= floor(center
+ fwidth
);
463 for (j
= left
; j
<= right
; ++j
) {
464 rweight
= center
- (double)j
;
465 rweight
= (*filterf
) (rweight
);
468 } else if (j
>= src
->width
) {
469 n
= (src
->width
- j
) + src
->width
- 1;
474 contrib
[i
].p
[k
].pixel
= n
* sch
;
475 contrib
[i
].p
[k
].weight
= rweight
;
480 /* apply filter to zoom horizontally from src to tmp */
483 for (k
= 0; k
< tmp
->height
; ++k
) {
486 sp
= src
->data
+ src
->width
* k
* sch
;
488 for (i
= 0; i
< tmp
->width
; ++i
) {
489 rweight
= gweight
= bweight
= 0.0;
493 for (j
= 0; j
< contrib
[i
].n
; ++j
) {
494 rweight
+= sp
[pp
[j
].pixel
] * pp
[j
].weight
;
495 gweight
+= sp
[pp
[j
].pixel
+ 1] * pp
[j
].weight
;
496 bweight
+= sp
[pp
[j
].pixel
+ 2] * pp
[j
].weight
;
498 *p
++ = CLAMP(rweight
, 0, 255);
499 *p
++ = CLAMP(gweight
, 0, 255);
500 *p
++ = CLAMP(bweight
, 0, 255);
504 /* free the memory allocated for horizontal filter weights */
505 for (i
= 0; i
< tmp
->width
; ++i
) {
510 /* pre-calculate filter contributions for a column */
511 contrib
= (CLIST
*) calloc(dst
->height
, sizeof(CLIST
));
513 width
= fwidth
/ yscale
;
514 fscale
= 1.0 / yscale
;
515 for (i
= 0; i
< dst
->height
; ++i
) {
517 contrib
[i
].p
= (CONTRIB
*) calloc((int)(width
* 2 + 1), sizeof(CONTRIB
));
518 center
= (double)i
/ yscale
;
519 left
= ceil(center
- width
);
520 right
= floor(center
+ width
);
521 for (j
= left
; j
<= right
; ++j
) {
522 rweight
= center
- (double)j
;
523 rweight
= (*filterf
) (rweight
/ fscale
) / fscale
;
526 } else if (j
>= tmp
->height
) {
527 n
= (tmp
->height
- j
) + tmp
->height
- 1;
532 contrib
[i
].p
[k
].pixel
= n
* 3;
533 contrib
[i
].p
[k
].weight
= rweight
;
537 for (i
= 0; i
< dst
->height
; ++i
) {
539 contrib
[i
].p
= (CONTRIB
*) calloc((int)(fwidth
* 2 + 1), sizeof(CONTRIB
));
540 center
= (double)i
/ yscale
;
541 left
= ceil(center
- fwidth
);
542 right
= floor(center
+ fwidth
);
543 for (j
= left
; j
<= right
; ++j
) {
544 rweight
= center
- (double)j
;
545 rweight
= (*filterf
) (rweight
);
548 } else if (j
>= tmp
->height
) {
549 n
= (tmp
->height
- j
) + tmp
->height
- 1;
554 contrib
[i
].p
[k
].pixel
= n
* 3;
555 contrib
[i
].p
[k
].weight
= rweight
;
560 /* apply filter to zoom vertically from tmp to dst */
561 sp
= malloc(tmp
->height
* 3);
563 for (k
= 0; k
< new_width
; ++k
) {
566 p
= dst
->data
+ k
* 3;
568 /* copy a column into a row */
571 unsigned char *p
, *d
;
574 for (i
= tmp
->height
, p
= tmp
->data
+ k
* 3; i
-- > 0; p
+= tmp
->width
* 3) {
580 for (i
= 0; i
< new_height
; ++i
) {
581 rweight
= gweight
= bweight
= 0.0;
585 for (j
= 0; j
< contrib
[i
].n
; ++j
) {
586 rweight
+= sp
[pp
[j
].pixel
] * pp
[j
].weight
;
587 gweight
+= sp
[pp
[j
].pixel
+ 1] * pp
[j
].weight
;
588 bweight
+= sp
[pp
[j
].pixel
+ 2] * pp
[j
].weight
;
590 *p
= CLAMP(rweight
, 0, 255);
591 *(p
+ 1) = CLAMP(gweight
, 0, 255);
592 *(p
+ 2) = CLAMP(bweight
, 0, 255);
598 /* free the memory allocated for vertical filter weights */
599 for (i
= 0; i
< dst
->height
; ++i
) {