Add qemu 2.4.0
[kvmfornfv.git] / qemu / pixman / pixman / pixman-filter.c
diff --git a/qemu/pixman/pixman/pixman-filter.c b/qemu/pixman/pixman/pixman-filter.c
new file mode 100644 (file)
index 0000000..b2bf53f
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2012, Red Hat, Inc.
+ * Copyright 2012, Soren Sandmann
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Soren Sandmann <soren.sandmann@gmail.com>
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "pixman-private.h"
+
+typedef double (* kernel_func_t) (double x);
+
+typedef struct
+{
+    pixman_kernel_t    kernel;
+    kernel_func_t      func;
+    double             width;
+} filter_info_t;
+
+static double
+impulse_kernel (double x)
+{
+    return (x == 0.0)? 1.0 : 0.0;
+}
+
+static double
+box_kernel (double x)
+{
+    return 1;
+}
+
+static double
+linear_kernel (double x)
+{
+    return 1 - fabs (x);
+}
+
+static double
+gaussian_kernel (double x)
+{
+#define SQRT2 (1.4142135623730950488016887242096980785696718753769480)
+#define SIGMA (SQRT2 / 2.0)
+    
+    return exp (- x * x / (2 * SIGMA * SIGMA)) / (SIGMA * sqrt (2.0 * M_PI));
+}
+
+static double
+sinc (double x)
+{
+    if (x == 0.0)
+       return 1.0;
+    else
+       return sin (M_PI * x) / (M_PI * x);
+}
+
+static double
+lanczos (double x, int n)
+{
+    return sinc (x) * sinc (x * (1.0 / n));
+}
+
+static double
+lanczos2_kernel (double x)
+{
+    return lanczos (x, 2);
+}
+
+static double
+lanczos3_kernel (double x)
+{
+    return lanczos (x, 3);
+}
+
+static double
+nice_kernel (double x)
+{
+    return lanczos3_kernel (x * 0.75);
+}
+
+static double
+general_cubic (double x, double B, double C)
+{
+    double ax = fabs(x);
+
+    if (ax < 1)
+    {
+       return ((12 - 9 * B - 6 * C) * ax * ax * ax +
+               (-18 + 12 * B + 6 * C) * ax * ax + (6 - 2 * B)) / 6;
+    }
+    else if (ax >= 1 && ax < 2)
+    {
+       return ((-B - 6 * C) * ax * ax * ax +
+               (6 * B + 30 * C) * ax * ax + (-12 * B - 48 * C) *
+               ax + (8 * B + 24 * C)) / 6;
+    }
+    else
+    {
+       return 0;
+    }
+}
+
+static double
+cubic_kernel (double x)
+{
+    /* This is the Mitchell-Netravali filter.
+     *
+     * (0.0, 0.5) would give us the Catmull-Rom spline,
+     * but that one seems to be indistinguishable from Lanczos2.
+     */
+    return general_cubic (x, 1/3.0, 1/3.0);
+}
+
+static const filter_info_t filters[] =
+{
+    { PIXMAN_KERNEL_IMPULSE,           impulse_kernel,   0.0 },
+    { PIXMAN_KERNEL_BOX,               box_kernel,       1.0 },
+    { PIXMAN_KERNEL_LINEAR,            linear_kernel,    2.0 },
+    { PIXMAN_KERNEL_CUBIC,             cubic_kernel,     4.0 },
+    { PIXMAN_KERNEL_GAUSSIAN,          gaussian_kernel,  6 * SIGMA },
+    { PIXMAN_KERNEL_LANCZOS2,          lanczos2_kernel,  4.0 },
+    { PIXMAN_KERNEL_LANCZOS3,          lanczos3_kernel,  6.0 },
+    { PIXMAN_KERNEL_LANCZOS3_STRETCHED, nice_kernel,      8.0 },
+};
+
+/* This function scales @kernel2 by @scale, then
+ * aligns @x1 in @kernel1 with @x2 in @kernel2 and
+ * and integrates the product of the kernels across @width.
+ *
+ * This function assumes that the intervals are within
+ * the kernels in question. E.g., the caller must not
+ * try to integrate a linear kernel ouside of [-1:1]
+ */
+static double
+integral (pixman_kernel_t kernel1, double x1,
+         pixman_kernel_t kernel2, double scale, double x2,
+         double width)
+{
+    /* If the integration interval crosses zero, break it into
+     * two separate integrals. This ensures that filters such
+     * as LINEAR that are not differentiable at 0 will still
+     * integrate properly.
+     */
+    if (x1 < 0 && x1 + width > 0)
+    {
+       return
+           integral (kernel1, x1, kernel2, scale, x2, - x1) +
+           integral (kernel1, 0, kernel2, scale, x2 - x1, width + x1);
+    }
+    else if (x2 < 0 && x2 + width > 0)
+    {
+       return
+           integral (kernel1, x1, kernel2, scale, x2, - x2) +
+           integral (kernel1, x1 - x2, kernel2, scale, 0, width + x2);
+    }
+    else if (kernel1 == PIXMAN_KERNEL_IMPULSE)
+    {
+       assert (width == 0.0);
+       return filters[kernel2].func (x2 * scale);
+    }
+    else if (kernel2 == PIXMAN_KERNEL_IMPULSE)
+    {
+       assert (width == 0.0);
+       return filters[kernel1].func (x1);
+    }
+    else
+    {
+       /* Integration via Simpson's rule */
+#define N_SEGMENTS 128
+#define SAMPLE(a1, a2)                                                 \
+       (filters[kernel1].func ((a1)) * filters[kernel2].func ((a2) * scale))
+       
+       double s = 0.0;
+       double h = width / (double)N_SEGMENTS;
+       int i;
+
+       s = SAMPLE (x1, x2);
+
+       for (i = 1; i < N_SEGMENTS; i += 2)
+       {
+           double a1 = x1 + h * i;
+           double a2 = x2 + h * i;
+
+           s += 2 * SAMPLE (a1, a2);
+
+           if (i >= 2 && i < N_SEGMENTS - 1)
+               s += 4 * SAMPLE (a1, a2);
+       }
+
+       s += SAMPLE (x1 + width, x2 + width);
+       
+       return h * s * (1.0 / 3.0);
+    }
+}
+
+static pixman_fixed_t *
+create_1d_filter (int             *width,
+                 pixman_kernel_t  reconstruct,
+                 pixman_kernel_t  sample,
+                 double           scale,
+                 int              n_phases)
+{
+    pixman_fixed_t *params, *p;
+    double step;
+    double size;
+    int i;
+
+    size = scale * filters[sample].width + filters[reconstruct].width;
+    *width = ceil (size);
+
+    p = params = malloc (*width * n_phases * sizeof (pixman_fixed_t));
+    if (!params)
+        return NULL;
+
+    step = 1.0 / n_phases;
+
+    for (i = 0; i < n_phases; ++i)
+    {
+        double frac = step / 2.0 + i * step;
+       pixman_fixed_t new_total;
+        int x, x1, x2;
+       double total;
+
+       /* Sample convolution of reconstruction and sampling
+        * filter. See rounding.txt regarding the rounding
+        * and sample positions.
+        */
+
+       x1 = ceil (frac - *width / 2.0 - 0.5);
+        x2 = x1 + *width;
+
+       total = 0;
+        for (x = x1; x < x2; ++x)
+        {
+           double pos = x + 0.5 - frac;
+           double rlow = - filters[reconstruct].width / 2.0;
+           double rhigh = rlow + filters[reconstruct].width;
+           double slow = pos - scale * filters[sample].width / 2.0;
+           double shigh = slow + scale * filters[sample].width;
+           double c = 0.0;
+           double ilow, ihigh;
+
+           if (rhigh >= slow && rlow <= shigh)
+           {
+               ilow = MAX (slow, rlow);
+               ihigh = MIN (shigh, rhigh);
+
+               c = integral (reconstruct, ilow,
+                             sample, 1.0 / scale, ilow - pos,
+                             ihigh - ilow);
+           }
+
+           total += c;
+            *p++ = (pixman_fixed_t)(c * 65536.0 + 0.5);
+        }
+
+       /* Normalize */
+       p -= *width;
+        total = 1 / total;
+        new_total = 0;
+       for (x = x1; x < x2; ++x)
+       {
+           pixman_fixed_t t = (*p) * total + 0.5;
+
+           new_total += t;
+           *p++ = t;
+       }
+
+       if (new_total != pixman_fixed_1)
+           *(p - *width / 2) += (pixman_fixed_1 - new_total);
+    }
+
+    return params;
+}
+
+/* Create the parameter list for a SEPARABLE_CONVOLUTION filter
+ * with the given kernels and scale parameters
+ */
+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)
+{
+    double sx = fabs (pixman_fixed_to_double (scale_x));
+    double sy = fabs (pixman_fixed_to_double (scale_y));
+    pixman_fixed_t *horz = NULL, *vert = NULL, *params = NULL;
+    int subsample_x, subsample_y;
+    int width, height;
+
+    subsample_x = (1 << subsample_bits_x);
+    subsample_y = (1 << subsample_bits_y);
+
+    horz = create_1d_filter (&width, reconstruct_x, sample_x, sx, subsample_x);
+    vert = create_1d_filter (&height, reconstruct_y, sample_y, sy, subsample_y);
+
+    if (!horz || !vert)
+        goto out;
+    
+    *n_values = 4 + width * subsample_x + height * subsample_y;
+    
+    params = malloc (*n_values * sizeof (pixman_fixed_t));
+    if (!params)
+        goto out;
+
+    params[0] = pixman_int_to_fixed (width);
+    params[1] = pixman_int_to_fixed (height);
+    params[2] = pixman_int_to_fixed (subsample_bits_x);
+    params[3] = pixman_int_to_fixed (subsample_bits_y);
+
+    memcpy (params + 4, horz,
+           width * subsample_x * sizeof (pixman_fixed_t));
+    memcpy (params + 4 + width * subsample_x, vert,
+           height * subsample_y * sizeof (pixman_fixed_t));
+
+out:
+    free (horz);
+    free (vert);
+
+    return params;
+}