// ---------------------------------------------------------------------------- void FontWithFace::init() { setDPI(); m_page = irr_driver->getVideoDriver()->createImage(video::ECF_A8R8G8B8, core::dimension2du(getGlyphPageSize(), getGlyphPageSize())); // Get the max height for this face assert(m_face_ttf->getTotalFaces() > 0); FT_Face cur_face = m_face_ttf->getFace(0); font_manager->checkFTError(FT_Set_Pixel_Sizes(cur_face, 0, getDPI()), "setting DPI"); for (int i = 32; i < 128; i++) { // Test all basic latin characters const int idx = FT_Get_Char_Index(cur_face, (wchar_t)i); if (idx == 0) continue; font_manager->checkFTError(FT_Load_Glyph(cur_face, idx, FT_LOAD_DEFAULT), "setting max height"); const int height = cur_face->glyph->metrics.height / BEARING; if (height > m_glyph_max_height) m_glyph_max_height = height; } reset(); } // init
void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); withReadLock([&] { _pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); }); APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, getSourceUrl()); APPEND_ENTITY_PROPERTY(PROP_DPI, getDPI()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT_URL, getScriptURL()); APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, getMaxFPS()); APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)getInputMode()); APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, getShowKeyboardFocusHighlight()); }
// ---------------------------------------------------------------------------- void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi) { assert(gi.glyph_index > 0); assert(gi.font_number < m_face_ttf->getTotalFaces()); FT_Face cur_face = m_face_ttf->getFace(gi.font_number); FT_GlyphSlot slot = cur_face->glyph; // Faces may be shared across regular and bold, // so reset dpi each time font_manager->checkFTError(FT_Set_Pixel_Sizes(cur_face, 0, getDPI()), "setting DPI"); font_manager->checkFTError(FT_Load_Glyph(cur_face, gi.glyph_index, FT_LOAD_DEFAULT), "loading a glyph"); if (dynamic_cast<BoldFace*>(this)) { // Embolden the outline of the glyph FT_Outline_Embolden(&(slot->outline), getDPI() * 2); } font_manager->checkFTError(FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL), "render a glyph to bitmap"); // Convert to an anti-aliased bitmap FT_Bitmap bits = slot->bitmap; core::dimension2du d(bits.width + 1, bits.rows + 1); core::dimension2du texture_size; texture_size = d.getOptimalSize(!(irr_driver->getVideoDriver() ->queryFeature(video::EVDF_TEXTURE_NPOT)), !(irr_driver ->getVideoDriver()->queryFeature(video::EVDF_TEXTURE_NSQUARE)), true, 0); if ((m_used_width + texture_size.Width > getGlyphPageSize() && m_used_height + m_temp_height + texture_size.Height > getGlyphPageSize()) || m_used_height + texture_size.Height > getGlyphPageSize()) { // Current glyph page is full: // Save the old glyph page video::ITexture* page_texture = irr_driver->getVideoDriver() ->addTexture("Glyph_page", m_page); m_spritebank->setTexture(m_spritebank->getTextureCount() - 1, page_texture); irr_driver->getVideoDriver()->removeTexture(page_texture); assert(page_texture->getReferenceCount() == 1); // Clear and add a new one createNewGlyphPage(); } video::IImage* glyph = NULL; switch (bits.pixel_mode) { case FT_PIXEL_MODE_GRAY: { // Create our blank image. glyph = irr_driver->getVideoDriver() ->createImage(video::ECF_A8R8G8B8, texture_size); glyph->fill(video::SColor(0, 255, 255, 255)); // Load the grayscale data in. const float gray_count = static_cast<float>(bits.num_grays); const unsigned int image_pitch = glyph->getPitch() / sizeof(unsigned int); unsigned int* image_data = (unsigned int*)glyph->lock(); unsigned char* glyph_data = bits.buffer; for (unsigned int y = 0; y < (unsigned int)bits.rows; y++) { unsigned char* row = glyph_data; for (unsigned int x = 0; x < (unsigned)bits.width; x++) { image_data[y * image_pitch + x] |= static_cast<unsigned int>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24; } glyph_data += bits.pitch; } glyph->unlock(); break; } default: assert(false); } if (!glyph) Log::fatal("FontWithFace", "Failed to load a glyph"); // Done creating a single glyph, now copy to the glyph page... // Determine the linebreak location if (m_used_width + texture_size.Width > getGlyphPageSize()) { m_used_width = 0; m_used_height += m_temp_height; m_temp_height = 0; } // Copy to the full glyph page glyph->copyTo(m_page, core::position2di(m_used_width, m_used_height)); // Store the rectangle of current glyph gui::SGUISpriteFrame f; gui::SGUISprite s; core::rect<s32> rectangle(m_used_width, m_used_height, m_used_width + bits.width, m_used_height + bits.rows); f.rectNumber = m_spritebank->getPositions().size(); f.textureNumber = m_spritebank->getTextureCount() - 1; // Add frame to sprite s.Frames.push_back(f); s.frameTime = 0; m_spritebank->getPositions().push_back(rectangle); m_spritebank->getSprites().push_back(s); // Save glyph metrics FontArea a; a.advance_x = cur_face->glyph->advance.x / BEARING; a.bearing_x = cur_face->glyph->metrics.horiBearingX / BEARING; const int cur_height = (cur_face->glyph->metrics.height / BEARING); const int cur_offset_y = cur_height - (cur_face->glyph->metrics.horiBearingY / BEARING); a.offset_y = m_glyph_max_height - cur_height + cur_offset_y; a.offset_y_bt = -cur_offset_y; a.spriteno = f.rectNumber; m_character_area_map[c] = a; // Clean the temporary glyph glyph->drop(); glyph = NULL; // Store used area m_used_width += texture_size.Width; if (m_temp_height < texture_size.Height) m_temp_height = texture_size.Height; } // insertGlyph
/** Render a glyph for a character into bitmap and save it into the glyph page. * \param c The character to be loaded. * \param c \ref GlyphInfo for the character. */ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi) { assert(gi.glyph_index > 0); assert(gi.font_number < m_face_ttf->getTotalFaces()); FT_Face cur_face = m_face_ttf->getFace(gi.font_number); FT_GlyphSlot slot = cur_face->glyph; // Same face may be shared across the different FontWithFace, // so reset dpi each time font_manager->checkFTError(FT_Set_Pixel_Sizes(cur_face, 0, getDPI()), "setting DPI"); font_manager->checkFTError(FT_Load_Glyph(cur_face, gi.glyph_index, FT_LOAD_DEFAULT), "loading a glyph"); font_manager->checkFTError(shapeOutline(&(slot->outline)), "shaping outline"); font_manager->checkFTError(FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL), "rendering a glyph to bitmap"); // Convert to an anti-aliased bitmap FT_Bitmap* bits = &(slot->bitmap); core::dimension2du texture_size(bits->width + 1, bits->rows + 1); if ((m_used_width + texture_size.Width > getGlyphPageSize() && m_used_height + m_current_height + texture_size.Height > getGlyphPageSize()) || m_used_height + texture_size.Height > getGlyphPageSize()) { // Add a new glyph page if current one is full createNewGlyphPage(); } // Determine the linebreak location if (m_used_width + texture_size.Width > getGlyphPageSize()) { m_used_width = 0; m_used_height += m_current_height; m_current_height = 0; } const unsigned int cur_tex = m_spritebank->getTextureCount() -1; #ifndef SERVER_ONLY video::ITexture* tex = m_spritebank->getTexture(cur_tex); glBindTexture(GL_TEXTURE_2D, tex->getOpenGLTextureName()); assert(bits->pixel_mode == FT_PIXEL_MODE_GRAY); if (CVS->isARBTextureSwizzleUsable()) { glTexSubImage2D(GL_TEXTURE_2D, 0, m_used_width, m_used_height, bits->width, bits->rows, GL_RED, GL_UNSIGNED_BYTE, bits->buffer); } else { const unsigned int size = bits->width * bits->rows; uint8_t* image_data = new uint8_t[size * 4]; memset(image_data, 255, size * 4); for (unsigned int i = 0; i < size; i++) image_data[4 * i + 3] = bits->buffer[i]; glTexSubImage2D(GL_TEXTURE_2D, 0, m_used_width, m_used_height, bits->width, bits->rows, GL_BGRA, GL_UNSIGNED_BYTE, image_data); delete[] image_data; } if (tex->hasMipMaps()) glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); #endif // Store the rectangle of current glyph gui::SGUISpriteFrame f; gui::SGUISprite s; core::rect<s32> rectangle(m_used_width, m_used_height, m_used_width + bits->width, m_used_height + bits->rows); f.rectNumber = m_spritebank->getPositions().size(); f.textureNumber = cur_tex; // Add frame to sprite s.Frames.push_back(f); s.frameTime = 0; m_spritebank->getPositions().push_back(rectangle); m_spritebank->getSprites().push_back(s); // Save glyph metrics FontArea a; a.advance_x = cur_face->glyph->advance.x / BEARING; a.bearing_x = cur_face->glyph->metrics.horiBearingX / BEARING; const int cur_height = (cur_face->glyph->metrics.height / BEARING); const int cur_offset_y = cur_height - (cur_face->glyph->metrics.horiBearingY / BEARING); a.offset_y = m_glyph_max_height - cur_height + cur_offset_y; a.offset_y_bt = -cur_offset_y; a.spriteno = f.rectNumber; m_character_area_map[c] = a; // Store used area m_used_width += texture_size.Width; if (m_current_height < texture_size.Height) m_current_height = texture_size.Height; } // insertGlyph
float Utils::getRealSizeInMM(float size) { return size / getDPI() * 25.4; }