(root)/
freetype-2.13.2/
src/
base/
ftbbox.c
       1  /****************************************************************************
       2   *
       3   * ftbbox.c
       4   *
       5   *   FreeType bbox computation (body).
       6   *
       7   * Copyright (C) 1996-2023 by
       8   * David Turner, Robert Wilhelm, and Werner Lemberg.
       9   *
      10   * This file is part of the FreeType project, and may only be used
      11   * modified and distributed under the terms of the FreeType project
      12   * license, LICENSE.TXT.  By continuing to use, modify, or distribute
      13   * this file you indicate that you have read the license and
      14   * understand and accept it fully.
      15   *
      16   */
      17  
      18  
      19    /**************************************************************************
      20     *
      21     * This component has a _single_ role: to compute exact outline bounding
      22     * boxes.
      23     *
      24     */
      25  
      26  
      27  #include <freetype/internal/ftdebug.h>
      28  
      29  #include <freetype/ftbbox.h>
      30  #include <freetype/ftimage.h>
      31  #include <freetype/ftoutln.h>
      32  #include <freetype/internal/ftcalc.h>
      33  #include <freetype/internal/ftobjs.h>
      34  
      35  
      36    typedef struct  TBBox_Rec_
      37    {
      38      FT_Vector  last;
      39      FT_BBox    bbox;
      40  
      41    } TBBox_Rec;
      42  
      43  
      44  #define FT_UPDATE_BBOX( p, bbox ) \
      45    FT_BEGIN_STMNT                  \
      46      if ( p->x < bbox.xMin )       \
      47        bbox.xMin = p->x;           \
      48      if ( p->x > bbox.xMax )       \
      49        bbox.xMax = p->x;           \
      50      if ( p->y < bbox.yMin )       \
      51        bbox.yMin = p->y;           \
      52      if ( p->y > bbox.yMax )       \
      53        bbox.yMax = p->y;           \
      54    FT_END_STMNT
      55  
      56  #define CHECK_X( p, bbox )                         \
      57            ( p->x < bbox.xMin || p->x > bbox.xMax )
      58  
      59  #define CHECK_Y( p, bbox )                         \
      60            ( p->y < bbox.yMin || p->y > bbox.yMax )
      61  
      62  
      63    /**************************************************************************
      64     *
      65     * @Function:
      66     *   BBox_Move_To
      67     *
      68     * @Description:
      69     *   This function is used as a `move_to' emitter during
      70     *   FT_Outline_Decompose().  It simply records the destination point
      71     *   in `user->last'. We also update bbox in case contour starts with
      72     *   an implicit `on' point.
      73     *
      74     * @Input:
      75     *   to ::
      76     *     A pointer to the destination vector.
      77     *
      78     * @InOut:
      79     *   user ::
      80     *     A pointer to the current walk context.
      81     *
      82     * @Return:
      83     *   Always 0.  Needed for the interface only.
      84     */
      85    FT_CALLBACK_DEF( int )
      86    BBox_Move_To( const FT_Vector*  to,
      87                  void*             user_ )
      88    {
      89      TBBox_Rec*  user = (TBBox_Rec*)user_;
      90  
      91  
      92      FT_UPDATE_BBOX( to, user->bbox );
      93  
      94      user->last = *to;
      95  
      96      return 0;
      97    }
      98  
      99  
     100    /**************************************************************************
     101     *
     102     * @Function:
     103     *   BBox_Line_To
     104     *
     105     * @Description:
     106     *   This function is used as a `line_to' emitter during
     107     *   FT_Outline_Decompose().  It simply records the destination point
     108     *   in `user->last'; no further computations are necessary because
     109     *   bbox already contains both explicit ends of the line segment.
     110     *
     111     * @Input:
     112     *   to ::
     113     *     A pointer to the destination vector.
     114     *
     115     * @InOut:
     116     *   user ::
     117     *     A pointer to the current walk context.
     118     *
     119     * @Return:
     120     *   Always 0.  Needed for the interface only.
     121     */
     122    FT_CALLBACK_DEF( int )
     123    BBox_Line_To( const FT_Vector*  to,
     124                  void*             user_ )
     125    {
     126      TBBox_Rec*  user = (TBBox_Rec*)user_;
     127  
     128  
     129      user->last = *to;
     130  
     131      return 0;
     132    }
     133  
     134  
     135    /**************************************************************************
     136     *
     137     * @Function:
     138     *   BBox_Conic_Check
     139     *
     140     * @Description:
     141     *   Find the extrema of a 1-dimensional conic Bezier curve and update
     142     *   a bounding range.  This version uses direct computation, as it
     143     *   doesn't need square roots.
     144     *
     145     * @Input:
     146     *   y1 ::
     147     *     The start coordinate.
     148     *
     149     *   y2 ::
     150     *     The coordinate of the control point.
     151     *
     152     *   y3 ::
     153     *     The end coordinate.
     154     *
     155     * @InOut:
     156     *   min ::
     157     *     The address of the current minimum.
     158     *
     159     *   max ::
     160     *     The address of the current maximum.
     161     */
     162    static void
     163    BBox_Conic_Check( FT_Pos   y1,
     164                      FT_Pos   y2,
     165                      FT_Pos   y3,
     166                      FT_Pos*  min,
     167                      FT_Pos*  max )
     168    {
     169      /* This function is only called when a control off-point is outside */
     170      /* the bbox that contains all on-points.  It finds a local extremum */
     171      /* within the segment, equal to (y1*y3 - y2*y2)/(y1 - 2*y2 + y3).   */
     172      /* Or, offsetting from y2, we get                                   */
     173  
     174      y1 -= y2;
     175      y3 -= y2;
     176      y2 += FT_MulDiv( y1, y3, y1 + y3 );
     177  
     178      if ( y2 < *min )
     179        *min = y2;
     180      if ( y2 > *max )
     181        *max = y2;
     182    }
     183  
     184  
     185    /**************************************************************************
     186     *
     187     * @Function:
     188     *   BBox_Conic_To
     189     *
     190     * @Description:
     191     *   This function is used as a `conic_to' emitter during
     192     *   FT_Outline_Decompose().  It checks a conic Bezier curve with the
     193     *   current bounding box, and computes its extrema if necessary to
     194     *   update it.
     195     *
     196     * @Input:
     197     *   control ::
     198     *     A pointer to a control point.
     199     *
     200     *   to ::
     201     *     A pointer to the destination vector.
     202     *
     203     * @InOut:
     204     *   user ::
     205     *     The address of the current walk context.
     206     *
     207     * @Return:
     208     *   Always 0.  Needed for the interface only.
     209     *
     210     * @Note:
     211     *   In the case of a non-monotonous arc, we compute directly the
     212     *   extremum coordinates, as it is sufficiently fast.
     213     */
     214    FT_CALLBACK_DEF( int )
     215    BBox_Conic_To( const FT_Vector*  control,
     216                   const FT_Vector*  to,
     217                   void*             user_ )
     218    {
     219      TBBox_Rec*  user = (TBBox_Rec*)user_;
     220  
     221  
     222      /* in case `to' is implicit and not included in bbox yet */
     223      FT_UPDATE_BBOX( to, user->bbox );
     224  
     225      if ( CHECK_X( control, user->bbox ) )
     226        BBox_Conic_Check( user->last.x,
     227                          control->x,
     228                          to->x,
     229                          &user->bbox.xMin,
     230                          &user->bbox.xMax );
     231  
     232      if ( CHECK_Y( control, user->bbox ) )
     233        BBox_Conic_Check( user->last.y,
     234                          control->y,
     235                          to->y,
     236                          &user->bbox.yMin,
     237                          &user->bbox.yMax );
     238  
     239      user->last = *to;
     240  
     241      return 0;
     242    }
     243  
     244  
     245    /**************************************************************************
     246     *
     247     * @Function:
     248     *   BBox_Cubic_Check
     249     *
     250     * @Description:
     251     *   Find the extrema of a 1-dimensional cubic Bezier curve and
     252     *   update a bounding range.  This version uses iterative splitting
     253     *   because it is faster than the exact solution with square roots.
     254     *
     255     * @Input:
     256     *   p1 ::
     257     *     The start coordinate.
     258     *
     259     *   p2 ::
     260     *     The coordinate of the first control point.
     261     *
     262     *   p3 ::
     263     *     The coordinate of the second control point.
     264     *
     265     *   p4 ::
     266     *     The end coordinate.
     267     *
     268     * @InOut:
     269     *   min ::
     270     *     The address of the current minimum.
     271     *
     272     *   max ::
     273     *     The address of the current maximum.
     274     */
     275    static FT_Pos
     276    cubic_peak( FT_Pos  q1,
     277                FT_Pos  q2,
     278                FT_Pos  q3,
     279                FT_Pos  q4 )
     280    {
     281      FT_Pos  peak = 0;
     282      FT_Int  shift;
     283  
     284  
     285      /* This function finds a peak of a cubic segment if it is above 0    */
     286      /* using iterative bisection of the segment, or returns 0.           */
     287      /* The fixed-point arithmetic of bisection is inherently stable      */
     288      /* but may loose accuracy in the two lowest bits.  To compensate,    */
     289      /* we upscale the segment if there is room.  Large values may need   */
     290      /* to be downscaled to avoid overflows during bisection.             */
     291      /* It is called with either q2 or q3 positive, which is necessary    */
     292      /* for the peak to exist and avoids undefined FT_MSB.                */
     293  
     294      shift = 27 - FT_MSB( (FT_UInt32)( FT_ABS( q1 ) |
     295                                        FT_ABS( q2 ) |
     296                                        FT_ABS( q3 ) |
     297                                        FT_ABS( q4 ) ) );
     298  
     299      if ( shift > 0 )
     300      {
     301        /* upscaling too much just wastes time */
     302        if ( shift > 2 )
     303          shift = 2;
     304  
     305        q1 *= 1 << shift;
     306        q2 *= 1 << shift;
     307        q3 *= 1 << shift;
     308        q4 *= 1 << shift;
     309      }
     310      else
     311      {
     312        q1 >>= -shift;
     313        q2 >>= -shift;
     314        q3 >>= -shift;
     315        q4 >>= -shift;
     316      }
     317  
     318      /* for a peak to exist above 0, the cubic segment must have */
     319      /* at least one of its control off-points above 0.          */
     320      while ( q2 > 0 || q3 > 0 )
     321      {
     322        /* determine which half contains the maximum and split */
     323        if ( q1 + q2 > q3 + q4 ) /* first half */
     324        {
     325          q4 = q4 + q3;
     326          q3 = q3 + q2;
     327          q2 = q2 + q1;
     328          q4 = q4 + q3;
     329          q3 = q3 + q2;
     330          q4 = ( q4 + q3 ) >> 3;
     331          q3 = q3 >> 2;
     332          q2 = q2 >> 1;
     333        }
     334        else                     /* second half */
     335        {
     336          q1 = q1 + q2;
     337          q2 = q2 + q3;
     338          q3 = q3 + q4;
     339          q1 = q1 + q2;
     340          q2 = q2 + q3;
     341          q1 = ( q1 + q2 ) >> 3;
     342          q2 = q2 >> 2;
     343          q3 = q3 >> 1;
     344        }
     345  
     346        /* check whether either end reached the maximum */
     347        if ( q1 == q2 && q1 >= q3 )
     348        {
     349          peak = q1;
     350          break;
     351        }
     352        if ( q3 == q4 && q2 <= q4 )
     353        {
     354          peak = q4;
     355          break;
     356        }
     357      }
     358  
     359      if ( shift > 0 )
     360        peak >>=  shift;
     361      else
     362        peak <<= -shift;
     363  
     364      return peak;
     365    }
     366  
     367  
     368    static void
     369    BBox_Cubic_Check( FT_Pos   p1,
     370                      FT_Pos   p2,
     371                      FT_Pos   p3,
     372                      FT_Pos   p4,
     373                      FT_Pos*  min,
     374                      FT_Pos*  max )
     375    {
     376      /* This function is only called when a control off-point is outside  */
     377      /* the bbox that contains all on-points.  So at least one of the     */
     378      /* conditions below holds and cubic_peak is called with at least one */
     379      /* non-zero argument.                                                */
     380  
     381      if ( p2 > *max || p3 > *max )
     382        *max += cubic_peak( p1 - *max, p2 - *max, p3 - *max, p4 - *max );
     383  
     384      /* now flip the signs to update the minimum */
     385      if ( p2 < *min || p3 < *min )
     386        *min -= cubic_peak( *min - p1, *min - p2, *min - p3, *min - p4 );
     387    }
     388  
     389  
     390    /**************************************************************************
     391     *
     392     * @Function:
     393     *   BBox_Cubic_To
     394     *
     395     * @Description:
     396     *   This function is used as a `cubic_to' emitter during
     397     *   FT_Outline_Decompose().  It checks a cubic Bezier curve with the
     398     *   current bounding box, and computes its extrema if necessary to
     399     *   update it.
     400     *
     401     * @Input:
     402     *   control1 ::
     403     *     A pointer to the first control point.
     404     *
     405     *   control2 ::
     406     *     A pointer to the second control point.
     407     *
     408     *   to ::
     409     *     A pointer to the destination vector.
     410     *
     411     * @InOut:
     412     *   user ::
     413     *     The address of the current walk context.
     414     *
     415     * @Return:
     416     *   Always 0.  Needed for the interface only.
     417     *
     418     * @Note:
     419     *   In the case of a non-monotonous arc, we don't compute directly
     420     *   extremum coordinates, we subdivide instead.
     421     */
     422    FT_CALLBACK_DEF( int )
     423    BBox_Cubic_To( const FT_Vector*  control1,
     424                   const FT_Vector*  control2,
     425                   const FT_Vector*  to,
     426                   void*             user_ )
     427    {
     428      TBBox_Rec*  user = (TBBox_Rec*)user_;
     429  
     430  
     431      /* We don't need to check `to' since it is always an on-point,    */
     432      /* thus within the bbox.  Only segments with an off-point outside */
     433      /* the bbox can possibly reach new extreme values.                */
     434  
     435      if ( CHECK_X( control1, user->bbox ) ||
     436           CHECK_X( control2, user->bbox ) )
     437        BBox_Cubic_Check( user->last.x,
     438                          control1->x,
     439                          control2->x,
     440                          to->x,
     441                          &user->bbox.xMin,
     442                          &user->bbox.xMax );
     443  
     444      if ( CHECK_Y( control1, user->bbox ) ||
     445           CHECK_Y( control2, user->bbox ) )
     446        BBox_Cubic_Check( user->last.y,
     447                          control1->y,
     448                          control2->y,
     449                          to->y,
     450                          &user->bbox.yMin,
     451                          &user->bbox.yMax );
     452  
     453      user->last = *to;
     454  
     455      return 0;
     456    }
     457  
     458  
     459    FT_DEFINE_OUTLINE_FUNCS(
     460      bbox_interface,
     461  
     462      (FT_Outline_MoveTo_Func) BBox_Move_To,   /* move_to  */
     463      (FT_Outline_LineTo_Func) BBox_Line_To,   /* line_to  */
     464      (FT_Outline_ConicTo_Func)BBox_Conic_To,  /* conic_to */
     465      (FT_Outline_CubicTo_Func)BBox_Cubic_To,  /* cubic_to */
     466      0,                                       /* shift    */
     467      0                                        /* delta    */
     468    )
     469  
     470  
     471    /* documentation is in ftbbox.h */
     472  
     473    FT_EXPORT_DEF( FT_Error )
     474    FT_Outline_Get_BBox( FT_Outline*  outline,
     475                         FT_BBox     *abbox )
     476    {
     477      FT_BBox     cbox = {  0x7FFFFFFFL,  0x7FFFFFFFL,
     478                           -0x7FFFFFFFL, -0x7FFFFFFFL };
     479      FT_BBox     bbox = {  0x7FFFFFFFL,  0x7FFFFFFFL,
     480                           -0x7FFFFFFFL, -0x7FFFFFFFL };
     481      FT_Vector*  vec;
     482      FT_UShort   n;
     483  
     484  
     485      if ( !abbox )
     486        return FT_THROW( Invalid_Argument );
     487  
     488      if ( !outline )
     489        return FT_THROW( Invalid_Outline );
     490  
     491      /* if outline is empty, return (0,0,0,0) */
     492      if ( outline->n_points == 0 || outline->n_contours <= 0 )
     493      {
     494        abbox->xMin = abbox->xMax = 0;
     495        abbox->yMin = abbox->yMax = 0;
     496  
     497        return 0;
     498      }
     499  
     500      /* We compute the control box as well as the bounding box of  */
     501      /* all `on' points in the outline.  Then, if the two boxes    */
     502      /* coincide, we exit immediately.                             */
     503  
     504      vec = outline->points;
     505  
     506      for ( n = 0; n < outline->n_points; n++ )
     507      {
     508        FT_UPDATE_BBOX( vec, cbox );
     509  
     510        if ( FT_CURVE_TAG( outline->tags[n] ) == FT_CURVE_TAG_ON )
     511          FT_UPDATE_BBOX( vec, bbox );
     512  
     513        vec++;
     514      }
     515  
     516      /* test two boxes for equality */
     517      if ( cbox.xMin < bbox.xMin || cbox.xMax > bbox.xMax ||
     518           cbox.yMin < bbox.yMin || cbox.yMax > bbox.yMax )
     519      {
     520        /* the two boxes are different, now walk over the outline to */
     521        /* get the Bezier arc extrema.                               */
     522  
     523        FT_Error   error;
     524        TBBox_Rec  user;
     525  
     526  
     527        user.bbox = bbox;
     528  
     529        error = FT_Outline_Decompose( outline, &bbox_interface, &user );
     530        if ( error )
     531          return error;
     532  
     533        *abbox = user.bbox;
     534      }
     535      else
     536        *abbox = bbox;
     537  
     538      return FT_Err_Ok;
     539    }
     540  
     541  
     542  /* END */