void WriteGlyphAsTGA(FT_Library &library, const std::string &fileName, wchar_t ch, FT_Face &face, int size, const Pixel32 &fontCol, const Pixel32 outlineCol, float outlineWidth) { // Set the size to use. if (FT_Set_Char_Size(face, size << 6, size << 6, 90, 90) == 0) { // Load the glyph we are looking for. FT_UInt gindex = FT_Get_Char_Index(face, ch); if (FT_Load_Glyph(face, gindex, FT_LOAD_NO_BITMAP) == 0) { // Need an outline for this to work. if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { // Render the basic glyph to a span list. Spans spans; RenderSpans(library, &face->glyph->outline, &spans); // Next we need the spans for the outline. Spans outlineSpans; // Set up a stroker. FT_Stroker stroker; FT_Stroker_New(library, &stroker); FT_Stroker_Set(stroker, (int)(outlineWidth * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph glyph; if (FT_Get_Glyph(face->glyph, &glyph) == 0) { FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1); // Again, this needs to be an outline to work. if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { // Render the outline spans to the span list FT_Outline *o = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline; RenderSpans(library, o, &outlineSpans); } // Clean up afterwards. FT_Stroker_Done(stroker); FT_Done_Glyph(glyph); // Now we need to put it all together. if (!spans.empty()) { // Figure out what the bounding rect is for both the span lists. Rect rect(spans.front().x, spans.front().y, spans.front().x, spans.front().y); for (Spans::iterator s = spans.begin(); s != spans.end(); ++s) { rect.Include(Vec2(s->x, s->y)); rect.Include(Vec2(s->x + s->width - 1, s->y)); } for (Spans::iterator s = outlineSpans.begin(); s != outlineSpans.end(); ++s) { rect.Include(Vec2(s->x, s->y)); rect.Include(Vec2(s->x + s->width - 1, s->y)); } #if 0 // This is unused in this test but you would need this to draw // more than one glyph. float bearingX = face->glyph->metrics.horiBearingX >> 6; float bearingY = face->glyph->metrics.horiBearingY >> 6; float advance = face->glyph->advance.x >> 6; #endif // Get some metrics of our image. int imgWidth = rect.Width(), imgHeight = rect.Height(), imgSize = imgWidth * imgHeight; // Allocate data for our image and clear it out to transparent. Pixel32 *pxl = new Pixel32[imgSize]; memset(pxl, 0, sizeof(Pixel32) * imgSize); // Loop over the outline spans and just draw them into the // image. for (Spans::iterator s = outlineSpans.begin(); s != outlineSpans.end(); ++s) for (int w = 0; w < s->width; ++w) pxl[(int)((imgHeight - 1 - (s->y - rect.ymin)) * imgWidth + s->x - rect.xmin + w)] = Pixel32(outlineCol.r, outlineCol.g, outlineCol.b, s->coverage); // Then loop over the regular glyph spans and blend them into // the image. for (Spans::iterator s = spans.begin(); s != spans.end(); ++s) for (int w = 0; w < s->width; ++w) { Pixel32 &dst = pxl[(int)((imgHeight - 1 - (s->y - rect.ymin)) * imgWidth + s->x - rect.xmin + w)]; Pixel32 src = Pixel32(fontCol.r, fontCol.g, fontCol.b, s->coverage); dst.r = (int)(dst.r + ((src.r - dst.r) * src.a) / 255.0f); dst.g = (int)(dst.g + ((src.g - dst.g) * src.a) / 255.0f); dst.b = (int)(dst.b + ((src.b - dst.b) * src.a) / 255.0f); dst.a = MIN(255, dst.a + src.a); } // Dump the image to disk. WriteTGA(fileName, pxl, imgWidth, imgHeight); delete [] pxl; } }
void Font::Printf(u32 x, u32 y,const char *a, ...){ char text[1024]; va_list va; va_start(va, a); vsnprintf(text, sizeof text, a, va); va_end(va); size_t len = strlen(a); if(len>0){ len=strlen(text); vec.x = 0; vec.y = FontSize; FT_GlyphSlot slot = face->glyph; FT_UInt glyph_index = 0; FT_UInt previous_glyph = 0; Kerning = FT_HAS_KERNING(face); for(unsigned int i=0;i<len;i++){ glyph_index = FT_Get_Char_Index(face, text[i]); if(Kerning && previous_glyph && glyph_index){ FT_Vector delta; FT_Get_Kerning(face, previous_glyph, glyph_index, FT_KERNING_DEFAULT, &delta); vec.x += delta.x >> 6; } FT_Load_Glyph(face, glyph_index,FT_LOAD_RENDER); FT_Get_Glyph(face->glyph, &glyph); FT_Glyph_StrokeBorder(&glyph,stroker,0,0); FontDrawBitmap(&slot->bitmap,vec.x + slot->bitmap_left + x, (vec.y - slot->bitmap_top + y -FontSize)); previous_glyph = glyph_index; vec.x += slot->advance.x >> 6; vec.y += slot->advance.y >> 6; FT_Done_Glyph(glyph); }
bool TTBMFont::addFontGlyph(int fontnum,FT_UInt glyphIndex,wchar32_t chr) { FT_Error error; FT_Face face=fontFaces_[fontnum].face; error = FT_Load_Glyph(face, glyphIndex, FT_LOAD_DEFAULT); if (error) return false; int top, left, width, height; if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { FT_BBox bbox; if (stroker) { FT_Glyph glyph; error = FT_Get_Glyph(face->glyph, &glyph); if (error) return false; error = FT_Glyph_StrokeBorder(&glyph, stroker, false, true); if (error) return false; FT_OutlineGlyph oGlyph = reinterpret_cast<FT_OutlineGlyph>(glyph); FT_Outline_Get_CBox(&oGlyph->outline, &bbox); FT_Done_Glyph(glyph); } else FT_Outline_Get_CBox(&face->glyph->outline, &bbox); bbox.xMin &= ~63; bbox.yMin &= ~63; bbox.xMax = (bbox.xMax + 63) & ~63; bbox.yMax = (bbox.yMax + 63) & ~63; width = (bbox.xMax - bbox.xMin) >> 6; height = (bbox.yMax - bbox.yMin) >> 6; top = bbox.yMax >> 6; left = bbox.xMin >> 6; } else if (face->glyph->format == FT_GLYPH_FORMAT_BITMAP) {
void Glyph::SetOutline(FT_Library& library, int outlineThickness) { _error = FT_Stroker_New(library, &_stroker); if (_error) return; FT_Stroker_Set(_stroker, outlineThickness * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); _error = FT_Glyph_StrokeBorder(&_glyph, _stroker, false, true); if (_error) return; FT_Raster_Params params; memset(¶ms, 0, sizeof(params)); params.target = 0; params.flags = FT_RASTER_FLAG_DIRECT | FT_RASTER_FLAG_AA; params.user = this; params.gray_spans = Glyph::SpanCallback; FT_OutlineGlyph outlineGlyph = reinterpret_cast<FT_OutlineGlyph>(_glyph); _error = FT_Outline_Render(library, &outlineGlyph->outline, ¶ms); if (_error) return; }
JNIEXPORT jlong JNICALL Java_com_badlogic_gdx_graphics_g2d_freetype_FreeType_00024Glyph_strokeBorder(JNIEnv* env, jclass clazz, jlong glyph, jlong stroker, jboolean inside) { //@line:522 FT_Glyph border_glyph = (FT_Glyph)glyph; FT_Glyph_StrokeBorder(&border_glyph, (FT_Stroker)stroker, inside, 1); return (jlong)border_glyph; }
void SubtitleRenderer::load_glyph(char32_t codepoint) { VGfloat escapement[2]{}; auto load_glyph_internal = [&](FT_Face ft_face, VGFont vg_font, bool border) { try { auto glyph_index = FT_Get_Char_Index(ft_face, codepoint); ENFORCE(!FT_Load_Glyph(ft_face, glyph_index, FT_LOAD_NO_HINTING)); FT_Glyph glyph; ENFORCE(!FT_Get_Glyph(ft_face->glyph, &glyph)); SCOPE_EXIT {FT_Done_Glyph(glyph);}; if (border) ENFORCE(!FT_Glyph_StrokeBorder(&glyph, ft_stroker_, 0, 1)); ENFORCE(!FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, 1)); FT_BitmapGlyph bit_glyph = (FT_BitmapGlyph) glyph; FT_Bitmap& bitmap = bit_glyph->bitmap; VGImage image{}; VGfloat glyph_origin[2]{}; if (bitmap.width > 0 && bitmap.rows > 0) { constexpr VGfloat blur_stddev = 0.6; const int padding = static_cast<int>(3*blur_stddev + 0.5); const int image_width = bitmap.width + padding*2; const int image_height = bitmap.rows + padding*2; image = vgCreateImage(VG_A_8, image_width, image_height, VG_IMAGE_QUALITY_NONANTIALIASED); assert(image); if (bitmap.pitch > 0) { vgImageSubData(image, bitmap.buffer + bitmap.pitch*(bitmap.rows-1), -bitmap.pitch, VG_A_8, padding, padding, bitmap.width, bitmap.rows); assert(!vgGetError()); } else { vgImageSubData(image, bitmap.buffer, bitmap.pitch, VG_A_8, padding, padding, bitmap.width, bitmap.rows); assert(!vgGetError()); } auto softened_image = vgCreateImage(VG_A_8, image_width, image_height, VG_IMAGE_QUALITY_NONANTIALIASED); assert(image); // Even out hard and soft edges vgGaussianBlur(softened_image, image, blur_stddev, blur_stddev, VG_TILE_FILL); assert(!vgGetError()); vgDestroyImage(image); assert(!vgGetError()); image = softened_image; glyph_origin[0] = static_cast<VGfloat>(padding - bit_glyph->left); glyph_origin[1] = static_cast<VGfloat>(padding + bitmap.rows - bit_glyph->top - 1); } escapement[0] = static_cast<VGfloat>((ft_face->glyph->advance.x + 32) / 64); escapement[1] = 0; vgSetGlyphToImage(vg_font, codepoint, image, glyph_origin, escapement); assert(!vgGetError()); if (image) { vgDestroyImage(image); assert(!vgGetError()); } } catch(...) { escapement[0] = 0; escapement[1] = 0; vgSetGlyphToImage(vg_font, codepoint, VG_INVALID_HANDLE, escapement, escapement); assert(!vgGetError()); } }; load_glyph_internal(ft_face_, vg_font_, false); glyphs_[codepoint].advance = escapement[0]; load_glyph_internal(ft_face_, vg_font_border_, true); }
// ----------------------------------------------- texture_font_load_glyphs --- size_t texture_font_load_glyphs( texture_font_t * self, const wchar_t * charcodes ) { assert( self ); assert( charcodes ); size_t i, x, y, width, height, depth, w, h; FT_Library library; FT_Error error; FT_Face face; FT_Glyph ft_glyph; FT_GlyphSlot slot; FT_Bitmap ft_bitmap; FT_UInt glyph_index; texture_glyph_t *glyph; ivec4 region; size_t missed = 0; width = self->atlas->width; height = self->atlas->height; depth = self->atlas->depth; region.x=1; region.y=1; #ifdef RENDERSTRING stringwidth=0; stringheight=0; stringshift=0; #endif if( !texture_font_load_face( &library, self->filename, self->size, &face ) ) { return wcslen(charcodes); } /* Load each glyph */ for( i=0; i<wcslen(charcodes); ++i ) { glyph_index = FT_Get_Char_Index( face, charcodes[i] ); // WARNING: We use texture-atlas depth to guess if user wants // LCD subpixel rendering FT_Int32 flags = 0; if( self->outline_type > 0 ) { flags |= FT_LOAD_NO_BITMAP; } else { flags |= FT_LOAD_RENDER; } if( !self->hinting ) { flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT; } else { flags |= FT_LOAD_FORCE_AUTOHINT; } if( depth == 3 ) { FT_Library_SetLcdFilter( library, FT_LCD_FILTER_LIGHT ); flags |= FT_LOAD_TARGET_LCD; if( self->filtering ) { // FT_Library_SetLcdFilterWeights( library, self->lcd_weights ); } } error = FT_Load_Glyph( face, glyph_index, flags ); if( error ) { fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n", __LINE__, FT_Errors[error].code, FT_Errors[error].message ); FT_Done_FreeType( library ); return wcslen(charcodes)-i; } int ft_bitmap_width = 0; int ft_bitmap_rows = 0; int ft_bitmap_pitch = 0; int ft_glyph_top = 0; int ft_glyph_left = 0; if( self->outline_type == 0 ) { slot = face->glyph; ft_bitmap = slot->bitmap; ft_bitmap_width = slot->bitmap.width; ft_bitmap_rows = slot->bitmap.rows; ft_bitmap_pitch = slot->bitmap.pitch; ft_glyph_top = slot->bitmap_top; ft_glyph_left = slot->bitmap_left; } else { FT_Stroker stroker; error = FT_Stroker_New( library, &stroker ); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } FT_Stroker_Set( stroker, (int)(self->outline_thickness *64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); error = FT_Get_Glyph( face->glyph, &ft_glyph); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } if( self->outline_type == 1 ) { error = FT_Glyph_Stroke( &ft_glyph, stroker, 1 ); } else if ( self->outline_type == 2 ) { error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 0, 1 ); } else if ( self->outline_type == 3 ) { error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 1, 1 ); } if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } if( depth == 1) { error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } } else { error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } } FT_BitmapGlyph ft_bitmap_glyph = (FT_BitmapGlyph) ft_glyph; ft_bitmap = ft_bitmap_glyph->bitmap; ft_bitmap_width = ft_bitmap.width; ft_bitmap_rows = ft_bitmap.rows; ft_bitmap_pitch = ft_bitmap.pitch; ft_glyph_top = ft_bitmap_glyph->top; ft_glyph_left = ft_bitmap_glyph->left; FT_Stroker_Done(stroker); } // We want each glyph to be separated by at least one black pixel // (for example for shader used in demo-subpixel.c) w = ft_bitmap_width/depth + 1; h = ft_bitmap_rows + 1; #ifdef RENDERSTRING static size_t maxh=0; if (charcodes[i]==13) { stringshift+=maxh+1; region.x=1; } if (h>maxh) maxh=h; //region.y=stringshift+maxh-h+1+(h-ft_glyph_top); region.y=stringshift+maxh-ft_glyph_top+1; // if (stringshift+h+h-ft_glyph_top+1>stringheight) stringheight=stringshift+h+h-ft_glyph_top+1; if (region.y+h>stringheight) stringheight=region.y+h; if (region.x+w>stringwidth) stringwidth=region.x+w; // if (region.y>=height) { missed++; continue; } // if (region.x+w>=width) {missed++; continue; } // if (h+h-ft_glyph_top+1>=height) { missed++; continue; } // if (h+h-ft_glyph_top+2>=height) { missed++; continue; } // if (region.y+maxh>=height) { missed++; continue; } #else region = texture_atlas_get_region( self->atlas, w, h ); if ( region.x < 0 ) { missed++; // fprintf( stderr, "Texture atlas is full (line %d)\n", __LINE__ ); continue; } #endif w = w - 1; h = h - 1; x = region.x; y = region.y; if (charcodes[i]!=13) { #ifdef RENDERSTRING if (!(x<width)||!((x+w)<=width)||!(y<height)||!((y+h)<=height)) { missed++; continue; } #endif texture_atlas_set_region( self->atlas, x, y, w, h, ft_bitmap.buffer, ft_bitmap.pitch ); } glyph = texture_glyph_new( ); glyph->charcode = charcodes[i]; glyph->width = w; glyph->height = h; glyph->outline_type = self->outline_type; glyph->outline_thickness = self->outline_thickness; glyph->offset_x = ft_glyph_left; glyph->offset_y = ft_glyph_top; glyph->s0 = x/(float)width; glyph->t0 = y/(float)height; glyph->s1 = (x + glyph->width)/(float)width; glyph->t1 = (y + glyph->height)/(float)height; // Discard hinting to get advance FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING); slot = face->glyph; glyph->advance_x = slot->advance.x/64.0; glyph->advance_y = slot->advance.y/64.0; #ifdef RENDERSTRING if (charcodes[i]!=13) region.x+=(int)glyph->advance_x; #endif vector_push_back( self->glyphs, &glyph ); } if( self->outline_type > 0 ) { FT_Done_Glyph( ft_glyph ); } FT_Done_Face( face ); FT_Done_FreeType( library ); // texture_atlas_upload( self->atlas ); texture_font_generate_kerning( self ); return missed; }
/** * Load glyphs corresponding to the UTF-32 codepoint code. */ static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code) { DrawTextContext *s = ctx->priv; FT_BitmapGlyph bitmapglyph; Glyph *glyph; struct AVTreeNode *node = NULL; int ret; /* load glyph into s->face->glyph */ if (FT_Load_Char(s->face, code, s->ft_load_flags)) return AVERROR(EINVAL); glyph = av_mallocz(sizeof(*glyph)); if (!glyph) { ret = AVERROR(ENOMEM); goto error; } glyph->code = code; if (FT_Get_Glyph(s->face->glyph, &glyph->glyph)) { ret = AVERROR(EINVAL); goto error; } if (s->borderw) { glyph->border_glyph = glyph->glyph; if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0) || FT_Glyph_To_Bitmap(&glyph->border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) { ret = AVERROR_EXTERNAL; goto error; } bitmapglyph = (FT_BitmapGlyph) glyph->border_glyph; glyph->border_bitmap = bitmapglyph->bitmap; } if (FT_Glyph_To_Bitmap(&glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) { ret = AVERROR_EXTERNAL; goto error; } bitmapglyph = (FT_BitmapGlyph) glyph->glyph; glyph->bitmap = bitmapglyph->bitmap; glyph->bitmap_left = bitmapglyph->left; glyph->bitmap_top = bitmapglyph->top; glyph->advance = s->face->glyph->advance.x >> 6; /* measure text height to calculate text_height (or the maximum text height) */ FT_Glyph_Get_CBox(glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox); /* cache the newly created glyph */ if (!(node = av_tree_node_alloc())) { ret = AVERROR(ENOMEM); goto error; } av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node); if (glyph_ptr) *glyph_ptr = glyph; return 0; error: if (glyph) av_freep(&glyph->glyph); av_freep(&glyph); av_freep(&node); return ret; }
/* * Load the glyphs of a paragraph. When shaping with HarfBuzz the glyph indices * have already been determined at this point, as well as the advance values. */ static int LoadGlyphs( filter_t *p_filter, paragraph_t *p_paragraph, bool b_use_glyph_indices, bool b_overwrite_advance ) { if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 ) { msg_Err( p_filter, "LoadGlyphs() invalid parameters. " "Paragraph size: %d. Runs count %d", p_paragraph->i_size, p_paragraph->i_runs_count ); return VLC_EGENERIC; } filter_sys_t *p_sys = p_filter->p_sys; for( int i = 0; i < p_paragraph->i_runs_count; ++i ) { run_desc_t *p_run = p_paragraph->p_runs + i; text_style_t *p_style = p_run->p_style; FT_Face p_face = 0; if( !p_run->p_face ) { p_face = LoadFace( p_filter, p_style ); if( !p_face ) { p_face = p_sys->p_face; p_style = &p_sys->style; p_run->p_style = p_style; } p_run->p_face = p_face; } else p_face = p_run->p_face; if( p_sys->p_stroker ) { double f_outline_thickness = var_InheritInteger( p_filter, "freetype-outline-thickness" ) / 100.0; f_outline_thickness = VLC_CLIP( f_outline_thickness, 0.0, 0.5 ); int i_radius = ( p_style->i_font_size << 6 ) * f_outline_thickness; FT_Stroker_Set( p_sys->p_stroker, i_radius, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 ); } for( int j = p_run->i_start_offset; j < p_run->i_end_offset; ++j ) { int i_glyph_index; if( b_use_glyph_indices ) i_glyph_index = p_paragraph->pi_glyph_indices[ j ]; else i_glyph_index = FT_Get_Char_Index( p_face, p_paragraph->p_code_points[ j ] ); glyph_bitmaps_t *p_bitmaps = p_paragraph->p_glyph_bitmaps + j; if( FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_DEFAULT ) && FT_Load_Glyph( p_face, i_glyph_index, FT_LOAD_DEFAULT ) ) { p_bitmaps->p_glyph = 0; p_bitmaps->p_outline = 0; p_bitmaps->p_shadow = 0; p_bitmaps->i_x_advance = 0; p_bitmaps->i_y_advance = 0; continue; } if( ( p_style->i_style_flags & STYLE_BOLD ) && !( p_face->style_flags & FT_STYLE_FLAG_BOLD ) ) FT_GlyphSlot_Embolden( p_face->glyph ); if( ( p_style->i_style_flags & STYLE_ITALIC ) && !( p_face->style_flags & FT_STYLE_FLAG_ITALIC ) ) FT_GlyphSlot_Oblique( p_face->glyph ); if( FT_Get_Glyph( p_face->glyph, &p_bitmaps->p_glyph ) ) { p_bitmaps->p_glyph = 0; p_bitmaps->p_outline = 0; p_bitmaps->p_shadow = 0; p_bitmaps->i_x_advance = 0; p_bitmaps->i_y_advance = 0; continue; } if( p_filter->p_sys->p_stroker ) { p_bitmaps->p_outline = p_bitmaps->p_glyph; if( FT_Glyph_StrokeBorder( &p_bitmaps->p_outline, p_filter->p_sys->p_stroker, 0, 0 ) ) p_bitmaps->p_outline = 0; } if( p_filter->p_sys->style.i_shadow_alpha > 0 ) p_bitmaps->p_shadow = p_bitmaps->p_outline ? p_bitmaps->p_outline : p_bitmaps->p_glyph; if( b_overwrite_advance ) { p_bitmaps->i_x_advance = p_face->glyph->advance.x; p_bitmaps->i_y_advance = p_face->glyph->advance.y; } } } return VLC_SUCCESS; }
Glyph::Glyph(FT_Library &library, FontFace &fontFace, int c, int outlineWidth, bool hinting) : mFont(fontFace) { mChar = c; FT_Face &face = fontFace.GetFTFace(); int flags = FT_LOAD_DEFAULT; if (!hinting) { flags = FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; } FT_Error error = FT_Load_Char(face, c, flags); if (error) { return; } mGlyphIndex = FT_Get_Char_Index(face, c); // Load The Glyph For Our Character. if(FT_Load_Glyph( face, mGlyphIndex, flags )) throw std::runtime_error("FT_Load_Glyph failed"); // Move The Face's Glyph Into A Glyph Object. FT_Glyph glyph; if(FT_Get_Glyph( face->glyph, &glyph )) throw std::runtime_error("FT_Get_Glyph failed"); FT_Stroker stroker; FT_Stroker_New(library, &stroker); FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph_StrokeBorder(&glyph, stroker, false, true); FT_OutlineGlyph olglyph = reinterpret_cast<FT_OutlineGlyph>(glyph); FT_Outline outline = olglyph->outline; RenderSpans(library, &outline); FT_Stroker_Done(stroker); // Get metrics FT_Glyph_Metrics metrics = face->glyph->metrics; mAdvance.x = metrics.horiAdvance * kOneOver64; mAdvance.y = metrics.vertAdvance * kOneOver64; mBearing.x = metrics.horiBearingX * kOneOver64; mBearing.y = metrics.horiBearingY * kOneOver64; mSize.x = glm::round(metrics.width * kOneOver64); mSize.y = glm::round(metrics.height * kOneOver64); // Adjust for outline? mAdvance.x += outlineWidth; // Draw spans if(mSpans.size() > 0) { GlyphSpan front = mSpans.front(); Rect bounds(front.x, front.y, front.x, front.y); for(int i = 0; i < mSpans.size(); i++) { bounds.Include(mSpans[i].x, mSpans[i].y + 1); bounds.Include(mSpans[i].x + mSpans[i].width, mSpans[i].y); } int width = bounds.GetWidth(); int height = bounds.GetHeight(); mDataSize.x = width; mDataSize.y = height; int size = width * height; mBuffer = new unsigned char[size]; memset(mBuffer, 0, size); for(int i = 0; i < mSpans.size(); i++) { GlyphSpan &span = mSpans[i]; for (int w = 0; w < span.width; ++w) { mBuffer[(int)((height - 1 - (span.y - bounds.top)) * width + span.x - bounds.left + w)] = span.coverage; } } } FT_Done_Glyph(glyph); }
long EMSCRIPTEN_KEEPALIVE c_Glyph_strokeBorder(long glyph, long stroker, int inside) { FT_Glyph border_glyph = (FT_Glyph)glyph; FT_Glyph_StrokeBorder(&border_glyph, (FT_Stroker)stroker, inside, 1); return (long)border_glyph; }
// ----------------------------------------------- texture_font_load_glyphs --- size_t texture_font_load_glyphs( texture_font_t * self, const wchar_t * charcodes ) { size_t i, j, x, y, width, height, depth, w, h; FT_Library library; FT_Error error; FT_Face face; FT_Glyph ft_glyph; FT_GlyphSlot slot; FT_Bitmap ft_bitmap; FT_UInt glyph_index; texture_glyph_t *glyph; FT_Int32 flags = 0; int ft_glyph_top = 0; int ft_glyph_left = 0; ivec4 region; size_t missed = 0; char pass; assert( self ); assert( charcodes ); width = self->atlas->width; height = self->atlas->height; depth = self->atlas->depth; if (!texture_font_get_face(self, &library, &face)) return wcslen(charcodes); /* Load each glyph */ for( i = 0; i < wcslen(charcodes); ++i ) { pass = 0; /* Check if charcode has been already loaded */ for( j = 0; j < self->glyphs->size; ++j ) { glyph = *(texture_glyph_t **) vector_get( self->glyphs, j ); // If charcode is -1, we don't care about outline type or thickness // if( (glyph->charcode == charcodes[i])) { if( (glyph->charcode == charcodes[i]) && ((charcodes[i] == (wchar_t)(-1) ) || ((glyph->outline_type == self->outline_type) && (glyph->outline_thickness == self->outline_thickness)) )) { pass = 1; break; } } if(pass) continue; flags = 0; ft_glyph_top = 0; ft_glyph_left = 0; glyph_index = FT_Get_Char_Index( face, charcodes[i] ); // WARNING: We use texture-atlas depth to guess if user wants // LCD subpixel rendering if( self->outline_type > 0 ) { flags |= FT_LOAD_NO_BITMAP; } else { flags |= FT_LOAD_RENDER; } if( !self->hinting ) { flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT; } else { flags |= FT_LOAD_FORCE_AUTOHINT; } if( depth == 3 ) { FT_Library_SetLcdFilter( library, FT_LCD_FILTER_LIGHT ); flags |= FT_LOAD_TARGET_LCD; if( self->filtering ) { FT_Library_SetLcdFilterWeights( library, self->lcd_weights ); } } error = FT_Load_Glyph( face, glyph_index, flags ); if( error ) { FT_Done_Face( face ); FT_Done_FreeType( library ); throw wcslen(charcodes)-i; } if( self->outline_type == 0 ) { slot = face->glyph; ft_bitmap = slot->bitmap; ft_glyph_top = slot->bitmap_top; ft_glyph_left = slot->bitmap_left; } else { FT_Stroker stroker; FT_BitmapGlyph ft_bitmap_glyph; error = FT_Stroker_New( library, &stroker ); if( error ) { FT_Done_Face( face ); FT_Stroker_Done( stroker ); FT_Done_FreeType( library ); throw; } FT_Stroker_Set(stroker, (int)(self->outline_thickness * HRES), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); error = FT_Get_Glyph( face->glyph, &ft_glyph); if( error ) { FT_Done_Face( face ); FT_Stroker_Done( stroker ); FT_Done_FreeType( library ); throw; } if( self->outline_type == 1 ) { error = FT_Glyph_Stroke( &ft_glyph, stroker, 1 ); } else if ( self->outline_type == 2 ) { error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 0, 1 ); } else if ( self->outline_type == 3 ) { error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 1, 1 ); } if( error ) { FT_Done_Face( face ); FT_Stroker_Done( stroker ); FT_Done_FreeType( library ); throw; } if( depth == 1) { error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1); if( error ) { FT_Done_Face( face ); FT_Stroker_Done( stroker ); FT_Done_FreeType( library ); throw; } } else { error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1); if( error ) { FT_Done_Face( face ); FT_Stroker_Done( stroker ); FT_Done_FreeType( library ); throw; } } ft_bitmap_glyph = (FT_BitmapGlyph) ft_glyph; ft_bitmap = ft_bitmap_glyph->bitmap; ft_glyph_top = ft_bitmap_glyph->top; ft_glyph_left = ft_bitmap_glyph->left; FT_Stroker_Done(stroker); } // We want each glyph to be separated by at least one black pixel // (for example for shader used in demo-subpixel.c) w = ft_bitmap.width/depth + 1; h = ft_bitmap.rows + 1; region = texture_atlas_get_region( self->atlas, w, h ); if ( region.x < 0 ) { missed++; fprintf( stderr, "Texture atlas is full (line %d)\n", __LINE__ ); continue; } w = w - 1; h = h - 1; x = region.x; y = region.y; texture_atlas_set_region( self->atlas, x, y, w, h, ft_bitmap.buffer, ft_bitmap.pitch ); glyph = texture_glyph_new( ); glyph->charcode = charcodes[i]; glyph->width = w; glyph->height = h; glyph->outline_type = self->outline_type; glyph->outline_thickness = self->outline_thickness; glyph->offset_x = ft_glyph_left; glyph->offset_y = ft_glyph_top; glyph->s0 = x/(float)width; glyph->t0 = y/(float)height; glyph->s1 = (x + glyph->width)/(float)width; glyph->t1 = (y + glyph->height)/(float)height; // Discard hinting to get advance FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING); slot = face->glyph; glyph->advance_x = slot->advance.x / HRESf; glyph->advance_y = slot->advance.y / HRESf; vector_push_back( self->glyphs, &glyph ); if( self->outline_type > 0 ) { FT_Done_Glyph( ft_glyph ); } } FT_Done_Face( face ); FT_Done_FreeType( library ); texture_atlas_upload( self->atlas ); texture_font_generate_kerning( self ); return missed; }
const CFontGlyph *CFreetypeFontFace::PrepareChar(wchar_t character, bool& bGlyphRestFlag) { bGlyphRestFlag = false; m_glyphMapLocker.lock(); auto itr = m_glyphMap.find(character); CFreetypeFontGlyph *pGlyph = itr != m_glyphMap.end() ? down_cast<CFreetypeFontGlyph *>(itr->second) : nullptr; m_glyphMapLocker.unlock(); if (!pGlyph) { float outlineWidth = GetBorderWeight() * GetScaleFactor(); ApplyFTSize(); FT_Face pFontFace = m_pFont->GetFontFace(); BEATS_ASSERT(pFontFace != NULL); bool bFindCharacterGlyph = FT_Get_Char_Index(pFontFace, character) != 0; BEYONDENGINE_UNUSED_PARAM(bFindCharacterGlyph); BEATS_ASSERT(bFindCharacterGlyph, _T("Character %d can't be found in all font face!"), character); FT_Error err = FT_Load_Char(pFontFace, character, FT_LOAD_NO_BITMAP); BEYONDENGINE_UNUSED_PARAM(err); BEATS_ASSERT(!err); FT_GlyphSlot pGlyphSlot = pFontFace->glyph; BEATS_ASSERT(pGlyphSlot != NULL); int32_t nBorderAdvanceX = pGlyphSlot->metrics.horiAdvance >> FT_SHIFT_NUM; int32_t nBorderAdvanceY = m_nLineHeight + (uint32_t)ceil(outlineWidth * 2.0f); int32_t nFontAdvanceX = nBorderAdvanceX; int32_t nFontHeight = 0; int32_t nBorderOriginWidth = 0; int32_t nFontOriginWidth = 0; int32_t nBorderHeight = 0; FT_BBox borderBox; FT_BBox fontBox; int32_t x = 0, y = 0; if (pGlyphSlot->format == FT_GLYPH_FORMAT_OUTLINE) { FT_Library ftLib = CFont::GetLibrary(); // Set up a stroker. FT_Stroker stroker; FT_Stroker_New(ftLib, &stroker); FT_Stroker_Set(stroker, (int32_t)(outlineWidth * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph pOutlineGlyph, pInnerGlyph; if (FT_Get_Glyph(pGlyphSlot, &pOutlineGlyph) == 0 && FT_Get_Glyph(pGlyphSlot, &pInnerGlyph) == 0) { FT_Glyph_StrokeBorder(&pOutlineGlyph, stroker, 0, 1); BEATS_ASSERT(pOutlineGlyph->format == FT_GLYPH_FORMAT_OUTLINE && pInnerGlyph->format == FT_GLYPH_FORMAT_OUTLINE); FT_Outline *pOutLine = &reinterpret_cast<FT_OutlineGlyph>(pOutlineGlyph)->outline; FT_Glyph_Get_CBox(pOutlineGlyph, FT_GLYPH_BBOX_GRIDFIT, &borderBox); FT_Glyph_Get_CBox(pInnerGlyph, FT_GLYPH_BBOX_GRIDFIT, &fontBox); nBorderOriginWidth = (borderBox.xMax - borderBox.xMin) >> FT_SHIFT_NUM; nFontOriginWidth = (fontBox.xMax - fontBox.xMin) >> FT_SHIFT_NUM; int32_t nBorderWidth = nextMOE(nBorderOriginWidth); // Because our GL_UNPACK_ALIGNMENT should be 8 here. nBorderHeight = (borderBox.yMax - borderBox.yMin) >> FT_SHIFT_NUM; nFontHeight = (fontBox.yMax - fontBox.yMin) >> FT_SHIFT_NUM; x = pGlyphSlot->metrics.horiBearingX >> FT_SHIFT_NUM; y = pGlyphSlot->metrics.horiBearingY >> FT_SHIFT_NUM; if(nBorderAdvanceX < x + nBorderOriginWidth) // It is true for most of the time, because border size always greater than nAdvanceX { nBorderAdvanceX = x + nBorderOriginWidth; } if (nFontAdvanceX < x + nFontOriginWidth) { nFontAdvanceX = nFontOriginWidth; } if(m_uCurrX + x + nBorderWidth > PAGE_WIDTH) { m_uCurrX = 0; m_uCurrY += (nBorderAdvanceY + m_nBorderSpace); if (m_uCurrY + nBorderAdvanceY > PAGE_HEIGHT) { BEATS_WARNING(false, "Freetype texture buffer overflow for %d glyphs, we will rebuild this texture buffer.", (uint32_t)m_glyphMap.size()); ResetGlyphs(); bGlyphRestFlag = true; return nullptr; } } int32_t nDataSize = nBorderWidth * nBorderHeight; float fBorderOffsetY = 1.0f; // Makes it look like a shadow. unsigned char* pBorderData = RenderFontDataToBmp(nBorderWidth, nBorderHeight, -borderBox.xMin, (int32_t)(-borderBox.yMin * fBorderOffsetY), pOutLine); FT_Outline *pInnerOutLine = &reinterpret_cast<FT_OutlineGlyph>(pInnerGlyph)->outline; unsigned char* pFontData = RenderFontDataToBmp(nBorderWidth, nBorderHeight, -borderBox.xMin, -borderBox.yMin, pInnerOutLine); unsigned char* pAllData = new unsigned char[nDataSize * 2]; for (int32_t i = 0; i < nDataSize; ++i) { pAllData[i * 2] = pBorderData[i]; pAllData[i * 2 + 1] = pFontData[i]; } BEATS_ASSERT(m_pTexture.Get() != nullptr); GLint nX = MAX((int32_t)m_uCurrX + x, 0); GLint nY = MAX((int32_t)m_uCurrY + (m_nAscender - y), 0); SFontUpdateImageInfo* pImageInfo = new SFontUpdateImageInfo; pImageInfo->m_pTexture = m_pTexture; pImageInfo->m_nWidth = nBorderWidth; pImageInfo->m_nHeight = nBorderHeight; pImageInfo->m_x = nX; pImageInfo->m_y = nY; pImageInfo->m_pData = pAllData; m_fontUpdateImageCacheMutex.lock(); m_fontUpdateImageCache.push_back(pImageInfo); m_fontUpdateImageCacheMutex.unlock(); // Clean up afterwards. FT_Stroker_Done(stroker); FT_Done_Glyph(pOutlineGlyph); FT_Done_Glyph(pInnerGlyph); BEATS_SAFE_DELETE_ARRAY(pBorderData); BEATS_SAFE_DELETE_ARRAY(pFontData); }