From 6d3228477f211b6a937ef4431252aec209d99801 Mon Sep 17 00:00:00 2001 From: Lynix Date: Fri, 16 Jan 2015 23:36:20 +0100 Subject: [PATCH] Added TextDrawer classes Former-commit-id: 4c5ace385f1a9b9ceebb774022bbc001b69a3bb4 --- include/Nazara/Utility/AbstractTextDrawer.hpp | 42 +++ include/Nazara/Utility/SimpleTextDrawer.hpp | 59 ++++ src/Nazara/Utility/AbstractTextDrawer.cpp | 9 + src/Nazara/Utility/SimpleTextDrawer.cpp | 305 ++++++++++++++++++ 4 files changed, 415 insertions(+) create mode 100644 include/Nazara/Utility/AbstractTextDrawer.hpp create mode 100644 include/Nazara/Utility/SimpleTextDrawer.hpp create mode 100644 src/Nazara/Utility/AbstractTextDrawer.cpp create mode 100644 src/Nazara/Utility/SimpleTextDrawer.cpp diff --git a/include/Nazara/Utility/AbstractTextDrawer.hpp b/include/Nazara/Utility/AbstractTextDrawer.hpp new file mode 100644 index 000000000..76df09a00 --- /dev/null +++ b/include/Nazara/Utility/AbstractTextDrawer.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ABSTRACTTEXTDRAWER_HPP +#define NAZARA_ABSTRACTTEXTDRAWER_HPP + +#include +#include +#include +#include + +class NzAbstractImage; +class NzFont; + +class NAZARA_API NzAbstractTextDrawer +{ + public: + struct Glyph; + + NzAbstractTextDrawer() = default; + virtual ~NzAbstractTextDrawer(); + + virtual const NzRectui& GetBounds() const = 0; + virtual NzFont* GetFont(unsigned int index) const = 0; + virtual unsigned int GetFontCount() const = 0; + virtual const Glyph& GetGlyph(unsigned int index) const = 0; + virtual unsigned int GetGlyphCount() const = 0; + + struct Glyph + { + NzColor color; + NzRectui atlasRect; + NzVector2f corners[4]; + NzAbstractImage* atlas; + bool flipped; + }; +}; + +#endif // NAZARA_ABSTRACTTEXTDRAWER_HPP diff --git a/include/Nazara/Utility/SimpleTextDrawer.hpp b/include/Nazara/Utility/SimpleTextDrawer.hpp new file mode 100644 index 000000000..ca4b11845 --- /dev/null +++ b/include/Nazara/Utility/SimpleTextDrawer.hpp @@ -0,0 +1,59 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_SIMPLETEXTDRAWER_HPP +#define NAZARA_SIMPLETEXTDRAWER_HPP + +#include +#include +#include +#include +#include +#include +#include + +class NAZARA_API NzSimpleTextDrawer : public NzAbstractTextDrawer, NzResourceListener +{ + public: + NzSimpleTextDrawer(); + virtual ~NzSimpleTextDrawer(); + + const NzRectui& GetBounds() const; + unsigned int GetCharacterSize() const; + const NzColor& GetColor() const; + NzFont* GetFont() const; + nzUInt32 GetStyle() const; + + void SetCharacterSize(unsigned int characterSize); + void SetColor(const NzColor& color); + void SetFont(NzFont* font); + void SetStyle(nzUInt32 style); + void SetText(const NzString& str); + + static NzSimpleTextDrawer Draw(unsigned int characterSize, const NzString& str, nzUInt32 style = nzTextStyle_Regular, const NzColor& color = NzColor::White); + static NzSimpleTextDrawer Draw(NzFont* font, unsigned int characterSize, const NzString& str, nzUInt32 style = nzTextStyle_Regular, const NzColor& color = NzColor::White); + + private: + NzFont* GetFont(unsigned int index) const override; + unsigned int GetFontCount() const override; + const Glyph& GetGlyph(unsigned int index) const override; + unsigned int GetGlyphCount() const override; + + bool OnResourceModified(const NzResource* resource, int index, unsigned int code) override; + void OnResourceReleased(const NzResource* resource, int index) override; + void UpdateGlyphs() const; + + mutable std::vector m_glyphs; + NzColor m_color; + NzFontRef m_font; + mutable NzRectui m_bounds; + NzString m_text; + nzUInt32 m_style; + mutable bool m_glyphUpdated; + unsigned int m_characterSize; +}; + +#endif // NAZARA_SIMPLETEXTDRAWER_HPP diff --git a/src/Nazara/Utility/AbstractTextDrawer.cpp b/src/Nazara/Utility/AbstractTextDrawer.cpp new file mode 100644 index 000000000..289836751 --- /dev/null +++ b/src/Nazara/Utility/AbstractTextDrawer.cpp @@ -0,0 +1,9 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +NzAbstractTextDrawer::~NzAbstractTextDrawer() = default; + diff --git a/src/Nazara/Utility/SimpleTextDrawer.cpp b/src/Nazara/Utility/SimpleTextDrawer.cpp new file mode 100644 index 000000000..e16161d69 --- /dev/null +++ b/src/Nazara/Utility/SimpleTextDrawer.cpp @@ -0,0 +1,305 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +///TODO: Listener de font (cas où l'atlas a changé) + +NzSimpleTextDrawer::NzSimpleTextDrawer() : +m_color(NzColor::White), +m_style(nzTextStyle_Regular) +{ + // SetFont(NzFont::GetDefault()); +} + +NzSimpleTextDrawer::~NzSimpleTextDrawer() +{ + if (m_font) + m_font->RemoveResourceListener(this); +} + +const NzRectui& NzSimpleTextDrawer::GetBounds() const +{ + if (!m_glyphUpdated) + UpdateGlyphs(); + + return m_bounds; +} + +unsigned int NzSimpleTextDrawer::GetCharacterSize() const +{ + return m_characterSize; +} + +const NzColor& NzSimpleTextDrawer::GetColor() const +{ + return m_color; +} + +NzFont* NzSimpleTextDrawer::GetFont() const +{ + return m_font; +} + +nzUInt32 NzSimpleTextDrawer::GetStyle() const +{ + return m_style; +} + +void NzSimpleTextDrawer::SetCharacterSize(unsigned int characterSize) +{ + m_characterSize = characterSize; + + m_glyphUpdated = false; +} + +void NzSimpleTextDrawer::SetColor(const NzColor& color) +{ + m_color = color; + + m_glyphUpdated = false; +} + +void NzSimpleTextDrawer::SetFont(NzFont* font) +{ + if (m_font) + m_font->RemoveResourceListener(this); + + m_font = font; + if (m_font) + m_font->AddResourceListener(this); + + m_glyphUpdated = false; +} + +void NzSimpleTextDrawer::SetStyle(nzUInt32 style) +{ + m_style = style; + + m_glyphUpdated = false; +} + +void NzSimpleTextDrawer::SetText(const NzString& str) +{ + m_text = str; + + m_glyphUpdated = false; +} + +NzSimpleTextDrawer NzSimpleTextDrawer::Draw(unsigned int characterSize, const NzString& str, nzUInt32 style, const NzColor& color) +{ + ///FIXME: Sans default font ça n'a aucun intérêt + NzSimpleTextDrawer drawer; + drawer.SetCharacterSize(characterSize); + drawer.SetColor(color); + drawer.SetStyle(style); + drawer.SetText(str); + + return drawer; +} + +NzSimpleTextDrawer NzSimpleTextDrawer::Draw(NzFont* font, unsigned int characterSize, const NzString& str, nzUInt32 style, const NzColor& color) +{ + NzSimpleTextDrawer drawer; + drawer.SetCharacterSize(characterSize); + drawer.SetColor(color); + drawer.SetFont(font); + drawer.SetStyle(style); + drawer.SetText(str); + + return drawer; +} + +NzFont* NzSimpleTextDrawer::GetFont(unsigned int index) const +{ + #if NAZARA_UTILITY_SAFE + if (index > 0) + { + NazaraError("Font index out of range (" + NzString::Number(index) + " >= 1)"); + return nullptr; + } + #endif + + return m_font; +} + +unsigned int NzSimpleTextDrawer::GetFontCount() const +{ + return 1; +} + +const NzAbstractTextDrawer::Glyph& NzSimpleTextDrawer::GetGlyph(unsigned int index) const +{ + if (!m_glyphUpdated) + UpdateGlyphs(); + + return m_glyphs[index]; +} + +unsigned int NzSimpleTextDrawer::GetGlyphCount() const +{ + if (!m_glyphUpdated) + UpdateGlyphs(); + + return m_glyphs.size(); +} + +bool NzSimpleTextDrawer::OnResourceModified(const NzResource* resource, int index, unsigned int code) +{ + NazaraUnused(resource); + NazaraUnused(index); + + #ifdef NAZARA_DEBUG + if (m_font != resource) + { + NazaraInternalError("Not listening to " + NzString::Pointer(resource)); + return false; + } + #endif + + if (code == NzFont::ModificationCode_AtlasChanged || + code == NzFont::ModificationCode_AtlasLayerChanged || + code == NzFont::ModificationCode_GlyphCacheCleared) + { + m_glyphUpdated = false; + } + + return true; +} + +void NzSimpleTextDrawer::OnResourceReleased(const NzResource* resource, int index) +{ + NazaraUnused(resource); + NazaraUnused(index); + + #ifdef NAZARA_DEBUG + if (m_font != resource) + { + NazaraInternalError("Not listening to " + NzString::Pointer(resource)); + return; + } + #endif + + SetFont(nullptr); +} + +void NzSimpleTextDrawer::UpdateGlyphs() const +{ + m_bounds.MakeZero(); + m_glyphs.clear(); + m_glyphUpdated = true; + + #if NAZARA_UTILITY_SAFE + if (!m_font || !m_font->IsValid()) + { + NazaraError("Invalid font"); + return; + } + #endif + + if (m_text.IsEmpty()) + return; + + ///TODO: Itération UTF-8 => UTF-32 sans allocation de buffer (Exposer utf8cpp ?) + unsigned int size; + std::unique_ptr characters(m_text.GetUtf32Buffer(&size)); + if (!characters) + { + NazaraError("Invalid character set"); + return; + } + + const NzFont::SizeInfo& sizeInfo = m_font->GetSizeInfo(m_characterSize); + + // "Curseur" de dessin + NzVector2ui drawPos(0, m_characterSize); + NzVector2ui lastPos(0, 0); + + m_glyphs.reserve(size); + nzUInt32 previousCharacter = 0; + for (unsigned int i = 0; i < size; ++i) + { + char32_t character = characters[i]; + + if (previousCharacter != 0) + drawPos.x += m_font->GetKerning(m_characterSize, previousCharacter, character); + + previousCharacter = character; + + bool whitespace = true; + switch (character) + { + case ' ': + drawPos.x += sizeInfo.spaceAdvance; + break; + + case '\n': + drawPos.x = 0; + drawPos.y += sizeInfo.lineHeight; + break; + + case '\t': + drawPos.x += sizeInfo.spaceAdvance*4; + break; + + default: + whitespace = false; + break; + } + + if (whitespace) + continue; // Inutile d'avoir un glyphe pour un espace blanc + + const NzFont::Glyph& fontGlyph = m_font->GetGlyph(m_characterSize, m_style, character); + if (!fontGlyph.valid) + continue; // Le glyphe n'a pas été correctement chargé, que pouvons-nous faire d'autre que le passer + + Glyph glyph; + glyph.atlas = m_font->GetAtlas()->GetLayer(fontGlyph.layerIndex); + glyph.atlasRect = fontGlyph.atlasRect; + glyph.color = m_color; + glyph.flipped = fontGlyph.flipped; + + float advance = fontGlyph.advance; + + NzRectf bounds(fontGlyph.aabb); + bounds.x += drawPos.x; + bounds.y += drawPos.y; + + if (fontGlyph.requireFauxBold) + { + // On va agrandir le glyphe pour simuler le gras (idée moisie, mais idée quand même) + NzVector2f center = bounds.GetCenter(); + + bounds.width *= 1.1f; + bounds.height *= 1.1f; + + // On le replace à la bonne hauteur + NzVector2f offset(bounds.GetCenter() - center); + bounds.y -= offset.y; + + // On ajuste l'espacement + advance *= 1.1f; + } + + // On "penche" le glyphe pour obtenir un semblant d'italique + float italic = (fontGlyph.requireFauxItalic) ? 0.208f : 0.f; + float italicTop = italic * bounds.y; + float italicBottom = italic * bounds.GetMaximum().y; + + glyph.corners[0].Set(bounds.x - italicTop, bounds.y); + glyph.corners[1].Set(bounds.x + bounds.width - italicTop, bounds.y); + glyph.corners[2].Set(bounds.x - italicBottom, bounds.y + bounds.height); + glyph.corners[3].Set(bounds.x + bounds.width - italicBottom, bounds.y + bounds.height); + + m_glyphs.push_back(glyph); + + lastPos = drawPos; + drawPos.x += advance; + } + + m_bounds.ExtendTo(lastPos); +}