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