1 /***************************************************************************/
5 /* FreeType bbox computation (body). */
7 /* Copyright 1996-2002, 2004, 2006, 2010, 2013, 2014 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. */
16 /***************************************************************************/
19 /*************************************************************************/
21 /* This component has a _single_ role: to compute exact outline bounding */
24 /*************************************************************************/
28 #include FT_INTERNAL_DEBUG_H
33 #include FT_INTERNAL_CALC_H
34 #include FT_INTERNAL_OBJECTS_H
37 typedef struct TBBox_Rec_
45 #define FT_UPDATE_BBOX( p, bbox ) \
47 if ( p->x < bbox.xMin ) \
49 if ( p->x > bbox.xMax ) \
51 if ( p->y < bbox.yMin ) \
53 if ( p->y > bbox.yMax ) \
57 #define CHECK_X( p, bbox ) \
58 ( p->x < bbox.xMin || p->x > bbox.xMax )
60 #define CHECK_Y( p, bbox ) \
61 ( p->y < bbox.yMin || p->y > bbox.yMax )
64 /*************************************************************************/
70 /* This function is used as a `move_to' emitter during */
71 /* FT_Outline_Decompose(). It simply records the destination point */
72 /* in `user->last'. We also update bbox in case contour starts with */
73 /* an implicit `on' point. */
76 /* to :: A pointer to the destination vector. */
79 /* user :: A pointer to the current walk context. */
82 /* Always 0. Needed for the interface only. */
85 BBox_Move_To( FT_Vector* to,
88 FT_UPDATE_BBOX( to, user->bbox );
96 /*************************************************************************/
102 /* This function is used as a `line_to' emitter during */
103 /* FT_Outline_Decompose(). It simply records the destination point */
104 /* in `user->last'; no further computations are necessary because */
105 /* bbox already contains both explicit ends of the line segment. */
108 /* to :: A pointer to the destination vector. */
111 /* user :: A pointer to the current walk context. */
114 /* Always 0. Needed for the interface only. */
117 BBox_Line_To( FT_Vector* to,
126 /*************************************************************************/
129 /* BBox_Conic_Check */
132 /* Find the extrema of a 1-dimensional conic Bezier curve and update */
133 /* a bounding range. This version uses direct computation, as it */
134 /* doesn't need square roots. */
137 /* y1 :: The start coordinate. */
139 /* y2 :: The coordinate of the control point. */
141 /* y3 :: The end coordinate. */
144 /* min :: The address of the current minimum. */
146 /* max :: The address of the current maximum. */
149 BBox_Conic_Check( FT_Pos y1,
155 /* This function is only called when a control off-point is outside */
156 /* the bbox that contains all on-points. It finds a local extremum */
157 /* within the segment, equal to (y1*y3 - y2*y2)/(y1 - 2*y2 + y3). */
158 /* Or, offsetting from y2, we get */
162 y2 += FT_MulDiv( y1, y3, y1 + y3 );
171 /*************************************************************************/
177 /* This function is used as a `conic_to' emitter during */
178 /* FT_Outline_Decompose(). It checks a conic Bezier curve with the */
179 /* current bounding box, and computes its extrema if necessary to */
183 /* control :: A pointer to a control point. */
185 /* to :: A pointer to the destination vector. */
188 /* user :: The address of the current walk context. */
191 /* Always 0. Needed for the interface only. */
194 /* In the case of a non-monotonous arc, we compute directly the */
195 /* extremum coordinates, as it is sufficiently fast. */
198 BBox_Conic_To( FT_Vector* control,
202 /* in case `to' is implicit and not included in bbox yet */
203 FT_UPDATE_BBOX( to, user->bbox );
205 if ( CHECK_X( control, user->bbox ) )
206 BBox_Conic_Check( user->last.x,
212 if ( CHECK_Y( control, user->bbox ) )
213 BBox_Conic_Check( user->last.y,
225 /*************************************************************************/
228 /* BBox_Cubic_Check */
231 /* Find the extrema of a 1-dimensional cubic Bezier curve and */
232 /* update a bounding range. This version uses iterative splitting */
233 /* because it is faster than the exact solution with square roots. */
236 /* p1 :: The start coordinate. */
238 /* p2 :: The coordinate of the first control point. */
240 /* p3 :: The coordinate of the second control point. */
242 /* p4 :: The end coordinate. */
245 /* min :: The address of the current minimum. */
247 /* max :: The address of the current maximum. */
250 cubic_peak( FT_Pos q1,
258 /* This function finds a peak of a cubic segment if it is above 0 */
259 /* using iterative bisection of the segment, or returns 0. */
260 /* The fixed-point arithmetic of bisection is inherently stable */
261 /* but may loose accuracy in the two lowest bits. To compensate, */
262 /* we upscale the segment if there is room. Large values may need */
263 /* to be downscaled to avoid overflows during bisection. */
264 /* It is called with either q2 or q3 positive, which is necessary */
265 /* for the peak to exist and avoids undefined FT_MSB. */
268 FT_MSB( FT_ABS( q1 ) | FT_ABS( q2 ) | FT_ABS( q3 ) | FT_ABS( q4 ) );
272 /* upscaling too much just wastes time */
289 /* for a peak to exist above 0, the cubic segment must have */
290 /* at least one of its control off-points above 0. */
291 while ( q2 > 0 || q3 > 0 )
293 /* determine which half contains the maximum and split */
294 if ( q1 + q2 > q3 + q4 ) /* first half */
301 q4 = ( q4 + q3 ) / 8;
305 else /* second half */
312 q1 = ( q1 + q2 ) / 8;
317 /* check whether either end reached the maximum */
318 if ( q1 == q2 && q1 >= q3 )
323 if ( q3 == q4 && q2 <= q4 )
340 BBox_Cubic_Check( FT_Pos p1,
347 /* This function is only called when a control off-point is outside */
348 /* the bbox that contains all on-points. So at least one of the */
349 /* conditions below holds and cubic_peak is called with at least one */
350 /* non-zero argument. */
352 if ( p2 > *max || p3 > *max )
353 *max += cubic_peak( p1 - *max, p2 - *max, p3 - *max, p4 - *max );
355 /* now flip the signs to update the minimum */
356 if ( p2 < *min || p3 < *min )
357 *min -= cubic_peak( *min - p1, *min - p2, *min - p3, *min - p4 );
361 /*************************************************************************/
367 /* This function is used as a `cubic_to' emitter during */
368 /* FT_Outline_Decompose(). It checks a cubic Bezier curve with the */
369 /* current bounding box, and computes its extrema if necessary to */
373 /* control1 :: A pointer to the first control point. */
375 /* control2 :: A pointer to the second control point. */
377 /* to :: A pointer to the destination vector. */
380 /* user :: The address of the current walk context. */
383 /* Always 0. Needed for the interface only. */
386 /* In the case of a non-monotonous arc, we don't compute directly */
387 /* extremum coordinates, we subdivide instead. */
390 BBox_Cubic_To( FT_Vector* control1,
395 /* We don't need to check `to' since it is always an on-point, */
396 /* thus within the bbox. Only segments with an off-point outside */
397 /* the bbox can possibly reach new extreme values. */
399 if ( CHECK_X( control1, user->bbox ) ||
400 CHECK_X( control2, user->bbox ) )
401 BBox_Cubic_Check( user->last.x,
408 if ( CHECK_Y( control1, user->bbox ) ||
409 CHECK_Y( control2, user->bbox ) )
410 BBox_Cubic_Check( user->last.y,
423 FT_DEFINE_OUTLINE_FUNCS(bbox_interface,
424 (FT_Outline_MoveTo_Func) BBox_Move_To,
425 (FT_Outline_LineTo_Func) BBox_Line_To,
426 (FT_Outline_ConicTo_Func)BBox_Conic_To,
427 (FT_Outline_CubicTo_Func)BBox_Cubic_To,
432 /* documentation is in ftbbox.h */
434 FT_EXPORT_DEF( FT_Error )
435 FT_Outline_Get_BBox( FT_Outline* outline,
438 FT_BBox cbox = { 0x7FFFFFFFL, 0x7FFFFFFFL,
439 -0x7FFFFFFFL, -0x7FFFFFFFL };
440 FT_BBox bbox = { 0x7FFFFFFFL, 0x7FFFFFFFL,
441 -0x7FFFFFFFL, -0x7FFFFFFFL };
447 return FT_THROW( Invalid_Argument );
450 return FT_THROW( Invalid_Outline );
452 /* if outline is empty, return (0,0,0,0) */
453 if ( outline->n_points == 0 || outline->n_contours <= 0 )
455 abbox->xMin = abbox->xMax = 0;
456 abbox->yMin = abbox->yMax = 0;
460 /* We compute the control box as well as the bounding box of */
461 /* all `on' points in the outline. Then, if the two boxes */
462 /* coincide, we exit immediately. */
464 vec = outline->points;
466 for ( n = 0; n < outline->n_points; n++ )
468 FT_UPDATE_BBOX( vec, cbox);
470 if ( FT_CURVE_TAG( outline->tags[n] ) == FT_CURVE_TAG_ON )
471 FT_UPDATE_BBOX( vec, bbox);
476 /* test two boxes for equality */
477 if ( cbox.xMin < bbox.xMin || cbox.xMax > bbox.xMax ||
478 cbox.yMin < bbox.yMin || cbox.yMax > bbox.yMax )
480 /* the two boxes are different, now walk over the outline to */
481 /* get the Bezier arc extrema. */
486 #ifdef FT_CONFIG_OPTION_PIC
487 FT_Outline_Funcs bbox_interface;
488 Init_Class_bbox_interface(&bbox_interface);
493 error = FT_Outline_Decompose( outline, &bbox_interface, &user );