int GraphicsElementManager::LoadTextureFont( const string& font_texture_filename, int width, int height, float bold, float italic, float shadow ) { LOG_PRINT( " - Loading a texture for font: " + font_texture_filename ); TextureFont* pFont = new TextureFont(); bool res = pFont->InitFont( font_texture_filename, (int)(width * m_fScale), (int)(height * m_fScale) ); if( res ) { // save the original size of the font // used when the scaling is requested m_vecOrigFontSize.push_back( Vector2( (float)width, (float)height ) ); // pFont->SetBold( bold ); pFont->SetItalic( italic ); m_vecpFont.push_back( pFont ); return (int)m_vecpFont.size() - 1; } else { LOG_PRINT_ERROR( " - Cannot load a texture for font: " + font_texture_filename ); SafeDelete( pFont ); return -1; } }
void TextLayout::_RenderRaw(float maxWidth) const { TextureFont *font = Gui::Screen::GetFont(); float py = 0; init_clip_test(); glPushMatrix(); const float spaceWidth = font->GetGlyph(' ').advx; std::list<word_t>::const_iterator wpos = this->words.begin(); // build lines of text while (wpos != this->words.end()) { float len = 0; int num = 0; std::list<word_t>::const_iterator i = wpos; len += (*i).advx; num++; bool overflow = false; bool explicit_newline = false; if ((*i).word != 0) { ++i; for (; i != this->words.end(); ++i) { if ((*i).word == 0) { // newline explicit_newline = true; num++; break; } if (len + spaceWidth + (*i).advx > maxWidth) { overflow = true; break; } len += (*i).advx + spaceWidth; num++; } } float _spaceWidth; if ((m_justify) && (num>1) && overflow) { float spaceleft = maxWidth - len; _spaceWidth = spaceWidth + (spaceleft/float(num-1)); } else { _spaceWidth = spaceWidth; } if (line_clip_test(py, py+font->GetHeight()*2.0)) { float px = 0; for (int j=0; j<num; j++) { if ((*wpos).word) font->RenderMarkup((*wpos).word, round(px), round(py)); px += (*wpos).advx + _spaceWidth; wpos++; } } else { for (int j=0; j<num; j++) wpos++; } py += font->GetHeight() * (explicit_newline ? PARAGRAPH_SPACING : LINE_SPACING); } glPopMatrix(); }
void TextLayout::_MeasureSizeRaw(const float layoutWidth, float outSize[2]) const { TextureFont *font = Gui::Screen::GetFont(); outSize[0] = 0; outSize[1] = 0; const float spaceWidth = font->GetGlyph(' ').advx; // build lines of text for (std::list<word_t>::const_iterator wpos = words.begin(); wpos != words.end(); ) { float len = 0; int num = 0; bool explicit_newline = false; std::list<word_t>::const_iterator i = wpos; len += (*i).advx; num++; bool overflow = false; if ((*i).word != 0) { ++i; for (; i != words.end(); ++i) { if ((*i).word == 0) { // newline explicit_newline = true; num++; break; } if (len + spaceWidth + (*i).advx > layoutWidth) { overflow = true; break; } len += (*i).advx + spaceWidth; num++; } } float _spaceWidth; if ((m_justify) && (num>1) && overflow) { float spaceleft = layoutWidth - len; _spaceWidth = spaceWidth + (spaceleft/float(num-1)); } else { _spaceWidth = spaceWidth; } float lineLen = 0; for (int j=0; j<num; j++) { word_t word = (*wpos); lineLen += word.advx; if (j < num-1) lineLen += _spaceWidth; wpos++; } if (lineLen > outSize[0]) outSize[0] = lineLen; outSize[1] += font->GetHeight() * (explicit_newline ? PARAGRAPH_SPACING : LINE_SPACING); } if (outSize[1]) outSize[1] += font->GetDescender(); }
/** Load a texture font from a chunk of data containing font data * in the TXF format used by GLUT. */ TextureFont* TextureFont::LoadTxf(const DataChunk* data) { TextureFont* font = new TextureFont(); if (font->loadTxf(data)) { // TODO: Should skip the addRef font->addRef(); } else { delete font; font = NULL; } return font; }
float FontManager::getStrLength(int faceID, float size, std::string text) { if (text.size() == 0) return 0; if ((faceID < 0) || (faceID > getNumFaces())) { DEBUG2("Trying to find length of string for invalid Font Face ID %d\n", faceID); return 0; } TextureFont* pFont = getClosestSize(faceID, size); if (!pFont) return 0; float scale = size / (float)pFont->getSize(); return pFont->getStrLength(scale, stripAnsiCodes(text).c_str()); }
void FontManager::loadAll(std::string directory) { if (directory.size() == 0) return; OSFile file; OSDir dir(directory.c_str()); while (dir.getNextFile(file, true)) { const char *ext = file.getExtension(); if (ext) { if (strcasecmp(ext, "fmt") == 0) { TextureFont *pFont = new TextureFont; if (pFont) { if (pFont->load(file)) { std::string str = pFont->getFaceName(); GetTypeFaceName((char*)str.c_str()); FontFaceMap::iterator faceItr = faceNames.find(str); int faceID = 0; if (faceItr == faceNames.end()) { // its new FontSizeMap faceList; fontFaces.push_back(faceList); faceID = (int)fontFaces.size() - 1; faceNames[str] = faceID; } else { faceID = faceItr->second; } fontFaces[faceID][pFont->getSize()] = pFont; } else { DEBUG4("Font Texture load failed: %s\n", file.getOSName()); delete(pFont); } } } } } }
void FontManager::drawString(float x, float y, float z, int faceID, float size, std::string text) { if (text.size() == 0) return; if ((faceID < 0) || (faceID > getNumFaces())) { DEBUG2("Trying to draw with invalid Font Face ID %d\n", faceID); return; } TextureFont* pFont = getClosestSize(faceID, size); if (!pFont) return; float scale = size / (float)pFont->getSize(); /* * Colorize text based on ANSI codes embedded in it * Break the text every time an ANSI code * is encountered and do a separate pFont->drawString code for * each segment, with the appropriate color parameter */ // sane defaults bool bright = true; bool blink = false; //FIXME: blinking bool underline = false; //FIXME: underline // negatives are invalid, we use them to signal "no change" GLfloat color[3] = {-1.0f, -1.0f, -1.0f}; // set underline color const char* uColor = BZDB.get("underlineColor").c_str(); GLfloat underlineColor[3] = {-1.0f, -1.0f, -1.0f}; if (strcasecmp(uColor, "text") == 0) { // use the initialized color, no change } else if (strcasecmp(uColor, "cyan") == 0) { underlineColor[0] = BrightColors[CyanColor][0]; underlineColor[1] = BrightColors[CyanColor][1]; underlineColor[2] = BrightColors[CyanColor][2]; } else if (strcasecmp(uColor, "grey") == 0) { underlineColor[0] = BrightColors[GreyColor][0]; underlineColor[1] = BrightColors[GreyColor][1]; underlineColor[2] = BrightColors[GreyColor][2]; } /* * ANSI code interpretation is somewhat limited, we only accept values * which have been defined in AnsiCodes.h */ bool doneLastSection = false; int startSend = 0; int endSend = (int)text.find("\033[", startSend); std::string tmpText; bool tookCareOfANSICode = false; float width = 0; // run at least once if (endSend == -1) { endSend = (int)text.size(); doneLastSection = true; } // split string into parts based on the embedded ANSI codes, render each separately // there has got to be a faster way to do this while (endSend >= 0) { // blink the text, if desired if (blink) getBlinkColor(color, color); // render text if (endSend - startSend > 0) { tmpText = text.substr(startSend, (endSend - startSend)); // get substr width, we may need it a couple times width = getStrLength(faceID, size, tmpText); glPushMatrix(); glTranslatef(x, y, z); GLboolean depthMask; glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask); glDepthMask(0); pFont->drawString(scale, color, tmpText.c_str()); if (underline) { OpenGLGState::resetState(); // FIXME - full reset required? if (underlineColor[0] >= 0) glColor3fv(underlineColor); // still have a translated matrix, these coordinates are // with respect to the string just drawn glBegin(GL_LINES); glVertex2f(0, -1.0f); glVertex2f(width, -1.0f); glEnd(); } glDepthMask(depthMask); glPopMatrix(); // x transform for next substr x += width; } if (!doneLastSection) { startSend = (int)text.find("m", endSend) + 1; } // we stopped sending text at an ANSI code, find out what it is and do something about it if (endSend != (int)text.size()) { tookCareOfANSICode = false; tmpText = text.substr(endSend, (text.find("m", endSend) - endSend) + 1); // colors for (int i = 0; i < 8; i++) { if (tmpText == ColorStrings[i]) { if (bright) { color[0] = BrightColors[i][0]; color[1] = BrightColors[i][1]; color[2] = BrightColors[i][2]; } else { color[0] = DimColors[i][0]; color[1] = DimColors[i][1]; color[2] = DimColors[i][2]; } tookCareOfANSICode = true; break; } } // didn't find a matching color if (!tookCareOfANSICode) { // settings other than color if (tmpText == ANSI_STR_RESET) { bright = true; blink = false; underline = false; color[0] = BrightColors[WhiteColor][0]; color[1] = BrightColors[WhiteColor][1]; color[2] = BrightColors[WhiteColor][2]; } else if (tmpText == ANSI_STR_RESET_FINAL) { bright = false; blink = false; underline = false; color[0] = DimColors[WhiteColor][0]; color[1] = DimColors[WhiteColor][1]; color[2] = DimColors[WhiteColor][2]; } else if (tmpText == ANSI_STR_BRIGHT) { bright = true; } else if (tmpText == ANSI_STR_DIM) { bright = false; } else if (tmpText == ANSI_STR_UNDERLINE) { underline = !underline; } else if (tmpText == ANSI_STR_BLINK) { blink = !blink; } else { DEBUG2("ANSI Code %s not supported\n", tmpText.c_str()); } } } endSend = (int)text.find("\033[", startSend); if ((endSend == -1) && !doneLastSection) { endSend = (int)text.size(); doneLastSection = true; } } }
TextureFont* TextureFont::load(istream& in) { char header[4]; in.read(header, sizeof header); if (!in.good() || strncmp(header, "\377txf", 4) != 0) { DPRINTF(0, "Stream is not a texture font!.\n"); return NULL; } uint32 endiannessTest = 0; in.read(reinterpret_cast<char*>(&endiannessTest), sizeof endiannessTest); if (!in.good()) { DPRINTF(0, "Error reading endianness bytes in txf header.\n"); return NULL; } bool byteSwap; if (endiannessTest == 0x78563412) byteSwap = true; else if (endiannessTest == 0x12345678) byteSwap = false; else { DPRINTF(0, "Stream is not a texture font!.\n"); return NULL; } int format = readUint32(in, byteSwap); unsigned int texWidth = readUint32(in, byteSwap); unsigned int texHeight = readUint32(in, byteSwap); unsigned int maxAscent = readUint32(in, byteSwap); unsigned int maxDescent = readUint32(in, byteSwap); unsigned int nGlyphs = readUint32(in, byteSwap); if (!in) { DPRINTF(0, "Texture font stream is incomplete"); return NULL; } DPRINTF(1, "Font contains %d glyphs.\n", nGlyphs); TextureFont* font = new TextureFont(); assert(font != NULL); font->setMaxAscent(maxAscent); font->setMaxDescent(maxDescent); float dx = 0.5f / texWidth; float dy = 0.5f / texHeight; for (unsigned int i = 0; i < nGlyphs; i++) { uint16 __id = readUint16(in, byteSwap); TextureFont::Glyph glyph(__id); glyph.width = readUint8(in); glyph.height = readUint8(in); glyph.xoff = readInt8(in); glyph.yoff = readInt8(in); glyph.advance = readInt8(in); readInt8(in); glyph.x = readInt16(in, byteSwap); glyph.y = readInt16(in, byteSwap); if (!in) { DPRINTF(0, "Error reading glyph %ud from texture font stream.\n", i); delete font; return NULL; } float fWidth = texWidth; float fHeight = texHeight; glyph.texCoords[0].u = glyph.x / fWidth + dx; glyph.texCoords[0].v = glyph.y / fHeight + dy; glyph.texCoords[1].u = (glyph.x + glyph.width) / fWidth + dx; glyph.texCoords[1].v = glyph.y / fHeight + dy; glyph.texCoords[2].u = (glyph.x + glyph.width) / fWidth + dx; glyph.texCoords[2].v = (glyph.y + glyph.height) / fHeight + dy; glyph.texCoords[3].u = glyph.x / fWidth + dx; glyph.texCoords[3].v = (glyph.y + glyph.height) / fHeight + dy; font->addGlyph(glyph); } font->texWidth = texWidth; font->texHeight = texHeight; if (format == TxfByte) { unsigned char* fontImage = new unsigned char[texWidth * texHeight]; if (fontImage == NULL) { DPRINTF(0, "Not enough memory for font bitmap.\n"); delete font; return NULL; } DPRINTF(1, "Reading %d x %d 8-bit font image.\n", texWidth, texHeight); in.read(reinterpret_cast<char*>(fontImage), texWidth * texHeight); if (in.gcount() != (signed)(texWidth * texHeight)) { DPRINTF(0, "Missing bitmap data in font stream.\n"); delete font; delete[] fontImage; return NULL; } font->fontImage = fontImage; } else { int rowBytes = (texWidth + 7) >> 3; unsigned char* fontBits = new unsigned char[rowBytes * texHeight]; unsigned char* fontImage = new unsigned char[texWidth * texHeight]; if (fontImage == NULL || fontBits == NULL) { DPRINTF(0, "Not enough memory for font bitmap.\n"); delete font; if (fontBits != NULL) delete[] fontBits; if (fontImage != NULL) delete[] fontImage; return NULL; } DPRINTF(1, "Reading %d x %d 1-bit font image.\n", texWidth, texHeight); in.read(reinterpret_cast<char*>(fontBits), rowBytes * texHeight); if (in.gcount() != (signed)(rowBytes * texHeight)) { DPRINTF(0, "Missing bitmap data in font stream.\n"); delete font; return NULL; } for (unsigned int y = 0; y < texHeight; y++) { for (unsigned int x = 0; x < texWidth; x++) { if ((fontBits[y * rowBytes + (x >> 3)] & (1 << (x & 0x7))) != 0) fontImage[y * texWidth + x] = 0xff; else fontImage[y * texWidth + x] = 0x0; } } font->fontImage = fontImage; delete[] fontBits; }
int main() { auto error_callback = [](int error, const char* description) { slog.e(TAG, "Error (%d): %s\n", error, description); }; glfwSetErrorCallback(error_callback); if(glfwInit() != GLFW_TRUE) { slog.e(TAG, "Failed to initialize GLFW library" ); return -1; } glfwWindowHint(GLFW_SAMPLES, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Open a window and create its OpenGL context GLFWwindow* window = glfwCreateWindow(window_size.width, window_size.height, TAG, NULL, NULL); if(!window) { slog.e(TAG, "Failed to open GLFW window. Not OpenGL 3.3 compatible"); glfwTerminate(); return -2; } glfwMakeContextCurrent(window); // Initialize GLEW glewExperimental = true; // Needed for core profile if(glewInit() != GLEW_OK) { slog.e(TAG, "Failed to initialize GLEW library"); glfwTerminate(); return -3; } // Ensure we can capture the escape key being pressed below glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); // Hide the mouse and enable unlimited mouvement glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); glfwPollEvents(); // Set the mouse at the center of the screen glfwSetCursorPos(window, window_size.width/2, window_size.height/2); // Enable depth test glEnable(GL_DEPTH_TEST); // Accept fragment if it closer to the camera than the former one glDepthFunc(GL_LESS); // Cull triangles which normal is not towards the camera glEnable(GL_CULL_FACE); // double lastTime = glfwGetTime(); std::string dir = File::dirname(__FILE__); bool flag = Program::loadFromFile(program, dir + "/font.vert", dir + "/font.frag"); assert(flag); program.use(); font.load(File::getResourcePath() + "/font/FreeSans.ttf", 48); while(true) { // Dark blue background glClearColor(0.0f, 0.0f, 0.24f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); // Check if the ESC key was pressed or the window was closed if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS || glfwWindowShouldClose(window) != 0) break; } // Close OpenGL window and terminate GLFW glfwTerminate(); return 0; }