void GSOsdManager::upload_texture_atlas(GSTexture* t) { if (!m_face) return; if (m_char_info.size() > 96) // we only reserved space for this many glyphs fprintf(stderr, "More than 96 glyphs needed for OSD"); // This can be sped up a bit by only uploading new glyphs int x = 0; for(auto &pair : m_char_info) { if(FT_Load_Char(m_face, pair.first, FT_LOAD_RENDER)) { fprintf(stderr, "failed to load char U%d\n", (int)pair.first); continue; } // Size of char pair.second.ax = m_face->glyph->advance.x >> 6; pair.second.ay = m_face->glyph->advance.y >> 6; pair.second.bw = m_face->glyph->bitmap.width; pair.second.bh = m_face->glyph->bitmap.rows; pair.second.bl = m_face->glyph->bitmap_left; pair.second.bt = m_face->glyph->bitmap_top; GSVector4i r(x, 0, x+pair.second.bw, pair.second.bh); if (r.width()) t->Update(r, m_face->glyph->bitmap.buffer, m_face->glyph->bitmap.pitch); if (r.width() > m_max_width) m_max_width = r.width(); pair.second.tx = (float)x / m_atlas_w; pair.second.ty = (float)pair.second.bh / m_atlas_h; pair.second.tw = (float)pair.second.bw / m_atlas_w; x += pair.second.bw; } m_texture_dirty = false; }
double PdfFontMetricsFreetype::UnicodeCharWidth( unsigned short c ) const { FT_Error ftErr; double dWidth = 0.0; if( static_cast<int>(c) < PODOFO_WIDTH_CACHE_SIZE ) { dWidth = m_vecWidth[static_cast<unsigned int>(c)]; } else { ftErr = FT_Load_Char( m_pFace, static_cast<FT_UInt>(c), FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ); if( ftErr ) return dWidth; dWidth = m_pFace->glyph->metrics.horiAdvance * 1000.0 / m_pFace->units_per_EM; } return dWidth * static_cast<double>(this->GetFontSize() * this->GetFontScale() / 100.0) / 1000.0 + static_cast<double>( this->GetFontSize() * this->GetFontScale() / 100.0 * this->GetFontCharSpace() / 100.0); }
double swfStrWidthUTF8(const char *str, const pGEcontext gc, pDevDesc dd) { #ifdef SWF_DEBUG Rprintf("strWidthUTF8 called\n"); Rprintf("** family = %s, str[0] = %d, str[1] = %d\n", gc->fontfamily, str[0], str[1]); #endif /* Convert UTF-8 string to Unicode array */ int maxLen = strlen(str); wchar_t *unicode = (wchar_t *) calloc(maxLen + 1, sizeof(wchar_t)); int len = utf8towcs(unicode, str, maxLen); /* Get the font face object */ FT_Face face = swfGetFTFace(gc); FT_Error err; double fontSize = gc->ps * gc->cex; double ratio = fontSize / face->units_per_EM; double width = 0.0; int i; /* Add up the 'advance' of each character */ for(i = 0; i < len; i++) { err = FT_Load_Char(face, unicode[i], FT_LOAD_NO_SCALE); if(err) { errorcode(err); continue; } width += face->glyph->metrics.horiAdvance * ratio; } free(unicode); #ifdef SWF_DEBUG Rprintf("** strWidthUTF8(width = %f)\n", width); #endif return width; }
void render_text(const std::string &str, FT_Face face, float x, float y, float sx, float sy) { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); const FT_GlyphSlot glyph = face->glyph; for (auto c : str) { if (FT_Load_Char(face, c, FT_LOAD_RENDER) != 0) continue; glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, glyph->bitmap.width, glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, glyph->bitmap.buffer); const float vx = x + glyph->bitmap_left * sx; const float vy = y + glyph->bitmap_top * sy; const float w = glyph->bitmap.width * sx; const float h = glyph->bitmap.rows * sy; struct { float x, y, s, t; } data[6] = { { vx , vy , 0, 0 }, { vx , vy - h, 0, 1 }, { vx + w, vy , 1, 0 }, { vx + w, vy , 1, 0 }, { vx , vy - h, 0, 1 }, { vx + w, vy - h, 1, 1 } }; glBufferData(GL_ARRAY_BUFFER, 24 * sizeof(float), data, GL_DYNAMIC_DRAW); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); glDrawArrays(GL_TRIANGLES, 0, 6); x += (glyph->advance.x >> 6) * sx; y += (glyph->advance.y >> 6) * sy; } glPixelStorei(GL_UNPACK_ALIGNMENT, 4); }
/** * Gets the width of a string. The width of a string is not necesserily * the sum of all the widths of it's glyphs. * * @param text The string to return the width of. * @return The width of a string. */ int OpenGLFont::getWidth(const std::string& text) const { unsigned int w = 0; const char* ptr = text.c_str(); size_t textlen = strlen(ptr); while (textlen > 0) { Uint32 c = UTF8_getch(&ptr, &textlen); if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED) { continue; } FT_GlyphSlot slot = this->pmpl->face->glyph; // Load glyph image into the slot int error = FT_Load_Char(this->pmpl->face, c, FT_LOAD_DEFAULT); if (error) continue; w += (slot->advance.x >> 6); } return w; }
Face *FontManager::getFont(const std::string fontPath, const int size) { if (fonts.count(fontPath + std::to_string(size)) == 0) { FT_Face face; if (FT_New_Face(ft, fontPath.c_str(), 0, &face)) { std::cerr << "Could not create font with path=[" << fontPath << "]" << std::endl << "Creating with default path=[" << DEFAULT_FONT_PATH << "]" << std::endl; //if path is broken, this can be broken too, we need better error handling FT_New_Face(ft, DEFAULT_FONT_PATH.c_str(), 0, &face); } fonts[fontPath + std::to_string(size)] = new Face(glHelper, fontPath, size, face); FT_Set_Pixel_Sizes(face, 0, size); //now we should calculate what we have unsigned int w = 0; unsigned int h = 0; for (unsigned int i = 0; i < 256; i++) { if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { fprintf(stderr, "Loading character %c failed!\n", i); continue; } w = std::max(w, face->glyph->bitmap.width); h = std::max(h, face->glyph->bitmap.rows); } std::cout << "atlas h: " << h << ", w " << w << std::endl; //now we have maximum size of the textures } return fonts[fontPath + std::to_string(size)]; }
static void YE_LoadGlyph(Font *font, FT_Face face, char c) { FT_Error error = FT_Load_Char(face, c, FT_LOAD_RENDER); if (error) { Log(1, "[Font loader] Failed to load glyph \"%c\": %s", c, FTErrorString(error)); return; } FT_GlyphSlot glyph = face->glyph; byte *image = YE_ConvertBitmap(&glyph->bitmap); auto size = Vector2i(glyph->bitmap.width, glyph->bitmap.rows); auto offset = Vector2f(glyph->metrics.horiBearingX>>6, -(size.y - (glyph->metrics.horiBearingY>>6))); auto advance = Vector2f(glyph->advance.x>>6, glyph->advance.y>>6); GLuint texture; GLenum origformat = GL_LUMINANCE; GLenum gpuformat = GL_LUMINANCE8; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); // Turn off mipmaps and enable linear interpolation glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, gpuformat, size.x, size.y, 0, // <----------------------------------------- old useless crap origformat, GL_UNSIGNED_BYTE, image); font->SetGlyph(c, make_unique<Glyph>(texture, size, offset, advance)); delete image; }
void swfMetricInfo(int c, const pGEcontext gc, double* ascent, double* descent, double* width, pDevDesc dd) { #ifdef SWF_DEBUG Rprintf("metricInfo called\n"); Rprintf("** family = %s, c = %d\n", gc->fontfamily, c); #endif pswfDesc swfInfo = (pswfDesc) dd->deviceSpecific; FT_Face face = swfGetFTFace(gc, swfInfo); FT_Error err; double fontSize = gc->ps * gc->cex; double ratio = fontSize / face->units_per_EM; if(c == 0) c = 77; if(c < 0) { c = -c; } /* c is the unicode of the character */ FT_Set_Char_Size(face, 0, fontSize * 64, 72, 0); err = FT_Load_Char(face, c, FT_LOAD_NO_SCALE); if(err) { errorcode(err); *ascent = *descent = *width = 0.0; return; } *ascent = face->glyph->metrics.horiBearingY * ratio; *descent = face->glyph->metrics.height * ratio - *ascent; *width = face->glyph->metrics.horiAdvance * ratio; #ifdef SWF_DEBUG Rprintf("** metricInfo(ascent = %f, descent = %f, width = %f)\n", *ascent, *descent, *width); #endif }
void create_font_atlas(FontAtlas* fa, char* filename, unsigned int height) { Font f; if (!load_font(&f, filename)) { fprintf(stderr, "Couldn't load font from file: %s\n", filename); } FT_Set_Pixel_Sizes(f.face, 0, height); FT_GlyphSlot g = f.face->glyph; int roww = 0; int rowh = 0; fa->w = 0; fa->h = 0; memset(fa->c, 0, sizeof fa->c); /* Find minimum size for a texture holding all visible ASCII characters */ for (int i = 32; i < 128; i++) { if (FT_Load_Char(f.face, i, FT_LOAD_RENDER)) { fprintf(stderr, "Loading character %c failed!\n", i); continue; } if (roww + g->bitmap.width + 1 >= MAXWIDTH) { fa->w = max(fa->w, roww); fa->h += rowh; roww = 0; rowh = 0; } roww += g->bitmap.width + 1; rowh = max(rowh, g->bitmap.rows); } fa->w = max(fa->w, roww); fa->h += rowh; init_atlas_texture(fa); copy_glyphs_to_texture(fa, &f); // Clean up delete_font(&f); }
CharStruct Measure(Char const *string, int length=0) { if (not length) while (string[length]) ++length; CharStruct extent; for (int it = 0; it < length; ++it) { FT_Load_Char(face, string[it], FT_LOAD_NO_BITMAP); FT_Glyph_Metrics &meter = face->glyph->metrics; extent.advance += meter.horiAdvance; if (extent.ascent < meter.horiBearingY) { extent.ascent = meter.horiBearingY; } FT_Pos diff = meter.height - meter.horiBearingY; if (extent.descent < diff) { extent.descent = diff; } } return extent; }
void SWFShape_addString(SWFShape shape, const wchar_t* str, size_t nchar, double fontSize, FT_Face face, FT_Outline_Funcs *funs) { OutlineData data; FT_Outline outline; FT_Error err; int i; data.shape = shape; data.ratio_EM = fontSize / face->units_per_EM; data.deltax = 0.0; for(i = 0; i < nchar; i++) { /* str should be Unicode */ err = FT_Load_Char(face, str[i], FT_LOAD_NO_SCALE); if(err) { errorcode(err); continue; } outline = face->glyph->outline; err = FT_Outline_Decompose(&outline, funs, &data); if(err) { errorcode(err); continue; } /* After we draw a character, we move the pen right to a distance of the advance */ /* See the picture in http://www.freetype.org/freetype2/docs/tutorial/step2.html */ data.deltax += face->glyph->metrics.horiAdvance * data.ratio_EM; } }
void FreeTypeFont::initGlCmds() { std::string vertex = vertexSource; std::string fragment = fragmentSource; mShaderManager = new ShaderManager(vertex, fragment, false); GLuint program = mShaderManager->getProgram(); if (program == 0) { LOGE("In Font::initGlCmds() program is 0"); } mHudMVPMatrixLocation = glGetUniformLocation(program, "u_mvpMatrix"); mVetextLocation = glGetAttribLocation(program, "a_position"); mTextureLocation = glGetAttribLocation(program, "a_texCoord"); mSamplerLocation = glGetUniformLocation(program, "s_texture"); mColorLocation = glGetUniformLocation(program, "u_color"); glGenBuffers(1, &mVertexVBO); // Bind the VBO glBindBuffer(GL_ARRAY_BUFFER, mVertexVBO); if(FT_Load_Char(mFace, 'A', FT_LOAD_RENDER)) { LOGE("Could not load character 'X'\n"); return ; } FT_GlyphSlot g = mFace->glyph; float sx = 2.0 / Scene::getInstance()->getWidth(); float sy = 2.0 / Scene::getInstance()->getHeight(); float x2 = 0.1 + g->bitmap_left * sx; float y2 = -0.1 - g->bitmap_top * sy; float w = g->bitmap.width * sx; float h = g->bitmap.rows * sy; GLfloat box[] = { x2, -y2 , x2 + w, -y2 , x2, -y2 - h, x2 + w, -y2 - h, }; glGenTextures(1, &mTextureId); // Set the filtering mode 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); // Bind the texture glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mTextureId); glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA, g->bitmap.width, g->bitmap.rows, 0, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer ); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, mTextureData); //delete[] mTextureData; float sceneWidth = Scene::getInstance()->getWidth(); float sceneHeight = Scene::getInstance()->getHeight(); float a = g->bitmap.width / sceneWidth; float b = g->bitmap.rows / sceneHeight; glm::mat4 mModelMatrix = glm::mat4(1.0); mModelMatrix = glm::translate(mModelMatrix, glm::vec3(0.5, 0.5, 0)); mModelMatrix = glm::scale(mModelMatrix, glm::vec3(a, b, 0)); Scene::getInstance()->getCamera()->updateHudMVP(128, 128); mHudMVPMatrix = Scene::getInstance()->getCamera()->getHudMVP() * mModelMatrix; mGLHasInitialized = true; }
void Font::buildAtlas() { //find the size we should use FT_GlyphSlot g = face->glyph; int w = 0; int h = 0; /*for(int i = 32; i < 128; i++) { if(FT_Load_Char(face, i, FT_LOAD_RENDER)) { fprintf(stderr, "Loading character %c failed!\n", i); continue; } w += g->bitmap.width; h = std::max(h, g->bitmap.rows); }*/ //the max size (GL_MAX_TEXTURE_SIZE) is like 3300 w = 2048; h = 512; textureWidth = w; textureHeight = h; //create the texture glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL); //copy the glyphs into the texture int x = 0; int y = 0; int maxHeight = 0; for(int i = 32; i < 128; i++) { if(FT_Load_Char(face, i, FT_LOAD_RENDER)) continue; //prints rendered texture to the console /*std::cout << "uploading at x: " << x << ", w: " << g->bitmap.width << " h: " << g->bitmap.rows << "\n"; for(int k = 0; k < g->bitmap.rows; k++) { for(int j = 0; j < g->bitmap.width; j++) { if(g->bitmap.buffer[g->bitmap.width * k + j]) std::cout << "."; else std::cout << " "; } std::cout << "\n"; }*/ if(x + g->bitmap.width >= textureWidth) { x = 0; y += maxHeight; maxHeight = 0; } if(g->bitmap.rows > maxHeight) maxHeight = g->bitmap.rows; glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer); charData[i].texX = x; charData[i].texY = y; charData[i].texW = g->bitmap.width; charData[i].texH = g->bitmap.rows; charData[i].advX = g->metrics.horiAdvance >> 6; charData[i].advY = g->advance.y >> 6; charData[i].bearingY = g->metrics.horiBearingY >> 6; if(charData[i].texH > mMaxGlyphHeight) mMaxGlyphHeight = charData[i].texH; x += g->bitmap.width; } glBindTexture(GL_TEXTURE_2D, 0); //std::cout << "generated texture \"" << textureID << "\" (w: " << w << " h: " << h << ")" << std::endl; }
//------------------------------------------------------------------------------------ // загрузка и генерация всех необходимых данных для символа (utf32) //------------------------------------------------------------------------------------ eFontChar* vw_LoadFontChar(unsigned UTF32) { // устанавливаем размеры if (FT_Set_Char_Size( InternalFace, InternalFontSize <<6, InternalFontSize <<6, 96, 96 )) { fprintf(stderr, "Can't set char size %i.", InternalFontSize); return 0; } // загрузка глифа нужного нам символа if (FT_Load_Char( InternalFace, UTF32, FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT)) { fprintf(stderr, "Can't load Char: %u\n", UTF32); return 0; } // создаем структуру FontChar eFontChar* NewChar; NewChar = new eFontChar; NewChar->UTF32 = UTF32; NewChar->CharTexture = 0; NewChar->FontSize = InternalFontSize; NewChar->TexturePositionLeft = 0; NewChar->TexturePositionRight = InternalFace->glyph->bitmap.width; // в случае одной текстуры совпадают с шириной NewChar->TexturePositionTop = 0; NewChar->TexturePositionBottom = InternalFace->glyph->bitmap.rows; // в случае одной текстуры совпадают с высотой NewChar->Width = InternalFace->glyph->bitmap.width; NewChar->Height = InternalFace->glyph->bitmap.rows; NewChar->Left = InternalFace->glyph->bitmap_left; NewChar->Top = InternalFace->glyph->bitmap_top; NewChar->AdvanceX = InternalFace->glyph->advance.x / 64.0f; NewChar->Prev = 0; NewChar->Next = 0; BYTE * pixels; pixels = new BYTE[NewChar->Width*NewChar->Height*4]; // битмап идет в 1 канале градаций серого, делаем 32бита rgba int k=0; BYTE ColorRGB[3]={255,255,255}; for (int j=0; j<NewChar->Height; j++) { for (int i=0; i<NewChar->Width; i++) { // RGB составляющую заполняем белым memcpy(pixels + k, ColorRGB, 3); k+=3; memcpy(pixels + k, InternalFace->glyph->bitmap.buffer+(NewChar->Height-j-1)*NewChar->Width+i, 1); k++; } } // называем текстуру номером символа в утф32 char texturefilename[MAX_PATH]; sprintf(texturefilename, "%i", UTF32); vw_SetTextureProp(RI_MAGFILTER_LINEAR | RI_MINFILTER_LINEAR | RI_MIPFILTER_NONE, RI_CLAMP_TO_EDGE, true, TX_ALPHA_GREYSC, false); NewChar->CharTexture = vw_CreateTextureFromMemory(texturefilename, pixels, NewChar->Width, NewChar->Height, 4, false, 0, 0, false); // очищаем память delete [] pixels; // подключаем к менеджеру vw_AttachFontChar(NewChar); return NewChar; }
//----------------------------------------------------------------------------- // делаем генерацию нужных символов по списку генерируя одну текстуру //----------------------------------------------------------------------------- void vw_GenerateFontChars(int FontTextureWidth, int FontTextureHeight, const char * CharsList) { printf("Font characters generation start.\n"); // будем использовать последовательность как имя текстуры const char *TextureName = CharsList; // временный массив BYTE * DIB; DIB = new BYTE[FontTextureWidth*FontTextureHeight*4]; // всегда делаем rgba // устанавливаем 0 везде, чтобы потом не краcить rgb, а только формировать альфу memset(DIB, 0, FontTextureWidth*FontTextureHeight*4); // данные для работы с вклеиванием в текстуру int CurrentDIBX = 0; int CurrentDIBY = 0; int EdgingSpace = 2; int MaxHeightInCurrentLine = 0; // устанавливаем размеры if (FT_Set_Char_Size( InternalFace, InternalFontSize <<6, InternalFontSize <<6, 96, 96 )) { fprintf(stderr, "Can't set char size %i.", InternalFontSize); return; } // первый проход, формируем одну большую текстуру const char *CharsList2 = CharsList; while (strlen(CharsList) > 0) { unsigned CurrentChar; // преобразуем в утф32 и "сдвигаемся" на следующий символ в строке CharsList = utf8_to_utf32(CharsList, &CurrentChar); // загрузка глифа нужного нам символа if (FT_Load_Char( InternalFace, CurrentChar, FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT)) { fprintf(stderr, "Can't load Char: %u\n", CurrentChar); return; } // создаем структуру FontChar eFontChar* NewChar; NewChar = new eFontChar; NewChar->UTF32 = CurrentChar; NewChar->CharTexture = 0; NewChar->FontSize = InternalFontSize; NewChar->TexturePositionLeft = 0; NewChar->TexturePositionRight = 0; NewChar->TexturePositionTop = 0; NewChar->TexturePositionBottom = 0; NewChar->Width = InternalFace->glyph->bitmap.width; NewChar->Height = InternalFace->glyph->bitmap.rows; NewChar->Left = InternalFace->glyph->bitmap_left; NewChar->Top = InternalFace->glyph->bitmap_top; NewChar->AdvanceX = InternalFace->glyph->advance.x / 64.0f; NewChar->Prev = 0; NewChar->Next = 0; // делаем установку параметров для вклеивания // если в текущую строку символов уже не можем вписывать - смещаемся на новую, ниже if (CurrentDIBX + NewChar->Width > FontTextureWidth) { CurrentDIBX = 0; CurrentDIBY += MaxHeightInCurrentLine + EdgingSpace; MaxHeightInCurrentLine = 0; } // если в текущую строку не влазит уже по высоте - значит это фейл... кричим чтоб дали больше текстуру if (CurrentDIBY + NewChar->Height > FontTextureHeight) { fprintf(stderr, "!!! Can't generate all font chars in one texture. Too many chars or too small texture size!\n"); delete NewChar; break; } // "вклеиваем" новый символ в массив BYTE ColorRGB[3]={255,255,255}; for (int j=0; j<NewChar->Height; j++) for (int i=0; i<NewChar->Width; i++) { memcpy(DIB + (FontTextureHeight-CurrentDIBY-j-1)*FontTextureWidth*4 + (CurrentDIBX+i)*4, ColorRGB, 3); memcpy(DIB + (FontTextureHeight-CurrentDIBY-j-1)*FontTextureWidth*4 + (CurrentDIBX+i)*4 + 3, InternalFace->glyph->bitmap.buffer+j*NewChar->Width+i, 1); } // устанавливаем параметры текстуры для прорисовки нашему символу NewChar->TexturePositionLeft = CurrentDIBX; NewChar->TexturePositionRight = CurrentDIBX + NewChar->Width; NewChar->TexturePositionTop = CurrentDIBY; NewChar->TexturePositionBottom = CurrentDIBY + NewChar->Height; // выбираем наибольшую высоту символов if (MaxHeightInCurrentLine < NewChar->Height) MaxHeightInCurrentLine = NewChar->Height; // смещаем указатель CurrentDIBX += NewChar->Width + EdgingSpace; // подключаем к менеджеру vw_AttachFontChar(NewChar); } ///////////////////////////////// /* // выводим в tga файл сгенерированный DIB, если нужно проверить SDL_RWops *TgaFile = SDL_RWFromFile("fontgenerationtest.tga", "wb"); if (TgaFile == NULL) { fprintf(stderr, "Can't open VFS file for write.\n"); return; } unsigned char UselessChar = 0; // used for useless char. short int UselessInt = 0; // used for useless int. unsigned char ImageType = 2; // Type of image we are saving. unsigned char ImageBits = 32; // Bit depth. short int ImageWidth = (short int)FontTextureWidth; short int ImageHeight = (short int)FontTextureHeight; // пишем неиспользуемые данные SDL_RWwrite(TgaFile, &UselessChar, sizeof(unsigned char), 1); SDL_RWwrite(TgaFile, &UselessChar, sizeof(unsigned char), 1); // тип картинки SDL_RWwrite(TgaFile, &ImageType, sizeof(unsigned char), 1); // пишем неиспользуемые данные SDL_RWwrite(TgaFile, &UselessInt, sizeof(short int), 1); SDL_RWwrite(TgaFile, &UselessInt, sizeof(short int), 1); SDL_RWwrite(TgaFile, &UselessChar, sizeof(unsigned char), 1); SDL_RWwrite(TgaFile, &UselessInt, sizeof(short int), 1); SDL_RWwrite(TgaFile, &UselessInt, sizeof(short int), 1); // записываем параметры картинки SDL_RWwrite(TgaFile, &ImageWidth, sizeof(short int), 1); SDL_RWwrite(TgaFile, &ImageHeight, sizeof(short int), 1); SDL_RWwrite(TgaFile, &ImageBits, sizeof(unsigned char), 1); // пишем неиспользуемые данные SDL_RWwrite(TgaFile, &UselessChar, sizeof(unsigned char), 1); // пишем данные диб массива SDL_RWwrite(TgaFile, DIB, FontTextureWidth*FontTextureHeight*4, 1); // закрываем файл SDL_RWclose(TgaFile); */ ///////////////////////////////// // создаем текстуру vw_SetTextureProp(RI_MAGFILTER_LINEAR | RI_MINFILTER_LINEAR | RI_MIPFILTER_NONE, RI_CLAMP_TO_EDGE, true, TX_ALPHA_GREYSC, false); eTexture* FontTexture = vw_CreateTextureFromMemory(TextureName, DIB, FontTextureWidth, FontTextureHeight, 4, false); // освобождаем память delete [] DIB; if (FontTexture == 0) { fprintf(stderr, "Can't create font texture.\n"); return; } // второй проход, всем FontChars из списка, присваиваем сгенерированную текстуру while (strlen(CharsList2) > 0) { unsigned CurrentChar; // преобразуем в утф32 и "сдвигаемся" на следующий символ в строке CharsList2 = utf8_to_utf32(CharsList2, &CurrentChar); // ставим нашу общую текстуру eFontChar* TMPChar = vw_FindFontCharByUTF32(CurrentChar); if (TMPChar != 0) TMPChar->CharTexture = FontTexture; } printf("Font characters generation end.\n\n"); }
osgText::Glyph3D * FreeTypeFont::getGlyph3D(unsigned int charcode) { OpenThreads::ScopedLock<OpenThreads::Mutex> lock(FreeTypeLibrary::instance()->getMutex()); // // GT: fix for symbol fonts (i.e. the Webdings font) as the wrong character are being // returned, for symbol fonts in windows (FT_ENCONDING_MS_SYMBOL in freetype) the correct // values are from 0xF000 to 0xF0FF not from 0x000 to 0x00FF (0 to 255) as you would expect. // Microsoft uses a private field for its symbol fonts // unsigned int charindex = charcode; if (_face->charmap != NULL) { if (_face->charmap->encoding == FT_ENCODING_MS_SYMBOL) { charindex |= 0xF000; } } FT_Error error = FT_Load_Char( _face, charindex, FT_LOAD_DEFAULT|_flags ); if (error) { OSG_WARN << "FT_Load_Char(...) error 0x"<<std::hex<<error<<std::dec<<std::endl; return 0; } if (_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) { OSG_WARN << "FreeTypeFont3D::getGlyph : not a vector font" << std::endl; return 0; } float coord_scale = _freetype_scale/64.0f; // ** init FreeType to describe the glyph FreeType::Char3DInfo char3d(_facade->getNumberCurveSamples()); char3d._coord_scale = coord_scale; FT_Outline outline = _face->glyph->outline; FT_Outline_Funcs funcs; funcs.conic_to = (FT_Outline_ConicToFunc)&FreeType::conicTo; funcs.line_to = (FT_Outline_LineToFunc)&FreeType::lineTo; funcs.cubic_to = (FT_Outline_CubicToFunc)&FreeType::cubicTo; funcs.move_to = (FT_Outline_MoveToFunc)&FreeType::moveTo; funcs.shift = 0; funcs.delta = 0; // ** record description FT_Error _error = FT_Outline_Decompose(&outline, &funcs, &char3d); if (_error) { OSG_WARN << "FreeTypeFont3D::getGlyph : - outline decompose failed ..." << std::endl; return 0; } // ** create geometry for each part of the glyph osg::ref_ptr<osg::Geometry> frontGeo(new osg::Geometry); osg::ref_ptr<osg::Vec3Array> rawVertices = new osg::Vec3Array(*(char3d._verts)); osg::Geometry::PrimitiveSetList rawPrimitives; for(osg::Geometry::PrimitiveSetList::iterator itr = char3d.get()->getPrimitiveSetList().begin(); itr != char3d.get()->getPrimitiveSetList().end(); ++itr) { rawPrimitives.push_back(dynamic_cast<osg::PrimitiveSet*>((*itr)->clone(osg::CopyOp::DEEP_COPY_ALL))); } // ** save vertices and PrimitiveSetList of each face in the Glyph3D PrimitiveSet face list osg::ref_ptr<osgText::Glyph3D> glyph3D = new osgText::Glyph3D(_facade, charcode); // copy the raw primitive set list before we tessellate it. glyph3D->getRawFacePrimitiveSetList() = rawPrimitives; glyph3D->setRawVertexArray(rawVertices.get()); FT_Glyph_Metrics* metrics = &(_face->glyph->metrics); glyph3D->setHorizontalBearing(osg::Vec2((float)metrics->horiBearingX * coord_scale,(float)(metrics->horiBearingY-metrics->height) * coord_scale)); // bottom left. glyph3D->setHorizontalAdvance((float)metrics->horiAdvance * coord_scale); glyph3D->setVerticalBearing(osg::Vec2((float)metrics->vertBearingX * coord_scale,(float)(metrics->vertBearingY-metrics->height) * coord_scale)); // top middle. glyph3D->setVerticalAdvance((float)metrics->vertAdvance * coord_scale); glyph3D->setWidth((float)metrics->width * coord_scale); glyph3D->setHeight((float)metrics->height * coord_scale); FT_BBox ftbb; FT_Outline_Get_BBox(&outline, &ftbb); long xmin = ft_floor( ftbb.xMin ); long xmax = ft_ceiling( ftbb.xMax ); long ymin = ft_floor( ftbb.yMin ); long ymax = ft_ceiling( ftbb.yMax ); osg::BoundingBox bb(xmin * coord_scale, ymin * coord_scale, 0.0f, xmax * coord_scale, ymax * coord_scale, 0.0f); glyph3D->setBoundingBox(bb); return glyph3D.release(); }
static bool font_renderer_create_atlas(ft_font_renderer_t *handle) { unsigned i; bool ret = true; uint8_t *buffer[FT_ATLAS_SIZE] = {NULL}; unsigned pitches[FT_ATLAS_SIZE] = {0}; unsigned max_width = 0; unsigned max_height = 0; for (i = 0; i < FT_ATLAS_SIZE; i++) { FT_GlyphSlot slot; struct font_glyph *glyph = &handle->glyphs[i]; if (!glyph) continue; if (FT_Load_Char(handle->face, i, FT_LOAD_RENDER)) { ret = false; goto end; } FT_Render_Glyph(handle->face->glyph, FT_RENDER_MODE_NORMAL); slot = handle->face->glyph; /* Some glyphs can be blank. */ buffer[i] = (uint8_t*)calloc(slot->bitmap.rows * slot->bitmap.pitch, 1); glyph->width = slot->bitmap.width; glyph->height = slot->bitmap.rows; pitches[i] = slot->bitmap.pitch; glyph->advance_x = slot->advance.x >> 6; glyph->advance_y = slot->advance.y >> 6; glyph->draw_offset_x = slot->bitmap_left; glyph->draw_offset_y = -slot->bitmap_top; if (buffer[i]) memcpy(buffer[i], slot->bitmap.buffer, slot->bitmap.rows * pitches[i]); max_width = MAX(max_width, (unsigned)slot->bitmap.width); max_height = MAX(max_height, (unsigned)slot->bitmap.rows); } handle->atlas.width = max_width * FT_ATLAS_COLS; handle->atlas.height = max_height * FT_ATLAS_ROWS; handle->atlas.buffer = (uint8_t*) calloc(handle->atlas.width * handle->atlas.height, 1); if (!handle->atlas.buffer) { ret = false; goto end; } /* Blit our texture atlas. */ for (i = 0; i < FT_ATLAS_SIZE; i++) { uint8_t *dst = NULL; unsigned offset_x = (i % FT_ATLAS_COLS) * max_width; unsigned offset_y = (i / FT_ATLAS_COLS) * max_height; handle->glyphs[i].atlas_offset_x = offset_x; handle->glyphs[i].atlas_offset_y = offset_y; dst = (uint8_t*)handle->atlas.buffer; dst += offset_x + offset_y * handle->atlas.width; if (buffer[i]) { unsigned r, c; const uint8_t *src = (const uint8_t*)buffer[i]; for (r = 0; r < handle->glyphs[i].height; r++, dst += handle->atlas.width, src += pitches[i]) for (c = 0; c < handle->glyphs[i].width; c++) dst[c] = src[c]; } } end: for (i = 0; i < FT_ATLAS_SIZE; i++) free(buffer[i]); return ret; }
bool mugg::gui::Font::Load(const std::string& path, unsigned int pixelSize = 48) { if(FT_New_Face(this->ft, path.c_str(), 0, &this->face)) { std::cout << "ERR: Failed to load font " << path << std::endl; return false; } FT_Set_Pixel_Sizes(this->face, 0, pixelSize); this->g = this->face->glyph; int w = 0; int h = 0; for(int i = 32; i < 128; i++) { if(FT_Load_Char(this->face, i, FT_LOAD_RENDER)) { std::cout << "ERR: Loading character " << i << " from " << path << " failed!\n"; } w += this->g->bitmap.width; h = std::max(h, this->g->bitmap.rows); this->width = w; } glGenTextures(1, &this->textureID); glBindTexture(GL_TEXTURE_2D, this->textureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 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); int x = 0; for(int i = 32; i < 128; i++) { if(FT_Load_Char(this->face, i, FT_LOAD_RENDER)) continue; glTexSubImage2D(GL_TEXTURE_2D, 0, x, 0, this->g->bitmap.width, this->g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, this->g->bitmap.buffer); x += this->g->bitmap.width; const uint8_t *p; this->c[*p].ax = this->g->advance.x >> 6; this->c[*p].ay = this->g->advance.y >> 6; this->c[*p].bw = this->g->bitmap.width; this->c[*p].bh = this->g->bitmap.rows; this->c[*p].bl = this->g->bitmap_left; this->c[*p].bt = this->g->bitmap_top; this->c[*p].tx = (float)x / w; } }
int init_timecode(p_info_rec* p_info, timecode_data* tc_data, char* font, const int size, const int aspect_ratio_num, const int aspect_ratio_den) { info_rec* info; FT_GlyphSlot slot; char cset[] = "0123456789:"; int bb_t, bb_b, bb_l, bb_r; // bounding box BYTE* dstLine; BYTE* srcPtr; BYTE* dstPtr; int c, j; int result; // initialise our data if (*p_info == NULL) { result = allocate_info(p_info); if (result < 0) return result; } info = *p_info; // create a suitable text image result = set_font(info, font, size, aspect_ratio_num, aspect_ratio_den); if (result < 0) return result; // get bounding box for characters bb_t = 1000000; bb_b = -1000000; bb_l = 1000000; bb_r = -1000000; for (c = 0; c < 11; c++) { /* load glyph image into the slot (erase previous one) */ if (FT_Load_Char(info->face, cset[c], FT_LOAD_RENDER)) return YUV_freetype; slot = info->face->glyph; /* a small shortcut */ if (bb_t > -slot->bitmap_top) bb_t = -slot->bitmap_top; if (bb_b < slot->bitmap.rows - slot->bitmap_top) bb_b = slot->bitmap.rows - slot->bitmap_top; if (bb_l > slot->bitmap_left) bb_l = slot->bitmap_left; if (bb_r < slot->bitmap_left + slot->bitmap.width) bb_r = slot->bitmap_left + slot->bitmap.width; } // expand bounding box a little bb_t -= 1; bb_b += 1; bb_l -= 1; bb_r += 1; tc_data->height = bb_b - bb_t; // initialise character overlays for (c = 0; c < 11; c++) { tc_data->tc_ovly[c].w = bb_r - bb_l; tc_data->tc_ovly[c].h = tc_data->height; tc_data->tc_ovly[c].ssx = -1; tc_data->tc_ovly[c].ssy = -1; tc_data->tc_ovly[c].buff = malloc(tc_data->tc_ovly[c].w * tc_data->tc_ovly[c].h * 2); if (tc_data->tc_ovly[c].buff == NULL) return YUV_no_memory; memset(tc_data->tc_ovly[c].buff, 0, tc_data->tc_ovly[c].w * tc_data->tc_ovly[c].h * 2); tc_data->tc_ovly[c].Cbuff = NULL; } // copy bitmaps for (c = 0; c < 11; c++) { /* load glyph image into the slot (erase previous one) */ if (FT_Load_Char(info->face, cset[c], FT_LOAD_RENDER)) return YUV_freetype; slot = info->face->glyph; /* a small shortcut */ if (c == 10) { // make colon narrower than other characters tc_data->tc_ovly[c].w = slot->advance.x / 64; } srcPtr = slot->bitmap.buffer; dstLine = tc_data->tc_ovly[c].buff; // add vertical offset dstLine += tc_data->tc_ovly[c].w * (-slot->bitmap_top - bb_t); // horizontally centre character dstLine += (tc_data->tc_ovly[c].w - slot->bitmap.width) / 2; for (j = 0; j < slot->bitmap.rows; j++) { dstPtr = dstLine; memcpy(dstLine, srcPtr, slot->bitmap.width); srcPtr += slot->bitmap.width; dstLine += tc_data->tc_ovly[c].w; } } tc_data->width = (tc_data->tc_ovly[0].w * 8) + (tc_data->tc_ovly[10].w * 3); // 8 digits and 3 colons return YUV_OK; }
euint8* EFontFT2::RenderString(const char *string, eint32 *width, eint32 *height, bool *is_mono, float size, float spacing, float shear, bool bold, eint32 length) { if(string == NULL || *string == 0 || length == 0 || width == NULL || height == NULL || is_mono == NULL) return NULL; EAutolock <ELocker> autolock(&etk_ft2_font_locker); if(!IsAttached()) return NULL; bool isfixed = IsFixedSize(size); if(!fScalable && !isfixed) return NULL; float stringWidth; e_font_height fontHeight; if((stringWidth = StringWidth(string, size, spacing, shear, bold, length)) <= 0) return NULL; GetHeight(&fontHeight, size, shear, bold); eint32 w, h; w = (eint32)ceil(stringWidth) + 1; h = (eint32)ceil(fontHeight.ascent + fontHeight.descent) + 1; euint8 *bitmap = new euint8[w * h]; if(!bitmap) { ETK_WARNING("[FONT]: %s --- Unable to alloc memory for bitmap data.", __PRETTY_FUNCTION__); return NULL; } bzero(bitmap, sizeof(euint8) * (size_t)(w * h)); eunichar *unicode = e_utf8_convert_to_unicode(string, length); if(!unicode) { delete[] bitmap; return NULL; } const eunichar *ch; euint32 x = 0; euint32 y = (euint32)ceil(fontHeight.ascent); bool do_mono = fForceFontAliasing; for(ch = unicode; !(ch == NULL || *ch == 0); ch = e_unicode_next(ch, NULL)) { if(FT_Load_Char(fFace, *ch, (do_mono ? (FT_LOAD_RENDER | FT_LOAD_MONOCHROME) : FT_LOAD_RENDER))) { ETK_DEBUG("[FONT]: %s --- FT_Load_Char failed.", __PRETTY_FUNCTION__); continue; } FT_Bitmap *ftbitmap = &(fFace->glyph->bitmap); eint32 xx = x + (eint32)(fFace->glyph->bitmap_left); eint32 yy = y - (eint32)(fFace->glyph->bitmap_top); eint32 bitmapWidth = (eint32)(ftbitmap->width); eint32 bitmapHeight = (eint32)(ftbitmap->rows); eint32 lineBytes = (eint32)(ftbitmap->pitch > 0 ? ftbitmap->pitch : -(ftbitmap->pitch)); eint32 maxxx = min_c(w, xx + bitmapWidth); eint32 maxyy = min_c(h, yy + bitmapHeight); for(eint32 i = yy, p = 0; i < maxyy; i++, p++) { euint8* dest = bitmap; dest += i * w + xx; unsigned char* src = ftbitmap->buffer; src += p * lineBytes; switch(ftbitmap->pixel_mode) { case FT_PIXEL_MODE_GRAY: for(eint32 j = xx; j < maxxx; j++) *dest++ = (euint8)(*src++); break; case FT_PIXEL_MODE_MONO: for(eint32 j = xx; j < maxxx; ) { euint8 val = (euint8)(*src++); eint32 left = maxxx - j >= 8 ? 8 : maxxx - j; euint8 left_offset = 7; for(eint32 k = 0; k < left; k++, left_offset--, j++) *dest++ = (val & (1 << left_offset)) ? 255 : 0; } break; default: ETK_DEBUG("[FONT]: %s --- The mode of freetype bitmap not supported.", __PRETTY_FUNCTION__); } } x += (euint32)((float)(fFace->glyph->metrics.horiAdvance) / 64.f) + (euint32)ceil((double)(spacing * size)); // next x } free(unicode); *width = w; *height = h; *is_mono = do_mono; return bitmap; }
/** * Load glyphs corresponding to the UTF-32 codepoint code. */ static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code) { DrawTextContext *s = ctx->priv; FT_BitmapGlyph bitmapglyph; Glyph *glyph; struct AVTreeNode *node = NULL; int ret; /* load glyph into s->face->glyph */ if (FT_Load_Char(s->face, code, s->ft_load_flags)) return AVERROR(EINVAL); glyph = av_mallocz(sizeof(*glyph)); if (!glyph) { ret = AVERROR(ENOMEM); goto error; } glyph->code = code; if (FT_Get_Glyph(s->face->glyph, &glyph->glyph)) { ret = AVERROR(EINVAL); goto error; } if (s->borderw) { glyph->border_glyph = glyph->glyph; if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0) || FT_Glyph_To_Bitmap(&glyph->border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) { ret = AVERROR_EXTERNAL; goto error; } bitmapglyph = (FT_BitmapGlyph) glyph->border_glyph; glyph->border_bitmap = bitmapglyph->bitmap; } if (FT_Glyph_To_Bitmap(&glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) { ret = AVERROR_EXTERNAL; goto error; } bitmapglyph = (FT_BitmapGlyph) glyph->glyph; glyph->bitmap = bitmapglyph->bitmap; glyph->bitmap_left = bitmapglyph->left; glyph->bitmap_top = bitmapglyph->top; glyph->advance = s->face->glyph->advance.x >> 6; /* measure text height to calculate text_height (or the maximum text height) */ FT_Glyph_Get_CBox(glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox); /* cache the newly created glyph */ if (!(node = av_tree_node_alloc())) { ret = AVERROR(ENOMEM); goto error; } av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node); if (glyph_ptr) *glyph_ptr = glyph; return 0; error: if (glyph) av_freep(&glyph->glyph); av_freep(&glyph); av_freep(&node); return ret; }
void Font::buildAtlas() { if(FT_New_Face(sLibrary, mPath.c_str(), 0, &face)) { LOG(LogError) << "Error creating font face! (path: " << mPath.c_str(); return; } //FT_Set_Char_Size(face, 0, size * 64, getDpiX(), getDpiY()); FT_Set_Pixel_Sizes(face, 0, mSize); //find the size we should use FT_GlyphSlot g = face->glyph; int w = 0; int h = 0; /*for(int i = 32; i < 128; i++) { if(FT_Load_Char(face, i, FT_LOAD_RENDER)) { fprintf(stderr, "Loading character %c failed!\n", i); continue; } w += g->bitmap.width; h = std::max(h, g->bitmap.rows); }*/ //the max size (GL_MAX_TEXTURE_SIZE) is like 3300 w = 2048; h = 512; textureWidth = w; textureHeight = h; //create the texture glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL); //copy the glyphs into the texture int x = 0; int y = 0; int maxHeight = 0; for(int i = 32; i < 128; i++) { if(FT_Load_Char(face, i, FT_LOAD_RENDER)) continue; //prints rendered texture to the console /*std::cout << "uploading at x: " << x << ", w: " << g->bitmap.width << " h: " << g->bitmap.rows << "\n"; for(int k = 0; k < g->bitmap.rows; k++) { for(int j = 0; j < g->bitmap.width; j++) { if(g->bitmap.buffer[g->bitmap.width * k + j]) std::cout << "."; else std::cout << " "; } std::cout << "\n"; }*/ if(x + g->bitmap.width >= textureWidth) { x = 0; y += maxHeight + 1; //leave one pixel of space between glyphs maxHeight = 0; } if(g->bitmap.rows > maxHeight) maxHeight = g->bitmap.rows; glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer); charData[i].texX = x; charData[i].texY = y; charData[i].texW = g->bitmap.width; charData[i].texH = g->bitmap.rows; charData[i].advX = g->metrics.horiAdvance / 64.0f; charData[i].advY = g->metrics.vertAdvance / 64.0f; charData[i].bearingY = g->metrics.horiBearingY / 64.0f; if(charData[i].texH > mMaxGlyphHeight) mMaxGlyphHeight = charData[i].texH; x += g->bitmap.width + 1; //leave one pixel of space between glyphs } glBindTexture(GL_TEXTURE_2D, 0); FT_Done_Face(face); if((y + maxHeight) >= textureHeight) { //failed to create a proper font texture LOG(LogWarning) << "Font with size " << mSize << " exceeded max texture size! Trying again..."; //try a 3/4th smaller size and redo initialization fontScale *= 1.25f; mSize = (int)(mSize * (1.0f / fontScale)); deinit(); init(); } else { LOG(LogInfo) << "Created font with size " << mSize << "."; } }
osgText::Glyph* FreeTypeFont::getGlyph(const osgText::FontResolution& fontRes, unsigned int charcode) { OpenThreads::ScopedLock<OpenThreads::Mutex> lock(FreeTypeLibrary::instance()->getMutex()); setFontResolution(fontRes); // // GT: fix for symbol fonts (i.e. the Webdings font) as the wrong character are being // returned, for symbol fonts in windows (FT_ENCONDING_MS_SYMBOL in freetype) the correct // values are from 0xF000 to 0xF0FF not from 0x000 to 0x00FF (0 to 255) as you would expect. // Microsoft uses a private field for its symbol fonts // unsigned int charindex = charcode; if (_face->charmap != NULL) { if (_face->charmap->encoding == FT_ENCODING_MS_SYMBOL) { charindex |= 0xF000; } } FT_Error error = FT_Load_Char( _face, charindex, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP|_flags ); if (error) { OSG_WARN << "FT_Load_Char(...) error 0x"<<std::hex<<error<<std::dec<<std::endl; return 0; } FT_GlyphSlot glyphslot = _face->glyph; int pitch = glyphslot->bitmap.pitch; unsigned char* buffer = glyphslot->bitmap.buffer; unsigned int sourceWidth = glyphslot->bitmap.width;; unsigned int sourceHeight = glyphslot->bitmap.rows; unsigned int width = sourceWidth; unsigned int height = sourceHeight; osg::ref_ptr<osgText::Glyph> glyph = new osgText::Glyph(_facade, charcode); unsigned int dataSize = width*height; unsigned char* data = new unsigned char[dataSize]; // clear the image to zeros. for(unsigned char* p=data;p<data+dataSize;) { *p++ = 0; } glyph->setImage(width,height,1, GL_ALPHA, GL_ALPHA,GL_UNSIGNED_BYTE, data, osg::Image::USE_NEW_DELETE, 1); glyph->setInternalTextureFormat(GL_ALPHA); // copy image across to osgText::Glyph image. switch(glyphslot->bitmap.pixel_mode) { case FT_PIXEL_MODE_MONO: for(int r=sourceHeight-1;r>=0;--r) { unsigned char* ptr = buffer+r*pitch; for(unsigned int c=0;c<sourceWidth;++c) { (*data++)= (ptr[c >> 3] & (1 << (~c & 7))) ? 255 : 0; } } break; case FT_PIXEL_MODE_GRAY: for(int r=sourceHeight-1;r>=0;--r) { unsigned char* ptr = buffer+r*pitch; for(unsigned int c=0;c<sourceWidth;++c,++ptr) { (*data++)=*ptr; } } break; default: OSG_WARN << "FT_Load_Char(...) returned bitmap with unknown pixel_mode " << glyphslot->bitmap.pixel_mode << std::endl; } FT_Glyph_Metrics* metrics = &(_face->glyph->metrics); #if 0 float coord_scale = _freetype_scale/64.0f; #else float coord_scale = 1.0f/64.0f; #endif glyph->setHorizontalBearing(osg::Vec2((float)metrics->horiBearingX * coord_scale,(float)(metrics->horiBearingY-metrics->height) * coord_scale)); // bottom left. glyph->setHorizontalAdvance((float)metrics->horiAdvance * coord_scale); glyph->setVerticalBearing(osg::Vec2((float)metrics->vertBearingX * coord_scale,(float)(metrics->vertBearingY-metrics->height) * coord_scale)); // top middle. glyph->setVerticalAdvance((float)metrics->vertAdvance * coord_scale); // cout << " in getGlyph() implementation="<<this<<" "<<_filename<<" facade="<<_facade<<endl; return glyph.release(); }
/** * Draws a single character of text * Called by ::renderText - you probably want that function instead * * @param Uint32 character A 32-bit character code **/ void OpenGLFont::renderCharacter(Uint32 character, float &x, float &y) { FreetypeChar *c = &(this->pmpl->char_tex[character]); // If the OpenGL tex does not exist for this character, create it if (c->tex == 0) { FT_GlyphSlot slot = this->pmpl->face->glyph; int error = FT_Load_Char(this->pmpl->face, character, FT_LOAD_DEFAULT); if (error) return; error = FT_Render_Glyph(this->pmpl->face->glyph, FT_RENDER_MODE_NORMAL); if (error) return; unsigned int width = MAX(nextPowerOfTwo(slot->bitmap.width), 2); unsigned int height = MAX(nextPowerOfTwo(slot->bitmap.rows), 2); GLubyte* gl_data = new GLubyte[2 * width * height]; for (unsigned int j = 0; j < height; j++) { for (unsigned int i = 0; i < width; i++) { int index = 2 * (i + j * width); if (i >= static_cast<unsigned int>(slot->bitmap.width) || j >= static_cast<unsigned int>(slot->bitmap.rows) ) { gl_data[index] = 0; gl_data[index + 1] = 0; } else { gl_data[index] = slot->bitmap.buffer[i + slot->bitmap.width * j]; gl_data[index + 1] = slot->bitmap.buffer[i + slot->bitmap.width * j]; } } } // Create a texture glGenTextures(1, &c->tex); glBindTexture(GL_TEXTURE_2D, c->tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); #ifdef OpenGL glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #endif glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, width, height, 0, GL_RG, GL_UNSIGNED_BYTE, gl_data); delete [] gl_data; c->w = slot->bitmap.width; c->h = slot->bitmap.rows; c->x = slot->bitmap_left; c->y = slot->bitmap_top; c->advance = slot->advance.x; c->tx = (float)slot->bitmap.width / (float)width; c->ty = (float)slot->bitmap.rows / (float)height; } else { glBindTexture(GL_TEXTURE_2D, c->tex); } GLfloat box[4][4] = { {x + c->x, y + -c->y + c->h, 0.f, c->ty}, {x + c->x + c->w, y + -c->y + c->h, c->tx, c->ty}, {x + c->x, y + -c->y, 0.f, 0.f}, {x + c->x + c->w, y + -c->y, c->tx, 0.f}, }; glBindBuffer(GL_ARRAY_BUFFER, this->pmpl->font_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof box, box, GL_DYNAMIC_DRAW); glVertexAttribPointer(ATTRIB_TEXTCOORD, 4, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(ATTRIB_TEXTCOORD); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); x += (float)(c->advance >> 6); }
/** * Creates a bitmap representation of the glyph for character 'character' * in this face */ int STFont::GenerateBitmap(unsigned int character) { FT_Load_Char(mImpl->ftFace, character, FT_LOAD_RENDER ); FT_GlyphSlot glyph = mImpl->ftFace->glyph; FT_Bitmap bitmap = glyph->bitmap; if (bitmap.width == 0 || bitmap.rows == 0) { // failure: the character might not exist in this face, so attempt to // use the glyph for character 0, which is usually rendered as the "missing // character" glyph if (FT_Load_Char(mImpl->ftFace, 0, FT_LOAD_RENDER | FT_LOAD_MONOCHROME )) { fprintf(stderr, "Could not load bitmap glyph for char '%c' (value=%d)\n", character, (int)character); } return -1; } int bitmapIndex = (int)mImpl->glyphBitmaps .size(); mImpl->charMap[character] = bitmapIndex; mImpl->glyphBitmaps .resize(mImpl->glyphBitmaps .size()+1); STBitmapGlyph& bitmapGlyph = mImpl->glyphBitmaps [bitmapIndex]; unsigned int srcWidth = bitmap.width; unsigned int srcHeight = bitmap.rows; unsigned int srcPitch = bitmap.pitch; // Set all the fields in our structure to represent the glyph bitmapGlyph.width = srcWidth; bitmapGlyph.height = srcHeight; bitmapGlyph.pitch = srcPitch; bitmapGlyph.offsetX = glyph->bitmap_left; bitmapGlyph.offsetY = glyph->bitmap_top - glyph->bitmap.rows; bitmapGlyph.advanceX = (float)glyph->advance.x / 64.0f; bitmapGlyph.data = new unsigned char[bitmapGlyph.width * bitmapGlyph.height * 2]; // Like most image formats a rendered bitmap representation of the face's // character is going to begin with the top row of the bitmap. OpenGL // bitmaps begin with the bottom row, so we need to reshuffle the data here unsigned char* dest = bitmapGlyph.data + (( bitmapGlyph.height - 1) * bitmapGlyph.width * 2); unsigned char* src = bitmap.buffer; size_t destStep = bitmapGlyph.width * 2 * 2; for( unsigned int y = 0; y < srcHeight; ++y) { for( unsigned int x = 0; x < srcWidth; ++x) { *dest++ = static_cast<unsigned char>(255); *dest++ = *src++; } dest -= destStep; } return bitmapIndex; }
static const struct font_glyph *font_renderer_ft_get_glyph( void *data, uint32_t charcode) { unsigned map_id; uint8_t *dst; FT_GlyphSlot slot; freetype_atlas_slot_t* atlas_slot; ft_font_renderer_t *handle = (ft_font_renderer_t*)data; if (!handle) return NULL; map_id = charcode & 0xFF; atlas_slot = handle->uc_map[map_id]; while(atlas_slot) { if(atlas_slot->charcode == charcode) { atlas_slot->last_used = handle->usage_counter++; return &atlas_slot->glyph; } atlas_slot = atlas_slot->next; } if (FT_Load_Char(handle->face, charcode, FT_LOAD_RENDER)) return NULL; FT_Render_Glyph(handle->face->glyph, FT_RENDER_MODE_NORMAL); slot = handle->face->glyph; atlas_slot = font_renderer_get_slot(handle); atlas_slot->charcode = charcode; atlas_slot->next = handle->uc_map[map_id]; handle->uc_map[map_id] = atlas_slot; /* Some glyphs can be blank. */ atlas_slot->glyph.width = slot->bitmap.width; atlas_slot->glyph.height = slot->bitmap.rows; atlas_slot->glyph.advance_x = slot->advance.x >> 6; atlas_slot->glyph.advance_y = slot->advance.y >> 6; atlas_slot->glyph.draw_offset_x = slot->bitmap_left; atlas_slot->glyph.draw_offset_y = -slot->bitmap_top; dst = (uint8_t*)handle->atlas.buffer + atlas_slot->glyph.atlas_offset_x + atlas_slot->glyph.atlas_offset_y * handle->atlas.width; if (slot->bitmap.buffer) { unsigned r, c; const uint8_t *src = (const uint8_t*)slot->bitmap.buffer; for (r = 0; r < atlas_slot->glyph.height; r++, dst += handle->atlas.width, src += slot->bitmap.pitch) for (c = 0; c < atlas_slot->glyph.width; c++) dst[c] = src[c]; } handle->atlas.dirty = true; atlas_slot->last_used = handle->usage_counter++; return &atlas_slot->glyph; }
void TextRenderer::initRenderData() { // 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, Constants::fontFilePath.c_str(), 0, &face)) std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl; // Set size to load glyphs as 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); 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); }
int main( int argc, char** argv ) { FT_Library library; FT_Face face; FT_GlyphSlot slot; FT_Matrix matrix; /* transformation matrix */ FT_Vector pen; /* untransformed origin */ FT_Error error; char* filename; // char* text; double angle; int target_height; int n, num_chars; wchar_t *chinese_str = L"ол╣Щ1g"; unsigned int *p = (unsigned int *)chinese_str; int i; printf("Uniocde: \n"); for (i = 0; i < wcslen(chinese_str); i++) { printf("0x%x ", p[i]); } printf("\n"); // return 0; if ( argc != 2 ) { fprintf ( stderr, "usage: %s font\n", argv[0] ); exit( 1 ); } filename = argv[1]; /* first argument */ // text = argv[2]; /* second argument */ num_chars = wcslen(chinese_str); angle = ( 0.0 / 360 ) * 3.14159 * 2; /* use 0 degrees */ target_height = HEIGHT; error = FT_Init_FreeType( &library ); /* initialize library */ /* error handling omitted */ error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */ /* error handling omitted */ /* use 20pt at 100dpi */ error = FT_Set_Char_Size( face, 20 * 64, 0, 100, 0 ); /* set character size */ /* error handling omitted */ slot = face->glyph; /* set up matrix */ matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); /* the pen position in 26.6 cartesian space coordinates; */ /* start at (0,40) relative to the upper left corner */ pen.x = 0 * 64; pen.y = ( target_height - 40 ) * 64; for ( n = 0; n < num_chars; n++ ) { /* set transformation */ FT_Set_Transform( face, &matrix, &pen ); /* load glyph image into the slot (erase previous one) */ error = FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER ); if ( error ) continue; /* ignore errors */ /* now, draw to our target surface (convert position) */ draw_bitmap( &slot->bitmap, slot->bitmap_left, target_height - slot->bitmap_top ); /* increment pen position */ pen.x += slot->advance.x; pen.y += slot->advance.y; } show_image(); FT_Done_Face ( face ); FT_Done_FreeType( library ); return 0; }
// Create font atlas texture void create_font_atlas(font_t *state) { glUseProgram(state->program); glActiveTexture(GL_TEXTURE0); glGenTextures(1, &state->tex_uniform); glBindTexture(GL_TEXTURE_2D, state->tex_uniform); glUniform1i(state->tex_uniform, 0); // Set texture parameters 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); // Set single byte alignment glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Get atlas dimensions FT_GlyphSlot g = state->face->glyph; int w = 0; // full texture width int h = 0; // full texture height int row_w = 0; // current row width int row_h = 0; // current row height int i; for(i=32; i<128; i++) { if(FT_Load_Char(state->face, i, FT_LOAD_RENDER)) { printf("Loading Character %d failed\n", i); exit(EXIT_FAILURE); } // If the width will be over max texture width // Go to next row if(row_w + g->bitmap.width+1 >= MAX_WIDTH) { w = max(w, row_w); h += row_h; row_w = 0; row_h = 0; } row_w += g->bitmap.width + 1; row_h = max(row_h, g->bitmap.rows); } // final texture dimensions w = max(row_w, w); h += row_h; state->atlas_width = w; state->atlas_height = h; // Allocate texture #ifdef RASPI glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, 0); #endif // Fill texture with glyph bitmaps and cache placements char_info_t *char_info = state->char_info; int offset_x = 0; int offset_y = 0; row_h = 0; for(i=32; i<128; i++) { if(FT_Load_Char(state->face, i, FT_LOAD_RENDER)) { printf("Loading Character %d failed\n", i); exit(EXIT_FAILURE); } // Set correct row if(offset_x + g->bitmap.width + 1 >= MAX_WIDTH) { offset_y += row_h; row_h = 0; offset_x = 0; } // fill texture with glyph #ifdef RASPI glTexSubImage2D(GL_TEXTURE_2D, 0, offset_x, offset_y, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer); #else glTexSubImage2D(GL_TEXTURE_2D, 0, offset_x, offset_y, g->bitmap.width, g->bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, g->bitmap.buffer); #endif // Cache values char_info[i].ax = g->advance.x >> 6; char_info[i].ay = g->advance.y >> 6; char_info[i].bw = g->bitmap.width; char_info[i].bh = g->bitmap.rows; char_info[i].bl = g->bitmap_left; char_info[i].bt = g->bitmap_top; char_info[i].tx = offset_x/(float)w; char_info[i].ty = offset_y/(float)h; // Update current position row_h = max(row_h, g->bitmap.rows); offset_x += g->bitmap.width + 1; } }
Atlas(FT_Face face, int height) { FT_Set_Pixel_Sizes(face, 0, height); FT_GlyphSlot g = face->glyph; int roww = 0; int rowh = 0; w = 0; h = 0; memset(c, 0, sizeof c); /* Find minimum size for a texture holding all visible ASCII characters */ for (int i = 32; i < 128; i++) { if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { fprintf(stderr, "Loading character %c failed!\n", i); continue; } if (roww + g->bitmap.width + 1 >= MAXWIDTH) { w = std::max(w, roww); h += rowh; roww = 0; rowh = 0; } roww += g->bitmap.width + 1; rowh = std::max(rowh, (int)g->bitmap.rows); } w = std::max(w, roww); h += rowh; /* Create a texture that will be used to hold all ASCII glyphs */ glActiveTexture(GL_TEXTURE0); glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, monohromeInternalformat, w, h, 0, monohromeformat, GL_UNSIGNED_BYTE, 0); /* We require 1 byte alignment when uploading texture data */ glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* Clamping to edges is important to prevent artifacts when scaling */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); /* Linear filtering usually looks best for text */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* Paste all glyph bitmaps into the texture, remembering the offset */ int ox = 0; int oy = 0; rowh = 0; for (int i = 32; i < 128; i++) { if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { fprintf(stderr, "Loading character %c failed!\n", i); continue; } if (ox + g->bitmap.width + 1 >= MAXWIDTH) { oy += rowh; rowh = 0; ox = 0; } glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, g->bitmap.width, g->bitmap.rows, monohromeformat, GL_UNSIGNED_BYTE, g->bitmap.buffer); c[i].ax = _FIXED2FLOAT(g->advance.x, 6); c[i].ay = _FIXED2FLOAT(g->advance.y, 6); c[i].bw = (float)g->bitmap.width; c[i].bh = (float)g->bitmap.rows; c[i].bl = (float)g->bitmap_left; c[i].bt = (float)g->bitmap_top; c[i].tx = ox / (float)w; c[i].ty = oy / (float)h; rowh = std::max(rowh, (int)g->bitmap.rows); ox += g->bitmap.width + 1; } fprintf(stderr, "Generated a %d x %d (%d kb) texture atlas\n", w, h, w * h / 1024); }