From a9ef2f7e3675ea1c9fed76c2ab617ec3bcd037a8 Mon Sep 17 00:00:00 2001 From: Lynix Date: Thu, 8 Jan 2015 14:19:53 +0100 Subject: [PATCH] Added support for faux-bold and faux-italic Former-commit-id: 259429e8d38b0299e30d539253b50e3aab12c76b --- include/Nazara/Utility/Font.hpp | 4 +- include/Nazara/Utility/FontData.hpp | 4 +- src/Nazara/Utility/Font.cpp | 99 +++++++++++++------ .../Utility/Loaders/FreeType/Loader.cpp | 44 +++++---- 4 files changed, 100 insertions(+), 51 deletions(-) diff --git a/include/Nazara/Utility/Font.hpp b/include/Nazara/Utility/Font.hpp index 2264b7604..4ebef9d3d 100644 --- a/include/Nazara/Utility/Font.hpp +++ b/include/Nazara/Utility/Font.hpp @@ -87,6 +87,8 @@ class NAZARA_API NzFont : public NzResource, NzNonCopyable { NzRecti aabb; NzRectui atlasRect; + bool requireFauxBold; + bool requireFauxItalic; bool flipped; bool valid; int advance; @@ -105,7 +107,7 @@ class NAZARA_API NzFont : public NzResource, NzNonCopyable nzUInt64 ComputeKey(unsigned int characterSize, nzUInt32 style) const; void OnAtlasCleared(); - const Glyph& PrecacheGlyph(GlyphMap& glyphMap, unsigned int characterSize, bool bold, char32_t character) const; + const Glyph& PrecacheGlyph(GlyphMap& glyphMap, unsigned int characterSize, nzUInt32 style, char32_t character) const; std::shared_ptr m_atlas; std::unique_ptr m_data; diff --git a/include/Nazara/Utility/FontData.hpp b/include/Nazara/Utility/FontData.hpp index 9805c1d2b..171735b10 100644 --- a/include/Nazara/Utility/FontData.hpp +++ b/include/Nazara/Utility/FontData.hpp @@ -18,7 +18,7 @@ class NAZARA_API NzFontData NzFontData() = default; virtual ~NzFontData(); - virtual bool ExtractGlyph(unsigned int characterSize, char32_t character, bool bold, NzFontGlyph* dst) = 0; + virtual bool ExtractGlyph(unsigned int characterSize, char32_t character, nzUInt32 style, NzFontGlyph* dst) = 0; virtual NzString GetFamilyName() const = 0; virtual NzString GetStyleName() const = 0; @@ -31,6 +31,8 @@ class NAZARA_API NzFontData virtual unsigned int QueryLineHeight(unsigned int characterSize) const = 0; virtual float QueryUnderlinePosition(unsigned int characterSize) const = 0; virtual float QueryUnderlineThickness(unsigned int characterSize) const = 0; + + virtual bool SupportsStyle(nzUInt32 style) const = 0; }; #endif // NAZARA_FONTDATA_HPP diff --git a/src/Nazara/Utility/Font.cpp b/src/Nazara/Utility/Font.cpp index e41d6b755..65909fe05 100644 --- a/src/Nazara/Utility/Font.cpp +++ b/src/Nazara/Utility/Font.cpp @@ -96,7 +96,7 @@ bool NzFont::ExtractGlyph(unsigned int characterSize, char32_t character, nzUInt } #endif - return m_data->ExtractGlyph(characterSize, character, style & nzTextStyle_Bold, glyph); + return m_data->ExtractGlyph(characterSize, character, style, glyph); } const NzAbstractFontAtlas* NzFont::GetAtlas() const @@ -284,12 +284,11 @@ nzUInt64 NzFont::ComputeKey(unsigned int characterSize, nzUInt32 style) const nzUInt64 sizePart = static_cast((characterSize/m_minimumSizeStep)*m_minimumSizeStep); nzUInt64 stylePart = 0; - if (style & nzTextStyle_Bold) // Les caractères gras sont générés différemment + if (style & nzTextStyle_Bold) stylePart |= nzTextStyle_Bold; - // Les caractères italiques peuvent venir d'une autre police, dans le cas contraire ils sont générés au runtime - //if (style & nzTextStyle_Italic) - // stylePart |= nzTextStyle_Italic; + if (style & nzTextStyle_Italic) + stylePart |= nzTextStyle_Italic; return (stylePart << 32) | sizePart; } @@ -302,52 +301,88 @@ void NzFont::OnAtlasCleared() NotifyModified(ModificationCode_GlyphCacheCleared); } -const NzFont::Glyph& NzFont::PrecacheGlyph(GlyphMap& glyphMap, unsigned int characterSize, bool bold, char32_t character) const +const NzFont::Glyph& NzFont::PrecacheGlyph(GlyphMap& glyphMap, unsigned int characterSize, nzUInt32 style, char32_t character) const { auto it = glyphMap.find(character); if (it != glyphMap.end()) // Si le glyphe n'est pas déjà chargé return it->second; Glyph& glyph = glyphMap[character]; // Insertion du glyphe + glyph.requireFauxBold = false; + glyph.requireFauxItalic = false; glyph.valid = false; - // On extrait le glyphe depuis la police - NzFontGlyph fontGlyph; - if (ExtractGlyph(characterSize, character, bold, &fontGlyph)) + // On vérifie que le style demandé est supporté par la police (dans le cas contraire il devra être simulé au rendu) + nzUInt32 supportedStyle = style; + if (style & nzTextStyle_Bold && !m_data->SupportsStyle(nzTextStyle_Bold)) { - glyph.atlasRect.width = fontGlyph.image.GetWidth(); - glyph.atlasRect.height = fontGlyph.image.GetHeight(); + glyph.requireFauxBold = true; + supportedStyle &= ~nzTextStyle_Bold; + } - // Insertion du rectangle dans l'un des atlas - if (glyph.atlasRect.width > 0 && glyph.atlasRect.height > 0) // Si l'image contient quelque chose + if (style & nzTextStyle_Italic && !m_data->SupportsStyle(nzTextStyle_Italic)) + { + glyph.requireFauxItalic = true; + supportedStyle &= ~nzTextStyle_Italic; + } + + // Est-ce que la police supporte le style demandé ? + if (style == supportedStyle) + { + // On extrait le glyphe depuis la police + NzFontGlyph fontGlyph; + if (ExtractGlyph(characterSize, character, style, &fontGlyph)) { - // Padding (pour éviter le débordement lors du filtrage) - const unsigned int padding = 1; // Un pixel de contour + glyph.atlasRect.width = fontGlyph.image.GetWidth(); + glyph.atlasRect.height = fontGlyph.image.GetHeight(); - glyph.atlasRect.width += padding*2; - glyph.atlasRect.height += padding*2; - - // Insertion du rectangle dans l'atlas virtuel - if (!m_atlas->Insert(fontGlyph.image, &glyph.atlasRect, &glyph.flipped, &glyph.layerIndex)) + // Insertion du rectangle dans l'un des atlas + if (glyph.atlasRect.width > 0 && glyph.atlasRect.height > 0) // Si l'image contient quelque chose { - NazaraError("Failed to insert glyph into atlas"); - return glyph; + // Padding (pour éviter le débordement lors du filtrage) + const unsigned int padding = 1; // Un pixel de contour + + glyph.atlasRect.width += padding*2; + glyph.atlasRect.height += padding*2; + + // Insertion du rectangle dans l'atlas virtuel + if (!m_atlas->Insert(fontGlyph.image, &glyph.atlasRect, &glyph.flipped, &glyph.layerIndex)) + { + NazaraError("Failed to insert glyph into atlas"); + return glyph; + } + + // Compensation du contour (centrage du glyphe) + glyph.atlasRect.x += padding; + glyph.atlasRect.y += padding; + glyph.atlasRect.width -= padding*2; + glyph.atlasRect.height -= padding*2; } - // Compensation du contour (centrage du glyphe) - glyph.atlasRect.x += padding; - glyph.atlasRect.y += padding; - glyph.atlasRect.width -= padding*2; - glyph.atlasRect.height -= padding*2; + glyph.aabb = fontGlyph.aabb; + glyph.advance = fontGlyph.advance; + glyph.valid = true; + } + else + { + NazaraWarning("Failed to extract glyph \"" + NzString::Unicode(character) + "\""); } - - glyph.aabb = fontGlyph.aabb; - glyph.advance = fontGlyph.advance; - glyph.valid = true; } else { - NazaraWarning("Failed to extract glyph \"" + NzString::Unicode(character) + "\""); + // La police ne supporte pas le style demandé, nous allons donc précharger le glyphe supportant le style "minimum" supporté + // et copier ses données + nzUInt64 newKey = ComputeKey(characterSize, supportedStyle); + const Glyph& referenceGlyph = PrecacheGlyph(m_glyphes[newKey], characterSize, supportedStyle, character); + if (referenceGlyph.valid) + { + glyph.aabb = referenceGlyph.aabb; + glyph.advance = referenceGlyph.advance; + glyph.atlasRect = referenceGlyph.atlasRect; + glyph.flipped = referenceGlyph.flipped; + glyph.layerIndex = referenceGlyph.layerIndex; + glyph.valid = true; + } } return glyph; diff --git a/src/Nazara/Utility/Loaders/FreeType/Loader.cpp b/src/Nazara/Utility/Loaders/FreeType/Loader.cpp index 46b91ffe8..35d6f0ff3 100644 --- a/src/Nazara/Utility/Loaders/FreeType/Loader.cpp +++ b/src/Nazara/Utility/Loaders/FreeType/Loader.cpp @@ -75,7 +75,7 @@ namespace return FT_Open_Face(s_library, &m_args, -1, nullptr) == 0; } - bool ExtractGlyph(unsigned int characterSize, char32_t character, bool bold, NzFontGlyph* dst) + bool ExtractGlyph(unsigned int characterSize, char32_t character, nzUInt32 style, NzFontGlyph* dst) override { #ifdef NAZARA_DEBUG if (!dst) @@ -97,10 +97,16 @@ namespace const FT_Pos boldStrength = 2 << 6; - bool outlineFormat = (glyph->format == FT_GLYPH_FORMAT_OUTLINE); - if (outlineFormat && bold) + bool embolden = (style & nzTextStyle_Bold); + + dst->advance = (embolden) ? boldStrength >> 6 : 0; + + if (embolden && glyph->format == FT_GLYPH_FORMAT_OUTLINE) + { // http://www.freetype.org/freetype2/docs/reference/ft2-outline_processing.html#FT_Outline_Embolden FT_Outline_Embolden(&glyph->outline, boldStrength); + embolden = false; + } // http://www.freetype.org/freetype2/docs/reference/ft2-glyph_management.html#FT_Glyph_To_Bitmap // Conversion du glyphe vers le format bitmap @@ -113,18 +119,16 @@ namespace // Dans le cas où nous voulons des caractères gras mais que nous n'avons pas pu agir plus tôt // nous demandons à FreeType d'agir directement sur le bitmap généré - if (!outlineFormat && bold) + if (embolden) { // http://www.freetype.org/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden // "If you want to embolden the bitmap owned by a FT_GlyphSlot_Rec, you should call FT_GlyphSlot_Own_Bitmap on the slot first" FT_GlyphSlot_Own_Bitmap(glyph); FT_Bitmap_Embolden(s_library, &glyph->bitmap, boldStrength, boldStrength); + embolden = false; } - dst->advance = glyph->metrics.horiAdvance >> 6; - if (bold) - dst->advance += boldStrength >> 6; - + dst->advance += glyph->metrics.horiAdvance >> 6; dst->aabb.x = glyph->metrics.horiBearingX >> 6; dst->aabb.y = -(glyph->metrics.horiBearingY >> 6); // Inversion du repère dst->aabb.width = glyph->metrics.width >> 6; @@ -177,22 +181,22 @@ namespace return true; } - NzString GetFamilyName() const + NzString GetFamilyName() const override { return m_face->family_name; } - NzString GetStyleName() const + NzString GetStyleName() const override { return m_face->style_name; } - bool HasKerning() const + bool HasKerning() const override { return FT_HAS_KERNING(m_face); } - bool IsScalable() const + bool IsScalable() const override { return FT_IS_SCALABLE(m_face); } @@ -202,7 +206,7 @@ namespace return FT_Open_Face(s_library, &m_args, 0, &m_face) == 0; } - int QueryKerning(unsigned int characterSize, char32_t first, char32_t second) const + int QueryKerning(unsigned int characterSize, char32_t first, char32_t second) const override { if (FT_HAS_KERNING(m_face)) { @@ -220,7 +224,7 @@ namespace return 0; } - unsigned int QueryLineHeight(unsigned int characterSize) const + unsigned int QueryLineHeight(unsigned int characterSize) const override { SetCharacterSize(characterSize); @@ -228,7 +232,7 @@ namespace return m_face->size->metrics.height >> 6; } - float QueryUnderlinePosition(unsigned int characterSize) const + float QueryUnderlinePosition(unsigned int characterSize) const override { if (FT_IS_SCALABLE(m_face)) { @@ -241,7 +245,7 @@ namespace return characterSize / 10.f; // Joker ? } - float QueryUnderlineThickness(unsigned int characterSize) const + float QueryUnderlineThickness(unsigned int characterSize) const override { if (FT_IS_SCALABLE(m_face)) { @@ -280,6 +284,12 @@ namespace m_args.stream = &m_stream; } + bool SupportsStyle(nzUInt32 style) const override + { + ///TODO + return style == nzTextStyle_None || style == nzTextStyle_Bold; + } + private: void SetCharacterSize(unsigned int characterSize) const { @@ -299,7 +309,7 @@ namespace bool IsSupported(const NzString& extension) { - ///FIXME: Je suppose qu'il en manque quelques uns.. + ///FIXME: Je suppose qu'il en manque quelques unes.. static std::set supportedExtensions = { "afm", "bdf", "cff", "cid", "dfont", "fnt", "pfa", "pfb", "pfm", "pfr", "sfnt", "tte", "ttf" };