From 54169b35f8ba994ba4cb9d46c75fdde78c8c0cc3 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 26 Oct 2000 00:06:35 +0000 Subject: [PATCH] - reviving the "ftbbox" component, used to compute exact bounding box computations - minor update to docmaker.py, more is coming --- docs/docmaker.py | 46 +++- include/freetype/ftbbox.h | 8 +- src/base/ftbbox.c | 447 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 495 insertions(+), 6 deletions(-) create mode 100644 src/base/ftbbox.c diff --git a/docs/docmaker.py b/docs/docmaker.py index b71d81f02..aa1ba84fb 100644 --- a/docs/docmaker.py +++ b/docs/docmaker.py @@ -3,6 +3,10 @@ # DocMaker is a very simple program used to generate HTML documentation # from the source files of the FreeType packages. # +# I should really be using regular expressions to do this, but hey, +# i'm too lazy right now, and the damn thing seems to work :-) +# - David +# import fileinput, sys, string @@ -110,6 +114,34 @@ source_footer = """ # instead of "". All marker identifiers are converted to # lower case during parsing, in order to simply sorting.. # +# We associate with each block the following source lines that do not +# begin with a comment. For example, the following: +# +# /********************************** +# * +# * blabla +# * +# */ +# +# bla_bla_bla +# bilip_bilip +# +# /* - this comment acts as a separator - */ +# +# blo_blo_blo +# +# +# will only keep the first two lines of sources with +# the "blabla" block +# +# However, the comment will be kept, with following source lines +# if it contains a starting '#' or '@' as in: +# +# /*@.....*/ +# /*#.....*/ +# /* @.....*/ +# /* #.....*/ +# def make_block_list(): @@ -146,9 +178,19 @@ def make_block_list(): # if this line begins with a comment and we are processing some # source, exit to state 0 # + # unless we encounter something like: + # + # /*@..... + # /*#..... + # + # /* @..... + # /* #..... + # if format >= 4 and l > 2 and line2[0 : 2] == '/*': - list.append( ( block, source ) ) - format = 0 + if l < 4 or ( line2[3] != '@' and line2[3:4] != ' @' and + line2[3] != '#' and line2[3:4] != ' #'): + list.append( ( block, source ) ) + format = 0 if format == 0: #### wait for beginning of comment #### diff --git a/include/freetype/ftbbox.h b/include/freetype/ftbbox.h index e1446647d..54a05b926 100644 --- a/include/freetype/ftbbox.h +++ b/include/freetype/ftbbox.h @@ -2,7 +2,7 @@ /* */ /* ftbbox.h */ /* */ -/* FreeType bbox computation (specification). */ +/* FreeType exact bbox computation (specification). */ /* */ /* Copyright 1996-2000 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ @@ -40,7 +40,7 @@ /*************************************************************************/ /* */ /* */ - /* FT_Raster_GetBBox */ + /* FT_Outline_Get_BBox */ /* */ /* */ /* Computes the exact bounding box of an outline. This is slower */ @@ -58,8 +58,8 @@ /* */ /* Error code. 0 means success. */ /* */ - FT_EXPORT_DEF(FT_Error) FT_Raster_GetBBox( FT_Outline* outline, - FT_BBox* abbox ); + FT_EXPORT_DEF(FT_Error) FT_Outline_Get_BBox( FT_Outline* outline, + FT_BBox *abbox ); #ifdef __cplusplus diff --git a/src/base/ftbbox.c b/src/base/ftbbox.c new file mode 100644 index 000000000..484ea4a9b --- /dev/null +++ b/src/base/ftbbox.c @@ -0,0 +1,447 @@ +/***************************************************************************/ +/* */ +/* ftbbox.c */ +/* */ +/* FreeType bbox computation (body). */ +/* */ +/* Copyright 1996-2000 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used */ +/* modified and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + /*************************************************************************/ + /* */ + /* This component has a _single_ role: to compute exact outline bounding */ + /* boxes. */ + /* */ + /* It is separated from the rest of the engine for various technical */ + /* reasons. It may well be integrated in `ftoutln' later. */ + /* */ + /*************************************************************************/ + + +#include +#include +#include + + typedef struct TBBox_Rec_ + { + FT_Vector last; + FT_BBox bbox; + + } TBBox_Rec; + + + /*************************************************************************/ + /* */ + /* */ + /* BBox_Move_To */ + /* */ + /* */ + /* This function is used as a `move_to' and `line_to' emitter during */ + /* FT_Outline_Decompose. It simply records the destination point in */ + /* `user->last'. */ + /* */ + /* */ + /* to :: A pointer to the destination vector. */ + /* */ + /* */ + /* user :: A pointer to the current walk context. */ + /* */ + /* */ + /* Error code. 0 means success. */ + /* */ + static + int BBox_Move_To( FT_Vector* to, + TBBox_Rec* user ) + { + user->last = *to; + + return 0; + } + + +#define CHECK_X( p, bbox ) \ + ( p->x < bbox.xMin || p->x > bbox.xMax ) + +#define CHECK_Y( p, bbox ) \ + ( p->y < bbox.yMin || p->y > bbox.yMax ) + + + /*************************************************************************/ + /* */ + /* */ + /* BBox_Conic_Check */ + /* */ + /* */ + /* Finds the extrema of a 1-dimensional conic Bezier curve and update */ + /* a bounding range. This version uses direct computation, as it */ + /* doesn't need square roots. */ + /* */ + /* */ + /* y1 :: The start coordinate. */ + /* y2 :: The coordinate of the control point. */ + /* y3 :: The end coordinate. */ + /* */ + /* */ + /* min :: The address of the current minimum. */ + /* max :: The address of the current maximum. */ + /* */ + static + void BBox_Conic_Check( FT_Pos y1, + FT_Pos y2, + FT_Pos y3, + FT_Pos* min, + FT_Pos* max ) + { + if( y1 == y3 ) + { + if ( y2 == y1 ) /* Flat arc */ + { + y3 = y1; + goto Suite; + } + } + else if ( y1 < y3 ) + { + if ( y2 >= y1 && y2 <= y3 ) /* Ascending arc */ + goto Suite; + } + else + { + if ( y2 >= y3 && y2 <= y1 ) /* Descending arc */ + { + y2 = y1; + y1 = y3; + y3 = y2; + goto Suite; + } + } + + y1 = y3 = FT_MulDiv( y2 - y1, y2 - y1, y1 - 2*y2 + y3 ); + + Suite: + if ( y1 < *min ) *min = y1; + if ( y3 > *max ) *max = y3; + } + + + /*************************************************************************/ + /* */ + /* */ + /* BBox_Conic_To */ + /* */ + /* */ + /* This function is used as a `conic_to' emitter during */ + /* FT_Raster_Decompose(). It checks a conic Bezier curve with the */ + /* current bounding box, and computes its extrema if necessary to */ + /* update it. */ + /* */ + /* */ + /* control :: A pointer to a control point. */ + /* to :: A pointer to the destination vector. */ + /* */ + /* */ + /* user :: The address of the current walk context. */ + /* */ + /* */ + /* Error code. 0 means success. */ + /* */ + /* */ + /* In the case of a non-monotonous arc, we compute directly the */ + /* extremum coordinates, as it is sufficiently fast. */ + /* */ + static + int BBox_Conic_To( FT_Vector* control, + FT_Vector* to, + TBBox_Rec* user ) + { + if ( CHECK_X( control, user->bbox ) || + CHECK_X( to, user->bbox ) ) + + BBox_Conic_Check( user->last.x, + control->x, + to->x, + &user->bbox.xMin, + &user->bbox.xMax ); + + if ( CHECK_Y( control, user->bbox ) || + CHECK_Y( to, user->bbox ) ) + + BBox_Conic_Check( user->last.y, + control->y, + to->y, + &user->bbox.yMin, + &user->bbox.yMax ); + + return 0; + } + + + /*************************************************************************/ + /* */ + /* */ + /* BBox_Cubic_Check */ + /* */ + /* */ + /* Finds the extrema of a 1-dimensional cubic Bezier curve and */ + /* updates a bounding range. This version uses splitting because we */ + /* don't want to use square roots and extra accuracies. */ + /* */ + /* */ + /* p1 :: The start coordinate. */ + /* p2 :: The coordinate of the first control point. */ + /* p3 :: The coordinate of the second control point. */ + /* p4 :: The end coordinate. */ + /* */ + /* */ + /* min :: The address of the current minimum. */ + /* max :: The address of the current maximum. */ + /* */ + static + void BBox_Cubic_Check( FT_Pos p1, + FT_Pos p2, + FT_Pos p3, + FT_Pos p4, + FT_Pos* min, + FT_Pos* max ) + { + FT_Pos stack[33], *arc; + + + arc = stack; + + arc[0] = p1; + arc[1] = p2; + arc[2] = p3; + arc[3] = p4; + + do + { + FT_Pos y1 = arc[0]; + FT_Pos y2 = arc[1]; + FT_Pos y3 = arc[2]; + FT_Pos y4 = arc[3]; + + + if ( y1 == y4 ) + { + if ( y1 == y2 && y1 == y3 ) /* Flat */ + { + y4 = y1; + goto Test; + } + } + else if ( y1 < y4 ) + { + if ( y2 >= y1 && y2 <= y4 && y3 >= y1 && y3 <= y4 ) /* Ascending */ + goto Test; + } + else + { + if ( y2 >= y4 && y2 <= y1 && y3 >= y4 && y3 <= y1 ) /* Descending */ + { + y2 = y1; + y1 = y4; + y4 = y2; + goto Test; + } + } + + /* Unknown direction, split the arc in two */ + arc[6] = y4; + arc[1] = y1 = ( y1 + y2 ) / 2; + arc[5] = y4 = ( y4 + y3 ) / 2; + y2 = ( y2 + y3 ) / 2; + arc[2] = y1 = ( y1 + y2 ) / 2; + arc[4] = y4 = ( y4 + y2 ) / 2; + arc[3] = ( y1 + y4 ) / 2; + + arc += 3; + goto Suite; + + Test: + if ( y1 < *min ) *min = y1; + if ( y4 > *max ) *max = y4; + arc -= 3; + + Suite: + ; + } while (arc >= stack); + } + + + /*************************************************************************/ + /* */ + /* */ + /* BBox_Cubic_To */ + /* */ + /* */ + /* This function is used as a `cubic_to' emitter during */ + /* FT_Raster_Decompose(). It checks a cubic Bezier curve with the */ + /* current bounding box, and computes its extrema if necessary to */ + /* update it. */ + /* */ + /* */ + /* control1 :: A pointer to the first control point. */ + /* control2 :: A pointer to the second control point. */ + /* to :: A pointer to the destination vector. */ + /* */ + /* */ + /* user :: The address of the current walk context. */ + /* */ + /* */ + /* Error code. 0 means success. */ + /* */ + /* */ + /* In the case of a non-monotonous arc, we don't compute directly */ + /* extremum coordinates, we subdivise instead. */ + /* */ + static + int BBox_Cubic_To( FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to, + TBBox_Rec* user ) + { + if ( CHECK_X( control1, user->bbox ) || + CHECK_X( control2, user->bbox ) || + CHECK_X( to, user->bbox ) ) + + BBox_Cubic_Check( user->last.x, + control1->x, + control2->x, + to->x, + &user->bbox.xMin, + &user->bbox.xMax ); + + if ( CHECK_Y( control1, user->bbox ) || + CHECK_Y( control2, user->bbox ) || + CHECK_Y( to, user->bbox ) ) + + BBox_Cubic_Check( user->last.y, + control1->y, + control2->y, + to->y, + &user->bbox.yMin, + &user->bbox.yMax ); + + return 0; + } + + + /*************************************************************************/ + /* */ + /* */ + /* FT_Outline_Get_BBox */ + /* */ + /* */ + /* Computes the exact bounding box of an outline. This is slower */ + /* than computing the control box. However, it uses an advanced */ + /* algorithm which returns _very_ quickly when the two boxes */ + /* coincide. Otherwise, the outline Bezier arcs are walked over to */ + /* extract their extrema. */ + /* */ + /* */ + /* outline :: A pointer to the source outline. */ + /* */ + /* */ + /* abbox :: A pointer to the outline's exact bounding box. */ + /* */ + /* */ + /* Error code. 0 means success. */ + /* */ + FT_EXPORT_FUNC( FT_Error ) FT_Outline_Get_BBox( FT_Outline* outline, + FT_BBox* abbox ) + { + FT_BBox cbox; + FT_BBox bbox; + FT_Vector* vec; + FT_UShort n; + + + /* if outline is empty, return (0,0,0,0) */ + if ( !outline ) + return FT_Err_Invalid_Outline; + + if ( outline->n_points == 0 || outline->n_contours <= 0 ) + { + abbox->xMin = abbox->xMax = 0; + abbox->yMin = abbox->yMax = 0; + return 0; + } + + /* We compute the control box as well as the bounding box of */ + /* all `on' points in the outline. Then, if the two boxes */ + /* coincide, we exit immediately. */ + + vec = outline->points; + bbox.xMin = bbox.xMax = cbox.xMin = cbox.xMax = vec->x; + bbox.yMin = bbox.yMax = cbox.yMin = cbox.yMax = vec->y; + + for ( n = 1; n < outline->n_points; n++ ) + { + FT_Pos x = vec->x; + FT_Pos y = vec->y; + + + /* update control box */ + if ( x < cbox.xMin ) cbox.xMin = x; + if ( x > cbox.xMax ) cbox.xMax = x; + + if ( y < cbox.yMin ) cbox.yMin = y; + if ( y > cbox.yMax ) cbox.yMax = y; + + if ( FT_CURVE_TAG( outline->tags[n] ) == FT_Curve_Tag_On ) + { + /* update bbox for `on' points only */ + if ( x < bbox.xMin ) bbox.xMin = x; + if ( x > bbox.xMax ) bbox.xMax = x; + + if ( y < bbox.yMin ) bbox.yMin = y; + if ( y > bbox.yMax ) bbox.yMax = y; + } + + vec++; + } + + /* test two boxes for equality */ + if ( cbox.xMin < bbox.xMin || cbox.xMax > bbox.xMax || + cbox.yMin < bbox.yMin || cbox.yMax > bbox.yMax ) + { + /* the two boxes are different, now walk over the outline to */ + /* get the Bezier arc extrema. */ + + static const FT_Outline_Funcs interface = + { + (FT_Outline_MoveTo_Func) BBox_Move_To, + (FT_Outline_LineTo_Func) BBox_Move_To, + (FT_Outline_ConicTo_Func)BBox_Conic_To, + (FT_Outline_CubicTo_Func)BBox_Cubic_To + }; + + FT_Error error; + TBBox_Rec user; + + + user.bbox = bbox; + + error = FT_Outline_Decompose( outline, &interface, &user ); + if ( error ) + return error; + + *abbox = user.bbox; + } + else + *abbox = bbox; + + return FT_Err_Ok; + } + + +/* END */