static av_cold int init(AVFilterContext *ctx) { int err; DrawTextContext *s = ctx->priv; Glyph *glyph; if ((err = parse_font(ctx)) < 0) return err; if (s->textfile) { uint8_t *textbuf; size_t textbuf_size; 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 = av_file_map(s->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) { av_log(ctx, AV_LOG_ERROR, "The text file '%s' could not be read or is empty\n", s->textfile); return err; } if (textbuf_size > SIZE_MAX - 1 || !(s->text = av_malloc(textbuf_size + 1))) { av_file_unmap(textbuf, textbuf_size); return AVERROR(ENOMEM); } memcpy(s->text, textbuf, textbuf_size); s->text[textbuf_size] = 0; av_file_unmap(textbuf, textbuf_size); } if (!s->text) { av_log(ctx, AV_LOG_ERROR, "Either text or a valid file must be provided\n"); return AVERROR(EINVAL); } if ((err = av_parse_color(s->fontcolor_rgba, s->fontcolor_string, -1, ctx))) { av_log(ctx, AV_LOG_ERROR, "Invalid font color '%s'\n", s->fontcolor_string); return err; } if ((err = av_parse_color(s->boxcolor_rgba, s->boxcolor_string, -1, ctx))) { av_log(ctx, AV_LOG_ERROR, "Invalid box color '%s'\n", s->boxcolor_string); return err; } if ((err = av_parse_color(s->shadowcolor_rgba, s->shadowcolor_string, -1, ctx))) { av_log(ctx, AV_LOG_ERROR, "Invalid shadow color '%s'\n", s->shadowcolor_string); return err; } 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); } /* load the face, and set up the encoding, which is by default UTF-8 */ if ((err = FT_New_Face(s->library, s->fontfile, 0, &s->face))) { av_log(ctx, AV_LOG_ERROR, "Could not load fontface from file '%s': %s\n", s->fontfile, FT_ERRMSG(err)); return AVERROR(EINVAL); } 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); } 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 !HAVE_LOCALTIME_R av_log(ctx, AV_LOG_WARNING, "strftime() expansion unavailable!\n"); #endif return 0; }
SWFFONT* swf_LoadTrueTypeFont(const char*filename, char flashtype) { FT_Face face; FT_Error error; const char* name = 0; FT_ULong charcode; FT_UInt gindex; SWFFONT* font; int t; int*glyph2glyph; int max_unicode = 0; int charmap = -1; if(ftlibrary == 0) { if(FT_Init_FreeType(&ftlibrary)) { fprintf(stderr, "Couldn't init freetype library!\n"); exit(1); } } error = FT_New_Face(ftlibrary, filename, 0, &face); if(error || !face) { fprintf(stderr, "Couldn't load file %s- not a TTF file?\n", filename); return 0; } int scale = flashtype?20:1; FT_Set_Pixel_Sizes (face, 16*loadfont_scale*scale, 16*loadfont_scale*scale); if(face->num_glyphs <= 0) { fprintf(stderr, "File %s contains %d glyphs\n", filename, (int)face->num_glyphs); return 0; } font = (SWFFONT*)rfx_calloc(sizeof(SWFFONT)); font->id = -1; font->version = flashtype?3:2; font->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT)); font->layout->bounds = (SRECT*)rfx_calloc(face->num_glyphs*sizeof(SRECT)); font->style = ((face->style_flags&FT_STYLE_FLAG_ITALIC)?FONT_STYLE_ITALIC:0) |((face->style_flags&FT_STYLE_FLAG_BOLD)?FONT_STYLE_BOLD:0); font->encoding = FONT_ENCODING_UNICODE; font->glyph2ascii = (U16*)rfx_calloc(face->num_glyphs*sizeof(U16)); font->maxascii = 0; font->glyph = (SWFGLYPH*)rfx_calloc(face->num_glyphs*sizeof(SWFGLYPH)); if(FT_HAS_GLYPH_NAMES(face)) { font->glyphnames = (char**)rfx_calloc(face->num_glyphs*sizeof(char*)); } font->layout->kerningcount = 0; name = face->family_name; if(!(name && *name)) name = FT_Get_Postscript_Name(face); if(name && *name) font->name = (U8*)strdup(name); while(1) { /* // Map Glyphs to Unicode, version 1 (quick and dirty): int t; for(t=0;t<65536;t++) { int index = FT_Get_Char_Index(face, t); if(index>=0 && index<face->num_glyphs) { if(font->glyph2ascii[index]<0) font->glyph2ascii[index] = t; } }*/ // Map Glyphs to Unicode, version 2 (much nicer): // (The third way would be the AGL algorithm, as proposed // by Werner Lemberg on [email protected]) charcode = FT_Get_First_Char(face, &gindex); while(gindex != 0) { if(gindex >= 0 && gindex<face->num_glyphs) { if(!font->glyph2ascii[gindex]) { font->glyph2ascii[gindex] = charcode; if(charcode + 1 > font->maxascii) { font->maxascii = charcode + 1; } } } charcode = FT_Get_Next_Char(face, charcode, &gindex); } /* if we didn't find a single encoding character, try the font's charmaps instead. That usually means that the encoding is no longer unicode. TODO: find a way to convert the encoding to unicode */ if(font->maxascii == 0 && charmap < face->num_charmaps - 1) { charmap++; FT_Set_Charmap(face, face->charmaps[charmap]); font->encoding = 0;//anything but unicode FIXME } else break; } if(full_unicode) font->maxascii = 65535; font->ascii2glyph = (int*)rfx_calloc(font->maxascii*sizeof(int)); for(t=0;t<font->maxascii;t++) { int g = FT_Get_Char_Index(face, t); if(!g || g>=face->num_glyphs) g = -1; font->ascii2glyph[t] = g; if(g>=0) { max_unicode = t+1; if(!font->glyph2ascii[g]) { font->glyph2ascii[g] = t; } } } font->maxascii = max_unicode; font->numchars = 0; glyph2glyph = (int*)rfx_calloc(face->num_glyphs*sizeof(int)); SRECT fontbbox = {0,0,0,0}; for(t=0; t < face->num_glyphs; t++) { FT_Glyph glyph; FT_BBox bbox; char name[128]; drawer_t draw; char hasname = 0; name[0]=0; if(FT_HAS_GLYPH_NAMES(face)) { error = FT_Get_Glyph_Name(face, t, name, 127); if(!error && name[0] && !strstr(name, "notdef")) { font->glyphnames[font->numchars] = strdup(name); hasname = 1; } } if(!font->glyph2ascii[t] && !hasname && skip_unused) { continue; } error = FT_Load_Glyph(face, t, FT_LOAD_NO_BITMAP); if(error) { //tends to happen with some pdfs fprintf(stderr, "Warning: Glyph %d has return code %d\n", t, error); glyph=0; if(skip_unused) continue; } else { error = FT_Get_Glyph(face->glyph, &glyph); if(error) { fprintf(stderr, "Couldn't get glyph %d, error:%d\n", t, error); glyph=0; if(skip_unused) continue; } } if(glyph) FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &bbox); else memset(&bbox, 0, sizeof(bbox)); bbox.yMin = -bbox.yMin; bbox.yMax = -bbox.yMax; if(bbox.xMax < bbox.xMin) { // swap bbox.xMax ^= bbox.xMin; bbox.xMin ^= bbox.xMax; bbox.xMax ^= bbox.xMin; } if(bbox.yMax < bbox.yMin) { // swap bbox.yMax ^= bbox.yMin; bbox.yMin ^= bbox.yMax; bbox.yMax ^= bbox.yMin; } swf_Shape01DrawerInit(&draw, 0); //error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw); if(glyph) error = FT_Outline_Decompose(&face->glyph->outline, &outline_functions, &draw); else error = 0; draw.finish(&draw); if(error) { fprintf(stderr, "Couldn't decompose glyph %d\n", t); draw.dealloc(&draw); continue; } #if 0 if(bbox.xMin > 0) { font->glyph[font->numchars].advance = (bbox.xMax*20*FT_SCALE)/FT_SUBPIXELS; } else { font->glyph[font->numchars].advance = ((bbox.xMax - bbox.xMin)*20*FT_SCALE)/FT_SUBPIXELS; } #else if(glyph) font->glyph[font->numchars].advance = glyph->advance.x*20/65536; else font->glyph[font->numchars].advance = 0; #endif SRECT b = swf_ShapeDrawerGetBBox(&draw); //font->layout->bounds[font->numchars].xmin = (bbox.xMin*FT_SCALE*20)/FT_SUBPIXELS; //font->layout->bounds[font->numchars].ymin = (bbox.yMin*FT_SCALE*20)/FT_SUBPIXELS; //font->layout->bounds[font->numchars].xmax = (bbox.xMax*FT_SCALE*20)/FT_SUBPIXELS; //font->layout->bounds[font->numchars].ymax = (bbox.yMax*FT_SCALE*20)/FT_SUBPIXELS; font->layout->bounds[font->numchars] = b; font->glyph[font->numchars].shape = swf_ShapeDrawerToShape(&draw); swf_ExpandRect2(&fontbbox, &font->layout->bounds[font->numchars]); draw.dealloc(&draw); if(glyph) FT_Done_Glyph(glyph); font->glyph2ascii[font->numchars] = font->glyph2ascii[t]; glyph2glyph[t] = font->numchars; font->numchars++; } //font->layout->ascent = abs(face->ascender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMin; //font->layout->descent = abs(face->descender)*FT_SCALE*loadfont_scale*20/FT_SUBPIXELS/2; //face->bbox.xMax; //font->layout->leading = font->layout->ascent + font->layout->descent; if(-fontbbox.ymin < 0) font->layout->ascent = 0; else font->layout->ascent = -fontbbox.ymin; if(fontbbox.ymax < 0) font->layout->descent = 0; else font->layout->descent = fontbbox.ymax; int leading = fontbbox.ymax - fontbbox.ymin; font->layout->leading = leading>0x7fff?0x7fff:leading; /* notice: if skip_unused is true, font->glyph2ascii, font->glyphnames and font->layout->bounds will have more memory allocated than just font->numchars, but only the first font->numchars are used/valid */ for(t=0;t<font->maxascii;t++) { if(font->ascii2glyph[t]>=0) { font->ascii2glyph[t] = glyph2glyph[font->ascii2glyph[t]]; } } rfx_free(glyph2glyph); FT_Done_Face(face); FT_Done_FreeType(ftlibrary);ftlibrary=0; return font; }
int DrawGlyph( _In_ wchar_t c, _In_ int pixel_size, _Out_ _Notnull_ int* pitch, _Out_ _Notnull_ int* rows, _Out_ _Notnull_ int* advance, _Out_ _Notnull_ int* horiBearingY, _Inout_ _Notnull_ unsigned char** ppBitmap ) { FT_Error error; FT_UInt glyph_index; glyph_index = FT_Get_Char_Index(face, c); error = FT_Set_Pixel_Sizes(face, 0, pixel_size); if (error) { return error; } error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); if (error) { return error; } if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { FT_Pos strength = 30; error = FT_Outline_Embolden(&face->glyph->outline, strength); if (error) { //cl_debug_output("Font Embolden failed."); DebugBreak(); } } /* FT_Matrix matrix; matrix.xx = 0x07FFFL; matrix.xy = 0; matrix.yx = 0; matrix.yy = 0x10000L; FT_Set_Transform(face, &matrix, 0); */ error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); if (error) { return error; } *ppBitmap = (unsigned char*)malloc(face->glyph->bitmap.rows * face->glyph->bitmap.pitch); memcpy(*ppBitmap, face->glyph->bitmap.buffer, face->glyph->bitmap.rows * face->glyph->bitmap.pitch); *pitch = face->glyph->bitmap.pitch; *rows = face->glyph->bitmap.rows; *advance = face->glyph->advance.x / 64; *horiBearingY = face->glyph->metrics.horiBearingY / 64; return error; }
Error DynamicFontAtSize::_load() { int error = FT_Init_FreeType(&library); ERR_EXPLAIN(TTR("Error initializing FreeType.")); ERR_FAIL_COND_V(error != 0, ERR_CANT_CREATE); // FT_OPEN_STREAM is extremely slow only on Android. if (OS::get_singleton()->get_name() == "Android" && font->font_mem == NULL && font->font_path != String()) { // cache font only once for each font->font_path if (_fontdata.has(font->font_path)) { font->set_font_ptr(_fontdata[font->font_path].ptr(), _fontdata[font->font_path].size()); } else { FileAccess *f = FileAccess::open(font->font_path, FileAccess::READ); ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); size_t len = f->get_len(); _fontdata[font->font_path] = Vector<uint8_t>(); Vector<uint8_t> &fontdata = _fontdata[font->font_path]; fontdata.resize(len); f->get_buffer(fontdata.ptr(), len); font->set_font_ptr(fontdata.ptr(), len); f->close(); } } if (font->font_mem == NULL && font->font_path != String()) { FileAccess *f = FileAccess::open(font->font_path, FileAccess::READ); ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); memset(&stream, 0, sizeof(FT_StreamRec)); stream.base = NULL; stream.size = f->get_len(); stream.pos = 0; stream.descriptor.pointer = f; stream.read = _ft_stream_io; stream.close = _ft_stream_close; FT_Open_Args fargs; memset(&fargs, 0, sizeof(FT_Open_Args)); fargs.flags = FT_OPEN_STREAM; fargs.stream = &stream; error = FT_Open_Face(library, &fargs, 0, &face); } else if (font->font_mem) { memset(&stream, 0, sizeof(FT_StreamRec)); stream.base = (unsigned char *)font->font_mem; stream.size = font->font_mem_size; stream.pos = 0; FT_Open_Args fargs; memset(&fargs, 0, sizeof(FT_Open_Args)); fargs.memory_base = (unsigned char *)font->font_mem; fargs.memory_size = font->font_mem_size; fargs.flags = FT_OPEN_MEMORY; fargs.stream = &stream; error = FT_Open_Face(library, &fargs, 0, &face); } else { ERR_EXPLAIN("DynamicFont uninitialized"); ERR_FAIL_V(ERR_UNCONFIGURED); } //error = FT_New_Face( library, src_path.utf8().get_data(),0,&face ); if (error == FT_Err_Unknown_File_Format) { ERR_EXPLAIN(TTR("Unknown font format.")); FT_Done_FreeType(library); } else if (error) { ERR_EXPLAIN(TTR("Error loading font.")); FT_Done_FreeType(library); } ERR_FAIL_COND_V(error, ERR_FILE_CANT_OPEN); /*error = FT_Set_Char_Size(face,0,64*size,512,512); if ( error ) { FT_Done_FreeType( library ); ERR_EXPLAIN(TTR("Invalid font size.")); ERR_FAIL_COND_V( error, ERR_INVALID_PARAMETER ); }*/ error = FT_Set_Pixel_Sizes(face, 0, id.size); ascent = face->size->metrics.ascender >> 6; descent = -face->size->metrics.descender >> 6; linegap = 0; texture_flags = 0; if (id.mipmaps) texture_flags |= Texture::FLAG_MIPMAPS; if (id.filter) texture_flags |= Texture::FLAG_FILTER; //print_line("ASCENT: "+itos(ascent)+" descent "+itos(descent)+" hinted: "+itos(face->face_flags&FT_FACE_FLAG_HINTER)); valid = true; return OK; }
void InitFreeType(){ PrintToLog("INFO: Initializing FreeType."); FT_Library ft; FT_Face face; if(FT_Init_FreeType(&ft)) { PrintToLog("ERROR: Could not init FreeType library!"); return; } // TODO - load from some configuration file... const char *font_filename = "/Orbitron Bold.otf"; PHYSFS_File *file; intmax_t length; uint8_t *font_buffer; file = PHYSFS_openRead(font_filename); if(!file){ PrintToLog("ERROR: unable to open Font File: \"%s\"", font_filename); return; } length = PHYSFS_fileLength(file); font_buffer = new uint8_t[length]; PHYSFS_read(file, font_buffer, 1, length); PHYSFS_close(file); FT_Error err = FT_New_Memory_Face(ft, font_buffer, length, 0, &face); if(err){ PrintToLog("ERROR: Could not load font \"%s\"! error code %x!", font_filename, err); delete[] font_buffer; return; } FT_Set_Pixel_Sizes(face, 0, FONT_SIZE); FT_GlyphSlot g = face->glyph; int width = 0; int height = 0; for(unsigned char c = FONT_FIRST_CHAR; c <= FONT_LAST_CHAR; c++) { if(FT_Load_Char(face, c, FT_LOAD_RENDER)) { PrintToLog("WARNING: Loading character %c failed!", c); continue; } width += g->bitmap.width + 1; height = std::max(height, int(g->bitmap.rows)); } main_atlas.width = width; glGenTextures(1, &main_atlas.texture); glBindTexture(GL_TEXTURE_2D, main_atlas.texture); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); std::vector<GLubyte> empty_image(width * height, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &empty_image[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); uint32_t x = 0; for(unsigned char i = FONT_FIRST_CHAR; i <= FONT_LAST_CHAR; i++) { if(FT_Load_Char(face, i, FT_LOAD_RENDER)) continue; glTexSubImage2D(GL_TEXTURE_2D, 0, x, 0, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer); main_atlas.characters[i].advance.x = g->advance.x >> 6; main_atlas.characters[i].advance.y = g->advance.y >> 6; main_atlas.characters[i].width = g->bitmap.width; main_atlas.characters[i].rows = g->bitmap.rows; main_atlas.characters[i].left = g->bitmap_left; main_atlas.characters[i].top = g->bitmap_top; main_atlas.characters[i].x = (float)x / (float)width; x += g->bitmap.width + 1; } PrintToLog("INFO: Created character atlas size %ix%i", width,height); FT_Done_Face(face); delete[] font_buffer; // TODO - perhaps leave this open for loading other fonts and close in a GarbageCollect() function? FT_Done_FreeType(ft); }
const Tempest::FontElement::Letter& Tempest::FontElement::fetchLeter( char16_t ch, Tempest::SpritesHolder & res ) const { Leters & leters = *lt; if( Letter *l = leters.find(ch) ){ if(l->surf.w()==l->size.w && l->surf.h()==l->size.h) return *l; } FT_Face face = nullptr; FT_Vector pen = {0,0}; FT_Error err = 0; char* data = nullptr; if(memData){ data = memData->data; } else if(fdata[key.name]!=nullptr){ data = fdata[key.name].get(); } else { RFile file(fnames[key.name].c_str()); size_t sz = file.size(); data = new char[sz]; fdata[key.name].reset(data); if(file.readData(data,sz)!=sz) fdata[key.name].reset(); } Tempest::MemReader reader(data,-1); FT_StreamRec stream; ft().mkStream(stream,reader); err = ft().New_Face( ft().library, stream, 0, &face ); if( err ) return nullLeter(ch); err = FT_Set_Pixel_Sizes( face, key.size, key.size ); if( err ) return nullLeter(ch); FT_Set_Transform( face, 0, &pen ); Letter letter; if( FT_Load_Char( face, ch, FT_LOAD_RENDER ) ){ Letter &ref = leters[ch]; ref = letter; return ref; } FT_GlyphSlot slot = face->glyph; FT_Bitmap& bmap = slot->bitmap; letter.dpos = Tempest::Point( slot->bitmap_left, key.size - slot->bitmap_top ); if( bmap.width!=0 && bmap.rows!=0 ){ Tempest::Pixmap pixmap( bmap.width, bmap.rows, true ); for( int r=0; r<pixmap.height(); ++r ) for( int i=0; i<pixmap.width(); ++i ) { uint8_t lum = bmap.buffer[r * bmap.width + i]; Tempest::Pixmap::Pixel p = {255, 255, 255, lum}; pixmap.set( i,r, p ); } //pixmap.save("./l.png"); letter.surf = res.load( pixmap ); } letter.size = Tempest::Size( bmap.width, bmap.rows ); letter.advance = Tempest::Point( slot->advance.x >> 6, slot->advance.y >> 6 ); FT_Done_Face( face ); Letter &ref = leters[ch]; ref = letter; return ref; }
rtgui_font_t *rtgui_freetype_font_create(const char *filename, int bold, int italic, rt_size_t size) { FT_Error err = 0; struct rtgui_font *font; font = (struct rtgui_font *) rtgui_malloc(sizeof(struct rtgui_font)); if (font != RT_NULL) { struct rtgui_freetype_font *freetype; freetype = (struct rtgui_freetype_font *) rtgui_malloc(sizeof(struct rtgui_freetype_font)); if (freetype == RT_NULL) { rtgui_free(font); font = RT_NULL; } else { err = FT_Init_FreeType(&freetype->library); if ((err = FT_New_Face(freetype->library, filename, 0, &freetype->face))) { FT_Done_FreeType(freetype->library); rtgui_free(font); font = RT_NULL; } else { err = FT_Select_Charmap(freetype->face, ft_encoding_unicode); if (err) { err = FT_Select_Charmap(freetype->face, ft_encoding_latin_1); } err = FT_Set_Pixel_Sizes(freetype->face, 0, size); if (err != 0) { rtgui_free(font); font = RT_NULL; FT_Done_FreeType(freetype->library); rtgui_free(freetype); return RT_NULL; } freetype->bold = bold; freetype->italic = italic; rt_kprintf("fonfile:%s\n", filename); rt_kprintf("font family_name:%s\n", freetype->face->family_name); rt_kprintf("font style_name:%s\n", freetype->face->style_name); /* set user data */ font->data = freetype; font->family = rt_strdup(freetype->face->family_name); font->height = (rt_uint16_t)size; font->refer_count = 0; font->engine = &freetype_font_engine; /* add to system */ rtgui_font_system_add_font(font); } } } return font; }
void find_font(font & f, std::string const& name, int height, int weight, int style) { // FIXME: shouldn't require fonts to be empty BOOST_ASSERT(!f); FcValue fcval; // build pattern FcPattern * pattern = FcPatternCreate(); // typeface name fcval.type = FcTypeString; fcval.u.s = (FcChar8 const*) name.c_str(); FcPatternAdd(pattern, FC_FAMILY, fcval, FcTrue); // pixel size fcval.type = FcTypeInteger; fcval.u.i = height; FcPatternAdd(pattern, FC_PIXEL_SIZE, fcval, FcTrue); // italic if (style & font_style::italic) { fcval.type = FcTypeInteger; fcval.u.i = FC_SLANT_ITALIC; FcPatternAdd(pattern, FC_SLANT, fcval, FcTrue); } // weight fcval.type = FcTypeInteger; switch (weight) { case font_weight::thin: fcval.u.i = FC_WEIGHT_THIN; break; case font_weight::extra_light: fcval.u.i = FC_WEIGHT_EXTRALIGHT; break; case font_weight::light: fcval.u.i = FC_WEIGHT_LIGHT; break; case font_weight::normal: fcval.u.i = FC_WEIGHT_NORMAL; break; case font_weight::medium: fcval.u.i = FC_WEIGHT_MEDIUM; break; case font_weight::semi_bold: fcval.u.i = FC_WEIGHT_SEMIBOLD; break; case font_weight::bold: fcval.u.i = FC_WEIGHT_BOLD; break; case font_weight::ultra_bold: fcval.u.i = FC_WEIGHT_ULTRABOLD; break; case font_weight::heavy: fcval.u.i = FC_WEIGHT_HEAVY; break; default: fcval.u.i = FC_WEIGHT_NORMAL; break; }; FcPatternAdd(pattern, FC_WEIGHT, fcval, FcTrue); // transform pattern FcConfigSubstitute(NULL, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); FcResult res; FcPattern * real_pattern = FcFontMatch(NULL, pattern, &res); FcPatternDestroy(pattern); // get filename if (real_pattern) { char * fname; int id; FcPatternGetString(real_pattern, FC_FILE, 0, (FcChar8 **) &fname); FcPatternGetInteger(real_pattern, FC_INDEX, 0, &id); // load font FT_Face face; if (FT_New_Face(::ftlib, fname, id, &face)) return; FcPatternDestroy(real_pattern); // create font ft_font_extra * extra = static_cast<ft_font_extra *>(f.extra_data); extra->face = face; FT_Set_Pixel_Sizes(face, 0, height); // get avg char width/height point pt = detail::font_units_to_pixels(face, face->max_advance_width, face->max_advance_height); extra->width = pt.x; extra->height = pt.y; pt = detail::font_units_to_pixels(face, 0, face->ascender); extra->ascender = pt.y; pt = detail::font_units_to_pixels(face, 0, face->descender); extra->descender = pt.y; } }
void CFontMaterial::Load( std::string dir, FT_Library & ftLib, int fontsize ) { if( !Util::DoesFileExist( dir ) ) { Log::Error( "Attempted to load non-existant font " + dir ); return; } m_FontSize = fontsize; m_LargestBearingY = 0; FT_Face face; FT_New_Face( ftLib, dir.c_str(), 0, &face ); FT_Set_Pixel_Sizes( face, 0, fontsize ); int sheetwidth = 0, sheetheight = 0; int offsetx = 0; for( int j = 0; j < FONT_ALLOC; j++ ) { FT_UInt glyphindex = FT_Get_Char_Index( face, j ); FT_Load_Glyph( face, glyphindex, FT_LOAD_RENDER ); FT_GlyphSlot slot = face->glyph; FT_Bitmap bitmap = slot->bitmap; sheetwidth += bitmap.width; sheetheight = ( bitmap.rows > sheetheight )? bitmap.rows : sheetheight; } m_FontSheet.InitPixels( sheetwidth, sheetheight ); for( int j = 0; j < FONT_ALLOC; j++ ) { FT_UInt glyphindex = FT_Get_Char_Index( face, j ); FT_Load_Glyph( face, glyphindex, FT_LOAD_RENDER ); FT_GlyphSlot slot = face->glyph; FT_Bitmap bitmap = slot->bitmap; int w = bitmap.width; int h = bitmap.rows; GLubyte * data = new GLubyte[2 * w * h]; for( int y = 0; y < bitmap.rows; y++ ) { for( int x = 0; x < bitmap.width; x++ ) { data[2 * ( x + y * w )] = bitmap.buffer[x + bitmap.width * y] > 0? 255 : 0; data[2 * ( x + y * w ) + 1] = bitmap.buffer[x + bitmap.width * y]; } } m_Characters[j].m_Trans = face->glyph->advance.x >> 6; m_Characters[j].m_Left = face->glyph->bitmap_left; m_Characters[j].m_Size.Set( bitmap.width, bitmap.rows ); m_Characters[j].m_Down = slot->metrics.horiBearingY >> 6; m_Characters[j].m_Height = slot->metrics.height >> 6; if( m_LargestBearingY < m_Characters[j].m_Down ) m_LargestBearingY = m_Characters[j].m_Down; m_Characters[j].m_UpperLeftST.Set( ( float )offsetx / ( float )sheetwidth, 0.0f ); m_Characters[j].m_LowerRightST.Set( ( ( float )offsetx + ( float )w ) / ( float )sheetwidth, ( ( float )h ) / ( float )sheetheight ); m_FontSheet.AddPixelDataLuminance( data, offsetx, 0, w, h ); m_Characters[j].m_Offset = offsetx; offsetx += w; delete [] data; } FT_Done_Face( face ); m_FontSheet.CreateGLTexture(); std::stringstream log; log << "Successfully loaded font " << dir << " at size: " << fontsize; Log::Log( log.str() ); }
/** Render a glyph for a character into bitmap and save it into the glyph page. * \param c The character to be loaded. * \param c \ref GlyphInfo for the character. */ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi) { assert(gi.glyph_index > 0); assert(gi.font_number < m_face_ttf->getTotalFaces()); FT_Face cur_face = m_face_ttf->getFace(gi.font_number); FT_GlyphSlot slot = cur_face->glyph; // Same face may be shared across the different FontWithFace, // so reset dpi each time font_manager->checkFTError(FT_Set_Pixel_Sizes(cur_face, 0, getDPI()), "setting DPI"); font_manager->checkFTError(FT_Load_Glyph(cur_face, gi.glyph_index, FT_LOAD_DEFAULT), "loading a glyph"); font_manager->checkFTError(shapeOutline(&(slot->outline)), "shaping outline"); font_manager->checkFTError(FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL), "rendering a glyph to bitmap"); // Convert to an anti-aliased bitmap FT_Bitmap* bits = &(slot->bitmap); core::dimension2du texture_size(bits->width + 1, bits->rows + 1); if ((m_used_width + texture_size.Width > getGlyphPageSize() && m_used_height + m_current_height + texture_size.Height > getGlyphPageSize()) || m_used_height + texture_size.Height > getGlyphPageSize()) { // Add a new glyph page if current one is full createNewGlyphPage(); } // Determine the linebreak location if (m_used_width + texture_size.Width > getGlyphPageSize()) { m_used_width = 0; m_used_height += m_current_height; m_current_height = 0; } const unsigned int cur_tex = m_spritebank->getTextureCount() -1; #ifndef SERVER_ONLY if (bits->buffer != NULL) { video::ITexture* tex = m_spritebank->getTexture(cur_tex); glBindTexture(GL_TEXTURE_2D, tex->getOpenGLTextureName()); assert(bits->pixel_mode == FT_PIXEL_MODE_GRAY); if (CVS->isARBTextureSwizzleUsable()) { glTexSubImage2D(GL_TEXTURE_2D, 0, m_used_width, m_used_height, bits->width, bits->rows, GL_RED, GL_UNSIGNED_BYTE, bits->buffer); } else { const unsigned int size = bits->width * bits->rows; uint8_t* image_data = new uint8_t[size * 4]; memset(image_data, 255, size * 4); for (unsigned int i = 0; i < size; i++) image_data[4 * i + 3] = bits->buffer[i]; glTexSubImage2D(GL_TEXTURE_2D, 0, m_used_width, m_used_height, bits->width, bits->rows, GL_RGBA, GL_UNSIGNED_BYTE, image_data); delete[] image_data; } if (tex->hasMipMaps()) glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } #endif // Store the rectangle of current glyph gui::SGUISpriteFrame f; gui::SGUISprite s; core::rect<s32> rectangle(m_used_width, m_used_height, m_used_width + bits->width, m_used_height + bits->rows); f.rectNumber = m_spritebank->getPositions().size(); f.textureNumber = cur_tex; // Add frame to sprite s.Frames.push_back(f); s.frameTime = 0; m_spritebank->getPositions().push_back(rectangle); m_spritebank->getSprites().push_back(s); // Save glyph metrics FontArea a; a.advance_x = cur_face->glyph->advance.x / BEARING; a.bearing_x = cur_face->glyph->metrics.horiBearingX / BEARING; const int cur_height = (cur_face->glyph->metrics.height / BEARING); const int cur_offset_y = cur_height - (cur_face->glyph->metrics.horiBearingY / BEARING); a.offset_y = m_glyph_max_height - cur_height + cur_offset_y; a.offset_y_bt = -cur_offset_y; a.spriteno = f.rectNumber; m_character_area_map[c] = a; // Store used area m_used_width += texture_size.Width; if (m_current_height < texture_size.Height) m_current_height = texture_size.Height; } // insertGlyph
TextRenderer::TextRenderer (const std::string &font, unsigned height, boost::shared_ptr<GLExtensionProxy> proxy) : priv (new Private (proxy)) { priv->height = 0; FT_Error err; err = FT_New_Face (priv->lib, font.c_str (), 0, &priv->ft_face); if (err) { throw std::logic_error ("Failed to load font"); } err = FT_Set_Pixel_Sizes (priv->ft_face, 0, height); if (err) { throw std::logic_error ("Failed to set font height"); } priv->glyphs.resize (256); priv->kern = FT_HAS_KERNING (priv->ft_face); FT_UInt glyph_index; for (FT_ULong char_code = FT_Get_First_Char (priv->ft_face, &glyph_index); glyph_index != 0; char_code = FT_Get_Next_Char (priv->ft_face, char_code, &glyph_index)) { err = FT_Load_Glyph (priv->ft_face, glyph_index, FT_LOAD_DEFAULT); if (err) { SS_WARN ("Ignoring char %c because of Freetype error: %d", (char) char_code, err); continue; } if (char_code >= 256) continue; FT_Glyph tmp; err = FT_Get_Glyph (priv->ft_face->glyph, &tmp); if (err) { SS_WARN ("Ignoring char %c because of Freetype error: %d", (char) char_code, err); continue; } err = FT_Glyph_To_Bitmap (&tmp, FT_RENDER_MODE_LIGHT, 0, 1); if (err) { SS_WARN ("Ignoring char %c because of Freetype error: %d", (char) char_code, err); continue; } priv->glyphs[char_code].ft_glyph = tmp; priv->glyphs[char_code].glyph_index = glyph_index; } }
void Text::LoadFont(GLfloat width, GLfloat height) { shader = new Shader( (pathToShaders + "Text.vert").c_str(), (pathToShaders + "Text.frag").c_str()); shader->Use(); glm::mat4 projection = glm::ortho(0.0f, width, 0.0f, height); glUniformMatrix4fv(glGetUniformLocation(shader->Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); if(FT_Init_FreeType(&freetype)) throw DError() << msg("Could not init freetype library"); if(FT_New_Face(freetype, (pathToFont + font).c_str(), 0, &face)) throw DError() << msg("Could not open font: " + font); FT_Set_Pixel_Sizes(face, 0, size); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); for (GLubyte c = 0; c < 128; c++) { // Load character glyph if (FT_Load_Char(face, c, FT_LOAD_RENDER)) { std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl; continue; } // Generate texture GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D( GL_TEXTURE_2D, 0, GL_RED, face->glyph->bitmap.width, face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer ); // Set texture options glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Now store character for later use Character character = { texture, glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), (GLuint)face->glyph->advance.x }; Characters.insert(std::pair<GLchar, Character>(c, character)); } glBindTexture(GL_TEXTURE_2D, 0); // Destroy FreeType once we're finished FT_Done_Face(face); FT_Done_FreeType(freetype); // Configure VAO/VBO for texture quads glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); }
console* Create_console(console_attribute attr) { console* cons; int i; cons = (console*) malloc(sizeof(console)); if(cons==NULL) return NULL; cons->attr = attr; // make a copy of attributes // Init of members cons->line_current = 0; cons->line_next = 0; cons->car_current = 0; cons->nb_line = attr.height / attr.textsize; cons->nb_car_per_line = attr.width / attr.textsize; cons->line_top = -1; cons->ft_library = NULL; cons->ft_face = NULL; // Construction of buffer : // -> buffer[line][row] cons->buffer = (char**) malloc(sizeof(char*) * cons->nb_line); if(cons->buffer == NULL) { Delete_console(cons); return NULL; } for(i=0; i<(cons->nb_line) ; i++) { cons->buffer[i] = (char*) malloc(sizeof(char) * (cons->nb_car_per_line + 1)); // +1 for '\0' if(cons->buffer[i] == NULL) { Delete_console(cons); return NULL; } cons->buffer[i][0]='\0'; // first caracter NULL for no printing at the begining cons->buffer[i][cons->nb_car_per_line]='\0'; // should always be NULL } if ( attr.ft_library != NULL && attr.ft_face != NULL) // library and face specifed in attributes { // use library and face from attributes cons->ft_library = attr.ft_library; cons->ft_face = attr.ft_face; } else { if ( FT_Init_FreeType( &(cons->ft_library) ) ) { Delete_console(cons); return NULL; } // Load Face "/dev_hdd0/tmp/arial.ttf" // Not a good way, this default Face will be on memory if ( FT_New_Face( cons->ft_library, "/dev_hdd0/tmp/arial.ttf", 0, &(cons->ft_face) ) ) { Delete_console(cons); return NULL; } } // Set font size if( FT_Set_Pixel_Sizes( cons->ft_face, 0, cons->attr.textsize) ) { Delete_console(cons); return NULL; } return cons; }
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; }
Error DynamicFontAtSize::_load() { int error = FT_Init_FreeType( &library ); ERR_EXPLAIN(TTR("Error initializing FreeType.")); ERR_FAIL_COND_V( error !=0, ERR_CANT_CREATE ); if (font->font_path!=String()) { FileAccess *f=FileAccess::open(font->font_path,FileAccess::READ); ERR_FAIL_COND_V(!f,ERR_CANT_OPEN); memset(&stream,0,sizeof(FT_StreamRec)); stream.base=NULL; stream.size=f->get_len(); stream.pos=0; stream.descriptor.pointer=f; stream.read=_ft_stream_io; stream.close=_ft_stream_close; FT_Open_Args fargs; memset(&fargs,0,sizeof(FT_Open_Args)); fargs.flags=FT_OPEN_STREAM; fargs.stream=&stream; error = FT_Open_Face( library,&fargs,0,&face); } else if (font->font_mem) { memset(&stream,0,sizeof(FT_StreamRec)); stream.base=(unsigned char*)font->font_mem; stream.size=font->font_mem_size; stream.pos=0; FT_Open_Args fargs; memset(&fargs,0,sizeof(FT_Open_Args)); fargs.memory_base=(unsigned char*)font->font_mem; fargs.memory_size=font->font_mem_size; fargs.flags= FT_OPEN_MEMORY; fargs.stream=&stream; error = FT_Open_Face( library,&fargs,0,&face); } else { ERR_EXPLAIN("DynamicFont uninitialized"); ERR_FAIL_V(ERR_UNCONFIGURED); } //error = FT_New_Face( library, src_path.utf8().get_data(),0,&face ); if ( error == FT_Err_Unknown_File_Format ) { ERR_EXPLAIN(TTR("Unknown font format.")); FT_Done_FreeType( library ); } else if ( error ) { ERR_EXPLAIN(TTR("Error loading font.")); FT_Done_FreeType( library ); } ERR_FAIL_COND_V(error,ERR_FILE_CANT_OPEN); /*error = FT_Set_Char_Size(face,0,64*size,512,512); if ( error ) { FT_Done_FreeType( library ); ERR_EXPLAIN(TTR("Invalid font size.")); ERR_FAIL_COND_V( error, ERR_INVALID_PARAMETER ); }*/ error = FT_Set_Pixel_Sizes(face,0,size); ascent=face->size->metrics.ascender>>6; descent=-face->size->metrics.descender>>6; linegap=0; //print_line("ASCENT: "+itos(ascent)+" descent "+itos(descent)+" hinted: "+itos(face->face_flags&FT_FACE_FLAG_HINTER)); valid=true; return OK; }
gboolean draw_overview_glyph (cairo_t* context, const char* font_file, gdouble width, gdouble height, gunichar character) { FT_Face face; int error; gdouble units_per_em; gdouble units; gdouble advance; int gid; // private use area if (0xe000 <= character && character <= 0xf8ff) { return FALSE; } // control characters if (character <= 0x001f || (0x007f <= character && character <= 0x008d)) { return FALSE; } if (font_file == NULL) { g_warning("font_file is null"); return FALSE; } gchar text[7]; int length = g_unichar_to_utf8 (character, text); text[length] = '\0'; if (freetype_library == NULL) { error = FT_Init_FreeType (&freetype_library); if (error) { g_warning ("Freetype init error %d.\n", error); return FALSE; } } error = FT_New_Face (freetype_library, font_file, 0, &face); if (error) { g_warning ("Freetype font face error %d\n", error); return FALSE; } units_per_em = face->units_per_EM; units = (height * 0.5) / units_per_em; error = FT_Select_Charmap (face , FT_ENCODING_UNICODE); if (error) { g_warning ("Freetype can not use Unicode, error: %d\n", error); FT_Done_Face (face); return FALSE; } error = FT_Set_Char_Size (face, 0, 64, (int) height, (int) height); if (error) { g_warning ("FT_Set_Char_Size, error: %d.\n", error); FT_Done_Face (face); return FALSE; } error = FT_Set_Pixel_Sizes (face, 0, (int) (height * 0.5)); if (error) { g_warning ("FT_Set_Pixel_Sizes, error: %d.\n", error); FT_Done_Face (face); return FALSE; } gid = FT_Get_Char_Index (face, character); advance = 0; if (gid != 0) { FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP | FT_LOAD_NO_SCALE); advance = face->glyph->metrics.horiAdvance; advance *= units; } else { FT_Done_Face (face); return FALSE; } static const cairo_user_data_key_t key; cairo_save (context); cairo_font_face_t* cairo_face = cairo_ft_font_face_create_for_ft_face (face, 0); if (cairo_face == NULL) { g_warning("cairo font face is null"); FT_Done_Face (face); return FALSE; } int status = cairo_font_face_set_user_data (cairo_face, &key, face, (cairo_destroy_func_t) FT_Done_Face); if (status != CAIRO_STATUS_SUCCESS) { cairo_font_face_destroy (cairo_face); FT_Done_Face (face); return FALSE; } cairo_set_font_face (context, cairo_face); cairo_set_font_size (context, height * 0.5); gdouble x = (width - advance) / 2; if (x < 0) { x = 0; } cairo_move_to (context, x, height - 30); cairo_show_text (context, text); cairo_font_face_destroy (cairo_face); cairo_restore (context); // cairo closes the font face and the library must be kept open return TRUE; }
//------------------------------------------------------------------------------ APP::TextureAtlas::TextureAtlas(const std::string& font, unsigned int fontSize) { // INIT FREETYPE AND CREATE FONT ATLAS // check if we have and OpenGL context ERROR_ASSERT(APP::IsInitialized()) FT_Library library; // load the library FT_Error err = FT_Init_FreeType(&library); ERROR_ASSERT(err == 0) FT_Face face; // load the font (face) err = FT_New_Face( library, font.c_str(), 0, &face ); ERROR_ASSERT(err == 0) FT_Set_Pixel_Sizes(face, 0, fontSize); FT_GlyphSlot g = face->glyph; int atlasWidth = 0; int atlasHeight = 0; // compute the dimensions of the atlas for (unsigned int i = 32; i < 128; i++) { if(FT_Load_Char(face, i, FT_LOAD_RENDER)) { ERROR_WARNING("Loading character %c failed!") continue; } atlasWidth += g->bitmap.width; atlasHeight = std::max(atlasHeight, g->bitmap.rows); } unsigned char* atlas = new unsigned char[atlasWidth*atlasHeight]; for (unsigned int i = 0; i < atlasHeight*atlasWidth; i++) { atlas[i] = 0; } int marker = 0; for (unsigned int i = 32; i < 128; i++) { if(FT_Load_Char(face, i, FT_LOAD_RENDER)) { continue; } int w = g->bitmap.width; int h = g->bitmap.rows; unsigned char* buffer = g->bitmap.buffer; // copy character to atlas for (unsigned int v = 0; v < h; v++) { for (unsigned int u = 0; u < w; u++) { int y = atlasHeight - 1 - v; int x = marker + u; atlas[y*atlasWidth + x] = buffer[v*w + u]; } } // save character info bitmapWidth_[i] = w; atlasOffX_[i] = marker; advances_[i] = Math::Vector2I(g->advance.x >> 6, g->advance.y >> 6); bearings_[i] = Math::Vector2I(g->bitmap_left, g->bitmap_top); // increase marker marker += w; } // initialize member atlas_ = new GL::Tex2DR8FR8UI( atlasWidth, atlasHeight, static_cast<void*>(atlas) ); width_ = atlasWidth; height_ = atlasHeight; // clean up delete[] atlas; FT_Done_FreeType(library); }
FT_Error TA_sfnt_build_glyph_instructions(SFNT* sfnt, FONT* font, FT_Long idx) { FT_Face face = sfnt->face; FT_Error error; FT_Byte* ins_buf; FT_UInt ins_len; FT_Byte* bufp; FT_Byte* p; SFNT_Table* glyf_table = &font->tables[sfnt->glyf_idx]; glyf_Data* data = (glyf_Data*)glyf_table->data; /* `idx' is never negative */ GLYPH* glyph = &data->glyphs[idx]; TA_GlyphHints hints; FT_UInt num_hints_records; Hints_Record* hints_records; Recorder recorder; FT_Int32 load_flags; FT_UInt size; /* XXX: right now, we abuse this flag to control */ /* the global behaviour of the auto-hinter */ load_flags = font->fallback_script << 30; load_flags |= 1 << 28; /* vertical hinting only */ if (font->increase_x_height) load_flags |= 1 << 29; if (!font->pre_hinting) load_flags |= FT_LOAD_NO_SCALE; /* computing the segments is resolution independent, */ /* thus the pixel size in this call is arbitrary */ error = FT_Set_Pixel_Sizes(face, 20, 20); if (error) return error; ta_loader_register_hints_recorder(font->loader, NULL, NULL); error = ta_loader_load_glyph(font->loader, face, (FT_UInt)idx, load_flags); if (error) return error; /* do nothing if we have an empty glyph */ if (!face->glyph->outline.n_contours) return FT_Err_Ok; hints = &font->loader->hints; /* do nothing if the setup delivered the dummy module only */ if (!hints->num_points) return FT_Err_Ok; /* we allocate a buffer which is certainly large enough */ /* to hold all of the created bytecode instructions; */ /* later on it gets reallocated to its real size */ ins_len = hints->num_points * 1000; ins_buf = (FT_Byte*)malloc(ins_len); if (!ins_buf) return FT_Err_Out_Of_Memory; /* initialize array with an invalid bytecode */ /* so that we can easily find the array length at reallocation time */ memset(ins_buf, INS_A0, ins_len); num_hints_records = 0; hints_records = NULL; /* handle composite glyph */ if (font->loader->gloader->base.num_subglyphs) { bufp = TA_font_build_subglyph_shifter(font, ins_buf); if (!bufp) { error = FT_Err_Out_Of_Memory; goto Err; } goto Done1; } /* only scale the glyph if the dummy hinter has been used */ if (font->loader->metrics->clazz == &ta_dummy_script_class) { /* since `TA_init_recorder' hasn't been called yet, */ /* we manually initialize the `glyph' field */ recorder.glyph = glyph; bufp = TA_sfnt_build_glyph_scaler(sfnt, &recorder, ins_buf); if (!bufp) { error = FT_Err_Out_Of_Memory; goto Err; } goto Done1; } error = TA_init_recorder(&recorder, face->glyph->outline.n_contours, font, glyph, hints); if (error) goto Err; bufp = TA_sfnt_build_glyph_segments(sfnt, &recorder, ins_buf); if (!bufp) { error = FT_Err_Out_Of_Memory; goto Err; } /* now we loop over a large range of pixel sizes */ /* to find hints records which get pushed onto the bytecode stack */ #ifdef DEBUGGING { int num_chars, i; num_chars = fprintf(stderr, "glyph %ld\n", idx); for (i = 0; i < num_chars - 1; i++) putc('=', stderr); fprintf(stderr, "\n\n"); } #endif /* we temporarily use `ins_buf' to record the current glyph hints, */ /* leaving two bytes at the beginning so that the number of actions */ /* can be inserted later on */ ta_loader_register_hints_recorder(font->loader, TA_hints_recorder, (void*)&recorder); for (size = font->hinting_range_min; size <= font->hinting_range_max; size++) { TA_rewind_recorder(&recorder, bufp, size); error = FT_Set_Pixel_Sizes(face, size, size); if (error) goto Err; /* calling `ta_loader_load_glyph' uses the */ /* `TA_hints_recorder' function as a callback, */ /* modifying `hints_record' */ error = ta_loader_load_glyph(font->loader, face, idx, load_flags); if (error) goto Err; /* append the point hints data collected in `TA_hints_recorder' */ TA_build_point_hints(&recorder, hints); /* store the number of actions in `ins_buf' */ *bufp = HIGH(recorder.hints_record.num_actions); *(bufp + 1) = LOW(recorder.hints_record.num_actions); if (TA_hints_record_is_different(hints_records, num_hints_records, bufp, recorder.hints_record.buf)) { #ifdef DEBUGGING { fprintf(stderr, " size %d:\n", size); ta_glyph_hints_dump_edges(_ta_debug_hints); ta_glyph_hints_dump_segments(_ta_debug_hints); ta_glyph_hints_dump_points(_ta_debug_hints); fprintf(stderr, " hints record:\n"); for (p = bufp; p < recorder.hints_record.buf; p += 2) fprintf(stderr, " %2d", *p * 256 + *(p + 1)); fprintf(stderr, "\n"); } #endif error = TA_add_hints_record(&hints_records, &num_hints_records, bufp, recorder.hints_record); if (error) goto Err; } } if (num_hints_records == 1 && !hints_records[0].num_actions) { /* since we only have a single empty record we just scale the glyph, */ /* overwriting the data from `TA_sfnt_build_glyph_segments' */ bufp = TA_sfnt_build_glyph_scaler(sfnt, &recorder, ins_buf); if (!bufp) { error = FT_Err_Out_Of_Memory; goto Err; } /* clear the rest of the temporarily used part of `ins_buf' */ p = bufp; while (*p != INS_A0) *(p++) = INS_A0; goto Done; } /* in most cases, the output of `TA_sfnt_build_glyph_segments' */ /* is shorter than the previously stored data, */ /* so clear the rest of the temporarily used part of `ins_buf' */ /* before appending the hints records */ p = bufp; while (*p != INS_A0) *(p++) = INS_A0; bufp = TA_sfnt_emit_hints_records(sfnt, hints_records, num_hints_records, bufp); Done: TA_free_hints_records(hints_records, num_hints_records); TA_free_recorder(&recorder); /* we are done, so reallocate the instruction array to its real size */ if (*bufp == INS_A0) { /* search backwards */ while (*bufp == INS_A0) bufp--; bufp++; } else { /* search forwards */ while (*bufp != INS_A0) bufp++; } Done1: ins_len = bufp - ins_buf; if (ins_len > sfnt->max_instructions) sfnt->max_instructions = ins_len; glyph->ins_buf = (FT_Byte*)realloc(ins_buf, ins_len); glyph->ins_len = ins_len; return FT_Err_Ok; Err: TA_free_hints_records(hints_records, num_hints_records); TA_free_recorder(&recorder); free(ins_buf); return error; }
Tempest::FontElement::LetterGeometry Tempest::FontElement::letterGeometry( char16_t ch ) const { Leters & letters = *lt; if( Letter *l = letters.find(ch) ){ LetterGeometry r; r.advance = l->advance; r.dpos = l->dpos; r.size = l->size; return r; } FT_Face face; FT_Vector pen = {0,0}; FT_Error err = 0; if(fdata[key.name]==nullptr){ RFile file(fnames[key.name].c_str()); size_t sz = file.size(); char * ch = new char[sz]; fdata[key.name].reset(ch); if(file.readData(ch,sz)!=sz) fdata[key.name].reset(); } Tempest::MemReader reader(fdata[key.name].get(),size_t(-1)); FT_StreamRec stream; ft().mkStream(stream, reader); err = ft().New_Face( ft().library, stream, 0, &face ); if( err ) return LetterGeometry(); err = FT_Set_Pixel_Sizes( face, key.size, key.size ); if( err ){ return LetterGeometry(); } FT_Set_Transform( face, 0, &pen ); LetterGeometry letter; if( FT_Load_Char( face, ch, FT_LOAD_RENDER ) ){ return letter; } FT_GlyphSlot slot = face->glyph; FT_Bitmap& bmap = slot->bitmap; letter.dpos = Tempest::Point( slot->bitmap_left, key.size - slot->bitmap_top ); letter.size = Tempest::Size( bmap.width, bmap.rows ); letter.advance = Tempest::Point( slot->advance.x >> 6, slot->advance.y >> 6 ); FT_Done_Face( face ); Letter &ref = letters[ch]; ref.size =letter.size; ref.dpos =letter.dpos; ref.advance=letter.advance; return letter; }
void inittext(Shader &shader) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Compile and setup the shader /*Shader shader("shaders/text.vs", "shaders/text.frag");*/ glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(WIDTH), 0.0f, static_cast<GLfloat>(HEIGHT)); shader.Use(); glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); // FreeType FT_Library ft; // All functions return a value different than 0 whenever an error occurred if (FT_Init_FreeType(&ft)) std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl; // Load font as face FT_Face face; if (FT_New_Face(ft, "DejaVuSansMono.ttf", 0, &face)) std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl; // Set size to load glyphs as int fontsize; cfg.SET(fontsize); FT_Set_Pixel_Sizes(face, 0, fontsize); //FT_Set_Pixel_Sizes(face, 0, 48); // Disable byte-alignment restriction glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Load first 128 characters of ASCII set for (GLubyte c = 0; c < 128; c++) { // Load character glyph if (FT_Load_Char(face, c, FT_LOAD_RENDER)) { std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl; continue; } // Generate texture GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D( GL_TEXTURE_2D, 0, GL_RED, face->glyph->bitmap.width, face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer ); // Set texture options glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Now store character for later use Character character = { texture, glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), face->glyph->advance.x }; Characters.insert(std::pair<GLchar, Character>(c, character)); } glBindTexture(GL_TEXTURE_2D, 0); // Destroy FreeType once we're finished FT_Done_Face(face); FT_Done_FreeType(ft); // Configure VAO/VBO for texture quads glGenVertexArrays(1, &VAO_text); glGenBuffers(1, &VBO_text); glBindVertexArray(VAO_text); glBindBuffer(GL_ARRAY_BUFFER, VBO_text); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); }
void FreetypeFont::SetFontSize(const U32 fontSize) { FT_Set_Pixel_Sizes(m_FontFace, 0, fontSize); }
int generateFontMetadata (const char* ttfFilePath, const char* outputPNG, const char* outputMETADATA) { //fprintf(stderr, "TTF file %s \n", ttfFilePath); //fprintf(stderr, "PNG file %s \n", outputPNG); //fprintf(stderr, "META file %s \n", outputMETADATA); // Now we can initialise FreeType /* Check if the files are already there */ FILE* fpMeta = fopen(outputMETADATA, "r"); FILE* fpPng = fopen(outputPNG, "r"); if (fpMeta) { fclose (fpMeta); if (fpPng) { fclose (fpPng); return 0; } } FT_Library ft; if (FT_Init_FreeType (&ft)) { fprintf (stderr, "Could not init FreeType library\n"); return 1; } // load a font face from a file FT_Face face; if (FT_New_Face (ft, ttfFilePath, 0, &face)) { fprintf(stderr, "Could not open .ttf file\n"); return 1; } int atlas_dimension_px = 1024; // atlas size in pixels int atlas_columns = 16; // number of glyphs across atlas int padding_px = 6; // total space in glyph size for outlines int slot_glyph_size = 64; // glyph maximum size in pixels int atlas_glyph_px = 64 - padding_px; // leave some padding for outlines // Next we can open a file stream to write our atlas image to unsigned char* atlas_buffer = (unsigned char*)malloc ( atlas_dimension_px * atlas_dimension_px * 4 * sizeof (unsigned char) ); unsigned int atlas_buffer_index = 0; // I'll tell FreeType the maximum size of each glyph in pixels int grows[256]; // glyph height in pixels int gwidth[256]; // glyph width in pixels int gpitch[256]; // bytes per row of pixels int gymin[256]; // offset for letters that dip below baseline like g and y unsigned char* glyph_buffer[256] = { NULL }; // stored glyph images // set height in pixels width 0 height 48 (48x48) FT_Set_Pixel_Sizes (face, 0, atlas_glyph_px); for (int i = 33; i < 256; i++) { if (FT_Load_Char (face, i, FT_LOAD_RENDER)) { fprintf(stderr, "Could not load character %i\n", i); return 1; } // draw glyph image anti-aliased FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL); // get dimensions of bitmap grows[i] = face->glyph->bitmap.rows; gwidth[i] = face->glyph->bitmap.width; gpitch[i] = face->glyph->bitmap.pitch; // copy glyph data into memory because it seems to be overwritten/lost later glyph_buffer[i] = (unsigned char*)malloc (grows[i] * gpitch[i]); memcpy ( glyph_buffer[i], face->glyph->bitmap.buffer, face->glyph->bitmap.rows * face->glyph->bitmap.pitch ); // get y-offset to place glyphs on baseline. this is in the bounding box FT_Glyph glyph; // a handle to the glyph image if (FT_Get_Glyph (face->glyph, &glyph)) { fprintf(stderr, "Could not get glyph handle %i\n", i); return 1; } // get bbox. "truncated" mode means get dimensions in pixels FT_BBox bbox; FT_Glyph_Get_CBox (glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox); gymin[i] = bbox.yMin; } for (int y = 0; y < atlas_dimension_px; y++) { for (int x = 0; x < atlas_dimension_px; x++) { // work out which grid slot[col][row] we are in e.g out of 16x16 int col = x / slot_glyph_size; int row = y / slot_glyph_size; int order = row * atlas_columns + col; int glyph_index = order + 32; // an actual glyph bitmap exists for these indices if (glyph_index > 32 && glyph_index < 256) { // pixel indices within padded glyph slot area int x_loc = x % slot_glyph_size - padding_px / 2; int y_loc = y % slot_glyph_size - padding_px / 2; // outside of glyph dimensions use a transparent, black pixel (0,0,0,0) if (x_loc < 0 || y_loc < 0 || x_loc >= gwidth[glyph_index] || y_loc >= grows[glyph_index]) { atlas_buffer[atlas_buffer_index++] = 0; atlas_buffer[atlas_buffer_index++] = 0; atlas_buffer[atlas_buffer_index++] = 0; atlas_buffer[atlas_buffer_index++] = 0; } else { // this is 1, but it's safer to put it in anyway //int bytes_per_pixel = gwidth[glyph_index] / gpitch[glyph_index]; //int bytes_in_glyph = grows[glyph_index] * gpitch[glyph_index]; int byte_order_in_glyph = y_loc * gwidth[glyph_index] + x_loc; unsigned char colour[4]; colour[0] = colour[1] = colour[2] = colour[3] = glyph_buffer[glyph_index][byte_order_in_glyph]; // print byte from glyph atlas_buffer[atlas_buffer_index++] = glyph_buffer[glyph_index][byte_order_in_glyph]; atlas_buffer[atlas_buffer_index++] = glyph_buffer[glyph_index][byte_order_in_glyph]; atlas_buffer[atlas_buffer_index++] = glyph_buffer[glyph_index][byte_order_in_glyph]; atlas_buffer[atlas_buffer_index++] = glyph_buffer[glyph_index][byte_order_in_glyph]; } // write black in non-graphical ASCII boxes } else { atlas_buffer[atlas_buffer_index++] = 0; atlas_buffer[atlas_buffer_index++] = 0; atlas_buffer[atlas_buffer_index++] = 0; atlas_buffer[atlas_buffer_index++] = 0; } //endif } // endfor } // endfor // write meta-data file to go with atlas image std::ofstream myfile; myfile.open (outputMETADATA, std::ios::in | std::ios::out | std::ios::app | std::ios::binary); if (!myfile.is_open()) { free (atlas_buffer); return 1; } myfile << "// ascii_code prop_xMin prop_width prop_yMin prop_height prop_y_offset" << std::endl; // write an unique line for the 'space' character myfile << "32" << " " << std::fixed << std::setprecision( 6 ) << 0.0f << " " << 0.5 << " " << 0.0f << " " << 1.0 << " " << 0.0f << std::endl; // write a line for each regular character for (int i = 33; i < 256; i++) { int order = i - 32; int col = order % atlas_columns; int row = order / atlas_columns; float x_min = (float)(col * slot_glyph_size) / (float)atlas_dimension_px; float y_min = (float)(row * slot_glyph_size) / (float)atlas_dimension_px; myfile << std::fixed << std::setprecision( 6 ) << i << " " << x_min << " " << (float)(gwidth[i] + padding_px) / 64.0f << " " << y_min << " " << (grows[i] + padding_px) / 64.0f << " " << -((float)padding_px - (float)gymin[i]) / 64.0f << std::endl; } myfile.close(); // free that buffer of glyph info for (int i = 0; i < 256; i++) { if (NULL != glyph_buffer[i]) { free (glyph_buffer[i]); } } // use stb_image_write to write directly to png if (!stbi_write_png ( outputPNG, atlas_dimension_px, atlas_dimension_px, 4, atlas_buffer, 0 )) { fprintf (stderr, "ERROR: could not write file %s\n", outputPNG); } free (atlas_buffer); return 0; }
static Font_Info * _font_slave_int_load(const Slave_Msg_Font_Load *msg, Font_Source_Info *fsi) { int error; int val, dv; int ret; Font_Info *fi = calloc(1, sizeof(*fi)); error = FT_New_Size(fsi->face, &(fi->size)); if (!error) FT_Activate_Size(fi->size); fi->fsize = msg->size; fi->dpi = msg->dpi; fi->real_size = msg->size * 64; fi->fsi = fsi; error = FT_Set_Char_Size(fsi->face, 0, fi->real_size, msg->dpi, msg->dpi); if (error) error = FT_Set_Pixel_Sizes(fsi->face, 0, fi->real_size); if (error) { int i, maxd = 0x7fffffff; int chosen_size = 0; int chosen_size2 = 0; for (i = 0; i < fsi->face->num_fixed_sizes; i++) { int s, cd; s = fsi->face->available_sizes[i].size; cd = chosen_size - fi->real_size; if (cd < 0) cd = -cd; if (cd < maxd) { maxd = cd; chosen_size = s; chosen_size2 = fsi->face->available_sizes[i].y_ppem; if (maxd == 0) break; } } fi->real_size = chosen_size; error = FT_Set_Pixel_Sizes(fsi->face, 0, fi->real_size); if (error) { error = FT_Set_Char_Size(fsi->face, 0, fi->real_size, fi->dpi, fi->dpi); if (error) { /* hack around broken fonts */ fi->real_size = (chosen_size2 / 64) * 60; error = FT_Set_Char_Size(fsi->face, 0, fi->real_size, fi->dpi, fi->dpi); if (error) { ERR("Could not choose the font size for font: '%s:%s'.", msg->file, msg->name); FT_Done_Size(fi->size); free(fi); return NULL; } } } } fi->max_h = 0; val = (int)fsi->face->bbox.yMax; if (fsi->face->units_per_EM != 0) { dv = (fsi->orig_upem * 2048) / fsi->face->units_per_EM; ret = (val * fsi->face->size->metrics.y_scale) / (dv * dv); } else ret = val; fi->max_h += ret; val = -(int)fsi->face->bbox.yMin; if (fsi->face->units_per_EM != 0) { dv = (fsi->orig_upem * 2048) / fsi->face->units_per_EM; ret = (val * fsi->face->size->metrics.y_scale) / (dv * dv); } else ret = val; fi->max_h += ret; fi->runtime_rend = FONT_REND_REGULAR; if ((msg->rend_flags & FONT_REND_SLANT) && !(fsi->face->style_flags & FT_STYLE_FLAG_ITALIC)) fi->runtime_rend |= FONT_REND_SLANT; if ((msg->rend_flags & FONT_REND_WEIGHT) && !(fsi->face->style_flags & FT_STYLE_FLAG_BOLD)) fi->runtime_rend |= FONT_REND_WEIGHT; return fi; }
int main(int argc, char** argv) { if(argc < 2) { printf("Error! No Font Specified!\n"); return 1; } std::string filename(argv[1]); const size_t last_slash_idx = filename.find_last_of("\\/"); if (std::string::npos != last_slash_idx) { filename.erase(0, last_slash_idx + 1); } // Remove extension if present. const size_t period_idx = filename.rfind('.'); if (std::string::npos != period_idx) { filename.erase(period_idx); } FIBITMAP* bitmap = FreeImage_Allocate(4096, 4096, 32); FreeImage_SetTransparent(bitmap, true); FT_Library library; FT_Init_FreeType(&library); FT_Face face; FT_New_Face(library, argv[1], 0, &face); rapidjson::StringBuffer s; rapidjson::Writer<rapidjson::StringBuffer> writer(s); writer.StartObject(); writer.String("glyphs"); writer.StartArray(); int tileSize = 64; int tileCount = 64; FT_Set_Pixel_Sizes(face, 0, tileSize); for (int c = 32, id = 0; c < 4096; ++c, ++id) { FT_Load_Char(face, c, FT_LOAD_RENDER); FT_Bitmap bmp = face->glyph->bitmap; FT_Glyph_Metrics metrics = face->glyph->metrics; int i = 0, j = 0; for (j = 0; j < bmp.rows; ++j) { for (i = 0; i < bmp.width; ++i) { RGBQUAD value = {bmp.buffer[j * bmp.width + i], bmp.buffer[j * bmp.width + i], bmp.buffer[j * bmp.width + i], bmp.buffer[j * bmp.width + i]}; FreeImage_SetPixelColor( bitmap, (id % tileCount) * tileSize + i, (id / tileCount) * tileSize + bmp.rows - j, &value); } } double x = (id % tileCount) * tileSize; double y = (id / tileCount) * tileSize; writer.StartObject(); { writer.String("id"); writer.Uint(c); writer.String("char"); writer.String((const char*)&c); writer.String("metrics"); writer.StartObject(); { writer.String("width"); writer.Int(metrics.width / 64); writer.String("height"); writer.Int(metrics.height / 64); writer.String("horiBearingX"); writer.Int(metrics.horiBearingX / 64); writer.String("horiBearingY"); writer.Int(metrics.horiBearingY / 64); writer.String("horiAdvance"); writer.Int(metrics.horiAdvance / 64); writer.String("vertBearingX"); writer.Int(metrics.vertBearingX / 64); writer.String("vertBearingY"); writer.Int(metrics.vertBearingY / 64); writer.String("vertAdvance"); writer.Int(metrics.vertAdvance / 64); } writer.EndObject(); writer.String("uv"); writer.StartArray(); writer.StartArray(); writer.Double(x / 4096.0); writer.Double((y - metrics.height / 64) / 4096.0); writer.EndArray(); writer.StartArray(); writer.Double(x / 4096.0); writer.Double(y / 4096.0); writer.EndArray(); writer.StartArray(); writer.Double((x + metrics.width / 64) / 4096.0); writer.Double(y / 4096.0); writer.EndArray(); writer.StartArray(); writer.Double((x + metrics.width / 64) / 4096.0); writer.Double((y - metrics.height / 64) / 4096.0); writer.EndArray(); writer.EndArray(); } writer.EndObject(); } writer.EndArray(); writer.EndObject(); FILE* fp = fopen((filename + ".json").c_str(), "w"); fputs(s.GetString(), fp); fclose(fp); if (FreeImage_Save(FIF_PNG, bitmap, (filename + ".png").c_str(), BMP_DEFAULT)) { printf("Saved file to %s\n", (filename + ".png").c_str()); } FreeImage_Unload(bitmap); return 0; }
//-------------------------------------------------------------- void ofxFreeType2::loadFont(string filepath, int size, bool _bAntiAlias, bool _bFullCharacterSet, bool _bMakeContours) { //Init vars fontSize = size; lineHeight = fontSize; bAntiAlias = _bAntiAlias; bFullCharacterSet = _bFullCharacterSet; bMakeContours = _bMakeContours; //Clear chars if a font has already been loaded if (bFaceLoaded){ if (chars != NULL){ delete[] chars; } if (charTextures != NULL){ for (int i = 0; i < nChars; i++){ glDeleteTextures(1, &charTextures[i]); } delete[] charTextures; } } bFaceLoaded = false; //FreeType Vars FT_Library library; FT_Face face; FT_Error error; //Init library if (FT_Init_FreeType(&library)) { ofLog(OF_LOG_ERROR,"Freetype Error: Unable to Initialize Library"); return; } //Load Font Face error = FT_New_Face(library, ofToDataPath(filepath).c_str(), 0, &face ); if (error == FT_Err_Unknown_File_Format) { //Font opened but unsupported ofLog(OF_LOG_ERROR,"Freetype Error: Unknown Format"); return; } else if (error) { //Font can not be read/is broken ofLog(OF_LOG_ERROR,"Freetype Error: Couldn't open font."); return; } //Set Size (pts or pixels) if(resolution) error = FT_Set_Char_Size(face, 0, (float(size) * (72.0/float(resolution)))*64.0, resolution, resolution); else error = FT_Set_Pixel_Sizes(face, size, size); if(error) { ofLog(OF_LOG_ERROR,"Freetype Error: Couldn't set font size. Font may be fixed width?"); return; } //Get total Chars from Face nChars = bFullCharacterSet ? 256 : 128; nChars -= START_CHAR; //Init arrays, generate textures chars = new charInfo[nChars]; charTextures = new GLuint[nChars]; glGenTextures(nChars, charTextures); //Clear/Create contour array if necessary if(bMakeContours){ charOutlines.clear(); charOutlines.assign(nChars, ofTTCharacter()); } //Create Characters FT_Glyph glyph; FT_BBox bbox; for(int i=START_CHAR; i<nChars+START_CHAR; i++) { //Load Glyph int charIndex = i - START_CHAR; if(bAntiAlias) { error = FT_Load_Char(face, i, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT); } else { error = FT_Load_Char(face, i, FT_LOAD_RENDER | FT_LOAD_MONOCHROME); } if(error) { ofLog(OF_LOG_ERROR,"Freetype Error: Couldn't load glyph."); return; } //Create Outline if necessary if(bMakeContours){ if( printVectorInfo )printf("\n\ncharacter %c: \n", char( i ) ); //int character = i + NUM_CHARACTER_TO_START; charOutlines[charIndex] = makeContoursForCharacter( face ); } //Get Size of Bitmap, convert from fractional pixel format float bmpW = face->glyph->metrics.width >> 6; float bmpH = face->glyph->metrics.height >> 6; //Create Texture Sizes //Add 2px to avoid edges on either side int width = ofNextPow2(bmpW+1); int height = ofNextPow2(bmpH+1); if (width == 1) width = 2; if (height == 1) height = 2; //Set Info for This Glyph FT_Get_Glyph(face->glyph, &glyph ); FT_Glyph_Get_CBox(glyph, 0, &bbox ); float xMin = bbox.xMin >> 6; float xMax = bbox.xMax >> 6; float yMin = bbox.yMin >> 6; float yMax = bbox.yMax >> 6; chars[charIndex].value = i; chars[charIndex].width = xMax - xMin; chars[charIndex].height = yMax - yMin; chars[charIndex].left = face->glyph->bitmap_left; chars[charIndex].top = yMax; chars[charIndex].bottom = yMin; chars[charIndex].advance = face->glyph->metrics.horiAdvance >> 6; //Set the position of the texture, centered with pow2 tex chars[charIndex].texWidth = bmpW; chars[charIndex].texHeight = bmpH; chars[charIndex].texXDiff = (float)bmpW/(float)width; chars[charIndex].texYDiff = (float)bmpH/(float)height; // Allocate Memory For The Texture Data. unsigned char* expanded_data = new unsigned char[2 * width * height]; //Clear Texture Data for(int j=0; j <height;j++) { for(int k=0; k < width; k++){ expanded_data[2*(k+(j*width))] = 255; // every luminance pixel = 255 expanded_data[2*(k+(j*width))+1] = 0; } } //Create texture from bitmap of glyph FT_Bitmap& bitmap= face->glyph->bitmap; if (bAntiAlias){ //----------------------------------- for(int j=0; j <height; j++) { for(int k=0; k < width; k++){ if ((k<bitmap.width) && (j<bitmap.rows)){ //Offset pixels into texture by 1px to avoid edges //expanded_data[2*(((j+1)*width)+k +1) + 1] = bitmap.buffer[k + bitmap.width*(j)]; int thisPixel = (((j * width) + k) * 2) + 1; expanded_data[thisPixel] = bitmap.buffer[k + bitmap.width*(j)]; } } } //----------------------------------- } else { //----------------------------------- // true type packs monochrome info in a // 1-bit format, hella funky // here we unpack it: unsigned char *src = bitmap.buffer; for(int j=0; j <bitmap.rows;j++) { unsigned char b=0; unsigned char *bptr = src; for(int k=0; k < bitmap.width ; k++){ if (k%8==0) b=(*bptr++); //Offset pixels into texture by 1px to avoid edges expanded_data[2*(k+j*width)+1] = b&0x80 ? 255 : 0; b <<= 1; } src += bitmap.pitch; } //----------------------------------- } //Now we just setup some texture paramaters. glBindTexture( GL_TEXTURE_2D, charTextures[charIndex]); if (bAntiAlias){ glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); } glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //Here we actually create the texture itself, notice //that we are using GL_LUMINANCE_ALPHA to indicate that //we are using 2 channel data. glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data ); //Clear Expanded Data delete [] expanded_data; } bFaceLoaded = true; }
Font::Font(const std::string filename, size_t size) { // These are the characters that get stored to texture. // Margins around characters to prevent them from 'bleeding' into each other. const size_t margin = 1; size_t image_height = 0, image_width = 512; // Initialize FreeType FT_Library library; if (FT_Init_FreeType(&library)) { LOG_FATAL("FreeType initializing failed"); } else { LOG_STRING("FreeType initialized"); } // Load the font LOG_STRING("Loading font '" + filename + "'"); FT_Face face; int error = FT_New_Face(library, filename.c_str(), 0, &face); if (error == FT_Err_Unknown_File_Format) { LOG_FATAL("Font loading failed: Unknown format of a file."); } else if (error) { LOG_FATAL("Font loading failed: The file could not be opened or read, or is broken, or else."); } LOG_STRING("Font loaded"); // Some checks if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) { LOG_FATAL("The font is not scalable)"); } if (!(face->face_flags & FT_FACE_FLAG_HORIZONTAL)) { LOG_FATAL("The font is not horizontal)"); } // Set the font size FT_Set_Pixel_Sizes(face, size, 0); pixel_height = size; int max_height = 0; size_t x = margin, y = margin; // Rasterize all the characters int total_chars = 0; uint32_t gindex; // glyph index uint32_t unicode = FT_Get_First_Char(face, &gindex); while (gindex != 0) { char buf[100]; FT_Get_Glyph_Name(face, gindex, buf, 100); //LOG_STRING("Available character: " + stringify<uint32_t>(unicode) + " (" + buf + ")"); // Look up the character in the font file. if (glyphs.find(gindex) != glyphs.end()) { unicode2glyph[unicode] = glyphs[gindex]; continue; } // Render the current glyph. FT_Load_Glyph(face, gindex, FT_LOAD_RENDER | FT_LOAD_NO_HINTING); // Save character metrics Glyph *g = glyphs[gindex] = unicode2glyph[unicode] = new Glyph; // shortcut g->advance = face->glyph->metrics.horiAdvance / 64; g->width = face->glyph->bitmap.width; g->height = face->glyph->bitmap.rows; g->bearingX = face->glyph->metrics.horiBearingX / 64; g->bearingY = face->glyph->metrics.horiBearingY / 64; // Compute kerning //for (int j = 0; j < 256; j++) { // // Look up the second character in the font file. // int char2_index = FT_Get_Char_Index(face, j); // FT_Vector akerning; // FT_Get_Kerning(face, char_index, char2_index, FT_KERNING_DEFAULT, &akerning); // kerning[i][j] = static_cast<float>(akerning.x) / 64; //} // Save glyph bitmap g->data = new unsigned char[g->width * g->height]; memcpy(g->data, face->glyph->bitmap.buffer, g->width * g->height); // If the line is full, go to the next line if (g->width + margin > image_width - x) { x = margin; y += max_height + margin; max_height = 0; } g->x = x; g->y = y; max_height = std::max(g->height, max_height); x += g->width + margin; total_chars += 1; unicode = FT_Get_Next_Char(face, unicode, &gindex); } LOG_STRING("Total chars in atlas: " + stringify<int>(total_chars)); FT_Done_FreeType(library); LOG_STRING("Generating font texture atlas"); // Compute how high the texture has to be. int needed_image_height = y + max_height + margin; // Get the first power of two in which it fits. image_height = 16; while (image_height < needed_image_height) { image_height *= 2; } // Allocate memory for the texture, and set it to 0. unsigned char *image = new unsigned char[image_height * image_width]; for (int i = 0; i < image_height * image_width; i++) { image[i] = 0; } // Drawing loop for (std::map<uint32_t, Glyph*>::iterator gindex = glyphs.begin(); gindex != glyphs.end(); gindex++) { Glyph *g = gindex->second; // shortcut if (g->data != NULL) { // Fill in the texture coords g->tex_left = static_cast<float>(g->x) / image_width; g->tex_right = static_cast<float>(g->x + g->width) / image_width; g->tex_top = static_cast<float>(g->y) / image_height; g->tex_bottom = static_cast<float>(g->y + g->height) / image_height; // Copy the glyph bitmap to the texture atlas for (size_t row = 0; row < g->height; ++row) { memcpy(&image[g->x + (g->y + row) * image_width], &g->data[row * g->width], g->width); } delete[] g->data; g->data = NULL; } } LOG_STRING("Font texture atlas generated"); // Create OpenGL texture from the image. glDeleteTextures(1, &texture); glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, image_width, image_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, image); // Free the image memory. delete[] image; LOG_STRING("Font texture loaded"); }
int main(int argc, char** argv) { FT_Face face; long max_bytes = CACHE_SIZE * 1024; char* test_string = NULL; int size = FACE_SIZE; int max_iter = 0; double max_time = BENCH_TIME; int compare_cached = 0; int i; while ( 1 ) { int opt; opt = getopt( argc, argv, "Cc:f:m:pr:s:t:b:" ); if ( opt == -1 ) break; switch ( opt ) { case 'C': compare_cached = 1; break; case 'c': max_iter = atoi( optarg ); break; case 'f': load_flags = strtol( optarg, NULL, 16 ); break; case 'm': max_bytes = atoi( optarg ); max_bytes *= 1024; break; case 'p': preload = 1; break; case 'r': render_mode = (FT_Render_Mode)atoi( optarg ); if ( render_mode >= FT_RENDER_MODE_MAX ) render_mode = FT_RENDER_MODE_NORMAL; break; case 's': size = atoi( optarg ); if ( size <= 0 ) size = 1; else if ( size > 500 ) size = 500; break; case 't': max_time = atof( optarg ); break; case 'b': test_string = optarg; break; default: usage(); break; } } argc -= optind; argv += optind; if ( argc != 1 ) usage(); if ( FT_Init_FreeType( &lib ) ) { fprintf( stderr, "could not initialize font library\n" ); return 1; } filename = *argv; if ( get_face( &face ) ) goto Exit; if ( FT_IS_SCALABLE( face ) ) { if ( FT_Set_Pixel_Sizes( face, size, size ) ) { fprintf( stderr, "failed to set pixel size to %d\n", size ); return 1; } } else size = face->available_sizes[0].width; FTC_Manager_New( lib, 0, 0, max_bytes, face_requester, face, &cache_man ); font_type.face_id = (FTC_FaceID) 1; font_type.width = (short) size; font_type.height = (short) size; font_type.flags = load_flags; for ( i = 0; i < N_FT_BENCH; i++ ) { btest_t test; FT_ULong flags; if ( !TEST( 'a' + i ) ) continue; test.title = NULL; test.bench = NULL; test.cache_first = 0; test.user_data = NULL; switch ( i ) { case FT_BENCH_LOAD_GLYPH: test.title = "Load"; test.bench = test_load; benchmark( face, &test, max_iter, max_time ); if ( compare_cached ) { test.cache_first = 1; test.title = "Load (image cached)"; test.bench = test_image_cache; benchmark( face, &test, max_iter, max_time ); test.title = "Load (sbit cached)"; test.bench = test_sbit_cache; benchmark( face, &test, max_iter, max_time ); } break; case FT_BENCH_LOAD_ADVANCES: test.user_data = &flags; test.title = "Load_Advances (Normal)"; test.bench = test_load_advances; flags = FT_LOAD_DEFAULT; benchmark( face, &test, max_iter, max_time ); test.title = "Load_Advances (Fast)"; test.bench = test_load_advances; flags = FT_LOAD_TARGET_LIGHT; benchmark( face, &test, max_iter, max_time ); break; case FT_BENCH_RENDER: test.title = "Render"; test.bench = test_render; benchmark( face, &test, max_iter, max_time ); break; case FT_BENCH_GET_GLYPH: test.title = "Get_Glyph"; test.bench = test_get_glyph; benchmark( face, &test, max_iter, max_time ); break; case FT_BENCH_GET_CBOX: test.title = "Get_CBox"; test.bench = test_get_cbox; benchmark( face, &test, max_iter, max_time ); break; case FT_BENCH_CMAP: { bcharset_t charset; get_charset( face, &charset ); if ( charset.code ) { test.user_data = (void*)&charset; test.title = "Get_Char_Index"; test.bench = test_get_char_index; benchmark( face, &test, max_iter, max_time ); if ( compare_cached ) { test.cache_first = 1; test.title = "Get_Char_Index (cached)"; test.bench = test_cmap_cache; benchmark( face, &test, max_iter, max_time ); } free( charset.code ); } } break; case FT_BENCH_CMAP_ITER: test.title = "Iterate CMap"; test.bench = test_cmap_iter; benchmark( face, &test, max_iter, max_time ); break; case FT_BENCH_NEW_FACE: test.title = "New_Face"; test.bench = test_new_face; benchmark( face, &test, max_iter, max_time ); break; case FT_BENCH_EMBOLDEN: test.title = "Embolden"; test.bench = test_embolden; benchmark( face, &test, max_iter, max_time ); break; } } Exit: /* The following is a bit subtle: When we call FTC_Manager_Done, this * normally destroys all FT_Face objects that the cache might have created * by calling the face requester. * * However, this little benchmark uses a tricky face requester that * doesn't create a new FT_Face through FT_New_Face but simply pass a * pointer to the one that was previously created. * * If the cache manager has been used before, the call to FTC_Manager_Done * discards our single FT_Face. * * In the case where no cache manager is in place, or if no test was run, * the call to FT_Done_FreeType releases any remaining FT_Face object * anyway. */ if ( cache_man ) FTC_Manager_Done( cache_man ); FT_Done_FreeType( lib ); return 0; }
int main(int argc, char **argv) { SDL_Init(SDL_INIT_VIDEO); SDL_Surface *screen = SDL_SetVideoMode(640,480,32,SDL_SWSURFACE); int error = FT_Init_FreeType( &library ); if ( error ) { printf("Failed initialising freetype\n"); exit(0); } error = FT_New_Face( library, "Vera.ttf", 0, &face ); if ( error == FT_Err_Unknown_File_Format ) { printf("Failed loading arial.ttf :O unsupported :O\n"); exit(0); } else if ( error ) { printf("File not found probably\n"); exit(0); } error = FT_Set_Pixel_Sizes( face, 0, 32 ); if(error) { printf("Failed setting size\n"); exit(0); } int quit = 0; char cchar = 0; for(;!quit;) { SDL_Event ev; while(SDL_PollEvent(&ev)) { if(ev.type == SDL_QUIT) { quit = 1; } if(ev.type == SDL_KEYDOWN) { if(ev.key.keysym.sym == SDLK_LEFT) { if(cchar>0) cchar--; else cchar = 255; } if(ev.key.keysym.sym == SDLK_RIGHT) { if(cchar<255) cchar++; else cchar = 0; } } } renderGlyph(screen, cchar); char info[32] = { 0 }; sprintf(info, "Char Num: %d",cchar); renderString(screen, info, 50,100); SDL_Flip(screen); } SDL_Quit(); return 0; }
void FTEnvironment::loadFont() { checkError(FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, stk_config->m_font_default.c_str(), true)).c_str(), 0, &m_ft_face[F_DEFAULT]), "loading F_DEFAULT"); checkError(FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, stk_config->m_font_default_fallback.c_str(), true)).c_str(), 0, &m_ft_face[F_DEFAULT_FALLBACK]), "loading F_DEFAULT_FALLBACK"); checkError(FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, stk_config->m_font_cjk.c_str(), true)).c_str(), 0, &m_ft_face[F_CJK]), "loading F_CJK"); checkError(FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, stk_config->m_font_ar.c_str(), true)).c_str(), 0, &m_ft_face[F_AR]), "loading F_AR"); checkError(FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, stk_config->m_font_bold.c_str(), true)).c_str(), 0, &m_ft_face[F_BOLD]), "loading F_BOLD"); checkError(FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, stk_config->m_font_bold_fallback.c_str(), true)).c_str(), 0, &m_ft_face[F_BOLD_FALLBACK]), "loading F_BOLD_FALLBACK"); checkError(FT_New_Face(m_ft_lib, (file_manager->getAssetChecked (FileManager::TTF, stk_config->m_font_digit.c_str(),true)).c_str(), 0, &m_ft_face[F_DIGIT]), "loading F_DIGIT"); //Set charmap for (int h = 0; h < F_COUNT; ++h) { for (int i = 0; i < m_ft_face[h]->num_charmaps; ++i) { FT_UShort pid = m_ft_face[h]->charmaps[i]->platform_id; FT_UShort eid = m_ft_face[h]->charmaps[i]->encoding_id; if (((pid == 0) && (eid == 3)) || ((pid == 3) && (eid == 1))) checkError(FT_Set_Charmap(m_ft_face[h], m_ft_face[h]->charmaps[i]), "setting charmaps"); } } // Set face dpi // font size is resolution-dependent. // normal text will range from 0.8, in 640x* resolutions (won't scale // below that) to 1.0, in 1024x* resolutions, and linearly up // normal text will range from 0.2, in 640x* resolutions (won't scale // below that) to 0.4, in 1024x* resolutions, and linearly up const s32 screen_width = irr_driver->getFrameSize().Width; const s32 screen_height = irr_driver->getFrameSize().Height; float scale = std::max(0, screen_width - 640)/564.0f; // attempt to compensate for small screens if (screen_width < 1200) scale = std::max(0, screen_width - 640) / 750.0f; if (screen_width < 900 || screen_height < 700) scale = std::min(scale, 0.05f); const u32 normal_dpi = u32((0.7f + 0.2f*scale)*27); const u32 title_dpi = u32((0.2f + 0.2f*scale)*120); const u32 digit_dpi = u32((0.7f + 0.2f*scale)*40); Log::info("Freetype Environment", "DPI for Normal Font is %d.", normal_dpi); Log::info("Freetype Environment", "DPI for Title Font is %d.", title_dpi); Log::info("Freetype Environment", "DPI for Digit Font is %d.", digit_dpi); checkError(FT_Set_Pixel_Sizes(m_ft_face[F_DEFAULT], 0, normal_dpi), "setting F_DEFAULT size"); checkError(FT_Set_Pixel_Sizes(m_ft_face[F_DEFAULT_FALLBACK], 0, normal_dpi), "setting F_DEFAULT_FALLBACK size"); checkError(FT_Set_Pixel_Sizes(m_ft_face[F_CJK], 0, normal_dpi), "setting F_CJK size"); checkError(FT_Set_Pixel_Sizes(m_ft_face[F_AR], 0, normal_dpi), "setting F_AR size"); checkError(FT_Set_Pixel_Sizes(m_ft_face[F_BOLD], 0, title_dpi), "setting F_BOLD size"); checkError(FT_Set_Pixel_Sizes(m_ft_face[F_BOLD_FALLBACK], 0, title_dpi), "setting F_BOLD_FALLBACK size"); checkError(FT_Set_Pixel_Sizes(m_ft_face[F_DIGIT], 0, digit_dpi), "setting F_DIGIT size"); }
//------------------------------------------------------------------------------ void createAtlasData(const std::string &font, unsigned int fontSize) { // INIT FREETYPE AND CREATE FONT ATLAS ERROR_ASSERT(APP::IsInitialized()) // load the library FT_Error err = FT_Init_FreeType(&library_); ERROR_ASSERT(err == 0) FT_Face face; // load the font (face) err = FT_New_Face( library_, font.c_str(), 0, &face ); ERROR_ASSERT(err == 0) FT_Set_Pixel_Sizes(face, 0, fontSize); FT_GlyphSlot g = face->glyph; int atlasWidth = 0; int atlasHeight = 0; int blMin = 0; int blMax = -1; // compute the dimensions of the atlas for (unsigned int i = 32; i < 128; i++) { if(FT_Load_Char(face, i, FT_LOAD_RENDER)) { ERROR_WARNING("Loading character %c failed!") continue; } atlasWidth += (g->bitmap.width + std::abs(g->bitmap_left)); blMin = std::min(blMin, g->bitmap_top); if (g->bitmap_top > 0) { blMin = std::min(blMin, g->bitmap_top - g->bitmap.rows); } blMax = std::max(blMax, g->bitmap_top); atlasHeight = std::max(atlasHeight, g->bitmap.rows); } atlasHeight = blMax - blMin; int baseline = 0 - blMin; unsigned char* atlas = new unsigned char[atlasWidth*atlasHeight]; int marker = 0; for (unsigned int i = 32; i < 128; i++) { if(FT_Load_Char(face, i, FT_LOAD_RENDER)) { continue; } int w = g->bitmap.width; int h = g->bitmap.rows; unsigned char* buffer = g->bitmap.buffer; // copy character to atlas for (unsigned int v = 0; v < h; v++) { for (unsigned int u = 0; u < w; u++) { int y = atlasHeight - (baseline + g->bitmap_top) + v; int x = marker + u + std::abs(g->bitmap_left); atlas[y*atlasWidth + x] = buffer[v*w + u]; } } // save character info characterInfo_[i].Width = w + std::abs(g->bitmap_left); characterInfo_[i].XOff0 = marker; // increase marker marker += (w + std::abs(g->bitmap_left)); } // initialize the atlas atlas_.Atlas = new GL::Texture2DA( atlasWidth, atlasHeight, static_cast<void*>(atlas) ); atlas_.Width = atlasWidth; atlas_.Height = atlasHeight; vertexArray_ = new GL::VertexArray(); // clean up delete[] atlas; FT_Done_FreeType(library_); }