float Font::getKerning(Uint32 first, Uint32 second, unsigned int characterSize) const { // Special case where first or second is 0 (null character) if (first == 0 || second == 0) return 0.f; FT_Face face = static_cast<FT_Face>(m_face); if (face && FT_HAS_KERNING(face) && setCurrentSize(characterSize)) { // Convert the characters to indices FT_UInt index1 = FT_Get_Char_Index(face, first); FT_UInt index2 = FT_Get_Char_Index(face, second); // Get the kerning vector FT_Vector kerning; FT_Get_Kerning(face, index1, index2, FT_KERNING_DEFAULT, &kerning); // X advance is already in pixels for bitmap fonts if (!FT_IS_SCALABLE(face)) return static_cast<float>(kerning.x); // Return the X advance return static_cast<float>(kerning.x) / static_cast<float>(1 << 6); } else { // Invalid font, or no kerning return 0.f; } }
void reset(bool preserveData = true) { __startOperation(ITask::TASK_HOST); setCurrentSize(this->getDataSpace().getElementCount()); if (!preserveData) memset(pointer, 0, this->getDataSpace().getElementCount() * sizeof (TYPE)); }
float Font::getLineSpacing(unsigned int characterSize) const { FT_Face face = static_cast<FT_Face>(m_face); if (face && setCurrentSize(characterSize)) { return static_cast<float>(face->size->metrics.height) / static_cast<float>(1 << 6); } else { return 0.f; } }
float Font::getUnderlineThickness(unsigned int characterSize) const { FT_Face face = static_cast<FT_Face>(m_face); if (face && setCurrentSize(characterSize)) { // Return a fixed thickness if font is a bitmap font if (!FT_IS_SCALABLE(face)) return characterSize / 14.f; return static_cast<float>(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)) / static_cast<float>(1 << 6); } else { return 0.f; } }
int Font::getKerning(Uint32 first, Uint32 second, unsigned int characterSize) const { // Special case where first or second is 0 (null character) if (first == 0 || second == 0) return 0; FT_Face face = static_cast<FT_Face>(m_face); if (face && FT_HAS_KERNING(face) && setCurrentSize(characterSize)) { // Convert the characters to indices FT_UInt index1 = FT_Get_Char_Index(face, first); FT_UInt index2 = FT_Get_Char_Index(face, second); // Get the kerning vector FT_Vector kerning; FT_Get_Kerning(face, index1, index2, FT_KERNING_DEFAULT, &kerning); // Return the X advance return kerning.x >> 6; }
Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) const { // The glyph to return Glyph glyph; // First, transform our ugly void* to a FT_Face FT_Face face = static_cast<FT_Face>(m_face); if (!face) return glyph; // Set the character size if (!setCurrentSize(characterSize)) return glyph; // Load the glyph corresponding to the code point if (FT_Load_Char(face, codePoint, FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT) != 0) return glyph; // Retrieve the glyph FT_Glyph glyphDesc; if (FT_Get_Glyph(face->glyph, &glyphDesc) != 0) return glyph; // Apply bold if necessary -- first technique using outline (highest quality) FT_Pos weight = 1 << 6; bool outline = (glyphDesc->format == FT_GLYPH_FORMAT_OUTLINE); if (bold && outline) { FT_OutlineGlyph outlineGlyph = (FT_OutlineGlyph)glyphDesc; FT_Outline_Embolden(&outlineGlyph->outline, weight); } // Convert the glyph to a bitmap (i.e. rasterize it) FT_Glyph_To_Bitmap(&glyphDesc, FT_RENDER_MODE_NORMAL, 0, 1); FT_Bitmap& bitmap = reinterpret_cast<FT_BitmapGlyph>(glyphDesc)->bitmap; // Apply bold if necessary -- fallback technique using bitmap (lower quality) if (bold && !outline) { FT_Bitmap_Embolden(static_cast<FT_Library>(m_library), &bitmap, weight, weight); } // Compute the glyph's advance offset glyph.advance = static_cast<float>(face->glyph->metrics.horiAdvance) / static_cast<float>(1 << 6); if (bold) glyph.advance += static_cast<float>(weight) / static_cast<float>(1 << 6); int width = bitmap.width; int height = bitmap.rows; if ((width > 0) && (height > 0)) { // Leave a small padding around characters, so that filtering doesn't // pollute them with pixels from neighbors const unsigned int padding = 1; // Get the glyphs page corresponding to the character size Page& page = m_pages[characterSize]; // Find a good position for the new glyph into the texture glyph.textureRect = findGlyphRect(page, width + 2 * padding, height + 2 * padding); // Make sure the texture data is positioned in the center // of the allocated texture rectangle glyph.textureRect.left += padding; glyph.textureRect.top += padding; glyph.textureRect.width -= 2 * padding; glyph.textureRect.height -= 2 * padding; // Compute the glyph's bounding box glyph.bounds.left = static_cast<float>(face->glyph->metrics.horiBearingX) / static_cast<float>(1 << 6); glyph.bounds.top = -static_cast<float>(face->glyph->metrics.horiBearingY) / static_cast<float>(1 << 6); glyph.bounds.width = static_cast<float>(face->glyph->metrics.width) / static_cast<float>(1 << 6); glyph.bounds.height = static_cast<float>(face->glyph->metrics.height) / static_cast<float>(1 << 6); // Extract the glyph's pixels from the bitmap m_pixelBuffer.resize(width * height * 4, 255); const Uint8* pixels = bitmap.buffer; if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { // Pixels are 1 bit monochrome values for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = (x + y * width) * 4 + 3; m_pixelBuffer[index] = ((pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 255 : 0; } pixels += bitmap.pitch; } } else { // Pixels are 8 bits gray levels for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = (x + y * width) * 4 + 3; m_pixelBuffer[index] = pixels[x]; } pixels += bitmap.pitch; } } // Write the pixels to the texture unsigned int x = glyph.textureRect.left; unsigned int y = glyph.textureRect.top; unsigned int w = glyph.textureRect.width; unsigned int h = glyph.textureRect.height; page.texture.update(&m_pixelBuffer[0], w, h, x, y); } // Delete the FT glyph FT_Done_Glyph(glyphDesc); // Force an OpenGL flush, so that the font's texture will appear updated // in all contexts immediately (solves problems in multi-threaded apps) glCheck(glFlush()); // Done :) return glyph; }
Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, float outlineThickness) const { // The glyph to return Glyph glyph; // First, transform our ugly void* to a FT_Face FT_Face face = static_cast<FT_Face>(m_face); if (!face) return glyph; // Set the character size if (!setCurrentSize(characterSize)) return glyph; // Load the glyph corresponding to the code point FT_Int32 flags = FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT; if (outlineThickness != 0) flags |= FT_LOAD_NO_BITMAP; if (FT_Load_Char(face, codePoint, flags) != 0) return glyph; // Retrieve the glyph FT_Glyph glyphDesc; if (FT_Get_Glyph(face->glyph, &glyphDesc) != 0) return glyph; // Apply bold and outline (there is no fallback for outline) if necessary -- first technique using outline (highest quality) FT_Pos weight = 1 << 6; bool outline = (glyphDesc->format == FT_GLYPH_FORMAT_OUTLINE); if (outline) { if (bold) { FT_OutlineGlyph outlineGlyph = (FT_OutlineGlyph)glyphDesc; FT_Outline_Embolden(&outlineGlyph->outline, weight); } if (outlineThickness != 0) { FT_Stroker stroker = static_cast<FT_Stroker>(m_stroker); FT_Stroker_Set(stroker, static_cast<FT_Fixed>(outlineThickness * static_cast<float>(1 << 6)), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph_Stroke(&glyphDesc, stroker, true); } } // Convert the glyph to a bitmap (i.e. rasterize it) FT_Glyph_To_Bitmap(&glyphDesc, FT_RENDER_MODE_NORMAL, 0, 1); FT_Bitmap& bitmap = reinterpret_cast<FT_BitmapGlyph>(glyphDesc)->bitmap; // Apply bold if necessary -- fallback technique using bitmap (lower quality) if (!outline) { if (bold) FT_Bitmap_Embolden(static_cast<FT_Library>(m_library), &bitmap, weight, weight); if (outlineThickness != 0) err() << "Failed to outline glyph (no fallback available)" << std::endl; } // Compute the glyph's advance offset glyph.advance = static_cast<float>(face->glyph->metrics.horiAdvance) / static_cast<float>(1 << 6); if (bold) glyph.advance += static_cast<float>(weight) / static_cast<float>(1 << 6); int width = bitmap.width; int height = bitmap.rows; if ((width > 0) && (height > 0)) { // Leave a small padding around characters, so that filtering doesn't // pollute them with pixels from neighbors const unsigned int padding = 1; width += 2 * padding; height += 2 * padding; // Get the glyphs page corresponding to the character size Page& page = m_pages[characterSize]; // Find a good position for the new glyph into the texture glyph.textureRect = findGlyphRect(page, width, height); // Make sure the texture data is positioned in the center // of the allocated texture rectangle glyph.textureRect.left += padding; glyph.textureRect.top += padding; glyph.textureRect.width -= 2 * padding; glyph.textureRect.height -= 2 * padding; // Compute the glyph's bounding box glyph.bounds.left = static_cast<float>(face->glyph->metrics.horiBearingX) / static_cast<float>(1 << 6); glyph.bounds.top = -static_cast<float>(face->glyph->metrics.horiBearingY) / static_cast<float>(1 << 6); glyph.bounds.width = static_cast<float>(face->glyph->metrics.width) / static_cast<float>(1 << 6) + outlineThickness * 2; glyph.bounds.height = static_cast<float>(face->glyph->metrics.height) / static_cast<float>(1 << 6) + outlineThickness * 2; // Resize the pixel buffer to the new size and fill it with transparent white pixels m_pixelBuffer.resize(width * height * 4); Uint8* current = &m_pixelBuffer[0]; Uint8* end = current + width * height * 4; while (current != end) { (*current++) = 255; (*current++) = 255; (*current++) = 255; (*current++) = 0; } // Extract the glyph's pixels from the bitmap const Uint8* pixels = bitmap.buffer; if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { // Pixels are 1 bit monochrome values for (unsigned int y = padding; y < height - padding; ++y) { for (unsigned int x = padding; x < width - padding; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = x + y * width; m_pixelBuffer[index * 4 + 3] = ((pixels[(x - padding) / 8]) & (1 << (7 - ((x - padding) % 8)))) ? 255 : 0; } pixels += bitmap.pitch; } } else { // Pixels are 8 bits gray levels for (unsigned int y = padding; y < height - padding; ++y) { for (unsigned int x = padding; x < width - padding; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = x + y * width; m_pixelBuffer[index * 4 + 3] = pixels[x - padding]; } pixels += bitmap.pitch; } } // Write the pixels to the texture unsigned int x = glyph.textureRect.left - padding; unsigned int y = glyph.textureRect.top - padding; unsigned int w = glyph.textureRect.width + 2 * padding; unsigned int h = glyph.textureRect.height + 2 * padding; page.texture.update(&m_pixelBuffer[0], w, h, x, y); } // Delete the FT glyph FT_Done_Glyph(glyphDesc); // Done :) return glyph; }