TagStruct FontEngine_Freetype::get_tag_struct(int cont, int index, FT_Outline *ft_outline) { TagStruct tags; int prev_index = get_index_of_prev_contour_point(cont, index, ft_outline); int next_index = get_index_of_next_contour_point(cont, index, ft_outline); tags.previous = FT_CURVE_TAG(ft_outline->tags[prev_index]); tags.next = FT_CURVE_TAG(ft_outline->tags[next_index]); tags.current = FT_CURVE_TAG(ft_outline->tags[index]); return tags; }
intermediate_point get_point(FT_Outline& outline, unsigned int contour, unsigned int point) { unsigned int i = get_point_index(outline, contour, point); intermediate_point res; res.x = outline.points[i].x; res.y = outline.points[i].y; res.tag = FT_CURVE_TAG(outline.tags[i]); // we're only interested in the type tag return res; }
// Find the relative offset from start index to first point of type ON. // This is neccesary because a contour may start with any type of point, but // refonter assumes (for simplicity) that the first point is type ON. unsigned int get_contour_start_offset(FT_Outline& outline, unsigned int contour) { // Determine start and end pos unsigned int start = get_contour_start_index(outline, contour); unsigned int stop = start + get_contour_num_points(outline, contour); // Look for first FT_CURVE_TAG_ON point unsigned int i = start; while (i<stop && FT_CURVE_TAG(outline.tags[i])!=FT_CURVE_TAG_ON) i++; // Return if (i<stop) return i-start; else return -1; }
static ofTTFCharacter makeContoursForCharacter(FT_Face &face){ //int num = face->glyph->outline.n_points; int nContours = face->glyph->outline.n_contours; int startPos = 0; char * tags = face->glyph->outline.tags; FT_Vector * vec = face->glyph->outline.points; ofTTFCharacter charOutlines; charOutlines.setUseShapeColor(false); for(int k = 0; k < nContours; k++){ if( k > 0 ){ startPos = face->glyph->outline.contours[k-1]+1; } int endPos = face->glyph->outline.contours[k]+1; if(printVectorInfo){ ofLogNotice("ofTrueTypeFont") << "--NEW CONTOUR"; } //vector <ofPoint> testOutline; ofPoint lastPoint; for(int j = startPos; j < endPos; j++){ if( FT_CURVE_TAG(tags[j]) == FT_CURVE_TAG_ON ){ lastPoint.set((float)vec[j].x, (float)-vec[j].y, 0); if(printVectorInfo){ ofLogNotice("ofTrueTypeFont") << "flag[" << j << "] is set to 1 - regular point - " << lastPoint.x << lastPoint.y; } charOutlines.lineTo(lastPoint/64); }else{ if(printVectorInfo){ ofLogNotice("ofTrueTypeFont") << "flag[" << j << "] is set to 0 - control point"; } if( FT_CURVE_TAG(tags[j]) == FT_CURVE_TAG_CUBIC ){ if(printVectorInfo){ ofLogNotice("ofTrueTypeFont") << "- bit 2 is set to 2 - CUBIC"; } int prevPoint = j-1; if( j == 0){ prevPoint = endPos-1; } int nextIndex = j+1; if( nextIndex >= endPos){ nextIndex = startPos; } ofPoint nextPoint( (float)vec[nextIndex].x, -(float)vec[nextIndex].y ); //we need two control points to draw a cubic bezier bool lastPointCubic = ( FT_CURVE_TAG(tags[prevPoint]) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG(tags[prevPoint]) == FT_CURVE_TAG_CUBIC); if( lastPointCubic ){ ofPoint controlPoint1((float)vec[prevPoint].x, (float)-vec[prevPoint].y); ofPoint controlPoint2((float)vec[j].x, (float)-vec[j].y); ofPoint nextPoint((float) vec[nextIndex].x, -(float) vec[nextIndex].y); //cubic_bezier(testOutline, lastPoint.x, lastPoint.y, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, nextPoint.x, nextPoint.y, 8); charOutlines.bezierTo(controlPoint1.x/64, controlPoint1.y/64, controlPoint2.x/64, controlPoint2.y/64, nextPoint.x/64, nextPoint.y/64); } }else{ ofPoint conicPoint( (float)vec[j].x, -(float)vec[j].y ); if(printVectorInfo){ ofLogNotice("ofTrueTypeFont") << "- bit 2 is set to 0 - conic- "; ofLogNotice("ofTrueTypeFont") << "--- conicPoint point is " << conicPoint.x << conicPoint.y; } //If the first point is connic and the last point is connic then we need to create a virutal point which acts as a wrap around if( j == startPos ){ bool prevIsConnic = ( FT_CURVE_TAG( tags[endPos-1] ) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG( tags[endPos-1]) != FT_CURVE_TAG_CUBIC ); if( prevIsConnic ){ ofPoint lastConnic((float)vec[endPos - 1].x, (float)-vec[endPos - 1].y); lastPoint = (conicPoint + lastConnic) / 2; if(printVectorInfo){ ofLogNotice("ofTrueTypeFont") << "NEED TO MIX WITH LAST"; ofLogNotice("ofTrueTypeFont") << "last is " << lastPoint.x << " " << lastPoint.y; } } } //bool doubleConic = false; int nextIndex = j+1; if( nextIndex >= endPos){ nextIndex = startPos; } ofPoint nextPoint( (float)vec[nextIndex].x, -(float)vec[nextIndex].y ); if(printVectorInfo){ ofLogNotice("ofTrueTypeFont") << "--- last point is " << lastPoint.x << " " << lastPoint.y; } bool nextIsConnic = ( FT_CURVE_TAG( tags[nextIndex] ) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG( tags[nextIndex]) != FT_CURVE_TAG_CUBIC ); //create a 'virtual on point' if we have two connic points if( nextIsConnic ){ nextPoint = (conicPoint + nextPoint) / 2; if(printVectorInfo){ ofLogNotice("ofTrueTypeFont") << "|_______ double connic!"; } } if(printVectorInfo){ ofLogNotice("ofTrueTypeFont") << "--- next point is " << nextPoint.x << " " << nextPoint.y; } //quad_bezier(testOutline, lastPoint.x, lastPoint.y, conicPoint.x, conicPoint.y, nextPoint.x, nextPoint.y, 8); charOutlines.quadBezierTo(lastPoint.x/64, lastPoint.y/64, conicPoint.x/64, conicPoint.y/64, nextPoint.x/64, nextPoint.y/64); if( nextIsConnic ){ lastPoint = nextPoint; } } } //end for } charOutlines.close(); } return charOutlines; }
FT_Outline_Decompose( const FT_Outline* outline, const FT_Outline_Funcs* func_interface, void* user ) { #undef SCALED #define SCALED( x ) ( ( (x) << shift ) - delta ) FT_Vector v_last; FT_Vector v_control; FT_Vector v_start; FT_Vector* point; FT_Vector* limit; char* tags; FT_Error error; FT_Int n; /* index of contour in outline */ FT_UInt first; /* index of first point in contour */ FT_Int tag; /* current point's state */ FT_Int shift; FT_Pos delta; if ( !outline || !func_interface ) return FT_Err_Invalid_Argument; shift = func_interface->shift; delta = func_interface->delta; first = 0; for ( n = 0; n < outline->n_contours; n++ ) { FT_Int last; /* index of last point in contour */ last = outline->contours[n]; if ( last < 0 ) goto Invalid_Outline; limit = outline->points + last; v_start = outline->points[first]; v_last = outline->points[last]; v_start.x = SCALED( v_start.x ); v_start.y = SCALED( v_start.y ); v_last.x = SCALED( v_last.x ); v_last.y = SCALED( v_last.y ); v_control = v_start; point = outline->points + first; tags = outline->tags + first; tag = FT_CURVE_TAG( tags[0] ); /* A contour cannot start with a cubic control point! */ if ( tag == FT_CURVE_TAG_CUBIC ) goto Invalid_Outline; /* check first point to determine origin */ if ( tag == FT_CURVE_TAG_CONIC ) { /* first point is conic control. Yes, this happens. */ if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) { /* start at last point if it is on the curve */ v_start = v_last; limit--; } else { /* if both first and last points are conic, */ /* start at their middle and record its position */ /* for closure */ v_start.x = ( v_start.x + v_last.x ) / 2; v_start.y = ( v_start.y + v_last.y ) / 2; v_last = v_start; } point--; tags--; } error = func_interface->move_to( &v_start, user ); if ( error ) goto Exit; while ( point < limit ) { point++; tags++; tag = FT_CURVE_TAG( tags[0] ); switch ( tag ) { case FT_CURVE_TAG_ON: /* emit a single line_to */ { FT_Vector vec; vec.x = SCALED( point->x ); vec.y = SCALED( point->y ); error = func_interface->line_to( &vec, user ); if ( error ) goto Exit; continue; } case FT_CURVE_TAG_CONIC: /* consume conic arcs */ v_control.x = SCALED( point->x ); v_control.y = SCALED( point->y ); Do_Conic: if ( point < limit ) { FT_Vector vec; FT_Vector v_middle; point++; tags++; tag = FT_CURVE_TAG( tags[0] ); vec.x = SCALED( point->x ); vec.y = SCALED( point->y ); if ( tag == FT_CURVE_TAG_ON ) { error = func_interface->conic_to( &v_control, &vec, user ); if ( error ) goto Exit; continue; } if ( tag != FT_CURVE_TAG_CONIC ) goto Invalid_Outline; v_middle.x = ( v_control.x + vec.x ) / 2; v_middle.y = ( v_control.y + vec.y ) / 2; error = func_interface->conic_to( &v_control, &v_middle, user ); if ( error ) goto Exit; v_control = vec; goto Do_Conic; } error = func_interface->conic_to( &v_control, &v_start, user ); goto Close; default: /* FT_CURVE_TAG_CUBIC */ { FT_Vector vec1, vec2; if ( point + 1 > limit || FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) goto Invalid_Outline; point += 2; tags += 2; vec1.x = SCALED( point[-2].x ); vec1.y = SCALED( point[-2].y ); vec2.x = SCALED( point[-1].x ); vec2.y = SCALED( point[-1].y ); if ( point <= limit ) { FT_Vector vec; vec.x = SCALED( point->x ); vec.y = SCALED( point->y ); error = func_interface->cubic_to( &vec1, &vec2, &vec, user ); if ( error ) goto Exit; continue; } error = func_interface->cubic_to( &vec1, &vec2, &v_start, user ); goto Close; } } } /* close the contour with a line segment */ error = func_interface->line_to( &v_start, user ); Close: if ( error ) goto Exit; first = last + 1; } return 0; Exit: return error; Invalid_Outline: return FT_Err_Invalid_Outline; }
static ofTTFCharacter makeContoursForCharacter(FT_Face &face){ //int num = face->glyph->outline.n_points; int nContours = face->glyph->outline.n_contours; int startPos = 0; char * tags = face->glyph->outline.tags; FT_Vector * vec = face->glyph->outline.points; ofTTFCharacter charOutlines; for(int k = 0; k < nContours; k++){ if( k > 0 ){ startPos = face->glyph->outline.contours[k-1]+1; } int endPos = face->glyph->outline.contours[k]+1; if( printVectorInfo )printf("--NEW CONTOUR\n\n"); vector <ofPoint> testOutline; ofPoint lastPoint; for(int j = startPos; j < endPos; j++){ if( FT_CURVE_TAG(tags[j]) == FT_CURVE_TAG_ON ){ lastPoint.set((float)vec[j].x, (float)-vec[j].y, 0); if( printVectorInfo )printf("flag[%i] is set to 1 - regular point - %f %f \n", j, lastPoint.x, lastPoint.y); testOutline.push_back(lastPoint); }else{ if( printVectorInfo )printf("flag[%i] is set to 0 - control point \n", j); if( FT_CURVE_TAG(tags[j]) == FT_CURVE_TAG_CUBIC ){ if( printVectorInfo )printf("- bit 2 is set to 2 - CUBIC\n"); int prevPoint = j-1; if( j == 0){ prevPoint = endPos-1; } int nextIndex = j+1; if( nextIndex >= endPos){ nextIndex = startPos; } ofPoint nextPoint( (float)vec[nextIndex].x, -(float)vec[nextIndex].y ); //we need two control points to draw a cubic bezier bool lastPointCubic = ( FT_CURVE_TAG(tags[prevPoint]) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG(tags[prevPoint]) == FT_CURVE_TAG_CUBIC); if( lastPointCubic ){ ofPoint controlPoint1((float)vec[prevPoint].x, (float)-vec[prevPoint].y); ofPoint controlPoint2((float)vec[j].x, (float)-vec[j].y); ofPoint nextPoint((float) vec[nextIndex].x, -(float) vec[nextIndex].y); cubic_bezier(testOutline, lastPoint.x, lastPoint.y, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, nextPoint.x, nextPoint.y, 8); } }else{ ofPoint conicPoint( (float)vec[j].x, -(float)vec[j].y ); if( printVectorInfo )printf("- bit 2 is set to 0 - conic- \n"); if( printVectorInfo )printf("--- conicPoint point is %f %f \n", conicPoint.x, conicPoint.y); //If the first point is connic and the last point is connic then we need to create a virutal point which acts as a wrap around if( j == startPos ){ bool prevIsConnic = ( FT_CURVE_TAG( tags[endPos-1] ) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG( tags[endPos-1]) != FT_CURVE_TAG_CUBIC ); if( prevIsConnic ){ ofPoint lastConnic((float)vec[endPos - 1].x, (float)-vec[endPos - 1].y); lastPoint = (conicPoint + lastConnic) / 2; if( printVectorInfo ) printf("NEED TO MIX WITH LAST\n"); if( printVectorInfo )printf("last is %f %f \n", lastPoint.x, lastPoint.y); } } //bool doubleConic = false; int nextIndex = j+1; if( nextIndex >= endPos){ nextIndex = startPos; } ofPoint nextPoint( (float)vec[nextIndex].x, -(float)vec[nextIndex].y ); if( printVectorInfo )printf("--- last point is %f %f \n", lastPoint.x, lastPoint.y); bool nextIsConnic = ( FT_CURVE_TAG( tags[nextIndex] ) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG( tags[nextIndex]) != FT_CURVE_TAG_CUBIC ); //create a 'virtual on point' if we have two connic points if( nextIsConnic ){ nextPoint = (conicPoint + nextPoint) / 2; if( printVectorInfo )printf("|_______ double connic!\n"); } if( printVectorInfo )printf("--- next point is %f %f \n", nextPoint.x, nextPoint.y); quad_bezier(testOutline, lastPoint.x, lastPoint.y, conicPoint.x, conicPoint.y, nextPoint.x, nextPoint.y, 8); if( nextIsConnic ){ lastPoint = nextPoint; } } } //end for } for(int g =0; g < (int)testOutline.size(); g++){ testOutline[g] /= 64.0f; } charOutlines.contours.push_back(ofTTFContour()); if( testOutline.size() ){ charOutlines.contours.back().pts = ofSimplifyContour(testOutline, (float)TTF_SHAPE_SIMPLIFICATION_AMNT); }else{ charOutlines.contours.back().pts = testOutline; } } return charOutlines; }
ah_outline_load( AH_Outline outline, FT_Fixed x_scale, FT_Fixed y_scale, FT_Face face ) { FT_Memory memory = outline->memory; FT_Error error = AH_Err_Ok; FT_Outline* source = &face->glyph->outline; FT_Int num_points = source->n_points; FT_Int num_contours = source->n_contours; AH_Point points; /* check arguments */ if ( !face || !face->size || face->glyph->format != FT_GLYPH_FORMAT_OUTLINE ) return AH_Err_Invalid_Argument; /* first of all, reallocate the contours array if necessary */ if ( num_contours > outline->max_contours ) { FT_Int new_contours = ( num_contours + 3 ) & -4; if ( FT_RENEW_ARRAY( outline->contours, outline->max_contours, new_contours ) ) goto Exit; outline->max_contours = new_contours; } /* then, reallocate the points, segments & edges arrays if needed -- */ /* note that we reserved two additional point positions, used to */ /* hint metrics appropriately */ /* */ if ( num_points + 2 > outline->max_points ) { FT_Int news = ( num_points + 2 + 7 ) & -8; FT_Int max = outline->max_points; if ( FT_RENEW_ARRAY( outline->points, max, news ) || FT_RENEW_ARRAY( outline->horz_edges, max * 2, news * 2 ) || FT_RENEW_ARRAY( outline->horz_segments, max * 2, news * 2 ) ) goto Exit; /* readjust some pointers */ outline->vert_edges = outline->horz_edges + news; outline->vert_segments = outline->horz_segments + news; outline->max_points = news; } outline->num_points = num_points; outline->num_contours = num_contours; outline->num_hedges = 0; outline->num_vedges = 0; outline->num_hsegments = 0; outline->num_vsegments = 0; /* We can't rely on the value of `FT_Outline.flags' to know the fill */ /* direction used for a glyph, given that some fonts are broken (e.g. */ /* the Arphic ones). We thus recompute it each time we need to. */ /* */ outline->vert_major_dir = AH_DIR_UP; outline->horz_major_dir = AH_DIR_LEFT; if ( ah_get_orientation( source ) > 1 ) { outline->vert_major_dir = AH_DIR_DOWN; outline->horz_major_dir = AH_DIR_RIGHT; } outline->x_scale = x_scale; outline->y_scale = y_scale; points = outline->points; if ( outline->num_points == 0 ) goto Exit; { /* do one thing at a time -- it is easier to understand, and */ /* the code is clearer */ AH_Point point; AH_Point point_limit = points + outline->num_points; /* compute coordinates */ { FT_Vector* vec = source->points; for ( point = points; point < point_limit; vec++, point++ ) { point->fx = vec->x; point->fy = vec->y; point->ox = point->x = FT_MulFix( vec->x, x_scale ); point->oy = point->y = FT_MulFix( vec->y, y_scale ); point->flags = 0; } } /* compute Bezier flags */ { char* tag = source->tags; for ( point = points; point < point_limit; point++, tag++ ) { switch ( FT_CURVE_TAG( *tag ) ) { case FT_CURVE_TAG_CONIC: point->flags = AH_FLAG_CONIC; break; case FT_CURVE_TAG_CUBIC: point->flags = AH_FLAG_CUBIC; break; default: ; } } } /* compute `next' and `prev' */ { FT_Int contour_index; AH_Point prev; AH_Point first; AH_Point end; contour_index = 0; first = points; end = points + source->contours[0]; prev = end; for ( point = points; point < point_limit; point++ ) { point->prev = prev; if ( point < end ) { point->next = point + 1; prev = point; } else { point->next = first; contour_index++; if ( point + 1 < point_limit ) { end = points + source->contours[contour_index]; first = point + 1; prev = end; } } } } /* set-up the contours array */ { AH_Point* contour = outline->contours; AH_Point* contour_limit = contour + outline->num_contours; short* end = source->contours; short idx = 0; for ( ; contour < contour_limit; contour++, end++ ) { contour[0] = points + idx; idx = (short)( end[0] + 1 ); } } /* compute directions of in & out vectors */ { for ( point = points; point < point_limit; point++ ) { AH_Point prev; AH_Point next; FT_Vector ivec, ovec; prev = point->prev; ivec.x = point->fx - prev->fx; ivec.y = point->fy - prev->fy; point->in_dir = ah_compute_direction( ivec.x, ivec.y ); next = point->next; ovec.x = next->fx - point->fx; ovec.y = next->fy - point->fy; point->out_dir = ah_compute_direction( ovec.x, ovec.y ); #ifndef AH_OPTION_NO_WEAK_INTERPOLATION if ( point->flags & (AH_FLAG_CONIC | AH_FLAG_CUBIC) ) { Is_Weak_Point: point->flags |= AH_FLAG_WEAK_INTERPOLATION; } else if ( point->out_dir == point->in_dir ) { AH_Angle angle_in, angle_out, delta; if ( point->out_dir != AH_DIR_NONE ) goto Is_Weak_Point; angle_in = ah_angle( &ivec ); angle_out = ah_angle( &ovec ); delta = angle_in - angle_out; if ( delta > AH_PI ) delta = AH_2PI - delta; if ( delta < 0 ) delta = -delta; if ( delta < 2 ) goto Is_Weak_Point; } else if ( point->in_dir == -point->out_dir ) goto Is_Weak_Point; #endif } } } Exit: return error; }
bool decompose_ft_outline(const FT_Outline& outline, bool flip_y, const mapserver::trans_affine& mtx, PathStorage& path) { FT_Vector v_last; FT_Vector v_control; FT_Vector v_start; double x1, y1, x2, y2, x3, y3; FT_Vector* point; FT_Vector* limit; char* tags; int n; // index of contour in outline int first; // index of first point in contour char tag; // current point's state first = 0; for(n = 0; n < outline.n_contours; n++) { int last; // index of last point in contour last = outline.contours[n]; limit = outline.points + last; v_start = outline.points[first]; v_last = outline.points[last]; v_control = v_start; point = outline.points + first; tags = outline.tags + first; tag = FT_CURVE_TAG(tags[0]); // A contour cannot start with a cubic control point! if(tag == FT_CURVE_TAG_CUBIC) return false; // check first point to determine origin if( tag == FT_CURVE_TAG_CONIC) { // first point is conic control. Yes, this happens. if(FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) { // start at last point if it is on the curve v_start = v_last; limit--; } else { // if both first and last points are conic, // start at their middle and record its position // for closure v_start.x = (v_start.x + v_last.x) / 2; v_start.y = (v_start.y + v_last.y) / 2; v_last = v_start; } point--; tags--; } x1 = int26p6_to_dbl(v_start.x); y1 = int26p6_to_dbl(v_start.y); if(flip_y) y1 = -y1; mtx.transform(&x1, &y1); path.move_to(x1,y1); while(point < limit) { point++; tags++; tag = FT_CURVE_TAG(tags[0]); switch(tag) { case FT_CURVE_TAG_ON: // emit a single line_to { x1 = int26p6_to_dbl(point->x); y1 = int26p6_to_dbl(point->y); if(flip_y) y1 = -y1; mtx.transform(&x1, &y1); path.line_to(x1,y1); //path.line_to(conv(point->x), flip_y ? -conv(point->y) : conv(point->y)); continue; } case FT_CURVE_TAG_CONIC: // consume conic arcs { v_control.x = point->x; v_control.y = point->y; Do_Conic: if(point < limit) { FT_Vector vec; FT_Vector v_middle; point++; tags++; tag = FT_CURVE_TAG(tags[0]); vec.x = point->x; vec.y = point->y; if(tag == FT_CURVE_TAG_ON) { x1 = int26p6_to_dbl(v_control.x); y1 = int26p6_to_dbl(v_control.y); x2 = int26p6_to_dbl(vec.x); y2 = int26p6_to_dbl(vec.y); if(flip_y) { y1 = -y1; y2 = -y2; } mtx.transform(&x1, &y1); mtx.transform(&x2, &y2); path.curve3(x1,y1,x2,y2); continue; } if(tag != FT_CURVE_TAG_CONIC) return false; v_middle.x = (v_control.x + vec.x) / 2; v_middle.y = (v_control.y + vec.y) / 2; x1 = int26p6_to_dbl(v_control.x); y1 = int26p6_to_dbl(v_control.y); x2 = int26p6_to_dbl(v_middle.x); y2 = int26p6_to_dbl(v_middle.y); if(flip_y) { y1 = -y1; y2 = -y2; } mtx.transform(&x1, &y1); mtx.transform(&x2, &y2); path.curve3(x1,y1,x2,y2); //path.curve3(conv(v_control.x), // flip_y ? -conv(v_control.y) : conv(v_control.y), // conv(v_middle.x), // flip_y ? -conv(v_middle.y) : conv(v_middle.y)); v_control = vec; goto Do_Conic; } x1 = int26p6_to_dbl(v_control.x); y1 = int26p6_to_dbl(v_control.y); x2 = int26p6_to_dbl(v_start.x); y2 = int26p6_to_dbl(v_start.y); if(flip_y) { y1 = -y1; y2 = -y2; } mtx.transform(&x1, &y1); mtx.transform(&x2, &y2); path.curve3(x1,y1,x2,y2); //path.curve3(conv(v_control.x), // flip_y ? -conv(v_control.y) : conv(v_control.y), // conv(v_start.x), // flip_y ? -conv(v_start.y) : conv(v_start.y)); goto Close; } default: // FT_CURVE_TAG_CUBIC { FT_Vector vec1, vec2; if(point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC) { return false; } vec1.x = point[0].x; vec1.y = point[0].y; vec2.x = point[1].x; vec2.y = point[1].y; point += 2; tags += 2; if(point <= limit) { FT_Vector vec; vec.x = point->x; vec.y = point->y; x1 = int26p6_to_dbl(vec1.x); y1 = int26p6_to_dbl(vec1.y); x2 = int26p6_to_dbl(vec2.x); y2 = int26p6_to_dbl(vec2.y); x3 = int26p6_to_dbl(vec.x); y3 = int26p6_to_dbl(vec.y); if(flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } mtx.transform(&x1, &y1); mtx.transform(&x2, &y2); mtx.transform(&x3, &y3); path.curve4(x1,y1,x2,y2,x3,y3); //path.curve4(conv(vec1.x), // flip_y ? -conv(vec1.y) : conv(vec1.y), // conv(vec2.x), // flip_y ? -conv(vec2.y) : conv(vec2.y), // conv(vec.x), // flip_y ? -conv(vec.y) : conv(vec.y)); continue; } x1 = int26p6_to_dbl(vec1.x); y1 = int26p6_to_dbl(vec1.y); x2 = int26p6_to_dbl(vec2.x); y2 = int26p6_to_dbl(vec2.y); x3 = int26p6_to_dbl(v_start.x); y3 = int26p6_to_dbl(v_start.y); if(flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } mtx.transform(&x1, &y1); mtx.transform(&x2, &y2); mtx.transform(&x3, &y3); path.curve4(x1,y1,x2,y2,x3,y3); //path.curve4(conv(vec1.x), // flip_y ? -conv(vec1.y) : conv(vec1.y), // conv(vec2.x), // flip_y ? -conv(vec2.y) : conv(vec2.y), // conv(v_start.x), // flip_y ? -conv(v_start.y) : conv(v_start.y)); goto Close; } } } path.close_polygon(); Close: first = last + 1; } return true; }
static FT_Error ah_hinter_compute_blues( AH_Hinter* hinter ) { AH_Blue blue; AH_Globals* globals = &hinter->globals->design; FT_Pos flats [MAX_TEST_CHARACTERS]; FT_Pos rounds[MAX_TEST_CHARACTERS]; FT_Int num_flats; FT_Int num_rounds; FT_Face face; FT_GlyphSlot glyph; FT_Error error; FT_CharMap charmap; face = hinter->face; glyph = face->glyph; /* save current charmap */ charmap = face->charmap; /* do we have a Unicode charmap in there? */ error = FT_Select_Charmap( face, ft_encoding_unicode ); if ( error ) goto Exit; /* we compute the blues simply by loading each character from the */ /* 'blue_chars[blues]' string, then compute its top-most and */ /* bottom-most points */ AH_LOG(( "blue zones computation\n" )); AH_LOG(( "------------------------------------------------\n" )); for ( blue = ah_blue_capital_top; blue < ah_blue_max; blue++ ) { const char* p = blue_chars[blue]; const char* limit = p + MAX_TEST_CHARACTERS; FT_Pos *blue_ref, *blue_shoot; AH_LOG(( "blue %3d: ", blue )); num_flats = 0; num_rounds = 0; for ( ; p < limit; p++ ) { FT_UInt glyph_index; FT_Vector* extremum; FT_Vector* points; FT_Vector* point_limit; FT_Vector* point; FT_Bool round; /* exit if we reach the end of the string */ if ( !*p ) break; AH_LOG(( "`%c'", *p )); /* load the character in the face -- skip unknown or empty ones */ glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p ); if ( glyph_index == 0 ) continue; error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); if ( error || glyph->outline.n_points <= 0 ) continue; /* now compute min or max point indices and coordinates */ points = glyph->outline.points; point_limit = points + glyph->outline.n_points; point = points; extremum = point; point++; if ( AH_IS_TOP_BLUE( blue ) ) { for ( ; point < point_limit; point++ ) if ( point->y > extremum->y ) extremum = point; } else { for ( ; point < point_limit; point++ ) if ( point->y < extremum->y ) extremum = point; } AH_LOG(( "%5d", (int)extremum->y )); /* now, check whether the point belongs to a straight or round */ /* segment; we first need to find in which contour the extremum */ /* lies, then see its previous and next points */ { FT_Int idx = (FT_Int)( extremum - points ); FT_Int n; FT_Int first, last, prev, next, end; FT_Pos dist; last = -1; first = 0; for ( n = 0; n < glyph->outline.n_contours; n++ ) { end = glyph->outline.contours[n]; if ( end >= idx ) { last = end; break; } first = end + 1; } /* XXX: should never happen! */ if ( last < 0 ) continue; /* now look for the previous and next points that are not on the */ /* same Y coordinate. Threshold the `closeness'... */ prev = idx; next = prev; do { if ( prev > first ) prev--; else prev = last; dist = points[prev].y - extremum->y; if ( dist < -5 || dist > 5 ) break; } while ( prev != idx ); do { if ( next < last ) next++; else next = first; dist = points[next].y - extremum->y; if ( dist < -5 || dist > 5 ) break; } while ( next != idx ); /* now, set the `round' flag depending on the segment's kind */ round = FT_BOOL( FT_CURVE_TAG( glyph->outline.tags[prev] ) != FT_Curve_Tag_On || FT_CURVE_TAG( glyph->outline.tags[next] ) != FT_Curve_Tag_On ); AH_LOG(( "%c ", round ? 'r' : 'f' )); } if ( round ) rounds[num_rounds++] = extremum->y; else flats[num_flats++] = extremum->y; } AH_LOG(( "\n" )); /* we have computed the contents of the `rounds' and `flats' tables, */ /* now determine the reference and overshoot position of the blue; */ /* we simply take the median value after a simple short */ sort_values( num_rounds, rounds ); sort_values( num_flats, flats ); blue_ref = globals->blue_refs + blue; blue_shoot = globals->blue_shoots + blue; if ( num_flats == 0 && num_rounds == 0 ) { *blue_ref = -10000; *blue_shoot = -10000; } else if ( num_flats == 0 ) { *blue_ref = *blue_shoot = rounds[num_rounds / 2]; } else if ( num_rounds == 0 ) { *blue_ref = *blue_shoot = flats[num_flats / 2]; } else { *blue_ref = flats[num_flats / 2]; *blue_shoot = rounds[num_rounds / 2]; } /* there are sometimes problems: if the overshoot position of top */ /* zones is under its reference position, or the opposite for bottom */ /* zones. We must thus check everything there and correct the errors */ if ( *blue_shoot != *blue_ref ) { FT_Pos ref = *blue_ref; FT_Pos shoot = *blue_shoot; FT_Bool over_ref = FT_BOOL( shoot > ref ); if ( AH_IS_TOP_BLUE( blue ) ^ over_ref ) *blue_shoot = *blue_ref = ( shoot + ref ) / 2; } AH_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot )); } /* reset original face charmap */ FT_Set_Charmap( face, charmap ); error = 0; Exit: return error; }
static void af_latin_metrics_init_blues( AF_LatinMetrics metrics, FT_Face face ) { FT_Pos flats [AF_LATIN_MAX_TEST_CHARACTERS]; FT_Pos rounds[AF_LATIN_MAX_TEST_CHARACTERS]; FT_Int num_flats; FT_Int num_rounds; FT_Int bb; AF_LatinBlue blue; FT_Error error; AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT]; FT_GlyphSlot glyph = face->glyph; /* we compute the blues simply by loading each character from the */ /* 'af_latin_blue_chars[blues]' string, then compute its top-most or */ /* bottom-most points (depending on `AF_IS_TOP_BLUE') */ AF_LOG(( "blue zones computation\n" )); AF_LOG(( "------------------------------------------------\n" )); for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ ) { const char* p = af_latin_blue_chars[bb]; const char* limit = p + AF_LATIN_MAX_TEST_CHARACTERS; FT_Pos* blue_ref; FT_Pos* blue_shoot; AF_LOG(( "blue %3d: ", bb )); num_flats = 0; num_rounds = 0; for ( ; p < limit && *p; p++ ) { FT_UInt glyph_index; FT_Int best_point, best_y, best_first, best_last; FT_Vector* points; FT_Bool round = 0; AF_LOG(( "'%c'", *p )); /* load the character in the face -- skip unknown or empty ones */ glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p ); if ( glyph_index == 0 ) continue; error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE ); if ( error || glyph->outline.n_points <= 0 ) continue; /* now compute min or max point indices and coordinates */ points = glyph->outline.points; best_point = -1; best_y = 0; /* make compiler happy */ best_first = 0; /* ditto */ best_last = 0; /* ditto */ { FT_Int nn; FT_Int first = 0; FT_Int last = -1; for ( nn = 0; nn < glyph->outline.n_contours; first = last+1, nn++ ) { FT_Int old_best_point = best_point; FT_Int pp; last = glyph->outline.contours[nn]; /* Avoid single-point contours since they are never rasterized. */ /* In some fonts, they correspond to mark attachment points */ /* which are way outside of the glyph's real outline. */ if ( last <= first ) continue; if ( AF_LATIN_IS_TOP_BLUE( bb ) ) { for ( pp = first; pp <= last; pp++ ) if ( best_point < 0 || points[pp].y > best_y ) { best_point = pp; best_y = points[pp].y; } } else { for ( pp = first; pp <= last; pp++ ) if ( best_point < 0 || points[pp].y < best_y ) { best_point = pp; best_y = points[pp].y; } } if ( best_point != old_best_point ) { best_first = first; best_last = last; } } AF_LOG(( "%5d", best_y )); } /* now check whether the point belongs to a straight or round */ /* segment; we first need to find in which contour the extremum */ /* lies, then inspect its previous and next points */ if ( best_point >= 0 ) { FT_Int prev, next; FT_Pos dist; /* now look for the previous and next points that are not on the */ /* same Y coordinate. Threshold the `closeness'... */ prev = best_point; next = prev; do { if ( prev > best_first ) prev--; else prev = best_last; dist = points[prev].y - best_y; if ( dist < -5 || dist > 5 ) break; } while ( prev != best_point ); do { if ( next < best_last ) next++; else next = best_first; dist = points[next].y - best_y; if ( dist < -5 || dist > 5 ) break; } while ( next != best_point ); /* now, set the `round' flag depending on the segment's kind */ round = FT_BOOL( FT_CURVE_TAG( glyph->outline.tags[prev] ) != FT_CURVE_TAG_ON || FT_CURVE_TAG( glyph->outline.tags[next] ) != FT_CURVE_TAG_ON ); AF_LOG(( "%c ", round ? 'r' : 'f' )); } if ( round ) rounds[num_rounds++] = best_y; else flats[num_flats++] = best_y; } AF_LOG(( "\n" )); if ( num_flats == 0 && num_rounds == 0 ) { /* * we couldn't find a single glyph to compute this blue zone, * we will simply ignore it then */ AF_LOG(( "empty!\n" )); continue; } /* we have computed the contents of the `rounds' and `flats' tables, */ /* now determine the reference and overshoot position of the blue -- */ /* we simply take the median value after a simple sort */ af_sort_pos( num_rounds, rounds ); af_sort_pos( num_flats, flats ); blue = & axis->blues[axis->blue_count]; blue_ref = & blue->ref.org; blue_shoot = & blue->shoot.org; axis->blue_count++; if ( num_flats == 0 ) { *blue_ref = *blue_shoot = rounds[num_rounds / 2]; } else if ( num_rounds == 0 ) { *blue_ref = *blue_shoot = flats[num_flats / 2]; } else { *blue_ref = flats[num_flats / 2]; *blue_shoot = rounds[num_rounds / 2]; } /* there are sometimes problems: if the overshoot position of top */ /* zones is under its reference position, or the opposite for bottom */ /* zones. We must thus check everything there and correct the errors */ if ( *blue_shoot != *blue_ref ) { FT_Pos ref = *blue_ref; FT_Pos shoot = *blue_shoot; FT_Bool over_ref = FT_BOOL( shoot > ref ); if ( AF_LATIN_IS_TOP_BLUE( bb ) ^ over_ref ) *blue_shoot = *blue_ref = ( shoot + ref ) / 2; } blue->flags = 0; if ( AF_LATIN_IS_TOP_BLUE( bb ) ) blue->flags |= AF_LATIN_BLUE_TOP; /* * The following flags is used later to adjust the y and x scales * in order to optimize the pixel grid alignment of the top of small * letters. */ if ( bb == AF_LATIN_BLUE_SMALL_TOP ) blue->flags |= AF_LATIN_BLUE_ADJUSTMENT; AF_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot )); } return; }
af_glyph_hints_reload( AF_GlyphHints hints, FT_Outline* outline ) { FT_Error error = FT_Err_Ok; AF_Point points; FT_UInt old_max, new_max; AF_Scaler scaler = &hints->metrics->scaler; FT_Fixed x_scale = hints->x_scale; FT_Fixed y_scale = hints->y_scale; FT_Pos x_delta = hints->x_delta; FT_Pos y_delta = hints->y_delta; FT_Memory memory = hints->memory; hints->scaler_flags = scaler->flags; hints->num_points = 0; hints->num_contours = 0; hints->axis[0].num_segments = 0; hints->axis[0].num_edges = 0; hints->axis[1].num_segments = 0; hints->axis[1].num_edges = 0; /* first of all, reallocate the contours array when necessary */ new_max = (FT_UInt) outline->n_contours; old_max = hints->max_contours; if ( new_max > old_max ) { new_max = (new_max + 3) & ~3; if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) ) goto Exit; hints->max_contours = new_max; } /* then, reallocate the points, segments & edges arrays if needed -- * note that we reserved two additional point positions, used to * hint metrics appropriately */ new_max = (FT_UInt)( outline->n_points + 2 ); old_max = hints->max_points; if ( new_max > old_max ) { FT_Byte* items; FT_ULong off1, off2, off3; /* we store in a single buffer the following arrays: * * - an array of N AF_PointRec items * - an array of 2*N AF_SegmentRec items * - an array of 2*N AF_EdgeRec items * */ new_max = ( new_max + 2 + 7 ) & ~7; #define OFF_PAD2(x,y) (((x)+(y)-1) & ~((y)-1)) #define OFF_PADX(x,y) ((((x)+(y)-1)/(y))*(y)) #define OFF_PAD(x,y) ( ((y) & ((y)-1)) ? OFF_PADX(x,y) : OFF_PAD2(x,y) ) #undef OFF_INCREMENT #define OFF_INCREMENT( _off, _type, _count ) \ ( OFF_PAD( _off, sizeof(_type) ) + (_count)*sizeof(_type)) off1 = OFF_INCREMENT( 0, AF_PointRec, new_max ); off2 = OFF_INCREMENT( off1, AF_SegmentRec, new_max*2 ); off3 = OFF_INCREMENT( off2, AF_EdgeRec, new_max*2 ); FT_FREE( hints->points ); if ( FT_ALLOC( items, off3 ) ) { hints->max_points = 0; hints->axis[0].segments = NULL; hints->axis[0].edges = NULL; hints->axis[1].segments = NULL; hints->axis[1].edges = NULL; goto Exit; } /* readjust some pointers */ hints->max_points = new_max; hints->points = (AF_Point) items; hints->axis[0].segments = (AF_Segment)( items + off1 ); hints->axis[1].segments = hints->axis[0].segments + new_max; hints->axis[0].edges = (AF_Edge) ( items + off2 ); hints->axis[1].edges = hints->axis[0].edges + new_max; } hints->num_points = outline->n_points; hints->num_contours = outline->n_contours; /* We can't rely on the value of `FT_Outline.flags' to know the fill */ /* direction used for a glyph, given that some fonts are broken (e.g. */ /* the Arphic ones). We thus recompute it each time we need to. */ /* */ hints->axis[ AF_DIMENSION_HORZ ].major_dir = AF_DIR_UP; hints->axis[ AF_DIMENSION_VERT ].major_dir = AF_DIR_LEFT; if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT ) { hints->axis[ AF_DIMENSION_HORZ ].major_dir = AF_DIR_DOWN; hints->axis[ AF_DIMENSION_VERT ].major_dir = AF_DIR_RIGHT; } hints->x_scale = x_scale; hints->y_scale = y_scale; hints->x_delta = x_delta; hints->y_delta = y_delta; points = hints->points; if ( hints->num_points == 0 ) goto Exit; { AF_Point point; AF_Point point_limit = points + hints->num_points; /* compute coordinates & bezier flags */ { FT_Vector* vec = outline->points; char* tag = outline->tags; for ( point = points; point < point_limit; point++, vec++, tag++ ) { point->fx = vec->x; point->fy = vec->y; point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta; point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta; switch ( FT_CURVE_TAG( *tag ) ) { case FT_CURVE_TAG_CONIC: point->flags = AF_FLAG_CONIC; break; case FT_CURVE_TAG_CUBIC: point->flags = AF_FLAG_CUBIC; break; default: point->flags = 0; ; } } } /* compute `next' and `prev' */ { FT_Int contour_index; AF_Point prev; AF_Point first; AF_Point end; contour_index = 0; first = points; end = points + outline->contours[0]; prev = end; for ( point = points; point < point_limit; point++ ) { point->prev = prev; if ( point < end ) { point->next = point + 1; prev = point; } else { point->next = first; contour_index++; if ( point + 1 < point_limit ) { end = points + outline->contours[contour_index]; first = point + 1; prev = end; } } } } /* set-up the contours array */ { AF_Point* contour = hints->contours; AF_Point* contour_limit = contour + hints->num_contours; short* end = outline->contours; short idx = 0; for ( ; contour < contour_limit; contour++, end++ ) { contour[0] = points + idx; idx = (short)( end[0] + 1 ); } } /* compute directions of in & out vectors */ { for ( point = points; point < point_limit; point++ ) { AF_Point prev; AF_Point next; FT_Pos in_x, in_y, out_x, out_y; prev = point->prev; in_x = point->fx - prev->fx; in_y = point->fy - prev->fy; point->in_dir = af_direction_compute( in_x, in_y ); next = point->next; out_x = next->fx - point->fx; out_y = next->fy - point->fy; point->out_dir = af_direction_compute( out_x, out_y ); if ( point->flags & ( AF_FLAG_CONIC | AF_FLAG_CUBIC ) ) { Is_Weak_Point: point->flags |= AF_FLAG_WEAK_INTERPOLATION; } else if ( point->out_dir == point->in_dir ) { AF_Angle angle_in, angle_out, delta; if ( point->out_dir != AF_DIR_NONE ) goto Is_Weak_Point; angle_in = af_angle_atan( in_x, in_y ); angle_out = af_angle_atan( out_x, out_y ); delta = af_angle_diff( angle_in, angle_out ); if ( delta < 2 && delta > -2 ) goto Is_Weak_Point; } else if ( point->in_dir == -point->out_dir ) goto Is_Weak_Point; } } } /* compute inflection points */ af_glyph_hints_compute_inflections( hints ); Exit: return error; }
bool RenderOutline(const FT_Outline& outline, Painter& path, double xx, double yy) { FT_Vector v_last; FT_Vector v_control; FT_Vector v_start; FT_Vector* point; FT_Vector* limit; char* tags; int n; // index of contour in outline char tag; // current point's state int first = 0; // index of first point in contour for(n = 0; n < outline.n_contours; n++) { int last = outline.contours[n]; limit = outline.points + last; v_start = outline.points[first]; v_last = outline.points[last]; v_control = v_start; point = outline.points + first; tags = outline.tags + first; tag = FT_CURVE_TAG(tags[0]); if(tag == FT_CURVE_TAG_CUBIC) return false; if(tag == FT_CURVE_TAG_CONIC) { if(FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) { // start at last point if it is on the curve v_start = v_last; limit--; } else { // if both first and last points are conic, // start at their middle and record its position // for closure v_start.x = (v_start.x + v_last.x) / 2; v_start.y = (v_start.y + v_last.y) / 2; v_last = v_start; } point--; tags--; } path.Move(ft_dbl(v_start.x) + xx, -ft_dbl(v_start.y) + yy); while(point < limit) { point++; tags++; tag = FT_CURVE_TAG(tags[0]); switch(tag) { case FT_CURVE_TAG_ON: path.Line(ft_dbl(point->x) + xx, -ft_dbl(point->y) + yy); continue; case FT_CURVE_TAG_CONIC: v_control.x = point->x; v_control.y = point->y; Do_Conic: if(point < limit) { FT_Vector vec; FT_Vector v_middle; point++; tags++; tag = FT_CURVE_TAG(tags[0]); vec.x = point->x; vec.y = point->y; if(tag == FT_CURVE_TAG_ON) { path.Quadratic(ft_dbl(v_control.x) + xx, -ft_dbl(v_control.y) + yy, ft_dbl(vec.x) + xx, -ft_dbl(vec.y) + yy); continue; } if(tag != FT_CURVE_TAG_CONIC) return false; v_middle.x = (v_control.x + vec.x) / 2; v_middle.y = (v_control.y + vec.y) / 2; path.Quadratic(ft_dbl(v_control.x) + xx, -ft_dbl(v_control.y) + yy, ft_dbl(v_middle.x) + xx, -ft_dbl(v_middle.y) + yy); v_control = vec; goto Do_Conic; } path.Quadratic(ft_dbl(v_control.x) + xx, -ft_dbl(v_control.y) + yy, ft_dbl(v_start.x) + xx, -ft_dbl(v_start.y) + yy); goto Close; default: FT_Vector vec1, vec2; if(point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC) return false; vec1.x = point[0].x; vec1.y = point[0].y; vec2.x = point[1].x; vec2.y = point[1].y; point += 2; tags += 2; if(point <= limit) { FT_Vector vec; vec.x = point->x; vec.y = point->y; path.Cubic(ft_dbl(vec1.x) + xx, -ft_dbl(vec1.y) + yy, ft_dbl(vec2.x) + xx, -ft_dbl(vec2.y) + yy, ft_dbl(vec.x) + xx, -ft_dbl(vec.y) + yy); continue; } path.Cubic(ft_dbl(vec1.x) + xx, -ft_dbl(vec1.y) + yy, ft_dbl(vec2.x) + xx, -ft_dbl(vec2.y) + yy, ft_dbl(v_start.x) + xx, -ft_dbl(v_start.y) + yy); goto Close; } } Close: path.Close(); first = last + 1; } return true; }
FTContour::FTContour(FT_Vector* contour, char* tags, unsigned int n) { FTPoint prev, cur(contour[(n - 1) % n]), next(contour[0]); FTPoint a, b = next - cur; double olddir, dir = atan2((next - cur).Y(), (next - cur).X()); double angle = 0.0; // See http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-6.html // for a full description of FreeType tags. for(unsigned int i = 0; i < n; i++) { prev = cur; cur = next; next = FTPoint(contour[(i + 1) % n]); olddir = dir; dir = atan2((next - cur).Y(), (next - cur).X()); // Compute our path's new direction. double t = dir - olddir; if(t < -M_PI) t += 2 * M_PI; if(t > M_PI) t -= 2 * M_PI; angle += t; // Only process point tags we know. if(n < 2 || FT_CURVE_TAG(tags[i]) == FT_Curve_Tag_On) { AddPoint(cur); } else if(FT_CURVE_TAG(tags[i]) == FT_Curve_Tag_Conic) { FTPoint prev2 = prev, next2 = next; // Previous point is either the real previous point (an "on" // point), or the midpoint between the current one and the // previous "conic off" point. if(FT_CURVE_TAG(tags[(i - 1 + n) % n]) == FT_Curve_Tag_Conic) { prev2 = (cur + prev) * 0.5; AddPoint(prev2); } // Next point is either the real next point or the midpoint. if(FT_CURVE_TAG(tags[(i + 1) % n]) == FT_Curve_Tag_Conic) { next2 = (cur + next) * 0.5; } evaluateQuadraticCurve(prev2, cur, next2); } else if(FT_CURVE_TAG(tags[i]) == FT_Curve_Tag_Cubic && FT_CURVE_TAG(tags[(i + 1) % n]) == FT_Curve_Tag_Cubic) { evaluateCubicCurve(prev, cur, next, FTPoint(contour[(i + 2) % n])); } } // If final angle is positive (+2PI), it's an anti-clockwise contour, // otherwise (-2PI) it's clockwise. clockwise = (angle < 0.0); }