FT_Outline_EmboldenXY( FT_Outline* outline, FT_Pos xstrength, FT_Pos ystrength ) { FT_Vector* points; FT_Vector v_prev, v_first, v_next, v_cur; FT_Int c, n, first; FT_Int orientation; if ( !outline ) return FT_Err_Invalid_Argument; xstrength /= 2; ystrength /= 2; if ( xstrength == 0 && ystrength == 0 ) return FT_Err_Ok; orientation = FT_Outline_Get_Orientation( outline ); if ( orientation == FT_ORIENTATION_NONE ) { if ( outline->n_contours ) return FT_Err_Invalid_Argument; else return FT_Err_Ok; } points = outline->points; first = 0; for ( c = 0; c < outline->n_contours; c++ ) { FT_Vector in, out, shift; FT_Fixed l_in, l_out, d; int last = outline->contours[c]; v_first = points[first]; v_prev = points[last]; v_cur = v_first; /* compute the incoming vector and its length */ in.x = v_cur.x - v_prev.x; in.y = v_cur.y - v_prev.y; l_in = FT_Vector_Length( &in ); for ( n = first; n <= last; n++ ) { if ( n < last ) v_next = points[n + 1]; else v_next = v_first; /* compute the outgoing vector and its length */ out.x = v_next.x - v_cur.x; out.y = v_next.y - v_cur.y; l_out = FT_Vector_Length( &out ); d = l_in * l_out + in.x * out.x + in.y * out.y; /* shift only if turn is less then ~160 degrees */ if ( 16 * d > l_in * l_out ) { /* shift components are rotated */ shift.x = FT_DivFix( l_out * in.y + l_in * out.y, d ); shift.y = FT_DivFix( l_out * in.x + l_in * out.x, d ); if ( orientation == FT_ORIENTATION_TRUETYPE ) shift.x = -shift.x; else shift.y = -shift.y; shift.x = FT_MulFix( xstrength, shift.x ); shift.y = FT_MulFix( ystrength, shift.y ); } else shift.x = shift.y = 0; outline->points[n].x = v_cur.x + xstrength + shift.x; outline->points[n].y = v_cur.y + ystrength + shift.y; in = out; l_in = l_out; v_cur = v_next; } first = last + 1; } return FT_Err_Ok; }
FT_Outline_EmboldenXY( FT_Outline* outline, FT_Pos xstrength, FT_Pos ystrength ) { FT_Vector* points; FT_Vector v_prev, v_first, v_next, v_cur; FT_Int c, n, first; FT_Int orientation; if ( !outline ) return FT_Err_Invalid_Argument; xstrength /= 2; ystrength /= 2; if ( xstrength == 0 && ystrength == 0 ) return FT_Err_Ok; orientation = FT_Outline_Get_Orientation( outline ); if ( orientation == FT_ORIENTATION_NONE ) { if ( outline->n_contours ) return FT_Err_Invalid_Argument; else return FT_Err_Ok; } points = outline->points; first = 0; for ( c = 0; c < outline->n_contours; c++ ) { FT_Vector in, out, shift; FT_Fixed l_in, l_out, l, q, d; int last = outline->contours[c]; v_first = points[first]; v_prev = points[last]; v_cur = v_first; /* compute the incoming vector and its length */ in.x = v_cur.x - v_prev.x; in.y = v_cur.y - v_prev.y; l_in = FT_Vector_Length( &in ); for ( n = first; n <= last; n++ ) { if ( n < last ) v_next = points[n + 1]; else v_next = v_first; /* compute the outgoing vector and its length */ out.x = v_next.x - v_cur.x; out.y = v_next.y - v_cur.y; l_out = FT_Vector_Length( &out ); d = l_in * l_out + in.x * out.x + in.y * out.y; /* shift only if turn is less then ~160 degrees */ if ( 16 * d > l_in * l_out ) { /* shift components are aligned along bisector */ /* and directed according to the outline orientation. */ shift.x = l_out * in.y + l_in * out.y; shift.y = l_out * in.x + l_in * out.x; if ( orientation == FT_ORIENTATION_TRUETYPE ) shift.x = -shift.x; else shift.y = -shift.y; /* threshold strength to better handle collapsing segments */ l = FT_MIN( l_in, l_out ); q = out.x * in.y - out.y * in.x; if ( orientation == FT_ORIENTATION_TRUETYPE ) q = -q; if ( FT_MulDiv( xstrength, q, l ) < d ) shift.x = FT_MulDiv( shift.x, xstrength, d ); else shift.x = FT_MulDiv( shift.x, l, q ); if ( FT_MulDiv( ystrength, q, l ) < d ) shift.y = FT_MulDiv( shift.y, ystrength, d ); else shift.y = FT_MulDiv( shift.y, l, q ); } else shift.x = shift.y = 0; outline->points[n].x = v_cur.x + xstrength + shift.x; outline->points[n].y = v_cur.y + ystrength + shift.y; in = out; l_in = l_out; v_cur = v_next; } first = last + 1; } return FT_Err_Ok; }
FontText * _PGFT_LoadFontText(FreeTypeInstance *ft, PgFontObject *fontobj, const FontRenderMode *mode, PGFT_String *text) { Py_ssize_t string_length = PGFT_String_GET_LENGTH(text); PGFT_char * buffer = PGFT_String_GET_DATA(text); PGFT_char * buffer_end; PGFT_char * ch; FontText *ftext = &(PGFT_INTERNALS(fontobj)->active_text); FontGlyph *glyph = 0; FontGlyph **glyph_array = 0; FontMetrics *metrics; FT_BitmapGlyph image; TextContext context; FT_Face font; FT_Size_Metrics *sz_metrics; FT_Vector pen = {0, 0}; /* untransformed origin */ FT_Vector pen1 = {0, 0}; FT_Vector pen2; FT_Vector *next_pos; int vertical = mode->render_flags & FT_RFLAG_VERTICAL; int use_kerning = mode->render_flags & FT_RFLAG_KERNING; int pad = mode->render_flags & FT_RFLAG_PAD; FT_UInt prev_glyph_index = 0; /* All these are 16.16 precision */ FT_Angle rotation_angle = mode->rotation_angle; /* All these are 26.6 precision */ FT_Vector kerning; FT_Pos min_x = FX6_MAX; FT_Pos max_x = FX6_MIN; FT_Pos min_y = FX6_MAX; FT_Pos max_y = FX6_MIN; FT_Pos glyph_width; FT_Pos glyph_height; FT_Pos top = FX6_MIN; FT_Fixed y_scale; FT_Error error = 0; /* load our sized font */ font = _PGFT_GetFontSized(ft, fontobj, mode->pt_size); if (!font) { PyErr_SetString(PyExc_SDLError, _PGFT_GetError(ft)); return 0; } sz_metrics = &font->size->metrics; y_scale = sz_metrics->y_scale; /* cleanup the cache */ _PGFT_Cache_Cleanup(&ftext->glyph_cache); /* create the text struct */ if (string_length > ftext->buffer_size) { _PGFT_free(ftext->glyphs); ftext->glyphs = (FontGlyph **) _PGFT_malloc((size_t)string_length * sizeof(FontGlyph *)); if (!ftext->glyphs) { PyErr_NoMemory(); return 0; } _PGFT_free(ftext->posns); ftext->posns = (FT_Vector *) _PGFT_malloc((size_t)string_length * sizeof(FT_Vector)); if (!ftext->posns) { PyErr_NoMemory(); return 0; } ftext->buffer_size = string_length; } ftext->length = string_length; ftext->ascender = sz_metrics->ascender; ftext->underline_pos = -FT_MulFix(font->underline_position, y_scale); ftext->underline_size = FT_MulFix(font->underline_thickness, y_scale); if (mode->style & FT_STYLE_STRONG) { FT_Fixed bold_str = mode->strength * sz_metrics->x_ppem; ftext->underline_size = FT_MulFix(ftext->underline_size, FX16_ONE + bold_str / 4); } /* fill it with the glyphs */ fill_context(&context, ft, fontobj, mode, font); glyph_array = ftext->glyphs; next_pos = ftext->posns; for (ch = buffer, buffer_end = ch + string_length; ch < buffer_end; ++ch) { pen2.x = pen1.x; pen2.y = pen1.y; pen1.x = pen.x; pen1.y = pen.y; /* * Load the corresponding glyph from the cache */ glyph = _PGFT_Cache_FindGlyph(*((FT_UInt32 *)ch), mode, &ftext->glyph_cache, &context); if (!glyph) { --ftext->length; continue; } image = glyph->image; glyph_width = glyph->width; glyph_height = glyph->height; /* * Do size calculations for all the glyphs in the text */ if (use_kerning && prev_glyph_index) { error = FT_Get_Kerning(font, prev_glyph_index, glyph->glyph_index, FT_KERNING_UNFITTED, &kerning); if (error) { _PGFT_SetError(ft, "Loading glyphs", error); PyErr_SetString(PyExc_SDLError, _PGFT_GetError(ft)); return 0; } if (rotation_angle != 0) { FT_Vector_Rotate(&kerning, rotation_angle); } pen.x += FX6_ROUND(kerning.x); pen.y += FX6_ROUND(kerning.y); if (FT_Vector_Length(&pen2) > FT_Vector_Length(&pen)) { pen.x = pen2.x; pen.y = pen2.y; } } prev_glyph_index = glyph->glyph_index; metrics = vertical ? &glyph->v_metrics : &glyph->h_metrics; if (metrics->bearing_rotated.y > top) { top = metrics->bearing_rotated.y; } if (pen.x + metrics->bearing_rotated.x < min_x) { min_x = pen.x + metrics->bearing_rotated.x; } if (pen.x + metrics->bearing_rotated.x + glyph_width > max_x) { max_x = pen.x + metrics->bearing_rotated.x + glyph_width; } next_pos->x = pen.x + metrics->bearing_rotated.x; pen.x += metrics->advance_rotated.x; if (vertical) { if (pen.y + metrics->bearing_rotated.y < min_y) { min_y = pen.y + metrics->bearing_rotated.y; } if (pen.y + metrics->bearing_rotated.y + glyph_height > max_y) { max_y = pen.y + metrics->bearing_rotated.y + glyph_height; } next_pos->y = pen.y + metrics->bearing_rotated.y; pen.y += metrics->advance_rotated.y; } else { if (pen.y - metrics->bearing_rotated.y < min_y) { min_y = pen.y - metrics->bearing_rotated.y; } if (pen.y - metrics->bearing_rotated.y + glyph_height > max_y) { max_y = pen.y - metrics->bearing_rotated.y + glyph_height; } next_pos->y = pen.y - metrics->bearing_rotated.y; pen.y -= metrics->advance_rotated.y; } *glyph_array++ = glyph; ++next_pos; } if (ftext->length == 0) { min_x = 0; max_x = 0; if (vertical) { ftext->min_y = 0; max_y = sz_metrics->height; } else { FT_Size_Metrics *sz_metrics = &font->size->metrics; min_y = -sz_metrics->ascender; max_y = -sz_metrics->descender; } } if (pad) { FT_Size_Metrics *sz_metrics = &font->size->metrics; if (pen.x > max_x) { max_x = pen.x; } else if (pen.x < min_x) { min_x = pen.x; } if (pen.y > max_y) { max_y = pen.y; } else if (pen.y < min_y) { min_y = pen.y; } if (vertical) { FT_Fixed right = sz_metrics->max_advance / 2; if (max_x < right) { max_x = right; } if (min_x > -right) { min_x = -right; } if (min_y > 0) { min_y = 0; } else if (max_y < pen.y) { max_y = pen.y; } } else { FT_Fixed ascender = sz_metrics->ascender; FT_Fixed descender = sz_metrics->descender; if (min_x > 0) { min_x = 0; } if (max_x < pen.x) { max_x = pen.x; } if (min_y > -ascender) { min_y = -ascender; } if (max_y <= -descender) { max_y = -descender + /* baseline */ FX6_ONE; } } } ftext->left = FX6_TRUNC(FX6_FLOOR(min_x)); ftext->top = FX6_TRUNC(FX6_CEIL(top)); ftext->min_x = min_x; ftext->max_x = max_x; ftext->min_y = min_y; ftext->max_y = max_y; ftext->advance.x = pen.x; ftext->advance.y = pen.y; return ftext; }