mirror of
https://github.com/SFML/SFML.git
synced 2024-11-28 22:31:09 +08:00
Fixed font glyphs always being 2 pixels larger than they are supposed to be in each dimension, fixed wrong underline offset with some fonts, offset underline and strike through by half of their thickness so their center is positioned correctly, changed glyph and font metrics to use floats instead of ints to support scaling better.
This commit is contained in:
parent
c36ea074d8
commit
b27cbd5036
@ -181,7 +181,7 @@ public :
|
|||||||
/// \return Kerning value for \a first and \a second, in pixels
|
/// \return Kerning value for \a first and \a second, in pixels
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
int getKerning(Uint32 first, Uint32 second, unsigned int characterSize) const;
|
float getKerning(Uint32 first, Uint32 second, unsigned int characterSize) const;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Get the line spacing
|
/// \brief Get the line spacing
|
||||||
@ -194,7 +194,7 @@ public :
|
|||||||
/// \return Line spacing, in pixels
|
/// \return Line spacing, in pixels
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
int getLineSpacing(unsigned int characterSize) const;
|
float getLineSpacing(unsigned int characterSize) const;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Get the position of the underline
|
/// \brief Get the position of the underline
|
||||||
@ -209,7 +209,7 @@ public :
|
|||||||
/// \see getUnderlineThickness
|
/// \see getUnderlineThickness
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
int getUnderlinePosition(unsigned int characterSize) const;
|
float getUnderlinePosition(unsigned int characterSize) const;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Get the thickness of the underline
|
/// \brief Get the thickness of the underline
|
||||||
@ -223,7 +223,7 @@ public :
|
|||||||
/// \see getUnderlinePosition
|
/// \see getUnderlinePosition
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
int getUnderlineThickness(unsigned int characterSize) const;
|
float getUnderlineThickness(unsigned int characterSize) const;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Retrieve the texture containing the loaded glyphs of a certain size
|
/// \brief Retrieve the texture containing the loaded glyphs of a certain size
|
||||||
|
@ -51,9 +51,9 @@ public :
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Member data
|
// Member data
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
int advance; ///< Offset to move horizontically to the next character
|
float advance; ///< Offset to move horizontically to the next character
|
||||||
IntRect bounds; ///< Bounding rectangle of the glyph, in coordinates relative to the baseline
|
FloatRect bounds; ///< Bounding rectangle of the glyph, in coordinates relative to the baseline
|
||||||
IntRect textureRect; ///< Texture coordinates of the glyph inside the font's texture
|
IntRect textureRect; ///< Texture coordinates of the glyph inside the font's texture
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sf
|
} // namespace sf
|
||||||
|
@ -314,11 +314,11 @@ const Glyph& Font::getGlyph(Uint32 codePoint, unsigned int characterSize, bool b
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
int Font::getKerning(Uint32 first, Uint32 second, unsigned int characterSize) const
|
float Font::getKerning(Uint32 first, Uint32 second, unsigned int characterSize) const
|
||||||
{
|
{
|
||||||
// Special case where first or second is 0 (null character)
|
// Special case where first or second is 0 (null character)
|
||||||
if (first == 0 || second == 0)
|
if (first == 0 || second == 0)
|
||||||
return 0;
|
return 0.f;
|
||||||
|
|
||||||
FT_Face face = static_cast<FT_Face>(m_face);
|
FT_Face face = static_cast<FT_Face>(m_face);
|
||||||
|
|
||||||
@ -334,37 +334,37 @@ int Font::getKerning(Uint32 first, Uint32 second, unsigned int characterSize) co
|
|||||||
|
|
||||||
// X advance is already in pixels for bitmap fonts
|
// X advance is already in pixels for bitmap fonts
|
||||||
if (!FT_IS_SCALABLE(face))
|
if (!FT_IS_SCALABLE(face))
|
||||||
return kerning.x;
|
return static_cast<float>(kerning.x);
|
||||||
|
|
||||||
// Return the X advance
|
// Return the X advance
|
||||||
return kerning.x >> 6;
|
return static_cast<float>(kerning.x) / static_cast<float>(1 << 6);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Invalid font, or no kerning
|
// Invalid font, or no kerning
|
||||||
return 0;
|
return 0.f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
int Font::getLineSpacing(unsigned int characterSize) const
|
float Font::getLineSpacing(unsigned int characterSize) const
|
||||||
{
|
{
|
||||||
FT_Face face = static_cast<FT_Face>(m_face);
|
FT_Face face = static_cast<FT_Face>(m_face);
|
||||||
|
|
||||||
if (face && setCurrentSize(characterSize))
|
if (face && setCurrentSize(characterSize))
|
||||||
{
|
{
|
||||||
return (face->size->metrics.height >> 6);
|
return static_cast<float>(face->size->metrics.height) / static_cast<float>(1 << 6);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return 0;
|
return 0.f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
int Font::getUnderlinePosition(unsigned int characterSize) const
|
float Font::getUnderlinePosition(unsigned int characterSize) const
|
||||||
{
|
{
|
||||||
FT_Face face = static_cast<FT_Face>(m_face);
|
FT_Face face = static_cast<FT_Face>(m_face);
|
||||||
|
|
||||||
@ -372,19 +372,19 @@ int Font::getUnderlinePosition(unsigned int characterSize) const
|
|||||||
{
|
{
|
||||||
// Return a fixed position if font is a bitmap font
|
// Return a fixed position if font is a bitmap font
|
||||||
if (!FT_IS_SCALABLE(face))
|
if (!FT_IS_SCALABLE(face))
|
||||||
return characterSize / 10;
|
return characterSize / 10.f;
|
||||||
|
|
||||||
return (FT_MulFix(face->underline_position, face->size->metrics.y_scale) >> 6);
|
return -static_cast<float>(FT_MulFix(face->underline_position, face->size->metrics.y_scale)) / static_cast<float>(1 << 6);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return 0;
|
return 0.f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
int Font::getUnderlineThickness(unsigned int characterSize) const
|
float Font::getUnderlineThickness(unsigned int characterSize) const
|
||||||
{
|
{
|
||||||
FT_Face face = static_cast<FT_Face>(m_face);
|
FT_Face face = static_cast<FT_Face>(m_face);
|
||||||
|
|
||||||
@ -392,13 +392,13 @@ int Font::getUnderlineThickness(unsigned int characterSize) const
|
|||||||
{
|
{
|
||||||
// Return a fixed thickness if font is a bitmap font
|
// Return a fixed thickness if font is a bitmap font
|
||||||
if (!FT_IS_SCALABLE(face))
|
if (!FT_IS_SCALABLE(face))
|
||||||
return characterSize / 14;
|
return characterSize / 14.f;
|
||||||
|
|
||||||
return (FT_MulFix(face->underline_thickness, face->size->metrics.y_scale) >> 6);
|
return static_cast<float>(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)) / static_cast<float>(1 << 6);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return 0;
|
return 0.f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,8 +501,7 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c
|
|||||||
|
|
||||||
// Convert the glyph to a bitmap (i.e. rasterize it)
|
// Convert the glyph to a bitmap (i.e. rasterize it)
|
||||||
FT_Glyph_To_Bitmap(&glyphDesc, FT_RENDER_MODE_NORMAL, 0, 1);
|
FT_Glyph_To_Bitmap(&glyphDesc, FT_RENDER_MODE_NORMAL, 0, 1);
|
||||||
FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyphDesc;
|
FT_Bitmap& bitmap = reinterpret_cast<FT_BitmapGlyph>(glyphDesc)->bitmap;
|
||||||
FT_Bitmap& bitmap = bitmapGlyph->bitmap;
|
|
||||||
|
|
||||||
// Apply bold if necessary -- fallback technique using bitmap (lower quality)
|
// Apply bold if necessary -- fallback technique using bitmap (lower quality)
|
||||||
if (bold && !outline)
|
if (bold && !outline)
|
||||||
@ -511,17 +510,12 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute the glyph's advance offset
|
// Compute the glyph's advance offset
|
||||||
glyph.advance = glyphDesc->advance.x >> 16;
|
glyph.advance = static_cast<float>(face->glyph->metrics.horiAdvance) / static_cast<float>(1 << 6);
|
||||||
if (bold)
|
if (bold)
|
||||||
glyph.advance += weight >> 6;
|
glyph.advance += static_cast<float>(weight) / static_cast<float>(1 << 6);
|
||||||
|
|
||||||
int width = bitmap.width;
|
int width = bitmap.width;
|
||||||
int height = bitmap.rows;
|
int height = bitmap.rows;
|
||||||
int ascender = face->size->metrics.ascender >> 6;
|
|
||||||
|
|
||||||
// Offset to make up for empty space between ascender and virtual top of the typeface
|
|
||||||
// Only applied to scalable fonts i.e. not to bitmap fonts
|
|
||||||
int offset = FT_IS_SCALABLE(face) ? (characterSize - ascender) : 0;
|
|
||||||
|
|
||||||
if ((width > 0) && (height > 0))
|
if ((width > 0) && (height > 0))
|
||||||
{
|
{
|
||||||
@ -535,11 +529,18 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c
|
|||||||
// Find a good position for the new glyph into the texture
|
// Find a good position for the new glyph into the texture
|
||||||
glyph.textureRect = findGlyphRect(page, width + 2 * padding, height + 2 * padding);
|
glyph.textureRect = findGlyphRect(page, width + 2 * padding, height + 2 * padding);
|
||||||
|
|
||||||
|
// Make sure the texture data is positioned in the centre
|
||||||
|
// 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
|
// Compute the glyph's bounding box
|
||||||
glyph.bounds.left = bitmapGlyph->left - padding;
|
glyph.bounds.left = static_cast<float>(face->glyph->metrics.horiBearingX) / static_cast<float>(1 << 6);
|
||||||
glyph.bounds.top = -bitmapGlyph->top - padding - offset;
|
glyph.bounds.top = -static_cast<float>(face->glyph->metrics.horiBearingY) / static_cast<float>(1 << 6);
|
||||||
glyph.bounds.width = width + 2 * padding;
|
glyph.bounds.width = static_cast<float>(face->glyph->metrics.width) / static_cast<float>(1 << 6);
|
||||||
glyph.bounds.height = height + 2 * padding;
|
glyph.bounds.height = static_cast<float>(face->glyph->metrics.height) / static_cast<float>(1 << 6);
|
||||||
|
|
||||||
// Extract the glyph's pixels from the bitmap
|
// Extract the glyph's pixels from the bitmap
|
||||||
m_pixelBuffer.resize(width * height * 4, 255);
|
m_pixelBuffer.resize(width * height * 4, 255);
|
||||||
@ -574,10 +575,10 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write the pixels to the texture
|
// Write the pixels to the texture
|
||||||
unsigned int x = glyph.textureRect.left + padding;
|
unsigned int x = glyph.textureRect.left;
|
||||||
unsigned int y = glyph.textureRect.top + padding;
|
unsigned int y = glyph.textureRect.top;
|
||||||
unsigned int w = glyph.textureRect.width - 2 * padding;
|
unsigned int w = glyph.textureRect.width;
|
||||||
unsigned int h = glyph.textureRect.height - 2 * padding;
|
unsigned int h = glyph.textureRect.height;
|
||||||
page.texture.update(&m_pixelBuffer[0], w, h, x, y);
|
page.texture.update(&m_pixelBuffer[0], w, h, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <SFML/Graphics/Text.hpp>
|
#include <SFML/Graphics/Text.hpp>
|
||||||
#include <SFML/Graphics/Texture.hpp>
|
#include <SFML/Graphics/Texture.hpp>
|
||||||
#include <SFML/Graphics/RenderTarget.hpp>
|
#include <SFML/Graphics/RenderTarget.hpp>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
|
||||||
namespace sf
|
namespace sf
|
||||||
@ -262,14 +263,14 @@ void Text::ensureGeometryUpdate() const
|
|||||||
bool underlined = (m_style & Underlined) != 0;
|
bool underlined = (m_style & Underlined) != 0;
|
||||||
bool strikeThrough = (m_style & StrikeThrough) != 0;
|
bool strikeThrough = (m_style & StrikeThrough) != 0;
|
||||||
float italic = (m_style & Italic) ? 0.208f : 0.f; // 12 degrees
|
float italic = (m_style & Italic) ? 0.208f : 0.f; // 12 degrees
|
||||||
float underlineOffset = static_cast<float>(m_font->getUnderlinePosition(m_characterSize));
|
float underlineOffset = m_font->getUnderlinePosition(m_characterSize);
|
||||||
float underlineThickness = static_cast<float>(m_font->getUnderlineThickness(m_characterSize));
|
float underlineThickness = m_font->getUnderlineThickness(m_characterSize);
|
||||||
|
|
||||||
// Compute the location of the strike through dynamically
|
// Compute the location of the strike through dynamically
|
||||||
// We use the center point of the lowercase 'x' glyph as the reference
|
// We use the center point of the lowercase 'x' glyph as the reference
|
||||||
// We reuse the underline thickness as the thickness of the strike through as well
|
// We reuse the underline thickness as the thickness of the strike through as well
|
||||||
IntRect xBounds = m_font->getGlyph(L'x', m_characterSize, bold).bounds;
|
FloatRect xBounds = m_font->getGlyph(L'x', m_characterSize, bold).bounds;
|
||||||
float strikeThroughOffset = static_cast<float>(xBounds.top) + static_cast<float>(xBounds.height) / 2.f;
|
float strikeThroughOffset = xBounds.top + xBounds.height / 2.f;
|
||||||
|
|
||||||
// Precompute the variables needed by the algorithm
|
// Precompute the variables needed by the algorithm
|
||||||
float hspace = static_cast<float>(m_font->getGlyph(L' ', m_characterSize, bold).advance);
|
float hspace = static_cast<float>(m_font->getGlyph(L' ', m_characterSize, bold).advance);
|
||||||
@ -294,8 +295,8 @@ void Text::ensureGeometryUpdate() const
|
|||||||
// If we're using the underlined style and there's a new line, draw a line
|
// If we're using the underlined style and there's a new line, draw a line
|
||||||
if (underlined && (curChar == L'\n'))
|
if (underlined && (curChar == L'\n'))
|
||||||
{
|
{
|
||||||
float top = y + underlineOffset;
|
float top = std::floor(y + underlineOffset - (underlineThickness / 2) + 0.5f);
|
||||||
float bottom = top + underlineThickness;
|
float bottom = top + std::floor(underlineThickness + 0.5f);
|
||||||
|
|
||||||
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
||||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||||
@ -308,8 +309,8 @@ void Text::ensureGeometryUpdate() const
|
|||||||
// If we're using the strike through style and there's a new line, draw a line across all characters
|
// If we're using the strike through style and there's a new line, draw a line across all characters
|
||||||
if (strikeThrough && (curChar == L'\n'))
|
if (strikeThrough && (curChar == L'\n'))
|
||||||
{
|
{
|
||||||
float top = y + strikeThroughOffset;
|
float top = std::floor(y + strikeThroughOffset - (underlineThickness / 2) + 0.5f);
|
||||||
float bottom = top + underlineThickness;
|
float bottom = top + std::floor(underlineThickness + 0.5f);
|
||||||
|
|
||||||
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
||||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||||
@ -344,10 +345,10 @@ void Text::ensureGeometryUpdate() const
|
|||||||
// Extract the current glyph's description
|
// Extract the current glyph's description
|
||||||
const Glyph& glyph = m_font->getGlyph(curChar, m_characterSize, bold);
|
const Glyph& glyph = m_font->getGlyph(curChar, m_characterSize, bold);
|
||||||
|
|
||||||
int left = glyph.bounds.left;
|
float left = glyph.bounds.left;
|
||||||
int top = glyph.bounds.top;
|
float top = glyph.bounds.top;
|
||||||
int right = glyph.bounds.left + glyph.bounds.width;
|
float right = glyph.bounds.left + glyph.bounds.width;
|
||||||
int bottom = glyph.bounds.top + glyph.bounds.height;
|
float bottom = glyph.bounds.top + glyph.bounds.height;
|
||||||
|
|
||||||
float u1 = static_cast<float>(glyph.textureRect.left);
|
float u1 = static_cast<float>(glyph.textureRect.left);
|
||||||
float v1 = static_cast<float>(glyph.textureRect.top);
|
float v1 = static_cast<float>(glyph.textureRect.top);
|
||||||
@ -375,8 +376,8 @@ void Text::ensureGeometryUpdate() const
|
|||||||
// If we're using the underlined style, add the last line
|
// If we're using the underlined style, add the last line
|
||||||
if (underlined)
|
if (underlined)
|
||||||
{
|
{
|
||||||
float top = y + underlineOffset;
|
float top = std::floor(y + underlineOffset - (underlineThickness / 2) + 0.5f);
|
||||||
float bottom = top + underlineThickness;
|
float bottom = top + std::floor(underlineThickness + 0.5f);
|
||||||
|
|
||||||
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
||||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||||
@ -389,8 +390,8 @@ void Text::ensureGeometryUpdate() const
|
|||||||
// If we're using the strike through style, add the last line across all characters
|
// If we're using the strike through style, add the last line across all characters
|
||||||
if (strikeThrough)
|
if (strikeThrough)
|
||||||
{
|
{
|
||||||
float top = y + strikeThroughOffset;
|
float top = std::floor(y + strikeThroughOffset - (underlineThickness / 2) + 0.5f);
|
||||||
float bottom = top + underlineThickness;
|
float bottom = top + std::floor(underlineThickness + 0.5f);
|
||||||
|
|
||||||
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
m_vertices.append(Vertex(Vector2f(0, top), m_color, Vector2f(1, 1)));
|
||||||
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
m_vertices.append(Vertex(Vector2f(x, top), m_color, Vector2f(1, 1)));
|
||||||
|
Loading…
Reference in New Issue
Block a user