1 /****************************************************************************
5 * FreeType bbox computation (body).
7 * Copyright (C) 1996-2023 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
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.
19 /**************************************************************************
21 * This component has a _single_ role: to compute exact outline bounding
27 #include <freetype/internal/ftdebug.h>
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>
36 typedef struct TBBox_Rec_
44 #define FT_UPDATE_BBOX( p, bbox ) \
46 if ( p->x < bbox.xMin ) \
48 if ( p->x > bbox.xMax ) \
50 if ( p->y < bbox.yMin ) \
52 if ( p->y > bbox.yMax ) \
56 #define CHECK_X( p, bbox ) \
57 ( p->x < bbox.xMin || p->x > bbox.xMax )
59 #define CHECK_Y( p, bbox ) \
60 ( p->y < bbox.yMin || p->y > bbox.yMax )
63 /**************************************************************************
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.
76 * A pointer to the destination vector.
80 * A pointer to the current walk context.
83 * Always 0. Needed for the interface only.
85 FT_CALLBACK_DEF( int )
86 BBox_Move_To( const FT_Vector* to,
89 TBBox_Rec* user = (TBBox_Rec*)user_;
92 FT_UPDATE_BBOX( to, user->bbox );
100 /**************************************************************************
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.
113 * A pointer to the destination vector.
117 * A pointer to the current walk context.
120 * Always 0. Needed for the interface only.
122 FT_CALLBACK_DEF( int )
123 BBox_Line_To( const FT_Vector* to,
126 TBBox_Rec* user = (TBBox_Rec*)user_;
135 /**************************************************************************
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.
147 * The start coordinate.
150 * The coordinate of the control point.
153 * The end coordinate.
157 * The address of the current minimum.
160 * The address of the current maximum.
163 BBox_Conic_Check( FT_Pos y1,
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 */
176 y2 += FT_MulDiv( y1, y3, y1 + y3 );
185 /**************************************************************************
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
198 * A pointer to a control point.
201 * A pointer to the destination vector.
205 * The address of the current walk context.
208 * Always 0. Needed for the interface only.
211 * In the case of a non-monotonous arc, we compute directly the
212 * extremum coordinates, as it is sufficiently fast.
214 FT_CALLBACK_DEF( int )
215 BBox_Conic_To( const FT_Vector* control,
219 TBBox_Rec* user = (TBBox_Rec*)user_;
222 /* in case `to' is implicit and not included in bbox yet */
223 FT_UPDATE_BBOX( to, user->bbox );
225 if ( CHECK_X( control, user->bbox ) )
226 BBox_Conic_Check( user->last.x,
232 if ( CHECK_Y( control, user->bbox ) )
233 BBox_Conic_Check( user->last.y,
245 /**************************************************************************
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.
257 * The start coordinate.
260 * The coordinate of the first control point.
263 * The coordinate of the second control point.
266 * The end coordinate.
270 * The address of the current minimum.
273 * The address of the current maximum.
276 cubic_peak( FT_Pos q1,
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. */
294 shift = 27 - FT_MSB( (FT_UInt32)( FT_ABS( q1 ) |
301 /* upscaling too much just wastes time */
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 )
322 /* determine which half contains the maximum and split */
323 if ( q1 + q2 > q3 + q4 ) /* first half */
330 q4 = ( q4 + q3 ) >> 3;
334 else /* second half */
341 q1 = ( q1 + q2 ) >> 3;
346 /* check whether either end reached the maximum */
347 if ( q1 == q2 && q1 >= q3 )
352 if ( q3 == q4 && q2 <= q4 )
369 BBox_Cubic_Check( FT_Pos p1,
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. */
381 if ( p2 > *max || p3 > *max )
382 *max += cubic_peak( p1 - *max, p2 - *max, p3 - *max, p4 - *max );
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 );
390 /**************************************************************************
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
403 * A pointer to the first control point.
406 * A pointer to the second control point.
409 * A pointer to the destination vector.
413 * The address of the current walk context.
416 * Always 0. Needed for the interface only.
419 * In the case of a non-monotonous arc, we don't compute directly
420 * extremum coordinates, we subdivide instead.
422 FT_CALLBACK_DEF( int )
423 BBox_Cubic_To( const FT_Vector* control1,
424 const FT_Vector* control2,
428 TBBox_Rec* user = (TBBox_Rec*)user_;
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. */
435 if ( CHECK_X( control1, user->bbox ) ||
436 CHECK_X( control2, user->bbox ) )
437 BBox_Cubic_Check( user->last.x,
444 if ( CHECK_Y( control1, user->bbox ) ||
445 CHECK_Y( control2, user->bbox ) )
446 BBox_Cubic_Check( user->last.y,
459 FT_DEFINE_OUTLINE_FUNCS(
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 */
471 /* documentation is in ftbbox.h */
473 FT_EXPORT_DEF( FT_Error )
474 FT_Outline_Get_BBox( FT_Outline* outline,
477 FT_BBox cbox = { 0x7FFFFFFFL, 0x7FFFFFFFL,
478 -0x7FFFFFFFL, -0x7FFFFFFFL };
479 FT_BBox bbox = { 0x7FFFFFFFL, 0x7FFFFFFFL,
480 -0x7FFFFFFFL, -0x7FFFFFFFL };
486 return FT_THROW( Invalid_Argument );
489 return FT_THROW( Invalid_Outline );
491 /* if outline is empty, return (0,0,0,0) */
492 if ( outline->n_points == 0 || outline->n_contours <= 0 )
494 abbox->xMin = abbox->xMax = 0;
495 abbox->yMin = abbox->yMax = 0;
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. */
504 vec = outline->points;
506 for ( n = 0; n < outline->n_points; n++ )
508 FT_UPDATE_BBOX( vec, cbox );
510 if ( FT_CURVE_TAG( outline->tags[n] ) == FT_CURVE_TAG_ON )
511 FT_UPDATE_BBOX( vec, bbox );
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 )
520 /* the two boxes are different, now walk over the outline to */
521 /* get the Bezier arc extrema. */
529 error = FT_Outline_Decompose( outline, &bbox_interface, &user );