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