Added TextDrawer classes

Former-commit-id: 4c5ace385f1a9b9ceebb774022bbc001b69a3bb4
This commit is contained in:
Lynix 2015-01-16 23:36:20 +01:00
parent 48ecb058f0
commit 6d3228477f
4 changed files with 415 additions and 0 deletions

View File

@ -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 <Nazara/Prerequesites.hpp>
#include <Nazara/Core/Color.hpp>
#include <Nazara/Math/Rect.hpp>
#include <Nazara/Math/Vector2.hpp>
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

View File

@ -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 <Nazara/Prerequesites.hpp>
#include <Nazara/Core/ResourceListener.hpp>
#include <Nazara/Core/String.hpp>
#include <Nazara/Utility/AbstractTextDrawer.hpp>
#include <Nazara/Utility/Enums.hpp>
#include <Nazara/Utility/Font.hpp>
#include <vector>
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<Glyph> 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

View File

@ -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 <Nazara/Utility/AbstractTextDrawer.hpp>
#include <Nazara/Utility/Debug.hpp>
NzAbstractTextDrawer::~NzAbstractTextDrawer() = default;

View File

@ -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 <Nazara/Utility/SimpleTextDrawer.hpp>
#include <memory>
#include <Nazara/Utility/Debug.hpp>
///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<char32_t[]> 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);
}