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