11176bdada62cabc6ec4b0308a930e83b679d5d36John Reck/*
21176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Copyright 2012, Red Hat, Inc.
31176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Copyright 2012, Soren Sandmann
41176bdada62cabc6ec4b0308a930e83b679d5d36John Reck *
51176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Permission is hereby granted, free of charge, to any person obtaining a
61176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * copy of this software and associated documentation files (the "Software"),
71176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * to deal in the Software without restriction, including without limitation
81176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * the rights to use, copy, modify, merge, publish, distribute, sublicense,
91176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * and/or sell copies of the Software, and to permit persons to whom the
101176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Software is furnished to do so, subject to the following conditions:
111176bdada62cabc6ec4b0308a930e83b679d5d36John Reck *
121176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * The above copyright notice and this permission notice (including the next
131176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * paragraph) shall be included in all copies or substantial portions of the
141176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Software.
151176bdada62cabc6ec4b0308a930e83b679d5d36John Reck *
161176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
181176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
191176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
201176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
211176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
221176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * DEALINGS IN THE SOFTWARE.
231176bdada62cabc6ec4b0308a930e83b679d5d36John Reck *
241176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Author: Soren Sandmann <soren.sandmann@gmail.com>
251176bdada62cabc6ec4b0308a930e83b679d5d36John Reck */
261176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include <string.h>
271176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include <stdlib.h>
281176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include <stdio.h>
291176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include <math.h>
301176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include <assert.h>
311176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include <config.h>
321176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include "pixman-private.h"
331176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
341176bdada62cabc6ec4b0308a930e83b679d5d36John Recktypedef double (* kernel_func_t) (double x);
351176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
361176bdada62cabc6ec4b0308a930e83b679d5d36John Recktypedef struct
371176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
381176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_kernel_t	kernel;
391176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    kernel_func_t	func;
401176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double		width;
411176bdada62cabc6ec4b0308a930e83b679d5d36John Reck} filter_info_t;
421176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
431176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
441176bdada62cabc6ec4b0308a930e83b679d5d36John Reckimpulse_kernel (double x)
451176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
461176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return (x == 0.0)? 1.0 : 0.0;
471176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
481176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
491176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
501176bdada62cabc6ec4b0308a930e83b679d5d36John Reckbox_kernel (double x)
511176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
521176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return 1;
531176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
541176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
551176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
561176bdada62cabc6ec4b0308a930e83b679d5d36John Recklinear_kernel (double x)
571176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
581176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return 1 - fabs (x);
591176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
601176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
611176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
621176bdada62cabc6ec4b0308a930e83b679d5d36John Reckgaussian_kernel (double x)
631176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
641176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#define SQRT2 (1.4142135623730950488016887242096980785696718753769480)
651176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#define SIGMA (SQRT2 / 2.0)
661176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
671176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return exp (- x * x / (2 * SIGMA * SIGMA)) / (SIGMA * sqrt (2.0 * M_PI));
681176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
691176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
701176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
711176bdada62cabc6ec4b0308a930e83b679d5d36John Recksinc (double x)
721176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
731176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (x == 0.0)
741176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return 1.0;
751176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    else
761176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return sin (M_PI * x) / (M_PI * x);
771176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
781176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
791176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
801176bdada62cabc6ec4b0308a930e83b679d5d36John Recklanczos (double x, int n)
811176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
821176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return sinc (x) * sinc (x * (1.0 / n));
831176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
841176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
851176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
861176bdada62cabc6ec4b0308a930e83b679d5d36John Recklanczos2_kernel (double x)
871176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
881176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return lanczos (x, 2);
891176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
901176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
911176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
921176bdada62cabc6ec4b0308a930e83b679d5d36John Recklanczos3_kernel (double x)
931176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
941176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return lanczos (x, 3);
951176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
961176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
971176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
981176bdada62cabc6ec4b0308a930e83b679d5d36John Recknice_kernel (double x)
991176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1001176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return lanczos3_kernel (x * 0.75);
1011176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
1021176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1031176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
1041176bdada62cabc6ec4b0308a930e83b679d5d36John Reckgeneral_cubic (double x, double B, double C)
1051176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1061176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double ax = fabs(x);
1071176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1081176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (ax < 1)
1091176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1101176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return ((12 - 9 * B - 6 * C) * ax * ax * ax +
1111176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		(-18 + 12 * B + 6 * C) * ax * ax + (6 - 2 * B)) / 6;
1121176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
1131176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    else if (ax >= 1 && ax < 2)
1141176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1151176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return ((-B - 6 * C) * ax * ax * ax +
1161176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		(6 * B + 30 * C) * ax * ax + (-12 * B - 48 * C) *
1171176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		ax + (8 * B + 24 * C)) / 6;
1181176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
1191176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    else
1201176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1211176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return 0;
1221176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
1231176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
1241176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1251176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
1261176bdada62cabc6ec4b0308a930e83b679d5d36John Reckcubic_kernel (double x)
1271176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1281176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    /* This is the Mitchell-Netravali filter.
1291176bdada62cabc6ec4b0308a930e83b679d5d36John Reck     *
1301176bdada62cabc6ec4b0308a930e83b679d5d36John Reck     * (0.0, 0.5) would give us the Catmull-Rom spline,
1311176bdada62cabc6ec4b0308a930e83b679d5d36John Reck     * but that one seems to be indistinguishable from Lanczos2.
1321176bdada62cabc6ec4b0308a930e83b679d5d36John Reck     */
1331176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return general_cubic (x, 1/3.0, 1/3.0);
1341176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
1351176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1361176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic const filter_info_t filters[] =
1371176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1381176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { PIXMAN_KERNEL_IMPULSE,	        impulse_kernel,   0.0 },
1391176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { PIXMAN_KERNEL_BOX,	        box_kernel,       1.0 },
1401176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { PIXMAN_KERNEL_LINEAR,	        linear_kernel,    2.0 },
1411176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { PIXMAN_KERNEL_CUBIC,		cubic_kernel,     4.0 },
1421176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { PIXMAN_KERNEL_GAUSSIAN,	        gaussian_kernel,  6 * SIGMA },
1431176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { PIXMAN_KERNEL_LANCZOS2,	        lanczos2_kernel,  4.0 },
1441176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { PIXMAN_KERNEL_LANCZOS3,	        lanczos3_kernel,  6.0 },
1451176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { PIXMAN_KERNEL_LANCZOS3_STRETCHED, nice_kernel,      8.0 },
1461176bdada62cabc6ec4b0308a930e83b679d5d36John Reck};
1471176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1481176bdada62cabc6ec4b0308a930e83b679d5d36John Reck/* This function scales @kernel2 by @scale, then
1491176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * aligns @x1 in @kernel1 with @x2 in @kernel2 and
1501176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * and integrates the product of the kernels across @width.
1511176bdada62cabc6ec4b0308a930e83b679d5d36John Reck *
1521176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * This function assumes that the intervals are within
1531176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * the kernels in question. E.g., the caller must not
1541176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * try to integrate a linear kernel ouside of [-1:1]
1551176bdada62cabc6ec4b0308a930e83b679d5d36John Reck */
1561176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
1571176bdada62cabc6ec4b0308a930e83b679d5d36John Reckintegral (pixman_kernel_t kernel1, double x1,
1581176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	  pixman_kernel_t kernel2, double scale, double x2,
1591176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	  double width)
1601176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1611176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    /* If the integration interval crosses zero, break it into
1621176bdada62cabc6ec4b0308a930e83b679d5d36John Reck     * two separate integrals. This ensures that filters such
1631176bdada62cabc6ec4b0308a930e83b679d5d36John Reck     * as LINEAR that are not differentiable at 0 will still
1641176bdada62cabc6ec4b0308a930e83b679d5d36John Reck     * integrate properly.
1651176bdada62cabc6ec4b0308a930e83b679d5d36John Reck     */
1661176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (x1 < 0 && x1 + width > 0)
1671176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1681176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return
1691176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    integral (kernel1, x1, kernel2, scale, x2, - x1) +
1701176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    integral (kernel1, 0, kernel2, scale, x2 - x1, width + x1);
1711176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
1721176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    else if (x2 < 0 && x2 + width > 0)
1731176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1741176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return
1751176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    integral (kernel1, x1, kernel2, scale, x2, - x2) +
1761176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    integral (kernel1, x1 - x2, kernel2, scale, 0, width + x2);
1771176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
1781176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    else if (kernel1 == PIXMAN_KERNEL_IMPULSE)
1791176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1801176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	assert (width == 0.0);
1811176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return filters[kernel2].func (x2 * scale);
1821176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
1831176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    else if (kernel2 == PIXMAN_KERNEL_IMPULSE)
1841176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1851176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	assert (width == 0.0);
1861176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return filters[kernel1].func (x1);
1871176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
1881176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    else
1891176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1901176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	/* Integration via Simpson's rule */
1911176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#define N_SEGMENTS 128
1921176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#define SAMPLE(a1, a2)							\
1931176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	(filters[kernel1].func ((a1)) * filters[kernel2].func ((a2) * scale))
1941176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1951176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	double s = 0.0;
1961176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	double h = width / (double)N_SEGMENTS;
1971176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	int i;
1981176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1991176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	s = SAMPLE (x1, x2);
2001176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2011176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	for (i = 1; i < N_SEGMENTS; i += 2)
2021176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	{
2031176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    double a1 = x1 + h * i;
2041176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    double a2 = x2 + h * i;
2051176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2061176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    s += 2 * SAMPLE (a1, a2);
2071176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2081176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    if (i >= 2 && i < N_SEGMENTS - 1)
2091176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		s += 4 * SAMPLE (a1, a2);
2101176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	}
2111176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2121176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	s += SAMPLE (x1 + width, x2 + width);
2131176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2141176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return h * s * (1.0 / 3.0);
2151176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
2161176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
2171176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2181176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic pixman_fixed_t *
2191176bdada62cabc6ec4b0308a930e83b679d5d36John Reckcreate_1d_filter (int             *width,
2201176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		  pixman_kernel_t  reconstruct,
2211176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		  pixman_kernel_t  sample,
2221176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		  double           scale,
2231176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		  int              n_phases)
2241176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
2251176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_fixed_t *params, *p;
2261176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double step;
2271176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double size;
2281176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    int i;
2291176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2301176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    size = scale * filters[sample].width + filters[reconstruct].width;
2311176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    *width = ceil (size);
2321176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2331176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    p = params = malloc (*width * n_phases * sizeof (pixman_fixed_t));
2341176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (!params)
2351176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        return NULL;
2361176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2371176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    step = 1.0 / n_phases;
2381176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2391176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    for (i = 0; i < n_phases; ++i)
2401176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
2411176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        double frac = step / 2.0 + i * step;
2421176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	pixman_fixed_t new_total;
2431176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        int x, x1, x2;
2441176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	double total;
2451176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2461176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	/* Sample convolution of reconstruction and sampling
2471176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	 * filter. See rounding.txt regarding the rounding
2481176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	 * and sample positions.
2491176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	 */
2501176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2511176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	x1 = ceil (frac - *width / 2.0 - 0.5);
2521176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        x2 = x1 + *width;
2531176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2541176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	total = 0;
2551176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        for (x = x1; x < x2; ++x)
2561176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        {
2571176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    double pos = x + 0.5 - frac;
2581176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    double rlow = - filters[reconstruct].width / 2.0;
2591176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    double rhigh = rlow + filters[reconstruct].width;
2601176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    double slow = pos - scale * filters[sample].width / 2.0;
2611176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    double shigh = slow + scale * filters[sample].width;
2621176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    double c = 0.0;
2631176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    double ilow, ihigh;
2641176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2651176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    if (rhigh >= slow && rlow <= shigh)
2661176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    {
2671176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		ilow = MAX (slow, rlow);
2681176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		ihigh = MIN (shigh, rhigh);
2691176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2701176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		c = integral (reconstruct, ilow,
2711176bdada62cabc6ec4b0308a930e83b679d5d36John Reck			      sample, 1.0 / scale, ilow - pos,
2721176bdada62cabc6ec4b0308a930e83b679d5d36John Reck			      ihigh - ilow);
2731176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    }
2741176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2751176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    total += c;
2761176bdada62cabc6ec4b0308a930e83b679d5d36John Reck            *p++ = (pixman_fixed_t)(c * 65535.0 + 0.5);
2771176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        }
2781176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2791176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	/* Normalize */
2801176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	p -= *width;
2811176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        total = 1 / total;
2821176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        new_total = 0;
2831176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	for (x = x1; x < x2; ++x)
2841176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	{
2851176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    pixman_fixed_t t = (*p) * total + 0.5;
2861176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2871176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    new_total += t;
2881176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    *p++ = t;
2891176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	}
2901176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2911176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	if (new_total != pixman_fixed_1)
2921176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    *(p - *width / 2) += (pixman_fixed_1 - new_total);
2931176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
2941176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2951176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return params;
2961176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
2971176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2981176bdada62cabc6ec4b0308a930e83b679d5d36John Reck/* Create the parameter list for a SEPARABLE_CONVOLUTION filter
2991176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * with the given kernels and scale parameters
3001176bdada62cabc6ec4b0308a930e83b679d5d36John Reck */
3011176bdada62cabc6ec4b0308a930e83b679d5d36John ReckPIXMAN_EXPORT pixman_fixed_t *
3021176bdada62cabc6ec4b0308a930e83b679d5d36John Reckpixman_filter_create_separable_convolution (int             *n_values,
3031176bdada62cabc6ec4b0308a930e83b679d5d36John Reck					    pixman_fixed_t   scale_x,
3041176bdada62cabc6ec4b0308a930e83b679d5d36John Reck					    pixman_fixed_t   scale_y,
3051176bdada62cabc6ec4b0308a930e83b679d5d36John Reck					    pixman_kernel_t  reconstruct_x,
3061176bdada62cabc6ec4b0308a930e83b679d5d36John Reck					    pixman_kernel_t  reconstruct_y,
3071176bdada62cabc6ec4b0308a930e83b679d5d36John Reck					    pixman_kernel_t  sample_x,
3081176bdada62cabc6ec4b0308a930e83b679d5d36John Reck					    pixman_kernel_t  sample_y,
3091176bdada62cabc6ec4b0308a930e83b679d5d36John Reck					    int              subsample_bits_x,
3101176bdada62cabc6ec4b0308a930e83b679d5d36John Reck					    int	             subsample_bits_y)
3111176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
3121176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double sx = fabs (pixman_fixed_to_double (scale_x));
3131176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double sy = fabs (pixman_fixed_to_double (scale_y));
3141176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_fixed_t *horz = NULL, *vert = NULL, *params = NULL;
3151176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    int subsample_x, subsample_y;
3161176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    int width, height;
3171176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3181176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    subsample_x = (1 << subsample_bits_x);
3191176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    subsample_y = (1 << subsample_bits_y);
3201176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3211176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    horz = create_1d_filter (&width, reconstruct_x, sample_x, sx, subsample_x);
3221176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    vert = create_1d_filter (&height, reconstruct_y, sample_y, sy, subsample_y);
3231176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3241176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (!horz || !vert)
3251176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        goto out;
3261176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3271176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    *n_values = 4 + width * subsample_x + height * subsample_y;
3281176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3291176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    params = malloc (*n_values * sizeof (pixman_fixed_t));
3301176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (!params)
3311176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        goto out;
3321176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3331176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    params[0] = pixman_int_to_fixed (width);
3341176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    params[1] = pixman_int_to_fixed (height);
3351176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    params[2] = pixman_int_to_fixed (subsample_bits_x);
3361176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    params[3] = pixman_int_to_fixed (subsample_bits_y);
3371176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3381176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    memcpy (params + 4, horz,
3391176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    width * subsample_x * sizeof (pixman_fixed_t));
3401176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    memcpy (params + 4 + width * subsample_x, vert,
3411176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    height * subsample_y * sizeof (pixman_fixed_t));
3421176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3431176bdada62cabc6ec4b0308a930e83b679d5d36John Reckout:
3441176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    free (horz);
3451176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    free (vert);
3461176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3471176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return params;
3481176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
349