1 /*
2  * Copyright 2012, Red Hat, Inc.
3  * Copyright 2012, Soren Sandmann
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Author: Soren Sandmann <soren.sandmann@gmail.com>
25  */
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <math.h>
30 #include <assert.h>
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34 #include "pixman-private.h"
35 
36 typedef double (* kernel_func_t) (double x);
37 
38 typedef struct
39 {
40     pixman_kernel_t	kernel;
41     kernel_func_t	func;
42     double		width;
43 } filter_info_t;
44 
45 static double
impulse_kernel(double x)46 impulse_kernel (double x)
47 {
48     return (x == 0.0)? 1.0 : 0.0;
49 }
50 
51 static double
box_kernel(double x)52 box_kernel (double x)
53 {
54     return 1;
55 }
56 
57 static double
linear_kernel(double x)58 linear_kernel (double x)
59 {
60     return 1 - fabs (x);
61 }
62 
63 static double
gaussian_kernel(double x)64 gaussian_kernel (double x)
65 {
66 #define SQRT2 (1.4142135623730950488016887242096980785696718753769480)
67 #define SIGMA (SQRT2 / 2.0)
68 
69     return exp (- x * x / (2 * SIGMA * SIGMA)) / (SIGMA * sqrt (2.0 * M_PI));
70 }
71 
72 static double
sinc(double x)73 sinc (double x)
74 {
75     if (x == 0.0)
76 	return 1.0;
77     else
78 	return sin (M_PI * x) / (M_PI * x);
79 }
80 
81 static double
lanczos(double x,int n)82 lanczos (double x, int n)
83 {
84     return sinc (x) * sinc (x * (1.0 / n));
85 }
86 
87 static double
lanczos2_kernel(double x)88 lanczos2_kernel (double x)
89 {
90     return lanczos (x, 2);
91 }
92 
93 static double
lanczos3_kernel(double x)94 lanczos3_kernel (double x)
95 {
96     return lanczos (x, 3);
97 }
98 
99 static double
nice_kernel(double x)100 nice_kernel (double x)
101 {
102     return lanczos3_kernel (x * 0.75);
103 }
104 
105 static double
general_cubic(double x,double B,double C)106 general_cubic (double x, double B, double C)
107 {
108     double ax = fabs(x);
109 
110     if (ax < 1)
111     {
112 	return (((12 - 9 * B - 6 * C) * ax +
113 		 (-18 + 12 * B + 6 * C)) * ax * ax +
114 		(6 - 2 * B)) / 6;
115     }
116     else if (ax < 2)
117     {
118 	return ((((-B - 6 * C) * ax +
119 		  (6 * B + 30 * C)) * ax +
120 		 (-12 * B - 48 * C)) * ax +
121 		(8 * B + 24 * C)) / 6;
122     }
123     else
124     {
125 	return 0;
126     }
127 }
128 
129 static double
cubic_kernel(double x)130 cubic_kernel (double x)
131 {
132     /* This is the Mitchell-Netravali filter.
133      *
134      * (0.0, 0.5) would give us the Catmull-Rom spline,
135      * but that one seems to be indistinguishable from Lanczos2.
136      */
137     return general_cubic (x, 1/3.0, 1/3.0);
138 }
139 
140 static const filter_info_t filters[] =
141 {
142     { PIXMAN_KERNEL_IMPULSE,	        impulse_kernel,   0.0 },
143     { PIXMAN_KERNEL_BOX,	        box_kernel,       1.0 },
144     { PIXMAN_KERNEL_LINEAR,	        linear_kernel,    2.0 },
145     { PIXMAN_KERNEL_CUBIC,		cubic_kernel,     4.0 },
146     { PIXMAN_KERNEL_GAUSSIAN,	        gaussian_kernel,  5.0 },
147     { PIXMAN_KERNEL_LANCZOS2,	        lanczos2_kernel,  4.0 },
148     { PIXMAN_KERNEL_LANCZOS3,	        lanczos3_kernel,  6.0 },
149     { PIXMAN_KERNEL_LANCZOS3_STRETCHED, nice_kernel,      8.0 },
150 };
151 
152 /* This function scales @kernel2 by @scale, then
153  * aligns @x1 in @kernel1 with @x2 in @kernel2 and
154  * and integrates the product of the kernels across @width.
155  *
156  * This function assumes that the intervals are within
157  * the kernels in question. E.g., the caller must not
158  * try to integrate a linear kernel ouside of [-1:1]
159  */
160 static double
integral(pixman_kernel_t kernel1,double x1,pixman_kernel_t kernel2,double scale,double x2,double width)161 integral (pixman_kernel_t kernel1, double x1,
162 	  pixman_kernel_t kernel2, double scale, double x2,
163 	  double width)
164 {
165     if (kernel1 == PIXMAN_KERNEL_BOX && kernel2 == PIXMAN_KERNEL_BOX)
166     {
167 	return width;
168     }
169     /* The LINEAR filter is not differentiable at 0, so if the
170      * integration interval crosses zero, break it into two
171      * separate integrals.
172      */
173     else if (kernel1 == PIXMAN_KERNEL_LINEAR && x1 < 0 && x1 + width > 0)
174     {
175 	return
176 	    integral (kernel1, x1, kernel2, scale, x2, - x1) +
177 	    integral (kernel1, 0, kernel2, scale, x2 - x1, width + x1);
178     }
179     else if (kernel2 == PIXMAN_KERNEL_LINEAR && x2 < 0 && x2 + width > 0)
180     {
181 	return
182 	    integral (kernel1, x1, kernel2, scale, x2, - x2) +
183 	    integral (kernel1, x1 - x2, kernel2, scale, 0, width + x2);
184     }
185     else if (kernel1 == PIXMAN_KERNEL_IMPULSE)
186     {
187 	assert (width == 0.0);
188 	return filters[kernel2].func (x2 * scale);
189     }
190     else if (kernel2 == PIXMAN_KERNEL_IMPULSE)
191     {
192 	assert (width == 0.0);
193 	return filters[kernel1].func (x1);
194     }
195     else
196     {
197 	/* Integration via Simpson's rule
198 	 * See http://www.intmath.com/integration/6-simpsons-rule.php
199 	 * 12 segments (6 cubic approximations) seems to produce best
200 	 * result for lanczos3.linear, which was the combination that
201 	 * showed the most errors.  This makes sense as the lanczos3
202 	 * filter is 6 wide.
203 	 */
204 #define N_SEGMENTS 12
205 #define SAMPLE(a1, a2)							\
206 	(filters[kernel1].func ((a1)) * filters[kernel2].func ((a2) * scale))
207 
208 	double s = 0.0;
209 	double h = width / N_SEGMENTS;
210 	int i;
211 
212 	s = SAMPLE (x1, x2);
213 
214 	for (i = 1; i < N_SEGMENTS; i += 2)
215 	{
216 	    double a1 = x1 + h * i;
217 	    double a2 = x2 + h * i;
218 	    s += 4 * SAMPLE (a1, a2);
219 	}
220 
221 	for (i = 2; i < N_SEGMENTS; i += 2)
222 	{
223 	    double a1 = x1 + h * i;
224 	    double a2 = x2 + h * i;
225 	    s += 2 * SAMPLE (a1, a2);
226 	}
227 
228 	s += SAMPLE (x1 + width, x2 + width);
229 
230 	return h * s * (1.0 / 3.0);
231     }
232 }
233 
234 static void
create_1d_filter(int width,pixman_kernel_t reconstruct,pixman_kernel_t sample,double scale,int n_phases,pixman_fixed_t * p)235 create_1d_filter (int              width,
236 		  pixman_kernel_t  reconstruct,
237 		  pixman_kernel_t  sample,
238 		  double           scale,
239 		  int              n_phases,
240 		  pixman_fixed_t *p)
241 {
242     double step;
243     int i;
244 
245     step = 1.0 / n_phases;
246 
247     for (i = 0; i < n_phases; ++i)
248     {
249         double frac = step / 2.0 + i * step;
250 	pixman_fixed_t new_total;
251         int x, x1, x2;
252 	double total, e;
253 
254 	/* Sample convolution of reconstruction and sampling
255 	 * filter. See rounding.txt regarding the rounding
256 	 * and sample positions.
257 	 */
258 
259 	x1 = ceil (frac - width / 2.0 - 0.5);
260 	x2 = x1 + width;
261 
262 	total = 0;
263         for (x = x1; x < x2; ++x)
264         {
265 	    double pos = x + 0.5 - frac;
266 	    double rlow = - filters[reconstruct].width / 2.0;
267 	    double rhigh = rlow + filters[reconstruct].width;
268 	    double slow = pos - scale * filters[sample].width / 2.0;
269 	    double shigh = slow + scale * filters[sample].width;
270 	    double c = 0.0;
271 	    double ilow, ihigh;
272 
273 	    if (rhigh >= slow && rlow <= shigh)
274 	    {
275 		ilow = MAX (slow, rlow);
276 		ihigh = MIN (shigh, rhigh);
277 
278 		c = integral (reconstruct, ilow,
279 			      sample, 1.0 / scale, ilow - pos,
280 			      ihigh - ilow);
281 	    }
282 
283             *p = (pixman_fixed_t)floor (c * 65536.0 + 0.5);
284 	    total += *p;
285 	    p++;
286         }
287 
288 	/* Normalize, with error diffusion */
289 	p -= width;
290         total = 65536.0 / total;
291         new_total = 0;
292 	e = 0.0;
293 	for (x = x1; x < x2; ++x)
294 	{
295 	    double v = (*p) * total + e;
296 	    pixman_fixed_t t = floor (v + 0.5);
297 
298 	    e = v - t;
299 	    new_total += t;
300 	    *p++ = t;
301 	}
302 
303 	/* pixman_fixed_e's worth of error may remain; put it
304 	 * at the first sample, since that is the only one that
305 	 * hasn't had any error diffused into it.
306 	 */
307 	*(p - width) += pixman_fixed_1 - new_total;
308     }
309 }
310 
311 
312 static int
filter_width(pixman_kernel_t reconstruct,pixman_kernel_t sample,double size)313 filter_width (pixman_kernel_t reconstruct, pixman_kernel_t sample, double size)
314 {
315     return ceil (filters[reconstruct].width + size * filters[sample].width);
316 }
317 
318 #ifdef PIXMAN_GNUPLOT
319 
320 /* If enable-gnuplot is configured, then you can pipe the output of a
321  * pixman-using program to gnuplot and get a continuously-updated plot
322  * of the horizontal filter. This works well with demos/scale to test
323  * the filter generation.
324  *
325  * The plot is all the different subposition filters shuffled
326  * together. This is misleading in a few cases:
327  *
328  *  IMPULSE.BOX - goes up and down as the subfilters have different
329  *		  numbers of non-zero samples
330  *  IMPULSE.TRIANGLE - somewhat crooked for the same reason
331  *  1-wide filters - looks triangular, but a 1-wide box would be more
332  *		     accurate
333  */
334 static void
gnuplot_filter(int width,int n_phases,const pixman_fixed_t * p)335 gnuplot_filter (int width, int n_phases, const pixman_fixed_t* p)
336 {
337     double step;
338     int i, j;
339     int first;
340 
341     step = 1.0 / n_phases;
342 
343     printf ("set style line 1 lc rgb '#0060ad' lt 1 lw 0.5 pt 7 pi 1 ps 0.5\n");
344     printf ("plot [x=%g:%g] '-' with linespoints ls 1\n", -width*0.5, width*0.5);
345     /* Print a point at the origin so that y==0 line is included: */
346     printf ("0 0\n\n");
347 
348     /* The position of the first sample of the phase corresponding to
349      * frac is given by:
350      *
351      *     ceil (frac - width / 2.0 - 0.5) + 0.5 - frac
352      *
353      * We have to find the frac that minimizes this expression.
354      *
355      * For odd widths, we have
356      *
357      *     ceil (frac - width / 2.0 - 0.5) + 0.5 - frac
358      *   = ceil (frac) + K - frac
359      *   = 1 + K - frac
360      *
361      * for some K, so this is minimized when frac is maximized and
362      * strictly growing with frac. So for odd widths, we can simply
363      * start at the last phase and go backwards.
364      *
365      * For even widths, we have
366      *
367      *     ceil (frac - width / 2.0 - 0.5) + 0.5 - frac
368      *   = ceil (frac - 0.5) + K - frac
369      *
370      * The graph for this function (ignoring K) looks like this:
371      *
372      *        0.5
373      *           |    |\
374      *           |    | \
375      *           |    |  \
376      *         0 |    |   \
377      *           |\   |
378      *           | \  |
379      *           |  \ |
380      *      -0.5 |   \|
381      *   ---------------------------------
382      *           0    0.5   1
383      *
384      * So in this case we need to start with the phase whose frac is
385      * less than, but as close as possible to 0.5, then go backwards
386      * until we hit the first phase, then wrap around to the last
387      * phase and continue backwards.
388      *
389      * Which phase is as close as possible 0.5? The locations of the
390      * sampling point corresponding to the kth phase is given by
391      * 1/(2 * n_phases) + k / n_phases:
392      *
393      *         1/(2 * n_phases) + k / n_phases = 0.5
394      *
395      * from which it follows that
396      *
397      *         k = (n_phases - 1) / 2
398      *
399      * rounded down is the phase in question.
400      */
401     if (width & 1)
402 	first = n_phases - 1;
403     else
404 	first = (n_phases - 1) / 2;
405 
406     for (j = 0; j < width; ++j)
407     {
408 	for (i = 0; i < n_phases; ++i)
409 	{
410 	    int phase = first - i;
411 	    double frac, pos;
412 
413 	    if (phase < 0)
414 		phase = n_phases + phase;
415 
416 	    frac = step / 2.0 + phase * step;
417 	    pos = ceil (frac - width / 2.0 - 0.5) + 0.5 - frac + j;
418 
419 	    printf ("%g %g\n",
420 		    pos,
421 		    pixman_fixed_to_double (*(p + phase * width + j)));
422 	}
423     }
424 
425     printf ("e\n");
426     fflush (stdout);
427 }
428 
429 #endif
430 
431 /* Create the parameter list for a SEPARABLE_CONVOLUTION filter
432  * with the given kernels and scale parameters
433  */
434 PIXMAN_EXPORT pixman_fixed_t *
pixman_filter_create_separable_convolution(int * n_values,pixman_fixed_t scale_x,pixman_fixed_t scale_y,pixman_kernel_t reconstruct_x,pixman_kernel_t reconstruct_y,pixman_kernel_t sample_x,pixman_kernel_t sample_y,int subsample_bits_x,int subsample_bits_y)435 pixman_filter_create_separable_convolution (int             *n_values,
436 					    pixman_fixed_t   scale_x,
437 					    pixman_fixed_t   scale_y,
438 					    pixman_kernel_t  reconstruct_x,
439 					    pixman_kernel_t  reconstruct_y,
440 					    pixman_kernel_t  sample_x,
441 					    pixman_kernel_t  sample_y,
442 					    int              subsample_bits_x,
443 					    int	             subsample_bits_y)
444 {
445     double sx = fabs (pixman_fixed_to_double (scale_x));
446     double sy = fabs (pixman_fixed_to_double (scale_y));
447     pixman_fixed_t *params;
448     int subsample_x, subsample_y;
449     int width, height;
450 
451     width = filter_width (reconstruct_x, sample_x, sx);
452     subsample_x = (1 << subsample_bits_x);
453 
454     height = filter_width (reconstruct_y, sample_y, sy);
455     subsample_y = (1 << subsample_bits_y);
456 
457     *n_values = 4 + width * subsample_x + height * subsample_y;
458 
459     params = malloc (*n_values * sizeof (pixman_fixed_t));
460     if (!params)
461 	return NULL;
462 
463     params[0] = pixman_int_to_fixed (width);
464     params[1] = pixman_int_to_fixed (height);
465     params[2] = pixman_int_to_fixed (subsample_bits_x);
466     params[3] = pixman_int_to_fixed (subsample_bits_y);
467 
468     create_1d_filter (width, reconstruct_x, sample_x, sx, subsample_x,
469 		      params + 4);
470     create_1d_filter (height, reconstruct_y, sample_y, sy, subsample_y,
471 		      params + 4 + width * subsample_x);
472 
473 #ifdef PIXMAN_GNUPLOT
474     gnuplot_filter(width, subsample_x, params + 4);
475 #endif
476 
477     return params;
478 }
479