• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ftbbox.c                                                               */
4 /*                                                                         */
5 /*    FreeType bbox computation (body).                                    */
6 /*                                                                         */
7 /*  Copyright 1996-2001, 2002, 2004, 2006, 2010 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 <ft2build.h>
28 #include FT_BBOX_H
29 #include FT_IMAGE_H
30 #include FT_OUTLINE_H
31 #include FT_INTERNAL_CALC_H
32 #include FT_INTERNAL_OBJECTS_H
33 
34 
35   typedef struct  TBBox_Rec_
36   {
37     FT_Vector  last;
38     FT_BBox    bbox;
39 
40   } TBBox_Rec;
41 
42 
43   /*************************************************************************/
44   /*                                                                       */
45   /* <Function>                                                            */
46   /*    BBox_Move_To                                                       */
47   /*                                                                       */
48   /* <Description>                                                         */
49   /*    This function is used as a `move_to' and `line_to' emitter during  */
50   /*    FT_Outline_Decompose().  It simply records the destination point   */
51   /*    in `user->last'; no further computations are necessary since we    */
52   /*    use the cbox as the starting bbox which must be refined.           */
53   /*                                                                       */
54   /* <Input>                                                               */
55   /*    to   :: A pointer to the destination vector.                       */
56   /*                                                                       */
57   /* <InOut>                                                               */
58   /*    user :: A pointer to the current walk context.                     */
59   /*                                                                       */
60   /* <Return>                                                              */
61   /*    Always 0.  Needed for the interface only.                          */
62   /*                                                                       */
63   static int
BBox_Move_To(FT_Vector * to,TBBox_Rec * user)64   BBox_Move_To( FT_Vector*  to,
65                 TBBox_Rec*  user )
66   {
67     user->last = *to;
68 
69     return 0;
70   }
71 
72 
73 #define CHECK_X( p, bbox )  \
74           ( p->x < bbox.xMin || p->x > bbox.xMax )
75 
76 #define CHECK_Y( p, bbox )  \
77           ( p->y < bbox.yMin || p->y > bbox.yMax )
78 
79 
80   /*************************************************************************/
81   /*                                                                       */
82   /* <Function>                                                            */
83   /*    BBox_Conic_Check                                                   */
84   /*                                                                       */
85   /* <Description>                                                         */
86   /*    Finds the extrema of a 1-dimensional conic Bezier curve and update */
87   /*    a bounding range.  This version uses direct computation, as it     */
88   /*    doesn't need square roots.                                         */
89   /*                                                                       */
90   /* <Input>                                                               */
91   /*    y1  :: The start coordinate.                                       */
92   /*                                                                       */
93   /*    y2  :: The coordinate of the control point.                        */
94   /*                                                                       */
95   /*    y3  :: The end coordinate.                                         */
96   /*                                                                       */
97   /* <InOut>                                                               */
98   /*    min :: The address of the current minimum.                         */
99   /*                                                                       */
100   /*    max :: The address of the current maximum.                         */
101   /*                                                                       */
102   static void
BBox_Conic_Check(FT_Pos y1,FT_Pos y2,FT_Pos y3,FT_Pos * min,FT_Pos * max)103   BBox_Conic_Check( FT_Pos   y1,
104                     FT_Pos   y2,
105                     FT_Pos   y3,
106                     FT_Pos*  min,
107                     FT_Pos*  max )
108   {
109     if ( y1 <= y3 && y2 == y1 )     /* flat arc */
110       goto Suite;
111 
112     if ( y1 < y3 )
113     {
114       if ( y2 >= y1 && y2 <= y3 )   /* ascending arc */
115         goto Suite;
116     }
117     else
118     {
119       if ( y2 >= y3 && y2 <= y1 )   /* descending arc */
120       {
121         y2 = y1;
122         y1 = y3;
123         y3 = y2;
124         goto Suite;
125       }
126     }
127 
128     y1 = y3 = y1 - FT_MulDiv( y2 - y1, y2 - y1, y1 - 2*y2 + y3 );
129 
130   Suite:
131     if ( y1 < *min ) *min = y1;
132     if ( y3 > *max ) *max = y3;
133   }
134 
135 
136   /*************************************************************************/
137   /*                                                                       */
138   /* <Function>                                                            */
139   /*    BBox_Conic_To                                                      */
140   /*                                                                       */
141   /* <Description>                                                         */
142   /*    This function is used as a `conic_to' emitter during               */
143   /*    FT_Outline_Decompose().  It checks a conic Bezier curve with the   */
144   /*    current bounding box, and computes its extrema if necessary to     */
145   /*    update it.                                                         */
146   /*                                                                       */
147   /* <Input>                                                               */
148   /*    control :: A pointer to a control point.                           */
149   /*                                                                       */
150   /*    to      :: A pointer to the destination vector.                    */
151   /*                                                                       */
152   /* <InOut>                                                               */
153   /*    user    :: The address of the current walk context.                */
154   /*                                                                       */
155   /* <Return>                                                              */
156   /*    Always 0.  Needed for the interface only.                          */
157   /*                                                                       */
158   /* <Note>                                                                */
159   /*    In the case of a non-monotonous arc, we compute directly the       */
160   /*    extremum coordinates, as it is sufficiently fast.                  */
161   /*                                                                       */
162   static int
BBox_Conic_To(FT_Vector * control,FT_Vector * to,TBBox_Rec * user)163   BBox_Conic_To( FT_Vector*  control,
164                  FT_Vector*  to,
165                  TBBox_Rec*  user )
166   {
167     /* we don't need to check `to' since it is always an `on' point, thus */
168     /* within the bbox                                                    */
169 
170     if ( CHECK_X( control, user->bbox ) )
171       BBox_Conic_Check( user->last.x,
172                         control->x,
173                         to->x,
174                         &user->bbox.xMin,
175                         &user->bbox.xMax );
176 
177     if ( CHECK_Y( control, user->bbox ) )
178       BBox_Conic_Check( user->last.y,
179                         control->y,
180                         to->y,
181                         &user->bbox.yMin,
182                         &user->bbox.yMax );
183 
184     user->last = *to;
185 
186     return 0;
187   }
188 
189 
190   /*************************************************************************/
191   /*                                                                       */
192   /* <Function>                                                            */
193   /*    BBox_Cubic_Check                                                   */
194   /*                                                                       */
195   /* <Description>                                                         */
196   /*    Finds the extrema of a 1-dimensional cubic Bezier curve and        */
197   /*    updates a bounding range.  This version uses splitting because we  */
198   /*    don't want to use square roots and extra accuracy.                 */
199   /*                                                                       */
200   /* <Input>                                                               */
201   /*    p1  :: The start coordinate.                                       */
202   /*                                                                       */
203   /*    p2  :: The coordinate of the first control point.                  */
204   /*                                                                       */
205   /*    p3  :: The coordinate of the second control point.                 */
206   /*                                                                       */
207   /*    p4  :: The end coordinate.                                         */
208   /*                                                                       */
209   /* <InOut>                                                               */
210   /*    min :: The address of the current minimum.                         */
211   /*                                                                       */
212   /*    max :: The address of the current maximum.                         */
213   /*                                                                       */
214 
215 #if 0
216 
217   static void
218   BBox_Cubic_Check( FT_Pos   p1,
219                     FT_Pos   p2,
220                     FT_Pos   p3,
221                     FT_Pos   p4,
222                     FT_Pos*  min,
223                     FT_Pos*  max )
224   {
225     FT_Pos  stack[32*3 + 1], *arc;
226 
227 
228     arc = stack;
229 
230     arc[0] = p1;
231     arc[1] = p2;
232     arc[2] = p3;
233     arc[3] = p4;
234 
235     do
236     {
237       FT_Pos  y1 = arc[0];
238       FT_Pos  y2 = arc[1];
239       FT_Pos  y3 = arc[2];
240       FT_Pos  y4 = arc[3];
241 
242 
243       if ( y1 == y4 )
244       {
245         if ( y1 == y2 && y1 == y3 )                         /* flat */
246           goto Test;
247       }
248       else if ( y1 < y4 )
249       {
250         if ( y2 >= y1 && y2 <= y4 && y3 >= y1 && y3 <= y4 ) /* ascending */
251           goto Test;
252       }
253       else
254       {
255         if ( y2 >= y4 && y2 <= y1 && y3 >= y4 && y3 <= y1 ) /* descending */
256         {
257           y2 = y1;
258           y1 = y4;
259           y4 = y2;
260           goto Test;
261         }
262       }
263 
264       /* unknown direction -- split the arc in two */
265       arc[6] = y4;
266       arc[1] = y1 = ( y1 + y2 ) / 2;
267       arc[5] = y4 = ( y4 + y3 ) / 2;
268       y2 = ( y2 + y3 ) / 2;
269       arc[2] = y1 = ( y1 + y2 ) / 2;
270       arc[4] = y4 = ( y4 + y2 ) / 2;
271       arc[3] = ( y1 + y4 ) / 2;
272 
273       arc += 3;
274       goto Suite;
275 
276    Test:
277       if ( y1 < *min ) *min = y1;
278       if ( y4 > *max ) *max = y4;
279       arc -= 3;
280 
281     Suite:
282       ;
283     } while ( arc >= stack );
284   }
285 
286 #else
287 
288   static void
test_cubic_extrema(FT_Pos y1,FT_Pos y2,FT_Pos y3,FT_Pos y4,FT_Fixed u,FT_Pos * min,FT_Pos * max)289   test_cubic_extrema( FT_Pos    y1,
290                       FT_Pos    y2,
291                       FT_Pos    y3,
292                       FT_Pos    y4,
293                       FT_Fixed  u,
294                       FT_Pos*   min,
295                       FT_Pos*   max )
296   {
297  /* FT_Pos    a = y4 - 3*y3 + 3*y2 - y1; */
298     FT_Pos    b = y3 - 2*y2 + y1;
299     FT_Pos    c = y2 - y1;
300     FT_Pos    d = y1;
301     FT_Pos    y;
302     FT_Fixed  uu;
303 
304     FT_UNUSED ( y4 );
305 
306 
307     /* The polynomial is                      */
308     /*                                        */
309     /*    P(x) = a*x^3 + 3b*x^2 + 3c*x + d  , */
310     /*                                        */
311     /*   dP/dx = 3a*x^2 + 6b*x + 3c         . */
312     /*                                        */
313     /* However, we also have                  */
314     /*                                        */
315     /*   dP/dx(u) = 0                       , */
316     /*                                        */
317     /* which implies by subtraction that      */
318     /*                                        */
319     /*   P(u) = b*u^2 + 2c*u + d            . */
320 
321     if ( u > 0 && u < 0x10000L )
322     {
323       uu = FT_MulFix( u, u );
324       y  = d + FT_MulFix( c, 2*u ) + FT_MulFix( b, uu );
325 
326       if ( y < *min ) *min = y;
327       if ( y > *max ) *max = y;
328     }
329   }
330 
331 
332   static void
BBox_Cubic_Check(FT_Pos y1,FT_Pos y2,FT_Pos y3,FT_Pos y4,FT_Pos * min,FT_Pos * max)333   BBox_Cubic_Check( FT_Pos   y1,
334                     FT_Pos   y2,
335                     FT_Pos   y3,
336                     FT_Pos   y4,
337                     FT_Pos*  min,
338                     FT_Pos*  max )
339   {
340     /* always compare first and last points */
341     if      ( y1 < *min )  *min = y1;
342     else if ( y1 > *max )  *max = y1;
343 
344     if      ( y4 < *min )  *min = y4;
345     else if ( y4 > *max )  *max = y4;
346 
347     /* now, try to see if there are split points here */
348     if ( y1 <= y4 )
349     {
350       /* flat or ascending arc test */
351       if ( y1 <= y2 && y2 <= y4 && y1 <= y3 && y3 <= y4 )
352         return;
353     }
354     else /* y1 > y4 */
355     {
356       /* descending arc test */
357       if ( y1 >= y2 && y2 >= y4 && y1 >= y3 && y3 >= y4 )
358         return;
359     }
360 
361     /* There are some split points.  Find them. */
362     {
363       FT_Pos    a = y4 - 3*y3 + 3*y2 - y1;
364       FT_Pos    b = y3 - 2*y2 + y1;
365       FT_Pos    c = y2 - y1;
366       FT_Pos    d;
367       FT_Fixed  t;
368 
369 
370       /* We need to solve `ax^2+2bx+c' here, without floating points!      */
371       /* The trick is to normalize to a different representation in order  */
372       /* to use our 16.16 fixed point routines.                            */
373       /*                                                                   */
374       /* We compute FT_MulFix(b,b) and FT_MulFix(a,c) after normalization. */
375       /* These values must fit into a single 16.16 value.                  */
376       /*                                                                   */
377       /* We normalize a, b, and c to `8.16' fixed float values to ensure   */
378       /* that its product is held in a `16.16' value.                      */
379 
380       {
381         FT_ULong  t1, t2;
382         int       shift = 0;
383 
384 
385         /* The following computation is based on the fact that for   */
386         /* any value `y', if `n' is the position of the most         */
387         /* significant bit of `abs(y)' (starting from 0 for the      */
388         /* least significant bit), then `y' is in the range          */
389         /*                                                           */
390         /*   -2^n..2^n-1                                             */
391         /*                                                           */
392         /* We want to shift `a', `b', and `c' concurrently in order  */
393         /* to ensure that they all fit in 8.16 values, which maps    */
394         /* to the integer range `-2^23..2^23-1'.                     */
395         /*                                                           */
396         /* Necessarily, we need to shift `a', `b', and `c' so that   */
397         /* the most significant bit of its absolute values is at     */
398         /* _most_ at position 23.                                    */
399         /*                                                           */
400         /* We begin by computing `t1' as the bitwise `OR' of the     */
401         /* absolute values of `a', `b', `c'.                         */
402 
403         t1  = (FT_ULong)( ( a >= 0 ) ? a : -a );
404         t2  = (FT_ULong)( ( b >= 0 ) ? b : -b );
405         t1 |= t2;
406         t2  = (FT_ULong)( ( c >= 0 ) ? c : -c );
407         t1 |= t2;
408 
409         /* Now we can be sure that the most significant bit of `t1'  */
410         /* is the most significant bit of either `a', `b', or `c',   */
411         /* depending on the greatest integer range of the particular */
412         /* variable.                                                 */
413         /*                                                           */
414         /* Next, we compute the `shift', by shifting `t1' as many    */
415         /* times as necessary to move its MSB to position 23.  This  */
416         /* corresponds to a value of `t1' that is in the range       */
417         /* 0x40_0000..0x7F_FFFF.                                     */
418         /*                                                           */
419         /* Finally, we shift `a', `b', and `c' by the same amount.   */
420         /* This ensures that all values are now in the range         */
421         /* -2^23..2^23, i.e., they are now expressed as 8.16         */
422         /* fixed-float numbers.  This also means that we are using   */
423         /* 24 bits of precision to compute the zeros, independently  */
424         /* of the range of the original polynomial coefficients.     */
425         /*                                                           */
426         /* This algorithm should ensure reasonably accurate values   */
427         /* for the zeros.  Note that they are only expressed with    */
428         /* 16 bits when computing the extrema (the zeros need to     */
429         /* be in 0..1 exclusive to be considered part of the arc).   */
430 
431         if ( t1 == 0 )  /* all coefficients are 0! */
432           return;
433 
434         if ( t1 > 0x7FFFFFUL )
435         {
436           do
437           {
438             shift++;
439             t1 >>= 1;
440 
441           } while ( t1 > 0x7FFFFFUL );
442 
443           /* this loses some bits of precision, but we use 24 of them */
444           /* for the computation anyway                               */
445           a >>= shift;
446           b >>= shift;
447           c >>= shift;
448         }
449         else if ( t1 < 0x400000UL )
450         {
451           do
452           {
453             shift++;
454             t1 <<= 1;
455 
456           } while ( t1 < 0x400000UL );
457 
458           a <<= shift;
459           b <<= shift;
460           c <<= shift;
461         }
462       }
463 
464       /* handle a == 0 */
465       if ( a == 0 )
466       {
467         if ( b != 0 )
468         {
469           t = - FT_DivFix( c, b ) / 2;
470           test_cubic_extrema( y1, y2, y3, y4, t, min, max );
471         }
472       }
473       else
474       {
475         /* solve the equation now */
476         d = FT_MulFix( b, b ) - FT_MulFix( a, c );
477         if ( d < 0 )
478           return;
479 
480         if ( d == 0 )
481         {
482           /* there is a single split point at -b/a */
483           t = - FT_DivFix( b, a );
484           test_cubic_extrema( y1, y2, y3, y4, t, min, max );
485         }
486         else
487         {
488           /* there are two solutions; we need to filter them */
489           d = FT_SqrtFixed( (FT_Int32)d );
490           t = - FT_DivFix( b - d, a );
491           test_cubic_extrema( y1, y2, y3, y4, t, min, max );
492 
493           t = - FT_DivFix( b + d, a );
494           test_cubic_extrema( y1, y2, y3, y4, t, min, max );
495         }
496       }
497     }
498   }
499 
500 #endif
501 
502 
503   /*************************************************************************/
504   /*                                                                       */
505   /* <Function>                                                            */
506   /*    BBox_Cubic_To                                                      */
507   /*                                                                       */
508   /* <Description>                                                         */
509   /*    This function is used as a `cubic_to' emitter during               */
510   /*    FT_Outline_Decompose().  It checks a cubic Bezier curve with the   */
511   /*    current bounding box, and computes its extrema if necessary to     */
512   /*    update it.                                                         */
513   /*                                                                       */
514   /* <Input>                                                               */
515   /*    control1 :: A pointer to the first control point.                  */
516   /*                                                                       */
517   /*    control2 :: A pointer to the second control point.                 */
518   /*                                                                       */
519   /*    to       :: A pointer to the destination vector.                   */
520   /*                                                                       */
521   /* <InOut>                                                               */
522   /*    user     :: The address of the current walk context.               */
523   /*                                                                       */
524   /* <Return>                                                              */
525   /*    Always 0.  Needed for the interface only.                          */
526   /*                                                                       */
527   /* <Note>                                                                */
528   /*    In the case of a non-monotonous arc, we don't compute directly     */
529   /*    extremum coordinates, we subdivide instead.                        */
530   /*                                                                       */
531   static int
BBox_Cubic_To(FT_Vector * control1,FT_Vector * control2,FT_Vector * to,TBBox_Rec * user)532   BBox_Cubic_To( FT_Vector*  control1,
533                  FT_Vector*  control2,
534                  FT_Vector*  to,
535                  TBBox_Rec*  user )
536   {
537     /* we don't need to check `to' since it is always an `on' point, thus */
538     /* within the bbox                                                    */
539 
540     if ( CHECK_X( control1, user->bbox ) ||
541          CHECK_X( control2, user->bbox ) )
542       BBox_Cubic_Check( user->last.x,
543                         control1->x,
544                         control2->x,
545                         to->x,
546                         &user->bbox.xMin,
547                         &user->bbox.xMax );
548 
549     if ( CHECK_Y( control1, user->bbox ) ||
550          CHECK_Y( control2, user->bbox ) )
551       BBox_Cubic_Check( user->last.y,
552                         control1->y,
553                         control2->y,
554                         to->y,
555                         &user->bbox.yMin,
556                         &user->bbox.yMax );
557 
558     user->last = *to;
559 
560     return 0;
561   }
562 
563 FT_DEFINE_OUTLINE_FUNCS(bbox_interface,
564     (FT_Outline_MoveTo_Func) BBox_Move_To,
565     (FT_Outline_LineTo_Func) BBox_Move_To,
566     (FT_Outline_ConicTo_Func)BBox_Conic_To,
567     (FT_Outline_CubicTo_Func)BBox_Cubic_To,
568     0, 0
569   )
570 
571   /* documentation is in ftbbox.h */
572 
FT_EXPORT_DEF(FT_Error)573   FT_EXPORT_DEF( FT_Error )
574   FT_Outline_Get_BBox( FT_Outline*  outline,
575                        FT_BBox     *abbox )
576   {
577     FT_BBox     cbox;
578     FT_BBox     bbox;
579     FT_Vector*  vec;
580     FT_UShort   n;
581 
582 
583     if ( !abbox )
584       return FT_Err_Invalid_Argument;
585 
586     if ( !outline )
587       return FT_Err_Invalid_Outline;
588 
589     /* if outline is empty, return (0,0,0,0) */
590     if ( outline->n_points == 0 || outline->n_contours <= 0 )
591     {
592       abbox->xMin = abbox->xMax = 0;
593       abbox->yMin = abbox->yMax = 0;
594       return 0;
595     }
596 
597     /* We compute the control box as well as the bounding box of  */
598     /* all `on' points in the outline.  Then, if the two boxes    */
599     /* coincide, we exit immediately.                             */
600 
601     vec = outline->points;
602     bbox.xMin = bbox.xMax = cbox.xMin = cbox.xMax = vec->x;
603     bbox.yMin = bbox.yMax = cbox.yMin = cbox.yMax = vec->y;
604     vec++;
605 
606     for ( n = 1; n < outline->n_points; n++ )
607     {
608       FT_Pos  x = vec->x;
609       FT_Pos  y = vec->y;
610 
611 
612       /* update control box */
613       if ( x < cbox.xMin ) cbox.xMin = x;
614       if ( x > cbox.xMax ) cbox.xMax = x;
615 
616       if ( y < cbox.yMin ) cbox.yMin = y;
617       if ( y > cbox.yMax ) cbox.yMax = y;
618 
619       if ( FT_CURVE_TAG( outline->tags[n] ) == FT_CURVE_TAG_ON )
620       {
621         /* update bbox for `on' points only */
622         if ( x < bbox.xMin ) bbox.xMin = x;
623         if ( x > bbox.xMax ) bbox.xMax = x;
624 
625         if ( y < bbox.yMin ) bbox.yMin = y;
626         if ( y > bbox.yMax ) bbox.yMax = y;
627       }
628 
629       vec++;
630     }
631 
632     /* test two boxes for equality */
633     if ( cbox.xMin < bbox.xMin || cbox.xMax > bbox.xMax ||
634          cbox.yMin < bbox.yMin || cbox.yMax > bbox.yMax )
635     {
636       /* the two boxes are different, now walk over the outline to */
637       /* get the Bezier arc extrema.                               */
638 
639       FT_Error   error;
640       TBBox_Rec  user;
641 
642 #ifdef FT_CONFIG_OPTION_PIC
643       FT_Outline_Funcs bbox_interface;
644       Init_Class_bbox_interface(&bbox_interface);
645 #endif
646 
647       user.bbox = bbox;
648 
649       error = FT_Outline_Decompose( outline, &bbox_interface, &user );
650       if ( error )
651         return error;
652 
653       *abbox = user.bbox;
654     }
655     else
656       *abbox = bbox;
657 
658     return FT_Err_Ok;
659   }
660 
661 
662 /* END */
663