LCOV - code coverage report
Current view: directory - gfx/cairo/cairo/src - cairo-spline.c (source / functions) Found Hit Coverage
Test: app.info Lines: 141 0 0.0 %
Date: 2012-06-02 Functions: 8 0 0.0 %

       1                 : /* cairo - a vector graphics library with display and print output
       2                 :  *
       3                 :  * Copyright © 2002 University of Southern California
       4                 :  *
       5                 :  * This library is free software; you can redistribute it and/or
       6                 :  * modify it either under the terms of the GNU Lesser General Public
       7                 :  * License version 2.1 as published by the Free Software Foundation
       8                 :  * (the "LGPL") or, at your option, under the terms of the Mozilla
       9                 :  * Public License Version 1.1 (the "MPL"). If you do not alter this
      10                 :  * notice, a recipient may use your version of this file under either
      11                 :  * the MPL or the LGPL.
      12                 :  *
      13                 :  * You should have received a copy of the LGPL along with this library
      14                 :  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
      15                 :  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
      16                 :  * You should have received a copy of the MPL along with this library
      17                 :  * in the file COPYING-MPL-1.1
      18                 :  *
      19                 :  * The contents of this file are subject to the Mozilla Public License
      20                 :  * Version 1.1 (the "License"); you may not use this file except in
      21                 :  * compliance with the License. You may obtain a copy of the License at
      22                 :  * http://www.mozilla.org/MPL/
      23                 :  *
      24                 :  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
      25                 :  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
      26                 :  * the specific language governing rights and limitations.
      27                 :  *
      28                 :  * The Original Code is the cairo graphics library.
      29                 :  *
      30                 :  * The Initial Developer of the Original Code is University of Southern
      31                 :  * California.
      32                 :  *
      33                 :  * Contributor(s):
      34                 :  *      Carl D. Worth <cworth@cworth.org>
      35                 :  */
      36                 : 
      37                 : #include "cairoint.h"
      38                 : 
      39                 : #include "cairo-slope-private.h"
      40                 : 
      41                 : cairo_bool_t
      42               0 : _cairo_spline_init (cairo_spline_t *spline,
      43                 :                     cairo_spline_add_point_func_t add_point_func,
      44                 :                     void *closure,
      45                 :                     const cairo_point_t *a, const cairo_point_t *b,
      46                 :                     const cairo_point_t *c, const cairo_point_t *d)
      47                 : {
      48               0 :     spline->add_point_func = add_point_func;
      49               0 :     spline->closure = closure;
      50                 : 
      51               0 :     spline->knots.a = *a;
      52               0 :     spline->knots.b = *b;
      53               0 :     spline->knots.c = *c;
      54               0 :     spline->knots.d = *d;
      55                 : 
      56               0 :     if (a->x != b->x || a->y != b->y)
      57               0 :         _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b);
      58               0 :     else if (a->x != c->x || a->y != c->y)
      59               0 :         _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c);
      60               0 :     else if (a->x != d->x || a->y != d->y)
      61               0 :         _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d);
      62                 :     else
      63               0 :         return FALSE;
      64                 : 
      65               0 :     if (c->x != d->x || c->y != d->y)
      66               0 :         _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d);
      67               0 :     else if (b->x != d->x || b->y != d->y)
      68               0 :         _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d);
      69                 :     else
      70               0 :         _cairo_slope_init (&spline->final_slope, &spline->knots.a, &spline->knots.d);
      71                 : 
      72               0 :     return TRUE;
      73                 : }
      74                 : 
      75                 : static cairo_status_t
      76               0 : _cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point)
      77                 : {
      78                 :     cairo_point_t *prev;
      79                 : 
      80               0 :     prev = &spline->last_point;
      81               0 :     if (prev->x == point->x && prev->y == point->y)
      82               0 :         return CAIRO_STATUS_SUCCESS;
      83                 : 
      84               0 :     spline->last_point = *point;
      85               0 :     return spline->add_point_func (spline->closure, point);
      86                 : }
      87                 : 
      88                 : static void
      89               0 : _lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result)
      90                 : {
      91               0 :     result->x = a->x + ((b->x - a->x) >> 1);
      92               0 :     result->y = a->y + ((b->y - a->y) >> 1);
      93               0 : }
      94                 : 
      95                 : static void
      96               0 : _de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2)
      97                 : {
      98                 :     cairo_point_t ab, bc, cd;
      99                 :     cairo_point_t abbc, bccd;
     100                 :     cairo_point_t final;
     101                 : 
     102               0 :     _lerp_half (&s1->a, &s1->b, &ab);
     103               0 :     _lerp_half (&s1->b, &s1->c, &bc);
     104               0 :     _lerp_half (&s1->c, &s1->d, &cd);
     105               0 :     _lerp_half (&ab, &bc, &abbc);
     106               0 :     _lerp_half (&bc, &cd, &bccd);
     107               0 :     _lerp_half (&abbc, &bccd, &final);
     108                 : 
     109               0 :     s2->a = final;
     110               0 :     s2->b = bccd;
     111               0 :     s2->c = cd;
     112               0 :     s2->d = s1->d;
     113                 : 
     114               0 :     s1->b = ab;
     115               0 :     s1->c = abbc;
     116               0 :     s1->d = final;
     117               0 : }
     118                 : 
     119                 : /* Return an upper bound on the error (squared) that could result from
     120                 :  * approximating a spline as a line segment connecting the two endpoints. */
     121                 : static double
     122               0 : _cairo_spline_error_squared (const cairo_spline_knots_t *knots)
     123                 : {
     124                 :     double bdx, bdy, berr;
     125                 :     double cdx, cdy, cerr;
     126                 : 
     127                 :     /* We are going to compute the distance (squared) between each of the the b
     128                 :      * and c control points and the segment a-b. The maximum of these two
     129                 :      * distances will be our approximation error. */
     130                 : 
     131               0 :     bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x);
     132               0 :     bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y);
     133                 : 
     134               0 :     cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x);
     135               0 :     cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y);
     136                 : 
     137               0 :     if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) {
     138                 :         /* Intersection point (px):
     139                 :          *     px = p1 + u(p2 - p1)
     140                 :          *     (p - px) ∙ (p2 - p1) = 0
     141                 :          * Thus:
     142                 :          *     u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
     143                 :          */
     144                 : 
     145                 :         double dx, dy, u, v;
     146                 : 
     147               0 :         dx = _cairo_fixed_to_double (knots->d.x - knots->a.x);
     148               0 :         dy = _cairo_fixed_to_double (knots->d.y - knots->a.y);
     149               0 :          v = dx * dx + dy * dy;
     150                 : 
     151               0 :         u = bdx * dx + bdy * dy;
     152               0 :         if (u <= 0) {
     153                 :             /* bdx -= 0;
     154                 :              * bdy -= 0;
     155                 :              */
     156               0 :         } else if (u >= v) {
     157               0 :             bdx -= dx;
     158               0 :             bdy -= dy;
     159                 :         } else {
     160               0 :             bdx -= u/v * dx;
     161               0 :             bdy -= u/v * dy;
     162                 :         }
     163                 : 
     164               0 :         u = cdx * dx + cdy * dy;
     165               0 :         if (u <= 0) {
     166                 :             /* cdx -= 0;
     167                 :              * cdy -= 0;
     168                 :              */
     169               0 :         } else if (u >= v) {
     170               0 :             cdx -= dx;
     171               0 :             cdy -= dy;
     172                 :         } else {
     173               0 :             cdx -= u/v * dx;
     174               0 :             cdy -= u/v * dy;
     175                 :         }
     176                 :     }
     177                 : 
     178               0 :     berr = bdx * bdx + bdy * bdy;
     179               0 :     cerr = cdx * cdx + cdy * cdy;
     180               0 :     if (berr > cerr)
     181               0 :         return berr;
     182                 :     else
     183               0 :         return cerr;
     184                 : }
     185                 : 
     186                 : void StoreSpline(int ax, int ay, int bx, int by, int cx, double cy, int dx, int dy);
     187                 : void CrashSpline(double tolerance, int ax, int ay, int bx, int by, int cx, int cy, int dx, int dy);
     188                 : 
     189                 : static cairo_status_t
     190               0 : _cairo_spline_decompose_into (cairo_spline_knots_t *s1, double tolerance_squared, cairo_spline_t *result)
     191                 : {
     192                 :     static int depth;
     193                 :     cairo_spline_knots_t s2;
     194                 :     cairo_status_t status;
     195                 : 
     196               0 :     depth++;
     197               0 :     if (depth == 200) {
     198               0 :         CrashSpline(tolerance_squared, s1->a.x, s1->a.y,
     199                 :                 s1->b.x, s1->b.y,
     200                 :                 s1->c.x, s1->c.y,
     201                 :                 s1->d.x, s1->d.y);
     202                 :     }
     203                 : 
     204               0 :     if (_cairo_spline_error_squared (s1) < tolerance_squared) {
     205               0 :         depth--;
     206               0 :         return _cairo_spline_add_point (result, &s1->a);
     207                 :     }
     208                 : 
     209               0 :     _de_casteljau (s1, &s2);
     210                 : 
     211               0 :     status = _cairo_spline_decompose_into (s1, tolerance_squared, result);
     212               0 :     if (unlikely (status)) {
     213               0 :         depth--;
     214               0 :         return status;
     215                 :     }
     216                 : 
     217               0 :     status = _cairo_spline_decompose_into (&s2, tolerance_squared, result);
     218               0 :     depth--;
     219               0 :     return status;
     220                 : }
     221                 : 
     222                 : cairo_status_t
     223               0 : _cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
     224                 : {
     225                 :     cairo_spline_knots_t s1;
     226                 :     cairo_status_t status;
     227                 : 
     228               0 :     StoreSpline(spline->knots.a.x,
     229                 :                 spline->knots.a.y,
     230                 :                 spline->knots.b.x,
     231                 :                 spline->knots.b.y,
     232                 :                 spline->knots.c.x,
     233               0 :                 spline->knots.c.y,
     234                 :                 spline->knots.d.x,
     235                 :                 spline->knots.d.y);
     236               0 :     s1 = spline->knots;
     237               0 :     spline->last_point = s1.a;
     238               0 :     status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline);
     239               0 :     if (unlikely (status))
     240               0 :         return status;
     241                 : 
     242               0 :     return _cairo_spline_add_point (spline, &spline->knots.d);
     243                 : }
     244                 : 
     245                 : /* Note: this function is only good for computing bounds in device space. */
     246                 : cairo_status_t
     247               0 : _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
     248                 :                      void *closure,
     249                 :                      const cairo_point_t *p0, const cairo_point_t *p1,
     250                 :                      const cairo_point_t *p2, const cairo_point_t *p3)
     251                 : {
     252                 :     double x0, x1, x2, x3;
     253                 :     double y0, y1, y2, y3;
     254                 :     double a, b, c;
     255                 :     double t[4];
     256               0 :     int t_num = 0, i;
     257                 :     cairo_status_t status;
     258                 : 
     259               0 :     x0 = _cairo_fixed_to_double (p0->x);
     260               0 :     y0 = _cairo_fixed_to_double (p0->y);
     261               0 :     x1 = _cairo_fixed_to_double (p1->x);
     262               0 :     y1 = _cairo_fixed_to_double (p1->y);
     263               0 :     x2 = _cairo_fixed_to_double (p2->x);
     264               0 :     y2 = _cairo_fixed_to_double (p2->y);
     265               0 :     x3 = _cairo_fixed_to_double (p3->x);
     266               0 :     y3 = _cairo_fixed_to_double (p3->y);
     267                 : 
     268                 :     /* The spline can be written as a polynomial of the four points:
     269                 :      *
     270                 :      *   (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3
     271                 :      *
     272                 :      * for 0≤t≤1.  Now, the X and Y components of the spline follow the
     273                 :      * same polynomial but with x and y replaced for p.  To find the
     274                 :      * bounds of the spline, we just need to find the X and Y bounds.
     275                 :      * To find the bound, we take the derivative and equal it to zero,
     276                 :      * and solve to find the t's that give the extreme points.
     277                 :      *
     278                 :      * Here is the derivative of the curve, sorted on t:
     279                 :      *
     280                 :      *   3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1
     281                 :      *
     282                 :      * Let:
     283                 :      *
     284                 :      *   a = -p0+3p1-3p2+p3
     285                 :      *   b =  p0-2p1+p2
     286                 :      *   c = -p0+p1
     287                 :      *
     288                 :      * Gives:
     289                 :      *
     290                 :      *   a.t² + 2b.t + c = 0
     291                 :      *
     292                 :      * With:
     293                 :      *
     294                 :      *   delta = b*b - a*c
     295                 :      *
     296                 :      * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if
     297                 :      * delta is positive, and at -b/a if delta is zero.
     298                 :      */
     299                 : 
     300                 : #define ADD(t0) \
     301                 :     { \
     302                 :         double _t0 = (t0); \
     303                 :         if (0 < _t0 && _t0 < 1) \
     304                 :             t[t_num++] = _t0; \
     305                 :     }
     306                 : 
     307                 : #define FIND_EXTREMES(a,b,c) \
     308                 :     { \
     309                 :         if (a == 0) { \
     310                 :             if (b != 0) \
     311                 :                 ADD (-c / (2*b)); \
     312                 :         } else { \
     313                 :             double b2 = b * b; \
     314                 :             double delta = b2 - a * c; \
     315                 :             if (delta > 0) { \
     316                 :                 cairo_bool_t feasible; \
     317                 :                 double _2ab = 2 * a * b; \
     318                 :                 /* We are only interested in solutions t that satisfy 0<t<1 \
     319                 :                  * here.  We do some checks to avoid sqrt if the solutions \
     320                 :                  * are not in that range.  The checks can be derived from: \
     321                 :                  * \
     322                 :                  *   0 < (-b±√delta)/a < 1 \
     323                 :                  */ \
     324                 :                 if (_2ab >= 0) \
     325                 :                     feasible = delta > b2 && delta < a*a + b2 + _2ab; \
     326                 :                 else if (-b / a >= 1) \
     327                 :                     feasible = delta < b2 && delta > a*a + b2 + _2ab; \
     328                 :                 else \
     329                 :                     feasible = delta < b2 || delta < a*a + b2 + _2ab; \
     330                 :                 \
     331                 :                 if (unlikely (feasible)) { \
     332                 :                     double sqrt_delta = sqrt (delta); \
     333                 :                     ADD ((-b - sqrt_delta) / a); \
     334                 :                     ADD ((-b + sqrt_delta) / a); \
     335                 :                 } \
     336                 :             } else if (delta == 0) { \
     337                 :                 ADD (-b / a); \
     338                 :             } \
     339                 :         } \
     340                 :     }
     341                 : 
     342                 :     /* Find X extremes */
     343               0 :     a = -x0 + 3*x1 - 3*x2 + x3;
     344               0 :     b =  x0 - 2*x1 + x2;
     345               0 :     c = -x0 + x1;
     346               0 :     FIND_EXTREMES (a, b, c);
     347                 : 
     348                 :     /* Find Y extremes */
     349               0 :     a = -y0 + 3*y1 - 3*y2 + y3;
     350               0 :     b =  y0 - 2*y1 + y2;
     351               0 :     c = -y0 + y1;
     352               0 :     FIND_EXTREMES (a, b, c);
     353                 : 
     354               0 :     status = add_point_func (closure, p0);
     355               0 :     if (unlikely (status))
     356               0 :         return status;
     357                 : 
     358               0 :     for (i = 0; i < t_num; i++) {
     359                 :         cairo_point_t p;
     360                 :         double x, y;
     361                 :         double t_1_0, t_0_1;
     362                 :         double t_2_0, t_0_2;
     363                 :         double t_3_0, t_2_1_3, t_1_2_3, t_0_3;
     364                 : 
     365               0 :         t_1_0 = t[i];          /*      t  */
     366               0 :         t_0_1 = 1 - t_1_0;     /* (1 - t) */
     367                 : 
     368               0 :         t_2_0 = t_1_0 * t_1_0; /*      t  *      t  */
     369               0 :         t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */
     370                 : 
     371               0 :         t_3_0   = t_2_0 * t_1_0;     /*      t  *      t  *      t      */
     372               0 :         t_2_1_3 = t_2_0 * t_0_1 * 3; /*      t  *      t  * (1 - t) * 3 */
     373               0 :         t_1_2_3 = t_1_0 * t_0_2 * 3; /*      t  * (1 - t) * (1 - t) * 3 */
     374               0 :         t_0_3   = t_0_1 * t_0_2;     /* (1 - t) * (1 - t) * (1 - t)     */
     375                 : 
     376                 :         /* Bezier polynomial */
     377               0 :         x = x0 * t_0_3
     378               0 :           + x1 * t_1_2_3
     379               0 :           + x2 * t_2_1_3
     380               0 :           + x3 * t_3_0;
     381               0 :         y = y0 * t_0_3
     382               0 :           + y1 * t_1_2_3
     383               0 :           + y2 * t_2_1_3
     384               0 :           + y3 * t_3_0;
     385                 : 
     386               0 :         p.x = _cairo_fixed_from_double (x);
     387               0 :         p.y = _cairo_fixed_from_double (y);
     388               0 :         status = add_point_func (closure, &p);
     389               0 :         if (unlikely (status))
     390               0 :             return status;
     391                 :     }
     392                 : 
     393               0 :     return add_point_func (closure, p3);
     394                 : }

Generated by: LCOV version 1.7