Imported Upstream version 2.13.2
[platform/upstream/freetype2.git] / 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 */