ftbbox.c

Go to the documentation of this file.
00001 /***************************************************************************/
00002 /*                                                                         */
00003 /*  ftbbox.c                                                               */
00004 /*                                                                         */
00005 /*    FreeType bbox computation (body).                                    */
00006 /*                                                                         */
00007 /*  Copyright 1996-2001, 2002, 2004, 2006, 2010 by                         */
00008 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
00009 /*                                                                         */
00010 /*  This file is part of the FreeType project, and may only be used        */
00011 /*  modified and distributed under the terms of the FreeType project       */
00012 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
00013 /*  this file you indicate that you have read the license and              */
00014 /*  understand and accept it fully.                                        */
00015 /*                                                                         */
00016 /***************************************************************************/
00017 
00018 
00019   /*************************************************************************/
00020   /*                                                                       */
00021   /* This component has a _single_ role: to compute exact outline bounding */
00022   /* boxes.                                                                */
00023   /*                                                                       */
00024   /*************************************************************************/
00025 
00026 
00027 #include <ft2build.h>
00028 #include FT_BBOX_H
00029 #include FT_IMAGE_H
00030 #include FT_OUTLINE_H
00031 #include FT_INTERNAL_CALC_H
00032 #include FT_INTERNAL_OBJECTS_H
00033 
00034 
00035   typedef struct  TBBox_Rec_
00036   {
00037     FT_Vector  last;
00038     FT_BBox    bbox;
00039 
00040   } TBBox_Rec;
00041 
00042 
00043   /*************************************************************************/
00044   /*                                                                       */
00045   /* <Function>                                                            */
00046   /*    BBox_Move_To                                                       */
00047   /*                                                                       */
00048   /* <Description>                                                         */
00049   /*    This function is used as a `move_to' and `line_to' emitter during  */
00050   /*    FT_Outline_Decompose().  It simply records the destination point   */
00051   /*    in `user->last'; no further computations are necessary since we    */
00052   /*    use the cbox as the starting bbox which must be refined.           */
00053   /*                                                                       */
00054   /* <Input>                                                               */
00055   /*    to   :: A pointer to the destination vector.                       */
00056   /*                                                                       */
00057   /* <InOut>                                                               */
00058   /*    user :: A pointer to the current walk context.                     */
00059   /*                                                                       */
00060   /* <Return>                                                              */
00061   /*    Always 0.  Needed for the interface only.                          */
00062   /*                                                                       */
00063   static int
00064   BBox_Move_To( FT_Vector*  to,
00065                 TBBox_Rec*  user )
00066   {
00067     user->last = *to;
00068 
00069     return 0;
00070   }
00071 
00072 
00073 #define CHECK_X( p, bbox )  \
00074           ( p->x < bbox.xMin || p->x > bbox.xMax )
00075 
00076 #define CHECK_Y( p, bbox )  \
00077           ( p->y < bbox.yMin || p->y > bbox.yMax )
00078 
00079 
00080   /*************************************************************************/
00081   /*                                                                       */
00082   /* <Function>                                                            */
00083   /*    BBox_Conic_Check                                                   */
00084   /*                                                                       */
00085   /* <Description>                                                         */
00086   /*    Finds the extrema of a 1-dimensional conic Bezier curve and update */
00087   /*    a bounding range.  This version uses direct computation, as it     */
00088   /*    doesn't need square roots.                                         */
00089   /*                                                                       */
00090   /* <Input>                                                               */
00091   /*    y1  :: The start coordinate.                                       */
00092   /*                                                                       */
00093   /*    y2  :: The coordinate of the control point.                        */
00094   /*                                                                       */
00095   /*    y3  :: The end coordinate.                                         */
00096   /*                                                                       */
00097   /* <InOut>                                                               */
00098   /*    min :: The address of the current minimum.                         */
00099   /*                                                                       */
00100   /*    max :: The address of the current maximum.                         */
00101   /*                                                                       */
00102   static void
00103   BBox_Conic_Check( FT_Pos   y1,
00104                     FT_Pos   y2,
00105                     FT_Pos   y3,
00106                     FT_Pos*  min,
00107                     FT_Pos*  max )
00108   {
00109     if ( y1 <= y3 && y2 == y1 )     /* flat arc */
00110       goto Suite;
00111 
00112     if ( y1 < y3 )
00113     {
00114       if ( y2 >= y1 && y2 <= y3 )   /* ascending arc */
00115         goto Suite;
00116     }
00117     else
00118     {
00119       if ( y2 >= y3 && y2 <= y1 )   /* descending arc */
00120       {
00121         y2 = y1;
00122         y1 = y3;
00123         y3 = y2;
00124         goto Suite;
00125       }
00126     }
00127 
00128     y1 = y3 = y1 - FT_MulDiv( y2 - y1, y2 - y1, y1 - 2*y2 + y3 );
00129 
00130   Suite:
00131     if ( y1 < *min ) *min = y1;
00132     if ( y3 > *max ) *max = y3;
00133   }
00134 
00135 
00136   /*************************************************************************/
00137   /*                                                                       */
00138   /* <Function>                                                            */
00139   /*    BBox_Conic_To                                                      */
00140   /*                                                                       */
00141   /* <Description>                                                         */
00142   /*    This function is used as a `conic_to' emitter during               */
00143   /*    FT_Outline_Decompose().  It checks a conic Bezier curve with the   */
00144   /*    current bounding box, and computes its extrema if necessary to     */
00145   /*    update it.                                                         */
00146   /*                                                                       */
00147   /* <Input>                                                               */
00148   /*    control :: A pointer to a control point.                           */
00149   /*                                                                       */
00150   /*    to      :: A pointer to the destination vector.                    */
00151   /*                                                                       */
00152   /* <InOut>                                                               */
00153   /*    user    :: The address of the current walk context.                */
00154   /*                                                                       */
00155   /* <Return>                                                              */
00156   /*    Always 0.  Needed for the interface only.                          */
00157   /*                                                                       */
00158   /* <Note>                                                                */
00159   /*    In the case of a non-monotonous arc, we compute directly the       */
00160   /*    extremum coordinates, as it is sufficiently fast.                  */
00161   /*                                                                       */
00162   static int
00163   BBox_Conic_To( FT_Vector*  control,
00164                  FT_Vector*  to,
00165                  TBBox_Rec*  user )
00166   {
00167     /* we don't need to check `to' since it is always an `on' point, thus */
00168     /* within the bbox                                                    */
00169 
00170     if ( CHECK_X( control, user->bbox ) )
00171       BBox_Conic_Check( user->last.x,
00172                         control->x,
00173                         to->x,
00174                         &user->bbox.xMin,
00175                         &user->bbox.xMax );
00176 
00177     if ( CHECK_Y( control, user->bbox ) )
00178       BBox_Conic_Check( user->last.y,
00179                         control->y,
00180                         to->y,
00181                         &user->bbox.yMin,
00182                         &user->bbox.yMax );
00183 
00184     user->last = *to;
00185 
00186     return 0;
00187   }
00188 
00189 
00190   /*************************************************************************/
00191   /*                                                                       */
00192   /* <Function>                                                            */
00193   /*    BBox_Cubic_Check                                                   */
00194   /*                                                                       */
00195   /* <Description>                                                         */
00196   /*    Finds the extrema of a 1-dimensional cubic Bezier curve and        */
00197   /*    updates a bounding range.  This version uses splitting because we  */
00198   /*    don't want to use square roots and extra accuracy.                 */
00199   /*                                                                       */
00200   /* <Input>                                                               */
00201   /*    p1  :: The start coordinate.                                       */
00202   /*                                                                       */
00203   /*    p2  :: The coordinate of the first control point.                  */
00204   /*                                                                       */
00205   /*    p3  :: The coordinate of the second control point.                 */
00206   /*                                                                       */
00207   /*    p4  :: The end coordinate.                                         */
00208   /*                                                                       */
00209   /* <InOut>                                                               */
00210   /*    min :: The address of the current minimum.                         */
00211   /*                                                                       */
00212   /*    max :: The address of the current maximum.                         */
00213   /*                                                                       */
00214 
00215 #if 0
00216 
00217   static void
00218   BBox_Cubic_Check( FT_Pos   p1,
00219                     FT_Pos   p2,
00220                     FT_Pos   p3,
00221                     FT_Pos   p4,
00222                     FT_Pos*  min,
00223                     FT_Pos*  max )
00224   {
00225     FT_Pos  stack[32*3 + 1], *arc;
00226 
00227 
00228     arc = stack;
00229 
00230     arc[0] = p1;
00231     arc[1] = p2;
00232     arc[2] = p3;
00233     arc[3] = p4;
00234 
00235     do
00236     {
00237       FT_Pos  y1 = arc[0];
00238       FT_Pos  y2 = arc[1];
00239       FT_Pos  y3 = arc[2];
00240       FT_Pos  y4 = arc[3];
00241 
00242 
00243       if ( y1 == y4 )
00244       {
00245         if ( y1 == y2 && y1 == y3 )                         /* flat */
00246           goto Test;
00247       }
00248       else if ( y1 < y4 )
00249       {
00250         if ( y2 >= y1 && y2 <= y4 && y3 >= y1 && y3 <= y4 ) /* ascending */
00251           goto Test;
00252       }
00253       else
00254       {
00255         if ( y2 >= y4 && y2 <= y1 && y3 >= y4 && y3 <= y1 ) /* descending */
00256         {
00257           y2 = y1;
00258           y1 = y4;
00259           y4 = y2;
00260           goto Test;
00261         }
00262       }
00263 
00264       /* unknown direction -- split the arc in two */
00265       arc[6] = y4;
00266       arc[1] = y1 = ( y1 + y2 ) / 2;
00267       arc[5] = y4 = ( y4 + y3 ) / 2;
00268       y2 = ( y2 + y3 ) / 2;
00269       arc[2] = y1 = ( y1 + y2 ) / 2;
00270       arc[4] = y4 = ( y4 + y2 ) / 2;
00271       arc[3] = ( y1 + y4 ) / 2;
00272 
00273       arc += 3;
00274       goto Suite;
00275 
00276    Test:
00277       if ( y1 < *min ) *min = y1;
00278       if ( y4 > *max ) *max = y4;
00279       arc -= 3;
00280 
00281     Suite:
00282       ;
00283     } while ( arc >= stack );
00284   }
00285 
00286 #else
00287 
00288   static void
00289   test_cubic_extrema( FT_Pos    y1,
00290                       FT_Pos    y2,
00291                       FT_Pos    y3,
00292                       FT_Pos    y4,
00293                       FT_Fixed  u,
00294                       FT_Pos*   min,
00295                       FT_Pos*   max )
00296   {
00297  /* FT_Pos    a = y4 - 3*y3 + 3*y2 - y1; */
00298     FT_Pos    b = y3 - 2*y2 + y1;
00299     FT_Pos    c = y2 - y1;
00300     FT_Pos    d = y1;
00301     FT_Pos    y;
00302     FT_Fixed  uu;
00303 
00304     FT_UNUSED ( y4 );
00305 
00306 
00307     /* The polynomial is                      */
00308     /*                                        */
00309     /*    P(x) = a*x^3 + 3b*x^2 + 3c*x + d  , */
00310     /*                                        */
00311     /*   dP/dx = 3a*x^2 + 6b*x + 3c         . */
00312     /*                                        */
00313     /* However, we also have                  */
00314     /*                                        */
00315     /*   dP/dx(u) = 0                       , */
00316     /*                                        */
00317     /* which implies by subtraction that      */
00318     /*                                        */
00319     /*   P(u) = b*u^2 + 2c*u + d            . */
00320 
00321     if ( u > 0 && u < 0x10000L )
00322     {
00323       uu = FT_MulFix( u, u );
00324       y  = d + FT_MulFix( c, 2*u ) + FT_MulFix( b, uu );
00325 
00326       if ( y < *min ) *min = y;
00327       if ( y > *max ) *max = y;
00328     }
00329   }
00330 
00331 
00332   static void
00333   BBox_Cubic_Check( FT_Pos   y1,
00334                     FT_Pos   y2,
00335                     FT_Pos   y3,
00336                     FT_Pos   y4,
00337                     FT_Pos*  min,
00338                     FT_Pos*  max )
00339   {
00340     /* always compare first and last points */
00341     if      ( y1 < *min )  *min = y1;
00342     else if ( y1 > *max )  *max = y1;
00343 
00344     if      ( y4 < *min )  *min = y4;
00345     else if ( y4 > *max )  *max = y4;
00346 
00347     /* now, try to see if there are split points here */
00348     if ( y1 <= y4 )
00349     {
00350       /* flat or ascending arc test */
00351       if ( y1 <= y2 && y2 <= y4 && y1 <= y3 && y3 <= y4 )
00352         return;
00353     }
00354     else /* y1 > y4 */
00355     {
00356       /* descending arc test */
00357       if ( y1 >= y2 && y2 >= y4 && y1 >= y3 && y3 >= y4 )
00358         return;
00359     }
00360 
00361     /* There are some split points.  Find them. */
00362     {
00363       FT_Pos    a = y4 - 3*y3 + 3*y2 - y1;
00364       FT_Pos    b = y3 - 2*y2 + y1;
00365       FT_Pos    c = y2 - y1;
00366       FT_Pos    d;
00367       FT_Fixed  t;
00368 
00369 
00370       /* We need to solve `ax^2+2bx+c' here, without floating points!      */
00371       /* The trick is to normalize to a different representation in order  */
00372       /* to use our 16.16 fixed point routines.                            */
00373       /*                                                                   */
00374       /* We compute FT_MulFix(b,b) and FT_MulFix(a,c) after normalization. */
00375       /* These values must fit into a single 16.16 value.                  */
00376       /*                                                                   */
00377       /* We normalize a, b, and c to `8.16' fixed float values to ensure   */
00378       /* that its product is held in a `16.16' value.                      */
00379 
00380       {
00381         FT_ULong  t1, t2;
00382         int       shift = 0;
00383 
00384 
00385         /* The following computation is based on the fact that for   */
00386         /* any value `y', if `n' is the position of the most         */
00387         /* significant bit of `abs(y)' (starting from 0 for the      */
00388         /* least significant bit), then `y' is in the range          */
00389         /*                                                           */
00390         /*   -2^n..2^n-1                                             */
00391         /*                                                           */
00392         /* We want to shift `a', `b', and `c' concurrently in order  */
00393         /* to ensure that they all fit in 8.16 values, which maps    */
00394         /* to the integer range `-2^23..2^23-1'.                     */
00395         /*                                                           */
00396         /* Necessarily, we need to shift `a', `b', and `c' so that   */
00397         /* the most significant bit of its absolute values is at     */
00398         /* _most_ at position 23.                                    */
00399         /*                                                           */
00400         /* We begin by computing `t1' as the bitwise `OR' of the     */
00401         /* absolute values of `a', `b', `c'.                         */
00402 
00403         t1  = (FT_ULong)( ( a >= 0 ) ? a : -a );
00404         t2  = (FT_ULong)( ( b >= 0 ) ? b : -b );
00405         t1 |= t2;
00406         t2  = (FT_ULong)( ( c >= 0 ) ? c : -c );
00407         t1 |= t2;
00408 
00409         /* Now we can be sure that the most significant bit of `t1'  */
00410         /* is the most significant bit of either `a', `b', or `c',   */
00411         /* depending on the greatest integer range of the particular */
00412         /* variable.                                                 */
00413         /*                                                           */
00414         /* Next, we compute the `shift', by shifting `t1' as many    */
00415         /* times as necessary to move its MSB to position 23.  This  */
00416         /* corresponds to a value of `t1' that is in the range       */
00417         /* 0x40_0000..0x7F_FFFF.                                     */
00418         /*                                                           */
00419         /* Finally, we shift `a', `b', and `c' by the same amount.   */
00420         /* This ensures that all values are now in the range         */
00421         /* -2^23..2^23, i.e., they are now expressed as 8.16         */
00422         /* fixed-float numbers.  This also means that we are using   */
00423         /* 24 bits of precision to compute the zeros, independently  */
00424         /* of the range of the original polynomial coefficients.     */
00425         /*                                                           */
00426         /* This algorithm should ensure reasonably accurate values   */
00427         /* for the zeros.  Note that they are only expressed with    */
00428         /* 16 bits when computing the extrema (the zeros need to     */
00429         /* be in 0..1 exclusive to be considered part of the arc).   */
00430 
00431         if ( t1 == 0 )  /* all coefficients are 0! */
00432           return;
00433 
00434         if ( t1 > 0x7FFFFFUL )
00435         {
00436           do
00437           {
00438             shift++;
00439             t1 >>= 1;
00440 
00441           } while ( t1 > 0x7FFFFFUL );
00442 
00443           /* this loses some bits of precision, but we use 24 of them */
00444           /* for the computation anyway                               */
00445           a >>= shift;
00446           b >>= shift;
00447           c >>= shift;
00448         }
00449         else if ( t1 < 0x400000UL )
00450         {
00451           do
00452           {
00453             shift++;
00454             t1 <<= 1;
00455 
00456           } while ( t1 < 0x400000UL );
00457 
00458           a <<= shift;
00459           b <<= shift;
00460           c <<= shift;
00461         }
00462       }
00463 
00464       /* handle a == 0 */
00465       if ( a == 0 )
00466       {
00467         if ( b != 0 )
00468         {
00469           t = - FT_DivFix( c, b ) / 2;
00470           test_cubic_extrema( y1, y2, y3, y4, t, min, max );
00471         }
00472       }
00473       else
00474       {
00475         /* solve the equation now */
00476         d = FT_MulFix( b, b ) - FT_MulFix( a, c );
00477         if ( d < 0 )
00478           return;
00479 
00480         if ( d == 0 )
00481         {
00482           /* there is a single split point at -b/a */
00483           t = - FT_DivFix( b, a );
00484           test_cubic_extrema( y1, y2, y3, y4, t, min, max );
00485         }
00486         else
00487         {
00488           /* there are two solutions; we need to filter them */
00489           d = FT_SqrtFixed( (FT_Int32)d );
00490           t = - FT_DivFix( b - d, a );
00491           test_cubic_extrema( y1, y2, y3, y4, t, min, max );
00492 
00493           t = - FT_DivFix( b + d, a );
00494           test_cubic_extrema( y1, y2, y3, y4, t, min, max );
00495         }
00496       }
00497     }
00498   }
00499 
00500 #endif
00501 
00502 
00503   /*************************************************************************/
00504   /*                                                                       */
00505   /* <Function>                                                            */
00506   /*    BBox_Cubic_To                                                      */
00507   /*                                                                       */
00508   /* <Description>                                                         */
00509   /*    This function is used as a `cubic_to' emitter during               */
00510   /*    FT_Outline_Decompose().  It checks a cubic Bezier curve with the   */
00511   /*    current bounding box, and computes its extrema if necessary to     */
00512   /*    update it.                                                         */
00513   /*                                                                       */
00514   /* <Input>                                                               */
00515   /*    control1 :: A pointer to the first control point.                  */
00516   /*                                                                       */
00517   /*    control2 :: A pointer to the second control point.                 */
00518   /*                                                                       */
00519   /*    to       :: A pointer to the destination vector.                   */
00520   /*                                                                       */
00521   /* <InOut>                                                               */
00522   /*    user     :: The address of the current walk context.               */
00523   /*                                                                       */
00524   /* <Return>                                                              */
00525   /*    Always 0.  Needed for the interface only.                          */
00526   /*                                                                       */
00527   /* <Note>                                                                */
00528   /*    In the case of a non-monotonous arc, we don't compute directly     */
00529   /*    extremum coordinates, we subdivide instead.                        */
00530   /*                                                                       */
00531   static int
00532   BBox_Cubic_To( FT_Vector*  control1,
00533                  FT_Vector*  control2,
00534                  FT_Vector*  to,
00535                  TBBox_Rec*  user )
00536   {
00537     /* we don't need to check `to' since it is always an `on' point, thus */
00538     /* within the bbox                                                    */
00539 
00540     if ( CHECK_X( control1, user->bbox ) ||
00541          CHECK_X( control2, user->bbox ) )
00542       BBox_Cubic_Check( user->last.x,
00543                         control1->x,
00544                         control2->x,
00545                         to->x,
00546                         &user->bbox.xMin,
00547                         &user->bbox.xMax );
00548 
00549     if ( CHECK_Y( control1, user->bbox ) ||
00550          CHECK_Y( control2, user->bbox ) )
00551       BBox_Cubic_Check( user->last.y,
00552                         control1->y,
00553                         control2->y,
00554                         to->y,
00555                         &user->bbox.yMin,
00556                         &user->bbox.yMax );
00557 
00558     user->last = *to;
00559 
00560     return 0;
00561   }
00562 
00563 FT_DEFINE_OUTLINE_FUNCS(bbox_interface,
00564     (FT_Outline_MoveTo_Func) BBox_Move_To,
00565     (FT_Outline_LineTo_Func) BBox_Move_To,
00566     (FT_Outline_ConicTo_Func)BBox_Conic_To,
00567     (FT_Outline_CubicTo_Func)BBox_Cubic_To,
00568     0, 0
00569   )
00570 
00571   /* documentation is in ftbbox.h */
00572 
00573   FT_EXPORT_DEF( FT_Error )
00574   FT_Outline_Get_BBox( FT_Outline*  outline,
00575                        FT_BBox     *abbox )
00576   {
00577     FT_BBox     cbox;
00578     FT_BBox     bbox;
00579     FT_Vector*  vec;
00580     FT_UShort   n;
00581 
00582 
00583     if ( !abbox )
00584       return FT_Err_Invalid_Argument;
00585 
00586     if ( !outline )
00587       return FT_Err_Invalid_Outline;
00588 
00589     /* if outline is empty, return (0,0,0,0) */
00590     if ( outline->n_points == 0 || outline->n_contours <= 0 )
00591     {
00592       abbox->xMin = abbox->xMax = 0;
00593       abbox->yMin = abbox->yMax = 0;
00594       return 0;
00595     }
00596 
00597     /* We compute the control box as well as the bounding box of  */
00598     /* all `on' points in the outline.  Then, if the two boxes    */
00599     /* coincide, we exit immediately.                             */
00600 
00601     vec = outline->points;
00602     bbox.xMin = bbox.xMax = cbox.xMin = cbox.xMax = vec->x;
00603     bbox.yMin = bbox.yMax = cbox.yMin = cbox.yMax = vec->y;
00604     vec++;
00605 
00606     for ( n = 1; n < outline->n_points; n++ )
00607     {
00608       FT_Pos  x = vec->x;
00609       FT_Pos  y = vec->y;
00610 
00611 
00612       /* update control box */
00613       if ( x < cbox.xMin ) cbox.xMin = x;
00614       if ( x > cbox.xMax ) cbox.xMax = x;
00615 
00616       if ( y < cbox.yMin ) cbox.yMin = y;
00617       if ( y > cbox.yMax ) cbox.yMax = y;
00618 
00619       if ( FT_CURVE_TAG( outline->tags[n] ) == FT_CURVE_TAG_ON )
00620       {
00621         /* update bbox for `on' points only */
00622         if ( x < bbox.xMin ) bbox.xMin = x;
00623         if ( x > bbox.xMax ) bbox.xMax = x;
00624 
00625         if ( y < bbox.yMin ) bbox.yMin = y;
00626         if ( y > bbox.yMax ) bbox.yMax = y;
00627       }
00628 
00629       vec++;
00630     }
00631 
00632     /* test two boxes for equality */
00633     if ( cbox.xMin < bbox.xMin || cbox.xMax > bbox.xMax ||
00634          cbox.yMin < bbox.yMin || cbox.yMax > bbox.yMax )
00635     {
00636       /* the two boxes are different, now walk over the outline to */
00637       /* get the Bezier arc extrema.                               */
00638 
00639       FT_Error   error;
00640       TBBox_Rec  user;
00641 
00642 #ifdef FT_CONFIG_OPTION_PIC
00643       FT_Outline_Funcs bbox_interface;
00644       Init_Class_bbox_interface(&bbox_interface);
00645 #endif
00646 
00647       user.bbox = bbox;
00648 
00649       error = FT_Outline_Decompose( outline, &bbox_interface, &user );
00650       if ( error )
00651         return error;
00652 
00653       *abbox = user.bbox;
00654     }
00655     else
00656       *abbox = bbox;
00657 
00658     return FT_Err_Ok;
00659   }
00660 
00661 
00662 /* END */

Generated on Tue Jul 5 14:13:42 2011 for ROOT_528-00b_version by  doxygen 1.5.1