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 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; }
/** * \brief Change border width * * \param render_priv renderer state object * \param info glyph state object */ void change_border(ASS_Renderer *render_priv, double border_x, double border_y) { int bord = 64 * border_x * render_priv->border_scale; if (bord > 0 && border_x == border_y) { if (!render_priv->state.stroker) { int error; error = FT_Stroker_New(render_priv->ftlibrary, &render_priv->state.stroker); if (error) { ass_msg(render_priv->library, MSGL_V, "failed to get stroker"); render_priv->state.stroker = 0; } render_priv->state.stroker_radius = -1.0; } if (render_priv->state.stroker && render_priv->state.stroker_radius != bord) { FT_Stroker_Set(render_priv->state.stroker, bord, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); render_priv->state.stroker_radius = bord; } } else { FT_Stroker_Done(render_priv->state.stroker); render_priv->state.stroker = 0; } }
unsigned create_freetype_font() { if(!s_freetype) { unsigned error = FT_Init_FreeType( &s_freetype); if(!error) error = FT_Stroker_New(s_freetype, &m_stroker ); if(error)return error; } return 0; }
stroker_ptr freetype_engine::create_stroker() { FT_Stroker s; FT_Error error = FT_Stroker_New(library_, &s); if (!error) { return std::make_shared<stroker>(s); } return stroker_ptr(); }
stroker_ptr freetype_engine::create_stroker() { FT_Stroker s; FT_Error error = FT_Stroker_New(library_, &s); if (!error) { return stroker_ptr(new stroker(s)); } return stroker_ptr(); }
FTDemo_Handle* FTDemo_New( void ) { FTDemo_Handle* handle; handle = (FTDemo_Handle *)malloc( sizeof ( FTDemo_Handle ) ); if ( !handle ) return NULL; memset( handle, 0, sizeof ( FTDemo_Handle ) ); error = FT_Init_FreeType( &handle->library ); if ( error ) PanicZ( "could not initialize FreeType" ); error = FTC_Manager_New( handle->library, 0, 0, 0, my_face_requester, 0, &handle->cache_manager ); if ( error ) PanicZ( "could not initialize cache manager" ); error = FTC_SBitCache_New( handle->cache_manager, &handle->sbits_cache ); if ( error ) PanicZ( "could not initialize small bitmaps cache" ); error = FTC_ImageCache_New( handle->cache_manager, &handle->image_cache ); if ( error ) PanicZ( "could not initialize glyph image cache" ); error = FTC_CMapCache_New( handle->cache_manager, &handle->cmap_cache ); if ( error ) PanicZ( "could not initialize charmap cache" ); FT_Bitmap_New( &handle->bitmap ); FT_Stroker_New( handle->library, &handle->stroker ); handle->encoding = FT_ENCODING_NONE; handle->hinted = 1; handle->antialias = 1; handle->use_sbits = 1; handle->autohint = 0; handle->lcd_mode = 0; handle->color = 1; handle->use_sbits_cache = 1; /* string_init */ memset( handle->string, 0, sizeof ( TGlyph ) * MAX_GLYPHS ); handle->string_length = 0; handle->string_reload = 1; return handle; }
FT_Stroker GetStroker() { if (!m_library) return NULL; FT_Stroker stroker; if (FT_Stroker_New(m_library, &stroker)) return NULL; return stroker; };
JNIEXPORT jlong JNICALL Java_com_badlogic_gdx_graphics_g2d_freetype_FreeType_00024Library_strokerNew(JNIEnv* env, jclass clazz, jlong library) { //@line:104 FT_Stroker stroker; FT_Error error = FT_Stroker_New((FT_Library)library, &stroker); if(error) return 0; else return (jlong)stroker; }
Font::Font(u32 Size, const char *Font_Path, Minimum *min){ FontColor = COLOR_BLACK; FontSize = Size; Lenght = 0; m = min; FT_Init_FreeType(&library); FT_New_Face(library,Font_Path,0,&face); FT_Stroker_New(library,&stroker); Kerning = FT_HAS_KERNING(face); FT_Set_Pixel_Sizes(face,0,FontSize); font=0; }
bool Font::loadFromMemory(const void* data, std::size_t sizeInBytes) { // Cleanup the previous resources cleanup(); m_refCount = new int(1); // Initialize FreeType // Note: we initialize FreeType for every font instance in order to avoid having a single // global manager that would create a lot of issues regarding creation and destruction order. FT_Library library; if (FT_Init_FreeType(&library) != 0) { err() << "Failed to load font from memory (failed to initialize FreeType)" << std::endl; return false; } m_library = library; // Load the new font face from the specified file FT_Face face; if (FT_New_Memory_Face(static_cast<FT_Library>(m_library), reinterpret_cast<const FT_Byte*>(data), static_cast<FT_Long>(sizeInBytes), 0, &face) != 0) { err() << "Failed to load font from memory (failed to create the font face)" << std::endl; return false; } // Load the stroker that will be used to outline the font FT_Stroker stroker; if (FT_Stroker_New(static_cast<FT_Library>(m_library), &stroker) != 0) { err() << "Failed to load font from memory (failed to create the stroker)" << std::endl; FT_Done_Face(face); return false; } // Select the Unicode character map if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0) { err() << "Failed to load font from memory (failed to set the Unicode character set)" << std::endl; FT_Stroker_Done(stroker); FT_Done_Face(face); return false; } // Store the loaded font in our ugly void* :) m_stroker = stroker; m_face = face; // Store the font information m_info.family = face->family_name ? face->family_name : std::string(); return true; }
void SubtitleRenderer:: initialize_fonts(const std::string& font_path, float font_size) { ENFORCE(!FT_Init_FreeType(&ft_library_)); ENFORCE2(!FT_New_Face(ft_library_, font_path.c_str(), 0, &ft_face_), "Unable to open font"); ENFORCE(!FT_Set_Pixel_Sizes(ft_face_, 0, font_size*screen_height_)); auto get_bbox = [this](char32_t cp) { auto glyph_index = FT_Get_Char_Index(ft_face_, cp); 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);}; FT_BBox bbox; FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox); return bbox; }; constexpr float padding_factor = 0.05f; int y_min = get_bbox('g').yMin; int y_max = get_bbox('M').yMax; y_max += -y_min*0.7f; line_height_ = y_max - y_min; const int v_padding = line_height_*padding_factor + 0.5f; line_height_ += v_padding*2; box_offset_ = y_min-v_padding; box_h_padding_ = line_height_/5.0f + 0.5f; constexpr float border_thickness = 0.045f; ENFORCE(!FT_Stroker_New(ft_library_, &ft_stroker_)); FT_Stroker_Set(ft_stroker_, line_height_*border_thickness*64.0f, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); vgSeti(VG_FILTER_FORMAT_LINEAR, VG_TRUE); assert(!vgGetError()); vgSeti(VG_IMAGE_QUALITY, VG_IMAGE_QUALITY_NONANTIALIASED); assert(!vgGetError()); auto create_vg_font = [](VGFont& font) { font = vgCreateFont(128); ENFORCE(font); }; create_vg_font(vg_font_); create_vg_font(vg_font_border_); }
face_manager::face_manager(font_library & library, freetype_engine::font_file_mapping_type const& font_file_mapping, freetype_engine::font_memory_cache_type const& font_cache) : face_cache_(new face_cache()), library_(library), font_file_mapping_(font_file_mapping), font_memory_cache_(font_cache) { FT_Stroker s; FT_Error error = FT_Stroker_New(library_.get(), &s); if (!error) { stroker_ = std::make_shared<stroker>(s); } }
Font::Font(const void *MemFont, u32 MemFont_size, Minimum *min){ FontColor = COLOR_BLACK; FontSize = DEFAULT_FONT_SIZE; Pointer = (FT_Byte*)MemFont; Lenght = MemFont_size; m = min; FT_Init_FreeType(&library); FT_New_Memory_Face(library,Pointer,Lenght,0,&face); FT_Stroker_New(library,&stroker); Kerning = FT_HAS_KERNING(face); FT_Set_Pixel_Sizes(face,0,FontSize); font=0; }
fontRenderClass::fontRenderClass(): fb(fbClass::getInstance()) { instance=this; eDebug("[FONT] initializing lib..."); { if (FT_Init_FreeType(&library)) { eDebug("[FONT] initializing failed."); return; } } eDebug("[FONT] loading fonts..."); fflush(stdout); font=0; int maxbytes=4*1024*1024; eDebug("[FONT] Intializing font cache, using max. %dMB...", maxbytes/1024/1024); fflush(stdout); { if (FTC_Manager_New(library, 8, 8, maxbytes, myFTC_Face_Requester, this, &cacheManager)) { eDebug("[FONT] initializing font cache failed!"); return; } if (!cacheManager) { eDebug("[FONT] initializing font cache manager error."); return; } if (FTC_SBit_Cache_New(cacheManager, &sbitsCache)) { eDebug("[FONT] initializing font cache sbit failed!"); return; } if (FTC_Image_Cache_New(cacheManager, &imageCache)) { eDebug("[FONT] initializing font cache imagecache failed!"); } if (FT_Stroker_New(library, &stroker)) { eDebug("[FONT] initializing font stroker failed!"); } } strokerRadius = -1; return; }
void SubtitleRenderer:: initialize_fonts(const std::string& font_path, const std::string& italic_font_path, unsigned int font_size) { ENFORCE(!FT_Init_FreeType(&ft_library_)); ENFORCE2(!FT_New_Face(ft_library_, font_path.c_str(), 0, &ft_face_), "Unable to open font"); ENFORCE2(!FT_New_Face(ft_library_, italic_font_path.c_str(), 0, &ft_face_italic_), "Unable to open italic font"); ENFORCE(!FT_Set_Pixel_Sizes(ft_face_, 0, font_size)); ENFORCE(!FT_Set_Pixel_Sizes(ft_face_italic_, 0, font_size)); auto get_bbox = [this](char32_t cp) { auto glyph_index = FT_Get_Char_Index(ft_face_, cp); 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);}; FT_BBox bbox; FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox); return bbox; }; constexpr float padding_factor = 0.05f; int y_min = get_bbox('g').yMin; int y_max = get_bbox('M').yMax; y_max += -y_min*0.7f; line_height_ = y_max - y_min; const int v_padding = line_height_*padding_factor + 0.5f; line_height_ += v_padding*2; box_offset_ = y_min-v_padding; box_h_padding_ = line_height_/5.0f + 0.5f; constexpr float border_thickness = 0.044f; ENFORCE(!FT_Stroker_New(ft_library_, &ft_stroker_)); FT_Stroker_Set(ft_stroker_, line_height_*border_thickness*64.0f, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); }
/** * \brief Change border width * negative value resets border to style value */ void change_border(ASS_Renderer *render_priv, double border_x, double border_y) { int bord; if (!render_priv->state.font) return; if (border_x < 0 && border_y < 0) { if (render_priv->state.style->BorderStyle == 1 || render_priv->state.style->BorderStyle == 3) border_x = border_y = render_priv->state.style->Outline; else border_x = border_y = 1.; } render_priv->state.border_x = border_x; render_priv->state.border_y = border_y; bord = 64 * border_x * render_priv->border_scale; if (bord > 0 && border_x == border_y) { if (!render_priv->state.stroker) { int error; error = FT_Stroker_New(render_priv->ftlibrary, &render_priv->state.stroker); if (error) { ass_msg(render_priv->library, MSGL_V, "failed to get stroker"); render_priv->state.stroker = 0; } } if (render_priv->state.stroker) FT_Stroker_Set(render_priv->state.stroker, bord, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); } else { FT_Stroker_Done(render_priv->state.stroker); render_priv->state.stroker = 0; } }
Font::Font(const int ID, Minimum *min){ const char *Font_Path; if(ID==JPN) Font_Path = "/dev_flash/data/font/SCE-PS3-NR-R-JPN.TTF"; else if(ID==KOR) Font_Path = "/dev_flash/data/font/SCE-PS3-YG-R-KOR.TTF"; else if(ID==CGB) Font_Path = "/dev_flash/data/font/SCE-PS3-DH-R-CGB.TTF"; else if(ID==KANA) Font_Path = "/dev_flash/data/font/SCE-PS3-CP-R-KANA.TTF"; else Font_Path = "/dev_flash/data/font/SCE-PS3-VR-R-LATIN2.TTF"; FontColor = COLOR_BLACK; FontSize = DEFAULT_FONT_SIZE; Lenght = 0; m = min; FT_Init_FreeType(&library); FT_New_Face(library,Font_Path,0,&face); FT_Stroker_New(library,&stroker); Kerning = FT_HAS_KERNING(face); FT_Set_Pixel_Sizes(face,0,FontSize); font=0; }
Stroker::Stroker(Library & library) { FT_Stroker_New(library.id_, &id_); }
static av_cold int init(AVFilterContext *ctx) { int err; DrawTextContext *s = ctx->priv; Glyph *glyph; if (!s->fontfile && !CONFIG_LIBFONTCONFIG) { av_log(ctx, AV_LOG_ERROR, "No font filename provided\n"); return AVERROR(EINVAL); } if (s->textfile) { if (s->text) { av_log(ctx, AV_LOG_ERROR, "Both text and text file provided. Please provide only one\n"); return AVERROR(EINVAL); } if ((err = load_textfile(ctx)) < 0) return err; } if (s->reload && !s->textfile) av_log(ctx, AV_LOG_WARNING, "No file to reload\n"); if (s->tc_opt_string) { int ret = av_timecode_init_from_string(&s->tc, s->tc_rate, s->tc_opt_string, ctx); if (ret < 0) return ret; if (s->tc24hmax) s->tc.flags |= AV_TIMECODE_FLAG_24HOURSMAX; if (!s->text) s->text = av_strdup(""); } if (!s->text) { av_log(ctx, AV_LOG_ERROR, "Either text, a valid file or a timecode must be provided\n"); return AVERROR(EINVAL); } #if CONFIG_LIBFRIBIDI if (s->text_shaping) if ((err = shape_text(ctx)) < 0) return err; #endif if ((err = FT_Init_FreeType(&(s->library)))) { av_log(ctx, AV_LOG_ERROR, "Could not load FreeType: %s\n", FT_ERRMSG(err)); return AVERROR(EINVAL); } err = load_font(ctx); if (err) return err; if (!s->fontsize) s->fontsize = 16; if ((err = FT_Set_Pixel_Sizes(s->face, 0, s->fontsize))) { av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n", s->fontsize, FT_ERRMSG(err)); return AVERROR(EINVAL); } if (s->borderw) { if (FT_Stroker_New(s->library, &s->stroker)) { av_log(ctx, AV_LOG_ERROR, "Coult not init FT stroker\n"); return AVERROR_EXTERNAL; } FT_Stroker_Set(s->stroker, s->borderw << 6, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); } s->use_kerning = FT_HAS_KERNING(s->face); /* load the fallback glyph with code 0 */ load_glyph(ctx, NULL, 0); /* set the tabsize in pixels */ if ((err = load_glyph(ctx, &glyph, ' ')) < 0) { av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n"); return err; } s->tabsize *= glyph->advance; if (s->exp_mode == EXP_STRFTIME && (strchr(s->text, '%') || strchr(s->text, '\\'))) av_log(ctx, AV_LOG_WARNING, "expansion=strftime is deprecated.\n"); av_bprint_init(&s->expanded_text, 0, AV_BPRINT_SIZE_UNLIMITED); av_bprint_init(&s->expanded_fontcolor, 0, AV_BPRINT_SIZE_UNLIMITED); return 0; }
bool Font::loadFromFile(const std::string& filename) { #ifndef SFML_SYSTEM_ANDROID // Cleanup the previous resources cleanup(); m_refCount = new int(1); // Initialize FreeType // Note: we initialize FreeType for every font instance in order to avoid having a single // global manager that would create a lot of issues regarding creation and destruction order. FT_Library library; if (FT_Init_FreeType(&library) != 0) { err() << "Failed to load font \"" << filename << "\" (failed to initialize FreeType)" << std::endl; return false; } m_library = library; // Load the new font face from the specified file FT_Face face; if (FT_New_Face(static_cast<FT_Library>(m_library), filename.c_str(), 0, &face) != 0) { err() << "Failed to load font \"" << filename << "\" (failed to create the font face)" << std::endl; return false; } // Load the stroker that will be used to outline the font FT_Stroker stroker; if (FT_Stroker_New(static_cast<FT_Library>(m_library), &stroker) != 0) { err() << "Failed to load font \"" << filename << "\" (failed to create the stroker)" << std::endl; FT_Done_Face(face); return false; } // Select the unicode character map if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0) { err() << "Failed to load font \"" << filename << "\" (failed to set the Unicode character set)" << std::endl; FT_Stroker_Done(stroker); FT_Done_Face(face); return false; } // Store the loaded font in our ugly void* :) m_stroker = stroker; m_face = face; // Store the font information m_info.family = face->family_name ? face->family_name : std::string(); return true; #else if (m_stream) delete (priv::ResourceStream*)m_stream; m_stream = new priv::ResourceStream(filename); return loadFromStream(*(priv::ResourceStream*)m_stream); #endif }
bool Font::loadFromStream(InputStream& stream) { // Cleanup the previous resources cleanup(); m_refCount = new int(1); // Initialize FreeType // Note: we initialize FreeType for every font instance in order to avoid having a single // global manager that would create a lot of issues regarding creation and destruction order. FT_Library library; if (FT_Init_FreeType(&library) != 0) { err() << "Failed to load font from stream (failed to initialize FreeType)" << std::endl; return false; } m_library = library; // Make sure that the stream's reading position is at the beginning stream.seek(0); // Prepare a wrapper for our stream, that we'll pass to FreeType callbacks FT_StreamRec* rec = new FT_StreamRec; std::memset(rec, 0, sizeof(*rec)); rec->base = NULL; rec->size = static_cast<unsigned long>(stream.getSize()); rec->pos = 0; rec->descriptor.pointer = &stream; rec->read = &read; rec->close = &close; // Setup the FreeType callbacks that will read our stream FT_Open_Args args; args.flags = FT_OPEN_STREAM; args.stream = rec; args.driver = 0; // Load the new font face from the specified stream FT_Face face; if (FT_Open_Face(static_cast<FT_Library>(m_library), &args, 0, &face) != 0) { err() << "Failed to load font from stream (failed to create the font face)" << std::endl; delete rec; return false; } // Load the stroker that will be used to outline the font FT_Stroker stroker; if (FT_Stroker_New(static_cast<FT_Library>(m_library), &stroker) != 0) { err() << "Failed to load font from stream (failed to create the stroker)" << std::endl; FT_Done_Face(face); delete rec; return false; } // Select the Unicode character map if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0) { err() << "Failed to load font from stream (failed to set the Unicode character set)" << std::endl; FT_Done_Face(face); FT_Stroker_Done(stroker); delete rec; return false; } // Store the loaded font in our ugly void* :) m_stroker = stroker; m_face = face; m_streamRec = rec; // Store the font information m_info.family = face->family_name ? face->family_name : std::string(); return true; }
// ----------------------------------------------- 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; }
fz_pixmap * fz_renderftstrokedglyph(fz_font *font, int gid, fz_matrix trm, fz_matrix ctm, fz_strokestate *state) { FT_Face face = font->ftface; float expansion = fz_matrixexpansion(ctm); int linewidth = state->linewidth * expansion * 64 / 2; FT_Matrix m; FT_Vector v; FT_Error fterr; FT_Stroker stroker; FT_Glyph glyph; FT_BitmapGlyph bitmap; fz_pixmap *pix; int y; m.xx = trm.a * 64; /* should be 65536 */ m.yx = trm.b * 64; m.xy = trm.c * 64; m.yy = trm.d * 64; v.x = trm.e * 64; v.y = trm.f * 64; fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */ if (fterr) { fz_warn("FT_Set_Char_Size: %s", ft_errorstring(fterr)); return nil; } FT_Set_Transform(face, &m, &v); fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); if (fterr) { fz_warn("FT_Load_Glyph(gid %d): %s", gid, ft_errorstring(fterr)); return nil; } fterr = FT_Stroker_New(fz_ftlib, &stroker); if (fterr) { fz_warn("FT_Stroker_New: %s", ft_errorstring(fterr)); return nil; } FT_Stroker_Set(stroker, linewidth, state->linecap, state->linejoin, state->miterlimit * 65536); fterr = FT_Get_Glyph(face->glyph, &glyph); if (fterr) { fz_warn("FT_Get_Glyph: %s", ft_errorstring(fterr)); FT_Stroker_Done(stroker); return nil; } fterr = FT_Glyph_Stroke(&glyph, stroker, 1); if (fterr) { fz_warn("FT_Glyph_Stroke: %s", ft_errorstring(fterr)); FT_Done_Glyph(glyph); FT_Stroker_Done(stroker); return nil; } fterr = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); if (fterr) { fz_warn("FT_Glyph_To_Bitmap: %s", ft_errorstring(fterr)); FT_Done_Glyph(glyph); FT_Stroker_Done(stroker); return nil; } bitmap = (FT_BitmapGlyph)glyph; pix = fz_newpixmap(NULL, bitmap->left, bitmap->top - bitmap->bitmap.rows, bitmap->bitmap.width, bitmap->bitmap.rows); for (y = 0; y < pix->h; y++) { memcpy(pix->samples + y * pix->w, bitmap->bitmap.buffer + (pix->h - y - 1) * bitmap->bitmap.pitch, pix->w); } FT_Done_Glyph(glyph); FT_Stroker_Done(stroker); return pix; }
static FT_Error Load_Glyph( TTF_Font* font, Uint16 ch, c_glyph* cached, int want ) { FT_Face face; FT_Error error; FT_GlyphSlot glyph; FT_Glyph_Metrics* metrics; FT_Outline* outline; if ( !font || !font->face ) { return FT_Err_Invalid_Handle; } face = font->face; /* Load the glyph */ if ( ! cached->index ) { cached->index = FT_Get_Char_Index( face, ch ); } error = FT_Load_Glyph( face, cached->index, FT_LOAD_DEFAULT | font->hinting); if( error ) { return error; } /* Get our glyph shortcuts */ glyph = face->glyph; metrics = &glyph->metrics; outline = &glyph->outline; /* Get the glyph metrics if desired */ if ( (want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS) ) { if ( FT_IS_SCALABLE( face ) ) { /* Get the bounding box */ cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = cached->minx + FT_CEIL(metrics->width); cached->maxy = FT_FLOOR(metrics->horiBearingY); cached->miny = cached->maxy - FT_CEIL(metrics->height); cached->yoffset = font->ascent - cached->maxy; cached->advance = FT_CEIL(metrics->horiAdvance); } else { /* Get the bounding box for non-scalable format. * Again, freetype2 fills in many of the font metrics * with the value of 0, so some of the values we * need must be calculated differently with certain * assumptions about non-scalable formats. * */ cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = cached->minx + FT_CEIL(metrics->horiAdvance); cached->maxy = FT_FLOOR(metrics->horiBearingY); cached->miny = cached->maxy - FT_CEIL(face->available_sizes[font->font_size_family].height); cached->yoffset = 0; cached->advance = FT_CEIL(metrics->horiAdvance); } /* Adjust for bold and italic text */ if( TTF_HANDLE_STYLE_BOLD(font) ) { cached->maxx += font->glyph_overhang; } if( TTF_HANDLE_STYLE_ITALIC(font) ) { cached->maxx += (int)ceil(font->glyph_italics); } cached->stored |= CACHED_METRICS; } if ( ((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) || ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) ) { int mono = (want & CACHED_BITMAP); int i; FT_Bitmap* src; FT_Bitmap* dst; FT_Glyph bitmap_glyph = NULL; /* Handle the italic style */ if( TTF_HANDLE_STYLE_ITALIC(font) ) { FT_Matrix shear; shear.xx = 1 << 16; shear.xy = (int) ( font->glyph_italics * ( 1 << 16 ) ) / font->height; shear.yx = 0; shear.yy = 1 << 16; FT_Outline_Transform( outline, &shear ); } /* Render as outline */ if( (font->outline > 0) && glyph->format != FT_GLYPH_FORMAT_BITMAP ) { FT_Stroker stroker; FT_Get_Glyph( glyph, &bitmap_glyph ); error = FT_Stroker_New( library, &stroker ); if( error ) { return error; } FT_Stroker_Set( stroker, font->outline * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 ); FT_Glyph_Stroke( &bitmap_glyph, stroker, 1 /* delete the original glyph */ ); FT_Stroker_Done( stroker ); /* Render the glyph */ error = FT_Glyph_To_Bitmap( &bitmap_glyph, mono ? ft_render_mode_mono : ft_render_mode_normal, 0, 1 ); if( error ) { FT_Done_Glyph( bitmap_glyph ); return error; } src = &((FT_BitmapGlyph)bitmap_glyph)->bitmap; } else { /* Render the glyph */ error = FT_Render_Glyph( glyph, mono ? ft_render_mode_mono : ft_render_mode_normal ); if( error ) { return error; } src = &glyph->bitmap; } /* Copy over information to cache */ if ( mono ) { dst = &cached->bitmap; } else { dst = &cached->pixmap; } memcpy( dst, src, sizeof( *dst ) ); /* FT_Render_Glyph() and .fon fonts always generate a * two-color (black and white) glyphslot surface, even * when rendered in ft_render_mode_normal. */ /* FT_IS_SCALABLE() means that the font is in outline format, * but does not imply that outline is rendered as 8-bit * grayscale, because embedded bitmap/graymap is preferred * (see FT_LOAD_DEFAULT section of FreeType2 API Reference). * FT_Render_Glyph() canreturn two-color bitmap or 4/16/256- * color graymap according to the format of embedded bitmap/ * graymap. */ if ( src->pixel_mode == FT_PIXEL_MODE_MONO ) { dst->pitch *= 8; } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY2 ) { dst->pitch *= 4; } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY4 ) { dst->pitch *= 2; } /* Adjust for bold and italic text */ if( TTF_HANDLE_STYLE_BOLD(font) ) { int bump = font->glyph_overhang; dst->pitch += bump; dst->width += bump; } if( TTF_HANDLE_STYLE_ITALIC(font) ) { int bump = (int)ceil(font->glyph_italics); dst->pitch += bump; dst->width += bump; } if (dst->rows != 0) { dst->buffer = (unsigned char *)malloc( dst->pitch * dst->rows ); if( !dst->buffer ) { return FT_Err_Out_Of_Memory; } memset( dst->buffer, 0, dst->pitch * dst->rows ); for( i = 0; i < src->rows; i++ ) { int soffset = i * src->pitch; int doffset = i * dst->pitch; if ( mono ) { unsigned char *srcp = src->buffer + soffset; unsigned char *dstp = dst->buffer + doffset; int j; if ( src->pixel_mode == FT_PIXEL_MODE_MONO ) { for ( j = 0; j < src->width; j += 8 ) { unsigned char c = *srcp++; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; } } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY2 ) { for ( j = 0; j < src->width; j += 4 ) { unsigned char c = *srcp++; *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0; c <<= 2; *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0; c <<= 2; *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0; c <<= 2; *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0; } } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY4 ) { for ( j = 0; j < src->width; j += 2 ) { unsigned char c = *srcp++; *dstp++ = (((c&0xF0) >> 4) >= 0x8) ? 1 : 0; c <<= 4; *dstp++ = (((c&0xF0) >> 4) >= 0x8) ? 1 : 0; } } else { for ( j = 0; j < src->width; j++ ) {
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); }
// ----------------------------------------------- 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; }
long EMSCRIPTEN_KEEPALIVE c_Library_strokerNew(long library) { FT_Stroker stroker; FT_Error error = FT_Stroker_New((FT_Library)library, &stroker); if(error) return 0; else return (long)stroker; }
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); }
int createFont(FT_Face fontFace, FT_UInt size) { initFreeTypeLibrary(); VGfloat glyphOrigin[2]; VGfloat escapement[2]; FT_Stroker fontStroker; int error = FT_Stroker_New(freeTypeLibrary, &fontStroker); if (error) { logInfo(LOG_FREETYPE, "Error FT_Stroker_New.(error=%d)\n", error); return -1; } FT_Stroker_Set(fontStroker, 2*64.0f, // Need to get the right value based on size. FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); VGFont tmpFont; struct fontListItem *tmpFontListItem = fontExists(fontFace, size); if (tmpFontListItem == NULL) { tmpFont = vgCreateFont(fontFace->num_glyphs); if (tmpFont == VG_INVALID_HANDLE) { logInfo(LOG_FREETYPE, "Error could not create vgCreateFont.\n"); return -1; } tmpFontListItem = malloc(sizeof(struct fontListItem)); tmpFontListItem->fontFace = fontFace; tmpFontListItem->size = size; tmpFontListItem->font = tmpFont; addToFontList(tmpFontListItem); } else { return 0; } error = FT_Set_Char_Size( fontFace, /* handle to face object */ 0, /* char_width in 1/64th of points */ size*64, /* char_height in 1/64th of points */ 72, /* horizontal device resolution */ 72 ); /* vertical device resolution */ if (error) { logInfo(LOG_FREETYPE, "Error FT_Set_Char_Size.(error=%d)\n", error); return -1; } int index; int counter = 0; FT_UInt charIndex; VGImage image = VG_INVALID_HANDLE; VGImage softenedImage; VGfloat blustStdDev; int padding; int image_width; int image_height; VGErrorCode vg_error; FT_Glyph glyph; logInfo(LOG_FREETYPE, "This font contains %ld glyphs.\n", fontFace->num_glyphs); for (index = 32; (index < 256) && (counter < fontFace->num_glyphs); index++) { counter++; charIndex = FT_Get_Char_Index(fontFace, index); logInfo(LOG_FREETYPE, "index=0x%x, charIndex=0x%x\n", index, charIndex); escapement[0] = 0; escapement[1] = 0; if (charIndex == 0) { vgSetGlyphToImage(tmpFont, index, VG_INVALID_HANDLE, escapement, escapement); logInfo(LOG_FREETYPE, "charindex== 0\n"); continue; } error = FT_Load_Glyph(fontFace, charIndex, FT_LOAD_NO_HINTING); if (error) { vgSetGlyphToImage(tmpFont, index, VG_INVALID_HANDLE, escapement, escapement); logInfo(LOG_FREETYPE, "Error FT_Load_Glyph (error:%d)\n", error); continue; } error = FT_Get_Glyph(fontFace->glyph, &glyph); if (error) { vgSetGlyphToImage(tmpFont, index, VG_INVALID_HANDLE, escapement, escapement); logInfo(LOG_FREETYPE, "Error FT_Get_Glyph (error:%d)\n", error); continue; } /* error = FT_Glyph_StrokeBorder(&glyph, fontStroker, 0, 1); if (error) { FT_Done_Glyph(glyph); vgSetGlyphToImage(tmpFont, index, VG_INVALID_HANDLE, escapement, escapement); logInfo(LOG_FREETYPE, "Error FT_Glyph_StrokeBorder (error:%d)\n", error); continue; } */ error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, 1); if (error) { FT_Done_Glyph(glyph); vgSetGlyphToImage(tmpFont, index, VG_INVALID_HANDLE, escapement, escapement); logInfo(LOG_FREETYPE, "Error FT_Glyph_To_Bitmap (error:%d)\n", error); continue; } FT_BitmapGlyph bitGlyph = (FT_BitmapGlyph)glyph; FT_Bitmap bitmap = bitGlyph->bitmap; if (bitmap.width > 0 && bitmap.rows > 0) { blustStdDev = 0.6; padding = (3*blustStdDev + 0.5); image_width = bitmap.width + padding*2; image_height = bitmap.rows + padding*2; image = vgCreateImage(VG_A_8, image_width, image_height, VG_IMAGE_QUALITY_NONANTIALIASED); if (image == VG_INVALID_HANDLE) { FT_Done_Glyph(glyph); vgSetGlyphToImage(tmpFont, index, VG_INVALID_HANDLE, escapement, escapement); logInfo(LOG_FREETYPE, "vgCreateImage (error:%d)\n", vgGetError()); continue; } if (bitmap.pitch > 0) { vgImageSubData(image, bitmap.buffer + bitmap.pitch*(bitmap.rows-1), -bitmap.pitch, VG_A_8, padding, padding, bitmap.width, bitmap.rows); } else { vgImageSubData(image, bitmap.buffer, bitmap.pitch, VG_A_8, padding, padding, bitmap.width, bitmap.rows); } vg_error = vgGetError(); if (vg_error) { vgDestroyImage(image); FT_Done_Glyph(glyph); vgSetGlyphToImage(tmpFont, index, VG_INVALID_HANDLE, escapement, escapement); showVGErrorStr(vg_error, "vgImageSubData"); continue; } softenedImage = vgCreateImage(VG_A_8, image_width, image_height, VG_IMAGE_QUALITY_NONANTIALIASED); if (softenedImage == VG_INVALID_HANDLE) { vgDestroyImage(image); FT_Done_Glyph(glyph); vgSetGlyphToImage(tmpFont, index, VG_INVALID_HANDLE, escapement, escapement); logInfo(LOG_FREETYPE, "vgCreateImage (error:%d)\n", vgGetError()); continue; } // Even out hard and soft edges vgGaussianBlur(softenedImage, image, blustStdDev, blustStdDev, VG_TILE_FILL); vg_error = vgGetError(); if (vg_error) { vgDestroyImage(softenedImage); vgDestroyImage(image); FT_Done_Glyph(glyph); vgSetGlyphToImage(tmpFont, index, VG_INVALID_HANDLE, escapement, escapement); showVGErrorStr(vg_error, "vgGaussianBlur"); continue; } vgDestroyImage(image); image = softenedImage; glyphOrigin[0] = (VGfloat)(padding - bitGlyph->left); glyphOrigin[1] = (VGfloat)(padding + bitmap.rows - bitGlyph->top - 1); } else { logInfo(LOG_FREETYPE, "Error bitmap.width = %d, bitmap.rows = %d\n", bitmap.width, bitmap.rows); } escapement[0] = (VGfloat)((fontFace->glyph->advance.x + 32) / 64); escapement[1] = 0; vgSetGlyphToImage(tmpFont, index, image, glyphOrigin, escapement); vg_error = vgGetError(); if (vg_error) { vgDestroyImage(softenedImage); vgDestroyImage(image); FT_Done_Glyph(glyph); vgSetGlyphToImage(tmpFont, index, VG_INVALID_HANDLE, escapement, escapement); showVGErrorStr(vg_error, "vgSetGlyphToImage"); continue; } logInfo(LOG_FREETYPE, "Create glyph %d.\n", index); FT_Done_Glyph(glyph); if (image != VG_INVALID_HANDLE) { vgDestroyImage(image); } } return 0; }