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