/*!
\brief Scan a folder for all valid fonts
\param fontspath Path of the folder to scan.
\return
- \c B_OK Success
- \c B_NAME_TOO_LONG The path specified is too long
- \c B_ENTRY_NOT_FOUND The path does not exist
- \c B_LINK_LIMIT A cyclic loop was detected in the file system
- \c B_BAD_VALUE Invalid input specified
- \c B_NO_MEMORY Insufficient memory to open the folder for reading
- \c B_BUSY A busy node could not be accessed
- \c B_FILE_ERROR An invalid file prevented the operation.
- \c B_NO_MORE_FDS All file descriptors are in use (too many open files).
*/
status_t FontServer::ScanDirectory(const char *fontspath)
{
// This bad boy does all the real work. It loads each entry in the
// directory. If a valid font file, it adds both the family and the style.
// Both family and style are stored internally as BStrings. Once everything
int32 validcount=0;
FT_Face face;
FT_Error error;
FT_CharMap charmap;
FontFamily *family;
DIR* hDir;
dirent* psEntry;
printf( "FontServer::ScanDirectory(): opening %s\n", fontspath );
if ( (hDir = opendir( fontspath ) ) )
{
while( (psEntry = readdir( hDir )) )
{
printf( "FontServer::ScanDirectory(): found entry %s\n", psEntry->d_name );
char zFullPath[ PATH_MAX ];
if ( strcmp( psEntry->d_name, "." ) == 0 || strcmp( psEntry->d_name, ".." ) == 0 )
{
continue;
}
strcpy( zFullPath, fontspath );
pathcat( zFullPath, psEntry->d_name );
error=FT_New_Face(ftlib, zFullPath,0,&face);
if (error!=0)
continue;
charmap=_GetSupportedCharmap(face);
if(!charmap)
{
FT_Done_Face(face);
continue;
}
face->charmap=charmap;
family=_FindFamily(face->family_name );
if (!family)
{
#ifdef PRINT_FONT_LIST
printf("Font Family: %s\n",face->family_name);
#endif
family=new FontFamily(face->family_name);
families->AddItem(family);
}
if(family->HasStyle(face->style_name))
{
FT_Done_Face(face);
continue;
}
#ifdef PRINT_FONT_LIST
printf("\tFont Style: %s\n",face->style_name);
#endif
// Has vertical metrics?
family->AddStyle(zFullPath,face);
validcount++;
FT_Done_Face(face);
}
printf( "Directory '%s' scanned, %ld fonts found\n", fontspath, validcount );
closedir( hDir );
}
need_update=true;
return B_OK;
}
/*-----------------------------------------------------------------------------------------------
Description:
Governs window creation, the initial OpenGL configuration (face culling, depth mask, even
though this is a 2D demo and that stuff won't be of concern), the creation of geometry, and
the creation of a texture.
Parameters:
argc (From main(...)) The number of char * items in argv. For glut's initialization.
argv (From main(...)) A collection of argument strings. For glut's initialization.
Returns:
False if something went wrong during initialization, otherwise true;
Exception: Safe
Creator:
John Cox (3-7-2016)
-----------------------------------------------------------------------------------------------*/
bool init(int argc, char *argv[])
{
glutInit(&argc, argv);
// I don't know what this is doing, but it has been working, so I'll leave it be for now
int windowWidth = 500; // square 500x500 pixels
int windowHeight = 500;
unsigned int displayMode = GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH | GLUT_STENCIL;
displayMode = defaults(displayMode, windowWidth, windowHeight);
glutInitDisplayMode(displayMode);
// create the window
// ??does this have to be done AFTER glutInitDisplayMode(...)??
glutInitWindowSize(windowWidth, windowHeight);
glutInitWindowPosition(300, 200); // X = 0 is screen left, Y = 0 is screen top
int window = glutCreateWindow(argv[0]);
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
// OpenGL 4.3 was where internal debugging was enabled, freeing the user from having to call
// glGetError() and analyzing it after every single OpenGL call, complete with surrounding it
// with #ifdef DEBUG ... #endif blocks
// Note: https://blog.nobel-joergensen.com/2013/02/17/debugging-opengl-part-2-using-gldebugmessagecallback/
int glMajorVersion = 4;
int glMinorVersion = 4;
glutInitContextVersion(glMajorVersion, glMinorVersion);
glutInitContextProfile(GLUT_CORE_PROFILE);
#ifdef DEBUG
glutInitContextFlags(GLUT_DEBUG); // if enabled,
#endif
// glload must load AFTER glut loads the context
glload::LoadTest glLoadGood = glload::LoadFunctions();
if (!glLoadGood) // apparently it has an overload for "bool type"
{
printf("glload::LoadFunctions() failed\n");
return false;
}
else if (!glload::IsVersionGEQ(glMajorVersion, glMinorVersion))
{
// the "is version" check is an "is at least version" check
printf("Your OpenGL version is %i, %i. You must have at least OpenGL %i.%i to run this tutorial.\n",
glload::GetMajorVersion(), glload::GetMinorVersion(), glMajorVersion, glMinorVersion);
glutDestroyWindow(window);
return 0;
}
else if (glext_ARB_debug_output)
{
// condition will be true if GLUT_DEBUG is a context flag
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
glDebugMessageCallbackARB(DebugFunc, (void*)15);
}
// these OpenGL initializations are for 3D stuff, where depth matters and multiple shapes can
// be "on top" of each other relative to the most distant thing rendered, and this barebones
// code is only for 2D stuff, but initialize them anyway as good practice (??bad idea? only
// use these once 3D becomes a thing??)
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LEQUAL);
glDepthRange(0.0f, 1.0f);
// FreeType needs to load itself into particular variables
// Note: FT_Init_FreeType(...) returns something called an FT_Error, which VS can't find.
// Based on the useage, it is assumed that 0 is returned if something went wrong, otherwise
// non-zero is returned. That is the only explanation for this kind of condition.
if (FT_Init_FreeType(&gFtLib))
{
fprintf(stderr, "Could not init freetype library\n");
return false;
}
// Note: FT_New_Face(...) also returns an FT_Error.
const char *fontFilePath = "FreeSans.ttf"; // file path relative to solution directory
if (FT_New_Face(gFtLib, fontFilePath, 0, &gFtFace))
{
fprintf(stderr, "Could not open font '%s'\n", fontFilePath);
return false;
}
gProgramId = CreateProgram();
// pick out the attributes and uniforms used in the FreeType GPU program
//.........这里部分代码省略.........
static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
{
FT_Error err = FT_Err_Cannot_Open_Resource;
HKEY hKey;
LONG ret;
TCHAR vbuffer[MAX_PATH], dbuffer[256];
TCHAR *font_namep;
char *font_path;
uint index;
/* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
* "Windows NT" key, on Windows 9x in the Windows key. To save us having
* to retrieve the windows version, we'll just query both */
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
if (ret != ERROR_SUCCESS) {
DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
return err;
}
/* For Unicode we need some conversion between widechar and
* normal char to match the data returned by RegEnumValue,
* otherwise just use parameter */
#if defined(UNICODE)
font_namep = MallocT<TCHAR>(MAX_PATH);
MB_TO_WIDE_BUFFER(font_name, font_namep, MAX_PATH * sizeof(TCHAR));
#else
font_namep = const_cast<char *>(font_name); // only cast because in unicode pointer is not const
#endif
for (index = 0;; index++) {
TCHAR *s;
DWORD vbuflen = lengthof(vbuffer);
DWORD dbuflen = lengthof(dbuffer);
ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
if (ret != ERROR_SUCCESS) goto registry_no_font_found;
/* The font names in the registry are of the following 3 forms:
* - ADMUI3.fon
* - Book Antiqua Bold (TrueType)
* - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
* We will strip the font-type '()' if any and work with the font name
* itself, which must match exactly; if...
* TTC files, font files which contain more than one font are seperated
* byt '&'. Our best bet will be to do substr match for the fontname
* and then let FreeType figure out which index to load */
s = _tcschr(vbuffer, _T('('));
if (s != NULL) s[-1] = '\0';
if (_tcschr(vbuffer, _T('&')) == NULL) {
if (_tcsicmp(vbuffer, font_namep) == 0) break;
} else {
if (_tcsstr(vbuffer, font_namep) != NULL) break;
}
}
if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
goto folder_error;
}
/* Some fonts are contained in .ttc files, TrueType Collection fonts. These
* contain multiple fonts inside this single file. GetFontData however
* returns the whole file, so we need to check each font inside to get the
* proper font.
* Also note that FreeType does not support UNICODE filesnames! */
#if defined(UNICODE)
/* We need a cast here back from wide because FreeType doesn't support
* widechar filenames. Just use the buffer we allocated before for the
* font_name search */
font_path = (char*)font_namep;
WIDE_TO_MB_BUFFER(vbuffer, font_path, MAX_PATH * sizeof(TCHAR));
#else
font_path = vbuffer;
#endif
ttd_strlcat(font_path, "\\", MAX_PATH * sizeof(TCHAR));
ttd_strlcat(font_path, WIDE_TO_MB(dbuffer), MAX_PATH * sizeof(TCHAR));
/* Convert the path into something that FreeType understands */
font_path = GetShortPath(font_path);
index = 0;
do {
err = FT_New_Face(_library, font_path, index, face);
if (err != FT_Err_Ok) break;
if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
/* Try english name if font name failed */
if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
err = FT_Err_Cannot_Open_Resource;
} while ((FT_Long)++index != (*face)->num_faces);
folder_error:
registry_no_font_found:
#if defined(UNICODE)
//.........这里部分代码省略.........
//------------------------------------------------------------------
void urFont::loadFont(string filename, int fontsize, bool _bAntiAliased, bool _bFullCharacterSet, bool makeContours){
bMakeContours = makeContours;
//------------------------------------------------
if (bLoadedOk == true){
// we've already been loaded, try to clean up :
if (cps != NULL){
delete[] cps;
}
if (texNames != NULL){
for (int i = 0; i < nCharacters; i++){
glDeleteTextures(1, &texNames[i]);
}
delete[] texNames;
}
bLoadedOk = false;
}
//------------------------------------------------
filename = ofToDataPath(filename);
bLoadedOk = false;
bAntiAlised = _bAntiAliased;
bFullCharacterSet = _bFullCharacterSet;
fontSize = fontsize;
//--------------- load the library and typeface
FT_Library library;
if (FT_Init_FreeType( &library )){
// ofLog(OF_LOG_ERROR," PROBLEM WITH FT lib");
return;
}
FT_Face face;
if (FT_New_Face( library, filename.c_str(), 0, &face )) {
return;
}
FT_Set_Char_Size( face, fontsize << 6, fontsize << 6, 96, 96);
lineHeight = fontsize * 1.43f;
//------------------------------------------------------
//kerning would be great to support:
//ofLog(OF_LOG_NOTICE,"FT_HAS_KERNING ? %i", FT_HAS_KERNING(face));
//------------------------------------------------------
nCharacters = bFullCharacterSet ? 256 : 128 - NUM_CHARACTER_TO_START;
//--------------- initialize character info and textures
cps = new charProps[nCharacters];
texNames = new GLuint[nCharacters];
glGenTextures(nCharacters, texNames);
if(bMakeContours){
charOutlines.clear();
charOutlines.assign(nCharacters, ofTTFCharacter());
}
//--------------------- load each char -----------------------
for (int i = 0 ; i < nCharacters; i++){
//------------------------------------------ anti aliased or not:
if(FT_Load_Glyph( face, FT_Get_Char_Index( face, (unsigned char)(i+NUM_CHARACTER_TO_START) ), FT_LOAD_DEFAULT )){
// ofLog(OF_LOG_ERROR,"error with FT_Load_Glyph %i", i);
}
if (bAntiAlised == true) FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
else FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
//------------------------------------------
FT_Bitmap& bitmap= face->glyph->bitmap;
// 3 pixel border around the glyph
// We show 2 pixels of this, so that blending looks good.
// 1 pixels is hidden because we don't want to see the real edge of the texture
border = 3;
visibleBorder = 2;
if(bMakeContours){
if( printVectorInfo )printf("\n\ncharacter %c: \n", char( i+NUM_CHARACTER_TO_START ) );
//int character = i + NUM_CHARACTER_TO_START;
charOutlines[i] = makeContoursForCharacter( face );
}
// prepare the texture:
int width = ofNextPow2( bitmap.width + border*2 );
int height = ofNextPow2( bitmap.rows + border*2 );
// ------------------------- this is fixing a bug with small type
// ------------------------- appearantly, opengl has trouble with
// ------------------------- width or height textures of 1, so we
//.........这里部分代码省略.........
//.........这里部分代码省略.........
NULL);
}
// Set default settings for missing pattern properties:
FcDefaultSubstitute(target);
if (!FcConfigSubstitute(NULL, target, FcMatchPattern)) {
// Failed!
if (_verbosity > 1) fprintf(stderr, "libptbdrawtext_ftgl: FontConfig failed to substitute default properties for family %s, size %f pts and style flags %i.\n", _fontName, (float) _fontSize, _fontStyle);
FcPatternDestroy(target);
return(1);
}
// Have a matching pattern:
if (_verbosity > 3) {
fprintf(stderr, "libptbdrawtext_ftgl: Trying to find font that closely matches following specification:\n");
FcPatternPrint(target);
}
// Perform font matching for the font in the default configuration (0) that best matches the
// specified target pattern:
FcPattern* matched = FcFontMatch(NULL, target, &result);
if ((matched == NULL) || (result == FcResultNoMatch)) {
// Failed!
if (_verbosity > 1) fprintf(stderr, "libptbdrawtext_ftgl: FontConfig failed to find a matching font for family %s, size %f pts and style flags %i.\n", _fontName, (float) _fontSize, _fontStyle);
FcPatternDestroy(target);
return(1);
}
// Success: Extract relevant information for Freetype-2, the font filename and faceIndex:
if (_verbosity > 3) {
fprintf(stderr, "libptbdrawtext_ftgl: Best matching font which will be selected for drawing has following specs:\n");
FcPatternPrint(matched);
}
// Retrieve font filename for matched font:
FcChar8* localfontFileName = NULL;
if (FcPatternGetString(matched, FC_FILE, 0, (FcChar8**) &localfontFileName) != FcResultMatch) {
// Failed!
if (_verbosity > 1) fprintf(stderr, "libptbdrawtext_ftgl: FontConfig did not find filename for font with family %s, size %f pts and style flags %i.\n", _fontName, (float) _fontSize, _fontStyle);
FcPatternDestroy(target);
FcPatternDestroy(matched);
return(1);
}
strcpy(_fontFileName, (char*) localfontFileName);
// Retrieve faceIndex within fontfile:
if (FcPatternGetInteger(matched, FC_INDEX, 0, &_faceIndex) != FcResultMatch) {
// Failed!
if (_verbosity > 1) fprintf(stderr, "libptbdrawtext_ftgl: FontConfig did not find faceIndex for font file %s, family %s, size %f pts and style flags %i.\n", _fontFileName, _fontName, (float) _fontSize, _fontStyle);
FcPatternDestroy(target);
FcPatternDestroy(matched);
return(1);
}
// Release target pattern and matched pattern objects:
FcPatternDestroy(target);
FcPatternDestroy(matched);
}
else {
// Use "raw" values as passed by calling client code:
strcpy(_fontFileName, _fontName);
_faceIndex = (int) _fontStyle;
}
// Load & Create new font and face object, based on current spec settings:
// We directly use the Freetype library, so we can spec the faceIndex for selection of textstyle, which wouldn't be
// possible with the higher-level OGLFT constructor...
FT_Error error = FT_New_Face( OGLFT::Library::instance(), _fontFileName, _faceIndex, &ft_face );
if (error) {
if (_verbosity > 1) fprintf(stderr, "libptbdrawtext_ftgl: Freetype did not load face with index %i from font file %s.\n", _faceIndex, _fontFileName);
return(1);
}
else {
if (_verbosity > 3) fprintf(stderr, "libptbdrawtext_ftgl: Freetype loaded face %p with index %i from font file %s.\n", ft_face, _faceIndex, _fontFileName);
}
// Create FTGL face from Freetype face with given size and a 72 DPI resolution, aka _fontSize == pixelsize:
if (_antiAliasing != 0) {
faceT = new OGLFT::TranslucentTexture(ft_face, _fontSize, 72);
// Test the created face to make sure it will work correctly:
if (!faceT->isValid()) {
if (_verbosity > 1) fprintf(stderr, "libptbdrawtext_ftgl: Freetype did not recognize %s as a font file.\n", _fontName);
return(1);
}
}
else {
faceM = new OGLFT::MonochromeTexture(ft_face, _fontSize, 72);
// Test the created face to make sure it will work correctly:
if (!faceM->isValid()) {
if (_verbosity > 1) fprintf(stderr, "libptbdrawtext_ftgl: Freetype did not recognize %s as a font file.\n", _fontName);
return(1);
}
}
// Ready!
_needsRebuild = false;
return(0);
}
请发表评论