Partially support _NET_WM_STRUT_PARTIAL.
[wmaker-crm.git] / wrlib / scale.c
blob82ed57e89ae782fd83183b518b57b8d1675255af
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.
22 #include <config.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <X11/Xlib.h>
28 #include <math.h>
30 #ifndef PI
31 #define PI 3.14159265358979323846
32 #endif
34 #include <assert.h>
36 #include "wraster.h"
39 *----------------------------------------------------------------------
40 * RScaleImage--
41 * Creates a scaled copy of an image.
43 * Returns:
44 * The new scaled image.
46 *----------------------------------------------------------------------
48 RImage *RScaleImage(RImage * image, unsigned new_width, unsigned new_height)
50 int ox;
51 int px, py;
52 register int x, y, t;
53 int dx, dy;
54 unsigned char *s;
55 unsigned char *d;
56 RImage *img;
58 if (new_width == image->width && new_height == image->height)
59 return RCloneImage(image);
61 img = RCreateImage(new_width, new_height, image->format == RRGBAFormat);
63 if (!img)
64 return NULL;
66 /* fixed point math idea taken from Imlib by
67 * Carsten Haitzler (Rasterman) */
68 dx = (image->width << 16) / new_width;
69 dy = (image->height << 16) / new_height;
71 py = 0;
73 d = img->data;
75 if (image->format == RRGBAFormat) {
76 for (y = 0; y < new_height; y++) {
77 t = image->width * (py >> 16);
79 s = image->data + (t << 2); /* image->data+t*4 */
81 ox = 0;
82 px = 0;
83 for (x = 0; x < new_width; x++) {
84 px += dx;
86 *(d++) = *(s);
87 *(d++) = *(s + 1);
88 *(d++) = *(s + 2);
89 *(d++) = *(s + 3);
91 t = (px - ox) >> 16;
92 ox += t << 16;
94 s += t << 2; /* t*4 */
96 py += dy;
98 } else {
99 for (y = 0; y < new_height; y++) {
100 t = image->width * (py >> 16);
102 s = image->data + (t << 1) + t; /* image->data+t*3 */
104 ox = 0;
105 px = 0;
106 for (x = 0; x < new_width; x++) {
107 px += dx;
109 *(d++) = *(s);
110 *(d++) = *(s + 1);
111 *(d++) = *(s + 2);
113 t = (px - ox) >> 16;
114 ox += t << 16;
116 s += (t << 1) + t; /* t*3 */
118 py += dy;
122 return img;
126 * Filtered Image Rescaling code copy/pasted from
127 * Graphics Gems III
128 * Public Domain 1991 by Dale Schumacher
132 * filter function definitions
134 #define box_support (0.5)
136 static double box_filter(double t)
138 if ((t > -0.5) && (t <= 0.5))
139 return (1.0);
140 return (0.0);
143 #define triangle_support (1.0)
145 static double triangle_filter(double t)
147 if (t < 0.0)
148 t = -t;
149 if (t < 1.0)
150 return (1.0 - t);
151 return (0.0);
154 #define bell_support (1.5)
156 static double bell_filter(double t) /* box (*) box (*) box */
158 if (t < 0)
159 t = -t;
160 if (t < .5)
161 return (.75 - (t * t));
162 if (t < 1.5) {
163 t = (t - 1.5);
164 return (.5 * (t * t));
166 return (0.0);
169 #define B_spline_support (2.0)
171 static double B_spline_filter(double t) /* box (*) box (*) box (*) box */
173 double tt;
175 if (t < 0)
176 t = -t;
177 if (t < 1) {
178 tt = t * t;
179 return ((.5 * tt * t) - tt + (2.0 / 3.0));
180 } else if (t < 2) {
181 t = 2 - t;
182 return ((1.0 / 6.0) * (t * t * t));
184 return (0.0);
187 static double sinc(double x)
189 x *= PI;
190 if (x != 0)
191 return (sin(x) / x);
192 return (1.0);
195 #define Lanczos3_support (3.0)
197 static double Lanczos3_filter(double t)
199 if (t < 0)
200 t = -t;
201 if (t < 3.0)
202 return (sinc(t) * sinc(t / 3.0));
203 return (0.0);
206 #define Mitchell_support (2.0)
208 #define B (1.0 / 3.0)
209 #define C (1.0 / 3.0)
211 static double Mitchell_filter(double t)
213 double tt;
215 tt = t * t;
216 if (t < 0)
217 t = -t;
218 if (t < 1.0) {
219 t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt))
220 + ((-18.0 + 12.0 * B + 6.0 * C) * tt)
221 + (6.0 - 2 * B));
222 return (t / 6.0);
223 } else if (t < 2.0) {
224 t = (((-1.0 * B - 6.0 * C) * (t * tt))
225 + ((6.0 * B + 30.0 * C) * tt)
226 + ((-12.0 * B - 48.0 * C) * t)
227 + (8.0 * B + 24 * C));
228 return (t / 6.0);
230 return (0.0);
233 static double (*filterf)(double) = Mitchell_filter;
234 static double fwidth = Mitchell_support;
236 void _wraster_change_filter(int type)
238 switch (type) {
239 case RBoxFilter:
240 filterf = box_filter;
241 fwidth = box_support;
242 break;
243 case RTriangleFilter:
244 filterf = triangle_filter;
245 fwidth = triangle_support;
246 break;
247 case RBellFilter:
248 filterf = bell_filter;
249 fwidth = bell_support;
250 break;
251 case RBSplineFilter:
252 filterf = B_spline_filter;
253 fwidth = B_spline_support;
254 break;
255 case RLanczos3Filter:
256 filterf = Lanczos3_filter;
257 fwidth = Lanczos3_support;
258 break;
259 default:
260 case RMitchellFilter:
261 filterf = Mitchell_filter;
262 fwidth = Mitchell_support;
263 break;
268 * image rescaling routine
271 typedef struct {
272 int pixel;
273 double weight;
274 } CONTRIB;
276 typedef struct {
277 int n; /* number of contributors */
278 CONTRIB *p; /* pointer to list of contributions */
279 } CLIST;
281 CLIST *contrib; /* array of contribution lists */
283 /* clamp the input to the specified range */
284 #define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : v)
286 /* return of calloc is not checked if NULL in the function below! */
287 RImage *RSmoothScaleImage(RImage * src, unsigned new_width, unsigned new_height)
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 < tmp->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;