void MFixedRenderer::drawText(MOText * textObj) { M_PROFILE_SCOPE(MFixedRenderer::drawText); MFont * font = textObj->getFont(); const char * text = textObj->getText(); vector <float> * linesOffset = textObj->getLinesOffset(); if(! (strlen(text) > 0 && font)) return; if(linesOffset->size() == 0) return; MRenderingContext * render = MEngine().getInstance()->getRenderingContext(); render->enableBlending(); render->enableTexture(); render->disableLighting(); render->setColor4(*textObj->getColor()); render->setBlendingMode(M_BLENDING_ALPHA); static MVector2 vertices[4]; static MVector2 texCoords[4]; render->disableNormalArray(); render->disableColorArray(); render->enableVertexArray(); render->enableTexCoordArray(); render->setVertexPointer(M_FLOAT, 2, vertices); render->setTexCoordPointer(M_FLOAT, 2, texCoords); render->bindTexture(font->getTextureId()); unsigned int lineId = 0; float lineOffset = (*linesOffset)[0]; float size = textObj->getSize(); float tabSize = size*2; float fontSize = (float)font->getFontSize(); float widthFactor = font->getTextureWith() / fontSize; float heightFactor = font->getTextureHeight() / fontSize; float xc = 0, yc = 0; unsigned int i; unsigned int textLen = strlen(text); for(i=0; i<textLen; i++) { if(text[i] == '\n') // return { if(((i+1) < textLen)) { lineId++; lineOffset = (*linesOffset)[lineId]; yc += size; xc = 0; } continue; } if(text[i] == '\t') // tab { int tab = (int)(xc / tabSize) + 1; xc = tab*tabSize; continue; } // get character unsigned int charCode = (unsigned int)((unsigned char)text[i]); MCharacter * character = font->getCharacter(charCode); if(! character) continue; MVector2 pos = character->getPos(); MVector2 scale = character->getScale(); MVector2 offset = character->getOffset() * size + MVector2(lineOffset, 0); float width = scale.x * widthFactor * size; float height = scale.y * heightFactor * size; // construct quad texCoords[0] = MVector2(pos.x, (pos.y + scale.y)); vertices[0] = MVector2(xc, (yc + height)) + offset; texCoords[1] = MVector2((pos.x + scale.x), (pos.y + scale.y)); vertices[1] = MVector2((xc + width), (yc + height)) + offset; texCoords[3] = MVector2((pos.x + scale.x), pos.y); vertices[3] = MVector2((xc + width), yc) + offset; texCoords[2] = MVector2(pos.x, pos.y); vertices[2] = MVector2(xc, yc) + offset; // draw quad render->drawArray(M_PRIMITIVE_TRIANGLE_STRIP, 0, 4); //move to next character xc += character->getXAdvance() * size; } }
void MOText::updateLinesOffset(void) { MFont * font = getFont(); const char * text = m_text.getData(); float tabSize = m_size*2; float fontSize = (float)font->getFontSize(); float widthFactor = font->getTextureWith() / fontSize; float xc = 0; float min = 0; float max = 0; // bounding box MVector3 * boxMin = m_boundingBox.getMin(); MVector3 * boxMax = m_boundingBox.getMax(); // clear lines m_linesOffset.clear(); unsigned int i; unsigned int size = strlen(text); for(i=0; i<size; i++) { if(text[i] == '\n') // return { switch(m_align) { case M_ALIGN_LEFT: m_linesOffset.push_back(0); break; case M_ALIGN_RIGHT: m_linesOffset.push_back(-(max - min)); break; case M_ALIGN_CENTER: m_linesOffset.push_back(-(max - min)*0.5f); break; } min = 0; max = 0; xc = 0; continue; } if(text[i] == '\t') // tab { int tab = (int)(xc / tabSize) + 1; xc = tab*tabSize; continue; } // get character unsigned int charCode = (unsigned int)((unsigned char)text[i]); MCharacter * character = font->getCharacter(charCode); if(! character) continue; MVector2 scale = character->getScale(); MVector2 offset = character->getOffset() * m_size; float width = scale.x * widthFactor * m_size; float charMin = xc + offset.x; float charMax = charMin + width; if(charMin < min) min = charMin; if(charMax > max) max = charMax; //move to next character xc += character->getXAdvance() * m_size; // last char if((i + 1) == size) { switch(m_align) { case M_ALIGN_LEFT: m_linesOffset.push_back(0); break; case M_ALIGN_RIGHT: m_linesOffset.push_back(-(max - min)); break; case M_ALIGN_CENTER: m_linesOffset.push_back(-(max - min)*0.5f); break; } } } float globalOffset = boxMax->x - boxMin->x; switch(m_align) { case M_ALIGN_RIGHT: boxMin->x -= globalOffset; boxMax->x -= globalOffset; break; case M_ALIGN_CENTER: boxMin->x -= globalOffset*0.5f; boxMax->x -= globalOffset*0.5f; break; } }
bool M_loadFont(const char * filename, void * data, void * arg) { MFont * font = (MFont *)data; int pen_x, pen_y, max_y; unsigned int n, max_code = 4096; unsigned int size = font->getFontSize(); unsigned int space = 2; unsigned int width = 1024; unsigned int height = 0; MImage image; FT_GlyphSlot slot; FT_Library library; FT_Face face; FT_Byte * file_base; FT_Long file_size; // init FT_Error error = FT_Init_FreeType(&library); if(error){ printf("ERROR Load Font : unable to init FreeType\n"); return false; } // open file MFile * file = M_fopen(filename, "rb"); if(! file) { FT_Done_FreeType(library); printf("ERROR Load Font : can't read file %s\n", filename); return false; } M_fseek(file, 0, SEEK_END); file_size = M_ftell(file); M_rewind(file); file_base = new FT_Byte[file_size]; if(file_size != M_fread(file_base, sizeof(FT_Byte), file_size, file)) { M_fclose(file); FT_Done_FreeType(library); delete [] file_base; return false; } // read font error = FT_New_Memory_Face(library, file_base, file_size, 0, &face); M_fclose(file); if(error) { printf("ERROR Load Font : unable to read data %s\n", filename); FT_Done_FreeType(library); delete [] file_base; return false; } // set font size error = FT_Set_Pixel_Sizes(face, 0, size); if(error) { printf("ERROR Load Font : unable to size font\n"); FT_Done_FreeType(library); delete [] file_base; return false; } // parse characters max_y = 0; slot = face->glyph; pen_x = space; pen_y = space; for(n = 0; n<max_code; n++) { // load glyph image into the slot (erase previous one) error = FT_Load_Char(face, n, FT_LOAD_RENDER | FT_LOAD_NO_HINTING); if(error) continue; if(FT_Get_Char_Index(face, n) == 0) continue; // max y max_y = MAX(max_y, slot->bitmap.rows); if((pen_x + slot->bitmap.width + space) > width) { pen_x = space; pen_y += max_y + space; height += max_y + space; max_y = 0; } // increment pen position pen_x += slot->bitmap.width + space; } if(height == 0) { printf("ERROR Load Font : unable to create font texture\n"); FT_Done_FreeType(library); delete [] file_base; return false; } // create image height = getNextPowerOfTwo(height); image.create(M_UBYTE, width, height, 4); memset(image.getData(), 0, image.getSize()*sizeof(char)); // init font font->setTextureWidth(width); font->setTextureHeight(height); // create font texture max_y = 0; slot = face->glyph; pen_x = space; pen_y = space; for(n = 0; n<max_code; n++) { // load glyph image into the slot (erase previous one) error = FT_Load_Char(face, n, FT_LOAD_RENDER | FT_LOAD_NO_HINTING); if(error) continue; if(FT_Get_Char_Index(face, n) == 0) continue; // max y max_y = MAX(max_y, slot->bitmap.rows); if((pen_x + slot->bitmap.width + space) > (int)image.getWidth()){ pen_x = space; pen_y += max_y + space; } // get character properties float xAdvance = (slot->advance.x >> 6) / ((float)size); MVector2 offset = MVector2((float)slot->bitmap_left - 1, - (float)slot->bitmap_top - 1) / ((float)size); MVector2 pos = MVector2((float)(pen_x-1) / (float)width, (float)(pen_y-1) / (float)height); MVector2 scale = MVector2((float)(slot->bitmap.width+2) / (float)width, (float)(slot->bitmap.rows+2) / (float)height); // set character font->setCharacter(n, MCharacter(xAdvance, offset, pos, scale)); // draw to image drawBitmap(&image, &slot->bitmap, pen_x, pen_y); // increment pen position pen_x += slot->bitmap.width + space; } // send texture MEngine * engine = MEngine().getInstance(); MRenderingContext * render = engine->getRenderingContext(); // gen texture id unsigned int textureId = font->getTextureId(); if(textureId == 0) { render->createTexture(&textureId); font->setTextureId(textureId); } // send texture image render->bindTexture(textureId); render->setTextureUWrapMode(M_WRAP_REPEAT); render->setTextureVWrapMode(M_WRAP_REPEAT); render->sendTextureImage(&image, 0, 1, 0); // finish FT_Done_FreeType(library); delete [] file_base; return true; }
void MOText::prepare(void) { MFont * font = getFont(); const char * text = m_text.getData(); if(! (strlen(text) > 0 && font)){ m_boundingBox = MBox3d(); return; } MVector3 * min = m_boundingBox.getMin(); MVector3 * max = m_boundingBox.getMax(); (*min) = (*max) = MVector3(0, 0, 0); float tabSize = m_size*2; float fontSize = (float)font->getFontSize(); float widthFactor = font->getTextureWith() / fontSize; float heightFactor = font->getTextureHeight() / fontSize; float xc = 0, yc = 0; unsigned int i; unsigned int size = strlen(text); for(i=0; i<size; i++) { if(text[i] == '\n') // return { yc += m_size; xc = 0; continue; } if(text[i] == '\t') // tab { int tab = (int)(xc / tabSize) + 1; xc = tab*tabSize; continue; } // get character unsigned int charCode = (unsigned int)((unsigned char)text[i]); MCharacter * character = font->getCharacter(charCode); if(! character) continue; MVector2 scale = character->getScale(); MVector2 offset = character->getOffset() * m_size; float width = scale.x * widthFactor * m_size; float height = scale.y * heightFactor * m_size; MVector2 charMin = MVector2(xc, yc) + offset; MVector2 charMax = charMin + MVector2(width, height); if(charMin.x < min->x) min->x = charMin.x; if(charMin.y < min->y) min->y = charMin.y; if(charMax.x > max->x) max->x = charMax.x; if(charMax.y > max->y) max->y = charMax.y; //move to next character xc += character->getXAdvance() * m_size; } updateLinesOffset(); }