Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3931 Serge 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 
25
 */
26
#include 
27
#include 
28
#include 
29
#include 
30
#include 
31
#ifdef HAVE_CONFIG_H
32
#include 
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
46
impulse_kernel (double x)
47
{
48
    return (x == 0.0)? 1.0 : 0.0;
49
}
50
 
51
static double
52
box_kernel (double x)
53
{
54
    return 1;
55
}
56
 
57
static double
58
linear_kernel (double x)
59
{
60
    return 1 - fabs (x);
61
}
62
 
63
static double
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
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
82
lanczos (double x, int n)
83
{
84
    return sinc (x) * sinc (x * (1.0 / n));
85
}
86
 
87
static double
88
lanczos2_kernel (double x)
89
{
90
    return lanczos (x, 2);
91
}
92
 
93
static double
94
lanczos3_kernel (double x)
95
{
96
    return lanczos (x, 3);
97
}
98
 
99
static double
100
nice_kernel (double x)
101
{
102
    return lanczos3_kernel (x * 0.75);
103
}
104
 
105
static double
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 * ax * ax +
113
		(-18 + 12 * B + 6 * C) * ax * ax + (6 - 2 * B)) / 6;
114
    }
115
    else if (ax >= 1 && ax < 2)
116
    {
117
	return ((-B - 6 * C) * ax * ax * ax +
118
		(6 * B + 30 * C) * ax * ax + (-12 * B - 48 * C) *
119
		ax + (8 * B + 24 * C)) / 6;
120
    }
121
    else
122
    {
123
	return 0;
124
    }
125
}
126
 
127
static double
128
cubic_kernel (double x)
129
{
130
    /* This is the Mitchell-Netravali filter.
131
     *
132
     * (0.0, 0.5) would give us the Catmull-Rom spline,
133
     * but that one seems to be indistinguishable from Lanczos2.
134
     */
135
    return general_cubic (x, 1/3.0, 1/3.0);
136
}
137
 
138
static const filter_info_t filters[] =
139
{
140
    { PIXMAN_KERNEL_IMPULSE,	        impulse_kernel,   0.0 },
141
    { PIXMAN_KERNEL_BOX,	        box_kernel,       1.0 },
142
    { PIXMAN_KERNEL_LINEAR,	        linear_kernel,    2.0 },
143
    { PIXMAN_KERNEL_CUBIC,		cubic_kernel,     4.0 },
144
    { PIXMAN_KERNEL_GAUSSIAN,	        gaussian_kernel,  6 * SIGMA },
145
    { PIXMAN_KERNEL_LANCZOS2,	        lanczos2_kernel,  4.0 },
146
    { PIXMAN_KERNEL_LANCZOS3,	        lanczos3_kernel,  6.0 },
147
    { PIXMAN_KERNEL_LANCZOS3_STRETCHED, nice_kernel,      8.0 },
148
};
149
 
150
/* This function scales @kernel2 by @scale, then
151
 * aligns @x1 in @kernel1 with @x2 in @kernel2 and
152
 * and integrates the product of the kernels across @width.
153
 *
154
 * This function assumes that the intervals are within
155
 * the kernels in question. E.g., the caller must not
156
 * try to integrate a linear kernel ouside of [-1:1]
157
 */
158
static double
159
integral (pixman_kernel_t kernel1, double x1,
160
	  pixman_kernel_t kernel2, double scale, double x2,
161
	  double width)
162
{
163
    /* If the integration interval crosses zero, break it into
164
     * two separate integrals. This ensures that filters such
165
     * as LINEAR that are not differentiable at 0 will still
166
     * integrate properly.
167
     */
168
    if (x1 < 0 && x1 + width > 0)
169
    {
170
	return
171
	    integral (kernel1, x1, kernel2, scale, x2, - x1) +
172
	    integral (kernel1, 0, kernel2, scale, x2 - x1, width + x1);
173
    }
174
    else if (x2 < 0 && x2 + width > 0)
175
    {
176
	return
177
	    integral (kernel1, x1, kernel2, scale, x2, - x2) +
178
	    integral (kernel1, x1 - x2, kernel2, scale, 0, width + x2);
179
    }
180
    else if (kernel1 == PIXMAN_KERNEL_IMPULSE)
181
    {
182
	assert (width == 0.0);
183
	return filters[kernel2].func (x2 * scale);
184
    }
185
    else if (kernel2 == PIXMAN_KERNEL_IMPULSE)
186
    {
187
	assert (width == 0.0);
188
	return filters[kernel1].func (x1);
189
    }
190
    else
191
    {
192
	/* Integration via Simpson's rule */
193
#define N_SEGMENTS 128
194
#define SAMPLE(a1, a2)							\
195
	(filters[kernel1].func ((a1)) * filters[kernel2].func ((a2) * scale))
196
 
197
	double s = 0.0;
198
	double h = width / (double)N_SEGMENTS;
199
	int i;
200
 
201
	s = SAMPLE (x1, x2);
202
 
203
	for (i = 1; i < N_SEGMENTS; i += 2)
204
	{
205
	    double a1 = x1 + h * i;
206
	    double a2 = x2 + h * i;
207
 
208
	    s += 2 * SAMPLE (a1, a2);
209
 
210
	    if (i >= 2 && i < N_SEGMENTS - 1)
211
		s += 4 * SAMPLE (a1, a2);
212
	}
213
 
214
	s += SAMPLE (x1 + width, x2 + width);
215
 
216
	return h * s * (1.0 / 3.0);
217
    }
218
}
219
 
220
static pixman_fixed_t *
221
create_1d_filter (int             *width,
222
		  pixman_kernel_t  reconstruct,
223
		  pixman_kernel_t  sample,
224
		  double           scale,
225
		  int              n_phases)
226
{
227
    pixman_fixed_t *params, *p;
228
    double step;
229
    double size;
230
    int i;
231
 
232
    size = scale * filters[sample].width + filters[reconstruct].width;
233
    *width = ceil (size);
234
 
235
    p = params = malloc (*width * n_phases * sizeof (pixman_fixed_t));
236
    if (!params)
237
        return NULL;
238
 
239
    step = 1.0 / n_phases;
240
 
241
    for (i = 0; i < n_phases; ++i)
242
    {
243
        double frac = step / 2.0 + i * step;
244
	pixman_fixed_t new_total;
245
        int x, x1, x2;
246
	double total;
247
 
248
	/* Sample convolution of reconstruction and sampling
249
	 * filter. See rounding.txt regarding the rounding
250
	 * and sample positions.
251
	 */
252
 
253
	x1 = ceil (frac - *width / 2.0 - 0.5);
254
        x2 = x1 + *width;
255
 
256
	total = 0;
257
        for (x = x1; x < x2; ++x)
258
        {
259
	    double pos = x + 0.5 - frac;
260
	    double rlow = - filters[reconstruct].width / 2.0;
261
	    double rhigh = rlow + filters[reconstruct].width;
262
	    double slow = pos - scale * filters[sample].width / 2.0;
263
	    double shigh = slow + scale * filters[sample].width;
264
	    double c = 0.0;
265
	    double ilow, ihigh;
266
 
267
	    if (rhigh >= slow && rlow <= shigh)
268
	    {
269
		ilow = MAX (slow, rlow);
270
		ihigh = MIN (shigh, rhigh);
271
 
272
		c = integral (reconstruct, ilow,
273
			      sample, 1.0 / scale, ilow - pos,
274
			      ihigh - ilow);
275
	    }
276
 
277
	    total += c;
278
            *p++ = (pixman_fixed_t)(c * 65535.0 + 0.5);
279
        }
280
 
281
	/* Normalize */
282
	p -= *width;
283
        total = 1 / total;
284
        new_total = 0;
285
	for (x = x1; x < x2; ++x)
286
	{
287
	    pixman_fixed_t t = (*p) * total + 0.5;
288
 
289
	    new_total += t;
290
	    *p++ = t;
291
	}
292
 
293
	if (new_total != pixman_fixed_1)
294
	    *(p - *width / 2) += (pixman_fixed_1 - new_total);
295
    }
296
 
297
    return params;
298
}
299
 
300
/* Create the parameter list for a SEPARABLE_CONVOLUTION filter
301
 * with the given kernels and scale parameters
302
 */
303
PIXMAN_EXPORT pixman_fixed_t *
304
pixman_filter_create_separable_convolution (int             *n_values,
305
					    pixman_fixed_t   scale_x,
306
					    pixman_fixed_t   scale_y,
307
					    pixman_kernel_t  reconstruct_x,
308
					    pixman_kernel_t  reconstruct_y,
309
					    pixman_kernel_t  sample_x,
310
					    pixman_kernel_t  sample_y,
311
					    int              subsample_bits_x,
312
					    int	             subsample_bits_y)
313
{
314
    double sx = fabs (pixman_fixed_to_double (scale_x));
315
    double sy = fabs (pixman_fixed_to_double (scale_y));
316
    pixman_fixed_t *horz = NULL, *vert = NULL, *params = NULL;
317
    int subsample_x, subsample_y;
318
    int width, height;
319
 
320
    subsample_x = (1 << subsample_bits_x);
321
    subsample_y = (1 << subsample_bits_y);
322
 
323
    horz = create_1d_filter (&width, reconstruct_x, sample_x, sx, subsample_x);
324
    vert = create_1d_filter (&height, reconstruct_y, sample_y, sy, subsample_y);
325
 
326
    if (!horz || !vert)
327
        goto out;
328
 
329
    *n_values = 4 + width * subsample_x + height * subsample_y;
330
 
331
    params = malloc (*n_values * sizeof (pixman_fixed_t));
332
    if (!params)
333
        goto out;
334
 
335
    params[0] = pixman_int_to_fixed (width);
336
    params[1] = pixman_int_to_fixed (height);
337
    params[2] = pixman_int_to_fixed (subsample_bits_x);
338
    params[3] = pixman_int_to_fixed (subsample_bits_y);
339
 
340
    memcpy (params + 4, horz,
341
	    width * subsample_x * sizeof (pixman_fixed_t));
342
    memcpy (params + 4 + width * subsample_x, vert,
343
	    height * subsample_y * sizeof (pixman_fixed_t));
344
 
345
out:
346
    free (horz);
347
    free (vert);
348
 
349
    return params;
350
}